Rev 1631 | Rev 1868 | 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 1641 2010-12-05 22:37:37Z nattgris $ |
||
| 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 |
||
| 58 | extern void __bios_start; // Start of BIOS area in flash, from ld-script. |
||
| 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 | } |
||
| 361 | andfri | 106 | } else if (BIOS_CanCallback) { |
| 107 | //printf("Calling app callback.\n"); |
||
| 108 | BIOS_CanCallback(msg); |
||
| 109 | } |
||
| 110 | } |
||
| 111 | |||
| 563 | andfri | 112 | int main(void) { |
| 361 | andfri | 113 | void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector. |
| 114 | uint8_t bios_state; |
||
| 115 | uint8_t nmt_type; |
||
| 116 | uint8_t pagebuf[SPM_PAGESIZE]; |
||
| 117 | // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy! |
||
| 118 | uint16_t base_addr = 0; |
||
| 119 | uint16_t offset = 0; |
||
| 120 | uint16_t addr = 0; |
||
| 121 | uint8_t len; |
||
| 122 | uint16_t data; |
||
| 123 | uint8_t send_msg = 0; |
||
| 124 | Can_Message_t tx_msg; |
||
| 125 | Can_Message_t bios_msg; |
||
| 126 | bios_msg_ptr = &bios_msg; |
||
| 585 | arune | 127 | |
| 128 | #if defined(AUTOSTART) |
||
| 129 | autostart_cnt = 0; |
||
| 130 | #endif |
||
| 131 | |||
| 361 | andfri | 132 | // Enable watchdog timer to protect against an application locking the |
| 1641 | nattgris | 133 | // node by leaving interrupts disabled. |
| 361 | andfri | 134 | wdt_enable(WDTO_2S); |
| 1641 | nattgris | 135 | |
| 361 | andfri | 136 | // Move interrupt vectors to start of bootloader section and enable interrupts |
| 137 | IVSEL_REG = _BV(IVCE); |
||
| 138 | IVSEL_REG = _BV(IVSEL); |
||
| 139 | sei(); |
||
| 1641 | nattgris | 140 | |
| 361 | andfri | 141 | if (Can_Init() != CAN_OK) BIOS_Reset(); |
| 1641 | nattgris | 142 | |
| 361 | andfri | 143 | tx_msg.RemoteFlag = 0; |
| 144 | tx_msg.ExtendedFlag = 1; |
||
| 145 | tx_msg.Id = CAN_ID_NMT_BIOS_START; |
||
| 769 | arune | 146 | tx_msg.DataLength = 8; |
| 147 | tx_msg.Data.bytes[0] = BIOS_VERSION&0xff; |
||
| 148 | tx_msg.Data.bytes[1] = (BIOS_VERSION>>8)&0xff; |
||
| 1629 | arune | 149 | tx_msg.Data.bytes[3] = DEVICETYPE; |
| 769 | arune | 150 | tx_msg.Data.bytes[4] = hwid&0xff; |
| 151 | tx_msg.Data.bytes[5] = (hwid>>8)&0xff; |
||
| 152 | tx_msg.Data.bytes[6] = (hwid>>16)&0xff; |
||
| 153 | tx_msg.Data.bytes[7] = (hwid>>24)&0xff; |
||
| 361 | andfri | 154 | |
| 155 | if (pgm_read_word(0) == 0xffff) { |
||
| 156 | // No application in flash |
||
| 157 | // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0) |
||
| 158 | tx_msg.Data.bytes[2] = 0; |
||
| 159 | bios_state = BIOS_NOAPP; |
||
| 160 | } else { |
||
| 161 | // Application exists |
||
| 162 | // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1) |
||
| 163 | tx_msg.Data.bytes[2] = 1; |
||
| 164 | bios_state = BIOS_APP; |
||
| 165 | } |
||
| 1641 | nattgris | 166 | |
| 361 | andfri | 167 | while (Can_Send(&tx_msg) != CAN_OK); |
| 168 | |||
| 169 | tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once. |
||
| 1641 | nattgris | 170 | |
| 361 | andfri | 171 | // Main loop |
| 172 | while (1) { |
||
| 173 | // Wait for message |
||
| 585 | arune | 174 | while (!bios_msg_full) { |
| 175 | #if defined(AUTOSTART) |
||
| 176 | if (bios_state == BIOS_APP && autostart_cnt == AUTOSTART) { |
||
| 177 | app_reset(); |
||
| 178 | } |
||
| 179 | #endif |
||
| 180 | } |
||
| 1641 | nattgris | 181 | |
| 361 | andfri | 182 | nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE; |
| 1641 | nattgris | 183 | |
| 361 | andfri | 184 | switch (bios_state) { |
| 185 | case BIOS_APP: |
||
| 769 | arune | 186 | //if (nmt_type == CAN_NMT_START_APP) { |
| 1641 | nattgris | 187 | if (nmt_type == CAN_NMT_START_APP |
| 188 | && bios_msg.DataLength == 4 |
||
| 189 | && bios_msg.Data.bytes[0] == (hwid&0xff) |
||
| 190 | && bios_msg.Data.bytes[1] == ((hwid>>8)&0xff) |
||
| 191 | && bios_msg.Data.bytes[2] == ((hwid>>16)&0xff) |
||
| 192 | && bios_msg.Data.bytes[3] == ((hwid>>24)&0xff)) { |
||
| 361 | andfri | 193 | app_reset(); // Will never return |
| 194 | } |
||
| 195 | // Fall through |
||
| 196 | case BIOS_NOAPP: |
||
| 769 | arune | 197 | //if (nmt_type == CAN_NMT_PGM_START) { |
| 1641 | nattgris | 198 | if (nmt_type == CAN_NMT_PGM_START |
| 199 | && bios_msg.DataLength == 8 |
||
| 200 | && bios_msg.Data.bytes[4] == (hwid&0xff) |
||
| 201 | && bios_msg.Data.bytes[5] == ((hwid>>8)&0xff) |
||
| 202 | && bios_msg.Data.bytes[6] == ((hwid>>16)&0xff) |
||
| 203 | && bios_msg.Data.bytes[7] == ((hwid>>24)&0xff)) { |
||
| 361 | andfri | 204 | // Set base address |
| 205 | base_addr = bios_msg.Data.words[0]; |
||
| 206 | flash_init(pagebuf); |
||
| 207 | //send CAN_NMT_PGM_ACK(offset) |
||
| 208 | tx_msg.Id = CAN_ID_NMT_PGM_ACK; |
||
| 209 | tx_msg.Data.words[0] = base_addr; |
||
| 210 | send_msg = 1; |
||
| 211 | bios_state = BIOS_PGM; |
||
| 212 | } |
||
| 213 | break; |
||
| 214 | case BIOS_PGM: |
||
| 215 | // Default to send CAN_NMT_PGM_ACK(offset) |
||
| 216 | tx_msg.Id = CAN_ID_NMT_PGM_ACK; |
||
| 1641 | nattgris | 217 | |
| 361 | andfri | 218 | if (nmt_type == CAN_NMT_PGM_DATA) { |
| 219 | // Set address = base address + offset. |
||
| 220 | offset = bios_msg.Data.words[0]; |
||
| 221 | addr = base_addr + offset; |
||
| 1641 | nattgris | 222 | |
| 361 | andfri | 223 | // One of ACK and NACK will be sent, both have offset as data. |
| 224 | tx_msg.Data.words[0] = offset; |
||
| 225 | send_msg = 1; |
||
| 226 | |||
| 227 | // Flash all data sent, beginning at addr. |
||
| 228 | len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up. |
||
| 229 | uint8_t i; |
||
| 230 | for (i=1; i<len; i++) { // Skip first word (offset). |
||
| 1641 | nattgris | 231 | |
| 361 | andfri | 232 | // Abort if trying to write in BIOS area. |
| 233 | if (addr >= (uint16_t)&__bios_start) { |
||
| 234 | // Send CAN_NMT_PGM_NACK(offset). |
||
| 235 | tx_msg.Id = CAN_ID_NMT_PGM_NACK; |
||
| 236 | break; //for loop |
||
| 237 | } |
||
| 238 | |||
| 239 | data = bios_msg.Data.words[i]; |
||
| 240 | flash_write_word(addr, data); |
||
| 241 | addr += 2; |
||
| 242 | } |
||
| 243 | // Adjust addr if an odd number of bytes were sent, for use in crc calc. |
||
| 1641 | nattgris | 244 | addr -= bios_msg.DataLength & 1; |
| 361 | andfri | 245 | } |
| 246 | if (nmt_type == CAN_NMT_PGM_END) { |
||
| 247 | // Make sure a partly filled page will be flashed. |
||
| 248 | flash_flush_buffer(); |
||
| 1641 | nattgris | 249 | |
| 361 | andfri | 250 | // Calculate crc on written data. |
| 251 | //TODO: This will not work if data is not sent sequentially without |
||
| 252 | // holes, starting with offset=0. We might want to send a NACK if the |
||
| 253 | // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset. |
||
| 254 | uint16_t loc, crc = 0; |
||
| 255 | for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2. |
||
| 256 | crc = _crc16_update(crc, pgm_read_byte(loc)); |
||
| 257 | } |
||
| 1641 | nattgris | 258 | |
| 361 | andfri | 259 | tx_msg.Data.words[0] = crc; |
| 260 | send_msg = 1; |
||
| 769 | arune | 261 | bios_state = BIOS_END_PGM; |
| 361 | andfri | 262 | } |
| 769 | arune | 263 | break; |
| 264 | case BIOS_END_PGM: |
||
| 265 | if (nmt_type == CAN_NMT_PGM_COPY) { |
||
| 266 | // For BIOS updating over CAN. Upload bios to application area, |
||
| 267 | // send this message with correct parameters to copy data from |
||
| 268 | // application to bios area and pray it will come alive again. |
||
| 1641 | nattgris | 269 | flash_copy_data(bios_msg.Data.words[0], |
| 270 | bios_msg.Data.words[1], |
||
| 769 | arune | 271 | bios_msg.Data.words[2]); |
| 272 | // flash_copy_data will never return. |
||
| 273 | } |
||
| 361 | andfri | 274 | } |
| 1641 | nattgris | 275 | |
| 361 | andfri | 276 | if (send_msg) { |
| 277 | while (Can_Send(&tx_msg) != CAN_OK); |
||
| 278 | send_msg = 0; |
||
| 279 | } |
||
| 280 | |||
| 281 | bios_msg_full = 0; // We're done with this message, ready for the next. |
||
| 282 | } |
||
| 283 | |||
| 284 | return 0; |
||
| 285 | } |