Subversion Repositories HomeAutomation

Rev

Rev 568 | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*
  2.  * $Id: bios.c 583 2007-08-06 11:57:59Z nattgris $
  3.  */
  4.  
  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.  
  19. #if defined(__AVR_ATmega8__)
  20. #define IVSEL_REG GICR
  21. #elif defined(__AVR_ATmega88__) || defined(__AVR_ATmega168__)
  22. #define IVSEL_REG MCUCR
  23. #else
  24. #error AVR device not supported!
  25. #endif
  26.  
  27. //---------------------------------------------------------------------------
  28. // Private declarations
  29.  
  30. volatile uint8_t bios_msg_full;
  31. Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space
  32. extern void __bios_start; // Start of BIOS area in flash, from ld-script.
  33.  
  34. #define BIOS_APP    1
  35. #define BIOS_NOAPP  2
  36. #define BIOS_PGM    3
  37.  
  38. //---------------------------------------------------------------------------
  39. // Private functions
  40.  
  41. #define BIOS_STD 1
  42. void Can_Process(Can_Message_t* msg) {
  43. #if BIOS_STD==1
  44.     if (msg->Flags & CAN_FLAG_EXT) {
  45.         if (BIOS_CanCallback) {
  46.                 //printf("Calling app callback.\n");
  47.                 BIOS_CanCallback(msg);
  48.         }
  49.     } else {
  50.         // Reset watchdog as long as STD messages are received periodically. If
  51.         // anything hangs the CAN communication, the node will be reset.
  52.         wdt_reset();
  53.        
  54.         if ((msg->Id & (CAN_MASK_STD_TYPE | CAN_MASK_STD_ID))
  55.                 == ((CAN_NMT_RESET << CAN_SHIFT_STD_TYPE)
  56.                     | (NODE_ID << CAN_SHIFT_STD_ID))) {
  57.             BIOS_Reset();
  58.         }
  59.         // Copy message to bios buffer if bios is done with the previous message.
  60.         // This is also a check to see if bios is still running. When app is
  61.         // started, bios_msg_full is never reset to 0 so the check fails and all
  62.         // NMT communication except reset is ignored.
  63.         if (!(bios_msg_full)) {
  64.             memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
  65.             bios_msg_full = 1;
  66.         }
  67.     }
  68. #else
  69.     if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames.
  70.    
  71.     if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
  72.        
  73.         if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) {
  74.             // Reset watchdog as long as time messages from the master node
  75.             // are received periodically. If anything hangs the CAN communication
  76.             // or the master node is not up, the node will be reset.
  77.             wdt_reset();
  78.             //TODO: Don't care about time right now... Figure out what to do with it.
  79.         }
  80.        
  81.         if ((msg->Id & CAN_MASK_NMT_RID)>>CAN_SHIFT_NMT_RID == NODE_ID) {
  82.  
  83.             if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_RESET) {
  84.                 BIOS_Reset();
  85.             }
  86.             // Copy message to bios buffer if bios is done with the previous message.
  87.             // This is also a check to see if bios is still running. When app is
  88.             // started, bios_msg_full is never reset to 0 so the check fails and all
  89.             // NMT communication except reset is ignored.
  90.             if (!(bios_msg_full)) {
  91.                 memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
  92.                 bios_msg_full = 1;
  93.             }
  94.         }      
  95.     } else if (BIOS_CanCallback) {
  96.         //printf("Calling app callback.\n");
  97.         BIOS_CanCallback(msg);
  98.     }
  99. #endif
  100. }
  101.  
  102. int main(void) {
  103.     void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
  104.     uint8_t bios_state;
  105.     uint8_t nmt_type;
  106.     uint8_t pagebuf[SPM_PAGESIZE];
  107.     // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
  108.     uint16_t base_addr = 0;
  109.     uint16_t offset = 0;
  110.     uint16_t addr = 0;
  111.     uint8_t len;
  112.     uint16_t data;
  113.     uint8_t send_msg = 0;
  114.     Can_Message_t tx_msg;
  115.     Can_Message_t bios_msg;
  116.     bios_msg_ptr = &bios_msg;
  117.    
  118.     // Enable watchdog timer to protect against an application locking the
  119.     // node by leaving interrupts disabled.
  120.     wdt_enable(WDTO_2S);
  121.    
  122.     // Move interrupt vectors to start of bootloader section and enable interrupts
  123.     IVSEL_REG = _BV(IVCE);
  124.     IVSEL_REG = _BV(IVSEL);
  125.     sei();
  126.    
  127.     if (Can_Init() != CAN_OK) BIOS_Reset();
  128.    
  129.     //tx_msg.RemoteFlag = 0;
  130.     //tx_msg.ExtendedFlag = 1;
  131.     tx_msg.Id = CAN_ID_NMT_BIOS_START;
  132.     tx_msg.DataLength = 3;
  133.     tx_msg.Data.words[0] = BIOS_VERSION;
  134.  
  135.     if (pgm_read_word(0) == 0xffff) {
  136.         // No application in flash
  137.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
  138.         tx_msg.Data.bytes[2] = 0;
  139.         bios_state = BIOS_NOAPP;
  140.     } else {
  141.         // Application exists
  142.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
  143.         tx_msg.Data.bytes[2] = 1;
  144.         bios_state = BIOS_APP;
  145.     }
  146.    
  147.     while (Can_Send(&tx_msg) != CAN_OK);
  148.  
  149.     tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
  150.    
  151.     // Main loop
  152.     while (1) {
  153.         // Wait for message
  154.         while (!bios_msg_full);
  155.        
  156.         nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
  157.        
  158.         switch (bios_state) {
  159.         case BIOS_APP:
  160.             if (nmt_type == CAN_NMT_START_APP) {
  161.                 app_reset(); // Will never return
  162.             }
  163.             // Fall through
  164.         case BIOS_NOAPP:
  165.             if (nmt_type == CAN_NMT_PGM_START) {
  166.                 // Set base address
  167.                 base_addr = bios_msg.Data.words[0];
  168.                 flash_init(pagebuf);
  169.                 //send CAN_NMT_PGM_ACK(offset)
  170.                 tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  171.                 tx_msg.Data.words[0] = base_addr;
  172.                 send_msg = 1;
  173.                 bios_state = BIOS_PGM;
  174.             }
  175.             if (nmt_type == CAN_NMT_PGM_COPY) {
  176.                 // For BIOS updating over CAN. Upload bios to application area,
  177.                 // send this message with correct parameters to copy data from
  178.                 // application to bios area and pray it will come alive again.
  179.                 flash_copy_data(bios_msg.Data.words[0],
  180.                                 bios_msg.Data.words[1],
  181.                                 bios_msg.Data.words[2]);
  182.                 // flash_copy_data will never return.
  183.             }
  184.             break;
  185.         case BIOS_PGM:
  186.             // Default to send CAN_NMT_PGM_ACK(offset)
  187.             tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  188.            
  189.             if (nmt_type == CAN_NMT_PGM_DATA) {
  190.                 // Set address = base address + offset.
  191.                 offset = bios_msg.Data.words[0];
  192.                 addr = base_addr + offset;
  193.                
  194.                 // One of ACK and NACK will be sent, both have offset as data.
  195.                 tx_msg.Data.words[0] = offset;
  196.                 send_msg = 1;
  197.  
  198.                 // Flash all data sent, beginning at addr.
  199.                 len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
  200.                 uint8_t i;
  201.                 for (i=1; i<len; i++) { // Skip first word (offset).
  202.                    
  203.                     // Abort if trying to write in BIOS area.
  204.                     if (addr >= (uint16_t)&__bios_start) {
  205.                         // Send CAN_NMT_PGM_NACK(offset).
  206.                         tx_msg.Id = CAN_ID_NMT_PGM_NACK;
  207.                         break; //for loop
  208.                     }
  209.  
  210.                     data = bios_msg.Data.words[i];
  211.                     flash_write_word(addr, data);
  212.                     addr += 2;
  213.                 }
  214.                 // Adjust addr if an odd number of bytes were sent, for use in crc calc.
  215.                 addr -= bios_msg.DataLength & 1;
  216.             }
  217.             if (nmt_type == CAN_NMT_PGM_END) {
  218.                 // Make sure a partly filled page will be flashed.
  219.                 flash_flush_buffer();
  220.                
  221.                 // Calculate crc on written data.
  222.                 //TODO: This will not work if data is not sent sequentially without
  223.                 // holes, starting with offset=0. We might want to send a NACK if the
  224.                 // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
  225.                 uint16_t loc, crc = 0;
  226.                 for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2.
  227.                     crc = _crc16_update(crc, pgm_read_byte(loc));
  228.                 }
  229.                
  230.                 tx_msg.Data.words[0] = crc;
  231.                 send_msg = 1;
  232.                 bios_state = BIOS_NOAPP;
  233.             }
  234.         }
  235.        
  236.         if (send_msg) {
  237.             while (Can_Send(&tx_msg) != CAN_OK);
  238.             send_msg = 0;
  239.         }
  240.  
  241.         bios_msg_full = 0; // We're done with this message, ready for the next.
  242.     }
  243.  
  244.     return 0;
  245. }
  246.