看中它带个 SPI 接口的 WiFi 模块,选了 RP2040 Pico W,可以支持需要高数据率的应用,比如音频流。价格小贵,50 块钱左右。
Pico W 基本上就是一个 RP2040 最小系统。留了 3 pin SWD 接口,需外接硬件调试器(debug adapter)。开放方案的 SWD 调试器,我所知易得的有两种,一是基于 CMSIS-DAP 的,估计价格 20 元左右;另一种就是 FTDI FT2232H/4232H 的开发板,贵一点,从 40 元到 100 元往上都有。
FT4232HL 共有 4 路通道,其中 2 路可用作 SWD/JTAG,全部 4 路都可作为虚拟串口。FT2232HL 主要的区别是通道数,一共有 2 路,都可以用作 SWD/JTAG 或者 USB 虚拟串口。
我用 FT4232HL 开发板。有时候也拿它做 JTAG 调试。
在跑起来第一个最简 bare metal 程序之前,准备工作颇费周折。
安装 pico-sdk
Pico-sdk 是用来做 C/C++ 开发的,但是需要用它来得到 bootloader stage 2。
我这里整理的安装步骤基于 RP2040 官方文档 “Getting started with Raspberry Pi Pico”,但针对 Windows 平台和个人偏好进行了调整。
安装 pico-sdk 之前,需要安装本地 C/C++ toolchain(用于在本地 build pioasm
和 elf2uf2
这两个工具)。在 Windows 平台上,除了 Visual Studio,也可以用 GCC,一般首先安装 MSYS2,再通过 pacman
安装 MinGW-w64。
Pico-sdk 另一重要的前置条件是 Python3。
安装了 Python3 和 GCC 后,还需要安装下列工具。这些工具都能下载 zip 包,解压即可使用:
- Arm GNU Toolchain
- CMake
- Git
- xPack Windows Build Tools(提供 make.exe 等)
除了 Python 外,其余工具都需要将可执行程序的路径(例如bin
目录)加入PATH
环境变量。
接下来在命令行下切换到选定的安装目录下,依次执行下列命令,下载 pico-sdk 和 pico-examples:
git clone -b master https://github.com/raspberrypi/pico-sdk.git
cd pico-sdk
git submodule update --init
cd ..
git clone -b master https://github.com/raspberrypi/pico-examples.git
最后一步,设置 PICO_SDK_PATH
环境变量,值为 pico-sdk 路径。
Build OpenOCD
树莓派自己搞了一个 OpenOCD 分支,以支持 RP2040,需要手工 build。
大体上按照 “Getting started” 文档 Appendix A 中 MSYS2 环境下的安装步骤,个别地方作了调整。
首先安装必要的软件包。如果前面已经安装了 MinGW-w64,这里就只需安装其他的包:
pacman -S mingw-w64-x86_64-toolchain git make libtool pkg-config autoconf automake texinfo
mingw-w64-x86_64-libusb
在 MSYS2 bash 命令行中,将 MinGW-w64 bin
目录添加到 PATH
环境变量,例如
PATH=/mingw64/bin:$PATH
切换到预定的安装目录下,依次执行下列命令。注意,可以执行./configure --help
查看所有的配置选项。
mkdir _build
cd _build
git clone https://github.com/raspberrypi/openocd.git --branch rp2040 --depth=1
cd openocd
./bootstrap
./configure --prefix=`realpath ../..` --enable-picoprobe --disable-werror --enable-ftdi --enable-cmsis-dap
make -j4
make install
Build 完成后将 OpenOCD 的bin
目录路径添加到 Windows PATH
环境变量中。
FTDI 配置
取决于 FTDI 开发板的芯片型号(FT2232H 或 FT4232H)以及使用的通道,需要进行各别配置。本节以 FT4232H 为例。
在 OpenOCD share\openocd\scripts\interface\ftdi
目录中新建一个配置文件,例如ft4232h-swd.cfg
。配置文件的内容为:
adapter driver ftdi
ftdi_vid_pid 0x0403 0x6011
ftdi_channel 1
ftdi_layout_init 0x0000 0x000b
ftdi_layout_signal nSRST -data 0x0040 -oe 0x0040
ftdi_layout_signal SWD_EN -data 0
ftdi_layout_signal SWDIO_OE -data 0
transport select swd
把 FT4232H 开发板接入电脑 USB 后,Windows 会识别并自动安装驱动程序,在设备管理器中看到出现 4 个虚拟串口设备,检查其中任一个的属性,查看“硬件 Id”属性即可获得 USB 设备的 vid
和 pid
值。例如,FTDIBUS\COMPORT&VID_0403&PID_6011
表明,vid
为0403
,pid
为6011
。 OpenOCD 配置文件中通过ftdi_vid_pid
命令指定 vid
和 pid
。
FT4232H 有 4 个通道,依次编号为0
-3
。在上面的配置文件中,ftdi_channel 1
命令指定使用第二个通道作为 SWD 调试通道。在 FTDI datasheet 中此通道被称为 “Channel B”。
为使 OpenOCD 能够进行 USB 通讯,必须将选定通道的设备驱动程序替换为 libusb。最简便的方法是使用 zadig 工具,对于通道 1(亦即 Channel B),选择设备名称 “USB <-> Serial Converter (Interface 1)”,将驱动程序替换为 “WinUSB (libusb)”。
ftdi_layout_xxx
命令的具体用法可查看 OpenOCD 使用手册。上面的配置文件将 D6 引脚用作 nRESET 信号。
FT4232H 每个通道有 8 个引脚,依次编号为 D0..7。对于 JTAG/SWD 协议,引脚 D0..4 的信号是固定的。OpenOCD 也支持 nRESET 信号,可配置为 D5..7 中的任一个。
上面的配置文件指定引脚与信号的对照关系如下表:
引脚 | JTAG 信号 | SWD 信号 |
---|---|---|
D0 | TCK | SWCLK |
D1 | TDI | SWDIO |
D2 | TDO | SWDIO |
D3 | TMS | n/a |
D6 | n/a | nRESET |
注意,作为 SWD 调试时,建议在引脚 D1(TDI)与目标 MCU(SWDIO)之间串联一个 220-470 Ohm 的电阻。参考 OpenOCD 自带的interface/ftdi/swd-resistor-hack.cfg
配置文件。
在我的开发板上,在引脚 D1(TDI)与 D2(TDO)之间已串联一个 820 ohm 的电阻,并可通过跳线开关控制其通断。因此,我只需将 D2(TDO)连接至 MCU SWDIO 即可。
Pico W 开发板 SWD 接口无 nRESET 信号,故此引脚不接。在 RP2040 目标配置文件 target/rp2040.cfg
中已配置为软件重设(soft reset)。
Pico W 与调试器的最终连接方式是:
- SWCLK -- D0(TCK)
- SWDIO -- D2(TDO)。注意 D1(TDI)已通过串联电阻连接 D2
- GND -- GND
供电、串口
调试器一般都可向目标板供电。将 5V 供电从 Pico W 的 VBUS 引脚(编号 40)输入。同时别忘了接地。
FT2232H/4232H 每个通道都可作为虚拟串口,引脚 D0, D1 分别为 TX, RX 信号。Pico W 引脚 1, 2 默认用作 UART0 的 TX, RX 信号。将调试器选定的串口引脚与 Pico W 的 UART0 交叉连接(即调试器 TX 连 Pico W RX,调试器 RX 接 Pico W TX)。
调试器与 Pico W 连接好后,将调试器连接到电脑的 USB 接口。执行以下命令,检查硬件连接和配置是否正确:
openocd -f interface/ftdi/ft4232h-swd.cfg -f target/rp2040.cfg -c "adapter speed 4000"
如果一切顺利,OpenOCD 将会识别出 RP2040 的 2 个 CPU 内核,并打印如下内容:
Open On-Chip Debugger 0.11.0-g228ede4 (2022-11-26-20:30)
Licensed under GNU GPL v2
For bug reports, read
http://openocd.org/doc/doxygen/bugs.html
Info : FTDI SWD mode enabled
swd
Info : Hardware thread awareness created
Info : Hardware thread awareness created
Info : RP2040 Flash Bank Command
adapter speed: 4000 kHz
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : clock speed 4000 kHz
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x00000001
Info : SWD DPIDR 0x0bc12477
Info : SWD DLPIDR 0x10000001
Info : rp2040.core0: hardware has 4 breakpoints, 2 watchpoints
Info : rp2040.core1: hardware has 4 breakpoints, 2 watchpoints
Info : starting gdb server for rp2040.core0 on 3333
Info : Listening on port 3333 for gdb connections
Hello World
编译 Hello World 示例程序。
修改pico-examples\hello_world\serial\CMakeLists.txt
文件,设置串口引脚:
target_compile_definitions(hello_serial PRIVATE
PICO_DEFAULT_UART_TX_PIN=0
PICO_DEFAULT_UART_RX_PIN=1
)
执行以下命令:
cd pico-examples
mkdir build
cd build
cmake -G "Unix Makefiles" ..
cd hello_world
make -j4
生成以下文件:
build\hello_world\serial\hello_serial.elf
- 程序文件build\pico-sdk\src\rp2_common\boot_stage2\bs2_default_padded_checksummed.S
- bootloader stage2 汇编源码,将会编译并链接到最终的程序文件(*.elf
)内
RP2040 bootloader stage2 必须保存在片外 Flash 的前 256 字节。生成的 bootloader stage2 汇编源码文件内容如下。将此文件留存以备将来使用。
.cpu cortex-m0plus
.thumb
.section .boot2, "ax"
.byte 0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
.byte 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
.byte 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
.byte 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
.byte 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
.byte 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
.byte 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
.byte 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
.byte 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
.byte 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
.byte 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
.byte 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
.byte 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
.byte 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
.byte 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a
使用 OpenOCD 将程序下载到 Pico W:
openocd -f interface/ftdi/ft4232h-swd.cfg -f target/rp2040.cfg -c "adapter speed 4000" -c "program serial/hello_serial.elf verify reset exit"
如果下载程序失败,可尝试降低接口速度(例如:-c "adapter speed 1000"
)。
以 115,200 波特率打开串口,将看到以 1s 为间隔打印 “Hello, world!” 文字。
Rust 程序
参考 The Embedonomicon 写一个最简程序。
https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
把 bootloader stage2 以数组形式直接写在程序里:
#[link_section = ".boot2"]
#[no_mangle]
pub static BOOT2 : [u8; 256] = [
0x00, 0xb5, 0x32, 0x4b, 0x21, 0x20, 0x58, 0x60, 0x98, 0x68, 0x02, 0x21, 0x88, 0x43, 0x98, 0x60
, 0xd8, 0x60, 0x18, 0x61, 0x58, 0x61, 0x2e, 0x4b, 0x00, 0x21, 0x99, 0x60, 0x02, 0x21, 0x59, 0x61
, 0x01, 0x21, 0xf0, 0x22, 0x99, 0x50, 0x2b, 0x49, 0x19, 0x60, 0x01, 0x21, 0x99, 0x60, 0x35, 0x20
, 0x00, 0xf0, 0x44, 0xf8, 0x02, 0x22, 0x90, 0x42, 0x14, 0xd0, 0x06, 0x21, 0x19, 0x66, 0x00, 0xf0
, 0x34, 0xf8, 0x19, 0x6e, 0x01, 0x21, 0x19, 0x66, 0x00, 0x20, 0x18, 0x66, 0x1a, 0x66, 0x00, 0xf0
, 0x2c, 0xf8, 0x19, 0x6e, 0x19, 0x6e, 0x19, 0x6e, 0x05, 0x20, 0x00, 0xf0, 0x2f, 0xf8, 0x01, 0x21
, 0x08, 0x42, 0xf9, 0xd1, 0x00, 0x21, 0x99, 0x60, 0x1b, 0x49, 0x19, 0x60, 0x00, 0x21, 0x59, 0x60
, 0x1a, 0x49, 0x1b, 0x48, 0x01, 0x60, 0x01, 0x21, 0x99, 0x60, 0xeb, 0x21, 0x19, 0x66, 0xa0, 0x21
, 0x19, 0x66, 0x00, 0xf0, 0x12, 0xf8, 0x00, 0x21, 0x99, 0x60, 0x16, 0x49, 0x14, 0x48, 0x01, 0x60
, 0x01, 0x21, 0x99, 0x60, 0x01, 0xbc, 0x00, 0x28, 0x00, 0xd0, 0x00, 0x47, 0x12, 0x48, 0x13, 0x49
, 0x08, 0x60, 0x03, 0xc8, 0x80, 0xf3, 0x08, 0x88, 0x08, 0x47, 0x03, 0xb5, 0x99, 0x6a, 0x04, 0x20
, 0x01, 0x42, 0xfb, 0xd0, 0x01, 0x20, 0x01, 0x42, 0xf8, 0xd1, 0x03, 0xbd, 0x02, 0xb5, 0x18, 0x66
, 0x18, 0x66, 0xff, 0xf7, 0xf2, 0xff, 0x18, 0x6e, 0x18, 0x6e, 0x02, 0xbd, 0x00, 0x00, 0x02, 0x40
, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x07, 0x00, 0x00, 0x03, 0x5f, 0x00, 0x21, 0x22, 0x00, 0x00
, 0xf4, 0x00, 0x00, 0x18, 0x22, 0x20, 0x00, 0xa0, 0x00, 0x01, 0x00, 0x10, 0x08, 0xed, 0x00, 0xe0
, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb2, 0x4e, 0x7a
] ;
在 link.x
中添加 .boot2
section:
/* Memory layout of the LM3S6965 microcontroller */
/* 1K = 1 KiBi = 1024 bytes */
MEMORY
{
FLASH : ORIGIN = 0x10000000, LENGTH = 2M
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
/* The entry point is the reset handler */
ENTRY(Reset);
EXTERN(RESET_VECTOR);
EXTERN(BOOT2);
SECTIONS
{
.boot2 ORIGIN(FLASH) :
{
KEEP(*(.boot2));
} > FLASH
.vector_table :
{
/* First entry: initial Stack Pointer value */
LONG(ORIGIN(RAM) + LENGTH(RAM));
/* Second entry: reset vector */
KEEP(*(.vector_table.reset_vector));
} > FLASH
.text :
{
*(.text .text.*);
} > FLASH
/DISCARD/ :
{
*(.ARM.exidx .ARM.exidx.*);
}
}
创建调试配置(可参考在下另一篇小文)。
由于 RP2040 需要专门的 OpenOCD,所以要修改 PATH
环境变量,修改 OpenOCD bin
路径,例如:
::set PATH=D:\devel\arm\tools\xpack-openocd-0.11.0-5\bin;%PATH%
set PATH=D:\devel\mcu\rpi_pico\msys64\mingw64\bin;%PATH%
set PATH=D:\devel\mcu\rpi_pico\openocd\bin;%PATH%
注意,OpenOCD 可执行程序依赖 libusb 动态库,位于 Mingw-w64 bin
目录中,因此,也须将其加入 PATH
环境变量。
为简便起见,新建一个 OpenOCD 配置文件 board/rp2040_ft4232h.cfg
,其内容如下。之后调用 OpenOCD 按-f board/rp2040_ft4232h.cfg
指定参数即可。
source [find interface/ftdi/ft4232h-swd.cfg]
source [find target/rp2040.cfg]
adapter speed 4000
在 Reset handler 上设置一个断点,启动调试: