0
votes

I'm having a look at fiddling the registers in assembly programming for the AVR family, on an Arduino UNO board, with its standard bootloader (avra+avrdude).

I'm having trouble with the Timer0. I've reduced the problem to a short program wich is supposed to

  • put a red LED on (on B5 = pin 13)
  • configure the timer0 for normal mode, prescale 1024
  • set the counter to 0
  • enter a loop, and escapes when counter is over 200
  • and then puts a green Led on (on B4).

Symptom: green led never turns on. With some other values than 200, turns on after a random duration (seconds).

Here is the code :

    .include "./m328Pdef.inc"

    .EQU ROUGE = 0b0100000   ; red
    .EQU VERT  = 0b0010000   ; green


main:   
    ldi r16,ROUGE+VERT      ; pins activated
    out DDRB,r16
    ldi r16,ROUGE           ; red on
    out portB,r16

    ;; configure timer
    lds r16,TCCR0B
    andi r16,0b11111000
    ori r16,0b00000101      ; prescale 1024
    sts TCCR0B,r16

    ldi r16,0       ; count is  0
    sts TCNT0,r16

loop:   
    lds r16,TCNT0            
    cpi r16,100
    brlo loop

    ldi r16,VERT     ; green on
    out PortB,r16

z:
    nop
    rjmp z

The same programs seems to work correctly with the Timer1 and its associated registers.

What's wrong? Some interference with the bootloader?

EDIT the hex file :

:020000020000FC
:1000000000E304B900E205B900912500087F05600E
:100010000093250000E0009326000091260004369E
:0A002000E0F300E105B90000FECF97
:00000001FF

Compiled by : avra bug0.asm

Upload :

avrdude -q -V -D -p atmega328p -C /etc/avrdude.conf \
    -c arduino -b 115200 -P /dev/ttyACM0 \
    -U flash:w:bug0.hex:i
1
The code seems OK. Can you include the hex/srec/whatever you use to flash the board?Aleksander Z.
You can take a look at the source of Arduino UNO standard bootloader here: github.com/arduino/Arduino/blob/master/hardware/arduino/avr/…. The bootloader uses timer1, timer0 is left untouched.Aleksander Z.
edit : hex and compile/upload commands added to questionMichel Billaud

1 Answers

1
votes

The problem is that constants: DDRB, PORTB, TCCR0B, TCNT0, etc. evaluate to I/O addresses of given SFRs (it's OK to use them with in/out) but you use TCCR0B and TCNT0 with lds/sts which expect their operands to containt data space addresses.

The solution is to either use in/out with TCCR0B and TCNT0 -- it's OK, because these registers belong to I/O registers (as opposed to extended I/O registers which must be accessed using their data space addresses):

    .include "./m328Pdef.inc"

    .EQU ROUGE = 0b0100000   ; red
    .EQU VERT  = 0b0010000   ; green


main:
    ldi r16,ROUGE+VERT      ; pins activated
    out DDRB,r16
    ldi r16,ROUGE           ; red on
    out portB,r16

    ;; configure timer
    in r16,TCCR0B
    andi r16,0b11111000
    ori r16,0b00000101      ; prescale 1024
    out TCCR0B,r16

    ldi r16,0       ; count is  0
    out TCNT0,r16

loop:
    in r16,TCNT0
    cpi r16,100
    brlo loop

    ldi r16,VERT     ; green on
    out PortB,r16

z:
    nop
    rjmp z

or to make this I/O addresses into data space addresses:

    .include "./m328Pdef.inc"

    .EQU ROUGE = 0b0100000   ; red
    .EQU VERT  = 0b0010000   ; green


main:
    ldi r16,ROUGE+VERT      ; pins activated
    out DDRB,r16
    ldi r16,ROUGE           ; red on
    out portB,r16

    ;; configure timer
    lds r16,TCCR0B+0x20
    andi r16,0b11111000
    ori r16,0b00000101      ; prescale 1024
    sts TCCR0B+0x20,r16

    ldi r16,0       ; count is  0
    sts TCNT0+0x20,r16

loop:
    lds r16,TCNT0+0x20
    cpi r16,100
    brlo loop

    ldi r16,VERT     ; green on
    out PortB,r16

z:
    nop
    rjmp z

For information on why you have to add 0x20 to these constants, see chapter 8.3 SRAM Data Memory on page 19 (especially the figure) of m328p datasheet and this bug report.


Why the original code worked with timer1?

Timer1 registers are located in extended I/O space and avra probably substitutes their names with extended I/O space addresses (as they doesn't even have I/O space addresses).


Why the original code behaved erraticaly with timer0?

One can write lds r16,TCNT0 as lds r16,PINC+0x20, reading the state of floating pins yields (more or less) random result.


To see what avra did with your code I used the command avr-objdump -b ihex -m avr5 -D *.hex, it shows what is actually written to the microcontroller.

For SFR addresses, see chapter 36. Register Summary of the aforementioned atmega328p datasheet.

You may need to install avr-gcc or avr-binutils to be able to use this command.