STM32
直播中

杨火亭

7年用户 986经验值
擅长:控制/MCU
私信 关注
[问答]

STM32F103ZET6的I2C硬件轮询方式作为主发送数据,第一次发送数据会出现等待从机ACK错误怎么解决?

STM32F103ZET6的开发板,MCU作为主I2C,用I2C硬件轮询的方式读写一个I2C从设备。板子启动后,通过一个按钮开关来打开或关闭I2C写数据和读数据,在开始的第一次写数据时会偶现出现BUSY超时错误,后面持续数百万次的写和读操作结果都是正常的,而且这个错误也不是必现,是随机出现的,大概有50%多的错误概率,完全发现不了规律。从调试跟踪的结果看,都是在主I2C给从I2C发送器件地址后,主I2C没有收到ACK,然后超时。看了网上以前的一些分析,提到的一些可能原因,对比分析了,都对应不上。这个是偶现的错误,而且后面的读写都正常,所以写的从I2C的地址肯定是没有问题的。主I2C读写测试是在MCU上电启动后通过按钮开关控制的,不是一上电就开始读写。IO的初始化顺序也试了,IO的端口速率400K和100K都试了,都不能解决这个问题。
通过在软件中判断写操作的结果,如果发现写错误就重复写几次,也解决不了第一次偶然写失败的问题。
另外用中断的方式读写I2C,也有一样的问题,偶现第一次写失败,中断方式中发现主I2C能收到I2C_IT_AF中断错误。从I2C设备暂时不太好分析,现在还不确定到底是STM32F103的I2C的问题还是从设备的I2C问题。
不知道有没有遇到类似的问题的?能不能解决?

下面是STM32F103的I2C的IO/I2C初始化代码和I2C读写单个字节的代码:
void I2C_Config(void)
{
        I2C_InitTypeDef    I2C_InitStructure;
        GPIO_InitTypeDef   GPIO_InitStructure;
        RCC_ClocksTypeDef  rcc_clocks;
        
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
        
        GPIO_AFIODeInit();
        RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2, ENABLE);
        RCC_APB1PeriphResetCmd(RCC_APB1Periph_I2C1|RCC_APB1Periph_I2C2, DISABLE);

        GPIO_DeInit(GPIOB);
        GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10 | GPIO_Pin_11;
        GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB,  GPIO_InitStructure);
        GPIO_SetBits(GPIOB, GPIO_InitStructure.GPIO_Pin);
        GPIO_PinLockConfig(GPIOB, GPIO_InitStructure.GPIO_Pin);
        
        I2C_DeInit(I2C2);
        I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
        I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
        I2C_InitStructure.I2C_OwnAddress1 = 0x23; //unuseful
        I2C_InitStructure.I2C_Ack  = I2C_Ack_Enable;
        I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
        I2C_InitStructure.I2C_ClockSpeed = 400000;
        
        I2C_Init(I2C2,  I2C_InitStructure);
        I2C_Cmd(I2C2, ENABLE);
        
        I2C_AcknowledgeConfig(I2C2, ENABLE);
        
        RCC_GetClocksFreq( rcc_clocks);
        g_ultimeOut = (rcc_clocks.SYSCLK_Frequency /10000);
}

void I2C_WriteByte(u8 addr, u8 data)
{
        u32  timeout = g_ulTimeOut;
        
        while((timeout--)  (I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)));
        I2C_GenerateSTART(I2C2, ENABLE);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)));
        I2C_Send7bitAddress(I2C2, IIC_DEVICE_ADDRESS, I2C_Direction_Transmitter);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)));
        I2C_SendData(I2C2, addr);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING)));
        I2C_SendData(I2C2, data);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)));
        I2C_GenerateSTOP(I2C2, ENABLE);
}

void I2C_ReadByte(u8 addr, u8 *pdata)
{        
        u32  timeout = g_ulTimeOut;
        
        while((timeout--)  I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));
        I2C_GenerateSTART(I2C2, ENABLE);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)));
        I2C_Send7bitAddress(I2C2, IIC_DEVICE_ADDRESS, I2C_Direction_Transmitter);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)));
        I2C_SendData(I2C2, addr);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTING)));               
    //restart
    I2C_GenerateSTART(I2C2, ENABLE);
        
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT)));
    // device address, read action
        I2C_Send7bitAddress(I2C2, IIC_DEVICE_ADDRESS, I2C_Direction_Receiver);

        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)));
    // receive byte
        I2C_AcknowledgeConfig(I2C2, DISABLE);
        I2C_GenerateSTOP(I2C2, ENABLE);
        // read
        timeout = g_ulTimeOut;
        while((timeout--)  (SUCCESS!=I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED)));
        
        *pdata = I2C_ReceiveData(I2C2);
        I2C_AcknowledgeConfig(I2C2, ENABLE);
}


回帖(2)

高媛

2024-3-20 14:55:15
建议先抓一下波形,先保证I2C不是被锁死的状态。可以考虑初始化时增加一个I2C的结束操作,让I2C总线的设备进入正常状态。另外,I2C总线上上拉电阻是否焊接,上拉电阻的阻值是多少?
举报

王婷婷

2024-3-20 14:55:20
启动I2C通讯时加一个延时看看。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分