尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
I2C通信外设与MPU6050(硬件I2C)(STM32标准库学习笔记)
在前一章节中通过软件模拟的方式实现了STM32对MPU6050传感器的数据读取深入理解了I2C通信的基本时序与协议细节。该方法虽灵活且适用于任意引脚配置但受限于CPU资源存在响应延迟高、实时性差的问题尤其在多设备交互或高频采样场景下表现不足。本章将转向STM32标准库中的硬件I2C外设利用芯片内置的I2C模块完成与MPU6050的通信。硬件I2C不仅显著降低主控负担提升数据传输效率与稳定性还支持更复杂的通信模式如DMA辅助传输与中断驱动机制。通过对比软硬实现的差异进一步掌握STM32外设配置流程、寄存器操作逻辑以及如何合理利用开发工具优化系统性能。这一转变标志着从理论理解迈向工程应用的关键一步为后续构建高精度姿态检测系统奠定坚实基础。如果觉得有收获可以为我的文章点点小赞这将激励我创作更好的内容谢谢一、I2C通信外设与MPU60501.1 I2C外设简介STM32内部集成了硬件I2C收发电路可以由硬件自动执行时钟生成、起始终止条件生成、应答位收发、数据收发等功能减轻CPU的负担支持多主机模型支持7位/10位地址模式支持不同的通讯速度标准速度(高达100 kHz)快速(高达400 kHz)支持DMA兼容SMBus协议STM32F103C8T6 硬件I2C资源I2C1、I2C21.2 I2C框图1.3 I2C基本结构1.4 主机发送1.5 主机接收1.6 软件/硬件波形对比软件硬件1.7 实例一 10.2.1硬件I2C读写MPU6050硬件结构参考读写时序参考序列图:配置stm32的硬件I2C外设的基本结构第一步配置I2C外设对stm32的I2C2外设进行初始化第二步控制外设电路实现指定地址写的时序第三步控制外设电路实现指定地址读的时序配置stm32的硬件I2C外设的基本流程第一步开启I2C外设和对应GPIO口的时钟第二步把I2C外设对应的GPIO口初始化为复用开漏模式第三步使用结构体对整个I2C进行配置第四步I2C_Cmd使能I2C。可能用到的库函数://初始化方面void I2C_DeInit(I2C_TypeDef* I2Cx);void I2C_Init(I2C_TypeDef* I2Cx, I2C_InitTypeDef* I2C_InitStruct);void I2C_StructInit(I2C_InitTypeDef* I2C_InitStruct);//使能I2Cvoid I2C_Cmd(I2C_TypeDef* I2Cx, FunctionalState NewState);//I2C_GenerateSTART生成起始条件void I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState);//I2C_GenerateSTOP生成停止条件void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);//I2C_AcknowledgeConfig配置在收到一个字节后是否给从机应答void I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState);//I2C_SendData发送数据void I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data);//I2C_ReceiveData接收数据uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx);//ErrorStatus I2C_CheckEvent状态监控同时判断一个或多个标志位ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT);//I2C_GetLastEvent高级状态监控不怎么高级获取SR1和SR2状态寄存器的值uint32_t I2C_GetLastEvent(I2C_TypeDef* I2Cx);//I2C_GetLastEvent基于标志位的状态监控获取状态标志位的值FlagStatus I2C_GetFlagStatus(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);//清除标志位读取中断标志位清除中断标志位void I2C_ClearFlag(I2C_TypeDef* I2Cx, uint32_t I2C_FLAG);ITStatus I2C_GetITStatus(I2C_TypeDef* I2Cx, uint32_t I2C_IT);void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT);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-----------------------------MPU6050.h↓-----------------------------#ifndef __MPU6050_H #define __MPU6050_H void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data); uint8_t MPU6050_ReadReg(uint8_t RegAddress); void MPU6050_Init(void); void MPU6050_GetData(int16_t *AccX,int16_t *AccY,int16_t *AccZ, int16_t *GyroX,int16_t *GyroY,int16_t *GyroZ); uint8_t MPU6050_GetID(void); #endif-----------------------------MPU6050.c↓-----------------------------#include stm32f10x.h // Device header #include MPU6050_Reg.h #define MPU6050_ADDRESS 0xD0 //MPU6050的I2C从机地址 /** 函 数MPU6050等待事件 参 数同I2C_CheckEvent 返 回 值无 / 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) //自减到0后等待超时 { /超时的错误处理代码可以添加到此处/ break; //跳出等待不等了 } } } /** 函 数MPU6050写寄存器 参 数RegAddress 寄存器地址范围参考MPU6050手册的寄存器描述 参 数Data 要写入寄存器的数据范围0x00~0xFF 返 回 值无 */ void MPU6050_WriteReg(uint8_t RegAddress,uint8_t Data) { I2C_GenerateSTART(I2C2,ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); //硬件I2C发送从机地址方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2,RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING); //等待EV8 I2C_SendData(I2C2,Data); //硬件I2C发送数据 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTOP(I2C2,ENABLE); //硬件I2C生成终止条件 } /** 函 数MPU6050读寄存器 参 数RegAddress 寄存器地址范围参考MPU6050手册的寄存器描述 返 回 值读取寄存器的数据范围0x00~0xFF */ uint8_t MPU6050_ReadReg(uint8_t RegAddress) { uint8_t Data; I2C_GenerateSTART(I2C2,ENABLE); //硬件I2C生成起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Transmitter); //硬件I2C发送从机地址方向为发送 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED); //等待EV6 I2C_SendData(I2C2,RegAddress); //硬件I2C发送寄存器地址 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED); //等待EV8_2 I2C_GenerateSTART(I2C2,ENABLE); //硬件I2C生成重复起始条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT); //等待EV5 I2C_Send7bitAddress(I2C2,MPU6050_ADDRESS,I2C_Direction_Receiver); //硬件I2C发送从机地址方向为接收 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED); //等待EV6 I2C_AcknowledgeConfig(I2C2,DISABLE); //在接收最后一个字节之前提前将应答失能 I2C_GenerateSTOP(I2C2,ENABLE); //在接收最后一个字节之前提前申请停止条件 MPU6050_WaitEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED); //等待EV7 Data I2C_ReceiveData(I2C2); //接收数据寄存器 I2C_AcknowledgeConfig(I2C2,ENABLE); //将应答恢复为使能为了不影响后续可能产生的读取多字节操作 return Data; } /** 函 数MPU6050初始化 参 数无 返 回 值无 */ 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_InitTypeDef I2C_InitStructure; I2C_InitStructure.I2C_Mode I2C_Mode_I2C; I2C_InitStructure.I2C_ClockSpeed 50000; I2C_InitStructure.I2C_DutyCycle I2C_DutyCycle_2; I2C_InitStructure.I2C_Ack I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_OwnAddress1 0x00; I2C_Init(I2C2,I2C_InitStructure); I2C_Cmd(I2C2,ENABLE); MPU6050_WriteReg(MPU6050_PWR_MGMT_1, 0x01); //电源管理寄存器1取消睡眠模式选择时钟源为X轴陀螺仪 MPU6050_WriteReg(MPU6050_PWR_MGMT_2, 0x00); //电源管理寄存器2保持默认值0所有轴均不待机 MPU6050_WriteReg(MPU6050_SMPLRT_DIV, 0x09); //采样率分频寄存器配置采样率 MPU6050_WriteReg(MPU6050_CONFIG, 0x06); //配置寄存器配置DLPF MPU6050_WriteReg(MPU6050_GYRO_CONFIG, 0x18); //陀螺仪配置寄存器选择满量程为±2000°/s MPU6050_WriteReg(MPU6050_ACCEL_CONFIG,0x18); //加速度计配置寄存器选择满量程为±16g } /** 函 数MPU6050获取ID号 参 数无 返 回 值MPU6050的ID号 */ uint8_t MPU6050_GetID(void) { return MPU6050_ReadReg(MPU6050_WHO_AM_I); //返回WHO_AM_I寄存器的值 } /** 函 数MPU6050获取数据 参 数AccX AccY AccZ 加速度计X、Y、Z轴的数据使用输出参数的形式返回范围-32768~32767 参 数GyroX GyroY GyroZ 陀螺仪X、Y、Z轴的数据使用输出参数的形式返回范围-32768~32767 返 回 值无 */ 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; //定义数据高8位和低8位的变量 DataH MPU6050_ReadReg(MPU6050_ACCEL_XOUT_H); //读取加速度计X轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_ACCEL_XOUT_L); //读取加速度计X轴的低8位数据 *AccX (DataH 8) | DataL; //数据拼接通过输出参数返回 DataH MPU6050_ReadReg(MPU6050_ACCEL_YOUT_H); //读取加速度计Y轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_ACCEL_YOUT_L); //读取加速度计Y轴的低8位数据 *AccY (DataH 8) | DataL; //数据拼接通过输出参数返回 DataH MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_H); //读取加速度计Z轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_ACCEL_ZOUT_L); //读取加速度计Z轴的低8位数据 *AccZ (DataH 8) | DataL; //数据拼接通过输出参数返回 DataH MPU6050_ReadReg(MPU6050_GYRO_XOUT_H); //读取陀螺仪X轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_GYRO_XOUT_L); //读取陀螺仪X轴的低8位数据 *GyroX (DataH 8) | DataL; //数据拼接通过输出参数返回 DataH MPU6050_ReadReg(MPU6050_GYRO_YOUT_H); //读取陀螺仪Y轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_GYRO_YOUT_L); //读取陀螺仪Y轴的低8位数据 *GyroY (DataH 8) | DataL; //数据拼接通过输出参数返回 DataH MPU6050_ReadReg(MPU6050_GYRO_ZOUT_H); //读取陀螺仪Z轴的高8位数据 DataL MPU6050_ReadReg(MPU6050_GYRO_ZOUT_L); //读取陀螺仪Z轴的低8位数据 *GyroZ (DataH 8) | DataL; //数据拼接通过输出参数返回 }-----------------------------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(); /*显示ID号*/ OLED_ShowString(1,1,ID:); ID MPU6050_GetID(); OLED_ShowHexNum(1,4,ID,2); while(1) { MPU6050_GetData(amp;AX,amp;AY,amp;AZ,amp;GX,amp;GY,amp;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); Delay_ms(100); } }-----------------------------二、总结本文围绕 STM32 的硬件 I2C 外设与 MPU6050 传感器展开从外设原理到实例代码完整走了一遍流程主要可以归纳为以下几点硬件 I2C 的价值STM32 内部集成了 I2C 收发电路时钟生成、起始与终止条件、应答位收发、数据收发都由硬件自动完成相比软件模拟 I2C 大幅减轻了 CPU 负担同时支持多主机模型、7 位/10 位地址、标准 100 kHz 与快速 400 kHz 速率、DMA 以及 SMBus 协议兼容。结构与时序理解通过 I2C 框图与基本结构图可以看清 SCL、SDA 引脚、数据寄存器和移位寄存器之间的关系主机发送与主机接收的流程差异核心在于方向位、重复起始条件以及最后一个字节前提前失能应答并申请停止条件。软硬件波形对比软件模拟与硬件外设产生的波形在时序细节上存在差异硬件 I2C 的波形更规整、速率更稳定也更容易通过状态寄存器判断事件是否完成。配置流程硬件 I2C 的配置可概括为开启 I2C 与 GPIO 时钟、将对应 GPIO 配置为复用开漏模式、用结构体配置 I2C 参数、最后调用I2C_Cmd使能外设四步在此基础上再实现指定地址的写时序与读时序。实例落地以 MPU6050 为例通过MPU6050_WriteReg与MPU6050_ReadReg封装寄存器读写配合MPU6050_WaitEvent做超时等待完成初始化、ID 读取与加速度计/陀螺仪六轴数据的采集并在 OLED 上实时显示。整体来看硬件 I2C 的关键在于事件等待与状态判断每一步操作后都要确认对应的 EV 事件已经发生再继续下一步否则容易出现卡死或数据错乱。掌握这套流程后读写其他 I2C 从机设备如 EEPROM、OLED、各类传感器都可以复用同样的思路只需替换从机地址与寄存器定义即可。都看到这了点点赞吧求求了
RELATED

