Skip to main content
Zaher
Senior II
March 14, 2017
Question

STM32CubeF4 - SPI Receiver doesn't work under HAL...

  • March 14, 2017
  • 14 replies
  • 3262 views
Posted on March 14, 2017 at 17:04

Hello everyone!

I was forced to move my project to HAL Libs for bunch of reasons beyond the scope of this discussion, and ever since, I ran into problems getting the SPI to work properly. I spent a tremendous time trying to get acquainted with the Std Periph Lib from ST, and I have been able to build several projects under that lib, until that TROUBLESOME HAL thing came out and changed everything, for the worst, in fact! I guess I'm not alone in reporting tons of bugs and issues moving from Std Periph Libs to HAL (Hell Always, not Hardware Abstraction Layer). 

Well, getting down to the problem, part of my project is to control a dSPIN device over SPI. I had a working code based on the Std.Periph.Libs and built with the AC6/GCC tools, where everything worked out as expected. I've been able to write and read the registers from the device, however, trying to do the same thing with HAL, keeping all settings for the SPI, RCC, GPIO has failed for no obvious reason. 

My problem lies in the receiving part of the SPI. Usually, the master sends a byte and the slave, dSPIN, sends response of 1~3 bytes long, depending on the command received. At the beginning, I noticed that there's no data being received by the master, STM32F429ZIT. The RXNE flag is never set and the program stuck in a while loop waiting for the RXNE flag forever. I tried everything possible, switched to another GPIO/SPI port, changed RCC settings, but all of that did not help in getting the response from the dSPIN/Slave. After almost two weeks of painful work modifying my code dozens of times, I managed to find out that the 2-Line Duplex mode was the reason the RX part did not work, thus the RXNE flag kept cleared. Although the same SPI configurations (2-Line Duplex) did the job very well for the old project that I built with the Std.PeriphLibs under the Eclipse-Based IDE. Now, after changing the SPI configurations from 2-Line to 1-Line, I started to receive data and the RXNE flag is set with each byte received, however, the data received seems to be a garbage and not related to any response from the dSPIN. I found out that others had similar issue with different series of STM32, and Flushing the RX FIFO is all they needed to get the correct data, but I believe the flushing function,RX_FIFO_flush

()

, is not available for the STM32F4 series. 

Below are some snippets from my project source code that had to do with the SPI:

/* SPI4 init function */

static void MX_SPI4_Init(void)

{

hspi4.Instance = SPI4;

hspi4.Init.Mode = SPI_MODE_MASTER;

hspi4.Init.Direction = SPI_DIRECTION_1LINE;

hspi4.Init.DataSize = SPI_DATASIZE_8BIT;

hspi4.Init.CLKPolarity = SPI_POLARITY_HIGH;

hspi4.Init.CLKPhase = SPI_PHASE_2EDGE;

hspi4.Init.NSS = SPI_NSS_SOFT;

hspi4.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;

hspi4.Init.FirstBit = SPI_FIRSTBIT_MSB;

hspi4.Init.TIMode = SPI_TIMODE_DISABLE;

hspi4.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;

hspi4.Init.CRCPolynomial = 7;

if (HAL_SPI_Init(&hspi4) != HAL_OK)

{

Error_Handler();

}

}

void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)

{

GPIO_InitTypeDef GPIO_InitStruct;

if(hspi->Instance==SPI4)

{

/* USER CODE BEGIN SPI4_MspInit 0 */

/* USER CODE END SPI4_MspInit 0 */

/* Peripheral clock enable */

__HAL_RCC_SPI4_CLK_ENABLE();

/**SPI4 GPIO Configuration

PE2 ------> SPI4_SCK

PE5 ------> SPI4_MISO

PE6 ------> SPI4_MOSI

*/

GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_5|GPIO_PIN_6;

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

GPIO_InitStruct.Pull = GPIO_NOPULL;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF5_SPI4;

HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

/* USER CODE BEGIN SPI4_MspInit 1 */

/* USER CODE END SPI4_MspInit 1 */

}

}

And here's my write function that write/reads a byte over SPI to/from a dSPIN slave:

/**

* @brief Transmits/Receives one byte to/from dSPIN over SPI.

* @param Transmited byte

* @retval Received byte

*/

uint8_t dSPIN_Write_Byte(uint8_t byte)

{

uint8_t Shifting_Byte = 0x00;

uint8_t Rx_Buffer[12] = 0;

uint8_t receivedbyte = 0;

uint8_t *Rx_Ptr;

/* nSS signal activation - low */

HAL_GPIO_WritePin(dSPIN_CS_GPIO_Port, dSPIN_CS_Pin, GPIO_PIN_RESET);

/* SPI byte send */

//HAL_Delay(10); /** Allow some time for CS **/

HAL_SPI_Transmit(&hspi4, (uint8_t*) &byte, 1, 100);

//HAL_SPI_TransmitReceive(&hspi4, (uint8_t*)&byte, Rx_Buffer, 1, 100);

/* Wait for SPIx Busy flag */

while(__HAL_SPI_GET_FLAG(&hspi4, SPI_FLAG_BSY) != RESET);

/* nSS signal deactivation - high */

HAL_GPIO_WritePin(dSPIN_CS_GPIO_Port, dSPIN_CS_Pin, GPIO_PIN_SET);

//HAL_SPI_Receive(&hspi4, &Rx_Buffer[0], 1, 100); /* Version 1 */

//HAL_SPI_Receive(&hspi4, (uint8_t*)&Resp, 1, 100); /* Version 2 */

//HAL_SPI_TransmitReceive(&hspi4, (uint8_t*)&Shifting_Byte,(uint8_t*) &receivedbyte, 1, 100); /* Version 3 */

HAL_SPI_Transmit(&hspi4, (uint8_t*) &Shifting_Byte, 2, 100);

//HAL_SPI_Receive(&hspi4, &receivedbyte, 1, 100 ); /* Version 4 */

//HAL_SPI_Receive(&hspi4, Rx_Ptr, 1, 100 ); /* Version 5 */

HAL_SPI_Receive(&hspi4, &Rx_Buffer[0], 1, 100 ); /* Version 6 */

/* Wait for SPIx Busy flag */

while(__HAL_SPI_GET_FLAG(&hspi4, SPI_FLAG_RXNE) != RESET);

//return Rx_Buffer[0];

//return (uint8_t)receivedbyte;

//return (uint8_t)*Rx_Ptr;

return Rx_Buffer[0];

}

As you can see above, I tried it in multitude of ways, but still can't get the correct response from the dSPIN. 

Hope someone has the solution for this! 

Regards,

