1. 什么是模拟I2C以及为什么需要它
在实际的STM32项目开发中,我经常遇到硬件I2C外设不够用或者不稳定的问题。特别是在需要连接多个I2C设备时,硬件I2C的资源就显得捉襟见肘了。这时候,模拟I2C就成了我的救命稻草。
模拟I2C说白了就是用两个普通的GPIO引脚,通过软件控制它们的电平变化,来模拟出I2C协议的时序。SCL引脚负责产生时钟信号,SDA引脚负责数据传输。虽然听起来简单,但里面有很多细节需要注意,否则通信就会失败。
我最初使用模拟I2C是因为项目需要同时读取SHT20温湿度传感器和MLX90614红外温度传感器,这两个设备都是I2C接口,但硬件I2C已经被其他设备占用了。通过模拟I2C,我成功实现了多设备通信,而且稳定性相当不错。
2. 模拟I2C的硬件准备与GPIO配置
2.1 GPIO引脚选择与配置
选择GPIO引脚时,我建议优先考虑那些有外部上拉电阻的引脚。虽然STM32的内部上拉电阻也能用,但外部4.7kΩ的上拉电阻能提供更稳定的通信质量。记得一定要配置为开漏输出模式,这是因为I2C总线具有"线与"特性,多个设备可以同时控制总线。
// GPIO初始化示例
void I2C_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 使能GPIO时钟
__HAL_RCC_GPIOB_CLK_ENABLE();
// SCL引脚配置
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// SDA引脚配置
GPIO_InitStruct.Pin = GPIO_PIN_7;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// 初始状态拉高总线
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_SET);
}
在实际项目中,我遇到过因为GPIO配置不当导致的通信失败。有一次我把模式误配置为推挽输出,结果SDA线无法被从设备拉低,导致永远收不到应答信号。折腾了好久才发现是这个小小的配置问题。
2.2 延时函数的实现
时序是模拟I2C的关键,延时精度直接影响通信稳定性。我通常使用SysTick定时器来实现微秒级延时,这样比简单的循环计数更准确。
// 微秒延时函数
void I2C_Delay(uint32_t us)
{
uint32_t ticks = us * (SystemCoreClock / 1000000);
uint32_t start = DWT->CYCCNT;
while ((DWT->CYCCNT - start)


2万+

被折叠的 条评论
为什么被折叠?



