Serial Peripheral Interface: SPI

On the ATSAM4S there is only one SPI interface on pins PA12, PA13, PA14, (MISO, MOSI, SPCK). In addition there are several NPCS pins on both peripheral A and B. If you need more, you can use the USART in serial mode, but it's not as fast.

Similar to i2c, you can connect several devices to the MISO, MOSI and SPCK, but you'll have to use an extra pin for each device so you can select each device one at a time. The Block diagram from the data sheet shows this:



SPI pins:


The SPI can be run up to the frequency of the peripheral clock in MASTER or SLAVE mode. You can use the NPCSn pins to select the slave device that you want. I had trouble getting this to work and I ended up using a regular GPIO for slave select. Set spi mode (polarity and ncpha)

Prerequisites

Set the system clock for the peripheral and give the peripheral control of the pins that will be used.

Setup

I will be using the flir lepton sensor in this example. We will connect the SPI pins MOSI, MISO, and SPCK as well as two GPIO pins for slave select and flir reset (active low). A full schematic will be available under projects later. To setup the SPI peripheral we will need to do the following:

  • Enable the spi peripheral clock
  • Set spi master mode on the ATSAM4S
  • Set polarity and clock phase (spi mode 3 for flir sensor)
  • Set the clock bit rate
  • Give peripheral control of the pins
  • Setup GPIO for slave select
  • Enable SPI

After SPI has been setup it's a simple matter of trading bytes between the master and slave and you can either poll for the trade to be finished or use an interrupt. To initiate the transfer you set the byte you want to send in the REG_SPI_TDR register, poll or interrupt until the transfer is complete and read REG_SPI_RDR for the data from the slave.

Example


#include "sam.h"

void clock_init(){
    REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN;
    while (!(REG_PMC_SR & PMC_SR_MOSCXTS));
    REG_CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
    REG_PMC_MCKR |= MC_MCKR_CSS_MAIN_CLK;
    while (!(REG_PMC_SR & PMC_SR_MCKRDY));
    REG_PMC_MCKR |= PMC_MCKR_PRES_CLK_1;
    while (!(REG_PMC_SR & PMC_SR_MCKRDY));
}

void SPI_setMode(uint8_t mode){	
    /*
    Mode		CPOL	NCPHA
    Mode0		0		1
    Mode1		0		0
    Mode2		1		1
    Mode3		1		0
    */
    if (mode == 0){
        REG_SPI_CSR &= ~SPI_CSR_CPOL;
        REG_SPI_CSR |= SPI_CSR_NCPHA;
    }
    else if (mode == 1){
        REG_SPI_CSR &= ~SPI_CSR_CPOL;
        REG_SPI_CSR &= ~SPI_CSR_NCPHA;
    }
    else if (mode == 2){
        REG_SPI_CSR |= SPI_CSR_CPOL;
        REG_SPI_CSR |= SPI_CSR_NCPHA;
    }
    else if (mode == 3){
        REG_SPI_CSR |= SPI_CSR_CPOL;
        REG_SPI_CSR &= ~SPI_CSR_NCPHA;
    }
}

void SPI_selectPeripheral(uint8_t peripheral){
    //chose peripheral
    //this only works if SPI_MR.PS = 0
    //if SPI_MR.PS = 1 then peripheral selection is done in SPI_THR.PCS
    if (peripheral == 0){
        //choose NPCS0
        REG_SPI_MR |= SPI_MR_PCS(0b1110);
    }
    else if (peripheral ==1){
        //choose NPCS1
        REG_SPI_MR |= SPI_MR_PCS(0b1101);
    }
    else if (peripheral ==2){
        //choose NPCS2
        REG_SPI_MR |= SPI_MR_PCS(0b1011);
    }
    else if (peripheral ==3){
        //choose NPCS3
        REG_SPI_MR |= SPI_MR_PCS(0b0111);
    }
}

void SPI_deselect/span>(){
    //wait for transmit register to be empty
    while (!(REG_SPI_SR & SPI_SR_TDRE));
    //send data to transmit register
    REG_SPI_TDR |= SPI_TDR_LASTXFER;
    //wait for received data to be ready to be read
    while (!(REG_SPI_SR & SPI_SR_RDRF));
}

