Skip to content
Snippets Groups Projects
Commit 953980a8 authored by Jonathan BAUDIN's avatar Jonathan BAUDIN
Browse files

Merge branch 'sercom' into zero

parents 2c41d468 1014ac90
No related branches found
No related tags found
No related merge requests found
......@@ -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
......@@ -41,6 +41,7 @@ class RingBuffer
int read_char();
int available();
int peek();
bool isFull();
private:
int nextIndex(int index);
......
......@@ -8,103 +8,280 @@ SERCOM::SERCOM(Sercom* sercom)
sercomSpi = sercom->SPI;
}
/* =========================
* ===== Sercom UART
* =========================
*/
void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate)
{
resetUART();
sercomUart->CTRLA.reg = SERCOM_UART_CTRLA_MODE(mode) |
//Setting the CTRLA register
sercom->USART.CTRLA.reg = SERCOM_UART_CTRLA_MODE(mode) |
SERCOM_USART_CTRLA_SAMPR(sampleRate);
//
if(mode == UART_INT_CLOCK)
{
uint32_t sampleRateValue;
if(sampleRate == SAMPLE_RATE_x16)
{
sampleRateValue = 16;
}
else if(sampleRate == SAMPLE_RATE_x8)
{
sampleRateValue = 8;
}
else
{
sampleRateValue = 3;
}
//Asynchronous arithmetic mode
sercomUart->BAUD.reg = 65535 * ( 1 - sampleRateValue * (baudrate / SERCOM_FREQ_REF));
sercom->USART.BAUD.reg = 65535 * ( 1 - sampleRateValue * (baudrate / SERCOM_FREQ_REF));
}
}
void initFrame(SercomCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits)
void initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits)
{
sercomUart->CTRLA.reg |= SERCOM_UART_CTRLA_FORM( (parityMode == NO_PARITY ? 0 : 1) ) |
//Setting the CTRLA register
sercom->USART.CTRLA.reg |= SERCOM_UART_CTRLA_FORM( (parityMode == NO_PARITY ? 0 : 1) ) |
dataOrder << SERCOM_UART_CTRLA_DORD_Pos;
sercomUart->CTRLB.reg |= SERCOM_UART_CTRLB_CHSIZE(charSize) |
//Setting the CRTLB register
sercom->USART.CTRLB.reg |= SERCOM_UART_CTRLB_CHSIZE(charSize) |
nbStopBits << SERCOM_UART_CTRLB_SBMODE_Pos |
(parityMode == NO_PARITY ? 0 : parityMode) << SERCOM_UART_CTRLB_PMODE_Pos |;
(parityMode == NO_PARITY ? 0 : parityMode) << SERCOM_UART_CTRLB_PMODE_Pos; //If no parity use default value
}
void initPads(SercomUartTXPad txPad, SercomUartRXPad rxPad)
void initPads(SercomUartTXPad txPad, SercomRXPad rxPad)
{
sercomUart->CTRLA.reg |= SERCOM_UART_CTRLA_TXPO(txPad) |
//Setting the CTRLA register
sercom->USART.CTRLA.reg |= SERCOM_UART_CTRLA_TXPO(txPad) |
SERCOM_UART_CTRLA_RXPO(rxPad);
sercomUart->CTRLB.reg |= SERCOM_UART_CTRLB_TXEN |
//Setting the CRTLB register (Enabling Transceiver and Receiver)
sercom->USART.CTRLB.reg |= SERCOM_UART_CTRLB_TXEN |
SERCOM_UART_CTRLB_RXEN;
}
void SERCOM::resetUART()
{
sercomUart->CTRLA.bit.SWRST = 0x1u;
while(sercomUart->CTRLA.bit.SWRST || sercomUart->SYNCBUSY.SWRST);
//Setting the Software bit to 1
sercom->USART.CTRLA.bit.SWRST = 0x1u;
//Wait for both bits Software Reset from CTRLA and SYNCBUSY are equal to 0
while(sercom->USART.CTRLA.bit.SWRST || sercom->USART.SYNCBUSY.SWRST);
}
void SERCOM::enableUART()
{
sercomUart->CTRLA.bit.ENABLE = 0x1u;
//Setting the enable bit to 1
sercom->USART.CTRLA.bit.ENABLE = 0x1u;
//Wait for then enable bit from SYNCBUSY is equal to 0;
while(sercom->USART.SYNCBUSY.bit.ENABLE);
}
void SERCOM::flushUART()
{
while(sercomUart->INTFLAG.bit.DRE != SERCOM_USART_INTFLAG_DRE);
// Wait for transmission to complete
while(sercom->USART.INTFLAG.bit.DRE != SERCOM_USART_INTFLAG_DRE);
}
void SERCOM::clearStatusUART()
{
sercomUart->STATUS.reg = SERCOM_USART_STATUS_RESETVALUE;
//Reset (with 0) the STATUS register
sercom->USART.STATUS.reg = SERCOM_USART_STATUS_RESETVALUE;
}
bool SERCOM::availableDataUART()
{
return sercomUart->INTFLAG.bit.RXC;
//RXC : Receive Complete
return sercom->USART.INTFLAG.bit.RXC;
}
bool SERCOM::isBufferOverflowErrorUART()
{
return sercomUart->STATUS.bit.BUFOVF;
//BUFOVF : Buffer Overflow
return sercom->USART.STATUS.bit.BUFOVF;
}
bool SERCOM::isFrameErrorUART()
{
return sercomUart->STATUS.bit.FERR;
//FERR : Frame Error
return sercom->USART.STATUS.bit.FERR;
}
bool SERCOM::isParityErrorUART()
{
return sercomUart->STATUS.bit.PERR;
//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 sercomUart->DATA.bit.DATA;
return sercom->USART.DATA.bit.DATA;
}
int SERCOM::writeDataUART(uint8_t data)
{
//Flush UART buffer
flushUART();
sercomUart->DATA.bit.DATA = data;
//Put data into DATA register
sercom->USART.DATA.bit.DATA = data;
return 1;
}
/* =========================
* ===== Sercom SPI
* =========================
*/
void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder)
{
resetSPI();
//Setting the CTRLA register
sercom->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE(SPI_MASTER_OPERATION) |
SERCOM_SPI_CTRLA_DOPO(mosi) |
SERCOM_SPI_CTRLA_DIPO(miso) |
dataOrder << SERCOM_SPI_CTRLA_DORD_Pos;
//Setting the CTRLB register
sercom->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_CHSIZE(charSize) |
(0x1ul) << SERCOM_SPI_CTRLB_RXEN_Pos; //Active the SPI receiver.
}
void SERCOM::initClock(SercomSpiClockMode clockMode, uint32_t baudrate)
{
//Extract data from clockMode
int cpha, cpol;
if((clockMode & (0x1ul)) == 0 )
cpha = 0;
else
cpha = 1;
if((clockMode & (0x2ul)) == 0)
cpol = 0;
else
cpol = 1;
//Setting the CTRLA register
sercom->SPI.CTRLA.reg |= cpha << SERCOM_SPI_CTRLA_CPHA_Pos |
cpol << SERCOM_SPI_CTRLA_CPOL_Pos;
//Synchronous arithmetic
sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(baudrate);
}
void SERCOM::resetSPI()
{
//Set the Software bit to 1
sercom->SPI.CTRLA.bit.SWRST = 0x1u;
//Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0
while(sercom->SPI.CTRLA.bit.SWRST || sercom->SPI.SYNCBUSY.SWRST);
}
void SERCOM::enableSPI()
{
//Set the enable bit to 1
sercom->SPI.CTRLA.bit.ENABLE = 0x1ul;
//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;
}
......@@ -49,7 +49,7 @@ typedef enum
5_BITS = 0x5u,
6_BITS,
7_BITS
} SercomCharSize;
} SercomUartCharSize;
typedef enum
{
......@@ -57,7 +57,7 @@ typedef enum
PAD_1,
PAD_2,
PAD_3
} SercomUartRXPad;
} SercomRXPad;
Vtypedef enum
{
......@@ -76,15 +76,25 @@ typedef enum
typedef enum
{
CPOL_0 = 0,
CPOL_1
} SercomSpiCphaMode;
SPI_MODE_0 = 0, // CPOL : 0 | CPHA : 0
SPI_MODE_1, // CPOL : 0 | CPHA : 1
SPI_MODE_2, // CPOL : 1 | CPHA : 0
SPI_MODE_3 // CPOL : 1 | CPHA : 1
} SercomSpiClockMode;
typedef enum
{
CPHA_0 = 0,
CPHA_1
} SercomSpiCpolMode;
PAD_0_SCK_1 = 0,
PAD_2_SCK_3,
PAD_3_SCK_1,
PAD_0_SCK_3
} SercomSpiTXPad;
typedef enum
{
8_BITS = 0,
9_BITS = 1
} SercomSpiCharSize;
class SERCOM
{
......@@ -93,8 +103,8 @@ class SERCOM
/* ========== UART ========== */
void initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate=0);
void initFrame(SercomCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits);
void initPads(SercomUartTXPad txPad, SercomUartRXPad rxPad);
void initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits);
void initPads(SercomUartTXPad txPad, SercomRXPad rxPad);
void resetUART();
void enableUART();
......@@ -104,17 +114,31 @@ class SERCOM
bool isBufferOverflowErrorUART();
bool isFrameErrorUART();
bool isParityErrorUART();
bool isDataRegisterEmptyUART()
uint8_t readDataUART();
int writeDataUART(uint8_t data);
/* ========== SPI ========== */
void initSPI();
void initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder);
void initClock(SercomSpiClockMode clockMode, uint32_t baudrate);
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;
SercomUart* sercomUart;
SercomSpi* sercomSpi;
uint8_t calculateBaudrateSynchronous(uint32_t baudrate);
};
#endif
......@@ -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())
......@@ -46,7 +51,7 @@ void SERCOMUart::IrqHandler()
}
}
bool SERCOMUart::available()
int SERCOMUart::available()
{
return rxBuffer.available();
}
......@@ -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)
......
......@@ -25,6 +25,7 @@ class SERCOMUart
private:
SERCOM *sercom;
RingBuffer rxBuffer;
RingBuffer txBuffer;
SercomNumberStopBit extractNbStopBit(uint8_t config);
SercomCharSize extractCharSize(uint8_t config);
......
......@@ -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()
}
......
......@@ -12,57 +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 detachInterrupt();
void attachInterrupt(void);
void detachInterrupt(void);
void begin(void);
void end(void);
// Attach/Detach pin to/from SPI controller
void begin(uint8_t _pin);
void end(uint8_t _pin);
// These methods sets a parameter on a single pin
void setBitOrder(uint8_t _pin, BitOrder);
void setDataMode(uint8_t _pin, uint8_t);
void setClockDivider(uint8_t _pin, uint8_t);
// These methods sets the same parameters but on default pin BOARD_SPI_DEFAULT_SS
void setBitOrder(BitOrder _order) { setBitOrder(BOARD_SPI_DEFAULT_SS, _order); };
void setDataMode(uint8_t _mode) { setDataMode(BOARD_SPI_DEFAULT_SS, _mode); };
void setClockDivider(uint8_t _div) { setClockDivider(BOARD_SPI_DEFAULT_SS, _div); };
void begin();
void end();
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
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment