查看: 5210|回复: 2

复旦微FM33L026实现远程升级

[复制链接]

217

主题

393

帖子

3477

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3477
QQ
发表于 2020-12-31 09:15:50 | 显示全部楼层 |阅读模式
前言

  本文主要记录FM33L02x在自己写的程序中通过某些途径获取到固件后,如何写入flash,以及从 bootloader引导程序 到 用户程序 的过程。

  固件数据流的获取方法(如:串口、spi、以太网、wifi等)本文不作分析。

  在 bootload 示例程序中有相关说明文档,并提供了三个示例:本地上电、远程备份、远程不备份。本文将以 “ 远程备份 ” 的例程改为自己实现的远程升级固件的程序,并记录开发过程中遇到的问题。

---------------------------------------------------------------------------------------------------------------------------------------

一、原理

我们先来看看地址空间分配:

1.png

我们可以看到,flash的存放地址在 0x0000_0000 ~ 0x0002_0000。

将存储空间分4区:

地址
名称
说明
0~16K
bootloader区
用于存放boot loader引导程序
16~68K
用户代码区
存放用户程序
68~120K
用户备份区
存放新用户程序
120~122K
配置信息区
存放升级配置信息和标志

根据个人情况,可以适当调整各区空间大小分布。

正常启动流程:

  • 进入引导程序。
  • 查看配置信息区标志,看是否需要升级固件。
  • 从引导程序跳到用户代码区,进入用户程序。

升级固件流程:

  • 在用户程序分包接收新固件,并写入用户备份区。
  • 写入升级配置信息到配置信息区。
  • 重启。
  • 进入引导程序,查看配置信息区标志。
  • 将用户备份区复制到用户代码区。
  • 跳到用户代码区,进入用户程序。

综上,我们写代码时要创建两个工程,一个是引导程序,一个是用户程序,分别烧写到不同的两个区域。


二、bootloader引导程序

宏定义:

  • 1 #define APPLICATION_ADDRESS_OFFSET                              (0x4000)        //用户程序起始地址 16K               
  • 2 #define BOOTLOAD_PAGE_SIZE                                            (512)                 //页长度(字节)
  • 3 #define BOOTLOAD_CODE_LEN_MAX                                     (0xD000)         //升级代码最大长度 52K
  • 4 #define BOOTLOAD_USER_START_ADDRESS                          (0x4000)         //用户程序起始地址 16K flash处
  • 5 #define BOOTLOAD_BACKUP_START_ADDRESS                       (0x11000)         //升级暂存程序起始地址68K flash处
  • 6 #define BOOTLOAD_CONFIG_START_ADDRESS                       (0x1E000)         //升级配置信息起始地址120K flash处

main函数:

int main (void)
{               
        uint32 JumpAddress;
        int i = 0;        Init_System();
        Init_CRC_CRC16CCITT();        // LED0 闪5下      
        for (i = 0; i < 5; ++i)        
       {
                GPIO_ToggleBits(GPIOC, GPIO_Pin_0);
                TicksDelayMs( 50, NULL );        
        }
        if(BOOTLOAD_file() == 0)// 查看是否有需要bootload的文件
       {
                // 升级成功
       }
        TicksDelayMs( 100, NULL ); //这里不加延时会卡死
    /* 跳转到应用程序位置 */
       JumpAddress = *(__IO uint32*) (APPLICATION_ADDRESS_OFFSET + 4);
       __set_MSP(*(__IO uint32*) APPLICATION_ADDRESS_OFFSET);
        (*( void (*)( ) )JumpAddress) ();
}

如果需要其他功能可以自行添加。

【注意】刷固件后,要延迟100ms左右再跳到用户程序,不然程序会卡死。


三、用户程序
1、修改中断向量偏移

既然整段程序都偏移了,那么中断向量也要偏移,不然用户程序里的中断就会跑到引导程序里了!

在项目中找到 system_FM33L0XX.c,在 SystemInit() 中添加 SCB->VTOR = 0x00004000;

2.png

【注意】我这里的项目选择的是 FM33L02X,所以修改的是 FM33L02X 文件夹中的 system_FM33L0XX.c,如果你选的是 FM33L01X,那就要改 FM33L01X 文件夹里的。

2、keil的相关配置

设置用户程序的地址偏移:设置修改ROM,从 0x4000 开始,长度为 0x1C000。

