上一节,我们完成对IIC通信的时序以及IIC的通信的讲解和代码实现,接下来,我们正式进入,利用上一节软件实现的IIC通信协议来对外设MPU6050进行读写操作。(本节IIC代码在上节)
本节,目的很明确,就是利用软件IIC通信协议来实现STM32与MPU6050的通信,说白了,就是我们就是要控制MPU6050,来实现我们的既定目标,(目标:读取MPU6050的6个轴传感器姿态数值)。那么,接下来跟着Whappy步伐。(源码在最后)
本程序的逻辑框架
视频现象:
本人总结:
本项目,主要目的STM32和MPU6050要进行通信。
第一,解决通信问题
因为MPU6050属于STM32的外部外设设,本身MPU6050是支持IIC接口的,STM32也是具备IIC硬件通信资源的接口的,但是,为了让我自己更加对IIC有深刻的认识,我软件实现了IIC时序,并利用软件模拟的时序与MPU6050交互,通过STM32的任意两个GPIO来模拟(GPIO设置成开漏输出模式)
第二,解决MPU6050驱动问题
MPU6050是STM32内部集成外的外设设备,是STM32部内部外的外设,我们是无法通过,stm32内部总线和库函数去操作这个设备的,因此,我们需要自己写MPU6050的驱动函数,所谓的写驱动函数,就是通过配置寄存器来控制和改变硬件电路的链路状态,寄存器是一个字节的二进制位(如下图)组合起来控制电路的开关,里面都是控制这个外设内部资源的开关电路,比如下图,我们给位6一个1(二进制位),此时芯片就会进入睡眠模式,寄存器可以理解成我们房子里的电闸控制系统,有控制厨房的灯,卧室的灯等等 , 寄存器如何配置是通过查找外设的数据手册规格书和寄存器操作映射操作说明书,两者结合起来配置我们需要的功能(如:测量获取,x,y,z加速计的值),查看数据手册是一件极为重要的事情,手册里包含了此外设的所用功能,电气特性,应用等,对于这个外设最最最基本操作就是读写寄存器(操作每个外部外设的基本操作单元就是读写寄存器),要操作读写寄存器,(寄存器地址通常为一个字节)我们就必须实现读写寄存器函数,要实现读写寄存器,我们就要有通信方式,这里就是利用的IIC通信,如下图2,利用IIC通信
将我们需要的字节数据写入对应的寄存器地址,在对应的寄存器地址,写入对应的数据,这个对应的数据就是要控制电路的开关了,如下图3,图4是寄存器内一个字节数据每一个对应的硬件电路,第一个函数MPU6050_Register_W(MPU_PWR_MGMT1_REG, 0x01);这个函数就是在对应的寄存器地址写入了对应的数据,这个数据就是我们要控制的那些需要的电路功能状态,功能就是将MPU6050的电源管理寄存器 1 设置为 0x01
,使MPU6050进入睡眠模式。这个操作是为了节省功耗,或者是配置MPU6050的状态。同样的,我们也要读取一些我们需要的数据,如,当我们向寄存器写入了一个字节数据,该字节的功能就是测量x轴的加速计,我们写好之后,这个外设就会控制执行相对应的操作(如1秒种测刷新一次x轴的测量数据),如何读取呢?也是同样的写一个函数读取相对应的寄存器地址就行了,一般处理好的数据就会放在这里寄存器对应的数据缓冲区里,如图5,通过读取X轴的高八位和低八位整合成一个完整数据(测量的数据是16位的 -32767~32768)
这就是写驱动函数,过程一定要完全遵循IIC通信(6步,开始,写一个字节,读一个字节,应答,非应答,停止,如本文章代码)(下图6是一个完整的写一个字节的IIC通信过程)
图2
图3
图4
图5
图6
第三,应用层。
业务逻辑非常简单,实现的目标也很简单就是测6个轴的传感器值
简要总结:
-
外设与内部通信的区别:
- MPU6050是一个外部设备,它并不是STM32内部的外设,因此不能直接通过STM32的内部总线(如SPI、UART等)操作。
- 为了与MPU6050通信,我们必须使用一种通信协议——I2C(Inter-Integrated Circuit),这是一种常用的串行总线协议,用于连接外部设备与微控制器。
-
读写寄存器的意义:
- 寄存器控制着外设内部的硬件电路。每个寄存器通常是一个字节(8位),每一位代表某个硬件功能的开关(如进入睡眠模式、启用传感器、选择时钟源等)。
- 通过向寄存器写入数据(比如
0x01
、0x00
等),可以改变外设的工作模式(如唤醒、休眠、启动数据采集等)。 - 寄存器地址和寄存器位的详细说明通常都可以在设备的数据手册中找到。
-
如何编写驱动函数:
- I2C通信过程:
- 通过I2C协议与MPU6050设备通信,一般的I2C操作包括:发送设备地址、发送寄存器地址、读取或写入数据、接收确认信号(ACK)和非确认信号(NACK)。
- 写入数据时,通过发送设备的I2C地址、寄存器地址、以及要写入的数据字节来配置设备的工作状态。
- 读取数据时,向设备发送寄存器地址后,再读取设备返回的数据字节(如获取传感器的加速度数据)。
- I2C通信过程:
-
驱动函数的工作原理:
- 驱动程序的核心就是寄存器操作,我们通过查阅设备的手册来确定每个寄存器的地址和功能,然后编写代码与外设进行通信。
- 例如,
MPU6050_Register_W(MPU_PWR_MGMT1_REG, 0x01);
这行代码的作用是通过I2C将0x01
写入MPU_PWR_MGMT1_REG
,使MPU6050进入休眠模式。 - 对于获取数据,我们通过I2C读取特定寄存器(如加速度计X轴的数据寄存器)并合并高低字节形成完整的16位数据。
-
I2C通信流程:
- 开始信号:通过I2C总线发送起始信号,通知通信开始。
- 发送设备地址:发送目标设备的I2C地址。
- 发送寄存器地址:指定我们要操作的寄存器地址。
- 读取/写入数据:根据操作类型(读或写),发送数据字节或读取数据字节。
- 应答与非应答:在每次字节传输后,接收或发送应答信号(ACK)或非应答信号(NACK)。
- 停止信号:完成通信后,发送停止信号,结束本次I2C通信。
主要步骤:
- 初始化I2C接口:配置STM32的I2C引脚和相关参数。
- 编写寄存器操作函数:
- 写寄存器(
MPU6050_Register_W
):通过I2C将数据写入MPU6050的特定寄存器,控制外设工作模式。 - 读寄存器(
MPU6050_Register_R
):通过I2C从MPU6050读取寄存器的数据,获取传感器的测量值。
- 写寄存器(
- 驱动功能封装:通过编写高层驱动接口(如
MPU6050_Init
、MPU6050_Struct_Data
等),简化外设控制和数据获取的操作。
I2C通信详细步骤(示例):
写操作:
- 发送 开始信号。
- 发送 设备地址(如MPU6050的地址,通常为
0xD0
或0xD1
)。 - 发送 寄存器地址(如
MPU_PWR_MGMT1_REG
)。 - 发送 数据(如
0x01
)。 - 接收 应答信号。
- 发送 停止信号。
读操作:
- 发送 开始信号。
- 发送 设备地址(写操作,低7位地址
0x68
)。 - 发送 寄存器地址(如
MPU_ACCEL_XOUTH_REG
)。 - 发送 重新启动(Re-start信号)。
- 发送 设备地址(读操作,
0x69
或0x68
)。 - 读取 数据字节(如读取加速度计X轴的高字节和低字节)。
- 接收 应答信号。
- 发送 停止信号。
总结:
- 驱动程序编写的核心是根据外设的寄存器地址和功能位配置设备,通过I2C通信协议读写数据。
- 寄存器控制硬件的各种功能和状态,就像控制电器开关一样。
- I2C通信流程确保了正确的数据交换,在STM32与MPU6050之间进行有效的读写操作。
- 驱动程序的写作需要深入理解设备手册,从中获取寄存器配置和设备操作的细节。
MPU6050资源免费共享!
【免费】中文版MPU-6000/MPU-6050寄存器映射与功能详解资源-CSDN文库
MPU-6000/MPU-6050运动传感技术规格及应用解析资源-CSDN文库
void MPU6050_Init(void);
作用:对所需硬件的配置及初始化
2.uint8_t MPU6050_Regiter_R(uint8_t MPU6050_Reg_Addr);
作用:向MPU6050读出相关配置后的数据
3.void MPU6050_Register_W(uint8_t Register_Address, uint8_t Data);
作用:向MPU6050写入相关配置
以下是 MPU6050 模块的主要函数及其作用说明,附带示例代码以便参考和应用。
1. void MPU6050_Init(void);
作用:
用于初始化 MPU6050 传感器和相关硬件环境,包括 I²C 总线初始化和传感器寄存器的基本配置。它确保传感器处于工作状态,设定必要的测量参数(如采样率、滤波器设置、量程范围等),为后续数据读取和处理打下基础。
示例:
void MPU6050_Init(void) {
MyIIC_Init(); // 初始化 I²C 总线
MPU6050_Register_W(0x6B, 0x00); // 写入电源管理寄存器,关闭休眠模式
MPU6050_Register_W(0x1A, 0x03); // 设置低通滤波器(配置采样频率)
MPU6050_Register_W(0x1B, 0x10); // 设置陀螺仪量程为 ±1000°/s
MPU6050_Register_W(0x1C, 0x08); // 设置加速度计量程为 ±4g
}
- 解释:此函数调用多个寄存器写入操作完成传感器初始化,例如取消休眠模式、设置滤波器和量程范围。完成后,MPU6050 即可正常工作。
2. uint8_t MPU6050_Regiter_R(uint8_t MPU6050_Reg_Addr);
作用:
从 MPU6050 的指定寄存器读取数据,用于获取配置或测量结果。例如,读取加速度计、陀螺仪的输出数据,或检查特定寄存器的状态。
参数说明:
reg
:要读取的寄存器地址。data
:读取到的数据存储位置。
示例:
uint8_t who_am_i;
MPU6050_Register_R(0x75, &who_am_i); // 读取 WHO_AM_I 寄存器
if (who_am_i == 0x68) {
printf("MPU6050 已正确连接。\n");
} else {
printf("MPU6050 连接失败。\n");
}
- 解释:WHO_AM_I 寄存器用于验证 MPU6050 是否正常连接,返回值
0x68
是设备的标识码。
3.void MPU6050_Register_W(uint8_t Register_Address, uint8_t Data);
作用:
向 MPU6050 的指定寄存器写入配置值,用于修改传感器的设置或发送命令。例如,设置电源管理模式、调整量程范围或启用特定功能。
参数说明:
reg
:目标寄存器地址。value
:要写入的值。
示例:
MPU6050_Register_W(0x6B, 0x40); // 设置 MPU6050 进入休眠模式
printf("传感器已进入休眠模式。\n");
- 解释:通过向电源管理寄存器写入
0x40
,MPU6050 被配置为休眠状态,减少功耗。
总结
这三个函数是 MPU6050 驱动程序的核心:
MPU6050_Init
负责硬件和传感器的初始化。MPU6050_Register_R
用于读取指定寄存器的数据(如验证连接或获取测量结果)。MPU6050_Register_W
用于向寄存器写入配置值(如设定传感器的工作模式)。
通过这些函数的分工,可以方便地实现对 MPU6050 的控制和数据交互。
本节是操作寄存器的一节,一定要知道寄存器中的每一位都对应着我们的硬件电路,可以理解,寄存器每一个为都像我们电路中导线连接着关键电路。寄存器的每一位都和我们硬件电路互动,掌握好读写寄存器,我们就能通过寄存器来控制电路了。
本节内容深入讲解了寄存器的操作,掌握这一部分是硬件编程的基础。寄存器可以看作是微控制器内部的一组存储单元,每个寄存器通常由多个二进制位(bit)组成,而这些二进制位直接控制硬件的不同部分。可以将寄存器中的每一位比作电路中导线的开关,开启或关闭这些开关,将直接影响到硬件电路的工作状态。理解寄存器中的每一位及其作用,使得我们能够精准地控制硬件行为。
举个例子:
假设我们在操作一个 MPU6050 传感器的 电源管理寄存器(PWR_MGMT_1),该寄存器的地址是 0x6B
。这个寄存器的第6位是 SLEEP 位,它控制着传感器是否处于睡眠模式。当 SLEEP 位为1 时,设备会进入低功耗模式;当 SLEEP 位为0 时,设备恢复正常工作模式。如果我们通过 I2C 总线设置该位为0,传感器就会从睡眠模式中唤醒,开始采集数据。
另一个常见的例子是 MPU6050 的时钟源选择。同样在 PWR_MGMT_1 寄存器 中, CLKSEL 位(位0到位2)用于选择设备的时钟源。我们可以通过修改这三位的值来指定时钟源。例如,选择内部8MHz振荡器、外部时钟源,或者通过陀螺仪输出作为时钟源。这些设置直接影响着传感器的工作频率和稳定性。
更具体的电路类比:
想象一下电路中有一个开关控制着某个电路的通断,当开关关闭时,电路就停止工作;而当开关打开时,电路开始运作。寄存器中的每一位就像这些开关,每一位都负责控制电路中的特定功能或状态。例如,一个寄存器的第5位可能控制一个 LED 灯的亮灭,第2位可能控制某个传感器的数据采集启动与否。通过设置这些位的值,我们能够精确地控制整个系统的行为。
寄存器位的组合:
寄存器中的各个位通常不是孤立工作的,它们通常是配合使用的。例如,MPU6050 的 PWR_MGMT_1 寄存器 中除了有 SLEEP 和 CLKSEL 位之外,还可能有其他控制位,如 TEMP_DIS(禁用温度传感器)等。这些位的组合决定了设备的行为,我们需要根据应用需求正确设置这些位,以便得到最佳的硬件配置。
结论:
在嵌入式编程和硬件开发中,寄存器的操作不仅是对硬件控制的基础,也是实现精细化管理的关键。通过掌握每一位的意义和功能,我们能够实现对硬件的精准控制,从而满足不同应用场景的需求。这就像是拥有了控制整个电路的开关,通过合理的组合和设置,灵活调整硬件的状态,达到理想的效果。
C语言实现多返回值的方法
三种函数返回多个返回值的方法:
1.全局变量法(慎用)
2.数组指针法
3.结构体指针
c语言||一个函数能return好几个?(产生多个返回值)_c语言return返回多个值-CSDN博客
IIC时序参考:4.STM32之通信接口《精讲》之IIC通信---软件实现IIC《深入浅出》面试必备!_stm32的sda接口-CSDN博客
总结:本节用了多层的模块架构,分层设计,能够让程序更具有模块化,代码
接下来就是代码实现模块:
MyIIC.h
#ifndef __MYIIC_H #define __MYIIC_H #include "stm32f10x.h" // 引入STM32F10x的设备头文件,包含对STM32F10系列微控制器的所有定义 // I2C接口相关的函数声明: // 初始化I2C接口 void MyIIC_Init(void); // 产生I2C起始信号 void MyIIC_Start(void); // 产生I2C停止信号 void MyIIC_Stop(void); // 发送一个字节的数据到I2C总线 void MyIIC_SendByte(uint8_t Byte); // 从I2C总线接收一个字节的数据 uint8_t MyIIC_ReveiveByte(void); // 向I2C总线发送ACK(确认)或NACK(非确认)信号,AckBit为0表示NACK,为1表示ACK void MyIIC_SendAck(uint8_t AckBit); // 从I2C总线接收ACK(确认)或NACK(非确认)信号 uint8_t MyIIC_ReceiveAck(void); #endif // 结束头文件保护
注释解释:
#ifndef __MYIIC_H
/#define __MYIIC_H
:防止头文件被重复包含,__MYIIC_H
是该头文件的宏定义。#include "stm32f10x.h"
:引入 STM32F10x 微控制器的头文件,包含 STM32F10x 系列相关的硬件寄存器、外设定义等。MyIIC_Init
:初始化 I2C 总线的相关设置,包括设置引脚、时钟等。MyIIC_Start
:生成 I2C 起始条件,通知从设备开始通信。MyIIC_Stop
:生成 I2C 停止条件,结束通信。MyIIC_SendByte
:向 I2C 总线上发送一个字节的数据。MyIIC_ReveiveByte
:从 I2C 总线上接收一个字节的数据。MyIIC_SendAck
:向 I2C 总线上发送确认(ACK)或非确认(NACK)信号,用于响应从设备是否成功接收到数据。MyIIC_ReceiveAck
:从 I2C 总线上接收 ACK 或 NACK 信号,确认数据是否被成功接收。这份头文件声明了I2C通信中常用的操作函数接口,实际实现则可能在
MyIIC.c
文件中。
MyIIC.c
#include "stm32f10x.h" // 引入 STM32F10x 微控制器的设备头文件,包含 STM32F10x 系列相关的硬件寄存器、外设定义等 #include "MyIIC.h" #include "Delay.h" // 写SCL信号到GPIO(时钟线),Bit为0或1表示低或高 void MyIIC_SCL_W(uint8_t Bit) { GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)Bit); // 写入SCL引脚(GPIOB Pin 10) Delay_us(10); // 延时10微秒 } // 写SDA信号到GPIO(数据线),Bit为0或1表示低或高 void MyIIC_SDA_W(uint8_t Bit) { GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)Bit); // 写入SDA引脚(GPIOB Pin 11) Delay_us(10); // 延时10微秒 } // 读取SDA引脚的状态(即I2C的数据线) uint8_t MyIIC_SDA_R(void) { uint8_t Bit; Bit = GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11); // 读取SDA引脚(GPIOB Pin 11)的电平状态 Delay_us(10); // 延时10微秒 return Bit; // 返回读取的状态 } // 初始化I2C通信接口的GPIO引脚 void MyIIC_Init(void) { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 设置为开漏输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // 配置GPIOB的Pin 10(SCL)和Pin 11(SDA) GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置GPIO速度为50MHz GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始化GPIOB的引脚 GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11); // 设置SCL和SDA为高电平(空闲状态) } // 产生I2C总线的起始条件 void MyIIC_Start(void) { MyIIC_SDA_W(1); // SDA保持高电平 MyIIC_SCL_W(1); // SCL保持高电平 MyIIC_SDA_W(0); // SDA拉低 MyIIC_SCL_W(0); // SCL拉低,准备开始通信 } // 产生I2C总线的停止条件 void MyIIC_Stop(void) { MyIIC_SDA_W(0); // SDA拉低 MyIIC_SCL_W(1); // SCL拉高 MyIIC_SDA_W(1); // SDA拉高,产生停止条件 } // 向I2C总线发送一个字节的数据(8位) void MyIIC_SendByte(uint8_t Byte) { uint8_t i; for (i = 0; i < 8; i++) // 遍历每一位 { MyIIC_SDA_W(Byte & (0x80 >> i)); // 通过位运算确定当前位并发送 MyIIC_SCL_W(1); // 拉高时钟线SCL MyIIC_SCL_W(0); // 拉低时钟线SCL,完成一个时钟周期 } } // 从I2C总线接收一个字节的数据(8位) uint8_t MyIIC_ReveiveByte(void) { uint8_t i, Byte = 0x00; MyIIC_SDA_W(1); // 设置SDA为输入模式,准备接收数据 for (i = 0; i < 8; i++) // 遍历每一位 { MyIIC_SCL_W(1); // 拉高时钟线SCL,开始接收 if (MyIIC_SDA_R() == 1) // 读取SDA数据线的电平,接收当前位 { Byte |= (0x80 >> i); // 将接收到的位存入字节 } MyIIC_SCL_W(0); // 拉低时钟线SCL,准备接收下一个位 } return Byte; // 返回接收到的字节 } // 向I2C总线发送ACK(确认)或NACK(非确认)信号 void MyIIC_SendAck(uint8_t AckBit) { MyIIC_SDA_W(AckBit); // 设置ACK或NACK,AckBit为0表示NACK,为1表示ACK MyIIC_SCL_W(1); // 拉高时钟线SCL,准备发送 MyIIC_SCL_W(0); // 拉低时钟线SCL,完成一个时钟周期 } // 从I2C总线接收ACK(确认)或NACK(非确认)信号 uint8_t MyIIC_ReceiveAck(void) { uint8_t AckBit; MyIIC_SDA_W(1); // 设置SDA为输入模式,准备接收ACK信号 MyIIC_SCL_W(1); // 拉高时钟线SCL,准备接收ACK AckBit = MyIIC_SDA_R(); // 读取ACK信号(SDA线的状态) MyIIC_SCL_W(0); // 拉低时钟线SCL,完成接收 return AckBit; // 返回接收到的ACK或NACK }
注释说明:
MyIIC_SCL_W
和MyIIC_SDA_W
:
- 这两个函数用于控制I2C时钟线(SCL)和数据线(SDA)的电平。
Bit
是传入的电平值(0 或 1),它控制SCL或SDA的高低电平。- 每次设置完电平后,延时10微秒,以确保信号稳定。
MyIIC_SDA_R
:
- 该函数用于读取SDA线的电平,即获取I2C总线数据线的状态。
MyIIC_Init
:
- 用于初始化I2C的GPIO引脚(SCL和SDA)。在此函数中,通过配置GPIOB的引脚10和11为开漏输出模式来实现I2C的通讯。
- 使能GPIOB时钟,并将SCL和SDA初始化为高电平,处于空闲状态。
MyIIC_Start
和MyIIC_Stop
:
MyIIC_Start
:通过控制SDA和SCL的电平,产生I2C总线的起始条件。MyIIC_Stop
:通过控制SDA和SCL的电平,产生I2C总线的停止条件。
MyIIC_SendByte
:
- 该函数用于发送一个字节的数据到I2C总线上。数据是按位发送的,逐位控制SDA线的电平,并通过时钟线(SCL)来同步数据传输。
MyIIC_ReveiveByte
:
- 该函数用于从I2C总线上接收一个字节的数据。每接收一位,都会拉高SCL线,然后读取SDA线的状态,再拉低SCL线。
MyIIC_SendAck
和MyIIC_ReceiveAck
:
MyIIC_SendAck
:用于发送ACK(确认)或NACK(非确认)信号,告知从设备是否成功接收到数据。MyIIC_ReceiveAck
:接收从设备发送的ACK或NACK信号,表示接收是否成功。作用:
本代码实现了I2C总线的基本操作函数,包括起始和停止信号的产生、字节的发送和接收、ACK信号的发送与接收。通过这些基本的I2C操作函数,可以与I2C从设备进行通信。
MPU6050.h
#ifndef __MPU6050_H #define __MPU6050_H #include "stm32f10x.h" // 引入 STM32F10x 微控制器的设备头文件,包含相关硬件寄存器和外设定义 // 定义一个结构体类型,用于存储MPU6050传感器的六个数据值(加速度和陀螺仪) typedef struct { int16_t Acce_X; // X轴加速度数据 int16_t Acce_Y; // Y轴加速度数据 int16_t Acce_Z; // Z轴加速度数据 int16_t Gyro_X; // X轴陀螺仪数据 int16_t Gyro_Y; // Y轴陀螺仪数据 int16_t Gyro_Z; // Z轴陀螺仪数据 } MPU6050_Data, *pMPU6050_Data; // 定义结构体类型MPU6050_Data,并定义其指针类型pMPU6050_Data // 函数声明:初始化MPU6050传感器 void MPU6050_Init(void); // 函数声明:向MPU6050的指定寄存器写入一个字节的数据 void MPU6050_Register_W(uint8_t Register_Address, uint8_t Data); // 函数声明:读取MPU6050指定寄存器的一个字节数据 uint8_t MPU6050_Regiter_R(uint8_t MPU6050_Reg_Addr); // 函数声明:读取MPU6050的加速度计和陀螺仪数据 void MPU6050_RegState_Data(int16_t* Acce_X, int16_t* Acce_Y, int16_t* Acce_Z, int16_t* GYRO_X, int16_t* GYRO_Y, int16_t* GYRO_Z); // 函数声明:获取MPU6050的设备ID uint8_t MPU6050_ID(void); // 函数声明:获取MPU6050的传感器数据并返回一个指向MPU6050_Data结构体的指针 MPU6050_Data* MPU6050_Struct_Data(void); #endif
注释说明:
#ifndef __MPU6050_H
和#define __MPU6050_H
:
- 这两行代码用于防止头文件被重复包含。当该头文件