#循循渐进学51单片机#IIC总线与EEPROM#not.13

news2024/10/5 14:00:30

1、彻底理解I2C的通信时序,不仅仅是记住。

前几章我们学了一种通信协议叫做 UART 异步串行通信,这节课我们要来学习第二种常用的通信协议 I 2 C I 2 C 总线是由 PHILIPS 公司开发的两线式串行总线,多用于连接微处理器及其外围芯片。I 2 C 总线的主要特点是接口方式简单,两条线可以挂多个参与通信的器件,即多机模式,而且任何一个器件都可以作为主机,当然同一时刻只能有一个主机。从原理上来讲,UART 属于异步通信,比如电脑发送给单片机,电脑只负责把数据通过TXD 发送出来即可,接收数据是单片机自己的事情。而 I 2 C 属于同步通信, SCL 时钟线负责收发双方的时钟节拍,SDA 数据线负责传输数据。 I 2 C 的发送方和接收方都以 SCL 这个时钟 节拍为基准进行数据的发送和接收。从应用上来讲,UART 通信多用于板间通信,比如单片机和电脑,这个设备和另外一个设备之间的通信。而 I 2 C 多用于板内通信,比如单片机和我们本章要学的 EEPROM 之间的通信.
在硬件上,
I 2 C 总线是由时钟总线 SCL 和数据总线 SDA 两条线构成,连接到总线上的所有器件的 SCL 都连到一起,所有 SDA 都连到一起。 I 2 C 总线是开漏引脚并联的结构,因此我们外部要添加上拉电阻。对于开漏电路外部加上拉电阻,就组成了线“与”的关系。总线上线“与”的关系就是说,所有接入的器件保持高电平,这条线才是高电平,而任何一个器件输出一个低电平,那这条线就会保持低电平,因此可以做到任何一个器件都可以拉低电平,也就是任何一个器件都可以作为主机,如图 14-1 所示,我们添加了 R63 R64 两个上拉电阻。
虽然说任何一个设备都可以作为主机,但绝大多数情况下我们都是用单片机来做主机, 而总线上挂的多个器件,每一个都像电话机一样有自己唯一的地址,在信息传输的过程中, 通过这唯一的地址就可以正常识别到属于自己的信息,在 KST-51 开发板上,就挂接了 2 个 I 2 C 设备,一个是 24C02 ,一个是 PCF8591
我们在学习 UART 串行通信的时候,知道了通信流程分为起始位、数据位、停止位这三 部分,同理在 I 2 C 中也有起始信号、数据传输和停止信号,如图 14-2 所示,
从图上可以看出来, I 2 C UART 时序流程有相似性,也有一定的区别。 UART 每个字 节中,都有一个起始位、8 个数据位、 1 位停止位。而 I 2 C 分为起始信号、数据传输部分、停 止信号。其中数据传输部分,可以一次通信过程传输很多个字节,字节数是不受限制的,而 每个字节的数据最后也跟了一位,这一位叫做应答位,通常用 ACK 表示,有点类似于 UART 的停止位。
下面我们一部分一部分的把 I 2 C 通信时序进行剖析。之前我们已经学过了 UART ,所以学习 I 2 C 的过程我尽量拿 UART 来作为对比,这样有助于更好的理解。但是有一点大家要理解清楚,就是 UART 通信虽然用了 TXD RXD 两根线,但是实际一次通信中, 1 条线就可 以完成,2 条线是把发送和接收分开而已,而 I 2 C 每次通信,不管是发送还是接收,必须 2条线都参与工作才能完成,为了更方便的看出来每一位的传输流程,我们把图 14-2 改进成图14-3。
起始信号: UART 通信是从一直持续的高电平出现一个低电平标志起始位;而 I 2 C 通信的起始信号的定义是 SCL 为高电平期间, SDA 由高电平向低电平变化产生一个下降沿,表示起始信号,如图 14-3 中的 Start 部分所示。
数据传输:首先, UART 是低位在前,高位在后;而 I 2 C 通信是高位在前,低位在后。其次,UART 通信数据位是固定长度,波特率分之一,一位一位固定时间发送完毕就可以了。而 I 2 C 没有固定波特率,但是有时序的要求,要求当 SCL 在低电平的时候, SDA 允许变化,也就是说,发送方必须先保持 SCL 是低电平,才可以改变数据线 SDA ,输出要发送的当前数据的一位;而当 SCL 在高电平的时候, SDA 绝对不可以变化,因为这个时候,接收方要来读取当前 SDA 的电平信号是 0 还是 1 ,因此要保证 SDA 的稳定,如图 14-3 中的每一位数据的变化,都是在 SCL 的低电平位置。 8 位数据位后边跟着的是一位应答位,应答位我们后 边还要具体介绍。
停止信号: UART 通信的停止位是一位固定的高电平信号;而 I 2 C 通信停止信号的定义 是 SCL 为高电平期间, SDA 由低电平向高电平变化产生一个上升沿,表示结束信号,如图14-3 中的 Stop 部分所示。

2、能够独立完成EEPROM任意地址的单字节读写、多字节跨页连续写入读出。

存储器件,掉电后数据不丢失

单字节读写

#include <REGX52.H>


extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
unsigned char E2ReadByte(unsigned char addr);
void E2WriteByte(unsigned char addr, unsigned char dat);

void main()
{
    unsigned char dat;
    unsigned char str[10];
	
	InitLcd1602();
	dat = E2ReadByte(0x02); 
	str[0] = (dat/100) + '0';  
    str[1] = (dat/10%10) + '0';
    str[2] = (dat%10) + '0';
    str[3] = '\0';
    LcdShowStr(0, 0, str);     
    dat++;                    
    E2WriteByte(0x02, dat);   
    
    while (1);
}
unsigned char E2ReadByte(unsigned char addr)
{
	unsigned char dat;
	
	I2CStart();
	I2CWrite(0x50<<1);
	I2CWrite(addr);
	I2CWrite((0x50<<1)|0x01);
	dat = I2CReadNAK(); 
	I2CStop();
	
	return dat;
}
void E2WriteByte(unsigned char addr, unsigned char dat)
{
    I2CStart();
    I2CWrite(0x50<<1); 
    I2CWrite(addr);    
    I2CWrite(dat);    
    I2CStop();
}

 1602液晶模块

#include <REGX52.H>

#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E  = P1^5;

void LcdWaitReady()
{
	unsigned char sta;
	
	LCD1602_DB = 0xff;
	LCD1602_RS = 0;
	LCD1602_RW = 1;
	do{
	LCD1602_E = 1;
	sta = LCD1602_DB;
 	LCD1602_E = 0;
	}while(sta & 0x80);
}

void LcdWriteCmd(unsigned char cmd)
{
   LcdWaitReady();
	 LCD1602_RS = 0;
   LCD1602_RW = 0;
	 cmd = LCD1602_DB;
	 LCD1602_E = 1;
	 LCD1602_E = 0;
}

void LcdWriteDat(unsigned char dat)
{
   LcdWaitReady();
	 LCD1602_RS = 1;
   LCD1602_RW = 0;
	 dat = LCD1602_DB;
	 LCD1602_E = 1;
	 LCD1602_E = 0;
}

void LcdSetCursor(unsigned char x, unsigned char y)
{
 unsigned char addr;
	if(y == 0)
		addr = 0x00 + x;
	else
		addr = 0x40 + x;
	LcdWriteCmd(addr | 0x80);
}

void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
		LcdSetCursor(x, y); 
	while(*str != '\0')
	{
	LcdWriteDat(*str++);
	}
}

void InitLcd1602()
{
    LcdWriteCmd(0x38); 
    LcdWriteCmd(0x0C); 
    LcdWriteCmd(0x06); 
    LcdWriteCmd(0x01);  
}

 I2C模块

#include <REGX52.H>
#include <intrins.h>

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;

