STM32F4

您所在的位置:网站首页 lcd驱动电压多少正常 STM32F4

STM32F4

2023-06-02 12:51| 来源: 网络整理| 查看: 265

目录

1. I2C是什么

2. I2C物理层介绍

3. I2C协议层介绍

3.1 I2C基本读写过程

3.1.1 通讯复合格式

3.2 通讯的起始和停止信号

3.3 数据有效性

3.4 地址及数据方向

3.5 响应

4. STM32的I2C特性及架构

4.1 I2C架构剖析

5. I2C通讯过程

5.1 主发送器

5.2 主接收器

6. I2C库函数

7. EEPROM简介

8. 初始化I2C

9. MPU-6050简介

10. 软件读写MPU-6050

10.1 I2C时序基本单元

10.2 实验程序

10.2.1 main.c

10.2.2 I2C.c

10.2.3 I2C.h

10.2.4 MPU6050.c

10.2.5 MPU6050.h

10.2.6 MPU6050_Reg.h

11. 硬件读写MPU-6050

11.1 MPU6050.c

11.2 MPU6050.h

12. I2C与AT24C02通信

12.1 实验程序

12.1.1 main.c

12.1.2 MyI2C.c

12.1.3 MyI2C.h

12.1.4 AT24C02.c

12.1.5 AT24C02.h

1. I2C是什么

首先从大的方向上明确I2C协议到底是干嘛的,进而理解什么是I2C?

        我们已经学习过串口通讯USART,串口通信就是通过USART_TX和USART_RX引脚和外部芯片通信,当然也可以通过CH340转电平芯片实现和电脑进行通信。I2C就类似与USART,也是一种通信方式,一般用于实现芯片与芯片(外设与外设)之间的通信,只需要通过SCL和SDA两条总线即可实现通信,这两条总线类似于TX和RX,但并不是说所有通信方式都只需要两根线。

I2C(内部集成电路)总线接口用作微控制器(主机)和I2C串行总线之间的接口。提供多主模式功能,可以控制所有I2C总线特定的序列、协议、仲裁和时序。

I2C只需要两根线即可在连接于总线上的设备之间传送信息。主设备用于启动总线传送数据。此时任何被寻址的设备均为从设备。总线上主设备和从设备不是固定的,而是根据收发双方数据传送的方向规定的。 当主设备发送数据给从设备,那么主设备首先要寻址从设备,从设备此时接收主设备发送的数据,主设备停止发送时,通讯结束。如果是从设备发送数据给主设备,那么则需要从设备寻址主设备,主设备负责接收从设备发送的数据,从设备停止发送,通讯结束。

I2C通信协议的英文全称是:Inter-Integrate Circuit,I2C是由菲利普(Phiilps)公司开发的,由于它引脚少,硬件实现简单,可扩展性强,不需要USART、CAN等通讯协议的外部收发设备,被广泛地使用在系统内多个集成电路IC间通讯。

2. I2C物理层介绍

物理层就是最基本的原理图这些,I2C接线最基本的特性。

I2C是一个支持多设备的总线。总线的意思就是多个设备共用的信号线。比如说MCU主机芯片与EEPROM进行通信,那么主机和从机只需要引出两个引脚接在SCL和SDA总线上,即可实现I2C协议通信。该协议简单就简单在不像USART和CAN通信,串口通信还需要CH340进行转电平处理,I2C不需要外围芯片参与。

正如上图中所示,一个I2C通信总线中,可以连接多个I2C通讯设备(触摸屏、传感器、EEPROM),支持多个通讯主机及多个通讯从机。既然可以实现多个主机和从机的通信,那么一定有具体的规则规定哪个主机和哪个从机进行通信(也就是说假设主机想要和EEPROM通信,怎么确定主机可以在多个从机中找到EEPROM从机?)

每个连接到总线的设备都有一个独立的地址(该地址是提前规定好的,I2C协议规定寻址的话地址可以是7位或者10位的,也就是说如果MCU地址设置是7位,采用寻址方法和EEPROM进行通信,那么EEPROM只能设置为7位,否则是无法寻址到EEPROM的,也就无法使双方进行通信),主机可以利用这个地址进行不同设备之间的访问。进而确定通信双方。

SCL总线:串行时钟线。任何外设通信都需要时钟的参与,I2C也不例外,SCL总线的作用是为了保持通讯双方收发一致,一方发送数据,另一方在收到发送标志后,能到在约定时间内收到数据。

