SoC App 开发指南¶
本文介绍 PAN1080 SDK APP 开发的基础知识,如何新建一个简单的 App 工程,并举例说明如何在 App 工程中操作 PAN1080 EVB 开发板的外设模块。
1 概述¶
PAN1080 SDK 基于 Zephyr OS 开发,其构建系统(Build System)使用 CMake。
01_SDK/zephyr
目录下存放有 Zephyr OS 的核心代码、内核配置信息、SoC 定义、开发板定义以及 Panchip 提供的例程等。
Zephyr 目录结构如下所示:
Zephyr 的核心代码位于
01_SDK/zephyr
目录下,其中包含一些重要的文件:CMakeLists.txt
:CMake 构建系统的顶层文件,包含构建 Zephyr 核心代码所需的 CMake 信息。Kconfig
:Kconfig 顶层文件,其直接引用另一个顶层文件Kconfig.zephyr
。west.yml
:west manifest 文件,其中列出了 west 工具能够识别和管理的外部模块目录。
Zephyr 的核心代码还包括如下这些顶层目录(每个顶层目录下还包括一个或多个子目录):
arch
:存放芯片 CPU 架构级信息,由于 PAN1080 是 ARM 架构,因此其中只保留了 ARM 架构信息。soc
:存放芯片 SoC 级信息及一些默认配置。boards
:存放板级信息,比如 PAN1080 各种 EVB 开发板。doc
:Zephyr 官方文档,其内容与 Zephyr文档官网一致。drivers
:存放设备驱动代码。dts
:存放 DeviceTree(芯片硬件初始化配置) 信息。include
:存放 Zephyr 公开的 API 头文件。kernel
:存放 Zephyr OS 内核代码。lib
:存放库文件,包含一个简单的标准C语言库等。misc
:其他一些不便分类的文件。samples_panchip
:存放 Panchip 提供的 PAN1080 官方例程。scripts
:存放编译测试相关的脚本文件。cmake
:存放 CMake 构建 Zephyr App 所需的相关配置和脚本文件。subsys
:Zephyr 子系统,包括:USB Device Stack(USB 设备栈)、File System(文件系统)、Bluetooth Host and Controller(低功耗蓝牙Host端与Controller端)等实现代码。share
:存放一些额外的架构无关的数据,目前包含 Zephyr CMake Package。
一个最简单的 APP 工程文件结构如下所示:
<work_dir>/my_app
├── CMakeLists.txt
├── prj.conf
└── src
└── main.c
其中:
CMakeLists.txt: 构建系统从此文件中查找待编译 APP 源码,并将 APP 目录与 Zephyr 链接起来,使得 APP 可以编译、配置、使用 Zephyr 提供的各种功能。
配置文件: APP 需要提供一个 Kconfig 配置文件(通常称为
prj.conf
),其中包含一个或多个 Zephyr 内核配置信息,这些配置与特定的芯片(SoC)配置、特定的开发板(Board)配置等配置信息合并,生成最终的配置文件(.config
文件与autoconf.h
文件)。APP 源码文件: APP 需要包含一个或多个C语言或汇编源码文件,这些文件通常位于名为
src
的目录下。<work_dir>:App 工程所在的目录,一般放在
samples_panchip
目录内。注:从文件结构上来说,构建系统允许我们将自己的 APP 工程创建在任意目录下;只要配置正确,命令行方式的构建系统可以在编译 APP 工程的时候,自动识别到 SDK 中 Zephyr 的路径;但目前 ZAL 工具只支持从
samples_panchip
和tests_panchip
两个目录中搜索 App 工程。
APP 工程创建后,我们即可以使用 ZAL
工具(或直接使用west build
CLI 命令)触发编译操作(其内部是调用 CMake),编译过程中会自动生成单独的 Build(构建)目录,其中存放编译输出的所有文件。
下面介绍如何创建、构建和运行一个自定义的 APP。
3 参考相关例程¶
SDK 中提供了一些例程(位于01_SDK/zephyr/samples_panchip
目录),可以直接编译下载到 EVB 开发板上执行,包括多线程打印消息、LED 闪灯、以及蓝牙相关例程等等。
在进行开发之前,建议先看一下相关的例程和文档,熟悉 Zephyr OS 的基本框架;然后实际将这些例程编译烧录至 EVB 开发板中查看运行效果,同时进一步熟悉开发环境的使用。
另外,SDK 中也提供了一些 Zephyr 的测试用例(位于01_SDK/zephyr/tests_panchip
目录),用于测试外设 Driver、OS Kernel 等功能是否正常。阅读这些测试用例代码,也有助于熟悉 Zephyr OS 提供的各种 API 的使用方法。
4 新建一个 App 工程¶
创建一个 APP 最简单的方式,是从 01_SDK/zephyr/samples_panchip
目录下,Copy 一个例程,并将其中的非必要文件(如README.rst
和sample.yaml
)删除即可。
假设我们希望创建一个名为my_led_blink
的 App 工程,使用 PWM 的方式将 EVB 开发板上的 LED 灯点亮并令其闪烁,要求:
首先,LED 灯在每 100ms 的时间内,亮 20ms、灭 80ms,持续 3s
然后,LED 灯在每 100ms 的时间内,亮 80ms、灭 20ms,持续 3s
重复上述闪烁规则
每次闪烁频率切换的时候,均向 UART 串口打印闪烁频率信息
下面我们详细讲解如何新建一个工程来实现上述需求。
4.1 从例程中拷贝一份相似的工程¶
例程blinky
(位置:01_SDK/zephyr/samples_panchip/basic/blinky
目录)演示了如何使用 GPIO 的方式点亮 LED 并令其每隔 1s 闪烁一次。
我们以此例程为基础进行修改:
拷贝一份
blinky
并将其重命名为my_led_blink
:打开
CMakeList.txt
,将第 5 行project参数修改为新的项目名称my_led_blink
:# SPDX-License-Identifier: Apache-2.0 cmake_minimum_required(VERSION 3.20.0) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) project(my_led_blink) target_sources(app PRIVATE src/main.c)
注:本 App 工程目录中,必要的文件为:
src\main.c
: 主程序代码CMakeList.txt
: cmake 文件prj.conf
: 项目配置文件
其它两个文件不是必须的(在本示例中我们可以直接将其删除):
README.rst
: 例程说明文件sample.yaml
: 用于 Zephyr 单元测试的信息描述文件
确认 EVB 硬件接线:
使用 USB 线,将 PC USB 与 EVB Type-C USB(USB->UART)相连,用于供电及串口 Log 打印
将 JLink SWD 正确连接至 EVB 板(
P46
:ICEK
,P47
:ICED
)使用跳线帽将 P16 引脚与 RGB 灯的蓝色通道(RGB-B)引脚相连
打开 ZAL 工具,在 Project List 中找到新建的工程:
如果在新增 App 工程目录之前,已经打开了 ZAL 工具,则在选择新的 App 工程之前,应先点击一下 ZAL 工具 Config 列表右面的
U
按钮,更新 Project 列表。点击
build
按钮,编译成功后会显示生成名为zephyr.elf
的输出文件,同时显示当前工程的 FLASH 和 SRAM 占用信息:点击
Flash
按钮,将程序烧录至芯片Flash中:烧录成功后,即可观察到 EVB 板上的蓝色 LED 灯以 1Hz 的频率不断闪烁:
最后,点击
Open IDE
按钮,启动VS Code
,后续代码修改与编译烧录调试均可在 VS Code 环境下进行:
4.2 硬件资源规划¶
4.2.1 时钟与电源配置¶
在实际项目中我们需要确定 SoC 时钟源、内部各模块的时钟配置、电源选择等事项。但这里我们直接使用 EVB 板开发,因此暂不关注这些内容,使用默认配置参数即可。
EVB 板的电源选择相关,请参考 PAN108x EVB 硬件资源介绍 文档。
4.2.2 PWM 模块配置¶
我们使用 PAN1080 SoC 的 PWM 模块控制 EVB 板上的 LED 灯,为此我们需要规划:
使用哪个 PWM 模块的哪个输出通道(PAN1080 SoC 中内置了 3 个硬件 PWM 模块,分别为
PWM0
、PWM1
、PWM2
,每个模块均有 8 个通道)将 PWM 波形输出到 SoC 的哪个引脚
需要注意的是,由于 PAN1080 SoC 的每个引脚最多只能配置成预设的 8 个功能之一,因此我们在规划引脚资源的时候,要查阅各个引脚的 PINMUX 定义,从中找到合适的引脚使用。
我们可以从 PAN1080 Development Kit 中的如下几个文件的任意一个中找到 PAN1080 SoC 的 PINMUX 引脚定义:
01_SDK/zephyr/dts/arm/panchip/pan1080/pan1080xx1_pinctrl.dtsi
(32-Pin 封装引脚定义)01_SDK/zephyr/dts/arm/panchip/pan1080/pan1080xx5_pinctrl.dtsi
(64-Pin 封装引脚定义)01_SDK/zephyr/include/drivers/pinmux/pinmux_pan1080.h
04_DOC/06_others/PAN108x-Datasheet.pdf
或04_DOC/06_others/PAN1080x-产品说明书.pdf
这里,我们直接从 VS Code 中打开
pan1080xx5_pinctrl.dtsi
文件(按快捷键Ctrl
+P
,输入文件名即可)由于 EVB 板上的蓝色 LED 灯已经与
P16
引脚相连,我们先查找P16
引脚的 PINMUX 定义,看是否有 PWM 功能;查阅后发现其正好有一个pwm0_ch6 (PWM0 Channel 6)
的定义,可以供我们使用:为使规划的硬件资源生效,我们需要修改 SDK 中的板级 DeviceTree 文件。
从 VS Code 中打开名为
pan108xxb5_evb.dts
的文件(按快捷键Ctrl
+P
,输入文件名即可):此文件中描述了关于 PAN108X-XB5 EVB 的所有板级硬件配置信息,这些配置保证了我们可以使用 EVB 开发板成功运行 SDK 中的所有例程(
samples_panchip
)和测试用例(tests_panchip
)。关于Zephyr DeviceTree(
.dts
/.dtsi
)的更多细节,请参考:我们暂不关心除PWM以外的模块配置,可以看到,
pan108xxb5_evb.dts
文件第 172~175 行描述了一个PWM0
模块的配置:... &pwm0 { pinctrl-0 = <&p1_0_pwm0_ch4 &p1_1_pwm0_ch5 &p1_6_pwm0_ch6>; status = "okay"; }; ...
其含义如下:
第一行
&pwm0
表示,当前配置文件中对pwm0
中属性值的修改会更新默认的(名为pan1080.dtsi
的)配置文件中的值,亦即,若本次更新的属性不在默认配置文件中,则为此属性赋予一个值;若本次更新的属性已经在默认配置文件中已经有一个值,则覆盖此属性的默认值;第二行
pinctrl-0
表示,修改当前pwm0
模块的 PINMUX 配置,其中尖括号中:&p1_0_pwm0_ch4
表示将芯片P10
引脚的 PINMUX 切换为PWM0 Channel 4功能;&p1_1_pwm0_ch5
表示将芯片P11
引脚的 PINMUX 切换为PWM0 Channel 5功能;&p1_6_pwm0_ch6
表示将芯片P16
引脚的 PINMUX 切换为PWM0 Channel 6功能;
第三行
status = "okay"
表示,在系统初始化过程中,使能 DeviceTree 中对当前PWM
模块的各项参数配置;
由于我们的规划是使用
PWM0 Channel 6
从芯片P16
引脚输出方波,而此处已经配置好,因此 DTS 文件中我们无需做任何改动。
4.2.3 UART 模块配置¶
我们希望使用 PAN1080 SoC 的UART
模块输出 Log 日志到 PC,为此我们需要明确:
使用哪个 UART 模块(PAN1080 SoC 中内置了 2 个 UART 模块,分别为
UART0
、UART1
)UART Tx/Rx 分别映射到 SoC 的哪个引脚
如何与 PC 通信
PAN1080 EVB 底板中提供了一个 USB 转 UART 的模块电路,其可以很方便地通过跳线帽与 PAN1080 SoC 的P00
(UART0 TX
)、P01
(UART0 RX
)相连,这里我们就使用这种方式与 PC 通信。
使用跳线帽将 EVB 底板(左侧)的 USB 转 UART 模块的 2 个通信排针连接至 PAN1080 SoC:
将 TX0 引脚连接至 P00
将 RX0 引脚连接至 P01
再次在 VS Code 中打开板级 DeviceTree 文件
pan108xxb5_evb.dts
,我们可以看到其中已经有了一些与UART 有关的配置:... chosen { zephyr,console = &uart0; zephyr,shell-uart = &uart0; zephyr,bt-mon-uart = &uart0; zephyr,bt-c2h-uart = &uart0; ... }; soc { pin-controller@40030000 { /* port, pin, pinmux_name, pinmux_sel [, flag1, ... ] */ DT_PAN_PINS(p0, 1, uart0_rx, PAN1080_PIN_FUNC_P01_UART0_RX, input-enable); }; }; ... &uart0 { current-speed = <921600>; pinctrl-0 = <&p0_0_uart0_tx &p0_1_uart0_rx>; status = "okay"; }; ...
其含义如下:
chosen
节点描述了 Zephyr OS 用到的硬件模块与实际硬件的映射关系:zephyr,console = &uart0
表示将 Zephyr Console 模块配置为使用UART0
通信,我们此次向 UART 打印 LED 状态消息即使用此方式;zephyr,shell-uart = &uart0
、zephyr,bt-mon-uart = &uart0
、zephyr,bt-c2h-uart = &uart0
均与本文内容无关,这里不做详细介绍,只需了解我们将 Zephyr 所有用到的串口均默认映射到了PAN1080 SoC 的UART0
模块即可;
soc
节点描述了芯片级的硬件配置信息:pin-controller@40030000
是一个子节点,代表 PAN1080 SoC 的 PINMUX(MFP)模块;DT_PAN_PINS(p0, 1, uart0_rx, PAN1080_PIN_FUNC_P00_UART0_RX, input-enable)
用来辅助配置UART0
的 PINMUX 参数;其中,此配置的前 4 个参数表示我们将芯片P01
引脚的 PINMUX 切换为 UART0 RX 功能,最后一个参数表示辅助配置参数,这里input-enable
表示打开此引脚的数字信号输入功能;注1:除
input-enable
参数外,此处还可以配置bias-pull-up
以使用当前引脚的内部上拉电阻,或配置bias-pull-down
以使用当前引脚的内部下拉电阻;注2:此处的 PINMUX 配置不是必须的,只有在需要打开某个引脚数字信号输入功能或内部上拉/下拉电阻功能的时候,才应该在此处进行配置,并且配置的引脚功能应当与对应外设模块中的
pinxtrl-0
中的配置一致,例如:我们在 DeviceTree 的
uart0
节点中将Tx功能配置到了P00
引脚,将Rx功能配置到了P01
引脚,因此我们需要在soc/pin-controller
节点中将P01
引脚的数字信号输入功能打开,但无需配置数字信号输出引脚P06
;如果我们使用
I2C
模块,则由于 I2C 协议要求有上拉电阻,因此我们可以配置bias-pull-up
参数以使用 SoC 内部上拉电阻;如果我们将I2C
模块配置为 Slave,则由于SCL
和SDA
两根线均为输入信号,因此还需要配置input-enable
参数以打开这两个引脚的数字信号输入功能;
&uart0
表示,修改uart0
节点中的属性,且其中的修改会更新默认配置文件(名为pan1080.dtsi
)中的值:current-speed = <921600>
表示,波特率配置为921600;pinctrl-0 = <&p0_0_uart0_tx &p0_1_uart0_rx>
表示:将芯片
P00
引脚的 PINMUX 切换为 UART1 TX 功能;将芯片
P01
引脚的 PINMUX 切换为 UART1 RX 功能(与soc/pin-controller
节点中的配置一致);
status = "okay"
表示,在系统初始化过程中,使能 DeviceTree 中对当前UART
模块的各项参数配置;
从 dts 文件中可知,SDK 中对于 UART 的配置已经与我们在 EVB 板中的接线一致,因此无需再做修改。
4.3 修改代码¶
下面我们演示如何在 VS Code 环境下写 App 代码,实现预期功能。
4.3.1 使用PWM¶
Zephyr 提供了标准的 PWM API 接口(头文件位置:
01_SDK/zephyr/include/drivers/pwm.h
),为了使用这些接口,我们需要先使能 Zephyr PWM Driver,方法是在 App 目录的prj.conf
,增加一个 Config 项:CONFIG_PWM=y
增加后文件内容如图所示:
接着,我们在
main.c
中,删除一些无关的代码,并增加 PWM 相关操作代码:/* * Copyright (c) 2021-2022 Shanghai Panchip Microelectronics Co.,Ltd. * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr.h> #include <device.h> #include <devicetree.h> #include <drivers/gpio.h> #include <drivers/pwm.h> void main(void) { const struct device *pwm_dev; bool flag = true; pwm_dev = DEVICE_DT_GET(DT_NODELABEL(pwm0)); if (pwm_dev == NULL) { return; } while (1) { if (flag == true) { flag = !flag; /* LED Blink, turn on 20ms and turn off 80ms in every 100ms */ pwm_pin_set_usec(pwm_dev, 6, 100000, 20000, PWM_POLARITY_NORMAL); } else { flag = !flag; /* LED Blink, turn on 80ms and turn off 20ms in every 100ms */ pwm_pin_set_usec(pwm_dev, 6, 100000, 80000, PWM_POLARITY_NORMAL); } k_msleep(3000); } }
点击 VS Code 的
Terminal
菜单,选择Run Build Task...
(或直接快捷键Ctrl + Shift + B
),依次执行 Build 和 Flash 命令,将程序烧录至芯片中,即可观察到 LED 灯闪烁,且每 3s 切换一次闪烁频率。
4.3.2 使用 UART¶
我们可以直接使用 zephyr 提供的 printk 函数,将 Log 信息输出至串口,修改while(1)
循环内的代码如下:
while (1) {
if (flag == true) {
flag = !flag;
/* LED Blink, turn on 20ms and turn off 80ms in every 100ms */
if (pwm_pin_set_usec(pwm_dev, 6, 100000, 20000, PWM_POLARITY_NORMAL) == 0) {
printk("LED Blink, turn-on 20ms and turn-off 80ms in every 100ms..");
} else {
printk("error occurs\n");
}
} else {
flag = !flag;
/* LED Blink, turn on 80ms and turn off 20ms in every 100ms */
if (pwm_pin_set_usec(pwm_dev, 6, 100000, 80000, PWM_POLARITY_NORMAL) == 0) {
printk("LED Blink, turn-on 80ms and turn-off 20ms in every 100ms..");
} else {
printk("error occurs\n");
}
}
k_msleep(3000);
}
重新编译烧录后,在 PC 中使用串口工具(如 SecureCRT)即可看到每 3s 打印一次消息:
5 更多相关文档¶
Zephyr Devicetree 与 Kconfig 配置指南:介绍 Zephyr Kconfig 配置的相关技巧
Zephyr DeviceTree APIs:Zephyr官方对于DeviceTree接口的详细介绍
Zephyr Peripheral APIs:Zephyr官方提供的外设Driver API接口详细介绍