void I2CStart()
{
  I2C_SDA = 1;
	I2C_SCL = 1;
	I2CDelay();
  I2C_SDA = 0;
	I2CDelay();
  I2C_SCL = 0;

}

void I2CStop()
{
  I2C_SDA = 0;
	I2C_SCL = 0;
	I2CDelay();
	I2C_SCL = 1;
	I2CDelay();
	I2C_SDA = 1;
	I2CDelay();
}

bit I2CWrite(unsigned char dat)
{
	bit ack;
	unsigned char mask;
	
	for(mask = 0x80;mask!=0;mask>>=1)
	{
	  if((mask&dat)==0)
			I2C_SDA = 0;
		else
			I2C_SDA = 1;
		I2CDelay();
		I2C_SCL = 1;
		I2CDelay();
		I2C_SCL = 0;
	}
	I2C_SDA = 1;
	I2CDelay();
	ack = I2C_SDA;
	I2CDelay();
	I2C_SCL = 0; 
	
	return (~ack);

}

unsigned char I2CReadNAK()
{
	unsigned char mask;
  unsigned char dat;

	I2C_SDA = 1;
   for(mask=0x80; mask!=0; mask>>=1)
    {
		    I2CDelay();
			  I2C_SCL = 1;
			if(I2C_SDA == 0)
			dat &= ~mask;
			else
				dat |= mask;
			I2CDelay();
			I2C_SCL = 0;
		}
	I2C_SDA = 1;
	I2CDelay();	
	I2C_SCL = 1;
	I2CDelay();	
		I2C_SCL = 0;
		
		return dat;
	}

unsigned char I2CReadACK()
{
    unsigned char mask;
    unsigned char dat;
	
	I2C_SDA = 1;
	for(mask=0x80; mask!=0; mask>>=1)
	{
	   I2CDelay();
		I2C_SCL = 1;
		if(I2C_SDA == 0)
			dat &= ~mask;
		else
			dat |= mask;
		I2CDelay();
		I2C_SCL = 0;
		
		return dat;
	}
}

 多字节读写

#include <REGX52.H>


extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadACK();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
void E2Read(unsigned char *buf, unsigned char addr, unsigned char len);
void E2Write(unsigned char *buf, unsigned char addr, unsigned char len);
void MemToStr(unsigned char *str, unsigned char *src, unsigned char len);


void main()
{
    unsigned char i;
		unsigned char buf[5];
    unsigned char str[20];
	
	InitLcd1602();
	E2Read(buf, 0x90, sizeof(buf));
	MemToStr(str, buf, sizeof(buf));
	LcdShowStr(0, 0, str);
	for(i = 0;i < sizeof(buf);i++)
	{
	    buf[i] = buf[i] + 1 + i;
	}
     E2Write(buf, 0x90, sizeof(buf));
    while (1);
}

void MemToStr(unsigned char *str, unsigned char *src, unsigned char len)
{
  unsigned char tmp;
  while(len--)
	{
	  tmp = *src>>4;
		if(tmp <= 9)
		*str++ = tmp + '0';
		else
			*str++ = tmp - 10 +'A';
		tmp = *str & 0x0f;
		if(tmp <= 9)
			*str++ = tmp - 10 - 'A';
		*str++ =' ';
		src++;
	}
	*str = '\0';
}

void E2Read(unsigned char *buf, unsigned char addr, unsigned char len)
{
		do{
		I2CStart();
			  if (I2CWrite(0x50<<1)) 
        {
            break;
        }
		}while(1);
	I2CWrite(addr);
	 I2CStart(); 	
		I2CWrite((0x50<<1)|0x01);
		while(len > 1)
		{
		  *buf++ = I2CReadACK();
			len--;
		}
		*buf = I2CReadACK();
		I2CStop();
}

void E2Write(unsigned char *buf, unsigned char addr, unsigned char len)
{
		while(len--)
		{
		do{
		I2CStart();
			  if (I2CWrite(0x50<<1)) 
        {
            break;
        }
			I2CStop();
		}while(1);
		I2CWrite(addr++);
		I2CWrite(*buf++);
		I2CStop();
		}
}
        //按页连续写入
        I2CWrite(addr);           
        while (len > 0)
        {
            I2CWrite(*buf++);    
            len--;                
            addr++;               
            if ((addr&0x07) == 0) 
            {                    
                break;            
            }
        }
        I2CStop();
    }


