STM32 I2C驱动开发全解析:从理论到实战 | 零基础入门STM32第五十步

devtools/2025/3/13 14:25:06/
主题内容教学目的/扩展视频
I2C总线电路原理,跳线设置,I2C协议分析。驱动程序与调用。熟悉I2C总线协议,熟练调用。

师从洋桃电子,杜洋老师


📑文章目录

    • 引言
    • 一、I2C驱动分层架构
    • 二、I2C总线驱动代码精析
      • 2.1 初始化配置(i2c.c)
      • 2.2 数据发送函数(I2C_SAND_BUFFER)
      • 2.3 数据接收函数(I2C_READ_BUFFER)
    • 三、总线速度配置原理
    • 四、用户应用实战(main.c)
    • 五、器件驱动开发(LM75A示例)
      • 5.1 温度读取函数
    • 六、常见问题排查指南
    • 七、进阶优化技巧
    • 八、相关资源
    • 总结


▲ 回顾上期🔍STM32入门之I2C总线应用详解(附LM75A温度传感器实战) | 零基础入门STM32第四十九步


引言

I2C总线是嵌入式系统中广泛使用的通信协议,具有接线简单、多设备共享总线等优点。本文将深入解析STM32的I2C驱动开发,通过分层架构设计代码逐行分析实战案例演示,帮助开发者快速掌握I2C通信的核心技术。


一、I2C驱动分层架构

用户应用层 main.c
器件驱动层 LM75A
总线驱动层 i2c.c
硬件抽象层 stm32f10x_i2c.c
硬件寄存器
  1. 硬件抽象层(HAL)
    ST官方提供的固件库(如stm32f10x_i2c.c),直接操作寄存器实现基础功能。
  2. 总线驱动层
    封装I2C协议的核心操作(发送/接收数据),提供I2C_Configuration()等接口。
  3. 器件驱动层
    针对具体外设(如LM75A温度传感器)的驱动实现。
  4. 用户应用层
    调用驱动函数实现业务逻辑,如温度显示。

二、I2C总线驱动代码精析

2.1 初始化配置(i2c.c)

void I2C_Configuration(void) {// GPIO配置:SCL=PB6, SDA=PB7GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;  // 复用开漏模式GPIO_Init(GPIOB, &GPIO_InitStruct);// I2C参数配置I2C_InitStruct.I2C_ClockSpeed = 100000;       // 100kHz标准模式I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 时钟占空比I2C_Init(I2C1, &I2C_InitStruct);I2C_Cmd(I2C1, ENABLE);                        // 使能I2C
}
  • 关键点
    • GPIO必须配置为复用开漏模式(支持总线仲裁)
    • 时钟速度需匹配从机设备(如LM75A支持400kHz)

2.2 数据发送函数(I2C_SAND_BUFFER)

void I2C_SAND_BUFFER(u8 SlaveAddr, u8 WriteAddr, u8* pBuffer, u16 NumByteToWrite) {I2C_GenerateSTART(I2C1, ENABLE);              // 发送起始信号while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); // 等待EV5I2C_Send7bitAddress(I2C1, SlaveAddr, I2C_Direction_Transmitter); // 发送设备地址while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); // 等待EV6I2C_SendData(I2C1, WriteAddr);                // 发送寄存器地址while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 等待EV8while(NumByteToWrite--) {                     // 循环发送数据I2C_SendData(I2C1, *pBuffer++);while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));}I2C_GenerateSTOP(I2C1, ENABLE);               // 发送停止信号
}
  • 协议流程
    1. 起始信号 → 2. 发送地址+写方向 → 3. 发送寄存器地址 → 4. 发送数据 → 5. 停止信号

2.3 数据接收函数(I2C_READ_BUFFER)

MCU Slave START + 设备地址(写) 寄存器地址 REPEATED START + 设备地址(读) 数据字节1 (ACK) 数据字节2 (NACK) STOP MCU Slave

三、总线速度配置原理

i2c.h中定义总线速率:

#define BusSpeed 200000  // 200kHz
  • 计算公式

SCL频率 = APB1时钟频率 2 × ( I2C_ClockSpeed + 1 ) \text{SCL频率} = \frac{\text{APB1时钟频率}}{2 \times (\text{I2C\_ClockSpeed} + 1)} SCL频率=2×(I2C_ClockSpeed+1)APB1时钟频率

  • 注意事项
    • APB1时钟需在初始化时正确配置(默认为36MHz)
    • 实际速率可通过示波器测量SCL引脚验证

四、用户应用实战(main.c)

int main(void) {u8 tempData[3];I2C_Configuration();          // 初始化I2CTM1640_Init();                // 初始化显示模块while(1) {LM75A_GetTemp(tempData);  // 读取温度// 显示温度值(示例代码略)delay_ms(200);            // 控制采样频率}
}
  • 调用链
    main()LM75A_GetTemp()I2C_READ_BUFFER() → 硬件寄存器操作

五、器件驱动开发(LM75A示例)

5.1 温度读取函数

void LM75A_GetTemp(u8 *Tempbuffer) {u8 rawData[2];I2C_READ_BUFFER(LM75A_ADD, 0x00, rawData, 2); // 读取原始数据// 数据解析(示例)int16_t temp = (rawData[0] << 8) | rawData[1];temp = temp >> 5;  // 有效数据为11位*Tempbuffer = temp * 0.125;  // 转换为实际温度值
}
  • 关键参数
    • LM75A_ADD = 0x9E(包含R/W位)
    • 温度数据为16位(高11位有效)

