Skip to content

Commit 7713326

Browse files
committed
Add QST QMI8658 IMU driver
1 parent 8001a25 commit 7713326

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
metadata(description="QST QMI8658 IMU driver.", version="1.0.0")
2+
module("qmi8658.py", opt=3)
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
# QMI8658 driver for MicroPython
2+
# MIT license; Copyright (c) 2022-2024 WEMOS.CC
3+
4+
import struct
5+
from machine import I2C
6+
import machine
7+
import time
8+
import array
9+
import math
10+
11+
WHO_AM_I = const(0x00) # Device identifier
12+
REVISION_ID = const(0x01)
13+
CTRL1 = const(0x02) # Serial Interface and Sensor Enable
14+
CTRL2 = const(0x03) # Accelerometer Settings
15+
CTRL3 = const(0x04) # Gyroscope Settings
16+
CTRL4 = const(0x05) # Magnetometer Settings
17+
CTRL5 = const(0x06) # Sensor Data Processing Settings
18+
CTRL7 = const(0x08) # Enable Sensors and Configure Data Reads
19+
CTRL8 = const(0x09) # Reserved – Special Settings
20+
21+
# <Sensor Data Output Registers>
22+
23+
TEMP_L = const(0x33)
24+
25+
AccX_L = const(0x35)
26+
AccX_H = const(0x36)
27+
AccY_L = const(0x37)
28+
AccY_H = const(0x38)
29+
AccZ_L = const(0x39)
30+
AccZ_H = const(0x3A)
31+
32+
GyrX_L = const(0x3B)
33+
GyrX_H = const(0x3C)
34+
GyrY_L = const(0x3D)
35+
GyrY_H = const(0x3E)
36+
GyrZ_L = const(0x3F)
37+
GyrZ_H = const(0x40)
38+
39+
40+
class QMI8658():
41+
def __init__(self, i2c, addr=0x6b, accel_scale=2, gyro_scale=2048,accel_odr=500,gyro_odr=500):
42+
self.i2c = i2c
43+
self.addr = addr
44+
45+
self.accel_scale = accel_scale
46+
self.gyro_scale = gyro_scale
47+
48+
self.accel_odr=accel_odr
49+
self.gyro_odr=gyro_odr
50+
51+
self.accel_sensitivity=32768/self.accel_scale
52+
self.gyro_sensitivity=32768/gyro_scale
53+
54+
self.scratch_int = array.array("h", [0, 0, 0])
55+
56+
if self.i2c.readfrom_mem(self.addr, WHO_AM_I, 1) != b'\x05':
57+
raise OSError(
58+
"No QMI8658 device was found at address 0x%x" % (self.addr))
59+
60+
# Accelerometer Full-scale
61+
SCALE_ACCEL = {2: 0, 4: 1, 8: 2, 16: 3}
62+
63+
# Gyroscope Full-scale
64+
SCALE_GYRO = {16: 0, 32: 1, 64: 2, 128: 3,
65+
256: 4, 512: 5, 1024: 6, 2048: 7}
66+
67+
# Accelerometer Output Data Rate (ODR)
68+
ODR_ACCEL = {
69+
# Normal
70+
8000: 0,
71+
4000: 1,
72+
2000: 2,
73+
1000: 3,
74+
500: 4,
75+
250: 5,
76+
125: 6,
77+
62.5: 7,
78+
31.25: 8,
79+
# Low Power
80+
128: 12,
81+
21: 13, # 58% Duty Cycle
82+
11: 14, # 31% Duty Cycle
83+
3: 15 # 8.5% Duty Cycle
84+
}
85+
86+
# Gyroscope Output Data Rate (ODR)
87+
ODR_GYRO = {
88+
# Normal
89+
8000: 0,
90+
4000: 1,
91+
2000: 2,
92+
1000: 3,
93+
500: 4,
94+
250: 5,
95+
125: 6,
96+
62.5: 7,
97+
31.25: 8,
98+
}
99+
100+
# Sanity checks
101+
102+
if not self.accel_scale in SCALE_ACCEL:
103+
raise ValueError("Invalid accelerometer scaling: %d" %
104+
self.accel_scale)
105+
106+
if not self.gyro_scale in SCALE_GYRO:
107+
raise ValueError("invalid gyro scaling: %d" % self.gyro_scale)
108+
109+
if not self.accel_odr in ODR_ACCEL:
110+
raise ValueError("Invalid sampling rate: %d" % self.accel_odr)
111+
112+
if not self.gyro_odr in ODR_GYRO:
113+
raise ValueError("Invalid sampling rate: %d" % self.gyro_odr)
114+
115+
# Serial Interface and Sensor Enable, address auto increment
116+
self.i2c.writeto_mem(self.addr, CTRL1, b'\x40')
117+
# Enable Sensors and Configure Data Reads<Enable Gyroscope Accelerometer>
118+
self.i2c.writeto_mem(self.addr, CTRL7, b'\x03')
119+
120+
# Accelerometer Settings
121+
parm=(ODR_ACCEL[self.accel_odr])|(SCALE_ACCEL[self.accel_scale]<<4)
122+
self.i2c.writeto_mem(self.addr, CTRL2, bytes([parm]))
123+
124+
# Gyroscope Settings< ±2048dps 500Hz>
125+
parm=(ODR_GYRO[self.gyro_odr])|(SCALE_GYRO[self.gyro_scale]<<4)
126+
self.i2c.writeto_mem(self.addr, CTRL3, bytes([parm]))
127+
128+
# Sensor Data Processing Settings<Enable Gyroscope Accelerometer Low-Pass Filter>
129+
self.i2c.writeto_mem(self.addr, CTRL5, b'\x11')
130+
131+
def test(self):
132+
return self.i2c.readfrom_mem(self.addr, AccX_L, 12)
133+
134+
def read_gyro(self):
135+
mv = memoryview(self.scratch_int)
136+
self.i2c.readfrom_mem_into(self.addr,GyrX_L, mv)
137+
return (mv[0] / self.gyro_sensitivity, mv[1] / self.gyro_sensitivity, mv[2] / self.gyro_sensitivity)
138+
139+
def read_accel(self):
140+
mv = memoryview(self.scratch_int)
141+
self.i2c.readfrom_mem_into(self.addr,AccX_L, mv)
142+
return (mv[0] / self.accel_sensitivity, mv[1] / self.accel_sensitivity, mv[2] / self.accel_sensitivity)
143+
144+
def read_temp(self):
145+
return struct.unpack("<h",self.i2c.readfrom_mem(self.addr, TEMP_L, 2))[0]/256
146+
147+
def invSqrt(self,number):
148+
threehalfs = 1.5
149+
x2 = number * 0.5
150+
y = number
151+
152+
packed_y = struct.pack('f', y)
153+
i = struct.unpack('i', packed_y)[0] # treat float's bytes as int
154+
i = 0x5f3759df - (i >> 1) # arithmetic with magic number
155+
packed_i = struct.pack('i', i)
156+
y = struct.unpack('f', packed_i)[0] # treat int's bytes as float
157+
158+
y = y * (threehalfs - (x2 * y * y)) # Newton's method
159+
return y
160+
161+
def get_euler_angles(self,gx,gy,gz,ax,ay,az):
162+
163+
Ki=const(0.008)
164+
Kp=const(10.0)
165+
halfT=const(0.002127)
166+
167+
q0 = 1.0
168+
q1 = 0.0
169+
q2 = 0.0
170+
q3 = 0.0 #/** quaternion of sensor frame relative to auxiliary frame */
171+
172+
exInt=0.0
173+
eyInt=0.0
174+
ezInt=0.0
175+
176+
q0q0 = q0*q0
177+
q0q1 = q0*q1
178+
q0q2 = q0*q2
179+
180+
q1q1 = q1*q1
181+
q1q3 = q1*q3
182+
183+
q2q2 = q2*q2
184+
q2q3 = q2*q3
185+
186+
q3q3 = q3*q3
187+
188+
189+
if( ax*ay*az==0):
190+
return 0
191+
#/* 对加速度数据进行归一化处理 */
192+
recipNorm = self.invSqrt( ax* ax +ay*ay + az*az)
193+
ax = ax *recipNorm
194+
ay = ay *recipNorm
195+
az = az *recipNorm
196+
#/* DCM矩阵旋转 */
197+
vx = 2*(q1q3 - q0q2)
198+
vy = 2*(q0q1 + q2q3)
199+
vz = q0q0 - q1q1 - q2q2 + q3q3
200+
#/* 在机体坐标系下做向量叉积得到补偿数据 */
201+
ex = ay*vz - az*vy
202+
ey = az*vx - ax*vz
203+
ez = ax*vy - ay*vx
204+
#/* 对误差进行PI计算,补偿角速度 */
205+
exInt = exInt + ex * Ki
206+
eyInt = eyInt + ey * Ki
207+
ezInt = ezInt + ez * Ki
208+
209+
gx = gx + Kp*ex + exInt
210+
gy = gy + Kp*ey + eyInt
211+
gz = gz + Kp*ez + ezInt
212+
#/* 按照四元素微分公式进行四元素更新 */
213+
q0 = q0 + (-q1*gx - q2*gy - q3*gz)*halfT
214+
q1 = q1 + (q0*gx + q2*gz - q3*gy)*halfT
215+
q2 = q2 + (q0*gy - q1*gz + q3*gx)*halfT
216+
q3 = q3 + (q0*gz + q1*gy - q2*gx)*halfT
217+
218+
recipNorm = self.invSqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3)
219+
220+
q0 = q0*recipNorm
221+
q1 = q1*recipNorm
222+
q2 = q2*recipNorm
223+
q3 = q3*recipNorm
224+
225+
roll = math.atan2(2*q2*q3 + 2*q0*q1, -2*q1*q1 - 2*q2*q2 + 1)*57.3
226+
pitch = math.asin(2*q1*q3 - 2*q0*q2)*57.3
227+
yaw = -math.atan2(2*q1*q2 + 2*q0*q3, -2*q2*q2 -2*q3*q3 + 1)*57.3
228+
229+
230+
231+
# print("pitch:{:>8.3f} roll:{:>8.3f} yaw:{:>8.3f}\r\n".format(pitch,roll,yaw))
232+
return pitch,roll,yaw

0 commit comments

Comments
 (0)