重力传感器是根据压电效应的原理来工作的。
所谓的压电效应就是 “对于不存在对称中心的异极晶体加在晶体上的外力除了使晶体发生形变以外,还将改变晶体的极化状态,在晶体内部建立电场,这种由于机械力作用使介质发生极化的现象称为正压电效应 ”。
重力传感器就是利用了其内部的由于加速度造成的晶体变形这个特性。由于这个变形会产生电压,只要计算出产生电压和所施加的加速度之间的关系,就可以将加速度转化成电压输出。当然,还有很多其它方法来制作加速度传感器,比如电容效应,热气泡效应,光效应,但是其最基本的原理都是由于加速度产生某个介质产生变形,通过测量其变形量并用相关电路转化成电压输出。
BMA250E
10位,数字型,三轴加速度传感器,运动触发,中断控制
主要特点:小封装,数字接口,可编程功能,板上FIFO,板上中断控制,低功耗。
I2C接口,2个中断Pin,电压范围1.2to3.6V
加速度范围: 2g/4g/8g/16g
动力触发中断信号产生:新数据,检测任何运动,单输出和双输出,方位识别,flat detection,无运动检测。低功耗,唤醒时间短,先进的系统电源管理
Vdd是内部块的主电源
Vddio是分成的电源供应Pin用于支持接口和内部块
电源模式:
有六种电源模式,除了普通模式支持这个设备的操作外,还有其他的五种节能模式:深度睡眠模式,睡眠模式,标准模式,低功耗模式一和低功耗模式二。
电源打开后就是普通模式。在deep-suspnd模式下,设备接近于最低功耗。只有接口保持活动。没有数据请求被响应,配置寄存器is lost.
OffsetCompensation:慢速补偿,快速补偿,快速补偿,在线校准
Non-volatile memory:三种寄存器:hardwired,volatile,non-volatile
BMA250_driver.c
BMA250_driver.c代码分析:
1、static int bma250_smbus_read_byte(struct i2c_client *client,
unsigned char reg_addr, unsigned char *data)
static int bma250_smbus_write_byte(struct i2c_client *client,
unsigned char reg_addr, unsigned char *data)
两个函数分别调用
i2c_smbus_read_byte_data(client, reg_addr);
i2c_smbus_write_byte_data(client, reg_addr, *data);
而这两个函数都调用i2c_smbus_xfer,函数原型为:
s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
char read_write, u8 command, int protocol,
union i2c_smbus_data *data)
两个调用时就是参数char read_write这个参数有了改变,读时参数为I2C_SMBUS_READ,写时参数为:I2C_SMBUS_WRITE,其他参数不变。参数 int protocol表示使用的协议。
在i2c-core.c中通过EXPORT_SYMBOL(i2c_smbus_read_byte_data);把这个函数导出来,然后在其他文件中使用。
2、static DEVICE_ATTR(mode, S_IRUGO|S_IWUSR, bma250_mode_show, bma250_mode_store);
static DEVICE_ATTR(value, S_IRUGO, bma250_value_show, NULL);
……;
static DEVICE_ATTR(update, S_IRUGO|S_IWUSR, NULL, bma250_update_store);
static DEVICE_ATTR(selftest, S_IRUGO|S_IWUSR, bma250_selftest_show, bma250_selftest_store);
完成了DEVICE_ATTR函数宏的填充,下面就需要创建接口了
static struct attribute *bma250_attributes[]= {
&dev_attr_range.attr,
……
&dev_attr_offset_filt_z.attr,
&dev_attr_orientation.attr,
NULL
};
当想要实现的接口名字是orientation的时候,需要实现结构体static struct attribute *bma250_attributes[]
其中成员变量的名字必须是&dev_attr_orientation.attr,然后再封装
static struct attribute_group bma250_attribute_group = {
.attrs = bma250_attributes
};
再利用sysfs_create_group(&data->input->dev.kobj,&bma250_attribute_group);创建接口.通过以上的几个步骤,就可以在adb shell 终端查看到接口了。当我们将数据 echo 到接口中时,在上层实际上完成了一次 write 操作,对应到 kernel ,调用了驱动中的 “store”。同理,当我们cat 一个 接口时则会调用 “show” 。到这里,只是简单的建立了 android 层到 kernel 的桥梁,真正实现对硬件操作的,还是在 "show" 和 "store" 中完成的。这些接口也是调试接口。
程序执行过程:
板子信息:
struct bma250acc{
s16 x,
y,
z;
} ;
struct bma250_data {/*kernel\lc1810\include\linux\Bmc.h*/
struct i2c_client *bma250_client;//bma250设备
atomic_t delay; //延迟
atomic_t enable; //使能
unsigned char mode; //工作模式
struct input_dev *input; //input设备
struct bma250acc value; //上报的坐标值
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
atomic_t selftest_result;
int orientation;
};
/*kernel\lc1810\arch\arm\mach-comip\board-lc1810.c*/
#if defined(CONFIG_SENSORS_BMA250)
static struct bma250_data bmc_bma250_data = {
.orientation = 0,
};
#endif
填充i2c_board_info:
static struct i2c_board_infocomip_i2c1_board_info[] = {
#if defined(CONFIG_SENSORS_BMA250)
{
I2C_BOARD_INFO("bma250", 0x18),
.platform_data = &bmc_bma250_data,
},
#endif
}
1、module_init(BMA250_init);
module_exit(BMA250_exit);
static int __init BMA250_init(void)
{
return i2c_add_driver(&bma250_driver);//加载驱动程序bma250_driver
}
其中,驱动程序为:
static struct i2c_driver bma250_driver = {
.driver = {
.owner = THIS_MODULE,
.name = SENSOR_NAME,
},
.id_table = bma250_id,//id_table是该驱动所支持的I2C设备的ID表
.probe = bma250_probe,/* bus->match成功后调用 */
.remove = bma250_remove,
};
2、bma250_probe(struct i2c_client *client,const struct i2c_device_id *id)函数的执行:
1)、利用i2c_check_functionality(client->adapter, I2C_FUNC_I2C)检查I2C设备适配器工作是否正常。
2)、给bma250_data分配空间并清零。
3)、利用i2c_smbus_read_word_data(client, BMA250_CHIP_ID_REG);检测bma250的ID号是否匹配,匹配继续,否则失败。
4)、让client的data指向新分配的空间,初始化data.
5)、互斥访问:利用
bma250_set_bandwidth(client, BMA250_BW_SET);
bma250_set_range(client, BMA250_RANGE_SET);设置带宽和范围。
用INIT_DELAYED_WORK(&data->work, bma250_work_func);动态初始化一个队列的work,并且和队列处理函数bma250_work_func()绑定在一起。过一段延迟后处理work。
而在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);
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);
input_sync(bma250->input);
bma250->value = acc;
printk("_______[%s]: acc.x=%d,y=%d,z=%d\n",__func__,acc.x,acc.y,acc.z);
schedule_delayed_work(&bma250->work, delay);
}
首先是一段延迟msecs_to_jiffies(atomic_read(&bma250->delay)),然后通过
bma250_read_accel_xyz(bma250->bma250_client, &acc)读取sensor的xyz的值,再通过input_report_abs()函数把这三个点报上去。再通过schedule_delayed_work()延迟一段时间后调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue。
INIT_DELAYED_WORK()和schedule_delayed_work()函数是成对出现的。
6)、通过bma250_input_init(data)函数初始化bma250_input。初始化时先通过input_allocate_device()分配空间。在设置报点范围,然后注册设备,让bma250_input指向它。
7)、通过sysfs_create_group()函数创建sysfs接口。
8)、初始化bma250电源管理的挂起和打开。
static void bma250_early_suspend(struct early_suspend *h)
{
struct bma250_data *data =container_of(h, struct bma250_data, early_suspend);
mutex_lock(&data->enable_mutex);
if (atomic_read(&data->enable) == 1) {
bma250_set_mode(data->bma250_client, BMA250_MODE_SUSPEND);
cancel_delayed_work_sync(&data->work);
}
mutex_unlock(&data->enable_mutex);
}
互斥的设置bma250工作模式是suspend,然后执行cancel_delayed_work_sync()函数,对一个延迟执行的工作来说,这个函数的作用是在这个工作还未执行的时候就把它给取消掉.
工作时:static void bma250_late_resume(struct early_suspend *h)
{
struct bma250_data *data =
container_of(h, struct bma250_data, early_suspend);
mutex_lock(&data->enable_mutex);
if (atomic_read(&data->enable) == 1) {
bma250_set_mode(data->bma250_client, BMA250_MODE_NORMAL);
schedule_delayed_work(&data->work,
msecs_to_jiffies(atomic_read(&data->delay)));
}
mutex_unlock(&data->enable_mutex);
}resume时首先互斥把工作模式设为普通工作模式,然后调用schedule_delayed_work()函数延迟一段时间后调用工作队列中的work进行处理。
工作者线程(events),或者说worker threads,更确切的说,这些应该是缺省的工作者线程.而与工作者线程相关的一个概念就是工作队列,或者叫work queue.events这么一个线程,它其实和内核线程一样,有事情就处理,没事情就睡眠,也是一个死循环,而schedule_delayed_work()的作用就是唤醒这个线程,确切的说,是先把自己的这个struct work_struct插入workqueue_struct这个队列里,然后唤醒昏睡中的events.然后events就会去处理,要是有延时,那么它就给安排延时以后执行,要是没有延时,或者设了延时为0,那就赶紧开始执行.
9)、初始化后通过函数bma250_set_mode(client, BMA250_MODE_SUSPEND);把sensor设置为suspend状态。如果出现错误就bma250_input_delete(data);释放设备。
至此,bma250_probe执行完毕,sensor初始化完毕。