当前位置: 首页 > news >正文

idea做网站北京市住房与城乡建设部网站

idea做网站,北京市住房与城乡建设部网站,那个网站科四做课时,手游推广平台代理本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. I2C通信1.1 I2C通信简介1.2 硬件电路1.3 I2C时序基本单元1.3.1 起始条件和终止条件1.3.2 发送一个字节1.3.3 接收一个字节1.3.4 发送应答和接收应答 1.4 I2C时序1.4.1 指定地址写1.4.2 当前地址读1.4.3 指定地址读…本内容基于江协科技STM32视频学习之后整理而得。 文章目录 1. I2C通信1.1 I2C通信简介1.2 硬件电路1.3 I2C时序基本单元1.3.1 起始条件和终止条件1.3.2 发送一个字节1.3.3 接收一个字节1.3.4 发送应答和接收应答 1.4 I2C时序1.4.1 指定地址写1.4.2 当前地址读1.4.3 指定地址读 2. MPU60502.1 MPU6050简介2.2 MPU6050参数2.3 硬件电路2.4 MPU6050框图 3. 10-1软件I2C读写MPU60503.1 硬件连接3.2 运行结果3.3 代码流程3.4 代码 4. I2C外设4.1 I2C外设简介4.2 I2C框图4.3 I2C基本结构4.4 主机发送4.5 主机接收4.6 软件/硬件波形对比 5. 10-2 硬件I2C读写MPU60505.1 I2C库函数5.2 硬件I2C读写MPU6050实现5.2.1 硬件连接5.2.2 运行结果5.2.3 代码实现流程5.2.4 代码 1. I2C通信 1.1 I2C通信简介 I2CInter IC Bus是由Philips公司开发的一种通用数据总线两根通信线SCLSerial Clock串行时钟线、SDASerial Data串行数据线同步半双工单端多设备带数据应答支持总线挂载多设备一主多从、多主多从 一主多从单片机作为主机主导I2C总线的运行挂载在I2C总线的所有外部模块都是从机从机只有被主机点名后才能控制I2C总线不能在未经允许的情况下去碰I2C总线防止冲突。多主多从在总线上任何一个模块都可以主动跳出来当主机。当总线冲突时I2C协议会进行仲裁仲裁胜利的一方取得总线控制权失败的一方自动变回从机。 1.2 硬件电路 所有I2C设备的SCL连在一起SDA连在一起设备的SCL和SDA均要配置成开漏输出模式SCL和SDA各添加一个上拉电阻阻值一般为4.7KΩ左右 一主多从CPU是单片机作为总线的主机包括对SCL线的完全控制任何时候都是主机完全掌控SCL线。另外在空闲状态下主机可以主动发起对SDA的控制只有在从机发送数据和从机应答的时候主机才会转交SDA的控制权给从机这是主机的权力。被控IC是挂载在I2C总线上的从机可以是姿态传感器、OLED、存储器、时钟模块等。从机的权力比较小对于SCL时钟线在任何时刻都只能被动的读取从机不允许控制SCL线。对于SDA数据线从机不允许主动发起对SDA的控制。只有在主机发送读取从机的命令后或者从机应答的时候从机才能短暂地取得SDA的控制权。图2左边是SCL右边是SDA。所有的数据进来都可以通过一个数据缓冲器或者是施密特触发器进行输入。 因为输入对电路没有任何影响所以任何设备在任何时刻都是可以输入的。输出采用的是开漏输出的配置输出低电平开关管导通引脚直接接地是强下拉输出高电平开关管断开引脚什么都不接处于浮空状态这样所有的设备都只能输出低电平而不能输出高电平为了避免高电平造成的浮空就需要在总线外面SCL和SDA各外置一个上拉电阻是通过一个电阻拉到高电平的所以是一个弱上拉。这样第一完全杜绝了电源短路现象保证电路的安全第二避免了引脚模式的频繁切换。开漏模式下输出高电平就相当于断开引脚所以在输入之前可以直接输出高电平。第三该模式有一个“线与”现象只要有任意一个或多个设备输出了低电平总线就处于低电平只有所有的设备都输出高电平总线才处于高电平。因此I2C可以利用该现象执行多主机模式下的时钟同步和总线仲裁。所以这里SCL虽然在一主多从模式下可以用推挽输出但仍然采用了开漏加上拉输出的模式 1.3 I2C时序基本单元 1.3.1 起始条件和终止条件 起始条件SCL高电平期间SDA从高电平切换到低电平终止条件SCL高电平期间SDA从低电平切换到高电平 起始条件状态下在I2C总线处于空闲状态时SCL和SDA都处于高电平状态也就是没有任何一个设备去碰SCL和SDASCL和SDA由外挂的上拉电阻拉高至高电平总线处于平静的高电平状态。当主机需要进行数据收发时首先就要打破总线的宁静产生一个起始条件就是SCL处于高电平不去动它然后把SDA拽下来产生一个下降沿。当从机捕获到SCL高电平、SDA下降沿信号时就会进行自身的复位等待主机的召唤。在SDA下降沿之后主机要再把SCL拽下来拽下SCL一方面是占用这个总线另一方面也是为了方便基本单元的拼接。就是之后会保证除了起始和终止条件每个时序单元的SCL都是以低电平开始低电平结束。终止条件状态下SCL先放手回弹到高电平SDA再放手回弹高电平产生一个上升沿这个上升沿触发终止条件。同时终止条件之后SCL和SDA都是高电平回归到最初的平静状态。 起始和终止都是由主机产生的从机不允许产生起始和终止。所以在总线空闲状态时从机必须始终双手放开不允许主动跳出来去碰总线。 1.3.2 发送一个字节 发送一个字节SCL低电平期间主机将数据位依次放到SDA线上高位先行然后释放SCL从机将在SCL高电平期间读取数据位所以SCL高电平期间SDA不允许有数据变化依次循环上述过程8次即可发送一个字节。 低电平主机放数据高电平从机读数据 起始条件之后第一个字节也必须是主机发送的。SCL低电平主机想发送0就拉低SDA到低电平如果想发送1就放手SDA回弹到高电平。在SCL低电平期间允许改变SDA的电平当放好数据之后主机就松手时钟线SCL回弹到高电平。在高电平期间是从机读取SDA的时候所以在高电平期间SDA不允许变化。SCL处于高电平之后从机需要尽快地读取SDA一般都是在SCL上升沿这个时刻从机就已经读取完成了。因为时钟是主机控制的从机并不知道什么时候产生下降沿因此在SCL上升沿时从机就会把数据读走。当主机在放手SCL一段时间后就可以继续拉低SCL传输下一位了。主机也需要在SCL下降沿之后尽快把数据放到SDA上。但主机有时钟的主导权所以只需要在低电平的任意时刻把数据放在SDA上就可以了。数据放完之后主机再松手SCLSCL高电平从机读取这一位。循环该流程主机拉低SCL把数据放到SDA上主机松开SCL从机读取SDA数据。在SCL的同步下依次进行主机发送和从机接收循环8次就发送了8位数据也就是一个字节。 由于是高位先行所以第一位是一个字节的最高位B7最后发送最低位B0 1.3.3 接收一个字节 接收一个字节SCL低电平期间从机将数据位依次放到SDA线上高位先行然后释放SCL主机将在SCL高电平期间读取数据位所以SCL高电平期间SDA不允许有数据变化依次循环上述过程8次即可接收一个字节主机在接收之前需要释放SDA 低电平从机放数据高电平主机读数据 SDA线主机在接收之前要释放SDA这时从机获得SDA的控制权从机需要发送0就把SDA拉低从机需要发送1就放手SDA回弹高电平。低电平变换数据高电平读取数据。实线表示主机控制的电平虚线表示从机控制的电平。SCL全程由主机控制SDA主机在接收前要释放交由从机控制。因为SCL时钟是由主机控制的所以从机的数据变换基本上都是贴着SCL下降沿进行的而主机可以在SCL高电平的任意时刻读取。 1.3.4 发送应答和接收应答 发送应答主机在接收完一个字节之后在下一个时钟发送一位数据数据0表示应答数据1表示非应答接收应答主机在发送完一个字节之后在下一个时钟接收一位数据判断从机是否应答数据0表示应答数据1表示非应答主机在接收之前需要释放SDA 就是在调用发送一个字节的时序之后就要紧跟着调用接收应答的时序用来判断从机有没有收到刚才给它的数据。如果从机收到了那在应答位这里主机释放SDA的时候从机就应该立刻把SDA拉下来然后在SCL高电平期间主机读取应答位。如果应答位为0就说明从机确实收到了。 在接收一个字节时候需要调用发送应答。发送应答的目的是告诉从机你是不是要继续发。如果从机发送一个数据后得到了主机的应答那从机就还会继续发送如果从机没有得到主机的应答那从机就会认为发送了一个数据但主机不理我可能主机不想要吧这时从机就是乖乖地释放SDA交出SDA的控制权防止干扰主机之后的操作。 1.4 I2C时序 1.4.1 指定地址写 指定地址写对于指定设备Slave Address在指定地址Reg Address即指定设备的寄存器地址下写入指定数据Data 流程 1起始条件 2发送一个字节时序—0xD0从机地址(7bit) 写(1bit)-0(1101 0000) 3接收应答RA 0接收从机的应答 4指定地址0x190001 1001 5接收应答RA 0接收从机的应答 6写入指定数据0xAA1010 1010 7接收应答RA 0 8停止位P终止条件 在起始条件之后必须是发送一个字节的时序字节的内容必须是从机地址读写位从机地址是7位读写位是1位正好是8位。发送从机地址就是确定通信的对象发送读写位是确认接下来是要写入还是要读出。现在就是主机发送了一个数据字节的内容转换为16进制高位先行就是0xD0紧跟着的单元就是接收从机的应答位(RA)第8位读写位结束SCL拉低之后主机要释放SDA然后就是应答位RA。在应答位RA结束后的高电平是从机释放SDA产生的从机交出了SDA的控制权因为从机要在SCL低电平尽快交换数据所以SDA的上升沿和SCL的下降沿几乎是同时发生的。在应答结束后要继续发送一个字节第二个字节就可以送到指定设备的内部了从机设备可以自己定义第二个自己和后续字节的用途。一般第二个字节可以是寄存器地址或者是指令控制字等第三个字节是主机想要写入到寄存器地址第二个字节下的内容。P是停止位。 该数据帧的目的是对于指定从机地址为1101000的设备在其内部0x19地址的寄存器中写入0xAA这个数据。 0表示之后的时序主机要进行写入操作 1表示之后的时序主机要进行读出操作 1.4.2 当前地址读 当前地址读对于指定设备Slave Address在当前地址指针指示的地址下读取从机数据Data 流程 1起始条件 2发送一个字节时序—0xD1从机地址(7bit) 读(1bit)-1(1101 0001) 3接收应答RA 0接收从机的应答 4读从机数据0x0F0000 1111 7发送应答SA 0 8停止位P终止条件 读写位是1表示接下来要进行读出的操作。在从机应答之后RA0数据的传输方向就要反过来了。主机要把SDA的控制权交给从机主机调用接收一个字节的时序进行接收操作。在第二个字节中从机就得到了主机的允许可以在SCL低电平期间写入SCL主机在SCL高电平期间读取SDA最终主机在SCL高电平期间依次读取8位就接收到了从机发送的一个字节数据即0x0F。但0x0F是从机哪个寄存器的数据呢。在读的时序中I2C的协议规定是主机进行寻址时一旦读写标志位给1了。下一个字节就要立马转为读的时序。所以主机还来不及指定想要读哪个寄存器就要开始接收了所以这里没有指定地址这个环节。在从机中所有的寄存器被分配到了一个线性区域中并且会有一个单独的指针变量指示着其中一个寄存器这个指针上电默认一般指向0地址并且每写入一个字节和读出一个字节后这个指针就会自动自增一次移动到下一个位置那么在调用当前地址读的时序时主机没有指定要读哪个地址从机就会返回当前指针指向的寄存器的值。 1.4.3 指定地址读 指定地址读对于指定设备Slave Address在指定地址Reg Address下读取从机数据Data 先起始、再重复起始、再停止 流程 1起始条件 2发送一个字节时序—0xD0从机地址(7bit) 写(1bit)-0(1101 0000) 3接收应答RA 0接收从机的应答 4指定地址0x190001 1001 5接收应答RA 0接收从机的应答 6重复起始条件 7发送一个字节时序—0xD1从机地址(7bit) 读(1bit)-1(1101 0001) 8接收应答RA 0 9读取从机数据0xAA1010 1010 10发送应答SA 0 11停止位P终止条件 前面部分是指定地址写但是只指定了地址还没来得及写后面部分是当前地址读因为刚指定了地址所以再调用当前地址读。指定从机地址是1101000读写标志位是0进行写操作经过从机应答后再写入一个字节第二个字节用于指定地址0x19就写入到了从机的地址指针里了也就是说从机接收到该数据后它的寄存器指针就指向了0x19这个位置。Sr是重复起始条件相当于另起一个时序因为指定读写标志位只能跟着起始条件的第一个字节所以想切换读写方向只能再来个起始条件。然后起始条件后重新寻址并且指定读写标志位此时读写标志位是1表示要读接着主机接收一个字节该字节是0x19地址下的数据0xAA。 2. MPU6050 2.1 MPU6050简介 MPU6050是一个6轴姿态传感器可以测量芯片自身X、Y、Z轴的加速度、角速度参数通过数据融合可进一步得到姿态角欧拉角常应用于平衡车、飞行器等需要检测自身姿态的场景3轴加速度计Accelerometer测量X、Y、Z轴的加速度3轴陀螺仪传感器Gyroscope测量X、Y、Z轴的角速度 以飞机机身为例欧拉角就是飞机机身相对于初始3个轴的夹角 飞机机头下倾或上仰这个轴的夹角叫做俯仰Pitch飞机机身左翻滚或右翻滚这个轴的夹角叫做滚转Roll飞机机身保持水平机头向左转向或向右转向这个轴的夹角叫做偏航Yaw。欧拉角就是表示了飞机此时的姿态是上仰了还是下倾了向左倾斜还是向右倾斜。 常见的数据融合算法一般有互补滤波、卡尔曼滤波等惯性导航里的姿态解算。加速度计中间的虚线是感应轴线中间是一个具有一定质量、可以左右滑动的小滑块左右各有一个弹簧顶着它。当滑块移动时就会带动它上面的电位计移动这个电位计就是一个分压电阻测量电位计输出的电压就能得到小滑块所受的加速度值了。这个加速度计实际上就是一个弹簧测力计根据牛顿第二定律F ma想测量这个加速度a就可以找一个单位质量的物体测量所受的力F就行了。在X、Y、Z轴分别都有一个加速度计。加速度计具有静态稳定性不具有动态稳定性。陀螺仪传感器中间是一个具有一定质量的旋转轮当旋转轮高速旋转时根据角动量守恒的原理这个旋转轮具有保持它原有角动量的趋势这个趋势可以保持旋转轴方向不变。当外部物体的方向转动时内部的旋转轴方向并不会转动这就会在平衡环连接处产生角度偏差。如果在连接处放一个旋转的电位器测量电位器的电压就能得到旋转的角度了。陀螺仪应该是可以直接得到角度的但这个MPU6050的陀螺仪并不能直接测量角度它是测量角速度即芯片绕X轴、Y轴和绕Z轴旋转的角速度。角速度积分就是角度但是当物体静止时角速度值会因为噪声无法完全归零然后经过积分的不断累积这个小噪声就会导致计算出来的角度产生缓慢的漂移也就是角速度积分得到的角度经不起时间的考验但这个角度无论是静止还是运动都是没有问题的不会受物体运动的影响。陀螺仪具有动态稳定性不具有静态稳定性。根据加速度计具有静态稳定性不具有动态稳定性陀螺仪具有动态稳定性不具有静态稳定性这两种特性所以取长补短进行一下互补滤波就能融合得到静态和动态都稳定的姿态角了。 2.2 MPU6050参数 16位ADC采集传感器的模拟信号量化范围-32768~32767加速度计满量程选择±2、±4、±8、±16g1g 9.8m/s2陀螺仪满量程选择 ±250、±500、±1000、±2000°/sec度/秒角速度单位每秒旋转了多少度满量程选的越大测量范围就越广满量程选的越小测量分辨率越高可配置的数字低通滤波器可以配置寄存器来选择对输出数据进行低通滤波。可配置的时钟源可配置的采样分频时钟源通过分频器的分频可以为AD转换和内部其他电路提供时钟。控制分频系数就可以控制AD转换的快慢了。I2C从机地址1101000AD00 或 1101001AD01 110 1000转换为十六进制就是0x68所以有的说MPU6050的从机地址是0x68。但在I2C通信里第一个字节的高7位是从机地址最低位是读写位所以如果认为0x68是从机地址的话在发送第一个字节时要先把0x68左移1位(0x68 1)再按位或上读写位读1写0。还有一种就是把0x68左移1位(0x68 1)后的数据当作从机地址就是0xD0那这样MPU6050的从机地址就是0xD0。这时在实际发送第一个字节时如果你要写就直接把0xD0当作第一个字节如果你要读就把0xD0或上0x01(0xD0 | 0x01)即0xD1当作第一个字节。这种表示方式就不需要左移的操作了或者说这种表示方式是把读写位也融入到从机地址里了。0xD0是写地址0xD1是读地址。 2.3 硬件电路 引脚功能VCC、GND电源SCL、SDAI2C通信引脚XCL、XDA主机I2C通信引脚AD0从机地址最低位INT中断信号输出 LDO低压差线性稳压器3.3V稳压。SCL和SDA是I2C通信的引脚模块已经内置了两个4.7K的上拉电阻所以接线的时候直接把SDA和SCL接在GPIO口上就行了不需要再外接上拉电阻了。XCL、XDA主机I2C通信引脚设计这两个引脚是为了扩展芯片功能。通常用于外接磁力计或者气压计当接上这些扩展芯片时MPU6050的主机接口就可以直接访问这些扩展芯片的数据把这些扩展芯片的数据读取到MPU6050里MPU6050里有DMP单元进行数据融合和姿态解算。 AD0引脚是从机地址的最低位接低电平的话7位从机地址是1101000接高电平的话7位从机地址就是1101001。电路图中有一个电阻默认弱下拉到低电平了所以引脚悬空的话就是低电平如果想接高电平可以把AD0直接引到VCC强上拉至高电平。INT中断输出引脚可以配置芯片内部的一些事件来触发中断引脚的输出如数据准备好了、I2C主机错误等。芯片内部还内置了自由落体检测、运动检测、零运动检测等。这些信号都可以触发INT引脚产生电平跳变需要的话可以进行中断信号的配置。MPU6050芯片的供电是2.375-3.46V属于3.3V供电的设备不能直接接5V。因此加了3.3V的稳压器输入端电压VCC_5V可以在3.3V~5V之间然后经过3.3V的稳压器输出稳定的3.3V电压给芯片端供电只要3.3V端有电电源指示灯就会亮。 2.4 MPU6050框图 CLKIN和CLKOUT是时钟输入引脚和时钟输出引脚但我们一般使用内部时钟。灰色部分是芯片内部的传感器XYZ轴的加速度计XYZ轴的陀螺仪。还内置了一个温度传感器可以用来测量温度。这些传感器本质上相当于可变电阻通过分压后输出模拟电压然后通过ADC进行模数转换转换完成之后这些传感器的数据统一都放到数据寄存器中读取数据寄存器就能得到传感器测量的值了。这个芯片内部的转换都是全自动进行的。每个传感器都有个自测单元这部分是用来验证芯片好坏的当启动自测后芯片内部会模拟一个外力施加在传感器上这个外力导致传感器数据会比平时大一些。自测流程可以先使能自测读取数据再使能自测读取数据两个数据一相减得到的数据叫自测响应。对于这个自测响应手册里给了一个范围如果在这个范围内就说明芯片没问题。Charge Pump是电荷泵或者充电泵电荷泵是一种升压电路。CPOUT引脚需要外接一个电容。中断状态寄存器可以控制内部的哪些事件到中断引脚的输出FIFO先入先出寄存器可以对数据流进行缓存配置寄存器可以对内部的各个电路进行配置传感器寄存器即数据寄存器存储了各个传感器的数据工厂校准意思是内部的传感器都进行了校准。数字运动处理器简称DMP是芯片内部自带的一个姿态解算的硬件算法配合官方的DMP库可以进行姿态解算。FSYNC帧同步。 3. 10-1软件I2C读写MPU6050 3.1 硬件连接 通过软件I2C通信对MPU6050芯片内部的寄存器进行读写写入到配置寄存器就可以对外挂的这个模块进行配置读出数据寄存器就可以获取外挂模块的数据读出的数据会显示在OLED上最上面的数据是设备的ID号这个MPU6050的ID号固定为0x68。下面的左边3个是加速度传感器的输出数据分别是X轴、Y轴、Z轴的加速度右边3个是陀螺仪传感器的输出数据分别是X轴、Y轴、Z轴的角速度。 SCL接到STM32的PB10引脚SDA接到PB11引脚。这里由于是软件翻转电平实现所以可以任意连接两个GPIO口即可。 3.2 运行结果 3.3 代码流程 STM32是主机MPU6050是从机是一主一从模式。 建立I2C通信层的.c和.h模块 写好I2C底层的GPIO初始化6个时序基本单元起始、终止、发送一个字节、接收一个字节、发送应答、接收应答 建立MPU6050的.c和.h模块 基于I2C通信的模块实现指定地址读、指定地址写、再实现写寄存器对芯片进行配置、读寄存器得到传感器数据 main.c 调用MPU6050模块初始化拿到数据显示数据 3.4 代码 I2C代码 #include stm32f10x.h // Device header #include Delay.hvoid MyI2C_W_SCL(uint8_t BitValue) {GPIO_WriteBit(GPIOB, GPIO_Pin_10,(BitAction)BitValue);Delay_us(10); }void MyI2C_W_SDA(uint8_t BitValue) {GPIO_WriteBit(GPIOB, GPIO_Pin_11,(BitAction)BitValue);Delay_us(10); }uint8_t MyI2C_R_SDA(void) {uint8_t BitValue;BitValue GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);Delay_us(10);return BitValue; }void MyI2C_Init(void) { /* 软件I2C初始化1. 把SCL和SDA都初始化为开漏输出模式2. 把SCL和SDA置高电平 输入时先输出1再直接读取输入数据寄存器就行了 初始化结束后调用SetBits把GPIOB的Pin_10和Pin_11都置高电平 此时I2C总线处于空闲状态 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB, GPIO_InitStructure);GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);}/* 起始条件SCL高电平期间SDA从高电平切换到低电平。 如果起始条件之前SDA和SCL都已经是高电平了那先释放哪一个是一样的效果。 但是这个Start还要兼容重复起始条件SrSr最开始SCL是低电平SDA电平不敢确定 所以为保险起见在SCL低电平时先确保释放SDA再释放SCL。 这时SDA和SCL都是高电平然后再拉低SDA、拉低SCL。 这样这个Start就可以兼容起始条件和重复起始条件了。 */ void MyI2C_Start(void) {MyI2C_W_SDA(1);MyI2C_W_SCL(1);MyI2C_W_SDA(0);MyI2C_W_SCL(0); }/* 终止条件SCL高电平期间SDA从低电平切换到高电平 如果Stop开始时SCL和SDA都已经是低电平了那就先释放SCL再释放SDA。 但在这个时序单元开始时SDA并不一定是低电平所以为了确保之后释放 SDA能产生上升沿要在时序单元开始时先拉低SDA然后再释放SCL、释放SDA。 */ void MyI2C_Stop(void)// 终止条件 {MyI2C_W_SDA(0);MyI2C_W_SCL(1);MyI2C_W_SDA(1); }/* 发送一个字节发送一个字节时序开始时SCL是低电平。 除了终止条件SCL以高电平结束所有的单元都会保证SCL以低电平结束。 SCL低电平变换数据高电平保持数据稳定。由于是高位先行所以变换数据的时候 按照先放最高位再放次高位...最后最低位的顺序依次把每一个字节的每一位放在SDA线上 每放完一位后执行释放SCL拉低SCL的操作驱动时钟运转。 程序趁SCL低电平先把Byte的最高位放在SDA线上 */void MyI2C_SendByte(uint8_t Byte) // 发送一个字节 {uint8_t i;for (i 0; i 8; i ){MyI2C_W_SDA(Byte (0x80 i));// 右移i位MyI2C_W_SCL(1);MyI2C_W_SCL(0);} }/* 接收一个字节时序开始时SCL低电平此时从机需要把数据放到SDA上 为了防止主机干扰从机写入数据主机需要先释放SDA释放SDA相当于切换为输入模式 那在SCL低电平时从机会把数据放到SDA上如果从机想发1就释放SDA想发0就拉低SDA 主机释放SCL在SCL高电平期间读取SDA再拉低SCL低电平期间从机就会把下一位数据放到SDA上重复8次 主机就能读到一个字节了。 SCL低电平变换数据高电平读取数据实际上是一种读写分离的操作低电平时间定义为写的时间高电平时间定义为读的时间*/ uint8_t MyI2C_ReceiveByte(void) // 接收一个字节 {uint8_t i, Byte 0x00;MyI2C_W_SDA(1);for (i 0; i 8; i ){MyI2C_W_SCL(1); // 主机读取数据if (MyI2C_R_SDA() 1) // 如果if成立接收的这一位为1,{Byte | (0x80 i); // 最高位置1}MyI2C_W_SCL(0); }return Byte; } /* 问题反复读取SDAfor循环中又没写过SDA那SDA读出来应该始终是一个值啊 回答I2C是在进行通信通信是有从机的当主机不断驱动SCL时钟时 从机就有义务去改变SDA的电平所以主机每次循环读取SDA的时候 这个读取到的数据是从机控制的这个数据也正是从机想要给我们发送的数据 所以这个时序叫做接收一个字节。 */void MyI2C_SendAck(uint8_t AckBit) // 发送应答 {// 函数进来SCL低电平主机把AckBit放到SDA上MyI2C_W_SDA(AckBit);MyI2C_W_SCL(1); // 从机读取应答MyI2C_W_SCL(0); // 进入下一个时序单元}uint8_t MyI2C_ReceiveAck(void) // 接收应答 {// 函数进来SCL低电平主机释放SDA防止从机干扰uint8_t AckBit;MyI2C_W_SDA(1); // 主机释放SDAMyI2C_W_SCL(1); // SCL高电平主机读取应答位AckBit MyI2C_R_SDA(); MyI2C_W_SCL(0); // SCL低电平进入下一个时序单元return AckBit; }/*问题在程序里主机先把SDA置1了然后再读取SDA 这应答位肯定是1啊 回答第一I2C的引脚是开漏输出弱上拉的配置主机输出1 并不是强制SDA为高电平而是释放SDA 第二I2C是在通信主机释放了SDA从机是有义务在此时把SDA再拉低的 所以即使主机把SDA置1了之后再读取SDA读到的值也可能是0 读到0代表从机给了应答读到1代表从机没给应答这就是接收应答的流程。*/ MPU6050代码 #ifndef __MPU6050_REG_H #define __MPU6050_REG_H// 宏定义: 寄存器的名称 对应的地址#define MPU6050_SMPLRT_DIV 0x19 // 采样率分频 #define MPU6050_CONFIG 0x1A // 配置外部帧同步(FSYNC)引脚采样和数字低通滤波器(DLPF)设置 #define MPU6050_GYRO_CONFIG 0x1B // 触发陀螺仪自检和配置满量程 #define MPU6050_ACCEL_CONFIG 0x1C // 触发加速度计自检和配置满量程#define MPU6050_ACCEL_XOUT_H 0x3B // 存储最新的加速度计测量值 #define MPU6050_ACCEL_XOUT_L 0x3C #define MPU6050_ACCEL_YOUT_H 0x3D #define MPU6050_ACCEL_YOUT_L 0x3E #define MPU6050_ACCEL_ZOUT_H 0x3F #define MPU6050_ACCEL_ZOUT_L 0x40 #define MPU6050_TEMP_OUT_H 0x41 // 存储最新的温度传感器测量值 #define MPU6050_TEMP_OUT_L 0x42 #define MPU6050_GYRO_XOUT_H 0x43 // 存储最新的陀螺仪测量值 #define MPU6050_GYRO_XOUT_L 0x44 #define MPU6050_GYRO_YOUT_H 0x45 #define MPU6050_GYRO_YOUT_L 0x46 #define MPU6050_GYRO_ZOUT_H 0x47 #define MPU6050_GYRO_ZOUT_L 0x48#define MPU6050_PWR_MGMT_1 0x6B // 电源管理寄存器1 #define MPU6050_PWR_MGMT_2 0x6C // 电源管理寄存器2 #define MPU6050_WHO_AM_I 0x75 // 用于验证设备身份#endif #include stm32f10x.h // Device header #include MyI2C.h #include MPU6050_Reg.h// 宏定义从机地址 #define MPU6050_ADDRESS 0xD0// 指定地址写 void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) {MyI2C_Start();MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址后接收应答MyI2C_ReceiveAck();// 寻址找到从机之后继续发送下一个字节MyI2C_SendByte(RegAddress); // 指定寄存器地址存在MPU6050的当前地址指针里用于指定具体读写哪个寄存器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); // 指定地址就是设置了MPU6050的当前地址指针MyI2C_ReceiveAck();// 转入读的时序重新指定读写位就必须重新起始MyI2C_Start();// 重复起始条件MyI2C_SendByte(MPU6050_ADDRESS | 0x01);// 指定从机地址和读写位0xD0是写地址或上0x01变为0xD1读写位为1接下来要读从机的数据MyI2C_ReceiveAck(); // 接收应答后总线控制权就正式交给从机了从机开始发送一个字节Data MyI2C_ReceiveByte();// 主机接收一个字节该函数返回值就是接收到的数据// 主机接收一个字节后要给从机发送一个应答MyI2C_SendAck(1);// 参数为0就是给从机应答参数给1就是不给从机应答// 如果想继续读多个字节就要给应答从机收到应答之后就会继续发送数据如果不想继续读了就不能给从机应答了。// 主机收回总线的控制权防止之后进入从机以为你还想要但你实际不想要的冲突状态// 这里只需要读取1个字节所以就给1不给从机应答MyI2C_Stop();return Data; }void MPU6050_Init(void) {MyI2C_Init();// 写入一些寄存器对MPU6050硬件电路进行初始化配置// 电源管理寄存器1设备复位0不复位睡眠模式0解除睡眠循环模式0不循环无关位i:0;温度传感器失能0不失能最后三位选择时钟000选择内部时钟001选择x轴的陀螺仪时钟MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);// 解除睡眠选择陀螺仪时钟// 电源管理寄存器2前两位循环模式唤醒频率00不需要后6位每一个轴的待机位全为0不需要待机MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00); // 均不待机// 采样率分频该8位决定了数据输出的快慢值越小越快MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);// 采样分频10分频// 配置寄存器外部同步全为0不需要数字低通滤波器110最平滑的滤波MPU6050_WriteReg(MPU6050_CONFIG,0x06);// 滤波参数给最大// 陀螺仪配置寄存器前三位自测使能全为0不自测满量程选择11最大量程后三位无关位为0MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);// 陀螺仪和加速度计都选最大量程// 加速度计配置寄存器前三位自测使能全为0不自测满量程选择11最大量程后三位高通滤波器用不到为000MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);}// 获取芯片的ID号 uint8_t MPU6050_GetID(void) {return MPU6050_ReadReg(MPU6050_WHO_AM_I); }// 获取寄存器数据的函数返回6个int16_t的数据分别表示XYZ的加速度值和陀螺仪值 // 指针地址传递的方法返回多值 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;// 加速度计XDataH MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX (DataH 8) | DataL; // 高8位左移8位再或上低8位得到加速度计X轴的16位数据// 加速度计YDataH MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY (DataH 8) | DataL;// 加速度计ZDataH MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ (DataH 8) | DataL;// 陀螺仪XDataH MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX (DataH 8) | DataL;// 陀螺仪YDataH MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY (DataH 8) | DataL;// 陀螺仪ZDataH MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ (DataH 8) | DataL; }#include stm32f10x.h // Device header #include Delay.h #include OLED.h #include MyI2C.h #include MPU6050.huint8_t ID; int16_t AX, AY, AZ, GX, GY, GZ;// 接收XYZ轴的加速度值和陀螺仪值int main(void) {OLED_Init(); // MyI2C_Init();MPU6050_Init(); // OLED_ShowString(1,1,ID:);ID MPU6050_GetID();OLED_ShowHexNum(1, 4, ID, 2);// // 指定地址写 // MyI2C_Start(); // 产生起始条件开始一次传输 // // 主机首先发送一个字节内容是从机地址读写位进行寻址 // MyI2C_SendByte(0xD0); // 1101 000 0,0代表即将进行写入操作 // // 发送一个字节后要接收一下应答位看看从机有没有收到刚才的数据 // uint8_t Ack MyI2C_ReceiveAck(); // // 接收应答之后要继续发送一个字节写入寄存器地址 // MyI2C_Stop(); // // OLED_ShowNum(1, 1, Ack, 3);// // 指定地址读 // uint8_t ID MPU6050_ReadReg(0X75);// 返回值是0x68 // OLED_ShowHexNum(1, 1, ID, 2);// // 指定地址写需要先解除睡眠模式否则写入无效 // // 睡眠模式是电源管理寄存器1的这一位SLEEP控制的把该寄存器写入0x00解除睡眠模式 // // 该寄存器地址是0x6B // MPU6050_WriteReg(0x6B, 0x00); // // 采样率分频寄存器地址是0x19值的内容是采样分频 // MPU6050_WriteReg(0x19, 0xAA); // // uint8_t ID MPU6050_ReadReg(0X19); // OLED_ShowHexNum(1, 1, ID, 2);//显示0x19地址下的内容应该是0xAAwhile(1){MPU6050_GetData(AX, AY, AZ, GX, GY, GZ);OLED_ShowSignedNum(2, 1, AX, 5);OLED_ShowSignedNum(3, 1, AY, 5);OLED_ShowSignedNum(4, 1, AZ, 5);OLED_ShowSignedNum(2, 8, GX, 5);OLED_ShowSignedNum(3, 8, GY, 5);OLED_ShowSignedNum(4, 8, GZ, 5);} } 4. I2C外设 4.1 I2C外设简介 STM32内部集成了硬件I2C收发电路可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能减轻CPU的负担支持多主机模型支持7位/10位地址模式支持不同的通讯速度标准速度(高达100 kHz)快速(高达400 kHz)支持DMA兼容SMBus协议STM32F103C8T6 硬件I2C资源I2C1、I2C2 4.2 I2C框图 左边是通信引脚SDA和SCLSMBALERT是SMBus用的 一般外设引出来的引脚一般是借用GPIO口的复用模式与外部世界相连的查表上面是数据控制部分SDA数据收发的核心部分是数据寄存器DR(DATA REGISTER)和数据移位寄存器。当需要发送数据时可以把一个字节数据写到数据寄存器DR当移位寄存器没有数据移位时数据寄存器的值就会进一步转到移位寄存器里。在移位的过程中就可以直接把下一个数据放到数据寄存器里等着了。一旦前一个数据移位完成下一个数据就可以无缝衔接继续发送。当数据由数据寄存器转到移位寄存器时就会置状态寄存器的TXE位为1表示发送寄存器为空。接收输入的数据一位一位地从引脚移入到移位寄存器里当一个字节的数据收齐之后数据就整体从移位寄存器转到数据寄存器同时置标志位RXNE表示接收寄存器非空这时就可以把数据从数据寄存器读出来了。至于什么时候收、什么时候发需要写入控制寄存器的对应位进行操作对于起始条件、终止条件、应答位等通过数据控制完成。比较器和地址寄存器是从机模式使用的。SCL 时钟控制是用来控制SCL线的。在时钟控制寄存器写对应的位电路就会执行对应的功能。控制逻辑电路写入控制寄存器可以对整个电路进行控制。读取状态寄存器可以得知电路的工作状态。在进行很多字节收发时可以配合DMA来提高效率。 4.3 I2C基本结构 SDA由于I2C是高位先行所以这个移位寄存器是向左移位。在发送时高位先移出去然后次高位。一个SCL时钟移位一次移位8次就能把8个字节从高位到低位依次放到SDA线上了。在接收时数据通过GPIO口从右边依次移进来最终移8次一个字节就接收完成了。输出的数据通过GPIO口输出到端口。输入数据通过GPIO口输入到移位寄存器GPIO口需要配置成复用开漏输出的模式复用就是GPIO口的状态是交由片上外设来控制的开漏输出是I2C协议要求的端口配置。即使是开漏输出模式GPIO口也是可以输入的。SCL时钟控制器通过GPIO去控制时钟线。 4.4 主机发送 当STM32想要执行指定地址写的时候需要按照着发送器传送序列图进行。 7位地址起始条件按后的一个字节是寻址10位地址起始条件后的两个字节都是寻址前一个字节是帧头内容是5位的标志位111102位地址1位读写位后一个字节就是纯粹的8位地址。7位流程起始、从机地址、应答、数据、应答、数据、应答 ··· 停止 初始化之后总线默认空闲状态STM默认是从模式为了产生一个起始条件STM32需要写入控制寄存器CR1写1之后STM32由从模式转为主模式。 EV5事件可以当作是标志位SB是状态寄存器的一个位表示了硬件的状态SB1表示起始条件已发送。 然后就可以发送一个字节的从机地址了从机地址需要写到数据寄存器DR中写入DR之后硬件电路就会自动将该地址字节转到移位寄存器里再把该字节发送到I2C总线上之后硬件会自动接收应答并判断如果没有应答硬件就会置应答失败的标志位然后该标志位可以申请中断来提醒我们。当寻址完成后会发生EV6事件ADDR标志位为1该标志位在主模式下表示地址发送结束。 EV8_1事件就是TxE标志位为1移位寄存器空数据寄存器空需要我们写入数据寄存器DR进行数据发送了写入DR之后由于移位寄存器为空DR就会立刻转到移位寄存器进行发送。就会进行EV8事件移位寄存器非空数据寄存器空就是移位寄存器正在发送数据的状态所以流程这里数据1的时序就产生了。在该时刻数据2就会被写入到数据寄存器里等着了接收应答位之后数据位就转入移位寄存器进行发送此时的状态是移位寄存器非空数据寄存器空因此此时EV8事件就又发生了。之后数据2正在发送但此次下一个数据就已经被写到数据寄存器等着了。一旦检测到EV8事件就可以写入下一个数据了。当想要发送的数据写完之后这时就没有新的数据写入到数据寄存器里了当移位寄存器当前的数据移位完成时此时就是移位寄存器空数据寄存器也空的状态即EV8_2事件TxE1是移位寄存器空、数据寄存器空BTF字节发送结束标志位在发送时当一个新数据将被发送且数据寄存器还未被写入新的数据。当检测到EV8_2时就可以产生终止条件Stop了。产生终止条件显然应该在控制寄存器里有相应的位可以控制。这样一个发送的时序就结束了。 4.5 主机接收 7位主接收起始、从机地址读、接收应答、接收数据、发送应答 ··· 接收数据、非应答、终止 首先写入控制寄存器的Start位产生起始条件然后等待EV5事件表示起始条件已发送。之后寻址接收应答结束后产生EV6事件表示寻址已完成。数据1表示数据正在通过移位寄存器进行输入。EV6_1表明数据还在进行移位在接收应答之后说明移位寄存器已经成功移入一个字节的数据1了这时移入的一个字节就整体转移到数据寄存器同时置RxNE标志位表示数据寄存器非空也就是收到了一个字节的数据该状态是EV7事件RxNE1读DR寄存器清除该事件也就说收到数据了当我们把数据读走之后该事件就没有了。当然数据1还没被读走时数据2就可以直接移入移位寄存器了之后数据2移位完成收到数据2产生EV7事件读走数据2EV7事件没有了。当不需要再接收时需要在最后一个时序单元发生时提前把应答位控制寄存器ACK置0并且设置终止条件请求即EV7_1事件之后就会给出非应答NA由于设置STOP位所以产生终止条件。 4.6 软件/硬件波形对比 5. 10-2 硬件I2C读写MPU6050 5.1 I2C库函数 void I2C_DeInit(I2C_TypeDef* I2Cx); void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct); void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct); void I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);// 生成起始条件、终止条件 void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState); void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);// 配置CR1的ACK这一位0无应答1应答 void I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState);// 发送数据把Data数据直接写入到DR寄存器 void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data); // 读取DR接收数据 uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);// Address参数也是通过DR发送的但在发送之前设置了Address最低位的读写位 // I2C_Direction不是发送是把Address的最低位置1读否则最低位清0写 void I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction);5.2 硬件I2C读写MPU6050实现 5.2.1 硬件连接 SCL接到STM32的PB10引脚SDA接到PB11引脚。这里由于是软件翻转电平实现所以可以任意连接两个GPIO口即可。 OLED最上面的数据是设备的ID号这个MPU6050的ID号固定为0x68。下面的左边3个是加速度传感器的输出数据分别是X轴、Y轴、Z轴的加速度右边3个是陀螺仪传感器的输出数据分别是X轴、Y轴、Z轴的角速度。 5.2.2 运行结果 5.2.3 代码实现流程 配置I2C外设对I2C外设进行初始化替换MyI2C_Init 1开启I2C外设和对应GPIO口的时钟 2把I2C外设对应的GPIO口初始化为复用开漏模式 3使用结构体对整个I2C进行配置 4I2C_Cmd使能I2C控制外设电路实现指定地址写的时序替换WriteReg控制外设电路实现指定地址读的时序替换ReadReg 5.2.4 代码 MPU6050代码 #include stm32f10x.h // Device header #include MPU6050_Reg.h/* 1. 配置I2C外设对I2C外设进行初始化替换MyI2C_Init1开启I2C外设和对应GPIO口的时钟2把I2C外设对应的GPIO口初始化为复用开漏模式3使用结构体对整个I2C进行配置4I2C_Cmd使能I2C 2. 控制外设电路实现指定地址写的时序替换WriteReg 3. 控制外设电路实现指定地址读的时序替换ReadReg */// 宏定义从机地址 #define MPU6050_ADDRESS 0xD0// 超时退出 void MPU6050_WaitEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT) {uint32_t TimeOut;TimeOut 10000;while (I2C_CheckEvent(I2Cx, I2C_EVENT) ! SUCCESS) { TimeOut --;if (TimeOut 0){break;// 跳出循环直接执行后面的程序}} }// 指定地址写发送器传送时序 void MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data) { // MyI2C_Start(); // MyI2C_SendByte(MPU6050_ADDRESS);// 发送从机地址后接收应答 // MyI2C_ReceiveAck();// 寻址找到从机之后继续发送下一个字节 // MyI2C_SendByte(RegAddress); // 指定寄存器地址存在MPU6050的当前地址指针里用于指定具体读写哪个寄存器 // MyI2C_ReceiveAck(); // MyI2C_SendByte(Data);// 指定写入指定寄存器地址下的数据 // MyI2C_ReceiveAck(); // MyI2C_Stop();I2C_GenerateSTART(I2C2, ENABLE); // 起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件// 发送从机地址接收应答。该函数自带了接收应答如果应答错误硬件会通过标志位和中断来提示我们I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6事件// 直接写入DR发送数据I2C_SendData(I2C2, RegAddress);// 写入了DRDR立刻转移到移位寄存器进行发送EV8事件出现的非常快基本不用等。因为有两级缓存// 第一个数据写进DR了会立刻跑到移位寄存器这时不用等第一个数据发完第二个数据就可以写进去等着了。MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //EV8事件I2C_SendData(I2C2, Data);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2事件I2C_GenerateSTOP(I2C2, ENABLE); }// 指定地址读接收器传送序列 uint8_t MPU6050_ReadReg(uint8_t RegAddress) {uint8_t Data;// MyI2C_Start(); // MyI2C_SendByte(MPU6050_ADDRESS); // MyI2C_ReceiveAck(); // MyI2C_SendByte(RegAddress); // 指定地址就是设置了MPU6050的当前地址指针 // MyI2C_ReceiveAck(); // // 转入读的时序重新指定读写位就必须重新起始 // MyI2C_Start();// 重复起始条件 // MyI2C_SendByte(MPU6050_ADDRESS | 0x01);// 指定从机地址和读写位0xD0是写地址或上0x01变为0xD1读写位为1接下来要读从机的数据 // MyI2C_ReceiveAck(); // 接收应答后总线控制权就正式交给从机了从机开始发送一个字节 // Data MyI2C_ReceiveByte();// 主机接收一个字节该函数返回值就是接收到的数据 // // 主机接收一个字节后要给发送从机一个应答 // MyI2C_SendAck(1);// 参数为0就是给从机应答参数给1就是不给从机应答 // MyI2C_Stop();I2C_GenerateSTART(I2C2, ENABLE); // 起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件// 发送从机地址接收应答。该函数自带了接收应答如果应答错误硬件会通过标志位和中断来提示我们I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //EV6事件// 直接写入DR发送数据I2C_SendData(I2C2, RegAddress);// 写入了DRDR立刻转移到移位寄存器进行发送EV8事件出现的非常快基本不用等。因为有两级缓存// 第一个数据写进DR了会立刻跑到移位寄存器这时不用等第一个数据发完第二个数据就可以写进去等着了。MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //EV8_2事件I2C_GenerateSTART(I2C2, ENABLE);// 重复起始条件// 主机接收MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //EV5事件// 接收地址I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver); // 函数内部就自动将该地址MPU6050_ADDRESS的最低位置1MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //EV6事件// 在最后一个数据之前就要把应答位ACK置0同时把停止条件生成位STOP置1I2C_AcknowledgeConfig(I2C2, DISABLE);I2C_GenerateSTOP(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //EV7事件// 等EV7事件产生后一个字节的数据就已经在DR里面了。// 读取DR就可拿出该字节Data I2C_ReceiveData(I2C2); // 返回值就是DR的数据// 在接收函数的最后要恢复默认的ACK 1。// 默认状态下ACK就是1给从机应答在收最后一个字节之前临时把ACK置0给非应答// 所以在接收函数的最后要恢复默认的ACK 1这个流程是为了方便指定地址收多个字节。I2C_AcknowledgeConfig(I2C2, ENABLE);return Data; }void MPU6050_Init(void) {// MyI2C_Init();RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_OD; // 复用开漏GPIO_InitStructure.GPIO_Pin GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz;GPIO_Init(GPIOB, GPIO_InitStructure);I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode I2C_Mode_I2C; // 模式I2C_InitStructure.I2C_ClockSpeed 50000; // 时钟速度,最大400kHz的时钟频率I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; // 时钟占空比只有在时钟频率大于100kHz也就是进入到快速状态时才有用小于100kHz占空比是固定的1:1I2C_InitStructure.I2C_Ack I2C_Ack_Enable; //I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; // STM32作为从机可以响应几位的地址I2C_InitStructure.I2C_OwnAddress1 0x00; // 自身地址1也是作为从机使用I2C_Init(I2C2, I2C_InitStructure); I2C_Cmd(I2C2,ENABLE);// 写入一些寄存器对MPU6050硬件电路进行初始化配置// 电源管理寄存器1设备复位0不复位睡眠模式0解除睡眠循环模式0不循环无关位i:0;温度传感器失能0不失能最后三位选择时钟000选择内部时钟001选择x轴的陀螺仪时钟MPU6050_WriteReg(MPU6050_PWR_MGMT_1,0x01);// 解除睡眠选择陀螺仪时钟// 电源管理寄存器2前两位循环模式唤醒频率00不需要后6位每一个轴的待机位全为0不需要待机MPU6050_WriteReg(MPU6050_PWR_MGMT_2,0x00); // 均不待机// 采样率分频该8位决定了数据输出的快慢值越小越快MPU6050_WriteReg(MPU6050_SMPLRT_DIV,0x09);// 采样分频10分频// 配置寄存器外部同步全为0不需要数字低通滤波器110最平滑的滤波MPU6050_WriteReg(MPU6050_CONFIG,0x06);// 滤波参数给最大// 陀螺仪配置寄存器前三位自测使能全为0不自测满量程选择11最大量程后三位无关位为0MPU6050_WriteReg(MPU6050_GYRO_CONFIG,0x18);// 陀螺仪和加速度计都选最大量程// 加速度计配置寄存器前三位自测使能全为0不自测满量程选择11最大量程后三位高通滤波器用不到为000MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18);}// 获取芯片的ID号 uint8_t MPU6050_GetID(void) {return MPU6050_ReadReg(MPU6050_WHO_AM_I); }// 获取寄存器数据的函数返回6个int16_t的数据分别表示XYZ的加速度值和陀螺仪值 // 指针地址传递的方法返回多值 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;// 加速度计XDataH MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L);*AccX (DataH 8) | DataL; // 高8位左移8位再或上低8位得到加速度计X轴的16位数据// 加速度计YDataH MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY (DataH 8) | DataL;// 加速度计ZDataH MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ (DataH 8) | DataL;// 陀螺仪XDataH MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX (DataH 8) | DataL;// 陀螺仪YDataH MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY (DataH 8) | DataL;// 陀螺仪ZDataH MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ (DataH 8) | DataL; } main.c #include stm32f10x.h // Device header #include Delay.h #include OLED.h #include MPU6050.huint8_t ID; int16_t AX, AY, AZ, GX, GY, GZ;// 接收XYZ轴的加速度值和陀螺仪值int main(void) {OLED_Init();MPU6050_Init();OLED_ShowString(1,1,ID:);ID MPU6050_GetID();OLED_ShowHexNum(1, 4, ID, 2);// // 指定地址写 // MyI2C_Start(); // 产生起始条件开始一次传输 // // 主机首先发送一个字节内容时从机地址读写位进行寻址 // MyI2C_SendByte(0xD0); // 1101 000 0,0代表即将进行写入操作 // // 发送一个字节后要接收一下应答位看看从机有没有收到刚才的数据 // uint8_t Ack MyI2C_ReceiveAck(); // // 接收应答之后要继续发送一个字节写入寄存器地址 // MyI2C_Stop(); // // OLED_ShowNum(1, 1, Ack, 3);// // 指定地址读 // uint8_t ID MPU6050_ReadReg(0X75);// 返回值是0x68 // OLED_ShowHexNum(1, 1, ID, 2);// // 指定地址写需要先解除睡眠模式否则写入无效 // // 睡眠模式是电源管理寄存器1的这一位SLEEP控制的把该寄存器写入0x00解除睡眠模式 // // 该寄存器地址是0x6B // MPU6050_WriteReg(0x6B, 0x00); // // 采样率分频寄存器地址是0x19值的内容是采样分频 // MPU6050_WriteReg(0x19, 0xAA); // // uint8_t ID MPU6050_ReadReg(0X19); // OLED_ShowHexNum(1, 1, ID, 2);//显示0x19地址下的内容应该是0xAAwhile(1){MPU6050_GetData(AX, AY, AZ, GX, GY, GZ);OLED_ShowSignedNum(2, 1, AX, 5);OLED_ShowSignedNum(3, 1, AY, 5);OLED_ShowSignedNum(4, 1, AZ, 5);OLED_ShowSignedNum(2, 8, GX, 5);OLED_ShowSignedNum(3, 8, GY, 5);OLED_ShowSignedNum(4, 8, GZ, 5);} }
文章转载自:
http://www.morning.bwzzt.cn.gov.cn.bwzzt.cn
http://www.morning.pwrkl.cn.gov.cn.pwrkl.cn
http://www.morning.bryyb.cn.gov.cn.bryyb.cn
http://www.morning.tfznk.cn.gov.cn.tfznk.cn
http://www.morning.fosfox.com.gov.cn.fosfox.com
http://www.morning.krfpj.cn.gov.cn.krfpj.cn
http://www.morning.czxrg.cn.gov.cn.czxrg.cn
http://www.morning.bzlfw.cn.gov.cn.bzlfw.cn
http://www.morning.bfgpn.cn.gov.cn.bfgpn.cn
http://www.morning.fpngg.cn.gov.cn.fpngg.cn
http://www.morning.kehejia.com.gov.cn.kehejia.com
http://www.morning.wqfj.cn.gov.cn.wqfj.cn
http://www.morning.ryxyz.cn.gov.cn.ryxyz.cn
http://www.morning.wkgyz.cn.gov.cn.wkgyz.cn
http://www.morning.coffeedelsol.com.gov.cn.coffeedelsol.com
http://www.morning.pljdy.cn.gov.cn.pljdy.cn
http://www.morning.rxfgh.cn.gov.cn.rxfgh.cn
http://www.morning.rbqlw.cn.gov.cn.rbqlw.cn
http://www.morning.wwjft.cn.gov.cn.wwjft.cn
http://www.morning.yqpzl.cn.gov.cn.yqpzl.cn
http://www.morning.slkqd.cn.gov.cn.slkqd.cn
http://www.morning.nqbkb.cn.gov.cn.nqbkb.cn
http://www.morning.vibwp.cn.gov.cn.vibwp.cn
http://www.morning.djwpd.cn.gov.cn.djwpd.cn
http://www.morning.rqwmt.cn.gov.cn.rqwmt.cn
http://www.morning.srgwr.cn.gov.cn.srgwr.cn
http://www.morning.dqxph.cn.gov.cn.dqxph.cn
http://www.morning.gblrn.cn.gov.cn.gblrn.cn
http://www.morning.linzhigongmao.cn.gov.cn.linzhigongmao.cn
http://www.morning.npbnc.cn.gov.cn.npbnc.cn
http://www.morning.gmwqd.cn.gov.cn.gmwqd.cn
http://www.morning.zlgr.cn.gov.cn.zlgr.cn
http://www.morning.hrnrx.cn.gov.cn.hrnrx.cn
http://www.morning.jikuxy.com.gov.cn.jikuxy.com
http://www.morning.tktcr.cn.gov.cn.tktcr.cn
http://www.morning.lzqdl.cn.gov.cn.lzqdl.cn
http://www.morning.npfkw.cn.gov.cn.npfkw.cn
http://www.morning.yrnll.cn.gov.cn.yrnll.cn
http://www.morning.xlclj.cn.gov.cn.xlclj.cn
http://www.morning.gnkdp.cn.gov.cn.gnkdp.cn
http://www.morning.rxzcl.cn.gov.cn.rxzcl.cn
http://www.morning.syhwc.cn.gov.cn.syhwc.cn
http://www.morning.dzzjq.cn.gov.cn.dzzjq.cn
http://www.morning.fqzz3.cn.gov.cn.fqzz3.cn
http://www.morning.rhzzf.cn.gov.cn.rhzzf.cn
http://www.morning.hmgqy.cn.gov.cn.hmgqy.cn
http://www.morning.kphyl.cn.gov.cn.kphyl.cn
http://www.morning.gjlst.cn.gov.cn.gjlst.cn
http://www.morning.zcqbx.cn.gov.cn.zcqbx.cn
http://www.morning.tyjp.cn.gov.cn.tyjp.cn
http://www.morning.zfhwm.cn.gov.cn.zfhwm.cn
http://www.morning.ynrzf.cn.gov.cn.ynrzf.cn
http://www.morning.lwzgn.cn.gov.cn.lwzgn.cn
http://www.morning.xfcjs.cn.gov.cn.xfcjs.cn
http://www.morning.jxzfg.cn.gov.cn.jxzfg.cn
http://www.morning.rnzbr.cn.gov.cn.rnzbr.cn
http://www.morning.hmbtb.cn.gov.cn.hmbtb.cn
http://www.morning.rhqn.cn.gov.cn.rhqn.cn
http://www.morning.hhrpy.cn.gov.cn.hhrpy.cn
http://www.morning.sjmxh.cn.gov.cn.sjmxh.cn
http://www.morning.kysport1102.cn.gov.cn.kysport1102.cn
http://www.morning.bzfwn.cn.gov.cn.bzfwn.cn
http://www.morning.tntgc.cn.gov.cn.tntgc.cn
http://www.morning.qmqgx.cn.gov.cn.qmqgx.cn
http://www.morning.ydnxm.cn.gov.cn.ydnxm.cn
http://www.morning.lffrh.cn.gov.cn.lffrh.cn
http://www.morning.mszwg.cn.gov.cn.mszwg.cn
http://www.morning.kfjnx.cn.gov.cn.kfjnx.cn
http://www.morning.hjwkq.cn.gov.cn.hjwkq.cn
http://www.morning.wrcgy.cn.gov.cn.wrcgy.cn
http://www.morning.ylph.cn.gov.cn.ylph.cn
http://www.morning.bzsqr.cn.gov.cn.bzsqr.cn
http://www.morning.wjhqd.cn.gov.cn.wjhqd.cn
http://www.morning.dgwrz.cn.gov.cn.dgwrz.cn
http://www.morning.tsdqr.cn.gov.cn.tsdqr.cn
http://www.morning.xfjwm.cn.gov.cn.xfjwm.cn
http://www.morning.pbdnj.cn.gov.cn.pbdnj.cn
http://www.morning.rqpgk.cn.gov.cn.rqpgk.cn
http://www.morning.c7495.cn.gov.cn.c7495.cn
http://www.morning.qkdjq.cn.gov.cn.qkdjq.cn
http://www.tj-hxxt.cn/news/272639.html

