[APM32F0] 实现APM32F003F6P6和EEPROM芯片(AT24C02C)的模拟I2C通信
·
AT24C02C 是一款由 MicrochipTechnology 生产的串行EEPROM(电可擦可编程只读存储器)芯片,是属于 I²C 接口的非易失性存储器件,可以用来存储需要掉电保存的数据,包括设备配置参数、校准数据和日志记录等。因为手里正好有一块带极海MCU APM32F003F6P6和AT24C02C的小板子,就抽空尝试用模拟I2C的方法实现了对AT24C02C芯片的数据写入和读取操作,下面就给大家分享一下基于模拟I2C的AT24C02C驱动代码及实测仿真结果。
硬件原理图部分如下所示:

对应的引脚是PB4接I2C的CLK时钟信号,PB5接I2C的DAT数据信号,首先需要对两个引脚进行初始化配置,这里需要注意,一般情况下,I2C的两个信号脚尽量配置为开漏输出模式,至于为什么要这么做,有兴趣的可以看下作者发的一篇文章(链接:https://bbs.21ic.com/icview-3464630-1-1.html)。以下便是引脚初始化配置代码:
复制
static void i2c_gpio_init(void){GPIO_Config_T gpioConfig;gpioConfig.mode = GPIO_MODE_OUT_OD; //open drain outputgpioConfig.speed = GPIO_SPEED_10MHz;gpioConfig.intEn = GPIO_EINT_DISABLE;gpioConfig.pin = I2C_CLK_PIN | I2C_DATA_PIN ;GPIO_Config(I2C_GPIO, &gpioConfig);I2C_CLK_SET; //init highI2C_DATA_SET; //init high}
其中为了增加可读性和移植性,用宏定义实现相关配置,代码如下:
复制
#define I2C_GPIO GPIOB#define I2C_CLK_PIN GPIO_PIN_4#define I2C_DATA_PIN GPIO_PIN_5#define I2C_CLK_SET GPIO_SetBit(I2C_GPIO, I2C_CLK_PIN)#define I2C_CLK_RESET GPIO_ClearBit(I2C_GPIO, I2C_CLK_PIN)#define I2C_DATA_SET GPIO_SetBit(I2C_GPIO, I2C_DATA_PIN)#define I2C_DATA_RESET GPIO_ClearBit(I2C_GPIO, I2C_DATA_PIN)#define I2C_DATA_MODE_IN GPIOB->MODE &= 0XFFFFFFDF;#define I2C_DATA_MODE_OUT GPIOB->MODE |= (1<<5);#define READ_I2C_DATA() GPIO_ReadInputBit(I2C_GPIO,I2C_DATA_PIN)
接着就是通过模拟I2C方法实现AT24C02C的驱动代码,所谓的模拟I2C,也就是通过写入GPIO口输出高低电平信号及读取GPIO高低电平信号的方法来模拟标准I2C的通信时序,类似的还有模拟UART和模拟SPI等。
以下即为AT24C02C的驱动代码,每个函数接口都加了注释说明:
复制
#include "at24c02c.h"void Delay_us(uint32_t delay){uint32_t i;for (i = 0; i < (delay*100); i++);}/**************************************************************函数名称:I2c_Start()功能:I2C起始信号IN:NONEOUT: NONE***************************************************************/void I2c_Start(){I2C_DATA_MODE_OUT;I2C_CLK_SET;I2C_DATA_SET;Delay_us(2);I2C_DATA_RESET;Delay_us(2);I2C_CLK_RESET;}/**************************************************************函数名称:I2c_Stop()功能:I2C停止信号IN:NONEOUT: NONE***************************************************************/void I2c_Stop(){I2C_DATA_MODE_OUT;Delay_us(2);I2C_DATA_RESET;Delay_us(3);I2C_CLK_SET;Delay_us(2);I2C_DATA_SET;Delay_us(5);}/**************************************************************函数名称:I2c_Free()功能:I2C总线空闲信号IN:NONEOUT: NONE***************************************************************/void I2c_Free(){I2C_DATA_MODE_OUT;I2C_DATA_SET;Delay_us(5);I2C_CLK_SET;Delay_us(5);}/**************************************************************函数名称:I2c_sendAck()功能:I2C发送应答信号IN:NONEOUT: NONE***************************************************************/void I2c_sendAck(){I2C_DATA_MODE_OUT;Delay_us(2);I2C_DATA_RESET;Delay_us(3);I2C_CLK_SET;Delay_us(5);I2C_CLK_RESET;}/**************************************************************函数名称:I2c_Nack()功能:I2C发送无应答信号IN:NONEOUT: NONE***************************************************************/void I2c_Nack(){I2C_DATA_MODE_OUT;Delay_us(2);I2C_DATA_SET;Delay_us(3);I2C_CLK_SET;Delay_us(5);I2C_CLK_RESET;}/**************************************************************函数名称:i2c_checkack()功能:I2C验证应答信号IN:NONEOUT: NONE***************************************************************/uint8_t i2c_checkack(){uint8_t temp;Delay_us(2);I2C_DATA_MODE_IN;Delay_us(3);I2C_CLK_SET;Delay_us(2);if(READ_I2C_DATA()){temp = 1;}else{temp = 0;}Delay_us(1);I2C_CLK_RESET;return temp;}/**************************************************************函数名称:I2c_Send_Byte()功能:I2C发送1个字节·数据IN:data :需要发送的数据OUT: NONE***************************************************************/void I2c_Send_Byte(uint8_t data){uint8_t temp;I2C_DATA_MODE_OUT;for(temp=0;temp<8;temp++){Delay_us(1);if((data & 0x80) == 0x80){I2C_DATA_SET;}else{I2C_DATA_RESET;}data <<= 1;Delay_us(2);I2C_CLK_SET;Delay_us(5);I2C_CLK_RESET;}}/**************************************************************函数名称:I2c_read_Byte()功能:I2C接收数据IN:NONEOUT: 接收到的数据***************************************************************/uint8_t I2c_read_Byte(){uint8_t temp,data;I2C_DATA_MODE_IN;for(temp=0;temp<8;temp++){data <<= 1;Delay_us(5);I2C_CLK_SET;Delay_us(2);if(READ_I2C_DATA()) {data += 1;}//Delay_us(3);I2C_CLK_RESET;}return(data);}/**************************************************************函数名称:write_24c02()功能:I2C写入数据到24c02IN:ADDR 写入地址 len 写入长度 data 写入数据BUFOUT: NONE***************************************************************/void write_24c02(uint8_t addr,uint8_t len,u32 data){uint8_t temp,data_len;u32 temp1;temp1 = data;I2c_Start();I2c_Send_Byte(0xa0);temp = i2c_checkack();I2c_Send_Byte(addr);temp = i2c_checkack();for(data_len=0;data_len<len;data_len++){I2c_Send_Byte((u8)temp1);temp = i2c_checkack();temp1 >>= 8;}I2c_Stop();}/**************************************************************函数名称:read_24c02()功能:从24c02中读出数据IN:ADDR 读出地址 len 读出长度 data 读出数据BUFOUT: NONE***************************************************************/u32 read_24c02(uint8_t addr,uint8_t len){uint8_t temp,data_len;u8 trmp2[4];u32 temp1=0;I2c_Start();I2c_Send_Byte(0xa0);temp = i2c_checkack();I2c_Send_Byte(addr);temp = i2c_checkack();Delay_us(5);I2c_Start();I2c_Send_Byte(0xa1);temp = i2c_checkack();for(data_len=0;data_len<len;data_len++){trmp2[data_len] = I2c_read_Byte();if((len-data_len) != 1){I2c_sendAck();}else{I2c_Nack();}}I2c_Stop();temp1 = trmp2[3];temp1 <<= 8;temp1 += trmp2[2];temp1 <<= 8;temp1 += trmp2[1];temp1 <<= 8;temp1 += trmp2[0];return (temp1);}/**************************************************************函数名称:write_24c02_byte()功能:24c02写入数据IN:ADDR 写入地址 data 写入数据BUFOUT: NONE***************************************************************/void write_24c02_byte(uint8_t addr,uint8_t data){uint8_t temp;I2c_Start();I2c_Send_Byte(0xa0);temp = i2c_checkack();I2c_Send_Byte(addr);temp = i2c_checkack();I2c_Send_Byte(data);temp = i2c_checkack();I2c_Stop();}/**************************************************************函数名称:read_24c02_byte()功能:从24c02中读出数据IN:ADDR 读出地址OUT: 读出的数据***************************************************************/uint8_t read_24c02_byte(uint8_t addr){uint8_t temp,data;I2c_Start();I2c_Send_Byte(0xa0);temp = i2c_checkack();I2c_Send_Byte(addr);temp = i2c_checkack();Delay_us(5);I2c_Start();I2c_Send_Byte(0xa1);temp = i2c_checkack();data = I2c_read_Byte();I2c_Nack();I2c_Stop();return data;}
其中Delay_us为非精准延时函数,对通信实时性要求不高的场景,可以将延时基准加长。write_24c02函数和read_24c02函数为写入指定长度数据和读取指定长度数据的接口;write_24c02_byte函数和read_24c02_byte函数为写入一个字节数据到指定地址和从指定地址读取一个字节数据的接口,实际使用时根据需求可自行选择。
接下来通过使用write_24c02_byte函数和read_24c02_byte函数编写测试代码实测一下写入和读取功能,测试代码为循环写入10个数据,写入的起始地址为1;写入完成后,再循环读取出来并保存到一个全局数组里,通过查看全局数组的值即可知道写入和读取是否成功。测试代码如下;
复制
#define EEPROM_DATA_LEN (10)uint8_t i2c_data[EEPROM_DATA_LEN];for(uint8_t i=0; i< EEPROM_DATA_LEN; i++){write_24c02_byte(i+1,i+0x55);}for(uint8_t i=0; i< EEPROM_DATA_LEN; i++){i2c_data[i]= read_24c02_byte(i+1);}
将GPIO初始化接口和测试代码一起,放到main初始化位置即可:

使用jlink仿真器+Keil IDE进入Debug运行测试,并使用Watch窗口监控i2c_data全局数组的值,实测结果如下图所示:

从以上结果可以看出,已成功完成对EEPROM芯片的数据写入和读取操作,但是在通信稳定性方面,当然也需要通过编写测试程序来验证,比如可以写入一定量的数据到不同的地址,然后将读取出来的数据与写入的数据逐一进行比较,如果出现不相等的情况,串口打印相关日志信息以记录,以此来判断通信稳定性,有兴趣的坛友如有条件,可以实测一下,顺带帮作者验证一下驱动代码是否存在Bug~~。



。
---------------------
作者:dffzh
链接:https://bbs.21ic.com/icview-3472400-1-1.html?_dsign=d8dd9fbe
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐

所有评论(0)