Просмотр исходного кода

Added support for ESP8266 and ESP32-S2

Martin Valik 5 лет назад
Родитель
Сommit
e658052789

+ 5 - 3
README.md

@@ -2,10 +2,10 @@
 
 ## Overview
 
-Serial flasher component provides portable library for flashing ESP32 from other host microcontroller. ESP32 is normally programmed via serial interface (UART) and port layer for given host microcontroller has to be implemented, if not available. Details can be found in section below.
+Serial flasher component provides portable library for flashing `ESP32`, `ESP32-S2`, `ESP8266` from other host microcontroller. Espressif SoCs are normally programmed via serial interface (UART) and port layer for given host microcontroller has to be implemented, if not available. Details can be found in section below.
 
 
-## Supporting new target
+## Supporting new host target
 
 In order to support new target, following function has to be implemented by user:
 
@@ -29,8 +29,10 @@ Please refer to ports in `port` directory. Currently, only [ESP32 port](port/esp
 
 ## Component integration
 
-At this point, this component can only be integrated with IDF. Improvement of existing CMakeList.txt file is to be done.   
+Apart from writing port (if not available), user has to provide `loader_config_user.h` header file and add its include path into build system. By defining appropriate macros, default configuration can be overwritten. As for now, slave target to be flashed (one of Espressif SoCs) and flash integrity verification can be configured. In case header file remains empty, `esp-serial-flasher` is compiled for ESP32 target by default.
 
 ## Known limitations
 
+At this point, component can only be integrated with IDF. Proper CMakeList.txt file is to be done.   
+
 Size of new binary image has to be known before flashing.

+ 1 - 0
example/CMakeLists.txt

@@ -3,6 +3,7 @@
 cmake_minimum_required(VERSION 3.5)
 
 set(EXTRA_COMPONENT_DIRS ../)
+include_directories(main)
 
 include($ENV{IDF_PATH}/tools/cmake/project.cmake)
 project(esp-serial-flasher)

+ 17 - 8
example/README.md

@@ -2,23 +2,31 @@
 
 ## Overview
 
-Example demonstrates how to flash ESP32 from another (host) MCU using esp_serial_flash component API. In this case, ESP32 is also used as host MCU. Binary to be flashed from one ESP32 to another is stored in partition named `spiffs`.
+Example demonstrates how to flash ESP32/ESP32-S2/ESP8266 from another (host) MCU using esp_serial_flash component API. In this case, ESP32 is also used as host MCU. Binary to be flashed from host MCU to ESP* is stored in partition named `spiffs`. Binaries are placed in separate folder for each target.
 
 This example is based on spiffsgen example located in IDF. For more information how to use SPIFFS Image Generator, refer to [spiffsgen example](https://github.com/espressif/esp-idf/tree/master/examples/storage/spiffsgen)
 
 Following steps are performed in order to re-program target's memory:
 
-1. UART1 through which new binary will be transfered is initialized.
-2. Filesystem is initialized and mounted.
-3. Binary file is opened and its size is acquired, as it has to be known before flashing.
-4. Host puts slave device into boot mode tries to connect by calling `loader_connect()`.
-5. Then `loader_flash_start()` is called to enter flashing mode.
-6. `loader_flash_write()` function is called repeatedly until the whole binary image is transfered.
+1. Filesystem is initialized and mounted.
+2. UART1 through which new binary will be transfered is initialized.
+3. Host puts slave device into boot mode tries to connect by calling `esp_loader_connect()`.
+4. Binary file is opened and its size is acquired, as it has to be known before flashing.
+5. Then `esp_loader_flash_start()` is called to enter flashing mode and erase amount of memory to be flashed.
+6. `esp_loader_flash_write()` function is called repeatedly until the whole binary image is transfered.
+
+Note: In addition, to steps mentioned above, `esp_loader_change_baudrate`  is called after connection is established in order to increase flashing speed. This does not apply for ESP8266, as its bootloader does not support this command. However, ESP8266 is capable of detecting baud rate during connection phase, and can be changed before calling `esp_loader_connect`, if necessary.
+
+## Target selection
+
+User can configure targets and `loader_config_user.h` header.
+Currently, three targets (ESP32, ESP32-S2, ESP8266) are supported.
+By default, example is compiled for ESP32 target.
 
 ## Hardware Required
 
 * Two development boards with ESP32 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.).
-* A USB cable for power supply and programming.
+* One or two USB cables for power supply and programming.
 
 ## Hardware connection
 
@@ -31,6 +39,7 @@ Table below shows connection between two ESP32 devices.
 |    IO4       |      RX0      |
 |    IO5       |      TX0      |
 
+Note: interconnection is the same for all three targets (slaves). 
 
 ### Build and flash
 

+ 8 - 0
example/main/loader_config_user.h

@@ -0,0 +1,8 @@
+#pragma once
+
+// Uncomment to compile for ESP8266 target
+// #define TARGET_ESP8266  1
+// #define MD5_ENABLED     0
+
+// Uncomment to compile for ESP32-S2 target
+// #define TARGET_ESP32_S2  1

+ 39 - 17
example/main/serial_flash_example_main.c

@@ -20,13 +20,31 @@
 
 static const char *TAG = "example";
 
-const uint32_t PARTITION_TABLE_ADDRESS = 0x8000;
-const uint32_t APPLICATION_ADDRESS = 0x10000;
+#ifndef TARGET_ESP8266
 const uint32_t BOOTLOADER_ADDRESS = 0x1000;
+#else
+const uint32_t BOOTLOADER_ADDRESS = 0x0;
+#endif
+const uint32_t PARTITION_ADDRESS = 0x8000;
+const uint32_t APPLICATION_ADDRESS = 0x10000;
 
 const uint32_t HIGHER_BAUD_RATE = 230400;
 static uint8_t payload[1024];
 
+#if defined TARGET_ESP8266
+#define PARTITION_FILE "/spiffs/ESP8266/partition-table.bin"
+#define BOOTLOADER_FILE "/spiffs/ESP8266/bootloader.bin"
+#define APPLICATION_FILE "/spiffs/ESP8266/hello-world.bin"
+#elif defined TARGET_ESP32
+#define PARTITION_FILE "/spiffs/ESP32/partition-table.bin"
+#define BOOTLOADER_FILE "/spiffs/ESP32/bootloader.bin"
+#define APPLICATION_FILE "/spiffs/ESP32/hello-world.bin"
+#elif defined TARGET_ESP32_S2
+#define PARTITION_FILE "/spiffs/ESP32_S2/partition-table.bin"
+#define BOOTLOADER_FILE "/spiffs/ESP32_S2/bootloader.bin"
+#define APPLICATION_FILE "/spiffs/ESP32_S2/hello-world.bin"
+#endif
+
 
 static void flash_binary(FILE *image, size_t image_size, size_t address)
 {
@@ -62,11 +80,13 @@ static void flash_binary(FILE *image, size_t image_size, size_t address)
 
     ESP_LOGI(TAG, "Finished programming");
 
+#ifndef TARGET_ESP8266
     err = esp_loader_flash_verify();
     if (err != ESP_LOADER_SUCCESS) {
         ESP_LOGE(TAG, "MD5 does not match. err: %d", err);
         return;
     }
+#endif
     ESP_LOGI(TAG, "Flash verified");
 }
 
@@ -112,7 +132,7 @@ static esp_err_t connect_to_target()
 
     // Initialize UART
     esp_loader_error_t err = loader_port_serial_init(&config);
-    if(err != ESP_LOADER_SUCCESS) {
+    if (err != ESP_LOADER_SUCCESS) {
         ESP_LOGE(TAG, "serial initialization failed.");
         return err;
     }
@@ -121,22 +141,24 @@ static esp_err_t connect_to_target()
 
     err = esp_loader_connect(&connect_config);
     if (err != ESP_LOADER_SUCCESS) {
-        ESP_LOGE(TAG, "Cannot connect to target.");
+        ESP_LOGE(TAG, "Cannot connect to target. Error: %u", err);
         return err;
     }
     ESP_LOGI(TAG, "Connected to target");
 
-    // err = esp_loader_change_baudrate(HIGHER_BAUD_RATE);
-    // if (err != ESP_LOADER_SUCCESS) {
-    //     ESP_LOGE(TAG, "Unable to change baud rate on target.");
-    //     return err;
-    // }
+#ifndef TARGET_ESP8266
+    err = esp_loader_change_baudrate(HIGHER_BAUD_RATE);
+    if (err != ESP_LOADER_SUCCESS) {
+        ESP_LOGE(TAG, "Unable to change baud rate on target.");
+        return err;
+    }
 
-    // err = loader_port_change_baudrate(HIGHER_BAUD_RATE);
-    // if (err != ESP_LOADER_SUCCESS) {
-    //     ESP_LOGE(TAG, "Unable to change baud rate.");
-    //     return err;
-    // }
+    err = loader_port_change_baudrate(HIGHER_BAUD_RATE);
+    if (err != ESP_LOADER_SUCCESS) {
+        ESP_LOGE(TAG, "Unable to change baud rate.");
+        return err;
+    }
+#endif
 
     return ESP_OK;
 }
@@ -172,9 +194,9 @@ void app_main(void)
 {
     if ( register_vfs() == ESP_OK ) {
         if ( connect_to_target() == ESP_OK) {
-            upload_file("/spiffs/partition-table.bin", PARTITION_TABLE_ADDRESS);
-            upload_file("/spiffs/bootloader.bin", BOOTLOADER_ADDRESS);
-            upload_file("/spiffs/hello-world.bin", APPLICATION_ADDRESS);
+            upload_file(PARTITION_FILE, PARTITION_ADDRESS);
+            upload_file(BOOTLOADER_FILE, BOOTLOADER_ADDRESS);
+            upload_file(APPLICATION_FILE, APPLICATION_ADDRESS);
         }
         esp_vfs_spiffs_unregister(NULL);
     }

+ 0 - 0
example/spiffs_image/bootloader.bin → example/spiffs_image/ESP32/bootloader.bin


+ 0 - 0
example/spiffs_image/hello-world.bin → example/spiffs_image/ESP32/hello-world.bin


+ 0 - 0
example/spiffs_image/partition-table.bin → example/spiffs_image/ESP32/partition-table.bin


BIN
example/spiffs_image/ESP32_S2/bootloader.bin


BIN
example/spiffs_image/ESP32_S2/hello-world.bin


BIN
example/spiffs_image/ESP32_S2/partition-table.bin


BIN
example/spiffs_image/ESP8266/bootloader.bin


BIN
example/spiffs_image/ESP8266/hello-world.bin


BIN
example/spiffs_image/ESP8266/partition-table.bin


+ 4 - 0
include/esp_loader.h

@@ -17,6 +17,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "loader_config.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -31,6 +32,7 @@ typedef enum
     ESP_LOADER_ERROR_FAIL,             /*!< Unspecified error */
     ESP_LOADER_ERROR_TIMEOUT,          /*!< Timeout elapsed */
     ESP_LOADER_ERROR_INVALID_MD5,      /*!< Computed and receied MD5 does not match */
+    ESP_LOADER_ERROR_INVALID_TARGET,   /*!< Connected target is invalid */
     ESP_LOADER_ERROR_INVALID_RESPONSE  /*!< Internal error */
 } esp_loader_error_t;
 
@@ -147,7 +149,9 @@ esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_valu
   *     - ESP_LOADER_ERROR_TIMEOUT Timeout
   *     - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
   */
+#ifndef TARGET_ESP8266
 esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate);
+#endif
 
 /**
   * @brief Verify target's flash integrity by checking MD5.

+ 11 - 0
include/loader_config.h

@@ -19,12 +19,23 @@
 extern "C" {
 #endif
 
+#include "loader_config_user.h"
+
 // MD5 checksum can be used to check, if new image was flashed successfully.
 // When enabled, esp_loader_flash_verify() function can be called to to verify
 // flash integrity. In case verification is unnecessary, this option can be 
 // disabled in order to reduce code size.
+#if !defined MD5_ENABLED
 #define MD5_ENABLED  1
+#endif
 
+#if !defined TARGET_ESP32_S2 && !defined TARGET_ESP8266
+#define TARGET_ESP32 1
+#endif
+
+#if MD5_ENABLED && defined TARGET_ESP8266
+#error "ESP8266 ROM loader does not support MD5 check"
+#endif
 
 #ifdef __cplusplus
 }

+ 8 - 2
private_include/serial_comm_prv.h

@@ -17,6 +17,7 @@
 
 #include <stdint.h>
 #include <stdbool.h>
+#include "loader_config.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -78,6 +79,9 @@ typedef struct __attribute__((packed))
     uint32_t packet_count;
     uint32_t packet_size;
     uint32_t offset;
+#ifdef TARGET_ESP32_S2
+    uint32_t encrypted;
+#endif
 } begin_command_t;
 
 typedef struct __attribute__((packed))
@@ -158,8 +162,10 @@ typedef struct __attribute__((packed))
 {
     uint8_t failed;
     uint8_t error;
-    uint8_t reserved_0; // ESP32 ROM only
-    uint8_t reserved_1; // ESP32 ROM only
+#ifndef TARGET_ESP8266
+    uint8_t reserved_0;
+    uint8_t reserved_1;
+#endif
 } response_status_t;
 
 typedef struct __attribute__((packed))

+ 48 - 6
src/esp_loader.c

@@ -25,8 +25,28 @@
 #define MAX(a, b) ((a) > (b)) ? (a) : (b)
 #endif
 
+typedef struct {
+    uint32_t reg_1;
+    uint32_t reg_2;
+} date_registers_t;
+
+static const date_registers_t date_regs = {
+#if defined TARGET_ESP8266
+    .reg_1 = 0x00062000,
+    .reg_2 = 0
+#elif defined TARGET_ESP32
+    .reg_1 = 0x15122500,
+    .reg_2 = 0
+#elif defined TARGET_ESP32_S2
+    .reg_1 = 0x00000500,
+    .reg_2 = 0x19031400
+#endif
+};
+
+static const uint32_t UART_DATE_REG_ADDR = 0x60000078;    // used to differentiate ESP8266 vs ESP32*
+static const uint32_t UART_DATE_REG2_ADDR = 0x3f400074;   // used to differentiate ESP32-S2 vs other models
+
 static const uint32_t DEFAULT_TIMEOUT = 500;
-static const uint32_t SPI_PIN_CONFIG_DEFAULT = 0;
 static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000;       // timeout for most flash operations
 static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 3000; // timeout (per megabyte) for erasing a region
 static const uint8_t  PADDING_PATTERN = 0xFF;
@@ -73,6 +93,19 @@ static uint32_t timeout_per_mb(uint32_t size_bytes, uint32_t time_per_mb)
     return MAX(timeout, DEFAULT_FLASH_TIMEOUT);
 }
 
+static esp_loader_error_t detect_chip(void)
+{
+    uint32_t reg_1, reg_2;
+
+    RETURN_ON_ERROR( esp_loader_read_register(UART_DATE_REG_ADDR,  &reg_1) );
+    RETURN_ON_ERROR( esp_loader_read_register(UART_DATE_REG2_ADDR, &reg_2) );
+
+    if (date_regs.reg_1 == reg_1 && (date_regs.reg_2 == 0 || date_regs.reg_2 == reg_2)) {
+        return ESP_LOADER_SUCCESS;
+    } else {
+        return ESP_LOADER_ERROR_INVALID_TARGET;
+    }
+}
 
 esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
 {
@@ -94,8 +127,15 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
         }
     } while (err != ESP_LOADER_SUCCESS);
 
+    RETURN_ON_ERROR( detect_chip() );
+
+#ifndef TARGET_ESP8266
+    uint32_t SPI_PIN_CONFIG_DEFAULT = 0;
     loader_port_start_timer(DEFAULT_TIMEOUT);
-    return loader_spi_attach_cmd(SPI_PIN_CONFIG_DEFAULT);
+    err = loader_spi_attach_cmd(SPI_PIN_CONFIG_DEFAULT);
+#endif
+
+    return err;
 }
 
 
@@ -154,12 +194,14 @@ esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_valu
     return loader_write_reg_cmd(address, reg_value, 0xFFFFFFFF, 0);
 }
 
+#ifndef TARGET_ESP8266
 esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate)
 {
     loader_port_start_timer(DEFAULT_TIMEOUT);
 
     return loader_change_baudrate_cmd(baudrate);
 }
+#endif
 
 #if MD5_ENABLED
 
@@ -195,16 +237,16 @@ esp_loader_error_t esp_loader_flash_verify(void)
     RETURN_ON_ERROR( loader_md5_cmd(s_start_address, s_image_size, received_md5) );
 
     bool md5_match = memcmp(hex_md5, received_md5, MD5_SIZE) == 0;
-    
-    if(!md5_match) {
+
+    if (!md5_match) {
         hex_md5[MD5_SIZE] = '\n';
         received_md5[MD5_SIZE] = '\n';
 
         loader_port_debug_print("Error: MD5 checksum does not match:\n");
         loader_port_debug_print("Expected:\n");
-        loader_port_debug_print((char*)received_md5);
+        loader_port_debug_print((char *)received_md5);
         loader_port_debug_print("Actual:\n");
-        loader_port_debug_print((char*)hex_md5);
+        loader_port_debug_print((char *)hex_md5);
 
         return ESP_LOADER_ERROR_INVALID_MD5;
     }

+ 10 - 11
src/serial_comm.c

@@ -20,6 +20,7 @@
 #include <string.h>
 // #include <stdio.h>
 
+#define CMD_SIZE(cmd) ( sizeof(cmd) - sizeof(command_common_t) )
 
 static uint32_t s_sequence_number = 0;
 
@@ -243,7 +244,7 @@ esp_loader_error_t loader_flash_begin_cmd(uint32_t offset,
         .common = {
             .direction = WRITE_DIRECTION,
             .command = FLASH_BEGIN,
-            .size = 16,
+            .size = CMD_SIZE(begin_cmd),
             .checksum = 0
         },
         .erase_size = erase_size,
@@ -264,13 +265,11 @@ esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = FLASH_DATA,
-            .size = 16,
+            .size = CMD_SIZE(data_cmd) + size,
             .checksum = compute_checksum(data, size)
         },
         .data_size = size,
         .sequence_number = s_sequence_number++,
-        .zero_0 = 0,
-        .zero_1 = 0
     };
 
     return send_cmd_with_data(&data_cmd, sizeof(data_cmd), data, size);
@@ -283,7 +282,7 @@ esp_loader_error_t loader_flash_end_cmd(bool stay_in_loader)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = FLASH_END,
-            .size = 4,
+            .size = CMD_SIZE(end_cmd),
             .checksum = 0
         },
         .stay_in_loader = stay_in_loader
@@ -299,7 +298,7 @@ esp_loader_error_t loader_sync_cmd(void)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = SYNC,
-            .size = 36,
+            .size = CMD_SIZE(sync_cmd),
             .checksum = 0
         },
         .sync_sequence = {
@@ -322,7 +321,7 @@ esp_loader_error_t loader_write_reg_cmd(uint32_t address, uint32_t value,
         .common = {
             .direction = WRITE_DIRECTION,
             .command = WRITE_REG,
-            .size = 16,
+            .size = CMD_SIZE(write_cmd),
             .checksum = 0
         },
         .address = address,
@@ -341,7 +340,7 @@ esp_loader_error_t loader_read_reg_cmd(uint32_t address, uint32_t *reg)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = READ_REG,
-            .size = 16,
+            .size = CMD_SIZE(read_cmd),
             .checksum = 0
         },
         .address = address,
@@ -357,7 +356,7 @@ esp_loader_error_t loader_spi_attach_cmd(uint32_t config)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = SPI_ATTACH,
-            .size = 8,
+            .size = CMD_SIZE(attach_cmd),
             .checksum = 0
         },
         .configuration = config,
@@ -373,7 +372,7 @@ esp_loader_error_t loader_change_baudrate_cmd(uint32_t baudrate)
         .common = {
             .direction = WRITE_DIRECTION,
             .command = CHANGE_BAUDRATE,
-            .size = 8,
+            .size = CMD_SIZE(baudrate_cmd),
             .checksum = 0
         },
         .new_baudrate = baudrate,
@@ -389,7 +388,7 @@ esp_loader_error_t loader_md5_cmd(uint32_t address, uint32_t size, uint8_t *md5_
         .common = {
             .direction = WRITE_DIRECTION,
             .command = SPI_FLASH_MD5,
-            .size = 16,
+            .size = CMD_SIZE(md5_cmd),
             .checksum = 0
         },
         .address = address,

+ 1 - 0
test/loader_config_user.h

@@ -0,0 +1 @@
+#pragma once

+ 10 - 2
test/test.cpp

@@ -132,7 +132,7 @@ struct __attribute__((packed)) flash_write_frame {
         .common = {
             .direction = WRITE_DIRECTION,
             .command = FLASH_DATA,
-            .size = 16,
+            .size = 16 + PAYLOAD_SIZE,
             .checksum = 0xef,
         },
         .data_size = PAYLOAD_SIZE,
@@ -231,8 +231,16 @@ TEST_CASE( "Large payload that does not fit BLOCK_SIZE is split into \
 
 TEST_CASE( "Can connect within specified time " )
 {
+    // Set date registers used for detection of attached chip
+    auto uart_date_reg_1 = read_reg_response;
+    auto uart_date_reg_2 = read_reg_response;
+    uart_date_reg_1.data.common.value = 0x15122500;
+    uart_date_reg_2.data.common.value = 0;
+
     clear_buffers();
     queue_response(sync_response);
+    queue_response(uart_date_reg_1);
+    queue_response(uart_date_reg_2);
     queue_response(attach_response);
 
     esp_loader_connect_args_t connect_config = {
@@ -301,7 +309,7 @@ TEST_CASE ( "SLIP is encoded correctly" )
         0xc0,       // Begin
         0x00,         // Write direction
         0x03,         // FLASH_DATA command
-        16, 0,        // Number of characters to send
+        16 + sizeof(data), 0, // Number of characters to send
         0x33, 0, 0, 0,// Checksum
         sizeof(data), 0, 0, 0, // Data size
         0, 0, 0, 0,   // Sequence number