4. 数据手册精读(下):时序图解读与关键参数

好,咱们接着往下聊。上一节我们把数据手册的“上半身”摸了个透,什么命令集、扇区结构、SPI模式,心里都有数了。这一节,咱们要啃硬骨头——时序图。

说实话,我早年刚入行时,最怕看时序图。密密麻麻的箭头、标号、时间参数,看着就头大。后来被项目逼着啃了几次,发现其实有套路。今天我把这些套路掰开揉碎了讲给你听。

4.1 读时序:从芯片里把数据“掏”出来

读操作是最基本的操作。你想想看,芯片上电后,CPU第一件事就是读代码,对吧?所以读时序必须搞懂。

标准的SPI读时序,大概长这样:

CS#     \________________________/
SCLK    _/ \_/ \_/ \_/ \_/ \_/ \_
SI      X  CMD  X  ADDR  X  DUMMY
SO      XXXXXXXXXXXXXXXX  DATA0 DATA1 ...

这里有几个关键点,我一个个说。

第一个,CS#片选信号。 拉低表示“我要跟你说话”,拉高表示“闭嘴”。注意,CS#拉高后必须保持一段时间(tSHSL),才能再次拉低。这个时间参数,手册里一定有。我见过有人把CS#拉高后立刻又拉低,结果芯片没反应,查了半天才发现是这里的问题。

第二个,命令字节。 读操作通常是0x03(普通读)或0x0B(快速读)。区别在于,快速读后面多了一个Dummy字节(空操作),给芯片一点准备时间。为什么?因为普通读的频率上限低,快速读可以跑高频。我习惯用快速读,除非对功耗有极致要求。

第三个,地址。 24位地址,先发高位。比如你要读地址0x123456,就发0x12、0x34、0x56。注意,有些芯片支持32位地址(比如大容量NOR Flash),那就得发4个字节。

第四个,数据输出。 在Dummy字节之后,SO引脚开始输出数据。每个时钟周期输出一个字节。注意,数据是在SCLK的上升沿被采样,还是下降沿?这个要看手册里的时序图标注。我踩过坑:有一次把采样沿搞反了,读出来的数据全是0xFF,折腾了两天才发现。

重要参数:

  • fSCK(时钟频率):普通读通常支持到50MHz,快速读可以到104MHz甚至更高
  • tCHQV(时钟高电平到数据有效):这个时间决定了数据输出的延迟,一般几纳秒
  • tSU(建立时间):数据必须在时钟沿之前稳定
  • tH(保持时间):数据必须在时钟沿之后保持稳定

4.2 写时序:把数据“塞”进芯片

写操作比读操作复杂。因为Flash写入前必须先擦除,而且写入需要时间。

标准的页编程时序:

CS#     \________________________________________/
SCLK    _/ \_/ \_/ ... \_/ \_/ \_/ \_/ \_/ \_
SI      0x02  ADDR  DATA0 DATA1 ... DATAn
SO      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

写操作的核心是页编程命令0x02。先发命令,再发地址,然后发数据。一页通常是256字节,但有些芯片是512字节。注意,你不能跨页写。什么意思?比如你从地址0x00F0开始写20个字节,写到0x0104,这就跨页了。芯片会报错,或者数据写不进去。

我有个习惯:写之前先读状态寄存器,确认芯片不忙(WIP位为0)。然后发写使能命令0x06,再发页编程命令。写完后,轮询状态寄存器,直到WIP位变0,才算写完。

注意: 写操作期间,CS#不能拉高。一旦拉高,芯片会认为命令结束,没写完的数据就丢了。我曾经在调试时,因为中断服务程序里不小心把CS#拉高了,导致数据写不全,查了整整一个下午。

4.3 擦除时序:把数据“清空”

擦除是Flash特有的操作。NOR Flash只能从1写0,不能从0写1。所以写之前必须先擦除,把整块区域变成全1(0xFF)。

擦除分两种:

  • 扇区擦除(0x20):擦除一个扇区,通常4KB或64KB
  • 整片擦除(0xC7):擦除整个芯片,慎用!

擦除时序和写时序类似,但数据阶段不需要发数据。发完命令和地址后,芯片就开始内部擦除。擦除时间比较长,扇区擦除一般几十到几百毫秒,整片擦除可能几秒。

