LIS3DH 三轴加速度传感器使用流程及示例

 

LIS3DH是一个三轴加速度传感器,可用于检测X、Y、Z轴上的加速度,以及设置阈值,在加速度超出阈值时触发中断,支持SPI和IIC读写。

下面主要是IIC方式读写:

SPI方式读写类似,参考手册即可。一般情况下,可以用2根线,没必要用3线或者4线。而且IIC用GPIO模拟很方便,网上例程很多,这里就不贴了,搜一搜就有。

 

一、寄存器读写流程


首先硬件连接会决定该模块的IIC地址
如果SAO脚接地,IIC地址为0X30(不包含最低位).
如果接V3.3,则IIC地址为0X32(不包含最低位).

这个器件地址很重要,因为读写寄存器都是要先写一下对应的器件地址,然后再执行命令的。

注意先发送的器件地址只是前7位,最后一位决定的是读写位,0是写寄存器,1为读寄存器,所以写寄存器时是先写入“LIS_ADDR|0x00”,读的时候则是先写入“LIS_ADDR|0x01”,(LIS_ADDR这里指上文的IIC地址)。

a、主机向从机写一个字节,下表是通过IIC向LIS3DH寄存器写一个字节的流程。简单来说就是:先写1个字节的写命令+IIC地址,再写一个字节的你要写的寄存器地址,最后写一个字节你要写入寄存器的内容。

示例:

//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
//其他,错误代码
uint8_t LIS_Write_Byte(uint8_t reg,uint8_t data)
{
  	IIC_Start(); 
	IIC_Send_Byte(LIS_ADDR|0x00);//发送器件地址+写命令
	if(IIC_Wait_Ack())	//等待应答
	{
		IIC_Stop();		 
		return 1;		
	}
	IIC_Send_Byte(reg|0x00);		//写寄存器地址  D7位为低用于单字节传输
	IIC_Wait_Ack();					//等待应答
	IIC_Send_Byte(data);			//发送数据
	if(IIC_Wait_Ack())				//等待ACK
	{
		IIC_Stop();
		return 1;
	}
	IIC_Stop();	
	Sleep(5000);    //休眠5ms
	return 0;
}

 

b、LIS3DH读寄存器一个字节
流程:先写1个字节的写命令+IIC地址,再写你要读的寄存器,然后写1个字节的读命令+IIC地址,最后读一个字节的数据

示例:

//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
uint8_t LIS_Read_Byte(uint8_t reg)
{
	uint8_t res=0x00;

  	IIC_Start();
	IIC_Send_Byte(LIS_ADDR|0x00);//发送器件地址+写命令	
	if(IIC_Wait_Ack())		//等待ACK
	{
		IIC_Stop();	 
		return 1;		 
	}
	IIC_Send_Byte(reg);	//写寄存器地址
	IIC_Wait_Ack();		//等待应答
	IIC_Start();
	IIC_Send_Byte(LIS_ADDR|0x01);	//发送器件地址+读命令	
	IIC_Wait_Ack();					//等待应答 
	res = IIC_Read_Byte(0);			//读取数据,发送nACK
	IIC_Stop();						//产生一个停止条件 
	return res;
}

c、IIC连续写寄存器:


示例:

//IIC连续写
//addr:器件地址 
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
//	其他,错误代码
uint8_t LIS_Write_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
	uint8_t i; 
  IIC_Start();
	IIC_Send_Byte(LIS_ADDR|0x00);//发送器件地址+写命令
	if(IIC_Wait_Ack())	//等待应答
	{
		IIC_Stop();		 
		return 1;		
	}
    IIC_Send_Byte(reg|0x80);	//写寄存器地址  D7位为高用于多字节传输
    IIC_Wait_Ack();		//等待应答
	for(i=0;i<len;i++)
	{
		IIC_Send_Byte(buf[i]);	//发送数据
		if(IIC_Wait_Ack())		//等待ACK
		{
			IIC_Stop();	 
			return 1;		 
		}		
	}    
    IIC_Stop();
	return 0;	
}

d、IIC方式连续读寄存器。注意MAK和NMAK。

示例:

