许多用户在使用 ESP32 做开发的时候,都会碰到如何选择外接 Flash 的难题,因为不同厂商的 Flash 存在很多差异,Flash 支持的工作模式也不同。
【如何给 ESP32 选择外接 Flash】
用户在选择 Flash 时,需要注意以下几个方面:
* 工作电压
* 容量
* 工作模式
* 工作环境温度
1. 工作电压
这是一个用户在选择 Flash 时很容易忽视但又非常重要的参数,下面列出几个常用的 ESP32 模组的 Flash 工作电压。
* ESP32-WROOM-32: 3.3V
* ESP32-WROOM-32D: 3.3V
* ESP32-WROVER: 1.8V
* ESP32-WROVER-B: 3.3V
2. 容量
ESP32 支持的 Flash 最大容量为 128 Mbit(16 Mbyte),推荐用户选择的容量范围为 16~128 Mbit。
3. 工作模式
Flash 常用的工作模式有 4 种:DOUT/DIO/QOUT/QIO
* DOUT:地址为 1 线模式输入,数据为 2 线模式输出
* DIO:地址为 2 线模式输入,数据为 2 线模式输出
* QOUT:地址为 1 线模式输入,数据为 4 线模式输出
* QIO:地址为 4 线模式输入,数据为 4 线模式输出
用户如果需要使用 QIO 模式,则需要在选择 Flash 时确认该 Flash 是否支持 QIO 模式。
4. 工作环境温度
不同用户开发的产品使用的环境温度也不同,可根据实际的工作环境进行选择。
【如何使能 QIO 模式】
有些用户选择了一颗支持 QIO 模式的 Flash,发现配置成 QIO 模式之后却跑不起来,或者 还是 DIO 模式在跑,这是由于没有成功使能 QIO 造成的。这时如果想要支持该 Flash 的 QIO 模式,则需要对 IDF 进行一点修改。具体步骤如下:
1. 首先配置成 DIO 模式运行,确保代码和硬件没有问题。
> 注意:有时 Flash 的 SD2/SD3(WP/HOLD) 脚虚焊或者短路也会造成 QIO 跑不起来,可以检查一下
2. 查看 IDF 中 `flash_qio_mode.c` 中的代码,通常在修改时只要在 `chip_data` 这个数组中添加一项参数配置,这个数组如下:
Code: Select all
const static qio_info_t chip_data[] = {
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
{ "MXIC", 0xC2, 0x2000, 0xFF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 },
{ "ISSI", 0x9D, 0x4000, 0xCF00, read_status_8b_rdsr, write_status_8b_wrsr, 6 }, /* IDs 0x40xx, 0x70xx */
{ "WinBond", 0xEF, 0x4000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 },
{ "GD", 0xC8, 0x6000, 0xFF00, read_status_16b_rdsr_rdsr2, write_status_16b_wrsr, 9 },
{ "XM25QU64A", 0x20, 0x3817, 0xFFFF, read_status_8b_xmc25qu64a, write_status_8b_xmc25qu64a, 6 },
/* Final entry is default entry, if no other IDs have matched.
This approach works for chips including:
GigaDevice (mfg ID 0xC8, flash IDs including 4016),
FM25Q32 (QOUT mode only, mfg ID 0xA1, flash IDs including 4016)
*/
{ NULL, 0xFF, 0xFFFF, 0xFFFF, read_status_8b_rdsr2, write_status_8b_wrsr2, 1 },
};
Manufacturer:这个用于标识 Flash 的制造厂商,也可以设置成型号,便于区分即可
mfg_id: 制造商 ID
flash_id: Flash ID,通常用制造商 ID + Flash ID 作为一款 Flash 的唯一标识。
id_mask: 由于一个厂商生产的多款 Flash 使能 QIO 的方式是一样的(比如某一个系列的产品只是容量不同,其他都是一致的),这时就可以通过 id_mask 来限定只要 ID 的部分 bit 相同即可使用相同的使能方式,通常 Flash ID 的后面 1 个字节用来区分容量,所以可以将 id_mask 设置为 0xFF00,即表示前 1 个字节相同即可。如果指定某一个特定型号的 Flash,则需要填写完整的 flash_id,并将 id_mask 设置为 0xFFFF。
Read Status:读状态寄存器回调函数,在选择回调函数时需要了解使用的 Flash 的寄存器读取方式,包括读取的命令和一次读取几个字节
Write Status:写状态寄存器回调函数,同样需要了解使用的 Flash 的寄存器写入方式,包括写入的命令和一次写入几个字节
QIE Bit:QIO 使能比特,QIO 使能即把状态寄存器中的 QE 比特写成 1。
3. 上面描述的这些参数在 Flash 的 datasheet 中都可以找到,下面就以 GD25LQ32C 为例子来讲解如果查找到对应的参数并选择相应的读写寄存器回调函数。
(1)找到 Flash ID,如下图所示: 我们使用 9FH 读取 Flash ID,所以从 9FH 一栏即可找到对应的 mfg_id 和 flash_id。GD25LQ32C 的 mfg_id 为 0xC8,flash_id 为 0x6016
(2)找到 QE bit 以及 QE bit 所在的寄存器,如下图所示: 可以看到 GD25LQ32C 的状态寄存器有 2 个字节,QE bit 为第 9 bit。
(3)根据状态寄存器的读写方式选择回调函数。从 Flash 操作的命令列表找到寄存器相关操作的部分,如下图所示: 可以看到 GD25LQ32C 的状态寄存器的需要使用 2 个命令来读取,每个命令读取 1 个字节,所以给该 Flash 选择的读取状态寄存器的回调函数为 `read_status_16b_rdsr_rdsr2`。写状态寄存器时使用 1 个命令写入 2 个字节的方式,所以给该 Flash 选择的写入状态寄存器的回调函数为 `write_status_16b_wrsr`
4. 添加 QIO 使能配置完成之后重新编译,下载,运行;能够正常运行且看到如下打印即说明 QIO 已使能成功。
Code: Select all
I (9) boot: ESP-IDF v3.2-dev-1287-g1c530c3-dirty 2nd stage bootloader
I (9) boot: compile time 17:01:34
I (10) boot: Enabling RNG early entropy source...
I (25) qio_mode: Enabling QIO for flash chip GD
I (31) boot: SPI Speed : 80MHz
I (35) boot: SPI Mode : QIO
I (39) boot: SPI Flash Size : 4MB
I (43) boot: Partition Table:
I (47) boot: ## Label Usage Type ST Offset Length
I (54) boot: 0 nvs WiFi data 01 02 00009000 00006000
I (62) boot: 1 phy_init RF data 01 01 0000f000 00001000
I (69) boot: 2 factory factory app 00 00 00010000 00100000
I (77) boot: End of partition table
I (81) esp_image: segment 0: paddr=0x00010020 vaddr=0x3f400020 size=0x08ffc ( 36860) map
I (99) esp_image: segment 1: paddr=0x00019024 vaddr=0x3ff80000 size=0x00000 ( 0) load
I (100) esp_image: segment 2: paddr=0x0001902c vaddr=0x3ff80000 size=0x00000 ( 0) load
I (107) esp_image: segment 3: paddr=0x00019034 vaddr=0x3ffb0000 size=0x0211c ( 8476) load
I (119) esp_image: segment 4: paddr=0x0001b158 vaddr=0x3ffb211c size=0x00000 ( 0) load
I (125) esp_image: segment 5: paddr=0x0001b160 vaddr=0x40080000 size=0x00400 ( 1024) load
I (135) esp_image: segment 6: paddr=0x0001b568 vaddr=0x40080400 size=0x04aa8 ( 19112) load
I (149) esp_image: segment 7: paddr=0x00020018 vaddr=0x400d0018 size=0x15efc ( 89852) map
I (176) esp_image: segment 8: paddr=0x00035f1c vaddr=0x40084ea8 size=0x04b44 ( 19268) load
I (182) esp_image: segment 9: paddr=0x0003aa68 vaddr=0x400c0000 size=0x00000 ( 0) load
I (183) esp_image: segment 10: paddr=0x0003aa70 vaddr=0x50000000 size=0x00000 ( 0) load
I (191) esp_image: segment 11: paddr=0x0003aa78 vaddr=0x50000000 size=0x00000 ( 0) load
I (206) boot: Loaded app from partition at offset 0x10000
I (207) boot: Disabling RNG early entropy source...
I (212) cpu_start: Pro cpu up.
I (216) cpu_start: Starting app cpu, entry point is 0x40080ecc
I (0) cpu_start: App cpu up.