CIU32 SPI — SPI1 主机·轮询收发

CIU32 CIU32F003_STDLib V1.0.3 SPI1 主机轮询全双工示例教学

SPI — SPI1 主机·轮询收发(SPI1_Master_Poll)

固件库: CIU32F003_STDLib V1.0.3 芯片: CIU32F003(Cortex-M0) 源文件: CIU32/CIU32F003_STDLib_V1.0.3/Projects/SPI/SPI1_Master_Poll/Source/


一、功能简介

本示例把 SPI1 配置为主机模式,采用查询(轮询)方式与从机进行全双工数据收发。 通信速率为 PCLK 的 32 分频,即 48MHz / 32 ≈ 1.5MHz。 主机先拉低片选 NSS 选中从机,随后发送 8 字节数据(0x01~0x08),每发送一字节同时读回一字节, 最后拉高 NSS 结束通信。

把 MOSI 与 MISO 用杜邦线短接(自环),可验证发送的数据能被原样收回。

二、硬件准备

  • CIU32F003 开发板
  • 若接真实从机:从机模块的 SCK/MOSI/MISO/NSS 与开发板对应相连,并共地
  • 若做自环测试:用杜邦线把 PA0(MOSI)PA1(MISO) 短接
  • 引脚映射(本示例):
SPI1 信号 引脚
NSS PB1
SCK PB0
MISO PA1
MOSI PA0

三、完整代码

3.1 主流程 main.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include "main.h"

uint8_t g_send_buffer[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
uint8_t g_recv_buffer[8] = {0};

int main(void)
{
    uint32_t index = 0;

    system_clock_config();   /* 系统时钟 RCH 48MHz */
    gpio_init();             /* 配置 SPI1 引脚为复用 */
    spi1_init();             /* 配置 SPI1 为主机 */

    std_spi_set_nss_output(SPI_NSS_OUTPUT_LOW);   /* ① 拉低 NSS,选中从机 */

    /* ② 逐字节全双工收发 */
    for (index = 0; index < 8; index++)
    {
        while (std_spi_get_flag(SPI_FLAG_TXFE) != SPI_FLAG_TXFE); /* 等待发送寄存器空 */
        std_spi_write_data(g_send_buffer[index]);                /* 发送 1 字节 */

        while (std_spi_get_flag(SPI_FLAG_RXFNE) != SPI_FLAG_RXFNE); /* 等待收到数据 */
        g_recv_buffer[index] = std_spi_read_data();              /* 读回 1 字节 */
    }

    /* ③ 等待发送完成、总线空闲,再拉高 NSS 结束通信 */
    while (!std_spi_get_flag(SPI_FLAG_TXFE));
    while (std_spi_get_flag(SPI_FLAG_BUSY));
    std_spi_set_nss_output(SPI_NSS_OUTPUT_HIGH);

    while (1) { }
}

3.2 引脚与 SPI 初始化 common.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/* GPIO 初始化:把引脚映射为 SPI1 复用功能 */
void gpio_init(void)
{
    std_gpio_init_t gpio_config = {0};

    std_rcc_gpio_clk_enable(RCC_PERIPH_CLK_GPIOA | RCC_PERIPH_CLK_GPIOB);

    /* PA0(MOSI)/PA1(MISO):复用功能 AF0_SPI1,推挽,无上下拉 */
    gpio_config.pin         = GPIO_PIN_0 | GPIO_PIN_1;
    gpio_config.mode        = GPIO_MODE_ALTERNATE;
    gpio_config.pull        = GPIO_NOPULL;
    gpio_config.output_type = GPIO_OUTPUT_PUSHPULL;
    gpio_config.alternate   = GPIO_AF0_SPI1;
    std_gpio_init(GPIOA, &gpio_config);

    /* PB0(SCK)/PB1(NSS):复用功能,上拉(NSS 空闲保持高电平) */
    gpio_config.pin   = GPIO_PIN_0 | GPIO_PIN_1;
    gpio_config.pull  = GPIO_PULLUP;
    std_gpio_init(GPIOB, &gpio_config);
}

/* SPI1 初始化为主机 */
void spi1_init(void)
{
    std_spi_init_t spi_config = {0};

    std_rcc_apb2_clk_enable(RCC_PERIPH_CLK_SPI1);  /* 使能 SPI1 时钟 */
    std_spi_disable();                             /* 配置前先禁止 SPI */

    spi_config.mode                = SPI_MODE_MASTER;      /* 主机模式 */
    spi_config.baud_rate_prescaler = SPI_BAUD_PCLKDIV_32; /* 波特率 = PCLK/32 = 1.5MHz */
    spi_config.clk_polarity        = SPI_POLARITY_HIGH;   /* CPOL = 1 */
    spi_config.clk_phase           = SPI_PHASE_1EDGE;     /* CPHA = 0(即第 1 个边沿采样) */
    spi_config.bitorder            = SPI_FIRSTBIT_MSB;    /* 高位先发 */
    std_spi_init(&spi_config);

    std_spi_nss_output_enable();   /* NSS 由硬件自动作为输出 */
    std_spi_enable();              /* 使能 SPI1 */
}

四、代码分段讲解

① 片选 NSS SPI 没有地址,靠 NSS 选片。主机开始通信前拉低 NSS,告诉从机“现在和你说话”; 结束后拉高,释放总线。本例使用硬件 NSS 输出(std_spi_nss_output_enable())。

② 全双工收发 SPI 每发送 1 位的同时也接收 1 位,所以“发送”和“接收”是同一个过程的两面:

  • 先等 TXFE(发送 FIFO 空)再写数据;
  • 再等 RXFNE(接收 FIFO 非空)读出刚收到的数据。 循环 8 次即完成 8 字节交换。

③ 收尾 发完所有数据后,必须等 TXFE(发送完毕)且 BUSY 清零(移位完成), 才能拉高 NSS,否则最后一字节可能没发完就被结束了。

④ 模式匹配 主机与从机的 CPOL / CPHA 必须一致。本例 CPOL=HIGH、CPHA=1EDGE(即 SPI 模式 2), 对接从机时要确认其支持该模式。

五、实验现象

  • 自环测试时:g_recv_buffer 应收回 {0x01,0x02,...,0x08},与发送缓冲一致;
  • 接真实从机时:从机按协议返回对应数据。

六、常见问题

现象 可能原因 解决办法
收不到数据 / 全 0 主机从机 CPOL/CPHA 不一致 确认双方 SPI 模式相同
收到数据错位 收尾时过早拉高 NSS 先等 TXFEBUSY 清零再拉高 NSS
通信速率异常 波特率分频理解错 速率为 PCLK / 分频系数,本例 48M/32
从机不响应 NSS 极性/接线错 检查 NSS 是否真正拉低,且 SCK/MOSI/MISO 交叉正确

七、小结

SPI 主机轮询收发三步走:配引脚为复用 → 配 SPI 为主机(定模式/波特率)→ 拉低 NSS 后“等空就写、等有就读”、结束拉高 NSS。 注意全双工特性:写的同时就在读;模式(CPOL/CPHA)与从机必须一致。

八、拓展应用

  • 用普通 GPIO 手动控制 NSS,实现“一主多从”多器件选片(见 SPI1_Master_GPIO_As_NSS);
  • 改用 DMA 或中断传输,释放 CPU(适合大批量数据);
  • 驱动 SPI LCD / Flash,把“发命令 + 发数据”封装成函数。
世界是你们
使用 Hugo 构建
主题 StackJimmy 设计