【单片机】14-I2C通信之EEPROM

1.EEPROM概念

1.EEPROM

1.1 一些概念

(1)一些概念:ROM【只读存储器---硬盘】,RAM【随机访问存储器--内存】,PROM【可编程的ROM】,EPROM【可擦除ROM】,EEPROM【电可擦除ROM】

1.2 为什么需要EEPROM

单片机内部的ROM只能在程序下载时进行擦除和改写,但是程序运行本身是不能改写的。

单片机内部的RAM中的数据程序运行时可以改,但是掉电就丢失了。

有时候我们需要有一些数据存在系统中,要求掉电不丢失,而且程序还要能改。所以内部ROM和RAM都不行。【这时候系统需要一块EEPROM】

1.3 EEPROM和flash的区别与联系

单片机解密中Flash和EEPROM的区别-电子工程世界

1.4 EEPROM存在系统中的2种形式

1:内置在单片机内部

2:外部扩展

2.EEPROM如何编程

1.I2C接口底层时序

底层:CPU和I2C的接口

2.器件定义的寄存器读写时序

上层:器件时序

2.AT24C02原理图和数据手册

1.接线确定

查看SCL和SDA无其他接线影响

SCL对应P2.1 SDA对应P2.0

2.数据手册理论

立创商城_一站式电子元器件采购自营商城_现货元器件交易网-嘉立创电子商城

24c02中文官方资料手册pdf - 百度文库

1.芯片的基本信息

类似于一个主持人叫A说话,其他人就不可以说话,但是其他人可以听到主持人和A说话,但是不可以回应。 --广播式

主设备:51单片机---发送器

从设备:24Cxxx---接收器

2.I2C从地址确定

每一个I2C都有从地址

3.I2C底层时序

起始信号:

发送字节:一般第一个是从设备的地址【因为我们在通话之前,要先发送要进行通话的地址,设备都与自己的地址是否相同,如果相同则响应;如果不同,则丢弃】

读取字节:

停止信号:

3.I2C总结

(1)主CPU和其附属芯片之间最常用的接口,尤其是各种传感器,因此在物联网时代非常重要

(2)三根线:SCL,SDA,GND,串行,电平式

(3)总线式结构:可以一对多,总线上可以挂上百个器件【一个主设备,多个从设备】,用【从地址】来区分--主设备不需要地址

(4)主从式,由主设备来发起通信及总线仲裁,从设备被动响应

(5)通信速率一般(kbps级别),不合适语音,视频等信息类型

4.I2C总线协议定义

起始信号

终止信号

应答信号

从设备回复主设备,判断从设备是否得到数据。

可以设置是否要进行”应答信号“【可有可无】

3.I2C低层时序图和程序

1.起始信号和结束信号

SCL和SDA交互进行判断

(1)起始信号:SCL保持高时,SDA有一个从高到低(下降沿)
(2)结束信号:SCL保持高时,SDA有一个从低到高(上升沿)

起始信号

/*******************************************************************************
* 函 数 名       : iic_start
* 函数功能		 : 产生IIC起始信号
* 输    入       : 无
* 输    出    	 : 无
*******************************************************************************/
void iic_start(void)
{IIC_SDA=1;//如果把该条语句放在SCL后面,第二次读写会出现问题delay_10us(1);IIC_SCL=1;delay_10us(1);IIC_SDA=0;	//当SCL为高电平时,SDA由高变为低,表示起始信号delay_10us(1);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据delay_10us(1);
}

终止信号

/*******************************************************************************
* 函 数 名         : iic_stop
* 函数功能		   : 产生IIC停止信号   
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void iic_stop(void)
{	IIC_SDA=0;//如果把该条语句放在SCL后面,第二次读写会出现问题delay_10us(1);IIC_SCL=1;delay_10us(1);IIC_SDA=1;	//当SCL为高电平时,SDA由低变为高,表示结束信号delay_10us(1);			
}

2.I2C发送一个字节

传输”0“或应答位(A)

传输”1“或应答位(/A)

/*******************************************************************************
* 函 数 名         : iic_write_byte
* 函数功能		   : IIC发送一个字节 
* 输    入         : dat:发送一个字节
* 输    出         : 无
*******************************************************************************/
void iic_write_byte(u8 dat)
{                        u8 i=0; //为了保证时序正确,这里要加上    //当SDA将数据放好才可以将SCL置为高电平IIC_SCL=0; for(i=0;i<8;i++)	//循环8次将一个字节传出,先传高再传低位{              if((dat&0x80)>0) IIC_SDA=1;elseIIC_SDA=0;dat<<=1; //将次高位移动到最高位	  delay_10us(1);  IIC_SCL=1; //产生一个上升沿delay_10us(1); IIC_SCL=0; //产生一个下降沿	delay_10us(1);}	 
}

 

