| name | tuyaopen/onchip-uart |
| description | On-chip UART (serial port) for TuyaOpen using the tal_uart API: init a user UART, send/receive bytes, RX interrupt callback. 串口、UART、串口收发、串口通信、tal_uart、用户串口。 |
| when_to_use | Use when the user wants a real serial port for data I/O (send/receive bytes over a UART). NOT for debug logging — that is PR_DEBUG/PR_* over the log console, a different facility entirely. |
| id | onchip-uart |
| surfaces | ["embedded"] |
| tags | ["uart","serial","on-chip","tal_uart"] |
TuyaOpen On-Chip UART (user serial)
⛔ STOP — for a "serial / 串口" request, confirm WHICH serial before ANY code
When the user says anything like "用串口发 X" / "send X over serial" / "发给电脑" /
"用 USB 串口" without explicitly stating which kind, you MUST ask and WAIT before
writing any code or choosing an API. Do NOT infer — "发给电脑" / "USB 串口" do
not imply the log console.
Ask these two fixed options and wait for the user's choice — prefer an interactive
multiple-choice prompt if your tooling supports one; otherwise list them as text:
你要用哪种串口?
- 调试日志口(
PR_*;T5AI 上就是 USB 转串口、电脑直接能收;零配置)
- 独立用户串口(
tal_uart;独立 UART 实例 + 自定义引脚)
- Picked 1 → just
PR_NOTICE(...); no UART init, nothing recorded. Done.
- Picked 2 → continue with this skill (confirm instance + pins, record
onchip:uart<N>).
❌ BAD: user says "用串口发 hello world 给电脑" → you write PR_DEBUG("hello world") without asking.
✅ GOOD: you ask the two options, wait for the answer, then write code only for the chosen path.
RED LINE — user UART vs log console (do not confuse)
| API | What it is |
|---|
| User serial port | tal_uart_init / tal_uart_write / tal_uart_read | A real UART peripheral you open on chosen pins. This skill. |
| Debug / log output | PR_DEBUG / PR_INFO / PR_NOTICE / PR_ERR (tal_log) | The logging console. NOT a user peripheral — do not model it as one. |
-
Which serial? → see the ⛔ STOP above: ask the two fixed options and wait;
never assume the log console. PR_* = log console (not a peripheral, nothing recorded);
tal_uart = dedicated user UART (this skill; confirm instance + pins, record onchip:uart<N>).
-
Pick the UART from platform.json — do NOT guess roles. Read
.tuyaopen/ide/platform.json → peripherals.uart.spec.ports[]. Each port carries:
role — its power-on default purpose: "log" (debug/log console),
"download" (flash/download port), or "general" (free for app use). This is the
default and is reconfigurable in code, but treat it as the guide.
pinGroups — its available TX/RX pin set(s).
For a user UART, prefer a role:"general" port. Never use the log port
(role:"log"). The download port is fine — its use is only
transient during flashing, so it's free at runtime. Confirm the chosen instance with
the user; don't infer roles from general board knowledge.
On-chip — no Kconfig, no TDD registration
- On-chip peripherals are provided by the platform. Do NOT write
CONFIG_ENABLE_UART (or any CONFIG_ENABLE_SPI/I2C/GPIO) — those are platform-selected. No app_default.config change is needed.
- There is no
board_register_hardware() / TDD step for a user UART. You call tal_uart_init() directly.
Step 1 — Confirm the instance and pins
A user UART is confirmed by which UART instance + which TX/RX pins.
Whether you need pin mux is decided by the port's pinGroups (from platform.json):
- Port with a single
pinGroups entry → those are its fixed default pins.
Do NOT call tkl_io_pinmux_config — just tal_uart_init() and use it. The vast
majority of cases (and the SDK default TUYA_UART_NUM_0) are this. Don't mux "just in case".
- Port with multiple
pinGroups → you must pick one; selecting a group needs mux.
Only then #include "tkl_pinmux.h". E.g. T5AI UART2 exposes PIN31/30 or PIN41/40:
#include "tkl_pinmux.h"
tkl_io_pinmux_config(TUYA_IO_PIN_40, TUYA_UART2_RX);
tkl_io_pinmux_config(TUYA_IO_PIN_41, TUYA_UART2_TX);
If the chosen port has one pin group, skip the block above and tkl_pinmux.h entirely.
Record the confirmed UART in .tuyaopen/used-peripherals.json as onchip:uart<N>
before generating code — see the hardware-vibe-coding skill.
Step 2 — Init + send/receive
Default-pin case (the common one) — no pin mux, no tkl_pinmux.h:
#include "tal_api.h"
#define USR_UART_NUM TUYA_UART_NUM_0
void app_uart_init(void)
{
TAL_UART_CFG_T cfg = {0};
cfg.base_cfg.baudrate = 115200;
cfg.base_cfg.databits = TUYA_UART_DATA_LEN_8BIT;
cfg.base_cfg.stopbits = TUYA_UART_STOP_LEN_1BIT;
cfg.base_cfg.parity = TUYA_UART_PARITY_TYPE_NONE;
cfg.rx_buffer_size = 256;
cfg.open_mode = O_BLOCK;
if (tal_uart_init(USR_UART_NUM, &cfg) != OPRT_OK) {
PR_ERR("uart init failed");
return;
}
const char *msg = "hello world\r\n";
tal_uart_write(USR_UART_NUM, (const uint8_t *)msg, strlen(msg));
}
static void uart_rx_loop(void)
{
uint8_t buf[256];
for (;;) {
int n = tal_uart_read(USR_UART_NUM, buf, sizeof(buf));
if (n <= 0) { tal_system_sleep(10); continue; }
tal_uart_write(USR_UART_NUM, buf, n);
}
}
RX-by-interrupt instead of polling:
static void uart_rx_cb(TUYA_UART_NUM_E port, void *buff, uint16_t len) { }
tal_uart_rx_reg_irq_cb(USR_UART_NUM, uart_rx_cb);
API Reference
| Function | Description |
|---|
tal_uart_init(port, &cfg) | Init UART; cfg.base_cfg = baudrate/databits/stopbits/parity, cfg.open_mode (O_BLOCK), cfg.rx_buffer_size |
tal_uart_write(port, buf, len) | Send bytes; returns write size or <0 |
tal_uart_read(port, buf, len) | Read bytes; returns read size (>=0) or <0 |
tal_uart_get_rx_data_size(port) | Bytes available in RX buffer |
tal_uart_rx_reg_irq_cb(port, cb) | Register RX interrupt callback |
tal_uart_deinit(port) | Release the UART |
open_mode flags: O_BLOCK, O_ASYNC_WRITE, O_FLOW_CTRL, O_TX_DMA, O_RX_DMA.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|
| Log output garbled / lost after init | Opened the log UART (TUYA_UART_NUM_0) as a user UART | Use a different instance; never reuse the console port |
| No data on non-default pins | Alternate pin group not muxed | tkl_io_pinmux_config(PIN, TUYA_UARTn_TX/RX) before tal_uart_init (default pins need no mux) |
tal_uart_read always returns 0 | Polling with nothing received, or wrong baud | Check wiring/baud; add tal_system_sleep in poll loop |
Reference example
SDK: examples/peripherals/uart/ (UART init, write, polled read/echo, UART2 pin mux).