编写三轴加速度传感器KXTJ3-1057 for LuaTask 驱动

三轴加速度传感器KXTJ3-1057 for LuaTask 驱动 手把手教程

KXTJ3-1057简介

KXTJ3-1057和KXTJ2-1009 pin to pin 兼容,驱动也兼容。原是Kionix出品的一款手机上非常常用的三轴加速度传感器,后被ROHM收购。产品数据手册: https://atta.szlcsc.com/upload/public/pdf/source/20191031/C442603_12AC3C8489586CE5186C5DB6A78C43C5.pdf?Expires=4070880000&OSSAccessKeyId=LTAIJDIkh7KmGS1H&Signature=xO%2B7CeKnGRDmRCYs1IojjqVATtw%3D&response-content-disposition=attachment%3Bfilename%3DC442603_KXTJ3-1057_2019-10-31.PDF

概述:

ROHM Semiconductor 的 KXTJ3-1057 是三轴 ±2g、±4g、±8g 或 ±16g 微加工硅晶加速计。这些元件采用 Kionix 的专有等离子微加工工艺技术制造。加速度使这些元件运动,进而产生电容器差是这些元件的加速度检测的基本原理,而且使用共模消除法减少由于工艺变化、温度和环境影响造成的误差。利用玻璃浆料将另一片盖形晶圆与该器件粘结在一起,使这些元件实现晶圆级密封。独立的 ASIC 器件与感测元件封装在一起,用于进行信号调节和数字化通讯。
这种加速计采用 2 mm x 2 mm x 0.9 mm LGA 塑料封装,由 1.71 VDC 至 3.6 VDC 电源供电。稳压器用于在输入电源电压范围内保持内部工作电压恒定。这样就能在输入电压范围内保持稳定的工作特性,而且实现几乎检测不到的比率误差。I2C 数字协议用于与芯片进行通讯,以便对该部件进行配置并监视输出。

特性:

编写KXTJ3-1057 for LuaTask 驱动

抽象驱动所用到的函数

KXTJ3-1057 的寄存器地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
local KXTJ3_ADDR = 0x0E --KXTJ3 的地址
local KXTJ3_DCST_RESP = 0x0C -- KXTJ3 的数字通自检寄存器
-- 加速度输出寄存器
local KXTJ3_XOUT_L = 0x06
local KXTJ3_XOUT_H = 0x07
local KXTJ3_YOUT_L = 0x08
local KXTJ3_YOUT_H = 0x09
local KXTJ3_ZOUT_L = 0x0A
local KXTJ3_ZOUT_H = 0x0B
-- 中断寄存器
local KXTJ3_INT_SOURCE1 = 0x16
local KXTJ3_INT_SOURCE2 = 0x17
local KXTJ3_STATUS_REG = 0x18
local KXTJ3_INT_REL = 0x1A
-- 控制寄存器
local KXTJ3_CTRL_REG1 = 0x1B -- KXTJ3的CTRL_REG1地址
local KXTJ3_CTRL_REG2 = 0x1D
local KXTJ3_INT_CTRL_REG1 = 0x1E
local KXTJ3_INT_CTRL_REG2 = 0x1F
local KXTJ3_DATA_CTRL_REG = 0x21
local KXTJ3_WAKEUP_COUNTER = 0x29
local KXTJ3_WAKEUP_THRESHOLD_H = 0x6A
local KXTJ3_WAKEUP_THRESHOLD_L = 0x6B

KXTJ3 的7位从机地址,请参阅你购买的产品手册给的规格书,通常是0x0E 或 0x0F 。

KXTJ3-1057 读写寄存器函数

LuaTask 的I2C 调用的是内部的硬件I2C,所以core给了几个API:https://wiki.openluat.com/doc/luatApi4G/#i2c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
-- 初始化并打开I2C操作
-- @param I2C 内部ID
-- @return number ,I2C的速率
local function i2c_open(id, speed)
if i2c.setup(id, speed or i2c.SLOW) ~= i2c.SLOW then
i2c.close(id)
return i2c.setup(id, speed or i2c.SLOW)
end
return i2c.SLOW
end

