Electronic – How to achieve an interrupt frequency of 100 kHz on SAM E70

atmelinterruptsmicrocontrollertimer

Setup: ATMEL Studio 7, ASF3.47.0, SAME70 XPlained eval board.

In the minimum working example shown below, I use the onboard LED for visualizing if the timer and interrupt enabling have executed. PIO_PD20 is used to check if the interrupt is working. For that I simply connected my oscilloscope probe to MISO on the six-pin header near the center of the board. The code below works as expected up to frequencies of 2287 Hz. Once I choose frequencies higher than that, the interrupt doesn't seem to get triggered. What am I doing wrong? I need this to clock at 100 kHz.

I've also used the debugger to step through the individual lines of code. A breakpoint in the TC0 handler gets trigger at frequencies at or below 2287 Hz. Higher frequencies do not trigger the breakpoint, reconfirming that the interrupt routine doesn't fire.

#include <asf.h>

void TC0_Handler(void) {
    volatile uint32_t ul_dummy;

    pio_set_output(PIOD, PIO_PD20, HIGH, DISABLE, DISABLE);

    ul_dummy=TC0->TC_CHANNEL[0].TC_SR;  // read status register to clear interrupt flag.
    UNUSED(ul_dummy);

    pio_set_output(PIOD, PIO_PD20, LOW, DISABLE, DISABLE);
}


int main (void) {
    static uint32_t ul_sysclk, ul_div, ul_tcclks;
    uint32_t interrupt_frequency=2287; // in Hz

    sysclk_init();
    board_init();
    pmc_enable_periph_clk(ID_PIOC);
    pmc_enable_periph_clk(ID_PIOD);

    // Turn onboard LED off
    pio_set_output(PIOC, PIO_PC8, HIGH, DISABLE, DISABLE);

    // Timer and interrupt configuration
    ul_sysclk=sysclk_get_cpu_hz();
    pmc_enable_periph_clk(ID_TC0);
    tc_find_mck_divisor(interrupt_frequency, ul_sysclk, &ul_div, &ul_tcclks, ul_sysclk);
    tc_init(TC0, 0, ul_tcclks | TC_CMR_CPCTRG);
    tc_write_rc(TC0, 0, (ul_sysclk/ul_div)/interrupt_frequency);
    NVIC_EnableIRQ((IRQn_Type) ID_TC0);
    tc_enable_interrupt(TC0, 0, TC_IER_CPCS);
    tc_start(TC0, 0);

    // Turn onboard LED on
    pio_set_output(PIOC, PIO_PC8, LOW, DISABLE, DISABLE);

    while(true){};
}

Best Answer

Thanks to @Justme, I've managed to configure the interrupt for 100 kHz. The problem was that the timer wasn't set to a width of 32 bits and the function tc_find_mck_divisor doesn't 'know' that. The following code is a solution that still works with a 16-b timer register (provided the master clock runs at 150 MHz):

int main (void) {

    sysclk_init();
    board_init();

    pmc_enable_periph_clk(ID_PIOC);
    pmc_enable_periph_clk(ID_PIOD);

    // Turn onboard LED off
    pio_set_output(PIOC, PIO_PC8, HIGH, DISABLE, DISABLE);

    // Timer and interrupt configuration for 100 kHz interrupt frequency
    pmc_enable_periph_clk(ID_TC0);
    tc_init(TC0, 0, 1 | TC_CMR_CPCTRG); // set prescaler to 8 (TCCLKS = 1)
    tc_write_rc(TC0, 0, 187);   // 150 MHz (MCK) divided by 8 (prescaler) divided by 100 kHz - 1.
    NVIC_EnableIRQ((IRQn_Type) ID_TC0);
    tc_enable_interrupt(TC0, 0, TC_IER_CPCS);
    tc_start(TC0, 0);

    // Turn onboard LED on
    pio_set_output(PIOC, PIO_PC8, LOW, DISABLE, DISABLE);

    while(true){};
}
Related Topic