测试适配的HDF驱动 原创

Hello_Kun
发布于 2024-9-19 14:54
浏览
1收藏

适配L2芯片后,一般需要验证hdf模块是否正常工作,下面介绍GPIO、ADC、PWM测试流程。注:硬件不同,但可参考流程。

1 GPIO

测试接口GPIO202,对应开发板的LED(D14)。

1.2 sysfs操作,测试GPIO

  • 首先,查看可导出的gpio

    ls /sys/class/gpio/
    export   gpiochip128  gpiochip192  gpiochip32   gpiochip96
    gpio188  gpiochip160  gpiochip224  gpiochip480  unexport
    
  • 导出gpio188,用于操作

    echo 188 > /sys/class/gpio/export
    // 查看gpio188的属性
    cd /sys/class/gpio/gpio188/
    # ls gpio188/
    active_low  device  direction  subsystem  uevent  value
    
  • 查看GPIO状态

    cat /sys/class/gpio/gpio188/direction
    
  • GPIO导出后默认为输入in,改为输出

    echo out > /sys/class/gpio/gpio188/direction
    // 输入
    echo in > /sys/class/gpio/gpio188/direction
    
  • 获取GPIO电平状态

    cat /sys/class/gpio/gpio188/value
    
  • GPIO输出控制

    #前提是输出
    echo out > /sys/class/gpio/gpio188/direction
    #高电平
    echo 1 > /sys/class/gpio/gpio188/value
    #低电平
    echo 0  /sys/class/gpio/gpio188/value
    

1.3 HDI操作GPIO

可以基于open、write操作gpio,也可以基于HDI硬HDI件驱动接口操作GPIO,具体代码见applications/sample/taget_board/app/gpio/src/hdf_gpio_test.c

参考1:GPIO 平台驱动开发(openharmony.cn)

参考2:GPIO平台驱动使用 (openharmony.cn)

// sysfs操作GPIO
#if 0
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <errno.h>

#ifndef MODE3
#define MODE3
#endif 

#define EXPORT_PATH  "/sys/class/gpio/export" 

#ifdef MODE3
#define DIRECT_PATH "/sys/class/gpio/gpio202/direction" 
#define DEV_PATH "/sys/class/gpio/gpio202/value" 
#define GPIO "202"
#endif

#define OUT  "out"
#define HIGH "1"
#define LOW "0"
int main(void)
{
        int fd_export = 0,fd_diret = 0,fd_dev = 0;
        int write_return = 0;

        fd_export = open(EXPORT_PATH, O_WRONLY);  
        if( fd_export < 0){

                perror("open export error:");
                return -1;
        }

        write_return = write(fd_export, GPIO, strlen(GPIO)); 

        fd_diret = open(DIRECT_PATH, O_RDWR); 
        if( fd_diret < 0){

                perror("open diret error");
                return -1;
        }

        fd_dev   = open(DEV_PATH, O_RDWR); 
    if( fd_dev < 0){

                perror("open dev error");
                return -1;
        }
        write_return = write(fd_diret, OUT, sizeof(OUT)); 
        while(1)
        {
                write_return=write(fd_dev, HIGH , sizeof(HIGH));
                sleep(1);
                write_return=write(fd_dev, LOW , sizeof(LOW));
                sleep(1);
        }

        write_return=write(fd_dev, LOW , sizeof(LOW)); 
        printf("write_return=%d\r\n",write_return);

        close(fd_export);
        close(fd_diret);
        close(fd_dev);

        return 0;
}
#else // HDI接口操作GPIO

#include <stdio.h>
#include <unistd.h>  // sleep
#include <stdlib.h>
#include "gpio_if.h"
int HIGH =  1;
int LOW =  0;

int main(void)
{
    printf("\r\n\t HDF-Gpio Out Example!\r\n");
    int32_t ret;
    uint16_t gpio = 202;  // 板载LED接口
    printf("请输入GPIO接口(202为板载led): ");
    ret  = scanf("%d",&gpio);
    ret = GpioSetDir(gpio, GPIO_DIR_OUT); //  配置为输出
    // 循环10S
    for(int i = 0; i < 3; i++){
       ret = GpioWrite(gpio, HIGH);
       printf("GPIO_%d 亮\r\n",gpio);
       sleep(1); // 阻塞1S
       ret =  GpioWrite(gpio, LOW);
       printf("GPIO_%d 灭\r\n",gpio);
       sleep(1); // 阻塞1S
    }
    printf("ret = %d\r\n",ret);
    return 0;
}
#endif