--- 读取寄存器值
-- @number id: I2C端口号
-- @number addr:KXTJ3-1057的从机地址
-- @number reg: KXTJ3-1057的寄存器地址
-- @return number: 寄存器当前值
function readRegister(id, addr, reg)
log.info("I2C OPEN Result:", i2c_open(id))
i2c.send(id, addr, reg)
local _, val = pack.unpack(i2c.recv(id, addr, 1), 'b')
i2c.close(id)
log.info("读取", string.format("寄存器:0x%02x 当前值:0x%02x", reg, val or 0))
return val;
end

--- 写寄存器方法
-- @number id: I2C端口号
-- @number addr:KXTJ3-1057的从机地址
-- @number reg: KXTJ3-1057的寄存器地址
-- @number ...: 要写入寄存器其他值
-- @return number: 寄存当前值
function writeReg(id, addr, reg, ...)
log.info("I2C OPEN Result:", i2c_open(id))
i2c.send(id, addr, {reg, ...})
local _, val = pack.unpack(i2c.recv(id, addr, 1), 'b')
log.info("重读", string.format("寄存器:0x%02x 当前值:0x%02x", reg, val or 0))
i2c.close(id)
return val
end

为了规避I2C异常,提高I2C抗干扰能力,建议I2C使用完后就立刻关闭。如上代码,每次读取寄存器1个字节。读写寄存器的函数可以根据实际情况,一次读取或写入两个字节,写寄存器函数每次写完后,会重读当前寄存器,方便调试。

I2C 数字通信自检函数

参考KXTJ3-1057 数据手册,可以找到寄存器“DCST_RESP” 专门用来做 KXTJ3-1057 数字通信验证的,如果接线正常,I2C工作正常, 那么读取该寄存器 KXTJ3-1057 会返回0x55,依据这个可以判断LuaTask的I2C和KXTJ3-1057的数字接线、数字通信是否正常,如果不返回0x55,要检查硬件和I2C端口号是否正确。

1
2
3
4
5
6
7
8
--- 数字通信自检验证
-- number id: I2C端口号
-- return number 正常返回0x55,其他值为异常
function dcst(id)
local val = readRegister(id, KXTJ3_ADDR, DCST_RESP)
log.info("KXTJ3C DCST Result:", string.format("0x%02x", val or 0))
return val;
end

读取和复位状态寄存器

当中断源寄存器1的DRDY和WUFS位被置位时,该寄存器Bit4置位,表示有位移或者加速度变化产生。当读取加速度数据或读取中断释放寄存器(INT_REL)时,Bit4复位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--- 读中断状态
-- number id: I2C端口号
-- return number 返回状态寄存器当前值
function readStatus(id)
local val = readRegister(id, KXTJ3_ADDR, KXTJ3_STATUS_REG)
log.info("KXTJ3C read interrupt status:", string.format("0x%02x", val or 0))
return val;
end

--- 清中断状态
-- number id: I2C端口号
-- return nil
function clearStatus(id)
readRegister(id, KXTJ3_ADDR, KXTJ3_INT_REL)
log.info("Clear Interrupt status register:", "OK")
end

读取中断源寄存器

实际上读取寄存器的操作可以直接用readRegister函数直接实现,这里演示的是二次封装该方法,方便跨页面调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
--- 读取中断源寄存器
-- number id: I2C端口号
-- @number src: 1 读中断源1寄存器, 2读中断源2寄存器
-- @return number: 返中断源寄存器的值
function readInterrupt(id, src)
local val = 0
if src == 2 then
val = readRegister(id, KXTJ3_ADDR, KXTJ3_INT_SOURCE2)
else
val = readRegister(id, KXTJ3_ADDR, KXTJ3_INT_SOURCE1)
end
log.info("readInterrupt register:", string.format("%02x", val or 0))
return val
end

KXTJ3-1057 工作模式切换

KXTJ3-1057 部分寄存器写入数据必须在准备模式才能写入,工作模式写入无效。配置CTRL_REG1寄存器Bit7的值可切换工作模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
--- 配置 KXTJ3工作模式
-- number id: I2C端口号
-- @number mode: 0 准备模式, 1工作模式
-- @return number: 返中断源寄存器的值
function setMode(id, mode)
log.info("-------->I2C OPEN Result:", i2c_open(id))
i2c.send(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1)
local _, val = pack.unpack(i2c.recv(id, KXTJ3_ADDR, 1), 'b')
i2c.send(id, KXTJ3_ADDR, {KXTJ3_CTRL_REG1, mode == 0 and bit.clear(val, 7) or bit.set(val, 7)})
val = readRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1)
i2c.close(id)
log.info("读取CTRL_REG1寄存器:", string.format("当前值:0x%02x", val or 0))
end

