引言
当今的电子消费领域,重力感应技术正以其迅猛的速度在发展。它在游戏机、移动存储设备、智能电动车中有着广泛的应用;在高端智能手机、平板电脑等嵌入式移动产品中,其应用也越来越普及,它将给人们带来更为方便、有趣、实用、丰富的全新体验。
MX51是飞思卡尔半导体基于ARM CortexA8内核的高端ARM嵌入式多媒体处理器,它支持丰富的多媒体功能组合和外围设备,处理器接口支持与所有常用外部存储的连接,在工作和各种低功耗模式下实现最小的系统功耗,满足操作系统和游戏系统越来越多的MIPS需求。嵌入式Linux操作系统以其免费、开源及功能强大等特点,被广泛应用于各类便携式产品中,由于Linux操作系统只提供相关设备的驱动接口,实际应用中,需针对具体芯片开发相关的驱动程序。本文以Linux2.6.31内核和MX51为系统的软、硬件平台,讨论了重力感应驱动程序的实现技术。
1 Gsensor概述
Gsensor表示重力传感器,它是一种可以将运动或重力转换为电信号的传感器。重力感应利用压电效应实现,通过测量内部一片重物(重物和压电片做成一体)重力正交两个方向的分力大小,来判断水平方向。Gsensor内置3轴(X、Y和Z轴)加速计,实际应用中通常以这3个轴(或任意两个轴)所构成的角度来计算物体的倾斜角度,从而计算出重力加速度的值。
当物体位置发生变化,如晃动、跌落、旋转、上升、下降时,其所产生的加速力被Gsensor转化为电信号,通过微处理器的计算分析后,可完成设计中的特定功能。重力感应技术应用在移动存储设备上,利用重力加速度原理,一旦侦测到意外,能在摔落的瞬间将磁头撤至安全停泊区,有效确保工作状态下的产品安全。在手机中应用此项技术,可以根据使用者的动作实现屏幕图像翻转、功能选择、游戏控制等。
2 硬件平台
本设计采用飞思卡尔半导体的MX51多媒体应用处理器开发板。处理器集成了3个I2C接口:I2C1和I2C2是普通I2C总线,HSI2C是高速I2C总线。Gsensor芯片LIS33DE通过I2C2接口连接到MX51的总线上。图1为硬件连接图。

