电控---IIC通信

一、IIC基础概念
  1. 起源与定义

    • 由飞利浦(Philips)1982年开发,全称Inter-Integrated Circuit,即集成电路间通信协议。
    • 目标:实现低速、短距离、多设备间的简单通信,广泛用于嵌入式系统。
  2. 物理层特性

    • 双线制:仅需两根信号线——时钟线(SCL)和数据线(SDA)。
    • 开漏输出(OD):需外接上拉电阻(通常4.7kΩ,高速场景可降至1kΩ),默认高电平,支持“线与”逻辑。
    • 电压兼容性:支持1.8V、3.3V、5V等多电压域,通过电平转换电路可跨电压通信。
    • 总线负载:单总线最多挂载约10-128个设备(取决于总线电容,通常≤400pF)。
    • 多主多从:允许多个主设备同时控制总线,通过仲裁机制避免冲突。

在这里插入图片描述

二、协议层核心机制
  1. 信号定义

    • 起始信号(Start):SCL高电平时,SDA由高变低,标志通信开始。
    • 停止信号(Stop):SCL高电平时,SDA由低变高,标志通信结束。
    • 重复起始(Repeated Start):在不发送Stop的情况下,重新发送Start信号,用于切换主从角色或改变传输方向(如主→从写切换为从→主读)。
  2. 数据传输规则

    • 数据有效性:SCL高电平时,SDA必须保持稳定;SCL低电平时,SDA可改变电平。
    • 字节传输:每次传输8位数据,高位在前,低位在后,后跟1位应答信号(ACK/NACK)。
      • ACK(应答):接收方在第9个时钟周期拉低SDA,表示数据接收成功。
      • NACK(非应答):接收方保持SDA高电平,可能因地址错误、数据溢出或主动终止传输。
  3. 寻址方式

  • 7位地址(主流)
    • 地址范围:0x00~0x7F(128个地址),其中保留地址:
      • 0x00:通用呼叫地址(广播模式)。
      • 0x01~0x03:保留给不同用途(如SMBus警报响应)。
      • 0x7E~0x7F:保留,用户不可用。
    • 实际可用地址:112个(排除16个保留地址)。
  • 10位地址
    • 第一字节(高 7 位 + R/W 位)
      • 固定前缀:11110(二进制,占 5 位),用于标识 10 位寻址模式。
      • 地址高位:XX(占 2 位,对应 10 位地址的最高两位,范围 00~11)。
      • 读写位(R/W):0 表示写操作,1 表示读操作。
      • 示例:若 10 位地址为 0x123(二进制 0010010011),则第一字节为 0b1111000(0xF0),R/W 位为 0。
    • 第二字节(地址低位)
      • 完整 8 位地址(对应 10 位地址的低 8 位)。
      • 示例:上述地址的第二字节为 0x23(二进制 00100011)。
  1. 数据帧结构

    • 写操作
      Start → 从地址(7位)+ W(0) → ACK → 数据字节 → ACK → ... → Stop
    • 读操作
      Start → 从地址 + W → ACK → 寄存器地址 → ACK → Repeated Start → 从地址 + R(1) → ACK → 数据字节 → NACK(主设备终止) → Stop
    • 多字节传输:从设备收到ACK后继续发送/接收数据,直到主设备发送NACK或Stop。
  2. 仲裁与时钟同步

    • 仲裁机制:多主设备冲突时,SDA线电平以“线与”为准,发送低电平的主设备获胜,发送高电平的设备检测到冲突后退出。
    • 时钟同步:通过“线与”使所有主设备的SCL保持低电平至最长的低电平时间,高电平时间由最短的决定,确保多主设备时钟一致。

在这里插入图片描述

三、时序与速度模式
  1. 时序参数

    • 建立时间(tSU:STA):Start信号前,SDA高电平保持时间(标准模式≥4.7μs)。
    • 保持时间(tHD:STA):Start信号后,SDA低电平保持时间(标准模式≥4μs)。
    • 时钟频率
      • 标准模式(SM):100kHz,适用于低速设备(如EEPROM)。
      • 快速模式(FM):400kHz,通用场景(如传感器)。
      • 高速模式(FM+):3.4MHz,需器件支持(如高速ADC)。
      • 超高速模式(HS):5MHz(仅主机发送,从机不响应,用于快速数据下载)。
  2. 上拉电阻计算

    • 公式:在这里插入图片描述

    • 目标:确保SDA上升时间符合时序要求,同时避免过大电流(通常取2.2kΩ~10kΩ)。

四、设备角色与分类
  1. 设备类型

    • 主设备:生成SCL时钟,发起通信(如MCU)。
    • 从设备:响应主设备寻址,接收或发送数据(如EEPROM、传感器)。
    • 复合设备:可在主/从角色间切换(如某些FPGA)。
  2. 从设备地址

    • 固定部分:由器件厂商分配(如EEPROM的前4位固定为1010)。
    • 可编程部分:通过硬件引脚(A0、A1)或软件配置,避免地址冲突。
