I2C总线——EEPROM读写——51单片机模拟通讯

发布者:温文儒雅最新更新时间:2020-06-13 来源: eefocus关键字:I2C总线  EEPROM读写  51单片机  模拟通讯 手机看文章 扫描二维码
随时随地手机看文章

I2C基础归纳

两根信号线,一根数据一根时序,主从模式,一应一答。龙顺宇讲stm8时举的例子:衙门断案,非常形象。今天在书店偶然看到,仔细翻阅了一下,收获很大。


我觉得这个难点主要在于应答位的掌握,究竟是主机应答还是从机应答,因为有的时候即便应答位设置错误,也能正常写入。这就导致了没有示波器情况下,无法简单的通过应答位来判断读写是否成功。所幸Proteus有I2C debug功能,今天一天终于分析出了可以稳定使用的模拟方式。还有就是接收,写入,开始,停止等子函数运行后SCL和SDA高低电平影响下一个子函数的问题。


I2C时序

我按照书上的几个时序分解画了一下,datasheet没太看懂。。。


借此机会,发现了一个画时序图很好用的软件TimeGen。

需要注意的就是,主从应答信号前一个操作结束,应该在SCL = 0后,将SDA = 1,不然从机信号无法发送。不,主要是因为线与的关系,都是漏极开路,无法检测信号。


其余的没啥难度,下降沿触发开始,上升沿结束,有效数据要保持5us左右。然后就可以根据这些时序,将子函数依次写出来。


全部程序

#include

#include

 

sbit SDA = P2^0;

sbit SCL = P2^1;

unsigned char ACK_flag = 0;

 

void delay_5us();

void I2C_init();

void I2C_Start();

void I2C_Stop();

void I2C_Send(unsigned char byte);

unsigned char I2C_Get();

void I2C_ACK_Send(bit A);

bit I2C_ACK_Get();

 

void I2C_Write(unsigned char add_7,unsigned char add,unsigned char byte);

unsigned char I2C_Read(unsigned char add_7,unsigned char add);

 

 

void delay(unsigned int z)

{

unsigned int i;

unsigned char j;

for(i = z;i>0;i--)

for(j =  114;j>0;j--);

}

 

 

 

void main()

{

I2C_init(); //初始化

/* 写入段 */

I2C_Write(0xa0,0x20,0x55);

delay(40);

 

/* 读取段 */

P1 = I2C_Read(0xa0,0x20);

if(ACK_flag) P1 = 0x00; //校验是否出现无应答

while(1);

}

 

/* 延时5微秒 */

void delay_5us()

{

_nop_();

}

 

/* I2C初始化 */

void I2C_init()

{

SCL = 1;  //拉高SDA和SCL

_nop_();

SDA = 1;

delay_5us();

}

 

/* I2C开始信号 */

void I2C_Start()

{

SDA = 1;

SCL = 1; //打开时钟

delay_5us();

SDA = 0; //产生SDA下降沿,触发开始信号

delay_5us();

}

 

/* I2C结束信号 */

void I2C_Stop()

{

SCL = 0;

SDA = 0;

_nop_();

SCL = 1;     //打开时钟

delay_5us();

SDA = 1;     //产生SDA上升沿,触发结束信号

delay_5us();

}

 

/* I2C数据发送 */

void I2C_Send(unsigned char byte)

{

unsigned char i,temp;

temp = byte;

for(i = 0;i<8;i++)

{

SCL = 0; //关闭时钟准备数据变化

if(temp & 0x80) //从最高位发送 1000 0000

{

SDA = 1;

}

else

{

SDA = 0;

}

delay_5us();

SCL = 1;   //打开时钟发送数据

delay_5us();

temp <<= 1;

}

}

 

/* I2C数据接收 */

unsigned char I2C_Get()

{

unsigned char i,byte;

for(i = 0;i<8;i++)

{

SCL = 0;  //关闭时钟准备数据变化

_nop_();

SCL = 1;     //打开时钟接收数据

delay_5us();

if(SDA) byte++; //从最高位接收

SCL = 0; //接收完毕关闭时钟

if(i == 7) return byte; 

byte <<= 1;

}

return 0x50;

}

 

/* I2C主机应答 */

void I2C_ACK_Send(bit A)