void SPI_init(){
    //enable peripheral clock
    REG_PMC_PCER0 |= PMC_PCER0_PID21;
    //set spi master mode
    REG_SPI_MR |= SPI_MR_MSTR;
    //set fixed peripheral select(peripheral chosen in SP_MR.PCS instead of SPI_THR.PCS)
    REG_SPI_MR &= ~SPI_MR_PS;
    //set polarity and clock phase
    SPI_setMode(3);
    //set clock generator (1 = peripheral clock rate), otherwise a divisor
    //SCBR = fperipheral clock / SPCK Bit Rate
    REG_SPI_CSR |= SPI_CSR_SCBR(1);
    //chip select remains low after transfer
    REG_SPI_CSR |= SPI_CSR_CSNAAT;
    //give peripheral control of pins (Chip select pins are optional)
    REG_PIOA_PDR |= PIO_PDR_P11; //NPCS0
    //REG_PIOA_PDR |= PIO_PDR_P31; //NPCS1
    REG_PIOA_PDR |= PIO_PDR_P12; //MISO
    REG_PIOA_PDR |= PIO_PDR_P13; //MOSI
    REG_PIOA_PDR |= PIO_PDR_P14; //MOSI
    //enable SPI
    REG_SPI_CR |= SPI_CR_SPIEN;
}

//flir slave select is pa30
    void slaveselect(){
    REG_PIOA_CODR |= PIO_CODR_P31; //set PA30 loaw	
}

void slavedeselect(){
    //wait for transmit register to be empty
    while (!(REG_SPI_SR & SPI_SR_TDRE));
    //send data to transmit register
    REG_SPI_TDR|= SPI_TDR_LASTXFER;
    //wait for received data to be ready to be read
    while (!(REG_SPI_SR & SPI_SR_RDRF));
    REG_PIOA_SODR |= PIO_SODR_P31; //set PA30 high
}

//flir reset is pa30
void flirReset_assert(){
    //clear pa30 to reset pin
    //check hold time and make delay
    REG_PIOA_CODR |= PIO_CODR_P30;
}
void flirReset_deassert(){
    //reset pin high to prevent reset
    REG_PIOA_SODR |= PIO_SODR_P30;
}

uint8_t SPI_byteExchange(uint8_t data){
    //wait for transmit register to be empty
    while (!(REG_SPI_SR & SPI_SR_TDRE));
    //send data to transmit register
    REG_SPI_TDR |= data;
    //wait for received data to be ready to be read
    while (!(REG_SPI_SR & SPI_SR_RDRF));
    //read received data
    return REG_SPI_RDR;
}

int main(void)
{
    /* Initialize the SAM system */
    SystemInit();
    clock_init();
    UART_Init();
    SPI_init();
    flirReset_deassert();
	
    //use pa31 as flir slave select
    REG_PIOA_PER |= PIO_PER_P31; //set PA30 as controllable by the PIO controller (disable peripheral)
    REG_PIOA_OER |= PIO_OER_P31; //set PA30 as output
    REG_PIOA_PUER |= PIO_PUER_P31; //enable light pull up
    //use pa30 as flir reset pin (active low)
    REG_PIOA_PER |= PIO_PER_P30;
    REG_PIOA_OER |= PIO_OER_P30;
    REG_PIOA_PUER |= PIO_PUER_P30;
	
    //Flir Sensor Array
    static uint8_t flir_array[160][60];
	
    uint8_t spiByte;
    uint8_t dummyByte = 0b00000000;
    uint8_t flirMSB;
    uint8_t flirLSB;
    uint8_t packet = 0; //flir packet number
	
    //start collecting data from flir sensor
    slaveselect();
	
    while (1){
        //get header
        flirMSB = SPI_byteExchange(dummyByte);
        flirLSB = SPI_byteExchange(dummyByte);
        //get crc (and ignore for now)
        spiByte = SPI_byteExchange(dummyByte);
        spiByte = SPI_byteExchange(dummyByte);
        //check header for sync start
        if((flirMSB & 0x0F) == 0x0F){
            //bad packet discard
            for (uint8_t i=0;i<80;i++){
                //read out the full packet and discard
                spiByte = SPI_byteExchange(dummyByte);
                spiByte = SPI_byteExchange(dummyByte);
            }
        }
	else if ((flirMSB & 0x0F) != 0x0F){
            //good packet
            for (uint8_t i=0;i<160;i++){
                //read out the full packet and save to array
                flir_array [i][packet] = SPI_byteExchange(dummyByte);											
            }    
            //Increment packet number. There are 60 packets in one frame
            packet+=1;
            if (packet == 60){
                break;
            }
        }
    }
    slavedeselect();
    while (1)
    {
    //do something with flir data array
    }
}