<alp/peripheral.h> — Core Buses
The most-used header in the SDK. <alp/peripheral.h> provides handle-based wrappers for the four core bus classes: GPIO, I²C, SPI, and UART.
Header
#include <alp/peripheral.h>
#include <alp/e1m_pinout.h> // for E1M_* instance IDs
GPIO
alp_gpio_t *led = alp_gpio_open(&(alp_gpio_config_t){
.pin_id = E1M_GPIO_IO1,
.direction = ALP_GPIO_DIR_OUTPUT,
});
if (led == NULL) {
int err = alp_last_error();
return err;
}
alp_gpio_write(led, 1); // drive high
alp_gpio_write(led, 0); // drive low
bool value;
alp_gpio_read(led, &value);
alp_gpio_close(led);
Input with pull-up + IRQ
alp_gpio_t *btn = alp_gpio_open(&(alp_gpio_config_t){
.pin_id = E1M_GPIO_IO0,
.direction = ALP_GPIO_DIR_INPUT,
.pull = ALP_GPIO_PULL_UP,
});
void on_press(alp_gpio_t *pin, void *user) { /* ... */ }
alp_gpio_set_irq(btn, ALP_GPIO_IRQ_FALLING_EDGE, on_press, NULL);
alp_gpio_enable_irq(btn);
Config struct
| Field | Type | Notes |
|---|---|---|
pin_id | alp_pin_id_t | Use E1M_GPIO_IO<N> from <alp/e1m_pinout.h>. |
direction | alp_gpio_dir_t | INPUT / OUTPUT. |
pull | alp_gpio_pull_t | NONE / UP / DOWN. |
drive | alp_gpio_drive_t | PUSH_PULL / OPEN_DRAIN. |
I²C
alp_i2c_t *bus = alp_i2c_open(&(alp_i2c_config_t){
.bus_id = E1M_I2C0,
.bitrate_hz = 400000u, // 100k / 400k / 1M / 3.4M (per-SoC)
});
uint8_t buf[2] = { 0x00, 0x42 };
alp_i2c_write(bus, /* addr */ 0x50, buf, sizeof(buf));
uint8_t reg = 0x00;
uint8_t rx[16];
alp_i2c_write_read(bus, 0x50, ®, 1, rx, sizeof(rx));
alp_i2c_close(bus);
Config struct
| Field | Type | Notes |
|---|---|---|
bus_id | alp_bus_id_t | E1M_I2C0 … E1M_I2C<N>. |
bitrate_hz | uint32_t | Bus speed. Capped by <alp/soc_caps.h> at _open(). |
address_mode | alp_i2c_addr_t | 7BIT / 10BIT. |
Common operations
| Function | Description |
|---|---|
alp_i2c_write(bus, a, buf, n) | Write n bytes. |
alp_i2c_read(bus, a, buf, n) | Read n bytes. |
alp_i2c_write_read(bus, a, tx, n, rx, m) | Repeated-start: write n, read m. |
alp_i2c_scan(bus, addrs, max, *count) | ACK-probe and fill addrs[]. |
SPI
alp_spi_t *spi = alp_spi_open(&(alp_spi_config_t){
.bus_id = E1M_SPI1,
.freq_hz = 8000000u,
.mode = ALP_SPI_MODE_0,
.bits_per_word = 8,
.cs_pin_id = E1M_GPIO_IO5,
});
uint8_t tx[4] = { 0xAA, 0x55, 0xC3, 0x3C };
uint8_t rx[4];
alp_spi_transfer(spi, tx, rx, sizeof(tx));
alp_spi_close(spi);
Config struct
| Field | Type | Notes |
|---|---|---|
bus_id | alp_bus_id_t | E1M_SPI0 … E1M_SPI<N>. |
freq_hz | uint32_t | SCLK frequency. |
mode | alp_spi_mode_t | MODE_0 / MODE_1 / MODE_2 / MODE_3. |
bits_per_word | uint8_t | Typically 8; some SoCs support 16/32. |
cs_pin_id | alp_pin_id_t | Per-transfer chip-select pin. |
UART
alp_uart_t *uart = alp_uart_open(&(alp_uart_config_t){
.bus_id = E1M_UART0,
.baudrate = 115200u,
.data_bits = 8,
.stop_bits = ALP_UART_STOP_1,
.parity = ALP_UART_PARITY_NONE,
.flow_control= ALP_UART_FLOW_NONE,
});
const char *msg = "hello\n";
alp_uart_write(uart, (const uint8_t *)msg, strlen(msg));
uint8_t buf[64];
size_t got;
alp_uart_read(uart, buf, sizeof(buf), &got);
alp_uart_close(uart);
RX ring buffer (interrupt-driven)
When the consumer can't keep up, enable a caller-supplied ring buffer:
#include <alp/peripheral.h>
static uint8_t rx_ring_storage[256];
alp_uart_rx_ringbuf_t ring;
alp_uart_rx_ringbuf_init(&ring, rx_ring_storage, sizeof(rx_ring_storage));
alp_uart_rx_ringbuf_attach(uart, &ring);
// Later, pull what's there without polling:
size_t got = alp_uart_rx_ringbuf_pop(&ring, buf, sizeof(buf));
CONFIG_ALP_SDK_UART_RX_RINGBUF=y enables the ring path. See the uart-rx-ringbuf example.
Capability validation
Every _open() cross-checks the requested config against <alp/soc_caps.h>. Example failures:
| Config | Result on a 12-bit SoC |
|---|---|
alp_adc_open(.resolution_bits = 16) | NULL + alp_last_error() == ALP_ERR_OUT_OF_RANGE |
alp_i2c_open(.bitrate_hz = 5_000_000) | NULL + OUT_OF_RANGE (SoC max 3.4M) |
alp_uart_open(.bus_id = E1M_UART9) | NULL + NOSUPPORT (instance not routed) |
E1M portability
Instance IDs come from <alp/e1m_pinout.h> and are portable across every conformant SoM. The companion E1M_<CLASS>_COUNT macros tell you how many instances the E1M standard guarantees:
for (size_t i = 0; i < E1M_I2C_COUNT; i++) {
alp_i2c_t *bus = alp_i2c_open(&(alp_i2c_config_t){
.bus_id = E1M_I2C0 + i,
.bitrate_hz = 100000u,
});
/* ... */
}
Apps that stay within the portable bound work on every E1M SoM, present and future.
See also
- Quick start — concrete walk-through
- Pinout — signal classes per pad
<alp/pwm.h>— PWM channels<alp/adc.h>— analog sampling- Examples: gpio-button-led
- Examples: i2c-scanner
Questions about this page? Discuss in Community Forum