无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在运行程序才会用到,有些数据体量较大对于获取时效性并不太强,各种各样的数据也就有不同的存储载体,这次在EEPROM读写中,顺道把看到的关于存储的一些东西整理一下,有些话来自于网友,所以还是那句话,看到的人要带着自己的思考去看,记住尽信书不如无书,fighting!!!

一、基本概念

最熟悉的两个词语应该是RAM与ROM,RAM(Random Access Memory)的全名为随机存取记忆体,它相当于PC机上的移动存储,用来存储和保存数据的。它在任何时候都可以读写,RAM通常是作为操作系统或其他正在运行程序的临时存储介质,它的一切都是最好的,唯一缺点断电一切东西都没有了。一般情况下,现在移动设备也多了,我们叫它内存,更通常的叫运行内存。还有一个熟悉的词DDR2或DDR3,后面还会学习到的。

 ROM(Read Only Memory)的全名为唯读记忆体,它相当于PC机上的硬盘,用来存储和保存数据。ROM数据不能随意更新,但是在任何时候都可以读取。即使是断电,ROM也能够保留数据。但是资料一但写入后只能用特殊方法或根本无法更改,但这么久了ROM已经有了很大的发展,不再是最初的摸样了。rom最初不能编程,出厂什么内容就永远什么内容,不灵活。后来出现了prom,可以自己写入一次,要是写错了,只能换一片,自认倒霉。人类文明不断进步,终于出现了可多次擦除写入的EPROM,每次擦除要把芯片拿到紫外线上照一下,想一下你往单片机上下了一个程序之后发现有个地方需要加一句话,为此你要把单片机放紫外灯下照半小时,然后才能再下一次,这么折腾一天也改不了几次。历史的车轮不断前进,伟大的EEPROM出现了,拯救了一大批程序员,终于可以随意的修改rom中的内容了,这一段话就说出了ROM的发展历程。

狭义的EEPROM:这种rom的特点是可以随机访问和修改任何一个字节,可以往每个bit中写入0或者1。这是最传统的一种EEPROM,掉电后数据不丢失,可以保存100年,可以擦写100w次。具有较高的可靠性,但是电路复杂/成本也高。它的改写是由高电压或者由控制端的逻辑电平来完成的。因此目前的EEPROM都是几十千字节到几百千字节的,绝少有超过512K的。我们也就发现了EEPROM的确可以实现随意读写,EEPROM的全称是“电可擦除可编程只读存储器”,即Electrically Erasable Programmable Read-Only Memory。可介绍的这两种都不存在大容量并且也十分昂贵,那我们平时见到的几十G的存储设备是什么?flash就应运而生了。flash属于广义的EEPROM,因为它也是电擦除的rom。但是为了区别于一般的按字节为单位的擦写的EEPROM,我们都叫它flash。flash做的改进就是擦除时不再以字节为单位,而是以为单位,一次简化了电路,数据密度更高,降低了成本。上M的rom一般都是flash。

ROM的应用

对数指数、三角函数等常规计算通过写出真值表,将自变量以地址码的形式输至ROM,用ROM表来实现。还有码制转换,例如二进制码转格雷码。脉冲序列发生器,伪彩色处理电路,也就是将一幅黑白图像变成彩色图像显示,将灰度图像对应到red , green , blue三个通道上,最后将三个通道的颜色值合成为需要显示的RGB颜色值即可。具体可参考       https://blog.csdn.net/huixingshao/article/details/42706699 。

接下来说一下flash的分类,flash分为nor flashnand flash。nor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。NOR Flash的读取,用户可以直接运行装载在NOR FLASH里面的代码。NAND Flash没有采取内存RAM的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。nandflash引脚上复用,因此读取速度比nor flash慢一点,但是擦除和写入速度比nor flash快很多。nand flash内部电路更简单,因此数据密度大,体积小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。nor flash可以进行字节寻址,所以程序可以在nor flash中运行。嵌入式系统多用一个小容量的nor flash存储引导代码,用一个大容量的nand flash存放文件系统和内核。

二、I2C总线

这个在我转载的一篇文章里面有很详细的描述,就不在提及了。有一个问题是无论UART还是I2C都是串行按位传输数据,区别在哪?还有SPI传输,下面分别总结一下三者的特点。

UART:两线,一根发送一根接收,可以全双工通信,数据异步传输,对双方的时序要求比较严格,在多机通信上面用的最多。按照标准波特率完成双向通讯,速度慢,之前提到采集一位数据就需要16个时钟周期,适合远距离传输,比如IEEE488定义并行通行状态时,规定设备线总常不得超过20米,并且任意两个设备间的长度不得超过2米;而对于串口而言,长度可达1200米。UART需要固定的波特率,就是说两位数据的间隔要相等,

