【问题标题】:Variadic functions not working on arduino可变参数函数不适用于arduino
【发布时间】:2016-11-15 17:20:18
【问题描述】:

我正在尝试编写自己的基本库来用纯 C++ 对 Arduino 进行编程。我尝试使用可变参数函数来实现类似于 Linux 的 ioctl() 的东西来控制 SPI 模块,但它不起作用,我不知道为什么。在 SPI 事务期间,我没有按预期点亮 13 针(Arduino SCK),表明 SPI 未运行。我的库中的所有其他功能都可以正常工作。

以下是我的 SPI 库:

/*
    spi.h:  SPI driver for Atmega328p
*/

#ifndef     _SPI_H
#define     _SPI_H

#include <avr/io.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdarg.h>
#include <inttypes.h>

// SPI bus pin mapping ///////////////////////////////////

#define PORT_SPI    PORTB           // Port register containing SPI pins
#define DDR_SPI     DDRB            // Data direction register containing SPI pins

#define DDR_SCK     DDB5            // Data direction bit of SPI SCK pin
#define DDR_MISO    DDB4            // Data direction bit of SPI MISO pin
#define DDR_MOSI    DDB3            // Data direction bit of SPI MOSI pin
#define DDR_HWCS    DDB2            // Data direction bit of SPI hardware chip select pin

#define PIN_SCK     PB5             // Port register bit of SPI SCK pin
#define PIN_MISO    PB4             // Port register bit of SPI MISO pin
#define PIN_MOSI    PB3             // Port register bit of SPI MOSI pin
#define PIN_HWCS    PB2             // Port register bit of SPI hardware chip select pin

// SPI ioctl commands ////////////////////////////////////

#define SPIIOCCONF          0       // Configure SPI command
#define SPIIOCDECONF        1       // Deconfigure SPI command
#define SPIIOCTRANSMIT      2       // SPI byte exchange command

// Clock frequency settings //////////////////////////////

#define SCK_DIV2    2               // Divide source pulse by 2
#define SCK_DIV4    4               // Divide source pulse by 4
#define SCK_DIV8    8               // Divide source pulse by 8
#define SCK_DIV16   16              // Divide source pulse by 16
#define SCK_DIV32   32              // Divide source pulse by 32
#define SCK_DIV64   64              // Divide source pulse by 64
#define SCK_DIV128  128             // Divide source pulse by 128

// SPI modes /////////////////////////////////////////////

#define SPI_MODE0   0
#define SPI_MODE1   1
#define SPI_MODE2   2
#define SPI_MODE3   3

// SPI transaction data orders ///////////////////////////

#define LSBFIRST    0
#define MSBFIRST    1

// The SPI module ////////////////////////////////////////

class spiModule {

    private:
            bool    configured;                             // Indicates whether SPI is operating with a valid configuration

            uint8_t ddrOld, portOld;                        // Value of DDR_SPI and PORT_SPI just before an SPI configuration was called for
                                                            // ( These variables are used to restore the state of the
                                                            // SPI pins when SPI is deconfigured )

            /* ioctls used to operate the SPI module */

            void    spiiocconf(int, int, int);              // Configure SPI with a valid clock frequency, data order and SPI mode
            void    spiiocdeconf(void);                     // Deconfigure SPI and restore SPI pins to their original states
            void    spiioctransmit(uint8_t, uint8_t *);     // Exchange a byte of data over SPI

            /* ioctl handlers */
            /* These routines check the validity of the arguments and call the ioctls (above) only if all arguments make sense */
            /* I've tested these functions by making them public and found that they work perfectly */

            int     conf(int, int, int);                    // call spiiocconf() if arguments are valid and SPI is configured
            int     deconf(void);                           // call spiiocdeconf() if all arguments are valid and SPI is configured
            int     transmit(uint8_t, uint8_t *);           // call spiioctransmit() if all arguments are valid and SPI is configured

    public:
                    spiModule(void);                        // Initialize this class
            int     ioctl(int action, ... );                // Core ioctl handler (supposed to work like the Linux ioctl() system call). But this one just won't work.
};

spiModule spi;                                              // Object of class spiModule for using SPI

// Constructor ///////////////////////////////////////////

spiModule::spiModule(void) {

    configured = false;
}

// Private routines //////////////////////////////////////

/* Ioctls */

