AnsweredAssumed Answered

SPI-DMA slave HAL_SPI_Transmit_DMA: How to flush rx afterwards?

Question asked by Fuchs.Gunter on Oct 17, 2016
Latest reply on Sep 27, 2017 by bulsink.tom
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 */

Outcomes