• 正文
    • bh1726光線傳感器簡介
    • 驅(qū)動(dòng)編寫
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡 input子系統(tǒng)之基于input子系統(tǒng)的光線傳感器驅(qū)動(dòng)

04/16 14:44
750
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點(diǎn)資訊討論

bh1726光線傳感器簡介

BH1726是一種數(shù)字光線傳感器,由ROHM Semiconductor開發(fā)和制造。它被設(shè)計(jì)用于測量環(huán)境中的光照強(qiáng)度。該傳感器采用CMOS圖像傳感器技術(shù),并具有高精度和高靈敏度。

BH1726光線傳感器可以檢測可見光范圍內(nèi)的光照強(qiáng)度,并將其轉(zhuǎn)換為數(shù)字輸出。它可以測量的光照范圍通常在0到100,000勒克斯(lux)之間,這使得它非常適用于室內(nèi)和室外環(huán)境的光照測量。

該傳感器具有內(nèi)置的模數(shù)轉(zhuǎn)換器ADC),可以將光照強(qiáng)度轉(zhuǎn)換為12位數(shù)字值。它還具有自動(dòng)增益控制(AGC)功能,可以自動(dòng)調(diào)整感應(yīng)器的增益以適應(yīng)不同光照條件下的測量。

BH1726傳感器還具有低功耗特性,適用于移動(dòng)設(shè)備和電池供電的應(yīng)用。它采用了I2C接口,可以與微控制器或其他數(shù)字設(shè)備進(jìn)行通信,并提供實(shí)時(shí)的光照強(qiáng)度數(shù)據(jù)。

總的來說,BH1726光線傳感器是一種高精度、高靈敏度的數(shù)字傳感器,適用于各種需要光照強(qiáng)度測量的應(yīng)用,包括自動(dòng)調(diào)光系統(tǒng)、室內(nèi)照明控制、移動(dòng)設(shè)備和環(huán)境監(jiān)測等。

驅(qū)動(dòng)編寫

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動(dòng)例程源碼8_input子系統(tǒng)bh1726

BH1726光線傳感器采用了i2c接口,所以需要將其注冊成i2c設(shè)備,來讀取其內(nèi)部寄存器的測量值,然后通過input子系統(tǒng)上報(bào)到用戶空間,下面來看一下驅(qū)動(dòng)的具體實(shí)現(xiàn)流程。

修改設(shè)備樹

(一)查看原理圖引腳復(fù)用表格,確定光線傳感器連接引腳。

(二)I2C2引腳復(fù)用,打開設(shè)備樹文件arch/arm/boot/dts/imx6ull-elf1-emmc.dts我們看到原來的設(shè)備樹文件已經(jīng)添加了pinctrl_i2c2子節(jié)點(diǎn),而且選擇的引腳與UART5_RX_DATA、UART5_TX_DATA一致,所以此處無需修改。

(三)添加設(shè)備節(jié)點(diǎn)

在arch/arm/boot/dts/imx6ull-elf1-emmc.dts文件中的i2c2節(jié)點(diǎn)下添加溫濕度傳感器子節(jié)點(diǎn)aht20:

rohm@29{

compatible = "rohm,bh1726";

reg = <0x29>;

????????????????status = "okay";

????????};

添加后的效果如下:

(四)重新編譯內(nèi)核和設(shè)備樹

. /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

編譯生成的設(shè)備樹文件為imx6ull-elf1-emmc.dtb,內(nèi)核文件為zImage,參考《01-0 ELF1、ELF1S開發(fā)板_快速啟動(dòng)手冊_V1》4.4節(jié)單獨(dú)更新設(shè)備樹和內(nèi)核。

編寫bh1726.c驅(qū)動(dòng)

(一)在驅(qū)動(dòng)中要操作很多芯片相關(guān)的寄存器,所以需要先新建一個(gè)bh1726.h的頭文件,用來定義相關(guān)寄存器值。

#ifndef __BH1726_DRIVER_H__

#define __BH1726_DRIVER_H__

/*************** Definitions ******************/

/* GENERAL */

#define BH1726_DRV_NAME "bh1726"

#define DRIVER_VERSION ???"1.0"

#define QCOM_SENSORS ??????(0)

先是定義了設(shè)備名稱和驅(qū)動(dòng)版本,然后定義是不是使用QCOM框架,QCOM框架是用于Android系統(tǒng),所以此處定義為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相關(guān)的宏定義,用于調(diào)試階段打印一些相關(guān)信息。