(1)I2C发送和接收字节时,都是从高位开始的

3.应答位处理

在接收完8位bit后,在第9个时间周期

应答处理:SDA变低【AT2402拉低】。

如果我们去检测,如果此时SDA为低电平,则表示已经被拉低,则表示已经响应到;如果SDA为高电平,则表示未能响应到。

 产生ACK应答  

/*******************************************************************************
* 函 数 名         : iic_ack
* 函数功能		   : 产生ACK应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void iic_ack(void)
{IIC_SCL=0;IIC_SDA=0;	//SDA为低电平delay_10us(1);IIC_SCL=1;   //将SCL拉高delay_10us(1);IIC_SCL=0;  //在将SCL拉低
}

 产生NACK非应答 

/*******************************************************************************
* 函 数 名         : iic_nack
* 函数功能		   : 产生NACK非应答  
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void iic_nack(void)
{IIC_SCL=0;IIC_SDA=1;	//SDA为高电平delay_10us(1);IIC_SCL=1;delay_10us(1);IIC_SCL=0;	
}

等待应答信号到来 

/*******************************************************************************
* 函 数 名         : iic_wait_ack
* 函数功能		   : 等待应答信号到来   
* 输    入         : 无
* 输    出         : 1,接收应答失败0,接收应答成功
*******************************************************************************/
u8 iic_wait_ack(void)
{u8 time_temp=0;IIC_SCL=1;delay_10us(1);while(IIC_SDA)	//等待SDA为低电平{time_temp++;if(time_temp>100)//超时则强制结束IIC通信{	iic_stop();return 1;	}			}IIC_SCL=0;return 0;	
}

4.I2C接收一个字节

释放总线

在51单片机中,SDA=1就是释放总线【相当于主持人把话筒给嘉宾】;在其他更高级的单片机(比如STM32)这里的处理还会不一样。【因为拉高,则可以拉低(接地);但是拉低了,但是无法拉高】

为什么SDA=1就是释放总线??是因为当51单片机把引脚拉高时,从设备可以选择再把引脚拉高或者拉低;但是当51单片机把这个引脚拉低(接地)后,从设备也没有办法把这个引脚拉高了。

unsigned char IIC_ReadByte()
{unsigned char a=0,dat=0;//释放总线IIC_SDA=1;  //起始和发送一个字节之后IIC_SCL都是0IIC_delay();//按道理来说这里应该有一个SCL=0的for(a=0;a<8;a++){IIC_SCL=1;//通知从设备我要开始读了,可以放bit数据到SDA了IIC_delay();dat<<=1; //读取的时候高位再前dat|=IIC_SDA;IIC_delay();IIC_SCL=0;// 拉低为下一个bit周期做准备ICC_delay();}return dat;
}

4.EEPROM读写测试

1.  器件寻址

(1)从器件的地址是由器件自身定义的,不同的从器件的地址的定义方式是不同的,要查具体的芯片数据手册来确定

(2)同一个I2C网络中只有一个主设备,但是从设备可以有多个。这多个从设备的地址不能相同。【硬件工程师必须保证这一点。因为从地址是不能通过软件设定的】

(3)A0,A1,A2----2的三次方=8【表示最多只能接8个EEPROM】

从CPU的角度来分析24C02的地址定义【如果不是从CPU角度看则得出结果不一样】

        从设备地址是:读地址:0xa1

                                 写地址:0xa0

2.24C02写高层时序

写操作时序

start-send_byte(从地址)--send_byte(字节地址)---send_byte(写入数据)

 字节写

/*******************************************************************************
* 函 数 名         : at24c02_write_one_byte
* 函数功能		   : 在AT24CXX指定地址写入一个数据
* 输    入         : addr:写入数据的目的地址 dat:要写入的数据
* 输    出         : 无
*******************************************************************************/
void at24c02_write_one_byte(u8 addr,u8 dat)
{				   	  	    																 iic_start();  iic_write_byte(0XA0);	//发送写命令,发送写器件地址	    	  iic_wait_ack();	//表示要接收应答 iic_write_byte(addr);	//发送写地址   iic_wait_ack(); //表示要接收应答 	 										  		   iic_write_byte(dat);	//发送字节    							   iic_wait_ack();  		    	   iic_stop();				//产生一个停止条件delay_ms(10);	 
}