擦除期间,你可以读状态寄存器来查进度。WIP位为1表示正在擦除,为0表示擦完了。注意,擦除期间不能发其他命令,否则芯片会忽略。

小技巧: 我习惯在擦除前先读一下状态寄存器的WEL位(写使能锁存位)。如果WEL为0,说明没使能,擦除命令不会执行。很多新手会漏掉这一步。

4.4 状态寄存器详解:芯片的“体检报告”

状态寄存器是芯片的“健康状态”指示器。通过读状态寄存器命令0x05,你可以知道芯片当前在干嘛。

标准的状态寄存器格式:

名称说明
7SRWD状态寄存器写保护
6BP3块保护位3
5BP2块保护位2
4BP1块保护位1
3BP0块保护位0
2WEL写使能锁存位
1BP块保护状态(有些芯片没有)
0WIP写进行中

最常用的是WIP位和WEL位。WIP为1表示芯片正在执行写或擦除操作,不能打扰。WEL为1表示写使能已打开,可以执行写或擦除命令。

BP0~BP3是块保护位,用来锁定某些区域不让写。比如你想保护Bootloader区域不被意外擦除,就可以设置这些位。注意,一旦设置了块保护,想改回来得先发写状态寄存器命令0x01,而且需要把SRWD位清0。

我遇到过一个问题:有客户说芯片写不进去,我远程一看,状态寄存器的BP位全置1了,整个芯片被保护了。一问才知道,是上电时序有问题,导致芯片误入了保护模式。

4.5 器件ID与唯一ID读取

每个NOR Flash芯片都有一个唯一的ID,用来识别厂家、型号、版本。读取ID的命令通常是0x9F(JEDEC ID)或0x90(厂家ID)。

JEDEC ID返回3个字节:

  • 第1字节:厂家代码(比如Winbond是0xEF,Macronix是0xC2)
  • 第2字节:内存类型
  • 第3字节:容量

比如Winbond W25Q64的ID是0xEF 0x40 0x17。你可以在驱动初始化时读一下ID,确认芯片型号对不对。我习惯在驱动里加一个校验函数,如果ID不匹配,直接报错返回。

除了器件ID,很多芯片还有唯一ID(Unique ID),长度64位或128位。这个ID全球唯一,可以用来做设备认证、加密等。读取命令通常是0x4B,后面跟4个Dummy字节,然后读数据。

实用场景: 我在一个物联网项目里,用唯一ID作为设备的MAC地址。省去了单独烧录MAC的步骤,生产时直接读芯片ID就行,省了不少事。

4.6 电气特性与功耗参数

这部分是硬件工程师最关心的。你写驱动时,时序参数必须和电气特性对上。

关键参数:

  • VCC(供电电压):通常2.7V~3.6V,有些低电压版本是1.8V
  • ICC(工作电流):读操作一般几mA到十几mA,写操作可能到20mA,擦除时电流最大
  • ISB(待机电流):芯片不工作时,电流一般几μA,深掉电模式可以到1μA以下
  • tPU(上电时间):从VCC稳定到芯片准备好,一般几ms

我特别提醒一下上电时序。芯片上电后,需要等VCC稳定,再等tPU时间,才能发命令。如果VCC还没稳定就发命令,芯片可能进入未知状态。我见过一个案例:某设备上电后偶尔死机,查了半个月,发现是电源纹波太大,导致芯片上电时序不满足要求。

另外,功耗也要注意。如果你做的是电池供电设备,擦除操作很耗电。我一般会在擦除前检查电池电量,如果电量低,就推迟擦除,或者分多次擦除。

4.7 本章小结

嗯,这一节内容不少。时序图、状态寄存器、ID读取、电气特性,都是驱动开发绕不开的硬知识。我个人觉得,最核心的是两点:

  • 时序参数要算准:建立时间、保持时间、时钟频率,一个都不能错
  • 状态寄存器要勤查:写之前查WEL,写之后查WIP,这是铁律

你把这些搞懂了,NOR Flash驱动的基本功就算扎实了。剩下的就是多写代码、多调试,慢慢积累经验。


公众号:蓝海资料掘金营,微信deep3321