ADXL345传感器详解
ADXL345硬件原理图设计如下:
SPI接口或者I2C接口选择
当CS上拉时,设置为I2C接口。当CS未有上拉时,设置为SPI接口
具体设置可以参考Datasheet
中断源为0X87
Activity检测可以用来检测传感器是否被触碰,或者处于活动状态。Inactivity用来检测传感器是否处于非活动状态。
Link位:当设置了Link位后,Activity和Inactivity交互进行。如果不设置Link位,而设备一直处静止状态,则将一直触发Inactivity中断。
ADXL345需要通过放在绝对水平的位置,进行校准。设计到校准的寄存器如下包括校准寄存器(0X1E,0X1F,0X20)。校准寄存器比例因子为15.6mg/LSB,也就是说0X7F代表2G;这个寄存器的数值会自动加到加速度值中。
加速度传感器内含MEMS机械结构,这个器件组装到PCB上之后,由于安装等各种原因,可能导致并非绝对精准,从而需要进行数据校准。通常校准时,Z轴为1G, X,Y为0G。校准时,通常采用默认的100Hz采样率连续采样10个样板(也就是对应于0.1秒采样时间),测量的值为X,Y,Z的偏移量,分别对应X,Y在0G, Z在1G的情况。
所以X,Y,Z对应实际值为:
注意:这里Z轴采样值,需要减去255,才是对应的Z0g的值,具体在下面例子中查看。
设置了偏移寄存器的好处是:输出数据将自动加上偏移寄存器的数值。
偏移寄存器的数值是用加法 计算的。因此,如果水平位置原始值为负值,则偏移寄存器数值为正,如果水平位置原始值为正,则偏移寄存器数值为负(用反码表示,反码中0XFF表示-1)
例如:水平放置时,X读数-5,y读数2, Z读数270;这时,正常值应该是0,0,255,因此需要把偏移量设置为5, -2, -15。但是这里注意了,ADXL345默认13位分辨率,16G量程时,1 LSB为1/256 = 3.9mg/LSB。
由于偏移寄存器为15.6mg/LSB,所以偏移寄存器写1个LSB,相当于偏移了4个LSB,所以偏移寄存器的值,需要除以4。例如,上面偏移量为4,则偏移寄存器写4/1 = 1即可。
所以实际偏移量应该为5/4=1, -2/4=0, -15/4=-4, 其中负数用反码表示,所以为1, 0 , FC, 分别写入off_x, off_y和off_z即可。
通过上述校准方式,可以设计自校准模式如下:
// 自动校准
// xval,yval,zval:x,y,z轴的校准值
void ADXL345_AUTO_Adjust(char *xval,char *yval,char *zval)
{
u8 i, powerCtl, dataFormat, bwRate, intEnable;
ACCELEROMETER acc;
short offx=0,offy=0,offz=0;
assert_param(xval);
assert_param(yval);
assert_param(zval);
// 先保存将被改写的寄存器内容
ADXL345_RD_Reg(POWER_CTL, &powerCtl, 1);
ADXL345_RD_Reg(DATA_FORMAT, &dataFormat, 1);
ADXL345_RD_Reg(BW_RATE, &bwRate, 1);
ADXL345_RD_Reg(INT_ENABLE, &intEnable, 1);
// 先将ADXL设置为休眠状态
ADXL345_WR_Reg(POWER_CTL,0x00);
delay_ms(100);
// 初始化为100Hz 16G量程
ADXL345_WR_Reg(DATA_FORMAT,0X2B);
ADXL345_WR_Reg(BW_RATE,0x0A);
ADXL345_WR_Reg(POWER_CTL,0x28);
ADXL345_WR_Reg(INT_ENABLE,0x00);
ADXL345_WR_Reg(OFSX,0x00);
ADXL345_WR_Reg(OFSY,0x00);
ADXL345_WR_Reg(OFSZ,0x00);
delay_ms(12);
for(i=0;i<10;i++)
{
ADXL345_RD_Avval(&acc);
offx += acc.x;
offy += acc.y;
offz += acc.z;
}
offx/=10;
offy/=10;
offz/=10;
*xval=-offx/4;
*yval=-offy/4;
*zval=-(offz-256)/4;
ADXL345_WR_Reg(OFSX,*xval);
ADXL345_WR_Reg(OFSY,*yval);
ADXL345_WR_Reg(OFSZ,*zval);
// 还原被改写的寄存器内容
ADXL345_WR_Reg(POWER_CTL, powerCtl);
ADXL345_WR_Reg(DATA_FORMAT, dataFormat);
ADXL345_WR_Reg(BW_RATE, bwRate);
ADXL345_WR_Reg(INT_ENABLE, intEnable);
}
注意:采用中断模式,使能敲击检测时,一定要设置DATA_FORMAT.INT_INVERT位为1。通常,大部分中断中为低电平触发,将INT_INVERT设置为1,能够满足中断的低电平触发效果。
对应的,通常设置DATA_FORMAT为0X2B,也就是低电平触发,全分辨率,范围为±16G.
设置敲击检测的阈值,其单位是62.5mg/LSB,例如:设置为0x24,则敲击阈值为2.25G
D3:Supress位:1:如果两次敲击之间的加速度,高于THRESH_TAP,则不作为双击检测;0:取消此功能;
D2~D0: Tap_x,y,z的使能位:1:该方向敲击检测使能;0:该方向敲击检测不使能;
记录敲击或者碰撞的方向。
D6~D4:ACT_XYZ source,记录碰撞的方向;
D2~D0:TAP_XYZ source,记录敲打的方向,例如,如果Z向敲打,就会记录为1;如果y向,则记录为2。如果多个方向均记录到敲打,则多个方向均会被设置。
DUR寄存器用来设置敲击时间长度,其单位是:625us/LSB,例如:设置为0X80,对应的DUR为:128*0.625 = 80ms
Latent(延时)寄存器用来第一次敲击后多久,作为窗口,开始计算第二次敲击。Latent仅在双击中起到作用。其单位是1.25ms/LSB
窗口寄存器用来设置两次敲击之间时间间隔。如果设置的端,则双击需要快一些,否则需要慢一些。其单位是1.25ms/LSB
双击测试结果:
中断使能,写1使能中断,写0禁止中断。
D7: DATA_READY D6:Single Tap; D5:Double Tap; D4:Activity; D3: Inactivity; D2: Free_Fall ; D1: WaterMark D0: Overrun;
需要注意:D7, D1, D0设置为0,并不影响输出,而仅仅影响中断输出。例如:D7设置为0,则不会产生中断,但是数据依旧会输出;
Int_Source与Int_enable一一对应,用来获取中断的触发原因。经过实际测试,发现water_mark中断位总是存在。
ADXL345_WR_Reg(POWER_CTL,0x28); //链接禁止,测量模式
ADXL345_WR_Reg(INT_ENABLE, 0x20); //singletap 0x40, single&double:0x60
ADXL345_WR_Reg(INT_MAP, 0x00); //中断发送到INT1
ADXL345_WR_Reg(OFSX,0x00);
ADXL345_WR_Reg(OFSY,0x00);
ADXL345_WR_Reg(OFSZ,0x00);
ADXL345_WR_Reg(TAP_AXES,0x07); //X,Y,Z方向均检测
ADXL345_WR_Reg(THRESH_TAP,0x24); //敲击阈值,62.5mg/LSB, 0x24表示2.25g
ADXL345_WR_Reg(DUR,0x80); //敲击时长
ADXL345_WR_Reg(Latent,0x22); //双击延时
ADXL345_WR_Reg(Window,0xff); //双击窗口
注意:INT_ENABLE.DATA_READY一定要设置为0,否则DATA_READY会不断使得MCU进入中断模式,从而影响其他中断判断。
1:使能SelfTest, 0:禁止SelfTest; SelfTest主要用来测试传感器是否损坏;
1: 3-WIRE SPI, 0: 4-WIRE SPI, 通常使用4-WIRE SPI;
1:中断低电平有效 0:中断高电平有效。这里需要设置为1, 中断低电平有效。
1:全分辨率模式; 0:非全分辨率模式;
1: 左对齐(MSB) 0:右对齐
00: 2G; 01: 4G; 10:8G, 11: 16G
默认设置:2B模式, 也就是:中断低电平有效, 16G满分辨率模式;
0:正常操作; 1: 低功耗模式
ADXL默认为16G模式,其读数分析如下:
1.读数为2BYTE;
2. 0FFF读数为4095,对应读数为1599(mg)
3. FFFF表示-1, 对应读数为-3.9mg
4. F000为-1600(mg)
这个寄存器用来设置监测ACTIVITY的阈值。数据是无符号的(不带正负),比例因子为62.5mg/LSB
同上,用来监测非活动的阈值。
用来设置非活动状态的时间阈值,比例因子为1sec/LSB;
GPIO初始化时,根据是否支持中断,分为两种方式。
如果不支持中断,则仅仅做到CS初始化即可。
如果支持中断,则需要做到中断先EXTI和中断分组NVIC初始化。
void ADXL_GPIO_Init(void)
{
#ifdef __ADXL_INT_FLAG_
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
#endif
GPIO_InitTypeDef GPIO_InitStructure;
// ADXL345 SPI通讯端口CS初始化
RCC_APB2PeriphClockCmd(ADXL_RCC_APB2Periph_GPIO, ENABLE); //使能PB端口时钟
GPIO_InitStructure.GPIO_Pin = ADXL_PIN_CS;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(ADXL_PORT_CS, &GPIO_InitStructure);
GPIO_SetBits(ADXL_PORT_CS, ADXL_PIN_CS); //PB0上拉
#ifdef __ADXL_INT_FLAG_
//ADXL345中断端口初始化
RCC_APB2PeriphClockCmd(ADXL_RCC_APB2Periph_GPIO_INT, ENABLE);//使能PORT时钟
GPIO_InitStructure.GPIO_Pin = ADXL_PIN_INT1|ADXL_PIN_INT2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //设置成上拉输入
GPIO_Init(ADXL_PORT_INT, &GPIO_InitStructure);
//ADXL345中断线以及中断初始化配置 下降沿触发
GPIO_EXTILineConfig(ADXL_INT_SOURCE_PORT, ADXL_INT_SOURCE1);
EXTI_InitStructure.EXTI_Line = ADXL_EXTI_LINE1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
//ADXL345中断线以及中断初始化配置 下降沿触发
GPIO_EXTILineConfig(ADXL_INT_SOURCE_PORT, ADXL_INT_SOURCE2);
EXTI_InitStructure.EXTI_Line = ADXL_EXTI_LINE2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); //根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器
// 设置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //使能按键KEY2所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02; //抢占优先级2,
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //使能按键KEY0所在的外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x00; //子优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能外部中断通道
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
#endif
}
ADXL345初始化时,会完成GPIO初始化,SPI初始化,然后读出ADXL345配置信息到g_adxlSetValue中。
//初始化ADXL345.
//返回值:0,初始化成功;1,初始化失败.
u8 ADXL345_Init(void)
{
u8 bRet = 1;
u16 *pTemp;
// 初始化ADXL相关的GPIO口
ADXL_GPIO_Init();
ADXL_SELECT_CS = 1;
SPIx_Init();
// 从后备寄存器中读取设置信息值并设置ADXL通信速率
pTemp = (u16*)&g_adxlSetValue; //pTemp指向全局变量地址
*pTemp = BKP_ReadBackupRegister(BKP_R_ADXL_SET);
ADXL345_RD_Reg(DEVICE_ID, &bRet, 1);
if(bRet==0xE5) //读取器件ID
{
ADXL345_Setup(g_adxlSetValue);
ADXL345_WR_Reg(POWER_CTL,0x28); //链接禁止,测量模式
ADXL345_WR_Reg(INT_ENABLE, 0x00); //使能DATA_READY中断
ADXL345_WR_Reg(OFSX,0x00);
ADXL345_WR_Reg(OFSY,0x00);
ADXL345_WR_Reg(OFSZ,0x00);
bRet = 0;
}
else
bRet = 1;
return bRet;
}
初始化时,写入的值为0x0a2b,其含义如下:
typedef struct _ADXL_VALUE_STRUCT_
{
u8 data_format; // 分辨率及量程
u8 power_speed; // 功耗模式及传输速率
} ADXL_VALUE_STRUCT;
其中0X31为DATA_FORMAT,写入值为0X0A
D3=1:全分辨率;
D1D0=10:对应范围±8g
转换速率的设置:
对应地址:0X2C
一段时间没有活动后,ADXL345可以进入自动休眠模式。要使能这个功能,需要设置THRESH_INACT寄存器和TIME_INACT
寄存器。这两个寄存器分别设定一个合适的阈值,例如,都设置为0X03; 然后设置POWER_CTL寄存器的AUTO_SLEEP
和Link位。这样一段时间活动值小于阈值后,就会进入自动休眠模式
如果把POWER_CTL的Measure位设置为0,则ADXL345进入standby模式,此模式下,不会检测加速度信息,同时功耗最低(0.1uA)
https://wenku.baidu.com/view/3a500cfc910ef12d2af9e7d0.html
https://wenku.baidu.com/view/fb1256d3b9f3f90f76c61b1d.html