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
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.
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_pollRun the build command. The Ai-M62 (BL616) and Ai-M61 (BL618) belong to the same series, so both use bl616:
make CHIP=bl616 BOARD=bl616dkConnect 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/ttyUSB0Open 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 MHzin the master examplerole:SPI_ROLE_MASTER/SPI_ROLE_SLAVEmode: SPI mode,SPI_MODE3data_width: data width,SPI_DATA_WIDTH_8BITbit_order: bit order,SPI_BIT_MSBbyte_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 handlecmd: command,SPI_CMD_SET_DATA_WIDTH/SPI_CMD_SET_CS_INTERVALarg: 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 handledata: 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 handletx: TX bufferrx: RX bufferlen: 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
#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.
Related Best Practices
Have questions?
For any other questions, visit the unified Q&A and discussion board: Ai-Thinker Discussions

