Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-devel / 2015-11

AVR embedded Atmega 328p interrupts

6 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Andrew Haines

Hi, I am experimenting with the avr embedded compiler from trunk and am trying to set an interrupt. I am not sure how to do it. The interrupt I want to change is TIMER0_COMPA_ISR.

I see in the startup code there is:
.weak TIMER0_COMPA_ISR

and later there is:
.set TIMER0_COMPA_ISR, Default_IRQ_handler

I have in my test project added a procedure:
procedure TIMER0_overflow; public name 'TIMER0_COMPA_ISR';
begin
   ToggleLED;
end;

OK I'll just include my test program. I am using an arduino duemilanove clone. I got most of the code from:
https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328

Anyway there is no LED visible. I have verified using a delay in the main loop toggling the LED works.

What am I not doing, or doing wrong? And btw it's awesome that I can do anything at all on this controller!

Thanks,

Andrew

program test;
{$mode objfpc}{$H-}
{uses
   ATmega328P;}
const
   PIN13 = $20;
   WGM01 = 2;
   CS02 = 2;
procedure ToggleLED;
begin
   PORTB := PORTB xor PIN13;
end;
procedure TIMER0_overflow; public name 'TIMER0_COMPA_ISR';
begin
   ToggleLED;
end;
procedure TimerInit;
begin
   TIMSK0:= 1 shl TOIE0; // 1
   TCNT0:=0;
   TCCR0A := TCCR0A or 1 shl WGM01;
   TCCR0B := TCCR0B or 1 shl CS02; // 1 shl 2 = 4
   OCR0A:=$F9;
   TIMSK0:= TIMSK0 or 1 shl OCIE0A;
   asm
     sei;
   end;
end;
begin
   DDRB := DDRB or $FF;// PIN13;
   TimerInit;
   while True do
   begin
     asm
      nop;
     end;
   end;
end.

Reply by email

Jeppe Johansen
to Andrew Haines

Not sure the timer initialization is right, but it looks ok.

One thing you need is the interrupt procedure directive:

procedure TIMER0_overflow; interrupt; public name 'TIMER0_COMPA_ISR';

Otherwise it'll probably reset at some point.

What compiler commandline are you using?

Best Regards,
Jeppe

Andrew Haines wrote 74 lines
Hi, I am experimenting with the avr embedded compiler from trunk and am trying to set an interrupt. I am not sure how to do it. The interrupt I want to change is TIMER0_COMPA_ISR. I see in the startup code there is: .weak TIMER0_COMPA_ISR and later there is: .set TIMER0_COMPA_ISR, Default_IRQ_handler I have in my test project added a procedure: procedure TIMER0_overflow; public name 'TIMER0_COMPA_ISR'; begin ToggleLED; end; OK I'll just include my test program. I am using an arduino duemilanove clone. I got most of the code from: https://sites.google.com/site/qeewiki/books/avr-guide/timers-on-the-atmega328 Anyway there is no LED visible. I have verified using a delay in the main loop toggling the LED works. What am I not doing, or doing wrong? And btw it's awesome that I can do anything at all on this controller! Thanks, Andrew program test; {$mode objfpc}{$H-} {uses ATmega328P;} const PIN13 = $20; WGM01 = 2; CS02 = 2; procedure ToggleLED; begin PORTB := PORTB xor PIN13; end; procedure TIMER0_overflow; public name 'TIMER0_COMPA_ISR'; begin ToggleLED; end; procedure TimerInit; begin TIMSK0:= 1 shl TOIE0; // 1 TCNT0:=0; TCCR0A := TCCR0A or 1 shl WGM01; TCCR0B := TCCR0B or 1 shl CS02; // 1 shl 2 = 4 OCR0A:=$F9; TIMSK0:= TIMSK0 or 1 shl OCIE0A; asm sei; end; end; begin DDRB := DDRB or $FF;// PIN13; TimerInit; while True do begin asm nop; end; end; end.

Reply by email

Andrew Haines
to Jeppe Johansen
Jeppe Johansen wrote 10 lines
Not sure the timer initialization is right, but it looks ok. One thing you need is the interrupt procedure directive: procedure TIMER0_overflow; interrupt; public name 'TIMER0_COMPA_ISR'; Otherwise it'll probably reset at some point. What compiler commandline are you using?

ppcrossavr -Tembedded -Pavr -WpATMEGA328P -XPavr- test.lpr

then after compile, a script with:
avrdude -F -vv -p atmega328p -c arduino -P /dev/ttyUSB2 -b57600 -D -U flash:w:test.hex || echo Failed to upload

Best Regards, Jeppe

I added the interrupt keyword with no effect. Also I added interrupt handlers for TIMER0_COMPB and TIMER0_OVF just in case.