{

SCL = 0;

_nop_();

if(A) //如果i = 1那么拉低数据总线,表示主机应答。

{

SDA = 0;

} //如果i = 0发送非应答

else

{

SDA = 1;

}

delay_5us();

SCL = 1;

_nop_();

SCL = 0;

_nop_();

SDA = 1;

_nop_();

}

/* I2C从机应答*/

bit I2C_ACK_Get()

{

bit flag;

SCL = 0;

SDA = 1;

_nop_();

SCL = 1;

_nop_();

  flag = SDA;

_nop_();

SCL = 0;

return flag; 

}

 

/* 写入段 */

void I2C_Write(unsigned char add_7,unsigned char add,unsigned char byte)

{

I2C_Start(); //开始

I2C_Send(add_7+0); //写eeprom

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

I2C_Send(add); //选择内存地址

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

I2C_Send(byte); //写数据

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

I2C_Stop(); //主机停止

}

/* 读取段 */

unsigned char I2C_Read(unsigned char add_7,unsigned char add)

{

unsigned char message;

I2C_Start(); //开始

I2C_Send(add_7+0); //写eeprom

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

I2C_Send(add); //选择内存地址

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

 

I2C_Start(); //重开始

I2C_Send(add_7+1); //读eeprom

if(I2C_ACK_Get()) ACK_flag = 1; //接收从机ACK

message = I2C_Get();         //接收从机数据

I2C_ACK_Send(0); //主机发送ACK

I2C_Stop(); //主机停止

return message;

}


Proteus仿真

I2C debug还是很好用的,但是示波器一直搞不懂怎么用。还好有视频教程的例程,分析了半天发现问题主要在第二段上。然而把两部分开执行都可以正常运行,之前在开发板上运行的时候也是这样。用例程的读取EEPROM程序也能读出之前写入的值。


最后的原因是,EEPROM写入可擦除存储部分需要花10ms的时间,datasheet上有写,但是没看懂。

关键字:I2C总线  EEPROM读写  51单片机  模拟通讯 引用地址:I2C总线——EEPROM读写——51单片机模拟通讯

上一篇:单片机8位和16位是怎么区分的
下一篇:[51单片机] EEPROM 24c02 [I2C代码封装-保存实现流水灯]

推荐阅读最新更新时间:2024-11-13 08:39

