SoC 底层驱动使用指南¶
提示
SoC 底层驱动演示例程请参考
<PAN1080-DK>/03_MCU
目录下的相关工程及说明文档SoC 底层驱动 API 文档请参考: PAN1080 Peripheral API Documentation
1 概述¶
我们在 Zephyr Driver Samples 文档中介绍了 SDK 目前实现的各个 Zephyr Driver 的演示例程。 然而,在实际应用中,Zephyr 提供的 Driver API 并不总是能满足我们需求的,原因是:
Zephyr Driver API 做了高度封装,执行效率会低一些,在时间要求极高的场景下可能无法适用
Zephyr Driver API 为了通用做了高度抽象,通常只实现了各个外设最常用的功能,一些特殊功能是没有支持的
鉴于以上原因,我们可以选择直接在 Zephyr App 中调用 PAN1080 SoC 的底层驱动来满足我们的需求。
2 底层驱动架构¶
PAN1080 底层驱动位于 <PAN1080-DK>/01_SDK/modules/hal/panchip/panplat/pan1080/bsp
目录中,一些常用的接口头文件如下:
API 头文件 |
描述 |
---|---|
|
Clock and Reset Ctrl Driver APIs |
|
eFuse Driver APIs |
|
KeyScan Driver APIs |
|
Qdec Driver APIs |
|
SoC 级寄存器定义 |
|
ADC Driver APIs |
|
DMA Driver APIs |
|
GPIO Driver APIs |
|
I2C Driver APIs |
|
I2S Driver APIs |
|
PWM Driver APIs |
|
SPI Driver APIs |
|
TIMER Driver APIs |
|
UART Driver APIs |
|
Watchdog Driver APIs |
|
Window Watchdog Driver APIs |
上述这些底层驱动,通过 <PAN1080-DK>/01_SDK/modules/hal/panchip/panplat/pan1080/bsp
目录下的 CMakeLists.txt
脚本导出到 Zephyr:
# These include directories would export to Zephyr
zephyr_include_directories(
device/Include/
peripheral/inc/
)
zephyr_library_sources(
device/Source/pan_clk.c
device/Source/pan_efuse.c
...
)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_ADC peripheral/src/pan_adc.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_DMAC peripheral/src/pan_dmac.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_FMC peripheral/src/pan_fmc.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_GPIO peripheral/src/pan_gpio.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_I2C peripheral/src/pan_i2c.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_I2S peripheral/src/pan_i2s.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_PWM peripheral/src/pan_pwm.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_SPI peripheral/src/pan_spi.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_TIMER peripheral/src/pan_timer.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_UART peripheral/src/pan_uart.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_WDT peripheral/src/pan_wdt.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_WWDT peripheral/src/pan_wwdt.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_KSCAN device/Source/pan_kscan.c)
zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_QDEC device/Source/pan_qdec.c)
...
在 Zephyr 中,使用形如 CONFIG_USE_PANPLAT_<XXX>
的 Kconfig 来使能各个底层 Driver,其定义位于
<PAN1080-DK>/01_SDK/zpehyr/modules/Kconfig.panchip
配置文件中:
...
config USE_PANPLAT_GPIO
bool
help
Enable Panchip General-Purpose-Input-and-Output (GPIO) low-level module driver
config USE_PANPLAT_I2C
bool
help
Enable Panchip Inter-Integrated-Circuit bus (I2C) low-level module driver
config USE_PANPLAT_I2S
bool
help
Enable Panchip Inter-IC Sound (I2S) low-level module driver
config USE_PANPLAT_KSCAN
bool
help
Enable Panchip Key Scan (KSCAN) low-level module driver
...
注意,对于 SDK 中默认的 EVB Board 来说,PAN1080 底层 Driver 已经在各个 Board 目录下的Kconfig.board
文件中使用 select
的方式使能,无需再做额外配置:
config BOARD_PAN108XXXX_EVB
bool "PAN108X-XXX Evaluation Board"
depends on SOC_PAN1080XXX
select USE_PANPLAT_ACC
select USE_PANPLAT_ADC
select USE_PANPLAT_CLKTRIM
select USE_PANPLAT_DMAC
select USE_PANPLAT_FMC
select USE_PANPLAT_GPIO
select USE_PANPLAT_I2C
select USE_PANPLAT_I2S
select USE_PANPLAT_KSCAN
select USE_PANPLAT_LOWPOWER
select USE_PANPLAT_PWM
select USE_PANPLAT_PRF
select USE_PANPLAT_QDEC
select USE_PANPLAT_SPI
select USE_PANPLAT_TIMER
select USE_PANPLAT_UART
select USE_PANPLAT_USB
select USE_PANPLAT_WDT
select USE_PANPLAT_WWDT
select USE_PANPLAT_EFUSE
3 底层驱动调用方法¶
在 Zephyr App 代码中,调用底层 Driver 的方法很简单:
按照上一小节的介绍,确认底层 Driver 对应的 Config 已经正确配置
在 App 的 C 代码中,包含
soc.h
头文件之后即可直接调用底层 Driver
这里我们举一个简单的例子来说明如何在 Zephyr App 中调用底层驱动。
假设我们想使用 GPIO 输出电平的方式来测量一段代码的执行时间,由于需要尽量减小拉 GPIO 电平本身的时间消耗,因此宜直接调用底层 GPIO Driver 的位映射(PDIO)方式直接输出电平,以确保最高效率。
以 basic/blinky Sample 为例:
先在
main.c
的起始位置处包含soc.h
头文件:... #include <soc.h>
我们可以看到原始的
main()
中共调用了 4 个不同的函数:device_get_binding()
gpio_pin_configure()
gpio_pin_set()
k_msleep()
void main(void) { const struct device *dev; bool led_is_on = true; int ret; dev = device_get_binding(LED0); if (dev == NULL) { return; } ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); if (ret < 0) { return; } while (1) { gpio_pin_set(dev, PIN, (int)led_is_on); led_is_on = !led_is_on; k_msleep(SLEEP_TIME_MS); } }
接着我们调用底层 GPIO Driver,将 4 根不同的 GPIO pin(P02、P03、P04、P05)配置为推挽输出,然后在待测量的函数执行前将其拉低,执行后将其拉高:
首先在 main.c 的开头加入头文件:
#include <soc.h>
然后在
main()
函数起始处配置 GPIO 模式,随后这些 GPIO 引脚开始输出默认的高电平:GPIO_SetMode(P0, BIT2, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT3, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT4, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT5, GPIO_MODE_OUTPUT);
接着在每个待测量函数前,将特定 GPIO 引脚拉低,执行完成后将 GPIO 引脚拉高:
void main(void) { const struct device *dev; bool led_is_on = true; int ret; GPIO_SetMode(P0, BIT2, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT3, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT4, GPIO_MODE_OUTPUT); GPIO_SetMode(P0, BIT5, GPIO_MODE_OUTPUT); P02 = 0; dev = device_get_binding(LED0); P02 = 1; if (dev == NULL) { return; } P03 = 0; ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS); P03 = 1; if (ret < 0) { return; } while (1) { P04 = 0; gpio_pin_set(dev, PIN, (int)led_is_on); P04 = 1; led_is_on = !led_is_on; P05 = 0; k_msleep(SLEEP_TIME_MS); P05 = 1; } }
修改完成后,编译烧录至 EVB:
将逻辑分析仪接入 EVB 板的 P02/P03/P04/P05 等 4 个 GPIO 引脚(接入前应先确认这些引脚没有接到其他模块上),然后分别观察 4 个波形:
从 P02 引脚捕获到的波形如下:
其中:
①处表示系统上电到执行
GPIO_SetMode(P0, BIT2, GPIO_MODE_OUTPUT)
语句之间的时间,此阶段 GPIO 处于默认的高阻输入状态②处表示将 GPIO P02 配置为推挽输出(GPIO_MODE_OUTPUT)后,到执行
P02 = 0
语句之间的时间,此阶段 GPIO 处于默认的输出高电平状态③处表示系统执行
P02 = 0
后,到执行P02 = 1
语句之间的时间,此阶段 GPIO 处于推挽输出低电平状态,使用逻分测量其时间为 13.48 us,也即dev = device_get_binding(LED0)
语句的执行时间③处表示系统执行
P02 = 1
后的时间,此阶段 GPIO 一直处于推挽输出高电平状态
从 P03 引脚捕获到的波形如下:
其中:
①处表示系统上电到执行
GPIO_SetMode(P0, BIT3, GPIO_MODE_OUTPUT)
语句之间的时间,此阶段 GPIO 处于默认的高阻输入状态②处表示将 GPIO P03 配置为推挽输出(GPIO_MODE_OUTPUT)后,到执行
P03 = 0
语句之间的时间,此阶段 GPIO 处于默认的输出高电平状态③处表示系统执行
P03 = 0
后,到执行P03 = 1
语句之间的时间,此阶段 GPIO 处于推挽输出低电平状态,使用逻分测量其时间为 19.66 us,也即ret = gpio_pin_configure(dev, PIN, GPIO_OUTPUT_ACTIVE | FLAGS)
语句的执行时间③处表示系统执行
P03 = 1
后的时间,此阶段 GPIO 一直处于推挽输出高电平状态
从 P04 引脚捕获到的波形如下:
其中:
①处表示系统上电到执行
GPIO_SetMode(P0, BIT4, GPIO_MODE_OUTPUT)
语句之间的时间,此阶段 GPIO 处于默认的高阻输入状态②处表示将 GPIO P04 配置为推挽输出(GPIO_MODE_OUTPUT)后,到第一次执行
P04 = 0
语句之间的时间,此阶段 GPIO 处于默认的输出高电平状态③处表示系统第一次执行
P04 = 0
后,到第一次执行P04 = 1
语句之间的时间,此阶段 GPIO 处于推挽输出低电平状态,使用逻分测量其时间为 5.7 us,也即gpio_pin_set(dev, PIN, (int)led_is_on)
语句的执行时间③处表示系统执行
P04 = 1
后,到第二次执行P04 = 0
语句之间的时间,此阶段 GPIO 一直处于推挽输出高电平状态
从 P05 引脚捕获到的波形如下:
其中:
①处表示系统上电到执行
GPIO_SetMode(P0, BIT5, GPIO_MODE_OUTPUT)
语句之间的时间,此阶段 GPIO 处于默认的高阻输入状态②处表示将 GPIO P05 配置为推挽输出(GPIO_MODE_OUTPUT)后,到第一次执行
P05 = 0
语句之间的时间,此阶段 GPIO 处于默认的输出高电平状态③处表示系统第一次执行
P05 = 0
后,到第一次执行P05 = 1
语句之间的时间,此阶段 GPIO 处于推挽输出低电平状态,使用逻分测量其时间为 1.00012 s,也即k_msleep(SLEEP_TIME_MS)
语句的执行时间
4 Troubleshooting¶
在使用底层 Driver 的过程中,如果调用某个 API,提示编译错误,则可以按以下的步骤检查工程配置:
确认编译输出目录中最终生成的
autoconf.h
文件中(位于<output_folder>/zephyr/include/generated
目录),有使能了当前底层 Driver 的 Config。例如我们希望使用底层 GPIO Driver,则在autoconf.h
中应存在如下的定义:#define CONFIG_USE_PANPLAT_GPIO 1
如果此定义不存在,则应检查当前 Board 目录下的
Kconfig.board
文件是否正确选择此 Config。如果上一步的定义存在,但仍然无法编译通过,则应继续检查(以 GPO Driver 为例):
<PAN1080-DK>/01_SDK/zephyr/soc/arm/panchip/pan1080/soc.h
文件中是否正确包含了底层 Driver 头文件:#ifdef CONFIG_USE_PANPLAT_GPIO #include <pan_gpio.h> #endif
<PAN1080-DK>/01_SDK/modules/hal/panchip/panplat/pan1080/bsp/CMakeLists.txt
文件中是否正确导出了底层 Driver 的 C 源文件:zephyr_library_sources_ifdef(CONFIG_USE_PANPLAT_GPIO peripheral/src/pan_gpio.c) #endif