LIS3DH 加速度传感器
LIS3DH 是一个 3D 加速度传感器,可以测量 3 个轴的加速度。它具有高分辨率、低功耗和宽工作温度范围的特点,适用于各种应用场景,如智能手机、平板电脑、可穿戴设备和工业设备等。
数据手册
主要特性
- 3D 加速度传感器,可以测量 X、Y 和 Z 三个轴的加速度
- 高分辨率,最高可达 12 位,分辨率为 0.061 mg/LSB
- 低功耗,工作电流可低至 200 μA
- 中断功能,可检测到 X、Y 和 Z 三个轴的加速度变化
程序开发要点
- 初始化传感器中的寄存器
c
// LIS30DH寄存器地址
#define LIS3DH_WHO_AM_I 0x0F
#define LIS3DH_CTRL_REG1 0x20
#define LIS3DH_CTRL_REG4 0x23
#define LIS3DH_OUT_X_L 0x28
#define LIS3DH_OUT_X_H 0x29
#define LIS3DH_OUT_Y_L 0x2A
#define LIS3DH_OUT_Y_H 0x2B
#define LIS3DH_OUT_Z_L 0x2C
#define LIS3DH_OUT_Z_H 0x2D
void init_lis3dh(void) {
// 验证设备ID
uint8_t id = lis3dh_read_reg(LIS3DH_WHO_AM_I);
if (id != 0x33) {
ESP_LOGE(TAG, "设备ID错误: 0x%02X (应为0x33)", id);
return;
}
ESP_LOGI(TAG, "检测到LIS3DH (ID: 0x%02X)", id);
// 配置CTRL_REG1: 100Hz ODR, 使能XYZ轴
lis3dh_write_reg(LIS3DH_CTRL_REG1, 0x57); // 0101 0111
// 配置CTRL_REG4: +-2g量程, 高分辨率模式
lis3dh_write_reg(LIS3DH_CTRL_REG4, 0x88); // 1000 1000
vTaskDelay(pdMS_TO_TICKS(20)); // 等待配置生效
}这里配置了两个寄存器的值,分别表示:
1. CTRL_REG1: 100Hz ODR, 使能XYZ轴 (数据手册的第35页)
2. CTRL_REG4: +-2g量程, 高分辨率模式(数据手册的第37页)
- 读取传感器数据
c
// 读取XYZ轴加速度
void read_acceleration(int16_t *x, int16_t *y, int16_t *z) {
uint8_t data[6];
// 多字节读取(自动递增地址)
spi_transaction_t t = {
.cmd = (LIS3DH_OUT_X_L | 0x80 | 0x40), // 读+自动递增
.length = 48, // 6字节 * 8位
.rx_buffer = data,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
// 组合高低字节(数据为小端模式)
*x = (int16_t)((data[1] << 8) | data[0]);
*y = (int16_t)((data[3] << 8) | data[2]);
*z = (int16_t)((data[5] << 8) | data[4]);
// 转换为12位有符号数(右移4位)
*x >>= 4;
*y >>= 4;
*z >>= 4;
}这里使用了多字节读取,因为LIS3DH的寄存器是连续的,使用自动递增地址可以一次性读取多个寄存器的数据。同时,由于数据是12位有符号数,需要右移4位才能得到正确的值。参考手册的第28页的说明.
使用SPI接口来驱动LIS3DH
使用ESP32-C3开发板
c
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "driver/gpio.h"
#include "esp_log.h"
// #include "esp_timer.h"
// 引脚配置(根据实际硬件连接修改)
#define PIN_NUM_MISO 2 // SPI MISO
#define PIN_NUM_MOSI 1 // SPI MOSI
#define PIN_NUM_CLK 0 // SPI CLK
#define PIN_NUM_CS 3 // SPI CS
#define PIN_LED GPIO_NUM_4 // LED指示灯
// LIS30DH寄存器地址
#define LIS3DH_WHO_AM_I 0x0F
#define LIS3DH_CTRL_REG1 0x20
#define LIS3DH_CTRL_REG4 0x23
#define LIS3DH_OUT_X_L 0x28
#define LIS3DH_OUT_X_H 0x29
#define LIS3DH_OUT_Y_L 0x2A
#define LIS3DH_OUT_Y_H 0x2B
#define LIS3DH_OUT_Z_L 0x2C
#define LIS3DH_OUT_Z_H 0x2D
// 配置参数
#define SPI_CLOCK_SPEED 1000000 // 1 MHz SPI时钟
#define SAMPLE_RATE_HZ 100 // 采样率100Hz
#define VIBRATION_THRESHOLD 5 // 振动阈值(约0.1g)
#define MOVING_AVG_SIZE 5 // 移动平均窗口大小
static const char *TAG = "LIS3DH";
spi_device_handle_t spi;
// SPI初始化
void init_spi(void) {
spi_bus_config_t buscfg = {
.miso_io_num = PIN_NUM_MISO,
.mosi_io_num = PIN_NUM_MOSI,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 0
};
spi_device_interface_config_t devcfg = {
.clock_speed_hz = SPI_CLOCK_SPEED,
.mode = 3, // CPOL=1, CPHA=1
.spics_io_num = PIN_NUM_CS,
.queue_size = 7,
.command_bits = 8, // 寄存器地址作为command发送
.address_bits = 0,
};
// 初始化SPI总线
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
ESP_ERROR_CHECK(spi_bus_add_device(SPI2_HOST, &devcfg, &spi));
}
// 写入寄存器
void lis3dh_write_reg(uint8_t reg, uint8_t value) {
spi_transaction_t t = {
.cmd = reg, // 清除读写位(bit7)和地址递增位(bit6)
.length = 8,
.tx_buffer = &value,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
}
// 读取寄存器
uint8_t lis3dh_read_reg(uint8_t reg) {
uint8_t rx_data = 0;
spi_transaction_t t = {
.cmd = (reg | 0x80), // 设置读位(bit7)
.length = 8,
.rx_buffer = &rx_data,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
return rx_data;
}
// 初始化LIS3DH
void init_lis3dh(void) {
// 验证设备ID
uint8_t id = lis3dh_read_reg(LIS3DH_WHO_AM_I);
if (id != 0x33) {
ESP_LOGE(TAG, "设备ID错误: 0x%02X (应为0x33)", id);
return;
}
ESP_LOGI(TAG, "检测到LIS3DH (ID: 0x%02X)", id);
// 配置CTRL_REG1: 100Hz ODR, 使能XYZ轴
lis3dh_write_reg(LIS3DH_CTRL_REG1, 0x57); // 0101 0111
// 配置CTRL_REG4: +-2g量程, 高分辨率模式
lis3dh_write_reg(LIS3DH_CTRL_REG4, 0x88); // 1000 1000
vTaskDelay(pdMS_TO_TICKS(20)); // 等待配置生效
}
// 读取三轴加速度
void read_acceleration(int16_t *x, int16_t *y, int16_t *z) {
uint8_t data[6];
// 多字节读取(自动递增地址)
spi_transaction_t t = {
.cmd = (LIS3DH_OUT_X_L | 0x80 | 0x40), // 读+自动递增
.length = 48, // 6字节 * 8位
.rx_buffer = data,
};
ESP_ERROR_CHECK(spi_device_transmit(spi, &t));
// 组合高低字节(数据为小端模式)
*x = (int16_t)((data[1] << 8) | data[0]);
*y = (int16_t)((data[3] << 8) | data[2]);
*z = (int16_t)((data[5] << 8) | data[4]);
// 转换为12位有符号数(右移4位)
*x >>= 4;
*y >>= 4;
*z >>= 4;
}
// 检测振动
bool detect_vibration(int16_t x, int16_t y, int16_t z) {
static int16_t history_x[MOVING_AVG_SIZE] = {0};
static int16_t history_y[MOVING_AVG_SIZE] = {0};
static int16_t history_z[MOVING_AVG_SIZE] = {0};
static uint8_t index = 0;
// 更新历史数据
history_x[index] = x;
history_y[index] = y;
history_z[index] = z;
index = (index + 1) % MOVING_AVG_SIZE;
// 计算移动平均
int32_t avg_x = 0, avg_y = 0, avg_z = 0;
for (int i = 0; i < MOVING_AVG_SIZE; i++) {
avg_x += history_x[i];
avg_y += history_y[i];
avg_z += history_z[i];
}
avg_x /= MOVING_AVG_SIZE;
avg_y /= MOVING_AVG_SIZE;
avg_z /= MOVING_AVG_SIZE;
// 计算与平均值的差异(瞬时振动)
int16_t diff_x = abs(x - avg_x);
int16_t diff_y = abs(y - avg_y);
int16_t diff_z = abs(z - avg_z);
// 检查是否超过阈值
return (diff_x > VIBRATION_THRESHOLD) ||
(diff_y > VIBRATION_THRESHOLD) ||
(diff_z > VIBRATION_THRESHOLD);
}
void app_main() {
// 初始化外设
// 将 PIN_LED 设置为推挽输出
gpio_reset_pin(PIN_LED);
gpio_set_direction(PIN_LED, GPIO_MODE_OUTPUT);
init_spi();
init_lis3dh();
int16_t x, y, z;
uint32_t last_wake_time = xTaskGetTickCount();
uint8_t led_state = 0;
while (1) {
// 读取加速度数据
read_acceleration(&x, &y, &z);
// 检测振动
if (detect_vibration(x, y, z)) {
// 翻转 PIN_LED 状态
led_state = led_state == 1 ? 0 : 1; // 翻转LED状态
gpio_set_level(PIN_LED, led_state);
ESP_LOGI(TAG, "检测到振动! X:%6d Y:%6d Z:%6d", x, y, z);
}
// 按固定频率采样
vTaskDelayUntil(&last_wake_time, pdMS_TO_TICKS(1000 / SAMPLE_RATE_HZ));
}
}使用 STC8G1K08A-8pin 板
如何使用INT中断
中断引脚的核心作用
LIS3DH的中断引脚(INT1和INT2)提供了高效的事件驱动机制,无需微控制器持续轮询传感器,具有以下核心优势:
- 即时响应:检测到特定事件时立即通知处理器
- 降低功耗:主处理器可休眠,仅当中断触发时唤醒
- 减少总线负载:避免持续SPI通信消耗带宽
- 简化程序设计:事件驱动编程模型更简洁高效
中断使用方法详解
- 1.运动检测中断: 振动唤醒、跌倒检测、运动检测
c
// 配置运动检测中断
lis3dh_write_reg(0x30, 0x2A); // XYZ轴高阈值检测
lis3dh_write_reg(0x32, 0x20); // 设置阈值(约0.4g)- 2.静止检测中断: 静止检测、静止唤醒
c
// 配置静止检测中断
lis3dh_write_reg(0x30, 0x15); // XYZ轴低阈值检测
lis3dh_write_reg(0x32, 0x10); // 设置低阈值(约0.2g)- 3.单击/双击中断: 单击/双击检测
c
// 配置单击检测
lis3dh_write_reg(0x38, 0x15); // CLICK_CFG: 使能XYZ轴单击检测
lis3dh_write_reg(0x3A, 0x10); // CLICK_THS: 单击阈值- 4.方向检测中断: 屏幕的旋转检测
c
// 配置6D方向检测
lis3dh_write_reg(0x30, 0x40); // 6D方向检测模式- 5.数据就绪中断: ADC数据采集
c
// 配置数据就绪中断
lis3dh_write_reg(0x22, 0x10); // I1_ZYXDA使能中断的使用代码示例:
c
// 寄存器定义
#define LIS3DH_CTRL_REG1 0x20
#define LIS3DH_CTRL_REG3 0x22
#define LIS3DH_CTRL_REG4 0x23
#define LIS3DH_INT1_CFG 0x30
#define LIS3DH_INT1_SRC 0x31
#define LIS3DH_INT1_THS 0x32
#define LIS3DH_INT1_DUR 0x33
#define LIS3DH_STATUS_REG 0x27
// 配置振动检测中断
void init_lis3dh(void) {
// 复位设备
lis3dh_write_reg(0x20, 0x57); // CTRL_REG1: 100Hz ODR, 高功率模式
// 配置高通滤波器 (关键!)
lis3dh_write_reg(0x21, 0x19); // CTRL_REG2: HPF使能, 截止频率1Hz
// 配置中断1
lis3dh_write_reg(0x30, 0x7F); // INT1_CFG: 差分阈值检测(XLIE,YLIE,ZLIE)
lis3dh_write_reg(0x32, 0x05); // INT1_THS: 0.06g阈值(用于微小振动)
lis3dh_write_reg(0x33, 0x01); // INT1_DUR: 50ms持续时间(用于微小振动)
// 配置量程和分辨率
lis3dh_write_reg(0x23, 0x08); // CTRL_REG4: ±2g, 高分辨率模式
// 配置中断引脚
lis3dh_write_reg(0x25, 0x02); // CTRL_REG6: INT1引脚推挽输出,
// 启用中断路由
lis3dh_write_reg(0x22, 0x40); // CTRL_REG3: 路由中断发生器1到INT1
}这段代码设置后,会感知轻微的震动.并触发INT1引脚的中断.
关于阀值的设置
在设置 INT1_THS (32h) 的值作为阈值时.需要考虑量程和阀值的位数(7位).其阀值的计算公式为:
c
threshold = (INT1_THS * 量程) / (2^7)量程是由 CTRL_REG4 的 FS 位决定的.其值如下: 量程选择. 默认值: 00 (00: ±2 g; 01: ±4 g; 10: ±8 g; 11: ±16 g)
默认情况下,量程为 ±2 g.因此,阀值的计算公式为:
c
threshold = (INT1_THS * 2) / (2^7)例如,如果 INT1_THS 的值为 0x30 (48),则阀值为:
c
threshold = (48 * 2) / (2^7) = 0.5 g如何使用ADC引脚采集数据
LIS3DH芯片有三个adc引脚,分别是:ADC1,ADC2,ADC3,可以用来采集加速度数据,也可以用来采集温度数据. LIS3DH 的 ADC 引脚是一个模拟输入通道,它扩展了加速度计的功能,使其不仅能测量加速度,还能采集外部模拟信号。这个引脚的关键作用在于:
- 多传感器集成:允许在不增加额外ADC芯片的情况下连接外部模拟传感器
- 系统简化:减少电路板上元件数量和PCB空间占用
- 同步采集:可实现加速度数据与模拟信号的精确时间同步
- 功耗优化:比外置ADC更省电,适用于电池供电设备
ADC原理框图

配置与使用流程
- 启用ADC功能
c
// 启用ADC和温度传感器(需同时启用)
#define LIS3DH_TEMP_CFG_REG 0x1F
void enable_adc(void) {
// 写入0xC0: 启用ADC+温度传感器
lis3dh_write_reg(LIS3DH_TEMP_CFG_REG, 0xC0);
// 设置ODR启动转换(示例100Hz)
lis3dh_write_reg(0x20, 0x57); // CTRL_REG1: 100Hz ODR
vTaskDelay(pdMS_TO_TICKS(10)); // 等待稳定
}- 读取ADC数据
c
// 读取ADC1的10位值(0-1023)
uint16_t read_adc1(void) {
uint8_t adc_l = lis3dh_read_reg(0x08); // OUT_ADC1_L
uint8_t adc_h = lis3dh_read_reg(0x09); // OUT_ADC1_H
// 组合10位值: adc_h[1:0] + adc_l[7:0]
return (uint16_t)(((adc_h & 0x03) << 8) | adc_l);
}- 转为实际值
c
// 假设Vdd=3.3V
float adc_to_voltage(uint16_t adc_value) {
return (adc_value / 1023.0f) * 3.3f;
}
// 使用示例:
uint16_t adc_val = read_adc1();
float voltage = adc_to_voltage(adc_val);
ESP_LOGI("ADC", "ADC1电压: %.2fV", voltage);