need help: timer0 intr service on mega328p

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rookie alert; I've been dabbling for a month or so.  I have an UNO 3 board and have been able to run small demo program to blink the on-board led attached to PORTB.

I want to use an interrupt handler for timer0 to do this.   Somehow I don't have things set up correctly.  I'd appreciate any help.

 

Below is my main program and interrupt service routine.   I'm using a Makefile to flash the board.  The same Makefile was used on a "main only" program that was able to blink the led, so I think that part is OK.  I did verify (via "avr-objdump -d" that the interrupt handler is in the table).

 

main.c:

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

volatile uint8_t duty = 128;        /* duty cycle wrt 256 */

int main(void) {

  DDRB |= _BV(PORTB5);

  TCCR0A = 0;                /* no output pins, count 00-FF */
  TCCR0B = 0x05;            /* use clk_IO/1024 => 16kHz */
  OCR0A = 0xFF;                /* interrupt when == 0xFF */
  TIMSK0 |= _BV(OCIE0A);        /* enable timer0/A interrrupt */
  TIMSK0 |= _BV(TOIE0);            /* needed ? */
  sei();                /* enable interrupts (needed?) */
  PRR &= ~PRTIM0;            /* enable timer 0 */

  for (;;) {
    _delay_ms(500);
    duty += 32;
  }
}

 

 

intr.S:

    .equ PORTB,0x5
    .equ PORTB5,5

    .section .text
    .global __vector_7
__vector_7:
    sbi PORTB,PORTB5
    reti

 

 

interrupt table:

00000000 <__vectors>:
   0:    0c 94 34 00     jmp    0x68    ; 0x68 <__ctors_end>
   4:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
   8:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
   c:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  10:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  14:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  18:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  1c:    0c 94 4b 00     jmp    0x96    ; 0x96 <__vector_7>
  20:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  24:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  28:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  2c:    .........

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The interrupt service routine is simplified to just turn the led on.  Later I want to do more.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is no interrupt handler for the Timer-Overflow interrupt TIMSK0 |= _BV(TOIE0); /* needed ? */

Yes, you will need to do sei(); /* enable interrupts (needed?) */

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks.  And I added a _BV() to the PRR expression to get

    PRR &= ~_BV(PRTIM0);

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I found the problem.  I had set up the wrong interrupt vector.

 

To find this I removed the assembly routine and in the C code  I added ISR(TIMER0_COMPA_vect, ISR_BLOCK) { ... } and then checked the vector table dumped by avr-objdump -d to see

 

Disassembly of section .text:

00000000 <__vectors>:
   0:    0c 94 34 00     jmp    0x68    ; 0x68 <__ctors_end>
   4:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
   8:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
...

  30:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  34:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  38:    0c 94 4b 00     jmp    0x96    ; 0x96 <__vector_14>
  3c:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>
  40:    0c 94 49 00     jmp    0x92    ; 0x92 <__bad_interrupt>

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why on earth are you using the (internal) __vector_N names for your ISR in the .S file? Why not do it properly as shown in the user manual?

 

http://nongnu.org/avr-libc/user-...

 

That does things like

.global TIMER0_COMPA_vect
TIMER0_COMPA_vect:

Because it is .S not .s it will be pre processed and the above will expand out to __vector_14 or whatever.

Last Edited: Sun. Mar 17, 2019 - 03:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I was not using avr/io.h because I once got an error I did not understand and was nervous about not understanding where everything comes from.  I fill fix that.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do use avr/io.h but also DO use __SFR_OFFSET defined to 0. (makes life infinitely easier!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's my problem: I was getting out of range errors on

    in r22,SREG

but

    #define __SFR_OFFSET 0

fixed it.

 

Thanks much, clawson.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The alternative would have been LDS not IN (LDS addresses ARE offset by 0x20 as usually defined by __SFR_OFFSET) 

 

(The reason for this is that C originally does LDSs for everything but an optimization stage then reduces to IN and subtracts 0x20 when the target address is seen to be in range)

 

((But the Asm programmer wants to do this optimization by hand))

Last Edited: Sun. Mar 17, 2019 - 03:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
((But the Asm programmer wants to do this optimization by hand))

It is "interesting" that a C program needed a separate ASM source file for an ISR.  Is the whole objective to explore mixing C and ASM with that particular toolchain?  Why?  If not, what is the reason?  Surely, if one wants to be at a detail level such as shown, then one needs to understand__SFR_OFFSET and similar, and not just override to something less painful in this particular context.  (which could well "break" other lines that "used to work, and all I did was...")

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my experience banging on embedded systems, you have to know what's happening at the ASM level and sometimes you just need to do that.

 

Down the line I will need to do asm as I am looking into multi-tasking on avr chips where allowing every task 32 registers will be a waste of SRAM (for task stack).   Unless you know some way to restrict which registers gcc uses.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Multi-tasking does not have to mean saving all 32 registers. That only needs to happen if you want to do pre-emptive multitasking. Stick with cooperative or task-switching and you don't need to do anything as the compiler will do it all for you.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

MattRW wrote:
In my experience banging on embedded systems, you have to know what's happening at the ASM level and sometimes you just need to do that.

Exactly.  So how does blindly overriding part of the "system" header accomplish that?  [the blanket "but you learn so much more" doesn't really answer my question about the rationale for the ASM ISR, but that can be left for another discussion] 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.