I2C:能用于替代标准的并行总线,能连接的各种集成电路和功能模块。I2C是多主控总线,所以任何一个设备都能像主控器一样工作,并控制总线。总线上每一个设备都有一个独一无二的地址,根据设备它们自己的能力,它们可以作为发射器或接收器工作。多路微控制器能在同一个I2C总线上共存,当然在任何时间点上只能有一个主控。一般用于同一板卡上芯片之间的通信,较少用于远距离通信。

SPI:SPI接口和UART相比,多了一条同步时钟线,对通信双方的时序要求不严格不同设备之间可以很容易结合,而且通信速度非常快。一般用在产品内部元件之间的高速数据通信上面,如大容量存储器flash等。高速同步串行口,3~4线接口,收发独立、可同步进行。

三、EEPROM通信举例

同样通过一个程序来学习里面内容。自加内容用红笔标出。

1)IIC_WR模块

写步骤:

a.     实现开始信号

b.     发送24LC04B设备地址,从机发送应答信号

c.     发送待存储数据的地址,接受应答信号

d.     发送待写入数据,接受应答信号

e.     实现结束信号

读步骤:

a.     实现开始信号

b.     发送24LC04B设备地址,从机发送应答信号

c.     发送待读取数据的地址,接受应答信号

d.     实现开始信号

e.     发送24LC04B设备地址,从机发送应答信号

f.     读取8位数据

g.     实现非应答信号

h.     实现结束信号

通过状态机 i 来切换 IIC 的不同状态,譬 如接收到写命令,状态机i=0 转入 Start 状态,SDA 先变低,再 SCL 变低;状态机i=1 开 始 转 入 写 设 备 地 址 0xA0; 之 后 状 态 机 转 到 7 开 始 发 送 8 位 的 数 据 , 其 中 状 态 机 i=7,8,9,10,11,12,13,14 是 IIC 发送8位的数据,然后状态机进入 i=15 等待 IIC 从设备的应答 信号。状态机 i=16 为判断是否有应答,如果有的话状态机转到 i=2 写 IIC 的地址,然后状态机 又是重复i=7,8,9,10,11,12,13,14 发送地址和 i=15 等待应答,i=16 判断应答。最后状态机 i=3 开始发送 IIC 写数据。发送完数据 i=4 发送 Stop 信号。

 module iic_com
