芯片:stm32f407
开发平台:stm32cubeide
上位机开发平台:visual studio 2017
1. FLASH分配
将flash划分为四个部分:
bootloader: 0x8000000-0x800ffff
app1: 0x8010000-0x805ffff
app2: 0x8060000-0x80affff
parameters: 0x80e0000-0x80fffff
其中,bootloader程序为启动程序,app1是当前运行程序,app2为准备升级程序,parameters存储程序相关参数。
2. BOOTLOADER
在app2中用最后一段flash地址0x80dfff0来存储升级标志Upgrade_Flag,比如有新程序下载到app2中,则0x80dfff0中数据为0xaaaaaa,如果为其他则表示没有新程序,开机后bootloader首先运行,启动后检测Upgrade_Flag是否为0xaaaaaa:
如果是,则将app2中的程序拷贝到app1中,然后重置标志位Upgrade_Flag为0xffffffff且跳转到app1起始地址0x8010000开始执行新的程序;
如果否,则直接跳转到app1起始地址0x8010000开始运行程序;
bootloader主要代码如下:
#define IAP_ADDRESS 0x8000000 //
#define APP1_ADDRESS 0x8010000 //
#define APP1_ADDRESS_END 0x805FFFF //
#define APP2_ADDRESS 0x8060000 //
#define APP2_ADDRESS_END 0x80AFFFF //
#define PARA_ADDRESS 0x80E0000 //
#define UPGRADE_ADDRESS 0x80DFFF0 //pFunction Jump_To_Application;
uint32_t JumpAddress;pFunction Jump_To_Application;
void Jump_To_APP()
{uint32_t JumpAddress;printf("jump to app: %#x\r\n", APP1_ADDRESS);//HAL_DeInit();__disable_irq();if(((*(__IO uint32_t*)APP1_ADDRESS)&0x2ffe0000)==0x20020000){/* Jump to user application */JumpAddress = *(__IO uint32_t*) (APP1_ADDRESS + 4);Jump_To_Application = (pFunction) JumpAddress;//printf("jump %#x success \r\n",APP1_ADDRESS);/* Initialize user application's Stack Pointer */__set_MSP(*(__IO uint32_t*) APP1_ADDRESS);Jump_To_Application();}else{printf("error [0x%08x]\r\n",(*(volatile uint32_t*)APP1_ADDRESS));}
}
uint8_t u8_Code_Buff[2048];//2k
uint16_t Num=0;
uint8_t NotUpgrade[4]={0xff,0xff,0xff,0xff};
uint8_t Upgrade[4]={0xaa,0xaa,0xaa,0xaa};
uint32_t Upgrade_Flag;void Copy_APP2_to_APP1(void)
{if(((*(__IO uint32_t*)APP2_ADDRESS)&0x2ffe0000)!=0x20020000){printf("invalid app in address [0x%08x]\r\n",APP2_ADDRESS);return;}MEM_IF_IniT();uint32_t APP1_Addr;uint32_t APP2_Addr;APP1_Addr=APP1_ADDRESS;APP2_Addr=APP2_ADDRESS;MEM_If_Erase(APP1_ADDRESS,APP1_ADDRESS_END);printf("Erase app1 flash\r\n");for(int i=0;i<150;i++)//copy 150*2k=300k, max size of app is 300k{printf("APP1_Addr = %x\r\n",APP1_Addr);printf("APP2_Addr = %x\r\n",APP2_Addr);MEM_If_Read(u8_Code_Buff,APP2_Addr,2048);HAL_Delay(10);MEM_If_Write_Byte(u8_Code_Buff,APP1_Addr,2048);HAL_Delay(10);APP1_Addr+=0x800;APP2_Addr+=0x800;memset(u8_Code_Buff,0,sizeof(u8_Code_Buff));Num=i;if(1){printf("Successfully copied page %d\r\n",Num);}else{printf("Copy failed page %d\r\n",Num);}}MEM_If_Erase(UPGRADE_ADDRESS,UPGRADE_ADDRESS);MEM_If_Write_Byte(NotUpgrade,UPGRADE_ADDRESS,sizeof(NotUpgrade));MEM_IF_DeInit();
}int main(void)
{/* USER CODE BEGIN 1 *///SCB->VTOR = FLASH_BASE | 0x40000;/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */printf("\r\n");printf("*****************************************\r\n");printf("* luckyzjian's ISP *\r\n");printf("* *\r\n");printf("* www.cdxcjc.com *\r\n");printf("* *\r\n");printf("* Version: v%d.%d.%d *\r\n",version_board,version_year,version_no);printf("* *\r\n");printf("* 026-82598116 *\r\n");printf("*****************************************\r\n");printf("************ flash table ***************\r\n");printf("*bootloader : %#x -------- %#x******\r\n",IAP_ADDRESS,APP1_ADDRESS-1);printf("*APP1 : %#x -------- %#x******\r\n",APP1_ADDRESS,APP1_ADDRESS_END);printf("*APP2 : %#x -------- %#x******\r\n",APP2_ADDRESS,APP2_ADDRESS_END);printf("*PRIVATE PARA: %#x -------- %#x******\r\n",PARA_ADDRESS,PARA_ADDRESS+0X1FFFF);printf("*****************************************\r\n");TM1629_init();Upgrade_Flag=*(__IO uint32_t*)(UPGRADE_ADDRESS);printf("Upgrade_Flag = 0x%x\r\n",Upgrade_Flag);if(0xaaaaaaaa==Upgrade_Flag){printf("there is new app in address: %x\r\n",APP2_ADDRESS);printf("copy new app in address: %x to address: %x\r\n",APP2_ADDRESS,APP1_ADDRESS);TM1629_printRow1("-----",ShowType_Right);TM1629_printRow2("-ISP-",ShowType_Right);TM1629_DISPLAY();Copy_APP2_to_APP1();printf("copy finished\r\n",APP2_ADDRESS,APP1_ADDRESS);printf("------%#x\r\n",(APP1_ADDRESS+4)&0XFF000000);if(((APP1_ADDRESS+4)&0xff000000)==0x08000000){printf("jump APP1 running");Jump_To_APP(APP1_ADDRESS);}}else{printf("there is no new app in address: %x\r\n",APP2_ADDRESS);printf("------%#x\r\n",(APP1_ADDRESS+4)&0xff000000);if(((APP1_ADDRESS+4)&0xff000000)==0x08000000){printf("jump APP1 running\r\n");Jump_To_APP(APP1_ADDRESS);}}/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
3. APP
APP程序因为是从0x08010000开始运行的,要注意修改如下两个地方
/Core/Src/system_stm32f4xx.c中:#define VECT_TAB_OFFSET 0x00010000U /*!< Vector Table base offset field.This value must be a multiple of 0x200. */STM32F407ZGTX_FLASH.ld:MEMORY
{CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64KRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128KFLASH (rx) : ORIGIN = 0x8010000, LENGTH = 320K
}
程序通过TCP下载,因为我项目中已经有一套TCP的通讯接口,为了方便,便在原来的自定义接口协议中增加了下载程序的协议
通讯格式定义如下:
发送: | |||
0x02 | 起始码 | 1 | |
ADD | 地址 | 1 | 控制仪地址 0X80~0X8F 代表0~15号地址 |
CMD | 命令字节 | 1 | |
LB | 数据[DF]长度 | 1 | |
[DF] | 数据 | LB | |
CS | 校验码 | 1 | =NOT(0x02+ADD+CMD+LB+[DF])+1 |
0X03 | 结束码 | 1 | |
响应: | |||
0x06 | 起始码 | 1 | |
ADD | 地址 | 1 | 控制仪地址 0X80~0X8F 代表0~15号地址 |
CMD | 命令字节 | 1 | |
LB | 数据[DF]长度 | 1 | |
[DF] | 数据 | LB | |
CS | 校验码 | 1 | =NOT(0x06+ADD+CMD+LB+[DF])+1 |
0X03 | 结束码 | 1 |
涉及下载的命令定义如下:
命令 | 命令代码 | 命令格式 | 响应 | 说明 |
开始下载固件 | 0x51 | 0x02 ADD 0x51 0x02 [分包数量] CS 0x03 | 0x06 ADD 0x51 0x01 <STATUS> CS 0x03 | [分包数量],固件数据包按1000字节一个包进行分包,最后一包不足1000字节时,用0x00填充到1000字节,将总的分包数量在开始升级固件指令时写入 返回STATUS: 0X06 成功; 0x15 失败 |
写入固件 | 0x52 | 0x02 ADD 0x52 [分包顺序][数据长度]{数据} CS 0x03 | 0x02 ADD 0x52 0x03 [分包顺序] <STATUS> CS 0x03 | [分包顺序][数据长度]《数据》三部分固定长度为1004个字节,其中: [分包顺序]为两字节,代表该帧数据为整个固件包的第几包数据,从0开始计数; [数据长度]为两字节,代表该包数据字节长度,应固定为1000字节; 《数据》为固件内容,当最后一包不足1000字节时,用0x00填充到1000字节 返回STATUS: 0X06 成功; 0x15 失败; |
写入固件结束 | 0x53 | 0x02 ADD 0x53 0x02 <CRCH> <CRCL> CS 0x03 | 0x06 ADD 0x53 0x01 <STATUS> CS 0x03 | CRCH,CHRL组成固件全部数据的CRC16校验,下位机收到结束指令后,对整包数据进行CRC16校验,与该指令CRC校准字比对,如果比对成功,则标记为升级成功(向地址0x80DFFF0写入0xaaaaaa) 返回STATUS: 0X06 升级成功; 0x15 升级失败; |
放弃写入固件 | 0x5f | 0x02 ADD 0x5f 0x00 CS 0x03 | 0x06 ADD 0x5f 0x00 CS 0x03 |
#define CLIENTMAX 5
typedef struct
{struct netconn *conn;uint8_t num;
}tcp_client;
typedef struct{struct netconn *client[CLIENTMAX+2];uint8_t state[CLIENTMAX+2];osThreadId client_taskid[CLIENTMAX+2];Controller_StatusDef realdatastatus[CLIENTMAX+2];
}client_ad;
client_ad clientad;uint8_t isIAPStart=0;
uint16_t IAP_frameTotalCount=0;
uint16_t IAP_frameCount=0;
uint16_t IAP_frameSeq=0;
uint16_t IAP_frameLength=0;
uint16_t IAP_frameCRC=0x0000;
uint8_t isIAPFinish=0;
uint8_t isIAPSuccess=0;uint8_t NotUpgrade[4]={0xff,0xff,0xff,0xff};
uint8_t Upgrade[4]={0xaa,0xaa,0xaa,0xaa};void svr_task(void const *arg);
void processCmd(Controller_AckDataDef frame,Controller_AckDataDef* ackframe,uint8_t clientindex);
osThreadDef(myTaskClient, tcp_server_thread, osPriorityNormal, CLIENTMAX, 1400);void MX_FREERTOS_Init(void) {osThreadDef(defaultTask, StartDefaultTask, osPriorityNormal, 0, 1024);defaultTaskHandle = osThreadCreate(osThread(defaultTask), NULL);osThreadDef(tcpmultiTask, svr_task, osPriorityNormal, 0, 1024);osThreadCreate(osThread(tcpmultiTask), NULL);clientad.state[CLIENTMAX]=1;clientad.realdatastatus[CLIENTMAX]=RealPass_Stop;comackframe.originaldata_data=comackdata; isCOMHasACK=0;}
err_t client_init(void *arg,uint8_t* clientnumindex)
{uint8_t clientnum; //?????? TCP client ????err_t err; //????(UCOSIII)uint8_t ifreceiveclient=0;for(clientnum=0;clientnum<CLIENTMAX;clientnum++) //???????????(????20????){if(clientad.state[clientnum]==0) //???? clientnum ?????????0(???){ ifreceiveclient=1;break; //???? ???? ????????}} if(ifreceiveclient==0){UsrLog("This no space to accept client");return 1;}*clientnumindex=clientnum;return ERR_OK; //??????
}void svr_task(void const *arg)
{err_t oserr; struct netconn *conn,*newconn; while(!isLWIPInitSuccess)osDelay(100);for(int i=0;i<CLIENTMAX;i++) {clientad.state[i]=0;clientad.realdatastatus[i]=RealPass_Stop;//clientad.iapstatus[i]=IAPStatus_app;} conn = netconn_new(NETCONN_TCP); netconn_bind(conn,IP_ADDR_ANY,sysParameterStruct._Para_ControlIP_PORT); netconn_listen(conn); sprintf((char*)iptxt, "%d", sysParameterStruct._Para_ControlIP_PORT); UsrLog("tcp start listen on port: %s",iptxt);while(1){uint8_t clientnumindex=0;//if(get_clientindex(&clientnumindex) != ERR_OK)// osDelay(100); if(netconn_accept(conn,&newconn) == ERR_OK) { if(client_init((void *)newconn,&clientnumindex) != ERR_OK){ netconn_close(newconn); netconn_delete(newconn); }else{clientad.client[clientnumindex]=newconn; clientad.state[clientnumindex]=1; clientad.client_taskid[clientnumindex] = osThreadCreate(osThread(myTaskClient), (void *)clientnumindex);osDelay(100);if(clientad.client_taskid[clientnumindex]==NULL){UsrLog("Failed to create the recv thread with id: %d",clientnumindex);}else{UsrLog("create the recv thread with id: %x",(int)(clientad.client_taskid[clientnumindex]));}}}osDelay(100);}
}
void tcp_server_thread(void const *arg)
{err_t err;struct netbuf *clientrecvbuf;Controller_AckDataDef recvframe;Controller_AckDataDef ackframe;uint8_t recvdata[40];uint8_t ackdata[80];/*recv tcp data buf*/ackframe.originaldata_head=0x06;ackframe.originaldata_address=sysParameterStruct._Para_ControlEquiAddress;ackframe.originaldata_end=0x03;ackframe.originaldata_data=ackdata;ip_addr_t addr; u16_t port;uint8_t *app_flash_buf;/* get remote IP address and port*/uint8_t clientindex=(uint8_t)arg; if((err=netconn_getaddr(clientad.client[clientindex],&addr, &port, 0))==ERR_OK){sprintf((char*)iptxt, "%d.%d.%d.%d:%d", (uint8_t)(addr.addr),(uint8_t)(addr.addr>>8),(uint8_t)(addr.addr>>16),(uint8_t)(addr.addr>>24),port); UsrLog("one client is connected:%s",iptxt);}uint8_t *data;u16_t len;while(1){ if((err=netconn_recv(clientad.client[clientindex],&clientrecvbuf))==ERR_OK){do{netbuf_data(clientrecvbuf,&data,&len);//printf("recevie one frame: length=%d, data=0x%x--0x%x\r\n",len,data[0],data[len-1]);if(len>=6){if(data[0]==0x02&&data[len-1]==0x03){recvframe.originaldata_head=data[0];recvframe.originaldata_address=data[1];recvframe.originaldata_cmd=data[2];if(recvframe.originaldata_cmd==0x52)//app upload data frame:0x02 add 0x52 [frame seq] [frame len] {app data} cs 0x03{IAP_frameSeq=(uint16_t)((data[3]<<8)|data[4]);IAP_frameLength=(uint16_t)((data[5]<<8)|data[6]);app_flash_buf=data+7;printf("recevie one frame data: frame seq=%d, frame data length=%d\r\n",IAP_frameSeq,IAP_frameLength);uint8_t frameRight=1;if(isIAPStart){if(IAP_frameSeq==IAP_frameCount&&IAP_frameSeq<IAP_frameTotalCount){if(IAP_frameLength!=500){printf("wrong frame length(%d), length should be 1000\r\n",IAP_frameLength);frameRight=0;}if(frameRight){printf("valid frame data\r\n");uint32_t flashdestination=APP2_ADDRESS+500*IAP_frameSeq;MEM_IF_IniT();if (MEM_If_Write_Word(app_flash_buf,flashdestination,IAP_frameLength) == HAL_OK){printf("write frame data seq(%d)(data:0x%x..) to flash(addr:0x%08x) success\r\n",IAP_frameSeq,*app_flash_buf,flashdestination);IAP_frameCount++;}else /* An error occurred while writing to Flash memory */{printf("write frame data seq(%d) to flash fail\r\n",IAP_frameSeq);frameRight=0;}MEM_IF_DeInit();}}else{frameRight=0;}}else{frameRight=0;}if(!frameRight){printf("invalid frame data or process data fail\r\n");}uint8_t realdatalength=0;ackframe.originaldata_head=0x06;ackframe.originaldata_address=sysParameterStruct._Para_ControlEquiAddress;ackframe.originaldata_end=0x03;ackframe.originaldata_cmd=recvframe.originaldata_cmd;*(ackframe.originaldata_data+realdatalength)=IAP_frameSeq>>8;realdatalength++;*(ackframe.originaldata_data+realdatalength)=IAP_frameSeq;realdatalength++;*(ackframe.originaldata_data+realdatalength)=frameRight?0x06:0x15;realdatalength++;ackframe.originaldata_length=realdatalength;netconn_ack(&ackframe,clientad.client[clientindex]);}else{recvframe.originaldata_length=data[3];recvframe.originaldata_cs=data[len-2];recvframe.originaldata_end=data[len-1];if(recvframe.originaldata_length+6==len){for(int i=0;i<recvframe.originaldata_length;i++){recvdata[i]=data[4+i];}recvframe.originaldata_data=recvdata;processCmd(recvframe,&ackframe,clientindex);netconn_ack(&ackframe,clientad.client[clientindex]);continue;}}}}ackframe.originaldata_cmd=0xf3;ackframe.originaldata_length=0;netconn_ack(&ackframe,clientad.client[clientindex]); }while(netbuf_next(clientrecvbuf)>=0);netbuf_delete(clientrecvbuf);}else if(err==ERR_CLSD||err==ERR_RST)break; }if(clientad.state[clientindex]==1){if((err=netconn_getaddr(clientad.client[clientindex],&addr, &port, 0))==ERR_OK){sprintf((char*)iptxt, "%d.%d.%d.%d:%d", (uint8_t)(addr.addr),(uint8_t)(addr.addr>>8),(uint8_t)(addr.addr>>16),(uint8_t)(addr.addr>>24),port); UsrLog("one ramote is closed:%s",iptxt); }clientad.state[clientindex]=0;netconn_close(clientad.client[clientindex]);netconn_delete(clientad.client[clientindex]); vTaskDelete(clientad.client_taskid[clientindex]);}
}void processCmd(Controller_AckDataDef frame,Controller_AckDataDef* ackframe,uint8_t clientindex)
{//Controller_AckDataDef ackframe;ackframe->originaldata_head=0x06;ackframe->originaldata_address=sysParameterStruct._Para_ControlEquiAddress;ackframe->originaldata_end=0x03;ackframe->originaldata_cmd=frame.originaldata_cmd;ackframe->originaldata_length=0;uint8_t *databuf=ackframe->originaldata_data;uint8_t realdatalength=0;switch(frame.originaldata_cmd){case 0x51://start download appprintf("start download app\r\n");IAP_frameTotalCount=(uint16_t)(frame.originaldata_data[0] << 8 | frame.originaldata_data[1]);printf("app frames total count is %d\r\n",IAP_frameTotalCount);MEM_IF_IniT();if(MEM_If_Erase(APP2_ADDRESS,APP2_ADDRESS_END)!=HAL_OK){printf("erase flash fail, abort app downloading\r\n");isIAPStart=false;*(databuf+realdatalength)=0x15;realdatalength++;ackframe->originaldata_length=realdatalength;}else{printf("erase flash from %#x to %#x, wait for frame transmitting\r\n",APP2_ADDRESS,APP2_ADDRESS_END);IAP_frameCount=0;isIAPStart=true;*(databuf+realdatalength)=0x06;realdatalength++;ackframe->originaldata_length=realdatalength;}MEM_IF_DeInit();break;case 0x53://finish download appprintf("finish download app\r\n");uint16_t IAP_crc16=(uint16_t)(frame.originaldata_data[0] << 8 | frame.originaldata_data[1]);printf("receive app crc16 is %x\r\n",IAP_crc16);if(1)//(IAP_crc16==IAP_frameCRC){printf("pass crc check, finish download app\r\n");MEM_IF_IniT();MEM_If_Erase(UPGRADE_ADDRESS,UPGRADE_ADDRESS);MEM_If_Write_Byte(Upgrade,UPGRADE_ADDRESS,sizeof(Upgrade));MEM_IF_DeInit();*(databuf+realdatalength)=0x06;realdatalength++;ackframe->originaldata_length=realdatalength;isIAPStart=false;}else{printf("crc check fail, finish download app\r\n");*(databuf+realdatalength)=0x15;realdatalength++;ackframe->originaldata_length=realdatalength;isIAPStart=false;}break;case 0x5f:printf("receive abort download app command\r\n");isIAPStart=false;IAP_frameCount=0;break;/*
other cmd process...*/default:break;}
}
3. 上位机实现升级工具(C# winform)
4. 实现效果
升级工具通过TCP与板卡进行通讯,之前版本是V20.2024.3
打开升级程序包bin文件
点击下载
开始下载,显示整片程序被分成399个包
下载完成,提示重启板卡完成升级
重启后,开始将程序从APP2拷贝到APP1
拷贝完成,跳转到APP1运行,程序已经更新成V20.2024.4