目录
一、写在前面
二、功能描述
三、主要模块介绍
3.1 I2C总线介绍
3.2 I2C总线协议
3.2.1数据有效规定
3.2.2起始信号和停止信号
3.2.3 发送应答和接收应答
3.2.4 主机发送一个字节和接收一个字节
3.3 AT24C02介绍
3.3 字节写和随机读
四、测试文件test.c
五、现象描述
一、写在前面
- AT24C02芯片有I2C接口,但是51单片机是没有I2C接口的,是用软件程序模拟I2C总线。
- 51单片机是主机,AT24C02是从机。在这个实验中最重要的就是,弄清是主机还是从机去发送或接收数据。
- 默认是主机控制SCL、SDA线。从机想控制SDA线,发送数据就要主机释放总线(SDA=1)。
二、功能描述
用单片机模拟I2C与AT24C02通信,把数据写入AT24C02中,利用AT24C02存储数据掉电不丢失的特点,在单片机重新启动后,把对应地址的数据读取出来。
三、主要模块介绍
3.1 I2C总线介绍
I2C 总线(Inter IC Bus)只有两根双向信号线。一根是数据线 SDA,另一根是时钟线 SCL。有时钟线而且是由主机发送,从机接收或者由从机发送,主机接收,所以I2C是同步、半双工的通讯方式。
3.2 I2C总线协议
3.2.1数据有效规定
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
3.2.2起始信号和停止信号
- 起始信号:当SCL为高电平时,SDA由高电平向低电平变化。
- 停止信号:当SCL为高电平时,SDA由低电平向高电平变化。
起始和终止信号都是由主机发出的,在起始信号产生后,总线就处于被占用的状态;在终止信号产生后,总线就处于空闲状态。
起始和终止信号由程序模拟如下:
/*
*函数名: I2C_star()
*函数功能:I2C的起始信号
*输入: 无
*输出: 无
*/
void I2C_star()
{
I2C_SDA=1;
I2C_SCL=1;
I2C_SDA=0;
I2C_SCL=0;
}
/*
*函数名: I2C_stop()
*函数功能:I2C的停止信号
*输入: 无
*输出: 无
*/
void I2C_stop()
{
I2C_SDA=0;
I2C_SCL=1;
I2C_SDA=1;
}
3.2.3 发送应答和接收应答
发送应答和接收应答都是相对于主机而言的。应答是低电平脉冲,非应答是高电平脉冲。
- 发送应答:当主机在接收从机发送的一个数据后,主机发送应答信号给从机,还要不要继续发送数据。
- 接收应答:主机发送一个数据给从机后,从机通过控制SDA(此时主机要释放总线)发送应答信号,主机根据应答信号决定还要不要发送数据。
程序模拟如下:
/*
*函数名: I2C_SendAck(bit ack)
*函数功能: 主机发送应答
*输入: ack:发送的一个应答
*输出: 无
*/
void I2C_SendAck(bit ack)//主机发送应答
{
I2C_SDA=ack;
I2C_SCL=1;
I2C_SCL=0;
}
/*
*函数名: I2C_ReadAck()
*函数功能: 主机接收应答
*输入: 无
*输出: ack:接收的应答
*/
bit I2C_ReadAck()//主机接收应答
{
bit ack=0;
I2C_SDA=1;//释放总线
I2C_SCL=1;
ack=I2C_SDA;
I2C_SCL=0;
return ack;
}
3.2.4 主机发送一个字节和接收一个字节
- 主机发送一个字节数据:在SCL低电平时,主机把数据依次写到SDA线上(高位在前),再拉高SCL,从机将在SCL高电平期间读取数据位,循环8次,即可发送一个字节数据。
- 主机接收一个字节数据:首先主机释放总线(SDA=1),在SCL低电平时,从机把数据依次写到SDA线上(高位在前),再拉高SCL,主机将在SCL高电平期间读取数据位,循环8次,即可接收一个字节数据。
程序如下:
/*
*函数名: I2C_Writebyte(unsigned char byte)
*函数功能: I2C总线主机发送一个字节数据
*输入: byte:要发送的字节数据
*输出: 无
*/
void I2C_Writebyte(unsigned char byte)//主机发送一个字节的数据
{
unsigned char i=0;
for(i=0;i<8;i++)
{
I2C_SDA=byte&(0x80>>i);
I2C_SCL=1;
I2C_SCL=0;
}
}
/*
*函数名: I2C_Readbyte()
*函数功能: I2C总线主机接收一个字节数据
*输入: 无
*输出: byte:读取的数据
*/
unsigned char I2C_Readbyte()//主机接收一个字节数据
{
unsigned char i,byte=0;
I2C_SDA=1;//释放总线
for(i=0;i<8;i++)
{
I2C_SCL=1;
if(I2C_SDA==1){byte|=(0x80>>i);}//I2C_SDA上的数据已经是从机发送的数据
I2C_SCL=0;
}
return byte;
}
3.3 AT24C02介绍
我们开发板上使用的是 AT24C02(EEPROM)芯片,此芯片具有 I2C 通信接口,芯片内保存的数据在掉电情况下都不丢失, 所以通常用于存放一些比较重要的数据等。
3.3 字节写和随机读
I2C总线寻址按照从机地址位数可分为两种,一种是 7 位,另一种是 10 位。采用 7 位的寻址字节的位定义如 下:
一个从机 的 7 位寻址位有 4 位是固定位,3 位是可编程位。
- AT24C02的固定为是1010,可编程位本开发板上为000。从机地址加读/写位(0/1),就是起始信号后的第一个字节。
- 字节写的顺序:起始信号+从机地址带读/写位(0/1)+接收应答+指定写入数据的地址+接收应答+写入的数据+接收应答+停止信号。
- 随机读的顺序:起始信号+从机地址带读/写位(0/1)+接收应答+指定读取数据的地址+接收应答+ 起始信号+从机地址带读/写位(0/1)+接收应答+主机接收数据+发送应答+停止信号。
程序如下:文章来源:https://www.toymoban.com/news/detail-634943.html
#define AT24C02_address 0xA0 //AT24C02的地址
/*
*函数名: AT24C02_Writebyte(unsigned char word_address,byte)
*函数功能:向AT24C02中的某个地址写入一个字节数据
*输入: word_address:字节地址 byte:写入的字节数据
*输出: 无
*/
void AT24C02_Writebyte(unsigned char word_address,byte)
{
bit ack=0;
I2C_star();
I2C_Writebyte(AT24C02_address);
ack=I2C_ReadAck();
I2C_Writebyte(word_address);
ack=I2C_ReadAck();
I2C_Writebyte(byte);
ack=I2C_ReadAck();
I2C_stop();
}
/*
*函数名: AT24C02_Readbyte(unsigned char word_address)
*函数功能: 主机读取AT24C02的指定地址的数据
*输入: word_address:数据的地址
*输出: byte:被读取的字节数据
*/
unsigned char AT24C02_Readbyte(unsigned char word_address)
{
unsigned char byte=0;
bit ack=0;
I2C_star();
I2C_Writebyte(AT24C02_address);
ack=I2C_ReadAck();
I2C_Writebyte(word_address);
ack=I2C_ReadAck();
I2C_star();
I2C_Writebyte(AT24C02_address|0x01);
ack=I2C_ReadAck();
byte=I2C_Readbyte();
I2C_SendAck(1);
I2C_stop();
return byte;
}
四、测试文件test.c
#include <REGX52.H>
#include"I2C.h"
#include"LCD1602.h"
#include"AT24C02.h"
#include"Independentkey.h"
#include"Delay.h"
int main()
{
unsigned char keynum,num=0;
LCD_Init();
LCD_ShowNum(1,1,0,3);
AT24C02_Writebyte(0x01,1);
Delay(5);
AT24C02_Writebyte(0x02,2);
Delay(5);
AT24C02_Writebyte(0x03,3);
Delay(5);
AT24C02_Writebyte(0x04,4);
Delay(5);
while(1)
{
keynum=Independentkey();
if(keynum!=0)
{
if(keynum==1)
{
num=AT24C02_Readbyte(0x01);
LCD_ShowNum(1,1,num,3);
}
if(keynum==2)
{
num=AT24C02_Readbyte(0x02);
LCD_ShowNum(1,1,num,3);
}
if(keynum==3)
{
num=AT24C02_Readbyte(0x03);
LCD_ShowNum(1,1,num,3);
}
if(keynum==4)
{
num=AT24C02_Readbyte(0x04);
LCD_ShowNum(1,1,num,3);
}
}
}
}
五、现象描述
在AT24C02的0x01,0x02,0x03,0x04地址处,写入1,2,3,4,然后按下独立按键,分别读取出来(可掉电不丢失数据),并显示在LCD1602上。文章来源地址https://www.toymoban.com/news/detail-634943.html
- 写在后面:当遇到听不懂的或者程序出现什么问题,首先不应该去抱怨什么什么这么难,先是静下心来,一遍听不懂我就听两遍,两边不懂就三遍,直到听懂为止。也可以出去走走,把思路捋清,再回去看视频,有些东西别人以为你是知道的就没说,但实际上你是不知道的,导致思路很混乱,觉得为什么是这样的啊,从而卡在那里很久。写在前面的内容就是我在看视频过程中不知道的点和值得注意的点。程序运行不出来就仔细找问题的原因,以前一直以为Warning没什么用,就只是报出个警告,程序照样可以运行,这次找问题找半天,原来是有个if()语句判断相等的时候只有一个“=”号,编译器报警告但是没有报错,我就以为是我程序哪个地方写的不对,改改改的,浪费很多时间。最后,遇到问题最重要的就是静下心来,仔细寻找问题原因,然后去解决问题。
到了这里,关于51单片机——模拟I2C总线与AT24C02通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!