页写

/*******************************************************************************
* 函 数 名         : at24c02_write_one_byte
* 函数功能		   : 在AT24CXX指定地址写入一个数据
* 输    入         : addr:写入数据的目的地址 dat:要写入的数据
* 输    出         : 无
*******************************************************************************/
void at24c02_write_one_byte(u8 addr,u8 dat[],u8 i)
{				   	u8 j;  	    																 iic_start();  iic_write_byte(0XA0);	//发送写命令,发送写器件地址	    	  iic_wait_ack();	//表示要接收应答 iic_write_byte(addr);	//发送写地址   iic_wait_ack(); //表示要接收应答 	 										  		   for(j=0;j<i;i++){iic_write_byte(dat[i]);	//发送字节    							   iic_wait_ack();}  		    	   iic_stop();				//产生一个停止条件delay_ms(10);	 
}

3. 24C02读高层时序

/*******************************************************************************
* 函 数 名         : at24c02_read_one_byte
* 函数功能		   : 在AT24CXX指定地址读出一个数据
* 输    入         : addr:开始读数的地址 
* 输    出         : 读到的数据
*******************************************************************************/
u8 at24c02_read_one_byte(u8 addr)
{				  u8 temp=0;		  	    																 iic_start();  iic_write_byte(0XA0);	//发送写命令	   iic_wait_ack(); iic_write_byte(addr); 	//发送写地址  iic_wait_ack();	    iic_start();  	 	   iic_write_byte(0XA1); 	//进入接收模式         			   iic_wait_ack();	 temp=iic_read_byte(0);	//读取字节		   iic_stop();				//产生一个停止条件    return temp;			//返回读取的数据
}

4.复合格式

1.先发送在接收

2.字节写+随机读

4.加入串口输出代码

/*******************************************************************************
* 实验名			  : EEPROM实验
* 使用的IO	    : 
* 实验效果      : 按K1保存显示的数据,按K2读取上次保存的数据,按K3显示数据加一,
*按K4显示数据清零。
*	注意					:由于P3.2口跟红外线共用,所以做按键实验时为了不让红外线影响实验效果,最好把红外线先
*取下来。
*
*********************************************************************************/
#include <reg51.h>
#include "at24c02.h"
#include "uart.h"void delay20ms(void)   //误差 -0.000000000005us
{unsigned char a,b,c;for(c=1;c>0;c--)for(b=222;b>0;b--)for(a=40;a>0;a--);
}/*******************************************************************************
* 函 数 名         : main
* 函数功能		   : 主函数
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/void main()
{unsigned char i;unsigned char addr;	unsigned char src_data[] = "()ab#cde!fg1234567";	unsigned char buf[8] = "ABCDEFGH";uart_init();
/*for (i=0; i<128; i++){uart_send_byte(i);}while (1);
*/// 先随便找一堆数据,譬如"abcdefg1234567-_-*&%@/\"// 把这些写入EEPROM的特定地址中// 然后读EEROM的这些地址,读出后通过串口打印出来看是不是我们写入的uart_send_byte('%');addr = 0;for (i=0; i<8; i++){At24c02Write(addr, src_data[i]);delay20ms();addr++;}//先打印出buf//如果这里没有给buf初始化,则打印会出现问题for (i=0; i<8; i++){uart_send_byte(buf[i]);}//分割for (i=0; i<20; i++){uart_send_byte('-');}// 读出测试addr = 0;for (i=0; i<8; i++){buf[i] = At24c02Read(addr);delay20ms();addr++;}//将数据打印出来for (i=0; i<8; i++){uart_send_byte(buf[i]);}while (1);// 进一步测试// 先写入一些特定内容,然后关机断电;然后改代码为读出并打印显示看内容}		

问题分析

(1)通过调试发现程序跑飞了,经检测发现uart中没有关中断

(2)读出内容不对,怀疑是EEPROM经不起快速的连续读写,所以在读和写之间加入20ms的delay,测试后发现读写正确了

(3)定义了局部变量没有初始化,程序中直接去通过串口输出,结果导致程序

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/123959.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WebSocket实战之三遇上PAC

