I am using the STM Peripheral Library to set up and use CANBus. Problem is that I have to put a delay between Tx packets or I start dropping packets. Putting in read status loops etc does not seem to have any effect. Does anyone have a working example that does not include adding a magic number delay?
-
Following that you cannot use an RTOS to schedule the transmit task, it is unclear exactly what you are doing.Which MCU are you using? What devices are you transmitting to? How can you tell that you are indeed dropping pack – mbmcavoy Feb 11 '16 at 20:02
-
STM32F303x, I have checked the dropping packets with both receiver s/w on a PC and a CAN Bus analyzer. I send 4095 numbers sequentially and after about 30 or so groups start to go missing – Dirk Bruere Feb 12 '16 at 08:28
-
1Unfortunately, I cannot provide any example code. I plan to incorporate CAN into my project in the future, but have not yet tried... It sounds like you are intending to transmit as fast as possible, using the status to determine if the CAN interface is ready. This is not a normal use case for CAN, but I don't see any reason it wouldn't work. I would recommend posting your code (in case someone can find an error in it), or trying to capture the output signal trace with a scope or logic analyzer to see if the packets are wholly lost or being corrupted; might be insightful. – mbmcavoy Feb 16 '16 at 20:16
-
Seems the STM Peripheral Library does not provide suitable tools for detecting Error Frames. I am seeing error frames on the CAN Bus analyzer but an "OK" on all Tx frames I send. Maybe going to have to start going down among the bits, and/or fix the library – Dirk Bruere Feb 18 '16 at 09:32
-
Again, please share your code that is not working. How you are determining that the "OK" status on the Tx frames you send? The reference manual (RM0316, Section 31.7.1) seems a little ambiguous. Are you looking at the ALST and TERR bits in the CAN_TSR register? Those are available in the peripheral library. – mbmcavoy Feb 18 '16 at 16:29
-
1Note that the reference manual states "Once the mailbox has been successfully transmitted, it will become empty again." I suspect that this is not an accurate statement, as it them describes how to determine success and failure. I would expect the mailbox to be empty after transmission, even if the transmission is not successful. It would then be up to the software to check for success and either retransmit or skip data as appropriate for the application. In your case, it seems you would want to retransmit. In other cases, it might be better to just skip failed data and transmit a fresh value. – mbmcavoy Feb 18 '16 at 16:36
-
1@mbmcavoy I use the function from the peripheral library to test for successful completion: CAN_TransmitStatus(CAN1, mailBoxNumber)!= CAN_TxStatus_Ok in a while loop – Dirk Bruere Feb 25 '16 at 15:05
3 Answers
This is not specific to CAN or STM32, but really about controlling how often something happens in a microcontroller.
There are a couple of ways you could solve this:
1) Use a timer
Set up a timer so that it will generate an interrupt at the rate you want to transmit. (i.e., every 10ms) Use the interrupt to trigger your transmit code. Note that your Interrupt Service Routine should be as short as possible; it could simply set "go" flag that is checked by your main loop, when it is set, clear the flag, transmit, and then wait for it to be set again.
2) Use a Real-Time Operating System, such as FreeRTOS
An RTOS usually works similar to the timer method above, but abstracts the details and does so in a robust way. You can create multiple tasks that operate on different schedules if you need. For example, you may need to transmit data every 10ms, but update an indication only every 200ms.

- 2,628
- 5
- 23
- 34
-
RTOS not possible. I am using polling to handle the Tx function. The real question is why the status checking does not appear to work. – Dirk Bruere Feb 11 '16 at 08:51
I'm using STM32 + HAL + FreeRTOS
You need to put magic# delay means that you've used out all 3x mailbox. So you need some queue to hold the extra data. This is how I solve the problem
I had a task, which accepts following events
- Transmission done IRQ
- Transmission request
The task had following structure
- Busy/idle status of 3x mailbox
- A local queue holding data to transmit
Upon transmission request, put the data to local queue, and invoke transmission()
- Upon transmission done IRQ, mark the corresponding mailbox as idle, and invoke transmission()
- Pseudo code of transmission()
- If there's NO free mailbox, just return
- Find free mailbox[n], mark mailbox[n] as busy, and do transmission with it

- 11
- 1
-
I tried implementing your design, but it seems like there's a performance bottleneck due to task switching overhead. I am running a Cortex M4 at 72Mhz (STM32F302R8) and I need to transmit messages at 1kHz. The 1kHz push data onto a FreeRTOS queue which gets consumed in the CAN TX task. It seems like I can only transmit around 10 message per 1ms, which is not nearly enough for my requirements. What kind of performance were you able to achieve with your implementation? – Ken Lin Jan 17 '20 at 04:08
-
Another note, is it possible to get away without using a task for this design? It seems that in your design, I can simply have transmission() as a function that stores the busy/idle status as a static variable. And the transmission done IRQ will simply invoke transmission in its callback without the need to trigger a context switch (to wake up a task). – Ken Lin Jan 17 '20 at 04:15
-
What's your CAN speed? If that's 500kbps, that means the bit time is 2us. Assume your CAN packet is ~100bits (including bit stuffing), then a CAN packet time is around 200us. That means in 1ms, you can only have 5 packets So I think you encounter CAN Bus Throughput bound instead of CPU bound – Lihgong Wu Jan 18 '20 at 09:29
-
Of course, you can eliminate the TASK (bare-metal design) That means you should design the code carefully. For example, there must be some global variable accessed by two context {IRQ handler access it, Some fore-ground function access it}. Probably you need to disable IRQ in fore-ground function to ensure safety. With RTOS, we can force all access to the global variable in a single context (and less trouble) – Lihgong Wu Jan 18 '20 at 09:32
-
I see, that makes sense. I forgot to account for the throughput threshold. By the way, how did you set the priority of your CAN TX task? Is it higher or lower than your periodic tasks? I had CAN TX task set to higher priority than my 1khZ task, but then when there's a burst of messages to send out, the 1kHz task won't get the chance to check in and my watchdog resets. – Ken Lin Jan 19 '20 at 17:57
-
Did you run into similar problems? I'm thinking of setting the CAN TX task at a lower priority than 1kHz task so it doesn't make the 1kHz task miss it's deadline. – Ken Lin Jan 19 '20 at 17:58
Look closely to SPL CAN_Transmit() return value:
- @retval The number of the mailbox that is used for transmission or CAN_TxStatus_NoMailBox if there is no empty mailbox.
So you can perform some kind of check:
uint8_t retry_count = RETRY_COUNT;
while(retry_count && CAN_Transmit(CAN1, &TxMessage) == CAN_TxStatus_NoMailBox)
{
// <-- still you could delay here
retry_count--;
}
or just
while(CAN_Transmit(CAN1, &TxMessage) == CAN_TxStatus_NoMailBox);

- 303
- 3
- 13