void        spiModule::spiiocconf(int clkDiv, int dataOrder, int mode) {

    // Store the values of DDR_SPI and PORT_SPI so they may be recovered when SPI is deconfigured

    ddrOld = DDR_SPI;
    portOld = PORT_SPI;

    // Configure SCK, MOSI and HWCS as output pins and MISO as an input pin

    DDR_SPI |= (_BV(DDR_HWCS) | _BV(DDR_SCK) | _BV(DDR_MOSI));
    DDR_SPI &= ~_BV(DDR_MISO);

    // Power up the SPI module

    PRR &= ~_BV(PRSPI);

    // Enable SPI and configure it as master

    SPCR = 0x00;
    SPCR |= (_BV(SPE) | _BV(MSTR));

    // Set data order

    switch(dataOrder)
    {
            case    LSBFIRST:
                    SPCR |= _BV(DORD);
                    break;

            case    MSBFIRST:
                    SPCR &= ~_BV(DORD);
                    break;
    }

    // Set SPI mode

    switch(mode)
    {
            case    SPI_MODE0:
                    SPCR &= ~(_BV(CPOL) | _BV(CPHA));
                    break;

            case    SPI_MODE1:
                    SPCR |= _BV(CPHA);
                    SPCR &= ~_BV(CPOL);
                    break;

            case    SPI_MODE2:
                    SPCR &= ~_BV(CPHA);
                    SPCR |= _BV(CPOL);
                    break;

            case    SPI_MODE3:
                    SPCR |= (_BV(CPOL) | _BV(CPHA));
                    break;
    }

    // Set SPI clock frequency

    switch(clkDiv)
    {
            case    SCK_DIV2:
                    SPCR &= ~(_BV(SPR0) | _BV(SPR1));
                    SPSR |= _BV(SPI2X);
                    break;

            case    SCK_DIV4:
                    SPCR &= ~(_BV(SPR0) | _BV(SPR1));
                    SPSR &= ~_BV(SPI2X);
                    break;

            case    SCK_DIV8:
                    SPCR |= _BV(SPR0);
                    SPCR &= ~_BV(SPR1);
                    SPSR |= _BV(SPI2X);
                    break;

            case    SCK_DIV16:
                    SPCR |= _BV(SPR0);
                    SPCR &= ~_BV(SPR1);
                    SPSR &= ~_BV(SPI2X);
                    break;

            case    SCK_DIV32:
                    SPCR &= ~_BV(SPR0);
                    SPCR |= _BV(SPR1);
                    SPSR |= _BV(SPI2X);
                    break;

            case    SCK_DIV64:
                    SPCR |= _BV(SPR0);
                    SPCR |= _BV(SPR1);
                    SPSR |= _BV(SPI2X);
                    break;

            case    SCK_DIV128:
                    SPCR |= _BV(SPR0);
                    SPCR |= _BV(SPR1);
                    SPSR &= ~_BV(SPI2X);
                    break;
    }

    // SPI is now configured

    configured = true;
    return;
}

void        spiModule::spiiocdeconf(void) {

    // Clear SPI configuration, power down the SPI module and restore the values of DDR_SPI and PORT_SPI

    SPCR = 0x00;
    PRR |= _BV(PRSPI);

    DDR_SPI = ddrOld;
    PORT_SPI = portOld; 

    // SPI is no longer configured

    configured = false;

    return;
}

void        spiModule::spiioctransmit(uint8_t txbyte, uint8_t * rxbyte) {

    // Write TX byte to data register

    SPDR = txbyte;
    while(!(SPSR & _BV(SPIF)))
    {
            /* wait for data transmission to complete */
    }
    SPSR &= ~_BV(SPIF);

    // Return RX byte by storing it at the specified location

    if(rxbyte != NULL)
    {
            *rxbyte = SPDR;
    }

    return;
}

/* Ioctl handlers (verify that all arguments are appropriate and only then proceed with the ioctl) */

int         spiModule::conf(int clkDiv, int dataOrder, int mode) {

    // Return with error of SPI is not configured

    if(!configured)
    {
            return -1;
    }

    // Verify validity of clkDiv (clock pulse division factor)

    switch(clkDiv)
    {
            case    SCK_DIV2:
                    break;
            case    SCK_DIV4:
                    break;
            case    SCK_DIV8:
                    break;
            case    SCK_DIV16:
                    break;
            case    SCK_DIV32:
                    break;
            case    SCK_DIV64:
                    break;
            case    SCK_DIV128:
                    break;
            default:
                    return -1;
    }

    // Verify validity of dataOrder (order of byte transfer)

    switch(dataOrder)
    {
            case    LSBFIRST:
                    break;
            case    MSBFIRST:
                    break;
            default:
                    return -1;
    }

    // Check validity of mode (SPI mode)

    switch(mode)
    {
            case    SPI_MODE0:
                    break;
            case    SPI_MODE1:
                    break;
            case    SPI_MODE2:
                    break;
            case    SPI_MODE3:
                    break;
            default:
                    return -1;
    }

    // If all goes well, execute the ioctl

    spiiocconf(clkDiv, dataOrder, mode);

    return 0;
}

int         spiModule::deconf(void) {

    // If SPI is configured, deconfigure it

    if(!configured)
    {
            return -1;
    }

    spiiocdeconf();

    return 0;
}

