目录

  • 一、I2C外设简介
  • 二、I2C框图
  • 三、I2C基本结构
  • 四、主机发送
  • 五、主机接收
  • 六、I2C的中断请求
  • 七、软件/硬件波形对比
  • 八、应用实例:硬件I2C读写MPU6050
    • 8.1接线图
    • 8.2程序代码

一、I2C外设简介

STM32内部集成了硬件I2C收发电路,可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能,减轻CPU的负担【对比USART外设是串口通信的的硬件收发器】

支持多主机模型【分为固定多主机和可变多主机,STM32为可变多主机】

支持7位/10位地址模式

10位地址模式下,发送的前2个字节为地址,其中前5位是10位寻址的标志位(11110),后10位是地址位
支持不同的通讯速度,标准速度(高达100 kHz),快速(高达400 kHz)

支持DMA

兼容SMBus协议(主要用于电源管理系统中)

STM32F103C8T6 硬件I2C资源:I2C1、I2C2

二、I2C框图


主要操作控制寄存器(CR),数据寄存器(DR),状态寄存器(SR)

引脚设定

当数据从数据寄存器转到移位寄存器时,就会置状态寄存器TXE为1,表示发送寄存器为空

当数据从移位寄存器转到接收寄存器时,就会置状态寄存器RXNE为1,表示接收寄存器非空

自身地址寄存器:设定从机地址

双地址寄存器:也是设定从机地址

比较器:stm32作为从机时有从机地址,会将收到的寻址通过比较器进行判断

三、I2C基本结构


I2C通信是高位先行
使用硬件I2C时,GPIO口的模式为复用开漏输出
对于SDA线的GPIO口,可以完成复用开漏输出和复用功能输入,【硬件I2C使用复用开漏模式,软件I2C采用通用开漏模式】

四、主机发送


起始条件写1,当起始条件发出后自动清零,接口在生成起始条件后自动从从模式切换到主模式

EV5事件修改了SB标志位,也是由硬件自动清零

EV6事件修改ADDR标志位,在主模式下表示地址发送结束

EV8事件修改TXE标志位,此时数据寄存器为空,移位寄存器非空,可以写入新的数据到DR中,该状态表示移位寄存器正在发送数据

EV8_2事件修改BTF标志位,表示字节发送结束

停止条件P写1

五、主机接收


EV7事件修改RXNE标志位,此时DR寄存器非空,移位寄存器为空

六、I2C的中断请求

七、软件/硬件波形对比


I2C同步时序对于波形的要求不高,硬件I2C的波形更加标准

八、应用实例:硬件I2C读写MPU6050

8.1接线图

8.2程序代码

MPU6050.c

