pico-sdk(二)-入门示例程序解析
本文以树莓派 Pico 官方示例 blink_simple
为例,剖析基于 pico-sdk
开发的应用程序代码框架。
程序介绍
blink_simple
程序控制 Pico 设备上的 led 灯光以每秒2次的速率闪烁。
在 pico-examples
案例集合中,还有一个名为 blink
的示例与该示例的功能一样,不同之处在于 blink
程序对 Pico W
设备做了支持。
blink_simple
主程序 C 源码
以下展示了 blink_simple
的主程序 C 源代码
/**
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "pico/stdlib.h"
#ifndef LED_DELAY_MS
#define LED_DELAY_MS 250
#endif
// Initialize the GPIO for the LED
void pico_led_init(void) {
#ifdef PICO_DEFAULT_LED_PIN
// A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN
// so we can use normal GPIO functionality to turn the led on and off
gpio_init(PICO_DEFAULT_LED_PIN);
gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
#endif
}
// Turn the LED on or off
void pico_set_led(bool led_on) {
#if defined(PICO_DEFAULT_LED_PIN)
// Just set the GPIO on or off
gpio_put(PICO_DEFAULT_LED_PIN, led_on);
#endif
}
int main() {
pico_led_init();
while (true) {
pico_set_led(true);
sleep_ms(LED_DELAY_MS);
pico_set_led(false);
sleep_ms(LED_DELAY_MS);
}
}
源码包含三个函数,main ()
函数为程序的入口。与普通的 C 语言程序1相比,main()
函数不接收任何输入参数,同时也可以不用返回任何参数。
main ()
函数的返回参数在运行时会被忽略,对于 Pico 这种设备,当程序退出时 CPU 就被挂起,它与普通的个人 PC 不同,没有多进程管理一说。
在 C 文件的顶部,包含了一个名为 pico/stdlib.h
的头文件。这是一个综合头文件,它包含了另外一些常用的头文件。在该示例程序中,主要用到了以下两个头文件的接口函数:
hardware/gpio.h
,它为 RP 系列微控制器上的通用输入输出(这里是 gpio_xxx 函数)提供访问接口。pico/time.h
,它包含了sleep_ms
函数。
一般来说,以 pico
开头的库提供了 SDK 的高层接口,或者聚合了某些较小的接口;以 hardware
开头的库提供访问 RP 系列微控制器片上硬件的较低层级的接口。在该示例中主要使用 hardware_gpio
和 pico_time
两个库,程序中通过控制 LED_GPIO
引脚来控制 Pico 上的 LED 每秒闪烁两次。
文件组织
在包含 C 文件的目录中,还有一个名为 CMakeList.txt
的文件。该文件是 CMake2 构建系统的入口文件,它告诉编译器如何将 C 文件转换为能在 RP 系列微控制器板上运行的二进制应用程序。
如果你需要一些不常见的功能,这些功能不包含在 pico_stdlib 中,比如访问 DMA 硬件,你应该在这里添加这些依赖项(例如,在 pico_stdlib 之前或之后列出 hardware_dma)。
blink_simple
├── blink_simple.c
└── CMakeLists.txt
0 directories, 2 files
CMake 源文件
以下展示了 blink_simple
的主程序 CMake 构建源码
add_executable(blink_simple
blink_simple.c
)
# pull in common dependencies
target_link_libraries(blink_simple pico_stdlib)
# create map/bin/hex/uf2 file etc.
pico_add_extra_outputs(blink_simple)
# call pico_set_program_url to set path to example on github, so users can find the source for an example via picotool
example_auto_set_url(blink_simple)
CMakeLists.txt
文件中使用 add_excutable
命令指定编译器生成一个名为 blink_simple
的可执行应用程序。CMake 中使用 target
的概念来管理输出对象和引用对象(依赖的第三方库),target
的类型有很多,主要有 库(包含动态库和静态库)
和 可执行文件
,一个 CMake 组织的项目中可以包含多个 target
,pico-examples
中就包含了所有示例程序,每一个示例程序编译构建后都会生成 可执行文件
。当然 CMake 项目中也可以只有一个 target
。
CMake 发展概述
target_link_libraries
命令设置 target
依赖于指定的 SDK 库,在构建 target
时,会将设置的 SDK 库的头文件搜索路径和依赖库文件添加到编译连接工具的指令参数中。blink_simple
依赖了 pio_stdlib
这个 SDK 库。如果在构建程序时需要使用一些其他的接口,比如想要访问 DMA 硬件,则需要将对应的 SDK 库添加到依赖中,比如应该将 hardware_dma
添加到 pico_stdlib
之前(DMA 硬件访问接口由 hardware_dma
库提供)。
如果 CMakeLists.txt
文件内容到此位置,那么构建系统在构建完程序后,会生成一个 ELF 文件,该文件包含了所有要执行的代码及其依赖的 SDK 库。可以将 ELF 文件通过串行调试端口加载到 RP 系列控制器的 RAM 或者外部闪存中,使用 gdb
3 和 openocd
4 或者直接使用 picotool
来运行调试 ELF 程序。另一种更简单的运行方式是,使用 BOOTSEL
模式,通过 USB 直接对 Pico 系列设备或其他 RP 系列微控制器进行运行。使用这种方式需要一种名为 UF2
格式的文件,这种格式的文件与 ELF
文件的功能类似,但是在 USB mass storage 模式下要更加方便。pico_add_extra_outputs
命令用来创建指定名称的 UF2
文件。也可以使用该命令来创建反汇编和映射文件。
使用
picotool
也可以将 ELF 文件转换为 UF2 格式。
最后的 example_auto_set_url
命令将示例代码的 github 链接地址写入生成的二进制文件中,可以通过 picotool info blink_simple.elf
查看。更多相关介绍可以参考树莓派(Raspberry Pi)picotool。
最后,简要介绍一下 pico_stdlib
库。除了常见的硬件和高级库,如 hardware_gpio
和 pico_time
,它还引入了其他系统组件,如 pico_runtime
,这是设置硬件和运行时环境所必需的,只需要在 CMakeLists.txt
文件中配置依赖 pico_stdlib
库就可以将间接依赖的库自动引入到构建系统中,但是在有的时候,我们不会用到所有的这些库,那么我们只需要将 pico_stdlib
替换成我们需要依赖的其他更小的库即可。
链接
在 C 标准中,main函数是程序的入口点。main函数有两种合法的声明形式:第一种形式是int main(void),第二种形式是int main(int argc, char *argv[])。GCC 在默认情况下不会对函数返回值进行严格检查。这意味着如果一个函数有返回值,但你在调用该函数后没有使用其返回值,GCC 通常不会报错。 ↩︎
CMake 是一个跨平台的开源构建系统,用于管理软件项目的构建过程。它不直接构建项目,而是生成特定平台下的构建文件(如 Unix/Linux 下的 Makefile,Windows 下的 Visual Studio 项目文件等),然后再由相应的构建工具来实际构建项目。详细介绍可参考《CMake 发展概述》。 ↩︎
GDB 是 GNU 调试器(GNU Debugger)的缩写。主要用于调试C、C++、Objective - C等程序。 ↩︎
OpenOCD(Open On - Chip Debugger)是一个开源的片上调试器。它能够对嵌入式处理器进行调试,包括 ARM、Avr32、MIPS 等多种架构。无论是开发微控制器应用还是嵌入式 Linux 系统相关的底层调试,OpenOCD 都能发挥作用。 ↩︎