/* * Firmwareupdater_ATSAMD.c * * Created: 04.11.2021 10:54:13 * Author : Firmwareupdater */ #include "sam.h" #include "include/samd51.h" #include #include /////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// Funktionsdeklarationen ////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////// void EEPROM_init(void); void init_UART_FTDI(void); void SERCOM1_2_Handler(void); void send_message(uint8_t message[], bool crc); void usart_rx_task(void); uint16_t crc_ccitt_update (uint16_t crc, uint8_t data); bool crc_check(uint8_t message[]); /////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// Variablen & Defines ///////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////// #define USART_MESSAGE_LENGTH 8 #define USART_RX_BUFFER_LENGTH 512 #define CRC_INIT_VALUE 0xFFFF #define CRC_ERROR_BYTE 0x80 // LEDs #define LED1_GN_AN PORT->Group[1].OUTCLR.reg = PORT_PB09 #define LED1_GN_AUS PORT->Group[1].OUTSET.reg = PORT_PB09 #define LED1_RT_AN PORT->Group[1].OUTCLR.reg = PORT_PB08 #define LED1_RT_AUS PORT->Group[1].OUTSET.reg = PORT_PB08 #define LED2_GN_AN PORT->Group[1].OUTCLR.reg = PORT_PB07 #define LED2_GN_AUS PORT->Group[1].OUTSET.reg = PORT_PB07 #define LED2_RT_AN PORT->Group[1].OUTCLR.reg = PORT_PB06 #define LED2_RT_AUS PORT->Group[1].OUTSET.reg = PORT_PB06 volatile uint16_t *Eeprom; volatile float softwareversion = 3.0; volatile uint8_t USART_Buffer[USART_RX_BUFFER_LENGTH]; volatile int16_t USART_buf_write_adr = 0; volatile int16_t USART_buf_read_adr = 0; volatile uint8_t usart_tx_message[USART_MESSAGE_LENGTH+2] = { 0 }; volatile uint8_t usart_rx_message[USART_MESSAGE_LENGTH+2] = { 0 }; volatile uint8_t USART_msg_adr = 0; volatile int16_t firmware_size; volatile uint16_t NVM_npages, NVM_pagesize; volatile uint32_t Startaddress_bank_B, NVM_blocksize; volatile uint16_t nblocks; volatile uint32_t * flash_write_addr; volatile bool discard_firmwareupdate = false; union{ struct INT16conv{ uint16_t loword; uint16_t hiword; }shortwords; uint32_t uint32val; int32_t int32val; float fval; uint8_t Float_bytes[4]; }convert; /////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// Smart-EEprom //////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////// // Smart-EEprom in µC initialisieren // Fuses setzen: // SEEBLK = 0x04 // SEEPSZ = 0x01 // -> entspricht 512 Byte -> 256 Eepromeinträge à 16 Bit void EEPROM_init(void) { Eeprom = (uint16_t *)SEEPROM_ADDR; MCLK->AHBMASK.bit.NVMCTRL_CACHE_ = 1; MCLK->AHBMASK.bit.NVMCTRL_SMEEPROM_ = 1; NVMCTRL->CTRLB.reg = 0xa535; // USEE command while(NVMCTRL->SEESTAT.bit.BUSY); NVMCTRL->CTRLB.reg = 0xa537; // USEER command while(NVMCTRL->SEESTAT.bit.BUSY); } uint16_t crc_ccitt_update (uint16_t crc, uint8_t data) // crc-xmodem aus der avr-libc mit 0xFFFF als Startwert, entspricht der CRC-CCITT { int i; crc = crc ^ ((uint16_t)data << 8); for (i=0; i<8; i++) { if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; else crc <<= 1; } return crc; } bool crc_check(uint8_t message[]) { int i; unsigned int crc = CRC_INIT_VALUE; for (i=0;i<(USART_MESSAGE_LENGTH);i++) { crc = crc_ccitt_update(crc,message[i]); } if((message[USART_MESSAGE_LENGTH] == (unsigned char)(crc>>8)) && (message[USART_MESSAGE_LENGTH+1] == (unsigned char)crc)) // CRC-Check OK { return true; }else // CRC-Check Fehlgeschlagen { return false; } } void init_UART_FTDI(void) { MCLK->APBAMASK.bit.SERCOM1_ = 1; // MCLK-Clock auf SERCOM1 aktivieren GCLK->PCHCTRL[8].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0; // GCLK0 (96MHz) auf SERCOM1 aktivieren SERCOM1->USART.CTRLA.bit.SWRST = 1; // Softwarereset SERCOM1 while(SERCOM1->USART.CTRLA.bit.SWRST); // warten bis reset fertig SERCOM1->USART.CTRLA.reg = SERCOM_USART_CTRLA_DORD | // LSB first für korrekte Anzeige im HTerm SERCOM_USART_CTRLA_RXPO(1) | // PAD[1] für Datenempfang (RX) SERCOM_USART_CTRLA_TXPO(2) | // PAD[0] für Daten versenden (TX) SERCOM_USART_CTRLA_MODE(1); // Internal Clock SERCOM1->USART.INTENSET.reg = SERCOM_USART_INTENSET_RXC; // Receive Complete Interrupt aktivieren SERCOM1->USART.CTRLB.reg = SERCOM_USART_CTRLB_RXEN | // Receiver aktivieren SERCOM_USART_CTRLB_TXEN; // Transceiver aktivieren SERCOM1->USART.BAUD.reg = SERCOM_USART_BAUD_BAUD(64278); // Ergibt 115200 Baud SERCOM1->USART.CTRLA.bit.ENABLE = 1; PORT->Group[0].PMUX[8].bit.PMUXE = 2; PORT->Group[0].PMUX[8].bit.PMUXO = 2; PORT->Group[0].PMUX[9].bit.PMUXE = 2; PORT->Group[0].PMUX[9].bit.PMUXO = 2; PORT->Group[0].PINCFG[16].reg = PORT_PINCFG_PMUXEN; PORT->Group[0].PINCFG[17].reg = PORT_PINCFG_PMUXEN; PORT->Group[0].PINCFG[18].reg = PORT_PINCFG_PMUXEN; PORT->Group[0].PINCFG[19].reg = PORT_PINCFG_PMUXEN; NVIC_EnableIRQ(52); // 52 entspricht zweitem Interrupt (RXC) von SERCOM1 -> Manual Seite 67 } void SERCOM1_2_Handler(void) { USART_Buffer[USART_buf_write_adr] = (uint8_t)SERCOM1->USART.DATA.reg; USART_buf_write_adr++; if(USART_buf_write_adr>USART_RX_BUFFER_LENGTH-1) USART_buf_write_adr = 0; } void send_message(uint8_t message[], bool crc) { uint8_t length = USART_MESSAGE_LENGTH; if(crc == true) { volatile uint16_t crc = CRC_INIT_VALUE; for (int i=0;i<(USART_MESSAGE_LENGTH);i++) { crc = crc_ccitt_update(crc,message[i]); } message[USART_MESSAGE_LENGTH] = (unsigned char)(crc>>8); message[USART_MESSAGE_LENGTH+1] = (unsigned char)(crc); length += 2; } for(int i = 0;iUSART.INTFLAG.bit.DRE); SERCOM1->USART.DATA.reg = (uint8_t)message[i]; } while(!SERCOM1->USART.INTFLAG.bit.TXC); // Warten bis Daten gesendet wurden } void usart_rx_task(void) { bool new_command_available = false; while((USART_buf_read_adr != USART_buf_write_adr) && new_command_available == false) { usart_rx_message[USART_msg_adr] = USART_Buffer[USART_buf_read_adr]; USART_buf_read_adr++; if(USART_buf_read_adr > USART_RX_BUFFER_LENGTH-1) USART_buf_read_adr = 0; USART_msg_adr++; if (USART_msg_adr == USART_MESSAGE_LENGTH+2) { new_command_available = true; USART_msg_adr = 0; } } if(new_command_available == true) { if(crc_check(usart_rx_message)) { if(usart_rx_message[0] == 0xAA && usart_rx_message[1] == 0x55) { if(usart_rx_message[2] == 0x06) { usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x06; convert.fval = softwareversion; usart_tx_message[3] = 0x02; // Softwareversion Sensor usart_tx_message[4] = convert.Float_bytes[0]; usart_tx_message[5] = convert.Float_bytes[1]; usart_tx_message[6] = convert.Float_bytes[2]; usart_tx_message[7] = convert.Float_bytes[3]; send_message(usart_tx_message, true); }else if(usart_rx_message[2] == 0x9) // Firmwareupdate { if(usart_rx_message[3] == 0) // Initialisieren -> DMA deaktivieren { LED1_GN_AUS; LED2_GN_AUS; LED1_RT_AN; LED2_RT_AN; usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x04; // µC bereit für neue Firmwaredaten usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); }else if(usart_rx_message[3] == 1) // Initialisieren -> Firmwaregröße empfangen und Flashbänke löschen { firmware_size = (uint16_t)usart_rx_message[4]<<8 | (uint16_t)usart_rx_message[5]; NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CACHEDIS1 | NVMCTRL_CTRLA_CACHEDIS0 | NVMCTRL_CTRLA_AUTOWS; NVMCTRL->INTFLAG.reg = 0x03cf; // Error interrupt flags löschen NVM_npages = NVMCTRL->PARAM.bit.NVMP; NVM_pagesize = 8 << NVMCTRL->PARAM.bit.PSZ; //NVM_blocksize = (uint32_t)NVM_pagesize * (uint32_t)NVM_npages / 32; //Löschen geht blockweise NVM_blocksize = (uint32_t)(16*NVM_pagesize); nblocks = firmware_size/NVM_blocksize+1; Startaddress_bank_B = (uint32_t)NVM_pagesize * (uint32_t)NVM_npages / 2; //Startadresse der oberen Speicherbank //Löschen von Blocks in Bank B for(int i=0; i<=nblocks; i++) { NVMCTRL->ADDR.reg = Startaddress_bank_B + i * NVM_blocksize; NVMCTRL->CTRLB.reg = 0xa500 | NVMCTRL_CTRLB_CMD_EB; while(!NVMCTRL->STATUS.bit.READY); } flash_write_addr = Startaddress_bank_B; usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x04; // µC bereit für neue Firmwaredaten usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); }else if (usart_rx_message[3] == 2) // Firmwaredaten übertragen { if(discard_firmwareupdate == false) { *flash_write_addr = ((uint32_t)usart_rx_message[4]<<24) | ((uint32_t)usart_rx_message[5]<<16) | ((uint32_t)usart_rx_message[6]<<8) | ((uint32_t)usart_rx_message[7]); if((((uint32_t)flash_write_addr&(uint32_t)0x01FC) == 0x1FC) && (flash_write_addr != Startaddress_bank_B)) // 512 Bytes geschrieben { NVMCTRL->INTFLAG.reg = NVMCTRL_INTFLAG_DONE; NVMCTRL->CTRLB.reg = 0xA500 | NVMCTRL_CTRLB_CMD_WP; while(!NVMCTRL->INTFLAG.bit.DONE); while(!NVMCTRL->STATUS.bit.READY); } flash_write_addr++; usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x04; // µC bereit für neue Firmwaredaten usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); }else { usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x05; // Abbruch Firmwareupdate wegen Übertragungsfehler usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); } }else if (usart_rx_message[3] == 3) // Übertragung Firmware fertig -> letzte Page schreiben { NVMCTRL->CTRLB.reg = 0xA500 | NVMCTRL_CTRLB_CMD_WP; while(!NVMCTRL->STATUS.bit.READY); usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x04; // µC bereit für neue Firmwaredaten usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); }else if (usart_rx_message[3] == 4) // -> Bank swap + reset { usart_tx_message[0] = 0xAA; usart_tx_message[1] = 0x55; usart_tx_message[2] = 0x09; usart_tx_message[3] = 0x06; // Firmwareupdate erfolgreich usart_tx_message[4] = 0x00; usart_tx_message[5] = 0x00; usart_tx_message[6] = 0x00; usart_tx_message[7] = 0x00; send_message(usart_tx_message, true); __disable_irq(); /* wait until it is ready to accept a new command */ while (!NVMCTRL->STATUS.bit.READY); /* execute the command BKSWRST (Bank swap and system reset) */ NVMCTRL->CTRLB.reg = NVMCTRL_CTRLB_CMD_BKSWRST | NVMCTRL_CTRLB_CMDEX_KEY; /* wait until command done */ while (NVMCTRL->INTFLAG.bit.DONE); /* At the end of this command, code starts running from 0x00000 upon reset */ } } }else { discard_firmwareupdate = true; } } } } int main(void) { int16_t i=0; WDT->CTRLA.reg = 0; //Watchdog aus OSCCTRL->XOSCCTRL[0].reg = OSCCTRL_XOSCCTRL_STARTUP(3) | OSCCTRL_XOSCCTRL_ENABLE; // XOSC0 enable while(!OSCCTRL->STATUS.bit.XOSCRDY0); OSCCTRL->Dpll[0].DPLLRATIO.bit.LDR = 31; // PLL Faktor 32 ( * 3MHz = 192 MHz) OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_REFCLK_XOSC0 | OSCCTRL_DPLLCTRLB_DIV(3); // PLL clock / 8; Refclk = XOSC0 OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_ENABLE; while(!OSCCTRL->Dpll[0].DPLLSTATUS.bit.LOCK); GCLK->GENCTRL[0].reg = GCLK_GENCTRL_SRC_DPLL0 | GCLK_GENCTRL_DIV(1) | GCLK_GENCTRL_GENEN; // 96 MHz GCLK->GENCTRL[2].reg = GCLK_GENCTRL_SRC_DPLL0 | GCLK_GENCTRL_DIV(2) | GCLK_GENCTRL_GENEN; // 48 MHz PORT->Group[1].DIRSET.reg = PORT_PB06; // LED2_R PORT->Group[1].DIRSET.reg = PORT_PB07; // LED2_G PORT->Group[1].DIRSET.reg = PORT_PB08; // LED1_R PORT->Group[1].DIRSET.reg = PORT_PB09; // LED1_G __enable_irq(); // Eeprom initialisieren und auslesen EEPROM_init(); // USART initialisieren init_UART_FTDI(); LED1_RT_AUS; LED2_RT_AUS; /* Replace with your application code */ while (1) { usart_rx_task(); } }