//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
//    其他,错误代码
uint8_t LIS_Read_Len(uint8_t addr,uint8_t reg,uint8_t len,uint8_t *buf)
{
 	IIC_Start();
	IIC_Send_Byte(LIS_ADDR|0x00);//发送器件地址+写命令
	if(IIC_Wait_Ack())	//等待应答
	{
		IIC_Stop();		 
		return 1;		
	}
    IIC_Send_Byte(reg|0x80);	//写寄存器地址  D7位为高用于多字节传输
    IIC_Wait_Ack();		//等待应答
    IIC_Start();
	IIC_Send_Byte(LIS_ADDR|0x01);//发送器件地址+读命令
    IIC_Wait_Ack();		//等待应答
	while(len)
	{
		if(len==1)*buf=IIC_Read_Byte(0);//读数据,发送nACK 
		else *buf=IIC_Read_Byte(1);		//读数据,发送ACK  
		len--;
		buf++; 
	}    
    IIC_Stop();	//产生一个停止条件 
	return 0;	
}

二、设置阈值震动触发中断应用

 

首先,通过读取一个叫WHO_AM_I的寄存器,可以用来确定芯片可用,因为这个寄存器读取的值是恒定的0x33,这通常用来做芯片的初始化检测。

 

设置阈值触发中断示例:

该实例设置了中断锁存。即直到重新读取INT1_SRC寄存器前,中断都不会被清除,引脚会被一直强拉到重新读取INT1_SRC为止。

一般情况情况下,三轴加速度传感器记录了XYZ三个轴的加速度数据,这个加速度应该包括重力加速度的。理论上,重力加速度会一直存在,影响我们的中断检测,所以我们需要想办法排除它。好消息是芯片本身已经自带了一个内部滤波器,可以通过设置一个初始状态,让芯片在随后的过程中始终将读到的加速度与这个初始值做对比,差值才视为加速度的值,这个值超过一定范围才触发中断,这样重力加速度的因素就可以被排除。下面的示例就是这么做的:

	LIS_Write_Byte(CTRL_REG1,0x3F);			//设置低功耗模式 xyz轴使能 采样频率25HZ
	LIS_Write_Byte(CTRL_REG2,0x09);			//高通滤波正常模式,数据从内部滤波器到输出寄存器 高通滤波器使能到中断1
	LIS_Write_Byte(CTRL_REG3,0x40);			//使能AOI中断1
	LIS_Write_Byte(CTRL_REG4,0x80);			//分辨率为+-2g  则单位为 4000/(2^8)=16mg       仅在LSB和MSB被读时才更新数据
	LIS_Write_Byte(CTRL_REG5,0x08);			//寄存器锁存中断,只有读INT1_SRC可以恢复
	LIS_Write_Byte(INT1_THS,0x10);			//设置阈值 16*16=256 mg
	LIS_Write_Byte(INT1_DURATION,0x00);		//中断持续时间设置,此处设为0,因为不读INT1_SRC中断就一直锁存
	LIS_Read_Byte(REFERENCE);				//读虚寄存器,强制将当前加速度值加载到高通滤波器值进行比较

	//INT1_CFG使能中断  中断模式为 “OR”组合 只使能xyz轴高事件
	//LIS_Write_Byte(INT1_CFG,0x2A);
	//INT1_CFG使能中断  中断模式6-direction position  使能xyz轴高低事件
	LIS_Write_Byte(INT1_CFG,0xFF);
	LIS_Read_Byte(INT1_SRC);//清除中断位

不过这样也要注意,在芯片初始化,记录起始状态的时候(装载滤波器值),要尽量保证芯片处于静止状态,否则对加速度的差值判断就不准了。

另外,还可以设置中断触发指定时间,在指定时间内中断引脚强制拉高,随后引脚会还原。主要通过设置INT1_DURATION (33h)寄存器。

这个ODR可以通过GTRL_REG1设置。

最后中断持续时间就是INT1_DURATION/ODR

 

三、FIFO模式读取X、Y、Z轴方向加速度值

 

一般如果没有高速读写需求,直接读OUT_X_L、OUT_X_H等6个寄存器,就可以读到XYZ轴的加速度值。通过配置部分CREG寄存器,可以设置这些加速度值的精度,采样频率等,具体可以参考手册,这里就不详细展开了。

