远程加载固件_flash基础知识_3-1

1 篇文章 0 订阅
1 篇文章 0 订阅

                                       Flash基础知识以及常用指令

  1. 背景知识

最近在做的项目中,有个需要支持远程升级固件的需求。大体架构就是通过上位机把需更新的固件下发到FPGA中,然后通过FPGA写入用来存放固件的Flash里。调试了一段时间,总算实现了这一功能,在实现的过程中,网上前辈们的分享帮了我很大的忙,所以作为回馈,我也把实现过程中相关的知识点记录下来与大家分享,可能有说得不对的地方,欢迎指正。打算分三篇文章来讲述。

 

  1. Flash的简单分类

flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。flash按照内部存储结构不同,大体上可以分为两种:nor flashnand flashnor flash数据线和地址线分开,可以实现ram一样的随机寻址功能,可以读取任何一个字节。但是擦除仍要按块来擦。nand flash同样是按块擦除,但是数据线和地址线复用,不能利用地址线随机寻址。读取只能按页来读取。NOR Flash的读取,用户可以直接运行装载在NOR FLASH里面的代码。NAND Flash没有采取内存RAM的随机读取技术,它的读取是以一次读取一块的形式来进行的,通常是一次读取512个字节,采用这种技术的Flash比较廉价。用户不能直接运行NAND Flash上的代码,因此好多使用NAND Flash的开发板除了使用NAND Flah以外,还作上了一块小的NOR Flash来运行启动代码。

现在有些芯片为了减少启动引脚,可以采用QSPI serial flash作为启动存储器。其采用spi/qspi 的方式进行串行的读取数据,减小引脚消耗。这个只是通信方式的改变,其内部结构一般还是nor flash或者nand flash。这个我们就不去深究了。

 

  1. Flash常用指令以及时序关系

Flash常用的指令操作有:读ID、擦除(有三种方式)、页编程、读操作(读寄存器、读数据)、写操作(设置寄存器)等。一般来说,Flash的地址是24bit,最大只能支持16MB,面对更大容量的Flash,需要将地址配置为32bit,也就是需要将flash设置成4-byte模式。

下面以微邦的一款flash芯片W25Q128手册为例,描述一下上述的主要操作指令。不同厂家的芯片可能指令码可能会不大一样,注意区别。涉及到的时序,以标准的四线SPI总线为例,因为这种模式用的最普遍。

 FLASH常用芯片指令表(部分)

指令

第一字节(指令编码)

第二字节

第三字节

第四字节

第五字节

第六字节

第七-N字节

Write Enable

06h

 

 

 

 

 

 

Write Disable

04h

 

 

 

 

 

 

Read Status Register

05h

(S7–S0)

 

 

 

 

 

Write Status Register

01h

(S7–S0)

 

 

 

 

 

Read Data

03h

A23–A16

A15–A8

A7–A0

(D7–D0)

(Next byte)

continuous

Fast Read

0Bh

A23–A16

A15–A8

A7–A0

dummy

(D7–D0)

(Next Byte) continuous

Fast Read Dual Output

3Bh

A23–A16

A15–A8

A7–A0

dummy

I/O = (D6,D4,D2,D0) O = (D7,D5,D3,D1)

(one byte per 4 clocks, continuous)

Page Program

02h

A23–A16

A15–A8

A7–A0

D7–D0

Next byte

Up to 256 bytes

Block Erase(64KB)

D8h

A23–A16

A15–A8

A7–A0

 

 

 

Sector Erase(4KB)

20h

A23–A16

A15–A8

A7–A0

 

 

 

Chip Erase

C7h

 

 

 

 

 

 

Power-down

B9h

 

 

 

 

 

 

Release Power- down / Device ID

ABh

dummy

dummy

dummy

(ID7-ID0)

 

 

Manufacturer/ Device ID

90h

dummy

dummy

00h

(M7-M0)

(ID7-ID0)

 

JEDEC ID

9Fh

(M7-M0)

生产厂商

(ID15-ID8)

 存储器类型

(ID7-ID0) 容量

 

 

 

该表中的第一列为指令名,第二列为指令编码,第三至第N列的具体内容根据指令的不同而有不同的含义。表中“A0~A23”指FLASH芯片内部存储器组织的地址;“M0~M7为厂商号(MANUFACTURER ID);ID0-ID15”为FLASH芯片的ID;“dummy”指该处可为任意数据;“D0~D7”为FLASH内部存储矩阵的内容。

 

  • Read ID

