Go Down

Topic: Leonardo Timer trouble... (Read 12677 times) previous topic - next topic

atronchi

After way more time than I want to admit, I'm ready to admit defeat on an apparent timer bug in the ATmega32u4 (Leonardo). I'm trying to setup two timers (1 & 3) to work together, and for some reason timer3 seems to call the compare ISR almost immediately in some cases.

The program is expected to produce a 1kHz pulse of minimum duration on pin 9, and a 500e-6s duration pulse on pin 4 for every 100 pulses of pin 9. The result is as expected, except that the pulse on pin 4 only has 10e-6s duration MOST of the time, and occasionally 15 to 20e-6s duration. Maybe there's some other interrupt interfering?

This is an excerpted function from a larger program which was exhibiting this weird behavior, so I cut it down to the simplest demo of the problem and haven't been able to sort it out still. It needs to use the timers because timing is critical and doing this in software with the other requirements does not seem possible.

Code to reproduce the problem is:
Code: [Select]

  const int tmr1Pin = 9;
  const int tmr3Pin = 4;
  volatile int tcntx = 0;

// the setup routine runs once when you press reset:
void setup() {
  pinMode(tmr1Pin,      OUTPUT);   digitalWrite(tmr1Pin,LOW);
  pinMode(tmr3Pin,      OUTPUT);   digitalWrite(tmr3Pin,LOW);

  cli();          // disable global interrupts
  GTCCR = 0; // clear Timer Sync Mode to restart all timers and prescalers
  GTCCR = (1 << TSM); // set Timer Sync Mode to pause all timers

    TCCR1A = 0;
    TCCR1B = (1 << CS10); // prescaler=1 -> use system clock speed
    TCCR1B|= (1 << WGM12); // turn on CTC mode, TOP=OCR1A (ie resets at OCR1A)
      OCR1A = 16000; // set compare match register to desired timer count (16000/16MHz=1ms)
    TIMSK1 = (1 << OCIE1A); // enable timer compare interrupt

    TCCR3A = 0; // leave OC3A to software control
    TCCR3B = (1 << CS30); // prescaler=1 -> use system clock speed
    TCCR3B|= (1 << WGM32); // turn on CTC mode, TOP=OCR3A
      OCR3A = 8000;

  // turn off other interrupts
    // TIMSK0 = 0; // timer 0
    // USBCON = 0; // USB interrupts (now you need to press the reset button to program...)
    // at least one more is still on but I can't find it...

  GTCCR = 0; // clear Timer Sync Mode to restart all timers and prescalers
  sei();          // enable global interrupts
}

ISR(TIMER1_COMPA_vect) { // called after gate time has expired
  digitalWrite(tmr1Pin,HIGH);  digitalWrite(tmr1Pin,LOW);
  if (tcntx > 100) {
    tcntx=0;
    TCNT3=0;
    digitalWrite(tmr3Pin,HIGH);
    TIMSK3 = (1 << OCIE3A); // enable timer compare interrupt
  } else {
    tcntx++;
  } 
}

ISR(TIMER3_COMPA_vect) { // called after gate time has expired
  digitalWrite(tmr3Pin,LOW);
  TIMSK3 = 0;
}


// the loop routine runs over and over again forever:
void loop() {
} // end loop()



Strangely enough, if the three lines for setting up timer3 in the timer1 ISR are moved outside of the conditional statement so that they are called every pulse, timer3 behaves as expected for the first couple of pulses but then reverts to the above problematic operation.

Code: [Select]


  const int tmr1Pin = 9;
  const int tmr3Pin = 4;
  volatile int tcntx = 0;

