打印
[综合信息]

HC32L130基于Xmodem协议实现IAP串口在线升级

[复制链接]
11387|19
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
一、BOOT程序移植

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





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





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

  • 系统时钟设置(选择设置)
  • 串口发送接收
  • IO操作
  • flash擦除写入
  • 定时器中断



使用特权

评论回复
沙发
八层楼|  楼主 | 2021-8-1 15:36 | 只看该作者
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();
               
               



}


使用特权

评论回复
板凳
八层楼|  楼主 | 2021-8-1 15:37 | 只看该作者
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 TX
       
    stcGpioCfg.enDir = GpioDirIn;
    Gpio_Init(GpioPortA,GpioPin10,&stcGpioCfg);
    Gpio_SetAfMode(GpioPortA,GpioPin10,GpioAf1);//配置PA0D 为UART1 RX
       
       
       
       
    Sysctrl_SetPeripheralGate(SysctrlPeripheralUart0,TRUE);//使能UART1外设时钟门控开关

    stcCfg.enRunMode = UartMskMode1;                       //模式1
    stcCfg.enStopBit = UartMsk1bit;                        //1位停止位
    stcCfg.enMmdorCk = UartMskEven;                        //偶校验
    stcCfg.stcBaud.u32Baud = baudRate;                         //波特率baudRate
    stcCfg.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);                      //清接收请求
       

       
}


使用特权

评论回复
地板
八层楼|  楼主 | 2021-8-1 15:37 | 只看该作者
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;                  //定时器功能,计数时钟为内部PCLK
    stcTim3BaseCfg.enPRS      = Tim3PCLKDiv64;              //PCLK/64 0.75M
    stcTim3BaseCfg.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 开中断
}


使用特权

评论回复
5
八层楼|  楼主 | 2021-8-1 15:38 | 只看该作者
定时器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 中断标志清除
    }
}


使用特权

评论回复
6
八层楼|  楼主 | 2021-8-1 15:39 | 只看该作者

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


使用特权

评论回复
7
八层楼|  楼主 | 2021-8-1 15:40 | 只看该作者
修改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;
}


使用特权

评论回复
8
八层楼|  楼主 | 2021-8-1 15:40 | 只看该作者
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();

       
}


使用特权

评论回复
9
八层楼|  楼主 | 2021-8-1 15:41 | 只看该作者
回到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 中断标志清除
    }
}


使用特权

评论回复
10
八层楼|  楼主 | 2021-8-1 15:42 | 只看该作者
主程序中挂载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.
        }
}       


使用特权

评论回复
11
八层楼|  楼主 | 2021-8-1 15:43 | 只看该作者
完整的主程序

int32_t main(void)
{
        App_RCH24MHzToPll48MHz();
       
  Flash_Init(12, TRUE);

       
        LED_GPIO_Config();
       
        UART0_Config(115200);
       
  App_Timer3Cfg(4); //4ms
  Tim3_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更新程序了。


使用特权

评论回复
12
八层楼|  楼主 | 2021-8-1 15:43 | 只看该作者
二、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文件,网上教程很多,不详细写了。




使用特权

评论回复
13
八层楼|  楼主 | 2021-8-1 15:44 | 只看该作者

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

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


使用特权

评论回复
14
0逍遥天尊0| | 2021-9-23 09:18 | 只看该作者
没有源码吗

使用特权

评论回复
15
lvanping| | 2023-3-2 15:57 | 只看该作者
mark下

使用特权

评论回复
16
saservice| | 2023-4-7 09:46 | 只看该作者
modbus和Xmodem有什么区别

使用特权

评论回复
17
uptown| | 2023-4-7 10:19 | 只看该作者
Xmodem:这种古老的传输协议速度较慢

使用特权

评论回复
18
jimmhu| | 2023-4-7 22:54 | 只看该作者
传输的准确率可高达99.6%。      

使用特权

评论回复
19
maqianqu| | 2023-4-7 23:04 | 只看该作者
如何使用Xmodem传输交换机系统文件

使用特权

评论回复
20
louliana| | 2023-4-8 14:01 | 只看该作者
如何使用Xshell6的XMODEM功能发送和接收文件

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

91

主题

4146

帖子

2

粉丝