Skip to content

Commit

Permalink
Merge branch 'feat/add_example_usj' into 'master'
Browse files Browse the repository at this point in the history
change(usb_serial_jtag): Add example for usb serial jtag echo

Closes IDFGH-11503, IDFGH-11493, and IDFGH-11478

See merge request espressif/esp-idf!27366
  • Loading branch information
mythbuster5 committed Dec 1, 2023
2 parents 2c5e876 + 2a0debd commit 2d4a924
Show file tree
Hide file tree
Showing 10 changed files with 217 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .gitlab/ci/target-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ pytest_examples_esp32c6_generic:
artifacts: false
tags: [ esp32c6, generic ]

pytest_examples_esp32c6_usj_device:
extends:
- .pytest_examples_dir_template
- .rules:test:example_test-esp32c6
needs:
- job: build_pytest_examples_esp32c6
artifacts: false
tags: [ esp32c6, usj_device ]

pytest_examples_esp32h2_generic:
extends:
- .pytest_examples_dir_template
Expand Down
33 changes: 29 additions & 4 deletions components/driver/usb_serial_jtag/usb_serial_jtag.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <string.h>
#include <stdbool.h>
#include <stdatomic.h>
#include "esp_log.h"
#include "hal/usb_serial_jtag_ll.h"
#include "hal/usb_phy_ll.h"
Expand All @@ -25,12 +26,19 @@
#define USJ_RCC_ATOMIC()
#endif

typedef enum {
FIFO_IDLE = 0, /*!< Indicates the fifo is in idle state */
FIFO_BUSY = 1, /*!< Indicates the fifo is in busy state */
} fifo_status_t;

// The hardware buffer max size is 64
#define USB_SER_JTAG_ENDP_SIZE (64)
#define USB_SER_JTAG_RX_MAX_SIZE (64)

typedef struct{
intr_handle_t intr_handle; /*!< USB-SERIAL-JTAG interrupt handler */
portMUX_TYPE spinlock; /*!< Spinlock for usb_serial_jtag */
_Atomic fifo_status_t fifo_status; /*!< Record the status of fifo */

// RX parameters
RingbufHandle_t rx_ring_buf; /*!< RX ring buffer handler */
Expand Down Expand Up @@ -67,7 +75,7 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
// If the hardware fifo is available, write in it. Otherwise, do nothing.
if (usb_serial_jtag_ll_txfifo_writable() == 1) {
// We disable the interrupt here so that the interrupt won't be triggered if there is no data to send.
usb_serial_jtag_ll_disable_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);

size_t queued_size;
uint8_t *queued_buff = NULL;
bool is_stashed_data = false;
Expand All @@ -91,7 +99,10 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {

// On ringbuffer wrap-around the size can be 0 even though the buffer returned is not NULL
if (queued_size > 0) {
portENTER_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
uint32_t sent_size = usb_serial_jtag_write_and_flush(queued_buff, queued_size);
portEXIT_CRITICAL_ISR(&p_usb_serial_jtag_obj->spinlock);

if (sent_size < queued_size) {
// Not all bytes could be sent at once; stash the unwritten bytes in a tx buffer
Expand All @@ -108,7 +119,6 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
if (is_stashed_data == false) {
vRingbufferReturnItemFromISR(p_usb_serial_jtag_obj->tx_ring_buf, queued_buff, &xTaskWoken);
}
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
} else {
// The last transmit may have sent a full EP worth of data. The host will interpret
// this as a transaction that hasn't finished yet and keep the data in its internal
Expand All @@ -119,6 +129,7 @@ static void usb_serial_jtag_isr_handler_default(void *arg) {
// flush will not by itself cause this ISR to be called again.
}
} else {
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
usb_serial_jtag_ll_clr_intsts_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
}
}
Expand Down Expand Up @@ -147,6 +158,7 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
p_usb_serial_jtag_obj->rx_buf_size = usb_serial_jtag_config->rx_buffer_size;
p_usb_serial_jtag_obj->tx_buf_size = usb_serial_jtag_config->tx_buffer_size;
p_usb_serial_jtag_obj->tx_stash_cnt = 0;
p_usb_serial_jtag_obj->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
if (p_usb_serial_jtag_obj == NULL) {
ESP_LOGE(USB_SERIAL_JTAG_TAG, "memory allocate error");
err = ESP_ERR_NO_MEM;
Expand All @@ -171,6 +183,7 @@ esp_err_t usb_serial_jtag_driver_install(usb_serial_jtag_driver_config_t *usb_se
USJ_RCC_ATOMIC() {
usb_serial_jtag_ll_enable_bus_clock(true);
}
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);

// Configure PHY
usb_phy_ll_int_jtag_enable(&USB_SERIAL_JTAG);
Expand Down Expand Up @@ -220,10 +233,22 @@ int usb_serial_jtag_write_bytes(const void* src, size_t size, TickType_t ticks_t
ESP_RETURN_ON_FALSE(src != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "Invalid buffer pointer.");
ESP_RETURN_ON_FALSE(p_usb_serial_jtag_obj != NULL, ESP_ERR_INVALID_ARG, USB_SERIAL_JTAG_TAG, "The driver hasn't been initialized");

size_t sent_data = 0;
BaseType_t result = pdTRUE;
const uint8_t *buff = (const uint8_t *)src;
if (p_usb_serial_jtag_obj->fifo_status == FIFO_IDLE) {
portENTER_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_BUSY);
sent_data = usb_serial_jtag_write_and_flush(src, size);
portEXIT_CRITICAL(&p_usb_serial_jtag_obj->spinlock);
}

// Blocking method, Sending data to ringbuffer, and handle the data in ISR.
BaseType_t result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*) (buff), size, ticks_to_wait);
// Now trigger the ISR to read data from the ring buffer.
if (size - sent_data > 0) {
result = xRingbufferSend(p_usb_serial_jtag_obj->tx_ring_buf, (void*) (buff+sent_data), size-sent_data, ticks_to_wait);
} else {
atomic_store(&p_usb_serial_jtag_obj->fifo_status, FIFO_IDLE);
}
usb_serial_jtag_ll_ena_intr_mask(USB_SERIAL_JTAG_INTR_SERIAL_IN_EMPTY);
return (result == pdFALSE) ? 0 : size;
}
Expand Down
8 changes: 8 additions & 0 deletions examples/peripherals/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -397,3 +397,11 @@ examples/peripherals/uart/uart_echo_rs485:
examples/peripherals/usb:
disable:
- if: SOC_USB_OTG_SUPPORTED != 1