/************ 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數(shù)據(jù)手冊的Register MAP章節(jié),需要注意的是數(shù)據(jù)手冊上的寄存器地址只寫了寄存器地址的低4位,所以定義時(shí)要在這個(gè)基礎(chǔ)上加上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)

寄存器參數(shù)相關(guān)定義,從Register MAP表中可以看到ADC_EN由寄存器0x80的第1位來控制,下表可以得知0表示ADC測量停止,1表示ADC測量開始。所以定義ADC_EN_ON 為(1 << 1),定義ADC_EN_OFF為 (0 << 1)。其它定義同理,此處就不一一講解了。

最后是相關(guān)結(jié)構(gòu)體的定義,如下:

/*************** 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 <linux/debugfs.h>

//#include <linux/wakelock.h>

#include <linux/regulator/consumer.h>

#include <linux/workqueue.h>

#include <linux/module.h>

#include <linux/init.h>

#include <linux/slab.h>

#include <linux/i2c.h>

#include <linux/mutex.h>

#include <linux/delay.h>

#include <linux/interrupt.h>

#include <linux/irq.h>

#include <linux/input.h>

#include <linux/kernel.h>

#include <linux/errno.h>

#include <linux/string.h>

#include <linux/miscdevice.h>

#include <linux/uaccess.h>

#include <linux/unistd.h>

#include <linux/platform_device.h>

#include <linux/pm.h>

#include <linux/of_gpio.h>

#include <asm/div64.h>

#include <linux/math64.h>

#ifdef CONFIG_OF

#include <linux/of_gpio.h>

#endif

#include "linux/bh1726.h"

(2)創(chuàng)建相關(guān)宏定義和變量

/*************** 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)驅(qū)動(dòng)模塊的入口和出口

module_init(bh1726_init);

module_exit(bh1726_exit);

(4)bh1726_init和bh1726_exit實(shí)現(xiàn)

static int __init bh1726_init(void)

{

return i2c_add_driver(&bh1726_driver);

}

static void __exit bh1726_exit(void)

{

i2c_del_driver(&bh1726_driver);

}

在入口函數(shù)中調(diào)用了i2c_add_driver函數(shù),來注冊I2C總線驅(qū)動(dòng)程序。在出口函數(shù)中調(diào)用了i2c_del_driver函數(shù),來注銷I2C驅(qū)動(dòng)程序。

(5)i2c_driver類型結(jié)構(gòu)體定義

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實(shí)現(xiàn),用來與設(shè)備樹中的compatible匹配

static struct of_device_id bh1726_match_table[] = {

{ .compatible = "rohm,bh1726",},

{ },

};

(7)remove函數(shù)實(shí)現(xiàn),執(zhí)行bh1726設(shè)備的清理操作

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函數(shù)實(shí)現(xiàn),此處簡略描述I2C設(shè)備和input子系統(tǒng)注冊的過程

①I2C設(shè)備注冊:

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);

先是調(diào)用to_i2c_adapter函數(shù)將設(shè)備與可用的I2C適配器進(jìn)行關(guān)聯(lián)和連接。

然后調(diào)用i2c_check_functionality函數(shù)檢查系統(tǒng)中I2C功能的支持情況和可用性。以確保所需的功能可用,并確定哪些功能可以被使用。

i2c_check_functionality函數(shù)原型如下:

int i2c_check_functionality(int adapter_num, unsigned long supported_func)

adapter_num是指要檢查的I2C適配器的編號或標(biāo)識符。不同的系統(tǒng)可能有不同的編號方案,通常從0開始遞增。

supported_func是一個(gè)表示所需功能的標(biāo)志位掩碼。這些標(biāo)志位表示可能的I2C功能,如I2C_M_TEN(10位地址模式支持)、I2C_FUNC_I2C(基本I2C功能支持)等。

函數(shù)返回一個(gè)整數(shù)值,表示I2C功能的支持情況。通常,返回值為0表示所需的功能不受支持或不可用,非零值表示所需的功能可用。

最后調(diào)用i2c_set_clientdata函數(shù),用于在I2C設(shè)備驅(qū)動(dòng)程序中設(shè)置與設(shè)備關(guān)聯(lián)的私有數(shù)據(jù)。

i2c_set_clientdata函數(shù)原型如下:

void i2c_set_clientdata(struct i2c_client *client, void *data)

client是指向struct i2c_client結(jié)構(gòu)的指針,表示與I2C設(shè)備相關(guān)聯(lián)的客戶端信息。

data是要設(shè)置的私有數(shù)據(jù)指針??梢允侨魏晤愋偷闹羔槪糜诖鎯εc設(shè)備相關(guān)的私有數(shù)據(jù)。

通過調(diào)用i2c_set_clientdata函數(shù),可以將私有數(shù)據(jù)指針存儲在struct i2c_client結(jié)構(gòu)的driver_data字段中。在驅(qū)動(dòng)程序的其他函數(shù)中,可以使用i2c_get_clientdata函數(shù)來檢索并使用該私有數(shù)據(jù)。

初始化延時(shí)工作隊(duì)列:

INIT_DELAYED_WORK(&als->als_dwork, bh1726_als_polling_work_handler);

INIT_DELAYED_WORK是一個(gè)宏,用于初始化延遲工作隊(duì)列。延遲工作可以在一定時(shí)間后執(zhí)行,可以用于處理定時(shí)任務(wù)、延遲處理或后臺處理等場景。本驅(qū)動(dòng)中用于定時(shí)上報(bào)采集的光線值。

INIT_DELAYED_WORK宏的典型定義如下:

INIT_DELAYED_WORK(struct delayed_work *dwork, work_func_t func);

dwork是指向struct delayed_work類型的指針,表示要初始化的延遲工作。

func是一個(gè)指向工作函數(shù)(work function)的指針,表示延遲工作的處理函數(shù)。工作函數(shù)的定義和形式根據(jù)具體的需求和實(shí)現(xiàn)而異。

②初始化BH1726:

/* Initialize the bh1726 chip */

