Skip to content

File port_esp32.c

File List > esp32 > port_esp32.c

Go to the documentation of this file

#if defined(ESP_PLATFORM) && !defined(ARDUINO)

#include "driver/gpio.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_random.h"
#include "esp_system.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "syntropic/common/syn_compiler.h"
#include "syntropic/common/syn_defs.h"
#include "syntropic/port/syn_port_gpio.h"
#include "syntropic/port/syn_port_system.h"
#include "syntropic/port/syn_port_uart.h"

static const char *TAG = "syn_port";

/* ── System Port ────────────────────────────────────────────────────────── */

static portMUX_TYPE critical_mux = portMUX_INITIALIZER_UNLOCKED;

void syn_port_enter_critical(void)
{
    portENTER_CRITICAL(&critical_mux);
}

void syn_port_exit_critical(void)
{
    portEXIT_CRITICAL(&critical_mux);
}

uint32_t syn_port_get_tick_ms(void)
{
    // Returns millisecond ticks since boot
    return (uint32_t)(esp_timer_get_time() / 1000);
}

void syn_port_delay_ms(uint32_t ms)
{
    vTaskDelay(pdMS_TO_TICKS(ms));
}

void syn_port_system_reset(void)
{
    esp_restart();
    for (;;)
        ;
}

SYN_NORETURN void syn_assert_failed(const char *file, int line)
{
    ESP_LOGE(TAG, "ASSERT FAILED at %s:%d", file, line);
    abort();
}

/* ── Random Port ────────────────────────────────────────────────────────── */

SYN_Status syn_port_random_fill(void *buf, size_t len)
{
    esp_fill_random(buf, len);
    return SYN_OK;
}

/* ── GPIO Port ──────────────────────────────────────────────────────────── */

SYN_Status syn_port_gpio_init(SYN_GPIO_Pin pin, SYN_GPIO_Mode mode)
{
    gpio_config_t io_conf = {};
    io_conf.pin_bit_mask = (1ULL << pin);
    io_conf.intr_type = GPIO_INTR_DISABLE;

    switch (mode) {
    case SYN_GPIO_INPUT:
        io_conf.mode = GPIO_MODE_INPUT;
        io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
        io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        break;
    case SYN_GPIO_OUTPUT:
        io_conf.mode = GPIO_MODE_OUTPUT;
        io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
        io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        break;
    case SYN_GPIO_INPUT_PULLUP:
        io_conf.mode = GPIO_MODE_INPUT;
        io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
        io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        break;
    case SYN_GPIO_INPUT_PULLDOWN:
        io_conf.mode = GPIO_MODE_INPUT;
        io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
        io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
        break;
    case SYN_GPIO_OUTPUT_OD:
        io_conf.mode = GPIO_MODE_OUTPUT_OD;
        io_conf.pull_up_en = GPIO_PULLUP_DISABLE;
        io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
        break;
    default:
        return SYN_NOT_IMPLEMENTED;
    }

    esp_err_t err = gpio_config(&io_conf);
    return (err == ESP_OK) ? SYN_OK : SYN_INVALID_PARAM;
}

SYN_Status syn_port_gpio_deinit(SYN_GPIO_Pin pin)
{
    gpio_reset_pin(pin);
    return SYN_OK;
}

SYN_Status syn_port_gpio_write(SYN_GPIO_Pin pin, SYN_GPIO_State state)
{
    esp_err_t err = gpio_set_level(pin, (state == SYN_GPIO_HIGH) ? 1 : 0);
    return (err == ESP_OK) ? SYN_OK : SYN_INVALID_PARAM;
}

SYN_GPIO_State syn_port_gpio_read(SYN_GPIO_Pin pin)
{
    return gpio_get_level(pin) ? SYN_GPIO_HIGH : SYN_GPIO_LOW;
}

SYN_Status syn_port_gpio_toggle(SYN_GPIO_Pin pin)
{
    int current = gpio_get_level(pin);
    esp_err_t err = gpio_set_level(pin, current ? 0 : 1);
    return (err == ESP_OK) ? SYN_OK : SYN_INVALID_PARAM;
}

/* ── UART Port (weak — override for chips with USB Serial JTAG) ─────── */

#include "soc/soc_caps.h"

