CH582 Bootloader

一、框架介绍

上一篇我们已经完成了CherryUSB的移植,今天我们就来基于CherryUSB来制作一个USB的bootloader。其实实现方式有挺多种,例如像cdc,hid,msc,dfu等等,只要能实现用usb将固件文件安全的发送至单片机,单片机再将收到的数据存到对应的app flash区域,然后再跳转运行bootloader即可。为此我编写了一个简单的bootloader框架,PlumBL,因为有了CherryUSB和PlumBL这样两个框架,在对不同平台的适配将会方便很多,usb只需完成usb_dc的移植,而bootloader只需要移植flash读写以及跳转等等。

CherryUSB 上一篇文章我们已经介绍过了,并且完成了CH582 usb_dc的编写,这次我们来简单介绍一下PlumBL这个bootloader小框架。仓库地址放在了文章的末尾。

首先我们看看核心的API:

/**
 * @brief            Whether the hardware enters the bootloader
 * @pre              None
 * @param[in]        None
 * @retval           bool true: Enter bootloader // false: Do not enter bootloader
 */
bool lgk_boot_hard_is_enter(void);

/**
 * @brief            Wait ms
 * @pre              None
 * @param[in]        ms Waiting time
 * @retval           None
 */
void lgk_boot_deley_ms(uint32_t ms);

/**
 * @brief            Boot loader jump to app
 * @pre              App is vaild
 * @param[in]        app_add App start address
 * @retval           None
 */
void lgk_boot_jump_app(uint32_t app_add);

/**
 * @brief            System software reset
 * @pre              None
 * @param[in]        None
 * @retval           None
 */
void lgk_boot_sys_reset(void);

/**
 * @brief            Check app is vaild
 * @pre              None
 * @param[in]        check_code_add Check code address
 * @retval           bool true: app is vaild // false is not vaild
 */
bool lgk_boot_app_is_vaild(uint32_t check_code_add);

/**
 * @brief            Code flash erase
 * @pre              None
 * @param[in]        start_add Erase start address
 * @param[in]        size      Erase size
 * @retval           None
 */
void lgk_boot_flash_erase(uint32_t start_add, uint32_t size);

/**
 * @brief            Code flash write
 * @pre              None
 * @param[in]        start_add Write start address
 * @param[in]        buffer    Data address to be written
 * @param[in]        size      Length of written data
 * @retval           None
 */
void lgk_boot_flash_write(uint32_t start_add, void *buffer, uint32_t size);

/**
 * @brief            Code flash read
 * @pre              None
 * @param[in]        start_add Read start address
 * @param[in]        buffer    Store read data
 * @param[in]        size      Length of data read
 * @retval           None
 */
void lgk_boot_flash_read(uint32_t start_add, void *buffer, uint32_t size);

/**
 * @brief            Initialize the interface for firmware upgrade
 * @pre              None
 * @param[in]        None
 * @retval           None
 */
void lgk_boot_intf_init(void);

/**
 * @brief            Basic system initialization
 * @pre              None
 * @param[in]        None
 * @retval           None
 */
void lgk_boot_sys_init(void);

这些函数针对不同的平台需要做不同的实现。下面简单介绍一下这些函数的功能。

1、bool lgk_boot_hard_is_enter(void);

这个函数就是用硬件来判断是否要进入bootloader,例如我们可以使用一个普通的gpio,在上电初始化完以后判断这个gpio电平的高低来判断是否要进入bootloader。

2、void lgk_boot_deley_ms(uint32_t ms);

这个函数就是简单的延时。

3、void lgk_boot_jump_app(uint32_t app_add);

这个函数是用来跳转到app里面。

4、void lgk_boot_sys_reset(void);

系统的复位函数。

5、bool lgk_boot_app_is_vaild(uint32_t check_code_add);

检查app是否合法。

6、void lgk_boot_flash_erase(uint32_t start_add, uint32_t size);

flash擦除。

7、void lgk_boot_flash_write(uint32_t start_add, void *buffer, uint32_t size);

flash写入。

8、void lgk_boot_flash_read(uint32_t start_add, void *buffer, uint32_t size);

flash读取。

9、void lgk_boot_intf_init(void);

bootloader接口初始化,例如UF2我们需要初始化一个MSC设备,DFU需要初始化USB DFU设备。

10、void lgk_boot_sys_init(void);

系统初始化,时钟的一些配置等等。


知道这些函数的基本功能以后,我们就来看一下框架如何搭建。

首先我们需要看一个比较关键的变量:

/**
 * Find the pointer at the top of the stack
 */
extern uint32_t _eusrstack[];
#define lgk_boot_flag _eusrstack[0]

对于CH582我将lgk_boot_flag定义为指向栈顶地址的变量,因为我希望这个变量在软复位的时候不被初始化,(当然了你也可以在链接脚本里面重新写一个段用no_init属性来限制一下)。如果放到栈顶就需要改一下ram的区域,默认的链接脚本RAM长度为32K,而且根据CH582的链接脚本,会将栈顶地址设置为RAM区域的末尾地址,这样如果不把最后面的四个字节空出来,往栈顶地址存数据会出现访存出错的问题,所以我们需要将长度修改为32K-4,这样根据CH582的链接脚本,栈顶地址就是0x20007FFC。因为RISCV的栈是向下增长的,所以0x20007FFC-0x20007FFF这四个byte系统不会用到,可以用来存储lgk_boot_flag。

