Skip to content

Commit 4e26444

Browse files
committed
feat: USB CDC ACM interface support
This adds support for the USB CDC ACM interface, and an ESP port using the interface. Closes #64
1 parent bd2fafa commit 4e26444

File tree

19 files changed

+640
-15
lines changed

19 files changed

+640
-15
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ empty_file.bin
55
binaries.c
66
*.lock
77
python_venv
8+
managed_components

.gitlab-ci.yml

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,29 @@ variables:
3838
- idf.py build
3939
- cd $CI_PROJECT_DIR/examples/esp32_spi_load_ram_example
4040
- idf.py build
41+
- cd $CI_PROJECT_DIR/examples/esp32_usb_cdc_acm_example
42+
- idf.py build
4143

44+
# Special case as ESP-IDF v4.3 is not supported by the usb_host_cdc_acm component
45+
# required by the USB CDC ACM interface port and its example
4246
build_idf_v4.3:
43-
extends: .build_idf_template
47+
stage: build
4448
image: espressif/idf:release-v4.3
49+
tags:
50+
- build
51+
- internet
52+
variables:
53+
PEDANTIC_FLAGS: "-Werror -Wall -Wextra"
54+
EXTRA_CFLAGS: "${PEDANTIC_FLAGS}"
55+
EXTRA_CXXFLAGS: "${PEDANTIC_FLAGS}"
56+
script:
57+
- cd $CI_PROJECT_DIR/examples/esp32_example
58+
- idf.py build -DMD5_ENABLED=1
59+
- idf.py build -DMD5_ENABLED=0
60+
- cd $CI_PROJECT_DIR/examples/esp32_load_ram_example
61+
- idf.py build
62+
- cd $CI_PROJECT_DIR/examples/esp32_spi_load_ram_example
63+
- idf.py build
4564

4665
build_idf_v4.4:
4766
extends: .build_idf_template

CMakeLists.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@ if (DEFINED ESP_PLATFORM)
1919
src/protocol_spi.c
2020
port/esp32_spi_port.c
2121
)
22+
elseif (${CONFIG_SERIAL_FLASHER_INTERFACE_USB})
23+
list(APPEND srcs
24+
src/protocol_uart.c
25+
src/slip.c
26+
port/esp32_usb_cdc_acm_port.c
27+
)
2228
endif()
2329

2430
# Register component to esp-idf build system
@@ -27,6 +33,10 @@ if (DEFINED ESP_PLATFORM)
2733
PRIV_INCLUDE_DIRS private_include
2834
PRIV_REQUIRES driver esp_timer)
2935

36+
if (${CONFIG_SERIAL_FLASHER_INTERFACE_USB})
37+
idf_component_set_property(${COMPONENT_NAME} PRIV_REQUIRES usb APPEND)
38+
endif()
39+
3040
set(target ${COMPONENT_LIB})
3141
component_compile_options(-Wstrict-prototypes)
3242

@@ -91,6 +101,11 @@ elseif (DEFINED SERIAL_FLASHER_INTERFACE_SPI OR CONFIG_SERIAL_FLASHER_INTERFACE_
91101
PUBLIC
92102
SERIAL_FLASHER_INTERFACE_SPI
93103
)
104+
elseif (DEFINED SERIAL_FLASHER_INTERFACE_USB OR CONFIG_SERIAL_FLASHER_INTERFACE_USB STREQUAL "y")
105+
target_compile_definitions(${target}
106+
PUBLIC
107+
SERIAL_FLASHER_INTERFACE_USB
108+
)
94109
endif()
95110

96111
if(DEFINED SERIAL_FLASHER_DEBUG_TRACE OR CONFIG_SERIAL_FLASHER_DEBUG_TRACE)

Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ menu "ESP serial flasher"
1717
config SERIAL_FLASHER_INTERFACE_SPI
1818
bool "SPI (Only supports downloading to RAM)"
1919

20+
config SERIAL_FLASHER_INTERFACE_USB
21+
bool "USB (Experimental)"
22+
2023
endchoice
2124

2225
config SERIAL_FLASHER_RESET_HOLD_TIME_MS

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
`esp-serial-flasher` is a portable C library for flashing or loading apps to RAM of Espressif SoCs from other host microcontrollers.
44