六、常见问题排查指南

现象可能原因解决方案
总线无响应1. 硬件连接错误检查SCL/SDA上拉电阻(4.7kΩ)
数据校验失败2. 时序不匹配降低时钟速度或调整延时
重复地址冲突3. 从机地址配置错误使用I2C扫描工具检测设备地址

七、进阶优化技巧

  1. DMA传输
    使用DMA减少CPU占用:
    I2C_DMACmd(I2C1, I2C_DMAReq_Tx | I2C_DMAReq_Rx, ENABLE);
    
  2. 错误恢复机制
    检测总线忙状态时自动复位:
    if(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) {I2C_SoftwareResetCmd(I2C1, ENABLE);I2C_SoftwareResetCmd(I2C1, DISABLE);
    }
    

八、相关资源

[1] 洋桃电子B站课程-STM32入门100步
[2] STM32F103xx官方数据手册
[3] STM32F103X8-B数据手册(中文)
[4] STM32F103固件函数库用户手册(中文)
[5] I2C总线规范(中文)
[6] LM75(温度传感器)数据手册+编程说明+应用
[7] 温度传感器数码管显示程序
[8] I2C驱动程序分析.pptx


总结

本文从STM32的I2C驱动架构出发,详细解析了总线初始化、数据收发和速度配置的实现原理,并结合LM75A温度传感器展示了实际应用场景。掌握以下核心要点:

  1. 分层架构设计提升代码可维护性
  2. 严格遵循I2C协议时序
  3. 合理配置总线速率匹配外设
  4. 善用调试工具(如逻辑分析仪)验证通信波形

通过理论结合实践的方式,开发者能够快速构建稳定可靠的I2C通信系统。


💬 技术讨论(请在评论区留言~)

📌 下期预告:下一期将探讨LM75A驱动程序分析,欢迎持续关注!

点击查阅🔍往期【STM32专栏】文章

版权声明:本文采用[CC BY-NC-SA 4.0]协议,转载请注明来源
实测开发版:洋桃1号开发版(基于STM32F103C8T6)
更新日志

  • v1.0 初始版本(2025-03-07)

http://www.ppmy.cn/devtools/166795.html

相关文章

git文件过大导致gitea仓库镜像推送失败问题解决(push failed: context deadline exceeded)

问题描述&#xff1a; 今天发现gitea仓库推送到某个镜像仓库的操作几个月前已经报错终止推送了&#xff0c;报错如下&#xff1a; 首先翻译报错提示可知是因为git仓库大小超过1G限制。检查本地.git文件&#xff0c;发现.git文件大小已达到1.13G。确定是.git文件过大导致&…

mysql安装(演示为mac安装流程)

mysql安装 前言安装流程1、下载mysql安装包2、安装步骤设置mysql的密码(密码需要时8位数字以上的)然后点击finish(完成)3、查看是否安装成功4、如果运行mysql -u root -p报错:zsh: command not found: mysql解决方案首先查看mysql的安装路径在终端中切换到这个路径下打开b…

使用 kubectl cp 命令可以在 Kubernetes Pod 和本地主机之间拷贝文件或文件夹

使用 kubectl cp 命令可以在 Kubernetes Pod 和本地主机之间拷贝文件或文件夹 kubectl cp <namespace>/<pod-name>:<pod-path> <local-path> # 从 Pod 拷贝到本地 kubectl cp <local-path> <namespace>/<pod-name>:<pod-path&g…

从零到一:如何系统化封装并发布 React 组件库到 npm

1. 项目初始化 1.1 创建项目 首先&#xff0c;创建一个新的项目目录并初始化 package.json 文件。 mkdir my-component-library cd my-component-library npm init -y1.2 安装依赖 安装开发所需的依赖项&#xff0c;如构建工具、测试框架等。 npm install --save-dev webp…

Linux Shell 脚本编程极简入门指南

一、学习前提准备 ✅ 环境要求&#xff1a; Linux系统&#xff08;Ubuntu/CentOS等&#xff09;或 WSL (Windows用户) 任意文本编辑器&#xff08;推荐VSCode/Vim&#xff09; 基础命令行操作能力 &#x1f50d; 验证环境&#xff1a; # 查看系统默认Shell echo $SHELL #…

Spring Boot 中如何使用 `@RequestHeader` 注解

文章目录 学习文章&#xff1a;Spring Boot 中如何使用 RequestHeader 注解 一、RequestHeader 注解简介常用属性&#xff1a; 二、基本用法1. 提取单个请求头2. 处理可选请求头 三、提取多个请求头1. 使用多个 RequestHeader 注解2. 使用 Map 提取所有请求头 四、处理多值请求…

植物学(botany)这个单词怎么记?牛吃草,草是一种植物

植物学&#xff08;botany&#xff09;这个单词怎么记&#xff1f; botany n.植物学 botany 这个单词&#xff0c;词源上来自 botanic 一词&#xff1a; botanic a.植物学的 SYN: botanical(a.植物学的) 因此&#xff0c;英语词根botan-&#xff0c;意为“植物”。比如&…

3.3.2 Proteus第一个仿真图

文章目录 文章介绍0 效果图1 新建“点灯”项目2 添加元器件3 元器件布局接线4 补充 文章介绍 本文介绍&#xff1a;使用Proteus仿真软件画第一个仿真图 0 效果图 1 新建“点灯”项目 修改项目名称和路径&#xff0c;之后一直点“下一步”直到完成 2 添加元器件 点击元…