int         spiModule::transmit(uint8_t tx, uint8_t * rx) {

    // If SPI is configured, make a byte exchange

    if(!configured)
    {
            return -1;
    }

    spiioctransmit(tx, rx);

    return 0;
}

// Public routines ///////////////////////////////////////

int         spiModule::ioctl(int action, ... ) {

    // This routine checks the value of action and executes the respective ioctl
    // It returns with error if the value of action is not valid

    va_list ap;

    int     clkDiv, dataOrder, mode;
    uint8_t txbyte;
    uint8_t * rxbyte;

    int     retVal;

    switch(action)
    {
            case    SPIIOCCONF:

                    va_start(ap, action);

                    clkDiv = va_arg(ap, int);
                    dataOrder = va_arg(ap, int);
                    mode = va_arg(ap, int);

                    va_end(ap);

                    retVal = conf(clkDiv, dataOrder, mode);
                    return retVal;

            case    SPIIOCDECONF:

                    retVal = deconf();
                    return retVal;

            case    SPIIOCTRANSMIT:

                    va_start(ap, action);

                    txbyte = va_arg(ap, uint8_t);
                    rxbyte = va_arg(ap, uint8_t*);

                    va_end(ap);

                    retVal = transmit(txbyte, rxbyte);
                    return retVal;

            default:
                    return -1;
    }
}

#endif

我正在使用以下命令编译并上传我的代码到 Arduino(spiTest.cpp 是我用来测试这个库的代码)

COMPILER=~/Softwares/arduino-1.6.8/hardware/tools/avr/bin/avr-g++
HEXGENERATOR=~/Softwares/arduino-1.6.8/hardware/tools/avr/bin/avr-objcopy
UPLOADER=~/Softwares/arduino-1.6.8/hardware/tools/avr/bin/avrdude

AVRDUDE_CFG=~/Softwares/arduino-1.6.8/hardware/tools/avr/etc/avrdude.conf

$COMPILER -c -g -w -D F_CPU=16000000UL -mmcu=atmega328p -std=gnu++11 -o spiTest.o spiTest.cpp
$COMPILER -mmcu=atmega328p spiTest.o -o spiTest

$HEXGENERATOR -O ihex -R .eeprom spiTest spiTest.hex

$UPLOADER -C $AVRDUDE_CFG -v -p atmega328p -c arduino -P /dev/ttyACM0 -b 115200 -D -U flash:w:spiTest.hex:i

我之前使用可变参数函数来实现ioctl(),当我使用 Arduino IDE 编译和上传我的程序时它可以工作。我不明白是什么阻止了可变参数函数在这段代码中正常工作。

【问题讨论】:

    标签: arduino variadic-functions avr-gcc avrdude


    【解决方案1】:

    您使用的是 C++,您可以做得更好。例如:

    class      SPIIOCONF_t {}      SPIIOCONF;
    class   SPIIOCDECONF_t {}   SPIIOCDECONF;
    class SPIIOCTRANSMIT_t {} SPIIOCTRANSMIT;
    
    int ioctl(SPIIOCONF_t, int clkDiv, int dataOrder, int mode) {
      return conf(clkDiv, dataOrder, mode);
    }
    
    int ioctl(SPIIOCDECONF_t) {
      return deconf();
    }
    
    int ioctl(SPIIOCTRANSMIT_t, uint8_t txbyte, uint8_t rxbyte) {
      return transmit(txbyte, rxbyte);
    }
    
    void setup() {
      Serial.begin(115200);
    
      Serial.println(ioctl(SPIIOCONF, 10, 1, 1));
      Serial.println(ioctl(SPIIOCDECONF));
    
      uint8_t rx;
      Serial.println(ioctl(SPIIOCTRANSMIT, 0xFF, &rx));
    }
    

    或类似地不定义虚拟类实例,但您必须在调用中创建一个:

    class      SPIIOCONF {};
    class   SPIIOCDECONF {};
    class SPIIOCTRANSMIT {};
    
    int ioctl(SPIIOCONF, int clkDiv, int dataOrder, int mode) {
      return conf(clkDiv, dataOrder, mode);
    }
    
    int ioctl(SPIIOCDECONF) {
      return deconf();
    }
    
    int ioctl(SPIIOCTRANSMIT, uint8_t txbyte, uint8_t rxbyte) {
      return transmit(txbyte, rxbyte);
    }
    
    void setup() {
      ioctl(SPIIOCONF{}, 10, 1, 1);
    
      uint8_t rx;
      Serial.println(ioctl(SPIIOCTRANSMIT{}, 0xFF, &rx));
    
      ioctl(SPIIOCDECONF{});
    }
    

    【讨论】:

    • 嗯...这应该可以,但我很好奇为什么可变参数函数在我使用 arduino IDE 时可以在手动编译时不起作用。
    猜你喜欢
    • 2011-04-01
    • 2022-01-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-13
    • 1970-01-01
    相关资源
    最近更新 更多