Skip to content
Snippets Groups Projects
SERCOM.cpp 11.83 KiB
#include "SERCOM.h"

// Constants for Clock multiplexers
#define GENERIC_CLOCK_SERCOM0	(0x14ul)
#define GENERIC_CLOCK_SERCOM1	(0x15ul)
#define GENERIC_CLOCK_SERCOM2	(0x16ul)
#define GENERIC_CLOCK_SERCOM3	(0x17ul)
#define GENERIC_CLOCK_SERCOM4	(0x18ul)
#define GENERIC_CLOCK_SERCOM5	(0x19ul)

SERCOM::SERCOM(Sercom* s)
{
	sercom = s;
}

/* 	=========================
 *	===== Sercom UART
 *	=========================
*/
void SERCOM::initUART(SercomUartMode mode, SercomUartSampleRate sampleRate, uint32_t baudrate)
{		
	resetUART();
	initClockNVIC();
	
	//Setting the CTRLA register
	sercom->USART.CTRLA.reg =	SERCOM_USART_CTRLA_MODE(mode) |
								SERCOM_USART_CTRLA_SAMPR(sampleRate);

	//Setting the Interrupt register
	sercom->USART.INTENSET.reg =	SERCOM_USART_INTENSET_RXC |  //Received complete
									SERCOM_USART_INTENSET_ERROR; //All others errors
									
	
	if(mode == UART_INT_CLOCK)
	{
		uint16_t sampleRateValue;	
		
		if(sampleRate == SAMPLE_RATE_x16)
			sampleRateValue = 16;
		else if(sampleRate == SAMPLE_RATE_x8)
			sampleRateValue = 8;
		else
			sampleRateValue = 3;
		
		//Asynchronous arithmetic mode
		//65535 * ( 1 - sampleRateValue * baudrate / SERCOM_FREQ_REF);
		sercom->USART.BAUD.reg = 65535.0 * ( 1.0 - (float)(sampleRateValue) * (float)(baudrate) / (float)(SERCOM_FREQ_REF));
		
	}
}
void SERCOM::initFrame(SercomUartCharSize charSize, SercomDataOrder dataOrder, SercomParityMode parityMode, SercomNumberStopBit nbStopBits)
{
	//Setting the CTRLA register
	sercom->USART.CTRLA.reg |=	SERCOM_USART_CTRLA_FORM( (parityMode == SERCOM_NO_PARITY ? 0 : 1) ) |
								dataOrder << SERCOM_USART_CTRLA_DORD_Pos;

	//Setting the CTRLB register
	sercom->USART.CTRLB.reg |=	SERCOM_USART_CTRLB_CHSIZE(charSize) |
								nbStopBits << SERCOM_USART_CTRLB_SBMODE_Pos |
								(parityMode == SERCOM_NO_PARITY ? 0 : parityMode) << SERCOM_USART_CTRLB_PMODE_Pos; //If no parity use default value
}

void SERCOM::initPads(SercomUartTXPad txPad, SercomRXPad rxPad)
{
	//Setting the CTRLA register
	sercom->USART.CTRLA.reg |=	SERCOM_USART_CTRLA_TXPO(txPad) |
								SERCOM_USART_CTRLA_RXPO(rxPad);

	//Setting the CTRLB register (Enabling Transceiver and Receiver)
	sercom->USART.CTRLB.reg |=	SERCOM_USART_CTRLB_TXEN |
								SERCOM_USART_CTRLB_RXEN;
}

void SERCOM::resetUART()
{
	//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.bit.SWRST);
}

void SERCOM::enableUART()
{
	//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()
{
	// Wait for transmission to complete
	while(sercom->USART.INTFLAG.bit.DRE != SERCOM_USART_INTFLAG_DRE);
}

void SERCOM::clearStatusUART()
{
	//Reset (with 0) the STATUS register
	sercom->USART.STATUS.reg = SERCOM_USART_STATUS_RESETVALUE;
}

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;
}

int SERCOM::writeDataUART(uint8_t data)
{
	//Flush UART buffer
	flushUART();

	//Put data into DATA register
	sercom->USART.DATA.reg = (uint16_t)data;
	return 1;
}

