当前文档版本为v0.3.0,您可以访问 开发中 的文档以获取最近可能的更新。

快速上手 SoC App 开发

本文演示如何新建一个简单的App工程,并举例说明如何在App工程中操作PAN1080 EVB开发板的外设模块。

1 确认开发环境

参考 SDK 快速入门,确认软硬件开发环境,可以正常编译、下载和调试程序。

2 参考相关例程

SDK中提供了一些例程(位于01_SDK/zephyr/samples_panchip目录),可以直接编译下载到EVB开发板上执行,包括多线程打印消息、LED闪灯、以及蓝牙相关例程等等。

在进行开发之前,建议先看一下相关的例程和文档,熟悉Zephyr OS的基本框架;然后实际将这些例程编译烧录至EVB开发板中查看运行效果,同时进一步熟悉开发环境的使用。

image

另外,SDK中也提供了一些外设模块的测试用例(位于01_SDK/zephyr/tests_panchip目录),用于测试外设Driver功能是否正常。阅读这些测试用例代码,也有助于熟悉Zephyr外设 Driver的使用方法。

image

3 新建一个App工程

假设我们希望创建一个名为my_led_blink的App工程,使用PWM的方式将EVB开发板上的红色LED灯点亮并令其闪烁,要求:

  1. 首先,LED灯以5Hz的频率闪烁,持续5s

  2. 重复上述闪烁规则

  3. 每次闪烁频率切换的时候,均向UART串口打印闪烁频率信息

下面我们详细讲解如何新建一个工程来实现上述需求。

3.1 从例程中拷贝一份相似的工程

例程blinky(位置:01_SDK/zephyr/samples_panchip/basic/blinky目录)演示了如何使用GPIO的方式点亮LED并令其每隔1s闪烁一次。

