Reputation: 1826
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
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
Upvotes: 0
Views: 1200
Reputation: 148
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.
Upvotes: 1