闪存驱动(W25R128FV、STC12C5A60S2)

lianxian连线 2017-06-07 05:11:06
前文汇编码、机器码、十六进制码相互转换阐述了在各种码字之间进行相互转换的方法。液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2)也说了使用此法实现的一个驱动。

本文再使用此法实现一个闪存驱动。

这次以 宏晶 STC12C5A60S2 与 闪存 W25R128FV 为例。

1. 连线

首先将 STC12C5A60S2 连接 PL2303



然后依 液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2) 1.节 连线 所述,连接 液晶显示屏 KD014QQTBN001:



然后再连接 W25R128FV:



W25R128FV 是一个 SPI 闪存,引脚少封装小,只用两个引脚 DIDO 就可以实现读写。同时还支持一种 四位串行SPI 的快速读写,用的是四个引脚 D0-D3

图中 STC12C5A60S2 的高 4 位 P3.4~P3.7 连接 W25R128FV 的数据线 DIDOWPHold/Reset,它们也是 四位串行SPI 模式下的 D0-D3P1.5P1.6 连接 W25R128FV 的控制线 CSCLK

2. 启动

闪存硬件重置之后是处于 SPI 模式的。命令数据的发送接收 都只有 DIDO。带来的好处很明显:读写简单。

如何实现读写 SPI 呢?

STC 芯片的 引脚 和 地址 对应关系 如下:



通过操作地址,我们可以某种方式,控制引脚电平。这样,我们就可以写出 汇编码:

2.1 写一位

根据 W25R128FV 芯片手册 SPI 写 时序图:



可以得到 汇编码:



0x96P1.6 对应 CLK0xb4P3.4 对应 DI

P3.4 的 一位 在 CLK 上升沿 写入 闪存。
或者说,标志位 C 的一位 在执行 SETB 0x96 时 写入 闪存。

2.2 读一位

同理根据 SPI 读 时序图:



可以得到 汇编码:



0xb5P3.5 对应 DO

P3.5 的 一位 在 CLK 下降沿 读出 闪存。
或者说,在执行 CLR 0x96 时 读出 闪存 一位 到 标志位 C。

可以看到 写/读一位 汇编码 形式很相似。这是因为我们是 在 上升沿 写入、在 下降沿 读出 的。

2.3 写一个字节

液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2)3.2节 小循环 所述的 循环方法,我们可以同理得到 写一个字节 的汇编码:



同理还有 读一个字节 的汇编码:

2.4 读一个字节



我们发现,在 2.3节、2.4节 中,都含有 2.1节、2.2节 的代码,因此也可按 液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2)2.3节 函数 所述,将 写一位、读一位 实现成为 函数,在 写一个字节、读一个字节 中进行调用。还可将 写一个字节、读一个字节 也实现成为 函数,提供给 驱动 进行调用。

我们将 写一个字节、读一个字节 分别写在 0x0009 和 0x001c 内存里。有了这些读写函数,接下来就可以进行 闪存读写。

2.5 软件重置

驱动 在读写 闪存 前需要进行 软件重置 0x66 0x99



这样应该就能正常读写了。

读写的命令有这些:
闪存 要先 擦除 0x20 然后 写入 0x02。擦除/写入 前还要记得 使写 0x06
闪存 读取 0x03 则可以直接读取。
还有 四位串行 读 0x32 和 写 0x6b 等等。
其他更多内容可以直接参见 W25R128FV 的芯片手册。

3. 驱动

闪存擦除 0x20 一次擦除一个区块 4KB,写入 0x02 一次写入一页 256B,因此 擦除/写入 一般分开实现。读取 0x03 也是一次读取一页 256B 的。

3.1 速度

W25R128FV 是一个 NOR 型闪存,读快(ns/bit)、写慢(μs/bit、ms/page)。1ns 是 10^-9s,1μs 是 10^-6s,1ms 是 10^-3s。可见读写速度比例是很大的,具体可以参考 芯片手册 的 交流电气特性表(AC Characteristics)。对于 STC12C5A60S2 装备 12MHz 晶振 的情况,一般是:



因此,在 闪存擦/写 后,要进行 同步。

3.2 同步

什么是同步?

写后读 情况,必须等待 写 完成才能 读。这样才能读取到正确的数值。同理,擦后写 情况 也一样。
可称之为 同步。

如何知道 擦/写 是否完成呢?

闪存 里有一个 标志位 BUSY 表示 闪存擦/写 是否还在进行。它是 只读 的。使用命令 0x05 可以读取到一个 8位 状态寄存器I,BUSY 就是其 第1位。

于是就有 汇编码 如下:



首先调用 写一个字节 0x0003 写命令 0x05,然后调用 读一个字节 0x0006 读状态寄存器I 到 累加器 A。
然后对 A 逻辑与 一个立即数 #0x01,得到 其中的 第1位:BUSY 标志位。循环等待其为 0。

注意了。读一个字节 前,一定要记得 将 引脚IO端口 置为 高阻(High Impedance) 态。可以 将引脚 DO 拉高电平 实现,这里就是 指令 SETB 0xb5

3.3 高阻

“高阻” 意即 “悬空”。在通常 空气 中电阻很大,相当于对地接 无穷大 电阻,因此得名。

“高阻” 是与 “高电平” 和 “低电平” 都不相同的 第三种 状态。如果对 寄存器 即 SRAM 静态存储单元 写入,则 “高阻” 即为 “保持原值”。

关于 SRAM 静态存储单元:



看:四个晶体管搭建静态存储单元,加两个晶体管搭建写控制电路

4. 操作系统

驱动 完成了 闪存 的 读写 和 同步。操作系统 直接 调用 驱动 即可完成读写。

