linux驱动及gsensor学习心得_linux下c编程学习心得
linux驱动及gsensor学习心得由刀豆文库小编整理,希望给你工作、学习、生活带来方便,猜你可能喜欢“linux下c编程学习心得”。
目录
1.MODULE_INIT和MODULE_EXIT..............................................................................................2 2.I2C_DRIVER......................................................................................................................................2 3.LINUX的INPUT子系统..................................................................................................................3 3.1 从驱动层到核心层.......................................................................................................................3 3.2 事件处理层的处理.......................................................................................................................3 3.3 设备结点的创建...........................................................................................................................4 3.4 设备的注销和释放.......................................................................................................................4 4.WORK QUEUE..................................................................................................................................4 5.HAL层开发........................................................................................................................................5 5.1 HAL_MODULE_INFO_SYM.....................................................................................................5 5.2 对设备文件的访问.......................................................................................................................7 6.BMA250功能描述..............................................................................................................................7 6.1 电源管理.......................................................................................................................................7 6.2 操作模式.......................................................................................................................................7 6.3 电源模式.......................................................................................................................................8 6.4 SENSOR数据..................................................................................................................................8 6.4.1 加速度....................................................................................................................................8 6.4.2 温度........................................................................................................................................8 6.5 偏移补偿.......................................................................................................................................9 6.5.1 慢补偿....................................................................................................................................9 6.5.2 快补偿....................................................................................................................................9 6.5.3 手动补偿..............................................................................................................................10 6.5.4 内嵌校准..............................................................................................................................10 6.6 非易失性存储器.........................................................................................................................10 6.7 中断控制器.................................................................................................................................10 6.7.1 new data中断.......................................................................................................................11 6.7.2 slope中断.............................................................................................................................11 6.7.3 tap中断.................................................................................................................................12 6.7.4 orientation中断....................................................................................................................12 6.7.5 flat中断................................................................................................................................14 6.7.6 low-g中断.............................................................................................................................15 6.7.7 high-g中断...........................................................................................................................15 7.SENSOR驱动开发步骤...................................................................................................................15 附录 BMA250寄存器表......................................................................................................................17 linux驱动及g-sensor学习心得
通过对g-sensor driver code的研究学习,我对linux设备驱动开发和g-sensor有了一定的了解,现将学习所得归纳如下。
1.module_init和module_exit
在pd318 project的g-sensor驱动程序bma250.c中,首先包含了两个头文件和。其中init.h定义了驱动的初始化和退出相关的函数,module.h则定义了内核模块相关的函数、变量和宏。module_init和module_exit这两个宏就被定义在init.h中。
module_init的作用是声明一个驱动程序的入口函数,在init.h中可以看到对这个宏的定义如下:
#define module_init(x)__initcall(x);#define __initcall(fn)device_initcall(fn)#define device_initcall(fn)__define_initcall(“6”,fn,6)#define __define_initcall(level,fn,id)static initcall_t __initcall_##fn##id __used __attribute__((__section__(“.initcall” level “.init”)))= fn
如果驱动程序要以func函数作为驱动的入口,则可以这样声明:module_init(func)。经过上面的宏处理以后,变成__initcall_func6 __used加入到内核映射的“.initcall”区。内核加载的时候,会搜索“.initcall”区内的所有条目,并按优先级加载它们。通过module_init声明的驱动程序优先级为6,优先级越小越先加载。
对于驱动入口函数func,一般要加上__init属性,这个宏告诉编译器如果这个模块被编译到内核,则把这个函数放到(.init.text)段。而module_exit用于声明驱动程序的出口,对于驱动出口函数,一般要加上__exit属性,这个宏与__init类似,如果驱动被编译进内核,则__exit宏会忽略清理函数。
在内核初始化后期,所有带__init属性的函数会被放在同一个section里,在用完之后,一次性释放掉整个section。当函数初始化完成后,这个区域可以被清除掉以节约系统内存。
2.i2c_driver 在pd318 project中,g-sensor的PS pin连接VDDIO,操作模式采用带I²C接口的general mode,在bma250.c中可以了解到bma250的i2c_driver的结构定义如下:
struct i2c_driver bma250_driver = {.driver = {
.owner = THIS_MODULE,.name = SENSOR_NAME, },.id_table = bma250_id,.probe = bma250_probe,.remove };= bma250_remove, 在这个结构中,定义了驱动所属module、驱动名、驱动的id表、驱动初始化以及移除接口。可以通过i2c_add_driver将sensor driver添加进module中,通过i2c_del_driver将driver移除。
3.linux的input子系统
Linux系统提供了input子系统,可以用以实现系统中大多数输入设备的设备驱动,pd318中g-sensor的设备驱动就是通过它来实现的。
input子系统由核心层、驱动层和事件处理层三部分组成。当输入设备产生了一个输入事件时,由驱动层对事件加以识别,然后提交给核心层,核心层根据事件的类型将事件交给事件处理层处理,事件处理层将处理结果再反馈给用户。其中核心层由/drivers/input/input.c及相关头文件实现,对下提供了设备驱动的接口,对上提供了事件处理层的编程接口。这样,一个输入设备的输入过程可以分离为独立的两部分:驱动层到核心层,核心层到事件处理层,整个链路的这两部分的接口的创建是独立的。
3.1 从驱动层到核心层
驱动层负责和底层的硬件设备打交道,将底层硬件对用户输入的响应转换为标准的输入事件以后再向上发送给核心层。
驱动层先要调用input_allocate_device接口来分配一个设备,对设备进行一些属性赋值,如设备名、总线类型、响应事件类型及事件参数、驱动数据等等,然后通过input_register_device函数向input子系统中注册所要驱动的输入设备。对于g-sensor来说,驱动需要响应EV_ABS事件,该事件需要向核心层提供一个绝对坐标,事件参数ABS_X、ABS_Y、ABS_Z分别对应g-sensor在x、y、z轴上的偏移量。当g-sensor发生坐标变化时,驱动程序需要从相应寄存器中读出偏移量,然后通过input_report_abs接口上报给input子系统,由input子系统负责将事件及参数变化发送给事件处理层处理。
3.2 事件处理层的处理
驱动层只是把输入设备注册到input子系统中,在驱动中的code中并不创建设备结点。应用程序用来与设备打交道的设备结点的创建由事件处理层调用核心层的函数来实现。而对于常用输入设备(如mouse、keyboard、joystick等),在创建具体的设备结点之前,事件处理层需要先通过input_register_handler注册一类设备的输入事件处理函数及相关接口。以鼠标事件处理为例,mouse input_handler定义如下:
static struct input_handler mousedev_handler = {.event = mousedev_event, //向系统报告input事件,系统通过read方法读取
.connect = mousedev_connect, //和input_dev匹配后调用connect构建
.disconnect = mousedev_disconnect,.fops = &mousedev_fops, //event设备文件的操作方法.minor = MOUSEDEV_MINOR_BASE, //次设备号基准值
.name = “mousedev”,.id_table = mousedev_ids, //匹配规则 };
在linux中没有对g-sensor事件处理register的通用标准定义,对于这类事件,事件处理层会统一地当作evdev事件处理,为设备创建一个对应的设备文件,在HAL层可以通过open该设备文件来实现与底层的通信。
3.3 设备结点的创建
向input子系统注册一个硬件设备后,在input_register_handler中调用已经注册的所有类型的input handler的connect函数,每一个connect函数会根据注册设备所支持的事件类型判断是否与自己有关,如果有关就调用input_register_minor创建一个具体的设备结点。
此外,如果已经注册了一些硬件设备,此后再注册一类新的input handler,则同样会对所有已注册的设备调用新的input handler的connect函数以确定是否需要创建新的设备结点。
3.4 设备的注销和释放
在remove driver同时注销掉所注册的input设备,通过input_unregister_device函数注销设备,通过input_free_device接口释放设备资源。
4.work queue 在pd318 bma250.c的bma25_probe函数中可以看到,对于g-sensor状态的获取和report是通过linux的work queue机制来实现的。这里可以看到一个宏:INIT_DELAYED_WORK。这个宏被定义在/linux/workqueue.h中,具体定义如下:
#define INIT_WORK(_work, _func)
do {
__INIT_WORK((_work),(_func), 0);} while(0)
#define INIT_DELAYED_WORK(_work, _func)
do {
INIT_WORK(&(_work)->work,(_func));
init_timer(&(_work)->timer);
} while(0)
从这个定义中可以看到,INIT_DELAYED_WORK就是带有timer的INIT_WORK,而INIT_WORK所做的事情就是初始化一个工作,但是并不马上执行,需要将来某个时刻去触发执行。INIT_DELAYED_WORK(INIT_WORK)需要两个参数,一个是struct delayed_work(struct work_struct)结构体,一个是一个函数指针_func。这个初始化实际上就是为一个struct work_struct结构体绑定一个函数。在/linux/workqueue.h中可以看到struct work_struct的定义如下:
struct work_struct { atomic_long_t data;#define WORK_STRUCT_PENDING 0 /* T if work item pending execution */ #define WORK_STRUCT_STATIC 1 /* static initializer(debugobjects)*/ #define WORK_STRUCT_FLAG_MASK(3UL)#define WORK_STRUCT_WQ_DATA_MASK(~WORK_STRUCT_FLAG_MASK)struct list_head entry;work_func_t func;#ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map;#endif };
在这个结构体中有一个成员函数func,初始化的目的就是使这个func指向INIT_WORK的第二个参数_func。以后调用schedule_delayed_work(schedule_work)时,只要传递struct delayed_work(struct work_struct)结构体参数即可,不需要再传递_func。这是因为,处理work的线程实际上跑的是一个死循环,有事情就处理,没事情就睡眠,而schedule_delayed_work(schedule_work)的作用就是唤醒这个线程。
INIT_DELAYED_WORK与schedule_delayed_work,INIT_WORK与schedule_work是对应着使用的,两者的区别就是schedule_work在唤醒线程的同时立刻执行_func,而schedule_delayed_work在唤醒线程后delay一段时间再执行。bma250.c通过使用INIT_DELAYED_WORK与schedule_delayed_work,实现g-sensor对不同取样频率的支持。
关于workqueue还有两个函数,它们是cancel_delayed_work和flush_scheduled_work。cancel_delayed_work的作用是对于一个延迟执行的工作而言,在工作还未执行的时候就把它给取消掉。而flush_scheduled_work的作用是为了防止有竞争条件的出现。一般在cancel_delayed_work之后都得调用flush_scheduled_work,函数会一直等待,直到队列中所有工作都被执行以后才返回。
5.HAL层开发
5.1 HAL_MODULE_INFO_SYM HAL层,即硬件抽象层,android层对于硬件的调用一般通过HAL的方式来完成。要想HAL的编写方法,首先需要了解HAL的运行机制。看android源码,可以发现android中实现调用HAL是通过hw_get_module来实现的。
int hw_get_module(const char *id, const struct hw_module_t **module);上面的是其函数原型,id会指定Hardware的id,这是一个字符串,比如sensor的id是#define SENSORS_HARDWARE_MODULE_ID “sensors”。如果找到了对应的hw_module_t结构体,会将其指针放入*module中。代码实现如下:
for(i=0;i
if(i
if(property_get(variant_keys[i], prop, NULL)== 0){
continue;
}
/* 获取并访问HAL动态链接库 */
snprintf(path, sizeof(path), “%s/%s.%s.so”,HAL_LIBRARY_PATH1, id, prop);
if(acce(path, R_OK)== 0)break;
snprintf(path, sizeof(path), “%s/%s.%s.so”, HAL_LIBRARY_PATH2, id, prop);
if(acce(path, R_OK)== 0)break;
} else {
snprintf(path, sizeof(path), “%s/%s.default.so”, HAL_LIBRARY_PATH1, id);
if(acce(path, R_OK)== 0)break;
} }
status =-ENOENT;if(i
/* 调用load函数打开动态链接库*/
status = load(id, path, module);}
这个函数的作用,就是获取动态链接库并调用load函数打开它。再看load函数,可以发现该函数中出现了一个宏HAL_MODULE_INFO_SYM_AS_STR。HAL_MODULE_INFO_SYM_AS_STR是硬件模块在动态链接库中的标志,定义在hardware.h中如下:
#define HAL_MODULE_INFO_SYM HMI #define HAL_MODULE_INFO_SYM_AS_STR “HMI”
再去对照一个sensors.app中的代码:
extern “C” const struct sensors_module_t HAL_MODULE_INFO_SYM = { common :{
tag : HARDWARE_MODULE_TAG,version_major : 1,version_minor : 0,id : SENSORS_HARDWARE_MODULE_ID,name : “Bosch sensor module”,author : “Bosch Sensortec”,methods : &sensors_module_methods,dso : NULL,reserved : {}, },get_sensors_list : sensors__get_sensors_list };这里的sensors_module_t结构体的定义为: struct sensors_module_t {
struct hw_module_t common;
int(*get_sensors_list)(struct sensors_module_t* module,struct sensor_t const** list);};它定义了一个hw_module_t类型的common成员,和获取sensors list的接口函数sensors__get_sensors_list。注意,这里的HAL_MODULE_INFO_SYM必须为这个名字,这样编译器才会将这个结构体的导出符号变为“HMI”。
从以上分析可以知道,HAL层开发就是要定义一个hw_module_t结构体,结构体名称命名为HAL_MODULE_INFO_SYM,然后实现结构体的相关内容即可。也就是说,HAL模块的入口地址就是HAL_MODULE_INFO_SYM变量,通过它,我们可以访问到HAL模块中所有想要外部访问到的方法。
5.2 对设备文件的访问
在HAL层可以通过访问3.2节中evdev.c生成的设备文件来实现对硬件设备的访问。对于g-sensor来说,要实现对于sensor的poll、setDelay、activate、close等函数接口。在poll接口中,程序通过一个循环去获取设备文件中显示出的sensor的坐标变化,在收到EV_SYN事件时,将sensor的坐标变化反馈给android系统,android对获取到的状态变化作出相应的处理。
6.bma250功能描述
6.1 电源管理
VDD :主电源,为所有内部模拟/数字功能块供电。VDDIO:专为数字I/O供电的单独电源引脚。
6.2 操作模式
General mode :sensor作为外部总线的一个从设备,可以配置、使用外部中断引脚INT1和INT2。
Dedicated mode :sensor作为一个独立设备,没有外部中断功能,使用内部中断机制,一次只能够被指派处理orientation recognition、tap sensing、slope detection三种中断中的一种。
PS引脚的连接状态决定了sensor采用哪种操作模式。PS = GND
general mode with SPI interface PS = VDDIO
general mode with I²C interface PS = float dedicated mode
6.3 电源模式
bma250具有三种电源模式:normal mode、low-power mode和suspend mode。normal mode :电路全开,整个g-sensor处于正常工作状态。
low-power mode :周期性地在睡眠和唤醒状态之间切换。睡眠状态下,除振荡器外所有器件不工作;唤醒状态下工作在正常状态。
suspend mode :包括振荡器在内的整个board处于power down状态,仅可以读取寄存器、写suspend位和softreset寄存器。
三种模式之间可能的状态切换如图1所示。
图1 电源模式切换图
6.4 sensor数据
6.4.1 加速度
每一个轴上的加速度数据都是10个bit,分为两个寄存器来存储(MSB :9~2 bit,LSB :1~0 bit)。x、y、z三个轴的加速度数据存储在寄存器0x02~0x07,依次存储x、y、z轴加速度的LSB、MSB数据。
加速度数据可以使用两种不同的数据流:unfiltered和filtered。使用哪种数据流由0x13寄存器的data_high_bw位决定。
unfiltered :采样频率为 2kHz,data_high_bw = 1。filtered : 采样频率为带宽的两倍,data_high_bw = 0。filtered加速度数据带宽由0x10寄存器的bw位决定。另外,bma250支持四种不同的加速度度量范围(range),分别为±2g、±4g、±8g和±16g,这由0x0F寄存器的低四位决定。
6.4.2 温度 由0x08寄存器决定,温度范围为-40°C ~ 87.5°C。
6.5 偏移补偿
偏移补偿就是根据ADC值对加速度数据作出一个补偿。不过用于补偿的公共寄存器(public register)0x38 ~ 0x3D对每个轴用于读写补偿的只有8 bit,而内部寄存器(internal register)对于每个加速度值都是用10 bit(MSB + LSB)来存储的。在这之间需要一个转换,补偿转换原则如图2所示。
图2 8-bit/10-bit补偿转换原则图
从上图可以看出,根据设定的range,公共寄存器的补偿值将要对输出的加速度数据作出补偿。例如,公共寄存器值为00000001b,从图2中得到加速度数据补偿为±7.8mg,对应内部寄存器为±2 LSB。
bma250支持四种偏移补偿方式:慢补偿、快补偿、手动补偿和内嵌校准。对于慢补偿和快补偿来说,0x37寄存器的offset_target_x、offset_target_y、offset_target_z决定了sensor在x、y、z轴上的目标值。而0x36的offset_reset位如果被置为1,则所有的偏移补偿寄存器都reset为0。
6.5.1 慢补偿
如果连续一定数量的采样加速度大于(小于)0x37寄存器所设置的目标值,偏移补偿值寄存器0x38 ~ 0x3D的值减少(增加)4 LSB。加速度数据的采样周期由0x37的cut_off位决定,如果cut_off位为0,采样周期为8,反之为16。
在x、y、z轴是否采用慢补偿分别由寄存器0x36中的hp_x_en、hp_y_en和hp_z_en决定。
6.5.2 快补偿
连续取16个加速度值,计算出平均值,将这个平均值与0x37寄存器所设置的目标值的差值写入0x37寄存器中。0x36寄存器的cal_trigger决定了对哪个轴实行快补偿。cal_trigger = 00b none cal_trigger = 01b x轴 cal_trigger = 10b y轴 cal_trigger = 11b z轴
当正在进行快补偿时,慢补偿被阻断。
6.5.3 手动补偿
通过数字接口去手动设置补偿值。在手动补偿时要注意不要影响正在进行的偏移补偿,所以一般在获得new data中断后立刻执行。在慢补偿有效或快补偿正在运行时,不允许对偏移补偿寄存器作出写操作。
6.5.4 内嵌校准
通过前面所提到的偏移补偿方法计算出合适的补偿值,然后将这些补偿值存放在EEPROM中。在每一次设备重置时,补偿值被load进image寄存器作为偏移补偿,直到它被用其他的补偿方式重写。
6.6 非易失性存储器
bma250具有三种存储器:hard-wired、valatile和non-valatile。non-valatile存储器通过EEPROM来实现,可以通过访问image寄存器来访问它。在bma250中共有八个image寄存器(0x38 ~ 0x3F),0x38 ~ 0x3D被用作存储偏移补偿值,0x3E和0x3F保留备用。
在两种情况下EEPROM内容会被load到image寄存器中去,一种情况是设备重置时,另一种情况是将0x33寄存器中的nvm_load值置为1。只要image寄存器还没有load完成,nv_ram位就为1,在完成后被置成0。image寄存器可以像其他寄存器一样执行读写操作。
写EEPROM要经过三步:
1)将新内容写入image寄存器;
2)将0x33寄存器中的nvm_prog_mode置成1,unlock EEPROM ;
3)将0x33寄存器中的nvm_prog_trig置为1,并保持nvm_prog_mode为1,激活写操作。
每次写EEPROM总是要更新整个EEPROM内容,可以通过读取0x33寄存器的nvm_rdy位来检测写状态。nvm_rdy等于1,表示写操作正在进行中;nvm_rdy等于0,表示写操作已完成。在写EEPROM时,电源模式必须处于normal mode,而且不能对image寄存器作出更新。
6.7 中断控制器
中断的清除取决于所选择的中断模式,bma250支持三种模式:non-latched、latched和temporary。中断模式可以通过0x21寄存器的低四位latch_int来设置,具体对应关系如表1所示。
只要满足中断激活条件,就会产生一个中断。在non-latched模式下,除了new data、orientation和flat中断会在一个固定时间后自动重置,其他中断只要激活条件不再有效,中断状态位和所选择的pin脚(INT1 and/or INT2)就会立刻被清除。
latched模式下,必须通过将0x21寄存器的reset_init位置为1来清除中断状态和pin脚。清除时如果中断条件仍然保持,中断状态会在加速度寄存器下次变化时再次被宣称。
表1 中断模式选择对应表
在temporary模式下,中断状态和pin脚在一段时间后被清除,这段时间由latch_int的具体值来决定。
bma250支持七种中断机制,分别为:new data、tap、slope、high-g、low-g、orientation和flat。其中,new data、tap、slope、high-g和low-g既可以使用filtered data也可以使用unfiltered data作为输入,这分别由0x1E寄存器的int_src_data、int_src_tap、int_src_slope、int_src_high和int_src_low位决定。而orientation和flat只能使用filtered data作为输入。
可以通过0x19 ~ 0x1B寄存器来建立中断类型到中断引脚的映射关系。例如,将0x19寄存器的int1_flag位置为1,就是建立了flat中断到INT1中断引脚的映射关系。
6.7.1 new data中断
new data中断提供了一个同步读取加速度数据的服务。它在将一个z轴的新值写入数据寄存器时产生,在下一次数据获取循环开始的时候自动清除。通过将0x17寄存器的data_en位置为1来使能new data中断,中断状态存储在0x0A的data_int位中。
6.7.2 slope中断
在sensor移动中产生连续的加速度信号,当一定数量的连续两个加速度信号的值差大于一个预设的临界值时,会产生一个slope中断。而在一定数量的连续两个加速度信号的值差小于这个预设值时,中断被清除。可以通过0x28寄存器的slope_th位来设置这个临界值,1 LSB slope_th对应1 LSB的加速度数据。
连续两个加速度信号的取值时间差取决于所选择的带宽,Δt=1/(2*bw)。而取值的数量由0x27寄存器的slope_dur来决定,N = slope_dur + 1。
通过设置0x16寄存器的slope_en_x、slope_en_y和slope_en_z为1来使能在x、y、z轴上的slope中断。在dedicated mode唤醒状态时,三个轴的slope中断都被使能,0x16寄存器上的这三个值无效。
中断状态存储在0x09的slope_int位。检测到的slope中断状态信息存放在0x0B寄存器中,slope_first_x、slope_first_y、slope_first_z分别标识了触发中断的轴状态,slope_sign位标识了正负(0为正,1为负)。
6.7.3 tap中断
tap中断类似于鼠标按键的click,当至少在一个轴上的加速度slope大于一个预定义的值时,该中断产生。
有两种tap事件:single tap和double tap。single tap是在一次tap事件后,紧跟着是一段安静时间,类似于鼠标的单击;而double tap是在一段定义好的时间内,连续发生两次tap事件,类似于鼠标的双击。
同一时间,只能有一种tap中断被使能。0x16寄存器的s_tap_en为1表示single tap使能,d_tap_en为1表示double tap使能。如果s_tap_en和d_tap_en同时被置为1,s_tap_en无效,只有double tap使能。
single tap的中断状态存储在0x09寄存器的s_tap_int位,double tap的中断状态存储在0x09寄存器的d_tap_int位。
通过设置0x2B寄存器的tap_th值来设置tap中断的slope预设临界值,在不同的range下,slope_th表示不同的数值。1 LSB的tap_th对应于2g-range下的62.5mg slope,4g-range下的125mg slope,8g-range下的250mg slope,16g-range下的500mg slope。
0x2A寄存器的tap_shock和tap_quiet对single tap和double tap都是有效的,tap_dur仅对double tap有效。在tap_shock的持续时间里,任何新的tap事件都会被忽略;在slope_quiet的持续时间里,不能有新的tap事件发生,否则第一次tap事件将会被取消。tap_shock为0(1)表示tap_shock的持续时间为50 ms(75 ms),tap_quiet为0(1)表示tap_quiet的持续时间为30 ms(20 ms)。tap_dur的持续时间如表2所示。
表2 tap_dur对应持续时间值表
tap中断产生后12.5 ms后会被自动清除。
0x0B 寄存器的tap_sign位标识了触发tap中断的第一次tap的slope正负(0为正,1为负)。触发中断的轴状态分别被存放在0x0B寄存器的tap_first_x、tap_first_y和tap_first_z中。
在低电模式(low-power mode)下,在唤醒状态时,对取样数量有一个限制,这由0x2B寄存器的tap_samp决定。tap_samp = 00b(01b、10b、11b),表示取样数量为2(4、8、16)。
6.7.4 orientation中断
orientation表示的是sensor相对于重力域矢量g的方向变化,假设z轴与g的夹角为θ,x轴与水平方向的夹角为φ,如下图3所示。
图3 sensor角度矢量图
假设x、y、z轴上的加速度分别为acc_x、acc_y和acc_z,则可以得到: acc_x = 1g * sinθ * cosφ acc_y =-1g * sinθ * sinφ acc_z = 1g * cosθ
有三种不同的orientation计算模式,在这三种模式下具有不同的转换临界值。这三种模式分别为:对称、高非对称和低非对称。这取决于0x2C寄存器的orient_mode值。orient_mode等于01b,工作在高非对称模式;orient_mode等于10b,工作在低非对称模式;否则,工作在对称模式。对于每一种orienttaion模式,0x0C寄存器的orient表示不同的意思。三种模式下,orient的意思分别如表
3、表
4、表5所示。
图3 对称模式下的0x0C寄存器orient意思示表
图4 高非对称模式下的0x0C寄存器orient意思示表
图5 低非对称模式下的0x0C寄存器orient意思示表
在以上三张表中,‘hyst’ 表示一个延迟,这个值通过0x0C寄存器的orient_hyst设置。1 LSB的orient_hyst对应于62.5 mg。
通过将0x16寄存器的orient_en位置为1来使能orientation中断。当0x0C寄存器的orient发生变化时,中断产生,一段之间后自动清除。中断状态存储在0x09寄存器的orient_int位。
0x0C寄存器的orient位发生变化后,中断的产生可能会被阻断,这可以通过0x2C寄存器的orient_blocking进行设置。
orient_blocking = 00b表示没有阻断; orient_blocking = 01b表示θ阻断;
orient_blocking = 10b表示θ阻断,或任一轴上的加速度slope > 0.2 g;
orient_blocking = 11b表示θ阻断,或任一轴上的加速度slope > 0.4 g,或orient值超过100 ms一直不稳定。
θ阻断通过下面的不等式定义:
参数blocking_theta由0x2D寄存器的orient_theta决定,由此可知阻断角度θ的取值范围为0° ~ 44.8°。
6.7.5 flat中断
flat中断,顾名思义,用于检测sensor是否位于水平状态,判断设备位于水平状态的条件时:
参数parameter_theta由0x2E寄存器的flat_theta决定,由此可知,flat角度可能取值范围也是0° ~ 44.8°。
通过将0x16寄存器的flat_en置为1使能flat中断。当0x0C寄存器的flat值发生变化,并且这个值保持稳定一段时间后,flat中断产生。保持稳定的时间的最小值由0x2F寄存器的flat_hold_time决定。flat_hold_time等于00b、01b、10b、11b分别表示0、512 ms、1024 ms、2048 ms。这个中断在一段之间后自动清除。flat中断状态存储在0x09寄存器的flat_int位。
6.7.6 low-g中断
这个中断是基于比较加速度数据和low-g临界值而产生的,比如对自由落体的侦测。通过设置0x17寄存器的low_en位使能该中断。low-g中断有两种模式:single mode和sum mode。single mode是只比较某一个轴的加速度,而sum mode是比较三个轴加速度的绝对值总和。这由0x24寄存器的low_mode位决定,0表示single mode,1表示sum mode。
low-g临界值通过0x23寄存器的low_th设置,1 LSB low_th对应于7.81 mg的加速度。可以通过设置0x24寄存器的low_hy来设置滞后值,1 LSB low_hy对应于125 mg的加速度值。
当某一轴上的加速度绝对值(single mode)或三轴加速度绝对值的总和(sum mode)小于临界值超过一段时间(这个时间由0x22寄存器的low_dur决定)后,中断产生;当某一轴上的加速度绝对值(single mode)或三轴加速度绝对值的总和(sum mode)大于临界值减去滞后值至少超过一个数据的获取时间后,中断被重置。
low-g中断产生前的延迟时间与low_dur的关系为:delay [ms] = [(0x22)low_dur + 1] * 2 ms。所以,延迟时间的可能范围为2 到512 ms。
6.7.7 high-g中断
这个中断是基于比较加速度数据和high-g临界值而产生的,主要用于对震动或其他的一些高加速度事件的侦测。
通过设置0x17寄存器的hign_en_x、high_en_y和high_en_z使能sensor在x、y和z轴上的high-g终端。
high-g临界值通过0x26寄存器的high_th设置,1 LSB high_th对应于2g-range下的7.81mg,4g-range下的15.63mg,8g-range下的31.25mg,16g-range下的62.5mg。可以通过设置0x24寄存器的high_hy来设置滞后值,1 LSB high_hy对应于2g-range下的125mg,4g-range下的250mg,8g-range下的500mg,16g-range下的1000mg。
当至少一个使能轴的加速度绝对值高于high-g临界值超过一段时间(这个时间由0x25寄存器的high_dur决定)后,中断产生。当所有使能轴的加速度绝对值都低于high-g临界值减去滞后值一段时间(由0x25寄存器的high_dur决定)后,中断被重置。
high-g中断产生前的延迟时间与high_dur的关系为:delay [ms] = [(0x22)high_dur + 1] * 2 ms。所以,延迟时间的可能范围为2 到512 ms。
7.sensor驱动开发步骤 综上所述,sensor驱动开发可以归纳为以下步骤来完成: 1.read && verify spec,确认chip功能以及register配置;
2.按照spec所述,在code中加入主要的配置数据,如电源模式、主要寄存器的配置、range支持、bandwidth支持、中断配置等; 3.设计主要的数据结构;
4.实现sensor数据的设置和获取接口;
5.通过module_init和module_exit接口加入driver层入口和出口函数函数;
6.定义i2c_driver,设置driver的probe和remove函数入口,通过i2c_add_driver和i2c_del_driver接口添加/移除driver; 7.实现driver的probe函数功能,函数中实现sensor的硬件配置,并通过linux的input子系统实现sensor数据的上报;
8.实现driver的remove函数功能,函数中实现设备的移除、资源的释放等。
9.HAL层中注册hw_module_t结构体,将结构体名称命名为HAL_MODULE_INFO_SYM;
10.判断sensor设备文件是否存在,通过open_input_device访问设备文件,并注册设备文件的poll、activate、close等函数; 11.实现sensor list的获取函数;
12.实现对设备文件的主要操作接口,完成上层与硬件设备的通信; 13.调试sensor功能。附录 bma250寄存器表