嵌入式硬件篇---IIC


文章目录

  • 前言
  • [1. I²C协议基础](#1. I²C协议基础)
  • [2. STM32F103RCT6的I²C硬件配置](#2. STM32F103RCT6的I²C硬件配置)
    • [2.1 硬件连接](#2.1 硬件连接)
    • [2.2 CubeMX配置](#2.2 CubeMX配置)
  • [3. HAL库代码实现](#3. HAL库代码实现)
    • [3.1 I²C初始化](#3.1 I²C初始化)
    • [3.2 基本读写函数](#3.2 基本读写函数)
      • [(1) 写入1字节数据](#(1) 写入1字节数据)
      • [(2) 读取1字节数据](#(2) 读取1字节数据)
      • [(3) 连续读写](#(3) 连续读写)
  • [4. 软件模拟I²C(GPIO模拟)](#4. 软件模拟I²C(GPIO模拟))
    • [4.1 初始化GPIO](#4.1 初始化GPIO)
    • [4.2 模拟时序函数](#4.2 模拟时序函数)
  • [5. 常见问题与调试](#5. 常见问题与调试)
  • [6. 完整示例:读取MPU6050的WHO_AM_I寄存器](#6. 完整示例:读取MPU6050的WHO_AM_I寄存器)
  • 总结

前言

I²C(Inter-Integrated Circuit)是一种同步、半双工串行通信协议 ,广泛用于连接微控制器与传感器、EEPROM低速外设。以下是I²C协议的详细说明及在STM32F103RCT6上的代码实现。


1. I²C协议基础

1.1 物理层特性

两根信号线

SCL

SCL(Serial Clock):时钟线,由主机控制。

SDA

SDA(Serial Data):数据线,双向传输。

支持多主多从

支持多主多从:通过地址寻址区分设备。

标准模式

  1. 100 kHz(低速)
  2. 400 kHz(快速模式)
  3. 1 MHz(高速模式,STM32F103支持最高400kHz)

电平

电平:开漏输出(需外接上拉电阻,通常4.7kΩ)。

1.2 通信流程

起始条件(Start Condition)

SCL高电平时,SDA由高→低。

从机地址(Slave Address)

7位地址 + 1位读写方向(0:写,1:读)。

应答(ACK/NACK)

每传输1字节后,接收方**拉低SDA(ACK)**表示成功。

数据传输:

主机发送数据或从机返回数据

停止条件(Stop Condition)

SCL高电平时,SDA由低→高。

1.3 典型通信序列

写数据

START → 从机地址(写) → ACK → 寄存器地址 → ACK → 数据 → ACK → STOP

读数据

START → 从机地址(写) → ACK → 寄存器地址 → ACK →

START → 从机地址(读) → ACK → 数据 → NACK → STOP

2. STM32F103RCT6的I²C硬件配置

STM32F103RCT6有2个I²C接口(I2C1、I2C2),支持主/从模式。以下以I2C1(PB6=SCL, PB7=SDA)为例:

2.1 硬件连接

I²C信号 STM32引脚

SCL PB6

SDA PB7

GND 共地

上拉电阻 4.7kΩ至3.3V

2.2 CubeMX配置

启用I2C1(模式:I2C)。

配置SCL/SDA 引脚(PB6/PB7)。

设置时钟速度(如100kHz)。

启用中断(可选,用于事件处理)。

3. HAL库代码实现

3.1 I²C初始化

c 复制代码
#include "stm32f1xx_hal.h"

I2C_HandleTypeDef hi2c1;

void MX_I2C1_Init(void) {
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;          // 100kHz
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;  // 50%占空比
  hi2c1.Init.OwnAddress1 = 0;              // 主机模式无需地址
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  
  if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
    Error_Handler();
  }
}

3.2 基本读写函数

(1) 写入1字节数据

c 复制代码
HAL_StatusTypeDef I2C_WriteByte(uint8_t devAddr, uint8_t regAddr, uint8_t data) {
  return HAL_I2C_Mem_Write(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, &data, 1, 100);
}
参数:
devAddr:从机地址(7位,左对齐,如0x68 << 1)。
regAddr:寄存器地址。
data:待写入数据。

(2) 读取1字节数据

c 复制代码
HAL_StatusTypeDef I2C_ReadByte(uint8_t devAddr, uint8_t regAddr, uint8_t *data) {
  return HAL_I2C_Mem_Read(&hi2c1, devAddr, regAddr, I2C_MEMADD_SIZE_8BIT, data, 1, 100);
}

(3) 连续读写

c 复制代码
// 写入多字节
HAL_I2C_Mem_Write(&hi2c1, 0x68<<1, 0x00, I2C_MEMADD_SIZE_8BIT, buffer, 4, 100);

// 读取多字节
HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, 0x00, I2C_MEMADD_SIZE_8BIT, buffer, 4, 100);

4. 软件模拟I²C(GPIO模拟)

如果硬件I²C不可用,可用GPIO模拟:

4.1 初始化GPIO

c 复制代码
void I2C_GPIO_Init() {
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  // SCL=PB6, SDA=PB7
  GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  
  // 初始状态:SDA和SCL高电平
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6 | GPIO_PIN_7, GPIO_PIN_SET);
}

4.2 模拟时序函数

c 复制代码
// 起始信号
void I2C_Start() {
  SDA_HIGH();
  SCL_HIGH();
  Delay_us(5);
  SDA_LOW();  // SCL高时SDA拉低
  Delay_us(5);
  SCL_LOW();  // 准备数据传输
}

// 停止信号
void I2C_Stop() {
  SDA_LOW();
  SCL_HIGH();
  Delay_us(5);
  SDA_HIGH(); // SCL高时SDA拉高
  Delay_us(5);
}

// 发送1字节
void I2C_WriteByte(uint8_t data) {
  for (uint8_t i = 0; i < 8; i++) {
    SCL_LOW();
    if (data & 0x80) SDA_HIGH();
    else SDA_LOW();
    Delay_us(2);
    SCL_HIGH();
    Delay_us(5);
    SCL_LOW();
    data <<= 1;
  }
  // 等待ACK
  SDA_HIGH(); // 释放SDA
  SCL_HIGH();
  Delay_us(2);
  if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) { // 检测ACK
    // NACK处理
  }
  SCL_LOW();
}

5. 常见问题与调试

5.1 I²C通信失败原因

从机地址错误:

确保地址正确(如MPU6050的7位地址是0x68,写入时左移1位:0x68 << 1)。

上拉电阻未接:

SDA/SCL必须接4.7kΩ上拉电阻

时序问题:

检查时钟速度是否匹配(从机是否支持400kHz?)。

硬件冲突:

确保没有多个设备同时驱动总线

5.2 逻辑分析仪抓取波形

使用Saleae或PulseView观察:

  1. 起始/停止条件是否正常?
  2. ACK/NACK是否正确?
  3. 数据线是否被意外拉低?

6. 完整示例:读取MPU6050的WHO_AM_I寄存器

c 复制代码
uint8_t who_am_i;
if (HAL_I2C_Mem_Read(&hi2c1, 0x68<<1, 0x75, I2C_MEMADD_SIZE_8BIT, &who_am_i, 1, 100) == HAL_OK) {
  printf("MPU6050 ID: 0x%02X\n", who_am_i); // 正确应返回0x68
}

总结

硬件I²C

硬件I²C:使用HAL库的HAL_I2C_Mem_Read/Write最方便。

软件模拟I²C

软件模拟I²C:灵活但占用CPU资源。

调试关键

调试关键:检查地址、上拉电阻、逻辑分析仪波形

通过上述方法,可稳定实现STM32F103RCT6与I²C设备的通信。


相关推荐
Rousson8 小时前
硬件学习笔记--82 连接器的选用原则与流程
笔记·单片机·学习
三佛科技-134163842128 小时前
高速风筒方案开发 高速风筒MCU控制方案设计
单片机·嵌入式硬件·智能家居·pcb工艺
清风66666613 小时前
基于单片机的螺旋藻生长大棚PH智能控制设计
单片机·嵌入式硬件·毕业设计·课程设计
ting_zh14 小时前
微控制器(Micro Controller Unit, MCU)基础整理
单片机·嵌入式硬件
清风66666615 小时前
基于单片机的图书馆智能座位管理平台
数据库·单片机·嵌入式硬件·毕业设计·课程设计
得单片机的运16 小时前
STM32的以太网的搭建
stm32·单片机·嵌入式硬件·物联网·以太网·iot·w5500
酷飞飞17 小时前
RTC和看门狗基于GD32F407VE的天空星的配置
stm32·单片机·嵌入式硬件·mcu
WD1372980155718 小时前
WD5030A,24V降5V,15A 大电流,应用于手机、平板、笔记本充电器
stm32·单片机·嵌入式硬件·智能手机·汽车·电脑·51单片机
日更嵌入式的打工仔19 小时前
GPIO 中断通用配置指南
stm32·单片机·嵌入式硬件
平凡灵感码头19 小时前
基于 STM32 的智能门锁系统,系统界面设计
stm32·单片机·嵌入式硬件