相关商品

浏览历史

当前位置: 极速3分彩 > 传感器常见问题 > 如何编写加速度传感器linux驱动(bma250)
如何编写加速度传感器linux驱动(bma250)
传感器驱动 / 2012-12-22

 

 1 Gsensor 驱动概述

  本文以Bma250驱动为例子,详细介绍Gsensor设计的一个模板。

 gsensor驱动在系统中的层次如下图所示:

 

图中包含三个部分:hardware, driver, input:

 

1, Hardware:其实我们可以认为Gsensor也是一个I2C设备。整个Gsensor芯片分为两部分,一个是sensor传感器,另一个是controller控制器,用于将sensor挂载在linux系统的I2C上。驱动程序则通过I2C与Gsensor做通信。

 

2,Gsensor Driver:是驻留于操作系统中,为gsensor hardware服务的一个内核模块;它将gsensor hardware采集到的原始数据,进行降噪,滤波,获得当前平板的空间状态,并按照操作系统的要求,将这些信息通过input core上报给操作系统。

 

3,Input core: 是linux为简化设备驱动程序开发,而开发的一个内核子系统;发给input core的数据将提供给操作系统使用。

 

实际使用时,驱动按照一定的时间间隔,通过数据总线,获取gsensor hardware采集到的数据,并按照操作系统的要求,将这些信息通过input core上报给操作系统。

 

2 Gsensor驱动设计要求

 

由gsensor驱动在系统中的层次,上有Input core,下有I2C,驱动需要通过I2C采集信息,并准确及时的上报数据至input core。驱动上报的数据,是被input core管理并被上层使用的,应符合input core和上层应用框架的要求;

 

2.1符合Input输入子系统的设计规范

 

1, 接口:Gsensor驱动,在设计上,不应自行决定是否上报,上报频率等,应提供接口,供上层应用控制驱动的运行和数据上报:包括使能控制Enable, 上报时延delay等;通常通过sysfs文件系统提供,这部分实现,遵循标准的linux规范;

 

2,上报数据的方式:或者提供接口供上层访问(eg: ioctl),或者挂接在系统子系统上,使用系统子系统的接口,供上层使用(eg: input core);

 

3,读取数据的支持:应满足读取数据的要求,进行相应的配置;本文以i2c总线为例,简要说明在A1x平台上,配置总线传输相关信息;

 

2.2 I2c总线的配置

 

要使用i2c总线进行数据传输,需注册i2c driver,创建i2c-client,以便使用i2c-adapter进行数据传输;

要成功注册i2c driver有两种方式:

1,使用i2c_register_board_info:此方式,需要在系统启动时,进行相关信息的注册,不利于模块化开发,现已不推荐;目前,在2.6内核上,还支持此方式,在3.0上已不再支持;

2,  使用detect方式:在模块加载时,进行检测,在条件成立时,注册i2c设备相关信息,创建i2c-client,并注册i2c driver,执行probe操作;

 

需要说明的是,此两种方式可共存,目前2。6就是这样的;在共存时,以i2c_register_board_info信息为更高优先级,在i2c_register_board_info已经占用设备的前提下,内核发现设备被占用,不会执行detect, 因而不会有冲突。

 

3 gsensor模块硬件说明

 

1,  gsensor硬件,负责获取gsensor传感器所处的空间状态信息,存放于fifo中,供主控使用,不同的硬件平台,数据准备好后,告知主控的方式及主控获取数据的方式略有不同。

2,   告知主控的方式:gsensor作为传感器,本身无法区分哪些数据是应该上报的,哪些数据是无效的,它只能接受主控的控制,以主控主动查询为主;

3,  主控获取数据的方式:通过ahb, i2c, spi,usb等方式获取都是可能的。以下以一种典型的硬件连接为例,描述gsensor 传感器,gsensor ic, 主控之间的连接关系;

3.1 gsensor硬件连接

 

Gsensor在硬件上,只有i2c连接,这些连接信息,需要事先告知驱动,从而从指定的设备上读取数据;这些连接信息,通过sysconfig1描述,在驱动中使用;

 

4 驱动设计

 

4.1 支持模组列表

 

在A1x平台上支持Gsensor列表如下:

支持的

模组

Chip ID

寄存器

Chip ID

I2C地址

