课题学习(十八)----捷联测试电路设计与代码实现(基于MPU6050和QMC5883L)

本文主要是介绍课题学习(十八)----捷联测试电路设计与代码实现(基于MPU6050和QMC5883L),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、 电路设计

  本周主要工作是在项目上,抽空做了一个跟本课题相关的电路板,之前用开发板来做测试,MPU6050和QMC5883L都是用杜邦线连接的,导致接线很乱,也不美观,当然也不符合“捷联”的定义。
  下面是电路的原理图和PCB,因为已经购买了MPU6050和QMC5883L模块,所以直接使用这些模块来做,不再设计外围电路(不是懒,主要是这个电路板做测试使用,后续做课题看不会用这中比较low的MEMS传感器)。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  本次设计的电路有以下几个特点:

  • 外扩排针,各模块仍然可以正常单独使用
  • 外扩电源,可以使用5V电源直接供电
  • 增加蓝牙模块,并设置为115200波特率,可以通过无线的方式获取传感器的数据
  • 增加ICM20602,可以选择使用MPU6050或者ICM20602
  • 单独设计各模块的封装库(目前无3D模型),并标注三轴的方向已经对应模块的螺孔,可以根据封装库的位置直接增加螺孔
      部分封装库如下所示:
    在这里插入图片描述
      有需要的可以联系我,或者去我的资源中进行下载。

二、 代码

  下面是相关的部分代码:
  ICM20602的代码可参考博客:STM32Cube高效开发教程<基础篇>(十四)----SPI通信及ICM20602软件开发,本博客后续的资源链接中也会包含ICM20602的代码。
  MPU6050.c:

#include "mpu6050.h"
#include "stm32f1xx_hal.h"
uint8_t addressMPU6050;int16_t adcAccel_MPU6050[3]={0},adcGyro_MPU6050[3]={0};  
//初始化MPU6050
//返回值:0,成功
//    其他,错误代码
uint8_t MPU_Init(void)
{ // MPU_IIC_Init();//初始化IIC总线MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);	//复位MPU6050HAL_Delay(100);MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);	//唤醒MPU6050 MPU_Set_Gyro_Fsr(3);					//陀螺仪传感器,±2000dpsMPU_Set_Accel_Fsr(0);					//加速度传感器,±2gMPU_Set_Rate(50);						//设置采样率50HzMPU_Write_Byte(MPU_INT_EN_REG,0X00);	//关闭所有中断MPU_Write_Byte(MPU_USER_CTRL_REG,0X00);	//I2C主模式关闭MPU_Write_Byte(MPU_FIFO_EN_REG,0X00);	//关闭FIFOMPU_Write_Byte(MPU_INTBP_CFG_REG,0X80);	//INT引脚低电平有效addressMPU6050=MPU_Read_Byte(MPU_DEVICE_ID_REG); if(addressMPU6050==MPU_ADDR)//器件ID正确{MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);	//设置CLKSEL,PLL X轴为参考MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);	//加速度与陀螺仪都工作MPU_Set_Rate(50);						//设置采样率为50Hz}else return 1;
//		MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01);	//设置CLKSEL,PLL X轴为参考
//		MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00);	//加速度与陀螺仪都工作
//		MPU_Set_Rate(50);						//设置采样率为50HzMPU_IIC_SCL_HIG;MPU_IIC_SDA_HIG;return 0;
}
//设置MPU6050陀螺仪传感器满量程范围
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,设置成功
//    其他,设置失败 
uint8_t MPU_Set_Gyro_Fsr(uint8_t fsr)
{return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围  
}
//设置MPU6050加速度传感器满量程范围
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,设置成功
//    其他,设置失败 
uint8_t MPU_Set_Accel_Fsr(uint8_t fsr)
{return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围  
}
//设置MPU6050的数字低通滤波器
//lpf:数字低通滤波频率(Hz)
//返回值:0,设置成功
//    其他,设置失败 
uint8_t MPU_Set_LPF(uint16_t lpf)
{uint8_t data=0;if(lpf>=188)data=1;else if(lpf>=98)data=2;else if(lpf>=42)data=3;else if(lpf>=20)data=4;else if(lpf>=10)data=5;else data=6; return MPU_Write_Byte(MPU_CFG_REG,data);//设置数字低通滤波器  
}
//设置MPU6050的采样率(假定Fs=1KHz)
//rate:4~1000(Hz)
//返回值:0,设置成功
//    其他,设置失败 
uint8_t MPU_Set_Rate(uint16_t rate)
{uint8_t data;if(rate>1000)rate=1000;if(rate<4)rate=4;data=1000/rate-1;data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data);	//设置数字低通滤波器return MPU_Set_LPF(rate/2);	//自动设置LPF为采样率的一半
}//得到温度值
//返回值:温度值(扩大了100倍)
short MPU_Get_Temperature(void)
{uint8_t buf[2]; short raw;float temp;MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf); raw=((uint16_t)buf[0]<<8)|buf[1];  temp=36.53+((double)raw)/340;  return temp*100;;
}
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
//    其他,错误代码
uint8_t MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{uint8_t buf[6],res;  res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);if(res==0){*gx=((uint16_t)buf[0]<<8)|buf[1];  *gy=((uint16_t)buf[2]<<8)|buf[3];  *gz=((uint16_t)buf[4]<<8)|buf[5];adcGyro_MPU6050[0]=(buf[0]<<8)|buf[1];  adcGyro_MPU6050[1]=(buf[2]<<8)|buf[3];  adcGyro_MPU6050[2]=(buf[4]<<8)|buf[5];} 	return res;;
}
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
//    其他,错误代码
uint8_t MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{uint8_t buf[6],res;  res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);if(res==0){*ax=((uint16_t)buf[0]<<8)|buf[1];  *ay=((uint16_t)buf[2]<<8)|buf[3];  *az=((uint16_t)buf[4]<<8)|buf[5];adcAccel_MPU6050[0]=(buf[0]<<8)|buf[1];  adcAccel_MPU6050[1]=(buf[2]<<8)|buf[3];  adcAccel_MPU6050[2]=(buf[4]<<8)|buf[5];} 	return res;
}

  QMC5883L.c

