Accelerometer Sensor
The accelerometer sensors include angular acceleration sensors (gyroscopes) and linear acceleration sensors, which can detect the real-time acceleration, angular velocity, and attitude of the device. Here are some typical application scenarios.
- Attitude Perception: Accelerometer sensors can be used to detect the attitude and orientation changes of a device. They can be used in smart wearables, smart security systems, etc., to achieve features such as screen rotation, posture recognition, and motion sensing.
- Motion Tracking: Accelerometer sensors can be used to track and record the acceleration and motion trajectory of objects. In embedded systems, they can be used in motion monitors, sports watches, smart bracelets, etc., to achieve features such as step counting, sleep monitoring, and motion trajectory recording.
- Anti-shake Control: Accelerometer sensors can be used to control the shake and stability of devices. They can be used in applications such as camera stabilization, electronic stabilizers, and robot control to achieve image stabilization and motion control features.
Supported List
List of Accelerometer Sensor Models Supported by QuecPython
Model | Interface | Datasheet | Code Link |
---|---|---|---|
LIS2DH12 | I2C/SPI | LIS2DH12 Datasheet | Code Link |
ADXL346 | I2C/SPI | ADXL346 Datasheet | Code Link |
BMA250 | I2C/SPI | BMA250 Datasheet | Code Link |
Hardware
This section demonstrates how to debug an accelerometer sensor based on the LTE OPEN-EVB_V1.1, EC600U TE-A and LIS2DH12TR sensor module.
See LIS2DH12TR Sensor Hardware Connection for details.
Software Design
Workflow
Features supported by LIS2DH12:
- Single/double-click detection
- Free fall detection
- Tilt angle measurement
- Switch between landscape/portrait mode
Enable the single-click detection feature. When a single-click event occurs, map it to the INT1 pin. The processing logic is roughly as follows:
LIS2SH12 initialization steps:
- Set the CTRL_REG2 register to enable high-pass filtering.
- Set the CTRL_REG3 register to route the interrupt to the INT1 pin.
- Set the CTRL_REG4 register to configure the full range.
Single-click interrupt configuration steps:
- Configure the CLICK_CFG register to enable the required sensing axis, X, Y, Z.
- Configure the CLICK_THS register to set the threshold.
- Configure the TIME_LIMIT register to set the time window limit.
- Configure the TIME_LATENCY register to set the latency.
Enable LIS2SH12 sensor:
- Configure the CTRL_REG1 register to enable the sensor.
API Reference
The following APIs provide the abstraction for the lis2dh12 sensor features. Users can directly reference the lis2dh12 class and call its APIs to write sensor applications.
Different accelerometer sensors may have significant differences. If you want to write your accelerometer sensor class, please read the corresponding datasheet first and develop it based on the source code in this chapter.
Class Reference
from lis2dh12 import lis2dh12
Instantiation
i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE)
g_sensor = lis2dh12(i2c_dev,14)
Parameter Description
i2c_obj
– Object type. I2C object. Required parameter.int_pin
– Integer type. GPIO number connected to the INT1 pin of the sensor. Required parameter.dev_addr
– Integer type. I2C salve address. Optional parameter. Default value: 0x19.
Note: If the SDO/SA0 pin is at a high level,
dev_addr
is 0x19; if the SDO/SA0 pin is at a low level,dev_addr
is 0x18.
lis2dh12.sensor_reset
Reset the sensor.
lis2dh12.int_enable
Enable the interrupt.
Parameter Description:
int_type
– Integer type. Interrupt type. Required parameter.0x01: Single-click on the x-axis
0x04: Single-click on the y-axis
0x10: Single-click on the z-axis
0x15: Single-click on all three axes
0x02: Double-click on the x-axis
0x08: Double-click on the y-axis
0x20: Double-click on the z-axis
0x2A: Double-click on all three axes
0x95: Free fall
int_ths
– Integer type. Interrupt threshold. Required parameter.time_limit
– Integer type. Time limit (single/double-click events). Optional parameter. Default value: 0x18.time_latency
– Integer type. Latency (double-click events). Optional parameter. Default value: 0x12.time_window
– Integer type. Time window (double-click events). Optional parameter. Default value: 0x55. The double-click must be completed within this time.duration
– Integer type. Duration of the event. Optional parameter. Default value: 0x03.
int_ths
is used to set the threshold to trigger an interrupt. When the measured value of the sensor (input acceleration) exceeds or falls below this threshold, an interrupt event is triggered.
time_limit
is used to set the time limit for a click event. If the time interval between two input accelerations exceeding the threshold is less than this time, a click event is considered to have occurred.
time_latency
is used to set the delay time for a double-click event. After the first click event, wait for a latency time before detecting the second click event. If the second click event occurs within the latency time, a double-click event is considered to have occurred.
time_window
is used to set the time window for a double-click event. The time window defines the time range for the second triggering event.
duration
is used to specify the duration threshold for an event. When the duration of an event reaches or exceeds this threshold, the corresponding processing logic or interrupt can be triggered.
lis2dh12.start_sensor
Starts the sensor (enables the x, y and z
axes).
lis2dh12.read_acceleration
Reads acceleration of three axes.
Return Value
acceleration
– Tuple type. acceleration of three axes(x,y,z)
. Unit:G
.
lis2dh12.set_int_callback
Sets the interrupt callback function ( INT1
pin).
lis2dh12.set_mode
Sets the working mode.
Parameter Description
mode
– Integer type. The acceleration working mode of three axes. Required parameter.0: High-resolution mode
1: Normal mode
2: Low-power mode.
Experimental Design
- Use the INT1 pin of the LIS2DH12 sensor to generate an interrupt.
- Poll the status of the INT1 pin. When a rising edge is detected, it indicates that an interrupt has occurred. You need to process the interrupt.
- Read the status of the three axes in the interrupt function.
Experimental Code
The sensor class code is designed as follows:
class lis2dh12(object):
'''
lis2dh12 class
'''
def __init__(self, i2c_dev, int_pin, slave_address=0x19):
'''
:param i2c_dev: i2c object
:param int_pin: gpio of pin which is connected with int1_pin
:param slave_address: device address
'''
self._address = slave_address
self._i2c_dev = i2c_dev
self._int_pin = int_pin
self._extint = None
self._sensor_init()
def _read_data(self, regaddr, datalen):
'''
i2c read data
:param regaddr: register address
:param datalen: length of reading data
:return: data
'''
r_data = bytearray(datalen)
reg_addres = bytearray([regaddr])
self._i2c_dev.read(self._address, reg_addres, 1, r_data, datalen, 1)
ret_data = list(r_data)
return ret_data
def _write_data(self, regaddr, data):
'''
i2c write data
:param regaddr: register address
:param data: data to write
'''
addr = bytearray([regaddr])
w_data = bytearray([data])
self._i2c_dev.write(self._address, addr, len(addr), w_data, len(w_data))
def sensor_reset(self):
'''
reset the sensor
'''
# Reset the chip
self._write_data(LIS2DH12_CTRL_REG5, 0x80)
print('reboot already. {}'.format(self._read_data(LIS2DH12_CTRL_REG5,1)))
utime.sleep_ms(100)
r_data = self._read_data(LIS2DH12_WHO_AM_I, 1)
while r_data[0] != 0x33:
r_data = self._read_data(LIS2DH12_WHO_AM_I, 1)
utime.sleep_ms(5)
def _sensor_init(self):
'''
initialize the sensor
'''
self.sensor_reset()
self._write_data(LIS2DH12_CTRL_REG1, 0x77) # set ODR 400HZ ,enable XYZ.
utime.sleep_ms(20) # (7/ODR) = 18ms
self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g
self._write_data(LIS2DH12_CLICK_CFG, 0) # clear click_cfg
self._write_data(LIS2DH12_INT1_CFG, 0) # clear int1_cfg
self._write_data(LIS2DH12_INT2_CFG, 0) # clear int2_cfg
def int_enable(self,int_type,int_ths=0x12,time_limit=0x18,time_latency=0x12,time_window=0x55,duration=0x03):
'''
interrupt enable
:param int_type: type of interrupt
:param int_ths: threshold
:param time_limit: click_int to send this parameter, time window limit
:param time_latency: click_int to send this parameter, set the time_latency
:param duration:
'''
# single_click int
if int_type in (XYZ_SINGLE_CLICK_INT, X_SINGLE_CLICK_INT, Y_SINGLE_CLICK_INT, Z_SINGLE_CLICK_INT):
self._write_data(LIS2DH12_CTRL_REG2, 0x07) # Enable high pass filter for click function
self._write_data(LIS2DH12_CTRL_REG3, 0x80) # Bind interrupt to INT1 pin, default high level is valid
self._write_data(LIS2DH12_CTRL_REG5, 0x08) # INT1 latch
self._write_data(LIS2DH12_CLICK_CFG, int_type) # enable click_int
self._write_data(LIS2DH12_CLICK_THS, int_ths) # set threshold
self._write_data(LIS2DH12_TIME_LIMIT, time_limit) # set time_limit
# double_click int
elif int_type in (XYZ_DOUBLE_CLICK_INT, X_DOUBLE_CLICK_INT, Y_DOUBLE_CLICK_INT, Z_DOUBLE_CLICK_INT):
self._write_data(LIS2DH12_CTRL_REG2, 0x07)
self._write_data(LIS2DH12_CTRL_REG3, 0x80)
self._write_data(LIS2DH12_CTRL_REG5, 0x08)
self._write_data(LIS2DH12_CLICK_CFG, int_type)
self._write_data(LIS2DH12_CLICK_THS, int_ths)
self._write_data(LIS2DH12_TIME_LIMIT, time_limit)
self._write_data(LIS2DH12_TIME_LATENCY, time_latency)
self._write_data(LIS2DH12_TIME_WINDOW, time_window)
# 6d int
elif int_type in (MOVE_RECOGNIZE, X_MOVE_RECOGNIZE, Y_MOVE_RECOGNIZE, Z_MOVE_RECOGNIZE,POSI_CHANGE_RECOGNIZE,
X_POSI_CHANGE_RECOGNIZE,Y_POSI_CHANGE_RECOGNIZE,Z_POSI_CHANGE_RECOGNIZE,FF_RECOGNIZE):
self._write_data(LIS2DH12_CTRL_REG2, 0x00) # switch off the high pass filter
self._write_data(LIS2DH12_CTRL_REG3, 0x40)
self._write_data(LIS2DH12_CTRL_REG5, 0x08)
self._write_data(LIS2DH12_INT1_CFG, int_type) # enable 6d int
self._write_data(LIS2DH12_INT1_THS, int_ths)
self._write_data(LIS2DH12_INT1_DURATION, duration) # set duration
def start_sensor(self):
'''
start the sensor
'''
self._write_data(LIS2DH12_CTRL_REG1, 0x77) # ODR 100HZ ,enable XYZ.
utime.sleep_ms(20) # (7/ODR) = 18ms
def process_xyz(self):
'''
Read registers and convert x-axis, y-axis, and z-axis data
:return: x,y,z data
'''
data = []
ctl4 = self._read_data(LIS2DH12_CTRL_REG4, 1)[0]
big_endian = ctl4 & 0x40
# read xl,xh,yl,yh,zl,zh
for i in range(6):
r_data = self._read_data(LIS2DH12_OUT_X_L + i, 1)
data.append(r_data[0])
if big_endian:
x = data[0] * 256 + data[1]
y = data[2] * 256 + data[3]
z = data[4] * 256 + data[5]
else:
x = data[1] * 256 + data[0]
y = data[3] * 256 + data[2]
z = data[5] * 256 + data[4]
return (x, y, z)
def int_processing_data(self):
'''
handle int_processing
:return: x,y,z-axis acceleration
'''
acc = self.read_acceleration
int_src = self._read_data(LIS2DH12_INT1_SRC,1) # read INT1_SRC,clear interrupt request
return acc
@property
def _resolution(self):
"""
resolution range.
:return: range_2_G, range_4_G, range_8_G,, range_16_G.
"""
ctl4 = self._read_data(LIS2DH12_CTRL_REG4,1)[0]
return (ctl4 >> 4) & 0x03
@property
def _acceleration(self):
"""
x,y,z-axis acceleration
:return: x,y,z-axis acceleration
"""
divider = 1
accel_range = self._resolution
if accel_range == 3: # range_16_G
divider = 2048
elif accel_range == 2: # range_8_G
divider = 4096
elif accel_range == 1: # range_4_G
divider = 8192
elif accel_range == 0: # range_2_G
divider = 16384
x, y, z = self.process_xyz()
x = x / divider
y = y / divider
z = z / divider
if accel_range == 3: # range_16_G
print('range_16_G')
x = x if x <= 16 else x - 32
y = y if y <= 16 else y - 32
z = z if z <= 16 else z - 32
elif accel_range == 2: # range_8_G
print('range_8_G')
x = x if x <= 8 else x - 16
y = y if y <= 8 else y - 16
z = z if z <= 8 else z - 16
elif accel_range == 1: # range_4_G
print('range_4_G')
x = x if x <= 4 else x - 8
y = y if y <= 4 else y - 8
z = z if z <= 4 else z - 8
elif accel_range == 0: # range_2_G
print('range_2_G')
x = x if x <= 2 else x - 4
y = y if y <= 2 else y - 4
z = z if z <= 2 else z - 4
return (x, y, z)
@property
def read_acceleration(self):
'''
read acceleration
:return: x,y,z-axis acceleration
'''
while 1:
status = self._read_data(LIS2DH12_STATUS_REG,1)[0]
xyzda = status & 0x08 # if xyz data exists, set 1
xyzor = status & 0x80
if not xyzda:
continue
else:
x,y,z = self._acceleration
return (x, y, z)
def set_mode(self,mode):
"""
set work mode
:param mode: 0: High resolution mode; 1: Normal mode; 2: Low power mode;
:return: None
"""
if mode == 0:
self._write_data(LIS2DH12_CTRL_REG1, 0x77) # ODR 400HZ ,enable XYZ.
self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, High resolution mode
elif mode == 1:
self._write_data(LIS2DH12_CTRL_REG1, 0x57) # ODR 100HZ ,enable XYZ.
self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, Normal mode
elif mode == 2:
self._write_data(LIS2DH12_CTRL_REG1, 0x8f)
self._write_data(LIS2DH12_CTRL_REG4, 0x08) # ±2g, Low power mode
else:
print("wrong mode.")
def set_int_callback(self, cb):
self._extint = ExtInt(self._int_pin, ExtInt.IRQ_FALLING, ExtInt.PULL_PU, cb)
Note
- The parameter of
int_enable
needs to be determined according to the datasheet and specific application scenarios. For example, if you want to avoid frequent false triggering of the interrupt, the parametersint_ths
andduration
must be set greater, and the values depend on the test results.- When the three-axis sensor is placed vertically with the z-axis upwards and there is no external force, the acceleration of the
x
,y
, andz
axes is basically (0,0,1), in units ofG
. This can be used to determine whether the sensor reading and acceleration calculation are normal.- It is recommended to use external interrupt to handle interrupts, rather than polling the interrupt register of the sensor. The latter method will prevent entering low-power mode.
The main program code is designed as follows:
def int_cb(args):
print('click just happened...')
acc = dev.int_processing_data()
print("read_acceleration result: ", acc)
if __name__ == "__main__":
# initialize i2c
i2c_dev = I2C(I2C.I2C1, I2C.STANDARD_MODE)
# initialize the lis2dh12 object
dev = lis2dh12(i2c_dev, 14)
# enable single_click_interrupt
dev.int_enable(XYZ_SINGLE_CLICK_INT)
# set interrupt callback
dev.set_int_callback(int_cb)
# start sensor
dev.start_sensor()
# LP mode
dev.set_mode(2)
print("interrupt detecting...")
Verification
Download the experimental code to the module and run it. When you click the sensor, you can see the content in the figure below printed on the "REPL" interface. The tuples in parentheses are the accelerations of the three axes at the moment of triggering, in units of G
.