基于野火霸道V2开发板IIC通信完全开发指南
野火霸道V2是一款基于STM32F103ZET6芯片的经典入门级开发板,板载硬件I2C外设,同时支持软件模拟IIC通信,可适配EEPROM、OLED、MPU6050、温湿度传感器等绝大多数IIC协议外设。IIC通信凭借双线串行、半双工、多设备挂载、引脚占用少等优势,成为嵌入式开发中最常用的短距离板级通信协议之一。本文结合野火霸道V2的硬件特性,系统讲解IIC通信原理、硬件配置、软件/硬件驱动实现、实操案例及问题排查,既适合零基础入门学习,也可作为工程开发参考。
一、IIC通信核心原理
1.1 总线硬件架构
IIC总线仅需两根信号线即可完成通信:SCL(串行时钟线)和SDA(串行数据线),配合GND共地即可实现多设备互连。其总线特性如下:
-
总线拓扑:支持单主机多从机架构。野火霸道V2作为主机,可同时挂载多个IIC从设备,所有设备共用SCL和SDA总线,通过唯一的设备地址区分通信目标。
-
电气特性:SCL和SDA引脚必须配置为开漏输出模式,总线需外接上拉电阻(开发板已板载上拉电路,无需额外焊接)。总线空闲时保持高电平,通信时通过拉低电平实现数据传输。
-
通信速率:标准模式为100 kHz(最通用),快速模式为400 kHz。STM32F103硬件IIC最高支持400 kHz,可满足绝大多数外设需求。
-
寻址方式:默认使用7位设备地址,总线最多可挂载127个从设备,覆盖常规嵌入式外设。
1.2 核心通信时序
IIC所有通信操作均基于以下四种基础时序,它们是软硬件驱动开发的根本依据:
-
起始信号(START):SCL为高电平期间,SDA由高电平跳变至低电平,标志通信开始。
-
停止信号(STOP):SCL为高电平期间,SDA由低电平跳变至高电平,标志通信结束。
-
应答信号(ACK):数据传输后,从机拉低SDA表示接收成功;无应答(NACK)则SDA保持高电平,表示通信异常。
-
数据位传输:SCL低电平期间切换SDA数据,SCL高电平期间从机读取/主机写入数据。每字节传输包含8位数据加1位应答位。
二、野火霸道V2 IIC硬件资源配置
2.1 硬件引脚定义
野火霸道V2搭载STM32F103ZET6,内置I2C1和I2C2两个硬件外设,板载引脚固定,无需手动焊接。核心引脚分配如下:
| I2C1 | PB6 | PB7 | 默认外设,适配OLED、AT24C02、MPU6050 |
| I2C2 | PB10 | PB11 | 扩展外设,多设备同时挂载场景 |
开发板硬件已为对应引脚配置上拉电阻,完全满足IIC总线电气要求,外接设备时无需额外电路。
2.2 硬件配置核心要点
无论是使用硬件IIC还是软件模拟IIC,都必须遵循以下配置规则,否则会出现通信失败或数据乱码:
GPIO模式:SCL和SDA统一配置为开漏输出(OD),严禁使用推挽输出,以保证总线电平可被多个设备拉低。
时钟使能:务必开启GPIOB时钟以及对应IIC外设时钟(硬件方式)或仅GPIO时钟(软件方式)。
关键参数:启用应答机制、使用7位地址模式、设置标准100 kHz通信速率(通用默认值)。
设备地址:严格匹配从设备手册中的地址,部分设备地址可通过硬件引脚修改,需在程序中同步配置。
IIC 写操作时序图(主机写入从机)

IIC 读操作时序图(主机读取从机)