3.png

4.png

设置按扇区擦除,不要全擦:

5.png

3、FLASH擦写库函数

在例程的 flash.c 里提供了flash的擦写库函数。

1)FLASH擦扇区函数

   1 | extern uint8_t Flash_Erase_Sector( uint16_t SectorNum ); //FLASH擦扇区
  • SectorNum:被擦扇区号 。512个字节为一个扇区。
  • 返回值: 0:成功;其他:发生错误

2)FLASH写入

  1 |extern uint8_t Flash_Write_String( uint32_t prog_addr, uint32_t* prog_data, uint16_t Len);


  • prog_addr:要写入的地址
  • prog_data:要写入的数据
  • Len: 数据长度(4字节的个数,以 uint32_t 为单位)
  • 返回值: 0:成功;其他:发生错误

【注意】flash写前必须先擦除,不然程序卡死。

四、keil生成固件
1、hex文件与bin文件的区别

参考:hex文件和bin文件的区别和联系

简单来说,hex文件是将bin文件的16进制转ASCII字符串保存起来,所以bin文件才是真正的固件。

所以我们直接写入flash的是bin文件而不是hex文件。

2、keil生成bin文件

使用 fromelf.exe 工具生成bin文件。

  • 在Keil里:Option - User - After Build/Rebuild 中,勾选Run #1。
  • Run #1 中填入:fromelf.exe --bin -o “@L.bin” “#L”

6.png


再次重新编译工程,bin文件会生成在.uvprojx文件所在文件夹中。


五、CRC-16/CCITT 算法实现

在例程中,bootloader引导程序里是用CRC-16/CCITT来校验固件的完整性。为了不对file_bootloader.c作太大的改动,我们的校验也用CRC-16/CCITT。

CRC-16/CCITT的原理和算法网上有很多:

  • 按位计算:运算量大,占用空间小。
  • 按字节计算:运算量小,占用空间大。
  • 按半字节计算:运算量比按字节计算大1倍左右,占用空间是按位计算的1/16。

大家可以根据个人情况挑选适合自己算法。


下面是我在Qt上用的按位计算的算法:

/**
*  x16+x12+x5+1
*  多项式 POLY        0x1021
*  初始值 INIT        0x0000
*  结果异或值 XOROUT   0x0000
*  输入数据反转(REFIN)
*  输出数据反转(REFOUT)
*/
quint16 MainWindow::CRC16CCITT(quint8 *data, quint16 len)
{
    quint16 crc_reg = 0;
    quint8  index;
    quint16 to_xor;


    for (int i = 0; i < len; i++)
    {
        index = (crc_reg ^ data[i) & 0xff;
        to_xor = index;
        for (int j = 0; j < 8; j++)
        {
            if (to_xor & 0x0001)
                to_xor = (to_xor >> 1) ^ 0x8408;
            else
                to_xor >>= 1;
        }
        crc_reg = (crc_reg >> 8) ^ to_xor;
    }
    return crc_reg;
}

crc16验证工具:CRC(循环冗余校验)在线计算


六、FM33L0xx软复位

查看说明书:

代码:

1| RCC_SOFTRST_Write(0x5C5CAABB); //软件复位



原帖:https://blog.csdn.net/qq_37388044/article/details/111057497【已征得作者同意】


回复

使用道具 举报

0

主题

5

帖子

68

积分

中级工程师

Rank: 2

积分
68
发表于 2021-2-2 20:48:33 | 显示全部楼层
复旦微的FAE在此基础上如果进一步完善更好:
(1) 各系列的MCU都有一个样例版本
(2) 上位机串口进行代码更新的DEMO,因为大部分远程更新本质上还是通过串口来获取数据。

回复

使用道具 举报

217

主题

393

帖子

3477

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
3477
QQ
 楼主| 发表于 2021-2-3 09:11:25 | 显示全部楼层
j1j1h1 发表于 2021-2-2 20:48
复旦微的FAE在此基础上如果进一步完善更好:
(1) 各系列的MCU都有一个样例版本
(2) 上位机串口进行代码更新 ...

感谢您的意见,目前主要系列的boot loader升级DEMO都是有的,以下是资料汇总链接,您可以看一下
http://www.fmdevelopers.com.cn/f ... thread&tid=1749
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表