PUYA SPI — 全双工主机(DMA)(HAL 库)

PUYA PY32F0xx_Firmware V1.5.0 SPI 全双工主机(SPI_TwoBoards_FullDuplexMaster_DMA)示例教学

SPI — 全双工主机(DMA)(HAL 库)

固件库: PY32F0xx_Firmware V1.5.0
芯片: PY32F030
源文件: PUYA/PY32F0xx_Firmware_V1.5.0/Projects/PY32F030-STK/Example/SPI/SPI_TwoBoards_FullDuplexMaster_DMA/Src/main.c


功能简介

SPI(Serial Peripheral Interface)是一种四线制、全双工、同步串行总线,速率远高于 I2C,常用于 LCD、Flash、ADC、无线通信模块等。

本示例演示 SPI1 主机 + DMA 全双工通信:

  • 主机通过 HAL_SPI_TransmitReceive_DMA() 同时收发 15 字节;
  • 数据搬运由 DMA 完成,CPU 在传输期间可腾出手做别的事;
  • 传输完成后回调函数里校验收发是否一致,一致则点亮绿灯。
  • 本工程仅提供主机(Master DMA)示例;同目录另有时钟/引脚由 HAL_SPI_MspInit 配置(在 py32f0xx_hal_msp.c 中绑定 SPI1 引脚与 DMA 通道)。

说明:PY32F0xx V1.5.0 的 STK 示例里 SPI 全双工只提供了 DMA 版(无纯轮询版)。若想用轮询,可把 HAL_SPI_TransmitReceive_DMA 换成 HAL_SPI_TransmitReceive(轮询版 API 同样存在),二者配置完全一致。

硬件准备

  • 两块 PY32F030 开发板(一主一从),或用一块板把 MOSI↔MISO 短接做回环
  • 交叉连接:主 MOSI↔从 MOSI,主 MISO↔从 MISO,主 SCK↔从 SCK,主 NSS↔从 NSS,共地
  • 板载 LED / 按键用于交互(示例需按键触发一次传输)

完整代码(主机部分)

 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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "main.h"

#define DATA_LENGTH 15

SPI_HandleTypeDef Spi1Handle;
uint8_t TxBuff[15] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
uint8_t RxBuff[15] = {0};
DMA_HandleTypeDef HdmaCh1;   /* 发送 DMA 通道 */
DMA_HandleTypeDef HdmaCh2;   /* 接收 DMA 通道 */

int main(void)
{
  HAL_Init();
  APP_SystemClockConfig();    /* 配置系统时钟(HSI 8MHz) */

  BSP_LED_Init(LED_GREEN);
  BSP_PB_Init(BUTTON_KEY, BUTTON_MODE_GPIO);

  /* SPI1 参数配置 */
  Spi1Handle.Instance               = SPI1;
  Spi1Handle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256; /* 波特率预分频 256 */
  Spi1Handle.Init.Direction         = SPI_DIRECTION_2LINES;      /* 全双工(2 线) */
  Spi1Handle.Init.CLKPolarity       = SPI_POLARITY_LOW;          /* 时钟极性:空闲低 */
  Spi1Handle.Init.CLKPhase          = SPI_PHASE_1EDGE;           /* 第 1 个边沿采样 */
  Spi1Handle.Init.DataSize          = SPI_DATASIZE_8BIT;         /* 8 位数据 */
  Spi1Handle.Init.FirstBit          = SPI_FIRSTBIT_MSB;          /* 高位先发 */
  Spi1Handle.Init.NSS               = SPI_NSS_HARD_OUTPUT;       /* NSS 硬件输出(主机自动拉低) */
  Spi1Handle.Init.Mode              = SPI_MODE_MASTER;           /* 主机模式 */
  if (HAL_SPI_DeInit(&Spi1Handle) != HAL_OK) APP_ErrorHandler();
  if (HAL_SPI_Init(&Spi1Handle) != HAL_OK)   APP_ErrorHandler();

  /* 等按键按下,启动一次全双工收发(DMA 方式) */
  while (BSP_PB_GetState(BUTTON_KEY) == 1) {}
  if (HAL_SPI_TransmitReceive_DMA(&Spi1Handle, (uint8_t*)TxBuff,
                                  (uint8_t*)RxBuff, DATA_LENGTH) != HAL_OK)
  {
    APP_ErrorHandler();
  }

  APP_WaitAndCheckEndOfTransfer();  /* 等传输完成并校验 */

  while (1) {}
}