/*	=========================
 *	===== Sercom SPI
 *	=========================
*/
void SERCOM::initSPI(SercomSpiTXPad mosi, SercomRXPad miso, SercomSpiCharSize charSize, SercomDataOrder dataOrder)
{
	resetSPI();
	initClockNVIC();
	
	//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::initSPIClock(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()
{
	//Setting the Software Reset 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.bit.SWRST);
}
	
void SERCOM::enableSPI()
{
	//Setting 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()
{
	//Setting 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 SERCOM::setDataOrderSPI(SercomDataOrder dataOrder)
{
	//Register enable-protected
	disableSPI();
	
	sercom->SPI.CTRLA.bit.DORD = dataOrder;
	
	enableSPI();
}

void SERCOM::setBaudrateSPI(uint8_t divider)
{
	//Can't divide by 0
	if(divider == 0)
		return;
		
	//Register enable-protected
	disableSPI();
	
	sercom->SPI.BAUD.reg = calculateBaudrateSynchronous(SERCOM_FREQ_REF / divider);
	
	enableSPI();
}

void SERCOM::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 SERCOM::writeDataSPI(uint8_t data)
{
	sercom->SPI.DATA.bit.DATA = data;
}

uint16_t SERCOM::readDataSPI()
{
	return sercom->SPI.DATA.reg;
}

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;
}


/*	=========================
 *	===== Sercom WIRE
 *	=========================
*/

void SERCOM::resetWIRE()
{
	//I2CM OR I2CS, no matter SWRST is the same bit.

	//Setting the Software bit to 1
	sercom->I2CM.CTRLA.bit.SWRST = 0x1ul;

	//Wait both bits Software Reset from CTRLA and SYNCBUSY are equal to 0
	while(sercom->I2CM.CTRLA.bit.SWRST || sercom->I2CM.SYNCBUSY.bit.SWRST);
}

void SERCOM::enableWIRE()
{
	//I2CM OR I2CS, no matter ENABLE is the same bit.
	
	//Setting the enable bit to 1
	sercom->I2CM.CTRLA.bit.ENABLE = 0x1ul;
	
	//Waiting the enable bit from SYNCBUSY is equal to 0;
	while(sercom->I2CM.SYNCBUSY.bit.ENABLE);
}

void SERCOM::initSlaveWIRE(uint8_t address)
{
	resetWIRE();
	
	//Setting slave mode
	sercom->I2CS.CTRLA.bit.MODE = I2C_SLAVE_OPERATION;
	
	//Enable Quick Command
	sercom->I2CM.CTRLB.bit.QCEN = 0x1ul;
	
	sercom->I2CS.ADDR.reg = SERCOM_I2CS_ADDR_ADDR(address & 0x7Ful) | // 0x7F, select only 7 bits
							SERCOM_I2CS_ADDR_ADDRMASK(0x3FFul);		// 0x3FF all bits set
	
	//Setting the interrupt register
	sercom->I2CS.INTENSET.reg = SERCOM_I2CS_INTENSET_AMATCH | //Address Match
								SERCOM_I2CS_INTENSET_DRDY; //Data Ready
							
	//Waiting the SYSOP bit from SYNCBUSY is equal to 0;
	while(sercom->I2CM.SYNCBUSY.bit.SYSOP);
}
void SERCOM::initMasterWIRE(uint32_t baudrate)
{
	resetWIRE();
	
	//Setting master mode and SCL Clock Stretch mode (stretch after ACK bit)
	sercom->I2CM.CTRLA.reg = 	SERCOM_I2CM_CTRLA_MODE(I2C_MASTER_OPERATION) |
								SERCOM_I2CM_CTRLA_SCLSM;
								
	//Enable Quick Command
	sercom->I2CM.CTRLB.bit.QCEN = 0x1ul;
	
	//Setting bus idle mode
	sercom->I2CM.STATUS.bit.BUSSTATE = WIRE_IDLE_STATE;
	
	//Setting all interrupts
	sercom->I2CM.INTENSET.reg = SERCOM_I2CM_INTENSET_MB |
								SERCOM_I2CM_INTENSET_SB |
								SERCOM_I2CM_INTENSET_ERROR;
	
	//Synchronous
	sercom->I2CM.BAUD.bit.BAUD = SERCOM_FREQ_REF / ( 2 * baudrate) - 1;
	
	//Waiting the SYSOP bit from SYNCBUSY is equal to 0;
	while(sercom->I2CM.SYNCBUSY.bit.SYSOP);
}

void SERCOM::prepareStopBitWIRE()
{
	sercom->I2CM.CTRLB.bit.CMD = WIRE_MASTER_ACT_STOP;
}

void SERCOM::prepareAckBitWIRE()
{
	sercom->I2CM.CTRLB.bit.CMD = WIRE_MASTER_ACT_READ;
}

bool SERCOM::startTransmissionWIRE(uint8_t address, SercomWireReadWriteFlag flag)
{
	//7-bits address + 1-bits R/W
	address = (address << 1) | flag;

	//Wait idle bus mode
	while(!isBusIdleWIRE());
	
	//Send start and address
	sercom->I2CM.ADDR.bit.ADDR = address;
	
	
	//Address Transmitted
	while(!sercom->I2CM.INTFLAG.bit.MB);
	
	//ACK received (0: ACK, 1: NACK)
	if(sercom->I2CM.STATUS.bit.RXNACK)
		return false;
	else
		return true;
}

bool SERCOM::sendDataMasterWIRE(uint8_t data)
{
	//Send data
	sercom->I2CM.DATA.bit.DATA = data;
	
	//Wait transmission successful
	while(!sercom->I2CM.INTFLAG.bit.MB);
	
	//Problems on line? nack received?
	if(sercom->I2CM.STATUS.bit.RXNACK)
		return false;
	else
		return true;
}

bool SERCOM::sendDataSlaveWIRE(uint8_t data)
{
	//Send data
	sercom->I2CS.DATA.bit.DATA = data;
	
	//Wait data transmission successful
	while(!sercom->I2CS.INTFLAG.bit.DRDY);
	
	//Problems on line? nack received?
	if(sercom->I2CS.STATUS.bit.RXNACK)
		return false;
	else
		return true;
}

bool SERCOM::isMasterWIRE()
{
	return sercom->I2CS.CTRLA.bit.MODE == I2C_MASTER_OPERATION;
}

bool SERCOM::isSlaveWIRE()
{
	return sercom->I2CS.CTRLA.bit.MODE == I2C_SLAVE_OPERATION;
}

bool SERCOM::isBusIdleWIRE()
{
	return sercom->I2CM.STATUS.bit.BUSSTATE == WIRE_IDLE_STATE;
}

bool SERCOM::isDataReadyWIRE()
{
	return sercom->I2CS.INTFLAG.bit.DRDY;
}

bool SERCOM::isStopDetectedWIRE()
{
	return sercom->I2CS.INTFLAG.bit.PREC;
}

bool SERCOM::isRestartDetectedWIRE()
{
	return sercom->I2CS.STATUS.bit.SR;
}

bool SERCOM::isAddressMatch()
{
	return sercom->I2CS.INTFLAG.bit.AMATCH;
}

bool SERCOM::isMasterReadOperationWIRE()
{
	return sercom->I2CS.STATUS.bit.DIR;
}

int SERCOM::availableWIRE()
{
	if(isMasterWIRE())
		return sercom->I2CM.INTFLAG.bit.SB;
	else
		return sercom->I2CS.INTFLAG.bit.DRDY;
}

uint8_t SERCOM::readDataWIRE()
{
	if(isMasterWIRE())
		return sercom->I2CM.DATA.reg;
	else
		return sercom->I2CS.DATA.reg;
}


void SERCOM::initClockNVIC()
{
	uint8_t clockId = 0;
	IRQn_Type IdNvic;
	
	if(sercom == SERCOM0)
	{
		clockId = GENERIC_CLOCK_SERCOM0;
		IdNvic = SERCOM0_IRQn;
	}
	else if(sercom == SERCOM1)
	{
		clockId = GENERIC_CLOCK_SERCOM1;
		IdNvic = SERCOM1_IRQn;
	}
	else if(sercom == SERCOM2)
	{
		clockId = GENERIC_CLOCK_SERCOM2;
		IdNvic = SERCOM2_IRQn;
	}
	else if(sercom == SERCOM3)
	{
		clockId = GENERIC_CLOCK_SERCOM3;
		IdNvic = SERCOM3_IRQn;
	}
	else if(sercom == SERCOM4)
	{
		clockId = GENERIC_CLOCK_SERCOM4;
		IdNvic = SERCOM4_IRQn;
	}
	else if(sercom == SERCOM5)
	{
		clockId = GENERIC_CLOCK_SERCOM5;
		IdNvic = SERCOM5_IRQn;
	}
	
	//Setting NVIC
	NVIC_EnableIRQ(IdNvic);
	NVIC_SetPriority (IdNvic, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority */
	
	//Setting clock
	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID( clockId ) | // Generic Clock 0 (SERCOMx)
						GCLK_CLKCTRL_GEN_GCLK0 | // Generic Clock Generator 0 is source
						GCLK_CLKCTRL_CLKEN ;
	
	while ( GCLK->STATUS.reg & GCLK_STATUS_SYNCBUSY )
	{
		/* Wait for synchronization */
	}
	
}