出于高速读写的需求,可能需要使用FIFO方式来进行加速度的读取。

LIS3DH的FIFO模式有4种:

1、Bypass mode

中间模式,只有切换模式的时候用得到

2、FIFO mode

数据填充满缓冲区后,停止继续填充

3、Stream mode

数据持续输出,填充满缓冲区后会覆盖旧的数据,数据依次向前推一字节

4、Stream-to-FIFO mode

FIFObuffer开始处于stream模式,等到中断脚选择触发时,切换到FIFO模式。FIFO_CTRL_REG (2Eh)的TR位可以决定是INT1脚还是INT2脚来进行控制。芯片手册上貌似写这是一种用于调试的模式,实际应用前两种应该就够了

 

示例:读取XYZ轴方向的加速度,用Stream mode。

watermark是指配置FSS [4:0]来检查读取的时候数据总量是否超出指定的范围。因为FIFO的缓冲区最多为32级,比如可以设置为20,则读取的时候,如果当时数据超出20个,则FIFO_SRC_REG (2Fh) (WTM)位会被置1,否则为0。

如果32级缓冲区全部满了,FIFO_SRC_REG (2Fh) 的 OVRN_FIFO会被置1。这两个标志位都是可以配置触发中断的,可以将这两个事件配置到芯片的两个中断引脚用于检测FIFO溢出。

//测试FIFO
IIC_WriteByte(LIS3DH_CTRL_REG1,0x80|0x0F);   //0010 0111	低功耗模式
IIC_WriteByte(LIS3DH_CTRL_REG2,0x00);				//高通滤波关闭

IIC_WriteByte(LIS3DH_CTRL_REG3,0x06);				//使能FIFO中断    0000 0110
IIC_WriteByte(LIS3DH_CTRL_REG4,0x00);				//分辨率+-16g 		0011 0000

IIC_WriteByte(LIS3DH_CTRL_REG5,0x48);					//FIFO使能	0100 1000
IIC_WriteByte(LIS3DH_FIFO_CTRL,0x80|0x1D);		//0100 1111	配置FIFO模式和水印
	
IIC_ReadByte(LIS3DH_INT1_SRC);	//清除中断位
	
collect_LIS_Data();



void collect_LIS_Data(void)
{
		uint16_t  LIS_temp_data[3] = {0,0,0};
		uint8_t data_len,i;

		FIFO_data_len = IIC_ReadByte(LIS3DH_FIFO_SRC);

		FIFO_data_len &= 0x1F;
		for(i=0;i<FIFO_data_len;i++)
		{
			LIS3DH_ReadData(LIS_temp_data);    //读取3个方向的数据
		}
}

数据会不断刷新,所以要定时读取,读取间隔可以通过ODR来控制调整,也可以利用watermark触发中断读取

 

如果要使用stm32的硬件IIC实现LIS3DH的各种功能配置,可以参考这篇:

https://blog.csdn.net/zhangfls/article/details/109078500

 

  • 25
    点赞
  • 115
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
以下是一个基于 LIS2DH12 三轴加速度传感器的算法来判断状态的C代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdbool.h> #include <wiringPiI2C.h> #define ACCEL_ADDR 0x18 #define ACCEL_CTRL_REG1 0x20 #define ACCEL_CTRL_REG4 0x23 #define ACCEL_X_AXIS 0x28 #define ACCEL_Y_AXIS 0x2A #define ACCEL_Z_AXIS 0x2C #define THRESHOLD 1.0 // 加速度变化阈值 #define SAMPLE_RATE 100 // 采样率(Hz) #define WINDOW_SIZE 10 // 窗口大小(个样本) enum State { IDLE, MOVING, FALLING }; int main(void) { int fd = wiringPiI2CSetup(ACCEL_ADDR); if(fd == -1) { printf("Failed to initialize I2C device.\n"); exit(1); } // 配置传感器 wiringPiI2CWriteReg8(fd, ACCEL_CTRL_REG1, 0x77); // 打开传感器并设置采样率 wiringPiI2CWriteReg8(fd, ACCEL_CTRL_REG4, 0x08); // 设置测量范围为 +/- 8g // 初始化前 WINDOW_SIZE 个样本的加速度值 float prev_accel_x[WINDOW_SIZE]; float prev_accel_y[WINDOW_SIZE]; float prev_accel_z[WINDOW_SIZE]; for(int i = 0; i < WINDOW_SIZE; i++) { int x = wiringPiI2CReadReg16(fd, ACCEL_X_AXIS); int y = wiringPiI2CReadReg16(fd, ACCEL_Y_AXIS); int z = wiringPiI2CReadReg16(fd, ACCEL_Z_AXIS); // 将读取到的原始数据转换为实际加速度值 prev_accel_x[i] = (float)x / 1024.0; prev_accel_y[i] = (float)y / 1024.0; prev_accel_z[i] = (float)z / 1024.0; usleep(1000000 / SAMPLE_RATE); // 等待一段时间,以达到采样率 } // 初始化状态 enum State state = IDLE; while(1) { // 读取最新的加速度值 int x = wiringPiI2CReadReg16(fd, ACCEL_X_AXIS); int y = wiringPiI2CReadReg16(fd, ACCEL_Y_AXIS); int z = wiringPiI2CReadReg16(fd, ACCEL_Z_AXIS); // 将读取到的原始数据转换为实际加速度值 float accel_x = (float)x / 1024.0; float accel_y = (float)y / 1024.0; float accel_z = (float)z / 1024.0; // 计算加速度变化值 float delta_accel_x = 0; float delta_accel_y = 0; float delta_accel_z = 0; for(int i = 0; i < WINDOW_SIZE; i++) { delta_accel_x += abs(accel_x - prev_accel_x[i]); delta_accel_y += abs(accel_y - prev_accel_y[i]); delta_accel_z += abs(accel_z - prev_accel_z[i]); } delta_accel_x /= WINDOW_SIZE; delta_accel_y /= WINDOW_SIZE; delta_accel_z /= WINDOW_SIZE; // 根据加速度变化值判断状态 switch(state) { case IDLE: if(delta_accel_x > THRESHOLD || delta_accel_y > THRESHOLD || delta_accel_z > THRESHOLD) { state = MOVING; printf("State: MOVING\n"); } break; case MOVING: if(delta_accel_x > THRESHOLD || delta_accel_y > THRESHOLD || delta_accel_z > THRESHOLD) { state = MOVING; } else { state = FALLING; printf("State: FALLING\n"); } break; case FALLING: if(delta_accel_z > THRESHOLD) { state = IDLE; printf("State: IDLE\n"); } break; } // 更新前 WINDOW_SIZE 个样本的加速度值 for(int i = WINDOW_SIZE - 1; i > 0; i--) { prev_accel_x[i] = prev_accel_x[i - 1]; prev_accel_y[i] = prev_accel_y[i - 1]; prev_accel_z[i] = prev_accel_z[i - 1]; } prev_accel_x[0] = accel_x; prev_accel_y[0] = accel_y; prev_accel_z[0] = accel_z; // 等待一段时间,以达到采样率 usleep(1000000 / SAMPLE_RATE); } return 0; } ``` 该代码使用了和前面类似的方式来读取 LIS2DH12 三轴加速度传感器的数据,并根据加速度变化的情况来判断状态。具体地,根据阈值 THRESHOLD 来判断加速度是否变化,根据窗口大小 WINDOW_SIZE 来计算加速度变化值,然后根据加速度变化值的大小来判断状态。如果加速度变化超过阈值且当前状态为 IDLE,则进入 MOVING 状态;如果加速度变化超过阈值且当前状态为 MOVING,则保持 MOVING 状态;如果加速度变化没有超过阈值且当前状态为 MOVING,则进入 FALLING 状态;如果加速度变化超过阈值且当前状态为 FALLING,则保持 FALLING 状态;如果加速度在 Z 轴方向上超过阈值且当前状态为 FALLING,则回到 IDLE 状态。注意,需要根据具体的传感器型号和配置修改代码中的地址和寄存器值,以及阈值、采样率和窗口大小的大小。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值