-1

I tried to build a simple l-meter with my arduino due. I programmed a simple frequency meter with one timer and one counter. The timer takes an interrupt each 10s, 100ms, 10ms and 1ms. But ones the interrupt is taken it is stopped and even a short Serial.println("Hello World"); does not appear in the serial monitor of the arduino IDE. I don't know what I forgot. Here is my code:

#define voltage 26

volatile uint32_t current_range = 10000;               //starts with highest range and decrements. longer measurements produce more precise results. measurements for small l values require 11.111ms
volatile const double c_1 = 2533029590000000.0;        //constant value for l value calculation
volatile double correction_value = 0;
volatile boolean calib = true;
volatile uint32_t counter_value = 0;

void initCounter(){
    pmc_enable_periph_clk(ID_TC0);              //enable peripheral clock for TC0

    REG_TC0_CMR0 = TC_CMR_TCCLKS_XC0;           //External clock 0 connected to PB26 (Digital Pin 22)
    REG_TC0_CCR0 = TC_CCR_CLKEN;                //Enable clock
}

void initTimer(){
    pmc_enable_periph_clk(ID_TC1);              //enable peripheral clock for TC1

    REG_TC0_CMR1 = TC_CMR_TCCLKS_TIMER_CLOCK1;  //42MHz clock
    REG_TC0_IER1 = TC_IER_CPCS;                 //Enable compare RC interrupt
    REG_TC0_IDR1 = ~TC_IDR_CPCS;                //Enable compare RC interrupt (disbale interrupt = 0)
    REG_TC0_CCR1 = TC_CCR_CLKEN;                //Enable clock

    NVIC_EnableIRQ(TC1_IRQn);                   //Enable interrupt in nested vectored interrupt controller NVIC (IRQN number = TC number * channels + channel number)
}

double calc_l(uint32_t CV){
    double freq = (double)CV / ((double)current_range / 1000.0);
    freq = freq * freq;

    return c_1 / freq;
}

void calibrate(){
    Serial.println("---Calibrating... shorten inputs then press enter---");
    while(!(Serial.available() == '\n'));
    Serial.print("Start calibration\n");
    initCounter();                              //initialize counter
    initTimer();                                //initialize timer

    REG_TC0_RC1  = 0x1908B100;                  //RC = 420.000.000 to run 10.000ms

    REG_TC0_BCR = TC_BCR_SYNC;                  //reset and start timer and counter simultaneously
}

void measure(){
    calib = false;

    //Serial.println("---Measure... connect inductivity then press any key---");
    //while(!(Serial.available() > 0));
    initCounter();                              //initialize counter
    initTimer();                                //initialize timer  
    REG_TC0_RC1  = 0x1908B100;                  //RC = 420.000.000 to run 10.000ms

    REG_TC0_BCR = TC_BCR_SYNC;                  //reset and start timer and counter simultaneously
}

void setup() {
    Serial.begin(250000);

    pmc_set_writeprotect(false);                //disable writeprotect for pmc 
    pinMode(13, OUTPUT);
    pinMode(voltage, OUTPUT);                   //set digital pin 26 as output
    digitalWrite(voltage, HIGH);                //set digital pin 26 HIGH
    delay(1000);                                //wait 1 second to get circuit working

    calibrate();
}

void loop() {
    analogWrite(13, 25);
    delay(100);
    Serial.print(REG_TC0_SR1);
    Serial.print(";");
    Serial.print(REG_TC0_CMR1);
    Serial.print(";");
    Serial.print(REG_TC0_IER1);
    Serial.print(";");
    Serial.print(REG_TC0_IDR1);
    Serial.print(";");
    Serial.print(REG_TC0_CCR1);
    Serial.print("\n");
}

