Project

General

Profile

Statistics
| Revision:

root / trunk / bootloader / bootloader.c @ 200

History | View | Annotate | Download (4.32 KB)

1
#include "bootloader.h"
2

    
3
// Setup the default fuses
4
// This seems to be broken for now...
5
/*
6
FUSES = {
7
    .low = (FUSE_SUT0 & FUSE_CKSEL3 & FUSE_CKSEL2 & FUSE_CKSEL0),
8
    .high = (FUSE_EESAVE & FUSE_SPIEN),
9
    .extended = (FUSE_SELFPRGEN)
10
};
11
*/
12

    
13
// Error thresholds
14
#define MAX_RETRIES 5       // Number of times to retry before giving up
15

    
16
//Status LED
17
#define LED_DDR  DDRB
18
#define LED_PORT PORTB
19
#define LED      PORTB1
20

    
21
/**
22
 * Where we store the jump to user code. The jump address is in words
23
 * due to how the rjmp instruction works. It is 1 word below the bootloader
24
 * We declare it as noreturn to save some space since we will never return
25
 * to the bootloader unless we do a system reset
26
 */
27
void (*main_start)(void) __attribute__((noreturn)) = BOOT_START/2 - 1;
28

    
29
typedef union {
30
    uint8_t bytes[2];
31
    int16_t sword;
32
} rjump_t;
33

    
34

    
35
// SPM_PAGESIZE is set to 32 bytes
36
void onboard_program_write(uint16_t page, uint8_t *buf) {
37
  uint16_t i;
38

    
39
  boot_page_erase (page);
40
  boot_spm_busy_wait ();      // Wait until the memory is erased.
41

    
42
  for (i=0; i < SPM_PAGESIZE; i+=2){
43
    // Set up little-endian word.
44
    boot_page_fill (page + i, buf[i] | (buf[i+1] <<8));
45
  }
46

    
47
  boot_page_write (page);     // Store buffer in flash page.
48
  boot_spm_busy_wait();       // Wait until the memory is written.
49
}
50

    
51
int main(void) {
52
  uint8_t mbuf[PROGD_PACKET_SIZE];
53
  rjump_t jbuf;
54
  uint16_t caddr = MAIN_ADDR;
55
  uint8_t iteration;
56
  uint8_t resp;
57
  uint16_t prog_len;
58
  uint8_t i;
59
  uint8_t retries;
60
  uint8_t addr;
61
  uint8_t boot;
62

    
63
retry_jpnt:
64
  iteration = 0;
65
  retries = 0;
66
  boot = 0;
67
  addr = eeprom_read_byte(EEPROM_ADDR);
68
  //set LED pins as output
69
  LED_DDR = 0x07;
70
  PORTB = 0x00;
71

    
72
  
73
  // Clear the watchdog timer
74
  if (MCUSR & _BV(WDRF)) {
75
      MCUSR &= ~_BV(WDRF);
76
      wdt_disable();
77
      WDTCSR = 0;
78
      boot = 1;
79
  }
80

    
81
  if (!(PINB & (_BV(PINB3) | _BV(PINB4)))) {
82
      PORTB |= 0x01;
83
      boot = 1;
84
  }
85

    
86
  rs485_init(BAUD9600);
87

    
88
  //Start bootloading process
89
  if (boot == 0) {
90
      PORTB |= 0x07;
91
      main_start();
92
  }
93

    
94
  send_packet(TT_BOOT, addr);
95
  resp = parse_packet(mbuf, addr);
96

    
97
  // Enter programming mode 
98
  if (resp == TT_PROGM) {
99
    prog_len = mbuf[0];
100
    prog_len |= mbuf[1] << 8;
101

    
102
    // This will insert a NOP into the user code jump in case
103
    // the programming fails
104
    for (i = 0; i < PROGD_PACKET_SIZE; i++) {
105
      mbuf[i]= 0;
106
    }
107
    onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
108

    
109
  // Run user code
110
  } else {
111
      PORTB = 0x07;
112
      main_start();
113
  }
114

    
115
  send_packet(TT_ACK, addr);
116

    
117
  while(1) {
118
      resp = parse_packet(mbuf, addr);
119

    
120
      if (resp == TT_PROGD) {
121
          // We need to muck with the reset vector jump in the first page
122
          if (iteration == 0) {
123
              // Store the jump to user code
124
              jbuf.bytes[0] = mbuf[0];
125
              jbuf.bytes[1] = mbuf[1];
126
             
127
              // Rewrite the user code jump to be correct since we are
128
              // using relative jumps (rjmp)
129
              jbuf.sword &= 0x0FFF;
130
              jbuf.sword -= (BOOT_START >> 1) - 1;
131
              jbuf.sword &= 0x0FFF;
132
              jbuf.sword |= 0xC000;
133

    
134
              // Rewrite the reset vector to jump to the bootloader
135
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
136
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
137

    
138
              iteration = 1;
139
          }
140

    
141
          // Write the page to the flash
142
          onboard_program_write(caddr, mbuf);
143
          caddr += PROGD_PACKET_SIZE;
144
          retries = 0;
145
      } else {
146
          send_packet(TT_NACK, addr);
147
          retries++;
148

    
149
          // If we failed too many times, reset. This goes to the start
150
          // of the bootloader function
151
          if (retries > MAX_RETRIES) {
152
              goto retry_jpnt;
153
          }
154
      }
155

    
156
      send_packet(TT_ACK, addr);
157

    
158
      // Once we write the last packet we must override the jump to
159
      // user code to point to the correct address
160
      if (prog_len <= PROGD_PACKET_SIZE) {
161
          for (i = 0; i < PROGD_PACKET_SIZE; i++) {
162
              mbuf[i]= 0;
163
          }
164

    
165
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
166
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
167

    
168
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
169
    
170
          PORTB = 0x07;
171
          main_start();
172
      } else { 
173
          prog_len -= PROGD_PACKET_SIZE;
174
      }
175
  }
176

    
177
  // Should never get here
178
  return -1;
179
}