ATSAMD21G18A SPI communication problems

Go To Last Post
3 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As the title says i'm working with ATSAMD21G18A and am trying to set up SPI. I tried following the SPI tutorial but i used SERCOM0 instead of 5. I connected an oscilloscope to see if anything happens on PIN PA09 (if i set up everything correctly it should be the MOSI pin), but i get nothing, it's just a flat line. Does a sensor or something similar have to be connected for everything to work? Because at the moment i didn't want to connect it to make testing as simple as possible. Here is my code (just slightly changed tutorial code).
 

#include "sam.h"

#define SPI_CLK_FREQ 8000000
#define SPI_BAUD 50000

// Configure SERCOM0  SPI PINS  PAD
//PA08 -> MISO
//PA09 -> MOSI
//PA10 -> SCK
//PA11 -> SS

void init_SPI_tutorial(){

	while(SERCOM0->SPI.SYNCBUSY.bit.ENABLE);
	SERCOM0->SPI.CTRLA.bit.ENABLE = 0;

	while(SERCOM0->SPI.SYNCBUSY.bit.SWRST);
	SERCOM0->SPI.CTRLA.bit.SWRST = 1;

	while(SERCOM0->SPI.CTRLA.bit.SWRST);
	while(SERCOM0->SPI.SYNCBUSY.bit.SWRST || SERCOM0->SPI.SYNCBUSY.bit.ENABLE);

	PORT->Group[PORT_PMUX_PMUXE_C_Val].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PMUX(MUX_PA08C_SERCOM0_PAD0) |						                        //Bulk configuration for PMUX "C" for SERCOM0
	PORT_WRCONFIG_HWSEL |												//Select the correct pin configurations for 16-31
	PORT_WRCONFIG_INEN |												//Enable input on this pin MISO
	PORT_WRCONFIG_PINMASK((uint16_t)((PORT_PA08) >> 16));				                                //Selecting which pin is configured  PB16  This bit needs to shift to fit the 16 bit macro requirements

	//Using the WRCONFIG register to bulk configure both PB22 and PB23 for being configured the SERCOM0 SPI MASTER MOSI and SCK pins
	PORT->Group[PORT_PMUX_PMUXE_C_Val].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUX(MUX_PA09C_SERCOM0_PAD1) |						                        //Bulk configuration for PMUX
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_HWSEL |												//Select the correct pin configurations for 16-31
	PORT_WRCONFIG_PINMASK ((uint16_t)((PORT_PA09 | PORT_PA10) >> 16));	                                        //Selecting which pin is configured

	PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0;								        //Enable the SERCOM 0 under the PM

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |				                        //Provide necessary clocks to the peripheral
	GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

	while(GCLK->STATUS.bit.SYNCBUSY);										//Wait for clock sync

	SERCOM0->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER|			                                //Configure the Peripheral as SPI Master
	SERCOM_SPI_CTRLA_DOPO(1);											//DOPO is set to PAD[2,3]

	SERCOM0->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_RXEN;						//Enable receive on SPI

	uint16_t BAUD_REG = ((float)SPI_CLK_FREQ / (float)(2 * SPI_BAUD)) - 1;	                //Calculate BAUD value
	SERCOM0->SPI.BAUD.reg =	SERCOM_SPI_BAUD_BAUD(BAUD_REG);				        //Set the SPI baud rate
	SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;				 	//Enable the Sercom SPI
	while(SERCOM0->SPI.SYNCBUSY.bit.ENABLE);

}

uint8_t spiSend(uint8_t data)
{
	while(SERCOM0->SPI.INTFLAG.bit.DRE == 0);
	SERCOM0->SPI.DATA.reg = data;
	while(SERCOM0->SPI.INTFLAG.bit.RXC == 0);
	return (uint8_t)SERCOM0->SPI.DATA.reg;
}

int main(void){

	SystemInit();
        init_SPI_tutorial();

	PORT->Group[0].DIRSET.reg = PORT_PA11;		// output

	PORT->Group[0].OUTSET.reg = PORT_PA11;		// high
	PORT->Group[0].OUTCLR.reg = PORT_PA11;		// low

	uint8_t EE_Read_data;

	while (1) {
		PORT->Group[0].OUTCLR.reg = PORT_PA11;	// low
		EE_Read_data = spiSend(0xaa);
		PORT->Group[0].OUTSET.reg = PORT_PA11;  // high
	}
}