图1 LIS33DE的硬件平台
LIS33DE是ST(意法半导体)公司的重力感应芯片,这是一款超紧凑,低功耗的三维线性加速度传感器。芯片采用LGA封装,16引脚,3×3×1 mm3大小,要求的额定输入电压范围为2.16~3.6 V,电路中的供电电压为2775 V。LIS33DE的功耗小于1 mW,控制接口支持I2C和SPI接口。设计中采用I2C接口进行数据通信。LIS33DE内置可编程中断发生器,软件可采用中断或查询方式读取重力感应的状态信息。
LIS33DE的主要寄存器包括控制寄存器CTRL_REG1~CTRL_REG3,状态寄存器STATUS_REG,数据寄存器OUT_X、OUT_Y和OUT_Z。它们用来控制Gsensor的行为,存放加速度的值。系统通过I2C接口访问这些寄存器。
控制寄存器CTRL_REG1(20h): DR位(bit7)控制输出数据的速率,可以选择100 Hz或400 Hz; PD位(bit6)控制芯片的工作状态:power down或active;Zen、Yen和Xen位(bit2~0),控制Z、Y和X轴的使能:1为enable,0为disable。
控制寄存器CTRL_REG2(21h): BOOT位(bit6)控制为复位寄存器内容,0为正常模式,1为复位寄存器内容。BOOT位用来恢复LIS33DE寄存器的默认值。
控制寄存器CTRL_REG3(22h): IHL位(bit7)控制中断有效电平,0为高电平有效,1为低电平有效。
状态寄存器STATUS_REG(27h):高4位表示X、Y、Z轴的数据是否有溢出,低4位表示X、Y、Z轴的数据是否有效。
数据寄存器OUT_X(29h):以二进制补码格式存储X轴数据。
数据寄存器OUT_Y(2Bh):以二进制补码格式存储Y轴数据。
数据寄存器OUT_Z(2Dh):以二进制补码格式存储Z轴数据。
3 Gsensor驱动程序设计
当物体位置发生变化时,LIS33DE实时检测、判断出各个方向轴上重力加速度的变化,并将其值存入数据寄存器OUT_X、OUT_Y和OUT_Z。驱动程序装载运行后,以查询方式定时调用工作队列中的任务处理函数:读数据寄存器,向系统报告事件及变化的坐标值。
MX51通过I2C2接口与LIS33DE芯片进行数据通信,使用一个总线设备,除了为其设计驱动程序,还须向Linux内核注册此设备。
3.1 I2C设备注册
在arch/arm/machmx51/mx51_3stack.c中,定义struct i2c_board_info结构变量,用于描述I2C设备特性:
static struct i2c_board_info mxc_i2c1_board_info[] __initdata={
{.type="lis33de",
.addr=0x1c,
……
},
};
其中type为定义的I2C设备名称,addr为I2C设备地址。然后,调用如下函数注册此设备:
i2c_register_board_info(1,mxc_i2c1_board_info,ARRAY_SIZE(mxc_i2c1_board_info));
系统初始化时,会根据板级I2C设备配置信息,创建I2C客户端设备i2c_client,并将其添加到I2C子系统中。
3.2 I2C设备驱动注册
I2C总线上的数据通信由I2C设备驱动实施,设备驱动通过I2C总线与具体设备进行交互。一个设备驱动需由2个结构struct i2c_driver和struct i2c_client来描述。其中i2c_driver表示一个I2C设备驱动,i2c_client表示使用i2c_driver驱动的设备。
在drivers/i2c/chips/lis33de.c中作如下定义:
static struct i2c_driver lis33de_driver={
.probe=lis33de_probe,//探测函数
.remove=lis33de_remove,//卸载函数
.id_table=lis33de_id,//将驱动与已定义的I2C设备相关联
.driver={
.name=LIS33DE_I2C_NAME,
},
};
lis33de_driver表示管理I2C设备的驱动程序。在驱动入口函数lis33de_init中调用函数i2c_add_driver(&lis33de_driver)注册这个设备驱动。驱动成功加载后,系统会自动调用探测函数lis33de_probe ,该函数原型如下:
static int lis33de_probe(struct i2c_client *client,const struct i2c_device_id *id);
其中参数i2c_client *client表示此前已向系统注册过的I2C设备,即lis33de芯片。
3.3 Linux输入子系统
Linux内核提供了输入子系统(Input Subsystem),常用输入设备如键盘、鼠标、触摸屏等都可以利用输入子系统的接口函数来实现设备驱动,重力感应驱动亦可纳入输入子系统框架。输入子系统由核心层、驱动层和事件处理层三部分组成,它们之间通过事件进行通信。我们将重力感应芯片LIS33DE作为一个输入设备,利用输入子系统实现重力感应驱动,需做以下工作:
◆ 在驱动模块加载函数中申请一个输入设备。
◆ 设置输入子系统所支持的事件类型。
◆ 设置坐标的取值范围。
◆ 注册输入设备。
◆ 报告发生的事件及对应的坐标。
3.4 主要功能函数设计
通过输入子系统实现重力感应驱动,需要在drivers/i2c/chips/lis33de.c中定义设备结构,对lis33de进行描述:
struct lis33de_data{
struct input_dev *input;
struct workqueue_struct *gsensor_wq;
struct delayed_work gsensor_work;
};
结构input_dev定义在<Linux/input.h>中,用于描述输入设备的驱动结构,主要包括响应的事件类型、按键内容、数据相对值的范围及一些处理函数。设备驱动的主要任务在以下函数中完成。
3.4.1 探测函数lis33de_probe
在入口函数lis33de_init中成功加载设备驱动后,探测函数lis33de_probe开始执行。函数主要实现以下操作:
① 定义结构指针“struct lis33de_data *sensor_data;”和“struct input_dev *input;”,申请设备所需空间。
sensor_data=kzalloc(sizeof(struct lis33de_data), GFP_KERNEL);
input=input_allocate_device();
② 设置事件类型。
input﹥evbit[0]=BIT_MASK(EV_ABS); // EV_ABS表示支持绝对坐标
③ 设置 X、Y、Z三个方向的坐标取值范围。
input_set_abs_params(g_sensor﹥input, ABS_X, -1872, 1872, 0, 0);
input_set_abs_params(g_sensor﹥input, ABS_Y, -1872, 1872, 0, 0);
input_set_abs_params(g_sensor﹥input, ABS_Z, -1872, 1872, 0, 0);
④ 注册输入设备。
input_register_device(input);
⑤ 创建工作队列,构建任务并提交到工作队列。
glb_sensor﹥gsensor_wq=create_singlethread_workqueue("gsensor_wq");
INIT_DELAYED_WORK(&glb_sensor﹥gsensor_work, sensor_workqueue_func);
queue_delayed_work(glb_sensor﹥gsensor_wq, &glb_sensor﹥gsensor_work, LONG_DELAY_TIME);
其中glb_sensor 为已定义的全局struct lis33de_data 类型的指针,glb_sensor﹥gsensor_work为任务名称,sensor_workqueue_func为函数名,表示执行该任务时从工作队列中调用的函数。用queue_delayed_work函数提交任务到工作队列,LONG_DELAY_TIME为指定的delay,定义为HZ*12,在lis33de_probe函数调用12 s后,自动执行任务处理函数sensor_workqueue_func。
⑥ 电源管理。
在嵌入式手持设备里,电源管理的实现和节电非常重要,重力感应驱动需要很好的电源管理。由于LIS33DE属于输入设备,我们需要实现early suspend和late resume。对已定义的全局变量“struct early_suspend gsensor_early_suspend;”进行初始化:
gsensor_early_suspend.suspend=lis33de_early_suspend;
gsensor_early_suspend.resume=lis33de_late_resume;
register_early_suspend(&gsensor_early_suspend);
lis33de_early_suspend()和lis33de_late_resume()函数是相应电源管理系统休眠时和唤醒后分别调用的函数。
在lis33de_early_suspend()函数中,首先使LIS33DE进入Power Down状态,然后取消对应的工作队列。
lis33de_write_reg(0x20, 0x27);//关机模式
cancel_rearming_delayed_work(&glb_sensor﹥gsensor_work);
在lis33de_late_resume()函数中,首先写LIS33DE的寄存器0x20,使其进入active模式,然后把工作队列加入工作队列。
lis33de_write_reg(0x20, 0x67);//工作模式
queue_delayed_work(glb_sensor﹥gsensor_wq, &glb_sensor﹥gsensor_work, DELAY_TIME);
3.4.2 数据处理函数sensor_workqueue_func
lis33de_probe函数执行12 s后,队列中的任务处理函数sensor_workqueue_fun被首次调用。函数的主要任务是读取Gsensor数据寄存器中的重力感应数据,经处理后向上层发送:
lis33de_read_reg(0x29, &DX8);
lis33de_read_reg(0x2B, &DY8);
lis33de_read_reg(0x2D, &DZ8);
读取的值在变量DX8、DY8、DZ8中,用以下代码对坐标值进行处理。
x=(DX8 & 0x80) ? (-(256-DX8)):(DX8);
y=(DY8 & 0x80) ? (-(256-DY8)):(DY8);
z=(DZ8 & 0x80) ? (-(256-DZ8)):(DZ8);
通过input_report_abs( )向上层发送。
input_report_abs(glb_sensor﹥input, ABS_X, x);
input_report_abs(glb_sensor﹥input, ABS_Y, y);
input_report_abs(glb_sensor﹥input, ABS_Z, z);
input_sync(glb_sensor﹥input);//此次报告结束
为了持续不断地查询下去,在sensor_workqueue_func( )函数的最后,再次提交任务到工作队列:
queue_delayed_work(glb_sensor﹥gsensor_wq, &glb_sensor﹥gsensor_work, DELAY_TIME);其中DELAY_TIME定义为40 ms。以后,系统以固定的时间间隔(40 ms)反复调用任务处理函数,只要系统运行,就存在重力感应的输入数据报告。
3.4.3 设备初始化函数lis_init
函数主要实现对LIS33DE寄存器初始化。
lis33de_write_reg(0x21, 0x40);//复位寄存器内容
mdelay(3);
lis33de_write_reg(0x20, 0x67);//工作模式
lis33de_write_reg(0x21, 0x00);
lis33de_write_reg(0x22, 0x84);//数据准备就绪
lis33de_write_reg(0x30, 0x2A);//使能Z,Y,X的中断
lis33de_write_reg(0x32, 0x02);//设置模式寄存器为缺省值
lis33de_write_reg(0x33, 0x01);//设置中断持续时间为2.5 ms
lis33de_read_reg(0x31, &tmp);
结语
在android 2.2系统下进行重力感应驱动测试。打开gallery图片浏览程序,旋转平板电脑,可以发现,图片也随之旋转,能够始终保持正确的浏览姿态。
MX51处理器通过I2C接口与LIS33DE重力感应芯片连接,以Linux输入设备驱动形式开发该驱动,设计中使用了查询、队列等技术。本文对重力感应驱动程序的架构及主要实现技术进行了讨论,该驱动有着较为清晰的结构,针对具体硬件,修改底层初始化和坐标读取代码,可以方便地移植到多种嵌入式Linux系统平台。