I am simulating a project where two STM32s communicate with each other through SPI (HAL drivers), where the SPI Master is an STM32F4 and the SPI Slave is an STM32F3. Basically, the master will send 5 bytes of data (1 command byte followed by 4 dummy bytes) and the slave will respond back with 4 bytes of data to the master depending on the first command byte received.
How the system should operate: Pressing the user button on the master will send SPI data (in non-interrupt mode) to the slave (and the slave will receive the data in interrupt mode). The slave then sends data back to the master depending on the first received byte. There is a blue LED indicator on the slave device whether an SPI data has been received, and a green LED indicator on the master that indicates whether it receives the correct sequence of data from the slave. Below is how the clocked data should be sent/received by the master:
Slave device issue: For the first few button presses, the slave successfully receives data from the master. After a few cycles, from observing the LEDs and setting breakpoints in Debug mode, I noticed that the slave starts to receive data after every 2 button presses on the master. In the slave device, the data received is correct. I also have to note that the interrupt service routine on the master executes at every button press, so SPI data should be sent at every button press.
Master device issue: The master never receives the correct sequence of data. If, for example, the slave sends the data: 0x01, 0x02, 0x03, 0x04 in that order, the master would receive the data in this order: 0x02, 0x03, 0x04, 0x01. Furthermore, this also happens every 2 button presses.
This is what the event looks like:
- n button press:
- Slave: Data is correctly received by slave (and sends data back to the master)
- Master: Garbage value received
- (n+1) button press:
- Slave: Data is not received by slave (and does not send data back because slave sends data based on received data)
- Master: Relevant values received but in incorrect order
- (n+2) button press (same as what happened on the n-th button press):
- Slave: Data is correctly received by slave (and sends data back to the master)
- Master: Garbage value received and so on.
Here is the SPI configuration code for the Master:
static void MX_SPI1_Init (void) {
/* SPI Configuration for the STM32F4 (Master) */
/* APB2 Clock set to 8MHz to match APB2 clock of Slave*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
Error_Handler();
}
}
The SPI configuration for the Slave:
static void MX_SPI1_Init (void) {
/* SPI Configuration for the STM32F3 (Slave) */
/* APB2 Clock is 8MHz */
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_2EDGE;
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 = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
Error_Handler();
}
/* Set interrupt priority and enable interrupts for SPI1 */
HAL_NVIC_SetPriority(SPI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
}
Here is the relevant code for the Master (the Push Button interrupt segment):
void HAL_GPIO_EXTI_Callback (uint16_t GPIO_Pin) {
/* Clear wake-up power flag */
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
if (GPIO_Pin == PUSH_BUTTON_PIN) {
/* Transmit one command byte located in pTxBuff array of length cTxLen */
HAL_SPI_Transmit(&hspi1, (uint8_t*)pTxBuff, cTxLen, spi_timeout);
/* Wait for end of SPI transmission */
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
/* Transmit dummy values located in pTempTxBuff and store retrieved relevant data in pRxBuff */
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)pTempTxBuff, (uint8_t*)pRxBuff, cRxLen, spi_timeout);
/* Flash LED if correct sequence stored in pRxBuff was received - cRxLen = 4 */
if((pRxBuff[0] == DATA0) && (pRxBuff[1] == DATA1) && (pRxBuff[2] == DATA2) && (pRxBuff[3] == DATA3)) {
/* Toggle dedicated green LED */
ToggleLED();
}
}
And the relevant code for the Slave:
void HAL_SPI_RxCpltCallback (SPI_HandleTypeDef *hspi) {
/* Toggle blue LED when SPI receives data through interrupt */
ToggleLED();
/* Check the data received through SPI interrupt stored in pRxBuff[0] (array is of size 1) */
if (pRxBuff[0] == COMMAND_DATA) {
/**
* pTxBuff := contains appropriate data to be sent to Master device based on this specific command
* pTempRxBuff := array to store dummy variables, this array is of size 4
*/
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)pTxBuff, (uint8_t*)pTempRxBuff, cTxLen, spi_timeout);
}
/* Set device in SPI Receive interrupt mode again and store received data in pRxBuff of size 1 */
HAL_SPI_Receive_IT(&hspi1, (uint8_t*)pRxBuff, cRxCmdLen);
}
I do not understand where the error is here. If it is only the master sending the data to the slave device, everything works fine. I have also matched the SPI configuration and bus clock speeds. I also tried changing the direction line to SPI_DIRECTION_1LINE
as suggested elsewhere but it did not work. I also attempted using HAL_SPI_Transmit()
and HAL_SPI_Receive()
separately instead of HAL_SPI_TransmitReceive()
, but the same issues still appear. Also, I would like to use HAL_SPI_TransmitReceive()
on both to verify on the Master and the Slave that the devices are communicating properly.Any help would be greatly appreciated.
Thank you!
HAL_GPIO_EXTI_Callback
. That callback is called directly from the EXTI interrupt handler, so you're then doing those blocking SPI calls from within the EXTI interrupt. Rather just set avolatile
flag from EXTI and then in your main loop, do the SPI operations when it see's the flag get set.. \$\endgroup\$HAL_SPI_RxCpltCallback
in the slave. You shouldn't be doing the blockingHAL_SPI_TransmitReceive
from inside a callback (which is actually still inside the interrupt). Either use anothervolatile
flag there, or doHAL_SPI_TransmitReceive_IT
instead and make yourHAL_SPI_RxCpltCallback
stateful so that it remembers whether it last did aHAL_SPI_TransmitReceive_IT
for 4 bytes or aHAL_SPI_Receive_IT
for 1 byte. \$\endgroup\$