examples/peripherals/usb_serial_jtag/usb_serial_jtag_echo:
disable:
- if: SOC_USB_SERIAL_JTAG_SUPPORTED != 1
disable_test:
- if: IDF_TARGET not in ["esp32c6"]
temporary: true
reason: lack of runners. Hardware is similar, test on one target is enough currently.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# 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)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(usb_serial_jtag_echo)
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
| Supported Targets | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
| ----------------- | -------- | -------- | -------- | -------- |

# USB SERIAL JTAG Echo Example

(See the README.md file in the upper level 'examples' directory for more information about examples.)

This example demonstrates how to utilize USB_SERIAL_JTAG interfaces by echoing back to the sender any data received on USB_SERIAL_JTAG.

## How to use example

### Hardware Required

The example can be run on development board that supports usb_serial_jtag, that is based on the Espressif SoC. The board shall be connected to a computer with a single USB cable for flashing and monitoring with UART port. The usb_serial_jtag port on board can be be connected to computer with another USB cable to get the echo.

### Configure the project

Use the command below to configure project using Kconfig menu as showed in the table above.
The default Kconfig values can be changed such as disable the `ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG`
```
idf.py menuconfig
```

### Build and Flash

Build the project and flash it to the board, then run monitor tool to view serial output:

```
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

Type some characters in the terminal connected to the external serial interface. As result you should see echo in the same terminal which you used for typing the characters.

For example, If I type `hi espressif`, `See you again!`, `Echo a very long buffer. Assume this buffer is very large and you can see whole buffer`

And you can get the log with:

```
I (296) main_task: Started on CPU0
I (296) main_task: Calling app_main()
I (296) main_task: Returned from app_main()
I (13346) Recv str: : 0x408117b8 68 69 20 65 73 70 72 65 73 73 69 66 0d 0a |hi espressif..|
I (16606) Recv str: : 0x408117b8 53 65 65 20 79 6f 75 20 61 67 61 69 6e 21 0d 0a |See you again!..|
I (18726) Recv str: : 0x408117b8 45 63 68 6f 20 61 20 76 65 72 79 20 6c 6f 6e 67 |Echo a very long|
I (18726) Recv str: : 0x408117c8 20 62 75 66 66 65 72 2e 20 41 73 73 75 6d 65 20 | buffer. Assume |
I (18726) Recv str: : 0x408117d8 74 68 69 73 20 62 75 66 66 65 72 20 69 73 20 76 |this buffer is v|
I (18736) Recv str: : 0x408117e8 65 72 79 20 6c 61 72 67 65 20 61 6e 64 20 79 6f |ery large and yo|
I (18746) Recv str: : 0x408117b8 75 20 63 61 6e 20 73 65 65 20 77 68 6f 6c 65 20 |u can see whole |
I (18756) Recv str: : 0x408117c8 62 75 66 66 65 72 0d 0a |buffer..|
```

## Troubleshooting

Note that you are not supposed to see the echo in the terminal if usb_serial_jtag port is used for flashing and monitoring.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
idf_component_register(SRCS "usb_serial_echo_main.c"
INCLUDE_DIRS ".")
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/usb_serial_jtag.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "esp_check.h"

#define BUF_SIZE (1024)
#define ECHO_TASK_STACK_SIZE (2048)

static void echo_task(void *arg)
{
// Configure USB SERIAL JTAG
usb_serial_jtag_driver_config_t usb_serial_jtag_config = {
.rx_buffer_size = BUF_SIZE,
.tx_buffer_size = BUF_SIZE,
};

ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&usb_serial_jtag_config));
ESP_LOGI("usb_serial_jtag echo", "USB_SERIAL_JTAG init done");

// Configure a temporary buffer for the incoming data
uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
if (data == NULL) {
ESP_LOGE("usb_serial_jtag echo", "no memory for data");
return;
}

while (1) {

int len = usb_serial_jtag_read_bytes(data, (BUF_SIZE - 1), 20 / portTICK_PERIOD_MS);

// Write data back to the USB SERIAL JTAG
if (len) {
usb_serial_jtag_write_bytes((const char *) data, len, 20 / portTICK_PERIOD_MS);
data[len] = '\0';
ESP_LOG_BUFFER_HEXDUMP("Recv str: ", data, len, ESP_LOG_INFO);
}
}
}

void app_main(void)
{
xTaskCreate(echo_task, "USB SERIAL JTAG_echo_task", ECHO_TASK_STACK_SIZE, NULL, 10, NULL);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0

from time import sleep

import pytest
import serial
import serial.tools.list_ports
from pytest_embedded import Dut


@pytest.mark.esp32c6 # usb_serial_jtag is very similar, test C6 is enough.
@pytest.mark.usj_device
def test_usb_device_serial_example(dut: Dut) -> None:
dut.expect_exact('USB_SERIAL_JTAG init done')
sleep(2)

ports = list(serial.tools.list_ports.comports())
for p in ports:
if (p.device == '/dev/ttyACM0'): # Get the usb_serial_jtag port
with serial.Serial(p.device) as s:
s.write(b'hi, espressif\n')
sleep(1)
dut.expect_exact('hi, espressif')
res = s.readline()
assert b'hi, espressif' in res
s.write(b'See you again!\n')
sleep(1)
dut.expect_exact('See you again!')
res = s.readline()
assert b'See you again!' in res
s.write(b'Echo a very long buffer. Assume this buffer is very large and you can see whole buffer\n')
sleep(1)
dut.expect_exact('Echo a very long') # 16 bytes a line because we use `ESP_LOG_BUFFER_HEXDUMP` in code.
dut.expect_exact(' buffer. Assume ')
dut.expect_exact('this buffer is v')
dut.expect_exact('ery large and yo')
dut.expect_exact('u can see whole ')
dut.expect_exact('buffer')
res = s.readline()
assert b'Echo a very long buffer. Assume this buffer is very large and you can see whole buffer' in res
s.write(b'64 bytes buffer:-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-\n')
sleep(1)
res = s.readline()
assert b'64 bytes buffer:-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-' in res

return

raise Exception('usb_serial_jtag port not found')
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_ESP_CONSOLE_SECONDARY_NONE=y
1 change: 1 addition & 0 deletions tools/ci/idf_pytest/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
'twai_network': 'multiple runners form a TWAI network.',
'sdio_master_slave': 'Test sdio multi board, esp32+esp32',
'sdio_multidev_32_c6': 'Test sdio multi board, esp32+esp32c6',
'usj_device': 'Test usb_serial_jtag and usb_serial_jtag is used as serial only (not console)'
}


Expand Down

0 comments on commit 2d4a924

Please sign in to comment.