STM32F103学习笔记——MPU6050数据读取与倾角检测

本文最后更新于:2022年5月29日 上午

使用STM32F103C8T6读取mpu6050的数据,转换成角度值,在串口中显示调式信息。

一、前言

由于项目需要检测物体倾斜角度,我们选择了陀螺仪 MPU6050 来进行角度检测。但是查找资料发现,这个小小的陀螺仪传感器涉及到一大堆令我一脸懵逼的名词,例如:「卡尔曼滤波」、「协方差阵列」「加速度陀螺仪融合」、「四元素欧拉角」、「陀螺仪积分」、「内部的 DMP」等,看着这些,如果是完全萌新的话,我想可能立刻就扔掉它跑路了。

但是,如果只是想得到「简单」的坐标夹角,完全没必要涉及到上面的那些名词,用高中三角函数知识已足够了。当你遇到更高精度数据的需求时,也没必要自己从零开始,直接移植现成的库即可。

不多说,直接上手实验,本文章将介绍一种简陋方法计算粗略的角度

二、软件程序设计

1,I2C 基本配置

MPU6050 与 MCU 通过 I2C 总线进行通讯。用软件模拟的方式实现 I2C 底层基本时序函数,包括起始、停止信号的产生,以及发送/接收单字节数据、检测/发送应答信号。

(1)i2c_gpio.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#ifndef __I2C_GPIO_H
#define __I2C_GPIO_H

#include "stm32f10x.h"


#define PIN_SCL GPIO_Pin_12
#define PIN_SDA GPIO_Pin_11
#define I2C_GPIO_PORT GPIOA
#define I2C_GPIO_CLK RCC_APB2Periph_GPIOA


//SDA的方向设置
void SDA_IN(void);
void SDA_OUT(void);

//I2C所有操作函数
void I2C_Config(void); //初始化I2C的IO口
void I2C_Start(void); //发送I2C开始信号
void I2C_Stop(void); //发送I2C停止信号
void I2C_Send_Byte(u8 txd); //I2C发送一个字节
uint8_t I2C_Read_Byte(unsigned char ack); //I2C读取一个字节
uint8_t I2C_Wait_Ack(void); //I2C等待ACK信号
void I2C_Ack(void); //I2C发送ACK信号
void I2C_NAck(void); //I2C不发送ACK信号
void delay_us(u32 m); //延时函数

#endif /* __I2C_GPIO_H */

(2)i2c_gpio.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
***************************************************************************************
* @file i2c_gpio.c
* @author Soso
* @date 2019-08-02
* @brief GPIO simulation i2c configuration.
***************************************************************************************
*/

#include "i2c_gpio.h"
#include "systick.h"


void I2C_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( I2C_GPIO_CLK, ENABLE ); //使能A口时钟

GPIO_InitStructure.GPIO_Pin = PIN_SCL | PIN_SDA;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);

GPIO_SetBits(I2C_GPIO_PORT,PIN_SCL);
GPIO_SetBits(I2C_GPIO_PORT,PIN_SDA);
}


//产生I2C开始信号
void I2C_Start(void)
{
SDA_OUT(); //sda线输出
GPIO_SetBits(GPIOA,PIN_SDA); //SDA =1
delay_us(10);
GPIO_SetBits(GPIOA,PIN_SCL); //SCL=1
delay_us(10);
GPIO_ResetBits(GPIOA,PIN_SDA);//SDA=0
delay_us(10);
GPIO_ResetBits(GPIOA,PIN_SCL);//SCL=0
}


//产生I2C停止信号
void I2C_Stop(void)
{
SDA_OUT(); //sda线输出
GPIO_ResetBits(GPIOA,PIN_SDA);
delay_us(10);
GPIO_SetBits(GPIOA,PIN_SCL);
delay_us(10);
GPIO_SetBits(GPIOA,PIN_SDA);
delay_us(10);
GPIO_ResetBits(GPIOA, PIN_SCL);//SCL=0;
delay_us(10);
}