55
## Using the library
6-
Espressif SoCs are normally programmed via serial interface (UART).
6+
`esp-serial-flasher` supports a variety of host/target/interface combinations:
77

88
Supported **host** microcontrollers:
99

@@ -26,14 +26,15 @@ Supported **target** microcontrollers:
2626
Supported hardware interfaces:
2727
- UART
2828
- SPI (only for RAM download)
29+
- USB CDC ACM (experimental)
2930

3031
For example usage check the `examples` directory.
3132

3233
## Configuration
3334

3435
These are the configuration toggles available to the user:
3536

36-
* `SERIAL_FLASHER_INTERFACE_UART/SERIAL_FLASHER_INTERFACE_SPI`
37+
* `SERIAL_FLASHER_INTERFACE_UART`/`SERIAL_FLASHER_INTERFACE_SPI`/`SERIAL_FLASHER_INTERFACE_USB`
3738

3839
This defines the hardware interface to use.
3940

examples/common/example_common.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
253253
}
254254
printf("Connected to target\n");
255255

256-
#ifdef SERIAL_FLASHER_INTERFACE_UART
256+
#if (defined SERIAL_FLASHER_INTERFACE_UART) || (defined SERIAL_FLASHER_INTERFACE_USB)
257257
if (higher_transmission_rate && esp_loader_get_target() != ESP8266_CHIP) {
258258
err = esp_loader_change_transmission_rate(higher_transmission_rate);
259259
if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
@@ -271,12 +271,12 @@ esp_loader_error_t connect_to_target(uint32_t higher_transmission_rate)
271271
printf("Transmission rate changed changed\n");
272272
}
273273
}
274-
#endif /* SERIAL_FLASHER_INTERFACE_UART */
274+
#endif /* SERIAL_FLASHER_INTERFACE_UART || SERIAL_FLASHER_INTERFACE_USB */
275275

276276
return ESP_LOADER_SUCCESS;
277277
}
278278

279-
#ifdef SERIAL_FLASHER_INTERFACE_UART
279+
#if (defined SERIAL_FLASHER_INTERFACE_UART) || (defined SERIAL_FLASHER_INTERFACE_USB)
280280
esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
281281
{
282282
esp_loader_error_t err;
@@ -329,7 +329,7 @@ esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
329329

330330
return ESP_LOADER_SUCCESS;
331331
}
332-
#endif /* SERIAL_FLASHER_INTERFACE_UART */
332+
#endif /* SERIAL_FLASHER_INTERFACE_UART || SERIAL_FLASHER_INTERFACE_USB */
333333