err = bh1726_init_client(als);

if (err)

goto exit_kfree;

bh1726_init_client()函數(shù)的實(shí)現(xiàn)如下:

/*************** 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);

}

在初始化函數(shù)中主要進(jìn)行了軟件復(fù)位,設(shè)置測量時(shí)間和設(shè)置增益。

③注冊input設(shè)備:

/* 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 alsn", __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: %sn", __func__,

als->input_dev_als->name);

goto exit_free_dev_als;

}

④創(chuàng)建sysfs文件系統(tǒng)中的屬性組:

/* Register sysfs hooks */

err = sysfs_create_group(&client->dev.kobj, &bh1726_attr_group);

if (err) {

BH1726_DBG("%s sysfs_create_groupXn", __func__);

goto exit_unregister_dev_als;

}

sysfs是一種用于向用戶空間公開設(shè)備和驅(qū)動(dòng)程序信息的虛擬文件系統(tǒng)。屬性組是一組相關(guān)屬性的集合,可以在sysfs中表示設(shè)備或驅(qū)動(dòng)程序的狀態(tài)、配置和控制信息。

以下是sysfs_create_group函數(shù)的典型定義:

int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp);

kobj是指向struct kobject類型的指針,表示在sysfs中創(chuàng)建屬性組的kobject對象。kobject表示設(shè)備或驅(qū)動(dòng)程序在內(nèi)核中的抽象對象,是sysfs文件系統(tǒng)的一部分。

grp是指向struct attribute_group類型的指針,表示要?jiǎng)?chuàng)建的屬性組。attribute_group結(jié)構(gòu)定義了屬性組的名稱和包含的屬性列表。

sysfs_create_group函數(shù)的作用是在sysfs文件系統(tǒng)中的給定kobject對象下創(chuàng)建一個(gè)新的屬性組。該屬性組將包含在struct attribute_group結(jié)構(gòu)中定義的一組屬性。通過創(chuàng)建屬性組,可以將一組相關(guān)的屬性組織在一起,并使其在sysfs中可用。

(9)bh1726_attr_group結(jié)構(gòu)體定義及綁定

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是一個(gè)宏(macro),用于定義設(shè)備屬性,設(shè)備屬性是一種用于在sysfs文件系統(tǒng)中表示設(shè)備狀態(tài)、配置和控制信息的機(jī)制。通過在設(shè)備的kobject對象下創(chuàng)建屬性文件,可以提供對設(shè)備屬性的訪問和操作。具體體現(xiàn)就是,加載驅(qū)動(dòng)后在文件系統(tǒng)的/sys/bus/i2c/devices/1-0029路徑下能訪問這些屬性(als_poll_delay、enable_als_sensor、als_data、type、reg),讀取屬性時(shí)會調(diào)用后邊的show類型函數(shù)指針( bh1726_show_als_poll_delay,bh1726_show_enable_als_sensor,bh1726_show_als_data,bh1726_show_typ,bh1726_show_allreg),寫入屬性時(shí)候調(diào)用后邊的store類型函數(shù)指針(bh1726_store_als_poll_delay,bh1726_store_enable_als_sensor、,bh1726_store_reg)。