I2C设备注册

名称

unuse_name

bma250

0x00

3

0x18

bma250

bma250

bma222

0x00

3

0x08

bma250

bma222

bma150

0x00

2

0x38

bma250

bma150

kxtik-1004

0x0f

0x05

0x0f

kxtik

kxtik

kxtj9-1005

0x0f

0x08

0x0f

kxtik

kxtik

dmard06

0x0f

0x06

0x1c

dmard06

dmard06

mma7660

0x4c

mma7660

mma7660

mma8452

0x0d

0x2a

SA0 = 0: 0x1c

mma8452

mma8452c

SA0 = 1: 0x1d

Mma8452d

afa750

0x37

0x3d or 0x3c

0x3d

afa750

afa750

mxc6225

0x15

mxc622x

mxc622x


4.2 Gsensor配置

 

在A1x的方案中,Gsensor的配置在sys_config1.fex文件中:

 [gsensor_para]

gsensor_used             = 1          //是否使用gsensor

gsensor_name             = "bma250"  //名称

gsensor_twi_id           = 1           //使用哪组I2C

gsensor_twi_addr         = 0x18        // I2C设备地址(7位地址)

 

4.3 关键数据结构

 

4.3.1 i2c_driver

static struct i2c_driver bma250_driver = {

              。class = I2C_CLASS_HWMON,

              .driver = {

                             .owner    = THIS_MODULE,

                             。name      = SENSOR_NAME,

              },

              。id_table   = bma250_id,

              。probe                    = bma250_probe,

              .remove                 = bma250_remove,

              .address_list           = u_i2c_addr.normal_i2c,

};

在驱动程序中,静态初始化i2c_driver结构体给bma250_driver变量,该变量完成gsensor驱动的主要工作,匹配设备名,设备的侦测等,文件操作结构体如上所示。

 

4.3.2 bma250_data

 

struct bma250_data {

              struct i2c_client *bma250_client;

              atomic_t delay;

              atomic_t enable;

              unsigned char mode;

              struct input_dev *input;

              struct bma250acc value;

              struct mutex value_mutex;

              struct mutex enable_mutex;

              struct mutex mode_mutex;

              struct delayed_work work;

              struct work_struct irq_work;

#ifdef CONFIG_HAS_EARLYSUSPEND

              struct early_suspend early_suspend;

#endif

};

代表了gsensor驱动所需要的信息的集合,用于帮助实现对采样信息的处理。

 

4.3.3 delayed_work

 

struct delayed_work {

              struct work_struct work;

              struct timer_list timer;

};

Delayed_work一般用来触发定时的操作,在定时时间到后,完成指定操作,通过不断的触发定时操作,实现按照一定频率的执行指定操作;

 

4.3.4 bma250acc

 

struct bma250acc{

              s16         x,

                             y,

                             z;

} ;

用于记录采样时获得的信息。

 

5 驱动流程解析

 

5.1模块加载

 

static struct i2c_driver bma250_driver = {

              。class = I2C_CLASS_HWMON,

              .driver = {

                             .owner    = THIS_MODULE,

                             .name      = SENSOR_NAME,

              },

              .id_table   = bma250_id,

              .probe                    = bma250_probe,         //注册完成时调用

              。remove                 = bma250_remove,

              。address_list           = u_i2c_addr。normal_i2c,   //IIC地址

};

static int __init BMA250_init(void)

{            

              if(gsensor_fetch_sysconfig_para()){       //解析sys_config1.fex文件

                             printk("%s: err。\n", __func__);

                             return -1;

              }

 

              bma250_driver.detect = gsensor_detect;

              ret = i2c_add_driver(&bma250_driver);   //开始向IIC注册

}

static void __exit BMA250_exit(void)

{

              i2c_del_driver(&bma250_driver);

}

module_init(BMA250_init);

module_exit(BMA250_exit);

  内核加载驱动模块的时候将调用到BMA250_init()方法

n         初始化了i2c_driver结构体给bma250_driver变量,将用于将设备注册到IIC。关键在于结构体中的probe()方法,注册完成的时候将调用

n         调用gsensor_fetch_sysconfig_para()解析sys_config1.fex文件,读取到IIC的地址,并赋值给u_i2c_addr.normal_i2c。

