OneNET麒麟座应用开发之五:获取加速度传感器ADXL345数据
由于数据采集站基本都安装在野外或者楼顶,安装位置以及震动对检测数据的准确性有一定影响。所以想要有一个位置状态数据,正好发现麒麟作上有ADXL345,这样一个数字输出的加速度传感器。如图中红框所示:
1、ADXL345概述
ADXL345是ADI公司推出的基于iMEMS技术的3轴、数字输出加速度传感器。该加速度传感器的特点如下:
- 分辨率高。最高13位分辨率。
- 量程可变。具有+/-2g,+/-4g,+/-8g,+/-16g可变的测量范围。
- 灵敏度高。最高达3.9mg/LSB,能测量不到1.0°的倾斜角度变化。
- 功耗低。 40~145uA 的超低功耗,待机模式只有 0.1uA。
- 尺寸小。整个 IC 尺寸只有 3mm*5mm*1mm, LGA 封装。
ADXL支持标准的I2C或SPI数字接口,自带32级FIFO存储,并且内部有多种运动状态检测和灵活的中断方式等特性。ADXL345传感器的检测轴如下图所示:
当 ADXL345 沿检测轴正向加速时,它对正加速度进行检测。在检测重力时用户需要注意,当检测轴的方向与重力的方向相反时检测到的是正加速度。下图所示为输出响应与相对于重力的方向的关系:
2、硬件设计
ADXL345支持SPI 和I2C两种通信方式,麒麟座采用的I2C通讯方式,具体连接如下图所示:
对于任何不使用的引脚,没有内部上拉或下拉电阻,因此,CS引脚或ALT ADDRESS引脚悬空或不连接时,任何已知状态或默认状态不存在。使用I2C时, CS引脚必须连接至VDD I/O, ALT ADDRESS引脚必须连接至任一VDD I/O或接地。
ALT ADDRESS引脚处于高电平,器件的7位I2C地址是0x1D,随后为R / W位。这转化为0x3A写入,0x3B读取。通过ALT ADDRESS引脚(引脚12)接地,可以选择备用I2C地址0x53(随后为R / W位)。这转化为0xA6写入, 0xA7读取。
由于在麒麟座中,ALT ADDRESS引脚(引脚12)接地,所以地址为0x53,也就是0xA6写入, 0xA7读取。此外2个终端信号也引入了MCU。
3、软件设计
接下来我们开始软件方面的设计。因为麒麟座中ADXL345使用I2C接口与MCU通讯,并连接到了I2C2(PB10、PB11)端口,接下来我们将逐步实现这部分软件。
(1)、I2C读写操作
因为ADXL345加速度传感器使用了标准的I2C通讯接口,所以我们来看一看I2C读写单个或多个子解释的报文格式(如下图)。
根据以上报文图示,并结合STM32F1的库函数我们封装读写字节操作的函数。封装发送命令和获取数据函数:
/*向ADXL345下发指令,指令格式均为1个字节*/ void WriteByteToADXL345(I2C_TypeDef* I2Cx,uint8_t deviceAddress,uint8_t command) { uint16_t i2cTimeout=TimeoutPeriod; /*产生I2C起始信号*/ I2C_GenerateSTART(I2C2, ENABLE); /*检测 EV5 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) { if ((i2cTimeout--) == 0) return ; } /*发送ADXL345的地址*/ I2C_Send7bitAddress(I2Cx,deviceAddress,I2C_Direction_Transmitter); i2cTimeout=TimeoutPeriod; /*检测 EV6 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if ((i2cTimeout--) == 0) return ; } /*下发操作命令*/ I2C_SendData(I2Cx, command); i2cTimeout=TimeoutPeriod; /*检测 EV8 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if ((i2cTimeout--) == 0) return ; } /* 产生I2C停止信号 */ I2C_GenerateSTOP(I2Cx, ENABLE); } /*从ADXL345读取多个字节数据的值*/ void ReadBytesFromADXL345(I2C_TypeDef* I2Cx,uint8_t deviceAddress,uint8_t *pData,uint16_t bytesNum) { uint16_t i2cTimeout=TimeoutPeriod; /*如果I2C总线忙,则等待一段时间*/ while (I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)) { if ((i2cTimeout--) == 0) return ; } /* 产生 I2C 起始信号 */ I2C_GenerateSTART(I2Cx, ENABLE); /*检测 EV5 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)) { if ((i2cTimeout--) == 0) return ; } /*发送ADXL345的地址*/ I2C_Send7bitAddress(I2Cx,deviceAddress,I2C_Direction_Receiver); i2cTimeout=TimeoutPeriod; /*检测 EV6 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if ((i2cTimeout--) == 0) return ; } while(bytesNum) { if(bytesNum==1) { I2C_AcknowledgeConfig(I2Cx, DISABLE);//关闭应答 I2C_GenerateSTOP(I2Cx, ENABLE);//停止信号 } i2cTimeout=TimeoutPeriod; /*检测 EV7 事件并清除标志*/ while (!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)) { if ((i2cTimeout--) == 0) return ; } /*通过 I2C,从设备中读取一个字节的数据 */ *pData=I2C_ReceiveData(I2Cx); pData++; bytesNum--; } /*使能应答,方便下一次 I2C 传输*/ I2C_AcknowledgeConfig(I2Cx, ENABLE); }
(2)ADXL寄存器及操作
ADXL345传感器拥有29个用户可见的寄存器,如下图所示。但是我们不对其进行具体的介绍,在数据手册上有非常详细的说明。这些寄存器我们并不会全部使用,事实上通常我们只是用配置寄存器和部分数据寄存器。
在使用ADXL345读取我们想要的数居前,我们先使用相关寄存器对传感器进行必要的配置,以期获取我们想要得到的值。如下初始化配置过程:
void ADXL345_Init_Configuration(void) { uint8_t devid = 0, val = 0; Delayus (300); ReadByteFromADXL345 (ADXL345_ADDRESS, 0x00, &devid); //获取设备ID Delayus (300); val = 0x2B; WriteByteToADXL345 (ADXL345_ADDRESS, DATA_FORMAT_REG, &val); //数据格式控制 Delayus(50); val = 0x0A; WriteByteToADXL345 (ADXL345_ADDRESS, BW_RATE, &val); //数据速率及功率模式控制 Delayus(50); val = 0x28; WriteByteToADXL345 (ADXL345_ADDRESS, POWER_CTL, &val); //上电特性控制 Delayus(50); val = 0; WriteByteToADXL345 (ADXL345_ADDRESS, INT_ENABLE, &val); //中断使能控制 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSX, &val); //X轴偏移 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSY, &val); //Y轴偏移 Delayus(50); WriteByteToADXL345 (ADXL345_ADDRESS, OFSZ, &val); //Z轴偏移 Delayus(500); }
(3)ADXL345数据获取
接下来我们就可以读取数据了,获取和解析数据前我们先来了解一下数据的格式。分别在寄存器0x2C中进行速率控制,在寄存器0x31中进行数据格式控制。ADXL345的输出数据可以采用右对齐和左对齐两种方式。
数据右对齐方式:
数据左对齐方式:
此外,ADXL345通过VS = 2.5 V电源电压的测试且以其为额定电压;然而, VS可高至3.6 V或低至2.0 V。在麒麟座中解释使用的3.3VDC供源,偏移、灵敏度、噪声、自测和电源电流等参数随着电源电压变化而变化。随着电源电压的改变,静电力也发生细微变化,因此,偏移和灵敏度也有细微变化。在电源电压VS = 3.3 V下运行时, x轴和y轴偏移通常比VS = 2.5 V运行时高25 mg。在电源电压3.3 V下运行与在VS = 2.5V时比较, z轴偏移一般低20 mg。VS = 2.5V时, x轴和y轴的灵敏度为标称256 LSB/g(全分辨率或±2 g, 10位运行),而电源电压为3.3 V时,其灵敏度转换为265 LSB/g。 z轴灵敏度不受电源电压影响, 2.5 V时或3.3 V时都相同。可以用简单的线性插值来确定其他电源电压下的偏移和灵敏度的典型转换。
搞清楚了以上问题就可以读取正确的数据了:
void GetValue FromADXL345 (void) { unsigned char devid = 0; unsigned char dataTemp[6]; Delayus(200); ReadByteFromADXL345(ADXL345_ADDRESS, 0x00, &devid); //读取设备ID Delayus(200); ReadBytes FromADXL345 (ADXL345_ADDRESS, 0x32, dataTemp, 6); //获取原始数据(4mg/LSB) adxlInfo.incidence_X = (short)(dataTemp[0] + ((unsigned short)dataTemp[1] << 8)); adxlInfo.incidence_Y = (short)(dataTemp[2] + ((unsigned short)dataTemp[3] << 8)); adxlInfo.incidence_Z = (short)(dataTemp[4] + ((unsigned short)dataTemp[5] << 8)); adxlInfo.incidence_Xf = (float)adxlInfo.incidence_X * 0.0039; //换算为物理量值 adxlInfo.incidence_Yf = (float)adxlInfo.incidence_Y * 0.0039; //换算为物理量值 adxlInfo.incidence_Zf = (float)adxlInfo.incidence_Z * 0.0039; //换算为物理量值 }
至此软件编写完成。
4、结果验证
完成编写后,编译无误。我们查看最终的运行结果如何。首先我们登录到OneNET,并组态好设备和应用,然后下载程序重新上电,就可以看到Xg、Yg、Zg的数据以及上次我们读取的温湿度数据:
我们再看一下显示屏中显示的数据是什么样的。因显示精度不同有些差异,但基本是一致的。
至此,我们完成了ADXL345数据的获取以及上传到OneNET,在后续我们将继续各种OneNET以及麒麟座开发板的研究。OneNET真的是越用越有意思。
如果阅读这篇文章让您略有所得,还请点击下方的【好文要顶】按钮。
当然,如果您想及时了解我的博客更新,不妨点击下方的【关注我】按钮。
如果您希望更方便且及时的阅读相关文章,也可以扫描上方二维码关注我的微信公众号【木南创智】