forked from espressif/esp-idf
Merge branch 'docs/update_cn_spi_flash_override_driver' into 'master'
docs: Update the CN translation for spi_flash_override_driver.rst Closes DOC-9720 See merge request espressif/esp-idf!35631
This commit is contained in:
@@ -5,16 +5,16 @@ Overriding Default Chip Drivers
|
||||
|
||||
.. warning::
|
||||
|
||||
Customizing SPI Flash Chip Drivers is considered an "expert" feature. Users should only do so at their own risk. (See the notes below)
|
||||
Customizing SPI Flash Chip Drivers is considered an "expert" feature. The user should only do so at their own risk (see the notes below).
|
||||
|
||||
During the SPI Flash driver's initialization (i.e., :cpp:func:`esp_flash_init`), there is a chip detection step during which the driver iterates through a Default Chip Driver List and determine which chip driver can properly support the currently connected flash chip. The Default Chip Drivers are provided by the ESP-IDF, thus are updated in together with each ESP-IDF version. However ESP-IDF also allows users to customize their own chip drivers.
|
||||
During the SPI Flash driver's initialization (i.e., :cpp:func:`esp_flash_init`), there is a chip detection step during which the driver iterates through a Default Chip Driver List and determine which chip driver can properly support the currently connected flash chip. The Default Chip Drivers are provided by the ESP-IDF, thus are updated in together with each ESP-IDF version. However ESP-IDF also allows to customize their own chip drivers.
|
||||
|
||||
Users should note the following when customizing chip drivers:
|
||||
Note the following points when customizing chip drivers:
|
||||
|
||||
1. You may need to rely on some non-public ESP-IDF functions, which have slight possibility to change between ESP-IDF versions. On the one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also be breaking changes (i.e., breaks your code).
|
||||
2. Some ESP-IDF bug fixes to other chip drivers are not automatically applied to your own custom chip drivers.
|
||||
3. If the protection of flash is not handled properly, there may be some random reliability issues.
|
||||
4. If you update to a newer ESP-IDF version that has support for more chips, you will have to manually add those new chip drivers into your custom chip driver list. Otherwise the driver will only search for the drivers in custom list you provided.
|
||||
1. You may need to rely on some non-public ESP-IDF functions, which have slight possibility to change between ESP-IDF versions. On the one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also be breaking changes (i.e., breaks your code).
|
||||
2. Some ESP-IDF bug fixes to other chip drivers are not automatically applied to your own custom chip drivers.
|
||||
3. If the protection of flash is not handled properly, there may be some random reliability issues.
|
||||
4. If you update to a newer ESP-IDF version that has support for more chips, you will have to manually add those new chip drivers into your custom chip driver list. Otherwise the driver will only search for the drivers in custom list you provided.
|
||||
|
||||
|
||||
Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver List
|
||||
@@ -23,226 +23,226 @@ Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver
|
||||
Bootloader Flash Driver
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
To implement bootloader driver in order that the ESP chip can boot successfully, please read this part carefully. Wrong code will make ESP chip fail to boot or introduce random issues. And issues in bootloader is not fixable via OTA. Please fully comprehend below parts before making any changes.
|
||||
To implement the bootloader driver so that the ESP chip can boot successfully, please read this part carefully. Wrong code will make ESP chip fail to boot or introduce random issues. Issues in the bootloader cannot be fixed via OTA. Please fully comprehend below parts before making any changes.
|
||||
|
||||
Currently there are two parts to override in bootloader: `bootloader_flash_unlock` and `bootloader_flash_qe_support_list` (and its size). See below sections.
|
||||
Currently, there are two parts to override in bootloader: ``bootloader_flash_unlock`` and ``bootloader_flash_qe_support_list`` (and its size). See sections below.
|
||||
|
||||
Ensure that all the commands should be sent out and recognized successfully. Otherwise if the flash receives an unknown command, it may ignore silently and continue working while the protection is not re-enabled. This might lead to mis-writing/erasing, mis-locked issues.
|
||||
Ensure that all commands are sent and recognized successfully. If the flash receives an unknown command, it may silently ignore it and continue operating without re-enabling protection. This could lead to mis-writing, erasing, or locking issues.
|
||||
|
||||
All bootloader flash driver functions reside in a component for the bootloader, which should be placed under `bootloader_components`. For example, :example:`storage/custom_flash_driver/bootloader_components/`. Only the components under `bootloader_components` will be added into the component list of bootloader automatically.
|
||||
All bootloader flash driver functions reside in a component for the bootloader, which should be placed under ``bootloader_components``. For example, :example:`storage/custom_flash_driver/bootloader_components/`. Only the components under ``bootloader_components`` will be added into the component list of bootloader automatically.
|
||||
|
||||
There is slight difference in the `CMakeLists.txt` of the component. The customized flash functions take effect by overriding existing weak ones. In order to link them into the bootloader, linker argument `-u` should be used against the overriding functions in the `CMakeLists.txt`. See :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/CMakeLists.txt`.
|
||||
There is a slight difference in the ``CMakeLists.txt`` of the component. The customized flash functions take effect by overriding existing weak ones. In order to link them into the bootloader, use the linker argument `-u` against the overriding functions in the ``CMakeLists.txt``. See :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/CMakeLists.txt`.
|
||||
|
||||
Bootloader flash unlock
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
`bootloader_flash_unlock` function is used to unlock flash write protection, which means once the flash is locked by accident, IDF is able to unlock it. By default, the unlock function will clear all bits in Status Register and Configuration Register except QE bit.
|
||||
``bootloader_flash_unlock`` function is used to unlock flash write protection, which means once the flash is locked by accident, IDF is able to unlock it. By default, the unlock function will clear all bits in status registers and configuration registers except QE bit.
|
||||
|
||||
Please don't touch QE bits, otherwise, your chip will not be able to run under QUAD modes.
|
||||
Please don not modify the QE bit; otherwise, your chip will not be able to run under QUAD modes.
|
||||
|
||||
Please check the default case below. If your flash have different behavior from that, some modifications are required.
|
||||
|
||||
You can start from copying `bootloader_flash_unlock_default` from `components/bootloader_support/bootloader_flash/src/bootloader_flash.c` into a file in your customized component folder before modifying it. The function should be renamed to `bootloader_flash_unlock` to override the IDF bootloader function. For example, :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
You can start from copying ``bootloader_flash_unlock_default`` from ``components/bootloader_support/bootloader_flash/src/bootloader_flash.c`` into a file in your customized component folder before modifying it. The function should be renamed to ``bootloader_flash_unlock`` to override the IDF bootloader function. For example, :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
|
||||
After that, your implementation of `bootloader_flash_unlock` will be linked instead of IDF default one, which has been declared as a weak symbol. So when IDF 2nd bootloader boots, your implementation of `bootloader_flash_unlock` will be executed. If you want to make sure your function is truly linked, you can enable `CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG` and you will see a ``Using overridden bootloader_flash_unlock`` log.
|
||||
After that, your implementation of ``bootloader_flash_unlock`` will be linked instead of IDF default one, which has been declared as a weak symbol. So when IDF second stage bootloader boots, your implementation of ``bootloader_flash_unlock`` will be executed. If you want to make sure your function is truly linked, you can enable ``CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG`` and you will see a ``Using overridden bootloader_flash_unlock`` log.
|
||||
|
||||
In the default function, we have provided 3 common behaviors, please check first:
|
||||
The default function includes three common behaviors, please check first:
|
||||
|
||||
- case 1 (**Default**): Please check whether QE bit in your flash chip is placed at bit 1 in status register-2 written by the second byte after command 01H (WRSR). If so, this matches the default behavior and you don't need to anything else.
|
||||
- case 2: Please check whether QE bit in your flash chip is placed at bit 1 in status register-2 to be written by command 31H. (And your flash doesn't support case 1, using 01H+2bytes to write it). If so , you need to add your chip ID in function :cpp:func:`is_qe_bit_in_bit1_at_reg2`, for example:
|
||||
- Case 1 (**Default**): Check if the QE bit in your flash chip is located at bit 1 in status register-2, written by the second byte after command 01H (WRSR). If so, this matches the default behavior, and no further action is needed.
|
||||
- Case 2: Check if the QE bit in your flash chip is located at bit 1 in status register-2, written by command 31H (and your flash does not support Case 1, which uses 01H + 2 bytes to write it). If this is the case, add your chip ID to the function :cpp:func:`is_qe_bit_in_bit1_at_reg2`. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit1_at_reg2(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/****GD series***/
|
||||
case 0xC84016:
|
||||
case 0xC84017:
|
||||
case 0xC84018:
|
||||
break;
|
||||
/**** your flash series ****/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
IRAM_ATTR bool is_qe_bit_in_bit1_at_reg2(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/****GD series***/
|
||||
case 0xC84016:
|
||||
case 0xC84017:
|
||||
case 0xC84018:
|
||||
break;
|
||||
/**** your flash series ****/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- case 3: Please check whether QE bit in your flash chip is placed at bit 6 in status register-1 to be written by command 01H. If so, you need to add your chip ID in function :cpp:func:`is_qe_bit_in_bit6_at_reg1`, for example:
|
||||
- Case 3: Check whether the QE bit in your flash chip is located at bit 6 in status register-1, which can be written by command 01H. If so, you need to add your chip ID in function :cpp:func:`is_qe_bit_in_bit6_at_reg1`. For example:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit6_at_reg1(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/***ISSI series***/
|
||||
case 0x9D4016:
|
||||
case 0x9D4017:
|
||||
break;
|
||||
IRAM_ATTR bool is_qe_bit_in_bit6_at_reg1(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/***ISSI series***/
|
||||
case 0x9D4016:
|
||||
case 0x9D4017:
|
||||
break;
|
||||
|
||||
/***MXIC series***/
|
||||
case 0xC22016:
|
||||
case 0xC22017:
|
||||
break;
|
||||
/***MXIC series***/
|
||||
case 0xC22016:
|
||||
case 0xC22017:
|
||||
break;
|
||||
|
||||
/****your clash series***/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
/****your flash series***/
|
||||
case /*your flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- case 4: If 3 cases mentioned above cannot cover your usage. Please add another `if` block and corresponding behavior function in function `bootloader_flash_unlock`. The determination function in the `if` block is suggested to be named after `is_qe_bit_in_bit_x_at_reg_x_` (x stands for behavior). Refer to example :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
- Case 4: If the three cases mentioned above cannot cover your usage, please add another `if` block and the corresponding behavior function in function ``bootloader_flash_unlock``. The determination function in the `if` block is suggested to be named after ``is_qe_bit_in_bit_x_at_reg_x_`` (x stands for behavior). Refer to example :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`.
|
||||
|
||||
Bootloader flash quad mode support
|
||||
Bootloader Flash Quad Mode Support
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Pointer `bootloader_flash_qe_support_list` is used for iteration in bootloader for selecting the correct behavior to enable flash chip work under QUAD mode. In order to make flash works under QUAD mode, enable QE bit in flash status register is necessary. If you want to use your flash chip under QUAD mode, please read this part and make implementation for yourself.
|
||||
Pointer ``bootloader_flash_qe_support_list`` is used for iteration in bootloader for selecting the correct behavior to enable flash chip work under QUAD mode. To operate the flash in QUAD mode, enabling the QE bit in the flash status register is necessary. If you want to use your flash chip under QUAD mode, please read this part and implement the necessary changes.
|
||||
|
||||
* case 1: If QE bit is placed at bit1 in status register-2 to be written by command 31H, nothing needs to be done because this is the default behavior.
|
||||
* case 2: If QE bit on your chip is placed at different places, or need to use different command. Please add your own support.
|
||||
* Case 1: If QE bit is placed at bit 1 in status register-2 to be written by command 31H, nothing needs to be done because this is the default behavior.
|
||||
* Case 2: If QE bit on your chip is placed at different places, or need to use different command. Please add your own support.
|
||||
|
||||
To add your own support, you can start from copying the `bootloader_flash_qe_support_list_user` function from `flash_qio_mode.c <https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/bootloader_flash/src/flash_qio_mode.c>`_ , into your file, renaming to `bootloader_flash_qe_support_list`. Please also define a corresponding `bootloader_flash_qe_list_count`.
|
||||
To add your own support, you can start from copying the ``bootloader_flash_qe_support_list_user`` function from `flash_qio_mode.c <https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/bootloader_flash/src/flash_qio_mode.c>`_ into your file, renaming to ``bootloader_flash_qe_support_list``. Please also define a corresponding ``bootloader_flash_qe_list_count``.
|
||||
|
||||
Add your flash into it, with the chip's name, ID, and the functions to write registers etc. into `bootloader_flash_qio_support_list`. You can also reuse the existing functions like `bootloader_read_status_8b_rdsr`.
|
||||
Add the details of your flash chip, including the chip's name, ID, and the functions to write registers, into ``bootloader_flash_qio_support_list``. You can also reuse the existing functions like ``bootloader_read_status_8b_rdsr``.
|
||||
|
||||
If functions there cannot fully cover your usage, you can define your own function with `bootloader_execute_flash_command`, like `bootloader_read_status_otp_mode_8b`, and `bootloader_write_status_otp_mode_8b`. For example, `bootloader_flash_custom.c <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/bootloader_flash_driver/bootloader_flash_custom.c>`_ .
|
||||
If the existing functions do not fully meet your needs, you can define your own functions using ``bootloader_execute_flash_command``, such as ``bootloader_read_status_otp_mode_8b`` and ``bootloader_write_status_otp_mode_8b``. For example, see `bootloader_flash_custom.c <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/bootloader_flash_driver/bootloader_flash_custom.c>`_.
|
||||
|
||||
Put everything together:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR bootloader_qio_info_t bootloader_flash_qe_support_list_user[] = {
|
||||
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
|
||||
{ "MXIC", 0xC2, 0x2000, 0xFF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "ISSI", 0x9D, 0x4000, 0xCF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "WinBond", 0xEF, 0x4000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "GD", 0xC8, 0x4000, 0xFFFF, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "XM25QU64A", 0x20, 0x3817, 0xFFFF, bootloader_read_status_8b_xmc25qu64a, bootloader_write_status_8b_xmc25qu64a, 6 },
|
||||
{ "TH", 0xCD, 0x6000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "EON", 0x1C, 0x7000, 0xFF00, bootloader_read_status_otp_mode_8b, bootloader_write_status_otp_mode_8b, 6 },
|
||||
const DRAM_ATTR bootloader_qio_info_t bootloader_flash_qe_support_list_user[] = {
|
||||
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
|
||||
{ "MXIC", 0xC2, 0x2000, 0xFF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "ISSI", 0x9D, 0x4000, 0xCF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "WinBond", 0xEF, 0x4000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "GD", 0xC8, 0x4000, 0xFFFF, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "XM25QU64A", 0x20, 0x3817, 0xFFFF, bootloader_read_status_8b_xmc25qu64a, bootloader_write_status_8b_xmc25qu64a, 6 },
|
||||
{ "TH", 0xCD, 0x6000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "EON", 0x1C, 0x7000, 0xFF00, bootloader_read_status_otp_mode_8b, bootloader_write_status_otp_mode_8b, 6 },
|
||||
|
||||
/* Final entry is default entry, if no other IDs have matched.
|
||||
/* 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)
|
||||
BY25Q32 (mfg ID 0x68, flash IDs including 4016)
|
||||
*/
|
||||
{ NULL, 0xFF, 0xFFFF, 0xFFFF, bootloader_read_status_8b_rdsr2, bootloader_write_status_8b_wrsr2, 1 },
|
||||
};
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list = bootloader_flash_qe_support_list_user;
|
||||
uint8_t DRAM_ATTR bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_user) / sizeof(bootloader_qio_info_t));
|
||||
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)
|
||||
BY25Q32 (mfg ID 0x68, flash IDs including 4016)
|
||||
*/
|
||||
{ NULL, 0xFF, 0xFFFF, 0xFFFF, bootloader_read_status_8b_rdsr2, bootloader_write_status_8b_wrsr2, 1 },
|
||||
};
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list = bootloader_flash_qe_support_list_user;
|
||||
uint8_t DRAM_ATTR bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_user) / sizeof(bootloader_qio_info_t));
|
||||
|
||||
App flash driver
|
||||
App Flash Driver
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Generic Flash driver
|
||||
Generic Flash Driver
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The flash driver in the application is used to read, write, erase, etc. to save data. And some advanced features like the OTA. Below is the guide on how to customize the driver for your own flash model.
|
||||
The flash driver in the application is used to read, write, erase, and save data. It also supports some advanced features like OTA. Below is a guide on how to customize the driver for your specific flash model.
|
||||
|
||||
- STEP 1: The last item of `default_registered_chips` should be the `generic chip driver <https://github.com/espressif/esp-idf/blob/master/components/spi_flash/spi_flash_chip_generic.c>`_ . When your flash chip cannot match any one chip driver above, it will use the generic driver. Please check what behavior of your flash is different from the generic driver, including but not limited to different commands, different dummys, different data bytes, different status registers.
|
||||
- STEP 2: If you have found something different from the generic driver, you need to implement your own chip driver. Create a new file naming `spi_flash_chip_<vendor>.c` implementing specific behavior there, and copy the `esp_flash_chip_generic` structure into it as a start. Don't forget to include "spi_flash_chip_generic.h". Here is an example `esp_flash_nor <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/>`_ .
|
||||
- STEP 3: Implement the functions with difference and point to them from the `spi_flash_chip_t`. Note: if some behavior of your flash is same as the generic one, just keep the function of generic driver and no need to customize them. Only the different part should be implemented. Here is an example:
|
||||
- Step 1: The last item of `default_registered_chips` should be the `generic chip driver <https://github.com/espressif/esp-idf/blob/master/components/spi_flash/spi_flash_chip_generic.c>`_. If your flash chip does not match any of the chip drivers listed above, it will use the generic driver. Check for any differences in behavior between your flash and the generic driver, including but not limited to different commands, dummy cycles, data bytes, and status registers.
|
||||
- Step 2: If you have found something different from the generic driver, you need to implement your own chip driver. Create a new file named ``spi_flash_chip_<vendor>.c`` to implement the specific behavior, and copy the ``esp_flash_chip_generic`` structure into it as a starting point. Remember to include ``spi_flash_chip_generic.h``. Here is an example `esp_flash_nor <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/>`_.
|
||||
- Step 3: Implement the functions with difference and point to them from the ``spi_flash_chip_t``. Note: if some behavior of your flash is the same as the generic one, retain the generic driver functions without customization. Only implement the parts that differ. Here is an example:
|
||||
|
||||
.. important::
|
||||
|
||||
Flash work for suspend (means you want to enable :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`) should be tested carefully and systematically due to different flash hardware design. If you want to use suspend feature for massive production, please contact espressif business support team.
|
||||
Flash work for suspend (i.e., enabling :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`) should be tested carefully and systematically due to different flash hardware design. If you want to use the suspend feature for mass production, please contact `Espressif's business team <https://www.espressif.com/en/contact-us/sales-questions>`_.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR spi_flash_chip_t esp_flash_chip_eon = {
|
||||
.name = chip_name,
|
||||
.timeout = &spi_flash_chip_generic_timeout, /*<! default behavior*/
|
||||
.probe = spi_flash_chip_eon_probe, /*<! EON specific */
|
||||
.reset = spi_flash_chip_generic_reset,
|
||||
.detect_size = spi_flash_chip_generic_detect_size,
|
||||
.erase_chip = spi_flash_chip_generic_erase_chip,
|
||||
.erase_sector = spi_flash_chip_generic_erase_sector,
|
||||
.erase_block = spi_flash_chip_generic_erase_block,
|
||||
.sector_size = 4 * 1024,
|
||||
.block_erase_size = 64 * 1024,
|
||||
const DRAM_ATTR spi_flash_chip_t esp_flash_chip_eon = {
|
||||
.name = chip_name,
|
||||
.timeout = &spi_flash_chip_generic_timeout, /*<! default behavior*/
|
||||
.probe = spi_flash_chip_eon_probe, /*<! EON specific */
|
||||
.reset = spi_flash_chip_generic_reset,
|
||||
.detect_size = spi_flash_chip_generic_detect_size,
|
||||
.erase_chip = spi_flash_chip_generic_erase_chip,
|
||||
.erase_sector = spi_flash_chip_generic_erase_sector,
|
||||
.erase_block = spi_flash_chip_generic_erase_block,
|
||||
.sector_size = 4 * 1024,
|
||||
.block_erase_size = 64 * 1024,
|
||||
|
||||
.get_chip_write_protect = spi_flash_chip_generic_get_write_protect,
|
||||
.set_chip_write_protect = spi_flash_chip_generic_set_write_protect,
|
||||
.get_chip_write_protect = spi_flash_chip_generic_get_write_protect,
|
||||
.set_chip_write_protect = spi_flash_chip_generic_set_write_protect,
|
||||
|
||||
.num_protectable_regions = 0,
|
||||
.protectable_regions = NULL,
|
||||
.get_protected_regions = NULL,
|
||||
.set_protected_regions = NULL,
|
||||
.num_protectable_regions = 0,
|
||||
.protectable_regions = NULL,
|
||||
.get_protected_regions = NULL,
|
||||
.set_protected_regions = NULL,
|
||||
|
||||
.read = spi_flash_chip_generic_read,
|
||||
.write = spi_flash_chip_generic_write,
|
||||
.program_page = spi_flash_chip_generic_page_program,
|
||||
.page_size = 256,
|
||||
.write_encrypted = spi_flash_chip_generic_write_encrypted,
|
||||
.read = spi_flash_chip_generic_read,
|
||||
.write = spi_flash_chip_generic_write,
|
||||
.program_page = spi_flash_chip_generic_page_program,
|
||||
.page_size = 256,
|
||||
.write_encrypted = spi_flash_chip_generic_write_encrypted,
|
||||
|
||||
.wait_idle = spi_flash_chip_generic_wait_idle,
|
||||
.set_io_mode = spi_flash_chip_eon_set_io_mode,
|
||||
.get_io_mode = spi_flash_chip_eon_get_io_mode,
|
||||
.wait_idle = spi_flash_chip_generic_wait_idle,
|
||||
.set_io_mode = spi_flash_chip_eon_set_io_mode,
|
||||
.get_io_mode = spi_flash_chip_eon_get_io_mode,
|
||||
|
||||
.read_reg = spi_flash_chip_generic_read_reg,
|
||||
.yield = spi_flash_chip_generic_yield,
|
||||
.sus_setup = spi_flash_chip_eon_suspend_cmd_conf,
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
.read_reg = spi_flash_chip_generic_read_reg,
|
||||
.yield = spi_flash_chip_generic_yield,
|
||||
.sus_setup = spi_flash_chip_eon_suspend_cmd_conf,
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
|
||||
.. note::
|
||||
|
||||
- When writing your own flash chip driver, you can set your flash chip capabilities through ``spi_flash_chip_***(vendor)_get_caps`` and points the function pointer ``get_chip_caps`` for protection to the ``spi_flash_chip_***_get_caps`` function. The steps are as follows.
|
||||
- When writing your own flash chip driver, you can set your flash chip capabilities through ``spi_flash_chip_***(vendor)_get_caps`` and point the function pointer ``get_chip_caps`` to the ``spi_flash_chip_***_get_caps`` function for protection. The steps are as follows.
|
||||
|
||||
1. Please check whether your flash chip have the capabilities listed in ``spi_flash_caps_t`` by checking the flash datasheet.
|
||||
2. Write a function named ``spi_flash_chip_***(vendor)_get_caps``. Take the example below as a reference (if the flash support ``suspend`` and ``read unique id``).
|
||||
3. Points the pointer ``get_chip_caps`` (in ``spi_flash_chip_t``) to the function mentioned above.
|
||||
1. Please check whether your flash chip has the capabilities listed in ``spi_flash_caps_t`` by checking the flash datasheet.
|
||||
2. Write a function named ``spi_flash_chip_***(vendor)_get_caps``. Take the example below as a reference (if the flash supports ``suspend`` and ``read unique id``).
|
||||
3. Point the pointer ``get_chip_caps`` (in ``spi_flash_chip_t``) to ``spi_flash_chip_***_get_caps``.
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
spi_flash_caps_t spi_flash_chip_***(vendor)_get_caps(esp_flash_t *chip)
|
||||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 32-bit-address flash is not supported
|
||||
caps_flags |= SPI_FLAHS_CHIP_CAP_SUSPEND;
|
||||
// flash read unique id.
|
||||
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
|
||||
return caps_flags;
|
||||
}
|
||||
spi_flash_caps_t spi_flash_chip_***(vendor)_get_caps(esp_flash_t *chip)
|
||||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 32-bit address flash is not supported
|
||||
caps_flags |= SPI_FLAHS_CHIP_CAP_SUSPEND;
|
||||
// Read flash unique id
|
||||
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
|
||||
return caps_flags;
|
||||
}
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
const spi_flash_chip_t esp_flash_chip_eon = {
|
||||
// Other function pointers
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
const spi_flash_chip_t esp_flash_chip_eon = {
|
||||
// Other function pointers
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
|
||||
- You also can see how to implement this in the example :example:`storage/custom_flash_driver`. This example demonstrates how to override the default chip driver list.
|
||||
- You can also see how to implement this in the example :example:`storage/custom_flash_driver`. This example demonstrates how to override the default chip driver list.
|
||||
|
||||
- STEP 4: Add a header (e.g. `spi_flash_chip_<vendor>.h`) and `extern` declare this structure, so that other component or source code can reuse this structure. Wrap all your chip drivers (source files as well as their headers) with a chip-driver component. Add the include path and the source file to component CMakeLists.txt.
|
||||
- Step 4: Create a header file (e.g., ``spi_flash_chip_<vendor>.h``) and declare the structure using `extern`, so that other components or source code can reuse this structure. Wrap all your chip drivers (including source files and their headers) into a chip-driver component. Add the include path and the source file to the component ``CMakeLists.txt``.
|
||||
|
||||
- STEP 5: The ``linker.lf`` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
|
||||
- Step 5: The ``linker.lf`` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
|
||||
|
||||
- STEP 6: Add a new component in your project, e.g., ``custom_chip_driver``. List your chip object under `default_registered_chips` in `custom_chip_driver/chip_drivers.c`. Then enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This prevents compilation and linking of the Default Chip Driver List (``default_registered_chips``) provided by ESP-IDF. Instead, the linker searches for the structure of the same name (``default_registered_chips``) that must be provided by the user. You can refer to :example_file:`storage/custom_flash_driver/components/custom_chip_driver/chip_drivers.c`.
|
||||
- Step 6: Add a new component in your project, e.g., ``custom_chip_driver``. List your chip object under ``default_registered_chips`` in ``custom_chip_driver/chip_drivers.c``. Then enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` configuration option. This prevents compilation and linking of the default chip driver list (``default_registered_chips``) provided by ESP-IDF. Instead, the linker searches for the structure of the same name (``default_registered_chips``) that must be provided by you. You can refer to :example_file:`storage/custom_flash_driver/components/custom_chip_driver/chip_drivers.c`.
|
||||
|
||||
- STEP 7: Build your project, and you will see the new flash driver is used.
|
||||
- Step 7: Build your project, and you will see the new flash driver in use.
|
||||
|
||||
.. only:: SOC_MEMSPI_SRC_FREQ_120M
|
||||
|
||||
High Performance Flash implementation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
High Performance Flash Implementation
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The high performance mode means frequency at frequencies higher than 80 MHz. Please check the datasheet according to your flash and find which approach can reach to 120MHz, which would be listed in DC Characteristics section. Now, we already have some behavior in `high performance file <https://github.com/espressif/components/spi_flash/spi_flash_hpm_enable.c>`_ . If your flash meets the behavior there, just extend the list as what has been introduced in `bootloader_flash_unlock` sector. If your flash has different behavior, please add the new behavior and override the behavior table `spi_flash_hpm_enable_list`.
|
||||
The high performance mode operates at frequencies higher than 80 MHz. Please check the datasheet for your flash and to determine which approach can reach to frequencies higher than 80 MHz, as listed in *DC Characteristics* section. Some behavior is already defined in the `high performance file <https://github.com/espressif/components/spi_flash/spi_flash_hpm_enable.c>`_ . If your flash meets the specified behavior, extend the list as introduced in the ``bootloader_flash_unlock`` section. If your flash has different behavior, please add the new behavior and override the behavior table ``spi_flash_hpm_enable_list``.
|
||||
|
||||
.. important::
|
||||
.. important::
|
||||
|
||||
Flash with frequencies set to 120 MHz or higher should be tested carefully due to it's strict timing. If you want to use high performance mode feature for massive production, please contact espressif business support team.
|
||||
Flash with a frequency set above 80 MHz should be tested carefully due to its strict timing requirements. If you want to use the high performance mode feature for mass production, please contact `Espressif's business team <https://www.espressif.com/en/contact-us/sales-questions>`_.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
See :example:`storage/custom_flash_driver`, which make use of an `external component <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor>`_ to add support for some customized flash. This example mainly shows how to override IDF driver, and the external component shows how to implement customized drivers.
|
||||
See :example:`storage/custom_flash_driver`, which uses an `external component <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor>`_ to add support for custom flash and implement customized drivers, while this document mainly focuses on illustrating how to override the IDF driver.
|
||||
|
@@ -5,78 +5,244 @@
|
||||
|
||||
.. warning::
|
||||
|
||||
自定义 SPI Flash 芯片驱动程序是一项高级功能,进行此操作时应自担风险。(请参阅下面的注意事项)
|
||||
自定义 SPI flash 芯片驱动程序是一项高级功能,进行此操作时应自担风险(请参阅下面的注意事项)。
|
||||
|
||||
在初始化 SPI Flash 驱动程序时(即,:cpp:func:`esp_flash_init`),驱动程序会在芯片检测步骤中遍历默认芯片驱动程序列表,并找到可以支持当前连接的 Flash 芯片的驱动程序。默认芯片驱动程序由 ESP-IDF 提供,因此会随着 ESP-IDF 的版本一起更新。当然,ESP-IDF 也允许自定义芯片驱动程序。
|
||||
在初始化 SPI flash 驱动程序时(即,:cpp:func:`esp_flash_init`),驱动程序会在芯片检测步骤中遍历默认芯片驱动程序列表,并找到可以支持当前连接的 flash 芯片的驱动程序。默认芯片驱动程序由 ESP-IDF 提供,因此会随着 ESP-IDF 的版本一起更新。当然,ESP-IDF 也允许自定义芯片驱动程序。
|
||||
|
||||
在自定义芯片驱动程序时,应注意以下事项:
|
||||
|
||||
1. 自定义芯片驱动可能会用到非公开的 ESP-IDF 函数。ESP-IDF 版本不同,这些函数也有极小的可能会随之变化,有的会修复驱动程序中的错误,也有可能会破坏代码。
|
||||
2. 针对某些芯片驱动程序的 ESP-IDF 错误修复并不会自动应用到自定义的芯片驱动程序中。
|
||||
3. 如果未能正确处理针对 flash 的保护,可能会出现一些随机的可靠性问题。
|
||||
4. 如果升级到支持更多芯片的新 ESP-IDF 版本,则必须手动将这些新的芯片驱动程序添加到自定义芯片驱动程序列表中。否则,驱动程序将仅搜索自定义列表中已有的驱动程序。
|
||||
1. 自定义芯片驱动可能会用到非公开的 ESP-IDF 函数。ESP-IDF 版本不同,这些函数也有极小的可能会随之变化,有的会修复驱动程序中的错误,也有可能会破坏代码。
|
||||
2. 针对某些芯片驱动程序的 ESP-IDF 错误修复并不会自动应用到自定义的芯片驱动程序中。
|
||||
3. 如果未能正确处理针对 flash 的保护,可能会出现一些随机的可靠性问题。
|
||||
4. 如果升级到支持更多芯片的新 ESP-IDF 版本,则必须手动将这些新的芯片驱动程序添加到自定义芯片驱动程序列表中。否则,驱动程序将仅搜索自定义列表中已有的驱动程序。
|
||||
|
||||
|
||||
创建自定义芯片驱动程序并覆盖 ESP-IDF 默认驱动程序列表的步骤
|
||||
-----------------------------------------------------------
|
||||
|
||||
.. highlight: cmake
|
||||
引导加载程序 flash 驱动程序
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
1. 启用 :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` 配置选项,确保不会编译和链接 ESP-IDF 的默认芯片驱动列表 (default_registered_chips),而链接器会搜索用户自定义的同名结构体 (default_registered_chips)。
|
||||
2. 在项目中添加一个新组件,例如 ``custom_chip_driver``。
|
||||
3. 从 ESP-IDF 的 ``spi_flash`` 组件中复制必要的芯片驱动程序文件。这可能包括:
|
||||
请仔细阅读本章节内容,从而实现引导加载程序驱动程序,确保 ESP 芯片能成功启动。错误的代码会引发 ESP 芯片无法启动等问题。注意,引导加载程序中的问题无法通过 OTA 修复,因此在进行任何改动前,请充分理解以下内容。
|
||||
|
||||
- ``spi_flash_chip_drivers.c`` (该文件中定义了 ``default_registered_chips`` 结构体)
|
||||
- 与你的 flash 型号最匹配的任何 ``spi_flash_chip_*.c`` 文件
|
||||
- ``CMakeLists.txt`` 和 ``linker.lf`` 文件
|
||||
目前,引导加载程序中有两个部分可以覆盖:``bootloader_flash_unlock`` 和 ``bootloader_flash_qe_support_list`` (及其大小)。请参阅以下内容。
|
||||
|
||||
适当修改上述文件,如:
|
||||
请确保所有命令均已成功发送和识别。如果 flash 接收到未知命令,则可能会静默忽略该指令并继续工作,且不会重新启用保护。这可能会导致误写入或误擦除,也可能造成误锁定问题。
|
||||
|
||||
- 将 ``default_registered_chips`` 变量更改为非静态,并删除相关的 #ifdef 逻辑。
|
||||
- 更新 ``linker.lf`` 文件,重命名分片头部和库名,从而匹配新组件。
|
||||
- 如果复用其他驱动程序,不在 spi_flash 组件中的分片头部需要在名称中添加 ``spi_flash/`` 前缀。
|
||||
所有引导加载程序 flash 驱动程序函数都位于一个引导加载程序组件中,该组件应放置在 ``bootloader_components`` 中。例如,:example:`storage/custom_flash_driver/bootloader_components/`。只有 ``bootloader_components`` 中的组件会自动添加到引导加载程序的组件列表中。
|
||||
|
||||
组件的 ``CMakeLists.txt`` 文件中有细微差别。自定义的 flash 函数通过覆盖现有的弱函数生效。在 ``CMakeLists.txt`` 文件中使用链接器参数 `-u`,显式指定被覆盖的函数名,确保自定义函数能链接到引导加载程序中。详情请参阅 :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/CMakeLists.txt`。
|
||||
|
||||
引导加载程序 flash 解锁
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``bootloader_flash_unlock`` 函数用于解锁 flash 写保护。一旦 flash 被意外锁定,IDF 可以帮助解锁。默认情况下,解锁函数会清除状态寄存器和配置寄存器中除 QE 位以外的所有位。
|
||||
|
||||
请不要修改 QE 位,否则芯片将无法在四线模式下运行。
|
||||
|
||||
请检查以下默认设置。如果你的 flash 芯片表现与此不同,则需要进行一些修改。
|
||||
|
||||
在修改前,可以将 ``components/bootloader_support/bootloader_flash/src/bootloader_flash.c`` 中的 ``bootloader_flash_unlock_default`` 函数复制到自定义组件文件夹中的文件里。重命名该函数为 ``bootloader_flash_unlock`` 以覆盖 IDF 引导加载程序函数。例如,:example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`。
|
||||
|
||||
此时,``bootloader_flash_unlock_default`` 已声明为弱符号,链接的函数将被替换为修改后的 ``bootloader_flash_unlock``。因此,当 IDF 的第二阶段引导加载程序启动时,将执行 ``bootloader_flash_unlock`` 实现。为确保你的函数被正确链接,可启用 ``CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG``,查看日志中是否有 ``Using overridden bootloader_flash_unlock`` 提示。
|
||||
|
||||
默认函数包含三种常见行为:
|
||||
|
||||
- 情况 1(**默认**):请检查 flash 芯片的 QE 位是否位于状态寄存器-2 的第一位,并通过命令 01H (WRSR) 后发送的第二个字节进行写入。如果属实,则符合默认行为,无需进行其他操作。
|
||||
- 情况 2:如果 flash 芯片不支持情况 1 中使用 01H + 2 字节写入的方式,则检查 flash 芯片的 QE 位是否位于状态寄存器-2 的第一位,并通过命令 31H 写入。此时,请在函数 :cpp:func:`is_qe_bit_in_bit1_at_reg2` 中添加芯片 ID。例如:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit1_at_reg2(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/****GD 系列***/
|
||||
case 0xC84016:
|
||||
case 0xC84017:
|
||||
case 0xC84018:
|
||||
break;
|
||||
/**** flash 系列 ****/
|
||||
case /*flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- 情况 3:请检查 flash 芯片中的 QE 位是否位于状态寄存器-1 的第六位,由命令 01H 写入。如果属实,请在函数 :cpp:func:`is_qe_bit_in_bit6_at_reg1` 中添加芯片 ID。例如:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
IRAM_ATTR bool is_qe_bit_in_bit6_at_reg1(const esp_rom_spiflash_chip_t* chip)
|
||||
{
|
||||
bool ret = true;
|
||||
switch (chip->device_id) {
|
||||
/***ISSI 系列***/
|
||||
case 0x9D4016:
|
||||
case 0x9D4017:
|
||||
break;
|
||||
|
||||
/***MXIC 系列***/
|
||||
case 0xC22016:
|
||||
case 0xC22017:
|
||||
break;
|
||||
|
||||
/****flash 系列***/
|
||||
case /*flash ID*/:
|
||||
break;
|
||||
default:
|
||||
ret = false;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
- 情况 4:如果你的芯片不符合上述三种情况,请在 ``bootloader_flash_unlock`` 函数中添加一个新的 `if` 块以及相应的行为函数。建议将 `if` 块中的确定性函数命名为 ``is_qe_bit_in_bit_x_at_reg_x_`` (其中 x 代表行为)。参考示例 :example_file:`storage/custom_flash_driver/bootloader_components/bootloader_flash/bootloader_flash_unlock_custom.c`。
|
||||
|
||||
引导加载程序 flash 四线模式支持
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
指针 ``bootloader_flash_qe_support_list`` 用于在引导加载程序中迭代选择正确的行为,使 flash 芯片在四线模式下工作。请阅读以下内容,启用 flash 状态寄存器中的 QE 位,使 flash 在四线模式下工作。
|
||||
|
||||
* 情况 1:如果 QE 位位于状态寄存器-2 的第一位,那么可以通过命令 31H 写入。这是默认的行为,无需进行其他操作。
|
||||
* 情况 2:如果芯片的 QE 位在其他位置,或需要使用其他命令,请自行添加支持。
|
||||
|
||||
想要自行添加支持,可以从 `flash_qio_mode.c <https://github.com/espressif/esp-idf/blob/master/components/bootloader_support/bootloader_flash/src/flash_qio_mode.c>`_ 中复制 ``bootloader_flash_qe_support_list_user`` 函数到你的文件中,将函数重命名为 ``bootloader_flash_qe_support_list``。与此同时,请自定义相应的 ``bootloader_flash_qe_list_count``。
|
||||
|
||||
将你的 flash 芯片信息(如芯片的名称、ID 和写寄存器的函数等)添加到 ``bootloader_flash_qio_support_list`` 函数中,也可以复用现有的函数,如 ``bootloader_read_status_8b_rdsr``。
|
||||
|
||||
如果现有的函数不能满足需求,则可以使用 ``bootloader_execute_flash_command`` 来自定义函数,如 ``bootloader_read_status_otp_mode_8b`` 和 ``bootloader_write_status_otp_mode_8b``。详情可参阅 `bootloader_flash_custom.c <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/bootloader_flash_driver/bootloader_flash_custom.c>`_。
|
||||
|
||||
整合以上内容:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR bootloader_qio_info_t bootloader_flash_qe_support_list_user[] = {
|
||||
/* Manufacturer, mfg_id, flash_id, id mask, Read Status, Write Status, QIE Bit */
|
||||
{ "MXIC", 0xC2, 0x2000, 0xFF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "ISSI", 0x9D, 0x4000, 0xCF00, bootloader_read_status_8b_rdsr, bootloader_write_status_8b_wrsr, 6 },
|
||||
{ "WinBond", 0xEF, 0x4000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "GD", 0xC8, 0x4000, 0xFFFF, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "XM25QU64A", 0x20, 0x3817, 0xFFFF, bootloader_read_status_8b_xmc25qu64a, bootloader_write_status_8b_xmc25qu64a, 6 },
|
||||
{ "TH", 0xCD, 0x6000, 0xFF00, bootloader_read_status_16b_rdsr_rdsr2, bootloader_write_status_16b_wrsr, 9 },
|
||||
{ "EON", 0x1C, 0x7000, 0xFF00, bootloader_read_status_otp_mode_8b, bootloader_write_status_otp_mode_8b, 6 },
|
||||
|
||||
/* 如果没有其他 ID 匹配当前条目,则最终条目为默认条目
|
||||
|
||||
以上方法适用于下列芯片:
|
||||
GigaDevice (制造商 ID 0xC8, flash ID 包括 4016),
|
||||
FM25Q32 (仅 QOUT 模式, 制造商 ID 0xA1, flash ID 包括 4016)
|
||||
BY25Q32 (制造商 ID 0x68, flash ID 包括 4016)
|
||||
*/
|
||||
{ NULL, 0xFF, 0xFFFF, 0xFFFF, bootloader_read_status_8b_rdsr2, bootloader_write_status_8b_wrsr2, 1 },
|
||||
};
|
||||
const DRAM_ATTR bootloader_qio_info_t* bootloader_flash_qe_support_list = bootloader_flash_qe_support_list_user;
|
||||
uint8_t DRAM_ATTR bootloader_flash_qe_list_count = (sizeof(bootloader_flash_qe_support_list_user) / sizeof(bootloader_qio_info_t));
|
||||
|
||||
应用程序 flash 驱动程序
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
通用 flash 驱动程序
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
应用程序中的 flash 驱动程序用于读取、写入、擦除、保存数据等操作,且支持 OTA 等高级功能。可参考下列指南,为你的 flash 芯片自定义驱动程序。
|
||||
|
||||
- 步骤 1:`default_registered_chips` 的最后一项应为 `通用芯片驱动程序 <https://github.com/espressif/esp-idf/blob/master/components/spi_flash/spi_flash_chip_generic.c>`_。如果你的 flash 芯片无法匹配以上列出的任何一个芯片驱动程序,则将使用通用驱动。请检查你的 flash 芯片行为与通用驱动是否存在差异,包括但不限于不同的命令、dummy 周期、数据字节以及状态寄存器。
|
||||
- 步骤 2:如果你的 flash 芯片行为与通用驱动存在差异,则需要实现自定义的芯片驱动程序。请创建一个名为 ``spi_flash_chip_<vendor>.c`` 的新文件,在其中实现特定行为。可以将 ``esp_flash_chip_generic`` 结构体复制到文件中进行修改。记得在文件中包含 ``spi_flash_chip_generic.h``。详情请参阅示例 `esp_flash_nor <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor/>`_。
|
||||
- 步骤 3:实现与通用驱动程序存在差异的函数,并从 ``spi_flash_chip_t`` 结构体中指向这些函数。注意:如果 flash 芯片的某些行为与通用驱动相同,那么可以保留通用驱动程序的函数,无需自定义,只要为与通用驱动不同的部分编写自定义函数即可。请参考以下示例:
|
||||
|
||||
.. important::
|
||||
|
||||
flash 芯片的硬件设计各不相同,因此启用 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 选项暂停 flash 时应仔细且系统地进行测试。如果想在量产过程中使用挂起功能,请联系 `乐鑫商务部 <https://www.espressif.com/zh-hans/contact-us/sales-questions>`_。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
const DRAM_ATTR spi_flash_chip_t esp_flash_chip_eon = {
|
||||
.name = chip_name,
|
||||
.timeout = &spi_flash_chip_generic_timeout, /*<! 默认行为*/
|
||||
.probe = spi_flash_chip_eon_probe, /*<! EON 特定 */
|
||||
.reset = spi_flash_chip_generic_reset,
|
||||
.detect_size = spi_flash_chip_generic_detect_size,
|
||||
.erase_chip = spi_flash_chip_generic_erase_chip,
|
||||
.erase_sector = spi_flash_chip_generic_erase_sector,
|
||||
.erase_block = spi_flash_chip_generic_erase_block,
|
||||
.sector_size = 4 * 1024,
|
||||
.block_erase_size = 64 * 1024,
|
||||
|
||||
.get_chip_write_protect = spi_flash_chip_generic_get_write_protect,
|
||||
.set_chip_write_protect = spi_flash_chip_generic_set_write_protect,
|
||||
|
||||
.num_protectable_regions = 0,
|
||||
.protectable_regions = NULL,
|
||||
.get_protected_regions = NULL,
|
||||
.set_protected_regions = NULL,
|
||||
|
||||
.read = spi_flash_chip_generic_read,
|
||||
.write = spi_flash_chip_generic_write,
|
||||
.program_page = spi_flash_chip_generic_page_program,
|
||||
.page_size = 256,
|
||||
.write_encrypted = spi_flash_chip_generic_write_encrypted,
|
||||
|
||||
.wait_idle = spi_flash_chip_generic_wait_idle,
|
||||
.set_io_mode = spi_flash_chip_eon_set_io_mode,
|
||||
.get_io_mode = spi_flash_chip_eon_get_io_mode,
|
||||
|
||||
.read_reg = spi_flash_chip_generic_read_reg,
|
||||
.yield = spi_flash_chip_generic_yield,
|
||||
.sus_setup = spi_flash_chip_eon_suspend_cmd_conf,
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
|
||||
.. note::
|
||||
|
||||
- 在编写自己的 flash 芯片驱动程序时,可以通过 ``spi_flash_chip_***(vendor)_get_caps`` 来设置 flash 芯片的功能,并将函数指针 ``get_chip_caps`` 指向 ``spi_flash_chip_***_get_caps`` 函数以进行保护。步骤如下:
|
||||
- 编写自定义的 flash 芯片驱动程序时,可以通过 ``spi_flash_chip_***(vendor)_get_caps`` 来设置芯片功能,并将函数指针 ``get_chip_caps`` 指向 ``spi_flash_chip_***_get_caps`` 函数,确保该函数在后续使用过程中不会被误改或覆盖。步骤如下:
|
||||
|
||||
1. 通过查阅 flash 技术规格书,确定自己的 flash 芯片具有 ``spi_flash_caps_t`` 所列出的功能。
|
||||
2. 编写一个名为 ``spi_flash_chip_***(vendor)_get_caps`` 的函数。如果 flash 支持 ``suspend`` 和 ``read unique id``,可参考以下示例。
|
||||
3. 将指针 ``get_chip_caps`` (在 ``spi_flash_chip_t`` 中)指向第二步中编写的函数。
|
||||
1. 查看芯片技术规格书,确认你的 flash 芯片是否具有 ``spi_flash_caps_t`` 中列出的功能。
|
||||
2. 编写一个名为 ``spi_flash_chip_***(vendor)_get_caps`` 的函数。如果你的 flash 芯片支持 ``挂起`` 和 ``读取唯一 ID`` 的功能,可参考以下示例进行编写。
|
||||
3. 将 ``spi_flash_chip_t`` 结构体中的指针 ``get_chip_caps`` 指向函数 ``spi_flash_chip_***_get_caps``。
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
spi_flash_caps_t spi_flash_chip_***(vendor)_get_caps(esp_flash_t *chip)
|
||||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 32-bit-address flash is not supported
|
||||
flash-suspend is supported
|
||||
caps_flags |= SPI_FLAHS_CHIP_CAP_SUSPEND;
|
||||
// flash read unique id.
|
||||
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
|
||||
return caps_flags;
|
||||
}
|
||||
spi_flash_caps_t spi_flash_chip_***(vendor)_get_caps(esp_flash_t *chip)
|
||||
{
|
||||
spi_flash_caps_t caps_flags = 0;
|
||||
// 不支持 32 位地址的 flash
|
||||
caps_flags |= SPI_FLAHS_CHIP_CAP_SUSPEND;
|
||||
// 读取唯一 ID
|
||||
caps_flags |= SPI_FLASH_CHIP_CAP_UNIQUE_ID;
|
||||
return caps_flags;
|
||||
}
|
||||
|
||||
.. code-block:: c
|
||||
.. code-block:: c
|
||||
|
||||
const spi_flash_chip_t esp_flash_chip_eon = {
|
||||
// Other function pointers
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
const spi_flash_chip_t esp_flash_chip_eon = {
|
||||
// 其他函数指针
|
||||
.get_chip_caps = spi_flash_chip_eon_get_caps,
|
||||
};
|
||||
|
||||
- 也可以在示例 :example:`storage/custom_flash_driver` 中查看如何实现此功能。该示例演示了如何覆盖默认芯片驱动列表。
|
||||
- :example:`storage/custom_flash_driver` 示例同样展示了实现过程,并且演示了如何覆盖默认的芯片驱动程序列表。
|
||||
|
||||
4. 为 ``custom_chip_driver`` 组件编写一个新的 ``CMakeLists.txt`` 文件,其中包含额外的一行,添加了从 ``spi_flash`` 到 ``custom_chip_driver`` 的链接依赖项::
|
||||
- 步骤 4:创建一个头文件(例如 ``spi_flash_chip_<vendor>.h``)并使用 `extern` 声明结构体,以便其他组件或源代码复用结构体。将所有芯片驱动程序(包括源文件及其头文件)封装为一个芯片驱动程序组件,并将包含路径和源文件添加到该组件的 ``CMakeLists.txt`` 文件中。
|
||||
|
||||
idf_component_register(SRCS "spi_flash_chip_drivers.c"
|
||||
"spi_flash_chip_mychip.c" # modify as needed
|
||||
REQUIRES hal
|
||||
PRIV_REQUIRES spi_flash
|
||||
LDFRAGMENTS linker.lf)
|
||||
idf_component_add_link_dependency(FROM spi_flash)
|
||||
- 步骤 5:在 cache 被禁用时,通过 ``linker.lf`` 文件把要使用的所有芯片驱动程序放入内部 RAM 中。详情请参阅 :doc:`/api-guides/linker-script-generation`。请确保 ``linker.lf`` 包含了你添加的所有源文件。
|
||||
|
||||
- CMakeLists.txt 组件的示例可查阅 :example_file:`storage/custom_flash_driver/components/custom_chip_driver/CMakeLists.txt`
|
||||
- 步骤 6:在项目中添加一个新的组件,例如 ``custom_chip_driver``。在 ``custom_chip_driver/chip_drivers.c`` 文件中将芯片对象列在 ``default_registered_chips`` 下。启用 :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` 配置选项,防止编译和链接由 ESP-IDF 提供的默认芯片驱动程序列表 ``default_registered_chips``;相反,链接器会搜索由你自定义的同名结构体 ``default_registered_chips``。详情请参阅 :example_file:`storage/custom_flash_driver/components/custom_chip_driver/chip_drivers.c`。
|
||||
|
||||
5. ``linker.lf`` 用于在禁用缓存时,把要使用的每个芯片驱动程序都放入内部 RAM 中。详情请参阅 :doc:`/api-guides/linker-script-generation`。请确保此文件包含所有添加的源文件。
|
||||
- 步骤 7:构建项目,你将看到新的 flash 驱动程序。
|
||||
|
||||
6. 构建你的项目,将会看到新的 flash 驱动程序已投入使用。
|
||||
.. only:: SOC_MEMSPI_SRC_FREQ_120M
|
||||
|
||||
高性能 flash 实现
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
高性能模式在高于 80 MHz 的频率下运行。请查阅 *直流电气特性* 章节,判断芯片是否支持在高于 80 MHz 的频率下工作。`高性能文件 <https://github.com/espressif/components/spi_flash/spi_flash_hpm_enable.c>`_ 中已经预定义了部分高性能模式下的行为,如果你的 flash 芯片符合指定行为,请按照 ``bootloader_flash_unlock`` 部分介绍的方法扩展列表。如果你的 flash 芯片有不同的行为,请添加新行为并覆盖 ``spi_flash_hpm_enable_list`` 行为表。
|
||||
|
||||
.. important::
|
||||
|
||||
频率设置为 80 MHz 以上的 flash 芯片应进行仔细的测试,因为此时系统对于时序的要求非常严格。如果想在量产过程中使用高性能模式的功能,请联系 `乐鑫商务部 <https://www.espressif.com/zh-hans/contact-us/sales-questions>`_。
|
||||
|
||||
示例
|
||||
----
|
||||
|
||||
请参阅 :example:`storage/custom_flash_driver`,该示例通过使用 `外部组件 <https://github.com/espressif/esp-flash-drivers/tree/main/esp_flash_nor>`_,增加了对自定义 flash 的支持,实现自定义驱动程序;本文则展示了如何覆盖 IDF 驱动程序。
|
||||
|
Reference in New Issue
Block a user