五、典型应用场景
  1. 存储设备:EEPROM(如24C02)、FRAM。
  2. 传感器:温度传感器(如DS1621)、加速度计(如MMA8452Q)、陀螺仪。
  3. 外设控制:LCD驱动(如SSD1306)、ADC/DAC(如PCF8591)、键盘扫描芯片(如TCA8418)。
  4. 系统管理:SMBus(电源管理,如电池监控)、PMBus(功率器件配置)。
六、优缺点与注意事项
  1. 优点

    • 接线简单,节省IO口。
    • 支持多主多从,地址空间灵活(7/10位)。
    • 跨电压域通信方便(通过上拉电阻)。
  2. 缺点

    • 速度受限(最高3.4MHz,HS模式5MHz但功能有限),低于SPI(通常几十MHz)。
    • 总线负载敏感,长距离或多设备时需考虑电容匹配。
    • 仲裁机制复杂,软件实现需处理冲突。
  3. 常见问题

    • 地址冲突:确保每个从设备地址唯一(通过硬件引脚或软件配置)。
    • ACK缺失:检查从设备是否上电、地址正确,或总线是否被拉低(短路)。
    • 时序错误:高速模式下需严格匹配SCL/SDA时序,避免使用软件模拟(推荐硬件I²C外设)。
    • 总线锁定:若SDA/SCL持续低电平,可通过复位主设备、断开电源或强制释放总线(上拉电阻作用)。
七、扩展与变种
  1. SMBus(系统管理总线)

    • IIC的子集,用于低速系统管理(如主板温度监控),定义了更严格的时序和协议(如超时机制)。
  2. PMBus(电源管理总线)

    • 基于IIC,专门用于电源设备通信(如电源模块配置、状态读取)。
  3. 软件IIC vs 硬件IIC

    • 软件模拟:通过GPIO模拟时序,灵活适配非标准时序,但速度慢(受MCU主频限制),适合低速设备。
    • 硬件外设:利用芯片内置I²C控制器,自动处理时序和ACK/NACK,效率高但需配置寄存器,兼容性依赖芯片实现。
八、实战建议
  1. 硬件设计

    • 短距离走线(≤10cm),避免高速模式下的信号反射。
    • 上拉电阻靠近从设备,或在总线末端并联电容(≤400pF)。
  2. 软件实现

    • 主设备需处理ACK/NACK,超时重试(通常3次)。
    • 多字节读写时,注意从设备是否支持自动地址递增(如EEPROM页写入)。
  3. 调试工具

    • 逻辑分析仪(如Saleae)抓取SCL/SDA波形,分析时序错误。
    • 万用表测量总线电压,确认上拉电阻是否正常工作。

代码示例

这是一个基于STM32的IIC通信示例代码,这里以STM32Cube HAL库为例,实现STM32作为主设备向从设备发送数据并读取数据的功能。

#include "stm32f4xx_hal.h"

// 定义I2C句柄
I2C_HandleTypeDef hi2c1;

// 从设备地址
#define SLAVE_ADDRESS 0x48

// 系统时钟配置
void SystemClock_Config(void);
// 外设初始化
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_I2C1_Init();

  uint8_t tx_data = 0x10; // 要发送的数据
  uint8_t rx_data;        // 接收的数据

  while (1)
  {
    // 向从设备发送数据
    if (HAL_I2C_Master_Transmit(&hi2c1, SLAVE_ADDRESS << 1, &tx_data, 1, 100) != HAL_OK)
    {
      // 处理发送错误
      Error_Handler();
    }

    // 从从设备读取数据
    if (HAL_I2C_Master_Receive(&hi2c1, SLAVE_ADDRESS << 1, &rx_data, 1, 100) != HAL_OK)
    {
      // 处理接收错误
      Error_Handler();
    }

    // 可以在这里添加对接收数据的处理逻辑

    HAL_Delay(1000);
  }
}

// 系统时钟配置
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** 初始化RCC振荡器 
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** 初始化RCC时钟 
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

// I2C1初始化
static void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
}

// GPIO初始化
static void MX_GPIO_Init(void)
{
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
}

// 错误处理函数
void Error_Handler(void)
{
  while(1)
  {
  }
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{ 
  // 可以自行在这里添加错误处理逻辑
  while(1)
  {
  }
}
#endif    

代码说明:

  1. 系统初始化:在main函数里,首先对HAL库、系统时钟、GPIO和I²C进行初始化操作。
  2. I²C通信:借助HAL_I2C_Master_Transmit函数向从设备发送数据,利用HAL_I2C_Master_Receive函数从从设备读取数据。
  3. 错误处理:在数据发送和接收过程中,若出现错误就会调用Error_Handler函数进行处理。

注意事项:

  • 要依据实际情况对SLAVE_ADDRESS进行修改。
  • 该示例代码假设使用的是STM32F4系列芯片,若使用其他系列芯片,可能需要对部分配置进行调整。

总结

IIC以其简洁性和灵活性成为嵌入式系统的主流通信协议,核心在于理解物理层的开漏特性、协议层的寻址与应答机制,以及多主环境下的仲裁逻辑。掌握时序要求、设备寻址和典型应用场景,即可应对95%以上的开发需求。实际项目中,需结合器件手册调试,注意总线负载和时序匹配,确保通信稳定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值