//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
uint8_t I2C_Wait_Ack(void)
{
uint8_t ucErrTime=0;
SDA_IN(); //SDA设置为输入
GPIO_SetBits(GPIOA,PIN_SDA);
delay_us(5);
GPIO_SetBits(GPIOA,PIN_SCL);
delay_us(5);
while(GPIO_ReadInputDataBit(GPIOA,PIN_SDA))
{
ucErrTime++;
if(ucErrTime>250)
{
I2C_Stop();
return 1;
}
}
GPIO_ResetBits(GPIOA, PIN_SCL);
return 0;
}


//产生ACK应答
void I2C_Ack(void)
{
GPIO_ResetBits(GPIOA,PIN_SCL);
SDA_OUT();
GPIO_ResetBits(GPIOA,PIN_SDA);
delay_us(5);
GPIO_SetBits(GPIOA,PIN_SCL);
delay_us(5);
GPIO_ResetBits(GPIOA,PIN_SCL);
}


//不产生ACK应答
void I2C_NAck(void)
{
GPIO_ResetBits(GPIOA,PIN_SCL);
SDA_OUT();
GPIO_SetBits(GPIOA,PIN_SDA);
delay_us(5);
GPIO_SetBits(GPIOA,PIN_SCL);
delay_us(5);
GPIO_ResetBits(GPIOA,PIN_SCL);
}


//I2C发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void I2C_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
GPIO_ResetBits(GPIOA,PIN_SCL);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
if((txd&0x80)==0x80)
GPIO_SetBits(GPIOA, PIN_SDA); //SDA=1,写 1
else
GPIO_ResetBits(GPIOA, PIN_SDA); //SDA=0,写 0
txd<<=1;
delay_us(5); //对TEA5767这三个延时都是必须的
GPIO_SetBits(GPIOA,PIN_SCL);
delay_us(5);
GPIO_ResetBits(GPIOA,PIN_SCL);
delay_us(5);
}
}


//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
uint8_t I2C_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
GPIO_ResetBits(GPIOA,PIN_SCL);
delay_us(5);
GPIO_SetBits(GPIOA,PIN_SCL);
receive<<=1;
if(GPIO_ReadInputDataBit(GPIOA,PIN_SDA))receive++;
delay_us(5);
}
if (!ack)
I2C_NAck();//发送nACK
else
I2C_Ack(); //发送ACK
return receive;
}


void SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SDA;
GPIO_Init(GPIOA, &GPIO_InitStruct );
}


void SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Pin = PIN_SDA;
GPIO_Init(GPIOA, &GPIO_InitStruct );
}
/*********************************************END OF FILE*********************************************/

2,MPU6050配置

MCU 作为主机与传感器通讯前需要发送 7 位的从机设备地址,设备地址的惯用套路是固定高位,并通过引脚电平确定低位。查阅寄存器手册得知,117 号寄存器 WHO AM I 决定着设备地址的高 6 位,上电复位值为 110100,最低 1 位则由外部引脚 AD0 决定(即一块电路板最多只能有两个 MPU6050)。查看模块或开发板电路图确定芯片 AD0 的电平(一般为低)最终得到 7 位的 I2C 从机设备地址为 1101 000(0xD0),在 mpu6050.h 文件中宏定义为 DEV_ADDR

ML4bYF.jpg

(1)mpu6050.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#ifndef __MPU6050_H
#define __MPU6050_H

#include "stm32f10x.h"
#include "i2c_gpio.h"
#include <math.h>


/* 坐标角度结构体 */
typedef struct Angle
{
double X_Angle;
double Y_Angle;
double Z_Angle;

}MPU6050_Angle;


/* 传感器数据修正值(消除芯片固定误差,根据硬件进行调整) */
#define X_ACCEL_OFFSET 0
#define Y_ACCEL_OFFSET 0
#define Z_ACCEL_OFFSET 0
#define X_GYRO_OFFSET 0
#define Y_GYRO_OFFSET 0
#define Z_GYRO_OFFSET 0


/*******************************************************/
#define DEV_ADDR 0xD0 // 6050 器件地址
//-----------------------------------------
// 定义MPU6050内部地址
//-----------------------------------------
#define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)

