/********************************************************************\ Name: upgrade.c Created by: Stefan Ritt Contents: Firmware upgrade code running at last page 0xF000 \********************************************************************/ #include #include #include #include #include "mscbemb.h" #include "fe_cc.h" sbit RS485_ENABLE = RS485_EN_PIN; /*------------------------------------------------------------------*/ #ifdef LED_0 sbit led_0 = LED_0; #endif #define SEND_BYTE(_b) \ TI0 = 0; \ DELAY_US(INTERCHAR_DELAY); \ SBUF0 = _b; \ while (TI0 == 0); #pragma OT(8, SIZE) // 9 would call subroutines in program body -> crash on upgrade void upgrade(void) { unsigned char idata cmd, page, crc, j, k; unsigned short idata i; unsigned char xdata * idata pw; unsigned char code * idata pr; /* disable all interrupts */ EA = 0; #if defined(CPU_C8051F120) SFRPAGE = UART1_PAGE; SCON1 &= ~0x03; // clear pending UART1 interrupts #endif /* disable watchdog */ #if defined(CPU_C8051F310) || defined(CPU_C8051F320) PCA0MD = 0x00; #else WDTCN = 0xDE; WDTCN = 0xAD; #endif cmd = page = 0; do { receive_cmd: #ifdef CPU_C8051F120 SFRPAGE = UART0_PAGE; #endif /* receive command */ while (!RI0) { for (i=0 ; !RI0 && i<5000 ; i++) DELAY_US(10); led_0 = !led_0; #if defined(CFG_EXT_WATCHDOG) && defined(EXT_WATCHDOG_PIN) EXT_WATCHDOG = !EXT_WATCHDOG; #endif } cmd = SBUF0; RI0 = 0; #if defined(CFG_EXT_WATCHDOG) && defined(EXT_WATCHDOG_PIN) EXT_WATCHDOG = !EXT_WATCHDOG; #endif /* cannot use case since it calls the C library */ if (cmd == MCMD_PING16) { for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; // LSB RI0 = 0; for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; // MSB RI0 = 0; for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; // CRC RI0 = 0; /* acknowledge ping, independent of own address */ RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK); DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == MCMD_UPGRADE) { for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; // CRC RI0 = 0; /* acknowledge upgrade */ RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK+1); SEND_BYTE(1); SEND_BYTE(0); // dummy CRC DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_ECHO) { RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK); SEND_BYTE(0); // dummy CRC, needed by subm_250 DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_ERASE) { /* receive page */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; RI0 = 0; crc = 0; led_0 = !(page & 1); /* erase page if not page of upgrade() function */ if (page*512 < (unsigned int)upgrade && page*512 < EEPROM_OFFSET) { #ifdef CPU_C8051F120 /* for F120, only erase even pages (1024kB page size!) */ if (page & 1) goto erase_ok; SFRPAGE = LEGACY_PAGE; #endif #if defined(CPU_C8051F000) FLSCL = (FLSCL & 0xF0) | 0x08; // set timer for 11.052 MHz clock #elif defined (CPU_C8051F020) || defined(CPU_C8051F120) FLSCL = FLSCL | 1; // enable flash writes #endif PSCTL = 0x03; // allow write and erase pw = (char xdata *) (512 * page); #if defined(CPU_C8051F310) || defined (CPU_C8051F320) FLKEY = 0xA5; // write flash key code FLKEY = _flkey; #endif *pw = 0; #if !defined(CPU_C8051F310) && !defined(CPU_C8051F320) FLSCL = (FLSCL & 0xF0); #endif PSCTL = 0x00; } else { crc = 0xFF; // return 'protected' flag } #ifdef CPU_C8051F120 SFRPAGE = UART0_PAGE; #endif #ifdef CPU_C8051F120 erase_ok: #endif /* return acknowledge */ RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK); SEND_BYTE(crc); DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_PROGRAM) { /* receive page */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; RI0 = 0; /* receive subpage */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; j = SBUF0; RI0 = 0; led_0 = page & 1; /* program page if not page of upgrade() function */ if (page*512 >= (unsigned int)upgrade || page*512 >= EEPROM_OFFSET) goto receive_cmd; #ifdef CPU_C8051F120 SFRPAGE = LEGACY_PAGE; #endif /* allow write */ #if defined(CPU_C8051F000) FLSCL = (FLSCL & 0xF0) | 0x08; // set timer for 11.052 MHz clock #elif defined (CPU_C8051F020) || defined(CPU_C8051F120) FLSCL = FLSCL | 1; // enable flash writes #endif PSCTL = 0x01; // allow write access pw = (char xdata *) (page*512 + j*32); #ifdef CPU_C8051F120 SFRPAGE = UART0_PAGE; #endif /* receive 32 bytes */ for (k = 0; k < 32; k++) { for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; #if defined(CPU_C8051F310) || defined (CPU_C8051F320) FLKEY = 0xA5; // write flash key code FLKEY = _flkey; #endif /* flash byte */ *pw++ = SBUF0; RI0 = 0; } #ifdef CPU_C8051F120 SFRPAGE = LEGACY_PAGE; #endif /* disable write */ #if !defined(CPU_C8051F310) && !defined(CPU_C8051F320) FLSCL = (FLSCL & 0xF0); #endif PSCTL = 0x00; #ifdef CPU_C8051F120 SFRPAGE = UART0_PAGE; #endif RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK); SEND_BYTE(0); DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_VERIFY) { /* receive page */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; RI0 = 0; pr = 512 * page; /* return simplified CRC */ for (i = crc = 0; i < 512; i++) crc += *pr++; /* return acknowledge */ RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK); SEND_BYTE(crc); DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_READ) { /* receive page */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; page = SBUF0; RI0 = 0; /* receive subpage */ for (i=0 ; !RI0 && i < 5000 ; i++) DELAY_US(10); if (!RI0) goto receive_cmd; j = SBUF0; RI0 = 0; RS485_ENABLE = RS485_ENABLE_ON; SEND_BYTE(MCMD_ACK+7); // send acknowledge, variable data length SEND_BYTE(32); // send data length pr = (512 * page + 32 * j); /* send 32 bytes */ for (k = crc = 0 ; k<32 ; k++) { SEND_BYTE(*pr); crc += *pr++; } SEND_BYTE(crc); DELAY_US(10); RS485_ENABLE = RS485_ENABLE_OFF; } else if (cmd == UCMD_REBOOT) { #ifdef CPU_C8051F120 SFRPAGE = LEGACY_PAGE; #endif RSTSRC = 0x10; } } while (cmd != UCMD_RETURN); EA = 1; // re-enable interrupts } /* block remainder of segment for linker */ unsigned char code BLOCK_F000_REMAINDER[0x67C] _at_ 0xF983; // for large model /*------------------------------------------------------------------*/