1.4 编写测试用例BUILD.gn

  • 编写测试用例的编译规则和依赖,applications/sample/taget_board/app/gpio/BUILD.gn
HDF_FRAMEWORKS = "//drivers/hdf_core/framework"

import("//build/ohos.gni")
import("//drivers/hdf_core/adapter/uhdf2/uhdf.gni")
import("//build/lite/config/component/lite_component.gni")

executable("gpio_led") {
    defines = [ "__USER__" ]
    
    output_name = "gpio_led"
    sources = [
        "src/hdf_gpio_test.c"
    ]
    include_dirs = [
        "$HDF_FRAMEWORKS/include",
        "$HDF_FRAMEWORKS/include/core",
        "$HDF_FRAMEWORKS/include/osal",
        "$HDF_FRAMEWORKS/include/platform",
        "$HDF_FRAMEWORKS/include/utils",
        "//third_party/bounds_checking_function/include",
        "//drivers/hdf_core/adapter/uhdf2/ipc/include",
        "//drivers/hdf_core/adapter/uhdf2/osal/include",
        "//drivers/hdf_core/framework/include/platform",
    ]

    deps = [
        "//drivers/hdf_core/adapter/uhdf/manager:hdf_core",
        "//drivers/hdf_core/adapter/uhdf/posix:hdf_posix_osal",
        "//drivers/hdf_core/adapter/uhdf/platform:hdf_platform"
     ]
    public_deps = [ "//third_party/bounds_checking_function:libsec_shared" ]

    cflags = [
        "-Wall",
        "-Wextra",
        "-Werror",
        "-Wno-format",
        "-Wno-format-extra-args",
        "-lhdf_platform"
    ]
}
  • 修改applications/sample/taget_board/app/BUILD.gn,加入编译

    其中 app是applications的组件targets名(component),features中gpio是目录,gpio_led是编译的目标/可执行程序

import("//build/lite/config/component/lite_component.gni")

lite_component("app") {
  features = [ 
    "gpio:gpio_led" ,  
   # "pwm:gpio_pwm", 
   # "adc:gpio_adc",
   # "uart:gpio_uart",
   # "spi:gpio_spi",
   # "i2c:gpio_i2c",
   # "sdio:gpio_sdio",
   # "rtc:gpio_rtc",
    ]
}

1.5 添加应用程序组件

OpenHarmony编译框架中编译用户程序和内核一样,需要配置成组件才能加入编译。

  • ①修改文件build/lite/components/communication.json

添加组件目标板如taget_board的配置(放在最前面),如下所示为communication.json文件片段,"##start##“和”##end##“之间为新增配置(”##start##“和”##end##"仅用来标识位置,添加完配置后删除这两行):