3、将前边学的交通灯进行改进,用EEPROM保存红灯和绿灯倒计时时间,并且可以通过UART改变红灯和绿灯倒计时时间。


4、使用按键、1602液晶、EEPROM做一个简单的密码锁程序。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java-day17(反射)

Reflection(反射) 动态语言的关键 允许程序在执行期借助于Reflection API取得任何类的内部信息&#xff0c;并能直接操作任意对象的内部属性及方法提供的功能: 在运行时判断任意一个对象所属类 在运行时构造任意一个类的对象 在运行时判断任意一个类所具有的成员变量和方法 在…

Vue3 动态组件 component:is= 失效

错误代码 用Vue3&#xff0c;组件无需注册&#xff0c;所以就会提示“注册了不不使用”的报错&#xff0c; 于是用了异步注册&#xff0c;甚至直接为了不报错就在下面使用3个组件&#xff0c;有异步加载&#xff0c;但还是实现不了预期效果 <script setup> import { re…

#define定义标识符详解

0.预定义符号 在讲解#define之前先给大家介绍几个预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C&#xff08;标准C&#xff09;&#xff…

服务器感染了Locked勒索病毒后的正确处理步骤,勒索病毒解密

在服务器中感染了Locked勒索病毒后&#xff0c;应该采取以下一系列步骤来应对和清除病毒&#xff1a; 立即断开网络连接&#xff1a;防止病毒进一步传播感染。备份重要数据&#xff1a;在执行任何操作之前&#xff0c;一定要先备份服务器上的重要数据。这样可以防止在处理病毒过…

经典网络解析(四) ResNet | 残差模块,网络结构代码实现全解析

文章目录 1 设计初衷2.网络结构2.1 残差块2.2 中间的卷积网络特征提取块1 两层33卷积层2 先11卷积层&#xff0c;再33卷积层&#xff0c;再33卷积层 2.3 结构总览表格 3 为什么残差模块有效&#xff1f;3.1 前向传播3.2 反向传播3.3 恒等映射3.4 集成模型 4.代码实现 1 设计初衷…

如何学习嵌入式Linux?

今日话题&#xff0c;如何学习嵌入式Linux&#xff1f;嵌入式底层开发是一种重要的技术&#xff0c;它被广泛应用于各种嵌入式系统中。随着科技的不断发展&#xff0c;嵌入式系统已经成为了我们日常生活中不可或缺的一部分。这就使得嵌入式开发的重要性也凸显出来。刚好我这有一…

opencv for unity package在unity中打开相机不需要dll

下载OpenCV for Unity 导入后&#xff0c;里面有很多案例 直接打开就可以运行 打开相机

Linux: errno: EADDRNOTAVAIL; ipv6-bind;Cannot assign requested address

文章目录 解释一种情况tentative 的解释 解释一种情况 #define EADDRNOTAVAIL 99 /* Cannot assign requested address */ 有一种情况是&#xff1a;当IP6的地址处于tentative的时候&#xff0c;就会返回这个错误。下面的是bind的调用的时候会check地址的flag。如果是tentati…

Java学习星球,十月集训,五大赛道(文末送书)

目录 什么是知识星球&#xff1f;我的知识星球能为你提供什么&#xff1f;专属专栏《Java基础教程系列》内容概览&#xff1a;《Java高并发编程实战》、《MySQL 基础教程系列》内容概览&#xff1a;《微服务》、《Redis中间件》、《Dubbo高手之路》、《华为OD机试》内容概览&am…

一款Python认证和授权的利器

迷途小书童 读完需要 7分钟 速读仅需 3 分钟 1 简介 authlib 是一个开源的 Python 库&#xff0c;旨在提供简单而强大的认证和授权解决方案。它支持多种认证和授权协议&#xff0c;如 OAuth、OpenID Connect 和 JWT。authlib 具有灵活的架构和丰富的功能&#xff0c;使开发人员…