#include "stm32f10x.h"                  // Device header
#include "MPU6050_Reg.h"#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)
{I2C_GenerateSTART(I2C2, ENABLE);//生成起始条件/*非阻塞式函数需要通过状态标志位判断是否完成*/MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);//检查EV5事件I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);//发送7位地址,读写标志位由第三个参数决定,该函数自带接收应答的过程【接收数据也自带发送应答功能】MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);//检查EV6事件I2C_SendData(I2C2, RegAddress);//将数据写入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;//指定地址写I2C_GenerateSTART(I2C2, ENABLE);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Transmitter);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED);I2C_SendData(I2C2, RegAddress);MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED);//当前地址读I2C_GenerateSTART(I2C2, ENABLE);//重复起始条件MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT);I2C_Send7bitAddress(I2C2, MPU6050_ADDRESS, I2C_Direction_Receiver);//发送7位地址,读写标志位由第三个参数决定,不是发送则则置1,发送则为0MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED);//EV6事件/*如果只需要接收一个字节,则在EV6事件之后需要ACK置0且配置stop标志位,否则会多读取一个字节如果是接收多个字节则直接等待EV7事件,在EV7_1事件之前ACK置0且配置stop标志位为1*/I2C_AcknowledgeConfig(I2C2, DISABLE);//配置ACK应答位为0I2C_GenerateSTOP(I2C2, ENABLE);//配置停止位MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED);//等待EV7事件Data = I2C_ReceiveData(I2C2);//将数据读取到DR寄存器,硬件自动转到移位寄存器I2C_AcknowledgeConfig(I2C2, ENABLE);//恢复ACK应答位为1,方便指定地址收多个字节return Data;
}
//硬件I2C初始化和指定地址写
void MPU6050_Init(void)
{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初始化I2C_InitTypeDef I2C_InitStructure;I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;I2C_InitStructure.I2C_ClockSpeed = 50000;//最大400KHzI2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;/*在标准速度下占空比为1:1,在快速模式下设置才有用,有2个参数可以选择,低电平:高电平分别是16:9和2:1,因为在scl低电平时sda会切换电平,此时适当多分配低电平时间可以在快速模式下完成电平变化。*/I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//配置ack应答位I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//stm32作为从机可以响应多少位地址I2C_InitStructure.I2C_OwnAddress1 = 0x00;//作为从机时设置自身地址1I2C_Init(I2C2, &I2C_InitStructure);I2C_Cmd(I2C2, ENABLE);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);
}uint8_t MPU6050_GetID(void)
{return MPU6050_ReadReg(MPU6050_WHO_AM_I);
}
//获取数据寄存器函数,参数为指针,指向各个数据寄存器的地址
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 << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L);*AccY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L);*AccZ = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_XOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_XOUT_L);*GyroX = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_YOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_YOUT_L);*GyroY = (DataH << 8) | DataL;DataH = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H);DataL = MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L);*GyroZ = (DataH << 8) | DataL;
}

MPU6050.h

#ifndef __MPU6050_H
#define __MPU6050_Hvoid MPU6050_WriteReg(uint8_t RegAddress, uint8_t Data);
uint8_t MPU6050_ReadReg(uint8_t RegAddress);void MPU6050_Init(void);
uint8_t MPU6050_GetID(void);
void MPU6050_GetData(int16_t *AccX, int16_t *AccY, int16_t *AccZ, int16_t *GyroX, int16_t *GyroY, int16_t *GyroZ);#endif

MPU6050_Reg.h
使用宏定义,将寄存器地址用一个字符串表示

#ifndef __MPU6050_REG_H
#define __MPU6050_REG_H#define  MPU6050_SMPLRT_DIV      0x19
#define MPU6050_CONFIG          0x1A
#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
#define MPU6050_PWR_MGMT_2      0x6C
#define MPU6050_WHO_AM_I        0x75#endif

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "MPU6050.h"uint8_t ID;
int16_t AX, AY, AZ, GX, GY, GZ;int main(void)
{OLED_Init();MPU6050_Init();OLED_ShowString(1, 1, "ID:");ID = MPU6050_GetID();OLED_ShowHexNum(1, 4, ID, 2);while (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);}
}

参考视频:江科大自化协

