> ## Documentation Index
> Fetch the complete documentation index at: https://launchpad.anxlabs.com/llms.txt
> Use this file to discover all available pages before exploring further.

# ANX SDK Communication APIs: UART, I2C, and SPI

> Send and receive data over UART, I2C, and SPI using the ANX SDK communication APIs. Includes configuration structs, read/write functions, and error handling.

The ANX SDK provides separate APIs for each serial communication protocol: UART for point-to-point serial communication with a host or another microcontroller, I2C for connecting multiple low-speed sensors and peripherals on a shared two-wire bus, and SPI for high-speed full-duplex data exchange with flash memory, displays, and other peripherals. Each protocol has its own header, configuration struct, and set of read/write functions — all returning `anx_err_t` for consistent error handling.

<Tabs>
  <Tab title="UART">
    ## UART — `anx/uart.h`

    UART is a point-to-point asynchronous serial protocol. Include the header to access the UART API:

    ```c theme={null}
    #include "anx/uart.h"
    ```

    ### Initialization

    Configure a UART port by populating an `anx_uart_config_t` struct and passing it to `anx_uart_init()`. Call `anx_uart_init()` once per port during application startup before any read or write operations.

    #### `anx_uart_config_t` members

    <ParamField path="port" type="anx_uart_port_t" required>
      The UART port to configure. Use `ANX_UART0` for the primary serial port or `ANX_UART1` for the secondary port.
    </ParamField>

    <ParamField path="baud" type="uint32_t" required>
      Baud rate in bits per second. Common values: `9600`, `19200`, `57600`, `115200`, `230400`, `921600`. Both ends of the connection must use the same baud rate.
    </ParamField>

    <ParamField path="data_bits" type="uint8_t" required>
      Number of data bits per frame. Typically `8`; `7` is supported for legacy protocols.
    </ParamField>

    <ParamField path="stop_bits" type="uint8_t" required>
      Number of stop bits. Use `1` for standard operation; `2` for compatibility with older hardware.
    </ParamField>

    <ParamField path="parity" type="anx_parity_t" required>
      Parity mode. Use `ANX_PARITY_NONE` for no parity, `ANX_PARITY_EVEN` for even parity, or `ANX_PARITY_ODD` for odd parity.
    </ParamField>

    <ResponseField name="anx_uart_init return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if any config member is out of range. `ANX_ERR_FAIL` if the hardware port is already in use.
    </ResponseField>

    ***

    ### `anx_uart_write()`

    Writes `len` bytes from `data` to the specified UART port. The function blocks until all bytes have been placed in the hardware transmit buffer or the operation times out.

    <ParamField path="port" type="anx_uart_port_t" required>
      The initialized UART port to write to (`ANX_UART0` or `ANX_UART1`).
    </ParamField>

    <ParamField path="data" type="const uint8_t *" required>
      Pointer to the byte array to transmit. Must not be NULL.
    </ParamField>

    <ParamField path="len" type="size_t" required>
      Number of bytes to transmit from `data`.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` when all bytes are sent. `ANX_ERR_TIMEOUT` if the transmit buffer does not drain within the hardware timeout. `ANX_ERR_INVALID_ARG` if `data` is NULL or `len` is zero.
    </ResponseField>

    ***

    ### `anx_uart_read()`

    Reads up to `len` bytes from the specified UART port into `buf`, waiting up to `timeout_ms` milliseconds for data to arrive.

    <ParamField path="port" type="anx_uart_port_t" required>
      The initialized UART port to read from (`ANX_UART0` or `ANX_UART1`).
    </ParamField>

    <ParamField path="buf" type="uint8_t *" required>
      Pointer to the buffer that receives the incoming data. Must be at least `len` bytes long.
    </ParamField>

    <ParamField path="len" type="size_t" required>
      Maximum number of bytes to read into `buf`.
    </ParamField>

    <ParamField path="timeout_ms" type="uint32_t" required>
      Maximum number of milliseconds to wait for data. Pass `0` to return immediately with whatever bytes are currently available in the receive buffer.
    </ParamField>

    <ResponseField name="return" type="int">
      The number of bytes actually read (0 to `len`). Returns `-1` on error (for example, if the port is not initialized).
    </ResponseField>

    ***

    ### Example: UART loopback test

    ```c uart_loopback.c theme={null}
    #include "anx/anx.h"
    #include "anx/uart.h"

    void app_main(void) {
        anx_uart_config_t cfg = {
            .port      = ANX_UART0,
            .baud      = 115200,
            .data_bits = 8,
            .stop_bits = 1,
            .parity    = ANX_PARITY_NONE,
        };
        anx_uart_init(&cfg);

        const char *msg = "Hello UART";
        anx_uart_write(ANX_UART0, (const uint8_t *)msg, strlen(msg));

        uint8_t buf[32];
        int n = anx_uart_read(ANX_UART0, buf, sizeof(buf), 1000);
        ANX_LOGI("UART", "Received %d bytes", n);
    }
    ```

    <Tip>
      Connect the TX and RX pins together on your dev module to run a loopback test without needing a second device. It is a fast way to verify your baud rate and framing settings before integrating a real peripheral.
    </Tip>
  </Tab>

  <Tab title="I2C">
    ## I2C — `anx/i2c.h`

    I2C is a two-wire, multi-device bus protocol. Include the header to access the I2C API:

    ```c theme={null}
    #include "anx/i2c.h"
    ```

    ### Initialization

    Configure an I2C bus by populating an `anx_i2c_config_t` struct and passing it to `anx_i2c_init()`. The ANX dev module exposes two independent I2C buses.

    #### `anx_i2c_config_t` members

    <ParamField path="bus" type="anx_i2c_bus_t" required>
      The I2C bus to configure. Use `ANX_I2C0` for the primary bus or `ANX_I2C1` for the secondary bus.
    </ParamField>

    <ParamField path="speed" type="anx_i2c_speed_t" required>
      Bus clock speed. Use `ANX_I2C_STANDARD` for 100 kHz (compatible with all I2C devices) or `ANX_I2C_FAST` for 400 kHz (requires all devices on the bus to support fast mode).
    </ParamField>

    <ResponseField name="anx_i2c_init return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if `speed` is not a valid value. `ANX_ERR_FAIL` if the bus is already initialized.
    </ResponseField>

    ***

    ### `anx_i2c_write()`

    Sends `len` bytes from `data` to the device at 7-bit address `addr` on the specified bus.

    <ParamField path="bus" type="anx_i2c_bus_t" required>
      The initialized I2C bus (`ANX_I2C0` or `ANX_I2C1`).
    </ParamField>

    <ParamField path="addr" type="uint8_t" required>
      The 7-bit I2C address of the target device (0x00–0x7F).
    </ParamField>

    <ParamField path="data" type="const uint8_t *" required>
      Pointer to the byte array to send. Must not be NULL.
    </ParamField>

    <ParamField path="len" type="size_t" required>
      Number of bytes to send.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_NOT_FOUND` if no device acknowledges the address. `ANX_ERR_TIMEOUT` if the bus does not become idle within the timeout window.
    </ResponseField>

    ***

    ### `anx_i2c_read()`

    Reads `len` bytes from the device at 7-bit address `addr` on the specified bus into `buf`.

    <ParamField path="bus" type="anx_i2c_bus_t" required>
      The initialized I2C bus (`ANX_I2C0` or `ANX_I2C1`).
    </ParamField>

    <ParamField path="addr" type="uint8_t" required>
      The 7-bit I2C address of the target device.
    </ParamField>

    <ParamField path="buf" type="uint8_t *" required>
      Pointer to the buffer that receives the read data. Must be at least `len` bytes long.
    </ParamField>

    <ParamField path="len" type="size_t" required>
      Number of bytes to read from the device.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_NOT_FOUND` if the device does not acknowledge. `ANX_ERR_TIMEOUT` if the read does not complete in time.
    </ResponseField>

    ***

    ### `anx_i2c_scan()`

    Scans the bus and logs the 7-bit address of every device that sends an acknowledgement. Use this during development to verify that all sensors are wired correctly and that their addresses match your expectations.

    <ParamField path="bus" type="anx_i2c_bus_t" required>
      The initialized I2C bus to scan (`ANX_I2C0` or `ANX_I2C1`).
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` after the scan completes, regardless of how many devices responded.
    </ResponseField>

    ***

    ### Example: register read pattern

    Most I2C sensors use a register-based interface: write the register address you want to read, then read back the value in a separate transaction.

    ```c i2c_register_read.c theme={null}
    #include "anx/anx.h"
    #include "anx/i2c.h"

    // Read WHO_AM_I register (0x0F) from a device at address 0x19
    void read_who_am_i(void) {
        uint8_t reg = 0x0F;
        uint8_t id;
        anx_i2c_write(ANX_I2C0, 0x19, &reg, 1);
        anx_i2c_read(ANX_I2C0, 0x19, &id, 1);
        ANX_LOGI("I2C", "WHO_AM_I = 0x%02X", id);
    }
    ```

    <Note>
      Some I2C devices require a repeated-start condition between the write and read phases. If `anx_i2c_read()` returns `ANX_ERR_NOT_FOUND` immediately after a successful write, check the device datasheet to see whether it supports repeated-start or requires a full stop/start sequence.
    </Note>
  </Tab>

  <Tab title="SPI">
    ## SPI — `anx/spi.h`

    SPI is a synchronous, full-duplex serial protocol well suited for high-speed peripherals. Include the header to access the SPI API:

    ```c theme={null}
    #include "anx/spi.h"
    ```

    ### Initialization

    Configure an SPI bus by populating an `anx_spi_config_t` struct and passing it to `anx_spi_init()`.

    #### `anx_spi_config_t` members

    <ParamField path="bus" type="anx_spi_bus_t" required>
      The SPI bus to configure. Use `ANX_SPI0` for the primary SPI bus.
    </ParamField>

    <ParamField path="speed" type="uint32_t" required>
      Clock frequency in Hz. The maximum supported speed is `40000000` (40 MHz). Start at a lower speed (4–8 MHz) when debugging and raise it once the connection is verified stable.
    </ParamField>

    <ParamField path="mode" type="anx_spi_mode_t" required>
      SPI clock polarity and phase. Choose from:

      * `ANX_SPI_MODE0` — CPOL=0, CPHA=0 (clock idles low, data sampled on rising edge)
      * `ANX_SPI_MODE1` — CPOL=0, CPHA=1 (clock idles low, data sampled on falling edge)
      * `ANX_SPI_MODE2` — CPOL=1, CPHA=0 (clock idles high, data sampled on falling edge)
      * `ANX_SPI_MODE3` — CPOL=1, CPHA=1 (clock idles high, data sampled on rising edge)
    </ParamField>

    <ResponseField name="anx_spi_init return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if `speed` exceeds 40 MHz or `mode` is not valid. `ANX_ERR_FAIL` if the bus is already initialized.
    </ResponseField>

    ***

    ### `anx_spi_transfer()`

    Performs a full-duplex SPI transfer, simultaneously clocking out `len` bytes from `tx` and clocking in `len` bytes into `rx`. Pass NULL for `tx` to receive only (MOSI held low), or NULL for `rx` to transmit only (MISO ignored).

    <ParamField path="bus" type="anx_spi_bus_t" required>
      The initialized SPI bus (`ANX_SPI0`).
    </ParamField>

    <ParamField path="tx" type="const uint8_t *">
      Pointer to the transmit buffer. Pass NULL to send zeroed bytes (receive-only operation).
    </ParamField>

    <ParamField path="rx" type="uint8_t *">
      Pointer to the receive buffer. Must be at least `len` bytes long if non-NULL. Pass NULL to discard incoming data (transmit-only operation).
    </ParamField>

    <ParamField path="len" type="size_t" required>
      Number of bytes to transfer. Both `tx` and `rx` (when non-NULL) must be at least this long.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` on success. `ANX_ERR_INVALID_ARG` if both `tx` and `rx` are NULL, or if `len` is zero. `ANX_ERR_TIMEOUT` if the transfer does not complete within the hardware timeout.
    </ResponseField>

    ***

    ### Chip-Select Control

    The ANX SPI API gives you manual control over the chip-select (CS) line so you can compose multi-transfer transactions without deselecting the device between calls.

    #### `anx_spi_cs_active()`

    Asserts the chip-select line (drives it low) to begin a transaction.

    <ParamField path="bus" type="anx_spi_bus_t" required>
      The initialized SPI bus whose CS line you want to assert.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` on success.
    </ResponseField>

    #### `anx_spi_cs_idle()`

    Deasserts the chip-select line (drives it high) to end a transaction.

    <ParamField path="bus" type="anx_spi_bus_t" required>
      The initialized SPI bus whose CS line you want to release.
    </ParamField>

    <ResponseField name="return" type="anx_err_t">
      `ANX_OK` on success.
    </ResponseField>

    ***

    ### Example: SPI flash read

    ```c spi_flash_read.c theme={null}
    #include "anx/anx.h"
    #include "anx/spi.h"

    // Read 4 bytes from SPI flash at address 0x001000
    void read_spi_flash(void) {
        anx_spi_config_t cfg = {
            .bus   = ANX_SPI0,
            .speed = 4000000, // 4 MHz
            .mode  = ANX_SPI_MODE0,
        };
        anx_spi_init(&cfg);

        uint8_t cmd[] = {0x03, 0x00, 0x10, 0x00}; // READ cmd + 24-bit addr
        uint8_t data[4];

        anx_spi_cs_active(ANX_SPI0);
        anx_spi_transfer(ANX_SPI0, cmd, NULL, 4);   // send command
        anx_spi_transfer(ANX_SPI0, NULL, data, 4);  // receive data
        anx_spi_cs_idle(ANX_SPI0);

        ANX_LOGI("SPI", "Data: %02X %02X %02X %02X",
                 data[0], data[1], data[2], data[3]);
    }
    ```

    <Warning>
      Always call `anx_spi_cs_idle()` after every transaction — even if `anx_spi_transfer()` returns an error. Leaving the CS line asserted prevents other devices that share the same SPI bus from communicating and may lock up the bus until the next reboot.
    </Warning>

    <Accordion title="Choosing the correct SPI mode">
      Consult your peripheral's datasheet and look for the CPOL and CPHA timing diagrams in the electrical characteristics section. Most SPI flash and display controllers use `ANX_SPI_MODE0`. If you see corrupted data at higher clock speeds, try lowering the frequency first before switching the mode — a marginal PCB trace is more commonly the culprit than a mode mismatch.
    </Accordion>
  </Tab>
</Tabs>
