M8_boot2.asm


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