通过指令表中的读ID指令“JEDEC ID”可以获取这两个编号,该指令编码为9F h”,其中“9F h”是指16进制数“9F” (相当于C语言中的0x9F)。紧跟指令编码的三个字节分别为FLASH芯片输出的“(M7-M0)”、“(ID15-ID8)”及“(ID7-ID0) 。通过read ID的指令,我们可以获取芯片的相关ID信息。

  • 擦除。也可以叫预编程吧,根据flash的特性,在进行写入之前,需要对目标区域进行擦除,将对应的位置置1。在所列举的芯片中,擦除分了三种类型(扇区擦除Sector_erase、块擦除Block_erase,以及整个芯片擦除Chip_erase)。他们之间的区别是,擦除区域的大小不一样。

 

扇区擦除指令将指定扇区(4K字节)内的所有存储器设置为全部擦除状态置1(FFh)。需要注意的是,在执行擦除之前, 必须执行一次写使能指令指令(状态寄存器位WEL必须等于1)。

 

    

   另外两种擦除方式,大同小异,就不展开说了,需要注意的是,Chip_erase在指令码后面不需要带目标擦除区域的起始地址。

 

  • 页编程。Page_program。

页编程,一次写入256个字节的数据到flash。需注意的是,在执行页编程之前,需执行一次写使能指令。

 

  • 写操作。

这里的写操作,主要是指设置单个寄存器的值,例如将flash设置成4字节地址模式,设置写使能等等。

 

 

 

设置状态寄存器的值,指令码后面,紧跟着对应的想要设置的寄存器值即可。

 

  • 读操作

这里读操作主要可以分两种,一种是读状态寄存器值,另外一种是读去flash存储的数据。

值得注意的是,读状态寄存器指令在任何时候都可以执行,即使在编程,擦除或写入时,也可以随时使用读状态寄存器指令。

       常规读数据:

读数据指令允许从存储器中顺序读取一个或多个数据字节。该通过将/ CS引脚驱动为低电平然后移位指令代码“03h”然后移位a来启动指令24位地址(A23-A0)进入DI引脚。 代码和地址位在上升沿锁存CLK引脚。 收到地址后,寻址存储单元的数据字节将被移出在CLK的下降沿处的DO引脚上,最高有效位(MSB)优先。地址会自动增加。也就是说在下发读指令的时候,只需要配置目标区域起始地址即可。然后数据会源源不断地被读出来,直到片选信号cs被拉高,读数结束

快速读:

快速读取指令与常规读取数据指令类似,只是它可以在FR最高频率下工作(参见交流电气特性),提高读取速度。其中dummy clock时间段里的数据可以忽略。(该指令目前我用的比较少,没实践过,就不展开了)

 

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,要将中文字库转换成二进制格式,可以使用一些工具,如FontCreator软件,将.ttf格式的字体文件转换成二进制文件。然后,将二进制文件读入程序中,通过SPI总线对W25Q64进行写入操作。以下是一份基本的实现代码,可供参考: ``` #include "stm32f10x.h" #include "spi.h" #include "font.h" // 中文字库的二进制文件 #define FLASH_CS_PIN GPIO_Pin_4 #define FLASH_CS_PORT GPIOA #define FLASH_CS_CLOCK RCC_APB2Periph_GPIOA void W25Q64_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; // 使能SPI时钟和GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE); // 配置SPI1_NSS引脚为推挽输出模式 GPIO_InitStructure.GPIO_Pin = FLASH_CS_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(FLASH_CS_PORT, &GPIO_InitStructure); // 配置SPI1_SCK引脚为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置SPI1_MISO引脚为输入模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置SPI1_MOSI引脚为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置SPI1为主机模式 SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStructure.SPI_CRCPolynomial = 7; SPI_Init(SPI1, &SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); } void W25Q64_Write(uint32_t addr, uint8_t *data, uint32_t len) { SPI_CS_LOW(); // 片选信号低电平有效 // 发送写使能命令 SPI_WriteByte(SPI1, 0x06); // 等待写使能命令完成 while(SPI_ReadByte(SPI1) & 0x80); // 发送写命令 SPI_WriteByte(SPI1, 0x02); // 发送地址 SPI_WriteByte(SPI1, (addr >> 16) & 0xff); SPI_WriteByte(SPI1, (addr >> 8) & 0xff); SPI_WriteByte(SPI1, addr & 0xff); // 发送数据 for(uint32_t i = 0; i < len; i++) { SPI_WriteByte(SPI1, data[i]); } SPI_CS_HIGH(); // 片选信号高电平无效 } int main(void) { W25Q64_Init(); // 初始化W25Q64 W25Q64_Write(0, (uint8_t *)font_bin, sizeof(font_bin)); // 将中文字库写入W25Q64 while(1) { } } ``` 需要注意的是,W25Q64的片选信号必须在操作写入操作过程中保持低电平有效,才能正常写入数据。SPI总线的操作方法可以参考固件库中的SPI驱动。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值