基于MSP432P401R的MPU6050陀螺仪串口输出姿态角程序
目录
基于MSP432P401R的MPU6050陀螺仪串口输出姿态角程序
前言
最近做电赛申请到了MSP432P401R板子,队友让我开发开发陀螺仪,期间遇到了一些问题,不过都解决了,所以写一篇总结记录一下ouo。不想看代码的同学直接看总结工程链接。
一、实验器材
MSP432P401R MPU6050陀螺仪
二、硬件资源
1.usb转ttl
2.串口1(波特率:9600,P2.2/P2.3)
3.MPU6050陀螺仪控制信号线
SDA <-------------> P6.4
SCL <-------------> P6.5
VCC <-------------> 3.3V
GND <-------------> GND
三.软件架构
keil5 CCS
四.代码
//main.c 主要配置
#include "main.h"
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "systeminit.h"
#include "motor.h"
#include "mpu6050.h"
#include "inv_mpu.h"
#include "inv_mpu_dmp_motion_driver.h"
#include "string.h"
#include "driverlib.h"
#include "delay.h"
#include "usart.h"
#include "uart_calc.h"
#include "motor.h"
//陀螺仪方向设置
static signed char gyro_orientation[9] = { 1, 0, 0,
0, 1, 0,
0, 0, 1};
void Wait_Connection(void)
{
unsigned char str[8];
sprintf((char*)str,"sw0.val=1\xff\xff\xff");
while(keyvalue!=5)
{
GPIO_toggleOutputOnPin(GPIO_PORT_P2,GPIO_PIN1);
USART1PutString(str);
delay_ms(300);
}
GPIO_setOutputLowOnPin(GPIO_PORT_P2,GPIO_PIN1);
}
u8 mpu_dmp_init1(void)
{
u8 res=0;
IIC_Init(); //初始化IIC总线
if(1) //初始化MPU6050
{
res=mpu_set_sensors(INV_XYZ_GYRO|INV_XYZ_ACCEL);//设置所需要的传感器
if(res)return 1;
else
{
sprintf((char*)str,"1\r\n");
USART1PutString(str);
delay_ms(100);
}
res=mpu_configure_fifo(INV_XYZ_GYRO | INV_XYZ_ACCEL);//设置FIFO
if(res)return 2;
else
{
sprintf((char*)str,"2\r\n");
USART1PutString(str);
delay_ms(100);
}
res=mpu_set_sample_rate(DEFAULT_MPU_HZ); //设置采样率
if(res)return 3;
else
{
sprintf((char*)str,"3\r\n");
USART1PutString(str);
delay_ms(100);
}
res=dmp_load_motion_driver_firmware(); //加载dmp固件
if(res)return 4;
else
{
sprintf((char*)str,"4\r\n");
USART1PutString(str);
delay_ms(100);
}
res=dmp_set_orientation(inv_orientation_matrix_to_scalar(gyro_orientation));//设置陀螺仪方向
if(res)return 5;
else
{
sprintf((char*)str,"5\r\n");
USART1PutString(str);
delay_ms(100);
}
res=dmp_enable_feature(DMP_FEATURE_6X_LP_QUAT|DMP_FEATURE_TAP| //设置dmp功能
DMP_FEATURE_ANDROID_ORIENT|DMP_FEATURE_SEND_RAW_ACCEL|DMP_FEATURE_SEND_CAL_GYRO|
DMP_FEATURE_GYRO_CAL);
if(res)return 6;
else
{
sprintf((char*)str,"6\r\n");
USART1PutString(str);
delay_ms(100);
}
res=dmp_set_fifo_rate(DEFAULT_MPU_HZ); //设置DMP输出速率(最大不超过200Hz)
if(res)return 7;
else
{
sprintf((char*)str,"7\r\n");
USART1PutString(str);
delay_ms(100);
}
res=run_self_test(); //自检
if(res)return 8;
else
{
sprintf((char*)str,"8\r\n");
USART1PutString(str);
delay_ms(100);
}
res=mpu_set_dmp_state(1); //使能DMP
if(res)return 9;
else
{
sprintf((char*)str,"9\r\n");
USART1PutString(str);
delay_ms(100);
}
}
}
int main(void)
{
u8 report=0;
float pitch,roll,yaw; //欧拉角
SystemClockInit(HFXT);//系统时钟初始化(系统主时钟)
SystemClockInit(DCO);//系统时钟初始化(子系统时钟)
Usart1tInit();
MPU_Init();
mpu_dmp_init();
delay_ms(100);
while(mpu_dmp_get_data(&pitch,&roll,&yaw))
{
sprintf((char*)str,"roll=%f,pitch=%f,yaw=%f\r\n",(roll),(pitch),(yaw));
USART1PutString(str);
delay_ms(100);
}
while(1)
{
mpu_dmp_get_data(&pitch,&roll,&yaw);
sprintf((char*)str,"roll=%f,pitch=%f,yaw=%f\r\n",(roll),(pitch),(yaw));
USART1PutString(str);
delay_ms(100);
}
}
//myiic.h主要配置
#ifndef __MYIIC_H
#define __MYIIC_H
#include "main.h"
#include "stdio.h"
//IO方向设置
#define SDA_IN() GPIO_setAsInputPin(GPIO_PORT_P6,GPIO_PIN4) //SDA输入模式
#define SDA_OUT() GPIO_setAsOutputPin(GPIO_PORT_P6,GPIO_PIN4) //SDA输出模式
//IO操作函数
#define IIC_SCL_High() GPIO_setOutputHighOnPin(GPIO_PORT_P6,GPIO_PIN5) //SCL_High
#define IIC_SCL_Low() GPIO_setOutputLowOnPin(GPIO_PORT_P6,GPIO_PIN5) //SCL_Low
#define IIC_SDA_High() GPIO_setOutputHighOnPin(GPIO_PORT_P6,GPIO_PIN4) //SDA_High
#define IIC_SDA_Low() GPIO_setOutputLowOnPin(GPIO_PORT_P6,GPIO_PIN4) //SDA_Low
#define READ_SDA GPIO_getInputPinValue(GPIO_PORT_P6,GPIO_PIN4) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
//myiic.c主要配置
#include "myiic.h"
#include "delay.h"
#include "oled.h"
#include "usart.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//IIC 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/6
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//初始化IIC
void IIC_Init(void)
{
GPIO_setAsOutputPin(SCL_PORT,SCL_PIN ); //CLK
GPIO_setAsOutputPin(SDA_PORT,SDA_PIN);//DIN
IIC_SCL_High();
IIC_SDA_High();
}
//产生IIC起始信号
void IIC_Start(void)//SDA 10 SCL 010
{
SDA_OUT(); //sda线输出
IIC_SCL_High();
IIC_SDA_High();
delay_us(4);
IIC_SDA_Low();//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL_Low();//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)//SDA 01 SCL 01
{
SDA_OUT();//sda线输出
IIC_SCL_Low();//STOP:when CLK is high DATA change form low to high
IIC_SDA_Low();
delay_us(4);
IIC_SCL_High();
IIC_SDA_High();//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)//
{
u8 cy;
SDA_IN(); //SDA设置为输入
IIC_SCL_High();delay_us(10);
IIC_SDA_High();delay_us(10);
if(READ_SDA)
{
cy=1;
IIC_SCL_Low();
return cy;
}
else
{
cy=0;
}
IIC_SCL_Low();//时钟输出0
return cy;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL_Low();
SDA_OUT();
IIC_SDA_Low();
delay_us(2);
IIC_SCL_High();
delay_us(2);
IIC_SCL_Low();
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL_Low();
SDA_OUT();
IIC_SDA_High();
delay_us(2);
IIC_SCL_High();
delay_us(2);
IIC_SCL_Low();
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL_Low();//拉低时钟开始数据传输
delay_us(2);
for(t=0;t<8;t++)
{
if(txd&0x80)
{
IIC_SDA_High();delay_us(2);
}
else
{
IIC_SDA_Low();delay_us(2);
}
txd<<=1;
IIC_SCL_High();
delay_us(4);
IIC_SCL_Low();
delay_us(2);
}
delay_us(2);
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL_Low();
delay_us(2);
IIC_SCL_High();
receive<<=1;
if(READ_SDA)
receive++;
delay_us(2);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
//mpu6050.c主要配置
#include "mpu6050.h"
#include "main.h"
#include "delay.h"
#include "usart.h"
#include "string.h"
#include "oled.h"
#include "stdio.h"
//初始化MPU6050
//返回值:0,成功
// 其他,错误代码
u8 MPU_Init(void)
{
u8 res;
IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00);//唤醒MPU6050
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80);//初始化MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_SAMPLE_RATE_REG,0x07);
MPU_Write_Byte(MPU_CFG_REG,0x06);
MPU_Write_Byte(MPU_GYRO_CFG_REG,0x18);
MPU_Write_Byte(MPU_ACCEL_CFG_REG,0x01);
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(0X75);
sprintf((char*)str,"res=%d\r\n",res);
USART1PutString(str);
if(res==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;
return 0;
}
//设置MPU6050陀螺仪传感器满量程范围
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围
}
//设置MPU6050加速度传感器满量程范围
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围
}
//设置MPU6050的数字低通滤波器
//lpf:数字低通滤波频率(Hz)
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_LPF(u16 lpf)
{
u8 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,设置成功
// 其他,设置失败
u8 MPU_Set_Rate(u16 rate)
{
u8 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)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp*100;;
}
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
IIC_Start();
IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
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;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
IIC_Start();
IIC_Send_Byte(MPU_WRITE);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Start();
IIC_Send_Byte(MPU_READ);//发送器件地址+读命令
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;
}
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
// 其他,错误代码
u8 MPU_Write_Byte(u8 reg,u8 data)
{
IIC_Start();
IIC_Send_Byte(MPU_WRITE);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
sprintf((char*)str,"error\r\n");
USART1PutString(str);
delay_ms(100);
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Send_Byte(data);//发送数据
if(IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
IIC_Stop();
return 0;
}
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
u8 MPU_Read_Byte(u8 reg)
{
u8 res;
IIC_Start();
IIC_Send_Byte(MPU_WRITE);//发送器件地址+写命令
IIC_Wait_Ack();
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(MPU_READ);//发送器件地址+读命令
IIC_Wait_Ack();
res=IIC_Read_Byte(0);//读取数据,发送nACK
IIC_Stop(); //产生一个停止条件
return res;
}
五.实验现象
串口输出姿态角 ,PC端显示
六.注意事项
1,串口波特率为9600.
2,MPU6050供电电压为3.3V,测试发现5V的电平跳变速度不够快会对时序产生影响,供电电源越稳定影响越小
3.使用串口1不要使能串口0
总结
代码是基于STM32F4的,移植过来之后在调试的过程中发现了F4的代码编写存在一些问题,具体表现是MSP432发送读指令0xD1给MPU6050的时候由于SDA变为高电平的时间不够导致最后接受的指令成了0XD0,所以造成了读指令失败的情况。
问题及解决办法:MSP432P401R I2C读取MPU6050的数值为0xff但是 ACK 是正常的解决办法
源码工程已经上传等待审核完成我会放上链接,需要的朋友麻烦点赞关注一下然后在评论区留下邮箱我有空就会私发(大三考研ing比较忙),实在着急要那就直接下载吧。
代码工程:(CCS)基于MSP432P401R的MPU6050陀螺仪串口输出姿态角程序.zip
代码工程:(keil5)基于MSP432P401R的MPU6050陀螺仪串口输出姿态角程序.zip
代码工程:MSP432-OLED
建议大家使用CCS版本,keil5版本测试有很多小问题。
版权声明:本文为CSDN博主「静·无羡」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_43577213/article/details/118974772
暂无评论