我们以此例程为基础进行修改:

  1. 首先,拷贝一份 blinky 并将其重命名为 my_led_blink

    image

    打开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单元测试的信息描述文件

  2. 然后,拷贝一份名为 blinky.bat 的 Quick Build 脚本(位于01_SDK/quick_build_samples/basic目录),用于执行编译烧录;我们同样将其重命名为与App工程相同,即my_led_blink.bat

    :脚本内容无需修改,其会根据脚本文件名自动关联同名的App工程。

    image

  3. 最后,确认新增的my_led_blink.bat可以正常编译、烧录、运行新增的my_led_blink工程:

    1. 将EVB开发板通过USB2(USB to UART)接口供电,并确认:

      1. JLink SWD正确连接至EVB板(P46ICEKP47ICED

      2. 使用跳线帽将EVB底板(左上侧)的LED/红外发射共用电路的排针连接至PAN1080 SoC(P21FRTX

  4. 双击my_led_blink.bat脚本,执行第一次编译,成功后,会显示成功生成名为zephyr.elf的输出文件,同时显示当前工程的FLASH和SRAM占用信息:

    image

  5. 输入指令 f并回车,将程序烧录至芯片Flash中:

    image

  6. 烧录成功后,即可观察到EVB底板左上角的红色LED灯以1Hz的频率不断闪烁:

    image

  7. 最后,我们输入指令 o,启动 VS Code ,后续代码修改与编译烧录调试均可在VS Code环境下进行:

    image

3.2 硬件资源规划

3.2.1 时钟与电源配置

在实际项目中我们需要确定SoC时钟源、内部各模块的时钟配置、电源选择等事项。但这里我们直接使用EVB板开发,因此暂不关注这些内容,使用EVB板的默认配置参数即可。

我们通过PC使用MicroUSB数据线连接至EVB开发板的USB to UART接口(如图蓝色圆框所示),为开发板提供5V电源;同时确认开发板相关电源跳线(如图红色方框所示)连接正确:

image

3.2.2 PWM模块配置

我们使用PAN1080 SoC的PWM模块控制EVB底板上的LED灯,为此我们需要规划:

  • 使用哪个PWM模块的哪个输出通道(PAN1080 SoC中内置了3个硬件PWM模块,分别为PWM0PWM1PWM2,每个模块均有8个通道)

  • 将PWM波形输出到SoC的哪个引脚

需要注意的是,由于PAN1080 SoC的每个引脚最多只能配置成预设的8个功能之一,因此我们在规划引脚资源的时候,要查阅各个引脚的PINMUX定义,从中找到合适的引脚使用。

  1. 我们可以从PAN1080 Development Kit中的如下几个文件的任意一个中找到PAN1080 SoC的PINMUX引脚定义:

    • 01_SDK/zephyr/dts/arm/panchip/pan1080/pan1080a_afld_pinctrl.dtsi

    • 01_SDK/zephyr/include/drivers/pinmux/pinmux_pan1080.h

    • 04_DOC/05_soc_manual/PAN1080 Datasheet.pdf04_DOC/05_soc_manual/PAN1080 产品说明书.pdf

  2. 这里,我们直接从VS Code中打开pan1080a_afld_pinctrl.dtsi文件(按快捷键 Ctrl+P,输入文件名即可):

    image

  3. 由于当前EVB底板上的LED灯已经与P21引脚相连,我们先查找P21引脚的PINMUX定义,看是否有PWM功能;查阅后发现其并没有PWM功能,但相邻的P22引脚,有一个pwm1_ch4 (PWM1 Channel 4)的定义,可以供我们使用:

    image

  4. 在EVB底板上,将P21排针与FRTX排针连接的跳线帽拔出,另使用一根杜邦线将P22排针与FRTX排针相连:

    image

  5. 为使我们规划的硬件资源生效,我们需要修改SDK中的板级DeviceTree文件。

    从VS Code中打开名为pan1080a_afld_evb.dts的文件(按快捷键 Ctrl+P,输入文件名即可):

    image

    此文件中描述了关于PAN1080A-AFLD EVB的所有板级硬件配置信息,这些配置保证了我们可以使用EVB开发板成功运行SDK中的所有例程(samples_panchip)和测试用例(tests_panchip)。

    关于Zephyr DeviceTree(.dts/.dtsi)的更多细节,请参考Zephyr官方文档:Introduction to devicetree

  6. 我们暂不关心除PWM以外的模块配置,可以看到,pan1080a_afld_evb.dts文件第123~126行描述了一个PWM0模块的配置:

    ...
    
    &pwm0 {
    	pinctrl-0 = <&p1_0_pwm0_ch4 &p1_1_pwm0_ch5 &p1_6_pwm0_ch6>;
    	status = "okay";
    };
    
    ...
    

    其含义如下:

    • 第一行&pwm0表示,当前配置文件中对pwm0中属性值的修改会更新默认配置文件(名为pan1080.dtsi)中的值,亦即,若本次更新的属性不在默认配置文件中,则为此属性赋予一个值;若本次更新的属性已经在默认配置文件中已经有一个值,则覆盖此属性的默认值;

    • 第二行pinctrl-0表示,修改当前pwm模块的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模块的各项参数配置;

  7. 由于我们的规划是使用PWM1 Channel 4从芯片P22引脚输出方波,因此我们仿照上述对PWM0模块的描述,添加一个PWM1模块配置:

    ...
    
    &pwm0 {
    	pinctrl-0 = <&p1_0_pwm0_ch4 &p1_1_pwm0_ch5 &p1_6_pwm0_ch6>;
    	status = "okay";
    };
    
    &pwm1 {
    	pinctrl-0 = <&p2_2_pwm1_ch4>;
    	status = "okay";
    };
    
    ...
    

3.2.3 UART模块配置

我们希望使用PAN1080 SoC的UART模块输出Log日志到PC,为此我们需要明确:

  • 使用哪个UART模块(PAN1080 SoC中内置了2个UART模块,分别为UART0UART1

  • UART Tx/Rx分别映射到SoC的哪个引脚

  • 如何与PC通信

PAN1080 EVB底板中提供了一个USB转UART的模块电路,其可以很方便地通过跳线帽与PAN1080 SoC的P06UART1 TX)、P07UART1 RX)相连,这里就采用这种方式与PC通信。

  1. 使用跳线帽将EVB底板(左上侧)的USB转UART模块的2个通信排针连接至PAN1080 SoC:

    image

  2. 再次在VS Code中打开板级DeviceTree文件pan1080a_afld_evb.dts,我们可以看到其中已经有了一些与UART有关的配置:

    ...
    
    	chosen {
    		zephyr,console = &uart1;
    		zephyr,shell-uart = &uart1;
    		zephyr,bt-mon-uart = &uart1;
    		zephyr,bt-c2h-uart = &uart1;
            ...
    	};
    
    	soc {
    		pin-controller@40030000 {
    			/* port, pin, pinmux_name, pinmux_sel [, flag1, ... ] */
    			DT_PAN_PINS(p0, 7, uart1_rx, PAN1080_PIN_FUNC_P07_UART1_RX, input-enable);
    		};
    	};
    
    ...
    
    &uart1 {
    	current-speed = <921600>;
    	pinctrl-0 = <&p0_6_uart1_tx &p0_7_uart1_rx>;
    	status = "okay";
    };
    
    ...
    

    其含义如下:

    • chosen节点描述了Zephyr OS用到的硬件模块与实际硬件的映射关系:

      • zephyr,console = &uart1表示将Zephyr Console模块配置为使用UART1通信,我们此次向UART打印LED状态消息即使用此方式;

      • zephyr,shell-uart = &uart1zephyr,bt-mon-uart = &uart1zephyr,bt-c2h-uart = &uart1均与本文内容无关,这里不做详细介绍,只需了解我们将Zephyr所有用到的串口均默认映射到了PAN1080 SoC的UART1模块即可;

    • soc节点描述了SoC级的硬件配置信息:

      • pin-controller@40030000是一个子节点,代表PAN1080 SoC的PINMUX(MFP)模块;

      • DT_PAN_PINS(p0, 7, uart1_rx, PAN1080_PIN_FUNC_P07_UART1_RX, input-enable)用来辅助配置UART1的PINMUX参数;其中,此配置的前4个参数表示我们将芯片P07引脚的PINMUX切换为UART1 RX功能,最后一个参数表示辅助配置参数,这里input-enable表示打开此引脚的数字信号输入功能;

        注1:除input-enable参数外,此处还可以配置bias-pull-up以使用当前引脚的内部上拉电阻,或配置bias-pull-down以使用当前引脚的内部下拉电阻;

        注2:此处的PINMUX配置不是必须的,只有在需要打开某个引脚数字信号输入功能或内部上拉/下拉电阻功能的时候,才应该在此处进行配置,并且配置的引脚功能应当与对应外设模块中的pinxtrl-0中的配置一致,例如:

        • 我们在DeviceTree的uart1节点中将Tx功能配置到了P06引脚,将Rx功能配置到了P07引脚,因此我们需要在soc/pin-controller节点中将P07引脚的数字信号输入功能打开,但无需配置数字信号输出引脚P06

        • 如果我们使用I2C模块,则由于I2C协议要求有上拉电阻,因此我们可以配置bias-pull-up参数以使用SoC内部上拉电阻;如果我们将I2C模块配置为Slave,则由于SCLSDA两根线均为输入信号,因此还需要配置input-enable参数以打开这两个引脚的数字信号输入功能;

    • &uart1表示,修改uart1节点中的属性,且其中的修改会更新默认配置文件(名为pan1080.dtsi)中的值:

      • current-speed = <921600>表示,波特率配置为921600;

      • pinctrl-0 = <&p0_6_uart1_tx &p0_7_uart1_rx>表示:

        • 将芯片P06引脚的PINMUX切换为UART1 TX功能;

        • 将芯片P07引脚的PINMUX切换为UART1 RX功能(与soc/pin-controller节点中的配置一致);

      • status = "okay"表示,在系统初始化过程中,使能DeviceTree中对当前UART模块的各项参数配置;

  3. 从dts文件中可知,SDK中对于UART的配置已经与我们在EVB板中的接线一致,因此无需再做修改。

3.3 修改代码

下面我们演示如何在VS Code环境下写App代码,实现预期功能。

3.3.1 使用PWM

  1. Zephyr提供了标准的PWM API接口(头文件位置:01_SDK/zephyr/include/drivers/pwm.h),为了使用这些接口,我们需要先使能Zephyr PWM Driver,方法是在App目录的prj.conf,增加一个Config项:

    CONFIG_PWM=y
    

    增加后文件内容如图所示:

    image

  2. 接着,我们在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 *dev;
    
    	dev = DEVICE_DT_GET(DT_NODELABEL(pwm1));
    	if (dev == NULL) {
    		return;
    	}
    
    	while (1) {
    		/* LED Blink 5Hz */
    		pwm_pin_set_usec(dev, 4, 200000, 100000, PWM_POLARITY_NORMAL);
    		k_msleep(5000);
    	}
    }
    
    
  3. 点击VS Code的Terminal菜单,选择Run Build Task...(或直接快捷键Ctrl + Shift + B),依次执行Build和Flash命令,将程序烧录至芯片中,即可观察到LED灯以5Hz的频率闪烁。

3.3.2 使用UART

我们可以直接使用zephyr提供的printk函数,将Log信息输出至串口,修改while(1)循环内的代码如下:

	while (1) {
		printk("LED to Blink 5 times per second\n");
		pwm_pin_set_usec(dev, 4, 200000, 100000, PWM_POLARITY_NORMAL);
		k_msleep(5000);
	}

重新编译烧录后,在PC中使用串口工具(如SecureCRT)即可看到每5s打印一次消息:

image

4 更多相关文档

  1. Zephyr Kconfig配置指南:介绍Zephyr Kconfig配置的相关技巧

  2. Zephyr DeviceTree APIs:Zephyr官方对于DeviceTree接口的详细介绍

  3. Zephyr Peripheral APIs:Zephyr官方提供的外设Driver API接口详细介绍