[APM32F4] 硬件I2C通信:速度飞快,但要小心卡死问题!

[复制链接]
 楼主| Fordhs168 发表于 2023-7-31 17:21 | 显示全部楼层 |阅读模式
本帖最后由 Fordhs168 于 2023-7-31 17:27 编辑

#技术资源#

  硬件I2C(Inter-Integrated Circuit)通信在APM32F407等微控制器上具有引人瞩目的速度和稳定性,成为众多应用领域的首选。相较于模拟I2C,硬件I2C不依赖于软件实现,而是由硬件直接控制数据传输,因此具备更快的通信速度。这意味着高效的数据交换,更快的响应时间,以及优化的性能表现。
然而,我们也应该认识到硬件I2C可能面临的一个主要问题——卡死或经常遇到总线BUSY。当通信中断、数据错误或外设故障发生时,硬件I2C有可能陷入僵局,导致整个系统崩溃。为了避免这种情况,开发者必须仔细设计错误处理机制,确保系统在异常情况下能够恢复正常运行。
尽管硬件I2C可能会面临一些挑战,但它的高速传输和稳定性仍然使其成为众多应用场景中的首选。无论是高频率数据传输、实时通信还是传感器读取,硬件I2C都能够胜任,并为您的应用程序带来卓越的性能表现。
综上所述,硬件I2C的速度之快让人叹为观止,但同时也需要我们谨慎对待可能出现的卡死问题。只要合理规划和处理,硬件I2C必将成为提高系统效率的得力工具。

