1)实验平台:正点原子ATK-DLRK3568开发板
2)平台购买地址:https://detail.tmall.com/item.htm?id=731866264428
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/docs/boards/xiaoxitongban
第四十四章 SH3001驱动实验
随着智能科技的飞速进步,SH3001传感器在多个应用方向上扮演着关键角色。其高精度的加速度和角速度测量能力使其在健康监测领域具有出色表现,为智能手环、智能手表等设备提供精准的活动数据。在虚拟现实和增强现实领域,SH3001传感器的快速响应能力为更逼真的交互体验创造了条件。此外,它在工业自动化和机器人技术中也有用武之地,实时的姿态测量有助于提高生产效率和设备安全。无论是健康、娱乐还是工业,SH3001传感器的功能为智能设备的性能和应用拓展提供了强有力的支持。本章依然运用的是I2C,让我们一起来学习通过I2C驱动SH3001。
44.1 SH3001简介
SH3001是一种高集成度、低功耗的6轴惯性测量单元(IMU)。在一个芯片上同时测量加速度和角速度。该设备集成了:
16位数字3轴加速度计±2g,±4g,±8g和±16g范围
16位数字3轴陀螺仪,具有±125d ps,±250d ps,±500d ps,±1000d ps,±2000d ps范围。
SH3001有以下特点和优点:
紧凑和小尺寸,14针LGA封装3.0×2.5 mm2空间。
宽工作温度范围:-40℃- 85℃
宽供电范围:VDD 1.71V-3.60V,独立VDDIO 1.71V-3.60V
用户可编程的低通滤波器加速度计和陀螺仪
用户可编程中断
片上数字输出温度传感器
可配置辅助数字接口的AUX设备
1kB片上FIFO加速度计,陀螺仪,温度和AUX传感器数据
2个独立的可编程I/O引脚中断
符合RoHS标准,无卤素和无铅
SH3001应用场景:
移动电话
智能手表、手环和健身追踪器
支持运动的游戏和应用程序框架
基于动作的游戏控制器
增强和虚拟现实眼镜和控制器
姿态监测
智能玩具
SH3001具有主接口(可配置I2C和SPI)和辅助接口。从接口只支持I2C接口。缺省情况下,SH3001工作在I2C模式。
SH3001引脚分布图如下。
图44.1.1 SH3001引脚分布图
关于SH3001的详细寄存器和位的介绍请参考SH3001的寄存器手册,如下图,SH3001的寄存器有相关介绍。
图44.1.2 部分寄存器
44.2 硬件原理图
ATK-DLRK3568开发板SH3001挂载在I2C5总线上,器件地址为0x36。
图44.2.1 硬件连接图
图44.2这是SH3001的硬件原理图。正点原子ATK-DLRK3568开发板的GPIO3_B3、GPIO3_B4和GPIO1_B0分别连接到SH3001的SCK、SDA和INT。其中INT为SH3001的中断引脚,但是我们在驱动程序中不使用此中断,因中断频率太高,可能会影响系统稳定性。
44.3 实验程序编写
本实验对应的例程路径为:开发板光盘01、程序源码03、Linux驱动例程28_sh3001。
44.3.1 修改设备树
1、添加或者查找SH3001所使用的IO的pinmux配置
在rk3568-atk-evb1-ddr4-v10.dtsi文件中添加中断IO配置信息,SH3001连接到了ATK-DLRK3568的I2C5接口上,并且中断IO为GPIO1_B0,虽然我们在驱动里没有用到,我们也要先配置。
示例代码44.3.1.1 中断IO的pinmux配置
1 sh3001 {
2 sh3001_irq_gpio: sh3001-irq-gpio {
3 rockchip,pins = <1 RK_PB0 RK_FUNC_GPIO &pcfg_pull_none>;
4 };
5 };
示例代码44.3.1.1里1~5行是设置SH3001的中断IO,直接复用为GPIO,设置不上拉也不下拉。
2、在I2C5节点下添加pinmux并追加SH3001子节点
在rk3568-atk-evb1-ddr4-v10.dtsi文件,追加I2C5节点,追加如下所示内容:
示例代码44.3.1.2 I2C5追加的内容
1 &i2c5 {
2 ......
3 sh3001_acc@36 {
4 compatible = "sh3001_acc";
5 reg = <0x36>;
6 type = <SENSOR_TYPE_ACCEL>;
7 pinctrl-names = "default";
8 pinctrl-0 = <&sh3001_irq_gpio>;
9 irq-gpio = <&gpio1 RK_PB0 IRQ_TYPE_LEVEL_HIGH>;
10 irq_enable = <0>;
11 poll_delay_ms = <30>;
12 layout = <1>;
13 status = "okay";
14 };
15
16 sh3001_gyro@36 {
17 compatible = "sh3001_gyro";
18 reg = <0x36>;
19 type = <SENSOR_TYPE_GYROSCOPE>;
20 //pinctrl-names = "default";
21 //pinctrl-0 = <&sh3001_irq_gpio>;
22 irq-gpio = <&gpio1 RK_PB0 IRQ_TYPE_LEVEL_HIGH>;
23 irq_enable = <0>; //Must be set to 0, disable interrupt
24 poll_delay_ms = <30>;
25 layout = <1>;
26 status = "okay";
27 };
28 };
第4行,兼容属性设置为“sh3001_acc”,可以看出这是一个加速度,这里加速度与陀螺仪(角速度)是分开的。
第5行,SH3001的器件地址为0x36。
第6行,设置类型为“SENSOR_TYPE_ACCEL”这个类型是RK内定的sensor类型。
第7~10行,设置pimmux及禁用中断。
第11行,设置轮询的时间为30ms,驱动里不用中断,用了轮询。
第12行,设置传感器的方向,1代表原方向。
第16~27行,同加速度的解释一样,这里换成了陀螺仪。
44.3.2 编写SH3001驱动
SH3001的驱动正点原子已经写好,在内核源码路径drivers/input/sensors/下,所以本章不用自己再重复编写。我们先来看看SH3001的寄存器定义,头文件是内核源码路径下的include/linux/sh3001.h文件,如下内容(有省略,详细请查看源码)。
示例代码44.3.2.1 SH3001的寄存器定义
1 #ifndef __SH3001_H
2 #define __SH3001_H
3
4
5 /******************************************************************
6 * SH3001 Registers Macro Definitions
7 ******************************************************************/
8 #define SH3001_ACC_XL (0x00)/* 低字节的acc x轴数据*/
9 #define SH3001_ACC_XH (0x01) /* 高字节的acc x轴数据*/
10 #define SH3001_ACC_YL (0x02) /* 详细SH3001手册有说明*/
11 #define SH3001_ACC_YH (0x03)
12 #define SH3001_ACC_ZL (0x04)
13 #define SH3001_ACC_ZH (0x05)
14 #define SH3001_GYRO_XL (0x06)
15 #define SH3001_GYRO_XH (0x07)
16 #define SH3001_GYRO_YL (0x08)
17 #define SH3001_GYRO_YH (0x09)
18 #define SH3001_GYRO_ZL (0x0A)
19 #define SH3001_GYRO_ZH (0x0B)
20 #define SH3001_TEMP_ZL (0x0C)
21 #define SH3001_TEMP_ZH (0x0D)
22 #define SH3001_CHIP_ID (0x0F)
23 #define SH3001_INT_STA0 (0x10)
24 #define SH3001_INT_STA1 (0x11)
25 #define SH3001_INT_STA2 (0x12)
26 #define SH3001_INT_STA3 (0x14)
27 #define SH3001_INT_STA4 (0x15)
28 #define SH3001_FIFO_STA0 (0x16)
29 #define SH3001_FIFO_STA1 (0x17)
30 #define SH3001_FIFO_DATA (0x18)
31 #define SH3001_TEMP_CONF0 (0x20)
32 #define SH3001_TEMP_CONF1 (0x21)
33
34 #define SH3001_ACC_CONF0 (0x22)// accelerometer config 0x22-0x26
35 #define SH3001_ACC_CONF1 (0x23)
36 #define SH3001_ACC_CONF2 (0x25)
37 #define SH3001_ACC_CONF3 (0x26)
38
39 #define SH3001_GYRO_CONF0 (0x28) // gyroscope config 0x28-0x2B
40 #define SH3001_GYRO_CONF1 (0x29)
41 #define SH3001_GYRO_CONF2 (0x2B)
42
43 #define SH3001_SPI_CONF (0x32)
44 #define SH3001_FIFO_CONF0 (0x35)
45 #define SH3001_FIFO_CONF1 (0x36)
46 #define SH3001_FIFO_CONF2 (0x37)
47 #define SH3001_FIFO_CONF3 (0x38)
48 #define SH3001_FIFO_CONF4 (0x39)
49 #define SH3001_MI2C_CONF0 (0x3A)
50 #define SH3001_MI2C_CONF1 (0x3B)
51 #define SH3001_MI2C_CMD0 (0x3C)
52 #define SH3001_MI2C_CMD1 (0x3D)
53 #define SH3001_MI2C_WR (0x3E)
54 #define SH3001_MI2C_RD (0x3F)
55 #define SH3001_INT_ENABLE0 (0x40)
56 #define SH3001_INT_ENABLE1 (0x41)
57 #define SH3001_INT_CONF (0x44)
58 #define SH3001_INT_LIMIT (0x45)
59 #define SH3001_ORIEN_INTCONF0 (0x46)
60 #define SH3001_ORIEN_INTCONF1 (0x47)
61 #define SH3001_ORIEN_INT_LOW (0x48)
62 #define SH3001_ORIEN_INT_HIGH (0x49)
63 #define SH3001_ORIEN_INT_SLOPE_LOW (0x4A)
64 #define SH3001_ORIEN_INT_SLOPE_HIGH (0x4B)
65 #define SH3001_ORIEN_INT_HYST_LOW (0x4C)
66 #define SH3001_ORIEN_INT_HYST_HIGH (0x4D)
67 #define SH3001_FLAT_INT_CONF (0x4E)
68 #define SH3001_ACT_INACT_INT_CONF (0x4F)
69 #define SH3001_ACT_INACT_INT_LINK (0x50)
70 #define SH3001_TAP_INT_THRESHOLD (0x51)
71 #define SH3001_TAP_INT_DURATION (0x52)
72 #define SH3001_TAP_INT_LATENCY (0x53)
73 #define SH3001_DTAP_INT_WINDOW (0x54)
74 #define SH3001_ACT_INT_THRESHOLD (0x55)
75 #define SH3001_ACT_INT_TIME (0x56)
76 #define SH3001_INACT_INT_THRESHOLDL (0x57)
77 #define SH3001_INACT_INT_TIME (0x58)
78 #define SH3001_HIGHLOW_G_INT_CONF (0x59)
79 #define SH3001_HIGHG_INT_THRESHOLD (0x5A)
80 #define SH3001_HIGHG_INT_TIME (0x5B)
81 #define SH3001_LOWG_INT_THRESHOLD (0x5C)
82 #define SH3001_LOWG_INT_TIME (0x5D)
83 #define SH3001_FREEFALL_INT_THRES (0x5E)
84 #define SH3001_FREEFALL_INT_TIME (0x5F)
85 #define SH3001_INT_PIN_MAP0 (0x79)
86 #define SH3001_INT_PIN_MAP1 (0x7A)
87 #define SH3001_INACT_INT_THRESHOLDM (0x7B)
88 #define SH3001_INACT_INT_THRESHOLDH (0x7C)
89 #define SH3001_INACT_INT_1G_REFL (0x7D)
90 #define SH3001_INACT_INT_1G_REFH (0x7E)
91 #define SH3001_SPI_REG_ACCESS (0x7F)
92 #define SH3001_GYRO_CONF3 (0x8F)
93 #define SH3001_GYRO_CONF4 (0x9F)
94 #define SH3001_GYRO_CONF5 (0xAF)
95 #define SH3001_CHIP_VERSION (0xDD)
96 #define SH3001_CHIP_ID1 (0xDF)
97 #define SH3001_AUX_I2C_CONF (0xFD)
98 ......
99 #endif
100
接下来就是查看驱动文件,驱动文件分两个,一个是加速度的驱动,一个是陀螺仪的驱动。我们先来看看加速度的驱动。
1、sh3001_acc设备结构体
驱动文件为drivers/input/sensors/accel/sh3001_acc.c,初始化一个结构体变量的代码内容如下。
示例代码44.3.2.2 结构体设备初始化
1 /* drivers/input/sensors/access/sh3001_acc.c
2 *
3 * Copyright (C) 2012-2015 ALIENTEK.
4 * Author: dengtao<dengtao@alientek.com>
5 *
6 * This software is licensed under the terms of the GNU General Public
7 * License version 2, as published by the Free Software Foundation, and
8 * may be copied, distributed, and modified under those terms.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16 #include <linux/interrupt.h>
17 #include <linux/i2c.h>
18 #include <linux/slab.h>
19 #include <linux/irq.h>
20 #include <linux/miscdevice.h>
21 #include <linux/gpio.h>
22 #include <linux/uaccess.h>
23 #include <asm/atomic.h>
24 #include <linux/delay.h>
25 #include <linux/input.h>
26 #include <linux/workqueue.h>
27 #include <linux/freezer.h>
28 #include <linux/of_gpio.h>
29 #ifdef CONFIG_HAS_EARLYSUSPEND
30 #include <linux/earlysuspend.h>
31 #endif
32 #include <linux/sensor-dev.h>
33 #include <linux/sh3001.h>
34 ... ...
35 static struct sensor_operate gsensor_sh3001_ops = {
36 .name = "sh3001_acc",
37 .type = SENSOR_TYPE_ACCEL,
38 .id_i2c = ACCEL_ID_SH3001,
39 .read_reg = SH3001_ACC_XL,
40 .read_len = 6,
41 .id_reg = SH3001_CHIP_ID,
42 .id_data = 0x61,
43 .precision = 16,
44 .ctrl_reg = -1,
45 .ctrl_data = -1,
46 .int_ctrl_reg = -1,
47 .int_status_reg = -1,
48 .range = {-32768, 32768},
49 .trig = IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
50 .active = sensor_active,
51 .init = sensor_init,
52 .report = sensor_report_value,
53 .suspend = NULL,
54 .resume = NULL,
55 };
56 ... ...
第35行,上面的结构体变量是名为 gsensor_sh3001_ops 的变量,其类型是 struct sensor_operate,它包含了一系列不同的字段,用于表示传感器操作和信息。其定义文件为sensor-dev.h这是一个RK写好的一个结构体。实现的代码路径为内核源码路径drivers/input/sensors/sensor-dev.c,由此看出基于这个结构体实现的都是一种传感器并且是一种输入设备。
第36行,.name: 传感器的名称,这里设置为 “sh3001_acc”。
第37行,.type: 传感器类型,这里设置为 SENSOR_TYPE_ACCEL,表示加速度传感器。
第38行,.id_i2c: I2C 总线上传感器的设备 ID,这里设置为 ACCEL_ID_SH3001。
第39行,.read_reg: I2C 读取传感器数据的寄存器地址,这里设置为 SH3001_ACC_XL。
第40行,.read_len: 读取的字节数,这里设置为 6。
第41行,.id_reg: 传感器芯片 ID 的寄存器地址,这里设置为 SH3001_CHIP_ID。
第42行,.id_data: 传感器芯片 ID 的预期值,这里设置为 0x61。
第43行,.precision: 数据的精度,这里设置为 16 位。SH3001精度最高16位。
第44行,.ctrl_reg: 控制传感器的寄存器地址,这里设置为 -1,表示没有特定的控制寄存器。
第45行.ctrl_data: 控制传感器的数据,这里设置为 -1,表示没有特定的控制数据。
第46行,.int_ctrl_reg: 中断控制寄存器的地址,这里设置为 -1,表示没有特定的中断控制寄存器。
第47行,.int_status_reg: 中断状态寄存器的地址,这里设置为 -1,表示没有特定的中断状态寄存器。
第48行,.range: 传感器测量范围,这里设置为 {-32768, 32768},表示数据范围,也就是精度2的N-1次方。
第49行,.trig: 中断触发模式,这里设置为 IRQF_TRIGGER_HIGH | IRQF_ONESHOT。中断在高电平触发时被触发或上只触发一次。这里我们没有用到中断,这里没起到实际作用。
第50行,.active: 激活传感器的函数指针,这里是 sensor_active 函数。
第51行,.init: 初始化传感器的函数指针,这里是 sensor_init 函数。
第52行,.report: 报告传感器值的函数指针,这里是 sensor_report_value 函数。
第53行,.suspend: 暂停传感器的函数指针,这里是 NULL,表示没有特定的暂停函数。
第54行,.resume: 恢复传感器的函数指针,这里是 NULL,表示没有特定的恢复函数。
2、sh3001_acc的i2c_driver注册
对于I2C设备驱动,首先就是要初始化并向系统注册i2c_driver,sh3001_acc的i2c_driver注册与驱动匹配代码如下:
示例代码44.3.2.3 sh3001_acc注册
1 /* @description : 驱动出口函数
2 * @param : 无
3 * @return : 无
4 */
5 static int gsensor_sh3001_remove(struct i2c_client *client)
6 {
7 return sensor_unregister_device(client, NULL, &gsensor_sh3001_ops);
8 }
9
10 /* 设备树匹配列表 */
11 static const struct i2c_device_id gsensor_sh3001_id[] = {
12 {"sh3001_acc", ACCEL_ID_SH3001},
13 {}
14 };
15
16 static struct i2c_driver gsensor_sh3001_driver = {
17 .probe = gsensor_sh3001_probe,
18 .remove = gsensor_sh3001_remove,
19 .shutdown = sensor_shutdown,
20 .id_table = gsensor_sh3001_id,
21 .driver = {
22 .name = "gsensor_sh3001",
23 #ifdef CONFIG_PM
24 .pm = &sensor_pm_ops,
25 #endif
26 },
27 };
28
29 module_i2c_driver(gsensor_sh3001_driver);
30
31 MODULE_AUTHOR("dengtao <dengtao@alientek.com>");
32 MODULE_DESCRIPTION("Senodia sh3001 3-Axis accelerometer driver");
33 MODULE_LICENSE("GPL");
第5~8行,驱动出口函数,这里也不提及注销函数,因为这个将会编译到内核,所以注销函数不写。
第11~14,结构体数组,字符串与标识符ID,其中的字符串“sh3001_acc”会与设备树的“compatible”匹配,驱动就会生效。
第16~20行,这里就是gsensor_sh3001_driver用到的函数,如xx_probe就是初始化设备、设备检测、驱动与设备绑定和创建设备节点。.id_table用于匹配设备树。
3、sh3001_acc的初始化与配置
sh3001的配置、初始化、复位等一些操作都是由这个传感器的厂商提供的代码,基本不会变动,这不是我们作为驱动开发关注的内容。如果有兴趣可以自己查看驱动代码,这里我们就不讲解了。在这个sh3001_acc.c初始化了加速度与陀螺仪,所以后面的陀螺仪初始化也是靠这个sh3001_acc驱动。
4、sh3001_acc数据上报
我们来关注一下加速度的数据上报,结构体变量gsensor_sh3001_ops的.report字段指向的函数就是sensor_report_value。我们一起来看看这个sensor_report_value函数。
示例代码44.3.2.4 sensor_report_value函数
1 static int sensor_report_value(struct i2c_client *client)
2 {
3 struct sensor_private_data *sensor =
4 (struct sensor_private_data *)i2c_get_clientdata(client);
5 struct sensor_platform_data *pdata = sensor->pdata;
6 struct sensor_axis axis;
7 u8 buf[6] = {0};
8 short x, y, z;
9 int ret = -1;
10
11 /* Data bytes from hardware xL, xH, yL, yH, zL, zH */
12 memset(buf, 0, sizeof(buf));
13 do {
14 ret = sh3001_read_regs(client, sensor->ops->read_reg,
15 sensor->ops->read_len, buf);
16 if (ret < 0) {
17 dev_err(&client->dev, "sh3001_read_regs error!\n");
18 return ret;
19 }
20 } while (0);
21
22 x = ((buf[1] << 8) & 0xFF00) + (buf[0] & 0xFF);
23 y = ((buf[3] << 8) & 0xFF00) + (buf[2] & 0xFF);
24 z = ((buf[5] << 8) & 0xFF00) + (buf[4] & 0xFF);
25 axis.x = (pdata->orientation[0]) * x + (pdata->orientation[1]) * y + (pdata->orientation[2]) * z;
26 axis.y = (pdata->orientation[3]) * x + (pdata->orientation[4]) * y + (pdata->orientation[5]) * z;
27 axis.z = (pdata->orientation[6]) * x + (pdata->orientation[7]) * y + (pdata->orientation[8]) * z;
28
29 if (sensor->status_cur == SENSOR_ON) {
30 /* Report acceleration sensor information */
31 input_report_abs(sensor->input_dev, ABS_X, axis.x);
32 input_report_abs(sensor->input_dev, ABS_Y, axis.y);
33 input_report_abs(sensor->input_dev, ABS_Z, axis.z);
34 input_sync(sensor->input_dev);
35 }
36
37 mutex_lock(&(sensor->data_mutex));
38 sensor->axis = axis;
39 mutex_unlock(&(sensor->data_mutex));
40
41 return ret;
42}
上面的代码,关于数据怎么计算及获取都是由sh3001厂商提供的,我们不用关注。我们主要看看第29~35行。在前面说过这个驱动将注册成一个输入设备,会产生/dev/input/eventX事件。当sensor->status_cur == SENSOR_ON条件判断成立时,开始上报数据。通过input_report_abs函数来上报,与我们前面讲的触摸驱动上报事件一样。在发送完一组(数据)输入事件后,调用 input_sync 函数来告知内核将这组事件同步起来,以便应用程序能够正确地解释它们。
陀螺仪的驱动与加速度的驱动类似,驱动文件为drivers/input/sensors/gyro/sh3001_gyro.c。这里笔者就不去讲解了。下面我们开始编写测试程序来获取传感器的数据。
44.3.3 编写测试APP
新建accelApp.c文件,然后在里面输入如下所示内容:
示例代码44.3.3.1 accelApp.c文件
1 /********************************************************************
2 Guangzhou Xingyi Electronic Technology Co., Ltd 2021-2030. All rights reserved.
3 * @brief accel.c
4 * @author 正点原子Linux团队
5 * @date 2023-06-26
6 * @link http://www.openedv.com/forum.php
7 *******************************************************************/
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <sys/ioctl.h>
15 #include <linux/input.h>
16 #include <dirent.h>
17 #include <string.h>
18
19 #define GSENSOR_IOCTL_MAGIC 'a'
20 #define GSENSOR_IOCTL_CLOSE _IO(GSENSOR_IOCTL_MAGIC, 0x02)
21 #define GSENSOR_IOCTL_START _IO(GSENSOR_IOCTL_MAGIC, 0x03)
22 #define SENSOR_ON 1
23 #define SENSOR_OFF 0
24
25 int main(int argc, char *argv[])
26 {
27 struct input_event ev = {0};
28 DIR *dir = NULL;
29 int accel_x, accel_y, accel_z;
30 int abs_x, abs_y, abs_z;
31 int fd = -1;
32 int misc_fd = -1;
33 int ret = -1;
34 char buf[64] = {0};
35
36 setbuf(stdout, NULL);
37 /* 1.先执行校准 */
38 /* 校准之前必须让开发板或手机等设备水平静止放置(Z轴垂直) */
39 printf(">>> 校准之前开发板必须处于水平放置状态 <<<\n");
40 printf(">>> 1秒后开始执行校准 <<<\n");
41 sleep(1);
42
43 fd = open("/sys/class/sensor_class/accel_calibration", O_RDWR);
44 if (fd < 0) {
45 printf("打开accel_calibration失败!\n");
46 return -1;
47 }
48 ret = write(fd, "1", 1);//写1进行校准
49 if (ret != 1) { //校准失败!
50 close(fd);
51 printf("执行校准失败!\n");
52 return -1;
53 }
54 usleep(100*1000);
55 printf(">>> 校准完成 <<<\n");
56
57 /* 2.读取校准值 */
58 ret = read(fd, buf, sizeof(buf));
59 close(fd);
60 if (ret < 18) { //读取失败
61 printf("读取校准值失败!\n");
62 return -1;
63 }
64 usleep(100*1000);
65
66 int x_off = atoi(&buf[18]);
67 int y_off = atoi(strchr(buf, ',') + 2);
68 int z_off = atoi(strrchr(buf, ',') + 2);
69 printf("offset: [%d, %d, %d]\n", x_off, y_off, z_off);
70
71 /* 3.打开加速计设备 */
72 misc_fd = open("/dev/mma8452_daemon", O_RDWR);
73 if (misc_fd < 0) {
74 printf("打开/dev/mma8452_daemon设备失败!\n");
75 return -1;
76 }
77
78 dir = opendir("/dev/input");
79 if(dir != NULL) {
80 struct dirent *de = NULL;
81
82 memset(buf, 0x0, sizeof(buf));
83 while((de = readdir(dir))) {
84 if(strncmp(de->d_name, "event", 5))//判断是不是eventX设备节点
85 continue;
86 fd = openat(dirfd(dir), de->d_name, O_RDONLY);
87 if(fd < 0) //打开失败
88 continue;
89
90 if (ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), &buf) < 1)
91 buf[0] = '\0';//重置为空字符串
92 if (strcmp(buf, "gsensor")) {//如果不是重力加速计传感器
93 close(fd);
94 continue;
95 }
96 closedir(dir);//关闭文件夹
97 goto start_read;//跳转到start_read
98 }
99
100 // 没有找到加速计传感器
101 closedir(dir);
102 goto err_out2;
103 }
104 else
105 goto err_out2;
106
107 start_read:
108 //传感器启动
109 if (ioctl(misc_fd, GSENSOR_IOCTL_START) < 0) {
110 printf("ioctl[/dev/mma8452_daemon] failed!\n");
111 }
112
113 accel_x = accel_y = accel_z = 0;
114 for ( ; ; ) {
115
116 if (read(fd, &ev, sizeof(ev)) != sizeof(ev)) {
117 printf("read failed!\n");
118 goto err_out4;
119 }
120
121 if (ev.type == EV_ABS) {
122 if (ev.code == ABS_X)//x轴加速度
123 accel_x = ev.value;
124 else if (ev.code == ABS_Y)//y轴加速度
125 accel_y = ev.value;
126 else if (ev.code == ABS_Z)//z轴加速度
127 accel_z = ev.value;
128 }
129 else if (ev.type == EV_SYN) {
130 if (ev.code == SYN_REPORT) {//同步事件
131 // 按照+-2G量程来计算 2^15 = 32768
132 // 32768 / 2 = 16384 / 1g重力(9.81m/s^2)
133 // 16384 / 9.81 = 1670左右 取值1600
134 abs_x = accel_x - x_off;
135 abs_y = accel_y - y_off;
136 abs_z = accel_z - z_off;
137 printf("%f %f %f\n", (float)abs_x / 16384 * 9.8, (float)abs_y / 16384 * 9.8, (float)abs_z / 16384 * 9.8);
138 }
139 }
140
141 usleep(50*1000);
142 }
143
144 err_out4:
145 ioctl(misc_fd, GSENSOR_IOCTL_CLOSE);//停止传感器上报数据
146 err_out3:
147 close(fd);
148 err_out2:
149 close(misc_fd);
150 }