g-sensor传感器
一、GSENSOR 概述
gsensor 即 G-sensor,重力传感器,又名加速度传感器。Air201 使用的 G-sensor 传感器型号是 da267。
da267 传感器是一款超低功耗高性能电容式三轴线性加速度计,采用微机械技术开发。该设备提供 2x2x0.9mm 的网格阵列封装(LGA),并确保在-40°C 至 +85°C 的扩展温度范围内正常运行。
传感元件采用单晶硅通过 DRIE 工艺制造,并通过密封硅保护罩隔离环境影响。
该设备提供用户可选择的 ±2g/ ±4g/ ±8g/ ±16g 测量范围,数据输出速率从 1Hz 到 1600Hz,具备信号调节、温度补偿、运动检测、步数计数等功能。
da267 内置 32 级先进先出(FIFO)缓存,允许数据存储以限制主处理器的中断需求。
两个独立且灵活的中断功能大大简化了各种运动状态检测的算法。标准 I²C 和 SPI 接口用于与芯片通信。
详细介绍可以参考如下数据手册:
二、演示功能概述
本示例将演示如何读取 da267 传感器的三轴数据和计步数据。如何设置中断阈值。201 中如何判断运动和静止状态。
三、准备硬件环境
3.1 Air201 模组
使用 Air201 开发套件,如下图所示:
淘宝购买链接:Air201 开发套件淘宝购买链接 ;
此开发套件的详细使用说明参考:Air201 产品手册 中的 Air201 硬件手册 和 Air201 的 LuatOS 快速入门。
3.2 PC 电脑
WINDOWS 系统,其他暂无特别要求;
3.3 数据通信线
USB 数据线(其一端为 Type-C 接口,用于连接 Air201)。
3.3 da267 传感器
da267 传感器是 Air201 开发板自带的,不用另外买。
四、准备软件环境
4.1 下载调试工具
使用说明参考:Luatools 下载和详细使用
五、软硬件资料
5.1 源码及固件
- Air201 模组使用固件:core/LuatOS-SoC_V1004_Air201.soc,本 demo 使用的固件版本是:LuatOS-SoC_V1004_Air201.soc
- 本教程使用的 demo:https://gitee.com/openLuat/LuatOS-Air201/tree/master/demo/da267
- 将固件和脚本烧录到模块中:Luatools 下载和使用教程 - 合宙模组资料中心
5.2 demo 使用 api 介绍
本教程使用 api 接口为:https://docs.openluat.com/air201/luatos/api/core/i2c/
六、代码示例介绍
6.1Air201 中使用 gsensor 步骤
程序流程图如下:
6.2 初始化 I2C
6.2.1 复用 I2C 引脚
--[[io外设功能复用选择
@param1 将I2C功能
@param2 复用到总线序号为i2cId
@param3 pad号为23
@param4 复用功能序号为2
@param5 不是输入功能
]]
mcu.altfun(mcu.I2C, i2cId, 23, 2, 0)
mcu.altfun(mcu.I2C, i2cId, 24, 2, 0)
6.2.2 配置 i2C
--关闭i2c,防止其他使用过
i2c.close(i2cId)
--重新打开i2c,i2c速度设置为低速
i2c.setup(i2cId, i2c.SLOW)
6.3 初始化 gsensor
6.3.1 配置 da267 的精度,测量范围,自检等。
-- 配置数据输出精度为16bit,测量范围为2g
i2c.send(i2cId, da267Addr, {0x0F, 0x00}, 1)
-- 配置电源为正常模式,开启模块自检
i2c.send(i2cId, da267Addr, {0x11, 0x34}, 1)
-- 配置ODR数据输出寄存器速率为100HZ
i2c.send(i2cId, da267Addr, {0x10, 0x07}, 1)
-- int set1,设置INT_SET1
-- 启动x、y、z轴产生中断
i2c.send(i2cId, da267Addr, {0x16, 0x87}, 1)
6.3.2 配置中断阈值
--[[设置中断阈值,用于设置触发中断信号的加速度值。
0x39是设置X轴中断阈值的地址
0x3A是设置Y轴中断阈值的地址
0x3B是设置Z轴中断阈值的地址
阈值取值为8位寄存器,即0-255
例如:在本示例中,量程上面设置的是2g,所以加速度为0x05*K=19.55mg
]]
i2c.send(i2cId, da267Addr, {0x39, 0x05}, 1)
i2c.send(i2cId, da267Addr, {0x3A, 0x05}, 1)
i2c.send(i2cId, da267Addr, {0x3B, 0x05}, 1)
6.3.3 将中断映射到 INT1 引脚
当 XYZ 轴合成的加速度超过阈值时,gsensor 传感器就可以产生中断,当中断产生时,这个状态可以映射到 INT1 引脚,然后模块与 INT1 引脚相连的引脚检测到中断信息后,就可以进行自己的业务处理。
-- 将活动中断映射到INT1引脚
i2c.send(i2cId, da267Addr, {0x19, 0x04}, 1)
6.3.4 配置 gsensor 模式,使能 XYZ 轴
-- 配置gsensor为正常模式,使能XYZ轴,禁用自测。
i2c.send(i2cId, da267Addr, {0x11, 0x30}, 1)
6.3.5 使能步数检测功能
-- init step counter
i2c.send(i2cId, da267Addr, {0x33, 0x80}, 1)
6.4 通过中断判定设备处于运动还是静止状态
初始化完 I2C 和 gsensor 后,我们先配置模块的中断引脚,这样当 gsensor 触发中断后,我们就可以在引脚中断回调函数中做自己的业务处理。
6.4.1 配置中断引脚
-- intPin是与传感器gsensor的INT1引脚相连的中断引脚。ind是中断回调函数。
gpio.setup(intPin, ind)
6.4.2 运动与静止状态逻辑介绍
静止状态:指根据业务逻辑判定,设备处于静止时的状态。在程序中可以通过变量 isRun=flase 来判定。
运动状态:指根据业务逻辑判定,设备处于运动时的状态。在程序中可以通过变量 isRun=true 来判定。
概述一下运动与静止状态切换逻辑,具体细节看下面关于 gsensor()函数的解析:
判定逻辑:
gsensor 的⼯作原理是, 在总加速度(x/y/z 三轴合成的加速度), 超过阈值时, 触发⼀次中断。因为每一秒会记录一次是否产生中断,所以, 从一段时间来看,gsensor 的数据, 是⼀个 0(未触发中断)或 1(触发中断)的离散值序列。
-:表示当前这一秒未触发中断。1:表示当前这一秒触发了中断。
--1------1-------1-1111-111-111-1111-111-11------------------111----
当有连续 5 个 1(中断)时,则判定为运动状态。
切换逻辑:
Air201 通过触发中断的次数及时间来确定设备是否是运动状态。
如果在 1 秒内多次触发只处理一次。
如果超过 60 秒没有触发中断,会清空之前的中断记录,重新进行计算。
如果在 60 秒内,并且有连续 5 秒都发生中断,则判定为运动状态。
当在运动状态的时候,会开启一个定时器(56~60 秒,具体看下面程序讲解),如果定时器超时,就会判断为静止状态。
其中 gsensor()函数的执行流程图如下:
在中断回调函数 ind()中,首先我们可以实现自己判定设备是否处于运动状态的逻辑。这个判定是通过函数 gsensor()来执行的。
local function ind()
logF("int", gpio.get(intPin))
gsensor() -- 调用gsensor函数,计算设备是否处于运动状态
6.4.3 时间相差不到 1 秒时
不做处理,直接退出。
if tnow[1] == tprev[1] and tnow[2] == tprev[2] then
-- 跟上一次中断还在同一秒,就不需要处理了
-- log.debug("gsensor", "依然是同一秒,不需要处理")
return
end
6.4.4 时间相差在 1 到 60 秒之间时
如果距离上一次触发中断过去了不到 60 秒,将 gs 中的数据写入到 gs2 中,并且从 tdiff(时间差)的位置开始写。写入的数据是时间长度。再将 gs2[0]赋值为 0x31,然后清空 gs。然后交换 gs 与 gs2,此时的 gs 内多了 gs[0]=0x31,gs2 全为 0x30。然后检查运动状态。
tprev = tnow
-- 少于60秒, 那就得搬动数据了
-- 首先, 把备用buff清空
gs2:clear(0x30)
-- 把原有数据拷贝过来
gs2:copy(tdiff, gs, 0, gtime)
gs2[0] = 0x31
gs:clear(0x30)
-- 交互数据
gs2, gs = gs, gs2
-- 计算指定时间区间内的震动情况
gsensor_check()
-- 还需要一个定时, 解决运动后变回静止状态
if gtimeid then
sys.timerStop(gtimeid)
gtimeid = nil
end
if not isRun then
return
end
-- 如果当前是运动状态, 启动定时器
local tt = 0
local tt2 = 0
for i = 2, gtime - 4 do
if gs[i] ~= 0x30 then
tt = tt + 1
tt2 = i
if tt > 2 then
break
end
end
end
--[[
为了能回到静止状态,开一个定时器,定时时间为
gtime - 4 - 连续触发的中断的次数,单位为秒。
定时器超时时,就将isRun=flase,然后初始化gs。
]]
local ttime = (gtime - 4 - tt2)*1000
logF("gsensor", "静止状态监听定时器", ttime, isRun)
gtimeid = sys.timerStart(function()
gs:clear(0x30)
logF("gsensor", "定时器回调", "回到静止状态")
gtimeid = nil
isRun = false
-- sys.publish("GSENSOR_INC", "DOWN")
end, ttime)
end
6.4.5 时间差在 60 秒以上
距离上一次中断超过 60 秒,直接初始化 gs,然后 tnow=tprev,并检查运动状态后退出.
if tnow[1] > tprev[1] or tdiff > gtime then
-- 距离上一次中断已经超过60秒,那之前的数据都没有意义
-- 直接初始化gs
logF("gsensor", "距离上一次中断已经超过60秒,那之前的数据都没有意义")
gs:clear(0x30)
gs[0] = 0x31
tprev = tnow
gsensor_check()
return
end
6.5 读取 gsensor 传感器 XYZ 轴和计步数据
想要读取 gsensor 传感器 XYZ 轴的数据需要发送指令 0x02,读取计步数据需要发布指令 0x0D.
初始化传感器后随时可以读取 XYZ 轴和计步数据,不是必须在中断中读取。
--[[ 通过给gsensor传感器发送0x02指令,接收到XYZ轴的数据
其中接收到的数据中,一个有六个字节,分别为:
X轴数据高八位,低八位
Y轴数据高八位,低八位
Z轴数据高八位,低八位
]]
i2c.send(i2cId, da267Addr, 0x02, 1)
local data = i2c.recv(i2cId, da267Addr, 6)
if data and #data == 6 then
logF("XYZ ORIGIN DATA", data:toHex())
local xl, xm, yl, ym, zl, zm = string.byte(data, 1, 1), string.byte(data, 2, 2), string.byte(data, 3, 3), string.byte(data, 4, 4), string.byte(data, 5, 5), string.byte(data, 6, 6)
local x, y, z = (xm << 8 | xl) >> 4, (ym << 8 | yl) >> 4, (zm << 8 | zl) >> 4
logF("x:", x, "y:", y, "z:", z)
else
sys.publish("RESTORE_GSENSOR")
return
end
--[[ 通过给gsensor传感器发送0x0D指令,接收到计步数据
一个两个字节,分别为:
计步数据的高八位,低八位
]]
i2c.send(i2cId, da267Addr, 0x0D, 1)
local data = i2c.recv(i2cId, da267Addr, 2)
if data and #data == 2 then
local xl, xm = string.byte(data, 1, 1), string.byte(data, 2, 2)
local step = ((xl << 8) + xm) // 2
logF("step:", step)
else
sys.publish("RESTORE_GSENSOR")
end
6.6 异常处理
异常处理有两种情况:
1、当读取 XYZ 轴和计步数据时,如果读取的数据不正确,则会发布一个消息 RESTORE_GSENSOR,当接收到这个消息时,会重新初始化 I2C 和 gsensor 传感器。
2、没收到消息 RESTORE_GSENSOR 时,每隔一分钟会读取一次 gsensor 的数据,如果数据不正确也会触发重新初始化的异常处理程序。
while true do
init()
while true do
--等待da267传感器数据不正确,复位的消息
local result = sys.waitUntil("RESTORE_GSENSOR", 60 * 1000)
--如果接收到了复位消息,则跳出读取数据的循环,重新执行init()函数
if result then
break
end
--读取da267传感器的型号值,默认是0x13
i2c.send(i2cId, da267Addr, 0x01, 1)
local data = i2c.recv(i2cId, da267Addr, 1)
-- 如果读取失败则跳出循环重新初始化
if not data or data == "" or string.byte(data) ~= 0x13 then
break
end
end
end
七、结果验证
在 60 秒内触发 5 次中断后,判断为运动状态,并打开定时器,打印出 XYZ 轴的数据和计算的步数。
当定时器时间到,会退出运动状态并初始化 gs。如果超过 60 秒没反应,会初始化 gs。
八、总结
本示例介绍了如何使用 da267 传感器确定设备的运动状态。
常见问题
1、如何改变设备判定运动状态的阈值?
1、可以通过改变 gensor 传感器配置来改变设备判定运动状态的阈值。
影响中断的参数有两个。
一个是量程,这个会影响加速度数据的分辨率和灵敏度,具体看上面代码示例里 6.2 的寄存器配置介绍。
还有一个是中断阈值,这个会影响触发中断的加速度值。
例如:量程为 2g(K=3.91),中断阈值配置为 0x05 时,触发中断的加速度值为 K*0x05=19.55mg.
2、可以通过改变中断处理逻辑来改变设备状态的阈值。
如在 gsensor_check()函数中,增加 gs[]的判断数量,现在是 5 个为 0x31 就判定为运动状态,可以改为 6 个,7 个等。如果代码能力较弱,建议使用 1 的方法改。
给读者的话
本篇文章由
Linden
开发;本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;
请登录合宙技术交流论坛;
用截图标注+文字描述的方式跟帖回复,记录清楚您发现的问题;
我们会迅速核实并且修改文档;
同时也会为您累计找错积分,您还可能赢取月度找错奖金!