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