下面给出基于APM32F407的硬件I2C例程供参考;
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date           Author       Notes
  8. * 2023-07-31     geehy       the first version
  9. */
  10. #ifndef APPLICATIONS_GEEHY_I2C_H_
  11. #define APPLICATIONS_GEEHY_I2C_H_
  12. #include <rtthread.h>
  13. #include "apm32f4xx_gpio.h"
  14. #include "apm32f4xx_i2c.h"
  15. #include "apm32f4xx_rcm.h"
  16. void I2C1_Init(void);
  17. void I2C_EE_BufferRead(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t ReadAddr, uint32_t NumByteToRead);
  18. void I2C_EE_BufferWrite(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t WriteAddr, uint32_t NumByteToWrite);
  19. void I2C_BufferWritePage(I2C_T* i2c,uint8_t DeviceAddr,uint16_t WriteAddr,uint8_t *pBuffer,uint32_t lenth);
  20. void EEprom_Test(void);

  21. #define SLAVE_ADDR 0X00A0  //AT24c256地址

  22. #endif /* APPLICATIONS_GEEHY_I2C_H_ */



  1. #include "Geehy_I2C.h"


  2. void I2C1_Init(void){
  3.    GPIO_Config_T gpioConfigStruct;
  4.    I2C_Config_T i2cConfigStruct;

  5.    /** Enable I2C related Clock */
  6.    RCM_EnableAHB1PeriphClockLPMode(RCM_AHB1_PERIPH_GPIOB);
  7.    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);
  8.    GPIO_ConfigPinAF(GPIOB,GPIO_PIN_SOURCE_6,GPIO_AF_I2C1);
  9.    GPIO_ConfigPinAF(GPIOB,GPIO_PIN_SOURCE_7,GPIO_AF_I2C1);
  10.    /** Free I2C_SCL and I2C_SDA */
  11.    gpioConfigStruct.mode = GPIO_MODE_AF;
  12.    gpioConfigStruct.otype = GPIO_OTYPE_OD;
  13.    gpioConfigStruct.speed = GPIO_SPEED_50MHz;
  14.    gpioConfigStruct.pin = GPIO_PIN_6;
  15.    GPIO_Config(GPIOB, &gpioConfigStruct);

  16.    gpioConfigStruct.mode = GPIO_MODE_AF;
  17.    gpioConfigStruct.otype = GPIO_OTYPE_OD;
  18.    gpioConfigStruct.speed = GPIO_SPEED_50MHz;
  19.    gpioConfigStruct.pin = GPIO_PIN_7;
  20.    GPIO_Config(GPIOB, &gpioConfigStruct);


  21.    /** Config I2C1 */
  22.    I2C_Reset(I2C1);
  23.    i2cConfigStruct.mode = I2C_MODE_I2C;
  24.    i2cConfigStruct.dutyCycle = I2C_DUTYCYCLE_2;
  25.    i2cConfigStruct.ackAddress = I2C_ACK_ADDRESS_7BIT;
  26.    i2cConfigStruct.ownAddress1 = 0X4f;
  27.    i2cConfigStruct.ack = I2C_ACK_ENABLE;
  28.    i2cConfigStruct.clockSpeed = 400000;
  29.    I2C_Config(I2C1,&i2cConfigStruct);
  30.    /** NVIC coniguration */
  31. //   NVIC_EnableIRQRequest(I2C1_EV_IRQn,1,0);
  32.    /** Enable the I2C1 Interrupt */
  33. //   I2C_EnableInterrupt(I2C1,I2C_INT_EVT);

  34.    /** Enable I2Cx */
  35.    I2C_Enable(I2C1);

  36. //   I2C_ClearIntFlag(I2C1,);


  37. }
  38. //void I2C_Start(I2C_T* i2c, uint8_t address, uint8_t direction){
  39. //}

  40. void I2C_EE_BufferRead(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t ReadAddr, uint32_t NumByteToRead){
  41.     uint8_t trans[2];
  42.     trans[0]=ReadAddr>>8;
  43.     trans[1]=ReadAddr;
  44.     while (I2C_ReadStatusFlag(i2c,I2C_FLAG_BUSBSY));
  45.     //产生起始信号
  46.     I2C_EnableGenerateStart(i2c);
  47.      //检测并清除EV5
  48.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
  49.     //发送从设备地址
  50.     I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_TX);

  51.     //检测并清除EV6
  52.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){}


  53.     /*AT24C256-从设备内部地址是16bit-分两次写入*/
  54.     I2C_TxData(i2c, trans[0]);
  55.     //检测并清除EV8
  56.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  57.     I2C_TxData(i2c, trans[1]);
  58.     //检测并清除EV8
  59.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  60.     //第二次Start信号
  61.     I2C_EnableGenerateStart(i2c);
  62.      //检测并清除EV5
  63.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
  64.     //发送从设备地址
  65.     I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_RX);
  66.     //检测并清除EV6
  67.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){}


  68.     while(NumByteToRead){
  69.     //读到最后一个数据时,发送非应答信号,结束传输
  70.         if(NumByteToRead==1){
  71.             //disable ACK
  72.         I2C_DisableAcknowledge(i2c);
  73.             //发送STOP信号
  74.         I2C_EnableGenerateStop(i2c);
  75.         }
  76.          while(I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_RECEIVED)==0){}


  77.         *pBuffer = I2C_RxData(i2c);

  78.         pBuffer++;

  79.         NumByteToRead--;

  80.     }
  81.     I2C_EnableAcknowledge(i2c);
  82. }
  83. void I2C_EE_BufferWrite(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t WriteAddr, uint32_t NumByteToWrite){
  84.     uint8_t trans[2];
  85.     trans[0]=WriteAddr>>8;
  86.     trans[1]=WriteAddr;
  87.     while (I2C_ReadStatusFlag(i2c,I2C_FLAG_BUSBSY));
  88.     //产生起始信号
  89.     I2C_EnableGenerateStart(i2c);
  90.      //检测并清除EV5
  91.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
  92.     //发送从设备地址
  93.     I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_TX);
  94.     //检测并清除EV6
  95.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){}

  96.      /*AT24C256-从设备内部地址是16bit-分两次写入*/
  97.     I2C_TxData(i2c, trans[0]);
  98.      //检测并清除EV8
  99.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  100.     I2C_TxData(i2c, trans[1]);
  101.      //检测并清除EV8
  102.     while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  103.     while(NumByteToWrite--){
  104.         I2C_TxData(i2c,*pBuffer);
  105.         pBuffer++;
  106.        //检测并清除EV8
  107.         while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  108.     }
  109.     I2C_EnableGenerateStop(i2c);
  110. }


  111. /*跨页写入数据*/
  112. void I2C_BufferWritePage(I2C_T* i2c,uint8_t DeviceAddr,uint16_t WriteAddr,uint8_t *pBuffer,uint32_t lenth)
  113. {
  114.     uint32_t page_remain = 64-WriteAddr%64;    //算出开始写入数据的某一页的剩余未写字节数
  115.     if(page_remain>=lenth)    //该页剩余的空间足够写入这些数据时
  116.     {
  117.         page_remain=lenth;    //把写入长度赋值
  118.     }
  119.     while(1)
  120.     {
  121.         rt_thread_mdelay(7);/*写完一页需要加个7ms左右延时,否者I2C容易卡死*/
  122.         I2C_EE_BufferWrite(i2c,pBuffer,DeviceAddr,WriteAddr,page_remain);

  123.         if(page_remain==lenth) break;   //判断是否写完,如果剩余的空间足够写入这些数据,就执行一次上面的函数,如果不够则进行下面的语句。

  124.         WriteAddr+=page_remain;    //如果不相等说明len比page_remain大,则地址偏移已经写入的长度
  125.         pBuffer+=page_remain;    //数据偏移
  126.         lenth-=page_remain;   //总长度减去已经写入的长度
  127.         if(lenth<64)   //判断剩余长度是否够写一整页,不够就赋值实际剩余长度,够就赋值64
  128.         {
  129.             page_remain=lenth;
  130.         }
  131.         else
  132.         {
  133.             page_remain=64;
  134.         }
  135.     }
  136. }

  137. void EEprom_Test(void){
  138.     u8 i=0;
  139.     uint8_t Wdata[255];
  140.     u8 Rdata[255];
  141.     /**eeprom*/
  142.     for( i = 0; i < 255; i++){
  143.         Wdata[i] = (i);
  144.     }
  145.     /*AT24C256 每页64字节 不可跨页操作,写入数据超过64字节需要做判断 -总共256K 分成512页*/
  146.     I2C_BufferWritePage(I2C1,SLAVE_ADDR,0x0000,Wdata,255);/*跨页操作*/
  147.     rt_thread_mdelay(7);
  148.     I2C_EE_BufferRead(I2C1,Rdata,SLAVE_ADDR,0x0020,255);/*从 AT24C256 读数据没有跨页限制   */
  149. }