51单片机内部特殊功能寄存器分析
1. Warning 280:’i’:unreferenced LOC al variable 说明局部变量i 在函数中未作任何的存取操作,解决方法消除函数中i 变量的宣告 2 Warning 206:’Mus IC 3’:missing function-prototype 说明Music3( )函数未作宣告或未作外部宣告所以无法给其他函数调用 解决方法将叙述void Music3(void)写在程序的最前端作宣告如果是其他文件的函数则要写成extern void Music3(void),即作外部宣告 3 Compling :C:\8051\MANN.C Error:318: CAN ’t open file ‘beep.h’
[单片机]
51单片机入门 - DS18B20温度传感器
DS18B20——温度传感器,单片机可以通过 1-Wire 和 DS18B20 进行通 信,最终将温度读出。1-Wire 总线的硬件接口很简单,只需要把 18B20 的数据引脚和单片 机的一个 IO 口接上就可以通信。最高12为的温度存储值,补码形式存储。 2字节,LSB低字节,MSB高字节,-55~125 1、初始化 检测存在脉冲:总线上存在DS18B20,总线会根据时序要求返回一个低电平脉冲。单片机要拉低这个引脚,持续大概 480us到960us之间 的时间即可,我们的程序中持续了 500us。然后,单片机释放总线,就是给高电平,DS18B20 等待大概 15 到 60us 后,会主动拉低这个引脚大概是 60 到 240u
[单片机]
8051单片机中访问int中字节的方法
在使用单片机中,unsigned int 占2个字节,unsigned char 占一个字节。而单片机是实行的字节寻址。16字节的bit寻址实在是不好用, 不好用在不能建数组。 在实际的开发过程中,要使用DPTR,还有定时器的TL0,TH0 的高低字节等。 需要我们先定义一个int类型的可以用来赋值什么的比较自然。 然后和0xff与得到低8,一个字节。可以放入DPTR的低字节或定时器的低字节。 将int类型的左移8位后,再和0xff与得到int的高8位,一个字节,移入到DPTR的高字节或定时器的高字节。 这样很麻烦。 实际上,我们可以通过struct和union方便的直接一步到位的得到int 类型的高字节和低字节,同时也能观察到5
[单片机]
80<font color='red'>51单片机</font>中访问int中字节的方法
51单片机寄存器配置小技巧
整型数据转换二进制数据的技巧知识点: 该技巧比较适合应用在51单片机寄存器的配置方面;减少了我们的计算量。 思路—— 我们比较习惯直接的整型数据;当我们需要配置寄存器,这时能不能直接利用该数据作为我们配置寄存器的数据呢?这过程需要将数据转换,例如将一个int 型 11110111 转换成 0xf7 该如何做到呢? 此时可以利用到##在C语言中的粘合作用,具体百度。 一, #define Bin(n) LongToBin(0x##n##l) //注意该处l是L的小写,而不是1。 此时使用时11110111则变为十六进制的0x11110111 而在此之前应先定义LongToBin(n) 二, #d
[单片机]
<font color='red'>51单片机</font>寄存器配置小技巧
单片机 EEPROM 单字节读写操作时序
EEPROM 写数据流程 第一步,首先是 I2C 的起始信号,接着跟上首字节,也就是我们前边讲的 I2C 的器件地址,并且在读写方向上选择“写”操作。 第二步,发送数据的存储地址。24C02 一共256个字节的存储空间,地址从 0x00~0xFF,我们想把数据存储在哪个位置,此刻写的就是哪个地址。 第三步,发送要存储的数据第一个字节、第二个字节„„注意在写数据的过程中,EEPROM 每个字节都会回应一个“应答位0”,来告诉我们写 EEPROM 数据成功,如果没有回应答位,说明写入不成功。 在写数据的过程中,每成功写入一个字节,EEPROM 存储空间的地址就会自动加1,当加到 0xFF 后,再写一个字节,地址会溢出又变成了 0x00
[单片机]
第三节 趣讲51单片机之P1P2P3口深入讲解
一、P1口 P1口是4组Parallel Ports中最简单的。其结构图如下: 与P0口的区别是:由于没有端口复用功能,所以,P0口的V1变成了一个上拉电阻。 由于内部就有上拉电阻,所以,作为GPIO时,P1口不需要接上拉电阻,当然,您接了也没关系啦,就相当于两个上拉电阻并联嘛。 读端口、读引脚、写功能在P0中已经讲得非常详细了,此处略个一万字。 二、P2口 P2口的原理图如下图: 咋一看,和P0口有点像,都有控制信号,选择作为 地址/数据 总线还是作为GPIO;又和P0口有些不同,P2口没有推挽式输出的结构,即没有V1 MOS管。 我们说过,P0在作为 地址/数据 总线时
[单片机]
第三节 趣讲<font color='red'>51单片机</font>之P1P2P3口深入讲解
基于51单片机电子秒表倒计时器
刚刚咱们讲了电子秒表用的是C语言,接下来咱们再讲一讲用汇编。 硬件设计 (末尾附文件) 电路图1: 数码管显示;时间清零;启动计时;暂定计时;继续计时; 电路图2 程序设计 TLOW EQU 78H THIGH EQU 0ECH LED_WEI_Pro EQU 0f0H;数码管位选 保护位 ;寄存器设置 COUN_INT EQU 21H ;5ms * 20 = 100ms = 0.1S COUN_PASSNUM EQU 22H ; COUN_TIME_L EQU 23H ;时间累加 低位 100MS加1 COUN_TIME_H EQU 24H ;时间累加 高位 C
[单片机]
基于<font color='red'>51单片机</font>电子秒表倒计时器
51单片机控制sg90舵机扇形摇摆程序
这是控制两个sg90的扇形摇摆程序! #include reg52.h sbit a=P1^0; sbit b=P1^1; #define uchar unsigned char #define uint unsigned int void delay(uint z) { uint x,y; for(x=z;x 0;x--) for(y=110;y 0;y--); } void delayus2x(unsigned char t) { while(--t); } void delay750us() { delayus2x(245); delayus2x(122); } void main()
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved