cancel
Showing results for 
Search instead for 
Did you mean: 

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

Zaher
Senior II
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

14 REPLIES 14
Zaher
Senior II
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
Lead
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;}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

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

Posted on March 19, 2017 at 00:06

Hi again,

Unfortunately, non of that has worked for me. I tried your functions along with some other modifications, however, I still cannot get the receiver to work. RXNE is always cleared and no data has been detected by the Master/STM32F429 whatsoever! Even the DMA approach has not changed the issue. The problem is I don't have a scope to find out what's going on on the bus level. Could it be a CS problem? IN the previous code that I built with Std.Periph.Libs., I used to deselect the chip so it interprets the last byte in its shift register, then I call the SPI_Receive function. 

Thank again!

Zaher

Posted on March 19, 2017 at 02:47

it works on my 'F091 perfectly, read an write to the LCD.

are you a slave SPI or master SPI ?

looks like you are a master, so you need to send clock pulses to receive the extra bytes...

Posted on March 19, 2017 at 14:33

Yes, the STM32F429 is always Master in 2-Line full duplex mode. Here's the old function that is based on the Std.Periph.Lib:

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);

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

GPIO_SetBits(dSPIN_nSS_Port, dSPIN_nSS_Pin);

return (uint8_t)(SPI_I2S_ReceiveData(dSPIN_SPI));

}

Here I'm trying to do the very same thing with HAL-Based librar. The Tx works properly without problems, however, I can't get any response back from the device:

uint8_t dSPIN_Write_Byte(uint8_t byte)

{

uint8_t dummy_byte = 0x00; // Dummy byte used for shifting

uint8_t response = 0x00;

/** CS Active (LOW), Enable Shift Register of device **/

HAL_GPIO_WritePin(dSPIN_nSS_Port, dSPIN_nSS_Pin, GPIO_PIN_RESET);

HAL_SPI_Transmit(&Spi6Handle, (uint8_t*) &byte, 1, 100); // Send Byte

/*** Wait while SPI is busy ***/

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

/** CS Disabled (High), Interpret command in shift register of device **/

HAL_GPIO_WritePin(dSPIN_nSS_Port, dSPIN_nSS_Pin, GPIO_PIN_SET); // CS Released (High)

HAL_SPI_Receive(&Spi6Handle, (uint8_t*)&response, 1, 1000);

return (uint8_t)response;

}

What might go wrong here? Did I miss anything in the above code?

Posted on March 19, 2017 at 17:14

Sometime when things gets swamped the bug so obvious that a step back is required.

Check the peripheral is clock enabled.

With a debugger, look at GPIO and SPI HW register values. (close this window when running the code, or refresh of this window by debugger will actually read DR and the SW won't.

Use an oscilloscope to monitor SPI lines (NSS, MISO/MOSI/SCK).

connect MISO and MOSI together to check what is sent is received...

Regardless of Stdlib and HAL or LL, check out what are the HW peripheral bits edited by the function to make sure all looks ok.

polling on TXE means the DR written byte is currently being push out and clocks pulses are under generation on SCK...

RNXE meaning a byte has been received and need to be read (only after the 8/16 bit shift register is completed)

BUSY meaning SCK pulse generation is over, NSS can be raised.

Posted on March 19, 2017 at 19:22

Unfortunately, I don't have an oscilloscope to monitor the signals, but as far as I know, the peripheral has been configured/clocked properly and I can send commands to my dSPIN and get those interpreted without a problem. The only problem is the receive part. I noticed that if I change the mode to 1Line, the RXNE flag sets when I call the receive function, but the returned data has nothing to do with the response from the dSPIN. Of course, Std.Lib, HAL, or whatever will end up the same at the hardware level, given that the values written to registers are the same, too! Not quite sure about that. 

Zaher
Senior II
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