测试适配的HDF驱动 原创
适配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
参考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:
使用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) 呢?