4.1 内存

内存(Memory)由 操作系统 管理。因此我们必须明确 内存 大小。

什么是内存?

内存 通常指的是一组 SRAM 静态存储单元 组成的一个 存储器。前几个最常用,因此编号成为 寄存器(Register)。中间一段,通常拿来当作 栈(Stack)。然后的一段,可以用来存储 变量(Variable),也可以用来实现 代码投影(Code Shadowing)。

关于 代码投影 参考 液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2)4.1节 代码投影

STC12C5A60S2 只有 256B 内存。
前 8 个编号成为 R0-7(0x00~0x07),可用于 汇编指令 中进行各种 寻址。
前 2 个 R0、R1 还支持 寄存器间接 寻址。
栈顶指针 SP 初始值是 0x07,入栈 微指令 是 先 (SP)<-(SP)+1 再 入栈的,因而 从 0x08 开始的一段 就是 栈。

设栈空间 0x08~0x7f,那么 0x80~0xff 这段 128B 内存,即可用于实现 调页。

4.2 调页

调页 用于在 内存 与 外存 之间 交换数据。

调页 与 分页 有所不同。分页用于 汇编语言 模块化程序设计。关于 分页,参见 液晶显示屏驱动(KD014QQTBN001、STC12C5A60S2)4.2节 分页

我们先用 分页 技术,将 2.节 的 启动、3.节 的 闪存驱动、4.节 的 操作系统 以及 前文所述 的 液晶显示屏驱动 以及我们的数据,分别部署在 内存的 5 个页中:



启动代码 执行完成后,跳转到 操作系统。操作系统 调用 驱动 实现 内存与闪存 之间的 换页 功能。

每页首部都是跳转指令(LJMPAJMP)。驱动中,居于首部的 4 个 AJMP 分别指向 4 个 说明的 驱动功能,由 操作系统 LJMP 长跳转 来调用;然后的几个 AJMP 指向一些 读写操作,由 驱动自己 来调用。

在 分页 基础上 如何实现 换页 呢?可以这样实现:

例如,指定 内存 0x80、闪存 区码 和 页码 都是 0x000000:
操作系统首先调用 ACALL 0x0005 长跳转 LJMP 0x1800 到闪存驱动 0x0000,擦除闪存一个区 0x000000 为首地址的 4KB。
然后调用 ACALL 0x000b 长跳转 LJMP 0x1804 到闪存驱动 0x0004,将内存 0x80 为首地址的半页 128B 写入 闪存的一个半页 0x000000 为首地址的 128B。然后再调用 ACALL 0x000e 从闪存再读出这 128B 到内存。接下来就可以显示啦。

5. 实物图

汇编码、机器码、十六进制码相互转换所述方法,将本文 汇编码 生成 十六进制码,同时 按前文所述 指定每个 页 的地址,这就得到一个 HEX文件。HEX文件 可以直接 烧写入 STC12C5A60S2。烧写成功实物图如下:



本文资源链接:
http://www.pudn.com/Download/item/id/3186010.html
http://download.csdn.net/detail/elecfans2csdn/9861717
...全文
811 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
lianxian连线 2017-07-26
  • 打赏
  • 举报
回复
引用 7 楼 bianjieduochongyinty 的回复:
这篇文章相比上篇,在液晶显示屏驱动上加了闪存驱动吧,我想问上篇文章中关于液晶显示屏驱动的汇编码,这篇还可否继续引用之?
当然可以 “引用”。因为这 3 篇文章的 “主旨” 都是 相同 的。
  • 打赏
  • 举报
回复
这篇文章相比上篇,在液晶显示屏驱动上加了闪存驱动吧,我想问上篇文章中关于液晶显示屏驱动的汇编码,这篇还可否继续引用之?
lianxian连线 2017-07-24
  • 打赏
  • 举报
回复
引用 5 楼 shangzuoyingxiangult 的回复:
连接 W25R128FV 的那张图 上左 部位似乎应该多画一个元件?(实物图有 2 个 PL2303转换器 然而电路图只找到 1 个。)
第 2 个 PL2303(上面的那个)纯粹是为了提供 3.3V 电源,以供 新 版 KD014QQTBN001。考虑到电路图 上左 部位将来还可以 “扩展”,从而就省略了一个 PL2303。
  • 打赏
  • 举报
回复
连接 W25R128FV 的那张图 上左 部位似乎应该多画一个元件?(实物图有 2 个 PL2303转换器 然而电路图只找到 1 个。)
lianxian连线 2017-06-26
  • 打赏
  • 举报
回复
引用 3 楼 qq_39192077 的回复:
“设栈空间 0x08~0x7f” 似乎不严谨哈。
哈哈,五天前你评论的那天真有趣!那天是 夏至 节气,而今年 夏至 交节时刻,地球 正对太阳位置 在 我们 中国!我给你计算一下: 夏至交接时刻时间:2017年06月21日 12:24:06 地球正对太阳位置:北京时间 正午 过后 24.1 分钟,东八区中心 120经度 转过 6.025度,就是 113度58分30秒 经度;太阳直射点 在 北回归线 上,就是 23度26分 纬度:广东省惠州市龙门县麻榨镇下龙村东南方向约1.72公里!
qq_39192077 2017-06-21
  • 打赏
  • 举报
回复
“设栈空间 0x08~0x7f” 似乎不严谨哈。
lianxian连线 2017-06-20
  • 打赏
  • 举报
回复
引用 1 楼 baidu_39207891 的回复:
楼主挺用心的!写得详细!
哈哈,谢谢!
baidu_39207891 2017-06-20
  • 打赏
  • 举报
回复
楼主挺用心的!写得详细!

21,595

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