longchute

about

ATmega32 Bare Metal Boot

23 Oct 2013

Bare metal boot in AVR assembly without avr-libc. Demonstrates interrupt vectors, enabling the stack, the watchdog timer, and blinking an LED connected to PORTB, pin 0.

On boot, the LED will cycle three times quickly, then begin blinking. After approximately 2.2 seconds, it will be reset by the Watchdog Timer because wdr is not called.

To build and program:

$ avr-as -mmcu=avr5 -o blink.o blink.s
$ avr-ld blink.0
$ avr-objcopy -j .text -O binary a.out blink.bin
$ sudo avrdude -c usbasp -p m32 -P usb -U flash:w:blink.bin:r

For more information, see:

blink.s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
    ;   Interrupt Handlers
    jmp boot            ;   RESET
    jmp ignore_int      ;   INT0
    jmp ignore_int      ;   INT1
    jmp ignore_int      ;   INT2
    jmp ignore_int      ;   TIMER2 COMP
    jmp ignore_int      ;   TIMER2 OVF
    jmp ignore_int      ;   TIMER1 CAPT
    jmp ignore_int      ;   TIMER1 COMPA
    jmp ignore_int      ;   TIMER1 COMPB
    jmp ignore_int      ;   TIMER1 OVF
    jmp ignore_int      ;   TIMER0 COMP
    jmp ignore_int      ;   TIMER0 OVF
    jmp ignore_int      ;   SPI, STC
    jmp ignore_int      ;   USART, RXC
    jmp ignore_int      ;   USART, UDRE
    jmp ignore_int      ;   USART, TXC
    jmp ignore_int      ;   ADC
    jmp ignore_int      ;   EE_RDY
    jmp ignore_int      ;   ANA_COMP
    jmp ignore_int      ;   TWI
    jmp ignore_int      ;   SPM_RDY
    
    ;   On RESET
    boot:
        enable_stack:
            ldi r16, 0x08
            out 0x3E, r16
            ldi r16, 0x5F
            out 0x3D, r16
        call boot_finish
        call start
    
    ;   Dummy interrupt handler (should be the 1st thing after `boot`.)
    ignore_int:
        reti
    
    ;   Additional boot chores post-stack-initialization, but before `start`
    boot_finish:
        call enable_led
        call flash_led
        call enable_watchdog
        ret
    
    ;   Main program
    start:
        main_loop:
            call toggle_led
            call delay
            rjmp main_loop
        ;   For completeness, you can return to `boot`, which drops through to `ignore_int`, calls 
        ;   `reti` and resets the processor. This is never executed.
        ret
    
    ;  LED "device driver" (reserves r26) for LED on PORTB, pin 0
    enable_led:
        push r16
        ldi r16, 0x01
        out 0x17, r16
        clr r26
        pop r16
        ret
    
    toggle_led:
        cpi r26, 0x01
        breq call_off
        call led_on
        ret
        call_off:
            call led_off
            ret 
    
    led_on:
        ldi r26, 0x01
        out 0x18, r26
        ret
    
    led_off:
        clr r26 
        out 0x18, r26
        ret
       
    ;   Library functions
    flash_led:
        push r16
        push r17
        push r18
        ldi r16, 0x06
        loop_a:
            call toggle_led
            clr r17
            loop_b:
                clr r18
                loop_c:
                    dec r18
                    brne loop_c
                dec r17
                brne loop_b
            dec r16
            brne loop_a
        pop r18
        pop r17
        pop r16
        ret
    
    ;   Enables the Watchdog Timer with a roughly 2.2 second timeout
    enable_watchdog:
        push r16
        ldi r16, 0x0F
        out 0x21, r16
        pop r16
        ret
    
    delay_small:
        push r16
        clr r16
        delay_small_while:
            cpi r16, 0x10
            breq end_delay_small_while
            inc r16
            rjmp delay_small_while
        end_delay_small_while:
        pop r16
        ret
    
    delay:
        push r16
        push r17
        clr r16
        while_a:
            cpi r16, 0x20
            breq end_while_a 
            inc r16
            clr r17
            while_b:
                cpi r17, 0xFF
                breq end_while_b
                inc r17
                call delay_small
                rjmp while_b
            end_while_b:
            rjmp while_a
        end_while_a:
        pop r17
        pop r16
        ret