一、前言 前两天销售数据实时刷新功能开发测试完成&#xff0c;开开心心部署到生产环境&#xff0c;然后直接懵逼傻眼了&#xff0c;竟然连接不上WebSocket服务端&#xff0c;浏览器端请求头报 Provisional headers are shown 信息&#xff0c;然后采用一系列操作排查问题。 …

九、2023.10.3.Linux(end).9

文章目录 33、简述mmap的原理和使用场景&#xff1f;34、互斥量能不能在进程中使用&#xff1f;35、协程是轻量级线程&#xff0c;轻量级表现在哪里&#xff1f;36、说说常见信号有哪些&#xff0c;表示什么含义&#xff1f;37、说说线程间通信的方式有哪些&#xff1f;38、说说…

pandas

一、pandas初级 安装matplotlib:pip install matplotlib 安装pandas:pip install pandas 本地C:\Users\Administrator\pip&#xff0c;在此目录配置清华园的远程下载 配置内容&#xff1a; [global] index-urlhttps://pypi.tuna.tsinghua.edu.cn/simple [install] trusted-ho…

java Spring Boot 自动启动热部署 (别再改点东西就要重启啦)

上文 java Spring Boot 手动启动热部署 我们实现了一个手动热部署的代码 但其实很多人会觉得 这叫说明热开发呀 这么捞 写完还要手动去点一下 很不友好 其实我们开发人员肯定是希望重启这种事不需要自己手动去做 那么 当然可以 我们就让它自己去做 Build Project 这个操作 我们…

WOL唤醒配置(以太网、PHY、MAC)

目录 wol 以太网 MAC PHY RMII 通信配置 总结 wol Wake-on-LAN简称WOL&#xff0c;WOL&#xff08;网络唤醒&#xff09; 是一种标准网络协议&#xff0c;它的功效在于让已经进入休眠状态或关机状态的计算机&#xff0c;透过局域网&#xff08;多半为以太网&#xff…

聊聊常见的IO模型 BIO/NIO/AIO 、DIO、多路复用等IO模型

聊聊常见的IO模型 BIO/NIO/AIO/DIO、IO多路复用等IO模型 文章目录 一、前言1. 什么是IO模型2. 为什么需要IO模型 二、常见的IO模型1. 同步阻塞IO&#xff08;Blocking IO&#xff0c;BIO&#xff09;2. 同步非阻塞IO&#xff08;Non-blocking IO&#xff0c;NIO&#xff09;3.…

测试用例的编写(面试常问)

作者&#xff1a;爱塔居 专栏&#xff1a;软件测试 作者简介&#xff1a;不断总结&#xff0c;才能变得更好~踩过的坑&#xff0c;不能再踩~ 文章简介&#xff1a;常见的几个测试用例。 一、淘宝购物车 二、登录页面 三、三角形测试用例 abc结果346普通三角形333等边三角形334…

计算机毕业设计 基于SpringBoot的图书馆管理系统的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

分享一款开源的QT的串口示波器

分享一款开源的QT的串口示波器&#xff0c;完全开源&#xff0c;支持串口、TCP、波形显示、通信协议。 Sailor Project功能说明 串口调试助手功能 支持传统的串口调试助手的基本收发功能&#xff0c;同时可以刷新大量的数据而不卡顿 支持保存接收的数据 支持最大200条可编辑…

软件工程与计算总结(二)软件工程的发展

本章开始介绍第二节内容&#xff0c;主要是一些历史性的东西~ 一.软件工程的发展脉络 1.基础环境因素的变化及其对软件工程的推动 抽象软件实体和虚拟计算机都是软件工程的基础环境因素&#xff0c;它们能从根本上影响软件工程的生产能力&#xff0c;而且是软件工程无法反向…

C++语言GDAL批量裁剪多波段栅格图像:基于像元个数裁剪

本文介绍基于C 语言的GDAL模块&#xff0c;按照给定的像元行数与列数&#xff0c;批量裁剪大量多波段栅格遥感影像文件&#xff0c;并将所得到的裁剪后新的多波段遥感影像文件保存在指定路径中的方法。 在之前的文章中&#xff0c;我们多次介绍了在不同平台&#xff0c;或基于不…

软件测试教程 自动化测试selenium篇(二)

掌握Selenium常用的API的使用 一、webdriver API public class Main {public static void main(String[] args) {ChromeOptions options=new ChromeOptions();//参数表示允许所有请求options.addArguments("--remote-allow-origins=*");WebDriver webDriver=new Chr…