STM32作为I2C SLAVE时钟延展问题与解决

需积分: 0 15 下载量 200 浏览量 更新于2024-08-05 1 收藏 196KB PDF 举报
"STM32与MPEG解码芯片之间的I2C通信因时钟延展问题变得不可靠,导致系统故障。STM32作为SLAVE节点,MPEG解码芯片作为MASTER节点,通信中出现SCL信号持续低电平的情况。经过分析,问题源于STM32的I2C接口在接收缓冲区满时发送时钟延展信号,但MPEG解码芯片不支持此功能,可能引发数据冲突和丢失。软件设计中采用边接收边处理的方式,增加了问题的可能性。通过优化软件,使用中断驱动的循环缓冲区来存储接收到的数据,解决了问题。" 详细说明: 在STM32与MPEG解码芯片的I2C通信中,问题的关键在于时钟延展(Clock Stretching)。时钟延展是I2C协议的一个特性,允许SLAVE设备在忙碌时拉低SCL线,延长时钟周期,以便有更多时间处理接收到的数据。然而,在这个案例中,STM32作为SLAVE节点,当其内部接收缓冲区满时,会自动产生时钟延展信号,拉低SCL线。 MPEG解码芯片作为MASTER节点,如果是软件模拟的I2C接口,可能不支持时钟延展功能。因此,当STM32发出时钟延展信号时,MPEG芯片可能无法正确响应,继续发送数据,从而导致数据丢失或冲突。这种不兼容性可能是通信失败并最终导致系统死机的原因。 在硬件层面,经过检查,STM32的相关电源、复位和I/O配置没有问题,I2C数据帧结构也正常。但在软件层面,问题更为明显。原本的设计中,STM32的I2C接口接收到数据后立即进行处理,这可能导致数据处理速度跟不上接收速度,使得接收缓冲区满,触发时钟延展。 为解决这个问题,工程师修改了软件设计,引入了一个32字节的循环缓冲区,并使用中断来处理I2C接口接收的数据。这样,数据一旦到达就会立即被存入缓冲区,而不影响接收新的数据。同时,数据处理部分从循环缓冲区取数据,避免了接收和处理间的直接依赖,从而提高了系统的稳定性和可靠性。经过这样的优化,I2C通信的问题得到解决,系统运行不再出现故障。

#include "i2c.h"#define I2C_SPEED 100000 // I2C总线速度,单位为Hzvoid i2c_init(void){ GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能GPIOB时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); // 使能I2C1时钟 // 配置GPIOB6和GPIOB7为复用推挽输出 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); // 配置I2C1为标准模式,时钟速度为100kHz I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0x00; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED; I2C_Init(I2C1, &I2C_InitStruct); I2C_Cmd(I2C1, ENABLE); // 使能I2C1}void i2c_write(uint8_t addr, uint8_t *data, uint16_t len){ uint32_t timeout = 0; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) { if (++timeout > 0x10000) return; } I2C_GenerateSTART(I2C1, ENABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if (++timeout > 0x10000) return; } I2C_Send7bitAddress(I2C1, addr << 1, I2C_Direction_Transmitter); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if (++timeout > 0x10000) return; } while (len--) { I2C_SendData(I2C1, *data++); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if (++timeout > 0x10000) return; } } I2C_GenerateSTOP(I2C1, ENABLE);}void i2c_read(uint8_t addr, uint8_t *data, uint16_t len){ uint32_t timeout = 0; while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)) { if (++timeout > 0x10000) return; } I2C_GenerateSTART(I2C1, ENABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)) { if (++timeout > 0x10000) return; } I2C_Send7bitAddress(I2C1, addr << 1, I2C_Direction_Receiver); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if (++timeout > 0x10000) return; } while (len--) { if (len == 0) I2C_AcknowledgeConfig(I2C1, DISABLE); timeout = 0; while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)) { if (++timeout > 0x10000) return; } *data++ = I2C_ReceiveData(I2C1); } I2C_AcknowledgeConfig(I2C1, ENABLE); I2C_GenerateSTOP(I2C1, ENABLE);}

2023-05-30 上传