Esp32's system initialization and startup process and design learning methods
For esp32, only one app_main function exists in the development program. This function is the entrance of the user program. This is equivalent to the main function in the system where FreeRTOS is not called, but before app_main, the system has another initialization process, which can be roughly divided into the following three processes:
- In the ROM, the first-level Boot Loader loads the second-level Boot Loader image of the flash memory offset 0x1000 to RAM (IRAM and DRAM ).
- The second-level boot program loads the partition table and primary application image from the flash memory. The main application contains the RAM segment and the read-only segment mapped through the flash cache.
- Main Application image execution. In this case, you can start the second CPU and RTOS scheduler.
Step 1 describes the three processes in detail as follows: Step 1.
System first-stage bootload starts. For system first-stage bootloader, the main task is to load the bootloader image to RAM from the Flash Address 0X1000 (the bootloader file of this project is supported by bootloader \ subproject \ main \ bootloader_start.c under the component directory in esp-idf. view Source Code ), after the SoC is reset, the pro cpu runs immediately and executes the Reset vector code, while the app cpu remains reset. During startup, the pro cpu executes all initialization.call_start_cpu0
The CPU reset of the APP in the APP startup code is canceled. The Reset vector code is located in the address 0x40000400 in the ESP32 chip mask ROM and cannot be modified.
Check the startup code called from the Reset vectorGPIO_STRAP_REG (defined by gpio_reg.h)
The register of the boot pin status to determine the boot mode. The following situations occur based on the reset reason:
- Reset from deep sleep: If the value is
RTC_CNTL_STORE6_REG
Non-zero, and CRC value of RTC memoryRTC_CNTL_STORE7_REG
Valid,RTC_CNTL_STORE6_REG
Use it as the entry point address and jump to it immediately. IfRTC_CNTL_STORE6_REG
Zero, orRTC_CNTL_STORE7_REG
Contains invalid CRC, orRTC_CNTL_STORE6_REG
The Returned Code continues to start, as if it were a power-on reset.Note:: Run the custom code and provide a deep sleep mechanism. See deep sleep documentation.
- For power-on reset, software SOC reset and watchdog SOC Reset:
GPIO_STRAP_REG
If you require UART or SDIO download mode, check the register. In this case, configure UART or SDIO and wait for the code to be downloaded. Otherwise, continue to start, as if due to software CPU reset.
- For software CPU reset and watchdog CPU Reset: Configure spi flash based on EFUSE values and try to load code from flash memory. This step is described in more detail in the following section. If the Code fails to be loaded from the flash memory, extract the BASIC Interpreter to RAM and start it. Please note that when this happens, the RTC watchdog is still enabled, so unless the interpreter receives any input, the watchdog will reset the SOC within several hundred milliseconds and repeat the entire process. If the interpreter receives any input from UART, It disables the watchdog.
It can be seen that the first stage is mainly to pave the way for the second stage, and the application binary is loaded from the address 0x1000 from the flash memory. The first 4kB flash sector is used to store the signature of the secure boot IV and application image. Check the security guide document for detailed information.
Step 2:
In the ESP-IDF, the binary image in flash memory at 0x1000 is the second-level boot loader. The components/bootloader directory of the ESP-IDF provides the second-stage Boot Loader source code. Please note that this arrangement is not the only possibility in the ESP32 chip. You can write a fully functional application that will work when flash memory reaches 0 x, using a second-phase boot loader in the ESP-IDF to increase the flexibility of the flash layout (Using Partitioned Tables ), it also allows various processes related to flash encryption, secure boot, and air Update (OTA.
When the first-stage Boot Loader completes the check and loads the second-stage boot loader, it jumps to the second-stage Boot Loader entry point found in the binary image header.
In the second stage, the pilot program reads the Partition Table found at the offset 0x8000. For more information, see the Partition Table documentation. Guide the loader to find the factory and OTA partitions, andOTA InformationThe data found in the partition determines which one to boot.
For the selected partition, the second-level Boot Loader copies the data and code segments mapped to IRAM and DRAM to its load address. The Flash MMU configuration provides the correct ing for the part with the load address in the DROM and IROM areas. Note that the second-stage Boot Loader configures flash MMU for the PRO and APP CPUs, but only enables flash MMU for the pro cpu. This is because the second-stage Bootstrap code is loaded into the memory used by the app cpu cache. The cache function of the app cpu is passed to the application. Once the code is loaded and the flash memory MMU is set, the second-level boot loader will jump to the application entry point in the binary image header.
Currently, the official website does not support loading programs and adding application definitions to define application partition selection logic. For example, you may need to load different application images based on the GPIO status. (But it is said that it will be supported in the future), what we can do now is to customize your own bootloader by copying the Bootloader component to the application directory and making necessary changes there. In this case, the ESP-IDF build system will compile components in the application directory instead of the ESP-IDF component directory.
Step 3:
The main function image starts executing, that is, main_task (ESP-IDF application entry point isCall_start_cpu0, which can be found in
In components/esp32/cpu_start.c, find
.) The two main functions of this function are to enable the heap distributor and redirect the app cpu to its entry point.call_start_cpu1
. The Code on the pro cpu sets the entry point of the app cpu, cancels the reset of the app cpu, and waits for the global flag set by the Code running on the app cpu, indicating that it has started. Once completed, pro cpu jumpsstart_cpu0
Function, and the app cpu will jumpstart_cpu1
Function.
The twostart_cpu0
AndStart_cpu
Functions are not unchangeable,start_cpu0
Enable or initialize the default component implementation based on your selectionMenuconfig, you can view the latest list of execution steps through omponents/esp32/cpu_start.c, but it is worth noting that,In this phase, all C ++ global constructors in the application are called. Once all the basic components are initialized, create the supervisor and start the FreeRTOS scheduler.What we know about esp32 is that it is a dual-core cpu. In this process, when the PRO CPUstart_cpu0
When the function is initialized, the app cpu automaticallystart_cpu1
Run the function and wait for the scheduling program to start on the pro cpu. Once the scheduling program is started on the pro cpu, the code on the app cpu also starts the scheduling program.
Main_task, which allows you to configure the size and priority of the chief Service Stack.menuconfig
. Of course, we can use this task for initial application-specific settings, such as starting other tasks. Applications can also use the main tasks of event loops and other common activities. However, note that ifapp_main
The function returns and main_task is deleted.
Paste main_task in the source code below
static void main_task(void* args){ // Now that the application is about to start, disable boot watchdogs REG_CLR_BIT(TIMG_WDTCONFIG0_REG(0), TIMG_WDT_FLASHBOOT_MOD_EN_S); REG_CLR_BIT(RTC_CNTL_WDTCONFIG0_REG, RTC_CNTL_WDT_FLASHBOOT_MOD_EN);#if !CONFIG_FREERTOS_UNICORE // Wait for FreeRTOS initialization to finish on APP CPU, before replacing its startup stack while (port_xSchedulerRunning[1] == 0) { ; }#endif //Enable allocation in region where the startup stacks were located. heap_caps_enable_nonos_stack_heaps(); app_main(); vTaskDelete(NULL); }
As you can see, the main task is to close the watchdog and call the app_main function (that is, the main function entry at the time of Secondary Development). Because there is no loop in main_task, after app_main is executed, the vTaskDelet function deletes the task.
Let's talk about esp32 development when developing such a Wi-Fi cpu. Because the non-arm chip does not provide detailed register configuration documents, although the register and pin functions are provided in the esp-idf in English annotations, however, the APIS starting with esp are basically provided in the form of linked libraries. You cannot see the implementation method of the api, so it is a reasonable and quick development method, because the esp-idf provides FreeRTOS operating system, and the API manual for related functions is provided in the official documents, the API function interfaces required by the relevant applications are described in detail, and a large number of examples (esp-idf/examples) are provided for the relevant applications in esp-idf ), so for esp32 learning, I think the better learning method is:
For those who do not have the foundation of the embedded operating system, it is best to first learn the FreeRTOS operating system and focus on the application of related queues and task scheduling. If there is an operating system learning and application, however, we do not use FreeRTOS but operating systems such as ucos. We recommend that you check the FreeRTOS API manual to start development. For the esp32 part, we recommend that you learn the official routines and call and use the APIs that you need to use. In this way, you can learn the application methods of related functions, (This part will be the key part of my later article) Final Completion of product-related design.