Skip to main content
May 28, 2018
Solved

STM32f407 as SPI slave[HAL]

  • May 28, 2018
  • 6 replies
  • 5324 views
Posted on May 28, 2018 at 09:05

Im trying to catch data from the unknown device, this device has data and clock. I decided to make my stm32 as spi slave and catch the data as recevier.And im struggling with it. I have this data flow which i want to catch:

0690X00000602W8QAI.bmp

Clock is yellow and green is data.

I need only 16bytes from it, when data line goes low, we can see this 16bytes:

0690X00000602WDQAY.bmp

0690X00000602WIQAY.bmp

And there is the speed of the clock, as you can see its around 1.5us each clock pulse. So what i have to do is configure spi with HAL, i toolk SPI1(PA5 - CLOCK, PA7- DATA) cuz it has more powerful MHZ, chose spi mode ''RECEIVE ONLY SLAVE'', HARDWARE NSS SIGNAL: DISABLED. And here is my spi1 config:

static void MX_SPI1_Init(void)

{

/* SPI1 parameter configuration*/

hspi1.Instance = SPI1;

hspi1.Init.Mode = SPI_MODE_SLAVE;

hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;

hspi1.Init.DataSize = SPI_DATASIZE_8BIT;

hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;

hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;

hspi1.Init.NSS = SPI_NSS_SOFT;

hspi1.Init.FirstBit = SPI_FIRSTBIT_LSB;

hspi1.Init.TIMode = SPI_TIMODE_DISABLE;

hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi1.Init.CRCPolynomial = 10;

if (HAL_SPI_Init(&hspi1) != HAL_OK)

{

_Error_Handler(__FILE__, __LINE__);

}

}

What im doing in main.c, as oscilo's pictures says we need to catch the moment when data goes low and then try to read a data, im trying to catch first pulse then wait until data goes high then low and only they read spi, at least only first byte - it should be 0.

int main(void)

{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration----------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_USB_DEVICE_Init();

MX_SPI1_Init();

/* USER CODE BEGIN 2 */

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

uint8_t collect_bits = 0;

char string_array[21] = {0};

uint8_t spi_buffer[16] ={0} ;

spi_buffer[0] = 5;

// while (1)

// {

// HAL_Delay(10000);

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == 0){} // wait until GPIOC 9 goes high

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == 1){} // wait until GPIOC 9 goes high

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == 0){} // wait until GPIOC 9 goes high

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == 1){} // wait until GPIOC 9 goes high

HAL_SPI_Receive(&hspi1, (uint8_t*)spi_buffer, 16,0);

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

// }

/* USER CODE END 3 */

}

And i catch nothing at all, my spi buffer all 16 bytes are 0. I tried SPI_DMA it changed only first byte on 0XFF. What am i doing wrong? Thanks

#stm32f4-spi #receive

Note: this post was migrated and contained many threaded conversations, some content may be missing.
This topic has been closed for replies.
Best answer by T J
Posted on May 29, 2018 at 13:05

are you sending and receiving from the same buffer ?

I would suggest:

uint8_t spi_Rxbuffer[16] ={0}

uint8_t spi_Txbuffer[16] ={0}

there will be a flag to say HAL_SPI_TransmitReceive has completed. but its difficult to manage HAL sometimes.

I would use the register level coding style, I guess along these lines;

// initialise table

char RxTable[16];     

char RxCounter=0;

// clear RxSPI buffers  // not sure if its double buffered or not.

    while ((hspi1.Instance->SR  & SPI_FLAG_RXNE))

          RxSPI = hspi1.Instance->DR & 0xFF;   // dump old bytes in buffer

while(1){

while(RxCounter <16){

    while (!(hspi1.Instance->SR  & SPI_FLAG_RXNE))

         ;

        RxSPI = hspi1.Instance->DR;

// now you have 8 SPI clock cycles to do something with the data

   RxTable[RxCounter++] = RxSPI;

}

RxCounter =0;

// Process data within 8 SPI clock cycles or start losing bytes

}

// after thought:

// now consider that you may have lost coherency:

0xFE0010

is your string

   if ((RxSPI  == 0xFE)  && ( RxState =0)) RxState =1;

   else  if ((RxSPI  == 0x00) && ( RxState =1)) RxState =2;

   else  if ((RxSPI  == 0x01) && ( RxState =2))){

// check if we have been here before, that means we already have a complete set of data

if( RxCounter ==15)

   ; // we have complete data set in bytes RxTable[0..12]

// have start position.. start recording data

      RxCounter =0;

      RxState =0;   // reset state machine

   }

   else RxState =0;

6 replies

S.Ma
Principal
May 28, 2018
Posted on May 28, 2018 at 09:37

Better use scope which can analyse/decode/log simple serial bus such as I2C or SPI.

https://www.keysight.com/en/pd-748302-pn-N5423A/ic-and-spi-serial-data-decode-option-for-infiniivision-6000-and-7000b-series-oscilloscopes?cc=FR&lc=fre

 
May 28, 2018
Posted on May 28, 2018 at 09:47

