Skip to content

Contributed by bzhou830, curated by Ai-Thinker

This article first introduces the concept of the LHAL library, then uses the GPIO peripheral of the Xiao An Pai, and finally uses a Raspberry Pi Pico as a logic analyzer to sample and verify the GPIO output.

1. LHAL Library Peripherals

Bouffalo's LHAL library provides a unified wrapper for peripheral drivers. The table below is excerpted from the SDK documentation and lists the BL616 chip peripherals used by the Xiao An Pai that are supported in the LHAL library. Here, √ means supported, × means not supported, and - means the chip does not have that peripheral.

parameterBL616/BL618
ADC
CAM×
CKS
DAC
DMA
EFUSE
EMAC
FLASH
GPIO
I2C
IR
MJPEG
PWM_v1-
PWM_v2
RTC
SEC_AES
SEC_SHA
SEC_TRNG
SEC_PKA
SPI
TIMER
UART
USB_v1-
USB_v2
WDG

All the peripherals in the table above are configured through the same struct:

cpp
struct bflb_device_s {
    const char *name;    // peripheral name
    uint32_t reg_base;   // peripheral register base address
    uint8_t irq_num;     // peripheral interrupt number
    uint8_t idx;         // peripheral id, e.g. UART0、UART1
    uint8_t sub_idx;     // peripheral sub id, e.g. DMA0_CH0、DMA0_CH1
    uint8_t dev_type;    // peripheral type
    void *user_data;     // user data
};

Alternatively, the peripheral object provides two methods:

Operation Steps

1
Getting the device by peripheral name

Getting the device by peripheral name

struct bflb_device_s *bflb_device_get_by_name(const char *name)
2
Getting the device by peripheral type and ID

Getting the device by peripheral type and ID

struct bflb_device_s *bflb_device_get_by_id(uint8_t type, uint8_t idx)

The implementation of these two functions can be found in aithinker_Ai-M6X_SDK\drivers\lhal\config\bl616\device_table.c, as shown in the figure below: image-20231013091526370.png

2. GPIO Configuration

With the basic understanding above, it's clear that to use the GPIO peripheral we first need to get the GPIO device, and then initialize it with the device we got. The init function provided in the SDK is defined as follows:

cpp
void bflb_gpio_init(struct bflb_device_s *dev, uint8_t pin, uint32_t cfgset);
  • The first parameter is the GPIO device obtained via get;
  • The second parameter is the port number corresponding to the hardware;
  • The third parameter sets this pin to a specific mode and function (including gpio mode, gpio function, gpio pupd, gpio smt, gpio drive). These settings can be combined with bitwise OR. The following are the setting parameters for each field, excerpted from the SDK.
cpp
// gpio mode
#define GPIO_INPUT (0 << GPIO_MODE_SHIFT) /* Input Enable */
#define GPIO_OUTPUT (1 << GPIO_MODE_SHIFT) /* Output Enable */
#define GPIO_ANALOG (2 << GPIO_MODE_SHIFT) /* Analog Enable */
#define GPIO_ALTERNATE (3 << GPIO_MODE_SHIFT) /* Alternate Enable */
// gpio pupd
#define GPIO_FLOAT (0 << GPIO_PUPD_SHIFT) /* No pull-up, pull-down */
#define GPIO_PULLUP (1 << GPIO_PUPD_SHIFT) /* Pull-up */
#define GPIO_PULLDOWN (2 << GPIO_PUPD_SHIFT) /* Pull-down */
// gpio smt
#define GPIO_SMT_DIS (0 << GPIO_SMT_SHIFT)
#define GPIO_SMT_EN (1 << GPIO_SMT_SHIFT)
// gpio drive
#define GPIO_DRV_0 (0 << GPIO_DRV_SHIFT)
#define GPIO_DRV_1 (1 << GPIO_DRV_SHIFT)
#define GPIO_DRV_2 (2 << GPIO_DRV_SHIFT)
#define GPIO_DRV_3 (3 << GPIO_DRV_SHIFT)
// gpio init trig mode
#define GPIO_INT_TRIG_MODE_SYNC_FALLING_EDGE 0
#define GPIO_INT_TRIG_MODE_SYNC_RISING_EDGE 1
#define GPIO_INT_TRIG_MODE_SYNC_LOW_LEVEL 2
#define GPIO_INT_TRIG_MODE_SYNC_HIGH_LEVEL 3
#define GPIO_INT_TRIG_MODE_SYNC_FALLING_RISING_EDGE 4
#define GPIO_INT_TRIG_MODE_ASYNC_FALLING_EDGE 8
#define GPIO_INT_TRIG_MODE_ASYNC_RISING_EDGE 9
#define GPIO_INT_TRIG_MODE_ASYNC_LOW_LEVEL 10
#define GPIO_INT_TRIG_MODE_ASYNC_HIGH_LEVEL 11
// gpio uart function
#define GPIO_UART_FUNC_UART0_RTS 0
#define GPIO_UART_FUNC_UART0_CTS 1
#define GPIO_UART_FUNC_UART0_TX 2
#define GPIO_UART_FUNC_UART0_RX 3
#define GPIO_UART_FUNC_UART1_RTS 4
#define GPIO_UART_FUNC_UART1_CTS 5
#define GPIO_UART_FUNC_UART1_TX 6
#define GPIO_UART_FUNC_UART1_RX 7

