芯片: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 = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (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 app
printf("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 app
printf("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