(
input CLK,
input RSTn, input [:] Start_Sig, //read or write command
input [:] Addr_Sig, //eeprom words address
input [:] WrData, //eeprom write data
output [:] RdData, //eeprom read data
output Done_Sig, //eeprom read/write finish output SCL,
inout SDA ); parameter F250K = 'd200; //250Khz的时钟分频系数 //200分频系数不必要用到9位,可改为8 reg [:]i; //用来只是状态机
reg [:]Go;
reg [:]C1;
reg [:]rData; //读信号
reg rSCL;
reg rSDA;
reg isAck;
reg isDone; //结束信号
reg isOut; assign Done_Sig = isDone;
assign RdData = rData;
assign SCL = rSCL;
assign SDA = isOut ? rSDA : 'bz; //SDA数据输出选择 //SDA的数据输出受到SCL的控制,SCL为高时SDA保持不变,在接收应答位期间SDA也受控制 //****************************************//
//* I2C读写处理程序 *//
//****************************************//
always @ ( posedge CLK or negedge RSTn )
if( !RSTn ) begin
i <= 'd0; //状态机初始为0
Go <= 'd0;
C1 <= 'd0;
rData <= 'd0;
rSCL <= 'b1; //数据线和时钟线保持高电平初始值
rSDA <= 'b1;
isAck <= 'b1; //信号为低电平时规定为有效应答位,高电平为非有效应答位。
isDone <= 'b0;
isOut <= 'b1;
end
else if( Start_Sig[] ) //I2C 数据写 //start_sig用来判断数据为读或写
case( i ) : //发送IIC开始信号
begin
isOut <= ; //SDA端口输出 if( C1 == ) rSCL <= 'b1;
else if( C1 == ) rSCL <= 'b0; //SCL由高变低 //分频系数为200,计数到200表明经过一个新的时钟周期时钟线变为低电平。 if( C1 == ) rSDA <= 'b1;
else if( C1 == ) rSDA <= 'b0; //SDA先由高变低 //计数到100时,rSDA变为低电平,符合信号开始发送条件。 if( C1 == -) begin C1 <= 'd0; i <= i + 1'b1; end
else C1 <= C1 + 'b1; //i=0用来表示开始信号发送,根据SDA与SCL变化可得,此处C1到达249,表示又过了四分之一个新时钟周期,i+1,运行下一步
end : // Write Device Addr
begin rData <= {'b1010, 3'b000, 'b0}; i <= 5'd7; Go <= i + 'b1; end //非阻塞赋值,Go为2 : // Wirte Word Addr
begin rData <= Addr_Sig; i <= 'd7; Go <= i + 1'b1; end //addr_sig为word address,Go为3 : // Write Data
begin rData <= WrData; i <= 'd7; Go <= i + 1'b1; end //写入数据,Go为4,WrData数值赋给rData。 : //发送IIC停止信号
begin
isOut <= 'b1; if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL先由低变高 if( C1 == ) rSDA <= 'b0;
else if( C1 == ) rSDA <= 'b1; //SDA由低变高 //SCL处于高电位时,SDA由低到高变化,处于结束位 if( C1 == - ) begin C1 <= 'd0; i <= i + 1'b1; end
else C1 <= C1 + 'b1;
end :
begin isDone <= 'b1; i <= i + 1'b1; end //写I2C 结束 :
begin isDone <= 'b0; i <= 5'd0; end ,,,,,,,: //发送Device Addr/Word Addr/Write Data
begin
isOut <= 'b1; //isout=1, SDA <= rSDA
rSDA <= rData[-i]; //高位先发送 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL高电平100个时钟周期,低电平100个时钟周期
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= i + 1'b1; end //产生250Khz的IIC时钟 //i=14运行之后,状态机i=15
else C1 <= C1 + 'b1;
end : // waiting for acknowledge
begin
isOut <= 'b0; //SDA端口改为输入
if( C1 == ) isAck <= SDA; //读取IIC 从设备的应答信号 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL高电平100个时钟周期,低电平100个时钟周期
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= i + 1'b1; end //产生250Khz的IIC时钟
else C1 <= C1 + 'b1;
end :
if( isAck != ) i <= 'd0; //判断是否接收到应答信号
else i <= Go; //状态机i=1时,计算出i=2

endcase else if( Start_Sig[] ) //I2C 数据读
case( i ) : // Start
begin
isOut <= ; //SDA端口输出 if( C1 == ) rSCL <= 'b1;
else if( C1 == ) rSCL <= 'b0; //SCL由高变低 if( C1 == ) rSDA <= 'b1;
else if( C1 == ) rSDA <= 'b0; //SDA先由高变低 if( C1 == - ) begin C1 <= 'd0; i <= i + 1'b1; end
else C1 <= C1 + 'b1;
end : // Write Device Addr(设备地址)
begin rData <= {'b1010, 3'b000, 'b0}; i <= 5'd9; Go <= i + 'b1; end //先进行一个伪写操作 : // Wirte Word Addr(EEPROM的写地址)
begin rData <= Addr_Sig; i <= 'd9; Go <= i + 1'b1; end : // Start again
begin
isOut <= 'b1; //开始进行读操作 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1;
else if( C1 == ) rSCL <= 'b0; if( C1 == ) rSDA <= 'b0;
else if( C1 == ) rSDA <= 'b1;
else if( C1 == ) rSDA <= 'b0; if( C1 == - ) begin C1 <= 'd0; i <= i + 1'b1; end
else C1 <= C1 + 'b1;
end : // Write Device Addr ( Read )
begin rData <= {'b1010, 3'b000, 'b1}; i <= 5'd9; Go <= i + 'b1; end : // Read Data
begin rData <= 'd0; i <= 5'd19; Go <= i + 'b1; end : // Stop
begin
isOut <= 'b1;
if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; if( C1 == ) rSDA <= 'b0;
else if( C1 == ) rSDA <= 'b1; if( C1 == - ) begin C1 <= 'd0; i <= i + 1'b1; end
else C1 <= C1 + 'b1;
end : //写I2C 结束
begin isDone <= 'b1; i <= i + 1'b1; end :
begin isDone <= 'b0; i <= 5'd0; end ,,,,,,,: //发送Device Addr(write)/Word Addr/Device Addr(read)
begin
isOut <= 'b1;
rSDA <= rData[-i]; //高位先发送 //将rData数据赋值给数据线,伪写操作 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL高电平100个时钟周期,低电平100个时钟周期
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= i + 1'b1; end //产生250Khz的IIC时钟
else C1 <= C1 + 'b1;
end : // waiting for acknowledge
begin
isOut <= 'b0; //SDA端口改为输入 if( C1 == ) isAck <= SDA; //读取IIC 的应答信号 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL高电平100个时钟周期,低电平100个时钟周期
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= i + 1'b1; end //产生250Khz的IIC时钟
else C1 <= C1 + 'b1;
end :
if( isAck != ) i <= 'd0;
else i <= Go; ,,,,,,,: // Read data
begin
isOut <= 'b0;
if( C1 == ) rData[-i] <= SDA; //高位先接收 if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1; //SCL高电平100个时钟周期,低电平100个时钟周期
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= i + 1'b1; end //产生250Khz的IIC时钟
else C1 <= C1 + 'b1;
end : // no acknowledge
begin
isOut <= 'b1; if( C1 == ) rSCL <= 'b0;
else if( C1 == ) rSCL <= 'b1;
else if( C1 == ) rSCL <= 'b0; if( C1 == F250K - ) begin C1 <= 'd0; i <= Go; end
else C1 <= C1 + 'b1;
end endcase endmodule

EEPROM读写学习笔记与I2C总线(二)的更多相关文章

  1. EEPROM读写学习笔记与I2C总线(转)

    reference:https://www.cnblogs.com/uiojhi/p/7565232.html 无论任何电子产品都会涉及到数据的产生与数据的保存,这个数据可能并不是用来长久保存,只是在 ...

  2. Java菜鸟学习笔记--数组篇(三):二维数组

    定义 //1.二维数组的定义 //2.二维数组的内存空间 //3.不规则数组 package me.array; public class Array2Demo{ public static void ...

  3. JavaScript学习笔记之数组(二)

    JavaScript学习笔记之数组(二) 1.['1','2','3'].map(parseInt) 输出什么,为什么? ['1','2','3'].map(parseInt)//[1,NaN,NaN ...

  4. vue2.0学习笔记之路由(二)路由嵌套+动画

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. vue2.0学习笔记之路由(二)路由嵌套

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  6. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十二章:四元数(QUATERNIONS)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第二十二章:四元数(QUATERNIONS) 学习目标 回顾复数,以及 ...

  7. Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader)

    原文:Introduction to 3D Game Programming with DirectX 12 学习笔记之 --- 第十二章:几何着色器(The Geometry Shader) 代码工 ...

  8. C# IO流与文件读写学习笔记

    本笔记摘抄自:https://www.cnblogs.com/liyangLife/p/4797583.html,记录一下学习过程以备后续查用. 一.文件系统 1.1文件系统类的介绍 文件操作类大都在 ...

  9. Dynamic CRM 2013学习笔记(三十二)自定义审批流3 - 节点及实体配置

    上次介绍了<Dynamic CRM 2013学习笔记(十九)自定义审批流1 - 效果演示> 以及如何配置自定义审批流的按钮:<Dynamic CRM 2013学习笔记(二十一)自定义 ...

随机推荐

  1. vagrant安装centos7

    1. 安装VirtualBox 去官网https://www.virtualbox.org/wiki/Downloads下载最新版的Virtualbox,然后双击安装,一直点击确认完成. 2. 安装V ...

  2. 设计模式:命令(Command)模式

    设计模式:命令(Command)模式 一.前言 命令也是类,将命令作为一个类来保存,当要使用的时候可以直接拿来使用,比如脚本语言写出的脚本,只需要一个命令就能执行得到我们想要的需要操作很长时间才能得到 ...

  3. 设计模式:外观(Facade)模式

    设计模式:外观(Facade)模式 一.前言   外观模式是一种非常简单的模式,简单到我们经常都会使用,比如对于类A和B,如果两者需要交互,经过一定的处理过程才能实现某一个具体的功能,那么我们可以将这 ...

  4. python 中的list&tuple

    list Python内置的一种数据类型是列表:list>>> classmates = ['Michael', 'Bob', 'Tracy']>>> classm ...

  5. php大文件上传失败的原因及解决方法

    为什么上传大文件总是失败,上传小文件就没有问题.关于PHP大文件上传失败的原因及解决方法如下: 第1种情况:文件上传时存放文件的临时目录必须是开启的并且是 PHP 进程所有者用户可写的目录.如果未指定 ...

  6. rolllup巧用

    --构造环境drop table dept purge;drop table emp purge;create table dept as select * from scott.dept;creat ...

  7. July 02nd 2017 Week 27th Sunday

    No safe wading in an unknown water. 未知水深浅,涉水有危险. Is this the theory that has been the guideline for ...

  8. Java关于日期时间的工具类

    import java.sql.Timestamp; import java.text.ParseException; import java.text.ParsePosition; import j ...

  9. 更新UI的几种方式

    在学习Handler的过程中牵涉到UI的更新,在这里就总结一下更新UI的四种方式吧,用法都比较简单,直接看代码就可以了. 一.使用Handler的post方法 新建项目,修改MainActivity代 ...

  10. “ping某个IP地址,如果ping不通则在dos窗口或弹出MsgBox提示原因”的批处理bat命令

    “ping某个IP地址,如果ping不通则在dos窗口提示原因”的批处理bat命令 @echo off&setlocal enabledelayedexpansion title Ping检测 ...