Удаленное обновление прошивки микроконтроллера

#include
#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;

//------------------------периферия--------------------------------------------
//------------------------------i2c--------------------------------------------
unsigned char twi_out_data[9];
unsigned char twi_in_data[8];
//----------------------------SPI----------------------------------------------
extern unsigned char spi_in_data[ext_flash_page_size];
extern unsigned char spi_out_data[ext_flash_page_size];
unsigned char temp_buffer_write[ext_flash_page_size];
unsigned long rtc_cnt;
//-------------------counters for cycles---------------------------------------
unsigned int i,m;
unsigned char led_cnt;
//------------------------ работа с прошивкой----------------------------------
unsigned int page_addr;
unsigned int CRC;
unsigned int CRC_read;
unsigned char zero[2]={0,0};
unsigned int last_page_length;
unsigned int page_num;
unsigned int byte_amount;
unsigned int last_page_number;
const uint8_t flash_data[256];
//-----------------------------------------------------------------------------
//-------------------init----------------------------------------------------

void MC_init(void)
{
struct nvm_config config;

system_init();
SystemClock_Init();
configure_tc2();
configure_pins();
configure_spi_master();
configure_i2c_master();
nvm_get_config_defaults(&config);
nvm_set_config(&config);
}

void init_variables(void)
{
//------------------------------i2c--------------------------------------------
clear_twi_in_buffer();
clear_twi_out_buffer();
//----------------------------SPI----------------------------------------------
for(i=0;i<ext_flash_page_size;i++) temp_buffer_write[i]=0;
rtc_cnt=0;
//-------------------counters for cycles---------------------------------------
led_cnt=0;
//------------------------ other-----------------------------------------------
last_page_number=0;
}

void external_init(void)
{
unsigned char temp;
//----------------external eeprom---------------------
//проверяем, что память не защищена от записи
clear_twi_in_buffer();
twi_read_bytes(twi_in_data, 1, EEPROM_ADR, 0xff);
if(twi_in_data[0]!=0)
{
clear_twi_out_buffer();
twi_out_data[0]=0xff;
twi_out_data[1]=0;
twi_write_bytes(twi_out_data, 2, EEPROM_ADR, EEPROM_OWN_METERS_1_STATUS);
}
//----------------FLASH-------------------------------
// проверяем размер страницы внешней flash
temp=flash_wait(spi_delay);
// если 264, то устанавливаем 256
if((temp&0x01)!=0x01) set_page_size_256();
temp=0;
}

// 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)
{
// Check if length is greater than Flash page size
if (len > NVMCTRL_PAGE_SIZE)
{
uint32_t offset = 0;
while (len > NVMCTRL_PAGE_SIZE)
{
// Check if it is first page of a row
if ((address & 0xFF) == 0)
{
//Erase row
nvm_erase_row(address);
}
// Write one page data to flash
nvm_write_buffer(address, buffer + offset, NVMCTRL_PAGE_SIZE);
// Increment the address to be programmed
address += NVMCTRL_PAGE_SIZE;
// Increment the offset of the buffer containing data
offset += NVMCTRL_PAGE_SIZE;
// Decrement the length
len -= NVMCTRL_PAGE_SIZE;
}
//Check if there is data remaining to be programmed
if (len > 0)
{
// Write the data to flash
nvm_write_buffer(address, buffer + offset, len);
}
}
else
{
// Check if it is first page of a row)
if ((address & 0xFF) == 0)
{
// Erase row
nvm_erase_row(address);
}
// Write the data to flash
nvm_write_buffer(address, buffer, len);
}
}

// 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)
{
// Pointer to the Application Section
void (*application_code_entry)(void);
// Rebase the Stack Pointer
__set_MSP(*(uint32_t *) APP_START_ADDRESS);
// Rebase the vector table base address
SCB->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)
{
while (len--)
{
int i;
unsigned char byte = *(p++);

for (i = 0; i < 8; ++i)
{
unsigned long osum = sum;
sum <<= 1;
if (byte & 0x80)
sum |= 1;
if (osum & 0x8000)
sum ^= 0x1021;
byte <<= 1;
}
}
return sum;
}

