Subversion Repositories HomeAutomation

Rev

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

  1. #include "bios_funs.h"
  2. #include "avr_cfg.h"
  3. #include <inttypes.h>
  4. #include <avr/io.h>
  5. #include <avr/interrupt.h>
  6. #include <avr/wdt.h>
  7. #include <avr/pgmspace.h>
  8. #ifdef BIOS_UART
  9. #include <stdio.h>
  10. #endif
  11. #include <string.h>
  12. #include <util/crc16.h>
  13. #include "flash.h"
  14. #include "vectors.h" // Must be included after sfr_defs.h but before any ISR()
  15. #include "mcp2515/mcp2515.h"
  16.  
  17. //---------------------------------------------------------------------------
  18. // Globals
  19.  
  20. volatile uint32_t gMilliSecTick;
  21. volatile uint8_t bios_msg_full;
  22. Can_Message_t* bios_msg_ptr; // only a pointer to main-local structure to save .bss space
  23.  
  24. //---------------------------------------------------------------------------
  25. // Function definitions for the exported interface
  26.  
  27. void reset(void) {
  28.     // Just loop and let the watchdog reset
  29.     cli();
  30.     while(1);
  31. }
  32.  
  33. #ifdef BIOS_UART
  34. void bios_putchar(char c) {
  35.     UART_PUTCHAR(c);
  36. }
  37.  
  38. char bios_getchar(void) {
  39.     char c;
  40.     UART_GETCHAR(c)
  41.     return c;
  42. }
  43. #endif
  44.  
  45. unsigned long timebase_get(void) {
  46.     uint8_t sreg;
  47.     unsigned long res;
  48.    
  49.     // Disable interrupts while reading tick count
  50.     sreg=SREG;
  51.     cli();
  52.    
  53.     res = gMilliSecTick;
  54.    
  55.     SREG=sreg;
  56.    
  57.     return res;
  58. }
  59.  
  60. // TODO: Insert can driver's Can_Send directly in bios interface.
  61. Can_Return_t can_send(Can_Message_t* msg) {
  62.     return Can_Send(msg);
  63. }
  64.  
  65. //---------------------------------------------------------------------------
  66. // Function definitions for the private functions
  67.  
  68. #ifdef BIOS_UART
  69. static int console_getchar(FILE *stream) {
  70.     return bios_getchar();
  71. }
  72.  
  73. static int console_putchar(char c, FILE *stream) {
  74.     bios_putchar(c);
  75.     return 0;
  76. }
  77.  
  78. static FILE bios_stdio = FDEV_SETUP_STREAM(console_putchar, console_getchar,
  79.                                          _FDEV_SETUP_RW);
  80. #endif
  81.  
  82. //---------------------------------------------------------------------------
  83. // Interrupt Service Routines
  84. // Read app_vectors.h!
  85.  
  86. ISR(TIMEBASE_VECTOR) {
  87.     TIMEBASE_COUNTER -= TIMEBASE_RELOAD;
  88.     gMilliSecTick++;
  89.     wdt_reset();
  90. }
  91.  
  92. void Can_Process(Can_Message_t* msg) {
  93.     if (!(msg->ExtendedFlag)) return; // We don't care about standard CAN frames.
  94.    
  95.     if ((msg->Id & CAN_MASK_CLASS)>>CAN_SHIFT_CLASS == CAN_NMT) {
  96.        
  97.         if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_TIME) {
  98.             // Reset watchdog as long as time messages from the master node
  99.             // are received periodically. If anything hangs the CAN communication
  100.             // or the master node is not up, the node will be reset.
  101.             wdt_reset();
  102.             //TODO: Don't care about time right now... Figure out what to do with it.
  103.         }
  104.        
  105.         if ((msg->Id & CAN_MASK_NMT_RID)>>CAN_SHIFT_NMT_RID == NODE_ID) {
  106.  
  107.             if ((msg->Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE == CAN_NMT_RESET) {
  108.                 reset();
  109.             }
  110.             // Copy message to bios buffer if bios is done with the previous message.
  111.             // This is also a check to see if bios is still running. When app is
  112.             // started, bios_msg_full is never reset to 0 so the check fails and all
  113.             // NMT communication except reset is ignored.
  114.             if (!(bios_msg_full)) {
  115.                 memcpy(bios_msg_ptr, msg, sizeof(Can_Message_t));
  116.                 bios_msg_full = 1;
  117.             }
  118.         }      
  119.     } else if (bios->can_callback) {
  120.         //printf("Calling app callback.\n");
  121.         bios->can_callback(msg);
  122.     }
  123. }
  124.  
  125. #define BIOS_APP    1
  126. #define BIOS_NOAPP  2
  127. #define BIOS_PGM    3
  128.  
  129. int main() {
  130.     void (*app_reset)(void) = 0; // Function pointer to jump to application reset vector.
  131.     uint8_t bios_state;
  132.     uint8_t nmt_type;
  133.     uint8_t pagebuf[SPM_PAGESIZE];
  134.     // These unneccessary initializations uses 12 bytes flash, just to make the compiler happy!
  135.     uint16_t base_addr = 0;
  136.     uint16_t offset = 0;
  137.     uint16_t addr = 0;
  138.     uint8_t len;
  139.     uint16_t data;
  140.     uint8_t send_msg = 0;
  141.     Can_Message_t tx_msg;
  142.     Can_Message_t bios_msg;
  143.     bios_msg_ptr = &bios_msg;
  144.    
  145. #ifdef BIOS_UART
  146.     // Setup USART for debug channel
  147.     UART_INIT();
  148.     stdout = &bios_stdio;
  149.     stdin = &bios_stdio;
  150. #endif
  151.    
  152.     // Initialize a suitable hardware timer to create a 1KHz interrupt.
  153.     //TODO: Any point in having timebase in bios after wdt_reset() is moved to Can_Process?
  154.     TIMEBASE_INIT();
  155.    
  156.     // Enable watchdog timer to protect against an application locking the
  157.     // node by leaving interrupts disabled.
  158.     wdt_enable(WDTO_2S);
  159.    
  160.     // Move interrupt vectors to start of bootloader section and enable interrupts
  161.     IVSEL_REG = _BV(IVCE);
  162.     IVSEL_REG = _BV(IVSEL);
  163.     sei();
  164.    
  165.     if (Can_Init() != CAN_OK) reset();
  166.    
  167.     tx_msg.RemoteFlag = 0;
  168.     tx_msg.ExtendedFlag = 1;
  169.     tx_msg.Id = CAN_ID_NMT_BIOS_START;
  170.     tx_msg.DataLength = 3;
  171.     tx_msg.Data.words[0] = BIOS_VERSION;
  172. #ifdef BIOS_UART
  173.     printf("BIOS start\n");
  174. #endif
  175.     if (pgm_read_word(0) == 0xffff) {
  176.         // No application in flash
  177.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 0)
  178.         tx_msg.Data.bytes[2] = 0;
  179.         bios_state = BIOS_NOAPP;
  180.     } else {
  181.         // Application exists
  182.         // Send CAN_NMT_BIOS_START(BIOS_VERSION, 1)
  183.         tx_msg.Data.bytes[2] = 1;
  184.         bios_state = BIOS_APP;
  185.     }
  186.    
  187.     while (Can_Send(&tx_msg) != CAN_OK);
  188.  
  189.     tx_msg.DataLength = 2; // All msg sent after BIOS_START are length 2 so set it once.
  190.    
  191.     // Main loop
  192.     while (1) {
  193.         // Wait for message
  194.         while (!bios_msg_full);
  195.        
  196.         nmt_type = (bios_msg.Id & CAN_MASK_NMT_TYPE)>>CAN_SHIFT_NMT_TYPE;
  197.        
  198.         switch (bios_state) {
  199.         case BIOS_APP:
  200.             if (nmt_type == CAN_NMT_START_APP) {
  201.                 app_reset(); // Will never return
  202.             }
  203.             // Fall through
  204.         case BIOS_NOAPP:
  205.             if (nmt_type == CAN_NMT_PGM_START) {
  206.                 // Set base address
  207.                 base_addr = bios_msg.Data.words[0];
  208.                 flash_init(pagebuf);
  209.                 //send CAN_NMT_PGM_ACK(offset)
  210.                 tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  211.                 tx_msg.Data.words[0] = base_addr;
  212.                 send_msg = 1;
  213.                 bios_state = BIOS_PGM;
  214.             }
  215.             if (nmt_type == CAN_NMT_PGM_COPY) {
  216.                 // For BIOS updating over CAN. Upload bios to application area,
  217.                 // send this message with correct parameters to copy data from
  218.                 // application to bios area and pray it will come alive again.
  219.                 flash_copy_data(bios_msg.Data.words[0],
  220.                                 bios_msg.Data.words[1],
  221.                                 bios_msg.Data.words[2]);
  222.                 // flash_copy_data will never return.
  223.             }
  224.             break;
  225.         case BIOS_PGM:
  226.             // Default to send CAN_NMT_PGM_ACK(offset)
  227.             tx_msg.Id = CAN_ID_NMT_PGM_ACK;
  228.            
  229.             if (nmt_type == CAN_NMT_PGM_DATA) {
  230.                 // Set address = base address + offset.
  231.                 offset = bios_msg.Data.words[0];
  232.                 addr = base_addr + offset;
  233.                
  234.                 // One of ACK and NACK will be sent, both have offset as data.
  235.                 tx_msg.Data.words[0] = offset;
  236.                 send_msg = 1;
  237.  
  238.                 // Flash all data sent, beginning at addr.
  239.                 len = (bios_msg.DataLength+1)/2; // Number of words in message, rounded up.
  240.                 uint8_t i;
  241.                 for (i=1; i<len; i++) { // Skip first word (offset).
  242.                    
  243.                     // Abort if trying to write in BIOS area.
  244.                     if (addr >= (uint16_t)&__bios_start) {
  245.                         // Send CAN_NMT_PGM_NACK(offset).
  246.                         tx_msg.Id = CAN_ID_NMT_PGM_NACK;
  247.                         break; //for loop
  248.                     }
  249.  
  250.                     data = bios_msg.Data.words[i];
  251.                     flash_write_word(addr, data);
  252.                     addr += 2;
  253.                 }
  254.                 // Adjust addr if an odd number of bytes were sent, for use in crc calc.
  255.                 addr -= bios_msg.DataLength & 1;
  256.             }
  257.             if (nmt_type == CAN_NMT_PGM_END) {
  258.                 // Make sure a partly filled page will be flashed.
  259.                 flash_flush_buffer();
  260.                
  261.                 // Calculate crc on written data.
  262.                 //TODO: This will not work if data is not sent sequentially without
  263.                 // holes, starting with offset=0. We might want to send a NACK if the
  264.                 // offset in a CAN_NMT_PGM_DATA msg differs from the expected offset.
  265.                 uint16_t loc, crc = 0;
  266.                 for (loc = base_addr; loc < addr; loc++) { // addr will be last location written + 2.
  267.                     crc = _crc16_update(crc, pgm_read_byte(loc));
  268.                 }
  269.                
  270.                 tx_msg.Data.words[0] = crc;
  271.                 send_msg = 1;
  272.                 bios_state = BIOS_NOAPP;
  273.             }
  274.         }
  275.        
  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. }
  286.