STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

STM32F 单片机硬件I2C Busy标志导致的I2C卡死的处理办法

原文链接:https://blog.csdn.net/PP_hui/article/details/112229696

    在调试多用户表的时候,发现如果人为短接I2C的SDA或SLK脚后,I2C的SR2的Busy标志将会置1,并且试了很多种办法也无法清除该标志位,只能复位芯片后I2C才能恢复正常。
    导致这个问题的原因是STM32芯片的硬件I2C接口是支持多个主设备同时使用的,STM32的I2C接口会一直检查SDA和SLK的状态,当出现非自己发出的电平变化等情况后,STM32芯片则判定为是有其它I2C的主在操作总线,这样STM32的Busy(总线忙标志)则会置位,只有在检查到一次I2C协议的停止位后才会硬件清除该标志,如下说明:

       如果Busy标志为1,则STM32的硬件I2C将不会发送数据,也就导致了I2C异常。

    芯片手册上有说明CR1的SWRST(软件复位I2C)可以清除Busy标志,如下图:

但是经过测试发现并不能解决这个问题,虽然能够复位I2C的所有寄存器,但是在读一次任何寄存器后所有寄存器的值又会恢复原来的样子,Busy标志也没有清除:

设置SWRST后:,任意操作后:

    在网上搜索了很久最终还是解决了这个问题:

链接《https://blog.csdn.net/jatamatadada/article/details/40860619》

经过代码修改后,处理逻辑为:

a.在操作I2C前,检测SR2的Busy是否为1,如下:

//检查是否有Busy标志及配置是否正确

    if(READ_BIT(hi2c1.Instance->SR2,I2C_SR2_BUSY) || READ_BIT(hi2c1.Instance->CR1,I2C_CR1_PE) == 0){

             I2C_Busy_C();

             return 0;

         }

b.I2C_Busy_C()函数内容:

//===================================================

//清除总线忙标志

//Xcp:2021/1/5

void I2C_Busy_C(void)

{

    SET_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);          

    CLEAR_BIT(hi2c1.Instance->CR1,I2C_CR1_SWRST);

    I2Cx_MspInit(&hi2c1);                      //自定义函数

    HAL_I2C_Init(&hi2c1);

}

c. I2Cx_MspInit()函数内容:

//---------------------------------------------------------

//复位I2c所有参数

static void I2Cx_MspInit(I2C_HandleTypeDef *hi2c)

{

  GPIO_InitTypeDef  GPIO_InitStruct;

  if (hi2c->Instance == I2C1)

  {

    /* Configure the GPIOs ---------------------------------------------------*/

    /* Enable GPIO clock */

             __I2C1_CLK_ENABLE();

    /* Configure I2C Tx as alternate function  */

    GPIO_InitStruct.Pin       = GPIO_PIN_6;

    GPIO_InitStruct.Mode      = GPIO_MODE_AF_OD;

    GPIO_InitStruct.Pull      = GPIO_NOPULL;

    GPIO_InitStruct.Speed     = GPIO_SPEED_FAST;

    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure I2C Rx as alternate function  */

    GPIO_InitStruct.Pin = GPIO_PIN_7;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Configure the Discovery I2Cx peripheral -------------------------------*/

    /* Enable I2C1 clock */

    __I2C1_FORCE_RESET();                                   //通过总复位模块对I2C一次复位操作

      __I2C1_RELEASE_RESET();

   }

}

以上代码中,和库函数HAL_I2C_MspDeInit()不同的是增加了

__I2C1_FORCE_RESET();                                 //通过总复位模块对I2C一次复位操作

__I2C1_RELEASE_RESET();