void MC_reset(void)
{
tc_reset(&tc_instance_tc2);
i2c_master_reset(&i2c_master_instance);
spi_reset(&spi_master_instance);

}
//------------------------!!!!!!MAIN!!!!!!-------------------------------------
int main(void)
{
unsigned int temp;
unsigned int j;
unsigned char k;

init_variables();
MC_init();

for(i=0;i<20;i++)
{
LED_2_TOGGLE();
delay_ms(100);
}

system_interrupt_enable_global();
external_init();
//------------проверяем, есть ли команда на upgrade-------------------------
for(i=0;i<8;i++) twi_in_data[i]=0;
// считываем upgrade из внешней EEPROM
twi_read_bytes(twi_in_data,4, EEPROM_ADR,EEPROM_UPGRADE);
// команда на апгрейд
if(twi_in_data[0]==0x55)
{
for(i=0;i<20;i++)
{
LED_1_TOGGLE();
delay_ms(100);
}
// определяем длину прошивки и положение crc в ней
// так как мы передаем по PLC всю память, а не только код
// пока не придумала, как передавать только код
for(page_addr=2; page_addr<194; page_addr++)
{
//читаем страницу
continuous_low_freq_read(spi_in_data, page_addr,0,ext_flash_page_size);
temp=0;
for(j=0;j<ext_flash_page_size;j++)
{
if(spi_in_data[j]==0xff)
{
temp++;
}
else temp=0;
}
//первая пустая страница
if(temp==ext_flash_page_size)
{
//читаем предыдущую страницу
page_addr--;
continuous_low_freq_read(spi_in_data, page_addr,0,ext_flash_page_size);
last_page_number=page_addr;
//анализируем последнюю страницу с кодом с конца
//если последний байт не ff, то это CRC
if(spi_in_data[ext_flash_page_size-1]!=0xff)
{
CRC_read=spi_in_data[ext_flash_page_size-2];
CRC_read<<=8;
CRC_read|=spi_in_data[ext_flash_page_size-1];
}
//если последний байц ff, то ищем место crc
else
{
i=ext_flash_page_size-1;
while((spi_in_data[i]==0xff)&&(i>0))
{
i--;
}
CRC_read=spi_in_data[i];
CRC_read<<=8;
CRC_read|=spi_in_data[i-1];
// это уже положение последнего байта crc
last_page_length=i+1;
break;
}
}
}
}
//-------------- проверяем CRC текущей прошивки-----------------------------
else
{
k=0;
CRC=0;
CRC_read=0xffff;
// если не сходится, будем пытаться 5 раз
// если не сойдется, то мигаем красным светодиодом до посинения)
while((CRC!=CRC_read)&&(k<5))
{
k++;
CRC=0;
CRC_read=0;
// определяем длину прошивки и положение crc в ней
for(page_addr=0x4000; page_addr<0x10000 ;page_addr+=NVMCTRL_PAGE_SIZE)
{
//читаем страницу
nvm_read_buffer(page_addr,spi_in_data,NVMCTRL_PAGE_SIZE);
temp=0;
for(j=0;j<64;j++)
{
if(spi_in_data[j]==0xff)
{
temp++;
}
else temp=0;
}
//первая пустая страница
if(temp==NVMCTRL_PAGE_SIZE)
{
//читаем предыдущую страницу
page_addr-=NVMCTRL_PAGE_SIZE;
nvm_read_buffer(page_addr,spi_in_data,NVMCTRL_PAGE_SIZE);
last_page_number=page_addr;
//анализируем последнюю страницу с кодом с конца
//если последний байт не ff, то это CRC
if(spi_in_data[NVMCTRL_PAGE_SIZE-1]!=0xff)
{
CRC_read=spi_in_data[NVMCTRL_PAGE_SIZE-2];
CRC_read<<=8;
CRC_read|=spi_in_data[NVMCTRL_PAGE_SIZE-1];
}
//если последний байц ff, то ищем место crc
else
{
i=NVMCTRL_PAGE_SIZE-1;
while((spi_in_data[i]==0xff)&&(i>0))
{
i--;
}
CRC_read=spi_in_data[i];
CRC_read<<=8;
CRC_read|=spi_in_data[i-1];
// это уже положение последнего байта crc
last_page_length=i+1;
break;
}
}
}
// непосредственно считаем crc
for(page_addr=0x4000; page_addr<last_page_number+1; page_addr+=NVMCTRL_PAGE_SIZE)
{
//читаем страницу
nvm_read_buffer(page_addr,spi_in_data,NVMCTRL_PAGE_SIZE);
temp=0;
//меняем местами соседние байты в считанном
for(j=0;j<NVMCTRL_PAGE_SIZE;j+=2)
{
temp=spi_in_data[j];
spi_in_data[j]=spi_in_data[j+1];
spi_in_data[j+1]=temp;
}
if(page_addr==last_page_number)
{
// считаем отдельно crc части страницы до CRC линкера
CRC=slow_crc16(CRC,spi_in_data, last_page_length-2);
// и после CRC линкера
temp=NVMCTRL_PAGE_SIZE-last_page_length;
CRC=slow_crc16(CRC,&(spi_in_data[last_page_length]), temp);
}
//если на всей странице код
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,spi_in_data,NVMCTRL_PAGE_SIZE);
page_addr+=NVMCTRL_PAGE_SIZE;
}
CRC=slow_crc16(CRC,zero,2);
}// end o fwhile((CRC!=CRC_read)&&(k<5))
// если crc не сошлось
if(CRC!=CRC_read)
{
while(1)
{
LED_1_TOGGLE();
delay_ms(500);
}
}
//если CRC сошлось
else
{
MC_reset();
start_application();
}
}
CRC=0;
////////////////////////////////////////////////////////////////////////////////
//------------------------------MAIN PROGRAM-----------------------------------
////////////////////////////////////////////////////////////////////////////////
while(1)
{
//считываем страницы для проверки CRC
for(page_addr=2; page_addr<last_page_number+1; page_addr++)
{
//читаем страницу
continuous_low_freq_read(spi_in_data, page_addr,0,ext_flash_page_size);
temp=0;
//меняем местами соседние байты в считанном
for(j=0;j<ext_flash_page_size;j+=2)
{
temp=spi_in_data[j];
spi_in_data[j]=spi_in_data[j+1];
spi_in_data[j+1]=temp;
}
if(page_addr==last_page_number)
{
// считаем отдельно crc части страницы до CRC линкера
CRC=slow_crc16(CRC,spi_in_data, last_page_length-2);
// и после CRC линкера
temp=ext_flash_page_size-last_page_length;
CRC=slow_crc16(CRC,&(spi_in_data[last_page_length]), temp);
}
//если на всей странице код
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,spi_in_data,NVMCTRL_PAGE_SIZE);
page_addr+=NVMCTRL_PAGE_SIZE;
}
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
{
twi_out_data[0]=EEPROM_FW_RESULT;
twi_out_data[1]=0xad;
twi_write_bytes(twi_out_data,2, EEPROM_ADR, EEPROM_FW_RESULT);
}
// стираем только команду на перепрошивку
// параметры прошивки не стираем
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++)
{
//стираем всю флеш, кроме первых двух страниц, в которых адреса забинденных
erase_page(j);
temp=flash_wait(spi_delay);
temp=0;
}
for(i=0;i<20;i++)
{
LED_1_TOGGLE();
delay_ms(300);
}
MC_reset();
start_application();

}// end of while(1)
}// end of main

© Geektimes