Rev 1868 | Rev 2034 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | SVN | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 361 | andfri | 1 | /* |
| 2 | * $Id: bios.c 2032 2012-06-27 23:08:42Z pengi $ |
||
| 3 | */ |
||
| 1641 | nattgris | 4 | |
| 361 | andfri | 5 | #include <inttypes.h> |
| 6 | #include <avr/io.h> |
||
| 7 | #include <avr/interrupt.h> |
||
| 8 | #include <avr/wdt.h> |
||
| 9 | #include <avr/pgmspace.h> |
||
| 10 | #include <string.h> |
||
| 11 | #include <util/crc16.h> |
||
| 12 | #include <config.h> |
||
| 13 | #include <vectors.h> // Must be included after sfr_defs.h but before any ISR() |
||
| 14 | #include <drivers/can/can.h> |
||
| 15 | #include <flash.h> |
||
| 16 | #include <bios_export.h> |
||
| 17 | #include CAN_DRIVER_H |
||
| 18 | |||
| 769 | arune | 19 | #if !defined( NODE_HW_ID ) |
| 20 | #error No NODE_HW_ID in bios.inc, set NODE_HW_ID=<GENERATE_HW> and Ill generate it for you |
||
| 21 | #endif |
||
| 22 | |||
| 361 | andfri | 23 | #if defined(__AVR_ATmega8__) |
| 24 | #define IVSEL_REG GICR |
||
| 1468 | arune | 25 | #elif defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) || defined(__AVR_ATmega328P__) |
| 361 | andfri | 26 | #define IVSEL_REG MCUCR |
| 27 | #else |
||
| 28 | #error AVR device not supported! |
||
| 29 | #endif |
||
| 30 | |||
| 1629 | arune | 31 | /* DEVICETYPE is sent in bios start frame */ |
| 32 | #if defined(__AVR_ATmega8__) |
||
| 1631 | linlun | 33 | #define DEVICETYPE 1 |
| 1641 | nattgris | 34 | #elif defined(__AVR_ATmega88__) |
| 1631 | linlun | 35 | #define DEVICETYPE 3 |
| 1630 | arune | 36 | #elif defined(__AVR_ATmega168__) || defined(__AVR_ATmega168P__) |
| 1631 | linlun | 37 | #define DEVICETYPE 4 |
| 1629 | arune | 38 | #elif defined(__AVR_ATmega328P__) |
| 1631 | linlun | 39 | #define DEVICETYPE 5 |
| 1629 | arune | 40 | #else |
| 41 | #error AVR device not supported! |
||
| 42 | #endif |
||
| 43 | |||
| 585 | arune | 44 | #if defined(AUTOSTART) |
| 1641 | nattgris | 45 | #if AUTOSTART == 0 |
| 585 | arune | 46 | #error Please choose a higher AUTOSTART value in bios.inc, the nod will cease to work, and you can only reflash node with ISP |
| 47 | #endif |
||
| 48 | #if AUTOSTART == 1 |
||
| 49 | #error Please choose a higher AUTOSTART value in bios.inc, there might be problems with updating software on node |
||
| 50 | #endif |
||
| 51 | #endif |
||
| 52 | |||
| 361 | andfri | 53 | //--------------------------------------------------------------------------- |
| 54 | // Private declarations |
||
| 55 | |||
| 56 | volatile uint8_t bios_msg_full; |
||
| 57 | Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space |
||
| 1868 | arune | 58 | extern char __bios_start; // Start of BIOS area in flash, from ld-script. |
| 361 | andfri | 59 | |
| 769 | arune | 60 | prog_uint32_t hwid = NODE_HW_ID; |
| 61 | |||
| 585 | arune | 62 | #if defined(AUTOSTART) |
| 63 | uint8_t autostart_cnt; |
||
| 64 | #endif |
||
| 65 | |||
| 1641 | nattgris | 66 | #define BIOS_APP 1 |
| 67 | #define BIOS_NOAPP 2 |
||
| 68 | #define BIOS_PGM 3 |
||
| 69 | #define BIOS_END_PGM 4 |
||
| 361 | andfri | 70 | |
| 71 | //--------------------------------------------------------------------------- |
||
| 72 | // Private functions |
||
| 73 | |||
| 74 | void Can_Process(Can_Message_t* msg) { |
||
| 75 | if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames. |
||
| 1641 | nattgris | 76 | |
| 361 | andfri | 77 | if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) { |
| 1641 | nattgris | 78 | |
| 361 | andfri | 79 | if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) { |
| 80 | // Reset watchdog as long as time messages from the master node |
||
| 81 | // are received periodically. If anything hangs the CAN communication |
||
| 82 | // or the master node is not up, the node will be reset. |
||
| 83 | wdt_reset(); |
||
| 1641 | nattgris | 84 | //TODO: Don't care about time right now... Figure out what to do with it. |
| 585 | arune | 85 | #if defined(AUTOSTART) |
| 86 | autostart_cnt++; |
||
| 87 | #endif |
||
| 361 | andfri | 88 | } |
| 1641 | nattgris | 89 | |
| 90 | if (((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE) == CAN_NMT_RESET |
||
| 91 | && msg->DataLength == 4 |
||
| 92 | && msg->Data.bytes[0] == (hwid&0xff) |
||
| 93 | && msg->Data.bytes[1] == ((hwid>>8)&0xff) |
||
| 94 | && msg->Data.bytes[2] == ((hwid>>16)&0xff) |
||
| 95 | && msg->Data.bytes[3] == ((hwid>>24)&0xff)) { |
||
| 769 | arune | 96 | BIOS_Reset(); |
| 97 | } |
||
| 98 | // Copy message to bios buffer if bios is done with the previous message. |
||
| 99 | // This is also a check to see if bios is still running. When app is |
||
| 100 | // started, bios_msg_full is never reset to 0 so the check fails and all |
||
| 1641 | nattgris | 101 | // NMT communication except reset is ignored. |
| 769 | arune | 102 | if (!(bios_msg_full)) { |
| 103 | memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t)); |
||
| 104 | bios_msg_full = 1; |
||
| 105 | } |
||
| 2032 | pengi | 106 | } |
| 107 | if (BIOS_CanCallback) { |
||
| 361 | andfri | 108 | //printf("Calling app callback.\n"); |
| 109 | BIOS_CanCallback(msg); |
||
| 110 | } |
||
| 111 | } |
||
| 112 | |||
| 563 | andfri | 113 | int main(void) { |
| 361 | andfri | 114 | void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector. |
| 115 | uint8_t bios_state; |
||
| 116 | uint8_t nmt_type; |
||
| 117 | uint8_t pagebuf[SPM_PAGESIZE]; |
||
| 118 | // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy! |
||
| 119 | uint16_t base_addr = 0; |
||
| 120 | uint16_t offset = 0; |
||
| 121 | uint16_t addr = 0; |
||
| 122 | uint8_t len; |
||
| 123 | uint16_t data; |
||
| 124 | uint8_t send_msg = 0; |
||
| 125 | Can_Message_t tx_msg; |
||
| 126 | Can_Message_t bios_msg; |
||
| 127 | bios_msg_ptr = &bios_msg; |
||
| 585 | arune | 128 | |
| 129 | #if defined(AUTOSTART) |
||
| 130 | autostart_cnt = 0; |
||
| 131 | #endif |
||
| 132 | |||
| 361 | andfri | 133 | // Enable watchdog timer to protect against an application locking the |
| 1641 | nattgris | 134 | // node by leaving interrupts disabled. |
| 361 | andfri | 135 | wdt_enable(WDTO_2S); |
| 1641 | nattgris | 136 | |
| 361 | andfri | 137 | // Move interrupt vectors to start of bootloader section and enable interrupts |
| 138 | IVSEL_REG = _BV(IVCE); |
||
| 139 | IVSEL_REG = _BV(IVSEL); |
||
| 140 | sei(); |
||
| 1641 | nattgris | 141 | |
| 361 | andfri | 142 | if (Can_Init() != CAN_OK) BIOS_Reset(); |
| 1747 | arune | 143 | #if defined(INITDDRB) |
| 144 | DDRB=INITDDRB; |
||
| 145 | #endif |
||
| 146 | #if defined(INITDDRC) |
||
| 147 | DDRC=INITDDRC; |
||
| 148 | #endif |
||
| 149 | #if defined(INITDDRD) |
||
| 150 | DDRD=INITDDRD; |
||
| 151 | #endif |
||
| 152 | #if defined(INITPORTB) |
||
| 153 | PORTB=INITPORTB; |
||
| 154 | #endif |
||
| 155 | #if defined(INITPORTC) |
||
| 156 | PORTC=INITPORTC; |
||
| 157 | #endif |
||
| 158 | #if defined(INITPORTD) |
||
| 159 | PORTD=INITPORTD; |
||
| 160 | #endif |
||
| 161 | #if defined(INITPINB) |
||
| 162 | PINB=INITPINB; |
||
| 163 | #endif |
||
| 164 | #if defined(INITPINC) |
||
| 165 | PINC=INITPINC; |
||
| 166 | #endif |
||
| 167 | #if defined(INITPIND) |
||
| 168 | PIND=INITPIND; |
||
| 169 | #endif |
||
| 1641 | nattgris | 170 | |
| 1747 | arune | 171 | |
| 361 | andfri | 172 | tx_msg.RemoteFlag = 0; |
| 173 | tx_msg.ExtendedFlag = 1; |
||
| 174 | tx_msg.Id = CAN_ID_NMT_BIOS_START; |
||
| 769 | arune | 175 | tx_msg.DataLength = 8; |
| 176 | tx_msg.Data.bytes[0] = BIOS_VERSION&0xff; |
||
| 177 | tx_msg.Data.bytes[1] = (BIOS_VERSION>>8)&0xff; |
||
| 1629 | arune | 178 | tx_msg.Data.bytes[3] = DEVICETYPE; |
| 769 | arune | 179 | tx_msg.Data.bytes[4] = hwid&0xff; |
| 180 | tx_msg.Data.bytes[5] = (hwid>>8)&0xff; |
||
| 181 | tx_msg.Data.bytes[6] = (hwid>>16)&0xff; |
||
| 182 | tx_msg.Data.bytes[7] = (hwid>>24)&0xff; |
||
| 361 | andfri | 183 | |
| 184 | if (pgm_read_word(0) == 0xffff) { |
||
| 185 | // No application in flash |
||
| 186 | // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0) |
||
| 187 | tx_msg.Data.bytes[2] = 0; |
||
| 188 | bios_state = BIOS_NOAPP; |
||
| 189 | } else { |
||
| 190 | // Application exists |
||
| 191 | // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1) |
||
| 192 | tx_msg.Data.bytes[2] = 1; |
||
| 193 | bios_state = BIOS_APP; |
||
| 194 | } |
||
| 1641 | nattgris | 195 | |
| 361 | andfri | 196 | while (Can_Send(&tx_msg) != CAN_OK); |
| 197 | |||
| 198 | tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once. |
||
| 1641 | nattgris | 199 | |
| 361 | andfri | 200 | // Main loop |
| 201 | while (1) { |
||
| 202 | // Wait for message |
||
| 585 | arune | 203 | while (!bios_msg_full) { |
| 204 | #if defined(AUTOSTART) |
||
| 205 | if (bios_state == BIOS_APP && autostart_cnt == AUTOSTART) { |
||
| 206 | app_reset(); |
||
| 207 | } |
||
| 208 | #endif |
||
| 209 | } |
||
| 1641 | nattgris | 210 | |
| 361 | andfri | 211 | nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE; |
| 1641 | nattgris | 212 | |
| 361 | andfri | 213 | switch (bios_state) { |
| 214 | case BIOS_APP: |
||
| 769 | arune | 215 | //if (nmt_type == CAN_NMT_START_APP) { |
| 1641 | nattgris | 216 | if (nmt_type == CAN_NMT_START_APP |
| 217 | && bios_msg.DataLength == 4 |
||
| 218 | && bios_msg.Data.bytes[0] == (hwid&0xff) |
||
| 219 | && bios_msg.Data.bytes[1] == ((hwid>>8)&0xff) |
||
| 220 | && bios_msg.Data.bytes[2] == ((hwid>>16)&0xff) |
||
| 221 | && bios_msg.Data.bytes[3] == ((hwid>>24)&0xff)) { |
||
| 361 | andfri | 222 | app_reset(); // Will never return |
| 223 | } |
||
| 224 | // Fall through |
||
| 225 | case BIOS_NOAPP: |
||
| 769 | arune | 226 | //if (nmt_type == CAN_NMT_PGM_START) { |
| 1641 | nattgris | 227 | if (nmt_type == CAN_NMT_PGM_START |
| 228 | && bios_msg.DataLength == 8 |
||
| 229 | && bios_msg.Data.bytes[4] == (hwid&0xff) |
||
| 230 | && bios_msg.Data.bytes[5] == ((hwid>>8)&0xff) |
||
| 231 | && bios_msg.Data.bytes[6] == ((hwid>>16)&0xff) |
||
| 232 | && bios_msg.Data.bytes[7] == ((hwid>>24)&0xff)) { |
||
| 361 | andfri | 233 | // Set base address |
| 234 | base_addr = bios_msg.Data.words[0]; |
||
| 235 | flash_init(pagebuf); |
||
| 236 | //send CAN_NMT_PGM_ACK(offset) |
||
| 237 | tx_msg.Id = CAN_ID_NMT_PGM_ACK; |
||
| 238 | tx_msg.Data.words[0] = base_addr; |
||
| 239 | send_msg = 1; |
||
| 240 | bios_state = BIOS_PGM; |
||
| 241 | } |
||
| 242 | break; |
||
| 243 | case BIOS_PGM: |
||
| 244 | // Default to send CAN_NMT_PGM_ACK(offset) |
||
| 245 | tx_msg.Id = CAN_ID_NMT_PGM_ACK; |
||
| 1641 | nattgris | 246 | |
| 361 | andfri | 247 | if (nmt_type == CAN_NMT_PGM_DATA) { |
| 248 | // Set address = base address + offset. |
||
| 249 | offset = bios_msg.Data.words[0]; |
||
| 250 | addr = base_addr + offset; |
||
| 1641 | nattgris | 251 | |
| 361 | andfri | 252 | // One of ACK and NACK will be sent, both have offset as data. |
| 253 | tx_msg.Data.words[0] = offset; |
||
| 254 | send_msg = 1; |
||
| 255 | |||
| 256 | // Flash all data sent, beginning at addr. |
||
| 257 | len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up. |
||
| 258 | uint8_t i; |
||
| 259 | for (i=1; i<len; i++) { // Skip first word (offset). |
||
| 1641 | nattgris | 260 | |
| 361 | andfri | 261 | // Abort if trying to write in BIOS area. |
| 262 | if (addr >= (uint16_t)&__bios_start) { |
||
| 263 | // Send CAN_NMT_PGM_NACK(offset). |
||
| 264 | tx_msg.Id = CAN_ID_NMT_PGM_NACK; |
||
| 265 | break; //for loop |
||
| 266 | } |
||
| 267 | |||
| 268 | data = bios_msg.Data.words[i]; |
||
| 269 | flash_write_word(addr, data); |
||
| 270 | addr += 2; |
||
| 271 | } |
||
| 272 | // Adjust addr if an odd number of bytes were sent, for use in crc calc. |
||
| 1641 | nattgris | 273 | addr -= bios_msg.DataLength & 1; |
| 361 | andfri | 274 | } |
| 275 | if (nmt_type == CAN_NMT_PGM_END) { |
||
| 276 | // Make sure a partly filled page will be flashed. |
||
| 277 | flash_flush_buffer(); |
||
| 1641 | nattgris | 278 | |
| 361 | andfri | 279 | // Calculate crc on written data. |
| 280 | //TODO: This will not work if data is not sent sequentially without |
||
| 281 | // holes, starting with offset=0. We might want to send a NACK if the |
||
| 282 | // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset. |
||
| 283 | uint16_t loc, crc = 0; |
||
| 284 | for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2. |
||
| 285 | crc = _crc16_update(crc, pgm_read_byte(loc)); |
||
| 286 | } |
||
| 1641 | nattgris | 287 | |
| 361 | andfri | 288 | tx_msg.Data.words[0] = crc; |
| 289 | send_msg = 1; |
||
| 769 | arune | 290 | bios_state = BIOS_END_PGM; |
| 361 | andfri | 291 | } |
| 769 | arune | 292 | break; |
| 293 | case BIOS_END_PGM: |
||
| 294 | if (nmt_type == CAN_NMT_PGM_COPY) { |
||
| 295 | // For BIOS updating over CAN. Upload bios to application area, |
||
| 296 | // send this message with correct parameters to copy data from |
||
| 297 | // application to bios area and pray it will come alive again. |
||
| 1641 | nattgris | 298 | flash_copy_data(bios_msg.Data.words[0], |
| 299 | bios_msg.Data.words[1], |
||
| 769 | arune | 300 | bios_msg.Data.words[2]); |
| 301 | // flash_copy_data will never return. |
||
| 302 | } |
||
| 361 | andfri | 303 | } |
| 1641 | nattgris | 304 | |
| 361 | andfri | 305 | if (send_msg) { |
| 306 | while (Can_Send(&tx_msg) != CAN_OK); |
||
| 307 | send_msg = 0; |
||
| 308 | } |
||
| 309 | |||
| 310 | bios_msg_full = 0; // We're done with this message, ready for the next. |
||
| 311 | } |
||
| 312 | |||
| 313 | return 0; |
||
| 314 | } |