这两句代码,它是通过系统的总复位对I2C进线一次复位操作,这样就能够清掉Busy的标志。
————————————————
版权声明:本文为CSDN博主「PP_hui」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/PP_hui/article/details/112229696

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用STM32F103单片机和0.96寸OLED屏幕的I2C程序。 首先,需要确保你已经连接好了OLED屏幕和STM32F103单片机,然后将以下代码复制到你的STM32F103开发板的IAR或Keil等开发环境中进行编译: ```c #include "stm32f10x.h" #include "stm32f10x_i2c.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" #define OLED_I2C_ADDR 0x78 void OLED_I2C_Init(void); void OLED_I2C_WriteCmd(uint8_t I2C_Command); void OLED_I2C_WriteData(uint8_t I2C_Data); void OLED_Init(void); void OLED_Clear(void); void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size); void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no); void OLED_Set_Pos(uint8_t x, uint8_t y); void Delay(__IO uint32_t nCount); int main(void) { OLED_I2C_Init(); OLED_Init(); OLED_Clear(); OLED_ShowString(0,2,"Hello,World!",16); OLED_ShowString(0,4,"这是一行中文测试",16); while(1); } void OLED_I2C_Init(void) { I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_Init(GPIOB,&GPIO_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 400000; I2C_Init(I2C1,&I2C_InitStructure); I2C_Cmd(I2C1,ENABLE); } void OLED_I2C_WriteCmd(uint8_t I2C_Command) { while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1,OLED_I2C_ADDR,I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1,0x00); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1,I2C_Command); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1,ENABLE); Delay(200); } void OLED_I2C_WriteData(uint8_t I2C_Data) { while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY)); I2C_GenerateSTART(I2C1,ENABLE); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1,OLED_I2C_ADDR,I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1,0x40); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1,I2C_Data); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1,ENABLE); Delay(200); } void OLED_Init(void) { Delay(200); OLED_I2C_WriteCmd(0xAE); OLED_I2C_WriteCmd(0x00); OLED_I2C_WriteCmd(0x10); OLED_I2C_WriteCmd(0x40); OLED_I2C_WriteCmd(0x81); OLED_I2C_WriteCmd(0xCF); OLED_I2C_WriteCmd(0xA1); OLED_I2C_WriteCmd(0xC8); OLED_I2C_WriteCmd(0xA6); OLED_I2C_WriteCmd(0xA8); OLED_I2C_WriteCmd(0x3F); OLED_I2C_WriteCmd(0xD3); OLED_I2C_WriteCmd(0x00); OLED_I2C_WriteCmd(0xD5); OLED_I2C_WriteCmd(0x80); OLED_I2C_WriteCmd(0xD9); OLED_I2C_WriteCmd(0xF1); OLED_I2C_WriteCmd(0xDA); OLED_I2C_WriteCmd(0x12); OLED_I2C_WriteCmd(0xDB); OLED_I2C_WriteCmd(0x40); OLED_I2C_WriteCmd(0x20); OLED_I2C_WriteCmd(0x02); OLED_I2C_WriteCmd(0x8D); OLED_I2C_WriteCmd(0x14); OLED_I2C_WriteCmd(0xA4); OLED_I2C_WriteCmd(0xA6); OLED_I2C_WriteCmd(0xAF); OLED_Clear(); } void OLED_Clear(void) { uint8_t i,j; for(i=0;i<8;i++) { OLED_I2C_WriteCmd(0xb0+i); OLED_I2C_WriteCmd(0x00); OLED_I2C_WriteCmd(0x10); for(j=0;j<128;j++) { OLED_I2C_WriteData(0); } } } void OLED_ShowString(uint8_t x,uint8_t y,uint8_t *chr,uint8_t Char_Size) { uint8_t c = 0,i = 0,j = 0; while (chr[j]!='\0') { c = chr[j] - 32; if(x > 120) { x = 0; y++; } OLED_Set_Pos(x,y); for(i=0;i<Char_Size;i++) { OLED_I2C_WriteData(F8X16[c*16+i]); } OLED_Set_Pos(x,y+1); for(i=0;i<Char_Size;i++) { OLED_I2C_WriteData(F8X16[c*16+i+8]); } x += Char_Size; j++; } } void OLED_ShowCHinese(uint8_t x,uint8_t y,uint8_t no) { uint8_t t,adder = 0; OLED_Set_Pos(x , y); for(t=0;t<16;t++) { OLED_I2C_WriteData(CHS[2*no][t]); adder += 1; } OLED_Set_Pos(x,y+1); for(t=0;t<16;t++) { OLED_I2C_WriteData(CHS[2*no+1][t]); adder += 1; } } void OLED_Set_Pos(uint8_t x, uint8_t y) { OLED_I2C_WriteCmd(0xb0+y); OLED_I2C_WriteCmd(((x&0xf0)>>4)|0x10); OLED_I2C_WriteCmd((x&0x0f)|0x01); } void Delay(__IO uint32_t nCount) { for(;nCount!=0;nCount--); } ``` 在上面的代码中,我们首先定义了OLED屏幕的I2C地址,然后实现了一些函数来初始化OLED屏幕、清屏、显示字符串、显示汉字等。在主函数中,我们初始化OLED屏幕并显示一些测试信息。 请注意,这只是一个示例程序,你需要根据你的具体需求进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值