334334
esp_loader_error_t load_ram_binary(const uint8_t *bin)
335335
{
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# For more information about build system see
2+
# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
3+
# The following five lines of boilerplate have to be in your project's
4+
# CMakeLists in this exact order for cmake to work correctly
5+
cmake_minimum_required(VERSION 3.16)
6+
7+
set(EXTRA_COMPONENT_DIRS ../../)
8+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
9+
project(esp32_usb_cdc_acm_example)
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Flashing multiple partitions over USB CDC ACM interface
2+
3+
## Overview
4+
5+
This example demonstrates how to flash an ESP32-S3 from another ESP32-S3 or an ESP32-S2 MCU using the `esp_serial_flash` component API. Binaries to be flashed from host MCU to another Espressif SoC can be found in `binaries` folder and are converted into C-array during build process.
6+
7+
> **Note:** The `esp32_usb_cdc_acm` port requires ESP-IDF v4.4 or newer to build
8+
9+
Following steps are performed in order to re-program target's memory:
10+
11+
1. The system is started
12+
2. The USB CDC ACM driver is initialized and a task to handle USB events is created
13+
3. As soon as an ESP32-S3 is connected to the bus, a connection is opened with it using `loader_port_esp32_usb_cdc_acm_init()`. If the target USB Serial/JTAG peripheral is not active (e.g the device firmware is using the USB OTG peripheral), it is necessary to manually put it in download mode.
14+
4. The flasher connection is started with the connected ESP32-S3 by calling `esp_loader_connect()`.
15+
5. Binary file is opened and its size is acquired, as it has to be known before flashing.
16+
6. Then `esp_loader_flash_start()` is called to enter flashing mode and erase amount of memory to be flashed.
17+
7. `esp_loader_flash_write()` function is called repeatedly until the whole binary image is transferred.
18+
8. After completion, the device can be manually reset and another ESP32-S3 can be connected to perform the flashing on.
19+
20+
> **Note:** The USB CDC ACM device of the ESP32-S3 does not support changing the baudrate, so the argument to `esp_loader_connect()` is irrelevant
21+
22+
## USB host driver usage
23+
24+
This example makes use of the Espressif [USB Host Driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/usb_host.html).
25+
In addition to initializing the host driver, a FreeRTOS task needs to be created to handle host usb events.
26+
27+
A binary semaphore is used as a lock for the connected device, and a callback is registered with the loader port which releases the lock so that a new device can be connected only after disconnection.
28+
29+
## Hardware Required
30+
31+
* One ESP32-S3 target board and one ESP32-S3 or ESP32-S2 board, with each board having USB connections
32+
* An USB OTG adapter for the host board
33+
* One or two USB cables for power supply and programming.
34+
35+
> **Note:** The USB connector on most ESP32-S3 and ESP32-S2 boards cannot supply power to the target, so a separate power connection is required
36+
37+
## Building and flashing
38+
39+
To run the example, type the following command:
40+
41+
```CMake
42+
idf.py -p PORT flash monitor
43+
```
44+
45+
(To exit the serial monitor, type ``Ctrl-]``.)
46+
47+
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
48+
49+
## Configuration
50+
51+
For details about available configuration option, please refer to top level [README.md](../../README.md).
52+
Compile definitions can be specified on command line when running `idf.py`, for example:
53+
54+
```
55+
idf.py build -DMD5_ENABLED=1
56+
```
57+
Binaries to be flashed are placed in separate folder (binaries.c) for each possible target and converted to C-array. Without explicitly enabling MD5 check, flash integrity verification is disabled by default.
58+
59+
## Example output
60+
61+
Here is the example's console output:
62+
63+
```
64+
...
65+
I (541) main_task: Calling app_main()
66+
I (541) usb_flasher: Installing USB Host
67+
I (571) usb_flasher: Installing the USB CDC-ACM driver
68+
I (571) usb_flasher: Opening CDC ACM device 0x303A:0x1001...
69+
Connected to target
70+
I (1121) usb_flasher: Loading bootloader...
71+
Erasing flash (this may take a while)...
72+
Start programming
73+
Progress: 100 %
74+
Finished programming
75+
I (1681) usb_flasher: Loading partition table...
76+
Erasing flash (this may take a while)...
77+
Start programming
78+
Progress: 100 %
79+
Finished programming
80+
I (1771) usb_flasher: Loading app...
81+
Erasing flash (this may take a while)...
82+
Start programming
83+
Progress: 100 %
84+
Finished programming
85+
I (5011) usb_flasher: Done!
86+
```
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
set(srcs main.c ../../common/example_common.c)
2+
set(include_dirs . ../../common)
3+
4+
idf_component_register(SRCS ${srcs}
5+
INCLUDE_DIRS ${include_dirs}
6+
PRIV_REQUIRES esp-serial-flasher usb
7+
)
8+
set(target ${COMPONENT_LIB})
9+
10+
# Embed binaries into the app.
11+
# In ESP-IDF this can also be done using EMBED_FILES option of idf_component_register.
12+
# Here an external tool is used to make file embedding similar with other ports.
13+
include(${CMAKE_CURRENT_LIST_DIR}/../../common/bin2array.cmake)
14+
create_resources(${CMAKE_CURRENT_LIST_DIR}/../../binaries/Hello-world ${CMAKE_BINARY_DIR}/binaries.c)
15+
set_property(SOURCE ${CMAKE_BINARY_DIR}/binaries.c PROPERTY GENERATED 1)
16+
target_sources(${target} PRIVATE ${CMAKE_BINARY_DIR}/binaries.c)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## IDF Component Manager Manifest File
2+
dependencies:
3+
usb_host_cdc_acm: "^2"
4+
idf: ">=4.4"

0 commit comments

Comments
 (0)