Let's also take a look at a few functions related to initialization:

cpp
void bflb_gpio_deinit(struct bflb_device_s *dev, uint8_t pin); //de-initialize, back to the state before init. io set to floating
void bflb_gpio_set(struct bflb_device_s *dev, uint8_t pin); //set high level
void bflb_gpio_reset(struct bflb_device_s *dev, uint8_t pin); //set low level
bool bflb_gpio_read(struct bflb_device_s *dev, uint8_t pin); //read io level

3. GPIO Configuration Example

Now let's pick an IO and configure it to alternately output high and low levels. Then use a logic analyzer to verify that our program works.

Open the Xiao An Pai schematic and see whether IO0 is brought out.

image-20231013094019743.png

From the figure above, IO0 corresponds to the network labeled I2C_SCL_TOP in the schematic. Searching directly in the schematic file, we find that connector J9 connects to I2C_SCL_TOP.

image-20231013094124445.png

Where is that on the board? Let's open the Xiao An Pai PCB file; for easier viewing, remove the copper pour first. Now we can see where I2C_SCL_TOP is on the board.

image-20231013094559600.png

Luckily, this interface can be connected. So we'll configure IO0 directly.

cpp
#include "bflb_gpio.h"
#include "bflb_mtimer.h"
#include "board.h"
struct bflb_device_s *gpio;
#define gpio_test GPIO_PIN_0
int main(void)
{
    board_init();
    // get gpio device
    gpio = bflb_device_get_by_name("gpio");
    // configure io 0 as pull-up output
    bflb_gpio_init(gpio, gpio_test, GPIO_OUTPUT | GPIO_PULLUP);
    while (1) {
        bflb_gpio_set(gpio, gpio_test);   // set high
        bflb_mtimer_delay_ms(100);        // delay 100ms
        bflb_gpio_reset(gpio, gpio_test); // set low
        bflb_mtimer_delay_ms(100);
    }
}

Compile and flash the code above to the Xiao An Pai. Next, use the logic analyzer made with an RP2040 to measure the IO0 output.

4. Verifying with a Logic Analyzer

Flash the logic analyzer firmware into the Raspberry Pi Pico and plug it into the computer. Configure PulseView (this process won't be detailed here).

Connect the Pico's GND and GP2 to the Xiao An Pai's GND and I2C_SCL_TOP respectively.

image-20231013101450021.png

Run the logic analyzer and capture the following waveform:

image-20231013101217814.png

From the waveform, the level changes are exactly 100ms apart, fully consistent with our program.

So now we understand the GPIO feature.

Have questions?

For other questions, please visit the unified discussion area: Ai-Thinker Discussions

Released under the MIT License. Build Time 2026-09-11 14:52:23