n         调用i2c_add_driver开始向IIC注册driver,完成注册后将调用bm250_probe()方法

 

5.2 bma250初始化工作-probe

 

static int bma250_probe(struct i2c_client *client,

                             const struct i2c_device_id *id)

{

              int err = 0;

              int tempvalue;

              struct bma250_data *data;

    ……

              data = kzalloc(sizeof(struct bma250_data), GFP_KERNEL); //为bma250_data结构体申请内存

             

              tempvalue = 0;

              tempvalue = i2c_smbus_read_word_data(client, BMA250_CHIP_ID_REG);

 

              if ((tempvalue&0x00FF) == BMA250_CHIP_ID) {

                             printk(KERN_INFO "Bosch Sensortec Device detected!\n" \

                                                         "BMA250 registered I2C driver!\n");

              } else if ((tempvalue&0x00FF) == BMA150_CHIP_ID) {

                             printk(KERN_INFO "Bosch Sensortec Device detected!\n" \

                                                         "BMA150 registered I2C driver!\n");

              }

    ……

              i2c_set_clientdata(client, data); //将设备驱动的私有数据连接到设备client中

              data->bma250_client = client;

              mutex_init(&data->value_mutex);

              mutex_init(&data->mode_mutex);

              mutex_init(&data->enable_mutex);

              bma250_set_bandwidth(client, BMA250_BW_SET);

              bma250_set_range(client, BMA250_RANGE_SET);

 

              INIT_DELAYED_WORK(&data->work, bma250_work_func);//创建工作队列

              bma_dbg("bma: INIT_DELAYED_WORK\n");

              atomic_set(&data->delay, BMA250_MAX_DELAY);

              atomic_set(&data->enable, 0);

              err = bma250_input_init(data);  //向Input子系统注册

    …...

              err = sysfs_create_group(&data->input->dev。kobj, //创建sysfs接口

                                                                                       &bma250_attribute_group);

}

在bma250_probe函数中,主要完成了以下几件事:

  •        为驱动私有数据结构体bma250_data分配内存空间
  •        读取IIC chip id
  •        将设备驱动的私有数据(bma250_data)连接到设备client(i2c_client)中
  •        创建工作队列
  •       将bma250驱动注册到linux input子系统
  •      创建sysfs接口

下面对以上这些工作做详细解释,分配数据内存空间就不讲了

 

5.2.1 读取IICchip id

 

在4.1的列表中,我们可以看到bma250的chip ID寄存器为0x00,chip ID的值为3。而上面代码有两个宏的定义:

#define BMA250_CHIP_ID_REG  0x00

#define BMA250_CHIP_ID       3

调用IIC接口i2c_smbus_read_word_data()读取IIC上bma250的chip id,返回的值tempvalue=3的时候,说明是正确的!

 

5.2.2 初始化工作队列

 

先提一个问题,为什么要创建工作队列?在前面的介绍中我们知道,sensor传感器获取数据后,将数据传给controller的寄存器中,供主控去查询读取数据。所以这里创建的工作队列,就是在一个工作者线程,通过IIC不断的去查询读取controller上的数据。

工作队列的作用就是把工作推后,交由一个内核线程去执行,更直接的说就是如果写了一个函数,而现在不想马上执行它,想在将来某个时刻去执行它,那用工作队列准没错.大概会想到中断也是这样,提供一个中断服务函数,在发生中断的时候去执行,没错,和中断相比,工作队列最大的好处就是可以调度可以睡眠,灵活性更好。

上面代码中我们看到INIT_DELAYED_WORK(&data->work, bma250_work_func),其实是一个宏的定义,在include/linux/workqueue。h中。bma250_work_func()就是我们定义的功能函数,用于查询读取Sensor数据的,并上报Input子系统,代码如下:

static void bma250_work_func(struct work_struct *work)

