跳转至

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 源码及固件

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开发;

本篇文章描述的内容,如果有错误、细节缺失、细节不清晰或者其他任何问题,总之就是无法解决您遇到的问题;

请登录合宙技术交流论坛

用截图标注+文字描述的方式跟帖回复,记录清楚您发现的问题;

我们会迅速核实并且修改文档;

同时也会为您累计找错积分,您还可能赢取月度找错奖金!