1
\$\begingroup\$

I am programming a SAMC21 for a control system and I need a milliseconds counter similar to the millis function in arduino to keep track of samples, etc. To do this I first set up a clock channel for the internal oscillator (48MHz) whose output I divide by 48. I then set up an 8 bit Timer/Counter with a period of 100. Everytime the TC reaches 100 it overflows and I use a callback to call a function which simply increments my millis counter variable (volatile uint32_t). This works fine but the counter is quite slower than it should be and delays by between 5 and 15s every minute. Why is that so and how could I make it more accurate?

Note: I use the UART driver over the Debug USB to read the counter value.

Here is the code which is called appropriately in main:

 void configure_gclock_generator(void) // Configuration of internal oscillator and channel
 {
     struct system_gclk_gen_config gclock_gen_conf;
     system_gclk_gen_get_config_defaults(&gclock_gen_conf);
     gclock_gen_conf.source_clock    = SYSTEM_CLOCK_SOURCE_OSC48M;
     gclock_gen_conf.division_factor = 48;
     system_gclk_gen_set_config(GCLK_GENERATOR_1, &gclock_gen_conf);
     system_gclk_gen_enable(GCLK_GENERATOR_1);
 }
 void configure_gclock_channel(void)
 {
     struct system_gclk_chan_config gclk_chan_conf;
     system_gclk_chan_get_config_defaults(&gclk_chan_conf);
     gclk_chan_conf.source_generator = GCLK_GENERATOR_1;
     system_gclk_chan_set_config(TC3_GCLK_ID, &gclk_chan_conf);
     system_gclk_chan_enable(TC3_GCLK_ID);
 }

void configure_tc(void) // COnfiguration of TC on internal oscillator
{
    struct tc_config config_tc;
    tc_get_config_defaults(&config_tc);
    config_tc.counter_size = TC_COUNTER_SIZE_8BIT;
    config_tc.clock_source = GCLK_GENERATOR_1;
    //config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV1;
    config_tc.counter_8_bit.period = 100;
    //config_tc.counter_16_bit.compare_capture_channel[0] = 48;
    tc_init(&tc_instance, CONF_TC_MODULE, &config_tc);
    tc_enable(&tc_instance);
}

void millis_count(struct tc_module *const module_inst) { //millis increment function
    millis++;
    if (millis>=1000000000) {
        millis=millis-start;
        start=0;
    }
}

void configure_tc_callbacks(void) //Set up of Callback for millis function.
{
    tc_register_callback(&tc_instance, millis_count,TC_CALLBACK_OVERFLOW);
    //tc_register_callback(&tc_instance, millis_count,TC_CALLBACK_CC_CHANNEL0);
    tc_enable_callback(&tc_instance, TC_CALLBACK_OVERFLOW);
    //tc_enable_callback(&tc_instance, TC_CALLBACK_CC_CHANNEL0);
}
\$\endgroup\$
  • \$\begingroup\$ Have you had a look at the datasheet and the accuracy it promises? \$\endgroup\$ – PlasmaHH Nov 30 '16 at 12:33
2
\$\begingroup\$

You're actually taking an interrupt every 100 µs, not millisecond as your naming implies. That's a lot of interrupts for a 48 MHz system.

Furthermore, the code hints at a rather elaborate interrupt handling structure that exists "behind the scenes", with callbacks being "registered" and "enabled". So while your actual callback function is short and sweet, the overall overhead for taking any individual interrupt may still be rather high.

You also hint at using a UART for communication, and I'm guessing that its driver is also interrupt-based.

All of this leads me to suggest that you are approaching the limit on the number of interrupts that this system can handle, and that some of your timer interrupts are simply being missed. This would be especially true if the UART interrupts have a higher priority than the timer interrupts, which is the usual case. Try increasing the timer interrupt period to 200 µs or 1000 µs. (In your callback, increment the millis variable by 2 or 10, respectively — then the rest of the system won't know the difference.)

\$\endgroup\$
  • \$\begingroup\$ I actually just found a sentence in the datasheet saying that the clock generator divides the source by 12, so it is actually running at 4MHz. I implemented accordingly with a division factor on the clock of 40 and a period of 100. It is now quite accurate and loses about 2 seconds every 4 minutes. Is that to be expected or do you think I could make it even better? \$\endgroup\$ – Eliott W Nov 30 '16 at 13:38
  • 2
    \$\begingroup\$ Well, now you're down to <1% error, which could be attributed to the accuracy of the clock (if it's an R-C clock source), but it still sounds rather high to me. It's possible you're still missing some interrupts. \$\endgroup\$ – Dave Tweed Nov 30 '16 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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