static uart_port_t get_uart_port(SYN_UARTInstance instance)
{
    if (instance == 0)
        return UART_NUM_0;
#if SOC_UART_HP_NUM > 1
    if (instance == 1)
        return UART_NUM_1;
#endif
#if SOC_UART_HP_NUM > 2
    if (instance == 2)
        return UART_NUM_2;
#endif
    return UART_NUM_MAX;
}

SYN_WEAK SYN_Status syn_port_uart_init(SYN_UARTInstance instance, uint32_t baudrate)
{
    uart_port_t port = get_uart_port(instance);
    if (port == UART_NUM_MAX)
        return SYN_INVALID_PARAM;

    uart_config_t uart_config = {
        .baud_rate = (int)baudrate,
        .data_bits = UART_DATA_8_BITS,
        .parity = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_DEFAULT,
    };

    if (uart_param_config(port, &uart_config) != ESP_OK)
        return SYN_INVALID_PARAM;
    if (uart_driver_install(port, 256, 256, 0, NULL, 0) != ESP_OK)
        return SYN_INVALID_PARAM;

    /* Use default pin mapping */
    uart_set_pin(port, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE,
                 UART_PIN_NO_CHANGE);

    return SYN_OK;
}

SYN_WEAK SYN_Status syn_port_uart_deinit(SYN_UARTInstance instance)
{
    uart_port_t port = get_uart_port(instance);
    if (port == UART_NUM_MAX)
        return SYN_INVALID_PARAM;
    uart_driver_delete(port);
    return SYN_OK;
}

SYN_WEAK SYN_Status syn_port_uart_transmit(SYN_UARTInstance instance, const uint8_t *data,
                                           size_t len, uint32_t timeout_ms)
{
    uart_port_t port = get_uart_port(instance);
    if (port == UART_NUM_MAX)
        return SYN_INVALID_PARAM;

    int written = uart_write_bytes(port, (const char *)data, len);
    if (written < 0)
        return SYN_INVALID_PARAM;

    esp_err_t err =
        uart_wait_tx_done(port, timeout_ms == 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms));
    return (err == ESP_OK) ? SYN_OK : SYN_TIMEOUT;
}

SYN_WEAK SYN_Status syn_port_uart_receive(SYN_UARTInstance instance, uint8_t *data, size_t len,
                                          size_t *received, uint32_t timeout_ms)
{
    uart_port_t port = get_uart_port(instance);
    if (port == UART_NUM_MAX)
        return SYN_INVALID_PARAM;

    int read = uart_read_bytes(port, data, len,
                               timeout_ms == 0 ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms));
    if (read < 0)
        return SYN_TIMEOUT;

    if (received)
        *received = (size_t)read;
    return (read > 0) ? SYN_OK : SYN_TIMEOUT;
}

SYN_WEAK SYN_Status syn_port_uart_transmit_byte(SYN_UARTInstance instance, uint8_t byte)
{
    return syn_port_uart_transmit(instance, &byte, 1, 100);
}

SYN_WEAK SYN_Status syn_port_uart_receive_byte(SYN_UARTInstance instance, uint8_t *byte,
                                               uint32_t timeout_ms)
{
    size_t rec = 0;
    return syn_port_uart_receive(instance, byte, 1, &rec, timeout_ms);
}

/* ── Console serial port (weak — override for USB CDC chips) ──────────── */

#include "syntropic/port/syn_port_serial.h"

#ifndef SYN_SERIAL_UART_INSTANCE
#define SYN_SERIAL_UART_INSTANCE 0
#endif

SYN_WEAK SYN_Status syn_port_serial_init(uint32_t baudrate)
{
    if (baudrate == 0)
        baudrate = 115200;
    return syn_port_uart_init(SYN_SERIAL_UART_INSTANCE, baudrate);
}

SYN_WEAK int syn_port_serial_write(const uint8_t *data, size_t len)
{
    SYN_Status s = syn_port_uart_transmit(SYN_SERIAL_UART_INSTANCE, data, len, 100);
    return (s == SYN_OK) ? (int)len : -1;
}

SYN_WEAK int syn_port_serial_read(uint8_t *buf, size_t max_len)
{
    size_t received = 0;
    SYN_Status s = syn_port_uart_receive(SYN_SERIAL_UART_INSTANCE, buf, max_len, &received, 0);
    if (s == SYN_TIMEOUT)
        return (int)received; /* got some bytes before timeout */
    if (s != SYN_OK)
        return -1;
    return (int)received;
}

#endif /* ESP_PLATFORM && !ARDUINO */