STM-32:I2C外设总线—硬件I2C读写MPU6050相关推荐

  1. STM32——I2C外设总线

    文章目录 一.I2C外设简介 二.I2C框图 三.I2C基本结构 四.主机发送 五.主机接收 六.I2C的中断请求 七.软件/硬件波形对比 八.硬件I2C读写MPU6050 电路设计 关键代码 状态监 ...

  2. 基于I2C/SPI总线的温湿度采集与OLED显示

    实验一 实验目的 学习I2C总线通信协议,使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集,并将采集的温度-湿度值通过串口输出.具体任务: 1)解释什么是"软件I2 ...

  3. STC8系列单片机硬件I2C使用教程(二)

    硬件I2C(中断方式)以STC8F2K08S2单片机为例 一.I2C相关的寄存器 二.I2C中断相关的寄存器 ① I2C中断源 ② I2C中断允许位 ③ I2C中断标志位 三.程序编写 ① 寄存器和相 ...

  4. STM32F10x_硬件I2C读写EEPROM(标准外设库版本)

    Ⅰ.写在前面 上一篇文章是"STM32F10x_模拟I2C读写EEPROM",讲述使用IO口模拟I2C总线通信,对EEPROM(AT24Xxx)进行读写操作的过程. 上一篇文章主要 ...

  5. STM32CubeMX学习教程之十:硬件I2C读写AT24C02

    完整源码下载: https://github.com/simonliu009/STM32CubeMX-hardware-I2C-AT24C02 网上有流传已久一种说法,就是STM的I2C有bug,不好 ...

  6. bme280 环境传感器开发板_STM32Cube14 | 使用硬件I2C读写环境光强度传感器

    更多精彩~点击上面蓝字关注我们呀!   寻求更好的阅读体验,请点击阅读原文移步:Mculover666的个人博客. 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I ...

  7. STC8A8K64单片机关于AT24C04基本读写操作(包含硬件I2C与软件模拟I2C)

    实验:AT24C04基本读写操作 步骤及现象:在下载程序前,选择stc-isp的IRC频率:12MHz. 程序下载完成后,在串口助手界面,HEX模式下,选择波 特率9600,然后点击"打开串 ...

  8. STM32HAL库学习笔记--硬件I2C读写AT24C512

    摘要:由于需要实现掉电存储功能,在无线传输模块上增加了一块EEPROM芯片,因为工程使用CubeMx配置并生成,为了方便不再使用IO口模拟I2C,而是使用f1自带的硬件I2C来实现.配置和调试过程和遇 ...

  9. I2C总线及AT24C02读写

    I2C总线介绍 I2C总线(Inter IC BUS)是由Philips公司开发的一种通用数据总线 两根通信线:SCL(Serial Clock).SDA(Serial Data) 同步.半双工,带数 ...

最新文章

  1. AttributeError: module ‘os‘ has no attribute ‘mknod‘
  2. java json解析 代码_Java构造和解析Json数据的两种方法详解一
  3. 以太坊服务器是什么_搭建以太坊节点服务器https API
  4. su一键封面插件_插件分享丨一键制作SU爆炸分析图插件分享
  5. java 二分法 应用_介绍一下java中的二分法运用
  6. vtk鼠标不交互_vtk 各种不同的鼠标交互方式
  7. RocketMQ为什么速度快
  8. string会传null吗_JVM 解剖公园(10): String.intern()
  9. Asterisk权威指南/第二章 Asterisk架构
  10. STM32之SDIO例程
  11. 虚拟机网络驱动(共享文件夹)不见了的解决方案-适用于win7~win10 and Windows Server 2008~Windows Server 2012R2
  12. 用jquery怎么删除table的一行
  13. 法语语音教学课件下载
  14. 存储器的分类及层次结构
  15. 太牛了,值得收藏!7000字22张图,精讲 Redis 知识!
  16. 网络新手ip隐藏器 v1.10 官方
  17. Extended Kalman Filter vs. Error State Kalman Filter for Aircraft Attitude Estimation 翻译
  18. 第13届中国生物特征识别大会即将盛大开幕,中科博宏期待您的到来!
  19. 王炸-GPT4.0的新能力与商业价值
  20. 后缀自动机入门/基本概念

热门文章

  1. VS2005的几个大版本简介
  2. android之仿qq
  3. 在eclipse中设置Tomcat启动和关闭时间
  4. 文件夹加密软件推荐-文件夹加密超级大师
  5. 2313 Problem B 魂之挽歌
  6. 【论文阅读】Learning Spatio-Temporal Representation with Pseudo-3D Residual Networks
  7. 2020校招薪酬大比拼,旧人没有新人香,你被倒挂了没?
  8. Web网页音视频通话之基于Sipjs
  9. 量化交巨变文艺复兴大幅削减趋势追踪交策略
  10. clion调试出现optimized out