Well, my scope can do that. But the point that i need my stm32 catch this 16bytes data and  then output it on PC

waclawek.jan
Super User
May 28, 2018
Posted on May 28, 2018 at 11:58

IMO the Cube/HAL code wouldn't manipulate SSI for the slave+softNSS case, so it will either start receiving as soon as enabled or never.

I don't Cube.

JW

May 28, 2018
Posted on May 28, 2018 at 12:41

 ,

 ,

Thanks for reply, what should i use then? Periph? CMSIS? Btw you said:

>,>,>, SSI for the slave+softNSS case, so it will either start receiving as

soon as enabled or never.

i puted NSS as hardware and pulled it down. So spi slave should start

receving

immediatly after it got first clocks. Why it doesnt happen?

2018-05-28 19:59 GMT+10:00 waclawek.jan <,st-microelectronics@jiveon.com>,:

STMicroelectronics Community

<,https://community.st.com/?et=watches.email.thread>,

Re: STM32f407 as SPI slave[HAL]

reply from waclawek.jan

<,

MCUs Forum* - View the full discussion

<,https://community.st.com/0D70X000006Sjw0SAC

waclawek.jan
Super User
May 28, 2018
Posted on May 28, 2018 at 14:57

i puted NSS as hardware and pulled it down. So spi slave should start

receving

immediatly after it got first clocks. Why it doesnt happen?

I don't know. Read out and check/post the content of SPI and relevant GPIO registers.

JW

May 29, 2018
Posted on May 29, 2018 at 11:47

i managed to synchronize slave and master with HAL function:

HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)spi_buffer, (uint8_t *)spi_buffer, 16, 2000);

Im sending from slave the bytes which i get from master, in purpose of debugging, and the data is same, so its perfectly synchronized and seems like slave reading all data master sends. Here is the images:

1) Slave is my STM32f4 and master that i catching, its the same

0690X00000602PTQAY.bmp

2)Zoomed up

0690X00000602WNQAY.bmp

The problem is that i dont know how to read that data, its 16bytes which i need somehow to read without slowing the spi down. All my attempts to do that are failed. The whole code are:

int main(void)

{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration----------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_USB_DEVICE_Init();

MX_SPI1_Init();

/* USER CODE BEGIN 2 */

//while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != RESET){}

// while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != SET){}

// while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != RESET){}

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

uint8_t collect_bits[16] = {0};

char string_array[21] = {0};

uint8_t spi_buffer[16] ={0} ;

uint8_t flag_when_start_print = 0;

spi_buffer[0] = 5;

uint8_t spi_transmit[16] = {0};

HAL_SPI_Init(&hspi1);

while (1)

{

HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)spi_buffer, (uint8_t *)spi_buffer, 16, 2000);

}

}

The important fact that the beggining of the messge i want to catch is always starts with 0xFE0010, so after those bytes i need to read next 13bytes

T J
T JBest answer
Senior III
May 29, 2018
Posted on May 29, 2018 at 13:05

are you sending and receiving from the same buffer ?

I would suggest:

uint8_t spi_Rxbuffer[16] ={0}

uint8_t spi_Txbuffer[16] ={0}

there will be a flag to say HAL_SPI_TransmitReceive has completed. but its difficult to manage HAL sometimes.

I would use the register level coding style, I guess along these lines;

// initialise table

char RxTable[16];     

char RxCounter=0;

// clear RxSPI buffers  // not sure if its double buffered or not.

    while ((hspi1.Instance->SR  & SPI_FLAG_RXNE))

          RxSPI = hspi1.Instance->DR & 0xFF;   // dump old bytes in buffer

while(1){

while(RxCounter <16){

    while (!(hspi1.Instance->SR  & SPI_FLAG_RXNE))

         ;

        RxSPI = hspi1.Instance->DR;

// now you have 8 SPI clock cycles to do something with the data

   RxTable[RxCounter++] = RxSPI;

}

RxCounter =0;

// Process data within 8 SPI clock cycles or start losing bytes

}

// after thought:

// now consider that you may have lost coherency:

0xFE0010

is your string

   if ((RxSPI  == 0xFE)  && ( RxState =0)) RxState =1;

   else  if ((RxSPI  == 0x00) && ( RxState =1)) RxState =2;

   else  if ((RxSPI  == 0x01) && ( RxState =2))){

// check if we have been here before, that means we already have a complete set of data

if( RxCounter ==15)

   ; // we have complete data set in bytes RxTable[0..12]

// have start position.. start recording data

      RxCounter =0;

      RxState =0;   // reset state machine

   }

   else RxState =0;

May 29, 2018
Posted on May 29, 2018 at 15:00

 ,

 ,

Thank you a lot T J for helping, really appreciate that. As i

understand RxTable

read data in buffer from registers, then do the same all over again, btw if

i want to print those 16bytes through UART, can it cause off timings? ' if

((RxSPI == 0xFE) &,&, ( RxState =0)) RxState =1, ' - i think ill collect

13bytes after i get start positon then delinit spi and print data. Again,

thanks a lot ill give it a try

