ESP32-S3 Baremetal Support

esp32s3_console.c

Text output over USB-Serial-JTAG.

The board's single USB-C port is the chip's built-in USB-Serial-JTAG peripheral, which the ROM has already enumerated - so printing costs two registers, no driver, no UART and no pin. Output appears on the same COM port you flash over.

Writes are dropped rather than blocked when no host is reading the port, so an unattended board keeps running at full speed.

API

Declared in esp32s3_console.h

Functions

void console_print_char(char c);

Prints a single character to the USB console.

c
the character to print
returns
nothing
void console_print(const char *text);

Prints a null-terminated string to the USB console.

text
the string to print
returns
nothing
void console_print_u32(uint32_t value);

Prints an unsigned number in decimal to the USB console.

value
the number to print (0 to 4294967295)
returns
nothing
void console_print_hex(uint32_t value, uint32_t digits);

Prints a number in hexadecimal, zero-padded, without a "0x" prefix. Register values and I2C addresses are far easier to read this way.

value
the number to print
digits
how many hex digits to pad to, 1 to 8 (2 for a byte)
returns
nothing

Source

board/esp32s3_console.c
board/esp32s3_console.c
60 lines
/*
 * Text output over the chip's built-in USB-Serial-JTAG peripheral.
 */

#include "esp32s3_console.h"
#include "esp32s3_regs.h"

// How many polls to spend waiting for FIFO space before giving up. Counted in
// loop passes, not time, so this costs nothing when a host is connected.
#define CONSOLE_FIFO_SPIN_LIMIT 200000u

void console_print_char(char c)
{
    // With no host reading the port the FIFO never drains, so give up rather
    // than stall the caller forever.
    for (uint32_t spin = 0; spin < CONSOLE_FIFO_SPIN_LIMIT; spin++) {
        if (ESP32S3_REG(USB_SERIAL_JTAG_EP1_CONF_REG) & USB_SERIAL_JTAG_IN_EP_DATA_FREE) {
            ESP32S3_REG(USB_SERIAL_JTAG_EP1_REG)      = (uint8_t)c;
            ESP32S3_REG(USB_SERIAL_JTAG_EP1_CONF_REG) = USB_SERIAL_JTAG_WR_DONE;  // ship it
            return;
        }
    }
}

void console_print(const char *text)
{
    while (*text) {
        console_print_char(*text++);
    }
}

void console_print_u32(uint32_t value)
{
    char digits[10];        // 4294967295 is the longest a uint32_t gets
    int  count = 0;

    do {
        digits[count++] = (char)('0' + value % 10);
        value /= 10;
    } while (value);

    while (count--) {
        console_print_char(digits[count]);
    }
}

void console_print_hex(uint32_t value, uint32_t digits)
{
    if (digits < 1) {
        digits = 1;
    } else if (digits > 8) {
        digits = 8;
    }

    // Most significant nibble first, so the shift counts down.
    while (digits--) {
        uint32_t nibble = (value >> (4 * digits)) & 0xFu;
        console_print_char((char)(nibble < 10 ? '0' + nibble : 'A' + nibble - 10));
    }
}
include/esp32s3_console.h— the header, in full
include/esp32s3_console.h
40 lines
/*
 * Text output over USB-Serial-JTAG.
 *
 * The board's single USB-C port is the chip's built-in USB-Serial-JTAG
 * peripheral, which the ROM has already enumerated - so printing costs two
 * registers, no driver, no UART and no pin. Output appears on the same COM
 * port you flash over.
 *
 * Writes are dropped rather than blocked when no host is reading the port, so
 * an unattended board keeps running at full speed.
 */

#ifndef ESP32S3_CONSOLE_H
#define ESP32S3_CONSOLE_H

#include <stdint.h>

// Prints a single character to the USB console.
// - c: the character to print
// returns: nothing
void console_print_char(char c);

// Prints a null-terminated string to the USB console.
// - text: the string to print
// returns: nothing
void console_print(const char *text);

// Prints an unsigned number in decimal to the USB console.
// - value: the number to print (0 to 4294967295)
// returns: nothing
void console_print_u32(uint32_t value);

// Prints a number in hexadecimal, zero-padded, without a "0x" prefix.
// Register values and I2C addresses are far easier to read this way.
// - value: the number to print
// - digits: how many hex digits to pad to, 1 to 8 (2 for a byte)
// returns: nothing
void console_print_hex(uint32_t value, uint32_t digits);

#endif // ESP32S3_CONSOLE_H