SPIN is my simple library to make it easier to support the different SPI busses. I did when I started doing testing on T3.6. I wanted to test the ILI9341_t3 code on all three of the busses. My first pass at this had the code do things like:
Code:
if (_spi_buss == 0) SPI.beginTransfer(...);
else if (_spi_buss == 1) SPI1.beginTransfer(...);
else if (_spi_buss == 2) SPI2.beginTransfer(...);
Then found I wanted to be able to compile on T3.2... So then had to complicate this and add #if code around each of these, which was not nice.
So I thought it sure would be nice if I could instead have function pointers... But SPI and SPI1 and SPI2 are all separate classes, So I created the SPIN code, which at first was simply files in my test project... Most of the beginning part of the class is simply making virtual functions for each of the standard methods...
Code:
/**********************************************************/
/* SPIN class definition */
/**********************************************************/
class SPINClass {
public:
// Initialize the SPI library
virtual void begin() = 0;
virtual void usingInterrupt(uint8_t n) = 0;
virtual void usingInterrupt(IRQ_NUMBER_t interruptName) = 0;
virtual void notUsingInterrupt(IRQ_NUMBER_t interruptName) = 0;
// Before using SPI.transfer() or asserting chip select pins,
// this function is used to gain exclusive access to the SPI bus
// and configure the correct settings.
virtual void beginTransaction(SPISettings settings) = 0;
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
virtual uint8_t transfer(uint8_t data) = 0;
virtual uint16_t transfer16(uint16_t data) = 0;
virtual void transfer(void *buf, size_t count) = 0;
// After performing a group of transfers and releasing the chip select
// signal, this function allows others to access the SPI bus
virtual void endTransaction(void) = 0;
virtual void end() = 0;
virtual void setBitOrder(uint8_t bitOrder) = 0;
virtual void setDataMode(uint8_t dataMode) = 0;
virtual void setClockDivider(uint8_t clockDiv) = 0;
// Teensy 3.x can use alternate pins for these 3 SPI signals.
virtual void setMOSI(uint8_t pin) = 0;
virtual void setMISO(uint8_t pin) = 0;
virtual void setSCK(uint8_t pin) = 0;
// Added ones that are not part of SPI (yet)
virtual bool pinIsMOSI(uint8_t pin) = 0;
virtual bool pinIsMISO(uint8_t pin) = 0;
virtual bool pinIsSCK(uint8_t pin) = 0;
// return true if "pin" has special chip select capability
virtual uint8_t pinIsChipSelect(uint8_t pin) = 0;
virtual bool pinIsChipSelect(uint8_t pin1, uint8_t pin2) = 0;
virtual uint8_t setCS(uint8_t pin) = 0;
// Add helper functions from ILI9341, which manage SPI FIFO
#if defined(KINETISK)
virtual KINETISK_SPI_t *kinetisk_spi (void) = 0;
virtual uint8_t sizeFIFO() = 0;
virtual void waitFifoNotFull(void) = 0;
virtual void waitFifoEmpty(void) = 0;
virtual void waitTransmitComplete(void) = 0;
virtual void waitTransmitComplete(uint32_t mcr) = 0;
// return DMA Channel information
virtual uint8_t dmaTXEvent(void) = 0;
virtual uint8_t dmaRXEvent(void) = 0;
#endif
#if defined(KINETISL)
virtual KINETISL_SPI_t *kinetisl_spi (void);
#endif
};
But I then added a few more methods, as I got tired of seeing all of the libraries trying to update to know what are valid MISO, MOSI, SCK pins for the different platforms... So I added methods to check these in the library in the same way as the SPI library has the pinIsChipSelect methods... Actually think it would be nice to propagate these methods back to SPI library...
I also have a method to return a pointer to the SPI register sets. And then needed to change any code that does things like look at:
KINETISK_SPI0.SR to instead use the pointer like _pkinesisk->SR to allow it to work with any of the busses...
I then added some of the code that was in ili9341_t3 library for managing the queue to the library: example waitFifoNotFull
This was a little more interesting as the queue lengths are different for SPI vs SPI1 and SPI2...
Again most of this code is really simple forwarding functions... That is most of the functions are like:
Code:
void SPIN0Class::begin() {SPI.begin(); }
void SPIN0Class::usingInterrupt(uint8_t n) {SPI.usingInterrupt(n); }
void SPIN0Class::usingInterrupt(IRQ_NUMBER_t interruptName) {SPI.usingInterrupt(interruptName); }
void SPIN0Class::notUsingInterrupt(IRQ_NUMBER_t interruptName) {SPI.notUsingInterrupt(interruptName); }
// Before using SPI.transfer() or asserting chip select pins,
// this function is used to gain exclusive access to the SPI bus
// and configure the correct settings.
void SPIN0Class::beginTransaction(SPISettings settings) {SPI.beginTransaction(settings);}
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
uint8_t SPIN0Class::transfer(uint8_t data) {return SPI.transfer(data);}
uint16_t SPIN0Class::transfer16(uint16_t data) {return SPI.transfer16(data);}
void SPIN0Class::transfer(void *buf, size_t count) {SPI.transfer(buf, count); }
...
void SPIN1Class::begin() {SPI1.begin(); }
void SPIN1Class::usingInterrupt(uint8_t n) {SPI1.usingInterrupt(n); }
void SPIN1Class::usingInterrupt(IRQ_NUMBER_t interruptName) {SPI1.usingInterrupt(interruptName); }
void SPIN1Class::notUsingInterrupt(IRQ_NUMBER_t interruptName) {SPI1.notUsingInterrupt(interruptName); }
// Before using SPI1.transfer() or asserting chip select pins,
// this function is used to gain exclusive access to the SPI bus
// and configure the correct settings.
void SPIN1Class::beginTransaction(SPISettings settings) {SPI1.beginTransaction(settings);}
// Write to the SPI bus (MOSI pin) and also receive (MISO pin)
uint8_t SPIN1Class::transfer(uint8_t data) {return SPI1.transfer(data);}
uint16_t SPIN1Class::transfer16(uint16_t data) {return SPI1.transfer16(data);}
void SPIN1Class::transfer(void *buf, size_t count) {SPI1.transfer(buf, count); }
...
Should mention this also works for TLC board for supporting SPI1.
Again if desired I could build a version that only uses SPI and probably only supports SPI (not SPI1 SPI2)...
One day it might be nice if we could incorporate this or something like this in the main builds