// the setup routine runs once when you press reset:
void setup() {
  pinMode(tmr1Pin,      OUTPUT);   digitalWrite(tmr1Pin,LOW);
  pinMode(tmr3Pin,      OUTPUT);   digitalWrite(tmr3Pin,LOW);

  cli();          // disable global interrupts
  GTCCR = 0; // clear Timer Sync Mode to restart all timers and prescalers
  GTCCR = (1 << TSM); // set Timer Sync Mode to pause all timers

    TCCR1A = 0;
    TCCR1B = (1 << CS10); // prescaler=1 -> use system clock speed
    TCCR1B|= (1 << WGM12); // turn on CTC mode, TOP=OCR1A (ie resets at OCR1A)
      OCR1A = 16000; // set compare match register to desired timer count (16000/16MHz=1ms)
    TIMSK1 = (1 << OCIE1A); // enable timer compare interrupt

    TCCR3A = 0; // leave OC3A to software control
    TCCR3B = (1 << CS30); // prescaler=1 -> use system clock speed
    TCCR3B|= (1 << WGM32); // turn on CTC mode, TOP=OCR3A
      OCR3A = 8000;

  // turn off other interrupts
    // TIMSK0 = 0; // timer 0
    // USBCON = 0; // USB interrupts (now you need to press the reset button to program...)
    // at least one more is still on but I can't find it...

  GTCCR = 0; // clear Timer Sync Mode to restart all timers and prescalers
  sei();          // enable global interrupts
}

ISR(TIMER1_COMPA_vect) { // called after gate time has expired
  digitalWrite(tmr1Pin,HIGH);  digitalWrite(tmr1Pin,LOW);
  if (tcntx > 100) {
    tcntx=0;
  } else {
    tcntx++;
  } 
    TCNT3=0;
    digitalWrite(tmr3Pin,HIGH);
    TIMSK3 = (1 << OCIE3A); // enable timer compare interrupt
}

ISR(TIMER3_COMPA_vect) { // called after gate time has expired
  digitalWrite(tmr3Pin,LOW);
  TIMSK3 = 0;
}


// the loop routine runs over and over again forever:
void loop() {
} // end loop()


Any ideas?

nickgammon

Without looking into this in depth, there is something you should know. Interrupts are remembered. So before enabling the timer interrupt you may wish to clear the interrupt condition first.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

nickgammon

See this page: http://www.gammon.com.au/interrupts

Scroll down to "How are interrupts queued?".

That is about pin change interrupts but the concept is the same.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

fungus


After way more time than I want to admit, I'm ready to admit defeat on an apparent timer bug in the ATmega32u4 (Leonardo). I'm trying to setup two timers (1 & 3) to work together, and for some reason timer3 seems to call the compare ISR almost immediately in some cases.


Do you set the TCNT register?
Advanced Arduino

atronchi

@fungus: The TCNT register is not set, but the TCNT3 register is as shown above. That doesn't throw any interrupts on it's own does it?

@Nick: SCORE, thanks for the tip!

I added the line
   TIFR3 = (1 << OCF3A);
before reenabling the timer3 compare and it works like a charm now.

StMark

I ran the code above, but uncommented the USB interupt disabling line. Now I can't reset my Leonardo. Any pointers?

1ChicagoDave


I ran the code above, but uncommented the USB interupt disabling line. Now I can't reset my Leonardo. Any pointers?


Don't do that again...?
:.

nickgammon


I ran the code above, but uncommented the USB interupt disabling line. Now I can't reset my Leonardo. Any pointers?


The timing can be tricky, but you can always reset it. You have to press and release Reset just as the sketch starts to upload. It can take a few tries.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

StMark

Ah! Thanks Nick!

Got it first shot after waiting for it to say "Uploading..."

woodstock

Hi Nick,
I need to measure a signal with a frequency in the order of 500KHz-600KHz. The amplitude is about 1V, but about this point I can add a level shifter.

Currently, I am using your code below for the Yun but I cannot measure signal with frequency above 250KHz. How can I extend the range? Thanks!!!


Code: [Select]

// Frequency timer using input capture unit
// Author: Nick Gammon
// Date: 31 August 2013

// Input: Pin D4 on YUN Bill Gilbert 20141015_13:44 Works

#include "Bridge.h"
#include "Console.h"

volatile boolean first;
volatile boolean triggered;
volatile unsigned long overflowCount;
volatile unsigned long startTime;
volatile unsigned long finishTime;

// timer overflows (every 65536 counts)
ISR (TIMER1_OVF_vect) 
{
  overflowCount++;
}  // end of TIMER1_OVF_vect

