From 1014ac90c1cfafbcde0fdddf460fcc1790d266ea Mon Sep 17 00:00:00 2001 From: Jonathan BAUDIN <jonathan.baudin@atmel.com> Date: Mon, 28 Apr 2014 09:22:09 +0200 Subject: [PATCH] Add txBuffer (ring buffer) to SercomUart. Add SPI functions into SERCOM class and create SPI class --- cores/arduino/RingBuffer.cpp | 5 ++ cores/arduino/RingBuffer.h | 1 + cores/arduino/SERCOM.cpp | 100 +++++++++++++++++++++++++- cores/arduino/SERCOM.h | 12 +++- cores/arduino/SERCOMUart.cpp | 13 +++- cores/arduino/SERCOMUart.h | 1 + libraries/SPI/SPI.cpp | 136 ++++++++++++++--------------------- libraries/SPI/SPI.h | 41 ++++------- 8 files changed, 193 insertions(+), 116 deletions(-) diff --git a/cores/arduino/RingBuffer.cpp b/cores/arduino/RingBuffer.cpp index 123f895e..f35e5df3 100644 --- a/cores/arduino/RingBuffer.cpp +++ b/cores/arduino/RingBuffer.cpp @@ -78,4 +78,9 @@ int RingBuffer::peek() int RingBuffer::nextIndex(int index) { return (uint32_t)(index + 1) % SERIAL_BUFFER_SIZE; +} + +int RingBuffer::isFull() +{ + return (nextIndex(_iTail) == _iHead); } \ No newline at end of file diff --git a/cores/arduino/RingBuffer.h b/cores/arduino/RingBuffer.h index 030620b3..49bbe8cd 100644 --- a/cores/arduino/RingBuffer.h +++ b/cores/arduino/RingBuffer.h @@ -41,6 +41,7 @@ class RingBuffer int read_char(); int available(); int peek(); + bool isFull(); private: int nextIndex(int index); diff --git a/cores/arduino/SERCOM.cpp b/cores/arduino/SERCOM.cpp index 920c56d7..d37396f8 100644 --- a/cores/arduino/SERCOM.cpp +++ b/cores/arduino/SERCOM.cpp @@ -91,24 +91,34 @@ void SERCOM::clearStatusUART() bool SERCOM::availableDataUART() { + //RXC : Receive Complete return sercom->USART.INTFLAG.bit.RXC; } bool SERCOM::isBufferOverflowErrorUART() { + //BUFOVF : Buffer Overflow return sercom->USART.STATUS.bit.BUFOVF; } bool SERCOM::isFrameErrorUART() { + //FERR : Frame Error return sercom->USART.STATUS.bit.FERR; } bool SERCOM::isParityErrorUART() { + //PERR : Parity Error return sercom->USART.STATUS.bit.PERR; } +bool SERCOM::isDataRegisterEmptyUART() +{ + //DRE : Data Register Empty + return sercom->USART.INTFLAG.bit.DRE; +} + uint8_t SERCOM::readDataUART() { return sercom->USART.DATA.bit.DATA; @@ -145,6 +155,7 @@ void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize ch void SERCOM::initClock(SercomSpiClockMode clockMode, uint32_t baudrate) { + //Extract data from clockMode int cpha, cpol; if((clockMode & (0x1ul)) == 0 ) @@ -162,7 +173,7 @@ void SERCOM::initClock(SercomSpiClockMode clockMode, uint32_t baudrate) cpol << SERCOM_SPI_CTRLA_CPOL_Pos; //Synchronous arithmetic - sercom->SPI.BAUD.reg = SERCOM_FREQ_REF / (2 * baudrate) - 1; + sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(baudrate); } void SERCOM::resetSPI() @@ -182,8 +193,95 @@ void SERCOM::enableSPI() //Waiting then enable bit from SYNCBUSY is equal to 0; while(sercom->SPI.SYNCBUSY.bit.ENABLE); } + +void SERCOM::disableSPI() +{ + //Set the enable bit to 0 + sercom->SPI.CTRLA.bit.ENABLE = 0x0ul; + + //Waiting then enable bit from SYNCBUSY is equal to 0; + while(sercom->SPI.SYNCBUSY.bit.ENABLE); +} + +void setDataOrderSPI(SercomDataOrder dataOrder) +{ + //Register enable-protected + disableSPI(); + + sercom->SPI.CTRLA.bit.DORD = dataOrder; + + enableSPI(); +} + +void setBaudrateSPI(uint8_t divider) +{ + //Can't divide by 0 + if(baudrate == 0) + return; + + //Register enable-protected + disableSPI(); + + sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(SERCOM_FREQ_REF / baudrate); + + enableSPI(); +} + +void setClockModeSPI(SercomSpiClockMode clockMode) +{ + int cpha, cpol; + if((clockMode & (0x1ul)) == 0) + cpha = 0; + else + cpha = 1; + + if((clockMode & (0x2ul)) == 0) + cpol = 0; + else + cpol = 1; + + //Register enable-protected + disableSPI(); + + sercom->SPI.CTRLA.bit.CPOL = cpol; + sercom->SPI.CTRLA.bit.CPHA = cpha; + + enableSPI(); +} +void writeDataSPI(uint8_t data) +{ + sercom->SPI.DATA.bit.DATA = data; +} + +uint8_t readDataSPI() +{ + return sercom->SPI.DATA.bit.DATA; +} bool SERCOM::isBufferOverflowErrorSPI() { return sercom->SPI.STATUS.bit.BUFOVF; } + +bool SERCOM::isDataRegisterEmptySPI() +{ + //DRE : Data Register Empty + return sercom->SPI.INTFLAG.bit.DRE; +} + +bool SERCOM::isTransmitCompleteSPI() +{ + //TXC : Transmit complete + return sercom->SPI.INTFLAG.bit.TXC; +} + +bool SERCOM::isReceiveCompleteSPI() +{ + //RXC : Receive complete + return sercom->SPI.INTFLAG.bit.RXC; +} + +uint8_t SERCOM::calculateBaudrateSynchronous(uint32_t baudrate) +{ + return SERCOM_FREQ_REF / (2 * baudrate) - 1; +} diff --git a/cores/arduino/SERCOM.h b/cores/arduino/SERCOM.h index a549db89..a7df4cf1 100644 --- a/cores/arduino/SERCOM.h +++ b/cores/arduino/SERCOM.h @@ -114,6 +114,7 @@ class SERCOM bool isBufferOverflowErrorUART(); bool isFrameErrorUART(); bool isParityErrorUART(); + bool isDataRegisterEmptyUART() uint8_t readDataUART(); int writeDataUART(uint8_t data); @@ -123,12 +124,21 @@ class SERCOM void resetSPI(); void enableSPI(); + void disableSPI(); + void setDataOrderSPI(SercomDataOrder dataOrder); + void setBaudrateSPI(uint8_t divider); + void setClockModeSPI(SercomSpiClockMode clockMode); + void writeDataSPI(uint8_t data); + uint8_t readDataSPI(); bool isBufferOverflowErrorSPI(); - + bool isDataRegisterEmptySPI(); + bool isTransmitCompleteSPI(); + bool isReceiveCompleteSPI(); private: Sercom* sercom; + uint8_t calculateBaudrateSynchronous(uint32_t baudrate); }; #endif diff --git a/cores/arduino/SERCOMUart.cpp b/cores/arduino/SERCOMUart.cpp index 87a8a11d..ee3767e3 100644 --- a/cores/arduino/SERCOMUart.cpp +++ b/cores/arduino/SERCOMUart.cpp @@ -37,7 +37,12 @@ void SERCOMUart::IrqHandler() { rxBuffer.store_char(sercom->readDataUART()) } - + + if(sercom->isDataRegisterEmptyUART()) + { + writeDataUART(txBuffer.read_char()); + } + if( sercom->isBufferOverflowErrorUART() || sercom->isFrameErrorUART() || sercom->isParityErrorUART()) @@ -63,7 +68,11 @@ int SERCOMUart::read() size_t SERCOMUart::write(uint8_t data) { - return sercom->writeDataUART(data); + if(txBuffer.isFull()) + return 0; + + txBuffer.store_char(data); + return 1; } SercomNumberStopBit extractNbStopBit(uint8_t config) diff --git a/cores/arduino/SERCOMUart.h b/cores/arduino/SERCOMUart.h index c1c8d8df..7d845899 100644 --- a/cores/arduino/SERCOMUart.h +++ b/cores/arduino/SERCOMUart.h @@ -25,6 +25,7 @@ class SERCOMUart private: SERCOM *sercom; RingBuffer rxBuffer; + RingBuffer txBuffer; SercomNumberStopBit extractNbStopBit(uint8_t config); SercomCharSize extractCharSize(uint8_t config); diff --git a/libraries/SPI/SPI.cpp b/libraries/SPI/SPI.cpp index 9517e222..a2a55c83 100644 --- a/libraries/SPI/SPI.cpp +++ b/libraries/SPI/SPI.cpp @@ -10,109 +10,79 @@ #include "SPI.h" -SPIClass::SPIClass(Spi *_spi, uint32_t _id, void(*_initCb)(void)) : - spi(_spi), id(_id), initCb(_initCb), initialized(false) +SPIClass::SPIClass(SERCOM *sercom) { - // Empty + this->sercom = sercom; } void SPIClass::begin() { - init(); - - // NPCS control is left to the user - - // Default speed set to 4Mhz - setClockDivider(BOARD_SPI_DEFAULT_SS, 21); - setDataMode(BOARD_SPI_DEFAULT_SS, SPI_MODE0); - setBitOrder(BOARD_SPI_DEFAULT_SS, MSBFIRST); -} - -void SPIClass::begin(uint8_t _pin) { - init(); - - uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); - PIO_Configure( - g_APinDescription[spiPin].pPort, - g_APinDescription[spiPin].ulPinType, - g_APinDescription[spiPin].ulPin, - g_APinDescription[spiPin].ulPinConfiguration); - - // Default speed set to 4Mhz - setClockDivider(_pin, 21); - setDataMode(_pin, SPI_MODE0); - setBitOrder(_pin, MSBFIRST); -} - -void SPIClass::init() { - if (initialized) - return; - initCb(); - SPI_Configure(spi, id, SPI_MR_MSTR | SPI_MR_PS | SPI_MR_MODFDIS); - SPI_Enable(spi); - initialized = true; -} - -void SPIClass::end(uint8_t _pin) { - uint32_t spiPin = BOARD_PIN_TO_SPI_PIN(_pin); - // Setting the pin as INPUT will disconnect it from SPI peripheral - pinMode(spiPin, INPUT); + // Default speed set to 4Mhz, SPI mode set to MODE 0 and Bit order set to MSB first. + sercom->initSPI(PAD_0_SCK_1, PAD_2, 8_BITS, MSB_FIRST); + sercom->initClock(MODE_0, 4000000); } void SPIClass::end() { - SPI_Disable(spi); - initialized = false; + sercom->resetSPI(); } -void SPIClass::setBitOrder(uint8_t _pin, BitOrder _bitOrder) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - bitOrder[ch] = _bitOrder; +void setBitOrder(BitOrder order) +{ + if(order == LSBFIRST) + sercom->setDataOrderSPI(LSB_FIRST); + else + sercom->setDataOrderSPI(MSB_FIRST); } -void SPIClass::setDataMode(uint8_t _pin, uint8_t _mode) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - mode[ch] = _mode | SPI_CSR_CSAAT; - // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed - // transfer. Some device needs that for working properly. - SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); +void setDataMode(uint8_t mode) +{ + switch(mode) + { + case SPI_MODE0: + sercom->setClockModeSPI(MODE_0); + break; + + case SPI_MODE1: + sercom->setClockModeSPI(MODE_1); + break; + + case SPI_MODE2: + sercom->setClockModeSPI(MODE_2); + break; + + case SPI_MODE3: + sercom->setClockModeSPI(MODE_3); + break; + + default: + break; + } } -void SPIClass::setClockDivider(uint8_t _pin, uint8_t _divider) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - divider[ch] = _divider; - // SPI_CSR_DLYBCT(1) keeps CS enabled for 32 MCLK after a completed - // transfer. Some device needs that for working properly. - SPI_ConfigureNPCS(spi, ch, mode[ch] | SPI_CSR_SCBR(divider[ch]) | SPI_CSR_DLYBCT(1)); +void setClockDivider(uint8_t div) +{ + sercom->setBaudrateSPI(div); } -byte SPIClass::transfer(byte _pin, uint8_t _data, SPITransferMode _mode) { - uint32_t ch = BOARD_PIN_TO_SPI_CHANNEL(_pin); - // Reverse bit order - if (bitOrder[ch] == LSBFIRST) - _data = __REV(__RBIT(_data)); - uint32_t d = _data | SPI_PCS(ch); - if (_mode == SPI_LAST) - d |= SPI_TDR_LASTXFER; - - // SPI_Write(spi, _channel, _data); - while ((spi->SPI_SR & SPI_SR_TDRE) == 0) - ; - spi->SPI_TDR = d; - - // return SPI_Read(spi); - while ((spi->SPI_SR & SPI_SR_RDRF) == 0) - ; - d = spi->SPI_RDR; - // Reverse bit order - if (bitOrder[ch] == LSBFIRST) - d = __REV(__RBIT(d)); - return d & 0xFF; +byte SPIClass::transfer(uint8_t _data) +{ + //Can writing new data? + while(!sercom->isDataRegisterEmptySPI()); + + //Writing the data + sercom->writeDataSPI(data); + + //Data sent? new data to read? + while(!sercom->isTransmitCompleteSPI() || !sercom->isReceiveCompleteSPI()); + + //Read data + return sercom->readDataSPI(); } -void SPIClass::attachInterrupt(void) { +void SPIClass::attachInterrupt() { // Should be enableInterrupt() } -void SPIClass::detachInterrupt(void) { +void SPIClass::detachInterrupt() { // Should be disableInterrupt() } diff --git a/libraries/SPI/SPI.h b/libraries/SPI/SPI.h index 697de19e..9f3830bf 100644 --- a/libraries/SPI/SPI.h +++ b/libraries/SPI/SPI.h @@ -12,46 +12,29 @@ #define _SPI_H_INCLUDED #include "variant.h" -#include <stdio.h> - -#define SPI_MODE0 0x02 -#define SPI_MODE1 0x00 -#define SPI_MODE2 0x03 -#define SPI_MODE3 0x01 +#include "SERCOM.h" -enum SPITransferMode { - SPI_CONTINUE, - SPI_LAST -}; +#include <stdio.h> class SPIClass { public: - SPIClass(Spi *_spi, uint32_t _id, void(*_initCb)(void)); + SPIClass(SERCOM *sercom); - byte transfer(uint8_t _data, SPITransferMode _mode = SPI_LAST) { return transfer(BOARD_SPI_DEFAULT_SS, _data, _mode); } - byte transfer(byte _channel, uint8_t _data, SPITransferMode _mode = SPI_LAST); + byte transfer(uint8_t _data); // SPI Configuration methods - void attachInterrupt(void); - void detachInterrupt(void); + void attachInterrupt(); + void detachInterrupt(); - void begin(void); - void end(void); + void begin(); + void end(); - void setBitOrder(BitOrder _order); - void setDataMode(uint8_t _mode); - void setClockDivider(uint8_t _div); + void setBitOrder(BitOrder order); + void setDataMode(uint8_t mode); + void setClockDivider(uint8_t div); private: - void init(); - - Spi *spi; - uint32_t id; - BitOrder bitOrder[SPI_CHANNELS_NUM]; - uint32_t divider[SPI_CHANNELS_NUM]; - uint32_t mode[SPI_CHANNELS_NUM]; - void (*initCb)(void); - bool initialized; + SERCOM *sercom; }; #if SPI_INTERFACES_COUNT > 0 -- GitLab