Project

General

Profile

Statistics
| Revision:

root / trunk / bootloader / bootloader.c @ 183

History | View | Annotate | Download (6.66 KB)

1
#include <avr/io.h>
2
#include <avr/boot.h>
3
#include <avr/wdt.h>
4
#include <tooltron.h>
5
#include "uart.h"
6

    
7
#define ADDR    18
8

    
9
// Error thresholds
10
#define MAX_TIMEOUT 60000   // Seconds to wait before exiting bootloader mode
11
#define MAX_RETRIES 5       // Number of times to retry before giving up
12

    
13
//Status LED
14
#define LED_DDR  DDRB
15
#define LED_PORT PORTB
16
#define LED      PORTB1
17

    
18
//Function prototypes
19
void (*main_start)(void) = BOOT_START/2 - 1;
20

    
21
typedef union {
22
    uint8_t bytes[2];
23
    int16_t sword;
24
} rjump_t;
25

    
26
void init_uart(uint16_t baud) {
27
        // Set baud rate
28
        UBRRH = (uint8_t)(baud>>8);
29
        UBRRL = (uint8_t)baud;
30
        
31
        // Enable RX/TX
32
        UCSRB = _BV(RXEN) | _BV(TXEN);
33
 
34
        // Enable the TXEN pin as output
35
        DDRD |= TX_EN;
36
    uart_toggle_transmit(UART_TX_OFF);
37
}
38

    
39
int8_t uart_get_byte(uint8_t *output_byte) {
40
    if (UCSRA & _BV(RXC)) {
41
        *output_byte = UDR;
42
        return 0;
43
    } else {
44
        return -1;
45
    }
46
}
47

    
48
void uart_send_byte(uint8_t data) {
49
        //Waits until current transmit is done
50
    while (!(UCSRA & _BV(UDRE)));
51

    
52
    // Enable writes and send
53
        uart_toggle_transmit(UART_TX_ON);
54
    UDR = data;
55

    
56
    // Waits until the transmit is done
57
    while(!(UCSRA & _BV(TXC)));
58
    uart_toggle_transmit(UART_TX_OFF);
59
    UCSRA |= _BV(TXC);
60

    
61
        return;
62
}
63

    
64
void uart_toggle_transmit(uint8_t state) {
65
        if (state == UART_TX_ON) {
66
                PORTD |= TX_EN;
67
        } else {
68
                PORTD &= ~TX_EN;
69
        }
70
}
71

    
72
char parse_packet(uint8_t *mbuf) {
73
  uint8_t r;        // Byte from the network
74
  uint8_t crc;      // Running checksum of the packet
75
  uint8_t cmd;      // The command received
76
  uint8_t pos;      // Position in the message buffer
77
  uint8_t lim;      // Max number of bytes to read into the message buf
78
  state_t state;    // State machine
79
  uint16_t count;
80

    
81
  r = 0;
82
  crc = 0;
83
  cmd = 0;
84
  pos = 0;
85
  lim = 0;
86
  state = sd;
87
  count = 0;
88

    
89
  while (1) {
90
    // Wait for the next byte
91
    while ((uart_get_byte(&r)) < 0) {
92
        if (count >= MAX_TIMEOUT) {
93
            return TT_BAD;
94
        } else {
95
            count++;
96
        }
97
    }
98

    
99
    switch (state) {
100
        case sd:
101
            if (r == DELIM) {
102
                state = src;
103
            }
104
            break;
105

    
106
        case src:
107
            if (r == DELIM) {
108
                state = src;
109
            } else {
110
                crc = r;
111
                state = dest;
112
            }
113
            break;
114

    
115
        case dest:
116
            if (r == DELIM) {
117
                state = src;
118
            } else if (r == ADDR) {
119
                crc ^= r;
120
                state = comd;
121
            } else {
122
                state = sd;
123
            }
124
            break;
125

    
126
        case comd:
127
            cmd = r;
128
            crc ^= r;
129

    
130
            if (r == DELIM) {
131
                state = src;
132
            } else if (r == TT_PROGM) {
133
                lim = PROGM_PACKET_SIZE;
134
                state = read;
135
            } else if (r == TT_PROGD) {
136
                lim = PROGD_PACKET_SIZE;
137
                state = read;
138
            } else {
139
                state = cs;
140
            }
141
            break;
142

    
143
        case read:
144
            mbuf[pos] = r;
145
            crc ^= r;
146
            pos++;
147

    
148
            if (pos == lim) {
149
                state = cs;
150
            }
151

    
152
            break;
153
       
154
        case cs:
155
            if (r == crc) {
156
                return cmd;
157
            } else {
158
                return TT_BAD;
159
            }
160

    
161
            break;
162

    
163
        default:
164
            return TT_BAD;
165
    }
166
  }
167
}
168

    
169
void send_packet(uint8_t cmd) {
170
    uart_send_byte(DELIM);
171
    uart_send_byte(ADDR);
172
    uart_send_byte(SERVER);
173
    uart_send_byte(cmd);
174
    uart_send_byte(ACK_CRC ^ cmd);
175
}
176

    
177
// SPM_PAGESIZE is set to 32 bytes
178
void onboard_program_write(uint16_t page, uint8_t *buf) {
179
  uint16_t i;
180

    
181
  boot_page_erase (page);
182
  boot_spm_busy_wait ();      // Wait until the memory is erased.
183

    
184
  for (i=0; i < SPM_PAGESIZE; i+=2){
185
    // Set up little-endian word.
186
    boot_page_fill (page + i, buf[i] | (buf[i+1] <<8));
187
  }
188

    
189
  boot_page_write (page);     // Store buffer in flash page.
190
  boot_spm_busy_wait();       // Wait until the memory is written.
191
}
192

    
193
int main(void) {
194
  uint8_t mbuf[PROGD_PACKET_SIZE];
195
  rjump_t jbuf;
196
  uint16_t caddr = MAIN_ADDR;
197
  uint8_t iteration;
198
  uint8_t resp;
199
  uint16_t prog_len;
200
  uint8_t i;
201
  uint8_t retries;
202

    
203
retry_jpnt:
204
  iteration = 0;
205
  retries = 0;
206
  
207
  // Clear the watchdog timer
208
  MCUSR &= ~_BV(WDRF);
209
  wdt_disable();
210
  WDTCSR = 0;
211

    
212

    
213
  init_uart(51); //MAGIC NUMBER??
214

    
215
  //set LED pin as output
216
  LED_DDR |= 0x07;
217
  PORTB = 0x07;
218

    
219
  //Start bootloading process
220
  send_packet(TT_BOOT);
221

    
222
  resp = parse_packet(mbuf);
223

    
224
  // Enter programming mode 
225
  if (resp == TT_PROGM) {
226
    prog_len = mbuf[0];
227
    prog_len |= mbuf[1] << 8;
228

    
229
    // This will insert a NOP into the user code jump in case
230
    // the programming fails
231
    for (i = 0; i < PROGD_PACKET_SIZE; i++) {
232
      mbuf[i]= 0;
233
    }
234
    onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
235

    
236
  // Run user code
237
  } else {
238
      main_start();
239
  }
240

    
241
  send_packet(TT_ACK);
242

    
243
  while(1) {
244
      resp = parse_packet(mbuf);
245

    
246
      if (resp == TT_PROGD) {
247
          // We need to muck with the reset vector jump in the first page
248
          if (iteration == 0) {
249
              // Store the jump to user code
250
              jbuf.bytes[0] = mbuf[0];
251
              jbuf.bytes[1] = mbuf[1];
252
             
253
              // Rewrite the user code jump to be correct since we are
254
              // using relative jumps (rjmp)
255
              jbuf.sword &= 0x0FFF;
256
              jbuf.sword -= (BOOT_START >> 1) - 1;
257
              jbuf.sword &= 0x0FFF;
258
              jbuf.sword |= 0xC000;
259

    
260
              // Rewrite the reset vector to jump to the bootloader
261
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
262
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
263

    
264
              iteration = 1;
265
          }
266

    
267
          // Write the page to the flash
268
          onboard_program_write(caddr, mbuf);
269
          caddr += PROGD_PACKET_SIZE;
270
          retries = 0;
271
      } else {
272
          send_packet(TT_NACK);
273
          retries++;
274

    
275
          // If we failed too many times, reset. This goes to the start
276
          // of the bootloader function
277
          if (retries > MAX_RETRIES) {
278
              goto retry_jpnt;
279
          }
280
      }
281

    
282
      send_packet(TT_ACK);
283

    
284
      // Once we write the last packet we must override the jump to
285
      // user code to point to the correct address
286
      if (prog_len <= PROGD_PACKET_SIZE) {
287
          for (i = 0; i < PROGD_PACKET_SIZE; i++) {
288
              mbuf[i]= 0;
289
          }
290

    
291
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
292
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
293

    
294
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
295

    
296
          main_start();
297
      } else { 
298
          prog_len -= PROGD_PACKET_SIZE;
299
      }
300
  }
301

    
302
  // Should never get here
303
  return -1;
304
}