STM32 SPI slave sometimes hang on busy flag

jokn

I am testing a simple SPI-Slave on a STM32F207 Nucleo board. It is a 2 line receive only SPI slave without hardware NSS. This is my SPI initialization:

  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_SLAVE;
  hspi1.Init.Direction = SPI_DIRECTION_2LINES_RXONLY;
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi1.Init.NSS = SPI_NSS_SOFT;
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  HAL_SPI_Init(&hspi1);

The Slave works under Interrupt with a quite simple interrupt service routine. An extra test pin shows the interrupt activity on my logic analyzer.

void SPI1_IRQHandler(void)
{
    HAL_GPIO_WritePin (GPIOE, SPI_IRQ_Pin, GPIO_PIN_SET);
    shift_reg = (shift_reg << 8) | SPI1->DR;
    ++SPI_byte_cnt;
    HAL_GPIO_WritePin (GPIOE, SPI_IRQ_Pin, GPIO_PIN_RESET);
}

Instead of an SPI-NSS signal, the master sends a strobe signal at the end of a 4-byte transmission. The slave has an additional interrupt service (EXTI-Callback) to react to this strobe signal. Again, a test-pin shows the interrupt activity.

void vmShiftreg_Latch()
{
    HAL_GPIO_WritePin (GPIOE, STR_IRQ_Pin, GPIO_PIN_SET);
    if (Check_SPI (4)) {
        shift_latch = shift_reg;
    }
    shift_reg = 0;
    HAL_GPIO_WritePin (GPIOE, STR_IRQ_Pin, GPIO_PIN_RESET);
}

This interrupt service checks also the SPI status and the number of bytes transferred:

int Check_SPI (int bytes_needed)
{
    int byte_read = SPI_byte_cnt;
    SPI_byte_cnt = 0;
    
    if ((SPI1->SR & SPI_SR_OVR ) != 0) {
        shift_reg = SPI1->DR;
        shift_reg = SPI1->SR;
        ++ovr_error_cnt;
        return 0;
    }

    if ((SPI1->SR & SPI_SR_BSY) != 0) {
        HAL_GPIO_WritePin (GPIOE, BSY_ERR_Pin, GPIO_PIN_SET);
        ++bsy_error_cnt;
        Recover_SPI1();
        HAL_GPIO_WritePin (GPIOE, BSY_ERR_Pin, GPIO_PIN_RESET);
        return 0;
    }

    if (byte_read != bytes_needed) {
        ++shift_error_cnt;
        return 0;
    }

    return 1;
}

Now sometimes it happens that the BSY flag stays active and nothing, not even additional SPI clocks, can reset this busy flag. This can happen very randomly sometimes after 100000 proper transfers or sometimes after 500. Yes, I checked the SPI signals (clock and data) with a scope again and again but they are clean and without any spikes. For me it seems very crazy, that even hundreds extra SPI-clocks can't reset the bsy signal.

The only solution to get him back is forcing a SPI reset, which I discovered in this post:

__HAL_RCC_SPI2_FORCE_RESET();
__HAL_RCC_SPI2_RELEASE_RESET();

Did anybody ever have seen such a crazy behavior? If I can't figure out the problem, I guess I have no choice but to reset the SPI after each transfer.

Here two screenshots from my logic analyzer. The first one with a proper transfer, the second shows the hanging busy flag.

There is an update: My extra clock connection which I used on the slave to generate the SPI-clocks did not work correct. Now I can see that it takes additional 8 clocks to get the BSY-flag low. In other words, the SPI did start a new frame internaly even there was no extra clock from the last frame.

Screenshot with proper transfer enter image description here

Second screenshot with hanging busy-flag (update) Second screenshot with hanging busy-flag

pmacfarlane

Check out section 2.6.2 of the errata for the STM32F207:

2.6.2 BSY bit may stay high at the end of data transfer in slave mode

Description

BSY flag may sporadically remain high at the end of a data transfer in slave mode. This occurs upon coincidence of internal CPU clock and external SCK clock provided by master

In such an event, if the software only relies on BSY flag to detect the end of SPI slave data transaction (for example to enter low-power mode or to change data line direction in halfduplex bidirectional mode), the detection fails. As a conclusion, the BSY flag is unreliable for detecting the end of data transactions.

Workaround

Depending on SPI operating mode, use the following means for detecting the end of transaction:

  • When NSS hardware management is applied and NSS signal is provided by master, use NSS flag.
  • In SPI receiving mode, use the corresponding RXNE event flag.
  • In SPI transmit-only mode, use the BSY flag in conjunction with a timeout expiry event. Set the timeout such as to exceed the expected duration of the last data frame and start it upon TXE event that occurs with the second bit of the last data frame. The end of the transaction corresponds to either the BSY flag becoming low or the timeout expiry, whichever happens first.

Prefer one of the first two measures to the third as they are simpler and less constraining.

There is other information in the document, but that is only relevant if you are transmitting. But you should look at the source document yourself.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related