TimerOne and interruptions

Hi, i am coding a program that will tell the time between two can messages
so i am coding a sending program so every 10ms he send me a message

The problem is that when i check the time between two IT i don't have 10ms, so i use micros() to check the time passed
if i don't put anything in the IT fonction i have 10ms but if i put the send i have ~8ms that should be longer in time !

The code is attached below
Thanks

CAN_send.ino (2.05 KB)

post the code directly into the forum (using code tags - read how to use the forum). Can't read that attachment from a smartphone or tablet easily

// CAN Send Example
//

#include <mcp_can.h>
#include <SPI.h>

#include<TimerOne.h>

#define DEBUG_1 23
#define ON HIGH
#define OFF LOW

#define DON1 digitalWrite(DEBUG_1, ON);
#define DOFF1 digitalWrite(DEBUG_1, OFF);
#define DALT digitalWrite(DEBUG_1, !digitalRead(DEBUG_1)); 


MCP_CAN CAN0(10);     // Set CS to pin 10
byte data[8] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};

int env_100;
int env_1000;
volatile unsigned long mrco;
volatile unsigned long t_old;

int flag;
unsigned long tmp;

void time(){
  mrco = micros();
  
  DALT
  env_100++;
  /*if(sndStat == CAN_OK){
    Serial.println("Message Sent Successfully!");
  } else {
    Serial.println("Error Sending Message...");
  }*/
//tmp = micros();
 Serial.println(mrco - t_old);
  t_old = mrco;
  CAN0.sendMsgBuf(0x1C7, 0, 4, data);
  //Serial.println(micros()-tmp);
  if(env_100 == 10){
  //DALT
  env_100 = 0;
  
  flag = 1;
  
  //byte sndStat = CAN0.sendMsgBuf(0x1C7, 0, 4, data);
  env_1000++;
  if(env_1000 == 10){
      //sndStat = CAN0.sendMsgBuf(0x1C8, 0, 4, data);
    env_1000 = 0;
  }
  /*if(sndStat == CAN_OK){
    Serial.println("Message Sent!");
  } else {
    Serial.println("Error Sending ...");
  }*/
  
  }
}
void setup()
{
  Serial.begin(115200);

  // Initialize MCP2515 running at 16MHz with a baudrate of 500kb/s and the masks and filters disabled.
  if(CAN0.begin(MCP_ANY, CAN_250KBPS, MCP_16MHZ) == CAN_OK) Serial.println("MCP2515 Initialized Successfully!");
  else Serial.println("Error Initializing MCP2515...");
  pinMode(DEBUG_1, OUTPUT);

  CAN0.setMode(MCP_NORMAL);   // Change to normal mode to allow messages to be transmitted
  Timer1.initialize(10000); // 10 ms
  Timer1.attachInterrupt(time);
  t_old = micros();
  flag = 0;
}

void loop()
{
// if(flag){
//  tmp = micros();
//  CAN0.sendMsgBuf(0x1C7, 0, 4, data);
//  Serial.println(micros()-tmp);
//  
// }
}

/*********************************************************************************************************
  END FILE
*********************************************************************************************************/

To run something every n ms approximately (assuming nothing comes to kills the millis() timer) the typical approach is to use a construct as such

const uint32_t period = 10ul;
uint32_t chrono;

void setup()
{
  chrono = millis();
}


void loop()
{
  if (millis() - chrono > period) {
    // it's time to do something, of course it should not be longer than period
    // ...
    chrono += period;
  }

  // you can do other stuff here
}

on this code to time how long things take:

//  tmp = micros();
//  CAN0.sendMsgBuf(0x1C7, 0, 4, data);
//  Serial.println(micros()-tmp);

--> are you sure sendMsgBuf() is synchronous?

for example if you were to time how long it takes to Serial.print() something, you'd be surprised that in most cases the function returns almost immediately...that's because the data is put in a buffer and interrupts are used to send it out whilst your code continues to do other stuff.

There is probably a function to check if a transmit has completed. that's what you should monitor for execution.

Hey thanks for answering
my problem is not the time that CanSendMsg takes , it was an old test.
The thing is that is want msg to be sent every 10ms but the loop will do other things, as LCD print or so
and IT are more precise than that loop exemple, i think they are ?

