Project

General

Profile

Statistics
| Revision:

root / trunk / bootloader / bootloader.c @ 209

History | View | Annotate | Download (4.75 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
/**
17
 * Where we store the jump to user code. The jump address is in words
18
 * due to how the rjmp instruction works. It is 1 word below the bootloader
19
 * We declare it as noreturn to save some space since we will never return
20
 * to the bootloader unless we do a system reset
21
 */
22
void (*main_start)(void) __attribute__((noreturn)) = BOOT_START/2 - 1;
23

    
24
typedef union {
25
    uint8_t bytes[2];
26
    int16_t sword;
27
} rjump_t;
28

    
29

    
30
// SPM_PAGESIZE is set to 32 bytes
31
void onboard_program_write(uint16_t page, uint8_t *buf) {
32
  uint16_t i;
33

    
34
  boot_page_erase (page);
35
  boot_spm_busy_wait ();      // Wait until the memory is erased.
36

    
37
  for (i=0; i < SPM_PAGESIZE; i+=2){
38
    // Set up little-endian word.
39
    boot_page_fill (page + i, buf[i] | (buf[i+1] <<8));
40
  }
41

    
42
  boot_page_write (page);     // Store buffer in flash page.
43
  boot_spm_busy_wait();       // Wait until the memory is written.
44
}
45

    
46
int main(void) {
47
  uint8_t mbuf[PROGD_PACKET_SIZE];
48
  rjump_t jbuf;
49
  uint16_t caddr;
50
  uint16_t prog_len;
51
  uint8_t resp;
52
  uint8_t i;
53
  uint8_t retries;
54
  uint8_t addr;
55
  uint8_t boot;
56

    
57
  // This is where we jump to if we fail out because of retries
58
  retry_jpnt:
59

    
60
  // We set the variables here
61
  caddr = MAIN_ADDR;
62
  retries = 0;
63
  boot = FALSE;
64

    
65
  // Grab the address from EEPROM
66
  addr = eeprom_read_byte((void*)EEPROM_ADDR);
67

    
68
  // Initialize the Pins
69
  DDRB = LED_GREEN | LED_YELLOW | LED_RED;  // Set the LED pins as outputs
70
  PORTB = 0x00;                             // LEDs on == in bootloader
71
  DDRD = RELAY;                             // Set Relay as an output
72
  PORTD = 0x00;                             // Turn relay off
73

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

    
82
  if (!(BUT_PORT & (BUT_RED | BUT_BLACK))) {
83
      boot = TRUE;
84
  }
85

    
86
  // Initialize the RS485
87
  rs485_init(BAUD9600);
88

    
89
  //Execute user code if we aren't supposed to enter bootloading mode
90
  if (boot == FALSE) {
91
      goto run_user_code;
92
  }
93

    
94
  // Send the boot packet
95
  send_packet(TT_BOOT, addr);
96
  resp = parse_packet(mbuf, addr);
97

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

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

    
110
  // Run user code
111
  } else {
112
      run_user_code:
113
      LED_PORT = LED_GREEN | LED_YELLOW | LED_RED;
114
      main_start();
115
  }
116

    
117
  send_packet(TT_ACK, addr);
118

    
119
  while(1) {
120
      resp = parse_packet(mbuf, addr);
121

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

    
136
              // Rewrite the reset vector to jump to the bootloader
137
              mbuf[0] = (BOOT_START/2 - 1) & 0xFF;
138
              mbuf[1] = 0xC0 | (((BOOT_START/2 - 1) >> 8) & 0x0F);
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
          } else {
154
              continue;
155
          }
156
      }
157

    
158
      send_packet(TT_ACK, addr);
159

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

    
167
          mbuf[PROGD_PACKET_SIZE-2] = jbuf.bytes[0];
168
          mbuf[PROGD_PACKET_SIZE-1] = jbuf.bytes[1];
169

    
170
          onboard_program_write(BOOT_START - SPM_PAGESIZE, mbuf);
171
    
172
          // Jump to user code that we just wrote
173
          goto run_user_code;
174
      } else { 
175
          prog_len -= PROGD_PACKET_SIZE;
176
      }
177
  }
178

    
179
  // Should never get here
180
  return -1;
181
}