cancel
Showing results for 
Search instead for 
Did you mean: 

How can I send more than 3 CAN messages in a row...

Stefan Meyre
Associate III
Posted on December 26, 2016 at 10:31

Hi all; I try to send by CAN bus to 4 individual motor controllers their pwm speed settings in one go. I found out however, that the first three messages go through perfectly, but the forth message got stuck. I can fix this only by adding some delay of 1-2msbefore sending the fourth message; with is of course terribly ugly to do. How can I solve this? Using STM32F4; see the code below. Thx!

if (dataToSendByCAN == 1)
{
myTxMessage.Data[0] = '#';
myTxMessage.Data[1] = 'p';
myTxMessage.Data[2] = 'w';
myTxMessage.Data[3] = 'm';
myTxMessage.Data[4] = '2';
myTxMessage.Data[5] = '/';
hcan1.pTxMsg = &myTxMessage;
myTxMessage.DLC = 8;
myTxMessage.IDE = CAN_ID_STD;
myTxMessage.ExtId = 0x00;
myTxMessage.RTR = CAN_RTR_DATA;
int16_t dummyLong = (uint16_t)(100*(setRPM - outputr - outputp));
myTxMessage.Data[6] = (uint8_t) dummyLong; //low byte
myTxMessage.Data[7] = (uint8_t) (dummyLong >> 8); //high byte
while (hcan1.State == HAL_CAN_STATE_BUSY);
myTxMessage.StdId = 0x231;
HAL_CAN_Transmit_IT(&hcan1);
dummyLong = (uint16_t) (100*(setRPM - outputr + outputp));
myTxMessage.Data[6] = (uint8_t) dummyLong; //low byte
myTxMessage.Data[7] = (uint8_t) (dummyLong >> 8); //high byte
while (hcan1.State == HAL_CAN_STATE_BUSY);
myTxMessage.StdId = 0x233;
HAL_CAN_Transmit_IT(&hcan1);
dummyLong = (uint16_t) (100*(setRPM + outputr + outputp));
myTxMessage.Data[6] = (uint8_t) dummyLong; //low byte
myTxMessage.Data[7] = (uint8_t) (dummyLong >> 8); //high byte
while (hcan1.State == HAL_CAN_STATE_BUSY);
myTxMessage.StdId = 0x232;
HAL_CAN_Transmit_IT(&hcan1);
dummyLong = (uint16_t) (100*(setRPM + outputr - outputp));
myTxMessage.Data[6] = (uint8_t) dummyLong; //low byte
myTxMessage.Data[7] = (uint8_t) (dummyLong >> 8); //high byte
while (hcan1.State == HAL_CAN_STATE_BUSY);
myTxMessage.StdId = 0x234;
HAL_Delay(1); //this is soooo ugly!!!
HAL_CAN_Transmit_IT(&hcan1);
dataToSendByCAN = 0;�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

7 REPLIES 7
Alan Chambers
Associate II
Posted on December 26, 2016 at 17:13

The CAN peripheral has three outgoing mailboxes for messages, and raises an interrupt when each message has been successfully sent. The best approach is to create a queue for your CAN messages, and transmit the next item from the queue when the interrupt occurs. I used this approach to send CAN messages every 5ms to 4 motors, and it was fine. Delaying for a couple of milliseconds is ugly, but might be sufficient for your needs if you are not sending messages regularly as in my case.

I believe you need to look at implementing a callback function called HAL_CAN_TxCpltCallback().

Posted on December 26, 2016 at 21:35

Thx Alan; I'll give it a try. I do have to update the controllers at 250Hz so all 4 messages in 4 ms... let you know how I do Progress (might take a bit...). Cheers

Posted on December 26, 2016 at 22:44

I should add that I don't use HAL, so don't know how capable the driver is: I will be interested in how you get on. My code used SPL. I had to send and receive several messages to all four motors at 200Hz. The bus was set up to run at 1MHz: there was plenty of headroom for more messages. Looking at your code, you will need a queue of whatever type myTxMessage is. The queue decouples your application code from the interrupt handler. It doesn't need to be very big as you are not expecting to create a huge backlog.

Posted on February 14, 2017 at 20:02

Hi Alan

I finally could try your suggestion to use HAL_CAN_TxCpltCallback(). This did not solve my issue. I still had to delay the fourth CAN message by about 2ms (HAL_Delay(2)). But: wrapping the HAL_CAN_Transmit_IT in a while loop to check the availability of the CAN-bus, did help; like this: while(HAL_CAN_Transmit_IT(&hcan1)!=HAL_OK);

The first three messages get send off immediately; the fourth while loop waits until the bus is ready and then immediately sends of the last (4th) message. This still blocks my main loop for the time of one CAN message to be sent (which is still not so great), but keeps the delayed time to a minimum.

Posted on February 14, 2017 at 20:49

No a big fan of the CUBE/HAL construct. On the SPL I, like Alan, just queue things up, and dispatch new packets to the FIFO during the IRQ Handler, and manage errors, etc.

If you don't want to block the main loop you'd need to enqueue things, or widen the loop so as to pump other activity while you wait for the CAN FIFO to clear out.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Franco Spediacci
Associate II
Posted on June 26, 2017 at 11:42

Hi Stefan,

I've read you question just by chance because I was looking for some other info about CAN.

I had your same issue on STM32L4 and I solved it. It's related to HAL library driver limitation, I added a patch.

If you are still searching for a solution I can give you some tips.
Franco Spediacci
Associate II
Posted on October 09, 2017 at 10:05

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6tb&d=%2Fa%2F0X0000000bxY%2Fz3qLfps51rVbSwHFKcz8ajhrr3T7SNoZ.apznU77nwU&asPdf=false