/* 等待 SPI 回到 READY,再比较收发缓冲 */
static void APP_WaitAndCheckEndOfTransfer(void)
{
  while (Spi1Handle.State != HAL_SPI_STATE_READY) {}
  if (APP_Buffercmp8((uint8_t*)TxBuff, (uint8_t*)RxBuff, DATA_LENGTH))
    APP_LedBlinking();   /* 不一致:闪烁报警 */
  else
    BSP_LED_On(LED_GREEN); /* 一致:绿灯常亮 */
}

代码分段讲解

1. 时钟极性 / 相位(CPOL / CPHA)

SPI 通信双方必须 CPOL、CPHA 一致,否则收到的是错位数据:

  • 本例 CPOL=LOW, CPHA=1EDGE 对应 Mode 0(常用默认)。
  • 四种组合:Mode0(0,0)、Mode1(0,1)、Mode2(1,0)、Mode3(1,1),按从设备手册选择。

2. 全双工本质

SPI 移位寄存器决定了“发的同时也在收”。HAL_SPI_TransmitReceiveTxBuff 逐位发出,同时把 MISO 上的数据逐位存入 RxBuff。回环(MOSI 接 MISO)时,发什么就收到什么,便于自测。

3. DMA vs 轮询

  • DMA 版HAL_SPI_TransmitReceive_DMA 启动后由 DMA 控制器搬运数据,CPU 立刻返回,可在回调/状态查询里做别的事,适合大数据量。
  • 轮询版:把上面函数换成 HAL_SPI_TransmitReceive(&Spi1Handle, Tx, Rx, len)(阻塞直到完成),代码更短,适合小数据量教学。

4. NSS 片选

本例 SPI_NSS_HARD_OUTPUT:主机 NSS 引脚由硬件自动在传输时拉低、结束后拉高,省去手动控制。若用软件片选,改 SPI_NSS_SOFT 并自行 HAL_GPIO_WritePin(NSS, RESET/SET)

实验现象

  • 按下用户键后启动一次 15 字节全双工收发;
  • 回环或正确对接从机时,收发一致,绿灯常亮
  • 数据异常则绿灯 500ms 周期闪烁。

常见问题

  1. 收不到数据 / 全是 0xFF?

    • 检查 CPOL/CPHA 是否与从机一致。
    • 确认接线:MOSI-MOSI、MISO-MISO、SCK-SCK、NSS-NSS,并共地
    • 波特率(BaudRatePrescaler)不要超过从机最大 SPI 时钟。
  2. 想用轮询版?

    • 直接调用 HAL_SPI_TransmitReceive()(不带 _DMA),其余配置不变。
  3. DMA 传输只发不收 / 卡在 BUSY?

    • 确认 HAL_SPI_MspInit 中已为 SPI1 的 TX/RX 绑定正确的 DMA 通道并使能 DMA 时钟;传输完成需等 State == HAL_SPI_STATE_READY

小结

  • SPI 主机配置要点:波特率分频、CPOL/CPHA、数据位宽、MSB/LSB、NSS 方式、主/从模式
  • 全双工 = 同时收发;回环自测最方便。
  • DMA 适合大批量数据,轮询版更简单;两者配置相同,仅启动函数不同。

拓展应用

  • 驱动 SPI Flash(W25Qxx)、LCD(ST7735/ILI9341)、SD 卡(SPI 模式)。
  • 对接 SPI ADC(如 ADS1118)、无线模块(nRF24L01、SX1278)。
  • 改用 SPI_NSS_SOFT 一主多从,用不同 NSS 选片。
  • 把 DMA 版改为中断/DMA 双缓冲,实现连续高速数据流(音频、屏幕刷新)。
世界是你们
使用 Hugo 构建
主题 StackJimmy 设计