Удаленное обновление прошивки микроконтроллера
В статье про USB bootloader дана краткая теория по организации памяти семейств SAM D20/D21, и дублировать ее тут мы не будем. В качестве целевого микроконтроллера используется ATSAMD20G16. Проект будем собирать в IAR используя ASF (Atmel Software Framework).
Алгоритмы работыРабота устройства всегда начинается с бутлоадера, затем в зависимости от команды в EEPROM, либо происходит обновление прошивки, либо просто проверяется корректность crc текущей прошивки. По окончании работы бутлоадера при отсутствии ошибок осуществляется переход в application:
Общий алгоритм работы bootloader представлен на рисунке ниже:
Проверка CRC своей прошивки выполняется при каждом запуске микроконтроллера, чтобы избежать некорректной работы в случае повреждения основного кода. Что делать, если расчетное CRC не совпадает со считанным, решается в каждом конкретном приложении индивидуально. Можно бесконечно мигать красным светодиодом, можно залить резервную прошивку (тогда нужно предусмотреть место, где ее хранить). Общий алгоритм работы application представлен на рисунке ниже. Здесь представлено только то, что непосредственно касается обновления прошивки.
Настройки проекта для application- считать средствами IAR контрольную сумму прошивки (CRC) и подставлять результат в виде последних двух байт получаемого бинарника
- размещать application не с нулевого адреса памяти микроконтроллера, для того, чтобы оставить «место» для бутлоадера.
Таким настройкам соответствует полином 0x1021, при этом не надо забывать менять байты местами при подсчете с помощью кода, а также о том, что CRC средствами IAR считается по всей памяти, а не только по заполненной кодом. После подсчета CRC по содержимому всей памяти application в конце необходимо перевернуть результат путем «добавления» к контрольной сумме еще 2 нулевых байт (см. EWARM_DevelopmentGuide.ENU).
Код проверки CRC:
Настройки расположения кода для applicationУказываем начало памяти и адрес таблицы векторов прерывания:
- NVM (контроллер энергонезависимой памяти),
- SERCOM SPI (для общения со внешней flash памятью),
- SERCOM I2C (для общения с внешней EEPROM памятью),
#include <asf.h> #include «twi_driver.h» #include «MCP7941x.h» #include «at45db041d.h» #include «init.h» #include «utils.h» //----------------------------------------------------------------------------- //---------------глобальные переменные----------------------------------------- //---------структуры экземпляров периферии------------------------------------- extern struct spi_module spi_master_instance; extern struct spi_slave_inst slave; extern struct tc_module tc_instance_tc2; extern struct i2c_master_module i2c_master_instance;
void MC_init(void)
void external_init(void)
// brief Function for programming data to Flash // This function will check whether the data is greater than Flash page size. // If it is greater, it splits and writes pagewise. // param address address of the Flash page to be programmed // param buffer pointer to the buffer containing data to be programmed // param len length of the data to be programmed to Flash in bytes
static void program_memory(uint32_t address, uint8_t *buffer, uint16_t len) NVMCTRL_PAGE_SIZE) NVMCTRL_PAGE_SIZE) //Check if there is data remaining to be programmed if (len > 0) > else >
// brief Function for starting application // This function will configure the WDT module and enable it. The LED is // kept toggling till WDT reset occurs. static void start_application(void) VTOR = ((uint32_t) APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk); // Load the Reset Handler address of the application application_code_entry = (void (*)(void))(unsigned *)(*(unsigned *)(APP_START_ADDRESS + 4)); // Jump to user Reset Handler in the application application_code_entry(); >
unsigned int slow_crc16(unsigned short sum, unsigned char *p,unsigned int len)
void MC_reset(void) > > //-------------- проверяем CRC текущей прошивки----------------------------- else < k=0; CRC=0; CRC_read=0xffff; // если не сходится, будем пытаться 5 раз // если не сойдется, то мигаем красным светодиодом до посинения) while((CRC!=CRC_read)&&(k<5)) > // непосредственно считаем crc for(page_addr=0x4000; page_addr<last_page_number+1; page_addr+=NVMCTRL_PAGE_SIZE) //если на всей странице код else < CRC=slow_crc16(CRC,spi_in_data,NVMCTRL_PAGE_SIZE); > > for(i=0;i<NVMCTRL_PAGE_SIZE;i++) spi_in_data[i]=0xff; while(page_addr<0x10000) CRC=slow_crc16(CRC,zero,2); >// end o fwhile((CRC!=CRC_read)&&(k<5)) // если crc не сошлось if(CRC!=CRC_read) //если CRC сошлось else > CRC=0; //////////////////////////////////////////////////////////////////////////////// //------------------------------MAIN PROGRAM----------------------------------- //////////////////////////////////////////////////////////////////////////////// while(1) < //считываем страницы для проверки CRC for(page_addr=2; page_addr<last_page_number+1; page_addr++) //если на всей странице код else < CRC=slow_crc16(CRC,spi_in_data,ext_flash_page_size); > > //переводим адрес в байты из номеров страниц внешней флеш page_addr<<=8; for(i=0;i<NVMCTRL_PAGE_SIZE;i++) spi_in_data[i]=0xff; while(page_addr<0xc200) CRC=slow_crc16(CRC,zero,2);
// если CRC совпало // считываем еще раз и записываем в свою flash // иначе переходим в application с каким-нибудь флагом // для отката прошивки CRC не считаем: не знаем ее размер и место CRC // но по умолчанию они обе равны 0 if(CRC==CRC_read) < page_addr=0x4000; for(i=0; i<15; i++) < // стираем и записываем новую прошивку continuous_low_freq_read(spi_in_data,i,0,NVMCTRL_PAGE_SIZE); program_memory(page_addr, spi_in_data, 256); page_addr+=256; delay_ms(10); > twi_out_data[0]=EEPROM_FW_RESULT; twi_out_data[1]=0xda; twi_write_bytes(twi_out_data,2, EEPROM_ADR,EEPROM_FW_RESULT); > else // стираем только команду на перепрошивку // параметры прошивки не стираем delay_ms(100); twi_out_data[0]=EEPROM_UPGRADE; twi_out_data[1]=0xff; twi_write_bytes(twi_out_data,2, EEPROM_ADR, EEPROM_UPGRADE); delay_ms(100); for(j=2;j<2048;j++) for(i=0;i<20;i++) MC_reset(); start_application();