2016-11-15 31 views
0

私は純粋なC++でArduinoをプログラムするための独自の基本ライブラリを作成しようとしています。私はSPIモジュールを制御するためにLinuxのioctl()に似た何かを実装するためにvariadic関数を使用しようとしましたが、それはうまく機能せず、なぜわかりません。私は、SPIが動作していないことを示すSPIトランザクション中に、13(Arduino SCK)が予期したとおりに点灯することはありません。私のライブラリ内の他のすべての機能は正常に動作します。Arduinoで動作しないVariadic関数

次は私の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 

私は、次のコマンドを使用してアルドゥイーノに私のコードをコンパイルしてアップロードしています(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を使用してプログラムをコンパイルしてアップロードすると機能しました。私はvariadic関数がこのコードで正しく機能しない原因を理解していません。

答えて

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)); 
} 

または同様に、しかし、あなたは、コールに1を作成する必要があります。

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{}); 
} 
+0

うーん...それが動作するはずですが、なぜ可変長私は好奇心が強いです関数は、arduino IDEを使用するときに動作するかどうかを手動でコンパイルするときには機能しません。 –