void TC1_Handler(){

    uint32_t SR = REG_TC0_SR0;                    //save status register 
    uint32_t CV = REG_TC0_CV0;                    //save counter value

    if((SR & TC_SR_CLKSTA) == TC_SR_CLKSTA){      //check whether counter clock is enabled
        if((SR & TC_SR_COVFS) == TC_SR_COVFS){      //check whether counter overflow accured
            switch(current_range){                    //check current range. decrement until smallest range is reached. then print error
                case 10000: REG_TC0_RC1  = 0x280DE80;                   //RC = 42.000.000 to run 1.000ms          
                            current_range = 1000;
                            REG_TC0_BCR  = TC_BCR_SYNC;
                            break;
                case 1000:  REG_TC0_RC1  = 0x401640;                    //RC = 4.200.000 to run 100ms
                            current_range = 100;
                            REG_TC0_BCR  = TC_BCR_SYNC;
                            break;
                case 100:   REG_TC0_RC1  = 0x668A0;                     //RC = 420.000 to run 10ms
                            current_range = 10;
                            REG_TC0_BCR  = TC_BCR_SYNC;
                            break;
                case 10:    REG_TC0_RC1  = 0xA410;                      //RC = 42.000 to run 1ms
                            current_range = 1;
                            REG_TC0_BCR  = TC_BCR_SYNC;
                            break;
                case 1:     //Serial.println("Error: l value out of range!");
                            REG_TC0_CCR0 = ~TC_CCR_CLKEN;
                            REG_TC0_CCR1 = ~TC_CCR_CLKEN;
                            break;
            }
        } else {
            if(calib){
                if(CV < 10){
                    //Serial.println("Error: wrong calibration value!");
                    REG_TC0_CCR0 = ~TC_CCR_CLKEN;
                    REG_TC0_CCR1 = ~TC_CCR_CLKEN;
                } else {
                    //correction_value = calc_l(CV);
                    measure();
                }
            } else {
                if(CV < 10){                           //check if counter value is below 10. then l value is to high to get accurate results
                    //Serial.println("Error: l value out of range!");
                    REG_TC0_CCR0 = ~TC_CCR_CLKEN;
                    REG_TC0_CCR1 = ~TC_CCR_CLKEN;
                } else {                               //l value in nH; 
                    counter_value = CV;
                    //Serial.print(calc_l(CV) - correction_value);
                    //Serial.print(" nH; ");
                    //Serial.print(CV);
                    //Serial.print(" ticks in ");
                    //Serial.print(current_range);
                    //Serial.println("ms.");
                    REG_TC0_CCR0 = ~TC_CCR_CLKEN;
                    REG_TC0_CCR1 = ~TC_CCR_CLKEN;
                }
            }
        }
    } else {
        REG_TC0_CCR0 = TC_CCR_CLKEN;
        REG_TC0_BCR  = TC_BCR_SYNC;
    }
}

It would be very nice if someone could help me with this.

  • 2
    Calling Serial from an ISR is not a very good idea. Have you tested removing that? – Mikael Patel Feb 16 '16 at 9:21
  • Yes but after removing these lines the µc still hangs in the ISR. The only thing that changes is that I don't know how far the TC1_Handler is executed. – Fabian Fahrenholz Feb 16 '16 at 15:03
  • But the ISR may call measure() and that waits for data on Serial, "while(!(Serial.available() > 0));". You need to rethink the control flow between the ISR and the measurement. – Mikael Patel Feb 16 '16 at 15:31
  • I removed these lines too for testing so that it does not wait for any serial data. I only printed the value of the counter via Serial.println(REG_TC0_CV0) but after some time this output stops and nothing is done. Without any serial communication the measure() function only initializes the timer and the counter and restarts them. – Fabian Fahrenholz Feb 16 '16 at 15:51
  • Now check the behavior by reading the timer registers in loop(). – Mikael Patel Feb 16 '16 at 16:04
1

Maybe I'm dense, but I don't understand why most of the config code is in the ISR. If the system is getting bound up there, I'd move much of it out and try to simplify it as much as possible, maybe saving only the counter value and the fact that the timer triggered on interrupt, and saving the overflow value if it overflowed. Then, outside the ISR, on the reporting timescale, I'd check that there was valid data and report or reconfigure as needed.

int validReading = 0;
uint32_t measuredCount;
int overflowed =0; 


void TC1_Handler(){
    measuredCount = REG_TC0_CV0;    
    if((SR & TC_SR_COVFS) == TC_SR_COVFS){  //counter overflow occurred
       validMeasurement=0;
       overflow=1;
    else {
       validMeasurement = 1;
       // either reset counter and clock or stop handling interrupts 
    }
 }


 loop(){
   ...
   if (validReading) { // report
      ...
   } else {
   if (overflowed ) { // shorten sampling time, calibrate, and initialize
      ...
   } 
   if ( measuredCount < 10 ) { // lengthen sampling time? 
      ...
   }
   // calibrate and reinitialize  
 }
  • Looks like it could work. Did not thought about putting data handling in the loop function. I will try this version tomorrow. Thanks for your help. – Fabian Fahrenholz Feb 17 '16 at 19:04

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.