Zaher

    This topic has been closed for replies.

    14 replies

    Zaher
    ZaherAuthor
    Senior II
    March 15, 2017
    Posted on March 15, 2017 at 20:09

    Sorry, just realized that the FIFO is only available with DMA-Type transfers, however, I'm still having the same problem and I can't get response back from my slave. By the way, the HAL-Based project was built with uVision MDK-ARM and most of the initialization was generated from CubeMx.

    T J
    Senior III
    March 15, 2017
    Posted on March 16, 2017 at 00:47

    hi,

    I use Cube, HAL and uV5. it is very stable and quick.

    I run this once to initialise the SPI,

    Tx16SPIBuffer[0] = 0xFFFF;

    HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)Tx16SPIBuffer, (uint8_t *)RxSPIBuffer, 1, 1000);

    then I use these to transmit and receive.

    void quickSend8SPI(SPI_HandleTypeDef *hspi){
     while( !( hspi->Instance->SR & SPI_FLAG_TXE)); *((__IO uint8_t *)&hspi->Instance->DR) = TxSPIByte; // force the SPI to transceive 8 bit }
    void quickSendReceive8SPI(SPI_HandleTypeDef *hspi){
     while( !( hspi->Instance->SR & SPI_FLAG_TXE));
     
     *((__IO uint8_t *)&hspi->Instance->DR) = TxSPIByte; // force the SPI to transceive 8 bit while( !( hspi->Instance->SR & SPI_FLAG_TXE)); while( ( hspi->Instance->SR & SPI_FLAG_BSY)); while( ( hspi->Instance->SR & SPI_FLAG_RXNE)) RxSPIByte1 = hspi->Instance->DR; // empty DR fifo, we only want the last byte
    }�?�?�?�?�?�?�?�?�?

    eg.

    void WriteCommand(uint8_t registerNum, uint16_t data){
     if(usingLCD1) Clear_Lcd1_nSS(); if(usingLCD2) Clear_Lcd2_nSS();
     WriteLCDAddress(registerNum); //wait_us(1); // this was just to see the data separated on the cro. WriteLCD16Data(data);
     if(usingLCD1) Set_Lcd1_nSS(); if(usingLCD2) Set_Lcd2_nSS();}
    void WriteLCD16Data(uint16_t writeSPIWord){ SPI_HandleTypeDef *hspi = &hspi1; TxSPIByte = writeSPIWord >>8; quickSend8SPI(&hspi1);
     TxSPIByte = writeSPIWord &0xff;
     quickSend8SPI(&hspi1); while ( __HAL_SPI_GET_FLAG(hspi, SPI_FLAG_BSY)); }
    void WriteLCDAddress(char writeLCDaddress){ TxSPIByte = 0x80; // Command Write quickSend8SPI(&hspi1); TxSPIByte = writeLCDaddress; quickSend8SPI(&hspi1);}
    char ReadLCDRegister (char registerNum){ 
     uint16_t RxSPIbyte; if(usingLCD1) Clear_Lcd1_nSS(); if(usingLCD2) Clear_Lcd2_nSS();
     WriteLCDAddress(registerNum);
     RxSPIbyte = ReadLCDData();
     if(usingLCD1) Set_Lcd1_nSS(); if(usingLCD2) Set_Lcd2_nSS();
     return RxSPIbyte;}
    char ReadLCDData(void){ TxSPIByte = 0x40; // Command Read quickSend8SPI(&hspi1); TxSPIByte = 00; quickSendReceive8SPI(&hspi1); // data appears in RxSPIByte.
     return RxSPIByte1;}
    char ReadLCDStatus(void){ if(usingLCD1) Clear_Lcd1_nSS(); if(usingLCD2) Clear_Lcd2_nSS(); TxSPIByte = 0xC0; // Command Read quickSend8SPI(&hspi1); TxSPIByte = 00; quickSendReceive8SPI(&hspi1); // data appears in RxSPIByte. LcdStatus = RxSPIByte1;
     if(usingLCD1) Set_Lcd1_nSS(); if(usingLCD2) Set_Lcd2_nSS(); return RxSPIByte1;}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

    Zaher
    ZaherAuthor
    Senior II
    March 16, 2017
    Posted on March 16, 2017 at 01:28

    Yes, I agree with you. The latest Cube/HAL has witnessed a number of improvements over the earlier versions, however, I'm still sort of allergic to HAL when considering moving from Std.Periph.Lib, which takes a completely different paradigm. Code compiles without any issue, however, I just need to figure out how to get response from my slave over SPI. Will give it a try and let you know! 

    Thanks,

    Zaher

    Zaher
    ZaherAuthor
    Senior II
    March 25, 2017
    Posted on March 25, 2017 at 02:01

    Well, because I had some issues debugging the old project that I built with GCC/AC6 toolchain, I had no way to find what's going on on the registers level. I just managed to build a new project based on the SPL 1.7 with uVision so I can debug and see what's going on. I noticed that the RXNE flag is never set, although I'm getting the data back from my slave without any problem. 

    Anyways, as far as the SPI Communication is concerned here, and from what I understand, the device goes inactive when CS forced high, which puts the SDO (MISO) in High Impedance state. According to the datasheet of the device, the communication starts when CS is forced low and the output (SDO) is latched on falling edges of the clock. This is shown also in the following timings diagram:

    0690X00000603dBQAQ.jpg

    As you can see, CS is forced high for certain time (tdisCS) then low again to start the reception of data. This is how the SPI is supposed to work across all similar devices. Even the CMSIS implementation for SPI follows the same rules for sending/receiving. But, what I don't understand here is the following function that is still doing the job and getting the response back from the slave. I just don't understand how the response byte is being shifted or read back from the device, while the CS is forced high? This is the function:

    uint8_t dSPIN_Write_Byte(uint8_t byte)

    {

    /* nSS signal activation - low */

    GPIO_ResetBits(dSPIN_nSS_Port, dSPIN_nSS_Pin);

    /* SPI byte send */

    SPI_I2S_SendData(dSPIN_SPI, byte);

    /* Wait for SPIx Busy flag */

    while (SPI_I2S_GetFlagStatus(dSPIN_SPI, SPI_I2S_FLAG_BSY) != RESET);

    /* nSS signal deactivation - high */

    GPIO_SetBits(dSPIN_nSS_Port, dSPIN_nSS_Pin);

    return (uint8_t)(SPI_I2S_ReceiveData(dSPIN_SPI)); // Here the response is read while CS is forced back to high. 

    }

    Could someone elaborate more on this? How and when the response byte is supposed to be shifted out of my slave? And how the SPL-based function above returns the response from the slave device (dSPIN) while the device is ''UNSELECTED'' by forcing CS High??

    Thank you again for your help! 

    Zaher

    T J
    Senior III
    March 25, 2017
    Posted on March 25, 2017 at 02:14

    to receive a byte you have to clock it, to clock the receiver, you have to send a byte.

    void quickSendReceive8SPI(SPI_HandleTypeDef *hspi){
     while( !( hspi->Instance->SR & SPI_FLAG_TXE));
     
     *((__IO uint8_t *)&hspi->Instance->DR) = TxSPIByte; // force the SPI to transceive 8 bit
     while( !( hspi->Instance->SR & SPI_FLAG_TXE));
     while( ( hspi->Instance->SR & SPI_FLAG_BSY));
     while( ( hspi->Instance->SR & SPI_FLAG_RXNE))
     RxSPIByte1 = hspi->Instance->DR; // empty DR fifo, we only want the last byte
    }�?�?�?�?�?�?�?�?�?�?

    as you can see here, I am clocking out TxSPIByte, to clock in data, if there is some earlier data in the Rx buffer (usually double or tripled buffered) then the final byte received is left in RxSPIByte1

    Zaher
    ZaherAuthor
    Senior II
    March 25, 2017
    Posted on March 25, 2017 at 19:52

    Just a quick update, the RXNE flag sets now as I send a new byte to the slave. The slave responds with 0x00 for most commands received, except those required to read parameters from the internal registers. Well, that was on the old version of the project I built with SPL. 

    As far as I remember, I tried everything possible, even the Send_Receive in order to clock data into the receiver. But I might have missed something in the receiving sequence. Now, cancelling all 'receive' functions from my old SPL code, I still get bytes shifted out of the slave's shift register as the RXNE flag sets with each byte sent to the device. That was only by calling the 

    SPI_I2S_SendData function. With that being said, all shifted bytes were detected at the master side. Now, if the HAL_SPI receives only by calling Send_Receive, then I believe I have to look back at my code to see if there's something I can do. 

    Thank you again!

    Zaher

    Zaher
    ZaherAuthor
    Senior II
    March 25, 2017
    Posted on March 25, 2017 at 21:23

    To make things easier, SPI specific, and apart from my own dSPIN/Slave device, here's what I have found so far:

    - Code built with SPL: Sending several bytes over SPI to my slave has always returned a response byte from the slave and the RXNE flag sets.

    -Code built with HAL: Doing the same thing with HAL_SPI_Transmit or HAL_SPI_TransmitReceive has no effect whatsoever on the receiving of data and RXNE flag is always cleared! 

    Tried with both pointers, casting to pointers, non has solved the issue! 

    I'm almost giving up on this. The problem is I can't go back to SPL for the entire project, as the majority of the modules in the project are built with HAL. 

    Thanks again,

    Zaher

    Zaher
    ZaherAuthor
    Senior II
    March 26, 2017
    Posted on March 26, 2017 at 06:23

    Finally, I came up with a solution that has worked out flawlessly and the code obviously compiles/runs much faster than ever. 

    While it might be a stupid workaround for some, I'm pretty confident many others out there are looking for such a solution. It is not only for SPI, it could be done with any other peripheral, too. Well, I often had the idea of mixing HAL libraries with SPL and Googling it has not revealed much, except the fact that many are trying to do it somehow. Yes, I did it! 

    I've been playing around this for the past 8 hours, and now my code compiles and runs smoothly without any issue. I can now use the same code I have written for SPL with the very same SPL-based SPI functions. I can now send and receive data to my dSPIN as expected. 

    What I did is creating an object file for the SPI-related modules in the SPL-based project, and had those linked/added to my HAL-Based project. Finally, I have modified the code again to work with the old-fashioned, yet very efficient and easy to handle, SPI functions, and now everything works out perfectly!

    It's a lot of fun to have some parts of the project built with the SPL, while still enjoy the CubeMx Initialization code generator, as well as the other modules needed in the project.

    So, it is not 'impossible' to get them both working in the same project. Just create pre-compiled object files out of your SPL-based project for the modules concerned, and with some tweaking, your ready to utilize SPL functions in HAL projects, like I did successfully! 

    I hope someone finds this helpful!

    Thanks,

    Zaher

    S.Ma
    Principal
    March 26, 2017
    Posted on March 26, 2017 at 10:28

    Interesting. This stretched can become a linkable library...