在开始写文章之前,要先吐槽一下国产单片机,不对是国产单片机厂家,他们的技术支持实在太烂了,烂的让你怀疑等技术支持还不如自己啃手册,尤其是那种中间有代理商的厂家,技术支持更是良莠不齐,时效性也不行,问个问题等几天之后回复,最可气的是回复告诉你这个功能芯片不支持,真是伤不起了。如果成本不是特别敏感,研发实力不是很强大,不要碰国产单片机,特别是有中间代理的。

一、BOOT程序移植

HC32官方提供了一个自定义协议的IAP升级demo,自定义协议,但是苦于没有提供上位机程序源码,不方便以后升级,而且官方的升级软件看着也很不完善。如下图所示。所以决定自己移植一下Xmodem协议。

之前在别的单片机上用过Xmodem协议做串口IAP升级协议,所以这里,只需要移植一下就可以了。Xmodem框架如下图所示。

因为框架已经写好,所以只需要修改对应的接口就可以了。在进行IAP的过程主要涉及到如下四个外设

  1. 系统时钟设置(选择设置)
  2. 串口发送接收
  3. IO操作
  4. flash擦除写入

  5. 定时器中断

1、hc32L130上电默认时钟为内部4M时钟,如果不想使用高的波特率这步可以省略,因为串口波特率我要使用115200,如果不倍频的话PCLK是4M,那么波特率115200误差会达到8.51%,而要想可靠使用串口,波特率误差应该小于2%。所以我将系统时钟倍频到48M,这时115200误差率0.16%,如下图所示。

将4MRCH倍频到48M代码如下。