{
  "components": [
############### start ###################  
    {
      "component": "taget_board",
      "description": "taget_board samples.",
      "optional": "true",
      "dirs": [
        "applications/sample/taget_board/app"
      ],
      "targets": [
        "//applications/sample/taget_board/app:app"
      ],
      "rom": "",
      "ram": "",
      "output": [],
      "adapted_kernel": [ "linux" ],
      "features": [],
      "deps": {
        "components": [],
        "third_party": []
      }
    },
############## end ######################
  {
  "component": "dsoftbus",
  "description": "dsoftbus open sources software",
  "optional": "true",
  "dirs": [
    "foundation/communication/dsoftbus"
  ],
  • ②修改vendor/xx/xx/config.json文件

    如下所示代码片段为applications子系统配置,新增taget_board组件的条目,让编写的应用程序加入编译。

{
    "product_name": "fx6evb",
      ·········
    "subsystems": [
      {
        "subsystem": "kernel",
        "components": [
          { "component": "linux", "features":[] }
        ]
      },
      ··········
############### start ################### 
      {
        "subsystem": "applications",
        "components": [
          { "component":"taget_board", "features":[] }
        ]
      }
############## end ######################
    ],
    "third_party_dir": "//third_party",
    "product_adapter_dir": "//vendor/openvalley/fx6evb/hals"
  }

1.6 编译gpio程序

只编译应用程序可以使用build.sh脚本编译,操作如下:

#在工程目录执行以下指令:
 ./build.sh --product-name taget_board

在工程根目录out/fx6evb/fx6evb/bin下可以看到应用程序的可执行文件,hb build -f全量编译时可执行文件会打包到文件镜像中/bin/下,更新文件系统即可更新对应程序;当然单独编译应用程序时,也可以单独拷贝编译出的可执行程序到开发板中执行。

gpio_led的可执行文件在:out/taget_board/taget_board/bin/gpio_led

1.7 运行应用程序

拷贝可执行文件到系统中,在启动界面,进入bin目录

cd  bin

进入bin目录后可以看到gpio_led文件,通过以下命令运行程序。

./gpio_led
输入 202

界面打印“GPIO_202 亮 灭log”,Led在闪烁,程序运行成功。

2 ADC

参考1:ADC (openharmony.cn)

参考2:OpenHarmony:全流程讲解如何编写ADC平台驱动以及应用程序

2.2 直接使用指令查看

// 可以接滑动变阻器,实测数值是0-4096则为12bit精度,注意输入限制,如0-3.3v
cat /sys/bus/iio/devices/iio:device0/in_voltage3_raw
cat /sys/bus/iio/devices/iio:device0/in_voltage4_raw

可以输出数值,taget_board的ADC0 一共10个通道,说明Linux内核驱动都正常

2.2 HDF框架下使用ADC

参考1:ADC平台驱动开发 (openharmony.cn)

参考2:ADC平台驱动使用 (openharmony.cn)

第一步,ADC适配层驱动程序
  • 实例化HdfDriverEntry结构体成员
  • 调用HDF_INIT将HdfDriverEntry实例化对象注册到HDF框架中。
ADC平台驱动在//drivers/hdf_core/adapter/khdf/linux/platform/adc/adc_iio_adapter.c已实现,用户不必编写HDF驱动。
第二步,配置ADC hcs文件
  • 在device_info.hcs文件中添加deviceNode描述。
  • 添加adc_config.hcs器件属性文件。

device_info.hcs中的配置:

            device_adc :: device {
                device0 :: deviceNode {
                    policy = 2;
                    priority = 60;
                    permission = 0644;
                    moduleName = "HDF_PLATFORM_ADC_MANAGER";
                    serviceName = "HDF_PLATFORM_ADC_MANAGER";
                }
                device1 :: deviceNode {
                    policy = 0;
                    priority = 65;
                    permission = 0644;
                    moduleName = "linux_adc_adapter";
                    deviceMatchAttr = "linux_adc_adapter";
                }

            }

adc_config.hcs中的配置

root {
    platform {
        adc_config {
            match_attr = "linux_adc_adapter";       // 与device_info.hcs的deviceMatchAttr的值一致
            template adc_device {   // 必须与//drivers/hdf_core/adapter/khdf/linux/platform/adc/adc_iio_adapter.c的配置树定义保持一致
                deviceNum = 0;                      // 设备号标识
                channelNum = 10;                     // ADC通道数量
                driver_channel0_name = "";          // 通道0在linux文件系统路径
                driver_channel1_name = "";          // 通道1在linux文件系统路径
                driver_channel2_name = "";          // 通道2在linux文件系统路径
                driver_channel3_name = "";          // 通道3在linux文件系统路径
                driver_channel4_name = "";          // 通道4在linux文件系统路径
                driver_channel5_name = "";          // 通道5在linux文件系统路径
                driver_channel6_name = "";          // 通道6在linux文件系统路径
                driver_channel7_name = "";          // 通道7在linux文件系统路径
                driver_channel8_name = "";          // 通道8在linux文件系统路径
                driver_channel9_name = "";          // 通道9在linux文件系统路径
                scanMode = 0;                       // 扫描模式(必要,但无意义)
                rate = 1000;                        // 转换速率(必要,但无意义)
            }
            device_0 :: adc_device {
                deviceNum = 0;
                channelNum = 10;
                driver_channel0_name = "/sys/bus/iio/devices/iio:device0/in_voltage0_raw";
                driver_channel1_name = "/sys/bus/iio/devices/iio:device0/in_voltage1_raw";
                driver_channel2_name = "/sys/bus/iio/devices/iio:device0/in_voltage2_raw";
                driver_channel3_name = "/sys/bus/iio/devices/iio:device0/in_voltage3_raw";
                driver_channel4_name = "/sys/bus/iio/devices/iio:device0/in_voltage4_raw";
                driver_channel5_name = "/sys/bus/iio/devices/iio:device0/in_voltage5_raw";
                driver_channel6_name = "/sys/bus/iio/devices/iio:device0/in_voltage6_raw";
                driver_channel7_name = "/sys/bus/iio/devices/iio:device0/in_voltage7_raw";
                driver_channel8_name = "/sys/bus/iio/devices/iio:device0/in_voltage8_raw";
                driver_channel9_name = "/sys/bus/iio/devices/iio:device0/in_voltage9_raw";
            }
        }
    }
}
第三步,实例化核心层接口函数
  • 初始化AdcDevice成员。
  • 实例化AdcDevice成员AdcMethod
ADC核心层驱动已在drivers/hdf_core/framework/support/platform/src/adc/adc_core.c中实现,用户不需要编写
第四步,编译ADC HDF到内核
  • vendor下的hdf.hcs中 #include “device/adc/adc_config_linux.hcs”
  • 打开内核宏配置, //kernel/linux/config/下的配置文件,CONFIG_DRIVERS_HDF_PLATFORM_ADC=y
  • 编译到内核,烧录内核镜像
第五步,编写ADC应用程序

applications/sample/taget_board/app/adc/src/hdf_adc_test.c

#include "adc_if.h"          /* ADC标准接口头文件 */
#include "hdf_log.h"         /* 标准日志打印头文件 */
#include <stdio.h>
#include <unistd.h>

/* 设备号0,通道号1 */ 
#define ADC_DEVICE_NUM  0
#define ADC_CHANNEL_NUM 1
#define ADC_TEST_NUM    30

/* ADC例程总入口 */ 
static int32_t TestCaseAdc(void)
{
    int32_t i;
    int32_t ret;
    int32_t num = 0;
    DevHandle adcHandle = NULL;
    uint32_t readBuf[ADC_TEST_NUM] = {0};

    /* 打开ADC设备 */ 

    printf("please input ADC0 Channel num: ");
    ret =  scanf("%u", &num);

    adcHandle = AdcOpen(ADC_DEVICE_NUM);
    if (adcHandle == NULL) {
        printf("%s: Open ADC%u fail!", __func__, num);
        return -1;
    }

    /* 连续进行30次AD转换并读取转换结果 */ 
    for (i = 0; i < ADC_TEST_NUM; i++) {
        ret = AdcRead(adcHandle, num, &readBuf[i]);
        if (ret != HDF_SUCCESS) {
            printf("%s: ADC read fail!:%d", __func__, ret);
            AdcClose(adcHandle);
            return -1;
        }
        else{
            printf("\treadBuf[%d]=%d\r\n",i, readBuf[i]);
        }
    }
    printf("%s: ADC read successful!\r\n", __func__);

    /* 访问完毕关闭ADC设备 */ 
    AdcClose(adcHandle);

    return 0;
}

int main(void)
{

    TestCaseAdc();
    return 0;
}

编译配置,applications/sample/taget_board/app/adc/BUILD.gn,参考gpio,只需修改如下内容

executable("gpio33_adc") {
    defines = [ "__USER__" ]
    
    output_name = "gpio33_adc"
    sources = [
        "src/hdf_adc_test.c"
    ]

编译后,gpio33_adc可执行文件在bin目录下,烧录文件系统到开发板,

第六步,ADC读取端口数值
运行开发板,进入系统根目录,运行程序后,可得出读取的数值
# ./bin/gpio33_adc
please input ADC0 Channel num: 4 
        readBuf[0]=4095
        ····
        readBuf[29]=4095
TestCaseAdc: ADC read successful!

3 PWM

3.1 预测试:

sysfs操作,测试内核是否正确加载pwm:

参考:嵌入式linux驱动开发——Linux PWM驱动

使用sysfs接口对PWM驱动进行功能调试,主要调试命令示例如下。
(1)查看PWM控制器节点
ls /sys/class/pwm/pwmchip0

(2)打开指定PWM通道信号
echo n > /sys/class/pwm/pwmchip0/export //n为通道编号

(3)设置PWM信号周期
echo pvalue > /sys/class/pwm/pwmchip0/pwm0/period //pvalue为周期值

(4)设置PWM信号占空比
echo dvalue > /sys/class/pwm/pwmchip0/pwm0/duty_cycle //dvalue为有效电平宽度值

(5)使能某个PWM通道信号
echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable

(6)禁止某个PWM通道信号

echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable

#查看某个pwm设备的通道数:如taget_board-h设备树中的pwm1: xpwm@a0389000
# cat /sys/devices/platform/soc/a0389000.xpwm/pwm/pwmchip2/npwm                         <
6

3.2 HDF测试:

参考1:PWM驱动开发指导 (openharmony.cn)

参考2:PWM平台驱动使用 (openharmony.cn)

PWM默认关闭的,修改设备树文件,打开全部通道,便于测试:

注意有冲突:

//pwm1与qspi0冲突
pinctrl-single a0202060.pinctrl: pin PIN126 already requested by a0389000.xpwm; cannot claim for a0993000.qspi

//pwm1与spi0冲突
pinctrl-single a0202060.pinctrl: pin PIN126 already requested by a0389000.xpwm; cannot claim for a0118000.spi

//pwm2与spi1冲突
pinctrl-single a0202060.pinctrl: pin PIN135 already requested by a038a000.xpwm; cannot claim for a0119000.spi
//修改前,设备树中只有如下两配置,
&pwm0 {
	status = "disabled";
};
&pwm3 {
	status = "disabled";
};
//修改后,添加pwm0、3都开打
&pwm0 {
	status = "okay";
};

&pwm3 {
	status = "okay";
};
  • 第二步,编写hcs,适配层、核心层驱动已经编写。
// 配置文件见 pwm_config_linux.hcs 、device_info.hcs
// 驱动程序见 drivers/hdf_core/adapter/khdf/linux/platform/pwm/pwm_adapter.c
  • 第三步,调用HDF用户层接口,编写测试案例
// 用户接口见 drivers/hdf_core/framework/include/platform/pwm_if.h
            drivers/hdf_core/framework/support/platform/src/pwm/pwm_if.c
// 测试案例见 hdf_pwm_test.c
  • 第四步,烧录内核镜像、设备树、文件系统
#运行: ./bin/gpio_pwm 【按提示输入0、3】
  • 第五步,运行调试
# ./bin/gpio188_pwm
please input pwm num: 0
[ 1267.560709] [E/pwm_core] PwmDeviceSetConfig: do not need to set config
[ 1267.567484] [E/pwm_core] PwmDeviceSetConfig: do not need to set config
[ 1267.574175] [E/pwm_core] PwmDeviceSetConfig: do not need to set config
[ 1267.580863] [I/pwm_adapter] HdfPwmSetConfig: set PwmConfig: number 0, period 10000, duty 5000, polarity 0, enable 1.
[ 1267.591500] [I/pwm_adapter] HdfPwmSetConfig: success.
will sleep for 3 seconds
[ 1277.596939] [I/pwm_adapter] HdfPwmSetConfig: set PwmConfig: number 0, period 10000, duty 5000, polarity 0, enable 0.
[ 1277.607615] [I/pwm_adapter] HdfPwmSetConfig: success.
# 可以看到底板上D1 LED有变暗

# ./bin/gpio188_pwm
please input pwm num: 1
# 可以看到底板上D2 LED有变暗
  • 思考:每个PWM设备都有不同的通道,那么在使用 handle = PwmOpen(num);时,num与pwm设备、通道号之间的关系是什么?

    pwm0:2channel 、 pwm1-3: 6 channel

    实测:PwmOpen(0) 为PWM0_0 PwmOpen(1)为PWM0_1,PwmOpen(2) 不是 PWM1_0,怎么使用 PWM(1-3)_(0-5) 呢?

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2024-9-19 14:58:20修改
收藏 1
回复
举报
回复
    相关推荐