实际应用中,也可以在准备模式配置玩寄存器后,配置CTRL_REG1的同时切换KXTJ3为工作模式。

读取三轴加速度数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
--- 读取三轴输出值,注意结果是 Tri-axis 数字量
-- @param axis: 'x','y','z' 分别表示x,y,z轴
-- @number n: 分辨率,可选值8,12,14(CTRL_REG1配置)
-- @return number: Tri-axis Digital
function readAxis(id, axis, n)
local val, recv, reg = 0, {}, {}
if axis == 'x' then
reg[1] = KXTJ3_XOUT_L
reg[2] = KXTJ3_XOUT_H
elseif axis == 'y' then
reg[1] = KXTJ3_YOUT_L
reg[2] = KXTJ3_YOUT_H
elseif axis == 'z' then
reg[1] = KXTJ3_ZOUT_L
reg[2] = KXTJ3_ZOUT_H
else
return 0
end
recv[1] = readRegister(id, KXTJ3_ADDR, reg[1])
recv[2] = readRegister(id, KXTJ3_ADDR, reg[2])
val = recv[2] * 256 + recv[1]
if n == 8 then
return recv[2]
elseif n == 12 then
return (recv[2] > 0x7F) and bit.bor(bit.rshift(val, 4), 0xF000) or bit.band(bit.rshift(val, 4), 0x0FFF)
elseif n == 14 then
return (recv[2] > 0x7F) and bit.bor(bit.rshift(val, 4), 0xC000) or bit.band(bit.rshift(val, 4), 0x3FFF)
end
return 0;
end

这部分没啥好说的,就是读取加速度三轴的高低位寄存器的值,根据CTRL_REG1寄存器配置的分辨率,然后换算成Tri-axis 数字量。

KXTJ3-1057 三轴功能自检函数

KXTJ3-1057 提供了一套功能自检逻辑,帮助用户判断三轴传感器是否正常工作。当写入0xCA到SELF_TEST寄存器,内部测量自检方法被启用, 加速度的静电驱动会让X,Y,Z三轴产生偏移,对该寄存器写入0x00,则关闭自建并恢复正常工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- KXTJ3-1057 功自检
-- number id: I2C端口号
-- @return nil
function selfTest(id)
setMode(id, 0)
writeRegister(id, KXTJ3_ADDR, KXTJ3_SELF_TEST, 0xCA)
setMode(id, 1)
log.info("on self test axis-x: ", readAxis(id, 'x', 8))
log.info("on self test axis-y: ", readAxis(id, 'y', 8))
log.info("on self test axis-z: ", readAxis(id, 'z', 8))
setMode(id, 0)
writeRegister(id, KXTJ3_ADDR, KXTJ3_SELF_TEST, 0x00)
setMode(id, 1)
log.info("out self test axis-x: ", readAxis(id, 'x', 8))
log.info("out self test axis-y: ", readAxis(id, 'y', 8))
log.info("out self test axis-z: ", readAxis(id, 'z', 8))
end

KXTJ3-1057 初始化示例

下面介绍 KXTJ3-1057 初始化的例子, 读者可根据自己的实际情况和项目需求进行更改,其中一些直接用writeRegister写寄存器的方法, 大家可以自己动手封装成方法,方便在其他页面调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
--- 初始化配置
-- number id: I2C端口号
-- @return nil
function init(id)
-- 进入配置模式
setMode(id, 0)
-- 复位控制寄存器2
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG2, 0x86)
-- 配置控制寄存器2 为50HZ
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG2, 0x06)
-- 配置唤醒延时和唤阈值
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_COUNTER, 50)
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_THRESHOLD_H, (1500 - (1500 % 256)) / 256)
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_THRESHOLD_L, 1500 % 256)
writeRegister(id, KXTJ3_ADDR, KXTJ3_DATA_CTRL_REG, 0x02)
-- 配置控制寄存器1 配置唤中断,(B0010 0010)
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1, 0x82)
-- 清中断
clearStatus(id)
log.info("KXTJ3C init done: ", "ok!")
end

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
--[[
Author: 稀饭放姜
Date: 2020-12-18 15:50:05
LastEditors: 稀饭放姜
LastEditTime: 2020-12-25 12:24:25
FilePath: \HeFeiYunShi-Air724\user\sensor.lua
--]]
require "utils"
module(..., package.seeall)
-- 初始化并打开I2C操作
-- @param I2C 内部ID
-- @return number ,I2C的速率
local function i2c_open(id, speed)
if i2c.setup(id, speed or i2c.SLOW) ~= i2c.SLOW then
i2c.close(id)
return i2c.setup(id, speed or i2c.SLOW)
end
return i2c.SLOW
end