2018-05-29 21:06 GMT+10:00 T J <,st-microelectronics@jiveon.com>,:

STMicroelectronics Community

<,https://community.st.com/?et=watches.email.thread>,

Re: STM32f407 as SPI slave[HAL]

reply from T J

<,

MCUs Forum* - View the full discussion

<,https://community.st.com/0D70X000006SfIPSA0

May 30, 2018
Posted on May 30, 2018 at 08:49

Okay, the  HAL_SPI_TransmitReceive is really strange for me. for exmaple if i use  HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)spi_Txbuffer, (uint8_t *)spi_Rxbuffer, 16, 2000);

  outside of while(1) it should read 16bytes from master when it get first clocks and send 16bytes to master, this is how i see it. But what excatly i got 0690X00000602WSQAY.bmp

I used diffrent buffers as T J suggested, so tx_buffer sending 02 on every clock, why does it happening if i inisialize get and send 16bytes only once. 

/* USER CODE BEGIN 0 */

uint8_t spi_Rxbuffer[16] ={0};

uint8_t spi_Txbuffer[16] ={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

char RxCounter=0;

/* USER CODE END 0 */

/**

* @brief The application entry point.

*

* @retval None

*/

int main(void)

{

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/* MCU Configuration----------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */

HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */

SystemClock_Config();

/* USER CODE BEGIN SysInit */

/* USER CODE END SysInit */

/* Initialize all configured peripherals */

MX_GPIO_Init();

MX_DMA_Init();

MX_USB_DEVICE_Init();

MX_SPI1_Init();

/* USER CODE BEGIN 2 */

//while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != RESET){}

// while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != SET){}

// while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) != RESET){}

/* USER CODE END 2 */

/* Infinite loop */

/* USER CODE BEGIN WHILE */

HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)spi_Txbuffer, (uint8_t *)spi_Rxbuffer, 16, 2000);

while (1)

{

}

}

T J
Senior III
May 30, 2018
Posted on May 30, 2018 at 09:02

What did you receive in the RXBuffer ?

T J
Senior III
May 30, 2018
Posted on May 30, 2018 at 09:07

you should single step, this is not trivial work. I cant do your work for you.

I already gave you a viable solution, I cant help more than that.

After a year of STM32 you wont need to ask any more questions.

Petr Sladecek
ST Employee
June 7, 2018
Posted on June 07, 2018 at 11:08

Hello,

I’m going back to begin of this thread to comment your polling example where can see few discrepancies at your code:

  • HAL_GPIO_ReadPin output should be compared rather with GPIO_PIN_RESET and GPIO_PIN_SET constants instead of 0,1
  • I don’t know your cpu frequency, but it would be nice to perform analysis what time sw spends by calling 4x HAL_GPIO_ReadPin function (you can try to toggle an auxiliary pin to see when the HAL_SPI_receive function is called). I would prefer in line assembly here to speed up the code.
  • HAL_SPI_receive is called with wrong parameters (pointer to hspi1 handle structure is missing, timeout set to zero produces immediate error output from the function – there is no test of any function output at your code).
  • HAL_SPI_receive is called when SCK signal is not at idle which is not suggested initialization of the slave by RM. This approach produces quite not defined start of the data reception (the function itself is written in common way and performs quite a lot operations before SPI is enabled). You can restore correct data from the received bit flow if begin of the transacted pattern is always constant
  • You can consider data packing and set reception threshold to 16 bit data. Then the polling sw works more relaxed.
  • If you want to use DMA you need to apply HAL_SPI_Receive_DMA and associate hspi DMA Rx handle but I believe it is not necessary for initial testing.

Hopefully this would be helpful information for you to continue in the project

Best regards,

Petr

June 8, 2018
Posted on June 08, 2018 at 14:08

Thank you Petr for reply. Actually i solved the problem, but it was test device where speed wasnt too high each clock was around 2us as you can see from the pictures. But real device has 50nanoseconds per clock. And here is a problem, it catches LOW on data and start reading  as i wants BUT seems like while MCU procceses the code with HAL_RECEIVE, we are loosing data, seems like HAL really slow, im loosing 2 bytes from the time when i catch zero on data, spi starts reading too late... THis is a code:

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == GPIO_PIN_RESET){} // wait until GPIOC 9 goes high

while (HAL_GPIO_ReadPin (GPIOA,GPIO_PIN_7) == GPIO_PIN_SET){}

 // 

wait until GPIOC 9 goes low

HAL_SPI_Receive(&hspi1, (uint8_t *)spi_Rxbuffer, 19, 2000);

  // read 19 bytes

Actually im not 100% sure that its 

HAL_SPI_Receive and not 

HAL_GPIO_ReadPin which causes this error. I tried to wait low on data with CMSIS register output which should be much faster, but i struggle with it., im waiting GPIOA.7 - 

while((GPIOA->IDR & 0x80) != 0x80) {} - doesnt know does it okay or not. Also i dont know why but after i initialize GPIO pins, IDR register holds 0x00000D40 value, i dont know why. How can i solve it?