1 | /*
|
2 | * AVR_Tiny_Bootloader.c
|
3 | *
|
4 | * Created: 11.02.2014 16:25:16
|
5 | * Author: Marius Dege
|
6 |
|
7 | // pf write hexfile to flash memory
|
8 | // return value: pf+
|
9 | // The + indicates successful interpretation of the command.
|
10 | // Now an intel hex file can be send to the MCU over the UART.
|
11 | // For every successful read and parsed record line a '.' is printed. When a flash page write is done, a '*' is printed.
|
12 | // In case a checksum error is detected in a record line, a '-' is printed and the command is terminated.
|
13 | //
|
14 | // pe write hexfile to eeprom memory
|
15 | // return value: pe+
|
16 | // The + indicates successful interpretation of the command.
|
17 | // Now an intel hex file can be send to the MCU over the UART.
|
18 | // For every successful read and parsed record line a '.' is printed. When a record line was written to eeprom, a '*' is printed.
|
19 | // In case a checksum error is detected in a record line, a '-' is printed and the command is terminated.
|
20 | //
|
21 | // g jump to application at bottom of flash 0x0000
|
22 | // return value: g+
|
23 | // The + indicates successful interpretation of the command.
|
24 |
|
25 | //NOTE: .bootloader==WORDADRESSE; BOOTADRESSE=BYTEADRESSE!!!!
|
26 | //Folgende ATTinys können unterstützt werden by Hardware UART (mind. 256byte SRAM):
|
27 | //ATtiny167/87 - Start @ WORD/WORD
|
28 | //ATtiny4313 - Start @ 0x4A0 WORD
|
29 | //ATtiny441/841 - Start @ WORD/WORD
|
30 | //ATtiny828 - Start @ WORD
|
31 | //ATtiny1634 - Start @ WORD - HouseBasic (0)
|
32 | */
|
33 |
|
34 | #define F_CPU 16000000UL
|
35 |
|
36 | // bootloader uses XON/XOFF handshake
|
37 | #define XON 'G' //17
|
38 | #define XOFF 'H' //19
|
39 |
|
40 | // other communication characters
|
41 | #define BREAK 3
|
42 | #define LINE_FEED 0x0A
|
43 |
|
44 | // define the line ending, make sure to have set your terminal program to match this!
|
45 | // 0x0a is '\n' - we write the hex number to avoid problems with different compilers handling the '\n' in different ways
|
46 | #define LINE_ENDING LINE_FEED
|
47 | #define CheckedVersion 'E'
|
48 | #define GoBackToLoader '^'
|
49 |
|
50 |
|
51 | #include <avr/io.h>
|
52 | #include <avr/interrupt.h>
|
53 | #include <avr/boot.h>
|
54 | #include <avr/wdt.h>
|
55 | #include <stdint.h>
|
56 | #include <avr/eeprom.h>
|
57 | #include <avr/pgmspace.h>
|
58 | #include <stdint.h>
|
59 | #include <avr/delay.h>
|
60 |
|
61 |
|
62 | void (*app_start)(void)=0x0000;
|
63 | static void boot_rs232init(uint32_t baud) BOOTLOADER_SECTION;
|
64 | static void boot_sendchar(char data) BOOTLOADER_SECTION;
|
65 | static void boot_sendstring(char *p) BOOTLOADER_SECTION;
|
66 | static char boot_readchar(void) BOOTLOADER_SECTION;
|
67 | uint8_t boot_kbhit(void) BOOTLOADER_SECTION;
|
68 | uint8_t asciiToHex(uint8_t ucNibble) BOOTLOADER_SECTION;
|
69 | uint8_t asciiToHex2(uint8_t ucNibbleHigh, uint8_t ucNibbleLow) BOOTLOADER_SECTION;
|
70 |
|
71 | void bootloader(uint8_t sync, uint8_t SystemVersion) BOOTLOADER_SECTION __attribute__((noreturn));
|
72 | void bootloader(uint8_t sync, uint8_t SystemVersion)
|
73 | {
|
74 | BOOTSTART:
|
75 |
|
76 | wdt_reset();
|
77 | wdt_disable();
|
78 | cli(); // no irqs.
|
79 |
|
80 | //For HouseBasic
|
81 | DDRB=0;
|
82 | PORTB=0;
|
83 | DDRC=0;
|
84 | PORTC=0;
|
85 | DDRA=(0|(1<<4)); //Set Bluetooth Module on (MD Device Support)
|
86 | PORTA=(0|(1<<4));
|
87 |
|
88 | uint32_t ulPageBaseAddress, size, BaudRates[]={300,600,1200,4800,9600,14400,19200,28800,38400,56000,57600,115200,128000,256000};
|
89 | uint16_t usAddr, usExtSegmentAddress, usPageAddressOffset;
|
90 | uint8_t ucFlagEepromWrite, ucFlagWrite, ucFlagEof, ucFlagError, ucComputedChecksum, ucHexRecChecksum, ucRecordType, ucByteCount, ucTmp=0, ucDataByte=0, ucBuffer[55], ucEepromBuffer[25], ucTmpDataByte=0, ucPageWriting=0, ucTmpPageWriting=0;
|
91 |
|
92 | boot_rs232init(BaudRates[4]);
|
93 | ucTmp=0;
|
94 |
|
95 | do
|
96 | {
|
97 | wdt_reset();
|
98 |
|
99 | if(boot_kbhit())
|
100 | {
|
101 | if (boot_readchar()=='U' || boot_readchar()=='^')
|
102 | ucTmp=150;
|
103 | else
|
104 | {
|
105 | ucTmp++;
|
106 |
|
107 | if (ucTmp>=14)
|
108 | ucTmp=0;
|
109 |
|
110 | boot_rs232init(BaudRates[ucTmp]);
|
111 | }
|
112 | }
|
113 | } while (ucTmp!=150);
|
114 |
|
115 | //Send Versions for MDTerm
|
116 | boot_sendchar('M');
|
117 | boot_sendchar('D');
|
118 | boot_sendchar('-');
|
119 | boot_sendchar('B');
|
120 | boot_sendchar('L');
|
121 | boot_sendchar(LINE_FEED);
|
122 | wdt_reset();
|
123 |
|
124 | // init the buffer to empty buffer
|
125 | ucBuffer[0]=0;
|
126 | ucTmp=0;
|
127 | ucDataByte=0;
|
128 | ucPageWriting=0;
|
129 | ucTmpPageWriting=0;
|
130 |
|
131 | //Send Versions for MDTerm of device
|
132 | do
|
133 | {
|
134 | wdt_reset();
|
135 |
|
136 | if(boot_kbhit())
|
137 | {
|
138 | boot_sendchar('H');
|
139 | boot_sendchar('o');
|
140 | boot_sendchar('u');
|
141 | boot_sendchar('s');
|
142 | boot_sendchar('e');
|
143 | boot_sendchar('B');
|
144 | boot_sendchar('a');
|
145 | boot_sendchar('s');
|
146 | boot_sendchar('i');
|
147 | boot_sendchar('c');
|
148 | boot_sendchar('-');
|
149 | boot_sendchar(SystemVersion);
|
150 | boot_sendchar(LINE_FEED);
|
151 | }
|
152 | } while (boot_readchar()!=CheckedVersion);
|
153 |
|
154 | while (1)
|
155 | {
|
156 | // show a prompt
|
157 | boot_sendchar('>');
|
158 | boot_sendchar(XON); // send XON to allow transmission
|
159 | boot_sendchar(LINE_FEED);
|
160 |
|
161 | ucTmp=0;
|
162 | do
|
163 | {
|
164 | wdt_reset();
|
165 |
|
166 | if (boot_kbhit() && ucTmp<sizeof(ucBuffer))
|
167 | {
|
168 | ucBuffer[ucTmp]=boot_readchar();
|
169 | ucTmp++;
|
170 | }
|
171 | else if(ucTmp>=sizeof(ucBuffer))
|
172 | ucBuffer[ucTmp]=BREAK;
|
173 |
|
174 | if (ucBuffer[ucTmp]==GoBackToLoader)
|
175 | goto BOOTSTART;
|
176 |
|
177 | if (ucBuffer[ucTmp]==BREAK)
|
178 | {
|
179 | ucBuffer[0]='X'; // set first character to 'X' to avoid any case match in switch
|
180 | break;
|
181 | }
|
182 | } while(ucBuffer[ucTmp-1] != LINE_ENDING);
|
183 | ucBuffer[ucTmp-1]=0; // terminate the string at the new line position
|
184 |
|
185 | boot_sendchar(XOFF); // send XOFF to pause transmission
|
186 | boot_sendstring(ucBuffer); // echo the command
|
187 |
|
188 | // parse first character of command buffer
|
189 | switch(ucBuffer[0])
|
190 | {
|
191 | // jump to application at flash start 0x0000
|
192 | case 'g': wdt_enable(WDTO_250MS);
|
193 | wdt_reset();
|
194 | boot_sendchar('+');
|
195 | boot_sendchar(XON);
|
196 | boot_sendchar(LINE_FEED);
|
197 | _delay_ms(550); // wait until last character is transmitted to avoid loss of XON due to uart initialization in main applicatio
|
198 | app_start(); // jump to bottom of flash -> application start
|
199 | while(1);
|
200 | break;
|
201 |
|
202 | // read hex file and write to flash or eeprom memory
|
203 | case 'p': boot_sendchar('+');
|
204 | boot_sendchar(LINE_FEED);
|
205 |
|
206 | wdt_reset();
|
207 |
|
208 | ucFlagEepromWrite = 0; // default is flash programming
|
209 |
|
210 | // except we received an 'e' as second character
|
211 | if(ucBuffer[1] == 'e')
|
212 | {
|
213 | ucFlagEepromWrite = 1; // set eeprom flag
|
214 | }
|
215 |
|
216 | ulPageBaseAddress = 0xFFFFFFFF; // preset page base address to impossible value
|
217 | usPageAddressOffset = 0; // clear offset
|
218 | usExtSegmentAddress = 0; // clear segment address
|
219 |
|
220 | do
|
221 | {
|
222 | wdt_reset();
|
223 | ucFlagError = 0; // clear error flag
|
224 | ucFlagEof = 0; // clear end-of-file flag
|
225 | ucFlagWrite = 0; // clear write flag
|
226 | ucTmpPageWriting = 0;
|
227 |
|
228 | // read one line from hex file into the buffer
|
229 | boot_sendchar(XON);
|
230 | boot_sendchar(LINE_FEED);
|
231 | ucTmp=0;
|
232 |
|
233 | do
|
234 | {
|
235 | wdt_reset();
|
236 |
|
237 | if(boot_kbhit())
|
238 | {
|
239 | ucBuffer[ucTmp]=boot_readchar();
|
240 | ucTmp++;
|
241 | }
|
242 |
|
243 | if (ucBuffer[ucTmp]==GoBackToLoader)
|
244 | goto BOOTSTART;
|
245 |
|
246 | } while(ucBuffer[ucTmp-1] != LINE_ENDING);
|
247 |
|
248 | boot_sendchar(XOFF); // send XOFF to pause transmission
|
249 | ucBuffer[ucTmp-1]=0;
|
250 |
|
251 | ucTmpPageWriting++;
|
252 |
|
253 | //Tinys writing 4 Pages => block page erasing
|
254 | if (ucTmpPageWriting>8)
|
255 | {
|
256 | ucTmpPageWriting=1;
|
257 | ucPageWriting=0;
|
258 | }
|
259 |
|
260 |
|
261 | // grab hex record information, like byte count, address and type of record
|
262 | ucByteCount = asciiToHex2(ucBuffer[1], ucBuffer[2]); // get the number of bytes
|
263 | ucComputedChecksum = ucByteCount; // compute checksum
|
264 | usAddr = asciiToHex2(ucBuffer[3], ucBuffer[4]); // get the address high byte
|
265 | ucComputedChecksum += (unsigned char)usAddr; // compute checksum
|
266 | usAddr = (usAddr << 8) + asciiToHex2(ucBuffer[5], ucBuffer[6]); // get the address low byte +
|
267 | ucComputedChecksum += (unsigned char)(usAddr & 0xFF); // compute checksum
|
268 | ucRecordType = asciiToHex2(ucBuffer[7], ucBuffer[8]); // get the record type
|
269 | ucComputedChecksum += ucRecordType; // compute checksum
|
270 | ucHexRecChecksum = asciiToHex2(ucBuffer[(ucByteCount*2)+9], ucBuffer[(ucByteCount*2)+10]); // get the checksum
|
271 |
|
272 | //Don't overwrite the Bootloader - so fake writing
|
273 | if (usAddr>=BOOTADDR && !ucFlagEepromWrite) //no eeprom
|
274 | {
|
275 | wdt_reset();
|
276 | ulPageBaseAddress=0xFFFFFFFF;
|
277 | usPageAddressOffset=0;
|
278 |
|
279 | boot_sendstring("FAKE\n");
|
280 | boot_sendchar(XOFF);
|
281 | boot_sendchar('.'); // progress indicator
|
282 | }
|
283 | else
|
284 | {
|
285 | // check the record type
|
286 | if(ucRecordType==2) // extendes segment address record
|
287 | {
|
288 | usExtSegmentAddress = (asciiToHex2(ucBuffer[9], ucBuffer[10]) << 8);
|
289 | ucComputedChecksum += (usExtSegmentAddress >> 8); // compute checksum
|
290 | usExtSegmentAddress += asciiToHex2(ucBuffer[11], ucBuffer[12]);
|
291 | ucComputedChecksum += (usExtSegmentAddress & 0xFF); // compute checksum
|
292 | }
|
293 | else if(ucRecordType==1) // end of file record
|
294 | {
|
295 | ucFlagEof=1;
|
296 | ucFlagWrite=1;
|
297 | ucTmpPageWriting=0;
|
298 | ucPageWriting=0;
|
299 | }
|
300 | else if(ucRecordType==0) // data record
|
301 | {
|
302 | // set base address for the flash page, if not set
|
303 | if(ulPageBaseAddress == 0xFFFFFFFF)
|
304 | {
|
305 | ulPageBaseAddress = usAddr;
|
306 | }
|
307 |
|
308 | // parse the data bytes
|
309 | for(ucTmp = 0; ucTmp < (2*ucByteCount); ucTmp += 4) // increment by four, since we parse the ascii buffer for four ascii character (for ex. two hex numbers)
|
310 | {
|
311 | wdt_reset();
|
312 | ucDataByte = asciiToHex2(ucBuffer[ucTmp+9], ucBuffer[ucTmp+10]); // get the low data byte
|
313 | ucComputedChecksum += ucDataByte; // compute checksum
|
314 | ucTmpDataByte = asciiToHex2(ucBuffer[ucTmp+11], ucBuffer[ucTmp+12]); // get the high data byte
|
315 | ucComputedChecksum += ucTmpDataByte; // compute checksum
|
316 |
|
317 | // if we want to program eeprom, we store the data in another buffer
|
318 | if(ucFlagEepromWrite == 1)
|
319 | {
|
320 | ucEepromBuffer[usPageAddressOffset] = ucDataByte;
|
321 | ucEepromBuffer[usPageAddressOffset+1] = ucTmpDataByte;
|
322 | }
|
323 | else // flash programming
|
324 | {
|
325 | wdt_reset();
|
326 | boot_page_fill((unsigned long)(ulPageBaseAddress + usPageAddressOffset + (usExtSegmentAddress * 16)), (((unsigned short)ucTmpDataByte) << 8) + (((unsigned short)ucDataByte) & 0x00FF));
|
327 | }
|
328 |
|
329 | wdt_reset();
|
330 | usPageAddressOffset += 2;
|
331 | }
|
332 | }
|
333 |
|
334 | // check checksum
|
335 | if((unsigned char)(ucComputedChecksum + ucHexRecChecksum) != 0x00) // computed checksum plus checksum from hexfile must be zero
|
336 | {
|
337 | ucFlagError = 1;
|
338 | break; // leave do loop, we do not program anything in case we get a checksum error
|
339 | }
|
340 |
|
341 | boot_sendchar('.'); // progress indicator
|
342 |
|
343 | // check if page is filled, if so, we set the write flag
|
344 | if(usPageAddressOffset >= (SPM_PAGESIZE))
|
345 | {
|
346 | ucFlagWrite = 1;
|
347 | }
|
348 | if(ucFlagEepromWrite == 1)
|
349 | {
|
350 | ucFlagWrite = 1;
|
351 | }
|
352 |
|
353 | // check if write flag is set
|
354 | if((ucFlagWrite == 1) && (ulPageBaseAddress != 0xFFFFFFFF))
|
355 | {
|
356 | ulPageBaseAddress += ((unsigned long)usExtSegmentAddress * 16); // add the extended segment address to the base address
|
357 |
|
358 | // we want to write eeprom
|
359 | if(ucFlagEepromWrite == 1)
|
360 | {
|
361 | eeprom_busy_wait(); // wait in case eeprom is busy
|
362 | eeprom_write_block(ucEepromBuffer, (unsigned char*)(unsigned short)ulPageBaseAddress, usPageAddressOffset);
|
363 | eeprom_busy_wait();
|
364 | }
|
365 | else // we want to write flash
|
366 | {
|
367 | // check if page base address exceeds maximum address before bootloader section begins (BOOTADDR comes from -D compiler option)
|
368 | if(ulPageBaseAddress >= (unsigned long)BOOTADDR)
|
369 | {
|
370 | wdt_reset();
|
371 | ucFlagError = 1;
|
372 | break;
|
373 | }
|
374 |
|
375 | //erasing 4 Pages ==> mage only once at times of 8 receive data strings
|
376 | if (!ucPageWriting)
|
377 | {
|
378 | ucPageWriting=1;
|
379 | boot_spm_busy_wait();
|
380 | boot_page_erase((unsigned long)ulPageBaseAddress); // do a page erase
|
381 | }
|
382 |
|
383 | boot_spm_busy_wait(); // wait for page erase done
|
384 | boot_page_write((unsigned long)ulPageBaseAddress); // do a page write
|
385 | boot_spm_busy_wait(); // wait for write completed
|
386 | //boot_rww_enable(); // reenable rww section again | goes bad for tinys
|
387 | }
|
388 |
|
389 | boot_sendchar('*'); // * indicates a page write
|
390 | ulPageBaseAddress = 0xFFFFFFFF;
|
391 | usPageAddressOffset = 0;
|
392 | }
|
393 | }
|
394 |
|
395 | wdt_reset();
|
396 |
|
397 | } while(!ucFlagError && !ucFlagEof); // in case of retval or end of file
|
398 |
|
399 | // test on error and print + or -
|
400 | if(!ucFlagError)
|
401 | boot_sendchar('+');
|
402 | else
|
403 | boot_sendchar('-');
|
404 | break;
|
405 |
|
406 | // no case found, so we listen
|
407 | default: boot_sendchar('-');
|
408 | }
|
409 | }
|
410 | }
|
411 |
|
412 | static void boot_rs232init(uint32_t baud)
|
413 | {
|
414 | uint32_t UBRR_VALE=(((F_CPU+baud*8)/(baud*16)-1));
|
415 |
|
416 | //set Baud
|
417 | UBRR0H = (uint8_t) (UBRR_VALE >> 8);
|
418 | UBRR0L = (uint8_t) (UBRR_VALE);
|
419 | UCSR0B = (1 << RXEN0) | (1 << TXEN0);
|
420 | UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); //8N1
|
421 | }
|
422 | static void boot_sendchar(char data)
|
423 | {
|
424 | // send character
|
425 | while(!(UCSR0A & (1 << UDRE0)));
|
426 | UDR0 = data;
|
427 | }
|
428 | static void boot_sendstring(char *p)
|
429 | {
|
430 | while (*p) //while not Null
|
431 | {
|
432 | boot_sendchar(*p); //send value at pointer
|
433 | p++; //inkrement pointer
|
434 | }
|
435 | }
|
436 | static char boot_readchar(void)
|
437 | {
|
438 | // warten bis Zeichen verfuegbar
|
439 | if((UCSR0A & (1<<RXC0)))
|
440 | return (UDR0);
|
441 | }
|
442 | volatile uint8_t boot_kbhit(void)
|
443 | {
|
444 | if((UCSR0A & (1<<RXC0)))
|
445 | return 1;
|
446 |
|
447 | return 0;
|
448 | }
|
449 | volatile uint8_t asciiToHex(uint8_t ucNibble)
|
450 | {
|
451 | // ============================================================================
|
452 | // convert hex ascii nibble ('0' - 'f'/'F') into a number
|
453 | // in: ascii character (nibble) to be converted
|
454 | // out: 4 bit hex number
|
455 | // ============================================================================
|
456 | uint8_t ucHex = 0;
|
457 |
|
458 | // check if ascii character is a lower case letter a-f
|
459 | if(ucNibble >= 'a' && ucNibble <= 'f')
|
460 | {
|
461 | ucHex = (ucNibble - 'a') + 10; //+10
|
462 | }
|
463 | else if(ucNibble >= 'A' && ucNibble <= 'F') // or an upper case letter A-F
|
464 | {
|
465 | ucHex = (ucNibble - 'A') + 10; //+10
|
466 | }
|
467 | else if(ucNibble >= '0' && ucNibble <= '9') // or if it is a number 0-9
|
468 | {
|
469 | ucHex = (ucNibble - '0');
|
470 | }
|
471 |
|
472 | return (ucHex); // return the hex number
|
473 | }
|
474 | volatile uint8_t asciiToHex2(uint8_t ucNibbleHigh, uint8_t ucNibbleLow)
|
475 | {
|
476 | // ============================================================================
|
477 | // convert two hex ascii nibbles into a number
|
478 | // in: two ascii characters (high, low nibble) to be converted
|
479 | // out: 8 bit hex number
|
480 | // ============================================================================
|
481 | return ((asciiToHex(ucNibbleHigh) << 4) + asciiToHex(ucNibbleLow)); // just call the function for one nibble twice +
|
482 | }
|