Skip to content

概念先知道

  • SPI:四根线的全双工串行总线——MOSI(主机发)、MISO(主机收)、SCLK(时钟)、CS(片选)。
  • 主从模式:主机产生时钟并决定通信节奏;例程以主机模式运行,自测时把 MOSI 与 MISO 短接即可回环。
  • SPI 模式(MODE3):时钟极性(CPOL)与相位(CPHA)的组合,通信双方必须一致;例程用 MODE3、MSB 先发。

例程功能简介

本页对应博流官方 SDK 的 spi_poll 例程(examples/peripherals/spi/spi_poll),演示 SPI 主机查询模式收发:

  • SPI 主机模式,频率 1MHz、模式 3(SPI_MODE3)、MSB 先发、8 位数据宽度起步;
  • 依次以 8/16/24/32 位宽度发送递增数据,并读回数据校验(自测时把 MOSI 与 MISO 短接即可回环);
  • 每组宽度测试通过后打印 spi poll send xx-bit test success!
  • 同族例程examples/peripherals/spi/):spi_dma(DMA 收发)、spi_int(中断收发)、spi_flash(SPI Flash)。

操作步骤

1
硬件准备

例程以 SPI 主机模式运行(SPI_CASE_SELECT 为 0)。自测最简单的方式:把开发板 SPI0 的 MOSI 与 MISO 短接(共地),主机发出的数据会从 MISO 回读;有 SPI 从机设备时也可以接从机测试。

2
进入例程目录

在终端进入 SDK 的 SPI 例程目录(前提:已按快速开始(Linux)Windows搭建好环境):

cd examples/peripherals/spi/spi_poll
3
编译工程

执行编译命令。Ai-M62(BL616)与 Ai-M61(BL618)同属一个系列,统一填写引脚最少的 bl616 即可:

make CHIP=bl616 BOARD=bl616dk
4
烧录固件

用 USB 线连接开发板,按住 BOOT 键(Ai-M61-32S-Kit 为 IO2)不放、短按 EN/RST 进入下载模式,然后执行烧录(把串口号换成实际值):

make flash CHIP=bl616 COMX=/dev/ttyUSB0
5
运行验证

打开串口助手(波特率 2000000)。例程依次以 8/16/24/32 位数据宽度执行查询发送,自测回环时每组都会打印 data check successspi poll send xx-bit test success!

代码执行流程

例程从启动到运行的完整流程如下(图中的循环箭头表示反复执行):

例程调用的 API 介绍

bflb_spi_init(spi, config)

初始化 SPI 外设。struct bflb_spi_config_s 中:

  • freq:时钟频率,主机例程 1MHz
  • role:角色,SPI_ROLE_MASTER(主机)/ SPI_ROLE_SLAVE(从机)
  • mode:SPI 模式,SPI_MODE3
  • data_width:数据宽度,SPI_DATA_WIDTH_8BIT
  • bit_order:位序,SPI_BIT_MSB
  • byte_order:字节序,SPI_BYTE_LSB

参数

  • spi:SPI 设备句柄(bflb_device_get_by_name("spi0")
  • config:配置结构体指针

返回值:成功返回 0;失败返回负值错误码

bflb_spi_feature_control(spi, cmd, arg)

动态调整 SPI 特性,例程用 SPI_CMD_SET_DATA_WIDTH 切换数据宽度、SPI_CMD_SET_CS_INTERVAL 设置片选间隔。

参数

  • spi:SPI 设备句柄
  • cmd:命令,SPI_CMD_SET_DATA_WIDTH / SPI_CMD_SET_CS_INTERVAL
  • arg:命令参数,如 SPI_DATA_WIDTH_16BIT

返回值:成功返回 0;失败返回负值错误码

bflb_spi_poll_send(spi, data)

查询方式发送一个数据字,同时读回一个数据字(全双工)。

参数

  • spi:SPI 设备句柄
  • data:要发送的数据

返回值:读回的数据

bflb_spi_poll_exchange(spi, tx, rx, len)

批量收发:把 tx 缓冲区数据发送出去,同时把接收数据写入 rx 缓冲区。

参数

  • spi:SPI 设备句柄
  • tx:发送缓冲区
  • rx:接收缓冲区
  • len:字节数

返回值:成功返回 0;失败返回负值错误码

完整代码

以下为 spi_poll/main.c 完整源码,与官方示例(examples/peripherals/spi/spi_poll)一致;点灯引脚已适配 Ai-M61/62-32S-Kit 板载 RGB 灯,默认折叠,点击展开:

📜 点击展开 spi_poll/main.c 完整代码
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

自测时请确认 MOSI 与 MISO 已短接且与开发板共地;接从机时确认从机工作在相同 SPI 模式(例程为 MODE3)与频率。

想改数据宽度或模式

修改 SPI_CASE_SELECT 可以切换主机/从机角色;data_widthmodespi_cfg 中修改,测试函数里的宽度由 bflb_spi_feature_control 动态切换。

例程没有输出

确认波特率 2000000 且已复位运行;SPI0 引脚接线不影响串口输出,若完全无打印先检查烧录与复位。

相关最佳实践

遇到问题?

如有其他问题,请到统一的提问与讨论区:Ai-Thinker Discussions

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