This line looks the most suspicious to me, but i'm not sure to what i should change it. 

PORT_WRCONFIG_PINMASK((uint16_t)((PORT_PA08) >> 16));

Any advice on what to try?

This topic has a solution.
Last Edited: Thu. Jul 29, 2021 - 12:51 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

The main problem you have is that you are not actually configuring PA08, PA09 or PA10. This is because of two errors in the code you use to write WRCONFIG.

 

First, PORT_PMUX_PMUXE_C_Val is not the correct value to be using as an index into PORT->Group. PORT_PMUX_PMUXE_C_Val is intended to be used as a value for writing to the PMXE field of the PMUXn registers, it is defined as 0x2ul, which is not a valid PORT group on the microcontroller you are using. On the SAMD21 your PORT->Group index should always be either 0 (for port A) or 1 (for port B), in this case you want 0 since the pins you are using are in port A.

 

The second thing wrong is what you suspected. The WRCONFIG register only has a 16 bit wide mask for selecting which pins should be configured. Because each port is 32 bits wide, there is an addition bit in the WRCONFIG register called HWSEL (half world select) which choses whether you want to configure some of the the upper 16 pins in the port group or some of the lower 16 pins. If you choose to configure the upper 16 pins then you need to shift the pin mask you are using to the right by 16 bits so that the correct portion of the mask is used.

 

The tutorial you are basing your code on used PB16, PB22 and PB23 as the SERCOM pins which are all in the upper half of the port. Because of this, in the tutorial the HWSEL bit is set and the pin mask is shifted right by 16 bits. The pins you are using are all in the lower half of the port, so you should not set the HWSEL bit and should not shift your mask to the right.

 

 

If you fix the issues mentioned above you should have the pin multiplexing configured properly, but you still won't see the output the you want because there is a more fundamental problem here. It is not possible to use PA09 as the MOSI pin for SERCOM0. This is because selecting the pins for a SERCOM is a two step process, first you configure which SERCOM signals are routed to which microcontroller pins using the PORT peripheral, but then you have to select which SERCOM signals are used for which purpose within the SERCOM peripheral. While you can use any of the SERCOM signals as the MISO pin, there is only a limited number of options for which pins can be used as MOSI and SCK. The DIPO (data in pinout) and DOPO (data out pinout) fields in the SERCOM's CTRLA register control which SERCOM signals are used for which purpose.

 

Your code configures the pinouts as follows:

 

PA08 -> SERCOM0[0] -> MISO

PA09 -> SERCOM0[1] -> SS (This is for hardware controlled SS, which you probably don't want. You don't actually have to configure a pin to connect to the SERCOM signal that is configured to be used as SS.)

PA10 -> SERCOM0[2] -> MOSI

        -> SERCOM0[3] -> SCK

 

You need to look at the documentation for the SERCOM in SPI mode to figure out which mapping of SERCOM signals to pin roles you would like to use. Or you could keep the DIPO and DOPO settings the way you have them and change your pin configuration code to match the SERCOM configuration.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

samueldewan wrote:

irst, PORT_PMUX_PMUXE_C_Val is not the correct value to be using as an index into PORT->Group. PORT_PMUX_PMUXE_C_Val is intended to be used as a value for writing to the PMXE field of the PMUXn registers, it is defined as 0x2ul, which is not a valid PORT group on the microcontroller you are using. On the SAMD21 your PORT->Group index should always be either 0 (for port A) or 1 (for port B), in this case you want 0 since the pins you are using are in port A.

Thanks for pointing it out this was a brain fart, I don't know why I wrote something that silly :D. Furthermore thanks for explaining how HWSEL works and why my settings were not correct now I understand, also for letting me know about the DIPO and DOPO registers. I made the changes you suggested and now everything seems fine. I will post the code below in case someone runs to the same problem. 
 

#include "sam.h"

#define SPI_CLK_FREQ 8000000
#define SPI_BAUD 50000

