2016-11-15 4 views
0

J'essaye d'écrire mes propres bibliothèques de base pour programmer l'Arduino en C++ pur. J'ai essayé d'utiliser une fonction variadique pour implémenter quelque chose de similaire au ioctl() de Linux pour contrôler le module SPI mais ça ne marchera pas et je n'ai aucune idée pourquoi. Je ne broche pas 13 (Arduino SCK) allumer comme prévu pendant les transactions SPI indiquant que SPI ne fonctionne pas. Toutes les autres fonctions de ma bibliothèque fonctionnent correctement.Fonctions variadiques ne fonctionnant pas sur arduino

Voici ma bibliothèque 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 

Je compile et télécharger mon code à l'Arduino en utilisant les commandes suivantes (spiTest.cpp est un code que j'ai utilisé pour tester cette bibliothèque)

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 

J'ai utilisé des fonctions variadiques pour implémenter ioctl() avant et cela a fonctionné lorsque j'ai utilisé l'IDE Arduino pour compiler et télécharger mon programme. Je ne comprends pas ce qui empêche les fonctions variées de fonctionner correctement dans ce code.

Répondre

0

Vous êtes dans le C++, vous pouvez le faire mieux. Par exemple quelque chose comme:

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

ou sans définir de la même instance de classe fictive, mais vous devez en créer un dans l'appel:

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

Eh bien ... Cela devrait fonctionner, mais je suis curieux de savoir pourquoi variadique Les fonctions ne fonctionnent pas lors de la compilation manuelle si elles fonctionnent lorsque j'utilise l'IDE arduino. –