Timers/counters

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

I'm using a timer to wait for a predefined time. This works great in assembly, but how do I do this in C?

1 - Set up timer
2 - Enable timer interrupt
3 - Sleep
4 - Interrupt handler just returns.

Greetings,
Børge

My current assembly code for the mega16 looks like this:

wait_tdSSCK:ldi   temp, $FD     ; Timer counts up. 
            out   TCNT1H, temp  ; 243us at 3.686MHz=0x037F
            ldi   temp, $A0     ; Start at FFFF-037F=FC80 
            out   TCNT1L, temp  ; Set up initial timer value
            rcall wait_timer    ; Halt until timer int.
            ret

wait_timer: ldi   temp, $04     ; Enable t/c 1 overflow int.
            out   TIMSK, temp   ; Set up interrupt mask
            clr   temp          ; Set up TCCR1A=0x00 for normal 
            out   TCCR1A, temp 
            ldi   temp, $01     ; TCCR1B=0x01 for clk/1 
            out   TCCR1B, temp  ; This starts the timer!
            sei                 ; Set the global int. flag
            ldi   temp, $40     ; Set sleep enable bit
            out   MCUCR, temp   ; Idle: timers run, stop cpu
            sleep               ; Int. handler simply returns
            clr   temp          ; Disable int. and timers
            out   MCUCR, temp   ; Clear sleep enable bit
            out   TCCR1B, temp  ; This stops the timer!
            cli                 ; Clear global int. flag
            ret
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
wait_tdSSCK:ldi   temp, $FD     ; Timer counts up.
            out   TCNT1H, temp  ; 243us at 3.686MHz = 0x037F
            ldi   temp, $A0     ; Start it at 0xFFFF - 0x037F = 0xFC80
            out   TCNT1L, temp  ; Set up initial timer value
            rcall wait_timer    ; Halt processor until timer interrupt
            ret

Your code contradicts your comments. Not unusual for assembly code...
I've seen that before.

Curious, why aren't you using OCR1A instead?

Anyway, assuming the 243 µs are what you mean:

#define F_CPU 3686000UL

#include 

void wait_timer(void); // forward declaration

void
wait_tdSSCK(void)
{
  /* wait 243 µs */
  TCNT1 = -((uint16_t)(243e-6 * F_CPU));
  wait_timer();
}

Don't worry about the FP math, the compiler will optimize that away as
it is compile-time constant.

wait_timer: ldi   temp, $04     ; Enable t/c 1 overflow interrupt
            out   TIMSK, temp   ; Set up interrupt mask
            clr   temp          ; Set up TCCR1A=0x00 for normal mode
            out   TCCR1A, temp  ; Lots of bits to set up here...
            ldi   temp, $01     ; TCCR1B=0x01 for clk/1 prescale
            out   TCCR1B, temp  ; This starts the timer!
            sei                 ; Set the global interrupt flag
            ldi   temp, $40     ; Set sleep enable bit
            out   MCUCR, temp   ; Idle: stop cpu while timers run
            sleep               ; Int. handler simply returns
            clr   temp          ; Disable interrupts and timers
            out   MCUCR, temp   ; Clear sleep enable bit
            out   TCCR1B, temp  ; This stops the timer!
            cli                 ; Clear the global interrupt flag
            ret

Hmm, there's at least an interrupt routine missing, I'm adding one
below.

#include 
#include 

SIGNAL(SIG_OVERFLOW1)
{
  /* just wake up the MCU */
}

#include 

void
wait_timer(void)
{
  TIMSK = _BV(TOIE1);  /* enable t/c 1 overflow int */
  TCCR1A = 0;          /* t/c 1 normal mode */
  TCCR1B = _BV(CS10);  /* start timer 1 with prescaler 1 */
  sei();
  sleep_mode();        /* wait until interrupted */
  cli();
  TCCR1B = 0;          /* stop timer 1 */
}

Of course, I'd actually rather set a bit in the interrupt handler to
know that actually *this* interrupt had been triggered, and would put
the sleep_mode() call into the main loop. That way, other events
could still be processed.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

Thanks a lot for your help!

My compiler tries to compile the interrupt handle:this:

SIGNAL(SIG_OVERFLOW1) { 
  // Just wake up the MCU again
} 

I get the following warnings:

Compiling: mcu_atmega16.c
avr-gcc -c -mmcu=atmega16 -I. -gdwarf-2   -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=mcu_atmega16.lst  -std=gnu99 -Wp,-M,-MP,-MT,mcu_atmega16.o,-MF,.dep/mcu_atmega16.o.d mcu_atmega16.c -o mcu_atmega16.o 
mcu_atmega16.c:13: warning: return type defaults to `int'
mcu_atmega16.c:13: warning: function declaration isn't a prototype
mcu_atmega16.c: In function `SIGNAL':
mcu_atmega16.c:13: warning: type of "__vector_8" defaults to "int"
mcu_atmega16.c:13: warning: control reaches end of non-void function

Line 13 is the line where SIGNAL(...) occurs. Even the avr-libc Reference Manual tells me to make the SIGNAL handle just like you showed me, Jörg.

Greetings,

Børge

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

You forgot to #include .

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

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

I put some sample code in the Academy section. It may help to have a look at it. Sleep is not implemeted. ID 358