So why does my IT is less precise when i put that function in it?

I'm unsure why you would see 8ms, but I'm unsure either I would trust your code timing.

Timer1 will run a function each time the timer period finishes. The function is run as an interrupt, so special care is needed to share any variables between the interrupt function and your main program (as you did using volatile) as well as other functions such as Serial.print or SPI or your CAN stuff can create challenges leading to weird behaviors.

For example the print you do in the time() function won't execute until after you exit the ISR, or could be blocking if you overflow the 64 bit buffer...

The micros difference as i show is for 10ms IT printing 7/8
and in 100ms it prints 97/98

but with an oscilloscope for 10 it says 10.1ms (which is ok)
and for 100ms it prints 101ms

So one of the both is lying either micros() is really not precise so the question would be how can i time the time between 2 can msg ?

The micros (On 16 MHz Arduino board) function has a resolution of four microseconds, so you should be on the safe side there.

The question is more what are you timing? Given you are within an ISR, whatever depends on interrupt (like sending your CAN message possibly) won't start happening until you exit the time() function.

100Hz (10ms) is possibly still a manageable pace and I would suggest you first drop the TimerOne approach and give a try to what I described in #3.

Of course it means the loop() should cycle in less than 10ms. LCDs are known to be slow and anything that uses interrupts will come into competition with your 100Hz requirement.

--> You need to time everything else to see if that can work

Well i think you didn't anderstood my problem sorry i think i'm not very confortable with it

I want to test if micros() is very precise to time lenght between message so i made an IT every 10ms and i check with micros to see if i get 10000

So when there's nothing in my IT function it's fine i get everytime 10000

BUT
when i put the sendCanmsg function it starts to move a lot for 10000 to 8999 -> 9500

So that make me think that micros is not trustable but i need to be at 1ms error maximum to time can msg how can i do that?

From what I can see you've failed to call timer1.start(), so the value of the timer is completely undefined

You are calling Serial functions from within an ISR - this can and will lock up the processor, don't do this.

MarkT:
From what I can see you've failed to call timer1.start(), so the value of the timer is completely undefined

well i don't care about the first value as i want the difference from that value and the next one

MarkT:
You are calling Serial functions from within an ISR - this can and will lock up the processor, don't do this.

How can i control the time then in loop?

If you record the difference in a volatile variable you can see this in loop().

But you are probably confusing yourself, timers work reliably, micros() works reliably, its 99.99% certain
its not a bug in the system, but in how you are using it.

Well can you post a code with that cause i posted one in the beggining that should work

AnotherBeginner:
Well i think you didn't understand my problem sorry i think i'm not very confortable with it

I want to test if micros() is very precise to time lenght between message so i made an IT every 10ms and i check with micros to see if i get 10000

So when there's nothing in my IT function it's fine i get everytime 10000

BUT
when i put the sendCanmsg function it starts to move a lot for 10000 to 8999 -> 9500

So that make me think that micros is not trustable but i need to be at 1ms error maximum to time can msg how can i do that?

I think I understand but you don't get what I'm saying. let me try again

micros() is relatively precise, not at the low end - ie it's less precise to measure a few microsecond (given the 4µs resolution it will tell you 4, 8, 12, 16 etc not 3µs or 7µs) than longer delays. Micros is managed through timer0 and the resolution is 4µs because of a prescaler of 64. Timers depends on the system clock of your Arduino, which by itself is also not purely 16MHz, but again for a relatively short duration and limited precision you can ignore drifts and imprecisions.

So here your are not talking about timing just a few µs and you theoretically would be fine. But you want to measure something with a tool that is sensitive to ISR conflicts.

micros() always reads the hardware timer 0 and as we said this is constantly being updated by the hardware every 4µs. Timer 0 is a 8 bit timer, so after 256 clock ticks , Timer 0 overflows and there is an interrupt which updates counts .

However if you spend too long in ISRs then you might miss the overflow update and precision will drop.

--> I'm unsure of what the CAN library stuff do but depending on what is being sent etc, it might affect time spent in ISR and thus miss some timer 0 cycles that would add up... just a guess...