Pages: 1 [2] 3   Go Down
Author Topic: interrupt timer problem  (Read 3402 times)
0 Members and 1 Guest are viewing this topic.
Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Having said this, in the first example, no one actually writes the sei() instruction in any of your programs. Just a hunch, but I may be right. Can someone have a look at the IDE internals to look for this?

Init sets interrupts up:

Code:
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
...
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

HardwareSerial.cpp has some pretty dodgy code in it:

Code:
struct ring_buffer
{
  unsigned char buffer[SERIAL_BUFFER_SIZE];
  volatile int head;
  volatile int tail;
};

...

inline void store_char(unsigned char c, ring_buffer *buffer)
{
  int i = (unsigned int)(buffer->head + 1) % SERIAL_BUFFER_SIZE;

  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
    buffer->buffer[buffer->head] = c;
    buffer->head = i;
  }
}

They are using 2-byte fields for the ring buffer, but no protection when accessing them (eg. clearing interrupts), so from time to time they will become corrupted. I would not use this for production code.
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

For example:

Code:
  // if we should be storing the received character into the location
  // just before the tail (meaning that the head would advance to the
  // current location of the tail), we're about to overflow the buffer
  // and so we don't write the character or advance the head.
  if (i != buffer->tail) {
 4ba: 80 91 6a 01 lds r24, 0x016A     // started accessing the buffer
 4be: 90 91 6b 01 lds r25, 0x016B     // <--- hope no interrupt here
 4c2: 28 17        cp r18, r24  // <--- or here
 4c4: 39 07        cpc r19, r25  // <--- or here
 4c6: 59 f0        breq .+22      ; 0x4de <__vector_18+0x50>
Logged


'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3282
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm having a lazy morning at work...

So, sei() is actually called before setup(), so that is not the reason why the string isn't printed.

One thing I have noticed in wiring.c is this:

Code:
// timers 1 and 2 are used for phase-correct hardware pwm
// this is better for motors as it ensures an even waveform
// note, however, that fast pwm mode can achieve a frequency of up
// 8 MHz (with a 16 MHz clock) at 50% duty cycle

#if defined(TCCR1B) && defined(CS11) && defined(CS10)
TCCR1B = 0;

// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
#endif
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
sbi(TCCR1, CS11);
#if F_CPU >= 8000000L
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
#elif defined(TCCR1)
#warning this needs to be finished
#endif

so there are some settings done beforehand that should be override before setting all up.

My suggestion is to clear all the TCC1 registers and then set them up.

I'd be happy to set the timers up, but I have no idea what is intended with it. So I guess that's a bit hard.

« Last Edit: January 09, 2012, 09:12:11 pm by bubulindo » Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4089
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm not sure why the timer keeps firing, but I am no expert on timers. A different mode, perhaps?

It just keeps running, nothing ever stopped it, but this is not a bad thing or even unusual. But the interrupt should not be occurring every 2.25µs, it should be every every 62.5µs with the original 1000 in OCR1A, or 187.5µs with 3000. bubulindo's suggestion to clear all the registers first (not knowing what init does to them) is a very good one, but it seems to me that's been done. Hmmmm...
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

The string isn't printed because it is madly processing interrupts, pure and simple. My timings show that it wasn't doing anything except processing interrupts. I can't quite account for why the LEDs lit up, except to say that after an interrupt returns, one instruction is always processed. So it would limp on and light up the LEDs. However once that one instruction is processed, it then handles the highest-priority interrupt which was the compare interrupt in preference to the serial port interrupt.
Logged


Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

But the interrupt should not be occurring every 2.25µs, it should be every every 62.5µs with the original 1000 in OCR1A, or 187.5µs with 3000.

That's the bit that puzzles me, but my eyes glaze over a bit when I read the datasheet part about the timers. I think it says in that mode once it hits the top it reverses and starts counting down. So maybe it hits 3000 (or whatever), reverses, finds 3000 again, reverses, and so on.
Logged


'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3282
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Do note that the overflow interrupt is activated... so you probably have an interrupt for the overflow (on the top) and compare interrupt (on the top again).

Wrong file.

Try this:

Code:
ISR(TIMER1_COMPA_vect)
{
  PORTB = 0x20;
  done = true;
  PORTB = 0;
}

void setup() {
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1C = 0;
  TIMSK1 = 0;

  for (byte i = 2; i <= 13; i++)
    pinMode (i, OUTPUT);
  Serial.begin(115200);
  Serial.println ("A");
  Serial.println ("B");
  OCR1A = 3000;   // compare A register value
  TCCR1A = 0;  // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  TIMSK1 = _BV (OCIE1A); // interrupt on Compare A Match

  while (!done)
    {}   // wait for interrupt
    
  
  Serial.println ("C");
  Serial.println ("D");
  Serial.println("done");
  for (byte i = 2; i <= 12; i++)
    {
    digitalWrite (i, HIGH);
    delay (200);
    }
}

void loop() { }


What about not using the Serial??? Has anyone tried that and see if it makes a difference`?
« Last Edit: January 09, 2012, 09:49:50 pm by bubulindo » Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4089
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Here's the fix. I started with the code in Nick's post here.

Simply interchange the two lines:
Code:
OCR1A = 1000;   // compare A register value
  TCCR1A = 0;  // normal operation

Works then in either 0022 or 1.0.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3282
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Simply interchange the two lines:
Code:
OCR1A = 1000;   // compare A register value
  TCCR1A = 0;  // normal operation
so your code is now:

Code:
 TCCR1A = 0;  // normal operation
OCR1A = 1000;   // compare A register value
  ????

This gives access to OCR1A instantly, whilst with the other option it would be buffered. I see...
You set the value in OCR, it goes to the buffer and you then disable the WGM and the buffer remains where it is without ever being updated.
This is why I don't use the Arduino IDE. LOL Good troubleshooting exercise though. smiley
Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 495
Posts: 19026
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks, Jack. smiley

Amended code now:

Code:
volatile boolean done = false;

ISR(TIMER1_COMPA_vect)
{
  PORTB = 0x20;   // pin 13 on
  done = true;
  PORTB = 0;
}

void setup() {
  Serial.begin(115200);
  pinMode (13, OUTPUT);
  
  // set up Timer 1
  TCCR1A = 0;      // normal operation
  TCCR1B = _BV(WGM12) | _BV(CS10);   // CTC, no prescaling
  OCR1A =  1000;     // compare A register value
  TIMSK1 = _BV (OCIE1A);             // interrupt on Compare A Match

  while (!done)
    {}   // wait for interrupt
      
  Serial.println("done");
}

void loop() { }

On the logic analyzer, pin 13 was toggled every 62.5 uS which is what you predicted before.
« Last Edit: January 09, 2012, 10:46:09 pm by Nick Gammon » Logged


Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4089
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Simply interchange the two lines:
Code:
OCR1A = 1000;   // compare A register value
  TCCR1A = 0;  // normal operation
so your code is now:

Code:
 TCCR1A = 0;  // normal operation
OCR1A = 1000;   // compare A register value
  ????

Yes, correct.

Quote
This gives access to OCR1A instantly, whilst with the other option it would be buffered. I see...
You set the value in OCR, it goes to the buffer and you then disable the WGM and the buffer remains where it is without ever being updated.
This is why I don't use the Arduino IDE. LOL Good troubleshooting exercise though. smiley

I'm slightly embarrassed because I think I may have solved this very problem once before. However, I'm not sure I'd read the datasheet close enough to understand why, so thank you for that!

The clue was when I replaced loop() with the following, millis() was not incrementing in 0022, so I knew then that the problem existed both in 0022 and in 1.0, it's just that the symptom with the original code was different, perhaps due to the changes in hardware serial.

Code:
void loop(void)
{
    Serial.print(millis(), DEC);
    Serial.print(' ');
    Serial.println("loop");
    delay(1000);
}

Don't think it would have helped in this case, but I'm also in the habit of setting TCCR1B last, as this sets the prescaler bits and starts the timer running. Of course Arduino initialization had already started the timer, so in some cases it may also be helpful to stop it via TCCR1B = 0.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3282
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm slightly embarrassed because I think I may have solved this very problem once before. However, I'm not sure I'd read the datasheet close enough to understand why, so thank you for that!

I had read it before... but never quite connected the dots. The new OCR1A would only be moved out of the buffer once the count folded back to 0. Since the mode was disabled right afterwards this will never happen. Come to think of it. with the right set of instructions taking the exact time for a folding to happen, this problem would never appear. LOL

Don't think it would have helped in this case, but I'm also in the habit of setting TCCR1B last, as this sets the prescaler bits and starts the timer running. Of course Arduino initialization had already started the timer, so in some cases it may also be helpful to stop it via TCCR1B = 0.

I do the same too. Set everything up, then start the timer.
Maybe there should be an entry on a wiki or something regarding these things when setting up timers/(include peripheral). Or, even more helpful, a dependencies table. Imagine that the user wants to use this timer and also some analogWrite. It might not be possible. This is more important if a lot of the hardware is running interrupts.
Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4089
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

But the interrupt should not be occurring every 2.25µs, it should be every every 62.5µs with the original 1000 in OCR1A, or 187.5µs with 3000.

That's the bit that puzzles me, but my eyes glaze over a bit when I read the datasheet part about the timers. I think it says in that mode once it hits the top it reverses and starts counting down. So maybe it hits 3000 (or whatever), reverses, finds 3000 again, reverses, and so on.

Arduino sets the timer to 8-bit phase-correct PWM mode. In this mode, updates to the OCR regs are buffered until the counter hits top or bottom. So the change never makes it to the register, which originally contains zero. Next we switch to CTC mode (which by the way counts up only, instead of up-then-down as with phase-correct PWM, but that's not terribly important here), which clears the timer when it matches OCR1A, and enable the associated interrupt! Quite the loop created there!

Hey, anyone who can figure out interrupts so well can easily conquer the timers! Here is a great paper that got me started, I had the same glaze going with the datasheet before I read it. I'm a guy that's got to get some hands on too, so once I read that paper and tried a few experiments I was a lot more comfortable. Still I learned something here, not having played much with PWM, I hadn't read the part about the OCR reg buffering.
Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Grand Blanc, MI, USA
Offline Offline
Faraday Member
**
Karma: 95
Posts: 4089
CODE is a mass noun and should not be used in the plural or with an indefinite article.
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Come to think of it. with the right set of instructions taking the exact time for a folding to happen, this problem would never appear. LOL

Yes! I thought the same! Ha!!!

Quote
Maybe there should be an entry on a wiki or something regarding these things when setting up timers/(include peripheral). Or, even more helpful, a dependencies table. Imagine that the user wants to use this timer and also some analogWrite. It might not be possible. This is more important if a lot of the hardware is running interrupts.

Good idea! I continually find myself digging back through links and notes to remember which functionality uses which timers, etc. so that I don't create conflicts.
« Last Edit: January 09, 2012, 10:53:41 pm by Jack Christensen » Logged

MCP79411/12 RTC ... "One Million Ohms" ATtiny kit ... available at http://www.tindie.com/stores/JChristensen/

Pages: 1 [2] 3   Go Up
Jump to: