Skip to content

Commit

Permalink
Merge branch 'feature/add_spi_interface' into 'master'
Browse files Browse the repository at this point in the history
feature: Add SPI interface for ports

Closes ESF-1

See merge request espressif/esp-serial-flasher!60
  • Loading branch information
dobairoland committed Jun 2, 2023
2 parents fae24c4 + 1d4d3d8 commit 26794d6
Show file tree
Hide file tree
Showing 24 changed files with 1,177 additions and 136 deletions.
2 changes: 2 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ variables:
- idf.py build -DMD5_ENABLED=0
- cd $CI_PROJECT_DIR/examples/esp32_load_ram_example
- idf.py build
- cd $CI_PROJECT_DIR/examples/esp32_spi_load_ram_example
- idf.py build

build_idf_v4.2:
extends: .build_idf_template
Expand Down
47 changes: 40 additions & 7 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
cmake_minimum_required(VERSION 3.5)

set(srcs
src/esp_loader.c
src/esp_targets.c
src/md5_hash.c
src/protocol.c
src/slip.c
src/esp_loader.c
src/protocol_common.c
)


if (DEFINED ESP_PLATFORM)
if (${CONFIG_SERIAL_FLASHER_INTERFACE_UART})
list(APPEND srcs
src/protocol_uart.c
src/slip.c
port/esp32_port.c
)
elseif (${CONFIG_SERIAL_FLASHER_INTERFACE_SPI})
list(APPEND srcs
src/protocol_spi.c
port/esp32_spi_port.c
)
endif()
# Register component to esp-idf build system
list(APPEND srcs port/esp32_port.c)
if ("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER "4.0")
# esp_timer component was introduced in v4.2
set(priv_requires driver)
Expand All @@ -36,6 +45,19 @@ if (DEFINED ESP_PLATFORM)
endif()

else()
if (NOT DEFINED SERIAL_FLASHER_INTERFACE_UART AND NOT DEFINED SERIAL_FLASHER_INTERFACE_SPI)
set(SERIAL_FLASHER_INTERFACE_UART true)
endif()

if (DEFINED SERIAL_FLASHER_INTERFACE_UART)
list(APPEND srcs
src/protocol_uart.c
src/slip.c
)
elseif (DEFINED SERIAL_FLASHER_INTERFACE_SPI)
list(APPEND srcs src/protocol_spi.c)
endif()

# Create traditional CMake target
add_library(flasher ${srcs})

Expand All @@ -56,8 +78,19 @@ else()

endif()

if(DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)
target_compile_definitions(${target} PUBLIC -DMD5_ENABLED=1)
if (DEFINED SERIAL_FLASHER_INTERFACE_UART OR CONFIG_SERIAL_FLASHER_INTERFACE_UART STREQUAL "y")
target_compile_definitions(${target}
PUBLIC
SERIAL_FLASHER_INTERFACE_UART
)
if (DEFINED MD5_ENABLED OR CONFIG_SERIAL_FLASHER_MD5_ENABLED)
target_compile_definitions(${target} PUBLIC MD5_ENABLED=1)
endif()
elseif (DEFINED SERIAL_FLASHER_INTERFACE_SPI OR CONFIG_SERIAL_FLASHER_INTERFACE_SPI STREQUAL "y")
target_compile_definitions(${target}
PUBLIC
SERIAL_FLASHER_INTERFACE_SPI
)
endif()

if(DEFINED CONFIG_SERIAL_FLASHER_RESET_HOLD_TIME_MS AND DEFINED CONFIG_SERIAL_FLASHER_BOOT_HOLD_TIME_MS)
Expand Down
14 changes: 14 additions & 0 deletions Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ menu "ESP serial flasher"
help
Select this option to enable MD5 hashsum check after flashing.

choice SERIAL_FLASHER_INTERFACE
prompt "Hardware interface to use for firmware download"
default SERIAL_FLASHER_INTERFACE_UART
help
esp-serial-flasher can work with UART and SPI interfaces.

config SERIAL_FLASHER_INTERFACE_UART
bool "UART"

config SERIAL_FLASHER_INTERFACE_SPI
bool "SPI (Only supports downloading to RAM, experimental)"

endchoice

config SERIAL_FLASHER_RESET_HOLD_TIME_MS
int "Time for which the reset pin is asserted when doing a hard reset"
default 100
Expand Down
40 changes: 27 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,57 @@ Supported **target** microcontrollers:
- ESP32-C2
- ESP32-H2

Supported hardware interfaces:
- UART
- SPI (only for RAM download, experimental)

## Supporting new host target

In order to support new target, following function has to be implemented by user:

- loader_port_read()
- loader_port_write()
- loader_port_enter_bootloader()
- loader_port_delay_ms()
- loader_port_start_timer()
- loader_port_remaining_time()
- `loader_port_read()`
- `loader_port_write()`
- `loader_port_enter_bootloader()`
- `loader_port_delay_ms()`
- `loader_port_start_timer()`
- `loader_port_remaining_time()`

Following functions are part of io.h header for convenience, however, user does not have to strictly follow function signatures, as there are not called directly from library.

- loader_port_change_transmission_rate()
- loader_port_reset_target()
- loader_port_debug_print()
- `loader_port_change_transmission_rate()`
- `loader_port_reset_target()`
- `loader_port_debug_print()`

For the SPI interface ports
- `loader_port_spi_set_cs()`
needs to be implemented as well.

Prototypes of all function mentioned above can be found in [io.h](include/io.h).
Please refer to ports in `port` directory. Currently, ports for [ESP32](port/esp32_port.c), [STM32](port/stm32_port.c), and [Zephyr](port/zephyr_port.c) are available.

## Configuration

* `SERIAL_FLASHER_INTERFACE_UART/SERIAL_FLASHER_INTERFACE_SPI`

This defines the hardware interface to use. SPI interface only supports RAM download mode and is in experimental stage and can undergo changes.

Default: SERIAL_FLASHER_INTERFACE_UART

These are the configuration toggles available to the user:
* MD5_ENABLED
* `MD5_ENABLED`

If enabled, serial flasher is capable of verifying flash integrity after writing to memory.
If enabled, serial flasher is capable of verifying flash integrity after writing to flash.

Default: Enabled
> Warning: As ROM bootloader of ESP8266 does not support MD5_CHECK, this option has to be disabled!
* SERIAL_FLASHER_RESET_HOLD_TIME_MS
* `SERIAL_FLASHER_RESET_HOLD_TIME_MS`

This is the time for which the reset pin is asserted when doing a hard reset in milliseconds.

Default: 100

* SERIAL_FLASHER_BOOT_HOLD_TIME_MS
* `SERIAL_FLASHER_BOOT_HOLD_TIME_MS`

This is the time for which the boot pin is asserted when doing a hard reset in milliseconds.

Expand Down
10 changes: 6 additions & 4 deletions examples/common/example_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
}
printf("Connected to target\n");

#ifdef SERIAL_FLASHER_INTERFACE_UART
if (higher_transmission_rate && esp_loader_get_target() != ESP8266_CHIP) {
err = esp_loader_change_transmission_rate(higher_transmission_rate);
if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
Expand All @@ -206,11 +207,12 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
printf("Transmission rate changed changed\n");
}
}
#endif /* SERIAL_FLASHER_INTERFACE_UART */

return ESP_LOADER_SUCCESS;
}


#ifdef SERIAL_FLASHER_INTERFACE_UART
esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
{
esp_loader_error_t err;
Expand Down Expand Up @@ -263,14 +265,14 @@ esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)

return ESP_LOADER_SUCCESS;
}

#endif /* SERIAL_FLASHER_INTERFACE_UART */

esp_loader_error_t load_ram_binary(const uint8_t *bin)
{
printf("Start loading\n");
esp_loader_error_t err;
const example_bin_header_t *header = (const example_bin_header_t *)bin;
example_bin_segment_t segments[header->segments];
const esp_loader_bin_header_t *header = (const esp_loader_bin_header_t *)bin;
esp_loader_bin_segment_t segments[header->segments];

// Parse segments
uint32_t seg;
Expand Down
7 changes: 7 additions & 0 deletions examples/esp32_spi_load_ram_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16)

set(EXTRA_COMPONENT_DIRS ../../)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32-spi-ram-loader)
80 changes: 80 additions & 0 deletions examples/esp32_spi_load_ram_example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Example of loading the program into RAM through SPI

## Overview

This example demonstrates how to upload an app to RAM of an Espressif MCU with SPI download support from another (host) MCU using the `esp_serial_flash` component API. In this case, another Espressif MCU is used as the host. Binaries to be uploaded to RAM from host MCU to the target MCU can be found in `binaries/RAM_APP` folder and are converted into C-array during build process.

Following steps are performed in order to re-program the target's memory:

1. SPI2 through which the binary will be transfered is initialized.
2. Host puts slave device into SPI download mode tries to connect by calling `esp_loader_connect()`.
3. Then `esp_loader_mem_start()` is called for each segment in RAM.
4. `esp_loader_flash_write()` function is called repeatedly for every segment until the whole binary image is transfered.
5. `esp_loader_mem_finish()` is called with the binary entrypoint, telling the chip to start the uploaded program.
6. UART2 is initialized for the connection to the target
7. Target output is continually read and printed

## Hardware Required

* Two development boards, one with any Espressif MCU (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) and one with an Espressif MCU with SPI download support. Here is a short list of supported MCUs:
1. ESP32-C3
2. ESP32-C2
3. ESP32-S3
4. ESP32-S2
5. ESP32-H2
* One or two USB cables for power supply and programming.

## Hardware connection

Table below shows connection between two Espressif MCUs.

| Host | Slave |
| IO_5 | RESET |
| IO_12 | CLK |
| IO_10 | CS |
| IO_13 | MISO |
| IO_11 | MOSI |
| IO_14 | QUADWP |
| IO_9 | QUADHD |
| IO_13 | STRAP_B0 |
| IO_2 | STRAP_B1 |
| IO_3 | STRAP_B2 |
| IO_4 | STRAP_B3 |
| IO_6 | UART0_RX |
| IO_7 | UART0_TX |

> Note 1: Strapping bit pins are documented in the TRM for each respective chip
> Note 2: For achieving highest speeds, check which pins go through the IO MUX bypassing the GPIO matrix and use those. Pins chosen here are IO MUX pins for ESP32S3 and ESP32S2 chips.
## Build and flash

To run the example, type the following command:

```CMake
idf.py -p PORT flash monitor
```

(To exit the serial monitor, type ``Ctrl-]``.)

See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.

## Example output

Here is the example's console output:

```
Connected to target
I (682) spi_ram_loader: Loading app to RAM ...
Start loading
Downloading 7840 bytes at 0x3fc96e00...
Downloading 312 bytes at 0x3fca0020...
Downloading 93164 bytes at 0x40380000...
Finished loading
I (802) spi_ram_loader: ********************************************
I (802) spi_ram_loader: *** Logs below are print from slave .... ***
I (812) spi_ram_loader: ********************************************
Hello world!
Hello world!
...
```
14 changes: 14 additions & 0 deletions examples/esp32_spi_load_ram_example/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
set(srcs main.c ../../common/example_common.c)
set(include_dirs . ../../common)

idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include_dirs})
set(target ${COMPONENT_LIB})

# Embed binaries into the app.
# In ESP-IDF this can also be done using EMBED_FILES option of idf_component_register.
# Here an external tool is used to make file embedding similar with other ports.
include(${CMAKE_CURRENT_LIST_DIR}/../../common/bin2array.cmake)
create_resources(${CMAKE_CURRENT_LIST_DIR}/../../binaries/RAM_APP ${CMAKE_BINARY_DIR}/binaries.c)
set_property(SOURCE ${CMAKE_BINARY_DIR}/binaries.c PROPERTY GENERATED 1)
target_sources(${target} PRIVATE ${CMAKE_BINARY_DIR}/binaries.c)
Loading

0 comments on commit 26794d6

Please sign in to comment.