ISR (TIMER1_CAPT_vect)
  {
  // grab counter value before it changes any more
  unsigned int timer1CounterValue;
  timer1CounterValue = ICR1;  // see datasheet, page 117 (accessing 16-bit registers)
  unsigned long overflowCopy = overflowCount;
  
  // if just missed an overflow
  if ((TIFR1 & bit (TOV1)) && timer1CounterValue < 0x7FFF)
    overflowCopy++;
  
  // wait until we noticed last one
  if (triggered)
    return;

  if (first)
    {
    startTime = (overflowCopy << 16) + timer1CounterValue;
    first = false;
    return; 
    }
    
  finishTime = (overflowCopy << 16) + timer1CounterValue;
  triggered = true;
  TIMSK1 = 0;    // no more interrupts for now
  }  // end of TIMER1_CAPT_vect
  
void prepareForInterrupts ()
  {
  noInterrupts ();  // protected code
  first = true;
  triggered = false;  // re-arm for next time
  // reset Timer 1
  TCCR1A = 0;
  TCCR1B = 0;
  
  TIFR1 = bit (ICF1) | bit (TOV1);  // clear flags so we don't get a bogus interrupt
  TCNT1 = 0;          // Counter to zero
  overflowCount = 0;  // Therefore no overflows yet
  
  // Timer 1 - counts clock pulses
  TIMSK1 = bit (TOIE1) | bit (ICIE1);   // interrupt on Timer 1 overflow and input capture
  // start Timer 1, no prescaler
  TCCR1B =  bit (CS10) | bit (ICES1);  // plus Input Capture Edge Select (rising on D8)
  interrupts ();
  }  // end of prepareForInterrupts
  

void setup ()
  {
      // initialize serial communication:
  Bridge.begin();
  Console.begin();

  pinMode(5,INPUT);
 
  while (!Console){
    ; // wait for Console port to connect.
  }
  Console.println("You're connected to the Console!!!!");
   delay(1000);
  
  Console.begin();       
  Console.println("Frequency Counter");
  // set up for interrupts
  prepareForInterrupts ();   
  } // end of setup

void loop ()
  {
  
  // wait till we have a reading
  if (!triggered)
    return;
 
  // period is elapsed time
  unsigned long elapsedTime = finishTime - startTime;
  // frequency is inverse of period, adjusted for clock period
  float freq = F_CPU / float (elapsedTime);  // each tick is 62.5 nS at 16 MHz
  
  Console.print ("Took: ");
  Console.print (elapsedTime);
  Console.print (" counts. ");

  Console.print ("Frequency: ");
  Console.print (freq);
  Console.println (" Hz. ");

  // so we can read it 
  delay (1);

  prepareForInterrupts ();   
}   // end of loop}


ands

#10
Apr 06, 2015, 02:05 pm Last Edit: May 18, 2015, 06:37 pm by ands Reason: found button for quoting source code ;)
Hi Nick,
I need to measure a signal with a frequency in the order of 500KHz-600KHz. The amplitude is about 1V, but about this point I can add a level shifter.
Use Timer 4.
I'm disappointed that nothing other people wrote for most AVR 8bitters works on the 32U4 regarding timers, PWM and such. So use the one advantage that the 32U4 has: its high speed timer 4, 10bit counter, 11bit PWM output compare registers; can be provided with up to 64MHz from PLL when running at 5V.
You can do PWM or capture (with de-noising) etc. Just ask me in a few weeks, I'm a re-beginner right now trying to do ulrichradig.de PWM in timers and DMX receiving for lighting control (in my case motor control and regulation;)

The problem with the USB devices is that you do not really know whats running underneath.
I did nothing fancy, but the arduino pro micro is not responding to upload any more. Hooked it up to a jtagIceMkII clone and use AVRstudio right now. Just for now I refrain from using cool libraries that are not really portable from the Atmega168/328/etc to 32U4.

The Timer4 is built totally different, it has no real 16bit registers, but a common high byte TC4H you first have to write to before writing to OCR4x.  You can see this in the example c code below.

I looked up all the names in the datasheet for the various bits to understand and be able to change the code without going back deep inside the datasheet, but some nice guy had it done already, see
http://wiki.dsvf.net/index.php?title=Fast_PWM_on_the_Atmega_32U4