/* 加速度相关寄存器地址 */
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40

/* 温度相关寄存器地址 */
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42

/* 陀螺仪相关寄存器地址 */
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48

#define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I 0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取

/*******************************************************/

void MPU6050_WR_Reg(uint8_t regAddr, uint8_t regData);
uint8_t MPU6050_RD_Reg(uint8_t regAddr);
void MPU6050_Init(void);
int16_t MPU6050_Get_Data(uint8_t regAddr);
void MPU6050_Get_Angle(MPU6050_Angle *data);

#endif /* __MPU6050_H */

(2)mpu6050.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/**
***************************************************************************************、
* @file mpu6050.c
* @author Soso
* @date 2019-08-02
* @brief mpu6050 configuration.
***************************************************************************************
*/

#include "mpu6050.h"


/**
* @brief MPU6050 write register function.
* @param regAddr:Register address
* @param regData:Register value to be written
* @retval None
*/
void MPU6050_WR_Reg(uint8_t regAddr, uint8_t regData)
{
/* 发送起始信号 */
I2C_Start();

/* 发送设备地址 */
I2C_Send_Byte(DEV_ADDR);
if (I2C_Wait_Ack())
goto stop;

/* 发送寄存器地址 */
I2C_Send_Byte(regAddr);
if (I2C_Wait_Ack())
goto stop;

/* 写数据到寄存器 */
I2C_Send_Byte(regData);
if (I2C_Wait_Ack())
goto stop;
I2C_Stop();

stop:
I2C_Stop();
}


/**
* @brief MPU6050 read register function.
* @param regAddr:Register address
* @retval Register address data
*/
uint8_t MPU6050_RD_Reg(uint8_t regAddr)
{
uint8_t regData;

/* 发送起始信号 */
I2C_Start();

/* 发送设备地址 */
I2C_Send_Byte(DEV_ADDR);
if (I2C_Wait_Ack())
goto stop;

/* 发送寄存器地址 */
I2C_Send_Byte(regAddr);
if (I2C_Wait_Ack())
goto stop;

/* 发送重复起始信号 */
I2C_Start();

/* 发送读模式设备地址 */
I2C_Send_Byte(DEV_ADDR | 0x01);
if (I2C_Wait_Ack())
goto stop;

/* 读寄存器数据 */
regData = I2C_Read_Byte(0);
I2C_Stop();

stop:
I2C_Stop();

return regData;
}


/**
* @brief MPU6050 initialization function.
* @param None
* @retval None
*/
void MPU6050_Init(void)
{
I2C_Config();

MPU6050_WR_Reg(PWR_MGMT_1, 0x00); //解除休眠状态
MPU6050_WR_Reg(SMPLRT_DIV, 0x07); //陀螺仪采样率,典型值:0x07(125Hz)
MPU6050_WR_Reg(CONFIG, 0x06); //低通滤波频率,典型值:0x06(5Hz)
MPU6050_WR_Reg(GYRO_CONFIG, 0x18); //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
MPU6050_WR_Reg(ACCEL_CONFIG, 0x01); //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
}


/**
* @brief MPU6050 get data function.
* @param regAddr:Register address
* @retval Register data
*/
int16_t MPU6050_Get_Data(uint8_t regAddr)
{
uint8_t Data_H, Data_L;
uint16_t data;

Data_H = MPU6050_RD_Reg(regAddr);
Data_L = MPU6050_RD_Reg(regAddr + 1);
data = (Data_H << 8) | Data_L; // 合成数据

return data;
}