评论

实试这个例程通用在F103、F407系列MCU上,硬件I2C,不同型号MCU,只需要修改一些头文件定义,IO配置等;  发表于 2023-7-31 17:25
updownq 发表于 2023-8-4 15:24 | 显示全部楼层
I2C总线是低速高阻总线,在PCB布线时要注意抗干扰
maudlu 发表于 2023-8-4 15:38 | 显示全部楼层
总线上挂载的设备数量应该适当,以避免影响通信质量和通信速度。
timfordlare 发表于 2023-8-4 15:47 | 显示全部楼层
在通信过程中增加异常处理机制,及时处理通信中断或协议错误,以避免卡死现象的发生。
burgessmaggie 发表于 2023-8-4 15:58 | 显示全部楼层
注意设置上拉电阻               
saservice 发表于 2023-8-4 16:06 | 显示全部楼层
硬件I2C通信的速度确实比软件I2C通信要快
mollylawrence 发表于 2023-8-4 16:16 | 显示全部楼层
尽可能缩短引线长度               
uiint 发表于 2023-8-4 16:25 | 显示全部楼层
I2C总线上的SCL和SDA线需要连接上拉电阻,以确保信号在空闲状态时保持高电平。
fengm 发表于 2023-8-4 16:33 | 显示全部楼层
在使用I2C传输之前做一个读取bus位来判断I2C总线是否卡死
sdCAD 发表于 2023-8-4 16:43 | 显示全部楼层
I2C总线的电缆长度应该尽量短,以减少信号的衰减和干扰。
hearstnorman323 发表于 2023-8-4 16:52 | 显示全部楼层
需要注意通信距离的限制              
primojones 发表于 2023-8-4 17:00 | 显示全部楼层
与STM32完全兼容但是解决了STM32的硬件I2C bug
i1mcu 发表于 2023-8-4 17:08 | 显示全部楼层
需要注意通信速率的选择              
abotomson 发表于 2023-8-4 17:16 | 显示全部楼层
在高速传输时,一些问题可能导致I2C通信卡死。
fengm 发表于 2023-8-4 17:31 | 显示全部楼层
通过优化通信协议,减少通信延迟和数据传输错误,从而降低卡死现象的发生。
yeates333 发表于 2023-8-4 17:39 | 显示全部楼层
从设备的上拉电阻应该选择合适的阻值
cemaj 发表于 2023-8-4 17:48 | 显示全部楼层
硬件IIC是有问题的              
tifmill 发表于 2023-8-4 17:55 | 显示全部楼层
硬件I2C 到底怎么样?               
chenjun89 发表于 2023-8-5 20:06 来自手机 | 显示全部楼层
硬件IIC速度飞快?何以见得?
weifeng90 发表于 2023-8-6 08:46 来自手机 | 显示全部楼层
第一,硬件IIC不算飞快。第二,跑死和硬件IIC有啥关系?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

17

主题

29

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部