本帖最后由 RISCVLAR 于 2020-12-30 19:06 编辑
CH32V103应用教程——I2C-软件模拟I2C读写EEPROM
前面章节第14章已经进行过硬件IIC读写EEPROM的实验,本章教程将使用软件模拟IIC读写EEPROM,并通过串口调试助手将读写结果打印显示。
1、I2C简介及相关函数介绍 内部集成电路总线(I2C)是一种两线式串行总线,可用于微控制器及其外围设备之间的通信。I2C总线由数据线SDA和时钟线SCL构成,可进行数据发送和接收,其通过上拉电阻接电源,当I2C总线空闲时,会输出高组态,此时数据线SDA和时钟线SCL均处于高电平。
I2C总线在使用过程中具有以下状态和信号: - 空闲状态:数据线SDA和时钟线SCL均处于高电平;
- 起始信号:时钟线SCL为高电平,数据线SDA由高电平向低电平跳变;
- 停止信号:时钟线SCL为高电平,数据线SDA由低电平向高电平跳变;
- 应答信号:应答信号分为ACK信号和NACK信号两种。主机在数据传输时会产生时钟,在产生第九个时钟时(即主机每发送完8位数据或地址),数据发送端会释放SDA的控制权,由数据接收端控制SDA,此时若SDA为高电平,则表示NACK应答信号,若SCL为低电平,则表示ACK应答信号。当为ACK信号时,发送方会继续发送下一个数据;为NACK信号时,则发送方会产生一个停止信号,结束数据传输。
关于硬件I2C和软件模拟I2C的区别,第14章已经进行过介绍,在此不再赘述。
关于I2C具体信息,可参考CH32V103应用手册。I2C标准库函数在第十四章节已介绍,在此不再赘述。
2、硬件设计 本章教程使用软件模拟I2C读写24C02,程序中配置PA1作为SDA线,PA2作为SCL线,连接方式如下: - PA1连接J5的SCL引脚
- PA2连接J5的SDA引脚
3、软件设计 软件模拟I2C读写24C02相较于硬件I2C读写24C02在程序代码量上多了很多,具体程序如下: iic.h文件 #ifndef __IIC_H
#define __IIC_H
#include "ch32v10x_conf.h"
#define IIC_SDA_H GPIO_SetBits(GPIOA,GPIO_Pin_1) //配置SDA接口高电平
#define IIC_SDA_L GPIO_ResetBits(GPIOA,GPIO_Pin_1) //配置SDA接口低电平
#define IIC_SCL_H GPIO_SetBits(GPIOA,GPIO_Pin_2) //配置SCL接口高电平
#define IIC_SCL_L GPIO_ResetBits(GPIOA,GPIO_Pin_2) //配置SCL接口低电平
#define I2C_SDA_READ() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) //读SDA口线状态
void IIC_Init(void); //IIC初始化函数
void IIC_Idle_State(void); //IIC空闲状态
void IIC_Start(void); //IIC开始信号函数
void IIC_Stop(void); //IIC停止信号函数
void IIC_SendByte(u8 data); //IIC发送一个字节
u8 IIC_ReadByte(void); //IIC读取一个字节
u8 IIC_WaitAck(void); //等待响应信号(ACK或者NACK)
void IIC_ACK(void); //IIC发出ACK信号
void IIC_NACK(void); //IIC发出NACK信号
#endif
iic.h文件主要进行相关宏定义和函数声明; iic.c文件 #include "iic.h"
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;//使用PA1和PA2作为模拟IIC引脚,PA1对应SDA,PA2对应SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ; //开漏输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
IIC_Idle_State();
}
//IIC空闲状态
//当IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为IIC总线的空闲状态
void IIC_Idle_State()
{
IIC_SDA_H;
IIC_SCL_H;
Delay_Us(4);
}
//IIC开始信号
//当IIC SCL线处于高电平时,SDA线由高电平向低电平跳变,为IIC开始信号,配置开始信号前必须保证IIC总线处于空闲状态
void IIC_Start()
{
IIC_SDA_H;
IIC_SCL_H;
Delay_Us(4);
IIC_SDA_L;
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
}
//IIC停止信号
//当IIC SCL线处于高电平时,SDA线由低电平向高电平跳变,为IIC停止信号
void IIC_Stop()
{
IIC_SDA_L;
IIC_SCL_H;
Delay_Us(4);
IIC_SDA_H;
}
//IIC发送一个字节数据(即8bit)
void IIC_SendByte(u8 data)
{
u8 i;
//先发送字节的高位bit7
for (i = 0; i < 8; i++)
{
if (data & 0x80) //判断8位数据每一位的值(0或1)
{
IIC_SDA_H;
}
else
{
IIC_SDA_L;
}
Delay_Us(4); //控制SCL线产生高低电平跳变,产生通讯时钟,同时利用延时函数在SCL为高电平期间读取SDA线电平逻辑
IIC_SCL_H;
Delay_Us(4);
IIC_SCL_L;
if (i == 7)
{
IIC_SDA_H; //控制SDA线输出高电平,释放总线,等待接收方应答信号
}
data <<= 1; //左移一个bit
Delay_Us(4);
}
}
//IIC读取一个字节
u8 IIC_ReadByte(void)
{
u8 i;
u8 value;
//读到第1个bit为数据的bit7
value = 0;
for(i = 0; i < 8; i++)
{
value <<= 1;
IIC_SCL_H;
Delay_Us(4);
if (I2C_SDA_READ()) //利用延时函数在SCL为高电平期间读取SDA线电平逻辑
{
value++;
}
IIC_SCL_L;
Delay_Us(4);
}
return value;
}
//IIC等待应答信号
u8 IIC_WaitAck(void)
{
uint8_t rvalue;
IIC_SDA_H; //发送端释放SDA总线,由接收端控制SDA线
Delay_Us(4);
IIC_SCL_H; //在SCL为高电平期间等待响应,若SDA线为高电平,表示NACK信号,反之则为ACK信号
Delay_Us(4);
if(I2C_SDA_READ()) //读取SDA线状态判断响应类型,高电平,返回去,为NACK信号,反之则为ACK信号
{
rvalue = 1;
}
else
{
rvalue = 0;
}
IIC_SCL_L;
Delay_Us(4);
return rvalue;
}
//产生应答信号ACK
void IIC_ACK(void)
{
IIC_SDA_L;
Delay_Us(4);
IIC_SCL_H; //在SCL线为高电平期间读取SDA线为低电平,则为ACK响应
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
IIC_SDA_H;
}
//产生非应答信号NACK
void IIC_NACK(void)
{
IIC_SDA_H;
Delay_Us(4);
IIC_SCL_H; //在SCL线为高电平期间读取SDA线为高电平,则为NACK响应
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
}
iic.c文件主要进行模拟I2C对应GPIO引脚初始化以及I2C在数据读写传输过程中各状态和信号的表示函数,具体如下:
1、因为采用模拟I2C,且设置PA1对应SDA线,PA2对应SCL线,因此需要对PA1和PA2进行GPIO初始化设置,具体如下: //初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_2;//使用PA1和PA2作为模拟IIC引脚,PA1对应SDA,PA2对应SCL
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD ; //开漏输出模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
IIC_Idle_State();
}
此处注意设置PA1和PA2的GPIO模式为开漏输出模式,因为I2C总线一般可以连接多个器件,如果选用推挽输出模式,可能会产生很大的电流从而导致器件毁坏,而开漏输出在不接上拉电阻情况下无法输出高电平,加上拉电阻时电流由上拉电阻决定,因此使用开漏输出模式。还有一个原因就是开漏输出可以匹配电平,满足电流型的驱动。
2、I2C空闲状态函数,当IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为IIC总线的空闲状态,函数具体如下: //IIC空闲状态
//当IIC总线的SDA和SCL两条信号线同时处于高电平时,规定为IIC总线的空闲状态
void IIC_Idle_State()
{
IIC_SDA_H;
IIC_SCL_H;
Delay_Us(4);
}
3、I2C开始信号函数,当IIC SCL线处于高电平时,SDA线由高电平向低电平跳变,为IIC开始信号,配置开始信号前必须保证IIC总线处于空闲状态,函数具体如下: //IIC开始信号
//当IIC SCL线处于高电平时,SDA线由高电平向低电平跳变,为IIC开始信号,配置开始信号前必须保证IIC总线处于空闲状态
void IIC_Start()
{
IIC_SDA_H;
IIC_SCL_H;
Delay_Us(4);
IIC_SDA_L;
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
}
4、I2C停止信号函数,当IIC SCL线处于高电平时,SDA线由低电平向高电平跳变,为IIC停止信号,函数具体如下: //IIC停止信号
//当IIC SCL线处于高电平时,SDA线由低电平向高电平跳变,为IIC停止信号
void IIC_Stop()
{
IIC_SDA_L;
IIC_SCL_H;
Delay_Us(4);
IIC_SDA_H;
}
5、I2C发送一个字节数据,一个字节数据即8位,对每一位值进行判断其为0还是1(从最高位起),并根据判断值控制SDA线高低电平变化,每判断完成一位,则左移一位。在此期间,要控制SCL线高低电平变化,具有通讯时钟的效果。同时利用延时函数在SCL线为高电平期间读取SDA线值大小。待8位数据传输完成之后,释放总线,等待接收方应答信号。具体函数如下: //IIC发送一个字节数据(即8bit)
void IIC_SendByte(u8 data)
{
u8 i;
//先发送字节的高位bit7
for (i = 0; i < 8; i++)
{
if (data & 0x80) //判断8位数据每一位的值(0或1)
{
IIC_SDA_H;
}
else
{
IIC_SDA_L;
}
Delay_Us(4); //控制SCL线产生高低电平跳变,产生通讯时钟,同时利用延时函数在SCL为高电平期间读取SDA线电平逻辑
IIC_SCL_H;
Delay_Us(4);
IIC_SCL_L;
if (i == 7)
{
IIC_SDA_H; //控制SDA线输出高电平,释放总线,等待接收方应答信号
}
data <<= 1; //左移一个bit
Delay_Us(4);
}
}
6、I2C读取一个字节数据,一个字节数据即8位,利用for循环在SCL为高电平期间对SDA数据线进行读取,依次判断每一位值为0还是1(共八位),最后将读取结果返回。具体函数如下: //IIC读取一个字节
u8 IIC_ReadByte(void)
{
u8 i;
u8 value;
//读到第1个bit为数据的bit7
value = 0;
for(i = 0; i < 8; i++)
{
value <<= 1;
IIC_SCL_H;
Delay_Us(4);
if (I2C_SDA_READ()) //利用延时函数在SCL为高电平期间读取SDA线电平逻辑
{
value++;
}
IIC_SCL_L;
Delay_Us(4);
}
return value;
}
7、I2C等待应答信号函数,当发送端发送完一个字节数据后,会释放对SDA线的控制权,及SDA线拉高,为1,此时由接收方控制SDA线,并控制其产生高低电平。在SCL线为高电平期间,发送端读取SDA线高低电平信号,若为低电平,表示ACK信号,若为高电平,表示NACK信号。具体函数如下: //IIC等待应答信号
u8 IIC_WaitAck(void)
{
uint8_t rvalue;
IIC_SDA_H; //发送端释放SDA总线,由接收端控制SDA线
Delay_Us(4);
IIC_SCL_H; //在SCL为高电平期间等待响应,若SDA线为高电平,表示NACK信号,反之则为ACK信号
Delay_Us(4);
if(I2C_SDA_READ()) //读取SDA线状态判断响应类型,高电平,返回去,为NACK信号,反之则为ACK信号
{
rvalue = 1;
}
else
{
rvalue = 0;
}
IIC_SCL_L;
Delay_Us(4);
return rvalue;
}
8、I2C应答信号函数和非应答信号函数。I2C 的数据和地址传输都带响应。响应包括“应答(ACK)”和“非应答(NACK)”两种 信号。作为数据接收端时,当设备(无论主从机)接收到 I2C 传输的一个字节数据或地址后, 若希望对方继续发送数据,则需要向对方发送“应答(ACK)”信号,发送方会继续发送下 一个数据;若接收端希望结束数据传输,则向对方发送“非应答(NACK)”信号,发送方接 收到该信号后会产生一个停止信号,结束信号传输。在SCL线为高电平期间读取SDA线为低电平,则为ACK响应;在SCL线为高电平期间读取SDA线为高电平,则为NACK响应。关于应答信号和非应答信号函数具体如下: //产生应答信号ACK
void IIC_ACK(void)
{
IIC_SDA_L;
Delay_Us(4);
IIC_SCL_H; //在SCL线为高电平期间读取SDA线为低电平,则为ACK响应
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
IIC_SDA_H;
}
//产生非应答信号NACK
void IIC_NACK(void)
{
IIC_SDA_H;
Delay_Us(4);
IIC_SCL_H; //在SCL线为高电平期间读取SDA线为高电平,则为NACK响应
Delay_Us(4);
IIC_SCL_L;
Delay_Us(4);
}
以上就是iic.c文件的各个函数,其表示I2C协议的各个环节。 eeprom.h文件 #ifndef __EEPROM_H
#define __EEPROM_H
#include "ch32v10x_conf.h"
#define AT24C01 127
#define AT24C02 255
#define AT24C04 511
#define AT24C08 1023
#define AT24C16 2047
#define AT24C32 4095
#define AT24C64 8191
#define AT24C128 16383
#define AT24C256 32767
//CH32V103开发板使用的是24C02,所以定义TYPE为AT24C02
#define TYPE AT24C02
#define SIZE 256
u8 AT24CXX_ReadOneByte(u16 raddr);
void AT24CXX_WriteOneByte(u16 waddr,u8 data);
void AT24CXX_WriteLenByte(u16 waddr,u32 data,u8 len);
u32 AT24CXX_ReadLenByte(u16 raddr,u8 len);
u8 AT24CXX_Check(void);
void AT24CXX_Read(u16 raddr,u8 *pBuffer,u16 num);
void AT24CXX_Write(u16 waddr,u8 *pBuffer,u16 num);
u8 AT24CXX_Test(void);
#endif
eeprom.h文件包含各种宏定义和函数声明; eeprom.c文件 #include "eeprom.h"
#include "iic.h"
//在AT24CXX指定地址读出一个数据
//raddr :开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 raddr)
{
u8 temp=0;
IIC_Start();
if(TYPE>AT24C16) //对芯片容量进行判断,芯片容量决定了设备地址和数据地址的分配方法
{
//当芯片容量大于16Kbit,设备地址则只起到一个标志读写操作的作用,由两位数据地址表示读写字节的地址
//因此此处首先发送0XA0,然后发送要写入地址的高八位,此处是将16位地址数据右移8位实现此操作
IIC_SendByte(0XA0); //发送写命令
IIC_WaitAck();
IIC_SendByte(raddr>>8);//发送高地址
IIC_WaitAck();
}
else
{
//当芯片容量小于16K时,设备地址中可能用于页寻址的位
IIC_SendByte(0XA0+((raddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_WaitAck();
IIC_SendByte(raddr%256); //发送低地址
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0XA1); //进入接收模式
IIC_WaitAck();
temp=IIC_ReadByte();
IIC_Stop(); //产生一个停止信号
return temp;
}
//在AT24CXX指定地址写入一个数据
//waddr :写入数据的目的地址
//data :要写入的数据
void AT24CXX_WriteOneByte(u16 waddr,u8 data)
{
IIC_Start();
if(TYPE>AT24C16)
{
IIC_SendByte(0XA0); //发送写命令
IIC_WaitAck();
IIC_SendByte(waddr>>8);//发送高地址
}else
{
IIC_SendByte(0XA0+((waddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_WaitAck();
IIC_SendByte(waddr%256); //发送低地址
IIC_WaitAck();
IIC_SendByte(data); //发送字节
IIC_WaitAck();
IIC_Stop(); //产生一个停止信号
Delay_Ms(10);
}
//在AT24CXX里面的指定地址开始读出长度为Len的数据,该函数用于读出16bit或者32bit的数据.
//raddr :开始读出的地址
//len :要读出数据的长度2,4
//返回值 :数据
u32 AT24CXX_ReadLenByte(u16 raddr,u8 len)
{
u8 t;
u32 temp=0;
for(t=0;t<len;t++)
{
temp<<=8;
temp+=AT24CXX_ReadOneByte(raddr+len-t-1);
}
return temp;
}
//在AT24CXX里面的指定地址开始写入长度为len的数据,该函数用于写入16bit或者32bit的数据.
//waddr :开始写入的地址
//data :数据数组首地址
//len :要写入数据的长度2,4
void AT24CXX_WriteLenByte(u16 waddr,u32 data,u8 len)
{
u8 t;
for(t=0;t<len;t++)
{
AT24CXX_WriteOneByte(waddr+t,(data>>(8*t))&0xff);
}
}
//检查AT24CXX是否正常,这里用了24XX的最后一个地址(255)来存储标志字.如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
if(temp==0X55)return 0;
else //排除第一次初始化的情况
{
AT24CXX_WriteOneByte(255,0X55);
temp=AT24CXX_ReadOneByte(255);
if(temp==0X55)return 0;
}
return 1;
}
//在AT24CXX里面的指定地址开始读出指定个数的数据
//raddr :开始读出的地址 对24c02为0~255
//pBuffer :数据数组首地址
//num :要读出数据的个数
void AT24CXX_Read(u16 raddr,u8 *pBuffer,u16 num)
{
while(num)
{
*pBuffer++=AT24CXX_ReadOneByte(raddr++);
num--;
}
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//waddr :开始写入的地址 对24c02为0~255
//pBuffer :数据数组首地址
//num :要写入数据的个数
void AT24CXX_Write(u16 waddr,u8 *pBuffer,u16 num)
{
while(num--)
{
AT24CXX_WriteOneByte(waddr,*pBuffer);
waddr++;
pBuffer++;
}
}
//AT24C02 读写测试
//正常返回1,异常返回0
u8 AT24CXX_Test(void)
{
u16 i;
u8 readbuf[SIZE];
u8 writebuf[SIZE];
/* 填充测试缓冲区 */
for (i = 0; i < SIZE; i++)
{
writebuf[i] = i;
}
AT24CXX_Write(0,(u8*)writebuf,SIZE);
AT24CXX_Read(0,(u8*)readbuf,SIZE);
for (i = 0; i < SIZE; i++)
{
if(readbuf[i] != writebuf[i])
{
printf("0x%4d ", readbuf[i]);
printf("错误:AT24C02读出与写入的数据不一致");
}
printf("%4d", readbuf[i]);
if ((i&15) == 15)
{
printf("\r\n");
}
}
printf("AT24C02读写测试成功\r\n");
return 1;
}
eeprom.c主要包含从EEPROM读取数据函数、向EEPROM写入数据函数以及EEPROM检测和测试函数,具体介绍如下: CH32V103开发板所用EEPROM芯片型号为AT24C02,其设备地址一共有7位,如下图,其中高4位固定为:1010b,低3位则由A0/A1/A2信号线的电平决定。根据CH32V103开发板原理图连接方式,A0、A1、A2均接GND,为0,所以设备地址为:1010000b。但由于I2C通讯通常将地址跟读写方向连在一起构成一个8位数,且当R/W位为0 时,表示写方向,加上7位地址,其值为“0xA0”,常称该值为I2C设备的“写地址”;当 R/W 位为 1 时,表示读方向,加上7位地址,其值为“0xA1”,常称该值为“读地址”。
1、关于根据AT24C02指定地址读取一个数据流程,首先对芯片容量进行判断,芯片容量决定了设备地址和数据地址的分配方法。当芯片容量大于16Kbit,设备地址则只起到一个标志读写操作的作用,由两位数据地址表示读写字节的地址,因此此处首先发送0XA0,然后发送要写入地址的高八位,此处是将16位地址数据右移8位实现此操作;当芯片的容量小于16k的时候,设备地址中可能用于页寻址的位:当芯片型号为AT24C02,则设备地址中无页寻址的位,则raddr的高八位均为0,则((raddr/256)<<1)也为0,因此最后就是发送0XA0。
当芯片型号为AT24C04,则设备地址中有一位页寻址的位,假如是1,则(raddr/256)为0X01,再左移一位为0x02,则最终发送的位0XA2。正好对应P0位为1,下面型号以此类推。关于从AT24Cxx中读取数据,却最先发送0XA0(写地址),解释如下:随机读需要设定需要读的地址,然后有一次伪写入过程,这个伪写入是为了修改存储器内部的工作指针。具体程序如下:
//在AT24CXX指定地址读出一个数据
//raddr :开始读数的地址
//返回值 :读到的数据
u8 AT24CXX_ReadOneByte(u16 raddr)
{
u8 temp=0;
IIC_Start();
if(TYPE>AT24C16) //对芯片容量进行判断,芯片容量决定了设备地址和数据地址的分配方法
{
//当芯片容量大于16Kbit,设备地址则只起到一个标志读写操作的作用,由两位数据地址表示读写字节的地址
//因此此处首先发送0XA0,然后发送要写入地址的高八位,此处是将16位地址数据右移8位实现此操作
IIC_SendByte(0XA0); //发送写命令
IIC_WaitAck();
IIC_SendByte(raddr>>8);//发送高地址
IIC_WaitAck();
}
else
{
//当芯片容量小于16K时,设备地址中可能用于页寻址的位
IIC_SendByte(0XA0+((raddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_WaitAck();
IIC_SendByte(raddr%256); //发送低地址
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0XA1); //进入接收模式
IIC_WaitAck();
temp=IIC_ReadByte();
IIC_Stop(); //产生一个停止信号
return temp;
}
2、关于根据AT24C02指定地址写入一个数据流程,与读取流程类似,在此不再介绍,具体程序如下: //在AT24CXX指定地址写入一个数据
//waddr :写入数据的目的地址
//data :要写入的数据
void AT24CXX_WriteOneByte(u16 waddr,u8 data)
{
IIC_Start();
if(TYPE>AT24C16)
{
IIC_SendByte(0XA0); //发送写命令
IIC_WaitAck();
IIC_SendByte(waddr>>8);//发送高地址
}else
{
IIC_SendByte(0XA0+((waddr/256)<<1)); //发送器件地址0XA0,写数据
}
IIC_WaitAck();
IIC_SendByte(waddr%256); //发送低地址
IIC_WaitAck();
IIC_SendByte(data); //发送字节
IIC_WaitAck();
IIC_Stop(); //产生一个停止信号
Delay_Ms(10);
}
其他函数大都在这两个函数基础上进行,在此不再介绍。 main.c文件 int main(void)
{
u8 datatemp[size];
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
USART_Printf_Init(115200);
IIC_Init();
printf("SystemClk:%d\r\n",SystemCoreClock);
if(AT24CXX_Check()==1)
{
/* 没有检测到EEPROM */
printf("24C02 Check Failed,Please Check!\n");
}
else
{
/* 没有检测到EEPROM */
printf("24C02 Check Succeed,Please Continue!\n");
}
if(AT24CXX_Test() == 0)
{
printf("24C02 Test Failed,Please Check!\n");
}
else
{
printf("24C02 Test Succeed,Please Check!\n");
}
AT24CXX_Write(0,(u8*)TEXT_Buffer,size);
AT24CXX_Read(0,datatemp,size);
printf("The Data Readed Is:%s\n",datatemp);
while(1)
{
}
}
main.c文件主要进行函数初始化以及AT24C02检测和读写测试。
4、下载验证 将编译好的程序下载到开发板并复位,同时将PA1连接J5的SCL引脚,PA2连接J5的SDA引脚,串口打印情况具体如下:
|