pico-sdk(五)-程序架构之库结构(2)
硬件结构体库
hardware_structs
库提供了一组 C 结构体,这些结构体表示了系统地址空间中 RP 系列微控制器寄存器的内存映射布局1。能够用来替换较低层级的接口调用(这些内容原本需要用较低级别的 hardware_regs
中的宏定义来编写)。
*(volatile uint32_t *)(PIO0_BASE + PIO_SM1_SHIFTCTRL_OFFSET) |= PIO_SM1_SHIFTCTRL_AUTOPULL_BITS;
可以用如下的代码来替换(其中 pio0
是指向类型为 pio_hw_t
的指针,指针的地址为 PIO0_BASE
):
pio0->sm[1].shiftctrl |= PIO_SM1_SHIFTCTRL_AUTOPULL_BITS;
这些结构体以及指向内存映射寄存器块的相关指针,隐藏了处理单个内存位置、指针类型转换和易失性访问的细节,降低了程序的复杂性和易错性风险。另外一个好处是,在使用较旧的编译器时,这些结构体往往能生成更优的二进制码,旧版本的编译器鼓励重复使用带偏移量的指针进行读写操作,不主张在每次访问寄存器时都生成一个 32 位的常量。
这些结构体头文件的命名与 hardware_
库和 hardware_regs
库的头文件保持一致。例如,可以通过引入头文件 hardware/pio.h
来访问 hardware_pio
库的功能,如果想要直接访问寄存器,可以将 hardware_structs
库的 hardware/structs/pio.h
头文件包含进来,这个头文件中默认包含了 hardware/regs/pio.h
头文件,以获取寄存器字段的定义。PIO 的头文件内容有点长,可以参考 hardware/structs/pll.h
较短的示例来了解这些头文件实际包含的内容:
typedef struct {
_REG_(PLL_CS_OFFSET) // PLL_CS
// Control and Status
// 0x80000000 [31] LOCK (0) PLL is locked
// 0x40000000 [30] LOCK_N (0) PLL is not locked +
// 0x00000100 [8] BYPASS (0) Passes the reference clock to the output instead of the...
// 0x0000003f [5:0] REFDIV (0x01) Divides the PLL input reference clock
io_rw_32 cs;
_REG_(PLL_PWR_OFFSET) // PLL_PWR
// Controls the PLL power modes
// 0x00000020 [5] VCOPD (1) PLL VCO powerdown +
// 0x00000008 [3] POSTDIVPD (1) PLL post divider powerdown +
// 0x00000004 [2] DSMPD (1) PLL DSM powerdown +
// 0x00000001 [0] PD (1) PLL powerdown +
io_rw_32 pwr;
_REG_(PLL_FBDIV_INT_OFFSET) // PLL_FBDIV_INT
// Feedback divisor
// 0x00000fff [11:0] FBDIV_INT (0x000) see ctrl reg description for constraints
io_rw_32 fbdiv_int;
_REG_(PLL_PRIM_OFFSET) // PLL_PRIM
// Controls the PLL post dividers for the primary output
// 0x00070000 [18:16] POSTDIV1 (0x7) divide by 1-7
// 0x00007000 [14:12] POSTDIV2 (0x7) divide by 1-7
io_rw_32 prim;
_REG_(PLL_INTR_OFFSET) // PLL_INTR
// Raw Interrupts
// 0x00000001 [0] LOCK_N_STICKY (0)
io_rw_32 intr;
_REG_(PLL_INTE_OFFSET) // PLL_INTE
// Interrupt Enable
// 0x00000001 [0] LOCK_N_STICKY (0)
io_rw_32 inte;
_REG_(PLL_INTF_OFFSET) // PLL_INTF
// Interrupt Force
// 0x00000001 [0] LOCK_N_STICKY (0)
io_rw_32 intf;
_REG_(PLL_INTS_OFFSET) // PLL_INTS
// Interrupt status after masking & forcing
// 0x00000001 [0] LOCK_N_STICKY (0)
io_ro_32 ints;
} pll_hw_t;
该结构体包含了一个寄存器映射的内存块布局,其中一些成员变量的定义与全局地址映射的基地址相关联。
此外,可以使用任一原子(set
、clear
或 xor
) 地址别名来分别对寄存器中指定的位进行 set
、clear
或 xor
操作(而不是让CPU执行读/改/写操作);例如:
hw_set_alias(pio0)->sm[1].shiftctrl = PIO_SM1_SHIFTCTRL_AUTOPULL_BITS;
等同于:
hw_set_bits(&pio0->sm[1].shiftctrl, PIO_SM1_SHIFTCTRL_AUTOPULL_BITS);
硬件原子(置位 / 清零 / 异或)输入输出别名在 SDK 库中被广泛使用,以避免在两个内核或中断请求(IRQ)和前台代码同时访问寄存器时发生某些类别的数据竞争。
在 RP 系列微控制器上,原子寄存器别名(atomic register aliases)2是外设的一个固有部分,而不是 CPU 的一项功能,因此系统直接内存访问(DMA)也能够对寄存器执行原子置位 / 清零 / 异或操作。
硬件寄存器库
硬件寄存器(hardware_regs)库是一整套针对所有 RP 系列微控制器寄存器的头文件,是根据硬件本身自动生成的。可以通过这个库直接查看或修改一个内存映射寄存器。不过,高级别库中提供了更友好的 C/C++ 接口。
据个例子,以下列出了 hardware/regs/sio.h
代码片段:
// Description : Single-cycle IO block
// Provides core-local and inter-core hardware for the two
// processors, with single-cycle access.
// =============================================================================
#ifndef HARDWARE_REGS_SIO_DEFINED
#define HARDWARE_REGS_SIO_DEFINED
// =============================================================================
// Register : SIO_CPUID
// Description : Processor core identifier
// Value is 0 when read from processor core 0, and 1 when read
// from processor core 1.
#define SIO_CPUID_OFFSET 0x00000000
#define SIO_CPUID_BITS 0xffffffff
#define SIO_CPUID_RESET "-"
#define SIO_CPUID_MSB 31
#define SIO_CPUID_LSB 0
#define SIO_CPUID_ACCESS "RO"
#endif
这些头文件都有大量的注释(与数据手册中寄存器列表或 SVD 文件所提供的信息相同)。它们定义了每个寄存器的偏移量、这些寄存器中各个字段的布局,以及字段的访问类型,例如,RO
表示只读。
硬件寄存器(hardware_regs)库中的头文件仅包含注释和 #define 语句。这意味着它们既可以被汇编文件(.S 文件,这样就可以使用 C 预处理器)包含,也可以被 C 和 C++ 文件包含。
TinyUSB 端口
除了核心 SDK 库之外,pico-sdk 中还提供了一个TinyUSB 端口,用来为 RP 系列微控制器提供标准设备和主机 USB3 支持,并在 SDK 中实现了一些构建基础架构,以便于将其轻松整合到应用程序中。
在应用程序的 CMakeLists.txt
文件中添加 tinyusb_dev
或 tinyusb_host
库的依赖,用来使应用程序使用 USB 设备或主机功能。此外,tinyusb_board
库可用于提供 TinyUSB 应用中经常使用的额外 “板级支持” 代码。欲了解更多信息和用于设置全功能应用程序的示例代码,请参阅 Pico Examples 中的 README。
RP 系列微控制器的 USB 硬件支持主机和设备模式,但这两种模式不能同时使用。TinyUSB 还提供
tinyusb_pico_pio_usb
库,通过 PIO 实现对 USB 的支持 。
FreeRTOS 端口
RP2040 和 RP2350(两者都支持 Arm 和 RISC-V 架构)均支持 FreeRTOS 端口,既可以在单个内核上运行,也可以在双核对称多处理(SMP)模式下运行。
SDK 并不直接依赖于 FreeRTOS,但确实提供了一些与 FreeRTOS 一起使用的库(特别是网络相关的库)。pico-examples 仓库中包含了一些使用 FreeRTOS 的示例,在构建时应设置 FREERTOS_KERNEL_PATH
变量。
在 Pico W 上使用 Wi-Fi
Pico SDK 中的 IP 支持由 lwIP 提供。lwIP 的原始 API 始终得到支持:完整的 API,包括阻塞套接字,可以在 FreeRTOS 下使用。
IP 和 Wi-Fi 支持中使用了几个不同的库构建块:pico_lwip
用于 lwIP,pico_cyw43_driver
用于 Wi-Fi 芯片驱动,pico_async_context
用于以一致的方式访问非线程安全的 API(lwIP),无论是轮询、使用多个内核还是运行 FreeRTOS。
默认情况下,
libcyw43
许可用于非商业用途,但 Raspberry Pi Pico W、Pico WH 或任何围绕 RP2040 和 CYW43439 构建产品的用户都可以享受免费的商业使用许可。
高级用户可以单独组合这些库,但在大多数常见情况下,它们会被整合到几个方便的库中,您可以将这些库添加到应用程序的 CMakeLists.txt 文件的依赖项中:
- pico_cyw43_arch_lwip_poll - 用于单核,传统的轮询方式访问 Pico W 上的 lwIP。
- pico_cyw43_arch_threadsafe_background - 用于单核或多核访问 Pico W 上的 lwIP,lwIP 回调在低优先级中断中处理,因此不需要轮询。
- pico_cyw43_arch_lwip_sys_freertos - 用于在 FreeRTOS 下完全访问 lwIP API(
NO_SYS=0
)。
有关更详细的说明,请参阅 pico_cyw43_arch 头文件。许多使用 Wi-Fi 和 lwIP 与 Pico SDK 的示例可以在 pico-examples 仓库中找到。
在 Pico W 上使用 蓝牙
Pico SDK 中的蓝牙支持由 BTstack 提供。BTstack 的文档可以在 BlueKitchen 的网站上找到。
除了标准的 BTstack 许可条款外,还提供了一个补充许可,涵盖了与 Raspberry Pi Pico W 或 Raspberry Pi Pico WH 一起使用 BTstack 的商业用途。
请参见 pico-examples 仓库,其中包含了来自 BTstack 的蓝牙示例。
SDK 中的蓝牙支持由多个库组成:
pico_btstack_ble
库增加了对蓝牙低功耗(BLE)的支持,而pico_btstack_classic
库则增加了对传统蓝牙的支持。可以单独链接到任一库,或者同时链接到这两个库以启用 BTstack 提供的双模支持。pico_btstack_cyw43
库对于蓝牙使用是必需的。它增加了对 Pico W 上的蓝牙硬件的支持,并将 BTstack 运行循环的概念与 SDK 的pico_async_context
库集成,允许通过轮询或后台运行蓝牙,以及多核和/或 FreeRTOS 支持。
以下额外的库是可选的:
- pico_btstack_sbc_encoder - 增加了蓝牙子带编码(SBC)编码器支持。
- pico_btstack_sbc_decoder - 增加了蓝牙子带编码(SBC)解码器支持。
- pico_btstack_bnep_lwip - 使用 LwIP 增加了蓝牙网络封装协议(BNEP)支持。
- pico_btstack_bnep_lwip_sys_freertos - 在
NO_SYS=0
模式下使用 LwIP 与 FreeRTOS 一起增加蓝牙网络封装协议(BNEP)支持。
要使用 BTstack,必须在 CMakeLists.txt
中将 pico_btstack_cyw43
以及 pico_btstack_ble
和/或 pico_btstack_classic
添加到应用依赖项中。此外,需要在源树中提供一个 btstack_config.h
文件,并将其位置添加到包含路径中。更多详情,请参阅 BlueKitchen 关于如何配置 BTstack 的文档,以及 pico-examples 仓库中相关的蓝牙示例代码。
CMake 函数 pico_btstack_make_gatt_header
可用于运行 BTstack 的 compile_gatt
工具,从 BTstack GATT 文件生成 GATT 头文件。
在微控制器系统中,不同的外设(如 PIO - 可编程输入输出接口、PLL - 锁相环等)都有自己对应的内存映射区域。这些区域就像是一个个 “房间”,每个 “房间”(内存映射区域)都存放着与特定外设相关的寄存器,处理器可以像访问内存一样访问这些 “房间” 里的寄存器来配置和控制外设。 ↩︎
原子寄存器别名(Atomic Register Alias)是在计算机系统,特别是微控制器相关环境下的一个概念。它是对实际寄存器的一种替代名称(别名)机制,主要用于实现对寄存器的特定原子操作(如原子置位、原子清零、原子异或等操作),且这些操作能够以原子的方式执行,即操作过程不会被中断,保证了数据的完整性和一致性。 ↩︎
RP 系列微控制器支持USB设备模式和USB主机模式。USB 设备模式(Device mode),在这种模式下,微控制器作为 USB 设备连接到主机(如电脑)。就像是一个 “从属” 角色,它会响应主机的请求。例如,当把一个带有微控制器且处于设备模式的 USB 设备(如 USB 鼠标、USB 存储设备等)插入电脑时,电脑作为主机可以向这个 USB 设备发送指令,要求获取设备信息、读取存储的数据或者执行某些操作,而 USB 设备需要按照主机的要求进行响应和操作。USB 主机模式(Host mode)。处于主机模式的微控制器可以作为 USB 主机来控制其他 USB 设备。它就像一个 “管理者”。例如,当微控制器处于主机模式并连接一个 USB 键盘时,微控制器可以主动发起对键盘的操作,如查询键盘按键状态等,而键盘会响应微控制器的请求。 ↩︎