ATSAMD21G18A SPI how to properly read response

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

This is related to my previous thread, however now i have problems with reading response from the RAM module (RAM datasheet). I connected the oscilloscope to every pin, and as far as i can tell everything is correct. In the code below i send "0x48" as data to the RAM module and i get that response on the oscilloscope when i try to read it (on the MISO pin), however it doesn't get saved to the "value" variable and nothing is send over UART (if i hard code value as 0x48 it gets send over UART and it's properly displayed in the terminal as "H").  

#include "sam.h"
#include "includes/USART.h"

#define SPI_CLK_FREQ 8000000
#define SPI_BAUD 50000

#define RDMR        5           // Read the Mode Register
#define WRMR        1           // Write to the Mode Register
#define READ        3           // Read command
#define WRITE       2           // Write command
#define RSTIO		0xFF    // Reset memory to SPI mode
#define ByteMode    0x00        // Byte mode (read/write one byte at a time)
#define Sequential  0x40        // Sequential mode (read/write blocks of memory)

#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 SERCOM1  SPI PINS  PAD
// MOSI PA16
// SCK  PA17
// MISO PA19

void init_SPI_tutorial(){

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

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

	while(SERCOM1->SPI.CTRLA.bit.SWRST);
	while(SERCOM1->SPI.SYNCBUSY.bit.SWRST || SERCOM1->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_PA19C_SERCOM1_PAD3) |						                        //Bulk configuration for PMUX "C" for SERCOM1
	PORT_WRCONFIG_HWSEL |
	PORT_WRCONFIG_INEN  |												//Enable input on this pin MISO
	PORT_WRCONFIG_PINMASK((uint16_t)((PORT_PA19) >> 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 SERCOM1 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_PA16C_SERCOM1_PAD0) |						                        //Bulk configuration for PMUX
	PORT_WRCONFIG_HWSEL |
	PORT_WRCONFIG_PMUXEN |												//Enables the PMUX for the pins
	PORT_WRCONFIG_PINMASK ((uint16_t)((PORT_PA16 | PORT_PA17) >> 16));	                                        //Selecting which pin is configured

	PM->APBCMASK.reg |= PM_APBCMASK_SERCOM1;							                //Enable the SERCOM 1 under the PM

	GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(SERCOM1_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

	SERCOM1->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER|			                        //Configure the Peripheral as SPI Master
	SERCOM_SPI_CTRLA_DOPO(0);
	SERCOM_SPI_CTRLA_DIPO(0x3);
	SERCOM1->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
	SERCOM1->SPI.BAUD.reg =	SERCOM_SPI_BAUD_BAUD(BAUD_REG);				                        //Set the SPI baud rate
	SERCOM1->SPI.CTRLA.reg |= SERCOM_SPI_CTRLA_ENABLE;				 			//Enable the Sercom SPI
	while(SERCOM1->SPI.SYNCBUSY.bit.ENABLE);
}

uint8_t spiSend(uint8_t data){

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

void SetMode(char Mode){								// Select for single or multiple byte transfer
	uint8_t retval;
	PORT->Group[0].OUTCLR.reg = PORT_PA18;				                // set CS pin to output mode
	retval = spiSend(WRMR);							        // command to write to mode register
	retval = spiSend(Mode);                                                         // set for sequential mode
	PORT->Group[0].OUTSET.reg = PORT_PA18;				                // high
}

void WriteByte(uint32_t address, uint8_t data_byte) {
	uint8_t retval;
	SetMode(ByteMode);									// set to send/receive single byte of data
	PORT->Group[0].OUTCLR.reg = PORT_PA18;              // set SPI slave select LOW;
	retval = spiSend(WRITE);                            // send WRITE command to the memory chip
	retval = spiSend((uint8_t)(address >> 16));         // send high byte of address
	retval = spiSend((uint8_t)(address >> 8));          // send middle byte of address
	retval = spiSend((uint8_t)address);                 // send low byte of address
	retval = spiSend(data_byte);                        // write the data to the memory location
	PORT->Group[0].OUTSET.reg = PORT_PA18;              //set SPI slave select HIGH
}

uint8_t ReadByte(uint32_t address) {
	SetMode(ByteMode);				    // set to send/receive single byte of data
	uint8_t retval;
	PORT->Group[0].OUTCLR.reg = PORT_PA18;              // set SPI slave select LOW;
	retval = spiSend(READ);                             // send READ command to memory chip
	retval = spiSend((uint8_t)(address >> 16));         // send high byte of address
	retval = spiSend((uint8_t)(address >> 8));          // send middle byte of address
	retval = spiSend((uint8_t)address);                 // send low byte of address
	retval = spiSend(0x00);				    // read the byte at that address
	//usart_putc(retval);
	PORT->Group[0].OUTSET.reg = PORT_PA18;              // set SPI slave select HIGH;
	return retval;				            // send data back to the calling function
}

int main(void){

	SystemInit();
	usart_init();
	init_SPI_tutorial();

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

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

	uint8_t EE_Read_data;
	PORT->Group[0].OUTCLR.reg = PORT_PA18;
	//for(;;)spiSend(0xfe);
	uint8_t value;

	while (1) {

		uint8_t data = 0x48;                                                             // initialize the data
		WriteByte(0, data);								 // now write the data to that
		value = ReadByte(0);							         // reads a byte of data at that memory

		usart_putc(value);
	}
}

Is there an easier way to test if SPI works correctly (i don't have SPI to USB or something like that at the moment)? 

Is it because I read it in the spiSend with this two lines?

while(SERCOM1->SPI.INTFLAG.bit.RXC == 0);
return (uint8_t)SERCOM1->SPI.DATA.reg;

 

This topic has a solution.
Last Edited: Tue. Aug 3, 2021 - 06:32 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1
    SERCOM1->SPI.CTRLA.reg = SERCOM_SPI_CTRLA_MODE_SPI_MASTER|			                        //Configure the Peripheral as SPI Master
    SERCOM_SPI_CTRLA_DOPO(0);
    SERCOM_SPI_CTRLA_DIPO(0x3);

The '|' needed on the 2:nd line has become a ';' somehow so MISO is not configured as PA19.

/Lars

 

 

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

BTW, that gets a warning when I try to compile it, don't ignore warnings.

 

        .././main.c: In function 'init_SPI_tutorial':
C:\Program Files (x86)\Atmel\Studio\7.0\Packs\atmel\SAMD21_DFP\1.3.395\samd21a\include\component\sercom.h(235,38): warning: statement with no effect [-Wunused-value]
         #define SERCOM_SPI_CTRLA_DIPO(value) (SERCOM_SPI_CTRLA_DIPO_Msk & ((value) << SERCOM_SPI_CTRLA_DIPO_Pos))
                                              ^
C:\Users\Lars\Dropbox\avr\__0Components__\atmelsam\d21_spi\main.c(63,2): info: in expansion of macro 'SERCOM_SPI_CTRLA_DIPO'
          SERCOM_SPI_CTRLA_DIPO(0x3);

/Lars 

Last Edited: Mon. Aug 2, 2021 - 02:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I facepalmed so hard you probably heard it xd, you have a good eye thanks. Btw do you know of any guide that shows how to send large integers over SPI and UART? because at the moment i am limited to 2 bytes. 

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

I don't get the same warnings the only ones i have are about unused variables. 

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

wildernessbagel wrote:
do you know of any guide that shows how to send large integers over SPI and UART? because at the moment i am limited to 2 bytes. 

The same way you'd send any "large" amount of data - you do it one byte at a time.

 

EDIT

 

Pseudo code:

WHILE more bytes to send

    WAIT for transmitter to be ready
    
    SEND the next byte

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Tue. Aug 3, 2021 - 09:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Fair enough that's basically what i did for SPI i just break down the number to bytes like this. 

long data = 0x7fffffff;
uint8_t temp[4]; 						
temp[0] = (data >> 24);						 
temp[1] = (data >> 16);                    
temp[2] = (data >> 8);                       
temp[3] = (data); 

Any hints on what to do when i send data over UART, at the moment if i want to send let's say 12345 i break down this number to a char array and i send those instead of a number. 

long data = 0x7fffffff;
char buffer [sizeof(int64_t) * 8 + 1];
int8_t cnt = 0;
while(data > 0){
	buffer[cnt] = data % 10 + '0';
	data /= 10;
	++cnt;
}
for(;cnt >= 0; --cnt){
	while (!(SERCOM2->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_DRE));
	SERCOM2->USART.DATA.reg = buffer[cnt];
}

This seems kind of "hacky" tho it there a more standard way of doing this?

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

wildernessbagel wrote:
Any hints on what to do when i send data over UART

Why not just do exactly the same as for SPI?

 

Or are you actually asking to format stuff into text srtings?

 

In which case, there are standard library functions for that - eg, sprintf()

 

https://www.cplusplus.com/reference/cstdio/sprintf/

 

Some implementations also have itoa() 

 

https://www.cplusplus.com/reference/cstdlib/itoa/

 

See: https://www.avrfreaks.net/commen... and follow the rest of the thread ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah i ment formatting stuff to text strings, thanks for the links (function overloading seems like the way to go, because i'm missing _sbrk() and doing it on my own seems easier :D). 

Last Edited: Tue. Aug 3, 2021 - 12:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

wildernessbagel wrote:
i'm missing _sbrk()

is that required for sprintf() - or just for printf() ?

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was using sprintf(), but i get the same error for either.