void App_RCH4MHzToPll48MHz (void)
{M0P_FLASH->BYPASS = 0x5A5A;M0P_FLASH->BYPASS = 0xA5A5;M0P_FLASH->CR_f.WAIT = FlashWaitCycle1;M0P_SYSCTRL->PLL_CR_f.FRSEL  = SysctrlPllInFreq4_6MHz;M0P_SYSCTRL->PLL_CR_f.FOSC   = SysctrlPllOutFreq36_48MHz;M0P_SYSCTRL->PLL_CR_f.DIVN   = SysctrlPllMul12;M0P_SYSCTRL->PLL_CR_f.REFSEL = SysctrlPllRch;M0P_SYSCTRL->PLL_CR_f.STARTUP = SysctrlPllStableCycle16384;M0P_SYSCTRL->SYSCTRL2 = 0x5A5A;M0P_SYSCTRL->SYSCTRL2 = 0xA5A5;M0P_SYSCTRL->SYSCTRL0_f.PLL_EN = TRUE;while(TRUE && (1 != M0P_SYSCTRL->PLL_CR_f.STABLE)){;}M0P_SYSCTRL->SYSCTRL2 = 0x5A5A;M0P_SYSCTRL->SYSCTRL2 = 0xA5A5;M0P_SYSCTRL->SYSCTRL0_f.CLKSW = SysctrlClkPLL;//更新Core时钟(HCLK)SystemCoreClockUpdate();}

2、串口初始化代码

void UART0_Config(uint32_t baudRate)
{stc_gpio_cfg_t stcGpioCfg;stc_uart_cfg_t  stcCfg;stc_uart_baud_t stcBaud;DDL_ZERO_STRUCT(stcCfg);                               //初始化变量DDL_ZERO_STRUCT(stcBaud);                              //初始化变量DDL_ZERO_STRUCT(stcGpioCfg);Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE); //GPIO外设模块时钟使能stcGpioCfg.enDir = GpioDirOut;Gpio_Init(GpioPortA,GpioPin9,&stcGpioCfg);Gpio_SetAfMode(GpioPortA,GpioPin9,GpioAf1); //配置PD00 为UART1 TXstcGpioCfg.enDir = GpioDirIn;Gpio_Init(GpioPortA,GpioPin10,&stcGpioCfg);Gpio_SetAfMode(GpioPortA,GpioPin10,GpioAf1);//配置PA0D 为UART1 RXSysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);//使能UART1外设时钟门控开关stcCfg.enRunMode = UartMskMode1;                       //模式1stcCfg.enStopBit = UartMsk1bit;                        //1位停止位stcCfg.enMmdorCk = UartMskEven;                        //偶校验stcCfg.stcBaud.u32Baud = baudRate;                         //波特率baudRatestcCfg.stcBaud.enClkDiv = UartMsk8Or16Div;             //通道采样分频配置stcCfg.stcBaud.u32Pclk = Sysctrl_GetPClkFreq();        //获得外设时钟(PCLK)频率值Uart_Init(M0P_UART0, &stcCfg);                         //串口初始化//    Uart_EnableIrq(M0P_UART1,UartRxIrq);
//      EnableNvic(UART1_IRQn, IrqLevel3, TRUE);                        ///<系统中断使能Uart_ClrStatus(M0P_UART0,UartRC);                      //清接收请求}

3、flash初始化代码

  Flash_Init(12, TRUE);    // PCLK 为48M

4、定时器3这里给xmodem提供时间基准定时时间为4ms。

//Timer3 配置
void App_Timer3Cfg(uint16_t u16Period)
{uint16_t                    u16ArrValue;uint16_t                    u16CntValue;stc_tim3_mode0_cfg_t     stcTim3BaseCfg;//结构体初始化清零DDL_ZERO_STRUCT(stcTim3BaseCfg);Sysctrl_SetPeripheralGate(SysctrlPeripheralTim3, TRUE); //Base Timer外设时钟使能stcTim3BaseCfg.enWorkMode = Tim3WorkMode0;              //定时器模式stcTim3BaseCfg.enCT       = Tim3Timer;                  //定时器功能,计数时钟为内部PCLKstcTim3BaseCfg.enPRS      = Tim3PCLKDiv64;              //PCLK/64 0.75MstcTim3BaseCfg.enCntMode  = Tim316bitArrMode;           //自动重载16位计数器/定时器stcTim3BaseCfg.bEnTog     = FALSE;stcTim3BaseCfg.bEnGate    = FALSE;stcTim3BaseCfg.enGateP    = Tim3GatePositive;Tim3_Mode0_Init(&stcTim3BaseCfg);                       //TIM3 的模式0功能初始化u16ArrValue = 0x10000 - 750*u16Period ;Tim3_M0_ARRSet(u16ArrValue);                            //设置重载值(ARR = 0x10000 - 周期)u16CntValue = 0x10000 - 750*u16Period;Tim3_M0_Cnt16Set(u16CntValue);                          //设置计数初值Tim3_ClearIntFlag(Tim3UevIrq);                          //清中断标志Tim3_Mode0_EnableIrq();                                 //使能TIM3中断(模式0时只有一个中断)EnableNvic(TIM3_IRQn, IrqLevel3, TRUE);                 //TIM3 开中断
}

定时器3中断函数

void TIM3_IRQHandler(void)
{static uint8_t i,count;//Timer3 模式0 计时溢出中断if(TRUE == Tim3_GetIntFlag(Tim3UevIrq)){
//          count++;
//          if(count>10){count = 0;xmodem_timeout++;if(0 == i)                        //测试{LED0(LED_OFF);i++;}else{LED0(LED_ON);i = 0;}}Tim3_ClearIntFlag(Tim3UevIrq);  //Timer3模式0 中断标志清除}
}

下面开始移植Xmodem协议程序,首先将框架程序添加进工程

修改xmodem_HW.C硬件接口。将串口发送接收程序移植进去。

//串口收发,实用查询方式。
void xm_port_write(uint8 *ch)
{/* 一直等待, 直到缓冲区为空 */M0P_UART0->ICR_f.TCCF = 0;M0P_UART0->SBUF = (uint8_t)*ch;while(M0P_UART0->ISR_f.TC == 0);M0P_UART0->ICR_f.TCCF = 0; }//串口接收函数,需要移植
sint8 xm_port_read(uint8 *ch)
{if(Uart_GetStatus(M0P_UART0, UartRC)){Uart_ClrStatus(M0P_UART0,UartRC);*ch = M0P_UART0->SBUF;return 1;}/* 返回接收到的8位数据 */return 0;
}

xmodem.c中KBflashcopy函数添加flash擦除写入函数。

void KBflashcopy(uint32_t flash_addr,uint8_t* src_addr,sint32 count)
{uint32_t start_addr = flash_addr;uint16_t i  =  0;uint32_t temp = 0;DI(); Flash_Erase(start_addr);Flash_Erase(start_addr+512);for(i=0;i<count;i+=4){temp = (src_addr[i+3] << 24) |(src_addr[i+2] << 16) |(src_addr[i+1] << 8)  |(src_addr[i+0]) ;
//    Flash_Program1LongWord(start_addr,temp);Flash_Write(start_addr,temp);start_addr += 4;}EI();}

回到main.c中在定时器3中断中添加xmodem超时计数变量xmodem_timeout,需要外部引入下。

void TIM3_IRQHandler(void)
{static uint8_t i,count;//Timer3 模式0 计时溢出中断if(TRUE == Tim3_GetIntFlag(Tim3UevIrq)){
//          count++;
//          if(count>10){count = 0;xmodem_timeout++;if(0 == i){LED0(LED_OFF);i++;}else{LED0(LED_ON);i = 0;}}Tim3_ClearIntFlag(Tim3UevIrq);  //Timer3模式0 中断标志清除}
}

主程序中挂载xm_receive()函数。FLASH_APP_ADDR为APP程序写入起始地址。

写入完成后执行APP程序,实现方法为

//跳转到应用程序段
//appxaddr:用户代码起始地址
void iap_load_app(uint32_t appxaddr)
{if(((*(__IO uint32_t*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法{ jump2app=(iapfun)*(__IO uint32_t*)(appxaddr+4);     //用户代码区的第二个字为程序开始地址(复位地址)     //MSR_MSP(*(__IO uint32_t*)appxaddr);                   //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址)__set_MSP(*(__IO uint32_t *)appxaddr);jump2app();                                 //跳转到APP.}
}        

完整的主程序

int32_t main(void)
{App_RCH24MHzToPll48MHz();Flash_Init(12, TRUE);LED_GPIO_Config();UART0_Config(115200);App_Timer3Cfg(4); //4msTim3_M0_Run();   //TIM3 运行。i = xm_receive(FLASH_APP_ADDR);            //xmodem接收的APP地址从APP_ADDR开始M0P_RESET->PERI_RESET = 0;      //所有外设复位M0P_RESET->PERI_RESET = 0xFFFFFFFF;while(1){iap_load_app(FLASH_APP_ADDR);    ///*IAP 跳转*///      //      };
}

程序移植完成编译通过后,下载进目标板打开上位机软件即可开始IAP更新程序了。

二、APP程序keil环境设置

APP 程序主要修改中断向量的偏移,打开APP程序中的启动文件startup_hc32l130j8ta.s文件,需要修改两处。

第一处在堆栈定义下面添加中断向量偏移,new_vect_table  EQU     0x00001800         ;中断向量偏移长度

第二处在RAMCODE处添加               LDR     R2, =new_vect_table

到这里启动文件就修改好了,接下来打开keil 魔术棒,修改编译生成的程序的偏移地址。因为APP占用了8K的flash,所以偏移量设置成8K,即0x1800。

在生成bin文件,网上教程很多,不详细写了。

三、一切准备就绪后打开上位机软件,上位机软件是基于一个QT开源软件修改的,感兴趣的可以上GitHub上搜索。

选择正确的串口,连接串口,加载生产的main.bin程序,点击升级,即可完成APP更新。

总结

总体上是单片机上电先运行Boot,然后运行app,这里有个问题,如果boot里面将PCLK倍频到了48M,在APP中再进行倍频单片机就会死机,所以建议APP中上电读取下当前系统频率,如果已经倍频了就不要在倍频了,否则分分钟死机。

HC32L130基于Xmodem协议实现IAP串口在线升级相关推荐

  1. STM32 IAP 串口在线升级源码详解

    本博文基于本人修改的代码进行编写如需代码, 请到[代码下载](https://download.csdn.net/download/qq_36257571/10695681)自行下载. 详细内容不做过 ...

  2. 基于Modbus协议的C#串口工具开发

    (Tips:本人纯萌新小白一枚,文章只为记录自己的编程历史,温故而知新,能帮助到大家倍感荣幸,有错误的地方劳烦指点!) 不多废话直接进入主题! 本文旨在基于Modbus协议.C#开发语言进行串口工具的 ...

  3. ALTERA系列的FPGA通过RS232串口在线升级

    ARM系列的芯片(例如STM32系列)可以通过YMODEM协议实现在线升级,在芯片中写入BOOT程序.APP程序就可以实现.那FPGA可以这样做吗,答案是可以的.要想搞清楚流程就必须了解FPGA的启动 ...

  4. 通俗易懂Xmodem协议

    在工作中写Bootloader时,需要串口传输代码数据,为了保证传输数据时不丢帧,需要用到通信协议,我选择的是Xmodem协议. 文章目录 1. 定义 2. 控制字符 3. 帧数据格式 4. 数据包说 ...

  5. 基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级

    基于Ymodem协议的stm32f405rgt6+CubeMx+IAP在线升级 目录 一.CubeMX的配置 1.IAP 2.APP 二.移植Ymodem官方代码 1.文件移植 2.MDK文件.路径添 ...

  6. 野火串口助手协议发送文件通讯协议——XMODEM协议——YMODEM协议

    野火串口助手协议发送文件通讯协议 修订历史 日期 版本 更新内容 2020/6/22 0.0.1 首次发布 XMODEM协议 上位机是现实了XModem-CRC16和XModem-1K; XModem ...

  7. STM32F103代码远程升级(三)基于YModem协议串口升级程序的实现

    文章目录 一.YModem协议简介 二.YModem的数据格式 1.起始帧的数据格式 2.数据帧的数据格式 3.结束帧的数据格式 4.文件传输过程 三.基于Ymodem协议串口升级程序的实现过程 1. ...

  8. Xmodem 协议介绍及应用(基于 ESP-IDF)

    Xmodem 协议介绍及应用(基于 ESP-IDF) 目录 1. 介绍 2. 目的 3. 硬件准备 4. 环境搭建 5. SDK 准备 6. 功能介绍 7. 编译&烧写&运行 1. 介 ...

  9. 基于MODBUS协议的单片机与(串口屏)触摸屏通信(图文)

    基于MODBUS协议的单片机与(串口屏)触摸屏通信(图文) 导读:触摸屏能够直观.生动地显示运行参数和运行状态,而且通过触摸屏画面可以直接修改系统运行参数,人机交互性好.触摸屏和单片机通信,需要根据触 ...

最新文章

  1. 2dpsk调制解调实验matlab_【无线通信篇】你知道ASK调制吗?
  2. html单行注释符号
  3. 每天一个JavaScript实例-获取元素当前高度
  4. [2009.08.09]博客园北京俱乐部活动暨《博客园精华集:Web标准之道》现场签售通知...
  5. 利用python爬虫(part1)--urllib.request模块
  6. android 彩蛋 miui,MIUI12最新更新,安卓11彩蛋终于出现,流畅度提升
  7. HashOperations
  8. java异常不懂,看不懂的异常提示啊
  9. mysql浮点数据怎么_MySQL数据浮点类型的实际应用操作
  10. 安卓robot自动化测试包经常用的方法
  11. linux如何压缩磁盘,Linux初级运维(十二)——磁盘及文件系统管理
  12. 从Java到JVM到OS线程睡眠
  13. ppt图片去水印-ppt图片去水印怎么设置
  14. mysql mtq_Mysql常用简介 - osc_r3mtqivi的个人空间 - OSCHINA - 中文开源技术交流社区
  15. 计算机视觉作业(一)Image Filtering and Hybrid Images
  16. Linux-smb服务器搭建
  17. 送书 | 《数据产品经理:实践进阶》
  18. 详细揭秘微信小程序框架技术——Mpx
  19. [BZOJ5020] [THUWC 2017]在美妙的数学王国中畅游
  20. WebKit(WKUIDelegate)

热门文章

  1. 如何注册和申请阿里云域名?
  2. JavaMail连接Office 365使用XOAUTH2身份认证
  3. CCP2.1协议基础知识
  4. C#上位机开发连接三菱PLC
  5. springboot配置两个parent的方法
  6. 计算机界的“武林秘籍”——经典教材推荐
  7. 跨境电商属于外贸吗,Starday跨境电商靠谱吗?
  8. 苏州微软面试--机器学习自然语言处理
  9. 创建数据库失败((Microsoft.SqlServer.Smo))执行Transact-SQL语句或批处理时发生了异常。
  10. 厦门大学计算机学院李平,学术盛会,知行合一 ——2020年厦门大学信息学院计算机科学系研究生学术论坛圆满举办...