Python部分这里就不说了,这里主要是参考官方C-SDK的datasheet和例程结合说明。后文中的guider指raspberry-pi-pico-c-sdk.pdf
这个手册。
除了这个手册,API的解释已经Example的解释在树莓派的文档可以看到:
Raspberry Pi Pico SDK: API Documentation
可以说树莓派做的这个单片机手册是相当完善,对于硬件库代码如何构建也是很好的学习材料。下面的内容基本上是手册的摘要 - 会补充少量结合之前配置工程的吐槽和提示 - 所以暂时很多没用上的就写个大概指出手册位置就好了。
0. SDK的结构
实际上这部分在guider中说的很明白了,看CMakelist怎么写的实际上也能看出来,不过为了简洁,这里也基本说一下。
CMake的库包含有源代码文件,包含路径,编译器定义,编译链接,依赖。这其中包含的每一个库也包含有其他的接口。比如pico_stdlib包含了很多基本功能。
高级API(Higher-level Libraries)
也就是依赖底层库实现的API,比如时间功能定时器功能,数据结构,多核支持和同步原语。一般叫做 pico_xxx
,他们基本是基于 hardware_xxx
的底层硬件库来完成的
同时以后也会出基于PIO构建的Audio, DPI/VGA/MIPI Video ,file system support, SDIO support等高级库,目前这些库还没完善(功能或文档上),所以说不着急折腾。库位于GitHub - raspberrypi/pico-extras ,可以参考github上的readme来引用。
这些API如何使用可以参考:Raspberry Pi Pico SDK: High Level APIs
运行库 (Runtime Support)
包含 (pico_runtime, pico_standard_link),
参考:Raspberry Pi Pico SDK: Runtime Infrastructure
pico_runtime 提供类C环境来执行代码,也就是包括浮点数运行,printf,还有对/ %符号的使用硬件除法器的支持(意味着不用自己去戳硬件除法器),runtime_init()可以提供最小运行环境。
pico_standard_link 封装了标准链接器。
这两个库被直接包含到了 pico_stdlib 这个库里面,所以可以看到例程基本都是直接开头是
#include "pico/stdlib.h"
这样直接方便使用,更多的内容后面说。
硬件支持库(Hardware Support Libraries)
正如很多例程里面看到的,hardware_xxx 所提供的就是对硬件的抽象(目前看来还是比较简单的寄存器操作的封装,所以也比较轻量),封装好的函数操作外设或者硬件比较简单,比如说 PIO 的操作:
pio_sm_set_wrap(pio, sm, bottom, top);
替代了:
pio->sm[sm].execctrl = (pio->sm[sm].execctrl & ~(PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) | (bottom << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) | (top << PIO_SM0_EXECCTRL_WRAP_TOP_LSB);
其实替代的不是很复杂,不过硬件库本身是轻量封装,没必要追求去写寄存器了。
一般来说这些库基本是独立的,除了hardware_structs and hardware_regs这俩需要被依赖,所以如果需要裁剪SDK, 保留hardware库就很简单。
实际上,例如上面举例的函数,是用static inline进行内联的,这样编译后开销很小,基本上函数只是为了方便使用。
库的使用
声明调用
基本上,添加头文件可以使用例如对应hardware_xxx的就是
#include "hardware/dma.h"
然后再CMakeLists.txt里面的对应link部分添加对应的库名称,例如
target_link_libraries(hello_world
pico_stdlib
hardware_dma )
具体这些源文件是怎么组织的可以去看文档或者直接打开文件管理器看看。
一般的函数名称
这里基本上要用啥就看啥手册就行,基本上熟悉了库函数编程的直接猜就行了,然后再就是去看文档的API说明。
一般来说,操作函数的前缀包含set_ get_ 或者是判断布尔值的is_ .
而后缀包含
_blocking
_blocking_until
_timeout_ms
_timeout_us
很容易看出来就是操作函数的阻塞操作和阻塞超时。
返回值就不说了。
预定义可以写在什么位置
pico配置了bsp,位置在 board/pico.h
里面。
这里纯粹是基础学习:
头文件#define
或者再CMakeLists.txt里面
# SPECIFY two preprocessor definitions for the target hello_world target_compile_definitions(hello_world PRIVATE
PICO_DEFAULT_UART_TX_PIN=16
PICO_DEFAULT_UART_RX_PIN=17 )
Note that all preprocessor definitions used by the SDK have a PICO_ prefix
上面这句是为啥?不清楚。
SDK 运行库
这里就稍微详细说前面说的Runtime
封装了printf,定向stdin stdout到硬件界面上。具体可以查看文档:Raspberry Pi Pico SDK: pico_stdio
支持的方式包含:
- GPIO0 (TX) and GPIO1 (RX)上的UART,波特率默认115200。
- 基于TinyUSB实现的USB CDC
- 实验性的minimal semihosting support(半主机模式),也就是直接通过SWD可以实现通过RAM来在主机端的debug终端实现输出。用过的应该都明白,前面也说过Segger Embedded Studio默认是支持这种方式输出的。
其中,stdout实现的功能是完善的,such asprintf、puts、putchar
(文档好像写错了,这里写了个getchar?),但是stdin,也就是输入交互,只有UART是默认开启的,因为尤其是USB, 实现起来很费资源。
他们可以分别通过下面方式开启:
- stdio_usb_init (void) ;//在cmakelist里添加pico_enable_stdio_usb(target_name, 1)
pico_enable_stdio_uart(target_name 0) - stdio_uart_init (void);
或者
stdio_uart_init_full (uart_inst_t *uart, uint baud_rate, int tx_pin, int rx_pin);
//在cmakelist里添加
pico_enable_stdio_uart(target_name 1)
pico_enable_stdio_usb(target_name, 0)
后者可以自定义使用UART,前者可以通过宏定义来定义参数,具体看上面文档。 - void stdio_semihosting_init (void ); //在CMakeList里面添加pico_enable_stdio_semihosting(target_name ENABLED)
但是,不需要分这么多种,直接调用 stdio_init_all(); 这个函数就好了,前提是Cmakelist里面定义好使能的方式就行。
不过,半主机模式似乎还是要IDE较好的支持才行,实测VSCODE、CLION都输出不太支持直接显示调试,会一次输出一个字符并进入中断一次,反正确实能输出,就是不能很好的显示,例如他会写出变化:
1090-var-update --thread 1 --frame 0 --all-values var_c@entry_256
-> 1090^done,changelist=[{name="var_c@entry_256",value="0x100043f8 \"GoodJob!\"",in_scope="true",type_changed="false",has_more="0"}]
能看到输出的字符GoodJob!这些。但是主机没有主动输出成字符并执行下去。
浮点支持
看手册
硬件除法器
手册写了速度对比GCC,反正是Pico直接实现的,自己不是为了跑分用不上,除法器很快就对了。
多核支持
多核就和开一个线程一样。参考文档:Raspberry Pi Pico SDK: pico_multicore
一般来说core0是跑主函数的,core1是可以被启动的。
multicore_launch_core1(some_function_pointer);函数来启动多核任务。
pico_sync也是一个库用来实现线程同步。线程安全的问题基本要自己用库解决,看手册。
C++的使用
看手册,也有库可以给你用,cmakelist也要定义,使用生成器生成工程模板的时候可以勾选。
1. 重磅: PIO 的使用
前面都是些基础知识介绍,杂糅的比较多,这里直接开始了树莓派rp2040的PIO,一个很好的外设。
手册上那套介绍就不抄写在这里了,感兴趣的可以去看,我介绍一点别的。
PIO就是一套可编程IO,这个概念很类似于FPGA或者CPLD, 但是再rp2040上它的实现更简单更廉价(对用户写程序不咋简单。。),因为它只提供了状态机来操作IO,通过汇编来实现编程和C对接。
实际上PSoC这样的产品早就存在,例如赛普拉斯的PSoc,也提供了可编程IO,功能还更强大,或者是FPGA里面带个软核或者硬核,比如Zynq这样的产品,还有国产的2K CPLD里面带个MCU,或者MCU里面带个2k FPGA这样产品,其中前者价格十分感人,国产的这个产品倒是价格挺有吸引力的(一二十块钱),但是经典不买批量就没资料(无力吐槽)。
目前rp2040的裸片大概4.5元(2022)。
扯回来,PIO的作用基本上手册上都介绍了,rp2040里面有俩PIO外设,每个PIO都有四个状态机,可以全量操作IO,这两个PIO块可以同时执行程序来操纵GPIO并传输原始数据。这意味这什么,双核,两组脱离CPU运行的设备,极大地拓展了这个单片机执行实时任务的能力,虽然其他外设本身也可以自己运行(比如ADC+DMA),但是可编程的状态机显然更灵活,而且,直连IO,使得IO可以输出极高的速度,相比GPIO,可能受限于CPU的速度(之前的测试中RP2040_IO翻转速率测试,GPIO翻转,即使不使用任何运算,也只能达到30Mhz左右的速度,但是PIO轻轻松松上90Mhz)
很难说他以后会不会出更方便的方法来编程PIO,除了库之外不知道还有什么方法,反正他现在的编程实在是晦涩。不过Pico-Example里面已经给出了很多例程,可以复制粘贴。