SDA总线:串行数据线。串行数据线很明显是用来表示双方通信的数据。

串行数据总线SDA和串行时钟总线SCL一般都需要接上拉电阻外接电源。上拉电阻的阻值一般设置为4.7K欧。

当I2C设备空闲时,会输出高阻态。意思就是:假设现在主机不需要和EEPROM通信,EEPROM就会处于空闲,可以理解为EEPROM连接的两条总线断开,输出高阻态可以假想为一个很大的电阻,设备断路,那么主机就不会受到EEPROM电压的影响。

当所有设备都空闲时,都输出高阻态,上拉电阻会把总线拉成高电平。意思就是:所有设备都处于空闲状态,也就是所有设备都和总线断开,那么所有设备的电压变化都不会影响到主机,那么主机通过上拉电阻连接的电阻是多少V就会使主机变成多少v,假设是3.3V,那么主机就会呈现3.3V,也就是高电平。

多个主机同时使用总线时,为了防止多个主机之间的数据进行冲突,会利用仲裁的方式决定哪个设备占用总线。(类似于中断中NVIC中断优先级,DMA直接存储器中的仲裁器)

I2C具有三种传输模式:

        标准模式传输效率为100kbit/s        快速模式为400kbit/s        高速模式下可达3.4Mbit/s  (但需要注意:目前大多数I2C设备尚且不支持高速模式)

连接到相同总线的IC数量受到总线的最大电容400pF限制。

3. I2C协议层介绍

I2C协议层定义了通讯的起始和停止信号、数据有效性、响应、仲裁、时钟同步和地址广播等环节。

3.1 I2C基本读写过程

主机写数据到从机: 

S:起始信号

SLAVE ADDRESS:从机地址---7位;(主机根据从机地址去选择与哪个从机进行通信)

R/W:主从写方向; 当将该位设置为0时,表示主机写数据到从机;当该位写1时,表示从机写数据到主机。

以上都是数据从主机传输至从机;也就是上图中阴影部分;

A:应答(ACK)信号;由上图主机写数据到从机,并且设置好写方向;接下来从机进行应答,表示此处通讯的从机可以使用;(就像老师问问题,学生收到问题后给出应答一样)

当从机发送应答 A 给到主机后,主机就会传输数据 DATA 到从机。

传输完一个字节后,从机会在此应答给到主机。(实际上,每当主机传完一个字节后,从机都会发送一次应答)

A/A杠:应答(ACK)或非应答(NACK)信号。从机在接收完上一次主机发送的一位字节后,从机发送非应答(NACK),表示不再让主机发送字节了。也可以认为是从机想要终止传输信号。

P:数据传输的结束。P对应于S,既然有传输的起始信号,那么必然有数据传输的终止信号。

主机由从机中读数据:

既然是由从机中读数据,那么比方说是:主机和EEPROM进行通信,此时的读方向就是从EEPROM到主机。

S:起始信号。也可以说是传输开始的信号

SLAVE ADDRESS:从机地址;

R/W:读写方向位。因为此时是由从机中读数据,所以该位须设置为1。

以上不变的是起始信号、从机地址、读写方向位始终是由主机来发送数据的。

A:应答(ACK)。从机发送应答。

DATA:数据位;不同于写的是,因为此时是由从机读数据,所以数据位在读的从机,然后由从机读一个字节,主机应答一次,这么依次循环,直到主机非应答(NACK)信号的来临。非应答信号就是不再进行数据传输了。

P:数据传输停止位。

3.1.1 通讯复合格式

通讯复合格式不同于单纯的读写过程的地方在于:

通讯复合格式具有两次S,也就是两次传输开始的信号。

整个过程是这样的:首先还是主机发送数据传输开始的信号,然后发送从机地址SLAVE_ADDRESS。这里不同的是,发送从机的地址一般是从机中某个确切位置的地址,比方说主机和EEPROM进行通信,从机EEPROM中的内存是很大的,分很多不同的地址,第一次传输的地址只是为了告诉从机,主机想和从机的哪块地址进行通讯。

接下来主机发送读写信号,一般复合格式下,第一次传输都是写信号,也就是将R/W位置0;然后从机进行响应,等待非应答来临,也可以说是从机已经明确了主机的信号,知道了主机想和我的哪块地址进行通讯。