lS1028 + 六网口TSN 硬交换+QNX/Linux实时系统解决方案在轨道交通系统的应用

lS1028 六网口TSN 硬交换QNX/Linux实时系统解决方案在轨道交通系统的应用 以下是在轨道交通应用的实物&#xff1a; CPUNXP LS1028A架构双核Cortex-A72主频1.5GHzRAM2GB DDR4ROM8GB eMMCOSUbuntu20.04供电DC 12V工作温度-40℃~ 80℃ 功能数量参数Display Port≤1路支持DP1.3…

UniAccess Agent卸载

异常场景&#xff1a; UniAccess Agent导致系统中的好多设置打不开 例如:ipv4的协议,注册表,host等等 需要进行删除,亲测有效,及多家答案平凑的 借鉴了这位大神及他里面引用的大神的内容 https://blog.csdn.net/weixin_44476410/article/details/121605455 问题描述 这个进…

Android开发MVP架构记录

Android开发MVP架构记录 安卓的MVP&#xff08;Model-View-Presenter&#xff09;架构是一种常见的软件设计模式&#xff0c;用于帮助开发者组织和分离应用程序的不同组成部分。MVP架构的目标是将应用程序的业务逻辑&#xff08;Presenter&#xff09;、用户界面&#xff08;V…

由于找不到msvcr110.dll的5种解决方法

在使用电脑的过程中&#xff0c;我们可能会遇到一些问题&#xff0c;比如打开软件时提示找不到 msvcr110.dll 文件丢失。这通常意味着该文件已被删除或损坏&#xff0c;导致程序无法正常运行。本文将介绍几种解决方案&#xff0c;帮助您解决这个问题。 首先&#xff0c;我们需…

Linxu下c语言实现socket+openssl数据传输加密

文章目录 1. Socket连接建立流程2、SocketSSL的初始化流程3、初始化SSL环境&#xff0c;证书和密钥4、SocketSSL 的c语言实现4.1 编写SSL连接函数4.2 编写加密服务端server.c4.3 编写加密客户端client.c 5、使用tcpdump检验源码获取 在进行网络编程的时候&#xff0c;我们通常使…

分布式算法相关,使用Redis落地解决1-2亿条数据缓存

面试题&#xff1a;1~2亿数据需要缓存&#xff0c;请问如何设计个存储案例 回答&#xff1a;单机单台100%不可能&#xff0c;肯定是分布式存储&#xff0c;用redis如何落地&#xff1f; 一般业界有三种解决方案&#xff1a; 哈希取余分区 2亿条记录就是2亿个k&#xff0c;v&…

Linux学习-HIS部署(3)

Jenkins插件资源下载 Jenkins部署 Jenkins部署 #Jenkins主机安装OpenJDK环境 [rootJenkins ~]# yum clean all; yum repolist -v ... Total packages: 8,265 [rootJenkins ~]# yum -y install java-11-openjdk-devel.x86_64 #安装OpenJDK11 [rootJenkins ~]# ln -s /usr/l…

Css 美化滚动条

/*设置滚动条宽度为 6px*/ ::-webkit-scrollbar {width: 6px; } /*设置背景颜色&#xff0c;并设置边框倒角&#xff0c;设置滚动动画&#xff0c;0.2 */ ::-webkit-scrollbar-thumb {background-color: #0003;border-radius: 10px;transition: all .2s ease-in-out; } /*设置滚…

探索创意的新辅助,AI与作家的完美合作

在现代社会&#xff0c;文学创作一直是人类精神活动中的重要一环。从古典文学到现代小说&#xff0c;从诗歌到戏剧&#xff0c;作家们以他们的独特视角和文学天赋为我们展示了丰富多彩的人生世界。而近年来&#xff0c;人工智能技术的快速发展已经渗透到各行各业&#xff0c;文…

Leetcode191. 位1的个数

力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 思路&…