Skip to content

Concepts First

  • SPI: a four-wire full-duplex bus — MOSI (master out), MISO (master in), SCLK (clock), CS (chip select).
  • Master/slave: the master generates the clock and controls the pace; the example runs as master, and shorting MOSI to MISO creates a loopback self-test.
  • SPI mode (MODE3): a combination of clock polarity (CPOL) and phase (CPHA); both ends must agree. The example uses MODE3, MSB first.

Example Overview

This page is based on the spi_poll example in the official Bouffalo SDK (examples/peripherals/spi/spi_poll), which demonstrates SPI master polling send/receive:

  • SPI master mode, 1 MHz, mode 3 (SPI_MODE3), MSB first, starting with 8-bit data width;
  • Sends incrementing data at 8/16/24/32-bit widths and reads the returned data for verification (short MOSI to MISO for a loopback self-test);
  • Each width test prints spi poll send xx-bit test success! when it passes.
  • Sibling examples (examples/peripherals/spi/): spi_dma (DMA), spi_int (interrupt), spi_flash (SPI Flash).

Operation Steps

1
Prepare the Hardware

The example runs in SPI master mode (SPI_CASE_SELECT is 0). The easiest self-test: short the SPI0 MOSI to MISO (sharing ground) so the master reads back its own data; you can also connect a real SPI slave device.

2
Enter the Example Directory

Open a terminal and enter the SPI example directory (prerequisite: set up the environment as in Quick Start (Linux) or Windows):

cd examples/peripherals/spi/spi_poll
3
Build the Project

Run the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:

make CHIP=bl616 BOARD=bl616dk
4
Flash the Firmware

Connect the board with a USB cable, hold the BOOT button (IO2 on the Ai-M61-32S-Kit), briefly press EN/RST to enter download mode, then flash (replace the serial port with the one on your computer):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
Run and Verify

Open a serial tool (baud rate 2000000). The example runs poll tests at 8/16/24/32-bit data widths; with the loopback wiring, each group prints data check success and spi poll send xx-bit test success!.

Code Execution Flow

The complete execution flow from startup to running is shown below (loop arrows mean repeated execution):

APIs Used by the Example

bflb_spi_init(spi, config)

Initializes the SPI peripheral. In struct bflb_spi_config_s:

  • freq: clock frequency, 1 MHz in the master example
  • role: SPI_ROLE_MASTER / SPI_ROLE_SLAVE
  • mode: SPI mode, SPI_MODE3
  • data_width: data width, SPI_DATA_WIDTH_8BIT
  • bit_order: bit order, SPI_BIT_MSB
  • byte_order: byte order, SPI_BYTE_LSB

Parameters:

  • spi: SPI device handle (bflb_device_get_by_name("spi0"))
  • config: pointer to the config struct

Return: 0 on success; negative error code on failure

bflb_spi_feature_control(spi, cmd, arg)

Dynamically adjusts SPI features; the example uses SPI_CMD_SET_DATA_WIDTH to switch data width and SPI_CMD_SET_CS_INTERVAL for the chip-select interval.

Parameters:

  • spi: SPI device handle
  • cmd: command, SPI_CMD_SET_DATA_WIDTH / SPI_CMD_SET_CS_INTERVAL
  • arg: command argument, e.g. SPI_DATA_WIDTH_16BIT

Return: 0 on success; negative error code on failure

bflb_spi_poll_send(spi, data)

Sends one data word in polling mode and reads one back (full duplex).

Parameters:

  • spi: SPI device handle
  • data: data to send

Return: the data read back

bflb_spi_poll_exchange(spi, tx, rx, len)

Batch exchange: sends the tx buffer and writes received data into the rx buffer.

Parameters:

  • spi: SPI device handle
  • tx: TX buffer
  • rx: RX buffer
  • len: byte count

Return: 0 on success; negative error code on failure

Complete Code

The complete source below matches the effect described on this page. It is based on the official example (examples/peripherals/spi/spi_poll); the LED pins are adapted to the Ai-M61/62-32S-Kit onboard RGB LED. Collapsed by default, click to expand:

📜 Click to expand spi_poll/main.c full code
c
#include "bflb_mtimer.h"
#include "bflb_spi.h"
#include "board.h"

#define SPI_MASTER_CASE 0
#define SPI_SLAVE_CASE  1

