0

Following this post I managed to setup my Nucleo board with two SPI devices & working interrupts with 1ms periodic 6 bytes payload transmission. My idea was to trigger the two SPI modules together as to have them run in parallel but something is preventing me from achieving that:

  • expected: SPI1 & SPI2 sequences are 99% overlapped, only dephased by a couple of system clocks
  • observed: the first bytes are overlapping as expected but then the first SPI module that was called (SPI1) seems to take precedence and complete its transmission before allowing SPI2 to terminate his. If SPI2 is called first then it will finish first. Scope view of two SPI transactions

Here are my main code pieces:

volatile uint8_t t[6] = {0x50, 0x60, 0x70, 0x80, 0x90, 0x10};
volatile uint8_t r[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

volatile uint8_t transmit_count1 = 6, transmit_count2 = 6, transmit_count3 = 6;
volatile uint8_t transmit_index1 = 0, transmit_index2 = 0, transmit_index3 = 0;

void do_SPI_ISR(){
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // SS1
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // SS2

    LL_SPI_Enable(SPI1);
    LL_SPI_Enable(SPI2);

    transmit_index1 = transmit_index2 = transmit_index3 = 0;
    transmit_count1 = transmit_count2 = transmit_count3 = 6;

    LL_SPI_EnableIT_TXE(SPI1); // enable SPI1 TX interrupt
    LL_SPI_EnableIT_TXE(SPI2); // enable SPI2 TX interrupt

    LL_SPI_TransmitData8(SPI1, t[transmit_index1]);
    LL_SPI_TransmitData8(SPI2, t[transmit_index2]);

    while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET || HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET){
        // SS are set HIGH by interrupts, remain here until that happens for both lines
    }
}

void SPI1_ISR_callback(){
    if (transmit_index1 < transmit_count1 - 1){
        transmit_index1 ++;
        LL_SPI_TransmitData8(SPI1, t[transmit_index1]);
    } else {
        LL_SPI_DisableIT_TXE(SPI1);
        transmit_index1 = 0;
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
        LL_SPI_Disable(SPI1);
    }
}

void SPI2_ISR_callback(){
    if (transmit_index2 < transmit_count2 - 1){
        transmit_index2 ++;
        LL_SPI_TransmitData8(SPI2, t[transmit_index2]);
    } else {
        LL_SPI_DisableIT_TXE(SPI2);
        transmit_index2 = 0;
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
        LL_SPI_Disable(SPI2);
    }
}

I did not tune anything regarding interrupt priorities, both SPIs have 0/0 priority. I was assuming that both interrupts would fire in turn and trigger bytes transmission but it's not the case, maybe some interrupt event constantly fire on SPI1, thus preventing SPI2 fire his? I tried removing all error & receive interrupts but that didn't change the outcome.

Edit, more code:

void SPI1_IRQHandler(void)
{
  /* USER CODE BEGIN SPI1_IRQn 0 */
    if (LL_SPI_IsActiveFlag_TXE(SPI1)){
        SPI1_ISR_callback();
    }

  /* USER CODE END SPI1_IRQn 0 */
  /* USER CODE BEGIN SPI1_IRQn 1 */

  /* USER CODE END SPI1_IRQn 1 */
}

/**
  * @brief This function handles SPI2 global interrupt.
  */
void SPI2_IRQHandler(void)
{
  /* USER CODE BEGIN SPI2_IRQn 0 */
    if (LL_SPI_IsActiveFlag_TXE(SPI2)){
        SPI2_ISR_callback();
    }

  /* USER CODE END SPI2_IRQn 0 */
  /* USER CODE BEGIN SPI2_IRQn 1 */

  /* USER CODE END SPI2_IRQn 1 */
}

Edit 2 (25/05/2020), with TXE flag managed from main app and interrupts disabled: enter image description here

Corresponding code:

void do_SPI_ISR(){
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); // SS1
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET); // SS2

    LL_SPI_Enable(SPI1);
    LL_SPI_Enable(SPI2);

    transmit_index1 = transmit_index2 = transmit_index3 = 0;
    transmit_count1 = transmit_count2 = transmit_count3 = 6;

    //LL_SPI_EnableIT_TXE(SPI1); // enable SPI1 TX interrupt
    //LL_SPI_EnableIT_TXE(SPI2); // enable SPI2 TX interrupt

    LL_SPI_TransmitData8(SPI1, t[transmit_index1]);
    LL_SPI_TransmitData8(SPI2, t[transmit_index2]);

    while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4) == GPIO_PIN_RESET || HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0) == GPIO_PIN_RESET){
        if (LL_SPI_IsActiveFlag_TXE(SPI1)){
            SPI1_ISR_callback();
        }

        if (LL_SPI_IsActiveFlag_TXE(SPI2)){
            SPI2_ISR_callback();
        }
    }
}

Any help much appreciated

Thomas Pujolle
  • 155
  • 1
  • 1
  • 11
  • You should consider not to start the next transmission inside the ISR. Perhaps you would also be better off with using DMA? Also are you clearing the ISR flag? – grml May 20 '20 at 18:14
  • The final prototype should have 6 SPI modules running in parallel, so I guess I would require 6 DMA too, so far from what I've seen there are only 2 DMA on STM32, that's why I'm trying to make this work with ISR only. I read that to clear the TXE flag I just need to write the DR buffer, is something more required ? Since I wrote this post I tried clearing them all, including overrun and RXE, that didn't change at all. I just don't understand why SP1 interrupt's are service first while SPI2 is hanging. – Thomas Pujolle May 21 '20 at 08:25
  • Yes there are only 2 DMA controler, but each with 8 channel. You can look this up in the DMA section of the reference manual (e.g. 'Table 42. DMA1 request mapping' for the F405). So you can check if your uC has enough SPIs distributed to different channels (I would asume it has). Next thing is you will problably not be able to sync six SPIs in ISR, but you could do taht in your main application e.g. with ready flags. At least during development you should also handle other interrupts then TXE. Might be the reason why your assumption of alternating ISRs ist not happening. – grml May 21 '20 at 08:44
  • So even if there are only 2 DMA I could potentially run multiple channels at once given they can be managed by those DMAs? – Thomas Pujolle May 25 '20 at 13:59
  • Yes you can configure multiple channel. The only thing you have to look at, is that the single hardware units are bound to specific channels. You can't switch them to another one. The only thing all channel can handle is memory to memory. But you can look this up in the reference manual, as I said before. – grml May 28 '20 at 15:21

0 Answers0