#define DelayTicks(ticks)              {volatile uint32_t n=ticks; while(n--);}				   //takes 8 cycles
#define DelayMs(ms)                    DelayTicks(MS_TO_DLYTICKS(ms))
#define F_CPU 8000000
#define CYCLES_IN_DLYTICKS_FUNC        8
#define MS_TO_DLYTICKS(ms)			   (uint32_t)(F_CPU / 1000 * ms / CYCLES_IN_DLYTICKS_FUNC) // ((float)(F_CPU)) / 1000.0


// Configure SERCOM0  SPI PINS  PAD 
// PA08   MOSI 
// PA09   SCK
// PA10   SS
// PA11   MISO 

void init_SPI_tutorial(){

	while(SERCOM0->SPI.SYNCBUSY.bit.ENABLE);
	SERCOM0->SPI.CTRLA.bit.ENABLE = 0;

	while(SERCOM0->SPI.SYNCBUSY.bit.SWRST);
	SERCOM0->SPI.CTRLA.bit.SWRST = 1;

	while(SERCOM0->SPI.CTRLA.bit.SWRST);
	while(SERCOM0->SPI.SYNCBUSY.bit.SWRST || SERCOM0->SPI.SYNCBUSY.bit.ENABLE);

	PORT->Group[0].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PMUX(MUX_PA11C_SERCOM0_PAD3) |						//Bulk configuration for PMUX "C" for SERCOM0
	PORT_WRCONFIG_INEN  |												//Enable input on this pin MISO
	PORT_WRCONFIG_PINMASK((uint16_t)((PORT_PA11)));				        //Selecting which pin is configured  PB16  This bit needs to shift to fit the 16 bit macro requirements

	//Using the WRCONFIG register to bulk configure both PB22 and PB23 for being configured the SERCOM0 SPI MASTER MOSI and SCK pins
	PORT->Group[0].WRCONFIG.reg =
	PORT_WRCONFIG_WRPINCFG |											//Enables the configuration of PINCFG
	PORT_WRCONFIG_WRPMUX |												//Enables the configuration of the PMUX for the selected pins
	PORT_WRCONFIG_PMUX(MUX_PA08C_SERCOM0_PAD0) |						//Bulk configuration for PMUX
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PINMASK ((uint16_t)((PORT_PA08 | PORT_PA09)));	    //Selecting which pin is configured

	PM->APBCMASK.reg |= PM_APBCMASK_SERCOM0;							//Enable the SERCOM 0 under the PM

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM0_GCLK_ID_CORE) |			//Provide necessary clocks to the peripheral
	GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

	while(GCLK->STATUS.bit.SYNCBUSY);									//Wait for clock sync

	SERCOM0->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER|			//Configure the Peripheral as SPI Master
	SERCOM_SPI_CTRLA_DOPO(0);											//DOPO is set to PAD[2,3]
	SERCOM_SPI_CTRLA_DIPO(0x3);
	SERCOM0->SPI.CTRLB.reg = SERCOM_SPI_CTRLB_RXEN;						//Enable receive on SPI

	uint16_t BAUD_REG = ((float)SPI_CLK_FREQ / (float)(2 * SPI_BAUD)) - 1;	    //Calculate BAUD value
	SERCOM0->SPI.BAUD.reg =	SERCOM_SPI_BAUD_BAUD(BAUD_REG);				        //Set the SPI baud rate
	SERCOM0->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;				 			//Enable the Sercom SPI
	while(SERCOM0->SPI.SYNCBUSY.bit.ENABLE);

}

uint8_t spiSend(uint8_t data){
	
	while(SERCOM0->SPI.INTFLAG.bit.DRE == 0);
	SERCOM0->SPI.DATA.reg = data;
	while(SERCOM0->SPI.INTFLAG.bit.RXC == 0);
	return (uint8_t)SERCOM0->SPI.DATA.reg;
}

int main(void){

	SystemInit();
	init_SPI_tutorial();

	PORT->Group[0].DIRSET.reg = PORT_PA10;		// output

	PORT->Group[0].OUTSET.reg = PORT_PA10;		// high
	PORT->Group[0].OUTCLR.reg = PORT_PA10;		// low

	uint8_t EE_Read_data;

	while (1) {
		PORT->Group[0].OUTCLR.reg = PORT_PA10;	// low
		EE_Read_data = spiSend(0x00);
		//DelayMs(10);
		PORT->Group[0].OUTSET.reg = PORT_PA10;  // high
		//DelayMs(10000);
	}
}