3
\$\begingroup\$

I have two STM32 boards connected via a CAN bus. They periodically transmit a message to each other.

For some transmissions however, HAL_CAN_Transmit returns with a timeout HAL_TIMEOUT. Similarly, sometimes the CAN receive interrupt does not get fired. There is not a real pattern when it works and when not. However, resetting a device seems to induce timeouts more often.

When investigating the signals with a scope, they appear to be fine. The receiving transceiver acknowledges the signal as well even when the CAN receive interrupt is not fired.

What could be the problem in this case?

This is the code to transmit a message. It is called by the main loop.

void send_can_message_handler(void* task)
{
    hcan.pTxMsg->StdId = 0x321;
    hcan.pTxMsg->ExtId = 0x01;
    hcan.pTxMsg->RTR = CAN_RTR_DATA;
    hcan.pTxMsg->IDE = CAN_ID_STD;
    hcan.pTxMsg->DLC = 2;

    hcan.pTxMsg->Data[0] = can_messages_sent;
    hcan.pTxMsg->Data[1] = 0xAD;
    HAL_StatusTypeDef status = HAL_CAN_Transmit(&hcan, 500);
    can_messages_sent = can_messages_sent + 1;

    if (status == HAL_OK) {
        uint8_t data[2] = { 79, 75 };
        HAL_UART_Transmit(&huart1, data, 2, 100);
    }
    else if (status == HAL_ERROR) {
        uint8_t data[2] = { 69, 82 };
        HAL_UART_Transmit(&huart1, data, 2, 100);
    }
    else if (status == HAL_BUSY) {
        uint8_t data[2] = { 66, 85 };
        HAL_UART_Transmit(&huart1, data, 2, 100);
    }
    else if (status == HAL_TIMEOUT) {
        uint8_t data[2] = { 84, 73 };
        HAL_UART_Transmit(&huart1, data, 2, 100);
    }

}

This is the interrupt code.

void USB_LP_CAN1_RX0_IRQHandler(void)
{
    /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */

    /* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
    HAL_CAN_IRQHandler(&hcan);
    /* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */

    FillBuffer(&canBuffer1.State, hcan);


    // Enable receiving again.
    if (HAL_CAN_Receive_IT(&hcan, CAN_FIFO0) != HAL_OK)
    {
        // Handle error.
    }

    /* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */
}

FillBuffer simply copies the data to a buffer.

void FillBuffer(CanBufferState* state, CAN_HandleTypeDef hcan)
{
    for (size_t i = 0; i < 1; i++)
    {
        state->Buffer[state->WriteIndex] = hcan.pRxMsg->Data[i];
        state->WriteIndex = (state->WriteIndex + 1) != BUFFER_SIZE ? state->WriteIndex + 1 : 0;
    }
}
\$\endgroup\$
7
  • \$\begingroup\$ Add your interrupt code \$\endgroup\$
    – Damien
    Commented Jan 2, 2019 at 12:13
  • 2
    \$\begingroup\$ I never could get it to work, but there is a new library version (1.7) which should fix something for STM32F103 series. Also check electronics.stackexchange.com/questions/334970/… \$\endgroup\$ Commented Jan 2, 2019 at 12:16
  • \$\begingroup\$ Is that normal you copy only 1 byte: for (size_t i = 0; i < 1; i++) ? \$\endgroup\$
    – Damien
    Commented Jan 3, 2019 at 3:28
  • 1
    \$\begingroup\$ Yes, that was just for a test. I'll change that to 8 to copy all 8 bytes later on. \$\endgroup\$ Commented Jan 3, 2019 at 17:10
  • 3
    \$\begingroup\$ After trying all the suggestions in the answers without any result, I tried upgrading the HAL library to version 1.7 which finally solved the problem. Thanks @michel-keijzers. If you write it as an answer, I'll mark it as the accepted answer. \$\endgroup\$ Commented Jan 6, 2019 at 11:05

3 Answers 3

4
\$\begingroup\$

I chatted with an STM32 employee about this problem some time ago, and he wrote I had to wait until the next version of the HAL library. My version at that time was version 1.6. In version 1.7 of the HAL library the problem is solved.

I moved to another communication method so never tried CAN with the new version 1.7, but this should solve the CAN transmit error, specifically on STM32F103. I have been notified by tyr.bentsen (see answer/comment of him in this post). Thanks for the notification.

The software can be downloaded from ST.com:

stm32cubef1

Scroll to the bottom where you see the software installations and download the STM32CubeF1 1.7 and Patch_CubeF1 1.7.1.

\$\endgroup\$
2
\$\begingroup\$

You need to add the code to verify this assumption but,

A common reason that the interrupt is not fired is that the interrupt flag is not reset soon enough.

On your interrupt, the interrupt flag reset should be the first instruction to be made, so that if a new interrupt happens when you are still in the interrupt routine, it will fire back again.

If you reset the interrupt flag at the end of the routine, then you may have had a new interrupt happening that you would miss.

Another common reason is that you have higher priority interrupt code running somewhere, like a timer, that starves the CPU, or that your interrupt routine is too long to execute.

\$\endgroup\$
1
\$\begingroup\$

First off, if you are using a breadboard check that your RS pin is properly connected to GND or pulled down to GND depending on your transceiver that you are using. That pin usually puts the transceiver in an idle mode if floating or high.

In your code check that the automatic Sleep and all of that is configured right. Here is my example

hcan1.Instance = CAN1;
hcan1.Init.Prescaler = 9;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.SJW = CAN_SJW_1TQ;
hcan1.Init.BS1 = CAN_BS1_13TQ;
hcan1.Init.BS2 = CAN_BS2_2TQ;
hcan1.Init.TTCM = DISABLE;
hcan1.Init.ABOM = DISABLE;  // Automatic bus-off management
hcan1.Init.AWUM = DISABLE;  // Automatic wake-up mode
hcan1.Init.NART = DISABLE;  // No automatic retransmission
hcan1.Init.RFLM = DISABLE;
hcan1.Init.TXFP = DISABLE;

These parameters can make the bus go in to a dormant state if the messages do not come in frequently.

Also I have noticed that the remote request frame scenario does what you described. Even if you got everything working and set up correctly it only reacts some times.

One last thing for your bit rate. The most accurate place to find the parameters is http://www.bittiming.can-wiki.info/. Make sure that both microcontrollers are set up the same.

As Damien mentioned:

On your interrupt, the interrupt flag reset should be the first instruction to be made, so that if a new interrupt happens when you are still in the interrupt routine, it will fire back again.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.