三、两种IIC实现方式对比
野火霸道V2支持硬件IIC和软件模拟IIC两种开发方式,分别适配不同场景。主要区别如下:
| 硬件IIC | 硬件时序生成,占用CPU少,传输速率高,稳定性强 | 部分芯片存在硬件bug,引脚固定,调试稍复杂 | 批量工程、高速传输、多设备长期运行 |
| 软件模拟 | 引脚任意配置,时序可控,调试简单,无硬件兼容问题 | 占用CPU资源,速率较低,依赖精准延时 | 新手入门、低速外设、引脚资源受限 |
四、软件模拟IIC完整实现(新手推荐)
软件模拟IIC通过GPIO电平翻转和微秒延时来模拟标准IIC时序,不依赖芯片硬件外设,可适配野火霸道V2的所有IO口,代码通用性强,适合初学者深入理解IIC通信本质。以下为基于PB6、PB7引脚的完整驱动代码。
4.1 头文件配置(bsp_iic.h)
c
#ifndef __BSP_IIC_H
#define __BSP_IIC_H
#include "stm32f10x.h"
// 自定义IIC引脚(野火霸道V2默认PB6、PB7)
#define IIC_SCL_PIN GPIO_Pin_6
#define IIC_SDA_PIN GPIO_Pin_7
#define IIC_GPIO_PORT GPIOB
#define IIC_GPIO_RCC RCC_APB2Periph_GPIOB
// 电平操作宏
#define IIC_SCL_HIGH GPIO_SetBits(IIC_GPIO_PORT, IIC_SCL_PIN)
#define IIC_SCL_LOW GPIO_ResetBits(IIC_GPIO_PORT, IIC_SCL_PIN)
#define IIC_SDA_HIGH GPIO_SetBits(IIC_GPIO_PORT, IIC_SDA_PIN)
#define IIC_SDA_LOW GPIO_ResetBits(IIC_GPIO_PORT, IIC_SDA_PIN)
#define IIC_SDA_READ GPIO_ReadInputDataBit(IIC_GPIO_PORT, IIC_SDA_PIN)
// 函数声明
void IIC_Init(void); // IIC引脚初始化
void IIC_Start(void); // 起始信号
void IIC_Stop(void); // 停止信号
uint8_t IIC_Write_Byte(uint8_t dat); // 写入单字节
uint8_t IIC_Read_Byte(uint8_t ack); // 读取单字节,ack=1应答,0无应答
#endif
4.2 驱动函数实现(bsp_iic.c)
c
#include "bsp_iic.h"
#include "delay.h"
// IIC引脚初始化
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(IIC_GPIO_RCC, ENABLE);
GPIO_InitStruct.GPIO_Pin = IIC_SCL_PIN | IIC_SDA_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(IIC_GPIO_PORT, &GPIO_InitStruct);
// 总线初始化为高电平(空闲状态)
IIC_SCL_HIGH;
IIC_SDA_HIGH;
}
// 起始信号
void IIC_Start(void)
{
IIC_SDA_HIGH;
IIC_SCL_HIGH;
delay_us(4);
IIC_SDA_LOW; // SCL高电平时,SDA下降沿
delay_us(4);
IIC_SCL_LOW; // 拉低时钟,准备传输数据
}
// 停止信号
void IIC_Stop(void)
{
IIC_SDA_LOW;
IIC_SCL_HIGH;
delay_us(4);
IIC_SDA_HIGH; // SCL高电平时,SDA上升沿
delay_us(4);
}
// 写入单字节,返回0表示应答成功,1表示无应答
uint8_t IIC_Write_Byte(uint8_t dat)
{
uint8_t i;
for (i = 0; i < 8; i++) {
IIC_SCL_LOW;
delay_us(2);
if (dat & 0x80)
IIC_SDA_HIGH;
else
IIC_SDA_LOW;
dat <<= 1;
delay_us(2);
IIC_SCL_HIGH;
delay_us(2);
}
// 释放SDA,等待从机应答
IIC_SCL_LOW;
IIC_SDA_HIGH;
delay_us(2);
IIC_SCL_HIGH;
delay_us(2);
return IIC_SDA_READ ? 1 : 0; // 1:无应答,0:应答成功
}
// 读取单字节,ack=1发送应答,ack=0发送无应答
uint8_t IIC_Read_Byte(uint8_t ack)
{
uint8_t i, dat = 0;
for (i = 0; i < 8; i++) {
dat <<= 1;
IIC_SCL_HIGH;
delay_us(2);
if (IIC_SDA_READ)
dat |= 0x01;
IIC_SCL_LOW;
delay_us(2);
}
// 主机发送应答/无应答
if (ack)
IIC_SDA_LOW;
else
IIC_SDA_HIGH;
delay_us(2);
IIC_SCL_HIGH;
delay_us(2);
return dat;
}
五、硬件IIC标准实现(工程优选)
硬件IIC直接调用STM32标准库外设驱动,由芯片硬件自动生成通信时序,CPU无需干预,稳定性和传输效率远优于软件模拟,适合正式项目开发。以下为野火霸道V2 I2C1的标准库配置。
5.1 硬件IIC初始化
c
#include "stm32f10x.h"
#include "i2c.h"
void I2C_Hardware_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
I2C_InitTypeDef I2C_InitStruct;
// 开启GPIOB和I2C1时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
// PB6(SCL)、PB7(SDA)复用开漏输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
// I2C参数配置
I2C_InitStruct.I2C_ClockSpeed = 100000; // 100 kHz
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; // 占空比2:1
I2C_InitStruct.I2C_OwnAddress1 = 0x00; // 主机地址忽略
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; // 使能应答
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitStruct);
I2C_Cmd(I2C1, ENABLE);
}
5.2 硬件IIC读写函数
c
// 向从设备写入单字节数据
void I2C_Write_Data(uint8_t dev_addr, uint8_t reg_addr, uint8_t data)
{
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)); // 等待总线空闲
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, dev_addr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, reg_addr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_SendData(I2C1, data);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_GenerateSTOP(I2C1, ENABLE);
}
// 从从设备读取单字节数据
uint8_t I2C_Read_Data(uint8_t dev_addr, uint8_t reg_addr)
{
uint8_t data = 0;
while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
// 发送写地址,指定寄存器
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, dev_addr, I2C_Direction_Transmitter);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
I2C_SendData(I2C1, reg_addr);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
// 重启总线,切换为读模式
I2C_GenerateSTART(I2C1, ENABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT));
I2C_Send7bitAddress(I2C1, dev_addr, I2C_Direction_Receiver);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
// 最后一字节无应答
I2C_AcknowledgeConfig(I2C1, DISABLE);
while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
data = I2C_ReceiveData(I2C1);
I2C_GenerateSTOP(I2C1, ENABLE);
I2C_AcknowledgeConfig(I2C1, ENABLE); // 恢复应答
return data;
}
六、实操案例:AT24C02 EEPROM读写实验
AT24C02是最常用的IIC存储外设,支持掉电数据保存。本实验采用软件模拟IIC实现读写测试,搭配串口打印结果,直观验证通信功能。
6.1 硬件连接
-
AT24C02 SCL → 霸道V2 PB6
-
AT24C02 SDA → 霸道V2 PB7
-
AT24C02 VCC → 3.3V
-
AT24C02 GND → 开发板GND
AT24C02默认设备地址:写地址 0xA0,读地址 0xA1。
6.2 主测试代码
c
#include "stm32f10x.h"
#include "bsp_iic.h"
#include "usart.h"
#include "led.h"
#include "delay.h"
int main(void)
{
uint8_t write_data = 0x55;
uint8_t read_data = 0x00;
LED_Init();
USART_Init();
IIC_Init();
printf("野火霸道V2 IIC通信测试启动\\r\\n");
// 写入0x01地址
IIC_Start();
IIC_Write_Byte(0xA0); // 设备写地址
IIC_Write_Byte(0x01); // 存储地址
IIC_Write_Byte(write_data);
IIC_Stop();
delay_ms(10); // 等待写入完成
// 读取0x01地址
IIC_Start();
IIC_Write_Byte(0xA0);
IIC_Write_Byte(0x01);
IIC_Start(); // 重启
IIC_Write_Byte(0xA1); // 设备读地址
read_data = IIC_Read_Byte(0); // 无应答
IIC_Stop();
printf("写入数据:0x%02X\\r\\n", write_data);
printf("读取数据:0x%02X\\r\\n", read_data);
// 结果指示
if (read_data == write_data) {
LED_GREEN;
printf("IIC通信测试成功!\\r\\n");
} else {
LED_RED;
printf("IIC通信测试失败!\\r\\n");
}
while (1) {}
}
6.3 实验现象
程序下载运行后,串口助手打印写入和读取的数据一致,开发板绿色LED点亮,表示IIC通信正常;若数据不一致或无法输出,说明通信异常,需检查硬件接线或代码配置。
七、常见问题排查与优化方案
7.1 通信失败、无数据返回
-
检查接线:确认SCL、SDA正确对应,必须共地,供电为3.3V(禁止接入5V外设)。
-
检查引脚模式:确保GPIO为开漏输出,推挽输出会导致总线失效。
-
核对设备地址:区分写地址和读地址,避免地址错误。
7.2 数据乱码或读写不一致
-
延时精度:软件模拟时微秒延时必须准确,延时过短会破坏时序。
-
降低速率:可降低通信速率至100 kHz,增加抗干扰能力。
-
应答处理:严格遵循时序中的ACK/NACK规则,尤其在多字节传输时。
7.3 硬件IIC总线卡死
STM32F1系列硬件IIC存在经典的总线卡死问题。建议方案:通信前检测总线忙状态,增加超时判断,异常时复位IIC外设,防止程序永久阻塞。
八、总结与拓展
野火霸道V2的IIC通信是嵌入式外设开发的基础核心。软件模拟IIC适合新手理解时序原理,且引脚可灵活配置;硬件IIC则更适合工程落地,兼顾效率与稳定性。本文提供的驱动代码可直接适配OLED、MPU6050、AHT10等常见IIC外设,只需修改设备地址和寄存器地址即可快速移植。
![[开源]基于STM32单片机WiFi智能停车场系统 Onene车位管理设计 云平台停车场车位检测 APP智慧停车场HK-010-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260921150408-6ab1476821e49-220x150.jpg)



