变种功能不适用于arduino

时间:2016-11-15 17:20:19

标签: arduino variadic-functions avr-gcc avrdude

我正在尝试编写自己的基本库来编写纯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编译和上传我的程序时,它工作正常。我不明白是什么阻止了可变参数函数在这段代码中正常工作。

1 个答案:

答案 0 :(得分:0)

你在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{});
}