0%

【RP2040】跳转Bootloader实现IAP

对于单片机来说,跳转BOOTLOADER一般可以通过自己实现一个BOOTLOADER来实现,同样,RP2040也可以这样来做,不过,如果没有特别的要求,也可以使用更简单的方法。

1、利用bootrom跳转USB boot

RP2040的片上ROM固化了一个Bootloader,这个bootloader可以通过实现一个USB大容量设备来在电脑上加载UF2格式的固件,这个Bootloader是默认上电的时候检查Flash不可用或者Flash中没有可启动的程序的时候会自动跳转。

如果希望在程序中跳转进入Bootloader,可以借用这个自带的Bootloader,对于一些简单应用可以省去很多事情,或者用于用于免下电免调试器下载固件。

为了不用操作RUN这个引脚或者下电之后按住BOOT引脚(FLASH的CS)重新进Bootloader,而是直接在程序内根据触发条件运行,通过代码来实现内部跳转显然是有必要的,实际上官方手册已经给出了详细方法,能够让RP2040可以直接跳转到Bootrom直接进入USB大容量模式来下载UF2固件。

WCH或者STC的单片机,他们默认支持串口下载或者USB下载,跳转到USB或者串口下载模式只需要跳转到ROM指定地址,只需要在代码中添加跳转地址并调用即可。同样,RP2040也是类似。

RP2040的启动顺序见Datasheet. 片上16k rom位于0x00000000地址,不过库函数提供了方法,不需要自己找对应的执行位置。

1.0 实现方法

这个方法在官方例程中没有单独列出,实际上这个功能已经在bootrom.h内实现,而是在数据手册中解释了,可能是实现这个太简单了因此没有单独拿出来说。

只需要在代码中加入下面这部分内容即可,然后每次调用enter_usb_dfu_bootloader()即可进入RP2040自带的Bootloader.

#include "pico/stdlib.h" 
#include "pico/bootrom.h"
void enter_usb_dfu_bootloader()
{
reset_usb_boot(0, 0);
}

reset_usb_boot这一句实际上是实现了重启到指定位置。 这里函数可以传入两个参数。 第一个是IO设置,0是表示禁用,其他数字表示设置单个bit的IO,在USB大容量模式下主机活动的时候,IO会被拉高,说白了进入Bootloader之后可以选用一个下载指示灯,这个可以指定GPIO。 第二个参数是要使用的接口:

  • 选0:同时开启了USB大容量模式和USB PICOBOOT接口(与正常的冷启动相同)
  • 参数为1:只使用USB PICOBOOT接口
  • 参数为2:只使用USB大容量模式。 例如,如果只需要启用USB 大容量储存模式,换成下面这个参数即可:
reset_usb_boot(0, 2);//禁用PICOBOOT模式 

实际上这个函数就是类似于下面这种方法,跳转地址执行,但是SDK中已经配置好了准确的地址,并能根据参数选择不同配置: (未验证):

void (*bootrom)(void) = (void (*)(void))(0x00000000 + 4); 
bootrom();//

这段代码定义了一个指针bootrom,指向0x00000004的地址,即bootrom的入口地址。然后调用该指针即可跳转到bootrom。需要注意的是,为了确保正确的跳转,必须将指针类型强制转换为void (*)(void),但是跳转到BOOTROM并不会直接进入USBBOOT.

tips:如果启用PICOBOOT,Windows下会显示RP2 BOOT的USB设备,显示没有驱动,因为没有WinUSB的MSOS支持,不过依然可以手动加载WINUSB驱动或者使用LIBUSB.
这种方法也就是和RP2040的Arduino框架中使用的相似的方法(arudino大概是基于PICOBOOT传输固件)。

1.1 实际使用

有了这个方法,对于有按键的开发板,可以设置一键进入Bootrom。 例如我买的这个板子上24号引脚用作了用户按键,因此加入引脚配置为输入并上拉:

#define BUTTON_GPIO 24
static void usr_btn_init()
{
//这个板子上的按键有个问题,因为设计了阻容,导致上电需要一段时间才能充满,如果用电平判断的话,这个处理器太快了电容还没充够就开始判断了。。。因此如果不是检测边沿,初始化必须延时,
gpio_init(BUTTON_GPIO);
//gpio_set_dir(BUTTON_GPIO, GPIO_IN);
gpio_pull_up(BUTTON_GPIO);
}
//检查按键下降沿
static void check_btn_task(void)
{
static uint16_t lastLevel=0;//初始认为是低电平,按键上拉建立之后才是高电平
//检测到按键按下就跳转
if(gpio_get(BUTTON_GPIO)==0)
{
if(lastLevel==1)
{
enter_usb_dfu_bootloader();//检测电平下降沿
}
else{
lastLevel=0;
}
}
else
{
lastLevel = 1;//更新电平状态
}
}
//主程序循环中添加
while(1)
{
···
check_btn_task();
···
}

或者使用中断来完成这个功能,可能更方便:

void usr_btn_callback(uint gpio, uint32_t events) {
// Put the GPIO event(s) that just happened into event_str
// "LEVEL_LOW", // 0x1
// "LEVEL_HIGH", // 0x2
// "EDGE_FALL", // 0x4
// "EDGE_RISE" // 0x8
// sleep_ms(20); //不要尝试在回调函数里面使用这个函数,会造成错误退出
if(gpio_get(BUTTON_GPIO)==0)
enter_usb_dfu_bootloader();
}

... 主函数中添加:

usr_btn_init(); //用户按键初始化
gpio_set_irq_enabled_with_callback(BUTTON_GPIO, GPIO_IRQ_EDGE_FALL, true, &usr_btn_callback);//配置IO中断

1.2 参考来源

  1. RP2040 Datasheet: 2.8章节说明了bootrom,其中包括有关USB PICOBOOT模式的详细说明和电气特性。(简单来说这是一个基本接口,提供了一些读写擦除RAM、FLASH、重启等简单的调试功能,除了手册上详细的描述,还可以看picoboot.h
  2. Raspberry Pi Pico C/C++ SDK: 这里面也对USB PICOBOOT的部分场合做了解释,不过不多,直接搜索即可。

2、实现自己的Bootloader

见 文章 RP2040的自定义Bootloader