--- 读取寄存器值
-- @number id: I2C端口号
-- @number addr:从机地址
-- @number reg: 寄存器地址
-- @return number: 寄存器当前值
function readRegister(id, addr, reg)
log.info("-------->I2C OPEN Result:", i2c_open(id))
i2c.send(id, addr, reg)
local _, val = pack.unpack(i2c.recv(id, addr, 1), 'b')
i2c.close(id)
log.info("读取", string.format("寄存器:0x%02x 当前值:0x%02x", reg, val or 0))
return val;
end

--- 写寄存器方法
-- @number id: I2C端口号
-- @number addr:从机地址
-- @number reg: 寄存器地址
-- @number ...: 要写入寄存器其他值
-- @return number: 寄存当前值
function writeRegister(id, addr, reg, ...)
log.info("-------->I2C OPEN Result:", i2c_open(id))
i2c.send(id, addr, {reg, ...})
local _, val = pack.unpack(i2c.recv(id, addr, 1), 'b')
log.info("重读", string.format("寄存器:0x%02x 当前值:0x%02x", reg, val or 0))
i2c.close(id)
return val
end

-------------------------------------- KXTJ3-1057 驱动代码开始 --------------------------------------
local KXTJ3_ADDR = 0x0E --KXTJ3 的地址
local KXTJ3_DCST_RESP = 0x0C -- KXTJ3 的数字通自检寄存器
local KXTJ3_SELF_TEST = 0x3A -- KXTJ3 的功自检寄存器
-- 加速度输出寄存器
local KXTJ3_XOUT_L = 0x06
local KXTJ3_XOUT_H = 0x07
local KXTJ3_YOUT_L = 0x08
local KXTJ3_YOUT_H = 0x09
local KXTJ3_ZOUT_L = 0x0A
local KXTJ3_ZOUT_H = 0x0B
-- 中断寄存器
local KXTJ3_INT_SOURCE1 = 0x16
local KXTJ3_INT_SOURCE2 = 0x17
local KXTJ3_STATUS_REG = 0x18
local KXTJ3_INT_REL = 0x1A
-- 控制寄存器
local KXTJ3_CTRL_REG1 = 0x1B -- KXTJ3的CTRL_REG1地址
local KXTJ3_CTRL_REG2 = 0x1D
local KXTJ3_INT_CTRL_REG1 = 0x1E
local KXTJ3_INT_CTRL_REG2 = 0x1F
local KXTJ3_DATA_CTRL_REG = 0x21
local KXTJ3_WAKEUP_COUNTER = 0x29
local KXTJ3_WAKEUP_THRESHOLD_H = 0x6A
local KXTJ3_WAKEUP_THRESHOLD_L = 0x6B

--- 数字通信自检验证
-- number id: I2C端口号
-- return number 正常返回0x55,其他值为异常
function dcst(id)
local val = readRegister(id, KXTJ3_ADDR, KXTJ3_DCST_RESP)
log.info("KXTJ3C DCST Result:", string.format("0x%02x", val or 0))
return val;
end

--- 读中断状态
-- number id: I2C端口号
-- return number 返回状态寄存器当前值
function readStatus(id)
local val = readRegister(id, KXTJ3_ADDR, KXTJ3_STATUS_REG)
log.info("KXTJ3C read interrupt status:", string.format("0x%02x", val or 0))
return val;
end

--- 清中断状态
-- number id: I2C端口号
-- return nil
function clearStatus(id)
readRegister(id, KXTJ3_ADDR, KXTJ3_INT_REL)
log.info("Clear Interrupt status register:", "OK")
end

