Re: Sending two CAN Messages with set up period. Interrupts and Timers vs Delays

Hi,
I read through examples where developers are talking about the “code for several things at the same time” and showing code examples of switching on two to four LED for a set duration, triggered by external analogue input. There are other topics suggesting using timers and interrupts instead of delays.
I can not get my head around how to implement suggested approach to my scenario

  • To send into the CAN Bus two to four CAN messages, each could have different preset period e.g. CanMsg1 - 10ms, CanMsg2 - 50ms.

  • There no need for input. The only trigger for sending messages into CAN Bus is powering the board. Messages to be sent as long as the board is powered for as long as 24 hours.

Schematics I am using (nano + mcp2515) is the same as here:

The Library they suggest: arduino-mcp2515-master.zip is here:
https://github.com/autowp/arduino-mcp2515/archive/master.zip

  • I am using example from that library: called “CAN_Write” with small modification in CAN Bus speed and mcp2515 frequency:

mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
With that code what happens is:

  • CanMsg1 is being sent non-stop with period < 1ms - that is too frequent, need it with 10ms period

  • canMsg2 being sent with period < 1ms stops shortly after power up. has to continue with 50 ms period

Could you suggest modification to the code (perhaps using timers and interrupts) so that those two messages are sent non-stop with set up period, e.g. 10ms and 50ms?

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);


void setup() {
  canMsg1.can_id  = 0x0F6;
  canMsg1.can_dlc = 8;
  canMsg1.data[0] = 0x8E;
  canMsg1.data[1] = 0x87;
  canMsg1.data[2] = 0x32;
  canMsg1.data[3] = 0xFA;
  canMsg1.data[4] = 0x26;
  canMsg1.data[5] = 0x8E;
  canMsg1.data[6] = 0xBE;
  canMsg1.data[7] = 0x86;

  canMsg2.can_id  = 0x036;
  canMsg2.can_dlc = 8;
  canMsg2.data[0] = 0x0E;
  canMsg2.data[1] = 0x00;
  canMsg2.data[2] = 0x00;
  canMsg2.data[3] = 0x08;
  canMsg2.data[4] = 0x01;
  canMsg2.data[5] = 0x00;
  canMsg2.data[6] = 0x00;
  canMsg2.data[7] = 0xA0;
  
  while (!Serial);
  Serial.begin(115200);
  
  mcp2515.reset();
  mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
  mcp2515.setNormalMode();
  
  Serial.println("Example: Write to CAN");
}

void loop() {
  mcp2515.sendMessage(&canMsg1);
  mcp2515.sendMessage(&canMsg2);

  Serial.println("Messages sent");
  
  delay(100);
}

You might try this. I don't have the mcp2515 library installed and so couldn't compile it.

Basically, it delays sending the 1st message by 1mS and the 2nd message by 6mS (giving a time displacement between the two messages of 5mS.)

Once the 1st message of each type goes, the period is then set to 10mS and 50mS. The offset of 5mS is intended to prevent both messages being sent "simultaneously" every 5th 1st message. Here, the messages are sent like:

1mS msg1
6mS msg2

11ms msg1
21ms msg1
31ms msg1
41ms msg1
51ms msg1

56ms msg2

61ms msg1
71ms msg1
81ms msg1
91ms msg1
101ms msg1

106ms msg2

111ms msg1

and so on.

As I said, not compiled and not tested. YMMV.

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);

uint32_t
    timeNow,
    timeMsg1,
    timeDelay1,
    timeMsg2,
    timeDelay2;
bool
    bFirst1,
    bFirst2;
    
void setup() 
{
    canMsg1.can_id  = 0x0F6;
    canMsg1.can_dlc = 8;
    canMsg1.data[0] = 0x8E;
    canMsg1.data[1] = 0x87;
    canMsg1.data[2] = 0x32;
    canMsg1.data[3] = 0xFA;
    canMsg1.data[4] = 0x26;
    canMsg1.data[5] = 0x8E;
    canMsg1.data[6] = 0xBE;
    canMsg1.data[7] = 0x86;

    canMsg2.can_id  = 0x036;
    canMsg2.can_dlc = 8;
    canMsg2.data[0] = 0x0E;
    canMsg2.data[1] = 0x00;
    canMsg2.data[2] = 0x00;
    canMsg2.data[3] = 0x08;
    canMsg2.data[4] = 0x01;
    canMsg2.data[5] = 0x00;
    canMsg2.data[6] = 0x00;
    canMsg2.data[7] = 0xA0;
 
    while (!Serial);
    Serial.begin(115200);
    
    mcp2515.reset();
    mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    
    Serial.println("Example: Write to CAN");    

    timeNow = micros();
    timeMsg1 = timeNow;
    timeMsg2 = timeNow;
    timeDelay1 = 1000ul;                //1mS initial delay for 1st message
    timeDelay2 = timeDelay1 + 5000ul;   //5mS offset from 1st message
    bFirst1 = false;                    //if true, we've sent at least one msg1
    bFirst2 = false;                    //if true, we've sent at least one msg2
    
}