Good luck

Ands


p.s.: got a demo unipolar PWM running now, doing a sweep in output %.
When you upload it, the reset to bootloader will not do. Do a manual reset then, or use a programmer. I still do not know what arduino is doing to be able to do a reset when 1200baud serial over usb are applied to a running sketch, but I suspect timer4 and watchdog timer (doing real reset) are involved.

Code: [Select]

/*
 * Micro_Test.c (on atmel studio 6)
 *
 * Setup Timer 4, allowing TOP up to 10bit, running with 64MHz Clock and do PWM in enhanced mode with +1 bit resolution as Compare Values.
 * For example you set TOP=255 written in OCR4C. So with MaxCompPWM = 2*TOP-1 = 511  you have 0..511 as OCRA/OCRB/OCRD PWM range 0..100%.
 * The PWM frequency for the Frequency and Phase Correct PWM is given by f_PWM = 64MHZ / (TOP+1) / 2 = 125kHz in the example.
 * The maximum resolution is given for 10bit TOP and 11bit OCR4x for x in {A,B,D} with MaxCompPWM=2*TOP-1 = 2047 = (1<<12)-1.
 *  The pwm frquency for TOP = 1023 ist f_PWM =  64MHz / 2 / 1024 = 31.250 kHz
 *   (For this PWM Freqency, motor is steered with 62.5kHz and has -2045 ... +2045 as speed settings in full resolution!
 *      ==> You can't expect to get it better. Oh yes, you can overclock Timer4 with full max PLL 96MHz ;)
 * Created: 15.05.2015 23:57:30
 *  Author: AndiSh
 */

#define TOP 255

#define MaxCompPWM (TOP*2-1)
#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include<util/atomic.h>

// ################### Input to PWM  ################################### Set_Input Unipolar PWM to OC4B / OC4D of 32u4 ***  Set_Input2
void Set_Input2(int16_t speed)
{
//  MotA = (MaxCompPWM + Power )  where Power is +- MaxCompPWM and TOP=MaxCompPWM>>1 = MaxCompPWM/2
//  MotB = (MaxCompPWM - Power )

// for MaxCompPWM=511 it has +-511 as Range, >0 forward, 0 Stop (with ShortCut-Braking with DC Brushed Motors), <0 backwards
// at the same time TOP of Timer4 is 255, so frequency for Phase and Frequency correct PWM (up down, factor 2) is
// with TCLK4=64MHZ f_PWM = 64MHz / (TOP+1) / 2 = 64MHz / 256 / 2 = 125 kHz. Wow.

int16_t MotA, MotB;
int16_t speed_ltd = speed; // limited speed to pwm max value. here 8bit.

if(speed < -MaxCompPWM ) speed_ltd =-MaxCompPWM;
if(speed >  MaxCompPWM ) speed_ltd = MaxCompPWM;

ATOMIC_BLOCK(ATOMIC_FORCEON)
{
MotA = (MaxCompPWM + speed_ltd) >>1;
TC4H = (MotA >> 8);
OCR4B = (unsigned char)MotA;

MotB = (MaxCompPWM - speed_ltd) >>1;
TC4H = (MotB >> 8);
OCR4D = (unsigned char)MotB;
}



return;
} // Ende Set_Input