接下来进行第二次传输开始的信号,这一次主机发送的地址SLAVE_ADDRESS是从机地址,和第一次传输的地址一般是一样的,R/W位设置为1,之所以设置为1,是因为第一次主机已经告诉了从机想要和从机的哪块地址进行通信,那么第二次显然是需要由从机中读数据,那么R/W位就需要设置为1,然后数据由从机一位一位的传输至主机,直到非应答信号的来临。

P:结束传输。

3.2 通讯的起始和停止信号

通过对51单片机的学习,我们知道通讯的起始和停止都是通过时序图的高低电位来设置的。

当串行时钟总线SCL是高电平时,串行数据总线SDA从高电平向低电平切换时,表示通讯的开始。也就是起始信号。

当串行时钟总线SCL是高电平时,串行数据总线SDA从低电平向高电平切换时,表示通讯的停止。也就是停止信号。

注意:起始信号和停止信号一般由主机产生。

3.3 数据有效性

I2C使用数据总线SDA来传输数据,使用时钟总线SCL来进行数据同步。SDA在SCL的每一个时钟周期内传输一位数据。

SCL为高电平时,传输的数据SDA有效,即此时的SDA为高电平时表示数据“1”,为低电平时表示数据“0”。

当SCL为低电平时,SDA的数据无效,一般这个时候SDA在进行电平切换(也就是图中的交叉处),电平切换是为下一次表示数据做好准备。

3.4 地址及数据方向

I2C总线上每个设备都有自己独立的地址,当主机发起通讯时,主机会通过寻址的方式找到从机的地址,具体是通过SDA信号线发送设备地址(SLAVE_ADDRESS)来查找从机。设备地址可以是7位或是10位。

紧跟设备地址的一个数据位 R/W杠 是用来表示数据传输方向的,也就是主机向从机写数据,或者由从机中读数据。具体的数据方向位为“1”时表示主机由从机读数据,该位为“0”时表示主机向从机中写数据。

这也就解释了为什么设备地址可以是7位,我们通常使用的寄存器最低也要是8位,因为只用到了从机地址的高7位,最低位也就是0位是R/W,读写数据方向位。

图中MSB表示高位,LSB表示低位。

3.5 响应

I2C 的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种信号。

        数据发送端控制SDA和数据接收端控制SDA是正好相反的。传输时主机产生时钟,SLAVE_ADDRESS+R/W一般是8位,也就是需要8个SCL时钟周期。在第九个时钟时,数据发送端会释放SDA的控制权,具体是通过将时序设置为高电平,也就是我们前面介绍的高阻态,高阻态下表示从机和总线断开,从机的电压变化不再影响主机。也就是非应答信号。相应的,低电平表示应答信号。

4. STM32的I2C特性及架构

软件模拟协议:所谓软件模拟协议就是像我们之前提及的通过控制时序图产生高低电平就可以产生符合通讯协议标准的逻辑,精确的说是使用CPU直接控制通讯引脚的电平。

硬件实现协议:有STM32的I2C片上外设专门负责实现I2C通讯协议,只要配置好该外设,他就会自动根据协议要求产生通讯信号,收发数据并缓存起来,CPU只要检测该外设的状态和访问数据寄存器,就能完成数据收发。这种由硬件外设处理I2C协议的方式减轻了CPU的工作,且使软件设计更加简单。

STM32的I2C外设可用作通讯的主机及从机,支持100Kbit/s和400Kbit/s的速率,支持7位、10位设备地址,支持DMA数据传输,并且具有数据效验功能。

4.1 I2C架构剖析

5. I2C通讯过程

使用I2C外设进行通讯时,在通讯的不同阶段它会对“状态寄存器“(SR1及SR2)”的不同数据位写入参数,通过读取这些寄存器标志来了解通讯状态。

5.1 主发送器

所谓主发送器就是I2C作为主机对外发送数据的意思。

整个发送器的通讯过程可以分为上下两部分,上部分很明显就是I2C协议读写的过程,下部分可以理解为状态寄存器在主机发送命令之后所产生的状态位。我们知道STM32的通讯速率是非常快的,可达72M,如果没有状态位的响应,那么就无法保证较快的通讯速率。

首先S起始位,发送通讯开始命令,状态寄存器SR就会产生EV5事件,置位0 SB=1;表示起始位已经发送。

然后主机发送地址和应答,状态寄存器产生EV6和EV8事件,分别置ADDR=1和TxE=1;表示地址已经发送结束,并且数据寄存器为空(因为I2C是将数据寄存器复制到数据移位寄存器中,所以一旦将数据一位一位的移到数据移位寄存器后,就会将数据寄存器置空,表示可以发送下一次数据了)

