调试遇到难点:
1、移植大佬程序,小容量和大容量芯片写flash不一样,单页单页写和双页写是有区分的。
比如我用的是STM32F103C8T6芯片,只有64KflashROM。修改程序如下:
iapfun jump2app;
u16 iapbuf[512];
//appxaddr:应用程序的起始地址
//appbuf:应用程序CODE.
//appsize:应用程序大小(字节).
void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize)
{
u16 t;
u16 i=0;
u16 temp;
u32 fwaddr=appxaddr;//当前写入的地址
u8 *dfu=appbuf;
for(t=0;t<appsize;t+=2)
{
temp=(u16)dfu[1]<<8;
temp+=(u16)dfu[0];
dfu+=2;//偏移2个字节
iapbuf[i++]=temp;
if(i==512)
{
i=0;
STMFLASH_Write(fwaddr,iapbuf,512);
fwaddr+=1024;//偏移2048 16=2*8.所以要乘以2.
}
}
if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去.
}//从app buff中取出bin 更新至app 分段加载
void iap_copy_appbin(u32 appxaddr,u32 appbuffaddr,u32 appsize)
{
u32 surplus_lenth;
u32 i;
for(i=0;i<appsize/1024;i++) //一次存2K
{
STMFLASH_Read(appbuffaddr+i*1024,iapbuf,512);
STMFLASH_Write(appxaddr+i*1024,iapbuf,512);
}
//获取剩余字节
if(i==0)
{
surplus_lenth = appsize;
if(surplus_lenth%2 != 0) //奇数
{
surplus_lenth+=1;
}
}
else
{
surplus_lenth = appsize%(i*1024); //得到剩余字节
if(surplus_lenth%2 != 0) //奇数
{
surplus_lenth+=1;
}
}
STMFLASH_Read(appbuffaddr+i*1024,iapbuf,surplus_lenth/2);
STMFLASH_Write(appxaddr+i*1024,iapbuf,surplus_lenth/2);
}
2、调试程序发现正点原子程序串口接受处理有点异常,不知道是不是中间接受BIN文件时回车换行HEX刚好满足条件。这里改成数组接受了:
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 r;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
if(USART_RX_CNT<USART_REC_LEN)
{
USART_RX_BUF[USART_RX_CNT]=r;
USART_RX_CNT++;
uart1_rev_cnt++;
}
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//接收完成后进入空闲中断
{
USART1->SR;//先读SR,再读DR才能完成idle中断的清零,否则会一直进入中断
USART1->DR;
if(USART_RX_CNT<2048)
{
usart_receive_flag=1;//接受完成标志位
}
else
{
usart_receive_flag1=1; //升级程序标志
printf("开始升级程序\r\n");
}
// USART_RX_CNT=0;
}串口初始化中增加串口空闲中断,这个针对传输速度慢的,如果时速度快,建议改成定时器3中放置中断延时,判断数据是否接受完成:
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//空闲中断使能
USART_Cmd(USART1, ENABLE); //使能串口1
3、串口接受好了之后,IAP程序中有以下几个函数:
//跳转到应用程序段
//appxaddr:用户代码起始地址.
void iap_load_app(u32 appxaddr)
{
if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.
{
printf("执行flash APP!\r\n");
jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址)
printf("程序开始地址是:%u \r\n",appxaddr);
MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)
printf("跳转APP!\r\n");
NVIC_SetVectorTable(0x08000000, 0x00);
__disable_irq();
// __set_FAULTMASK(1); // 关闭全部中断
// INTX_DISABLE();
jump2app(); //跳转到APP.
// NVIC_SystemReset(); // 复位
// printf("复位!\r\n");
}
}
//是否需要更新APP
s8 needUpdata(void)
{
unsigned short read;
STMFLASH_Read(UPDATA_FLAG_ADDR,&read,1);
if(read == UPDATA_FLAG) //程序需要更新
return 0;
else if(read == UPDATA_RUN) //程序已经成功运行过,直接跳转到APP
return 1;
else
return -1;
}
//设置更新标志及长度
void setNeedUpdata(u32 lenth)
{
u16 flag = UPDATA_FLAG;
STMFLASH_Write(UPDATA_FLAG_ADDR,&flag,1);
STMFLASH_Write(UPDATA_LENTH_ADDR,&flag,2);
}
//设置程序已经运行标志
void setBinRun(void)
{
u16 flag = UPDATA_RUN;
STMFLASH_Write(UPDATA_FLAG_ADDR,&flag,1);
}
//清除更新标志
void clearNeedpdata(void)
{
u16 flag = UPDATA_CLEAR;
STMFLASH_Write(UPDATA_FLAG_ADDR,&flag,1);
}
//判断程序是否是第一次运行,设置运行标志
void ifAppFirstRunFlag(void)
{
//程序还没有运行过 则设置标志告知bootload可以直接跳转至APP 不需要等待升级或更新
if(needUpdata() != 1)
{
setBinRun();
printf("设置bin运行标志成功\r\n");
}
else
{
printf("BIN不是第一次运行\r\n");
}
}
//读取BIN长度
u32 readBinLenth(void)
{
u32 lenth;
STMFLASH_Read(UPDATA_LENTH_ADDR,(u16 *)&lenth,2);
if(lenth == 0 || lenth == 0xffffffff)
{
return 0;
}
else
{
return lenth;
}
}//从缓存更新固件并跳转至APP
void binUpdateAndRunFromBuff(void)
{
u32 bin_lenth;
clearNeedpdata(); //清除更新标志
printf("需要更新程序!\r\n");
bin_lenth = readBinLenth();//读取固件长度
//判断长度是否有效
if(bin_lenth>0)
{
//判断缓存中的bin是否是合法固件.
if(((*(vu32*)(APP_BUFF_ADDR+4))&0xFF000000)==0x08000000)
{
iap_copy_appbin(FLASH_APP1_ADDR,APP_BUFF_ADDR,bin_lenth); //拷贝bin
printf("BIN文件拷贝成功!\r\n");
printf("跳转至APP!\r\n");
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}else
{
printf("非FLASH应用程序,无法执行!\r\n");
}
}
else
{
printf("固件长度非法\r\n");
}
}
//从flash执行APP
void execAppFromFlash(void)
{
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
INTX_DISABLE();
}
else
{
printf("非FLASH应用程序,无法执行!\r\n");
}
}
//等待接收升级固件--需要用户按自己协议实现
void waitRevUpdateBin(void)
{
static u32 add;
static u32 oldcount=0;
static u32 applenth=0;
printf("等待升级文件!\r\n");
while(1)
{
if(uart1_rev_cnt)
{
if(oldcount==uart1_rev_cnt)//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=uart1_rev_cnt;
oldcount=0;
uart1_rev_cnt =0;
USART_RX_CNT=0;
printf("用户程序接收完成,len=%d!\r\n",applenth);
//判断中断向量表地址(栈顶+4)是否合法 判断固件是否有效
add =(((u32)USART_RX_BUF[7])<<24)+(((u32)USART_RX_BUF[6])<<16)+(((u32)USART_RX_BUF[5])<<8)+(((u32)USART_RX_BUF[4])<<0);
printf("用户程序接收完成,add=%d!\r\n",add);
if((add&0xFF000000)==0x08000000)
{
iap_write_appbin(FLASH_APP1_ADDR,USART_RX_BUF,applenth);//更新FLASH代码
delay_ms(100);
//清除更新标志
clearNeedpdata();
printf("固件更新完成!\r\n");//判断将执行的固件是否有效
if(((*(vu32*)(FLASH_APP1_ADDR+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.
{
printf("执行APP程序!\r\n");
iap_load_app(FLASH_APP1_ADDR);//执行FLASH APP代码
}
}
else
{
oldcount=0;
// uart1_rev_OK=0;
uart1_rev_cnt =0;
printf("固件更新失败!\r\n");
}
}else oldcount=uart1_rev_cnt;
}
delay_ms(10);
}
}
//强制升级,防止因APP存在但有严重故障,bootloader却仍跳转运行,导致无法远程升级的问题
//可通过检测按键,串口命令等方式进入强制升级
void forceUpdate(u8 flag)
{
if(flag)
{
clearNeedpdata();
waitRevUpdateBin();
}
}
//bootloadr主函数
void bootLoaderRuning(void)
{s8 if_update=0;
printf("if_update1:%d\r\n",if_update);
if_update =needUpdata();
printf("if_update2:%d\r\n",if_update);
if(if_update == 0)
{
binUpdateAndRunFromBuff();
}
else if(if_update == 1)
{
execAppFromFlash();
}
else
{
waitRevUpdateBin();
}
}
4、主要判断函数如下:
//升级测试程序
void binUpdate(void)
{
static u32 oldcount=0;
static u32 applenth=0;
static u32 add;
if(uart1_rev_cnt)
{
if(oldcount==uart1_rev_cnt)//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=uart1_rev_cnt;
//判断中断向量表地址(栈顶+4)是否合法 判断固件是否有效
add = (((u32)USART_RX_BUF[7])<<24)+(((u32)USART_RX_BUF[6])<<16)+(((u32)USART_RX_BUF[5])<<8)+(((u32)USART_RX_BUF[4])<<0);
if((add&0xFF000000)==0x08000000)
{
iap_write_appbin(APP_BUFF_ADDR,USART_RX_BUF,applenth);//下载APP代码至缓存区
delay_ms(100);
printf("bin拷贝至缓存\r\n");
setNeedUpdata(applenth);//设置更新标志
printf("跳转入boot\r\n");
iap_load_app(FLASH_BOOTLODER_ADDR);//bootloader 代码
}
oldcount=0;
uart1_rev_cnt =0;
}else oldcount=uart1_rev_cnt;
}
}
5、IAP的main函数如下:
int main(void)
{
systick_init(T_TICK);//系统滴答时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//中断优先级2 2位抢占优先级 /2位响应优先级
uart_init(9600); //串口初始化函数
// ifAppFirstRunFlag();
printf("is run*************\r\n");
//强制升级
forceUpdate(0);
//执行bootLoader
bootLoaderRuning();
}6、基本就是这几个,加上对FLASH读写操作的几个函数,移植正点原子即可。
APP的main函数如下:
int main(void)
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x3C00);
__enable_irq();
systick_init(T_TICK);//系统滴答时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//中断优先级2 2位抢占优先级 /2位响应优先级
uart_init(9600); //串口初始化函数
ifAppFirstRunFlag();
while(1)
{
binUpdate();//
User_Scan();//循环扫描的任务
User_Task();//用户程序
My_Usart1Check();//串口接受函数
}
}void binUpdate(void)
{
static u32 oldcount=0;
static u32 applenth=0;
static u32 add;
static u16 Bootflag;//此状态用于监控概率性升级失败标志位无法复原
static u16 timer1;
if(usart_receive_flag1)
{
Bootflag=1;
if(uart1_rev_cnt)
{
if(oldcount==uart1_rev_cnt)//新周期内,没有收到任何数据,认为本次数据接收完成.
{
applenth=uart1_rev_cnt;
oldcount=0;
uart1_rev_cnt =0;
usart_receive_flag1=0;//接受标志清零
//判断中断向量表地址(栈顶+4)是否合法 判断固件是否有效
add = (((u32)USART_RX_BUF[7])<<24)+(((u32)USART_RX_BUF[6])<<16)+(((u32)USART_RX_BUF[5])<<8)+(((u32)USART_RX_BUF[4])<<0);
if((add&0xFF000000)==0x08000000)
{
iap_write_appbin(APP_BUFF_ADDR,USART_RX_BUF,applenth);//下载APP代码至缓存区
delay_ms(100);
USART_RX_STA=0;//接受数组清零
printf("bin拷贝至缓存\r\n");
setNeedUpdata(applenth);//设置更新标志
printf("跳转入boot\r\n");
iap_load_app(FLASH_BOOTLODER_ADDR);//bootloader 代码
}
}else oldcount=uart1_rev_cnt;
}
}
if(Bootflag==1)
{
//延时20S如果更新失败则清零标志并且复位程序
if(IS_TMR100MS_GET(timer1,200))
{
RST_TMR100MS(timer1);
Bootflag=0;
usart_receive_flag1=0;
//复位程序
NVIC_SystemReset(); // 复位
}
}
}
以上有侵权可以联系本人,业余学习使用
3月16号补充:增加对标志位读写判断的宏定义
#define UPDATA_RUN 0x1144 //APP已经存在标志 告知bootload可以直接跳转至APP 不需要等待升级或更新
#define UPDATA_FLAG 0x5566 //更新代码标志
#define UPDATA_CLEAR 0xffff //更新代码标志清除
#define UPDATA_FLAG_ADDR 0x8003C00-2 //是否更新代码的标志地址
#define UPDATA_LENTH_ADDR UPDATA_FLAG_ADDR-4 //记录升级代码的长度地址#define FLASH_APP_BASE_ADDR 0x08000000 //STM32用户程序基地址地址
#define FLASH_BOOTLODER_ADDR 0x08000000 //BOOTLOADER地址
#define FLASH_BOOTLODER_ADDR_OFFSET 15*1024 //BOOTLOADER偏移地址 56K#define FLASH_APP1_ADDR FLASH_APP_BASE_ADDR+FLASH_BOOTLODER_ADDR_OFFSET //第一个应用程序起始地址
#define FLASH_APP1_OFFSET_ADDR 23*1024 //第一个应用程序偏移 90K#define APP_BUFF_ADDR FLASH_APP1_ADDR+FLASH_APP1_OFFSET_ADDR //APP下载时候缓存地址
#define APP_OFFSET_ADDR 23*1024 //APP下载时候缓存偏移 90K#define USER_FLASH_CODE_ADDR APP_BUFF_ADDR+APP_OFFSET_ADDR //用户自定义flash空间起始地址
#define USER_FLASH_CODE_OFFSET 3*1024 //用户自定义flash控件偏移 20k#define USART_REC_LEN 15*1024 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
增加部分串口定义
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u32 USART_RX_CNT; //接收状态标记 USART_RX_CNT
extern u32 uart1_rev_cnt ;