STM32的IO模拟I2C主机与硬件I2C从机实现

STM32的IO模拟I2C主机与硬件I2C从机实现


一、系统架构设计

  1. 通信模式

    • 主机:通过GPIO模拟I2C时序(软件I2C)
    • 从机:使用STM32硬件I2C外设
    • 总线拓扑:单主机-单从机通信(支持扩展多从机)
  2. 关键参数

    • 通信速率:标准模式100Kbps(软件模拟需精确延时)
    • 总线配置:开漏输出模式 + 4.7kΩ上拉电阻
    • 地址分配:从机7位地址(如0x50~0x57)

二、硬件设计

1. 电路连接
主机(STM32) 从机(STM32) 功能说明
PA0(模拟SCL) PB6(硬件SCL) 开漏输出,需上拉电阻
PA1(模拟SDA) PB7(硬件SDA) 开漏输出,需上拉电阻
GND GND 共地
2. 硬件注意事项

三、软件实现

1. 主机(软件I2C)
(1) 关键函数实现
// i2c_soft.c
#include "stm32f10x.h"

#define SDA_PIN  PA1
#define SCL_PIN  PA0

void I2C_Soft_Init(void) {
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
  
  GPIO_InitStructure.GPIO_Pin = SDA_PIN | SCL_PIN;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  // 开漏输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
  I2C_Soft_Start();  // 初始化总线为高电平
}

// 产生起始信号
void I2C_Soft_Start(void) {
  SDA_OUT();  // 设置SDA为输出
  SDA_H();    // SDA高
  SCL_H();    // SCL高
  delay_us(5);
  SDA_L();    // SDA拉低
  delay_us(5);
  SCL_L();    // SCL拉低
}

// 发送一个字节
void I2C_Soft_SendByte(uint8_t data) {
  for (int i=0; i<8; i++) {
    SDA_OUT();
    if (data & 0x80) SDA_H(); else SDA_L();
    data <<= 1;
    SCL_H(); delay_us(5);
    SCL_L(); delay_us(5);
  }
  // 接收应答
  SDA_IN();
  SCL_H(); delay_us(5);
  while (SDA_READ());  // 等待从机拉低SDA
  SCL_L();
}

// 接收一个字节
uint8_t I2C_Soft_RecvByte(uint8_t ack) {
  uint8_t data = 0;
  SDA_IN();
  for (int i=0; i<8; i++) {
    SCL_H(); delay_us(5);
    data <<= 1;
    if (SDA_READ()) data |= 0x01;
    SCL_L(); delay_us(5);
  }
  // 发送应答
  SDA_OUT();
  if (ack) SDA_L(); else SDA_H();
  SCL_H(); delay_us(5);
  SCL_L(); delay_us(5);
  return data;
}
(2) 通信流程示例
// 向从机地址0x50写入数据0x12
void I2C_WriteData(uint8_t addr, uint8_t data) {
  I2C_Soft_Start();
  I2C_Soft_SendByte((addr << 1) | 0x00);  // 写模式
  while (!I2C_Soft_CheckAck());           // 等待应答
  I2C_Soft_SendByte(data);
  while (!I2C_Soft_CheckAck());
  I2C_Soft_Stop();
}
2. 从机(硬件I2C)
(1) CubeMX配置
(2) 关键代码
// i2c_slave.c
#include "stm32f10x.h"

void I2C_Slave_Init(void) {
  I2C_InitTypeDef I2C_InitStructure;
  
  RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
  
  I2C_InitStructure.I2C_Mode = I2C_Mode_Slave;
  I2C_InitStructure.I2C_AddressingMode = I2C_AddressingMode_7bit;
  I2C_InitStructure.I2C_OwnAddress1 = 0x50 << 1;  // 7位地址左移
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_Init(I2C1, &I2C_InitStructure);
  
  NVIC_EnableIRQ(I2C1_EV_IRQn);  // 使能事件中断
  I2C_Cmd(I2C1, ENABLE);
}

// 中断服务函数
void I2C1_EV_IRQHandler(void) {
  if (I2C_CheckEvent(I2C1, I2C_EVENT_SLAVE_RECEIVE)) {
    uint8_t data = I2C_ReceiveData(I2C1);  // 读取数据
    // 处理接收到的数据
  }
}

四、通信协议实现

1. 主从通信流程
主机操作                从机响应
1. 发送起始信号(S)
2. 发送从机地址+写位(0)
3. 等待从机应答(ACK)
4. 发送寄存器地址
5. 发送数据
6. 发送停止信号(P)
2. 关键时序参数
参数 软件模拟要求 硬件I2C要求
起始/停止时间 >4.7μs 自动满足
时钟低电平 >4.7μs 由硬件控制
时钟高电平 >4.0μs 由硬件控制

参考代码 STM32的IO模拟I2C主机_硬件I2C从机 www.youwenfan.com/contentcsr/102056.html

五、调试与问题排查

1. 常见问题
现象 可能原因 解决方案
无应答(ACK) 从机地址错误 检查7位地址配置
数据错误 总线干扰/噪声 添加总线滤波电容
通信中断 硬件I2C中断未使能 在NVIC中启用中断
SDA线持续低电平 总线锁定(死锁) 复位I2C外设并重新初始化
2. 调试工具

 

专注于matlab/simulink,电子电路,编程