1 | .include "c:\avrtools\appnotes\m8def.inc"
|
2 |
|
3 | .def temp = r16
|
4 | .def page = r17
|
5 | .def checksum = r19
|
6 | .def counter = r18
|
7 | .def checksum2 = r20
|
8 | .def spmreg = r21
|
9 |
|
10 | ;**********************************************************
|
11 | ;* I²C-Bootloader für ATMega8 *
|
12 | ;* Möglichkeit 1: *
|
13 | ;* Der Bootloader wird durch einen speziellen Befehl *
|
14 | ;* vom Anwenderprogramm aufgerufen. Falls das Programm *
|
15 | ;* Fehlerhaft ist, läßt sich aber nichts mehr booten. *
|
16 | ;* Möglichkeit 2: *
|
17 | ;* Durch programmieren des BOOTRST-Bits wird der *
|
18 | ;* Bootloader grundsätzlich gestartet. Ein General Call *
|
19 | ;* (Adresse $00) startet das Anwendungsprogramm. *
|
20 | ;* (Sprung zu Adresse $0000) *
|
21 | ;* Angesprochen wird der Bootloader über die I²C-Adresse *
|
22 | ;* $F0. Es werden immer 66 Bytes erwartet, wenn ein *
|
23 | ;* Schreibbefehl über Adresse $F0 gesendet wird: *
|
24 | ;* 1. Byte = Page-Nr. (0...127) *
|
25 | ;* 64 Datenbytes *
|
26 | ;* Prüfsumme (XOR) *
|
27 | ;* Mit Adresse F1 wird ein Lesezugriff ausgelöst, hiermit *
|
28 | ;* kann die Prüfsumme überprüft werden (Rückgabe 1 Byte) *
|
29 | ;* BOOTSZ0/BOOTSZ1 = 0, Boot-Sektor 128 Bytes *
|
30 | ;**********************************************************
|
31 |
|
32 |
|
33 | .org $0F80
|
34 | boot4:
|
35 | ldi temp,low(ramend) ;Den Stackpointer auf das Ende des RAM setzen
|
36 | out SPL,temp
|
37 | ldi temp,high(ramend)
|
38 | out SPH,temp
|
39 |
|
40 | ldi checksum2,$AA ;Initialisieren des I²C-Rückgabewertes auf einen Defaultwert
|
41 |
|
42 | twi_init:
|
43 | ldi temp,$D0 ;minimalste Geschwindigkeit auswählen (hier entsprechende
|
44 | ;Änderungen vornehmen, wenns schneller gehen soll).
|
45 | ;Allerdings sollte diese Einstelleung keinen Einfluß haben,
|
46 | ;da der Bootloader nur im Slave-Modus arbeitet
|
47 | out TWBR,temp
|
48 | ldi temp,(1<<TWPS0)|(1<<TWPS1) ;prescaler 64
|
49 | out TWSR,temp
|
50 |
|
51 | ldi temp,(1<<TWINT)|(1<<TWEN)|(1<<TWEA)
|
52 | out TWCR,temp ;I²C-Bus initialisieren (TWINT-Flag löschen, einschalten, ACK
|
53 | ;senden bei eigener Adressierung)
|
54 |
|
55 | ldi temp,$F1
|
56 | out TWAR,temp ;Adress-Register mit der Slave-Adresse definieren,
|
57 | ;General Call (Adresse $00) ebenfalls akzeptieren
|
58 |
|
59 | wait_i2c:
|
60 | in temp,TWCR ;auf eine I²C-Datenübertragung warten
|
61 | sbrs temp,TWINT
|
62 | rjmp wait_i2c
|
63 |
|
64 | in temp,TWSR ;Status-Byte lesen
|
65 | andi temp,$F8 ;Status-Code isolieren
|
66 | cpi temp,$60
|
67 | breq slaw ;die eigene Adresse+W wurde empfangen
|
68 |
|
69 | cpi temp,$70
|
70 | breq gencw ;ein "General Call" (Adresse 0) wurde empfangen
|
71 |
|
72 | cpi temp,$A8
|
73 | breq slar ;eigene Adresse +R wurde empfangen
|
74 |
|
75 | rjmp boot4 ;Boot-Reset (nächste Übertragung abwarten)
|
76 |
|
77 | ;* SLAW = schreibender Zugriff
|
78 | ;* das nächste Byte ist bereits die Page-Nummer
|
79 |
|
80 | slaw:
|
81 | rcall get_i2c ;ein Byte vom I²C-Bus lesen
|
82 | brcs boot4 ;falls unerwartet ein Stop-Bit empfangen wurde, Neustart
|
83 | mov page,temp ;Page-Nummer in "page" speichern
|
84 |
|
85 | ldi counter,$40 ;64 Bytes Daten lesen
|
86 | ldi yh,$01 ;Page 1 des SRAM verwenden
|
87 | clr yl
|
88 | clr checksum ;Register für die Prüfsumme initialisieren
|
89 | slaw_loop:
|
90 | rcall get_i2c ;Datenbyte lesen
|
91 | brcs boot4 ;falls unerwartet ein Stop-Bit kommt Neustart des Boot-Programms
|
92 | st y+,temp ;aktuelles Datenbyte in den SRAM schreiben
|
93 | eor checksum,temp ;Prüfsumme aktualisieren
|
94 | dec counter ;Zähler aktualisieren
|
95 | brne slaw_loop ;wiederholen, bis 64 Bytes übertragen wurden
|
96 |
|
97 | rcall get_i2c ;Prüfsumme vom Master lesen
|
98 | mov checksum2,temp ;speichern in checksum2 (für I²C-Ausgabe)
|
99 | cp checksum2,checksum ;die übertragene mit der selbst berechneten Prüfsumme vergleichen
|
100 |
|
101 | rcall flash_write
|
102 |
|
103 | rjmp wait_i2c ;auf die nächste Übertragung warten
|
104 |
|
105 | ;* SLAR = lesender Zugriff
|
106 | ;* Die selbst berechnete Prüfsumme wird zurückgesendet, kann zur Kontrolle
|
107 | ;* verwendet werden.
|
108 |
|
109 | slar:
|
110 | out TWDR,checksum2 ;die Prüfsumme ins I²C-Datenregister schreiben
|
111 | in temp,TWCR
|
112 | out TWCR,temp ;das TWINT-Flag löschen
|
113 |
|
114 | slar_wait:
|
115 | in temp,TWCR ;warten, bis der Schreibvorgang beendet ist
|
116 | sbrs temp,TWINT
|
117 | rjmp slar_wait
|
118 | in temp,TWCR ;das Stop-Bit verarbeiten
|
119 | out TWCR,temp
|
120 | rjmp wait_i2c ;zurück zur Hauptschleife
|
121 | ;An diesem Punkt kann es zu unvorhersehbaren Fehlern kommen, falls der Master
|
122 | ;mehr als ein Byte liest, da das Stop-Bit nicht überprüft wird.
|
123 |
|
124 | ;* General Call = Adresse 0. Das I²C-Device geht nach dem Reset sofort in den Boot-Modus,
|
125 | ;* ein senden des General Call startet das Anwenderprogramm. Dadurch können mit einem
|
126 | ;* General Call alle Devices auf dem Bus mit einem Befehl gezielt gestartet werden.
|
127 |
|
128 | gencw:
|
129 | rcall get_i2c ;übertragenes Datenbyte lesen
|
130 | brcc gencw ;wiederholen, bis ein Stop-Bit empfangen wird
|
131 | clr temp
|
132 | out TWCR,temp ;Die I²C-Schnittstelle abschalten
|
133 | jmp $0000 ;reset, Sprung ins Benutzer-Programm
|
134 |
|
135 | ;Subroutine get_i2c liest ein Byte vom I²C-Bus und gibt es in "temp" zurück.
|
136 | ;Ferner wird das Carry-Flag gesetzt, falls ein Stop-Bit empfangen wurde.
|
137 |
|
138 | get_i2c:
|
139 | in temp,TWCR
|
140 | out TWCR,temp ;das TWINT-Flag löschen
|
141 | get_i2c1:
|
142 | in temp,TWCR ;warten, bis das TWINT-Flag wieder gesetzt wird
|
143 | sbrs temp,TWINT ;falls =1, dann Sprung
|
144 | rjmp get_i2c1 ;ansonsten warten
|
145 | in temp,TWSR ;Status-Register abfragen
|
146 | andi temp,$F8 ;Status-Code isolieren
|
147 |
|
148 | cpi temp,$A0 ;wurde ein Stop-Bit empfangen?
|
149 | breq get_i2c2 ;Sprung, falls ja
|
150 |
|
151 | in temp,TWDR ;Datenbyte lesen
|
152 | clc ;das Carry-Flag löschen (Byte empfangen)
|
153 | ret
|
154 | get_i2c2: ;Stop-Bit empfangen
|
155 | in temp,TWCR
|
156 | out TWCR,temp ;Das TWINT-Bit nochmals löschen
|
157 | sec ;das Carry-Flag setzen (Stop-Bit empfangen)
|
158 | ret
|
159 |
|
160 |
|
161 | ;***************************************************************************
|
162 | ;* Per I²C wurde eine Page (64 Bytes) Daten in den SRAM ($0100...$013F) *
|
163 | ;* übertragen. In "page" ist die Nummer der Page (0..127) abgelegt. *
|
164 | ;* Der Boot-Bereich beginnt ab $0F80, d.h. die Pages 124..127 dürfen *
|
165 | ;* nicht programmiert werden, da sonst der Boot-Loader überschrieben *
|
166 | ;* wird. Eine Sicherheitsabfrage existiert nicht, kann aber leicht ein- *
|
167 | ;* gefügt werden. *
|
168 | ;***************************************************************************
|
169 |
|
170 | flash_write:
|
171 | ldi yh,$01 ;SRAM-Adresse $0100 auswählen
|
172 | clr yl
|
173 |
|
174 | clr zl ;Low-Byte des Z-Registers löschen
|
175 | mov zh,page
|
176 | lsr zh ;die Page-Nummer wurde ins High-Byte
|
177 | ror zl ;übertragen. Durch 2x rechts shiften
|
178 | lsr zh ;wird die Page-Nummer mit 64 multipliziert
|
179 | ror zl ;in zh:zl ist nun die Byte-Adresse der Page
|
180 |
|
181 | push zl ;die berechnete Adresse für später speichern
|
182 | push zh
|
183 |
|
184 | ldi spmreg,(1<<PGERS)|(1<<SPMEN) ;die angewählte Page löschen, bevor sie
|
185 | rcall schreib_spm ;beschrieben wird.
|
186 |
|
187 | ldi counter,$20 ;Größe der Page, 32 Words
|
188 | flash_write_loop:
|
189 | ld r0,y+ ;Datenbytes aus dem SRAM lesen
|
190 | ld r1,y+
|
191 |
|
192 | ldi spmreg,(1<<SPMEN) ;spmreg mit dem Befehl laden
|
193 | rcall schreib_spm ;Befehl ausführen
|
194 | adiw ZH:ZL,2 ;Das Adressierungswort (zh:zl) korrigieren
|
195 | dec counter ;den Zähler aktualisieren
|
196 | brne flash_write_loop ;wiederholen, bis die komplette Page übertragen wurde
|
197 |
|
198 | pop zh ;Die Adresse der Page wiederherstellen
|
199 | pop zl
|
200 |
|
201 | ldi spmreg,(1<<PGWRT)|(1<<SPMEN) ;Befehl für Flash beschreiben
|
202 | rcall schreib_spm ;ausführen
|
203 |
|
204 | ret ;Subroutine beenden
|
205 |
|
206 | schreib_spm:
|
207 | in temp,SPMCR ;Den Status der SPM-Schnittstelle abfragen
|
208 | sbrc temp,SPMEN ;ist der letzte Befehl ausgeführt?
|
209 | rjmp schreib_spm ;falls nein, dann warten
|
210 |
|
211 | out SPMCR,spmreg ;Befehlsregister schreiben
|
212 | spm ;Befehl ausführen
|
213 | ret
|