// 模拟IIC发送单字节时序
uint8_t QMC5883L_SendByte(uint8_t Sim_i2c_data)
{uint8_t i;SDA1_OUT();QMC5883L_SCL_LOW;for(i=0; i<8; i++){if(Sim_i2c_data&0x80) QMC5883L_SDA_HIG;else QMC5883L_SDA_LOW;Sim_i2c_data<<=1;QMC5883L_NOP;QMC5883L_SCL_HIG;QMC5883L_NOP;QMC5883L_SCL_LOW;QMC5883L_NOP;}return QMC5883L_READY;
}// 模拟IIC发送单字节时序
uint8_t QMC5883L_ReceiveByte(void)
{uint8_t i,Sim_i2c_data;SDA1_IN();//QMC5883L_SDA_HIG;
// QMC5883L_SCL_LOW;Sim_i2c_data=0;for(i=0; i<8; i++){QMC5883L_SCL_LOW;QMC5883L_NOP;QMC5883L_SCL_HIG;// QMC5883L_NOP;Sim_i2c_data<<=1;SDA_Pin_State = QMC5883L_SDA_STATE;if(QMC5883L_SDA_STATE)	Sim_i2c_data|=0x01;// QMC5883L_SCL_LOW;QMC5883L_NOP;}QMC5883L_SendNACK();return Sim_i2c_data;
}// 模拟IIC读单字节,带应答
uint8_t QMC5883L_ReceiveByte_WithACK(void)
{uint8_t i,Sim_i2c_data;SDA1_IN();//QMC5883L_SDA_HIG;
// QMC5883L_SCL_LOW;Sim_i2c_data=0;for(i=0; i<8; i++){QMC5883L_SCL_LOW;QMC5883L_NOP;QMC5883L_SCL_HIG;// QMC5883L_NOP;Sim_i2c_data<<=1;if(QMC5883L_SDA_STATE)	Sim_i2c_data|=0x01;// QMC5883L_SCL_LOW;QMC5883L_NOP;}QMC5883L_SendACK();return Sim_i2c_data;
}// 模拟IIC的多字节读
uint8_t QMC5883L_Read8(uint8_t moni_dev_addr, uint8_t moni_reg_addr, uint8_t moni_i2c_len, uint8_t *moni_i2c_data_buf)
{QMC5883L_START();QMC5883L_SendByte(moni_dev_addr << 1 | WRITE );QMC5883L_Wait_Ack();QMC5883L_SendByte(moni_reg_addr);QMC5883L_Wait_Ack();//QMC5883L_STOP();QMC5883L_START();QMC5883L_SendByte(moni_dev_addr <<1 | READ );QMC5883L_Wait_Ack();while (moni_i2c_len){if (moni_i2c_len==1) *moni_i2c_data_buf =QMC5883L_ReceiveByte();else *moni_i2c_data_buf =QMC5883L_ReceiveByte_WithACK();moni_i2c_data_buf++;moni_i2c_len--;}QMC5883L_STOP();return 0x00;
}// 模拟IIC的多字节写
int8_t QMC5883L_Write8(uint8_t moni_dev_addr, uint8_t moni_reg_addr, uint8_t moni_i2c_len, uint8_t *moni_i2c_data_buf)
{uint8_t i;QMC5883L_START();QMC5883L_SendByte(moni_dev_addr << 1 | WRITE);  // 写指令QMC5883L_Wait_Ack();QMC5883L_SendByte(moni_reg_addr);QMC5883L_Wait_Ack();//QMC5883L_START();for (i=0; i<moni_i2c_len; i++){QMC5883L_SendByte(moni_i2c_data_buf[i]);QMC5883L_Wait_Ack();}QMC5883L_STOP();	return 0;
}uint8_t highByte(uint16_t value)
{uint8_t ret;value = value>>8;ret = (uint8_t)value;return ret;
}uint8_t lowByte(uint16_t value)
{uint8_t ret;value = value&0x00ff;ret = (uint8_t)value;return ret;
}// 读一个字节
uint8_t readOneByte(uint8_t in_adr)
{uint8_t retVal = -1;QMC5883L_Read8(QMC5883L_ADDR,in_adr,1,&retVal);QMC5883L_NOP;return retVal;
}// 读两个字节
uint16_t readTwoBytes(uint8_t in_adr_hi, uint8_t in_adr_lo)
{uint16_t retVal = -1;uint8_t low=0,high=0;/* Read Low Byte */low = readOneByte(in_adr_lo);/* Read High Byte */  high = readOneByte(in_adr_hi);//printf("high:%d,low:%d  ",high,low);retVal = high << 8;retVal = retVal | low;//printf("retVal:%d\r\n",retVal);return retVal;
}// 写一个字节
void writeOneByte(uint8_t adr_in, uint8_t dat_in)
{uint8_t dat = dat_in;QMC5883L_Write8(QMC5883L_ADDR,adr_in,1,&dat);
}// 获取地址
int16_t getAddress()
{return QMC5883L_ADDR; 
}//************************写入单字节数据***************************//初始化QMC5883,根据需要请参考pdf进行修改****
void Init_QMC5883()
{uint8_t addrVale;addrVale = 0x1D;QMC5883L_Write8(QMC5883L_ADDR,CONTROL_REG_1,1,&addrVale);  //控制寄存器配置addrVale = 0x01;QMC5883L_Write8(QMC5883L_ADDR,SET_RESET_REG,1,&addrVale);  //设置清除时间寄存器
}void GetMagValue(void)
{QMC5883L_Read8(QMC5883L_ADDR,XOUT_L,6,Buff);MagnetRawAd[0] = ((int16_t)Buff[1] << 8) | Buff[0];MagnetRawAd[1] = ((int16_t)Buff[3] << 8) | Buff[2];MagnetRawAd[2] = ((int16_t)Buff[5] << 8) | Buff[4];
}

  QMC5883L.h

