第3章 并行接口时序基础:读/写时序图、建立时间、保持时间、访问周期
并行接口的时序,说白了就是CPU和LCD之间怎么“握手”通信。我刚开始做LCD驱动时,总觉得时序图看着像天书——一堆波形、箭头、标注,完全不知道从哪看起。后来被一块屏折腾了三天,才发现问题就出在建立时间差了那么几纳秒。嗯,从那以后我再也不敢小看时序图了。
3.1 读/写时序图——先看懂“握手”信号
并行接口的读写操作,本质上是CPU向LCD发送命令/数据(写),或者从LCD读取状态/数据(读)。每次操作都靠一组控制信号来协调:
- CS (Chip Select):片选信号,低电平有效。只有CS拉低时,LCD才会响应总线上的操作。
- RS (Register Select):寄存器选择。RS=0表示写命令/读状态,RS=1表示写数据/读数据。
- RD (Read):读使能,低电平有效。拉低时LCD把数据放到数据总线上。
- WR (Write):写使能,低电平有效。拉低时CPU把数据锁存到LCD内部。
- D[7:0]:8位数据总线,双向传输。
我习惯把读写操作想象成一次“借书”的过程:CS是“图书馆开门”,RS是“你要借小说还是工具书”,RD/WR就是“把书递出来”或“把书放回去”。
3.1.1 写时序
写操作就是把数据从CPU送到LCD。典型的写时序流程如下:
- 先拉低CS,选中LCD芯片。
- 设置RS电平——写命令就拉低,写数据就拉高。
- 在数据总线上放好要发送的数据。
- 拉低WR,保持一段时间(WR低电平宽度),再拉高。
- LCD在WR上升沿锁存数据。
- 拉高CS,结束本次操作。
关键点:数据必须在WR上升沿之前稳定,并且在WR上升沿之后保持一段时间。这两个时间参数就是建立时间和保持时间。
3.1.2 读时序
读操作是从LCD读取数据到CPU。流程类似,但方向相反:
- 拉低CS,选中LCD。
- 设置RS电平——读状态拉低,读数据拉高。
- 拉低RD,LCD开始把数据放到总线上。
- CPU在RD上升沿读取总线上的数据。
- 拉高RD,LCD释放数据总线。
- 拉高CS,结束操作。
注意:读操作时,LCD需要一定时间才能把数据驱动到总线上(数据输出延迟)。如果CPU读得太快,可能读到的是上一笔数据或者高阻态。我曾经就因为RD脉冲太短,读回来的状态寄存器全是0xFF,排查了整整一个下午。
3.2 建立时间与保持时间——时序的“黄金法则”
这两个概念是所有同步接口的基石。你想想看,如果数据在时钟边沿到来时还在“抖动”,那锁存的结果就是不确定的。建立时间和保持时间就是为了保证数据在锁存时刻是稳定的。
| 参数 | 定义 | 典型值(以某款12864 LCD为例) |
|---|---|---|
| 建立时间 (tsu) | 数据在WR/RD有效沿之前必须稳定的最短时间 | 20 ns |
| 保持时间 (th) | 数据在WR/RD有效沿之后必须保持稳定的最长时间 | 10 ns |
| WR低电平宽度 (tWL) | WR信号保持低电平的最短时间 | 40 ns |
| RD低电平宽度 (tRL) | RD信号保持低电平的最短时间 | 40 ns |
| 访问周期 (tcyc) | 连续两次操作之间的最短间隔 | 100 ns |
我个人的经验是:建立时间比保持时间更容易出问题。因为保持时间通常较短(10ns左右),而建立时间受总线电容、走线长度影响更大。如果你发现LCD偶尔显示异常,先检查建立时间是否留够了余量。
实战技巧:在写代码时,我习惯在WR拉低之前插入一个NOP(空操作)指令,或者在WR拉高之后再读数据。这样虽然牺牲了一点速度,但能保证时序余量充足。对于低速MCU(如12MHz的51单片机),一个NOP大约83ns,足够覆盖大多数LCD的建立时间要求。
3.3 访问周期——别让LCD“喘不过气”
访问周期是指连续两次操作之间必须等待的最短时间。为什么要有这个限制?因为LCD内部需要时间来处理刚收到的数据——比如更新显示缓存、切换内部状态机等。
如果你连续快速发送命令,LCD可能来不及处理,导致数据丢失或显示错乱。我记得有一次调试一个动画效果,画面总是闪烁,后来发现是连续写数据太快,访问周期没满足,LCD内部缓冲区溢出了。
访问周期通常包括:
- 写周期:从WR下降沿到下一次WR下降沿的最短时间。
- 读周期:从RD下降沿到下一次RD下降沿的最短时间。
- 读写切换周期:从一次读操作结束到下一次写操作开始(或反之)的最短时间。
核心原则:所有时序参数都要满足LCD数据手册中的最小值。如果MCU速度太快,可以通过软件插入延时或调整GPIO翻转速度来降低速率。
3.4 时序参数速查表
下面这张表汇总了并行接口最常用的时序参数,建议你在设计时对照LCD的数据手册逐项核对:
| 符号 | 参数名称 | 方向 | 说明 |
|---|---|---|---|
| tsu | 数据建立时间 | 写 | 数据在WR上升沿前必须稳定 |
| th | 数据保持时间 | 写 | 数据在WR上升沿后必须保持 |
| tWL | WR低电平宽度 | 写 | WR拉低的最短持续时间 |
| tRL | RD低电平宽度 | 读 | RD拉低的最短持续时间 |
| td | 数据输出延迟 | 读 | RD下降沿到数据有效的最大时间 |
| tcyc | 访问周期 | 读写 | 连续操作的最小间隔 |
3.5 时序知识结构图
下面这张SVG图帮你理清并行接口时序的核心脉络:
3.6 实战中的避坑指南
最后分享几个我踩过的坑,希望能帮你少走弯路:
- 别只看典型值:数据手册里给的时序参数通常是“最小值”或“最大值”。我习惯在设计时留出20%~30%的余量,尤其是温度变化大的场合。
- GPIO翻转速度:MCU的GPIO输出不是瞬间完成的。如果你用50MHz的STM32,GPIO翻转一次大约需要20ns。算时序时要把这个时间算进去。
- 总线电容:如果LCD和MCU距离较远(比如通过排线连接),总线电容会增加信号上升时间。我曾经用了一根20cm的杜邦线,结果建立时间直接翻倍。
- 读操作要小心:读时序中,LCD输出数据需要时间(td)。如果RD脉冲太短,CPU读到的是无效数据。我一般会在RD拉高后再等一个NOP才去读数据总线。
调试小技巧:如果你手头有逻辑分析仪,强烈建议抓一下实际的时序波形。把CS、RS、RD/WR、数据线都接上,看看实际波形和数据手册是否一致。很多时候,眼睛看到的“差不多”和实际需要的“精确”之间,差的就是那几十纳秒。