精华内容
下载资源
问答
  • 硬件I2C

    千次阅读 2010-08-03 14:26:00
    之前有抄过别人的IO模拟I2C,其实很多的芯片内部都有硬件I2C,这个的操作相对就比较简单。这里我们以Telechip8902 为例,在Wince系统下面如何构建I2C的驱动程序。 1:I2C的驱动是基于流驱动模式。 2:无非是显示一下...

    之前有抄过别人的IO模拟I2C,其实很多的芯片内部都有硬件I2C,这个的操作相对就比较简单。这里我们以Telechip8902 为例,在Wince系统下面如何构建I2C的驱动程序。

    1:I2C的驱动是基于流驱动模式。

    2:无非是显示一下接口函数:

    LIBRARY TCC_I2C

    EXPORTS
       I2C_Init
       I2C_Deinit
       I2C_Open
       I2C_Close
       I2C_IOControl
       I2C_PowerUp
       I2C_PowerDown
       I2C_Read
       I2C_Write
       I2C_Seek

     

    3:首先是初始化,因为都是GPIO口复用,所以首先需要配置IO口,使之成为SDA,SCL接口模拟。

    void I2C_GpioSetting(void)
    {
        PGPIO pGPIO;
        pGPIO = (PGPIO)tcc_allocbaseaddress((unsigned int)&HwGPIO_BASE);

        //SCL0-GPIOA0, SDA0-GPIOA1
        BITCSET(pGPIO->GPAFN0, (Hw5-Hw0), Hw0); //GPIOA[0] function set 1
        BITCSET(pGPIO->GPAFN0, (Hw8-Hw4), Hw4); //GPIOA[1] function set 1

        //SCL1-GPIOA8, SDA1-GPIOA9
        BITCSET(pGPIO->GPAFN1, (Hw5-Hw0), Hw0); //GPIOA[8] function set 1
        BITCSET(pGPIO->GPAFN1, (Hw8-Hw4), Hw4); //GPIOA[9] function set 1
    }

    这个地方的许多的配置都是不一样的。这里说明有两个I2C接口

     

    4:初始化完了就得实际配置下I2C,可以参加一下函数:

       void I2C_Initialize()

    {

       pI2C = (PI2C)tcc_allocbaseaddress((unsigned int)&HwI2CMASTER0_BASE);
        //I2C Channel 0 setting 100kHz
          pI2C->PRES0 = 7; 

       //Enable I2C core and Enable I2C Core Interrupt bit

        pI2C->CTRL0 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_STO_EN|HwI2CM_CTRL_MOD_8;
        BITSET( pI2C->CMD0, HwI2CM_CMD_IACK_CLR);
        //Sleep(1);
        BITSET( pI2C->CMD0,HwI2CM_CMD_IACK_CLR);
        //I2C Channel 1 setting 400kHz
        /* Required Frequency  */
        //pI2C->PRES1 = 2; // (400K)
        pI2C->PRES1 = 7; // (100K)
        pI2C->CTRL1 = HwI2CM_CMD_STA_EN | HwI2CM_CMD_STO_EN|HwI2CM_CTRL_MOD_8;
        BITSET( pI2C->CMD1, HwI2CM_CMD_IACK_CLR);
        //Sleep(1);
        BITSET( pI2C->CMD1, HwI2CM_CMD_IACK_CLR);

    }

     

    5:前面的步骤已经完成了配置工作,我们使用I2C最主要还是来通讯的。所以要写读写函数。我们一般定义一下结构体:

    ypedef struct _I2C_Param{
        BYTE    DeviceAddr;
        BYTE    nPort;
        BYTE    nMode;
        BYTE    DUMMY;
        DWORD    nWriteByte;
        DWORD    nReadByte;
        BYTE    *pWriteBuffer;
        BYTE    *pReadBuffer;
        DWORD    nTimeout;
    } I2C_Param;

    typedef struct _RetParam{
        DWORD nReadByte;
        BYTE *pReadBuffer;
    } RetParam;

    第一个结构用来包装我们的传递的参数。第二个用来包装我们的返回值。

     

     

    6:读写函数:

    DWORD ReadChannel0(BYTE deviceAddr, DWORD writeBytes, BYTE* pWriteBuffer, DWORD readBytes, BYTE* pReadBuffer, BYTE nMode, DWORD nTimeout)
    {
        DWORD nCount=0;
        if(writeBytes > 0)
        {
            pI2C->TXR0    = deviceAddr | I2C_WR;
            pI2C->CMD0    = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;
            while( !(pI2C->IRQSTR & Hw0) )   
            {
                nCount++;
                if(nCount > 100000)
                {
                    RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                    return FALSE;
                }
            }
            BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
            for(unsigned int i=0; i< writeBytes; i++) {
                RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]WriteData[%d]=[%x]/n"), i, pWriteBuffer[i]));
                pI2C->TXR0 = pWriteBuffer[i];
                pI2C->CMD0 = HwI2CM_CMD_WR_EN;
                nCount=0;
                while( !(pI2C->IRQSTR & Hw0) )   
                {
                    nCount++;
                    if(nCount > 100000)
                    {
                        RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                        return FALSE;
                    }
                }
                BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
            }
            if(!nMode || ((readBytes <= 0)&&(nMode != 2)&&(nMode != 4)))
            {   
                pI2C->CMD0 = HwI2CM_CMD_STO_EN;
                nCount=0;   
                while( !(pI2C->IRQSTR & Hw0) )   
                {
                    nCount++;
                    if(nCount > 100000)
                    {
                        RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT[STOP] /n")));
                        return FALSE;
                    }
                }
                BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupts
            }
        }// writeBytes
        if(readBytes > 0)
        {
            pI2C->TXR0    = deviceAddr | I2C_RD;
            pI2C->CMD0    = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;
            nCount=0;
            while( !(pI2C->IRQSTR & Hw0) )   
            {
                nCount++;
                if(nCount > 100000)
                {
                    RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                    return FALSE;
                }
            }
            BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
            for(unsigned int i=0; i< readBytes; i++) {
                if(nMode)
                {
                    if(i==readBytes-1)
                        pI2C->CMD0 = HwI2CM_CMD_RD_EN |HwI2CM_CMD_ACK_EN;   
                    else            
                        pI2C->CMD0 = HwI2CM_CMD_RD_EN ;
                }
                else
                    pI2C->CMD0 = HwI2CM_CMD_RD_EN |HwI2CM_CMD_ACK_EN;
                nCount=0;
                while( !(pI2C->IRQSTR & Hw0) )   
                {
                    nCount++;
                    if(nCount > 100000)
                    {
                        RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                        return FALSE;
                    }
                }
                BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
                pReadBuffer[i] =(BYTE)pI2C->RXR0;
                RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]BUFFER[%d]:%x /n/r"),i,pReadBuffer[i]));
            }
            if(nMode != 4)
            pI2C->CMD0 = HwI2CM_CMD_STO_EN;
            nCount=0;
            while( !(pI2C->IRQSTR & Hw0) )   
            {
                nCount++;
                if(nCount > 100000)
                {
                    RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                    return FALSE;
                }
            }
            BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt
        }//readBytes   
        return TRUE;
    }

    //这个函数是重点,在于如何告诉你读写操作。可以看的出来,以写为例:

    //写

    首先把要写数据导入到Transmit Data Register   也就是TX 里面。首先需要写入的是目标地址和控制位,这里是些,所以

    pI2C->TXR0    = deviceAddr | I2C_WR;

    这样需要发送的数据就准备好了,下次就可以直接发送这个信息。

    pI2C->CMD0    = HwI2CM_CMD_STA_EN | HwI2CM_CMD_WR_EN;

    上面的这句话是表示,允许写操作,并且开始Start Condition Generation

    这样第一个语句就发送出去了,记住,这个只是类似于握手操作,我们真正需要的数据还没有开始传递。

    当Master发送出去数据后,Salve需要有反应,如果没有反应,说明地址不对,或者线路不对。所以我们需要检查是否有反应。

    while( !(pI2C->IRQSTR & Hw0) )   
            {
                nCount++;
                if(nCount > 100000)
                {
                    RETAILMSG(TC_LOG_LEVEL(TC_DEBUG),(TEXT("[I2C         ]TIMEOUT /n")));
                    return FALSE;
                }
            }

    实际上就是不停得检查IRQSTR第一位死否为1,如果是,则说明已经有了反馈信号。下面的这段话是Telechips8902的DataSheet关于I2C寄存器的:

    IRQ Status of I2C Master Controller Channel 0
    0/1
    IRQ Status Flag for I2C Master Controller of Channel 0.
    This would be read as ‘1’ when the IRQ status of I2C master controller
    channel 0 is activated.

    当检查到这个标志后,说明完成,我们也需要手动清除这个标志:

    BITSET( pI2C->CMD0, Hw0); //Clear a pending interrupt

    那写其他数据就是同样的道理了,只是注意,已经Start 了,不用重新Start.

    读也是一样的,发出读命令后,不停得检查ACK信息,当得到ACK信息后,就读取寄存器值RXR0即可。

    记住,读写完成后需要STOP操作。

     

    一般暴露给用户的是

    BOOL I2C_IOControl( DWORD hOpenContext, DWORD dwCode, PBYTE pBufIn, DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut, PDWORD pdwActualOut )
    这个函数,这个参数就很明显。

    展开全文
  • 硬件I2C与模拟I2C

    千次阅读 多人点赞 2019-03-11 17:48:22
    硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C;一般也较为稳定,但是程序较为繁琐。硬件(固件)I2C是直接调用内部寄存器进行配置;而软件I2C是...

    硬件I2C对应芯片上的I2C外设,有相应I2C驱动电路,其所使用的I2C管脚也是专用的,因而效率要远高于软件模拟的I2C;一般也较为稳定,但是程序较为繁琐。硬件(固件)I2C是直接调用内部寄存器进行配置;而软件I2C是没有寄存器这个概念的。

    软件I2C一般是使用GPIO管脚,用软件控制SCL,SDA线输出高低电平,模拟i2c协议的时序。

     

    例如下面这段I2C的开始和结束信号,我们使用GPIOB模拟:

     

    I2C接线
    GPIO引脚模拟I2C引脚
    GPIOB11SDA
    GPIOB12SCL

                                     

                                           

                                                                                             I2C开始/结束时序图

    代码如下:

    Void I2C_START(void)
    
    {
    
        GPIOB11 = High;
    
        GPIOB12 = High;
    
        Delay();
    
        GPIOB11 = Low;
    
        Delay();
    
        GPIOB12 = Low;
    
    }
    
    
    
    
    Void I2C_STOP(void)
    
    {
    
        GPIOB11 = Low;
    
        GPIOB12 = High;
    
        Delay();
    
        GPIOB11 = High;
    
    }

    同样,我们可以按照波形完成读/写一个字节的函数,再进一步封装完成更为复杂的功能(发送指令等等)

    硬件i2c程序员只要调用i2c的控制函数即可,不用直接的去控制SCL,SDA高低电平的输出。

    但是有些单片机的硬件i2c不太稳定,调试问题较多。例如网上很多人吐槽的STM32…

     

    主要对比

    1.硬件IIC用法比较复杂,模拟IIC的流程更清楚一些。

    2.硬件IIC速度比模拟快,并且可以用DMA

    3.模拟IIC可以在任何管脚上,而硬件只能在固定管脚上。

     

    展开全文
  • STM32硬件I2C例程,包括主机工程和从机工程,主机和从机可通过硬件I2C通信
  • STM32F4 硬件I2C 使用DMA

    热门讨论 2013-05-27 02:59:55
    STM32F4 硬件I2C 使用DMA 測試過OK
  • STM32硬件I2C与OLED

    千次阅读 2019-07-18 22:08:32
    RCT6为PA2,PA3软件I2C读写EEPROM,VET6为PB6,PB7硬件I2C读写EEPROM. 1)将I2C1改为I2C2 惠特自动化的OLED屏幕。 修改PB6,PB7引脚为PB10,PB11 I2C1全部替换为I2C2(仅OLED_I2C.c文件即可。) 2)OLED显示原理 如...

    RCT6为PA2,PA3软件I2C读写EEPROM,VET6为PB6,PB7硬件I2C读写EEPROM.

    1)将I2C1改为I2C2

    惠特自动化的OLED屏幕。

    修改PB6,PB7引脚为PB10,PB11

    I2C1全部替换为I2C2(仅OLED_I2C.c文件即可。)

    2)OLED显示原理

    如小写字母l,6*8显示,即需要x轴写入6次数据,每次数据表征y方向上的8个点。l的数据串为0x00, 0x00, 0x41, 0x7F, 0x40, 0x00

    则有:

    000000

    001110

    000100

    000100

    000100

    000100

    000100

    001100

    倒过来即为小写l。

    3)所购买的1.3寸屏为64*128像素点的。

    即全部填满需要写8行。

    每行写128次。(一次可以写1*8的面积)

    4)波形显示简单思路(待验证)

    一次存储8*128个8位二进制数,记为一个数据块。显示在屏幕上。

    待下一个8*128个u8收集够时,与原数据块逐数据比较,若相同则不变,若不同则刷新(应该可以防止闪烁问题。)

    5)OLED屏幕购买于淘宝龙嘉盛电子元器件商城,1.3寸

    展开全文
  • STM32 HAL库 硬件I2C 从机主机防BUG程序

    千次阅读 多人点赞 2020-05-24 02:23:31
    终于在结合各方资料即自己的思考后,做出了稳定的硬件I2C代码(这个文章中应该是目前为止能查到的最详述可用的硬件I2C代码),经过各种奇怪的I2C主机型号的蹂躏后,通讯都可以恢复正常,不会被卡死。证明该方案拥有...

    前言

    最近死磕了5天的STM32F1硬件I2C从机的程序,天天早上8点到凌晨,几乎全程心流状态。终于在结合各方资料及自己的思考后,做出了稳定的硬件I2C代码(这个文章中应该是目前为止能查到的最详述可用的硬件I2C代码),经过I2C主机发出的各种奇怪的信号蹂躏后,通讯都可以恢复正常,不会被卡死。证明该方案拥有极高稳定性。

    需要注意我这次使用的是 STM32F103C8T6 的兼容型号 GD32F103C8T6 。要问他的兼容性有多强,连I2C bug都能做到一样,哈哈。我当初用GD想着硬件I2C应该能舒服用了,万万没想到,兆易连i2c 硬件BUG都复制了。

    大家不要纠结于单片机的型号,我推测应该STM32FXXX 家族硬件I2C应该都是这个样子,具体情况我也无法一一测试,如果大家看了文章在自己的系统上测试成功后别忘了留个言,说下自己系统的配置,方便后来人~

    STM32 硬件I2C BUG简述

    相信很多接触STM32的用户在尝试使用I2C代码时都被警告过stm32 硬件I2C有bug,那么这个bug具体是什么,又是如何发生的呢,我们具体来分析一下,给我们之后代码解决这个问题来做个铺垫。

    首先我们来看一下STM32 I2C的一个神奇的寄存器,SR2BUSY 位,具体他是做什么的参考手册中已经很清晰的描述,我就不多说了,我直接说它的问题。

    当STM32 硬件I2C模块在通讯(无论做主机还是从机)中遇到总线被占用时,使得 STM32 在接管总线时发生总线仲裁失败,进而失去对总线的控制,导致BUSY位被置位,且无法通过使用官方驱动库自动清除。而后即表现为锁死状态。

    该情况多会出现在I2C通讯错误,或者从机程序接收到了非预定的I2C指令时产生。
    关于这个情况,在官方的一篇 《2C 接口进入 Busy 状态不能退出.pdf》 文档中也有描述,也正是这篇文档给了我思路,让我解决了这个问题。
    在这里插入图片描述在这里插入图片描述

    BUG实例

    由于我使用的是硬件I2C从机,所以以下内容均以I2C从机为主要内容讲述,(如果你使用的是主机也请耐心看完,因为主机也可以按照该思路修改后解决)也可能之后我会补充主机内容(主要是I2C做主机时,从机大多时候都成熟器件,想要人为产生BUG情况也蛮困难的)

    首先我们来看一段我用逻辑分析仪抓取的硬件系统的波形, 和造成该波形的测试代码(以下I2C写指令由主机发出,主机为其他公司的单片机承担,不建议使用两个STM32 硬件I2C对传测试,否则都出BUG找问题都找不到)
    在这里插入图片描述

        while (1)
        {
            while(HAL_I2C_Slave_Receive_IT(&hi2c1, &i2c_buff, 1) != HAL_OK)
            {
            }
            while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
            {
            }
            delay_ms(10);
        }
    
    • 问题分析:

    我们可以看到,由于我的代码中只有对一个I2C写指令接收的内容,而后就进入了延时等待状态,从而错过了主机连着发送的第二条写指令,我们的单片机甚至都没有对其做ACK响应。此时我们用调试器查看STM32寄存器,就会发现 BUSY进入了锁死状态。第二次进入的HAL_I2C_Slave_Receive_IT() 函数中会在等待 BUSY 清零的查询操作中超时退出,而后在之后的循环中再次循环这个过程。

    而如果我们把循环中的延时去掉,我们会发现I2C的通讯就会变得正常了。即, 由于我们没能对主机发出的第二条的I2C指令及时处理,STM32 I2C就会出问题。而这种情况在使用中是不可能避免的,主机任何一次的误操作或误编程都可能会导致我们的I2C锁死。

    解决方案

    放上代码

    void i2c_reset()
    {
        /* 开漏输出,关闭I2C输入通道,并尝试将总线拉高 */
        GPIO_InitTypeDef GPIO_InitStruct;
        GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9;
        GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
        HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8 | GPIO_PIN_9, GPIO_PIN_SET);
    
        // SCL PB8 拉高
        for (uint8_t i = 0; i < 10; i++) {
    
            if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_8) == GPIO_PIN_SET)
            {
                rt_kprintf("retry %d\n", i);
                break;
            }
            /* 该延时循环的周期和时长,请根据你的实际主机对I2C通讯出错的处理来修改 */
            rt_thread_mdelay(10);
        }
    
        /* 归还总线控制权 */
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        /* 复位I2C */
        hi2c1.Instance->CR1 |= I2C_CR1_SWRST;
        hi2c1.Instance->CR1 &= ~I2C_CR1_SWRST;
    
        /* 重新初始化I2C */
        MX_I2C1_Init();
    }
    
    /*
     * I2C最大的问题在于如果主机随意发送内容,而stm32作为从机时, 接收到程序意图之外的通讯内容后会卡死BUSY位。
     * 发生该情况后,可配置SCL SDA为开漏输出,关闭I2C输入通道防止错误再次被触发,而后尝试拉高SCL SDA线,或者
     * 等待总线被主机释放,这里的情况依据具体主机的不同可根据实际操作判断,最终我们需要等待I2C总线为空闲即SCL
     * SDA线为高电平保持时。将SCL SDA配回IIC,释放总线控制权 ,使用 SWRST复位I2C,清除全部I2C寄存器内容,
     * (BUSY位也会在该操作中被清除) 并重新初始化I2C总线。
     */
    void i2c_salve_thread(void *parameter)
    {
        while (1)
        {
            if(HAL_I2C_Slave_Receive_IT(&hi2c1, &i2c_buff, 1) != HAL_OK)
            {
            	// I2C设备出现故障无法开启接收
                i2c_reset();
            }
    
    		// 检测标志位,防止I2C被二次开启,导致BUG
            while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY)
            {
                rt_thread_mdelay(1);
            }
            rt_thread_mdelay(10);
        }
    }
    
    void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
    {
        direction = TransferDirection;
    }
    
    
    

    如代码中注释,我们在检测 HAL_I2C_Slave_Receive_IT() 返回超时或错误时,使用 i2c_reset() 函数来复位I2C硬件模块以重启I2C,使得STM32可以在主机恢复发送正常设定的I2C命令后可以恢复通讯。

    i2c_reset() 函数核心操作思路如下:

    1. 配置 对应IO为开漏输出,以关闭I2C模块的硬件输入通道,防止后续的通讯继续触发BUG,并尝试将总线拉高。

    2. 尝试让从机释放总线,或等待主机释放总线(此步骤根据你的实际系统为准,最好查一下波形,看看具体故障表现是什么,如果STM32是从机,那么主机在通讯失败后,大部分会在一定时间后超时并释放总线。所以我的代码中只是在一定时间内检测总线是否被释放。如果你是主机的话则可以按照 《2C 接口进入 Busy 状态不能退出.pdf》 文档中的方法,在SCL线上发送脉冲来使得从机释放I2C总线)还是一句话:该步骤根据你的实际系统为准,我们在这步中核心需求就是让总线被释放。

    3. 将引脚配置回I2C的模式,将总线归还STM32的I2C模块

    4. 将CR1寄存器的SWRST置位后再清零,以复位I2C模块,在此过程中I2C除DR寄存器外所有寄存器都会被清空,包括BUSY位 (此处记忆有些模糊,不确定DR寄存器是否会被清空。DR为I2C收发数据缓存寄存器)

    5. 重新初始化I2C,原因是我们上一步的复位操作清空了I2C的配置

    加入这些代码后,我们可以再次使用逻辑分析仪观察波形,或通过串口打印来检测程序运行。(由于逻辑分析仪波形太长,图片放不下就不做展示了)我们可以看到,不管主机如何发送诱发错误的信息,我们的代码都能让 stm32在i2c复位后的第一次接收时工作正常。而如果主机按照预定协议,间隔发送指令时,通讯就会完全恢复正常,不会触发i2c_reset() 函数。

    小计

    1. 如果使用 HAL_I2C_Slave_Receive_IT() 函数接收了主机发送的读取指令,并不会触发BUG,主机会读取回 0x00的数据。

    2. 例子中我使用了 RT-thread 实时操作系统,所以延时不大一样。使用RTOS的延时时还可以让我们在等待时,将CPU调度到其他线程使用,防止一个I2C占用全部CPU周期。

    3. 有了故障处理程序后,我们就可以使用 HAL 库中自带的 I2C_TwoBoards_AdvComIT 例程来处理收发数据了。我使用的官方HAL库例程路径:

       D:\ST\STM32Cube\Repository\STM32Cube_FW_F1_V1.7.0\Projects\STM32F103RB-Nucleo\Examples\I2C\I2C_TwoBoards_AdvComIT
      
    4. i2c_reset() 核心操作思路的步骤顺序有严格要求,随意变更1~5操作的前后顺序会导致BUG再次触发

    5. IIC总线调试具有特殊性,我们最好还是准备一个逻辑分析仪来抓取波形,结合DEBUG时对寄存器的查看来分析解决故障

    6. 接上一条,在调试IIC前,我们应该确保自己对STM IIC的各个寄存器和位功能以及I2C波形时序的熟悉,以求在出现问题时能够找到问题所在,新版的HAL库IIC内容很清晰,只要了解寄存器,通过DEBUG+查看波形 的方法可以很快定位解决错误。

    7. 示例代码仅提供思路,具体使用需要修改为你的配置。

    8. 注意一定要使用新版本的HAL库,我已经被旧版的有明显错误的HAL I2C库坑过了(居然直接对只读的标志位赋值,来想要清除标志位)

    总结

    STM32的IIC模块确实存在BUG, 具体表现就是在我们代码没有处理预设之外的IIC指令或数据时会发生由于总线仲裁失败,导致BUSY位锁死的问题。出现这种问题,只要我们能够用代码监测到此情况的发生,并使用上述的 i2c_reset()核心操作思路就可以解决,从而让我们在实际工程应用中使用。毕竟软件也许可以方便的模拟100khz的IIC主机,但对400khz I2c通讯来说,无论是主机还是从机都很难实现(从机的软件模拟技巧性很强,且CPU消耗也大)。

    最后

    如果这个文章,解决了你的问题,请留个言交流下,因为我目前精力有限也只是在自己的硬件上测试了这个内容,让我知道你的问题被解决会让我很开心,也会让之后的朋友不在为这个问题困扰~

    因为目前我的工程涉及到自己公司的产品,不方便直接发出,之后有空了,我会做一个示例工程挂到 githubgitee 上,方便大家修改使用。

    展开全文
  • 通过STM32 Cubermx 实现 硬件I2C对MPU6050的操作,包括DMP的移植,程序代码注释详细,有部分程序代码摘录于正点原子的函数, 基于STM32F407ZG 芯片开发
  • STM32F103,HAL实现软件I2C,与硬件I2C

    千次阅读 2019-03-15 00:20:32
    1,硬件I2C HAL_I2C_Mem_Write(&amp;hi2c1,0x88,0,I2C_MEMADD_SIZE_8BIT,0,0,0X10);//写 HAL_I2C_Master_Receive(&amp;hi2c1,0x89,(uint8_t*)datatemp,4,0x10);//读 2软件I2C #ifndef __MYI2C_H__ #...
  • nRF52832之硬件I2C

    万次阅读 2016-03-30 23:45:28
    这几天一直在折腾nRF52832的硬件I2C,到了今天终于出现了成果,在此也印证了那句话:“耕耘就有收获”52832的硬件I2C虽然官方提供了demo,但是自己对I2C通信理解的不够深入,再一个52832的代码也封装的太深了,但是对...
  • STM8硬件I2C配置

    千次阅读 2019-05-04 21:47:56
    首先感谢网上分享的朋友分享资料,网上有很多教程配置STM8硬件I2C,但是也有说STM8硬件I2C有问题的,不过我还是通过实际项目已经调通STM8硬件I2C,所以在此分享经验; 1.选项字节配置 void Flash_Init_I2c(void) {...
  • STM32F103 硬件I2C主从机通信

    千次阅读 热门讨论 2019-03-01 21:53:12
    肝了这么多天,查了很多资料,逛了不少论坛终于把stm32f103的硬件I2C写出来了,就先做个记录。 开发板是自己画的兼容原子精英两块板子,型号是f103zet6。主从用的都是硬件I2C,用的是st官方例程,感谢,同时官方还给...
  • 完整源码下载: https://github.com/simonliu009/STM32F10x-I2C-SHT20-LCD1602 ...上一篇我们通过硬件I2C读取了SHT20的温湿度信息并且通过printf重定向到USART1串口打印信息。那么这一次是通过硬件I...
  • stm32硬件I2C测试例程,亲测可用

    万次阅读 2018-04-22 22:56:03
    对于stm32的硬件I2C确实有不尽人意的地方。但是还是可以实现的,毕竟使用stm32的硬件I2C确实比使用IO口来模拟简单的多。下面的程序代码是使用stm32F03ZET6的I2C1(PB6,PB7)和AT24C02的EEPROM来测试的。希望对于需要的...
  • STM32CubeMX学习教程之十:硬件I2C读写AT24C02

    万次阅读 多人点赞 2018-04-23 17:36:01
    完整源码下载: ... 网上有流传已久一种说法,就是STM的I2C有bug,不好用。确实很多人在实际应用中都遇到了各种...有了STM32CubeMX,我们可以尝试使用硬件I2C了,官方的优化总不会有错了吧?(其实还是有个小bug,不...
  • STM8L051的硬件I2C调试

    千次阅读 2018-08-23 01:31:18
    I2C是现代一种极为常见的低速外设通信协议,比起SPI或者UART,它最大的优势应该就是节省芯片管脚了:理论上只要...本文不专门介绍I2C的时序和协议,而介绍我在调试STM8L051的硬件I2C的过程以及遇到的问题,和大家分...
  • 关于stm32硬件I2C BUSY锁死的一点说法

    千次阅读 2019-03-21 18:57:56
    首先在上一篇文章中我们搞出来了在没有外界干扰情况下的硬件i2c不卡死的一个办法 但是考虑到工程可靠性,我在后来又不断的对四个管脚进行了反复插拔测试,终于又发现了问题 在vcc和gnd两个脚上不论如何变化,硬件i2c...
  • STM32的硬件I2C与AT24C16

    千次阅读 2017-05-17 16:59:02
    刚学STM32的时候就听闻STM32的硬件I2C存在重大bug,会导致运行卡死在等待ACK的过程中,所以一直以来对其避而远之,转而以模拟I2C取代之。最近这段时间一直在用STM32 CubeMX,图形化设置界面屡试不爽,连USB这种复杂...
  • STM32的硬件I2C不好用,很多人在开发I2C的过程中,使用的是软件I2C进行调试。其实这么多年的改进,以及ST公司库函数的不断升级,现在可以比较方便的调用I2C,不过CubeMX的函数生成存在小问题,导致硬件i2c使用依然不...
  • STM32 的硬件I2C非常难以使用,调试成功的不多,这里的代码经过我们很长时间的开发才得到 加速度传感器的应用倒是次要的了
  • 龙芯1c库是把龙芯1c的常用外设的常用功能封装为一个库,类似于STM32库。完整源码请移步到...以此验证硬件I2C接口是否正常工作,其后在详细讲解是如何封装硬件I2C接口的。龙芯1c库中硬件I2C接口使用示例硬件...
  • stm32f030 硬件I2C配置

    千次阅读 2018-04-09 14:43:09
    使用硬件I2C的说明 STM32F0使用硬件I2C作为master,与外设通信,code步骤如下: 配置GPIO引脚功能 初始化I2C外设 调用I2C的外设库函数进行读写I2C 下面是详细代码: 1. 配置GPIO引脚功能 RCC_...
  • 完整源码下载: ... 上一篇教程我们使用I2C进行了EEPROM的读写操作,实际上I2C上面...本文的目标就是使用STM32CubeMX的硬件I2C读取麒麟座连接在I2C2上的SHT20芯片温湿度信息。 软件版本: STM32CubeMX V4.25.0   ...
  • 网上有些人说STM32的硬件I2C使用起来有问题,我用起来一点问题都没有,下面大致说一下最近做这个的心得 CubeMX设置 软件采用最新的CubeMX和SDK生成,芯片型号STM32F107RC 首先时钟使用常规的72M,这部分配置很多芯片...
  • EFM32 硬件I2C操作

    千次阅读 2015-02-05 17:07:42
    EFM32 硬件I2C 代码
  • 硬件I2C(查询方式) 以STC8F2K08S2单片机为例 一、I2C相关的寄存器 ① I2C 配置寄存器 ② I2C 主机控制寄存器 ③ I2C 主机辅助控制寄存器 ④ I2C 主机状态寄存器 ⑤ I2C 数据寄存器 ⑥ 外设端口切换...
  • STM32的硬件I2C实现及问题

    万次阅读 多人点赞 2018-08-14 18:17:05
    (网上传言STM32硬件I2C有问题,但仍然有人实现出来) 再次启动程序,依旧是停在原来的位置 等待EV6,网上搜索相关问题好多人都停在了等待EV5上。分析EV5等待问题,主机发送起始信号,没能接受从设备发送的应答...
  • 本篇详细的记录了如何使用STM32CubeMX配置STM32L431RCT6的硬件I2C外设读取EEPROM数据(以AT24C02为例)。
  • STM32F1系列 调试硬件I2C应注意的问题

    千次阅读 2016-03-06 01:07:41
    今天一直在调试STM32的硬件I2C,刚开始一直都没有调试成功。 出现的问题是有时候可以,有时候不可以,例如我写了一个接收多字节的函数,有时候接收9个字节可以,接收10个字节就不通了,让我摸不着头脑。 后来...
  • 鉴于各位对于stm32的硬件i2c均存在质疑且在此处下载的所有硬件i2c程序均基于一个模板 大家对于硬件i2c的说法均在初始化上而对于发送接收程序并没有多少改动 所以我个人对这一段进行了优化,加上了超时自动跳出和标志...
  • STM32 硬件I2C EEPROM命令解析

    千次阅读 2017-04-17 16:28:11
    STM32 硬件I2C EEPROM命令解析 转载▼     转载自互联网 void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite) {  u8 NumOfPage = 0, NumOfSingle = 0, ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 362,822
精华内容 145,128
关键字:

硬件i2c