{

              struct bma250_data *bma250 = container_of((struct delayed_work *)work,

                                           struct bma250_data, work);

              static struct bma250acc acc;

              unsigned long delay = msecs_to_jiffies(atomic_read(&bma250->delay)); //延时时间

 

              bma250_read_accel_xyz(bma250->bma250_client, &acc); //读取Sensor数据

              input_report_abs(bma250->input, ABS_X, acc.x);

              input_report_abs(bma250->input, ABS_Y, acc.y);

              input_report_abs(bma250->input, ABS_Z, acc.z);

              bma_dbg("acc.x %d, acc.y %d, acc.z %d\n", acc.x, acc.y, acc.z);

              input_sync(bma250->input);

              mutex_lock(&bma250->value_mutex);

              bma250->value = acc;

              mutex_unlock(&bma250->value_mutex);

              schedule_delayed_work(&bma250->work, delay); //设定delay时间后再次执行这个函数

}

我们调用INIT_DELAYED_WORK()宏初始化了工作队列之后,那么什么时候将执行我们定义的功能函数bma250_work_func()呢?那么只需要调用schedule_delayed_work()即可在delay时间后执行功能函数。

在驱动设计中,我们在Sensor使能函数中调用schedule_delayed_work()开始启动工作队列,调用cancel_delayed_work_sync()停止工作队列。而我们在上面的功能函数bma250_work_func()中也调用了schedule_delayed_work(),这样功能函数将被重复调用,也就可以按照一个设定的频率查询读取Sensor数据了。然后通过input系统提供的接口函数input_report_abs(),向input系统报告新的数据。

5.2.3向input系统注册

Gsensor作为一个输入设备,按照linux设计标准,需要将Gsensor驱动注册到Input子系统中,注册代码如下:

static int bma250_input_init(struct bma250_data *bma250)

{

              struct input_dev *dev;

              int err;

 

       dev = input_allocate_device();

              if (!dev)

                             return -ENOMEM;

              dev->name = SENSOR_NAME;

              dev->id。bustype = BUS_I2C;

 

              input_set_capability(dev, EV_ABS, ABS_MISC);

              input_set_abs_params(dev, ABS_X, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_abs_params(dev, ABS_Y, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_abs_params(dev, ABS_Z, ABSMIN_2G, ABSMAX_2G, 0, 0);

              input_set_drvdata(dev, bma250);

       err = input_register_device(dev);

 

              bma250->input = dev;

}

就是由上面的代码,完成了Gsensor驱动向Input子系统注册,又三个步骤:

  • n         申请一个新的input设备,即为一个input_dev申请内存空间
  • n         设置input设备支持的数据类型
  • n         向input系统注册
  •  

5.2.4 创建sysfs 接口

 

为什么要创建sysfs接口?在驱动层创建了sysfs接口,HAL层通过这些sysfs接口,对Sensor进行操作,如使能、设置delay等。

5.2.4.1 sysfs接口函数的建立DEVICE_ATTR  

说道sysfs接口,就不得不提到函数宏 DEVICE_ATTR,原型在<include/linux/device.h> :

#define DEVICE_ATTR(_name, _mode, _show, _store) \

struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)

函数宏DEVICE_ATTR内封装的是__ATTR(_name,_mode,_show,_stroe)方法:

  • n         _show:表示的是读方法,
  • n         _stroe表示的是写方法。

当然_ATTR不是独生子女,他还有一系列的姊妹__ATTR_RO宏只有读方法,__ANULL>

 

 


本文标题: 如何编写加速度传感器linux驱动(bma250)
本文地址: http://www.dagou88.com/article-137.html
本文标签: 加速度传感器驱动,bma250驱动程序

用户评论(共0条评论)

  • 暂时还没有任何用户评论
总计 0 个记录,共 1 页。 第一页 上一页 下一页 最末页
用户名: 游客
E-mail:
评价等级:
评论内容:

挑选商品 > 确认购买 > 网上支付/货到付款 > 验货满意 > 点评商品

原装正品 金牌服务 购物有保障
  • 放心:我们的任何商品,均为原装正品
  • 舒心:倾力为您打造人性化的购物平台
  • 省心:把麻烦交给我,把快乐网购交给您
新手上路
售后流程
购物流程
订购方式
配送与支付
货到付款区域
配送支付智能查询
支付方式说明
会员中心
资金管理
我的收藏
我的订单
服务保证
退换货原则
售后服务保证
产品质量保证
联系我们
网站故障报告
选机咨询
投诉与建议
上海时时乐开奖 秒速时时彩 上海时时乐 极速3分彩 秒速快3 秒速快3 极速快乐8 飞速赛车平台 秒速时时彩 极速快乐8