pico-sdk(五)-程序架构之库结构(2)

pico-sdk(五)-程序架构之库结构(2)

  • 硬件结构体库
  • 硬件寄存器库
  • TinyUSB 端口
  • FreeRTOS 端口
  • 在 Pico W 上使用 Wi-Fi
  • 在 Pico W 上使用 蓝牙

硬件结构体库

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;

该结构体包含了一个寄存器映射的内存块布局,其中一些成员变量的定义与全局地址映射的基地址相关联。

此外,可以使用任一原子(setclearxor) 地址别名来分别对寄存器中指定的位进行 setclearxor 操作(而不是让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_devtinyusb_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 头文件。


  1. 在微控制器系统中,不同的外设(如 PIO - 可编程输入输出接口、PLL - 锁相环等)都有自己对应的内存映射区域。这些区域就像是一个个 “房间”,每个 “房间”(内存映射区域)都存放着与特定外设相关的寄存器,处理器可以像访问内存一样访问这些 “房间” 里的寄存器来配置和控制外设。 ↩︎

  2. 原子寄存器别名(Atomic Register Alias)是在计算机系统,特别是微控制器相关环境下的一个概念。它是对实际寄存器的一种替代名称(别名)机制,主要用于实现对寄存器的特定原子操作(如原子置位、原子清零、原子异或等操作),且这些操作能够以原子的方式执行,即操作过程不会被中断,保证了数据的完整性和一致性。 ↩︎

  3. RP 系列微控制器支持USB设备模式和USB主机模式。USB 设备模式(Device mode),在这种模式下,微控制器作为 USB 设备连接到主机(如电脑)。就像是一个 “从属” 角色,它会响应主机的请求。例如,当把一个带有微控制器且处于设备模式的 USB 设备(如 USB 鼠标、USB 存储设备等)插入电脑时,电脑作为主机可以向这个 USB 设备发送指令,要求获取设备信息、读取存储的数据或者执行某些操作,而 USB 设备需要按照主机的要求进行响应和操作。USB 主机模式(Host mode)。处于主机模式的微控制器可以作为 USB 主机来控制其他 USB 设备。它就像一个 “管理者”。例如,当微控制器处于主机模式并连接一个 USB 键盘时,微控制器可以主动发起对键盘的操作,如查询键盘按键状态等,而键盘会响应微控制器的请求。 ↩︎

你可能感兴趣的:(linux,树莓派,linux,单片机,c++)