cancel
Showing results for 
Search instead for 
Did you mean: 

Unable to communicate between two Nucleo board using SPI

ercjethava1992
Associate II

I am writing sample demo Application to test data transfer between SPI Master(Nucleo F411RE) and SPI Slavee(Nucleo H755ZI-Q) device.

Master SPI:
/**SPI2 GPIO Configuration
PC2 ------> SPI2_MISO
PC3 ------> SPI2_MOSI
PB10 ------> SPI2_SCK

Slave SPI:
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI

For validating what data Slave SPI device receiving over SPI I am sending this data over UART to display it on Host PC 

Here is SLAVE SPI device UART response:
What I am seeing is that SPI receives wrong data instead of 0x41 (Send by master)

 

 

Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA

 

 

 


Master SPI Code:

 

 

  uint8_t message[1] = {0x41};
  int counter = 0;
  while (1)
  {
	  HAL_GPIO_TogglePin(LD2_GPIO_Port, LD2_Pin);
	  HAL_SPI_Transmit(&hspi2, message, 1, HAL_MAX_DELAY);
	  HAL_Delay(500);
	  counter++;
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }

static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

 

 

 


SLAVE SPI code:

 

 

uint8_t txData[1] = { 0xAA }; // Data to send to the master
uint8_t rxData[1] = { 0x00 }; // Buffer to store received data

  uint8_t tx_uart_buff[50];
  while (1)
  {
	  // Wait for data from the Master
      if (HAL_SPI_Receive(&hspi1, rxData, 1, HAL_MAX_DELAY) == HAL_OK) {
          // Data received successfully, respond to master

    	  snprintf((char*)tx_uart_buff, sizeof(tx_uart_buff), "Slave Received SPI data: 0x%02X\r\n", rxData[0]);
	  	  HAL_UART_Transmit(&huart3,tx_uart_buff,sizeof(tx_uart_buff),10);

    	  HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin); // Example: Toggle an LED
          HAL_SPI_Transmit(&hspi1, txData, 1, HAL_MAX_DELAY); // Send response to the master
          snprintf((char*)tx_uart_buff, sizeof(tx_uart_buff), "Slave Transmit SPI data: 0x%02X\r\n", txData[0]);
	  	  HAL_UART_Transmit(&huart3,tx_uart_buff,sizeof(tx_uart_buff),10);
      } else {
    	  snprintf((char*)tx_uart_buff, sizeof(tx_uart_buff), "Slave Receive SPI error\r\n");
    	  HAL_UART_Transmit(&huart3,tx_uart_buff,sizeof(tx_uart_buff),10);
      }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }


static void MX_SPI1_Init(void)
{

  /* USER CODE BEGIN SPI1_Init 0 */

  /* USER CODE END SPI1_Init 0 */

  /* USER CODE BEGIN SPI1_Init 1 */

  /* USER CODE END SPI1_Init 1 */
  /* SPI1 parameter configuration*/
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_SLAVE;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi1.Init.CRCPolynomial = 0x0;
  hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
  hspi1.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
  hspi1.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
  hspi1.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
  hspi1.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_00CYCLE;
  hspi1.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
  hspi1.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
  hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
  hspi1.Init.IOSwap = SPI_IO_SWAP_DISABLE;
  if (HAL_SPI_Init(&hspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI1_Init 2 */

  /* USER CODE END SPI1_Init 2 */

}

 

 

 


I have also attached full Master and Slave CubeIDE Project.
If anyone can help me to figure out issue here as SPI Slave always reads wrong data.
@Andrew Neil @tjaekel @Peter BENSCH @B.Montanari 

10 REPLIES 10
gbm
Principal

The data received is the data you send. And what you send is the least significant byte of the address of "message" variable. That's what happens if you ignore the compiler warnings instead of fixing the problems with your code.

Correction - edited: you are sending some data from the address which least significant byte is stored in "message" variable.

Also, please read this topic:

https://community.st.com/t5/stm32-mcus-products/regarding-function-calls-when-performing-spi-communication-in/m-p/738412#M265178

 

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice

Wouldn't TransmitReceive be a more appropriate method where you're concurrently collecting bits on the back edge of the clock that's pushing data out?

Observe what's going on over the wire with a logic analyze so you can visualize the interaction and relate it to the data you see and expectations.

 

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@gbm 
yes that was an issue instead of sending data was sending Address
However after fixing that original issue still persist Slave receives wrong data:

 

SLAVE Data:

Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60
Slave Transmit SPI data: 0xAA
Slave Received SPI data: 0x60

 

 

You didn't fix the error - you've introduced another. Look at the compiler messages and fix the problem. First, get the code which compiles without warnings.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
ercjethava1992
Associate II

@gbm I have tried capturing variable using debugger after fixing compilation error ,still same issue and found that DR register not updating its value to 0x41  

Screenshot 2025-01-16 090531.png

Screenshot 2025-01-16 090449.png

Don't inspect the registers like this, it's invasive, reading DR clears bits in SR. It will also break FIFOs on models that have those.

Also DR is TWO registers at the same address, the data you write in there is NOT the data it reads back.

It's not a MEMORY cell, its a view on a more abstract and complex logic structure, if you looped back the MOSI to MISO you'd need to clock 8 times to see the same data returned on the input side.

Understand that SPI is a symetrical clocked interface, there's ONE clock source, as each bit shifts out and new bit shifts in.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

With the version of SPI you use, the debugger can only rarely see the received data in DR. Don't open the debugger SPI register view during any transmission. Look at the values of program variables from variables. Still, you didn't show the updated code.

In the thread I referred you to, you may read why SPI slave is very hard to implement with an MCU unless the master side observes the timing constraints of the slave. Master-to-slave-only is easy, slave-to master is hard. Try Master to slave first.

My STM32 stuff on github - compact USB device stack and more: https://github.com/gbm-ii/gbmUSBdevice
ercjethava1992
Associate II

@gbm what I am trying to test first is from Master to Slave only
-> Master sends one byte 0x41 to Slave , which I am expecting on Slave to receive it however I am receiving wrong data = 0x60

I have tried removing SPI_trasmit from Slave to Master , still same issue persists 

Slave code:

uint8_t rxData[1] = { 0x00 }; // Buffer to store received data  
uint8_t tx_uart_buff[50];
  while (1)
  {
	  // Wait for data from the Master
      if (HAL_SPI_Receive(&hspi1, rxData, 1, HAL_MAX_DELAY) == HAL_OK) {
    	  snprintf((char*)tx_uart_buff, sizeof(tx_uart_buff), "Slave Received SPI data: 0x%02X\r\n", rxData[0]);
	  	  HAL_UART_Transmit(&huart3,tx_uart_buff,sizeof(tx_uart_buff),10);

    	  HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin); // Example: Toggle an LED
      } else {
    	  snprintf((char*)tx_uart_buff, sizeof(tx_uart_buff), "Slave Receive SPI error\r\n");
    	  HAL_UART_Transmit(&huart3,tx_uart_buff,sizeof(tx_uart_buff),10);
      }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }



Slave Received SPI data: 0x60
Slave Received SPI data: 0x60
Slave Received SPI data: 0x60
Slave Received SPI data: 0x60
Slave Received SPI data: 0x60
Slave Received SPI data: 0x60

 

OK, jumping in a bit late.

My questions:

  • You use Master and Slave in NSS mode
  • I think: on a Slave, even with NSS mode (instead of an incoming nCS signal) -
    you have to enable the Slave (a bit to release SPI Rx via SW, like an external HW nCS would do).
    Where do you do?
  • When you test SPI in "loopback" mode - feeding back MOSI to MISO - did you make sure to use the correct MISO vs. MOSI? (I do not see a SWAP done, but it can be confusing what is MISO vs. MOSI when it comes to Master and Slave relation: the meaning does not change, so MOsi goes to moSI and for a "loopback" test: MOsi to moSI). People think often: for a Slave the MOSI signal flips - it does not!

Could your provide an oscilloscope screenshot? (just to make sure MOSI comes out correctly from Master)

My suggestion:

  • configure your Master in "full-duplex" mode (2-wires, MOSI and MISO used)
  • connect MISO with MOSI (on the Master side)
  • send something - and you should receive the same
  • use TransmitReceive function to do so ("bi-directional")
  • if this works - your Master should be fine

Now configure and use a Slave device:

  • connect Master MOSI to Slave MOSI! (same name!, not MISO! otherwise a SWAP config needed)
  • you might need to toggle a nCS GPIO signal in HW mode (nCS handled in HW), if the nCS is connected
    and used on Slave to enable Rx - but your Master and Slave use NSS mode (so, fine)
  • if NSS mode on Slave and no nCS signal provided and not used (slave just with 2 signals instead of 3) - still a need to enable Slave Rx mode (via setting a bit in Slave registers)
  • it should now receive what the Master has sent (assuming both run the same SPI mode)
  • make also sure, that the CLK Config for a Slave makes sense: a slave will "follow" the SPI Clock (nothing to set as Slave bitrate, it will use the Master SCLK generated as input), but the Slave device must have an internal peripheral clock configured for it which is fast enough to do so!
  • Also to bear in mind: often, a SPI Slave Rx can only follow up to 1/2 of the Master maximum clock speed.
    So, you need to run a Slave internal peripheral clock at least as 2x of the maximum Master SCLK generated.

For me it looks like more as:
- your connection between Master and Slave has an issue, e.g.:
   - MOSI - MOSI connection
   - no external nCS connected (NSS mode)
   - but wrong handling of SW NSS mode (forgotten to enable Slave Rx?!)

Just test in steps:

  • use a scope to check if Master generates correct SPI transaction
  • check your FW/SW if Slave is properly enabled (to start Rx) - in NSS mode there must be a bit setting
  • verify that Master and Slave use the same SPI mode (it looks like based on your code)
  • make sure that Master SPI and Slave SPI have proper internal peripheral clock config
  • lower the Master SPI clock speed generated - Slave will follow the Master but just up to 1/2 for maximum
    speed possible to be generated by a Master (a Slave is often just capable to keep up with 1/2 speed of a master)

Even, you do not use any INT or DMA - you should be able to see a single byte received in the Slave DR register.

If not: potentially the slave was not enabled.

Even if you connect just SCLK (the Master Clock out) to a Slave (as Slave Clock in), but you leave MISO on Slave open: if you tie down now the Slave MISO (input) to 0 or VDD - you should get all 0x00 or 0xFF.

If this does not work --> your SCLK from Master to Slave does not work, or the Slave is not enabled (what I assume).