然后依次发送数据、应答、发送数据、应答,直到非应答命令的到来,状态寄存器会产生EV8_2事件。表示数据字节传送成功。

最后控制寄存器发送结束命令P。

5.2 主接收器

主接收器和主发送器本质上的意义是相同的,只不过主发送器表示从主机写数据到从机,而主接收器表示由从机读数据到主机。一个是写数据,一个是读数据。

主接收器同主发送器一样,依然是主机产生起始位S,然后状态寄存器SR产生EV5、EV6、EV7、EV7_1事件,响应控制寄存器产生的状态位,其目的都是为了加强通讯双方的效率。

起始信号S是由主机端产生的(这里需要说明I2C协议规定,不管是发送数据还是接受数据起始和停止信号都是主机产生的),控制发生起始信号后,产生事件EV5,并会对SR1寄存器的SB位置1,表示起始信号已经发送;

发送设备地址并等待应答信号,若有从机应答,而产生事件EV6,这时SR1寄存器的ADDR位被置1,表示地址已经发送。

后续的过程和发送器相同。 

6. I2C库函数

I2C初始化结构体:

typedef struct { uint32_t I2C_ClockSpeed; //设置I2C时钟频率,此值要低于40 0000 uint16_t I2C_Mode; // 指定工作模式,可选为I2C模式和SMBUS模式 uint16_t I2C_DutyCycle; //指定时钟占空比,可选为low/high=2:1及16:9模式 uint16_t I2C_OwnAddress1; //指定自身I2C设备地址 uint16_t I2C_Ack; //使能或关闭应答(一般来说都是要使能的) uint16_t I2C_AcknowledgedAddress; //指定地址长度,可为7位及10位 }I2C_InitTypeDef;

①:I2C_ClockSpeed

设置I2C传输速率,在调用初始化函数时,函数会根据我们输入的数值经过运算后把时钟因子写入到 I2C 的时钟控制寄存器CCR。我们写入的这个参数值不得高于400KHz。

②:I2C_Mode

选择I2C的使用方式。可供选择的方式有I2C模式(I2C_Mode_I2C)和SMBus主、从模式(I2C_Mode_SMBusHost、I2C_Mode_SMBusDevice)。

③:I2C_DutyCycle

设置I2C的SCL线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平时间为2:1(I2C_DutyCycle_2)和16:9(I2C_DutyCycle_16_9)

④:I2C_OwnAddress1

配置STM32F4的I2C设备自己的地址,每个连接到I2C总线上的设备都要有一个自己的地址,作为主机当然也不例外,在上面讲过的读写程序中,开启读写命令S后,就需要写入从机地址进行应答。地址可以设置为7位或者10位(受下面I2C_AcknowledgeAddress成员决定),只要该地址是I2C总线上唯一的即可。

⑤:I2C_Ack

配置I2C应答是否使能,设置为使能则可以发送响应信号。一般情况下配置为允许应答(I2C_Ack_Enable),这是绝大多数遵循I2C标准的设备的通讯要求,倘若要使用I2C,可还是将该位设置为I2C_Ack_Disable往往会导致通讯错误。

⑥:I2C_AcknowledgedAddress

选择I2C的寻址模式是7位还是10位地址。这需要根据I2C实际连接的从机设备的地址进行选择,比方说,I2C要与EEPROM通讯,I2C设置地址为7位,那么EEPROM地址也要是7位。这个成员的配置也影响到I2C_OwnAddress1成员,只有这里设置成10位模式时,I2C_OwnAddress1才支持10位地址。

7. EEPROM简介

EEPROM全称是:electrically erasable and programmable read-only memory  (电擦除的只读存储器)

EEPROM引脚6和7连接总线SCL和SDA,直接和芯片引脚相接,接上拉电阻4.7K,外接电源3.3V。

24C02是芯片的型号,全称是AT24C02芯片。

封装:

A0-A2:地址输入

SCL:串行时钟线

SDA:串行数据线

WP:写保护(上图原理图中WP是直接接地的,也就是低电平,没有开启写保护,如果将其置为高电平,就意味着开启了写保护,就不能再对EEPROM进行写数据)

前四个位是固定的,1010 A2 A1 A0需要根据引脚来配置,R/W用来设置写保护。 

8. 初始化I2C         初始化I2C相关的GPIO        配置I2C外设的工作模式        编写I2C写入EEPROM的Byte write函数        编写I2C读取EEPROM和Random Read函数        使用read函数及write函数进行读写校验        编写page write函数及write函数进行读写校验

注意:Byte write以及page write都是AT24C02芯片中的读写过程函数。

9. MPU-6050简介

        MPU-60X0是全球首例 9 轴运动处理传感器。它集成了3轴MEMS陀螺仪,3轴MEMS加速度计,以及一个可拓展的数字运动处理器DMP(Digital Motion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。扩展之后就可以通过其I2C或SPI接口输出一个9轴的信号(SPI接口仅在MPU-6000可用)。MPU-60X0也可以通过其I2C接口连接非惯性的数字传感器,比如压力传感器。

MPU-6050是一个6轴姿态传感器,可以测量芯片自身X、Y、Z轴的加速度、角速度等参数,通过数据融合,可进一步得到姿态角,常应用于平衡车、飞行器等需要检测自身姿态的场景。

3轴加速度计:测量X、Y、Z轴的加速度

3轴陀螺仪传感器:测量X、Y、Z轴的角速度

既然说了MPU-6050可以用来测量加速度和角速度这些模拟量,那么必定有ADC参与其中,进行模拟转数字的操作。

I2C从机地址:1101000(AD0=0)

                        1101001(AD0=1)

MPU-6050也可以通过INT引脚产生中断。

10. 软件读写MPU-6050 10.1 I2C时序基本单元

起始条件:

SCL高电平期间,SDA从高电平切换到低电平

终止条件:

SCL高电平期间,SDA从低电平切换到高电平

发送一个字节:

SCL低电平期间,主机将数据依次放到SDA上(高位先行),然后释放SCL,从机将SCL在高电平期间读取数据位,SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节。

接收一个字节:

SCL低电平期间,从机将数据依次放到SDA上(高位先行),然后释放SCL,主机将在SCL高电平期间读取数据位,SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

发送应答:

主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答。

接收应答:

主机在发送完一个字节后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA)

10.2 实验程序 10.2.1 main.c #include "stm32f4xx.h" #include "delay.h" #include "usart.h" #include "LED.h" #include "lcd.h" #include "usmart.h" #include "I2C.h" #include "MPU6050.h" //LCD状态设置函数 void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数 { LED1=sta; } //函数参数调用测试函数 void test_fun(void(*ledset)(u8),u8 sta) { led_set(sta); } uint8_t ID; int16_t AX,AY,AZ,GX,GY,GZ; int main(void) { delay_init(168); uart_init(115200); LCD_Init(); MPU6050_Init(); while(1) { MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); POINT_COLOR=RED; LCD_ShowxNum(30,50,AX,5,16,0); LCD_ShowxNum(30,70,AY,5,16,0); LCD_ShowxNum(30,90,AZ,5,16,0); LCD_ShowxNum(30,110,GX,5,16,0); LCD_ShowxNum(30,130,GY,5,16,0); LCD_ShowxNum(30,150,GZ,5,16,0); } } 10.2.2 I2C.c #include "stm32f4xx.h" #include "I2C.h" #include "delay.h" #define SCL GPIO_Pin_8 #define SDA GPIO_Pin_9 void MyI2C_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType=GPIO_OType_OD; GPIO_Init(GPIOB,&GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_8|GPIO_Pin_9);//初始化引脚设置为高电平,释放引脚,I2C处于空闲状态 } void MyI2C_Start(void) { //SCL高电平期间,SDA从高电平切换到低电平 GPIO_SetBits(GPIOB,SCL|SDA);//引脚置高电平 delay_ms(10); GPIO_ResetBits(GPIOB,SDA);//SDA置低电平 delay_ms(10); GPIO_ResetBits(GPIOB,SCL);//SCL置低电平 delay_ms(10); } void MyI2C_Stop(void) { GPIO_ResetBits(GPIOB,SDA); //SDA低电平 delay_ms(10); GPIO_SetBits(GPIOB,SCL); //SCL高电平期间,SDA 由低电平变为高电平表示终止信号 delay_ms(10); GPIO_SetBits(GPIOB,SDA); //SDA 高电平 delay_ms(10); } void MyI2C_SendByte(uint8_t Byte)//发送一个字节 ,高位在前 { uint8_t i; for(i=0;i>i));//写字节将数据写到SDA串行数据总线 delay_ms(10); //将数据写入到串行数据总线上以后,释放SCL,从机会在SCL高电平期间读取数据,SCL高电平期间SDA不允许有数据变化 GPIO_SetBits(GPIOB,SCL); delay_ms(10); GPIO_ResetBits(GPIOB,SCL);//SCL由高变低表示发送完成一位 delay_ms(10); //通过for循环重复上述过程8次即可发送完成一个字节 } } uint8_t MyI2C_ReceiveByte(void)//接收一个字节 {//首先需要明确,接收字节一定是数据寄存器中有数据,主机才可以接收数据 GPIO_SetBits(GPIOB,SDA);//SDA设置为高电平是为了读取数据时判断的 delay_ms(10); uint8_t i,Byte=0x00; for(i=0;i>i);}//读取SDA数据位,因为每一次进入MyI2C_ReceiveByte时都先将SDA置为了高电流,所以条件语句成立,通过或运算将8位依次赋值给变量Byte delay_ms(10); GPIO_ResetBits(GPIOB,SCL); //SCL置低电平,为传输下一位做准备 delay_ms(10); //重复上述操作8次,即可完整接收一个字节 } return Byte; } void MyI2C_SendAck(uint8_t AckBit)//发送一个应答 { GPIO_WriteBit(GPIOB,SDA,AckBit);//往串行数据总线SDA最低位写应答,0表示应答,1表示非应答 delay_ms(10); GPIO_SetBits(GPIOB,SCL);//SCL由高电平变为低电平 delay_ms(10); GPIO_ResetBits(GPIOB,SCL); delay_ms(10); } uint8_t MyI2C_ReceiveAck(void)//接收一个应答 { uint8_t AckBit; GPIO_SetBits(GPIOB,SDA); delay_ms(10); GPIO_SetBits(GPIOB,SCL); delay_ms(10); AckBit=GPIO_ReadInputDataBit(GPIOB,SDA); //读取数据总线SDA的数据位 delay_ms(10); GPIO_ResetBits(GPIOB,SCL); //SCL由高电平变为低电平 delay_ms(10); return AckBit; } 10.2.3 I2C.h #ifndef _I2C__H_ #define _I2C__H_ void MyI2C_Init(void); void MyI2C_Start(void); void MyI2C_Stop(void); void MyI2C_SendByte(uint8_t Byte); uint8_t MyI2C_ReceiveByte(void); void MyI2C_SendAck(uint8_t AckBit); uint8_t MyI2C_ReceiveAck(void); #endif 10.2.4 MPU6050.c #include "stm32f4xx.h" #include "MPU6050.h" #include "I2C.h" #include "MPU6050_Reg.h" #define MPU6050_ADDRESS 0xD0 void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data) //写一个字节的时序,整个过程和流程图是一模一样的 { MyI2C_Start(); //发送起始信号 MyI2C_SendByte(MPU6050_ADDRESS); //发送MPU6050写地址 MyI2C_ReceiveAck();//主机接收从机的应答,表示从机响应主机,可以开始通信了 MyI2C_SendByte(RegAddress);//发送从机寄存器地址,表示主机想要和从机的哪块地址进行通讯 MyI2C_ReceiveAck(); MyI2C_SendByte(Data); //发送数据 MyI2C_ReceiveAck(); MyI2C_Stop(); } uint8_t MPU6050_ReadReg(uint8_t RegAddress)//读一个字节的时序 { //整个过程是复合时序,所有对应博客中的内容,有两个起始 uint8_t Data; MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS); MyI2C_ReceiveAck(); MyI2C_SendByte(RegAddress); MyI2C_ReceiveAck();//以上述和写是一样的,因为不管是读还是写,一开始都需要主机进行以上的操作 MyI2C_Start(); MyI2C_SendByte(MPU6050_ADDRESS|0x01);//因为0xD0是MU6050的写地址,或上0x01变为0xD1,读地址 MyI2C_ReceiveAck();//告诉主机要开始读了 Data=MyI2C_ReceiveByte();//把接收到的字节给到数据位 MyI2C_SendAck(1);//因为是只发送一个字节,所有发送应答为1,就是非应答,如果需要接着发,该位设置为0 MyI2C_Stop(); return Data; } void MPU6050_Init(void) { MyI2C_Init(); MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); MPU6050_WriteReg(MPU6050_CONFIG, 0x06); MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); MPU6050_WriteReg(MPU6050_ACCEL_CONFIG, 0x18); } void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ) { uint8_t DataH, DataL; DataH = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); DataL = MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); *AccX = (DataH


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3