相关推荐

从零把 Ubuntu 摸熟:一份写给自己的 Linux 命令手抄本(Shell 篇)

从零把 Ubuntu 摸熟:一份写给自己的 Linux 命令手抄本(Shell 篇)

上一篇记的是日常命令,这篇把脚本这一层补上。先说一个坑。脚本手动跑得好好的,丢进 crontab 里一行输出都没有,多半是环境变量不一样:cron 的 PATH 比登录 shell 短一截,脚本里调用的命令就找不到了。这类问题跟语法无…

📅 2026/10/30 11:22:41
微商城哪家好?签约前照这6条清单给平台打分

微商城哪家好?签约前照这6条清单给平台打分

微商城哪家好?这个问题问客服,各家都说自家的好;看宣传页,每家都写着功能齐全。我的办法是把问题换掉:不问哪家好,问哪家能过清单。2026年10月,我拿个体户营业执照注册了6家的试用账号&#xff…

📅 2026/10/30 11:22:41
VPKTool实用指南:用C语言高效解析、解包与打包VPK文件

VPKTool实用指南:用C语言高效解析、解包与打包VPK文件

简介:VPKTool是一套面向Valve游戏资源处理的C语言库与命令行工具,主要服务于CS:GO、半条命2等引擎项目的开发者和Mod制作者,尤其适合具备一定C语言基础、想深入了解文件格式与资源管线的中高级开发者。它实现了VPK文件的读取、写入、目录解析…

📅 2026/10/30 11:22:41
MORE NEWS

更多资讯

📰

效率工具软件选型与自动化指南:从工具收集到流程自动化

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

用户脚本自动填问卷:DOM事件模拟与受控组件赋值实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

全栈自造Status Deck:ESP32-S3与BLE技术栈选型实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

嵌入式I2C外设调试全攻略:从硬件波形到软件驱动的系统化排查方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

导弹飞控为何禁用malloc:硬实时系统内存确定性设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Qt QWidget自研滚动选择器:无QML实现iOS风格Picker

简介:这是一份基于Qt框架实现的滚动数字选择器(类似手机闹钟时间设置控件)源码资源,面向Qt初学者与GUI开发进阶者,解决移动端风格交互控件在桌面/嵌入式场景下的复现难题。资源包共31个文件,含7个C源文件&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