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

news/2024/4/16 3:23:25

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;elseaddr = 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;elseI2C_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;elsedat |= 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;elsedat |= 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.ppmy.cn/news/1124752.html

相关文章

HttpClientr入门

HttpClientr入门 介绍 HttpClient是Apache Jakarta Common下的子项目&#xff0c;可以用来提供高效的&#xff0c;最新的、功能丰富的支持HTTP协议的客户端编程工具包&#xff0c;并且它支持HTTP协议的版本和建议。 依赖导入 <dependency><groupId>org.apache.…

小程序-uniapp:URL Link / 适用于在移动端 从短信、邮件、微信外网页 等场景打开小程序任意页面

一、背景介绍 小程序URL Scheme、URL Link是微信小程序后台生成的一种地址&#xff0c;适用于从短信、邮件、微信外网页 等场景打开小程序任意页面。所以&#xff0c;适用性极强。可与微信扫码携带参数跳转到小程序指定页面技术互补 若在微信外打开&#xff0c;用户可以在浏览…

怎么改变placeholder提示字的颜色用CSS

怎么改变placeholder提示字的颜色用CSS 要改变输入框&#xff08;input&#xff09;或文本区域&#xff08;textarea&#xff09;的 placeholder 提示文本的颜色&#xff0c;你可以使用 ::placeholder 伪元素选择器。以下是如何使用CSS改变 placeholder 文本的颜色&#xff1a…

12大常用自动化测试工具,请记得转发收藏!

常用自动化测试工具 1、Appium AppUI自动化测试 Appium 是一个移动端自动化测试开源工具&#xff0c;支持iOS 和Android 平台&#xff0c;支持Python、Java 等语言&#xff0c;即同一套Java 或Python 脚本可以同时运行在iOS 和Android平台&#xff0c;Appium 是一个C/S 架构&…

SpringMVC 学习(二)Hello SpringMVC

3. Hello SpringMVC (1) 新建 maven 模块 springmvc-02-hellomvc (2) 确认依赖的导入 (3) 配置 web.xml <!--web/WEB-INF/web.xml--> <?xml version"1.0" encoding"UTF-8"?> <web-app xmlns"http://xmlns.jcp.org/xml/ns/javaee…

关于业务库从MySQL迁移到DM8的操作指南

升级前准备 jdbc:dm://10.252.10.15:5237 username: datashare password: datashare123 把当前MySQL数据库下的数据库表以及数据迁移到DM8。通过达梦8自带的工具可以实现迁移&#xff08;仅支持Win&#xff09; DM8管理工具下载&#xff1a;https://www.dameng.com/DM8.html…

在Scrapy框架中使用隧道代理

今天我要和大家分享一些实战经验&#xff0c;教你如何在Scrapy框架中使用隧道代理。如果你是一个热爱网络爬虫的开发者&#xff0c;或者对数据抓取和处理感兴趣&#xff0c;那么这篇文章将帮助你走上更高级的爬虫之路。 首先&#xff0c;让我们简单介绍一下Scrapy框架。Scrapy…

彩色文本进度条

动态加色打印&#xff0c;\033控制&#xff0c;显示进行到的百分比&#xff0c;实时更新总共用时。 (本笔记适合能熟练应用字符串和循环技能的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程…

登录业务实现 - token登录鉴权

登录业务实现&#xff1a; 登录成功/失败实现 -> pinia管理用户数据及数据持久化 -> 不同登录状态的模板适配 -> 请求拦截器携带token&#xff08;登录鉴权&#xff09; -> 退出登录实现 -> token失效&#xff08;401响应拦截&#xff09; 1. 登录成…

基于微信小程序的线上教育课程付费商城(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

vue3中withDefaults是什么

问: const props withDefaults(defineProps<{// 数据列表lotteryList: { pic: string; name?: string }[];// 中奖idwinId: number;// 抽奖初始转动速度initSpeed: number;// 抽奖最快转动速度fastSpeed: number;// 抽奖最慢转动速度slowSpeed: number;// 基本圈数baseCi…

CompletableFuture-CompletionStage接口源码分析和四大静态方法初讲

2.3 CompletableFuture对Future的改进 2.3.1 CompletableFuture为什么会出现 get()方法在Future计算完成之前会一直处在阻塞状态下&#xff0c;阻塞的方式和异步编程的设计理念相违背。 isDene()方法容易耗费cpu资源&#xff08;cpu空转&#xff09;&#xff0c; 对于真正的…

11.2.0.4Oracle rac grid卸载安装注意事项

还有三天放国庆和中秋了&#xff0c;今天周一上班就跟丢了魂一样&#xff0c;本来两台11g RAC一个半小时搞定的事情&#xff0c;我从早上九点一直干到现在晚上八点半才干完&#xff0c;简直是昏了头了。安装的时候将大页设置错误&#xff0c;导致一套库直接系统崩了&#xff0c…

[TI] [Textual Inversion] An image is worth an word

自己的理解&#xff1a; 根据几个图像&#xff0c;找出来一个关键字可以代表它们&#xff0c;然后我们可以再用这个关键字去生成新的东西。 提出关键字 1 Introduction word->token->embedding Textual Inversion过程 需要&#xff1a; ① a fixed, pre-trained text…

Windows Server 2008 R2安装CVE-2023-35359相关补丁问题及自动进入修复模式无法启动的问题处理

近日微软发布了Windows紧急高危漏洞“Windows权限提升漏洞”&#xff0c;漏洞编号为&#xff1a;CVE-2023-35359&#xff0c;在Windows Server 2008 R2安装过程中出现系列问题&#xff0c;并出现了自动进入修复模式无法启动的问题&#xff0c;本文记录了相关处理过程。 一、Wi…

Java反序列化漏洞

我想时至今日&#xff0c;这个漏洞依然存在&#xff0c;据说都已经有人写出了webshell&#xff0c;很恐怖呀。 接下来分析下这个漏洞。 Java序列化和反序列化 具体实现细节可参考: Java序列化机制和原理。 一个简易的漏洞程序 在Java反序列化中&#xff0c;会调用被反序列化…

kubernetes问题(一)-异常事件

1 pod状态处于Evicted 0/1 现象&#xff1a; 1&#xff09;kubectl get events发现“failed to garbage collect required amount of images”。 2&#xff09;同时磁盘空间不足的节点有大量pod处于Evicted 0/1状态&#xff0c;但并未进行重新调度。 原因描述&#xff1a; …

MySQL索引是什么

1、索引是什么 1、MySQL官方对索引的定义为&#xff1a;索引&#xff08;Index&#xff09;是帮助MySQL高效获取数据的数据结构 索引的本质&#xff1a;数据结构。 索引的目的在于提高查询效率&#xff0c;可以类比字典或者一本书的目录&#xff0c;如果要查“mysql”这个单词…

Android最好用的日志打印库(自动追踪日志代码位置)

给大家推荐一个自己写的日志打印的库&#xff0c;我愿称之为最强日志打印库&#xff1a;BytUtilLog Byt是Big一统的缩写&#xff0c;大一统日志打印库&#xff0c;哈哈&#xff01;搞个笑&#xff0c;很早就写好了&#xff0c;但后面忙起来就忘了写一篇文章推一下它了&#xff…

90行代码写一个视频播放器

上一篇文章视频播放器的技术组成写了视频播放器的整体结构 下面我们就来上伪代码&#xff1a; #include <iostream> #include <thread> #include <list> #include <string> using namespace std;//下文代码都需要考虑并发&#xff0c;并发代码没写是…
最新文章