bh1726光线传感器简介 BH1726是一种数字光线传感器,由ROHM Semiconductor开发和制造。它被设计用于测量环境中的光照强度。该传感器采用CMOS图像传感器技术,并具有高精度和高灵敏度。 BH1726光线传感器可以检测可见光范围内的光照强度,并将其转换为数字输出。它可以测量的光照范围通常在0到100,000勒克斯(lux)之间,这使得它非常适用于室内和室外环境的光照测量。 该传感器具有内置的模数转换器(ADC),可以将光照强度转换为12位数字值。它还具有自动增益控制(AGC)功能,可以自动调整感应器的增益以适应不同光照条件下的测量。 BH1726传感器还具有低功耗特性,适用于移动设备和电池供电的应用。它采用了I2C接口,可以与微控制器或其他数字设备进行通信,并提供实时的光照强度数据。 总的来说,BH1726光线传感器是一种高精度、高灵敏度的数字传感器,适用于各种需要光照强度测量的应用,包括自动调光系统、室内照明控制、移动设备和环境监测等。 驱动编写 例程代码路径:ELF 1开发板资料包\03-例程源码\03-2 驱动例程源码\08_input子系统\bh1726 BH1726光线传感器采用了i2c接口,所以需要将其注册成i2c设备,来读取其内部寄存器的测量值,然后通过input子系统上报到用户空间,下面来看一下驱动的具体实现流程。 修改设备树 (一)查看原理图和引脚复用表格,确定光线传感器连接引脚。 (二)I2C2引脚复用,打开设备树文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts我们看到原来的设备树文件已经添加了pinctrl_i2c2子节点,而且选择的引脚与UART5_RX_DATA、UART5_TX_DATA一致,所以此处无需修改。 (三)添加设备节点 在arch/arm/boot/dts/imx6ull-elf1-emmc.dts文件中的i2c2节点下添加温湿度传感器子节点aht20: rohm@29{ compatible = "rohm,bh1726"; reg = <0x29>; status = "okay"; }; 添加后的效果如下: (四)重新编译内核和设备树 . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make dtbs elf@ubuntu:~/work/linux-imx-imx_4.1.15_2.0.0_ga$ make zImage 编译生成的设备树文件为imx6ull-elf1-emmc.dtb,内核文件为zImage,参考《01-0 ELF1、ELF1S开发板_快速启动手册_V1》4.4节单独更新设备树和内核。 编写bh1726.c驱动 (一)在驱动中要操作很多芯片相关的寄存器,所以需要先新建一个bh1726.h的头文件,用来定义相关寄存器值。 #ifndef __BH1726_DRIVER_H__ #define __BH1726_DRIVER_H__ /*************** Definitions ******************/ /* GENERAL */ #define BH1726_DRV_NAME "bh1726" #define DRIVER_VERSION "1.0" #define QCOM_SENSORS (0) 先是定义了设备名称和驱动版本,然后定义是不是使用QCOM框架,QCOM框架是用于Android系统,所以此处定义为0,表示不使用QCOM框架。 /*-----------------------DEBUG------------------------------*/ #define BH1726_DGB_SWITCH // debug switch #define BH1726_TAG "[ALS/PS]BH1726" #ifdef BH1726_DGB_SWITCH #define BH1726_DEBUG 1 #else #define BH1726_DEBUG 0 #endif #define BH1726_ERR(f, a...) do {printk(KERN_ERR BH1726_TAG "ERROR (%s(), %d):" f, __func__, __LINE__, ## a);} while (0) #define BH1726_WARNING(f, a...) do {printk(KERN_WARNING BH1726_TAG "(%s(), %d):" f, __func__, __LINE__, ## a);} while (0) #define BH1726_INFO(f, a...) do {printk(KERN_INFO BH1726_TAG "INFO (%s(), %d):" f, __func__, __LINE__, ## a);} while (0) #if BH1726_DEBUG #define BH1726_FUN() do {printk(KERN_INFO BH1726_TAG "(%s(), %d)\n", __func__, __LINE__);} while (0) #define BH1726_DBG(f, a...) do {printk(KERN_DEBUG BH1726_TAG "DEBUG (%s(), %d):" f, __func__, __LINE__, ## a);} while (0) #else #define BH1726_FUN() do {} while (0) #define BH1726_DBG(f, a...) do {} while (0) #endif /*-----------------------------------------------------*/ 上面是DEBUG相关的宏定义,用于调试阶段打印一些相关信息。 /************ define register for IC ************/ /* BH1726 REGSTER */ #define REG_INTERRUPT_RST (0xE1) #define REG_SOFT_RST (0xE4) #define REG_CONTROL (0x80) #define REG_TIMING (0x81) #define REG_INTERRUPT (0x82) #define REG_THRED_LOW (0x83) #define REG_THRED_HIGH (0x85) #define REG_GAIN (0x87) #define REG_PART_ID (0x92) #define REG_DATA0 (0x94) #define REG_DATA1 (0x96) #define REG_WAIT (0x98) #define PART_ID_VALUE (0x72) 定义寄存器地址,此部分可以看BH1726数据手册的Register MAP章节,需要注意的是数据手册上的寄存器地址只写了寄存器地址的低4位,所以定义时要在这个基础上加上0x80。 /************ define parameter for register ************/ #define ADC_EN_ON (1 << 1) #define ADC_EN_OFF (0 << 1) #define POWER_ON (1 << 0) #define POWER_OFF (0 << 0) #define BH1726_ENABLE (ADC_EN_ON | POWER_ON) #define BH1726_DISABLE (ADC_EN_OFF | POWER_OFF) #define CLR_ADC_POWER (0xFC) #define CLR_GAIN0 (0xF3) #define CLR_GAIN1 (0xFC) #define ALS_VALID_HIGH (1 << 4) ////////////////// #define DATA0_VALUE_40000 (40000) #define DATA0_VALUE_10000 (10000) #define DATA0_VALUE_1400 (1400) #define DATA0_VALUE_700 (700) #define DATA0_VALUE_400 (400) #define DATA0_VALUE_120 (120) #define DATA0_VALUE_40 (40)/* Gain (0x87) */ #define DATA0_GAIN_X1 (0x00 << 2) #define DATA1_GAIN_X1 (0x00) #define DATA0_GAIN_X2 (0x01 << 2) #define DATA1_GAIN_X2 (0x01) #define DATA0_GAIN_X64 (0x02 << 2) #define DATA1_GAIN_X64 (0x02) #define DATA0_GAIN_X128 (0x03 << 2) #define DATA1_GAIN_X128 (0x03) #define JUDGE_FIXED_COEF (1000) #define MAX_OUTRANGE (65535) #define TIME_COEF (102600) // 102.6ms * 1000, unit is us #define TINT_TYP (28) // 2.8 * 10 #define TINT_DIV (10) // #define INTEGRAL_COEF (964) #define TIME_BASE_CALC (256L) // #define USE_TIME_EXECUTE (714) #define MEASUREMENTS_WIDTH (10) // rate down, unit is % #define MAX_MEASURE_VAL (760000000UL) #define IC_INTERNAL_TIME ((TINT_TYP * USE_TIME_EXECUTE) / TINT_DIV) /* Time(0x81) */ /* measure time = (2.8*964*(256-X)+2.8*714)/1000, X is the value of 0x81 register */ /* reg_value = 256-time*1000/2.8/964 */ #define MEASURE_25MS (25) #define MEASURE_50MS (50) #define MEASURE_100MS (100) #define MEASURE_135MS (135) #define MEASURE_300MS (300) #define MEASURE_400MS (400) #define MEASURE_700MS (691) #define MEASURE_MAX_TIME (MEASURE_700MS) /* POWER SUPPLY VOLTAGE RANGE */ #define BH1726_VDD_MIN_UV (2000000) #define BH1726_VDD_MAX_UV (3300000) #define BH1726_VIO_MIN_UV (1750000) #define BH1726_VIO_MAX_UV (1950000) #define ALS_SET_MIN_DELAY_TIME (100) 寄存器参数相关定义,从Register MAP表中可以看到ADC_EN由寄存器0x80的第1位来控制,下表可以得知0表示ADC测量停止,1表示ADC测量开始。所以定义ADC_EN_ON 为(1 << 1),定义ADC_EN_OFF为 (0 << 1)。其它定义同理,此处就不一一讲解了。 最后是相关结构体的定义,如下: /*************** Structs ******************/ typedef struct { struct i2c_client *client; struct regulator *vdd; struct regulator *vio; struct mutex update_lock; struct delayed_work als_dwork; /* for ALS polling */ struct input_dev *input_dev_als; #if QCOM_SENSORS struct sensors_classdev als_cdev; #endif unsigned int enable; unsigned int als_time; /* control flag from HAL */ unsigned int enable_als_sensor; unsigned int als_suspend_flag; /* ALS parameters */ unsigned int als_data; /* to store ALS data */ unsigned int als_level; unsigned int gain; unsigned int als_poll_delay; unsigned int dev_id; }BH1726_ALS_DATA; /* structure to read data value from sensor */ typedef struct { unsigned int data0; unsigned int data1; } READ_DATA_ARG; typedef struct { unsigned long long data0; unsigned long long data1; unsigned char gain_data0; unsigned char gain_data1; unsigned long als_time; unsigned int als_data0; unsigned int als_data1; }CALC_DATA; #endif (二)bh1726.c文件编写 (1)头文件引用 #include //#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_OF #include #endif #include "linux/bh1726.h" (2)创建相关宏定义和变量 /*************** Global Data ******************/ //////////////////////////////////////////////////////////////// #define COEFFICIENT_SIZE (4) #define GAIN_FACTOR (1) //(128), this value is related with gain when make coefficient const int judge_coefficient[COEFFICIENT_SIZE] = {157, 261, 1121, 4910}; const int data0_coefficient[COEFFICIENT_SIZE] = {159, 403, 256, 221}; const int data1_coefficient[COEFFICIENT_SIZE] = {941, -615,-84, -45}; /* gain table */ #define GAIN_FACTOR_SIZE (16) static const struct GAIN_TABLE { unsigned char data0; unsigned char data1; } gain_table[GAIN_FACTOR_SIZE] = { { 1, 1}, /* 0 */ { 1, 2}, /* 1 */ { 1, 64}, /* 2 */ { 1, 128}, /* 3 */ { 2, 1}, /* 4 */ { 2, 2}, /* 5 */ { 2, 64}, /* 6 */ { 2, 128}, /* 7 */ { 64, 1}, /* 8 */ { 64, 2}, /* 9 */ { 64, 64}, /* 10 */ { 64, 128}, /* 11 */ {128, 1}, /* 12 */ {128, 2}, /* 13 */ {128, 64}, /* 14 */ {128, 128} /* 15 */ }; (3)驱动模块的入口和出口 module_init(bh1726_init); module_exit(bh1726_exit); (4)bh1726_init和bh1726_exit实现 static int __init bh1726_init(void) { return i2c_add_driver(&bh1726_driver); } static void __exit bh1726_exit(void) { i2c_del_driver(&bh1726_driver); } 在入口函数中调用了i2c_add_driver函数,来注册I2C总线驱动程序。在出口函数中调用了i2c_del_driver函数,来注销I2C驱动程序。 (5)i2c_driver类型结构体定义 static struct i2c_driver bh1726_driver = { .driver = { .name = BH1726_DRV_NAME, .owner = THIS_MODULE, .of_match_table = bh1726_match_table, }, // .suspend = bh1726_suspend, // .resume = bh1726_resume, .probe = bh1726_probe, .remove = bh1726_remove, .id_table = bh1726_id, }; (6)bh1726_match_table实现,用来与设备树中的compatible匹配 static struct of_device_id bh1726_match_table[] = { { .compatible = "rohm,bh1726",}, { }, }; (7)remove函数实现,执行bh1726设备的清理操作 static int bh1726_remove(struct i2c_client *client) { BH1726_ALS_DATA *als = i2c_get_clientdata(client); #if QCOM_SENSORS sensors_classdev_unregister(&als->als_cdev); #endif cancel_delayed_work(&als->als_dwork); input_unregister_device(als->input_dev_als); input_free_device(als->input_dev_als); sysfs_remove_group(&client->dev.kobj, &bh1726_attr_group); /* Power down the device */ bh1726_enable_als_sensor(client, POWER_OFF); kfree(als); return 0; } (8)probe函数实现,此处简略描述I2C设备和input子系统注册的过程 ①I2C设备注册: struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); BH1726_ALS_DATA * als; int err = 0; int dev_id; BH1726_INFO("%s probe started.\n", __func__); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) { err = -EIO; goto exit; } als = kzalloc(sizeof(BH1726_ALS_DATA), GFP_KERNEL); if (!als) { err = -ENOMEM; goto exit; } als->client = client; i2c_set_clientdata(client, als); 先是调用to_i2c_adapter函数将设备与可用的I2C适配器进行关联和连接。 然后调用i2c_check_functionality函数检查系统中I2C功能的支持情况和可用性。以确保所需的功能可用,并确定哪些功能可以被使用。 i2c_check_functionality函数原型如下: int i2c_check_functionality(int adapter_num, unsigned long supported_func) adapter_num是指要检查的I2C适配器的编号或标识符。不同的系统可能有不同的编号方案,通常从0开始递增。 supported_func是一个表示所需功能的标志位掩码。这些标志位表示可能的I2C功能,如I2C_M_TEN(10位地址模式支持)、I2C_FUNC_I2C(基本I2C功能支持)等。 函数返回一个整数值,表示I2C功能的支持情况。通常,返回值为0表示所需的功能不受支持或不可用,非零值表示所需的功能可用。 最后调用i2c_set_clientdata函数,用于在I2C设备驱动程序中设置与设备关联的私有数据。 i2c_set_clientdata函数原型如下: void i2c_set_clientdata(struct i2c_client *client, void *data) client是指向struct i2c_client结构的指针,表示与I2C设备相关联的客户端信息。 data是要设置的私有数据指针。可以是任何类型的指针,用于存储与设备相关的私有数据。 通过调用i2c_set_clientdata函数,可以将私有数据指针存储在struct i2c_client结构的driver_data字段中。在驱动程序的其他函数中,可以使用i2c_get_clientdata函数来检索并使用该私有数据。 初始化延时工作队列: INIT_DELAYED_WORK(&als->als_dwork, bh1726_als_polling_work_handler); INIT_DELAYED_WORK是一个宏,用于初始化延迟工作队列。延迟工作可以在一定时间后执行,可以用于处理定时任务、延迟处理或后台处理等场景。本驱动中用于定时上报采集的光线值。 INIT_DELAYED_WORK宏的典型定义如下: INIT_DELAYED_WORK(struct delayed_work *dwork, work_func_t func); dwork是指向struct delayed_work类型的指针,表示要初始化的延迟工作。 func是一个指向工作函数(work function)的指针,表示延迟工作的处理函数。工作函数的定义和形式根据具体的需求和实现而异。 ②初始化BH1726: /* Initialize the bh1726 chip */ err = bh1726_init_client(als); if (err) goto exit_kfree; bh1726_init_client()函数的实现如下: /*************** Initialze Functions ******************/ static int bh1726_init_client(BH1726_ALS_DATA *als) { int result = 0; /* execute software reset */ result = bh1726_driver_reset(als->client); if (result != 0) { return (result); } result = bh1726_driver_set_measure_time(als, MEASURE_100MS); if (result == 0) { bh1726_driver_set_data0_gain(als, DATA0_GAIN_X128 ); //set data0 gain bh1726_driver_set_data1_gain(als, DATA1_GAIN_X128 ); //set data1 gain } else{ BH1726_ERR(" I2c write failed! \n"); } return (result); } 在初始化函数中主要进行了软件复位,设置测量时间和设置增益。 ③注册input设备: /* Register to Input Device */ als->input_dev_als = input_allocate_device(); if (!als->input_dev_als) { err = -ENOMEM; BH1726_DBG("%s: Failed to allocate input device als\n", __func__); goto exit_kfree; } input_set_drvdata(als->input_dev_als, als); set_bit(EV_ABS, als->input_dev_als->evbit); input_set_abs_params(als->input_dev_als, ABS_MISC, 0, ROHM_ALS_MAX, 0, 0); als->input_dev_als->name = "lightsensor"; als->input_dev_als->id.bustype = BUS_I2C; als->input_dev_als->dev.parent = &als->client->dev; err = input_register_device(als->input_dev_als); if (err) { err = -ENOMEM; BH1726_DBG("%s:register input device als fail: %s\n", __func__, als->input_dev_als->name); goto exit_free_dev_als; } ④创建sysfs文件系统中的属性组: /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &bh1726_attr_group); if (err) { BH1726_DBG("%s sysfs_create_groupX\n", __func__); goto exit_unregister_dev_als; } sysfs是一种用于向用户空间公开设备和驱动程序信息的虚拟文件系统。属性组是一组相关属性的集合,可以在sysfs中表示设备或驱动程序的状态、配置和控制信息。 以下是sysfs_create_group函数的典型定义: int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); kobj是指向struct kobject类型的指针,表示在sysfs中创建属性组的kobject对象。kobject表示设备或驱动程序在内核中的抽象对象,是sysfs文件系统的一部分。 grp是指向struct attribute_group类型的指针,表示要创建的属性组。attribute_group结构定义了属性组的名称和包含的属性列表。 sysfs_create_group函数的作用是在sysfs文件系统中的给定kobject对象下创建一个新的属性组。该属性组将包含在struct attribute_group结构中定义的一组属性。通过创建属性组,可以将一组相关的属性组织在一起,并使其在sysfs中可用。 (9)bh1726_attr_group结构体定义及绑定 static DEVICE_ATTR(als_poll_delay, 0660, bh1726_show_als_poll_delay, bh1726_store_als_poll_delay); static DEVICE_ATTR(enable_als_sensor, 0660, bh1726_show_enable_als_sensor, bh1726_store_enable_als_sensor); static DEVICE_ATTR(als_data, S_IRUGO, bh1726_show_als_data, NULL); static DEVICE_ATTR(type, S_IRUGO, bh1726_show_type, NULL); static DEVICE_ATTR(reg, 0660, bh1726_show_allreg, bh1726_store_reg); static struct attribute *bh1726_attributes[] = { &dev_attr_enable_als_sensor.attr, &dev_attr_als_poll_delay.attr, &dev_attr_als_data.attr, &dev_attr_type.attr, &dev_attr_reg.attr, NULL }; static const struct attribute_group bh1726_attr_group = { .attrs = bh1726_attributes, }; DEVICE_ATTR是一个宏(macro),用于定义设备属性,设备属性是一种用于在sysfs文件系统中表示设备状态、配置和控制信息的机制。通过在设备的kobject对象下创建属性文件,可以提供对设备属性的访问和操作。具体体现就是,加载驱动后在文件系统的/sys/bus/i2c/devices/1-0029路径下能访问这些属性(als_poll_delay、enable_als_sensor、als_data、type、reg),读取属性时会调用后边的show类型函数指针( bh1726_show_als_poll_delay,bh1726_show_enable_als_sensor,bh1726_show_als_data,bh1726_show_typ,bh1726_show_allreg),写入属性时候调用后边的store类型函数指针(bh1726_store_als_poll_delay,bh1726_store_enable_als_sensor、,bh1726_store_reg)。 以下是DEVICE_ATTR宏的典型定义: DEVICE_ATTR(name, mode, show, store) name是设备属性的名称,以字符串形式表示。 mode是设备属性的访问权限和类型,使用类似于UNIX文件权限的形式指定(如S_IRUGO表示只读权限)。具体的权限常量定义可以在头文件中找到。 show是一个指向show函数的指针,用于读取设备属性的值。show函数的定义和形式根据具体的需求和实现而异。 store是一个指向store函数的指针,用于写入设备属性的值。store函数的定义和形式根据具体的需求和实现而异。 DEVICE_ATTR宏的作用是创建一个名为name的设备属性,其中包含读取和写入属性值的处理函数。通过定义设备属性,可以在sysfs中为设备提供对应的属性文件,从而实现对设备属性的读写操作。 (10)enable_als_sensor使能属性写入调用 static ssize_t bh1726_store_enable_als_sensor(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); BH1726_ALS_DATA *als = i2c_get_clientdata(client); /*unsigned long val = simple_strtoul(buf, NULL, 10);*/ unsigned long val; int result = 0; unsigned char power_set; unsigned char write_data; result = kstrtoul(buf, 10, &val); if (result) return result; if ((val != POWER_ON) && (val != POWER_OFF)) { BH1726_DBG("%s: enable als sensor=%ld\n", __func__, val); return count; } bh1726_enable_als_sensor(client, val); return count; } bh1726_store_enable_als_sensor函数又会掉用bh1726_enable_als_sensor(client, val);接下来看一下bh1726_enable_als_sensor(client, val)函数的实现: static int bh1726_enable_als_sensor(struct i2c_client *client, int val) { BH1726_ALS_DATA *als = i2c_get_clientdata(client); int result = 0; unsigned char power_set; unsigned char write_data; if (!als || ( val != POWER_ON && val != POWER_OFF)) { BH1726_ERR(" Parameter error \n"); return EINVAL; } BH1726_WARNING(" val=%d\n", val); mutex_lock(&als->update_lock); /* read control1 register */ result = i2c_smbus_read_byte_data(als->client, REG_CONTROL); if (result < 0) { mutex_unlock(&als->update_lock); /* i2c communication error */ return (result); } if (val == POWER_ON) { power_set = BH1726_ENABLE; if (als->enable_als_sensor == POWER_OFF) { als->enable_als_sensor = POWER_ON; //mask adc_en and power write_data = ((unsigned char)(result & CLR_ADC_POWER)) | power_set; result = i2c_smbus_write_byte_data(als->client, REG_CONTROL, write_data); if (result < 0) { // i2c communication error BH1726_ERR(" I2C write error \n"); mutex_unlock(&als->update_lock); return (result); } } cancel_delayed_work(&als->als_dwork); schedule_delayed_work(&als->als_dwork, msecs_to_jiffies(als->als_poll_delay)); } else { power_set = BH1726_DISABLE; if (als->enable_als_sensor == POWER_ON) { als->enable_als_sensor = POWER_OFF; //mask adc_en and power write_data = ((unsigned char)(result & CLR_ADC_POWER)) | power_set; result = i2c_smbus_write_byte_data(als->client, REG_CONTROL, write_data); if (result < 0) { // i2c communication error BH1726_ERR(" I2C write error \n"); mutex_unlock(&als->update_lock); return (result); } } cancel_delayed_work(&als->als_dwork); } mutex_unlock(&als->update_lock); return result; } 该函数中主要判断val 值是否等于等于POWER_ON,如果等于POWER_ON就会开启延时任务队列中的服务函数,延时队列在probe函数中已经进行了初始化,服务函数为:bh1726_als_polling_work_handler,接下来看一下该函数的实现: /* ALS polling routine */ static void bh1726_als_polling_work_handler(struct work_struct *work) { BH1726_ALS_DATA * als = container_of(work, BH1726_ALS_DATA, als_dwork.work); struct i2c_client *client = als->client; int tmp = 0; //get valid from REG_CONTROL(0x80) tmp = i2c_smbus_read_byte_data(client, REG_CONTROL); if (tmp < 0) { BH1726_ERR("Read data from IC error.\n"); return ; } //BH1726_WARNING("Data valid REG_MODECONTROL2(0x%x) = 0x%x\n", REG_MODECONTROL2, result); if ((tmp & ALS_VALID_HIGH) == 0)//not valid { BH1726_WARNING("Data Not valid. But it does not matter, please ignore it.\n"); } else { READ_DATA_ARG data = {0}; unsigned char gain; unsigned short time_reg; //read data0 tmp = i2c_smbus_read_word_data(client, REG_DATA0); if (tmp < 0){ BH1726_DBG("%s: i2c read data0 fail.\n", __func__); return ; } data.data0 = (unsigned int)tmp; //read data1 tmp = i2c_smbus_read_word_data(client, REG_DATA1); if (tmp < 0){ BH1726_DBG("%s: i2c read data1 fail.\n", __func__); return ; } data.data1 = (unsigned int)tmp; //read gain tmp = i2c_smbus_read_byte_data(client, REG_GAIN); if (tmp < 0){ BH1726_DBG("%s: i2c read gain fail.\n", __func__); return ; } gain = (unsigned char)tmp; //read time_reg tmp = i2c_smbus_read_byte_data(client, REG_TIMING); if (tmp < 0){ BH1726_DBG("%s: i2c read time fail.\n", __func__); return ; } time_reg = (unsigned short)tmp; als->als_data = bh1726_calculate_light(data, gain, time_reg); //measure time change auto according to data0 bh1726_auto_change_measure_time(als, data); if (als->als_data == 0){ als->als_data++; } als->als_level = bh1726_als_data_to_level(als->als_data); } input_report_abs(als->input_dev_als, ABS_MISC, als->als_level); input_sync(als->input_dev_als); schedule_delayed_work(&als->als_dwork, msecs_to_jiffies(als->als_poll_delay)); } 可以看到此函数会读取光线传感器中的寄存器值,并转换成lux值,通过input_report_abs和input_sync函数将数据上报给用户空间,然后再次开启延时任务调度函数,实现了定时上报数据。由于驱动完整源码过长,请到资料包中查看,路径:ELF 1开发板资料包\03-例程源码\03-2 驱动例程源码\08_input子系统\bh1726。 编译 复制7.9.3驱动中的Makefile文件,将其中的my_keyboard.o修改为bh1726.0,效果如下: . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/test/08_input子系统/bh1726$ make 将编译生成的bh726.ko模块拷贝到开发板。 编写测试源码bh726_app.c #include #include #include #include #include static struct input_event ev_data; static int fd=-1; #define ALS_DEV "/dev/input/event2" static int init_device(char *DEV) { system("echo 1 > /sys/bus/i2c/devices/1-0029/enable_als_sensor"); if((fd = open(DEV, O_RDONLY)) < 0) { printf("Error open %s\n\n", DEV); return -1; } return fd; } static int als_test() { if(init_device(ALS_DEV) < 0) { printf("init_device faild\n"); return -1; } while(1) { read(fd, &ev_data, sizeof(ev_data)); if (ev_data.type == EV_ABS) { printf(" ev_data.type = %d,ev_data.code = %d, ev_data.value = %d\n\n",ev_data.type,ev_data.code, ev_data.value); } } return 0; } int main(void) { als_test(); return 0; } 编译应用 . /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi elf@ubuntu:~/work/test/08_input子系统/bh1726_app$ $CC bh1726_app.c -o bh172\6_app 可以看到测试源码中先是使能了enable_als_sensor属性,然后就开始循环读取上报的input事件了。 测试 加载驱动模块: root@ELF1:~# insmod bh1726.ko [ALS/PS]BH1726INFO (bh1726_probe(), 1003):bh1726_probe probe started. [ALS/PS]BH1726INFO (bh1726_probe(), 1028):enable = 0 [ALS/PS]BH1726(bh1726_driver_set_measure_time(), 173): time_value=100, time_reg=219, als->als_time=100 [ALS/PS]BH1726(bh1726_driver_set_data0_gain(), 816) [ALS/PS]BH1726(bh1726_driver_set_data1_gain(), 847) input: lightsensor as /devices/platform/soc/2100000.aips-bus/21a4000.i2c/i2c-1/1-0029/input/input3 [ALS/PS]BH1726INFO (bh1726_probe(), 1098):bh1726 probe success! 查看event事件号: root@ELF1:~# cat /proc/bus/input/devices I: Bus=0019 Vendor=0000 Product=0000 Version=0000 N: Name="20cc000.snvs:snvs-powerkey" P: Phys=snvs-pwrkey/input0 S: Sysfs=/devices/platform/soc/2000000.aips-bus/20cc000.snvs/20cc000.snvs:snvs-powerkey/input/input0 U: Uniq= H: Handlers=kbd event0 B: PROP=0 B: EV=3 B: KEY=100000 0 0 0 I: Bus=0019 Vendor=0000 Product=0000 Version=0000 N: Name="iMX6UL TouchScreen Controller" P: Phys= S: Sysfs=/devices/platform/soc/2000000.aips-bus/2040000.tsc/input/input1 U: Uniq= H: Handlers=mouse0 event1 B: PROP=0 B: EV=b B: KEY=400 0 0 0 0 0 0 0 0 0 0 B: ABS=3 I: Bus=0018 Vendor=0000 Product=0000 Version=0000 N: Name="lightsensor" P: Phys= S: Sysfs=/devices/platform/soc/2100000.aips-bus/21a4000.i2c/i2c-1/1-0029/input/input2 U: Uniq= H: Handlers=event2 B: PROP=0 B: EV=9 B: ABS=100 0 如果与用于程序中不一致,需要更改应用测试程序中的ALS_DEV宏定义。运行测试应用程序,可以看到一直在上报光线值: root@ELF1:~# ./bh1726_app [ALS/PS]BH1726(bh1726_enable_als_sensor(), 447): val=1 [ALS/PS]BH1726(bh1726_calculate_light(), 257):Data0 is 0xFFFF, return max lux 65535. [ALS/PS]BH1726(bh1726_driver_set_measure_time(), 173): time_value=25, time_reg=247, als->als_time=25 ev_data.type = 3,ev_data.code = 40, ev_data.value = 65535 ev_data.type = 3,ev_data.code = 40, ev_data.value = 342 ev_data.type = 3,ev_data.code = 40, ev_data.value = 343 ev_data.type = 3,ev_data.code = 40, ev_data.value = 346 ev_data.type = 3,ev_data.code = 40, ev_data.value = 348 ev_data.type = 3,ev_data.code = 40, ev_data.value = 352 现在说明驱动上报数据正常。接下来看一下驱动中定义的属性值: root@ELF1:~# ls /sys/bus/i2c/devices/1-0029/ als_data driver input name power subsystem uevent als_poll_delay enable_als_sensor modalias of_node reg type 在驱动中定义的als_data、als_poll_delay、enable_als_sensor、reg、type等属性在这都能查询到。可以使用cat命令来查询这些属性: root@ELF1:~# cat /sys/bus/i2c/devices/1-0029/name bh1726 root@ELF1:~# cat /sys/bus/i2c/devices/1-0029/als_data 37818 4680 root@ELF1:~# cat /sys/bus/i2c/devices/1-0029/reg reg(0x80) = 0x13 reg(0x81) = 0xf7 reg(0x82) = 0x1 reg(0x83) = 0x0 reg(0x84) = 0x0 reg(0x85) = 0xff reg(0x86) = 0xff reg(0x87) = 0xf reg(0x92) = 0x72 reg(0x94) = 0xfa reg(0x95) = 0x93 reg(0x96) = 0xbf reg(0x97) = 0x12 reg(0x98) = 0x0 judge_coefficient[4]=[157,261,1121,4910] data0_coefficient[4]=[159,403,256,221] data1_coefficient[4]=[941,-615,-84,-45] You can read/write a register just like the follow: read: echo "r 0x40 " > reg write: echo "w 0x40 0xFF" > reg para: echo "para " > reg (Use dmesg to see kernel log) 0x80 0x13 0x81 0xf7 0x82 0x01 0x83 0x00 0x84 0x00 0x85 0xff 0x86 0xff 0x87 0x0f 0x92 0x72 0x94 0xfa 0x95 0x93 0x96 0xbf 0x97 0x12 0x98 0x00 |
225 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-input子系统之基于input子系统的光线传感器驱动
896 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-I2C设备驱动之I2C驱动构建流程
1022 浏览 0 评论
迅为RK3568多个系统版本并流畅运行Android12和11版本
1264 浏览 0 评论
1662 浏览 0 评论