It looks like the default handler enters an infinite loop. Afaict all unassigned interrupts go to the default handler. Maybe I need to assign every handler?

procedure Default_IRQ_handler; assembler; nostackframe; public name
'_Default_IRQ_handler';noreturn;
       asm
       .Lloop:
         jmp .Lloop
       end;

Regards,

Andrew

Reply by email

Andrew Haines
to Jeppe Johansen: Not sure the timer initialization is right, but it looks ok. One thing you need is the…
Jeppe Johansen wrote 8 lines
Not sure the timer initialization is right, but it looks ok. One thing you need is the interrupt procedure directive: procedure TIMER0_overflow; interrupt; public name 'TIMER0_COMPA_ISR'; Otherwise it'll probably reset at some point.
Ok I got it to work. I have an interrupt:
procedure TIMER0_COMPA; noreturn; public name 'TIMER0_COMPA_ISR';
begin
   ToggleLED;
end;
and the timer is setup with:
procedure TimerInit;
begin
   TCCR0A := TCCR0A or (1 shl WGM01);
   OCR0A := $F9;
   TIMSK0 := TIMSK0 or (1 shl OCIE0A);
   sei;
   TCCR0B := TCCR0B or 5;//scale  5 = Clock / 1024
end;

The keywords "interrupt" or "noreturn" seem to have the same effect but are mutually exclusive.

Without the interrupt or noreturn keyword, TIMER0_COMPA is only called once and presumably a crash or infinite loop starts somewhere. ( return is causing an error?)
With the interrupt or noreturn keyword and those timer settings, the led blinks at the edge of human detection. Success!

Setting interrupts for TIMER0_COMPB and TIMER0_OVC have no effect; they are never called. I'm probably not understanding some of how to configure timers.

Regards,

Andrew

Reply by email

Jeppe Johansen
to Andrew Haines
Andrew Haines wrote 42 lines
On 11/29/2015 02:13 PM, Jeppe Johansen wrote: > Not sure the timer initialization is right, but it looks ok. > > One thing you need is the interrupt procedure directive: > > procedure TIMER0_overflow; interrupt; public name 'TIMER0_COMPA_ISR'; > > Otherwise it'll probably reset at some point. > Ok I got it to work. I have an interrupt: procedure TIMER0_COMPA; noreturn; public name 'TIMER0_COMPA_ISR'; begin ToggleLED; end; and the timer is setup with: procedure TimerInit; begin TCCR0A := TCCR0A or (1 shl WGM01); OCR0A := $F9; TIMSK0 := TIMSK0 or (1 shl OCIE0A); sei; TCCR0B := TCCR0B or 5;//scale 5 = Clock / 1024 end; The keywords "interrupt" or "noreturn" seem to have the same effect but are mutually exclusive. Without the interrupt or noreturn keyword, TIMER0_COMPA is only called once and presumably a crash or infinite loop starts somewhere. ( return is causing an error?) With the interrupt or noreturn keyword and those timer settings, the led blinks at the edge of human detection. Success! Setting interrupts for TIMER0_COMPB and TIMER0_OVC have no effect; they are never called. I'm probably not understanding some of how to configure timers. Regards, Andrew

Don't use noreturn. It signals to the compiler that your function will not return (i.e. do an infinite loop).

Use the interrupt directive for interrupt handlers. That makes them emit the iret instruction instead of ret to return from the interrupt. If you don't use that when you return from the ISR it will not have interrupts enabled. That's why it only blinks once.

Only enable interrupts that you have written handlers for otherwise they end up in the default handler which is an infinite loop as you saw. TIMER0_COMPB and TIMER0_OVF are not called because you don't set OCIE0B or OCIE0A in TIMSK0.

Reply by email

Andrew Haines
to Jeppe Johansen
Jeppe Johansen wrote
Don't use noreturn. It signals to the compiler that your function will not return (i.e. do an infinite loop).

Ok, this is good to know.

Show quoted text 5 lines
Use the interrupt directive for interrupt handlers. That makes them emit the iret instruction instead of ret to return from the interrupt. If you don't use that when you return from the ISR it will not have interrupts enabled. That's why it only blinks once.

This is also good to know. Interrupts are new to me. On a side note the procedure is called repeatedly using the noreturn keyword, but I'll change it to interrupt.

I had not used "interrupt" because this warning was emitted when I used it: project1.lpr(139,25) Warning: Calling convention directive ignored: "OldFPCCall"

Show quoted text 5 lines
Only enable interrupts that you have written handlers for otherwise they end up in the default handler which is an infinite loop as you saw. TIMER0_COMPB and TIMER0_OVF are not called because you don't set OCIE0B or OCIE0A in TIMSK0.

This makes sense.

Thanks very much for your input!

Andrew

Reply by email