#define SPI_CASE_SELECT SPI_MASTER_CASE

#define BUFF_LEN (8 * 1024)

uint32_t tx_buff[BUFF_LEN / 4];
uint32_t rx_buff[BUFF_LEN / 4];

struct bflb_device_s *spi0;

/* poll test func */
int bflb_spi_poll_test(uint32_t data_width)
{
    uint32_t data_mask;
    uint32_t *p_tx = (uint32_t *)tx_buff;
    uint32_t *p_rx = (uint32_t *)rx_buff;

    switch (data_width) {
        case SPI_DATA_WIDTH_8BIT:
            data_mask = 0x000000FF;
            break;
        case SPI_DATA_WIDTH_16BIT:
            data_mask = 0x0000FFFF;
            break;
        case SPI_DATA_WIDTH_24BIT:
            data_mask = 0x00FFFFFF;
            break;
        case SPI_DATA_WIDTH_32BIT:
            data_mask = 0xFFFFFFFF;
            break;
        default:
            printf("data_width err\r\n");
            return -1;
            break;
    }

    /* data init */
    for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
        p_tx[i] = i;
        p_rx[i] = 0;
    }

    /* set data width */
    bflb_spi_feature_control(spi0, SPI_CMD_SET_DATA_WIDTH, data_width);

    /* send data */
    for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
        p_rx[i] = bflb_spi_poll_send(spi0, p_tx[i]);
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
        bflb_mtimer_delay_us(10); /* delay for slave device prepare ok */
#endif
    }

    /* check data */
    for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
        if (p_rx[i] != (p_tx[i] & data_mask)) {
            printf("data error, data[%d]:tx 0x%08lX, rx 0x%08lX\r\n", i, p_tx[i], p_rx[i]);
            return -1;
        }
    }
    printf("data check success\r\n");

    return 0;
}

/* poll_exchange test func */
int bflb_spi_poll_exchange_test(uint32_t data_width)
{
    void *p_tx = (uint32_t *)tx_buff;
    void *p_rx = (uint32_t *)rx_buff;

    /* data init */
    switch (data_width) {
        case SPI_DATA_WIDTH_8BIT:
            for (uint16_t i = 0; i < BUFF_LEN; i++) {
                ((uint8_t *)p_tx)[i] = i;
                ((uint8_t *)p_rx)[i] = 0;
            }
            break;
        case SPI_DATA_WIDTH_16BIT:
            for (uint16_t i = 0; i < BUFF_LEN / 2; i++) {
                ((uint16_t *)p_tx)[i] = i << 0;
                ((uint16_t *)p_rx)[i] = 0;
            }
            break;
        case SPI_DATA_WIDTH_24BIT:
            for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
                ((uint32_t *)p_tx)[i] = ((i << 0) | i) & 0x00FFFFFF;
                ((uint32_t *)p_rx)[i] = 0;
            }
            break;
        case SPI_DATA_WIDTH_32BIT:
            for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
                ((uint32_t *)p_tx)[i] = (i << 0) | i;
                ((uint32_t *)p_rx)[i] = 0;
            }
            break;
        default:
            return -1;
            break;
    }

    /* set data width */
    bflb_spi_feature_control(spi0, SPI_CMD_SET_DATA_WIDTH, data_width);

    /* send data */
    printf("spi poll exchange width %ld, len %d\r\n", data_width, BUFF_LEN);
    bflb_spi_poll_exchange(spi0, p_tx, p_rx, BUFF_LEN);

    /* check data */
    for (uint16_t i = 0; i < BUFF_LEN / 4; i++) {
        if (((uint32_t *)p_rx)[i] != ((uint32_t *)p_tx)[i]) {
            printf("data error, data[%d]:tx 0x%08lX, rx 0x%08lX\r\n", i, ((uint32_t *)p_tx)[i], ((uint32_t *)p_rx)[i]);
            return -1;
        }
    }
    printf("data check success\r\n");

    return 0;
}

/* main */
int main(void)
{
    board_init();
    board_spi0_gpio_init();

    struct bflb_spi_config_s spi_cfg = {
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
        .freq = 1 * 1000 * 1000,
        .role = SPI_ROLE_MASTER,
#else
        .freq = 32 * 1000 * 1000,
        .role = SPI_ROLE_SLAVE,
#endif
        .mode = SPI_MODE3,
        .data_width = SPI_DATA_WIDTH_8BIT,
        .bit_order = SPI_BIT_MSB,
        .byte_order = SPI_BYTE_LSB,
        .tx_fifo_threshold = 0,
        .rx_fifo_threshold = 0,
    };

    spi0 = bflb_device_get_by_name("spi0");
    bflb_spi_init(spi0, &spi_cfg);

    bflb_spi_feature_control(spi0, SPI_CMD_SET_CS_INTERVAL, 0);

    printf("\r\n************** spi poll send 8-bit test **************\r\n");
    if (bflb_spi_poll_test(SPI_DATA_WIDTH_8BIT) < 0) {
        printf("poll send 8-bit test error!!!\r\n");
    } else {
        printf("poll send 8-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll send 16-bit test **************\r\n");
    if (bflb_spi_poll_test(SPI_DATA_WIDTH_16BIT) < 0) {
        printf("poll send 16-bit test error!!!\r\n");
    } else {
        printf("poll send 16-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll send 24-bit test **************\r\n");
    if (bflb_spi_poll_test(SPI_DATA_WIDTH_24BIT) < 0) {
        printf("poll send 24-bit test error!!!\r\n");
    } else {
        printf("poll send 24-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll send 32-bit test **************\r\n");
    if (bflb_spi_poll_test(SPI_DATA_WIDTH_32BIT) < 0) {
        printf("poll send 32-bit test error!!!\r\n");
    } else {
        printf("poll send 32-bit test success!\r\n");
    }

#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif

    printf("\r\n************** spi poll exchange 8-bit test **************\r\n");

    if (bflb_spi_poll_exchange_test(SPI_DATA_WIDTH_8BIT) < 0) {
        printf("poll exchange 8-bit test error!!!\r\n");
    } else {
        printf("poll exchange 8-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll exchange 16-bit test **************\r\n");
    if (bflb_spi_poll_exchange_test(SPI_DATA_WIDTH_16BIT) < 0) {
        printf("poll exchange 16-bit test error!!!\r\n");
    } else {
        printf("poll exchange 16-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll exchange 24-bit test **************\r\n");
    if (bflb_spi_poll_exchange_test(SPI_DATA_WIDTH_24BIT) < 0) {
        printf("poll exchange 24-bit test error!!!\r\n");
    } else {
        printf("poll exchange 24-bit test success!\r\n");
    }
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll exchange 32-bit test **************\r\n");
    if (bflb_spi_poll_exchange_test(SPI_DATA_WIDTH_32BIT) < 0) {
        printf("poll exchange 32-bit test error!!!\r\n");
    } else {
        printf("poll exchange 32-bit test success!\r\n");
    }

#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif

    printf("\r\n************** spi poll exchange only send 32-bit test **************\r\n");
    bflb_spi_poll_exchange(spi0, tx_buff, NULL, BUFF_LEN);
    printf("poll exchange 32-bit only send test end!\r\n");
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll exchange only receive 32-bit test **************\r\n");
    bflb_spi_poll_exchange(spi0, NULL, rx_buff, BUFF_LEN);
    printf("poll exchange 32-bit only receive test end!\r\n");
#if (SPI_CASE_SELECT == SPI_MASTER_CASE)
    bflb_mtimer_delay_ms(1000); /* delay for slave device prepare ok */
#endif
    printf("\r\n************** spi poll exchange spare time clock 32-bit test **************\r\n");
    bflb_spi_poll_exchange(spi0, NULL, NULL, BUFF_LEN);
    printf("poll exchange 32-bit spare time clock test end!\r\n");

    printf("\r\nspi test end\r\n");

    while (1) {
    }
}

FAQ

data check fail

For a self-test, confirm MOSI and MISO are shorted and share ground; when using a slave, make sure it uses the same SPI mode (MODE3) and a compatible frequency.

I want to change the data width or mode

Change SPI_CASE_SELECT to switch between master and slave roles; data_width and mode are in spi_cfg, while the width inside the test functions is switched dynamically with bflb_spi_feature_control.

No output at all

Set the baud rate to 2000000 and make sure the board restarted after flashing. The SPI pin wiring does not affect the serial output, so if there is no print at all, check flashing and reset first.

Have questions?

For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

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