1 | #include <string.h>
|
2 | #include <avr/io.h>
|
3 | #include <avr/interrupt.h>
|
4 | #include <avr/pgmspace.h>
|
5 | #include <avr/boot.h>
|
6 |
|
7 | #include "uart.h"
|
8 | #include "hard_def.h"
|
9 |
|
10 | #ifndef F_CPU
|
11 | #define F_CPU 16e6
|
12 | #endif
|
13 |
|
14 | #include <util/delay.h>
|
15 |
|
16 | #define BOOT_UART_BAUD_RATE 9600 /* Baudrate */
|
17 | #define XON 17 /* XON Zeichen */
|
18 | #define XOFF 19 /* XOFF Zeichen */
|
19 | #define START_SIGN ':' /* Hex-Datei Zeilenstartzeichen */
|
20 |
|
21 | /* Zustände des Bootloader-Programms */
|
22 | #define BOOT_STATE_EXIT 0
|
23 | #define BOOT_STATE_PARSER 1
|
24 |
|
25 | /* Zustände des Hex-File-Parsers */
|
26 | #define PARSER_STATE_START 0
|
27 | #define PARSER_STATE_SIZE 1
|
28 | #define PARSER_STATE_ADDRESS 2
|
29 | #define PARSER_STATE_TYPE 3
|
30 | #define PARSER_STATE_DATA 4
|
31 | #define PARSER_STATE_CHECKSUM 5
|
32 | #define PARSER_STATE_ERROR 6
|
33 |
|
34 |
|
35 |
|
36 |
|
37 | void appStart(uint8_t rstFlags) {
|
38 | // save the reset flags in the designated register
|
39 | // This can be saved in a main program by putting code in .init0 (which
|
40 | // executes before normal c init code) to save R2 to a global variable.
|
41 | __asm__ __volatile__ ("mov r2, %0\n" :: "r" (rstFlags));
|
42 |
|
43 | __asm__ __volatile__ (
|
44 | #ifdef VIRTUAL_BOOT_PARTITION
|
45 | // Jump to WDT vector
|
46 | "ldi r30,4\n"
|
47 | "clr r31\n"
|
48 | #else
|
49 | // Jump to RST vector
|
50 | "clr r30\n"
|
51 | "clr r31\n"
|
52 | #endif
|
53 | "ijmp\n"
|
54 | );
|
55 | }
|
56 |
|
57 | void program_page (uint32_t page, uint8_t *buf)
|
58 | {
|
59 | uint16_t i;
|
60 | uint8_t sreg;
|
61 |
|
62 | /* Disable interrupts */
|
63 | sreg = SREG;
|
64 | cli();
|
65 |
|
66 | eeprom_busy_wait ();
|
67 |
|
68 | boot_page_erase (page);
|
69 | boot_spm_busy_wait (); /* Wait until the memory is erased. */
|
70 |
|
71 | for (i=0; i<SPM_PAGESIZE; i+=2)
|
72 | {
|
73 | /* Set up little-endian word. */
|
74 | uint16_t w = *buf++;
|
75 | w += (*buf++) << 8;
|
76 |
|
77 | boot_page_fill (page + i, w);
|
78 | }
|
79 |
|
80 | boot_page_write (page); /* Store buffer in flash page. */
|
81 | boot_spm_busy_wait(); /* Wait until the memory is written.*/
|
82 |
|
83 | /* Reenable RWW-section again. We need this if we want to jump back */
|
84 | /* to the application after bootloading. */
|
85 | boot_rww_enable ();
|
86 |
|
87 | /* Re-enable interrupts (if they were ever enabled). */
|
88 | SREG = sreg;
|
89 | }
|
90 |
|
91 | static uint16_t hex2num (const uint8_t * ascii, uint8_t num)
|
92 | {
|
93 | uint8_t i;
|
94 | uint16_t val = 0;
|
95 |
|
96 | for (i=0; i<num; i++)
|
97 | {
|
98 | uint8_t c = ascii[i];
|
99 |
|
100 | /* Hex-Ziffer auf ihren Wert abbilden */
|
101 | if (c >= '0' && c <= '9') c -= '0';
|
102 | else if (c >= 'A' && c <= 'F') c -= 'A' - 10;
|
103 | else if (c >= 'a' && c <= 'f') c -= 'a' - 10;
|
104 |
|
105 | val = 16 * val + c;
|
106 | }
|
107 |
|
108 | return val;
|
109 | }
|
110 |
|
111 | void write_page(uint32_t page, uint8_t *buf)
|
112 | {
|
113 | uart_puts("P\n\r");
|
114 | _delay_ms(100);
|
115 | program_page(page, buf);
|
116 | memset(buf, 0xFF, sizeof(SPM_PAGESIZE));
|
117 | }
|
118 |
|
119 | int main()
|
120 | {
|
121 | /* Intel-HEX Zieladresse */
|
122 | uint32_t hex_addr = 0,
|
123 | /* Intel-HEX Zieladress-Offset */
|
124 | hex_addr_offset = 0,
|
125 | /* Zu schreibende Flash-Page */
|
126 | flash_page = 0;
|
127 | /* Empfangenes Zeichen + Statuscode */
|
128 | uint16_t c = 0,
|
129 | /* Intel-HEX Checksumme zum Überprüfen des Daten */
|
130 | hex_check = 0,
|
131 | /* Positions zum Schreiben in der Datenpuffer */
|
132 | flash_cnt = 0;
|
133 | /* temporäre Variable */
|
134 | uint8_t temp,
|
135 | /* Flag zum steuern des Programmiermodus */
|
136 | boot_state = BOOT_STATE_EXIT,
|
137 | /* Empfangszustandssteuerung */
|
138 | parser_state = PARSER_STATE_START,
|
139 | /* Flag zum ermitteln einer neuen Flash-Page */
|
140 | flash_page_flag = 1,
|
141 | /* Datenpuffer für die Hexdaten*/
|
142 | flash_data[SPM_PAGESIZE],
|
143 | /* Position zum Schreiben in den HEX-Puffer */
|
144 | hex_cnt = 0,
|
145 | /* Puffer für die Umwandlung der ASCII in Binärdaten */
|
146 | hex_buffer[5],
|
147 | /* Intel-HEX Datenlänge */
|
148 | hex_size = 0,
|
149 | /* Zähler für die empfangenen HEX-Daten einer Zeile */
|
150 | hex_data_cnt = 0,
|
151 | /* Intel-HEX Recordtype */
|
152 | hex_type = 0,
|
153 | /* empfangene HEX-Checksumme */
|
154 | hex_checksum=0;
|
155 | /* Funktionspointer auf 0x0000 */
|
156 | void (*start)( void ) = (void*)0x0000;
|
157 |
|
158 | /* Füllen der Puffer mit definierten Werten */
|
159 | memset(hex_buffer, 0x00, sizeof(hex_buffer));
|
160 | memset(flash_data, 0xFF, sizeof(flash_data));
|
161 |
|
162 | /* Interrupt Vektoren verbiegen */
|
163 | temp = GICR;
|
164 | GICR = temp | (1<<IVCE);
|
165 | GICR = temp | (1<<IVSEL);
|
166 |
|
167 | /* Einstellen der Baudrate und aktivieren der Interrupts */
|
168 | uart_init( UART_BAUD_SELECT(BOOT_UART_BAUD_RATE,F_CPU) );
|
169 | sei();
|
170 |
|
171 | _delay_ms(500);
|
172 |
|
173 | do
|
174 | {
|
175 | c = uart_getc();
|
176 | if( !(c & UART_NO_DATA) )
|
177 | {
|
178 | /* Programmzustand: Parser */
|
179 | if(boot_state == BOOT_STATE_PARSER)
|
180 | {
|
181 | switch(parser_state)
|
182 | {
|
183 | /* Warte auf Zeilen-Startzeichen */
|
184 | case PARSER_STATE_START:
|
185 | if((uint8_t)c == START_SIGN)
|
186 | {
|
187 | uart_putc(XOFF);
|
188 | parser_state = PARSER_STATE_SIZE;
|
189 | hex_cnt = 0;
|
190 | hex_check = 0;
|
191 | uart_putc(XON);
|
192 | }
|
193 | break;
|
194 | /* Parse Datengröße */
|
195 | case PARSER_STATE_SIZE:
|
196 | hex_buffer[hex_cnt++] = (uint8_t)c;
|
197 | if(hex_cnt == 2)
|
198 | {
|
199 | uart_putc(XOFF);
|
200 | parser_state = PARSER_STATE_ADDRESS;
|
201 | hex_cnt = 0;
|
202 | hex_size = (uint8_t)hex2num(hex_buffer, 2);
|
203 | hex_check += hex_size;
|
204 | uart_putc(XON);
|
205 | }
|
206 | break;
|
207 | /* Parse Zieladresse */
|
208 | case PARSER_STATE_ADDRESS:
|
209 | hex_buffer[hex_cnt++] = (uint8_t)c;
|
210 | if(hex_cnt == 4)
|
211 | {
|
212 | uart_putc(XOFF);
|
213 | parser_state = PARSER_STATE_TYPE;
|
214 | hex_cnt = 0;
|
215 | hex_addr = hex_addr_offset;
|
216 | hex_addr += hex2num(hex_buffer, 4);
|
217 | hex_check += (uint8_t) hex_addr;
|
218 | hex_check += (uint8_t) (hex_addr >> 8);
|
219 |
|
220 | uart_putc(XON);
|
221 | }
|
222 | break;
|
223 | /* Parse Zeilentyp */
|
224 | case PARSER_STATE_TYPE:
|
225 | hex_buffer[hex_cnt++] = (uint8_t)c;
|
226 | if(hex_cnt == 2)
|
227 | {
|
228 | uart_putc(XOFF);
|
229 | hex_cnt = 0;
|
230 | hex_data_cnt = 0;
|
231 | hex_type = (uint8_t)hex2num(hex_buffer, 2);
|
232 | hex_check += hex_type;
|
233 | switch(hex_type)
|
234 | {
|
235 | case 1: parser_state = PARSER_STATE_CHECKSUM; break;
|
236 | case 0:
|
237 | case 2:
|
238 | case 4: parser_state = PARSER_STATE_DATA;
|
239 |
|
240 | /* Berechnen der neue Flash-Page (abhängig von hex_type) */
|
241 | /* Liegen die Daten noch in der aktuellen Flash-Page? */
|
242 | if(!flash_page_flag && (flash_page != (hex_addr - hex_addr % SPM_PAGESIZE)) )
|
243 | {
|
244 | /* Wenn die Daten nicht in der aktuellen Flash-Page liegen, */
|
245 | /* wird die aktuelle Page geschrieben und ein Flag */
|
246 | /* zum berechnen der neuen Page-Startadresse gesetzt */
|
247 | write_page(flash_page, flash_data);
|
248 | flash_cnt = 0;
|
249 | flash_page_flag = 1;
|
250 | }
|
251 |
|
252 | /* Muss die Page-Startadresse neu berechnet werden? */
|
253 | if(flash_page_flag)
|
254 | {
|
255 | /* Berechnen der neuen Page-Startadresse */
|
256 | flash_page = hex_addr - hex_addr % SPM_PAGESIZE;
|
257 |
|
258 | /* Füllen des Flash-Puffers mit dem "alten" Inhalt der Page */
|
259 | memcpy_PF(flash_data, flash_page, SPM_PAGESIZE);
|
260 |
|
261 | /* Flag setzen um anzuzeigen das eine neue Adresse da ist */
|
262 | flash_page_flag = 0;
|
263 | }
|
264 | break;
|
265 | default: parser_state = PARSER_STATE_DATA; break;
|
266 | }
|
267 | uart_putc(XON);
|
268 | }
|
269 | break;
|
270 | /* Parse Flash-Daten */
|
271 | case PARSER_STATE_DATA:
|
272 | hex_buffer[hex_cnt++] = (uint8_t)c;
|
273 | switch(hex_type)
|
274 | {
|
275 | case 0: /* Record Typ 00 - Data Record auswerten */
|
276 | if(hex_cnt == 2)
|
277 | {
|
278 | uart_putc(XOFF);
|
279 | uart_putc(c);
|
280 | hex_cnt = 0;
|
281 | flash_data[flash_cnt] = (uint8_t)hex2num(hex_buffer, 2);
|
282 | hex_check += flash_data[flash_cnt];
|
283 | flash_cnt++;
|
284 | hex_data_cnt++;
|
285 | if(hex_data_cnt == hex_size)
|
286 | {
|
287 | parser_state = PARSER_STATE_CHECKSUM;
|
288 | hex_data_cnt=0;
|
289 | hex_cnt = 0;
|
290 | }
|
291 | /* Puffer voll -> schreibe Page */
|
292 | if(flash_cnt == SPM_PAGESIZE)
|
293 | {
|
294 | write_page(flash_page, flash_data);
|
295 | flash_cnt = 0;
|
296 | flash_page_flag = 1;
|
297 | }
|
298 | uart_putc(XON);
|
299 | }
|
300 | break;
|
301 | case 2: /* Record Typ 02 - Extended Segment Address auswerten */
|
302 | case 4: /* Record Typ 04 - Extended Linear Address auswerten */
|
303 | if(hex_cnt == 4)
|
304 | {
|
305 | uart_putc(XOFF);
|
306 | uart_putc('J');
|
307 | hex_cnt = 0;
|
308 |
|
309 | /* Schreibe angfangene Flash-Page vor Segment-Sprung */
|
310 | write_page(flash_page, flash_data);
|
311 | flash_cnt = 0;
|
312 | flash_page_flag = 1;
|
313 |
|
314 | /* Berechnen der Offsetadresse */
|
315 | switch(hex_type)
|
316 | {
|
317 | case 2: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) << 4; break;
|
318 | case 4: hex_addr_offset = ((uint32_t)hex2num(hex_buffer, 4)) << 16; break;
|
319 | }
|
320 |
|
321 | /* Addieren der empfangenen Werte für die Checksumme */
|
322 | hex_check += (uint8_t) hex2num(hex_buffer, 2);
|
323 | hex_check += (uint8_t) hex2num(hex_buffer + 2, 2);
|
324 |
|
325 | parser_state = PARSER_STATE_CHECKSUM;
|
326 | hex_data_cnt=0;
|
327 | hex_cnt = 0;
|
328 | }
|
329 | break;
|
330 | default:
|
331 | break;
|
332 | }
|
333 | break;
|
334 | /* Parse Checksumme */
|
335 | case PARSER_STATE_CHECKSUM:
|
336 | hex_buffer[hex_cnt++] = (uint8_t)c;
|
337 | if(hex_cnt == 2)
|
338 | {
|
339 | uart_putc(XOFF);
|
340 | hex_checksum = (uint8_t)hex2num(hex_buffer, 2);
|
341 | hex_check += hex_checksum;
|
342 | hex_check &= 0x00FF;
|
343 | /* Dateiende -> schreibe Restdaten */
|
344 | if(hex_type == 1)
|
345 | {
|
346 | write_page(flash_page, flash_data);
|
347 | boot_state = BOOT_STATE_EXIT;
|
348 | }
|
349 | /* Überprüfe Checksumme -> muss '0' sein */
|
350 | if(hex_check == 0) parser_state = PARSER_STATE_START;
|
351 | else parser_state = PARSER_STATE_ERROR;
|
352 | uart_putc(XON);
|
353 | }
|
354 | break;
|
355 | /* Parserfehler (falsche Checksumme) */
|
356 | case PARSER_STATE_ERROR:
|
357 | uart_putc('#');
|
358 | break;
|
359 | default:
|
360 | break;
|
361 | }
|
362 | }
|
363 | /* Programmzustand: UART Kommunikation */
|
364 | else if(boot_state != BOOT_STATE_PARSER)
|
365 | {
|
366 | switch((uint8_t)c)
|
367 | {
|
368 | case 'p':
|
369 | boot_state = BOOT_STATE_PARSER;
|
370 | uart_puts("paste .hex here\n\r");
|
371 | break;
|
372 | case 'q':
|
373 | boot_state = BOOT_STATE_EXIT;
|
374 | uart_puts("leave Booty\n\r");
|
375 | break;
|
376 | default:
|
377 | uart_putc((unsigned char)c);
|
378 | uart_puts("\n\r");
|
379 | break;
|
380 | }
|
381 | }
|
382 | }
|
383 | }
|
384 | while(boot_state!=BOOT_STATE_EXIT);
|
385 |
|
386 | uart_puts("* ->Reset now!\n\r" );
|
387 | uart_puts("***********************\n\r" );
|
388 | _delay_ms(500);
|
389 |
|
390 | cli();
|
391 |
|
392 | UCSRA |= (1<<TXC);
|
393 |
|
394 | /* Interrupt Vektoren wieder gerade biegen */
|
395 | temp = GICR;
|
396 | GICR = temp | (1<<IVCE);
|
397 | GICR = temp & ~(1<<IVSEL);
|
398 |
|
399 | /* Reset */
|
400 | start();
|
401 |
|
402 | return 0;
|
403 | }
|