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

SoC 底层驱动使用指南

提示

  1. SoC 底层驱动演示例程请参考 <PAN1080-DK>/03_MCU 目录下的相关工程及说明文档

  2. 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 头文件

描述

device/Include/pan_clk.h

Clock and Reset Ctrl Driver APIs

device/Include/pan_efuse.h

eFuse Driver APIs

device/Include/pan_kscan.h

KeyScan Driver APIs

device/Include/pan_qdec.h

Qdec Driver APIs

device/Include/PanSeries.h

SoC 级寄存器定义

peripheral/inc/pan_adc.h

ADC Driver APIs

peripheral/inc/pan_dmac.h

DMA Driver APIs

peripheral/inc/pan_gpio.h

GPIO Driver APIs

peripheral/inc/pan_i2c.h

I2C Driver APIs

peripheral/inc/pan_i2s.h

I2S Driver APIs

peripheral/inc/pan_pwm.h

PWM Driver APIs

peripheral/inc/pan_spi.h

SPI Driver APIs

peripheral/inc/pan_timer.h

TIMER Driver APIs

peripheral/inc/pan_uart.h

UART Driver APIs

peripheral/inc/pan_wdt.h

Watchdog Driver APIs

peripheral/inc/pan_wwdt.h

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 的方法很简单:

  1. 按照上一小节的介绍,确认底层 Driver 对应的 Config 已经正确配置

  2. 在 App 的 C 代码中,包含 soc.h 头文件

  3. 之后即可直接调用底层 Driver

这里我们举一个简单的例子来说明如何在 Zephyr App 中调用底层驱动。

假设我们想使用 GPIO 输出电平的方式来测量一段代码的执行时间,由于需要尽量减小拉 GPIO 电平本身的时间消耗,因此宜直接调用底层 GPIO Driver 的位映射(PDIO)方式直接输出电平,以确保最高效率。

以 basic/blinky Sample 为例:

  1. 先在main.c的起始位置处包含soc.h头文件:

    ...
    #include <soc.h>
    
  2. 我们可以看到原始的 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);
       }
    }
    
  3. 接着我们调用底层 GPIO Driver,将 4 根不同的 GPIO pin(P02、P03、P04、P05)配置为推挽输出,然后在待测量的函数执行前将其拉低,执行后将其拉高:

    1. 首先在 main.c 的开头加入头文件:

      #include <soc.h>
      
    2. 然后在 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);
      
    3. 接着在每个待测量函数前,将特定 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;
        }
      }
      
    4. 修改完成后,编译烧录至 EVB:

      img

      编译烧录修改后的 Blinky Sample

  4. 将逻辑分析仪接入 EVB 板的 P02/P03/P04/P05 等 4 个 GPIO 引脚(接入前应先确认这些引脚没有接到其他模块上),然后分别观察 4 个波形:

    1. 从 P02 引脚捕获到的波形如下:

      img

      使用逻辑分析仪观察 GPIO 翻转 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 一直处于推挽输出高电平状态

    2. 从 P03 引脚捕获到的波形如下:

      img

      使用逻辑分析仪观察 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 一直处于推挽输出高电平状态

    3. 从 P04 引脚捕获到的波形如下:

      img

      使用逻辑分析仪观察 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 一直处于推挽输出高电平状态

    4. 从 P05 引脚捕获到的波形如下:

      img

      使用逻辑分析仪观察 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,提示编译错误,则可以按以下的步骤检查工程配置:

  1. 确认编译输出目录中最终生成的 autoconf.h 文件中(位于<output_folder>/zephyr/include/generated目录),有使能了当前底层 Driver 的 Config。例如我们希望使用底层 GPIO Driver,则在autoconf.h中应存在如下的定义:

    #define CONFIG_USE_PANPLAT_GPIO 1
    

    如果此定义不存在,则应检查当前 Board 目录下的Kconfig.board文件是否正确选择此 Config。

  2. 如果上一步的定义存在,但仍然无法编译通过,则应继续检查(以 GPO Driver 为例):

    1. <PAN1080-DK>/01_SDK/zephyr/soc/arm/panchip/pan1080/soc.h文件中是否正确包含了底层 Driver 头文件:

      #ifdef CONFIG_USE_PANPLAT_GPIO
      #include <pan_gpio.h>
      #endif
      
    2. <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