Electronic – Simple AVR LED blink in assembly – why does this code not work

assemblyatmegaavrtimer

I've been trying to blink an LED with AVR assembly. I'm using an ATMega168 chip and avr-gcc toolchain. Here's my code:

.include "m168.h"

.global main

main:
    ldi r16, 0b00000001
    out DDRB,r16            ; Set PB0 to output
    out PORTB,r16           ; Set PB0 high
    ldi r16, 0b00000101
    out TCCR0B,r16          ; Set prescaler to 1024
loop:
    in r17, TCNT0           ; If the counter is >= 128,
    cpi r17, 128            ; branch to dim
    brge dim                ; otherwise continue to light
light:
    sbi PORTB, 0
    rjmp loop
dim:
    cbi PORTB,0
    rjmp loop

I figure that my LED should be on ~50% of the time (values 0-127) and off 50% of the time (values 128-255). But it doesn't light at all (visibly).

If I run the program though my head it goes like this.

  1. Counter TCNT0 starts at zero
  2. LED is on
  3. loop runs through without branching to dim
  4. light jumps back to loop

Steps 3 and 4 happen in a loop for all vales of TCNT0 where 0 <= TCNT0 <= 127, meaning the light is on all this time

  1. TCCR0B reaches 128, so branches to dim
  2. dim jumps back to loop

Steps 5 and 5 happen in a loop for all vales of TCNT0 where 128 <= TCNT0 <= 255, meaning the light is off all this time

  1. Eventually TCNT0 overflows, and we go back to the LED being on again. And so on…

Assuming my clock speed is 8Mhz (don't know how to find this in the datasheet), the timer increments once every 1024 clock cycles or roughly 4ms (10248 (1/8^6)). Which means my LED should be on for 512ms (128 * 4) and then off for 512ms.

Clearly I'm getting something wrong here or logic above isn't sound but I'm not sure where.

Edit:

If I set the comparison to compare with 255, it does blink. This confuses me even more!

Best Answer

brge is a signed greater-than-or-equal-to comparison. The 8-bit literal 128 is -128 in two's complement, which is the lowest possible value, so TCNT will always be more than it and the LED will remain off. Try using brsh instead if you want to use unsigned values.

Incidentally, the literal 255 is -1 in two's complement which is why your code works correctly when you use it instead.