Project

General

Profile

Statistics
| Revision:

root / trunk / toolbox / bootloader / bootloader.c @ 155

History | View | Annotate | Download (8.26 KB)

1
/*
2
    1-24-09
3
    Copyright Spark Fun Electronics? 2009
4
    Nathan Seidle
5
    
6
    Wireless bootloader for the ATmega168 and XBee Series 1 modules
7
        
8
        This is a small (728 byte) serial bootloader designed to be a robust solution for remote reset and wireless
9
        booloading. It's not extremely fast, but is very hardy.
10
    
11
        The remote unit (the AVR usually) broadcasts the non-visible character ASCII(6). It then waits for a response
12
        over the serial link for the non-visible character ASCII(5). If received, the remote unit enters bootloading mode.
13
        If the correct character 5 is not received, the remote unit jumps to the beginning of the regular program code.
14
        
15
        Bootloading includes checksum calculation, and timeouts. Timeouts is most important because a wireless
16
        link does not always deliver segments of the serial stream in a deterministic fashion - a good wireless unit
17
        will buffer all sorts of stuff, making the connection stream irregular in throughput.
18
        
19
        This bootloader accepts a pure binary stream (not an intel hex file format). All file parsing is done on the 
20
        base side (usually a beefy computer with lots of extra processing ability).
21
        
22
        Things I learned from testing:
23
        
24
        XBee series 2.5 units have their uses, but not here. I beat my head against the wall trying to form a sensible link and failed.
25
        Ultimately, plugging series 1 in, it worked wonderfully. If you need point-to-point, series 1 is wonderful. If you really
26
        need true mesh node networking, Series 2.5 is good.
27
        
28
        XBee Series 2.5 ships with CTS enabled! That's why the AT commands through hyperterminal were not working. Grr.
29
        
30
        To get a Series 2.5 link to work, you must configure device on XBee Explorer as Coordinator, and the device in your arduino board as the end device.
31
        
32
        Trying to use Series 2.5 for a good point-to-point link:
33
        With CTS Enabled, 19200bps, Packetization Timeout at 3 (default), still bit errors, even with 1ms delay between characters
34
        With CTS Enabled, 19200bps, Packetization Timeout at 0, with 1ms delay between characters helps a lot, but will get character errors if there is RF interferance (units further than a few feet apart)
35

36
        With CTS/RTS Enabled, 19200bps, Packet timeout at 0, no delay but with flow control, we have very solid link -> one way!
37
        while( (PIND & (1<<CTS)) != 0); //Don't send anything to the XBee, it is thinking
38
        
39
        You do not seem to need CTS/RTS/DTR to read or program an XBee.
40

41
        With XB24-ZB unit, the end device can transmit all it wants, the coordinator seems to die after a few seconds. This
42
        was the ultimate downfall of the series 2.5 for me. The link would work, but the coordinator would drop off after a few
43
        seconds? Series 1 did not do this.
44
        
45
        All of the following code works exceptionally well with Series 1 "XB24" "XBee 802.15.4" "v10CD" firmware
46
        
47
        To configure the XBees, follow "Lady Ada wireless arduino" info
48

49
        Series 1 module settings:
50
        Baud: 19200
51
        No flow control (CTS is left on as default)
52
        No change to packetization timeout (default = 3?)
53
        
54
        RTS on XBee board goes up and down with the com advanced trick NOT checked, and hardware control turned ON under terminal
55

56
        In VB, turn handshaking off. When RTSEnable = True, the RTS pin goes low, resetting the AVR
57

58
        Wireless:
59
        38 seconds to load 14500 code words (most of the space) at 38400 / 8MHz (internal osc)
60
        38 seconds to load 14500 code words (most of the space) at 19200 / 8MHz (internal osc)
61
        Wired:
62
        11 seconds to load 14500 code words (most of the space) at 19200 / 8MHz (internal osc)
63
        so you see, there is no benefit to a higher baud rate. The XBee protocol is the bottleneck
64

65
        How to read the flash contents to file : 
66
        avrdude -c stk200 -p m168 -P lpt1 -Uflash:r:bl.hex:i
67
        This will dump the current flash contents of an AVR to a read-able hex file called "bl.hex". This
68
        was very helpful when testing whether flash writing was actually working.
69

70
        Oh, and if you happen to be using an XBee with a UFL antenna connector (and don't have a UFL antenna sitting around)
71
        you can convert it to a wire antenna simply by soldering in a short wire into the XBee. It may not be the best, 
72
        but it works.
73

74
*/
75

    
76

    
77
#include <avr/io.h>
78
#include <util/delay.h>
79
#include <avr/boot.h>
80

    
81
#include "uart.h"
82

    
83
#define TRUE        0
84
#define FALSE        1
85

    
86
#define MAX_WAIT_IN_CYCLES 800000
87

    
88
//Status LED
89
#define LED_DDR  DDRB
90
#define LED_PORT PORTB
91
#define LED      PORTB1
92

    
93
#define PAGE_SIZE 32
94

    
95
//Function prototypes
96
void flash_led(uint8_t);
97
void onboard_program_write(uint32_t page, uint8_t *buf);
98
void (*main_start)(void) = 0x0000;
99

    
100
//Variables
101
uint8_t incoming_page_data[PAGE_SIZE];
102
uint8_t page_length;
103
uint8_t retransmit_flag = FALSE;
104

    
105
union page_address_union {
106
  uint16_t word;
107
  uint8_t  byte[2];
108
} page_address;
109

    
110
char getch(void);
111

    
112
int main(void)
113
{
114
  uint8_t check_sum = 0;
115
  uint16_t i;
116

    
117
  init_uart(51); //MAGIC NUMBER??
118

    
119
  //set LED pin as output
120
  LED_DDR |= _BV(LED);
121

    
122
  //flash onboard LED to signal entering of bootloader
123
  flash_led(1);
124

    
125
  //Start bootloading process
126
   uart_send_byte(5); //Tell the world we can be bootloaded
127

    
128
  //Check to see if the computer responded
129
  uint32_t count = 0;
130
  uint8_t resp;
131
  while(uart_get_byte(&resp) == -1) {
132
    count++;
133
    if (count > MAX_WAIT_IN_CYCLES)
134
      //TODO: flash some leds or something
135
      main_start();
136
  }
137

    
138
  /* If the computer did not respond correctly with a ACK, we jump to
139
   * user's program
140
   */
141
  if(resp != 6)
142
    main_start(); 
143

    
144
  while(1) {
145
    //Determine if the last received data was good or bad
146
    if (check_sum != 0) //If the check sum does not compute, tell computer to resend same line
147
    RESTART:
148
      uart_send_byte(7); //Ascii character BELL
149
    else            
150
      uart_send_byte('T'); //Tell the computer that we are ready for the next line
151
        
152
    while(1) {//Wait for the computer to initiate transfer
153
      if (getch() == ':') break; //This is the "gimme the next chunk" command
154
      if (retransmit_flag == TRUE) goto RESTART;
155
    }
156

    
157
    page_length = getch(); //Get the length of this block
158
    if (retransmit_flag == TRUE) goto RESTART;
159
    if(page_length > PAGE_SIZE) {
160
      while(1) {
161
        flash_led(1);
162
      }
163
    }
164

    
165
    if (page_length == 'S') {//Check to see if we are done - this is the "all done" command
166
      //boot_rww_enable (); //Wait for any flash writes to complete?
167
      main_start(); 
168
    }
169
        
170
    //Get the memory address at which to store this block of data
171
    page_address.byte[0] = getch(); if (retransmit_flag == TRUE) goto RESTART;
172
    page_address.byte[1] = getch(); if (retransmit_flag == TRUE) goto RESTART;
173

    
174
    check_sum = getch(); //Pick up the check sum for error dectection
175
    if (retransmit_flag == TRUE) goto RESTART;
176
                
177
    for(i = 0 ; i < page_length ; i++) {//Read the program data
178
      incoming_page_data[i] = getch();
179
      if (retransmit_flag == TRUE) goto RESTART;
180
    }
181
        
182
    //Calculate the checksum
183
    for(i = 0 ; i < page_length ; i++)
184
      check_sum = check_sum + incoming_page_data[i];
185
        
186
    check_sum = check_sum + page_length;
187
    check_sum = check_sum + page_address.byte[0];
188
    check_sum = check_sum + page_address.byte[1];
189
                
190
    if(check_sum == 0) //If we have a good transmission, put it in ink
191
      onboard_program_write((uint32_t)page_address.word, incoming_page_data);
192
  }
193

    
194
}
195

    
196
//#define SPM_PAGESIZE 128
197
void onboard_program_write(uint32_t page, uint8_t *buf)
198
{
199
  uint16_t i;
200
        //uint8_t sreg;
201

    
202
        // Disable interrupts.
203

    
204
        //sreg = SREG;
205
        //cli();
206

    
207
        //eeprom_busy_wait ();
208

    
209
  boot_page_erase (page);
210
  boot_spm_busy_wait ();      // Wait until the memory is erased.
211

    
212
  for (i=0; i<SPM_PAGESIZE; i+=2){
213
    // Set up little-endian word.
214

    
215
    uint16_t w = *buf++;
216
    w += (*buf++) << 8;
217
        
218
    boot_page_fill (page + i, w);
219
  }
220

    
221
  boot_page_write (page);     // Store buffer in flash page.
222
  boot_spm_busy_wait();       // Wait until the memory is written.
223

    
224
        // Reenable RWW-section again. We need this if we want to jump back
225
        // to the application after bootloading.
226

    
227
        //boot_rww_enable ();
228

    
229
        // Re-enable interrupts (if they were ever enabled).
230

    
231
        //SREG = sreg;
232
}
233

    
234
char getch(void) {
235
  retransmit_flag = FALSE;
236
        
237
  uint32_t count = 0;
238
  uint8_t resp;
239
  while(uart_get_byte(&resp)==-1) {
240
    count++;
241
    if (count > MAX_WAIT_IN_CYCLES) {
242
      retransmit_flag = TRUE;
243
      break;
244
    }
245
  }
246

    
247
  return resp;
248
}
249

    
250
void flash_led(uint8_t count)
251
{
252
        uint8_t i;
253
        
254
        for (i = 0; i < count; ++i) {
255
                LED_PORT |= _BV(LED);
256
                _delay_ms(100);
257
                LED_PORT &= ~_BV(LED);
258
                _delay_ms(100);
259
        }
260
}