基于STM32F103C8T6串口IAP升级

调试遇到难点:

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 ;

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值