2017-08-09 02:28 PM
Hi,
There seems to be an issue with the SPI peripheral in the STM32F10x family of devices.
I have the same issue with multiple devices (STM32F103ZGT8, STM32F107VCT6)
Here is the issue:
When I try to read in master mode, I must send data for the peripheral to send out a clock so I can read the data.
When I am reading data, I am reading back what I sent.
I have used the SPI in interrupt and with DMA and have the same issue.
When I try to write/read, I read the first byte properly, but then I read back what I placed in the DR register
Using a logic analyzer, I was able to see that my master and slave are transmitting the bytes properly.
From this stream, I would expect to read 0xFF 8 times.
However, my RX buffer will contain: 0xFF 0x12 0x34 0x56 0x78 0x9A 0xBC 0xDE
When using breakpoints in interrupt mode, here is the sequence of events that I have seen.
The odd thing is that we get a TXE interrupt (step 2) even though we are still transferring the first byte. This means that we have not read single byte but we have already given the SPI peripheral 2 bytes.
Here is thehypothesis I have arrived to:
It seems that the DR register overwrites the data in the RX buffer if I write to it too soon.This makes the use of the DMA for reading data useless.
I was able to resolve the issue by followingthe steps below (this only works in interrupt mode):
I originally used the HAL that comes with STMcubemx but had to write my own code to properly manage the SPI transfers.
Is this a known issue with the STM32F10x devices? Or am I missing something?
Below is my init code with DMA:
// To transfer data with the SPI in DMA mode, I use thiss line of code:
HAL_StatusTypeDef status = HAL_SPI_TransmitReceive_DMA(&hSpi, txData, rxData, size);
/**
* @brief This function initializes the SPI peripheral
* @param None
* @retval None
*/
void SPI_Init(void)
{
spiEventHandle = xEventGroupCreateStatic(&spiEvent);
hSpi.Instance = SPI1;
hSpi.Init.Mode = SPI_MODE_MASTER;
hSpi.Init.Direction = SPI_DIRECTION_2LINES;
hSpi.Init.DataSize = SPI_DATASIZE_8BIT;
hSpi.Init.CLKPolarity = SPI_POLARITY_LOW;
hSpi.Init.CLKPhase = SPI_PHASE_1EDGE;
hSpi.Init.NSS = SPI_NSS_SOFT;
hSpi.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
hSpi.Init.FirstBit = SPI_FIRSTBIT_MSB;
hSpi.Init.TIMode = SPI_TIMODE_DISABLE;
hSpi.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hSpi.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hSpi) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
/**
* @brief This function initializes the low level peripherals used by the SPI
* @param SPI_HandleTypeDef * - pointer to the SPI handle
* @retval None
*/
void HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
{
// Enable SPI1 clock
__HAL_RCC_SPI1_CLK_ENABLE();
// SPI1 GPIO Configuration
GPIO_InitPin(SPI_CS);
GPIO_InitPin(SPI_SCK);
GPIO_InitPin(SPI_MOSI);
GPIO_InitPin(SPI_MISO);
// SPI1 DMA Init
// SPI1_RX Init
hDmaSpiRx.Instance = DMA1_Channel2;
hDmaSpiRx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hDmaSpiRx.Init.PeriphInc = DMA_PINC_DISABLE;
hDmaSpiRx.Init.MemInc = DMA_MINC_ENABLE;
hDmaSpiRx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hDmaSpiRx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hDmaSpiRx.Init.Mode = DMA_NORMAL;
hDmaSpiRx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hDmaSpiRx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(spiHandle, hdmarx, hDmaSpiRx);
// SPI1_TX Init
hDmaSpiTx.Instance = DMA1_Channel3;
hDmaSpiTx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hDmaSpiTx.Init.PeriphInc = DMA_PINC_DISABLE;
hDmaSpiTx.Init.MemInc = DMA_MINC_ENABLE;
hDmaSpiTx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hDmaSpiTx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hDmaSpiTx.Init.Mode = DMA_NORMAL;
hDmaSpiTx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hDmaSpiTx) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(spiHandle, hdmatx, hDmaSpiTx);
}
/**
* @brief This function initializes the DMA peripheral
* @param None
* @retval None
*/
void DMA_Init(void)
{
// DMA controller clock enable
__HAL_RCC_DMA1_CLK_ENABLE();
}
/**
* @brief This function enables the interrupts used by the DMA
* @param None
* @retval None
*/
void DMA_EnableIrq(void)
{
// DMA interrupt init
// DMA1_Channel2_IRQn interrupt configuration
HAL_NVIC_SetPriority(DMA1_Channel2_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel2_IRQn);
// DMA1_Channel3_IRQn interrupt configuration
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
}
/**
* @brief This function handles DMA1 channel2 global interrupt.
*/
void DMA1_Channel2_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hDmaSpiRx);
}
/**
* @brief This function handles DMA1 channel3 global interrupt.
*/
void DMA1_Channel3_IRQHandler(void)
{
HAL_DMA_IRQHandler(&hDmaSpiTx);
}
/**
* @brief This function is a callback used to notify when a transfer is completed
* @note This function is called by the HAL
* @param SPI_HandleTypeDef * - pointer to the SPI handle
* @retval None
*/
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
BaseType_t xHigherPriorityTaskWoken;
BaseType_t result = xEventGroupSetBitsFromISR(spiEventHandle, SPI_EVENT_SUCCESS, &xHigherPriorityTaskWoken);
if (result == pdPASS)
{
/* If xHigherPriorityTaskWoken is now set to pdTRUE then a context
switch should be requested. */
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
}
�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
Thank you in advance for the help.
Michel
2017-08-10 04:07 PM
Hello!!
>>The odd thing is that we get a TXE interrupt (step 2) even though we are still transferring the first byte. This means that we have not read single byte but we have already given the SPI peripheral 2 bytes.
The TXE flag means buffer empty, after buffer the data goes to shift register and clocked out (not with APB clock)
Transmission begins when you write DR. (TXE , for a very short time, goes low).
At your pseudocode (2) you enable interrupts after you begin to send the byte. Its very posible when interrupt enabled finaly and called the ISR , the buffer to be empty.
>>It seems that the DR register overwrites the data in the RX buffer
tx buffer and rx buffer are two comletely different buffers. Are accessed only from sane address.
>>I was able to resolve the issue by following the steps below (this only works in interrupt mode):
Yes .. if you just want to read from SPI your code is good.
can work also with no interrupts by waiting after transmitt the byte while(!RXNE); clearRXNE;
>> Is this a known issue with the STM32F10x devices? Or am I missing something?
As i know , not exist any issue about SPI (read also device's
)I use the SPI from f10x family for years and i didn't faced any problem
Regards!!
2017-08-14 02:10 PM
Hello,
Thank you for your answer.
I might have phrased my question wrong, but I would like to use the SPI in full duplex mode using the DMA.
Somehow, the read of the SPI doesn't seem to work, because as I have mentioned, I am reading back what I sent (so it's as if RX and TX buffer are the same). If you look back at the logic analyzer screenshot from my original post, I should be reading 0xFF 8 times, but it's not the case.
Any help would be greatly appreciated!!
Thanks again for the help.
Michel