接下来我们看看主函数,用lgk_boot_flag来判断进入哪个部分,一共分为三个部分

一、直接进入APP

二、直接进入Bootloader(超时1分钟无操作会自动退出)

三、上电默认的状态(进入bootloader 且不会因为超时退出)

先看第一部分---直接进入APP:

如果条件成立,清掉lgk_boot_flag,然后调用跳转到app的函数。

第二部分---进入Bootloader(超时1分钟无操作会自动退出)

条件成立需要初始化一些接口,然后需要对USB枚举不成功做超时操作,接着红色框子里面的是对系统的几个状态的判断,在用UF2boot的时候会用到,具体实现请看仓库里面的代码。

第三部分---进入Bootloader(进入bootloader 且不会因为超时退出)

跟二差不多,只是少了超时退出的操作。

二、bootloadr 接口

一、uf2

uf2是微软的一个文件格式,我也没做多少了解,我们只需要知道,用uf2搭配USBMSC设备,可以做一个U盘,并且只需要根据官方提供的py脚本将bin或者hex固件转成uf2文件,就可以放进U盘完成固件升级。下面我们看看如何做支持uf2文件的u盘。

上图中的这些文件,红色箭头所指的都是从tinyuf2那边拿过来的,msc_flash.c这个文件是将USB设备做成MSC类设备的应用文件,会提供两个读写函数:

这个文件就是用CherryUSB里面的demo改了一点,上图中usbd_msc_sector_write这个函数是USB主机将数据下发至USB设备,然后我们将固件数据推送给uf2进行处理。usbd_msc_sector_read这个函数是将USB设备的数据返回给主机。

另外我们还需要注意的是这个BOARD_UF2_FAMILY_ID,在后面制作UF2文件的时候会要用到。制作UF2文件时传入的ID参数要与这个保持一致。

 port_uf2.c这个文件实现了uf2读写的一些依赖,如下图:

这几个函数通常必须要实现,board_flash_write会被uf2_write_block调用,所以要保证能正确的将指定长度的数据写入到指定的flash地址中。

接下来就是uf2接口的初始化。

完成这些,编译成功以后下载到芯片里面,插上电脑就会弹出一个U盘。但是这个时候U盘还不能用,因为我们还没有实现:

void lgk_boot_flash_write(uint32_t start_add, void *buffer, uint32_t size);

void lgk_boot_flash_read(uint32_t start_add, void *buffer, uint32_t size);

这些函数。

二、dfu

见CherryUSB里面的例子或者直接去PlumBL仓库里面看。

三、移植CH582

经过前面的介绍,框架我们已经搭建完成了,接下来针对不同的平台做移植,这里只介绍移植CH582,ST那些请看仓库里面的代码。

移植一个平台需要编写两个文件,并且实现以下函数:

这个jumpApp()其实就是一个函数指针,

#define jumpApp           ((void (*)(void))((uint32_t *)APP_START_ADDRESS))

要能正确完成跳转,需要在启动的时候开启机器模式。

如上图的修改,不知道现在wch的sdk有没有修改过来。

这个函数需要用户根据自己的应用自己实现一下,默认是返回true。

上图是操作flash的部分,直接调用sdk的api即可。

上图的接口初始化在port_uf2.c里面已经完成编写。系统初始化在ch582平台上就是初始化时钟,如果要使用串口来输出调试信息,还需要初始化串口。

这个硬件决定是否进入bootloadr,我们这里只介绍双击reset的方式,首先这个DBRST_TAP_REG其实和lgk_boot_flag是同一个变量,在第一次初始化运行到lgk_boot_deley_ms的时候,DBRST_TAP_REG==DBRST_TAP_MAGCI,如果在延时的期间再次发生复位,系统运行到这个函数的时候则会被检测到双击reset,函数会返回true,则会进入到bootloader。如果在延时的期间一直没有发生复位,则会清除DBRST_TAP_REG,返回false。

如果想软件(从app中软件操作)进入bootloader,则需要在app中运行类似下面的代码:

void boot_jump(void)
{
    uint32_t *boot_magic = (uint32_t *)0x20007ffc;
    *boot_magic = 0xc220b134;
    mcu_reset();
}

四、制作app文件

首先需要将你的app链接脚本中的flash起始地址改为0x10000,RAM长度修改为32K-4,像下面的图一样:

启动文件要记得开启机器模式,前文提到过。

接着就可以编译生成hex或者bin文件。然后确保电脑有安装python,下载了uf2conv.py和uf2families.json两个文件,接着你就可以运行下面的命令:

python uf2conv.py test.hex -o test.uf2 -c -f 0xabcdc582
python uf2conv.py test.bin -o test.uf2 -c -f 0xabcdc582 -b 0x10000

最后将生成的test.uf2文件直接拖入U盘完成下载。

仓库地址:GitHub - HaiMianBBao/PlumBL: A small BootLoader upgraded through USB firmwareA small BootLoader upgraded through USB firmware. Contribute to HaiMianBBao/PlumBL development by creating an account on GitHub.https://github.com/HaiMianBBao/PlumBL

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值