/**
* @brief Get MPU6050 angle function.
* @param data: MPU6050_Angle Structure
* @retval None
*/
void MPU6050_Get_Angle(MPU6050_Angle *data)
{
/* 计算x, y, z 轴倾角,返回弧度值*/
data->X_Angle = acos((MPU6050_Get_Data(ACCEL_XOUT_H) + X_ACCEL_OFFSET) / 16384.0);
data->Y_Angle = acos((MPU6050_Get_Data(ACCEL_YOUT_H) + Y_ACCEL_OFFSET) / 16384.0);
data->Z_Angle = acos((MPU6050_Get_Data(ACCEL_ZOUT_H) + Z_ACCEL_OFFSET) / 16384.0);

/* 弧度值转换为角度值 */
data->X_Angle = data->X_Angle * 57.29577;
data->Y_Angle = data->Y_Angle * 57.29577;
data->Z_Angle = data->Z_Angle * 57.29577;
}

/*********************************************END OF FILE*********************************************/

3,串口模块配置

(1)usart.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef __USART_H
#define __USART_H

#include "stm32f10x.h"
#include "stdio.h"

//Pin definition
/*******************************************************/
#define DEBUG_USART USART3
#define DEBUG_USART_CLK RCC_APB1Periph_USART3
#define DEBUG_USART_AF RCC_APB2Periph_AFIO
#define DEBUG_USART_GPIO_CLK RCC_APB2Periph_GPIOB
#define DEBUG_USART_BAUDRATE 115200

#define DEBUG_USART_RX_GPIO_PORT GPIOB
#define DEBUG_USART_RX_PIN GPIO_Pin_11

#define DEBUG_USART_TX_GPIO_PORT GPIOB
#define DEBUG_USART_TX_PIN GPIO_Pin_10


#define DEBUG_USART_IRQ USART3_IRQn
#define DEBUG_USART_IRQHandler USART3_IRQHandler

/************************************************************/

void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);

void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

#endif /* __USART_H */

(2)usart.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
******************************************************************************
* @file uasrt.c
* @author Soso
* @date 2018-09-16
* @brief Redirect c library printf function to usart port, interrupt receiving mode.
******************************************************************************
*/

#include "usart.h"


/* Configuring nested vectored interrupt controller NVIC */
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;

/* Nested vector interrupt controller group selection */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

/* Preemptio Priority is 1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* Sub Priority is 1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* Enable interrupt */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

/* Configure USART as the interrupt source */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* Initialize configuration NVIC */
NVIC_Init(&NVIC_InitStructure);
}

void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;

/* Enable port alternate function and enable USART GPIO clock */
RCC_APB2PeriphClockCmd(DEBUG_USART_AF|DEBUG_USART_GPIO_CLK,ENABLE);
/* Enable USART Clock */
RCC_APB1PeriphClockCmd(DEBUG_USART_CLK,ENABLE);

/* Configure the Tx pin for alternate function */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

/* Configure the Rx pin for the alternate function */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

/* Configuration USART mode */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/* USART mode control: simultaneous enable send and receive */
USART_InitStructure.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;

USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
USART_Init(DEBUG_USART, &USART_InitStructure);

/* Configure the serial port to receive interrupts. */
NVIC_Configuration();

/* Enable serial port receive interrupt */
USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);

/* Enable serial */
USART_Cmd(DEBUG_USART, ENABLE);
}


/***************** Send a character **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* Send a byte of data to the USART */
USART_SendData(pUSARTx,ch);

/* Waiting for the transmit data register to be empty */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/***************** Send string **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');

/* Waiting for transmission to complete */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}

/***************** Send a 16-digit number **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;

/* Take out the high eight */
temp_h = (ch&0XFF00)>>8;
/* Take the lower eight */
temp_l = ch&0XFF;

/* Send high eight */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

/* Send the lower eight digits */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/* Redirect the c library function printf to the serial port,
use the printf function after redirection. */
int fputc(int ch, FILE *f)
{
/* Send a byte of data to the serial port */
USART_SendData(DEBUG_USART, (uint8_t) ch);

/* Waiting to send */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);

return (ch);
}

/* Redirect the c library function scanf to the serial port,
rewrite backwards can use scanf, getchar and other functions. */
int fgetc(FILE *f)
{
/* Waiting for serial input data */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET);

return (int)USART_ReceiveData(DEBUG_USART);
}
/*********************************************END OF FILE*********************************************/

4,延时函数配置

