Slan 士兰微SC32F5664 通过串口IAP,实现OTA

  • 前言
  • 一、整体思路
    • 1.单片机
    • 2. 上位机(c#)
  • 二、整体流程
    • 1.单片机
      • 1.1 boot程序
      • 1.1 APP程序
    • 2.c#
  • 总结

前言

越来越多的设备在出厂的时候,自带boot,以便在出现问题不拆壳的情况下实现对设备的软件升级。士兰微的IAP在线更新程序因为资料比较少,做的过程中原厂给的资料也是模糊其词,所以我在做这个项目的过程中就想着做完的时候整理一下发出来,供需要的人参考。


一、整体思路

1.单片机

程序上电运行boot程序(注意:士兰微的芯片和ST的不太一样的地方是boot的启动地址不一样,从最后1K开始运行boot的程序大小是依次往前排,flash的地址也不一样,真正从0x0开始),看得懂的看下图,在boot程序里面会判断是否需要升级,需要升级会接收升级指令并作出应答,升级结束会校验app的完整性,然后校验通过才会跳转至APP程序运行;不需要升级的时候会直接校验APP完整性然后跳转至APP程序运行,在APP程序里面会接收升级指令,然后软重启至boot运行,重复上面的升级流程。单片机的实现流程很简单,要是有不太理解的朋友,可以先看一下我之前的一篇博客,关于STM32F051R8T6的远程升级的文章,链接: 点我查看

2. 上位机(c#)

因为我们使用的芯片IAP功能,考虑到升级过程中对flash擦写错误造成的不可控因素,所以会对整个app进行加密,在boot里面校验APP的完整性,这个就不描述了,因为加密的方式有很多,只说一下这个思路。加密的软件是一个独立的,不会共享出来。还有一个就是升级的软件,界面如下,操作起来也比较简单,因为项目上使用的升级是双驱的,所以增加了升级设备地址选择项,正常打开串口后,选择地址,没有问题就点击开始升级,上面有进度条提示,下面会一直刷新发送的升级数据和收到的单片机设备返回的数据。

二、整体流程

1.单片机

boot程序下载地址:链接 点我下载

1.1 boot程序

使用keil_mdk,先安装pack主持包,在boot程序里面选择带boot的芯片,如下图,在APP的程序里只需要选择不带boot的芯片,然后flashDownLoad的位置根据提示选择64K就可以了。

boot 代码如下(示例):

u8 dat=0;u16 FileCRCNum=0;uint8_t Erase_Cnt_8u = 0;uint8_t FlashSendBuff[8]={0};  ReadAddr = 0x0000CC00;
//  uint16_t i=0,j = 0;System_init();
//  MotorFile_Par_Set();
//  MAX_485_Send_Launch = 0;IRQ_Init();FlashUpdata=ReadFlashWord(0xcc00,8,FlashSendBuff);FlashUpdata=FlashSendBuff[0]+FlashSendBuff[1]*256;Updata_PackSize=FlashSendBuff[4]+FlashSendBuff[5]*256+(FlashSendBuff[6]+FlashSendBuff[7]*256)*65536;if(FlashUpdata==0xaa55){Databit.Bit.SendDataReadyFlag = 1;Databit.Bit.EraseFlashFlag = 1;Program_Addr = APP_ADDR;MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xfd;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x03;MAX_485_TX_BUF[6]=0x00;MAX_485_TX_BUF[7]=0x00;MAX_485_TX_BUF[8]=0x80;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],7);MAX_485_TX_BUF[9]=SendCRC_Value%256;MAX_485_TX_BUF[10]=SendCRC_Value/256;MAX_485_Send_Launch=1;G_Send_Len=11;TimeStopFlag=1;}  while(1){if(TimeBase_100ms==1){TimeBase_100ms=0;TGLBit(LED_ALARM_PORT_ABBR->OUTTGL,LED_ALARM_PIN);}#if 1if(TimeStopFlag == 0){if(L_TimeOutCnt_32u < 200*5000) L_TimeOutCnt_32u++;  //else{CheckApp();if(AppCheckPass)  //检查栈顶地址是否合法&APP是否完整正确{EraseFlashSector(0xcc00);CHIPCTL->CHIP_KEY =0x05fa659aul;CHIPCTL->REMAP_CTRL =0xa05f0000;            //REMAP清零,切换到从0地址开始运行SCB->AIRCR=((0x05FA0000ul)|(1<<2));   //系统软件复位}else{TimeStopFlag = 1;L_TimeOutCnt_32u = 0;}}        }   if(MAX_485_Receive_Sucess){if(MAX_485_RX_BUF[2]==Slave_ID){if(MAX_485_RX_BUF[3]==0xfd){if((MAX_485_RX_BUF[4]==0x00)&&(MAX_485_RX_BUF[5]==0x07)){Soft_Version=(MAX_485_RX_BUF[6]<<16)+(MAX_485_RX_BUF[7]<<8)+(MAX_485_RX_BUF[8]);Updata_PackSize=(MAX_485_RX_BUF[9]<<24)+(MAX_485_RX_BUF[10]<<16)+(MAX_485_RX_BUF[11]<<8)+(MAX_485_RX_BUF[12]);Databit.Bit.SendDataReadyFlag = 1;Databit.Bit.EraseFlashFlag = 1;Program_Addr = APP_ADDR;MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xfd;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x03;MAX_485_TX_BUF[6]=0x00;MAX_485_TX_BUF[7]=0x00;MAX_485_TX_BUF[8]=0x80;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],7);MAX_485_TX_BUF[9]=SendCRC_Value%256;MAX_485_TX_BUF[10]=SendCRC_Value/256;MAX_485_Send_Launch=1;G_Send_Len=11;EraseFlashSector(0xcc00);WriteFlashWord(0xcc00,0xaa55);
//                          CHIPCTL->CHIP_KEY =0x05fa659aul;
//                          CHIPCTL->REMAP_CTRL =0xa05f0001;            //REMAP清零,切换到从0地址开始运行
//                          SCB->AIRCR=((0x05FA0000ul)|(1<<2));   //系统软件复位}}else if(MAX_485_RX_BUF[3]==0xef){Databit.Bit.SendDataReadyFlag = 1;Uart0_Data.FlashWriteCnt++;if((Uart0_Data.FlashWriteCnt - 8) == 0) {Uart0_Data.FlashWriteCnt = 0;Databit.Bit.EraseFlashFlag = 1;}Uart0_Data.G_Offset_32u = (MAX_485_RX_BUF[6]<<24)+(MAX_485_RX_BUF[7]<<16)+(MAX_485_RX_BUF[8]<<8)+MAX_485_RX_BUF[9];if((Program_Addr == (APP_ADDR + (Uart0_Data.G_Offset_32u-1)*128))&&(Program_Addr <= 0x0000CC00)){Databit.Bit.WriteErrFlag=0;for(Uart0_Data.FlashWriteCnt_16u = 0;Uart0_Data.FlashWriteCnt_16u < (Uart0_Data.U0Data_Length-4);Uart0_Data.FlashWriteCnt_16u+=4)             //(ModbusRegAmount-1){if(Uart0_Data.U0Data_Length<132){if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)<4){if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==3){Byte_Word=((MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x00ffffff);WriteFlashWord(Program_Addr,Byte_Word);Program_Addr += 3;dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];}else if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==2){Byte_Word=(MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x0000ffff);WriteFlashWord(Program_Addr,Byte_Word);Program_Addr +=2;dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];}else if((Uart0_Data.U0Data_Length-4-Uart0_Data.FlashWriteCnt_16u)==1){Byte_Word=(MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u])&(0x000000ff);WriteFlashWord(Program_Addr,Byte_Word);Program_Addr += 1;dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];}}else {Byte_Word=(MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u];WriteFlashWord(Program_Addr,Byte_Word);Program_Addr += 4;dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]];}}else  if(Uart0_Data.U0Data_Length==132){Byte_Word=(MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u])*65536+MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]*256+MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u];WriteFlashWord(Program_Addr,Byte_Word);Program_Addr += 4;dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[10+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[11+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[12+Uart0_Data.FlashWriteCnt_16u]];dat = (FileCRCNum >> 8); FileCRCNum <<= 8;FileCRCNum ^= crc16tab[dat^MAX_485_RX_BUF[13+Uart0_Data.FlashWriteCnt_16u]];}}}else{Databit.Bit.WriteErrFlag =1;}if(Databit.Bit.WriteErrFlag==0){MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xef;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x04;MAX_485_TX_BUF[6]=(Uart0_Data.G_Offset_32u&0xff000000)>>24;MAX_485_TX_BUF[7]=(Uart0_Data.G_Offset_32u&0x00ff0000)>>16;MAX_485_TX_BUF[8]=(Uart0_Data.G_Offset_32u&0x0000ff00)>>8;MAX_485_TX_BUF[9]=(Uart0_Data.G_Offset_32u&0x000000ff)>>0;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],8);MAX_485_TX_BUF[10]=SendCRC_Value%256;MAX_485_TX_BUF[11]=SendCRC_Value/256;}else  //错误,返回0,关闭升级{MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xef;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x04;MAX_485_TX_BUF[6]=0;MAX_485_TX_BUF[7]=0;MAX_485_TX_BUF[8]=0;MAX_485_TX_BUF[9]=0;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],8);MAX_485_TX_BUF[10]=SendCRC_Value%256;MAX_485_TX_BUF[11]=SendCRC_Value/256;}G_Send_Len=12;MAX_485_Send_Launch=1;}else if(MAX_485_RX_BUF[3]==0xff){Databit.Bit.SendDataReadyFlag = 1;Uart0_Data.FileCRC_Num_16u= (MAX_485_RX_BUF[8]<<8)+MAX_485_RX_BUF[9];if(FileCRCNum==Uart0_Data.FileCRC_Num_16u){CheckApp();if(AppCheckPass){Databit.Bit.UpdataSussFlag=1;MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xff;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x01;MAX_485_TX_BUF[6]=0;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);MAX_485_TX_BUF[7]=SendCRC_Value%256;MAX_485_TX_BUF[8]=SendCRC_Value/256;}else{MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xff;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x01;MAX_485_TX_BUF[6]=2;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);MAX_485_TX_BUF[7]=SendCRC_Value%256;MAX_485_TX_BUF[8]=SendCRC_Value/256;}}else {MAX_485_TX_BUF[0]=0xa9;MAX_485_TX_BUF[1]=0x20;MAX_485_TX_BUF[2]=Slave_ID;MAX_485_TX_BUF[3]=0xff;MAX_485_TX_BUF[4]=0x00;MAX_485_TX_BUF[5]=0x01;MAX_485_TX_BUF[6]=1;SendCRC_Value=crc16(&MAX_485_TX_BUF[2],5);MAX_485_TX_BUF[7]=SendCRC_Value%256;MAX_485_TX_BUF[8]=SendCRC_Value/256;}G_Send_Len=9;MAX_485_Send_Launch=1;}}MAX_485_Receive_Sucess = 0;}if(Databit.Bit.SendDataReadyFlag){if(Databit.Bit.EraseFlashFlag){Databit.Bit.EraseFlashFlag = 0;if(Erase_Cnt_8u < 102)     //51K=102/2{EraseFlashSector(APP_ADDR+(Erase_Cnt_8u*512));                    //擦51KErase_Cnt_8u++;EraseFlashSector(APP_ADDR+(Erase_Cnt_8u*512));Erase_Cnt_8u++;}}Uart0_Data.U0SendDelayTime=300;Databit.Bit.SendDataReadyFlag =0;}#endif}}

1.1 APP程序

app程序牵扯到工程项目比较多,不方便外发,说一下具体的ota实现,正常运行程序,接收升级指令后会写升级标志位到flash,然后实现软重启至boot程序区运行。

2.c#

主要功能:串口通信,bin文件或者hex文件读取,数据重组等等,软件链接: 上位机软件下载

总结

以上是对文章的描述,希望可以帮助到需要的人,谢谢。

Slan 士兰微SC32F5664 通过串口IAP,实现OTA相关推荐

  1. (实验50)单片机,STM32F4学习笔记,代码讲解【串口IAP实验】【正点原子】【原创】

    文章目录 ❤2023重新理解记录 其它文章链接,独家吐血整理 实验现象 主程序 IAP初始化程序 代码讲解 文章目录 ❤2023重新理解记录 其它文章链接,独家吐血整理 实验现象 主程序 IAP初始化 ...

  2. c语言串口通信_STM32串口IAP分享

    点击上方「嵌入式大杂烩」,选择「置顶公众号」第一时间查看编程笔记! 什么是IAP? IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过程中对Use ...

  3. STM32F103/429串口IAP+Ymodem升级

    起因: 串口IAP升级在正点原子的例程中有讲解,正点原子的方法是:在RAM中开辟一个120K的数据空间,用来存放bin文件,bin文件通过串口一次性发送到单片机,然后再实现程序的跳转.但是这种方法在实 ...

  4. gcc编译-hal库-stm32f4xx串口IAP升级

    gcc编译-hal库-stm32f4xx串口IAP升级 gcc编译hal库方式实现STM32F4系列芯片IAP升级 1.Bootloader程序: 调用iap_load_app函数即可,输入参数为ap ...

  5. STM32 CUbeIDE 使用Ymodem协议进行串口IAP升级

    STM32 CUbeIDE 使用Ymodem协议进行串口IAP升级 1.Ymodem协议简介 2.Boodload程序 2.1 传输32K限制解决 3.APP程序 4.升级测试 5.工程文件下载    ...

  6. 差分升级 增量升级 单片机 STM32 IAP升级OTA升级,物联网车联网可用

    差分升级 增量升级 单片机 STM32 IAP升级OTA升级,物联网车联网可用 介绍博客 https://blog.csdn.net/zhou74281/article/details/1177776 ...

  7. 单片机串口IAP原理

    关注+星标公众号,不错过精彩内容 作者 | 正念 微信公众号 | 嵌入式大杂烩 什么是IAP? IAP是In Application Programming的首字母缩写,IAP是用户自己的程序在运行过 ...

  8. STM32 OTA应用开发——通过串口/RS485实现OTA升级(方式2)

    STM32 OTA应用开发--通过串口/RS485实现OTA升级(方式2) 目录 STM32 OTA应用开发--通过串口/RS485实现OTA升级(方式2) 前言 1 环境搭建 2 功能描述 3 程序 ...

  9. 【云隐】STM32F103C8T6实现串口IAP方式升级固件

    首先下载官方STM32F10X的IAP Bootloader源码,STM32F10x_AN2557_FW_V3.3.0. 源码包地址:http://download.csdn.net/download ...

最新文章

  1. jQuery选择器回顾,IE8还需要你发光发热
  2. Vue.js 整理笔记
  3. HIVE元数据表/数据字典
  4. ols残差_python数据关系型图表散点图系列残差分析图
  5. Hadoop初级之Hadoop基本概念与应用前景
  6. 如果你喜欢上了一个程序员小伙 献给所有程序员女友(来自ITeye博客的文章 作者:talent2012)...
  7. Microsoft Build 2018 直播来啦!
  8. shell循环和分支
  9. Nginx学习总结(13)——Nginx 重要知识点回顾
  10. 今日恐慌与贪婪指数为60 贪婪程度有所缓解
  11. 告别求职难!一汽-大众专场直播招聘来了
  12. 早上内部资源共享讨论发言稿
  13. DVWA中学习PHP常见漏洞及修复方法
  14. css常见样式命名思想
  15. 谷歌高管地震:谷歌大脑联合创始人Samy Bengio离职了
  16. 第二章 七个习惯概论
  17. 计算机一级怎么查错题,独家秘笈计算机一级错题解释.ppt
  18. 走着瞧Anbsp;Talenbsp;ofnbsp;Twonbsp;Donkeysnbsp;(200…
  19. 自动开机和自动关机设定方法(包括linux和windows)
  20. 天天在捣鼓Docker,你是否真正的把握住了?

热门文章

  1. 他山之石 | 百度大小模型联动及落地实践
  2. 联想小新潮7000触摸板失灵_联想小新潮7000装的w7系统鼠标触控板不能用了,能解决吗?...
  3. oracle 排序值相等排名相等,Oracle-分析函数之排序值rank()和dense_rank()
  4. python学完后做什么_python学完之后主要是做什么?
  5. 基于JSP的足球直播论坛的设计与实现
  6. ubuntu16.04下安装GTX1080TI显卡驱动+安装CUDA+Cudnn+anaconda+GPU版tensorflow
  7. 精讲js数组的添加、删除、截取、合并、拷贝
  8. Ubuntu 20.04 桌面版安装显卡驱动过程
  9. Reveal安装教程
  10. python制作会动的表情包_有趣的python小项目,自动生成有趣的表情包!