STM32F401xx双机I2C主从通信Proteus仿真及代码问题排查
STM32F401xx I2C主从通信仿真故障排查指南
问题描述(翻译自英文提问)
在Proteus中使用两块STM32F401xx MCU搭建I2C主从通信系统:一块作为主设备(Master),另一块作为从设备(Slave)。主设备代码采用MicroC Pro For Arm编写,寄存器级I2C驱动代码如下:
#include <stdint.h> #include <stdio.h> uint8_t temp=0; void begin(uint8_t addr); void end(void); void Write (uint8_t dat); void main() { // Enable the I2C CLOCK and GPIO CLOCK RCC_APB1ENR |= (1<<21); // enable I2C CLOCK RCC_AHB1ENR |= (1<<1); // Enable GPIOB CLOCK // Configure the I2C PINs for ALternate Functions //PB8 and PB9 are connected to I2C1_SCL and I2C1_SDA GPIOB_MODER |= (2<<16) | (2<<18); GPIOB_OTYPER |= (1<<8) | (1<<9); GPIOB_OSPEEDR |= (3<<16) | (3<<18); GPIOB_PUPDR |= (1<<16) | (1<<18); GPIOB_AFRH |= (4<<0) | (4<<4); begin(0x08); Write(0x01); end(); } void begin(uint8_t addr){ //join I2C as a master mode //Generate a start condition I2C1_CR1 |= (1<<10); // Enable the ACK I2C1_CR1 |= (1<<8); // Generate START while (!(I2C1_SR1 & (1<<0))); // Wait for SB bit to set to 1 //Send the Slave Address to the DR Register I2C1_DR = addr; // send the address while (!(I2C1_SR1 & (1<<1))); // temp = I2C1_SR1 | I2C1_SR2; // read SR1 and SR2 to clear the ADDR bit // Reset the I2C I2C1_CR1 |= (1<<15); //I2C Peripheral under reset state -->page.493 manual I2C1_CR1 &= ~(1<<15); //I2C Peripheral not under reset -->page.493 manual // Program the peripheral input clock in I2C_CR2 Register in order to generate correct timings I2C1_CR2 |= (42 <<0); // PCLK1 FREQUENCY in MHz I2C1_CCR = 210<<0; // Configure the clock control registers I2C1_TRISE = 43; // Configure the rise time register I2C1_CR1 |= (1<<0); // Enable I2C } void end(void) { I2C1_CR1 |= (1<<9);//stop generation // Wait until the STOP condition is complete while (I2C1_SR2 & (1<<0)); //Cleared by hardware after detecting a Stop condition on the bus // Clear the STOP bit I2C1_CR1 &= ~(1<<9); } void Write (uint8_t dat) { while (!(I2C1_SR1 & (1<<7))); // wait for TXE bit to set I2C1_DR = dat ; // wait for BTF bit to set while (!(I2C1_SR1 & (1<<2))); //waiting while BTF=0 but when BTF=1; Data byte transfer succeeded }
从设备代码如下:
#include <stdint.h> #include <stdio.h> uint8_t temp=0; uint8_t state =0x00; void begin(uint8_t addr); void end(void); uint8_t Read (); void main() { RCC_APB1ENR |= (1<<21); // enable I2C CLOCK - RCC_AHB1ENR |= (1<<1); // Enable GPIOB CLOCK RCC_AHB1ENR |= (1<<0); // Enable GPIOA CLOCK GPIOA_MODER |=(1<<0); //set pin 0 as output // Configure the I2C PINs for ALternate Functions //PB8 and PB9 are connected to I2C1_SCL and I2C1_SDA GPIOB_MODER |= (2<<16) | (2<<18); GPIOB_OTYPER |= (1<<8) | (1<<9); GPIOB_OSPEEDR |= (3<<16) | (3<<18); GPIOB_PUPDR |= (1<<16) | (1<<18); GPIOB_AFRH |= (4<<0) | (4<<4); begin(0x08); state = Read(); end(); if (state == 0x01){ GPIOA_ODR |=(1<<0);//set 1 for pin 0 } } void begin(uint8_t addr){ //join I2C bus as a slave mode I2C1_CR2 |= (42<<0); // PCLK1 FREQUENCY in MHz I2C1_OAR1 = addr;//own address interface I2C1_CR1 |= (1<<0); // Enable I2C I2C1_CR1 |= (1<<10); // Enable the ACK ,indicate that a byte is received } void end(void) { I2C1_CR1 |= (1<<9);//stop generation // Wait until the STOP condition is complete while (I2C1_SR2 & (1<<0)); //Cleared by hardware after detecting a Stop condition on the bus // Clear the STOP bit I2C1_CR1 &= ~(1<<9); } uint8_t Read (){ uint8_t receivedData = 0; I2C1_CR1 &= ~(1<<10); // clear the ACK bit temp = I2C1_SR1 | I2C1_SR2; // read SR1 and SR2 to clear the ADDR bit.... EV6 condition I2C1_CR1 |= (1<<9); // Stop I2C while (!(I2C1_SR1 & (1<<6))); // wait for RxNE to set receivedData = I2C1_DR; // Read the data from the DATA REGISTER return receivedData; }
参考STM32F401xx数据手册和参考手册编写了上述驱动,但仿真时LED始终未点亮,需排查代码问题并了解正确的Proteus仿真操作步骤。Proteus连接示意图说明:两块STM32F401xx的PB8(SCL)、PB9(SDA)分别相连,从设备PA0串联LED及限流电阻到GND,两块芯片均接3.3V电源与GND。
一、代码核心问题排查
1. 主设备代码错误
- I2C初始化顺序完全颠倒:
begin函数中先发送起始信号、从地址,再重置I2C并配置时钟参数,违反I2C初始化流程。正确顺序应为:重置I2C → 配置时钟参数 → 使能I2C → 发送起始信号 → 发送从地址。 - 从地址格式错误:I2C的8位设备地址由7位地址+1位读写位组成,主设备写操作需将7位地址左移1位(读写位设为0)。原代码直接传入
0x08,应改为0x08 << 1(即0x10)。 - 重置操作时机错误:I2C重置应在配置时钟参数前执行,而非发送地址后。
修正后主设备begin函数:
void begin(uint8_t addr){ // 重置I2C外设 I2C1_CR1 |= (1<<15); I2C1_CR1 &= ~(1<<15); // 配置时钟参数 I2C1_CR2 |= (42 << 0); // PCLK1频率为42MHz I2C1_CCR = 210 << 0; // 标准模式100kHz(CCR = PCLK1/(2*SCL频率) = 42000/(2*100)=210) I2C1_TRISE = 43; // TRISE = PCLK1频率(MHz) + 1 = 42+1=43 I2C1_CR1 |= (1<<10); // 使能ACK I2C1_CR1 |= (1<<0); // 使能I2C // 发送起始信号 I2C1_CR1 |= (1<<8); while (!(I2C1_SR1 & (1<<0))); // 等待SB位置位(起始信号发送完成) // 发送带读写位的从地址(写操作,最低位为0) I2C1_DR = (addr << 1) | 0; while (!(I2C1_SR1 & (1<<1))); // 等待ADDR位置位(地址匹配成功) temp = I2C1_SR1 | I2C1_SR2; // 读取SR1和SR2清除ADDR标志 }
2. 从设备代码错误
- 从地址寄存器(OAR1)配置缺失关键位:STM32F4的I2C_OAR1寄存器第15位必须置1,否则从地址不生效;7位地址需左移1位存入OAR1的bit7-1位。原代码
I2C1_OAR1 = addr应改为I2C1_OAR1 = (addr << 1) | (1 << 15)。 - Read函数逻辑错误:从设备不应主动发送停止信号,停止信号由主设备发起;且需先等待地址匹配(ADDR置位)再读取数据,原代码直接清ACK、发停止,导致无法正确接收数据。
- 无地址匹配等待循环:从设备main函数中
begin后直接调用Read,未等待主设备发起通信,需在Read中加入等待ADDR置位的循环。
修正后从设备begin和Read函数:
void begin(uint8_t addr){ I2C1_CR2 |= (42 << 0); // PCLK1频率为42MHz // 配置7位从地址,必须置位OAR1的bit15 I2C1_OAR1 = (addr << 1) | (1 << 15); I2C1_CR1 |= (1<<10); // 使能ACK I2C1_CR1 |= (1<<0); // 使能I2C } uint8_t Read (){ uint8_t receivedData = 0; // 等待地址匹配(ADDR位置位) while (!(I2C1_SR1 & (1<<1))); temp = I2C1_SR1 | I2C1_SR2; // 读取SR1和SR2清除ADDR标志 // 等待接收数据就绪(RxNE位置位) while (!(I2C1_SR1 & (1<<6))); receivedData = I2C1_DR; // 等待主设备发送停止信号(STOPF位置位) while (!(I2C1_SR1 & (1<<4))); temp = I2C1_SR1; // 读取SR1清除STOPF标志 return receivedData; }
3. 通用代码问题
需确保RCC_APB1ENR、I2C1_CR1等寄存器已通过头文件或宏定义正确映射,避免编译错误。
二、Proteus正确仿真操作步骤
原理图搭建
- 放置两块STM32F401xx芯片,分别配置为主、从设备。
- I2C总线连接:主设备PB8(SCL)与从设备PB8相连,主设备PB9(SDA)与从设备PB9相连;总线需串联10kΩ上拉电阻到3.3V(I2C总线必须上拉,否则无法正常通信)。
- 从设备PA0串联220Ω限流电阻后连接LED,LED另一端接GND。
- 给两块芯片分别连接3.3V电源和GND。
代码编译与hex文件生成
- 使用MicroC Pro For Arm分别编译主、从设备代码,生成对应的hex文件。
- 编译时需选择正确的芯片型号(STM32F401xx),并配置时钟树,确保PCLK1为42MHz(与代码中
I2C1_CR2的配置一致)。
Proteus仿真配置
- 分别给两块STM32芯片加载对应的hex文件:双击芯片 → 打开属性窗口 → 选择Program File为对应hex文件。
- 配置芯片系统时钟:设置为84MHz(STM32F401默认最大时钟,确保PCLK1为42MHz)。
- 启动仿真:点击运行按钮,观察LED状态。
调试技巧
- 使用Proteus的逻辑分析仪监测SCL、SDA信号,查看起始信号、地址帧、数据帧是否正常。
- 若通信无响应,优先检查总线是否上拉、芯片电源是否正常、寄存器配置是否正确。
内容的提问来源于stack exchange,提问作者Ahm2025
相关产品推荐
相关产品推荐