#ifndef __MD_AS5600_H
#define __MD_AS5600_H#include "stm32f1xx_hal.h"  //这个需要根据不同的芯片类型进行变换
#include "main.h"// QMC地址
#define QMC5883L_ADDR               0x0D// 三轴磁力计输出寄存器
#define XOUT_L               0x00
#define XOUT_H               0x01#define YOUT_L               0x02
#define YOUT_H               0x03#define ZOUT_L               0x04
#define ZOUT_H               0x05// 状态寄存器
#define STATUS               0x06// 温度输出寄存器
#define TEMP_L               0x07
#define TEMP_H               0x08// 控制寄存器
#define CONTROL_REG_1               0x09
#define CONTROL_REG_2               0x0A// SET/RESET,推荐写入0x01
#define SET_RESET_REG               0x0B// 芯片ID号
#define CHIP_ID               0x0D#define QMC5883L_SCL_LOW           HAL_GPIO_WritePin(QMC_SCL_GPIO_Port,QMC_SCL_Pin,GPIO_PIN_RESET)
#define QMC5883L_SCL_HIG           HAL_GPIO_WritePin(QMC_SCL_GPIO_Port,QMC_SCL_Pin,GPIO_PIN_SET)
#define QMC5883L_SDA_LOW           HAL_GPIO_WritePin(QMC_SDA_GPIO_Port,QMC_SDA_Pin,GPIO_PIN_RESET)
#define QMC5883L_SDA_HIG           HAL_GPIO_WritePin(QMC_SDA_GPIO_Port,QMC_SDA_Pin,GPIO_PIN_SET)//#define Sim_I2C1_SDA_STATE        (IIC_SDA_GPIO_Port->IDR &= (IIC_SDA_Pin))
#define QMC5883L_SDA_STATE        (QMC_SDA_GPIO_Port->IDR &= (QMC_SDA_Pin))
//#define QMC5883L_SDA_STATE        HAL_GPIO_ReadPin(QMC_SDA_GPIO_Port,QMC_SDA_Pin)#define QMC5883L_DELAY 		QMC5883L_Delay(100000)
#define QMC5883L_NOP		  QMC5883L_Delay(1000)  //25 #define QMC5883L_READY			  0x00
#define QMC5883L_BUS_BUSY		0x01
#define QMC5883L_BUS_ERROR	  0x02#define QMC5883L_NACK	        0x00
#define QMC5883L_ACK				  0x01#define SDA_Pin_Num  1#define WRITE  0x00
#define READ   0x01extern uint16_t rawdata;
extern float degress ;extern int16_t MagnetRawAd[3];  // 磁力计数据void SDA1_IN ( void );
void SDA1_OUT ( void );void QMC5883L_Delay(uint32_t delay);
uint8_t QMC5883L_START(void);
void QMC5883L_STOP(void);
unsigned char QMC5883L_Wait_Ack(void);
void QMC5883L_SendACK(void);
void 	QMC5883L_SendNACK(void);
uint8_t QMC5883L_SendByte(uint8_t Sim_i2c_data);
uint8_t QMC5883L_ReceiveByte(void);
uint8_t QMC5883L_ReceiveByte_WithACK(void);
uint8_t QMC5883L_Read8(uint8_t moni_dev_addr, uint8_t moni_reg_addr, uint8_t moni_i2c_len, uint8_t *moni_i2c_data_buf);
int8_t  QMC5883L_Write8(uint8_t moni_dev_addr, uint8_t moni_reg_addr, uint8_t moni_i2c_len, uint8_t *moni_i2c_data_buf);
uint8_t highByte(uint16_t value);
uint8_t lowByte(uint16_t value);
uint8_t readOneByte(uint8_t in_adr);
uint16_t readTwoBytes(uint8_t in_adr_hi, uint8_t in_adr_lo);
void writeOneByte(uint8_t adr_in, uint8_t dat_in);
int16_t getAddress();void Init_QMC5883();
void GetMagValue(void);#endif 		

  有关QMC5883L芯片会在假期之后更新一篇,讲解IIC通信以及该芯片,后续会补充上链接。
  资源链接:
基于STM32F103C6T6的九轴传感器的工程
STM32F103C6T6模块、MPU6050模块、ICM20602模块等封装库

三、往期回顾

课题学习(一)----静态测量
课题学习(二)----倾角和方位角的动态测量方法(基于磁场的测量系统)
课题学习(三)----倾角和方位角的动态测量方法(基于陀螺仪的测量系统)
课题学习(四)----四元数解法
课题学习(五)----阅读论文《抗差自适应滤波的导向钻具动态姿态测量方法》
课题学习(六)----安装误差校准、实验方法
课题学习(七)----粘滑运动的动态算法
课题学习(八)----卡尔曼滤波动态求解倾角、方位角
课题学习(九)----阅读《导向钻井工具姿态动态测量的自适应滤波方法》论文笔记
课题学习(十)----阅读《基于数据融合的近钻头井眼轨迹参数动态测量方法》论文笔记
课题学习(十一)----阅读《Attitude Determination with Magnetometers and Accelerometers to Use in Satellite》
课题学习(十二)----阅读《Extension of a Two-Step Calibration Methodology to Include Nonorthogonal Sensor Axes》
课题学习(十三)----阅读《Calibration of Strapdown Magnetometers in Magnetic Field Domain》论文笔记
课题学习(十四)----三轴加速度计+三轴陀螺仪传感器-ICM20602
课题学习(十五)----阅读《测斜仪旋转姿态测量信号处理方法》论文
课题学习(十六)----阅读《Continuous Wellbore Surveying While Drilling Utilizing MEMS Gyroscopes Based…》论文
课题学习(十七)----姿态更新的四元数算法总结

这篇关于课题学习(十八)----捷联测试电路设计与代码实现(基于MPU6050和QMC5883L)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/551554

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来