AVR_Tiny_Bootloader.c


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
}