int main(void)
{
static volatile uint16_t copyTop; // want to debug a copy of TOP


// Init Timer4 High Speed PLL-96Mhz /1,5 -> 64MHz Timer Clock.
PLLCSR |= (1<<PINDIV) | (1<<PLLE); // Pll Control and Status Register 16MHz Xtal /2 =8MHz, needed by PLL, see p. 40 6.10.2
/* #define PLLCSR _SFR_IO8(0x29)
#define PLOCK 0 #define PLLE 1  #define PINDIV 4
*/

_delay_ms(100);

PLLFRQ = 0b01101010;   //  7:PINMUX=0 (Xtal no RC), PLLUSB=1 (/2), PLLTM1:==10=/1.5, PDIV3:0=1010 =96MHz
//from define PLLFRQ _SFR_IO8(0x32) : 7:PINMUX 6:PLLUSB 5:PLLTM1 4:PLLTM0 [3:0]:PDIV[3:0]

while(! (PLLCSR & (1<<PLOCK))); // wait for pll to be active and stable.

DDRB |= (1<<PB0)|(1<<PB4); // LED Outputs as Diagnostics LED.
PORTB &= ~(1<<PB0); // Led on.

// PWM
DDRD |= (1<<PD7);
DDRB |= (1<<PB6);
PORTD |= (1<<PD7); // OC4D as Output PD7=D6 on Arduino Micro pro
PORTB |= (1<<PB6); // OC4B as Output, PB6=D10 on Arduino Micro pro

TCCR4A = 0b00100011; // |= (1<<COM4B1) | (1<<PWM4B); // COM4B1:Set on COmpMatch when downcounting, Clear--upCounting PWM4B:Enable.
//7:COM4A1 6:COM4A0 5:COM4B1 4:COM4B0 3:FOC4A 2:FOC4B 1:PWM4A 0:PWM4B

TCCR4B = 0b0100001; // |= (1<<CS40);   // Precsaler to 1 i.e. PCK/1 or CK/1
// 7:PWM4X 6:PSR4 5:DTPS41 4:DTPS40 3:CS43 2:CS42 1:CS41 0:CS40

TCCR4C = 0b10101001; // |= (1<<COM4D1) | (1<<PWM4D); // SET OC4D on. s. p.157
//7:COM4A1S 6:COM4A0S 5:COM4B1S 4:COMAB0S 3:COM4D1 2:COM4D0 1:FOC4D 0:PWM4D
// Problem: "S" = Mirror bits, like the real, read and write access to original bits. Set them consistently or don't touch.

TCCR4D = 0b00000001; // |= (1<<WGM40); // phase and freqency correct PWM mode. Top is OCR4C, tov4 set on bottom
// 7:FPIE4 6:FPEN4 5:FPNC4 4:FPES4 3:FPAC4 2:FPF4 1:WGM41 0:WGM40 - Waveform Generation Mode 01=PhaseAndFreqCorrPwm

TCCR4E = 0b01000000; // |= (1<<ENHC4); 11bit mode, 2047 (0x7FF) (if top is max.10bit, 0x03FF; values in PWM Compare Reg. (NOT in the counter! 10bit there.).
// 7:TLOCK4 6:ENHC4 5:OC4OE5 4:OC4OE4 3:OC4OE3 2:OC4OE2 1:OC4OE1 0:OC4OE0   - ENHC4 Enhanced PWM Mode.

// TCCR4D &= ~(1<<WGM40 | 1<<WGM41);// set WGM10 = 00 to stop timer to update TOP...:
  TC4H = (MaxCompPWM >>9); // TOP_PWM is given as maximum PWM value, using Enhanced PWM Mode this is 1 bit more than TOP in OCR4C.
  OCR4C = (unsigned char)(MaxCompPWM>>1); // set TOP ; defines resolution and frequency of pwm *****TOP*****
TCCR4D = 0b00000001; // |= (1<<WGM40); // phase and freqency correct PWM mode. Top is OCR4C, tov4 set on bottom

Set_Input2(0); // Set PWM to 50% on both Motor Pins to have 0V  Voltage. google for "unipolar PWM".
// Goal is: Double PWM frequency without doubling frequency on each 1/2 H Bridge.

copyTop=OCR4C+(TC4H<<8); // Have to debug and look at this...


// do a demo sweep starting at speed 0 forward with delay of Delay ms between setting next speed step.
uint16_t Delay=1;

for(int i=0; i<=MaxCompPWM; i++) {
Set_Input2(i);
_delay_ms(Delay);
}


    while(1)
    {

for(int i=-MaxCompPWM; i<=MaxCompPWM; i++) {
Set_Input2(i);
_delay_ms(Delay);
}

for(int i=MaxCompPWM-1; i>=-MaxCompPWM+1; i--) {
Set_Input2(i);
_delay_ms(Delay);
}

PORTB ^=((1<<PB0)|(1<<PB4)); // only as debug signal a toggle of the RX LED
    }
}

Go Up