/* clear the ADDSEND bit */
i2c_flag_clear(I2C0, I2C_FLAG_ADDSEND);
if(1 == number_of_byte)
{
/* send a stop condition to I2C bus */
i2c_stop_on_bus(I2C_BUS[i2c_id]);
}
/* while there is data to be read */
while(number_of_byte)
{
if(3 == number_of_byte)
{
/* wait until BTC bit is set */
while(!i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_BTC));
/* disable acknowledge */
i2c_ack_config(I2C_BUS[i2c_id], I2C_ACK_DISABLE);
}
if(2 == number_of_byte)
{
/* wait until BTC bit is set */
while(!i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_BTC));
/* send a stop condition to I2C bus */
i2c_stop_on_bus(I2C_BUS[i2c_id]);
}
/* wait until the RBNE bit is set and clear it */
if(i2c_flag_get(I2C_BUS[i2c_id], I2C_FLAG_RBNE))
{
/* read a byte from the EEPROM */
*p_buffer = i2c_data_receive(I2C_BUS[i2c_id]);
/* point to the next location where the byte read will be saved */
p_buffer++;
/* decrement the read bytes counter */
number_of_byte--;
}
}
/* wait until the stop condition is finished */
while(I2C_CTL0(I2C_BUS[i2c_id]) & 0x0200);
/* enable acknowledge */
i2c_ack_config(I2C_BUS[i2c_id], I2C_ACK_ENABLE);
i2c_ackpos_config(I2C_BUS[i2c_id], I2C_ACKPOS_CURRENT);
}
最后看下主函数吧。
/*
brief main function
param[in] none
param[out] none
retval none
*/
int main(void)
{
//systick init
sysTick_init();
// led init
led_init(LED1);
//usart init 115200 8-N-1
com_init(COM1, 115200, 0, 1);
/* initialize EEPROM */
I2C_EE_Init(IIC0);
i2c_24c02_test(IIC0);
while(1)
{
led_toggle(LED1);
delay_ms(1000);
}
}
很简单,往AT24C02中写入数据,然后再读取数据,读写测试的函数如下:
/*
brief I2C read and write functions
param[in] i2c_typedef_enum i2c_id
param[out] none
retval I2C_OK or I2C_FAIL
*/
uint8_t i2c_24c02_test(i2c_typedef_enum i2c_id)
{
uint16_t i;
uint8_t i2c_buffer_write[BUFFER_SIZE];
uint8_t i2c_buffer_read[BUFFER_SIZE];
printf('\r\nAT24C02 writing...\r\n');
/* initialize i2c_buffer_write */
for(i = 0; i < BUFFER_SIZE; i++)
{
i2c_buffer_write[i] = i;
printf('0x%02X ', i2c_buffer_write[i]);
if(15 == i % 16)
{
printf('\r\n');
}
}
/* EEPROM data write */
eeprom_buffer_write(i2c_id, i2c_buffer_write, EEP_FIRST_PAGE, BUFFER_SIZE);
printf('AT24C02 reading...\r\n');
/* EEPROM data read */
eeprom_buffer_read(i2c_id, i2c_buffer_read, EEP_FIRST_PAGE, BUFFER_SIZE);
/* compare the read buffer and write buffer */
for(i = 0; i < BUFFER_SIZE; i++)
{
if(i2c_buffer_read[i] != i2c_buffer_write[i])
{
printf('0x%02X ', i2c_buffer_read[i]);
printf('Err:data read and write aren't matching.\n\r');
return I2C_FAIL;
}
printf('0x%02X ', i2c_buffer_read[i]);
if(15 == i % 16)
{
printf('\r\n');
}
}
printf('I2C-AT24C02 test passed!\n\r');
return I2C_OK;
}
当然在读写测试之前应该对AT24C02进行初始化操作。
/**
* @brief I2C 外设(EEPROM)初始化
* @param i2c_typedef_enum i2c_id
* @retval 无
*/
void I2C_EE_Init(i2c_typedef_enum i2c_id)
{
/* 选择EEPROM要写入的地址 */
#ifdef EEPROM_BLOCK0_ADDRESS
/* 选择 EEPROM Block0 来写入 */
eeprom_address = EEPROM_BLOCK0_ADDRESS;
#endif
#ifdef EEPROM_BLOCK1_ADDRESS
/* 选择 EEPROM Block1 来写入 */
eeprom_address = EEPROM_BLOCK2_ADDRESS;
#endif
#ifdef EEPROM_BLOCK2_ADDRESS
/* 选择 EEPROM Block2 来写入 */
eeprom_address = EEPROM_BLOCK2_ADDRESS;
#endif
#ifdef EEPROM_BLOCK3_ADDRESS
/* 选择 EEPROM Block3 来写入 */
eeprom_address = EEPROM_BLOCK3_ADDRESS;
#endif
i2c_gpio_config(i2c_id);
i2c_mode_config(i2c_id);
}
5.2 实验现象
最后,我们使用逻辑分析来查看数据。
使用的400kHz的速率,可以看到数据的写操作和前面分析的时序是一样的,完全吻合。
6 软件I2C
6.1 具体代码实现
首先实现I2C的协议。
/**
* @brief 延时nus
@param nus为要延时的us数. nus:0~190887435(最大值即2^32/fac_us@fac_us=22.5)
* @retval None
*/
void I2C_Delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD=nus*fac_us; //时间加载
SysTick->VAL=0x00; //清空计数器
SysTick->CTRL=0x01 ; //开始倒数
do
{
temp=SysTick->CTRL;
}while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达
SysTick->CTRL=0x00; //关闭计数器
SysTick->VAL =0X00; //清空计数器
}
/**
* @brief 延时nms
@param nms:要延时的ms数
* @retval None
*/
void I2C_Delay_ms(uint16_t nms)
{
uint32_t i;
for(i=0;i
注释很清楚,对照I2C的协议看就行。
接着就是实现AT2C02的读写操作。
/**
* @brief EEPROM_CheckOk, 判断串行EERPOM是否正常
* @param None
* @retval 1 表示正常, 0 表示不正常
*/
uint8_t EEPROM_CheckOk(void)
{
if (I2C_CheckDevice(EEPROM_DEV_ADDR) == 0)
{
return 1;
}
else
{
/* 失败后,切记发送I2C总线停止信号 */
I2C_Stop();
return 0;
}
}
/**
* @brief EEPROM_ReadBytes, 从串行EEPROM指定地址处开始读取若干数据
* @param _usAddress : 起始地址
* _usSize : 数据长度,单位为字节
* _pReadBuf : 存放读到的数据的缓冲区指针
* @retval 0 表示失败,1表示成功
*/
uint8_t EEPROM_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
uint16_t i;
/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
/* 第1步:发起I2C总线启动信号 */
I2C_Start();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_SendByte(EEPROM_DEV_ADDR | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
if (I2C_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
I2C_SendByte((uint8_t)_usAddress);
/* 第5步:发送ACK */
if (I2C_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
I2C_Start();
/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
I2C_SendByte(EEPROM_DEV_ADDR | I2C_RD); /* 此处是读指令 */
/* 第8步:发送ACK */
if (I2C_WaitAck() != 0)
{
goto cmd_fail; /* EEPROM器件无应答 */
}
/* 第9步:循环读取数据 */
for (i = 0; i < _usSize; i++)
{
_pReadBuf[i] = I2C_ReadByte(); /* 读1个字节 */
/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
if (i != _usSize - 1)
{
I2C_Ack(); /* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
}
else
{
I2C_NAck(); /* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
}
}
/* 发送I2C总线停止信号 */
I2C_Stop();
return 1; /* 执行成功 */
cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
/* 发送I2C总线停止信号 */
I2C_Stop();
return 0;
}
/**
* @brief EEPROM_WriteBytes, 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
* @param _usAddress : 起始地址
上一篇:GD32开发实战指南(基础篇) 第18章 CRC校验
下一篇:GD32 MCU超频后无法再次下载程序的解决办法
推荐阅读最新更新时间:2026-03-25 01:01
- 支持 BLE 连接、由 4mA 至 20mA 电流回路供电的现场发送器参考设计
- AM2DM-0515DH60-NZ ±15 Vout、2W 双路输出 DC-DC 转换器的典型应用
- LTC6261IDC 音频耳机桥式驱动器运算放大器的典型应用
- LTC1775CS 2.5V/5A 可调输出降压稳压器的典型应用电路
- AD8601ARTZ-REEL7 符合 PC100 标准的线路输出放大器的典型应用
- 一种基于分立的 315MHz 振荡器解决方案,用于使用 BFR182 射频双极晶体管的远程无钥匙进入系统
- 使用 ROHM Semiconductor 的 BD49E39G-TR 的参考设计
- AM30EW-2405SZ 5V 三路输出 DC/DC 转换器的典型应用
- LTC3564 的电池在 1.2A 应用中达到 1.2V
- AL1676EV2,基于 AP1676 高亮度降压 LED 驱动控制器的评估板



【Follow me第三季第4期】英飞凌CY8CPROTO-063-BLE开发板全任务实战源码
FANUC 0系统维修说明书
非常经典的关于LLC的杨波博士论文
VI-27WIU






京公网安备 11010802033920号