1、背景:
需要监测风机的启动与停止,希望通过测量其振动频率来辅助判定。
2、传感器:
传统的振动传感器如下图,其有两个缺点:一是测量精度低,对于微小的振动不敏感;二是无法测出分频信号。
选择使用LSM6DSL加速度/角速度传感器。传感器所在的板子为X-NUCLEO-IKSO1A2。如下图所示。
LSM6DSL可以测量出三轴方向的加速度和角速度,而我们监测振动只需要加速度信息。
3、建工程:
首先使用的开发底板是ST官方的MB1136。所有相关的资料会在下方给出链接。
习惯使用Stm32cubeMX快速建立工程。根据选用开发板的主芯片型号以及传感器链接的引脚,占用的资源进行配置。最终一键生成工程。(关于如何使用stm32MX,可自行度娘,也可在下方讨论)
值得一提的是:
(1)配置I2C时,需要使用“快速模式”,速度设置为最大(400000Hz),这样可以加快和加速度传感器之间的通信。加速度传感器的输出频率也就可以尽可能提升。实际在测试时发现,在最快的IIC通信速度下,传感器最大可以设置的输出频率为3.33KHz。
(2)配置一个中断输入引脚,接收传感器数据准备好时发来的中断。(也可以自行去读取某状态位)
(3)ST下载口自带的串口,波特率为固定的9600.如果想要更高,需要单独接线。(我就是单独接线,设置为115200)
接下来是去工程里编写代码,相信大家都可以编出属于自己逻辑的代码。我的逻辑思路如下:
(1)初始化设备和传感器
(2)传感器启动,准备好一组加速度数据(3个数据,X,Y,Z),产生引脚中断。
(3)F401RE接收到中断,读取6个字节(3个加速度的值)。(直接读取的是补码的形式,而加速度值有正负,需要我们定义变量时使用int16)
(4)将一组数据(3方向加速度)进行矢量计算,求得其模长ACC。将ACC存入数组直到存满。(考虑到单片机存储空间有限,因而采用模长的数组,1024个double数据)
(5)存满后,不再存储,置为标志位,进行FFT快速傅里叶变换。相关知识参考自:《C》C语言实现FFT算法_杨贵安的博客-CSDN博客_c语言fft 。
该链接里面有C语言代码,可以直接运行。在使用时需要注意输入参数关系:
FFT需要定义多个与模长数组同样大小的数组,比较费空间。
double pr[NUM],pi[NUM],fr[NUM],fi[NUM];
(6)变换得到的结果被存在pr[1024]中,如果需要观察变换得到的结果,可以将这个数组里的double数据 打印到窗口助手中,再用excel观察变换结果。(打印时,单个数据接换行(\n\r),串口助手中打印的一列数据,直接复制到excel中)。得到的数据列表中第一个为直流分量,可以删除。另外因为某种原因,剩余数据构成的图像呈现左右对称的情况,我们在显示时只取一半就好。如下图所示,传感器放在电脑主机上,测量主机风扇的振动。共计1024个采样点。输出频率为833Hz。(提高采样点个数,横坐标会向右移动。提高输出频率,横坐标会向左移动)(同样是1024个采样点,833Hz输出频率对应145,1.66KHz输出频率对应72, 3.33KHz输出频率对应35)。
(7)横坐标与振动频率有着直接的关系。我通过模拟输入数据观察发现,采样点数为1024时,傅里叶变换后得到的峰峰极大值的很坐标与实际的频率一致(略有偏移,可以理解)。
模拟输入pr[1024],包括10Hz,20Hz,30Hz,40Hz,50Hz,60Hz,70Hz,80Hz,90Hz。(参考频率和角速度之间的关系)
for(i=0;i<1024;i++) {
pr[i]=cos(2*PI*0.001*i*10)+cos(2*PI*0.001*i*20)+cos(2*PI*0.001*i*30)+cos(2*PI*0.001*i*40)+cos(2*PI*0.001*i*50)+cos(2*PI*0.001*i*60)+cos(2*PI*0.001*i*70)+cos(2*PI*0.001*i*80)+cos(2*PI*0.001*i*90);
}
下图只使用了其中的第1~128个点做图(采样点数还是1024)
4、编写上位机
上述显示的过程有点繁琐,因而考虑编写一个上位机用于实时调试。
下位机设备在进行转换之后,直接通过串口,将数据发送到电脑。(每个double数字都先被转换为字符串,以字符串的形式发送。每个数据发送之后,追加发送一个换行符\n,发送完所有数据之后,发送一个'#')
上位机编写,使用的是Qt和C++。具体的实现和源代码感兴趣的童鞋可以问我要一下。
我最方案是,1024个采样点数组,传感器3.33KHz输出频率。电脑主机的频率被测为35Hz(2100转每分钟)。
纯属个人经验总结,如有错误,欢迎讨论!
12月17日 补充分享LSM6DSL 的初始化函数以及数据读取的函数
LSM6DSL pdf, LSM6DSL description, LSM6DSL datasheets, LSM6DSL view ::: ALLDATASHEET :::
#include "main.h"#define FUNC_CFG_ACCESS 0x01 //嵌入式功能配置寄存器#define SENSOR_SYNC_TIME_FRAME 0x04 //传感器同步配置寄存器#define SENSOR_SYNC_RES_RATIO 0x05 //传感器同步配置寄存器#define FIFO_CTRL1 0x06 //FIFO 配置寄存器#define FIFO_CTRL2 0x07 //FIFO 配置寄存器#define FIFO_CTRL3 0x08 //FIFO 配置寄存器#define FIFO_CTRL4 0x09 //FIFO 配置寄存器#define FIFO_CTRL5 0x0a //FIFO 配置寄存器#define DRDY_PULSE_CFG_G 0x0b //#define INT1_CTRL0x0d //#define INT2_CTRL0x0e //#define WHO_AM_I 0x0f //芯片ID#define CTRL1_XL 0x10 //加速度计和陀螺仪控制寄存器#define CTRL2_G 0x11 //#define CTRL3_C 0x12 //#define CTRL4_C 0x13 //#define CTRL5_C 0x14 //#define CTRL6_C 0x15 //#define CTRL7_G 0x16 //#define CTRL8_XL 0x17 //#define CTRL9_XL 0x18 //#define CTRL10_C 0x19 //#define MASTER_CONFIG0x1a //I2C主配置寄存器#define WAKE_UP_SRC 0x1b //中断寄存器#define TAP_SRC 0x1c //中断寄存器#define D6D_SRC 0x1d //中断寄存器#define STATUS_REG 0x1e //用户接口的状态数据寄存器#define OUT_TEMP_L 0x20 //温度输出数据寄存器#define OUT_TEMP_H 0x21 //温度输出数据寄存器#define OUTX_L_G 0x22 //用户界面的陀螺仪输出寄存器#define OUTX_H_G 0x23 //用户界面的陀螺仪输出寄存器#define OUTY_L_G 0x24 //用户界面的陀螺仪输出寄存器#define OUTY_H_G 0x25 //用户界面的陀螺仪输出寄存器#define OUTZ_L_G 0x26 //用户界面的陀螺仪输出寄存器#define OUTZ_H_G 0x27 //用户界面的陀螺仪输出寄存器#define OUTX_L_XL 0x28 //加速度计输出寄存器#define OUTX_H_XL 0x29 //加速度计输出寄存器#define OUTY_L_XL 0x2a //加速度计输出寄存器#define OUTY_H_XL 0x2b //加速度计输出寄存器#define OUTZ_L_XL 0x2c //加速度计输出寄存器#define OUTZ_H_XL 0x2d //加速度计输出寄存器#define SENSORHUB1_REG0x2e //传感器集线器输出寄存#define SENSORHUB2_REG0x2f //传感器集线器输出寄存#define SENSORHUB3_REG0x30 //传感器集线器输出寄存#define SENSORHUB4_REG0x31 //传感器集线器输出寄存#define SENSORHUB5_REG0x32 //传感器集线器输出寄存#define SENSORHUB6_REG0x33 //传感器集线器输出寄存#define SENSORHUB7_REG0x34 //传感器集线器输出寄存#define SENSORHUB8_REG0x35 //传感器集线器输出寄存#define SENSORHUB9_REG0x36 //传感器集线器输出寄存#define SENSORHUB10_REG0x37 //传感器集线器输出寄存#define SENSORHUB11_REG0x38 //传感器集线器输出寄存#define SENSORHUB12_REG0x39 //传感器集线器输出寄存#define FIFO_STATUS1 0x3a //FIFO状态寄存器#define FIFO_STATUS2 0x3b //#define FIFO_STATUS3 0x3c //#define FIFO_STATUS4 0x3d //#define FIFO_DATA_OUT_L 0x3e //FIFO数据输出寄存器#define FIFO_DATA_OUT_H 0x3f //#define TIMESTAMP0_REG0x40 //时间戳输出寄存器#define TIMESTAMP1_REG0x41 //#define TIMESTAMP2_REG0x42 ///*reserved*/#define STEP_TIMESTAMP_L 0x49 //#define STEP_TIMESTAMP_H 0x4A //#define STEP_COUNTER_L0x4b //#define STEP_COUNTER_H0x4c //#define SENSORHUB13_REG 0x4d //传感器集线器输出寄存#define SENSORHUB14_REG 0x4e //传感器集线器输出寄存#define SENSORHUB15_REG 0x4f //传感器集线器输出寄存#define SENSORHUB16_REG 0x50 //传感器集线器输出寄存#define SENSORHUB17_REG 0x51 //传感器集线器输出寄存#define SENSORHUB18_REG 0x52 //传感器集线器输出寄存#define FUNC_SRC10x53 //中断寄存器#define FUNC_SRC20x54 //#define WRIST_TILT_IA0x55 //中断寄存器/*reserved*/#define TAP_CFG 0x58 //中断寄存器#define TAP_THS_6D 0x59 //#define INT_DUR2 0x5a //#define WAKE_UP_THS 0x5b //#define WAKE_UP_DUR 0x5c //#define FREE_FALL0x5d //#define MD1_CFG 0x5e //#define MD2_CFG 0x5f //#define MASTER_CMD_CODE 0x60 //----------#define SENS_SYNC_SPI_ERROR_CODE0x61 ///*reserved*/#define OUT_MAG_RAW_X_L 0x66 //#define OUT_MAG_RAW_X_H 0x67 //#define OUT_MAG_RAW_Y_L 0x68 //#define OUT_MAG_RAW_Y_H 0x69 //#define OUT_MAG_RAW_Z_L 0x6a //#define OUT_MAG_RAW_Z_H 0x6b ///*reserved*/#define X_OFS_USR0x73 //加速度用户偏移矫正#define Y_OFS_USR0x74 //#define Z_OFS_USR0x75 ///* 配置 */uint8_t LSM6DSL_Config[]={CTRL3_C , 0x81,//软件重启//FUNC_CFG_ACCESS, 0x00,//SENSOR_SYNC_TIME_FRAME , 0x00,//SENSOR_SYNC_RES_RATIO , 0x00,//FIFO_CTRL1, 0x00,//FIFO_CTRL2, 0x00,//FIFO_CTRL3, 0x00,//FIFO_CTRL4, 0x00,//FIFO_CTRL5, 0x00,DRDY_PULSE_CFG_G , 0x80,//脉冲触发(不锁存中断引脚)INT1_CTRL , 0x01,//INT1引脚上各种中断事件的使能位//加速度中断// INT2_CTRL , 0x02,//INT2引脚上各种中断事件的使能位//角速度中断CTRL1_XL , 0x94,//加速度输出频率,量程,带宽//[0x60]416Hz 16g// CTRL2_G, 0x64,//角速度输出速率,量程,满量程125,0//416hZ 500DPS// CTRL4_C , 0x13,//// CTRL5_C , 0x14,//// CTRL6_C , 0x15,//// CTRL7_G , 0x16,//// CTRL8_XL , 0x17,//// CTRL9_XL , 0x18,//// CTRL10_C , 0x19,//// MASTER_CONFIG , 0x1a,//I2C主配置寄存器// TAP_CFG , 0x58,//中断寄存器// TAP_THS_6D, 0x59,//// INT_DUR2 , 0x5a,//// WAKE_UP_THS , 0x5b,//// WAKE_UP_DUR , 0x5c,//// FREE_FALL, 0x5d,//// MD1_CFG , 0x5e,//// MD2_CFG , 0x5f,//// MASTER_CMD_CODE, 0x60,//----------// SENS_SYNC_SPI_ERROR_CODE , 0x61,//// X_OFS_USR, 0x73,//加速度用户偏移矫正// Y_OFS_USR, 0x74,//// Z_OFS_USR, 0x75,//};/* 初始化 */int LSM6DSL_init(){uint8_t i=0,ID=0;//---------------------------------------I2C_ReadData(&ID,1,WHO_AM_I);if(ID==0x6a) {//printf("product ID: 0x%02X\n\r",ID);} else {printf("[error] ID Wrong! %02X\n\r",ID);return -1;}//---------------------------------------for(i=0;i<sizeof(LSM6DSL_Config)/sizeof(uint8_t)/2;i++) {//printf("R=0x%02X\tV=0x%02X\n\r",LSM6DSL_Config[i*2],LSM6DSL_Config[i*2+1]);if(I2C_WriteData(LSM6DSL_Config+i*2+1,1,LSM6DSL_Config[i*2])!=0) {printf("[error] I2C_WriteData Wrong!\n\r");return -2;}}//---------------------------------------printf("[success] LSM6DSL_init()\n\r");return 0;}
以下是硬件IIC 二次封装的函数,建议放在i2c.c文件中。
/* USER CODE BEGIN 1 */#include <stdio.h>//IIC#define LSM6DSL_DEV_Adress 0xd6#define IIS2DLPC_DEV_Adress 0x30#define LIS2DH12_DEV_Adress 0x30int I2C_WriteData(uint8_t* pData,uint16_t Size,uint16_t MeAddress){HAL_StatusTypeDef ret =HAL_ERROR;uint16_t DevAddress=0;//设备地址uint16_t MemAddsize=I2C_MEMADD_SIZE_8BIT;uint32_t Timeout=1000;I2C_HandleTypeDef* hi2cX=NULL;hi2cX=&hi2c1;DevAddress=LIS2DH12_DEV_Adress;//IIC发送ret=HAL_I2C_Mem_Write(hi2cX,DevAddress,MeAddress,MemAddsize,pData,Size,Timeout);return ret;}int I2C_ReadData(uint8_t* pData,uint16_t Size,uint16_t MeAddress){uint16_t DevAddress=0;//设备地址//uint16_t MeAddress=0;uint16_t MemAddsize=0;uint32_t Timeout=1000;I2C_HandleTypeDef* hi2cX=NULL;hi2cX=&hi2c1;DevAddress=LIS2DH12_DEV_Adress;MemAddsize=I2C_MEMADD_SIZE_8BIT;//IIC读取if(HAL_OK == HAL_I2C_Mem_Read(hi2cX,DevAddress,MeAddress,MemAddsize,pData,Size,Timeout)) {return 0;}return -1;}/* USER CODE END 1 */
以下函数 用于计算 加速度模长。
uint16_t Calculate_AccXYZ(int16_t X,int16_t Y,int16_t Z){uint32_t temp = X*X+Y*Y+Z*Z;return (uint16_t)sqrt(temp);}
以下函数为读取加速度值,其中ZZZ表示Z轴方向上的数值。
uint8_t Acc_Value[6];int16_t ZZZ=0;I2C_ReadData(Acc_Value,6,0x28);// 0x28 //加速度计输出寄存器ZZZ =Acc_Value[4] + (Acc_Value[5]<<8);
希望能够对大家有所帮助。