相关文章:

  • 龙口网站建设公司报价太原建设网站
  • 企业建立网站做百度网络营销推广
  • 基本的网站建设步骤淘宝推广方法有哪些
  • 推荐外贸网站建设的公司做淘宝客网站 首选霍常亮
  • 网站建设价格裙青岛seo整站优化
  • 贵阳网站上门备案业务电脑做系统网站
  • 顺企网哈尔滨网站建设青岛网站商城设计
  • 免费外贸网站大全线上运营推广方案
  • 锚文本对网站上海工商网官网电话
  • 优速网站建设优化seo东莞建筑公司前十强
  • 网站代码查询百度 网站 说明
  • 2019做地方门户网站免费推广网站如何提高排名
  • 国外设计网站pinterest网址nodejs 网站开发
  • 物流信息网站合作网站账号登录方式
  • 创建网站的好处ensp企业网络拓扑图
  • 厦门城乡住房建设厅网站欢迎访问中国建设银行官方网站
  • 做衣服网站的实验感想我想建立个网站怎么弄
  • 网站群如何做网站山西太原今天重大新闻
  • 重庆招生院校网站益阳网站seo
  • 湖南企业建网站公司wordpress打包主题
  • 网站推广怎么做才有效果海外推广平台有哪些
  • 网站开发实训报告总结上海网络推广方式
  • 电脑做视频的网站吗gta5显示网站建设中
  • 网站开发的可行性分析wordpress 分期 插件
  • 广州城市建设规划局网站上杭网站建设公司
  • 个人网站赚钱wordpress制作图片站
  • 常州外贸公司网站建设财经资讯网站该怎么做推广
  • c 做网站全网门户网站制做
  • 购买网站域名多少钱电商网站运营怎么做
  • 即墨专业医院网站制作公司建设实业公司网站设计模板