Project

General

Profile

Statistics
| Revision:

root / trunk / bootloader / bootloader.c @ 184

History | View | Annotate | Download (5.82 KB)

1
#include <avr/io.h>
2
#include <avr/boot.h>
3
#include <avr/wdt.h>
4
#include <tooltron.h>
5
#include "rs485_sw.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

    
27
char parse_packet(uint8_t *mbuf) {
28
  uint8_t r;        // Byte from the network
29
  uint8_t crc;      // Running checksum of the packet
30
  uint8_t cmd;      // The command received
31
  uint8_t pos;      // Position in the message buffer
32
  uint8_t lim;      // Max number of bytes to read into the message buf
33
  state_t state;    // State machine
34
  uint16_t count;
35

    
36
  r = 0;
37
  crc = 0;
38
  cmd = 0;
39
  pos = 0;
40
  lim = 0;
41
  state = sd;
42
  count = 0;
43

    
44
  while (1) {
45
    // Wait for the next byte
46
    while ((rs485_get_byte(&r)) < 0) {
47
        if (count >= MAX_TIMEOUT) {
48
            return TT_BAD;
49
        } else {
50
            count++;
51
        }
52
    }
53

    
54
    switch (state) {
55
        case sd:
56
            if (r == DELIM) {
57
                state = src;
58
            }
59
            break;
60

    
61
        case src:
62
            if (r == DELIM) {
63
                state = src;
64
            } else {
65
                crc = r;
66
                state = dest;
67
            }
68
            break;
69

    
70
        case dest:
71
            if (r == DELIM) {
72
                state = src;
73
            } else if (r == ADDR) {
74
                crc ^= r;
75
                state = comd;
76
            } else {
77
                state = sd;
78
            }
79
            break;
80

    
81
        case comd:
82
            cmd = r;
83
            crc ^= r;
84

    
85
            if (r == DELIM) {
86
                state = src;
87
            } else if (r == TT_PROGM) {
88
                lim = PROGM_PACKET_SIZE;
89
                state = read;
90
            } else if (r == TT_PROGD) {
91
                lim = PROGD_PACKET_SIZE;
92
                state = read;
93
            } else {
94
                state = cs;
95
            }
96
            break;
97

    
98
        case read:
99
            mbuf[pos] = r;
100
            crc ^= r;
101
            pos++;
102

    
103
            if (pos == lim) {
104
                state = cs;
105
            }
106

    
107
            break;
108
       
109
        case cs:
110
            if (r == crc) {
111
                return cmd;
112
            } else {
113
                return TT_BAD;
114
            }
115

    
116
            break;
117

    
118
        default:
119
            return TT_BAD;
120
    }
121
  }
122
}
123

    
124
void send_packet(uint8_t cmd) {
125
    rs485_send_byte(DELIM);
126
    rs485_send_byte(ADDR);
127
    rs485_send_byte(SERVER);
128
    rs485_send_byte(cmd);
129
    rs485_send_byte(ACK_CRC ^ cmd);
130
}
131

    
132
// SPM_PAGESIZE is set to 32 bytes
133
void onboard_program_write(uint16_t page, uint8_t *buf) {
134
  uint16_t i;
135

    
136
  boot_page_erase (page);
137
  boot_spm_busy_wait ();      // Wait until the memory is erased.
138

    
139
  for (i=0; i < SPM_PAGESIZE; i+=2){
140
    // Set up little-endian word.
141
    boot_page_fill (page + i, buf[i] | (buf[i+1] <<8));
142
  }
143

    
144
  boot_page_write (page);     // Store buffer in flash page.
145
  boot_spm_busy_wait();       // Wait until the memory is written.
146
}
147

    
148
int main(void) {
149
  uint8_t mbuf[PROGD_PACKET_SIZE];
150
  rjump_t jbuf;
151
  uint16_t caddr = MAIN_ADDR;
152
  uint8_t iteration;
153
  uint8_t resp;
154
  uint16_t prog_len;
155
  uint8_t i;
156
  uint8_t retries;
157

    
158
retry_jpnt:
159
  iteration = 0;
160
  retries = 0;
161
  
162
  // Clear the watchdog timer
163
  MCUSR &= ~_BV(WDRF);
164
  wdt_disable();
165
  WDTCSR = 0;
166

    
167

    
168
  rs485_init(51); //MAGIC NUMBER??
169

    
170
  //set LED pin as output
171
  LED_DDR |= 0x07;
172
  PORTB = 0x07;
173

    
174
  //Start bootloading process
175
  send_packet(TT_BOOT);
176

    
177
  resp = parse_packet(mbuf);
178

    
179
  // Enter programming mode 
180
  if (resp == TT_PROGM) {
181
    prog_len = mbuf[0];
182
    prog_len |= mbuf[1] << 8;
183

    
184
    // This will insert a NOP into the user code jump in case
185
    // the programming fails
186
    for (i = 0; i < PROGD_PACKET_SIZE; i++) {
187
      mbuf[i]= 0;
188
    }
189
    onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
190

    
191
  // Run user code
192
  } else {
193
      main_start();
194
  }
195

    
196
  send_packet(TT_ACK);
197

    
198
  while(1) {
199
      resp = parse_packet(mbuf);
200

    
201
      if (resp == TT_PROGD) {
202
          // We need to muck with the reset vector jump in the first page
203
          if (iteration == 0) {
204
              // Store the jump to user code
205
              jbuf.bytes[0] = mbuf[0];
206
              jbuf.bytes[1] = mbuf[1];
207
             
208
              // Rewrite the user code jump to be correct since we are
209
              // using relative jumps (rjmp)
210
              jbuf.sword &= 0x0FFF;
211
              jbuf.sword -= (BOOT_START >> 1) - 1;
212
              jbuf.sword &= 0x0FFF;
213
              jbuf.sword |= 0xC000;
214

    
215
              // Rewrite the reset vector to jump to the bootloader
216
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
217
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
218

    
219
              iteration = 1;
220
          }
221

    
222
          // Write the page to the flash
223
          onboard_program_write(caddr, mbuf);
224
          caddr += PROGD_PACKET_SIZE;
225
          retries = 0;
226
      } else {
227
          send_packet(TT_NACK);
228
          retries++;
229

    
230
          // If we failed too many times, reset. This goes to the start
231
          // of the bootloader function
232
          if (retries > MAX_RETRIES) {
233
              goto retry_jpnt;
234
          }
235
      }
236

    
237
      send_packet(TT_ACK);
238

    
239
      // Once we write the last packet we must override the jump to
240
      // user code to point to the correct address
241
      if (prog_len <= PROGD_PACKET_SIZE) {
242
          for (i = 0; i < PROGD_PACKET_SIZE; i++) {
243
              mbuf[i]= 0;
244
          }
245

    
246
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
247
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
248

    
249
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
250

    
251
          main_start();
252
      } else { 
253
          prog_len -= PROGD_PACKET_SIZE;
254
      }
255
  }
256

    
257
  // Should never get here
258
  return -1;
259
}