以下是DEVICE_ATTR宏的典型定義:

DEVICE_ATTR(name, mode, show, store)

name是設(shè)備屬性的名稱,以字符串形式表示。

mode是設(shè)備屬性的訪問權(quán)限和類型,使用類似于UNIX文件權(quán)限的形式指定(如S_IRUGO表示只讀權(quán)限)。具體的權(quán)限常量定義可以在<linux/stat.h>頭文件中找到。

show是一個(gè)指向show函數(shù)的指針,用于讀取設(shè)備屬性的值。show函數(shù)的定義和形式根據(jù)具體的需求和實(shí)現(xiàn)而異。

store是一個(gè)指向store函數(shù)的指針,用于寫入設(shè)備屬性的值。store函數(shù)的定義和形式根據(jù)具體的需求和實(shí)現(xiàn)而異。

DEVICE_ATTR宏的作用是創(chuàng)建一個(gè)名為name的設(shè)備屬性,其中包含讀取和寫入屬性值的處理函數(shù)。通過定義設(shè)備屬性,可以在sysfs中為設(shè)備提供對應(yīng)的屬性文件,從而實(shí)現(xiàn)對設(shè)備屬性的讀寫操作。

(10)enable_als_sensor使能屬性寫入調(diào)用

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=%ldn", __func__, val);

return count;

}

bh1726_enable_als_sensor(client, val);

return count;

}

bh1726_store_enable_als_sensor函數(shù)又會掉用bh1726_enable_als_sensor(client, val);接下來看一下bh1726_enable_als_sensor(client, val)函數(shù)的實(shí)現(xiàn):

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=%dn", 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;

}

該函數(shù)中主要判斷val 值是否等于等于POWER_ON,如果等于POWER_ON就會開啟延時(shí)任務(wù)隊(duì)列中的服務(wù)函數(shù),延時(shí)隊(duì)列在probe函數(shù)中已經(jīng)進(jìn)行了初始化,服務(wù)函數(shù)為:bh1726_als_polling_work_handler,接下來看一下該函數(shù)的實(shí)現(xiàn):

/* 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%xn", 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));

}

可以看到此函數(shù)會讀取光線傳感器中的寄存器值,并轉(zhuǎn)換成lux值,通過input_report_abs和input_sync函數(shù)將數(shù)據(jù)上報(bào)給用戶空間,然后再次開啟延時(shí)任務(wù)調(diào)度函數(shù),實(shí)現(xiàn)了定時(shí)上報(bào)數(shù)據(jù)。由于驅(qū)動(dòng)完整源碼過長,請到資料包中查看,路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動(dòng)例程源碼8_input子系統(tǒng)bh1726。

編譯

復(fù)制7.9.3驅(qū)動(dòng)中的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子系統(tǒng)/bh1726$ make

將編譯生成的bh726.ko模塊拷貝到開發(fā)板。

編寫測試源碼bh726_app.c

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <fcntl.h>

#include <linux/input.h>

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 %snn", DEV);

return -1;

}

return fd;

}

static int als_test()

{

if(init_device(ALS_DEV) < 0)

{

printf("init_device faildn");

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 = %dnn",ev_data.type,ev_data.code, ev_data.value);

}

}

return 0;

}

int main(void)

{

als_test();

return 0;

}

編譯應(yīng)用

. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/08_input子系統(tǒng)/bh1726_app$ $CC bh1726_app.c -o bh1726_app

可以看到測試源碼中先是使能了enable_als_sensor屬性,然后就開始循環(huán)讀取上報(bào)的input事件了。

測試

加載驅(qū)動(dòng)模塊:

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

如果與用于程序中不一致,需要更改應(yīng)用測試程序中的ALS_DEV宏定義。運(yùn)行測試應(yīng)用程序,可以看到一直在上報(bào)光線值:

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

現(xiàn)在說明驅(qū)動(dòng)上報(bào)數(shù)據(jù)正常。接下來看一下驅(qū)動(dòng)中定義的屬性值:

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

在驅(qū)動(dòng)中定義的als_data、als_poll_delay、enable_als_sensor、reg、type等屬性在這都能查詢到??梢允褂胏at命令來查詢這些屬性:

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

相關(guān)推薦