--- 读取中断源寄存器
-- number id: I2C端口号
-- @number src: 1 读中断源1寄存器, 2读中断源2寄存器
-- @return number: 返中断源寄存器的值
function readInterrupt(id, src)
local val = 0
if src == 2 then
val = readRegister(id, KXTJ3_ADDR, KXTJ3_INT_SOURCE2)
else
val = readRegister(id, KXTJ3_ADDR, KXTJ3_INT_SOURCE1)
end
log.info("readInterrupt register:", string.format("%02x", val or 0))
return val
end

--- 配置 KXTJ3工作模式
-- number id: I2C端口号
-- @number mode: 0 准备模式, 1工作模式
-- @return number: 返中断源寄存器的值
function setMode(id, mode)
log.info("-------->I2C OPEN Result:", i2c_open(id))
i2c.send(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1)
local _, val = pack.unpack(i2c.recv(id, KXTJ3_ADDR, 1), 'b')
i2c.send(id, KXTJ3_ADDR, {KXTJ3_CTRL_REG1, mode == 0 and bit.clear(val, 7) or bit.set(val, 7)})
val = readRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1)
i2c.close(id)
log.info("读取CTRL_REG1寄存器:", string.format("当前值:0x%02x", val or 0))
end

--- 读取三轴输出值,注意结果是 Tri-axis 数字量
-- @param axis: 'x','y','z' 分别表示x,y,z轴
-- @number n: 分辨率,可选值8,12,14(CTRL_REG1配置)
-- @return number: Tri-axis Digital
function readAxis(id, axis, n)
local val, recv, reg = 0, {}, {}
if axis == 'x' then
reg[1] = KXTJ3_XOUT_L
reg[2] = KXTJ3_XOUT_H
elseif axis == 'y' then
reg[1] = KXTJ3_YOUT_L
reg[2] = KXTJ3_YOUT_H
elseif axis == 'z' then
reg[1] = KXTJ3_ZOUT_L
reg[2] = KXTJ3_ZOUT_H
else
return 0
end
recv[1] = readRegister(id, KXTJ3_ADDR, reg[1])
recv[2] = readRegister(id, KXTJ3_ADDR, reg[2])
val = recv[2] * 256 + recv[1]
if n == 8 then
return recv[2]
elseif n == 12 then
return (recv[2] > 0x7F) and bit.bor(bit.rshift(val, 4), 0xF000) or bit.band(bit.rshift(val, 4), 0x0FFF)
elseif n == 14 then
return (recv[2] > 0x7F) and bit.bor(bit.rshift(val, 4), 0xC000) or bit.band(bit.rshift(val, 4), 0x3FFF)
end
return 0;
end

-- KXTJ3-1057 功自检
-- number id: I2C端口号
-- @return nil
function selfTest(id)
setMode(id, 0)
writeRegister(id, KXTJ3_ADDR, KXTJ3_SELF_TEST, 0xCA)
setMode(id, 1)
log.info("on self test axis-x: ", readAxis(id, 'x', 8))
log.info("on self test axis-y: ", readAxis(id, 'y', 8))
log.info("on self test axis-z: ", readAxis(id, 'z', 8))
setMode(id, 0)
writeRegister(id, KXTJ3_ADDR, KXTJ3_SELF_TEST, 0x00)
setMode(id, 1)
log.info("out self test axis-x: ", readAxis(id, 'x', 8))
log.info("out self test axis-y: ", readAxis(id, 'y', 8))
log.info("out self test axis-z: ", readAxis(id, 'z', 8))
end
-------------------------------------- KXTJ3-1057 驱动代码结束 --------------------------------------
--- 初始化配置
-- number id: I2C端口号
-- @return nil
function init(id)
-- 进入配置模式
setMode(id, 0)
-- 复位控制寄存器2
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG2, 0x86)
-- 配置控制寄存器2 为50HZ
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG2, 0x06)
-- 配置唤醒延时和唤阈值
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_COUNTER, 50)
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_THRESHOLD_H, (1500 - (1500 % 256)) / 256)
writeRegister(id, KXTJ3_ADDR, KXTJ3_WAKEUP_THRESHOLD_L, 1500 % 256)
writeRegister(id, KXTJ3_ADDR, KXTJ3_DATA_CTRL_REG, 0x02)
-- 配置控制寄存器1 配置唤中断,(B0010 0010)
writeRegister(id, KXTJ3_ADDR, KXTJ3_CTRL_REG1, 0x82)
-- 清中断
clearStatus(id)
log.info("KXTJ3C init done: ", "ok!")
end

上次更新 2021-01-28