void loop() 
{
    //get microsecond count since reset
    timeNow = micros();
    //time to send message 1?
    if( (timeNow - timeMsg1) >= timeDelay1 )
    {
        //send message
        mcp2515.sendMessage(&canMsg1);    
        //save time for next message timing
        timeMsg1 = timeNow;

        //if 1st message sent, set up interval to be 10mS
        if( !bFirst1 )
        {
            timeDelay1 = 10000ul;
            bFirst1 = true;
            
        }//if     
        
    }//if

    //same logic for message 2
    if( (timeNow - timeMsg2) >= timeDelay2 )
    {
        mcp2515.sendMessage(&canMsg2);    
        timeMsg2 = timeNow;

        //but for message 2 we set the offset to 50mS
        if( !bFirst2 )
        {
            timeDelay2 = 50000ul;
            bFirst2 = true;
            
        }//if
        
    }//if
        
}//loop

Thank you Blackfin for the quick reply.
I tried it - same story. Second message stopped after being sent 18 times, First messages goes non-stop.
Period each message sent is 4 times per 1 ms (i.e. 0.25us), see monitor and trace screenshots:

Moded w micros - Monitor.jpg

Moded w micros - Trace.jpg

Moded w micros - Monitor.jpg

Moded w micros - Trace.jpg

Hmm. I loaded the library, made a few changes and tested with the scope and this looks good. Give it a try:

#include <SPI.h>
#include <mcp2515.h>

struct can_frame canMsg1;
struct can_frame canMsg2;
MCP2515 mcp2515(10);

const uint8_t pinTest1 = 8;
const uint8_t pinTest2 = 9;

uint32_t
    timeNow,
    timeMsg1,
    timeMsg2;
uint8_t
    msgCnt;
bool
    bFlagMsg2;
    
void setup( void ) 
{
    pinMode( pinTest1, OUTPUT );
    pinMode( pinTest2, OUTPUT );
    
    canMsg1.can_id  = 0x0F6;
    canMsg1.can_dlc = 8;
    canMsg1.data[0] = 0x8E;
    canMsg1.data[1] = 0x87;
    canMsg1.data[2] = 0x32;
    canMsg1.data[3] = 0xFA;
    canMsg1.data[4] = 0x26;
    canMsg1.data[5] = 0x8E;
    canMsg1.data[6] = 0xBE;
    canMsg1.data[7] = 0x86;

    canMsg2.can_id  = 0x036;
    canMsg2.can_dlc = 8;
    canMsg2.data[0] = 0x0E;
    canMsg2.data[1] = 0x00;
    canMsg2.data[2] = 0x00;
    canMsg2.data[3] = 0x08;
    canMsg2.data[4] = 0x01;
    canMsg2.data[5] = 0x00;
    canMsg2.data[6] = 0x00;
    canMsg2.data[7] = 0xA0;
 
    while (!Serial);
    Serial.begin(115200);
    
    mcp2515.reset();
    mcp2515.setBitrate(CAN_500KBPS, MCP_8MHZ);
    mcp2515.setNormalMode();
    
    Serial.println("Example: Write to CAN");    

    timeNow = micros();
    timeMsg1 = timeNow;
    timeMsg2 = 0ul;
    bFlagMsg2 = false;                    //if true, we're about to send a msg2
    msgCnt = 5;
    
}//setup

