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