#技术资源#
硬件I2C(Inter-Integrated Circuit)通信在APM32F407等微控制器上具有引人瞩目的速度和稳定性,成为众多应用领域的首选。相较于模拟I2C,硬件I2C不依赖于软件实现,而是由硬件直接控制数据传输,因此具备更快的通信速度。这意味着高效的数据交换,更快的响应时间,以及优化的性能表现。
然而,我们也应该认识到硬件I2C可能面临的一个主要问题——卡死或经常遇到总线BUSY。当通信中断、数据错误或外设故障发生时,硬件I2C有可能陷入僵局,导致整个系统崩溃。为了避免这种情况,开发者必须仔细设计错误处理机制,确保系统在异常情况下能够恢复正常运行。
尽管硬件I2C可能会面临一些挑战,但它的高速传输和稳定性仍然使其成为众多应用场景中的首选。无论是高频率数据传输、实时通信还是传感器读取,硬件I2C都能够胜任,并为您的应用程序带来卓越的性能表现。
综上所述,硬件I2C的速度之快让人叹为观止,但同时也需要我们谨慎对待可能出现的卡死问题。只要合理规划和处理,硬件I2C必将成为提高系统效率的得力工具。
下面给出基于APM32F407的硬件I2C例程供参考;
/*
- Copyright © 2006-2021, RT-Thread Development Team
*
- SPDX-License-Identifier: Apache-2.0
*
- Change Logs:
- Date Author Notes
- 2023-07-31 geehy the first version
/
#ifndef APPLICATIONS_GEEHY_I2C_H_
#define APPLICATIONS_GEEHY_I2C_H_
#include <rtthread.h>
#include “apm32f4xx_gpio.h”
#include “apm32f4xx_i2c.h”
#include “apm32f4xx_rcm.h”
void I2C1_Init(void);
void I2C_EE_BufferRead(I2C_T i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t ReadAddr, uint32_t NumByteToRead);
void I2C_EE_BufferWrite(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t WriteAddr, uint32_t NumByteToWrite);
void I2C_BufferWritePage(I2C_T* i2c,uint8_t DeviceAddr,uint16_t WriteAddr,uint8_t *pBuffer,uint32_t lenth);
void EEprom_Test(void);
#define SLAVE_ADDR 0X00A0 //AT24c256地址
#endif /* APPLICATIONS_GEEHY_I2C_H_ */
#include “Geehy_I2C.h”
void I2C1_Init(void){
GPIO_Config_T gpioConfigStruct;
I2C_Config_T i2cConfigStruct;
/** Enable I2C related Clock */
RCM_EnableAHB1PeriphClockLPMode(RCM_AHB1_PERIPH_GPIOB);
RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_I2C1);
GPIO_ConfigPinAF(GPIOB,GPIO_PIN_SOURCE_6,GPIO_AF_I2C1);
GPIO_ConfigPinAF(GPIOB,GPIO_PIN_SOURCE_7,GPIO_AF_I2C1);
/** Free I2C_SCL and I2C_SDA */
gpioConfigStruct.mode = GPIO_MODE_AF;
gpioConfigStruct.otype = GPIO_OTYPE_OD;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.pin = GPIO_PIN_6;
GPIO_Config(GPIOB, &gpioConfigStruct);
gpioConfigStruct.mode = GPIO_MODE_AF;
gpioConfigStruct.otype = GPIO_OTYPE_OD;
gpioConfigStruct.speed = GPIO_SPEED_50MHz;
gpioConfigStruct.pin = GPIO_PIN_7;
GPIO_Config(GPIOB, &gpioConfigStruct);
/** Config I2C1 */
I2C_Reset(I2C1);
i2cConfigStruct.mode = I2C_MODE_I2C;
i2cConfigStruct.dutyCycle = I2C_DUTYCYCLE_2;
i2cConfigStruct.ackAddress = I2C_ACK_ADDRESS_7BIT;
i2cConfigStruct.ownAddress1 = 0X4f;
i2cConfigStruct.ack = I2C_ACK_ENABLE;
i2cConfigStruct.clockSpeed = 400000;
I2C_Config(I2C1,&i2cConfigStruct);
/** NVIC coniguration */
// NVIC_EnableIRQRequest(I2C1_EV_IRQn,1,0);
/** Enable the I2C1 Interrupt */
// I2C_EnableInterrupt(I2C1,I2C_INT_EVT);
/** Enable I2Cx */
I2C_Enable(I2C1);
// I2C_ClearIntFlag(I2C1,);
}
//void I2C_Start(I2C_T* i2c, uint8_t address, uint8_t direction){
//}
void I2C_EE_BufferRead(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t ReadAddr, uint32_t NumByteToRead){
uint8_t trans[2];
trans[0]=ReadAddr>>8;
trans[1]=ReadAddr;
while (I2C_ReadStatusFlag(i2c,I2C_FLAG_BUSBSY));
//产生起始信号
I2C_EnableGenerateStart(i2c);
//检测并清除EV5
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
//发送从设备地址
I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_TX);
//检测并清除EV6
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){}
/*AT24C256-从设备内部地址是16bit-分两次写入*/
I2C_TxData(i2c, trans[0]);
//检测并清除EV8
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_TxData(i2c, trans[1]);
//检测并清除EV8
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
//第二次Start信号
I2C_EnableGenerateStart(i2c);
//检测并清除EV5
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
//发送从设备地址
I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_RX);
//检测并清除EV6
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)){}
while(NumByteToRead){
//读到最后一个数据时,发送非应答信号,结束传输
if(NumByteToRead==1){
//disable ACK
I2C_DisableAcknowledge(i2c);
//发送STOP信号
I2C_EnableGenerateStop(i2c);
}
while(I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_RECEIVED)==0){}
*pBuffer = I2C_RxData(i2c);
pBuffer++;
NumByteToRead--;
}
I2C_EnableAcknowledge(i2c);
}
void I2C_EE_BufferWrite(I2C_T* i2c,uint8_t* pBuffer,uint8_t DeviceAddr, uint16_t WriteAddr, uint32_t NumByteToWrite){
uint8_t trans[2];
trans[0]=WriteAddr>>8;
trans[1]=WriteAddr;
while (I2C_ReadStatusFlag(i2c,I2C_FLAG_BUSBSY));
//产生起始信号
I2C_EnableGenerateStart(i2c);
//检测并清除EV5
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_MODE_SELECT));
//发送从设备地址
I2C_Tx7BitAddress(i2c,DeviceAddr,I2C_DIRECTION_TX);
//检测并清除EV6
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){}
/*AT24C256-从设备内部地址是16bit-分两次写入*/
I2C_TxData(i2c, trans[0]);
//检测并清除EV8
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
I2C_TxData(i2c, trans[1]);
//检测并清除EV8
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
while(NumByteToWrite--){
I2C_TxData(i2c,*pBuffer);
pBuffer++;
//检测并清除EV8
while(!I2C_ReadEventStatus(i2c,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}
I2C_EnableGenerateStop(i2c);
}
/跨页写入数据/
void I2C_BufferWritePage(I2C_T* i2c,uint8_t DeviceAddr,uint16_t WriteAddr,uint8_t pBuffer,uint32_t lenth)
{
uint32_t page_remain = 64-WriteAddr%64; //算出开始写入数据的某一页的剩余未写字节数
if(page_remain>=lenth) //该页剩余的空间足够写入这些数据时
{
page_remain=lenth; //把写入长度赋值
}
while(1)
{
rt_thread_mdelay(7);/写完一页需要加个7ms左右延时,否者I2C容易卡死*/
I2C_EE_BufferWrite(i2c,pBuffer,DeviceAddr,WriteAddr,page_remain);
if(page_remain==lenth) break; //判断是否写完,如果剩余的空间足够写入这些数据,就执行一次上面的函数,如果不够则进行下面的语句。
WriteAddr+=page_remain; //如果不相等说明len比page_remain大,则地址偏移已经写入的长度
pBuffer+=page_remain; //数据偏移
lenth-=page_remain; //总长度减去已经写入的长度
if(lenth<64) //判断剩余长度是否够写一整页,不够就赋值实际剩余长度,够就赋值64
{
page_remain=lenth;
}
else
{
page_remain=64;
}
}
}
void EEprom_Test(void){
u8 i=0;
uint8_t Wdata[255];
u8 Rdata[255];
/**eeprom/
for( i = 0; i < 255; i++){
Wdata = (i);
}
/AT24C256 每页64字节 不可跨页操作,写入数据超过64字节需要做判断 -总共256K 分成512页/
I2C_BufferWritePage(I2C1,SLAVE_ADDR,0×0000,Wdata,255);/跨页操作/
rt_thread_mdelay(7);
I2C_EE_BufferRead(I2C1,Rdata,SLAVE_ADDR,0×0020,255);/从 AT24C256 读数据没有跨页限制 */
}