void loop( void ) 
{
    //get microsecond count since reset
    timeNow = micros();
    
    //time to send message 1?
    if( (timeNow - timeMsg1) >= 10000ul )
    {
        //save time for next message timing        
        timeMsg1 = timeNow;
        
        //send message
        digitalWrite( pinTest1, HIGH );
        mcp2515.sendMessage(&canMsg1); 
        digitalWrite( pinTest1, LOW );           

        msgCnt--;
        if( msgCnt == 0 )
        {
            timeMsg2 = timeNow;
            bFlagMsg2 = true;
            msgCnt = 5;
            
        }//if
        
    }//if
    
    //same logic for message 2
    if( bFlagMsg2 )
    {
        //send msg2 5mS after 5th msg1
        if( (timeNow - timeMsg2) >= 5000ul ) 
        {
            digitalWrite( pinTest2, HIGH );
            mcp2515.sendMessage(&canMsg2); 
            digitalWrite( pinTest2, LOW );   

            bFlagMsg2 = false;
            
        }//if
        
    }//if
        
}//loop

With this one:
message 2 - doesn't show at all
message 1 - non-stop with 0,25uc period

Double checked how I downloaded library, noticed that in comparison with example the first line:

#include <SPI.h> // in example 
#include <can.h> // in library itself

Tried both options with both of your previous suggestions - makes no difference - result is the same

Well, I've verified both with an oscilloscope and with a logic analyzer that the msg1 frame is being sent every 10mS and the msg2 frame is sent once every 50mS:

portion of msg1:

portion of msg2:

I'll state without equivocation that msg1 is not being sent every 250uS and that msg2 is being sent.

What's actually being sent out the SPI for msg1 is:

03 30 00    02 31 1E C0 00 00 08 8E 87 32 FA 26 8E BE 86    05 30 08 08    03 30 00

For msg2 I see:

03 30 00    02 31 06 C0 00 00 08 0E 00 00 08 01 00 00 A0    05 30 08 08    03 30 00

The spaces represent periods where SS goes high so for each transfer, there are actually 4 SS events.

Question:
You want msg1 sent every 10mS and msg2 every 50mS. Does that mean that you want 4 msg1 messages (10, 20, 30 40mS) and at 50mS send msg2 instead of msg1? Or what I'm doing which is sending msg1 every 10mS and slipping in a msg2 5mS after the 5th msg1?

valer1:
message 2 - doesn't show at all
message 1 - non-stop with 0,25uc period

The Nano & MCP2515 combination has a max message rate of about 600 per second.
Seeing a continuous stream of messages faster than that indicates a problem at the CAN level.

My guess is that the sending MCP2515 isn't receiving ACK bits and therefore keeps sending the same message.

Is the CAN analyzer configured as a "listen only" device?
If so you need to configure it to send ACK bits.

Blackfin, it did work!!! Genius.

I followed hint from mikb55, big thanks for that, and "ticked off" "listen only" in my CAN Hacker.

Here is the Trace:
Time ID DLC Data
04.270 036 8 0E 00 00 08 01 00 00 A0
04.275 0F6 8 8E 87 32 FA 26 8E BE 86
04.285 0F6 8 8E 87 32 FA 26 8E BE 86
04.295 0F6 8 8E 87 32 FA 26 8E BE 86
04.304 0F6 8 8E 87 32 FA 26 8E BE 86
04.314 0F6 8 8E 87 32 FA 26 8E BE 86
04.319 036 8 0E 00 00 08 01 00 00 A0
04.324 0F6 8 8E 87 32 FA 26 8E BE 86
04.334 0F6 8 8E 87 32 FA 26 8E BE 86
04.344 0F6 8 8E 87 32 FA 26 8E BE 86
04.354 0F6 8 8E 87 32 FA 26 8E BE 86
04.364 0F6 8 8E 87 32 FA 26 8E BE 86
04.369 036 8 0E 00 00 08 01 00 00 A0
04.374 0F6 8 8E 87 32 FA 26 8E BE 86
04.384 0F6 8 8E 87 32 FA 26 8E BE 86
04.394 0F6 8 8E 87 32 FA 26 8E BE 86
04.404 0F6 8 8E 87 32 FA 26 8E BE 86
04.414 0F6 8 8E 87 32 FA 26 8E BE 86
04.419 036 8 0E 00 00 08 01 00 00 A0

Blackfin:
Question:
You want msg1 sent every 10mS and msg2 every 50mS.

You code worked perfectly. In this set up it was a proof of concept that two messages with different pre-set period on nano+mcp2515 could actually work. That means I am now next to the step two - get the block from the wrecked car I want to emulate, record it's messages which I expect to be no more than 4, make an emulator and do the field test.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.