SPI-DMA slave HAL_SPI_Transmit_DMA: How to flush rx afterwards?
My code configures SPI as slave and then enters an infinite loop where it receives a message, sets a GPIO line indicating to the host that the processor is busy, and readies to transmit a response. After one round, calling HAL_SPI_Receive_DMA a second time immediately triggers a DMA interrupt, probably for a byte having been shifted in with every byte out by HAL_SPI_Transmit_DMA. Such, I guess, DMA sees SPI DR as not being empty as soon as it is enabled. (Although TXE is 0, the underlying shift register might not be.) I ''solved'' the problem of having an unwanted leading byte in the receive buffer by calling HAL_SPI_Receive_DMA for one byte after the transmit DMA has completed.
Is there a simpler way like the for example SHARC DSP provides with a flush tx / rx control bit?while
(
true
) {
// Receive message.
// todo How to flush receive buffer?
memset
(bufferRx, 0,
sizeof
bufferRx);
spiRxComplete =
false
;
spiStatus = HAL_SPI_Receive_DMA(&hspi1, bufferRx, COMM_RX_MESSAGE_SIZE);
CHECK_SPI_STATUS(spiStatus);
HAL_GPIO_WritePin(busy_GPIO_Port, busy_Pin, GPIO_PIN_RESET);
while
(!spiRxComplete);
HAL_GPIO_WritePin(busy_GPIO_Port, busy_Pin, GPIO_PIN_SET);
if
(spiStatus != HAL_OK) {
PRINTF(
''spiStatus = %d\n''
, spiStatus);
// We are dead in the water. Just sit here until the host resets us.
while
(
true
);
}
uint8_t command = bufferRx[COMM_INDEX_COMMAND];
if
(command != COMM_CMD_DATA)
PRINTF(
''rx: %08X\n''
, *((unsigned *) bufferRx));
if
(command == COMM_CMD_DATA) {
if
(noConfig ==
false
) {
// Align buffer content to 32 bit (type float).
memmove
(bufferRx +
sizeof
(
float
), bufferRx + COMM_INDEX_COMMAND + 1, COMM_RX_DATA_SIZE);
poStatus = VerifyInput((
float
*) bufferRx);
if
(poStatus == STATUS_SUCCESS) {
// Process command.
}
}
else
poStatus = STATUS_CONFIG;
}
else
{
poStatus = STATUS_INVALID_COMMAND;
}
if
(poStatus < STATUS_SUCCESS)
memset
((
void
*) bufferTx, 0,
sizeof
bufferTx);
// Put the status in the tx buffer. Make it non-zero so that the host can distinguish it from
// just clocking in empty data because it started receiving (clocking as SPI master) too early.
bufferTx[0] = poStatus == STATUS_SUCCESS ? STATUS_HOST_ACK : poStatus;
// Transmit result.
spiTxComplete =
false
;
spiStatus = HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *) bufferTx, COMM_TX_SIZE);
CHECK_SPI_STATUS(spiStatus);
HAL_GPIO_WritePin(busy_GPIO_Port, busy_Pin, GPIO_PIN_RESET);
while
(!spiTxComplete);
// The lines below (mutual exclusively used) don't fix the misbehavior of HAL_SPI_Transmit_DMA, see below.
// hspi1.Instance->CR1 &= ~SPI_CR1_SPE;
// uint16_t dummy = hspi1.Instance->DR;
// HAL_SPI_DMAStop(&hspi1);
// Set busy flag for getting ready to receive.
HAL_GPIO_WritePin(busy_GPIO_Port, busy_Pin, GPIO_PIN_SET);
// Misbehavior of HAL_SPI_Transmit_DMA. Switching to transmit immediately triggers a DMA interrupt,
// probably for a byte having been shifted in with every byte out. Such, I guess, DMA sees SPI DR as
// not being empty as soon as it is enabled. (Although TXE is 0, the underlying shift register might not be.)
spiRxComplete =
false
;
spiStatus = HAL_SPI_Receive_DMA(&hspi1, bufferRx, 1);
CHECK_SPI_STATUS(spiStatus);
while
(!spiRxComplete);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
#spi-dma-slave-hal