# Tuyaopen/onchip Gpio

> On-chip GPIO for TuyaOpen using the tkl_gpio API: configure a pin as output/input, read/write level, and register edge interrupts. GPIO、引脚、电平、输入输出、中断、tkl_gpio、拉高拉低、读引脚。

- Skill: `tuya/tuyaopen-onchip-gpio` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tuya/tuyaopen-onchip-gpio`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tuya/tuyaopen-onchip-gpio/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- Author: tuya (https://skillmd.com/u/tuya)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tuya/tuyaopen-onchip-gpio

---


# TuyaOpen On-Chip GPIO

## ⛔ STOP if the load is an LED, button, or other catalogued device type

This skill is for **raw digital pins only** (an enable line, a relay, a generic
logic signal). If what's on the pin is an **LED** (incl. an external/extra GPIO
LED) → use `peripheral-led`; a **button/key** → `peripheral-button`. Those go
through the `tdl_*` component, not `tkl_gpio_write`. Do **not** drive an LED with
`tkl_gpio_write` here just because the user named a GPIO — "GPIO47 high = on LED"
is still an LED. The `tkl_gpio` example below is for non-LED loads.

## On-chip — no Kconfig, no TDD

- Provided by the platform. **Do NOT** write `CONFIG_ENABLE_GPIO` — platform-selected. No `app_default.config` change needed.
- No `board_register_hardware()` step. Call `tkl_gpio_*` directly with `TUYA_GPIO_NUM_<n>`.
- Confirm **which GPIO** and its direction with the user; record it in `used-peripherals.json` as `onchip:gpio<n>` (see `hardware-vibe-coding`).
- **Do not reuse a pin already taken** by a confirmed peripheral — check the pin-occupancy summary in the hardware context first.

## Platform spec — read, don't hardcode

Valid pins/modes for THIS board are in `.tuyaopen/ide/platform.json` →
`peripherals.gpio.spec`: `pins` (usable GPIOs), `mode` (allowed pull/drive modes),
`irq.triggers` + `irq.pins` (which pins support edge IRQ). The enum names below are
SDK-wide, but the **pin set differs per platform**.

`pins` / `irq.pins` are **inclusive `[start,end]` range pairs** (multi-segment),
e.g. `[[0,55]]` = GPIO0–55, or `[[0,9],[12,55]]` = 0–9 and 12–55 (10–11 excluded).
Check the user's pin falls inside one of the ranges (and inside `irq.pins` for an
interrupt).

But `gpio.spec.pins` is the **SoC superset**. For a USER pin, pick only from
`board.json.expansionPins` (the pins actually broken out, each with its muxable
`functions` — a plain GPIO pin must list `GPIO`). If `expansionPins` is empty/absent,
the board is sealed — no free GPIO; tell the user.

## Output + input + IRQ

```c
#include "tal_api.h"
#include "tkl_gpio.h"

#define OUT_PIN   TUYA_GPIO_NUM_1
#define IN_PIN    TUYA_GPIO_NUM_6
#define IRQ_PIN   TUYA_GPIO_NUM_7

static void gpio_irq_cb(void *args) { /* keep short — runs in IRQ context */ }

void app_gpio_init(void)
{
    /* Output */
    TUYA_GPIO_BASE_CFG_T out_cfg = {
        .mode = TUYA_GPIO_PUSH_PULL, .direct = TUYA_GPIO_OUTPUT, .level = TUYA_GPIO_LEVEL_LOW };
    tkl_gpio_init(OUT_PIN, &out_cfg);
    tkl_gpio_write(OUT_PIN, TUYA_GPIO_LEVEL_HIGH);

    /* Input (pull-up) */
    TUYA_GPIO_BASE_CFG_T in_cfg = { .mode = TUYA_GPIO_PULLUP, .direct = TUYA_GPIO_INPUT };
    tkl_gpio_init(IN_PIN, &in_cfg);
    TUYA_GPIO_LEVEL_E lvl;
    tkl_gpio_read(IN_PIN, &lvl);

    /* Falling-edge interrupt */
    tkl_gpio_init(IRQ_PIN, &in_cfg);
    TUYA_GPIO_IRQ_T irq = { .mode = TUYA_GPIO_IRQ_FALL, .cb = gpio_irq_cb, .arg = NULL };
    tkl_gpio_irq_init(IRQ_PIN, &irq);
    tkl_gpio_irq_enable(IRQ_PIN);
}
```

## API Reference

| Function | Description |
|----------|-------------|
| `tkl_gpio_init(pin, &cfg)` | `cfg.mode` (`TUYA_GPIO_PUSH_PULL` / `TUYA_GPIO_PULLUP` / `TUYA_GPIO_PULLDOWN` / `TUYA_GPIO_HIGH_IMPEDANCE`), `cfg.direct` (`TUYA_GPIO_OUTPUT` / `TUYA_GPIO_INPUT`), `cfg.level` |
| `tkl_gpio_write(pin, level)` | `TUYA_GPIO_LEVEL_HIGH` / `TUYA_GPIO_LEVEL_LOW` |
| `tkl_gpio_read(pin, &level)` | Read current level |
| `tkl_gpio_irq_init(pin, &irq)` | `irq.mode` (`TUYA_GPIO_IRQ_RISE` / `_FALL` / `_LOW` / `_HIGH`), `irq.cb`, `irq.arg` |
| `tkl_gpio_irq_enable(pin)` / `tkl_gpio_irq_disable(pin)` | Enable/disable the IRQ |
| `tkl_gpio_deinit(pin)` | Release the pin |

## Troubleshooting

| Symptom | Likely cause | Fix |
|---------|--------------|-----|
| Output level doesn't change | Pin in input mode / wrong `direct` | Set `.direct = TUYA_GPIO_OUTPUT` |
| Floating input reads random | No pull configured | Use `TUYA_GPIO_PULLUP` / `TUYA_GPIO_PULLDOWN` |
| IRQ never fires | Forgot `tkl_gpio_irq_enable` | Enable after `tkl_gpio_irq_init` |
| Pin conflicts with a device | Reused a confirmed peripheral's GPIO | Pick a free pin per the pin-occupancy summary |

## Reference example

SDK: `examples/peripherals/gpio/` (output toggle, pull-up input read, falling-edge IRQ).