(1)systick.h
1
2
3
4
5
6
7
8
9
10
11
#ifndef __SYSTICK_H
#define __SYSTICK_H

#include"stm32f10x.h"

void delay_us(u32 i);
void delay_ms(u32 i);


#endif /* __SYSTICK_DELAY_H */

(2)systick.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include "systick.h"

void delay_us(u32 i)
{
u32 temp;
SysTick->LOAD=9*i; //Set the reload value, at 72MHZ
SysTick->CTRL=0X01; //Enable, reduce to zero is no action, use external clock source
SysTick->VAL=0; //Clear counter
do
{
temp=SysTick->CTRL; //Read current countdown value
}
while((temp&0x01)&&(!(temp&(1<<16)))); //Waiting time to arrive
SysTick->CTRL=0; //Close counter
SysTick->VAL=0; //Empty counter
}
void delay_ms(u32 i)
{
u32 temp;
SysTick->LOAD=9000*i; //Set the reload value, at 72MHZ
SysTick->CTRL=0X01; //Enable, reduce to zero is no action, use external clock source
SysTick->VAL=0; //Clear counter
do
{
temp=SysTick->CTRL; //Read current countdown value
}
while((temp&0x01)&&(!(temp&(1<<16)))); //Waiting time to arrive
SysTick->CTRL=0; //Close counter
SysTick->VAL=0; //Empty counter
}

5, 数据读取并在串口打印

main.c:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
******************************************************************************
* @file main.c
* @author Soso
* @date 2019-08-02
* @brief I2C read mpu6050 data.
*
******************************************************************************
*/

#include "stm32f10x.h"
#include "usart.h"
#include "i2c_gpio.h"
#include "mpu6050.h"
#include "systick.h"

int main(void)
{
USART_Config();
I2C_Config();
MPU6050_Init();

MPU6050_Angle data;
printf("MPU6050 initialzation.\r\n");

while(1)
{
MPU6050_Get_Angle(&data); // 计算三轴倾角
printf("X_Angle = %lf° Y_Angle = %lf° Z_Angle = %lf°\r\n", data.X_Angle,data.Y_Angle,data.Z_Angle);

// /* 打印 x, y, z 轴加速度 */
// printf("ACCEL_X: %d ACCEL_Y: %d ACCEL_Z: %d\r\n",
// MPU6050_Get_Data(ACCEL_XOUT_H)+ X_ACCEL_OFFSET,MPU6050_Get_Data(ACCEL_YOUT_H)+ Y_ACCEL_OFFSET,MPU6050_Get_Data(ACCEL_ZOUT_H)+ Z_ACCEL_OFFSET);

// /* 打印温度,需要根据手册的公式换算为摄氏度 */
// printf("TEMP: %0.2f\r\n", MPU6050_Get_Data(TEMP_OUT_H) / 340.0 + 36.53);

// /* 打印 x, y, z 轴角速度 */
// printf("GYRO_X: %d GYRO_Y: %d GYRO_Z: %d\r\n",
// MPU6050_Get_Data(GYRO_XOUT_H)+ X_GYRO_OFFSET,MPU6050_Get_Data(GYRO_YOUT_H)+ Y_GYRO_OFFSET,MPU6050_Get_Data(GYRO_ZOUT_H)+ Z_GYRO_OFFSET);

delay_ms(200);
}
}




/*********************************************END OF FILE*********************************************/

三、元器件接线说明

ML4HFU.jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
【 !】 引脚分配
STM32 MPU6050
PA12 -> SCL PIN
PA11 -> SDA PIN
+3.3V -> VCC
GND -> GND

STM32 串口模块
PB11 -> Tx
PB10 -> Rx
+3.3V -> +3.3V
GND -> GND
波特率:115200

四、参考链接

[1] https://zhuanlan.zhihu.com/p/30621372

[2] https://blog.csdn.net/he__yuan/article/details/76559569


STM32F103学习笔记——MPU6050数据读取与倾角检测
https://kevinloongc.github.io/posts/b1f7002e.html
作者
Kevin Loongc
发布于
2019年8月2日
许可协议