第十七章 SPI——读写串行FLASH

第十七章 SPI——读写串行FLASH

目录

第十七章 SPI——读写串行FLASH

1 SPI协议简介

1.1 SPI物理层

1.2 协议层

1.2.1 SPI基本通讯过程

1.2.2 通讯的起始和停止信号

1.2.3 数据有效性

1.2.4 CPOL/CPHA及通讯模式

2 W55MH32的SPI特性及架构

2.1 W55MH32的SPI外设简介

2.2 W55MH32的SPI架构剖析

2.2.1 通讯引脚

2.2.4 整体控制逻辑

2.3 通讯过程

3 SPI初始化结构体详解

4 SPI—DMA

4.1 编程要点

4.2 代码分析

4.3 下载验证


本章参考资料:《W55MH32中文参考手册》SPI章节及《SPI总线协议介绍》。

1 SPI协议简介

SPI协议是由摩托罗拉公司提出的通讯协议(Serial Peripheral Interface),即串行外围设备接口, 是一种高速全双工的通信总线。它被广泛地使用在ADC、LCD等设备与MCU间,要求通讯速率较高的场合。

下面我们分别对SPI协议的物理层及协议层进行讲解。

1.1 SPI物理层

SPI通讯设备之间的常用连接方式见下图,常见的SPI通讯系统 :

SPI通讯使用3条总线及片选线,3条总线分别为SCK、MOSI、MISO,片选线为SS,它们的作用介绍如下:

SS ( Slave Select):从设备选择信号线,常称为片选信号线,也称为NSS、CS,以下用NSS表示。当有多个SPI从设备与SPI主机相连时, 设备的其它信号线SCK、MOSI及MISO同时并联到相同的SPI总线上,即无论有多少个从设备,都共同只使用这3条总线; 而每个从设备都有独立的这一条NSS信号线,本信号线独占主机的一个引脚,即有多少个从设备,就有多少条片选信号线。 I2C协议中通过设备地址来寻址、选中总线上的某个设备并与其进行通讯;而SPI协议中没有设备地址,它使用NSS信号线来寻址, 当主机要选择从设备时,把该从设备的NSS信号线设置为低电平,该从设备即被选中,即片选有效, 接着主机开始与被选中的从设备进行SPI通讯。所以SPI通讯以NSS线置低电平为开始信号,以NSS线被拉高作为结束信号。

SCK (Serial Clock):时钟信号线,用于通讯数据同步。它由通讯主机产生,决定了通讯的速率,不同的设备支持的最高时钟频率不一样, 如W55MH32的SPI时钟频率最大为fpclk/2,两个设备之间通讯时,通讯速率受限于低速设备。

MOSI (Master Output, Slave Input):主设备输出/从设备输入引脚。主机的数据从这条信号线输出, 从机由这条信号线读入主机发送的数据,即这条线上数据的方向为主机到从机。

MISO (Master Input,,Slave Output):主设备输入/从设备输出引脚。主机从这条信号线读入数据, 从机的数据由这条信号线输出到主机,即在这条线上数据的方向为从机到主机。

1.2 协议层

SPI协议定义了通讯的起始和停止信号、数据有效性、时钟同步等环节。

1.2.1 SPI基本通讯过程

先看看SPI通讯的通讯时序,见下图,SPI通讯时序 :

这是一个主机的通讯时序。NSS、SCK、MOSI信号都由主机控制产生,而MISO的信号由从机产生,主机通过该信号线读取从机的数据。 MOSI与MISO的信号只在NSS为低电平的时候才有效,在SCK的每个时钟周期MOSI和MISO传输一位数据。

以上通讯流程中包含的各个信号分解如下:

1.2.2 通讯的起始和停止信号

在图 SPI通讯时序 中的标号处,NSS信号线由高变低,是SPI通讯的起始信号。NSS是每个从机各自独占的信号线, 当从机在自己的NSS线检测到起始信号后,就知道自己被主机选中了,开始准备与主机通讯。在图中的标号处,NSS信号由低变高, 是SPI通讯的停止信号,表示本次通讯结束,从机的选中状态被取消。

1.2.3 数据有效性

SPI使用MOSI及MISO信号线来传输数据,使用SCK信号线进行数据同步。MOSI及MISO数据线在SCK的每个时钟周期传输一位数据, 且数据输入输出是同时进行的。数据传输时,MSB先行或LSB先行并没有作硬性规定,但要保证两个SPI通讯设备之间使用同样的协定, 一般都会采用图 SPI通讯时序 中的MSB先行模式。

观察图中的标号处,MOSI及MISO的数据在SCK的上升沿期间变化输出,在SCK的下降沿时被采样。即在SCK的下降沿时刻, MOSI及MISO的数据有效,高电平时表示数据“1”,为低电平时表示数据“0”。在其它时刻,数据无效,MOSI及MISO为下一次表示数据做准备。

SPI每次数据传输可以8位或16位为单位,每次传输的单位数不受限制。

1.2.4 CPOL/CPHA及通讯模式

上面讲述的图 SPI通讯时序 中的时序只是SPI中的其中一种通讯模式,SPI一共有四种通讯模式, 它们的主要区别是总线空闲时SCK的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性CPOL”和“时钟相位CPHA”的概念。

时钟极性CPOL是指SPI通讯设备处于空闲状态时,SCK信号线的电平信号(即SPI通讯开始前、 NSS线为高电平时SCK的状态)。CPOL=0时, SCK在空闲状态时为低电平,CPOL=1时,则相反。

时钟相位CPHA是指数据的采样的时刻,当CPHA=0时,MOSI或MISO数据线上的信号将会在SCK时钟线的“奇数边沿”被采样。当CPHA=1时, 数据线在SCK的“偶数边沿”采样。

由CPOL及CPHA的不同状态,SPI分成了四种模式,见下表,SPI的四种模式 , 主机与从机需要工作在相同的模式下才可以正常通讯,实际中采用较多的是“模式0”与“模式3”:

SPI 模式

CPOL

CPHA

空闲时 SCK 时钟

采样时刻

0

0

0

低电平

奇数边沿

1

0

1

低电平

偶数边沿

2

1

0

高电平

奇数边沿

3

1

1

高电平

偶数边沿

2 W55MH32的SPI特性及架构

与I2C外设一样,W55MH32芯片也集成了专门用于SPI协议通讯的外设。

2.1 W55MH32的SPI外设简介

SPI 接口可以配置为支持 SPI 协议或者支持 I2S 音频协议。SPI 接口默认工作在 SPI 方式,可以通过软件把功能从 SPI 模式切换到 I2S 模式。

串行外设接口(SPI)允许芯片与外部设备以半/全双工、同步、串行方式通信。此接口可以被配置成主模式,并为外部从设备提供通信时钟(SCK)。接口还能以多主配置方式工作。

它可用于多种用途,包括使用一条双向数据线的双线单工同步传输,还可使用 CRC 校验的可靠通信。

  1. 3 线全双工同步传输
  2. 带或不带第三根双向数据线的双线单工同步传输
  3. 8或16位传输帧格式选择主或从操作,支持多主模式
  4. 8个主模式波特率预分频系数(最大为fPCLK/2)
  5. 从模式频率(最大为fPCLK/2)
  6. 主模式和从模式的快速通信
  7. 主模式和从模式下均可以由软件或硬件进行NSS 管理:主/从操作模式的动态改变
  8. 可编程的时钟极性和相位,可编程的数据顺序,MSB在前或LSB在前
  9. 可触发中断的专用发送和接收标志
  10. SPI 总线忙状态标志
  11. 支持可靠通信的硬件CRC

2.2 W55MH32的SPI架构剖析

W55MH32的SPI架构图如下:

2.2.1 通讯引脚

SPI的所有硬件架构都从图 SPI架构图 中左侧MOSI、MISO、SCK及NSS线展开的。W55MH32芯片有多个SPI外设, 它们的SPI通讯信号引出到不同的GPIO引脚上,使用时必须配置到这些指定的引脚,见下表,W55MH32的SPI引脚 。 关于GPIO引脚的复用功能,可查阅《W55MH32规格书》,以它为准。

引脚

SPI1

SPI2

SPI3

NSS

PA4

PB12

PA15 下载口的 TDI

CLK

PA5

PB13

PB3 下载口的 TDO

MISO

PA6

PB14

PB4 下载口的 NTRST

MOSI

PA7

PB15

PB5

2.2.4 整体控制逻辑

整体控制逻辑负责协调整个SPI外设,控制逻辑的工作模式根据我们配置的“控制寄存器(CR1/CR2)”的参数而改变, 基本的控制参数包括前面提到的SPI模式、波特率、LSB先行、主从模式、单双向模式等等。在外设工作时, 控制逻辑会根据外设的工作状态修改“状态寄存器(SR)”,我们只要读取状态寄存器相关的寄存器位, 就可以了解SPI的工作状态了。除此之外,控制逻辑还根据要求,负责控制产生SPI中断信号、DMA请求及控制NSS信号线。

实际应用中,我们一般不使用W55MH32 SPI外设的标准NSS信号线,而是更简单地使用普通的GPIO,软件控制它的电平输出,从而产生通讯起始和停止信号。

2.3 通讯过程

主或从模式下(BIDIMODE=0 并且 RXONLY=0)全双工发送和接收过程模式,软件必须遵循下述过程,发送和接收数据:

1. 设置 SPE 位为'1',使能 SPI 模块;

2. 在 SPI_DR 寄存器中写入第一个要发送的数据,这个操作会清除 TXE 标志;

3. 等待 TXE=1,然后写入第二个要发送的数据。等待 RXNE=1,然后读出 SPI_DR 寄存器并获得第一个接收到的数据,读 SPI_DR 的同时清除了 RXNE 位。重复这些操作,发送后续的数据同时接收 n-1 个数据;

4. 等待 RXNE=1,然后接收最后一个数据;

5. 等待 TXE=1,在 BSY=0 之后关闭 SPI 模块。

也可以在响应 RXNE 或 TXE 标志的上升沿产生的中断的处理程序中实现这个过程。主模式、全双工模式下(BIDIMODE=0 并且 RXONLY=0)连续传输时,TXE/RXNE/BSY 的变化示意图如下:

从模式、全双工模式下(BIDIMODE=0 并且 RXONLY=0)连续传输时,TXE/RXNE/BSY 的变化只发送过程(BIDIMODE=0 并且 RXONLY=0)示意图如下:

在此模式下,传输过程可以简要说明如下,使用 BSY 位等待传输的结束:

1. 设置 SPE 位为”1“,使能 SPI 模块;

2. 在 SPI_DR 寄存器中写入第一个要发送的数据,这个操作会清除 TXE 标志;

3. 等待 TXE=1,然后写入第二个要发送的数据。重复这个操作,发送后续的数据;

4. 写入最后一个数据到 SPI_DR 寄存器之后,等待 TXE=1;然后等待 BSY=0,这表示最后一个数据的传输已经完成。也可以在响应 TXE 标志的上升沿产生的中断的处理程序中实现这个过程。

注:

1.对于不连续的传输,在写入 SPI_DR 寄存器的操作与设置 BSY 位之间有 2 个 APB 时钟周期的延迟,因此在只发送模式下,写入最后一个数据后,最好先等待 TXE=1,然后再等待 BSY=0。454/671 W55MH32 参考手册 V1.0.0。

2.只发送模式下,在传输 2 个数据之后,由于不会读出接收到的数据,SPI_SR 寄存器中的 OVR位会变为'1'。软件不必理会这个 OVR 标志。

主设备只发送模式(BIDIMODE=0 并且 RXONLY=0)下连续传输时,TXE/BSY 变化示意图如下:

从设备只发送模式(BIDIMODE=0 并且 RXONLY=0)下连续传输时,TXE/BSY 变化示意图如下:

双向发送过程(BIDIMODE=1 并且 BIDIOE=1)

在此模式下,操作过程类似于只发送模式,不同的是:在使能 SPI 模块之前,需要在 SPI_CR2 寄存器中同时设置 BIDIMODE 和 BIDIOE 位为”1” 。

单向只接收模式(BIDIMODE=0 并且 RXONLY=1)

在此模式下,传输过程可以简要说明如下:

1. 在 SPI_CR2 寄存器中,设置 RXONLY=1;

2. 设置 SPE=1,使能 SPI 模块:

a) 主模式下,立刻产生 SCK 时钟信号,在关闭 SPI(SPE=0)之前,不断地接收串行数据;

b) 从模式下,当 SPI 主设备拉低 NSS 信号并产生 SCK 时钟时,接收串行数据。

3. 等待 RXNE=1,然后读出 SPI_DR 寄存器以获得收到的数据(同时会清除 RXNE 位)。重复这个操作接收所有数据。

也可以在响应 RXNE 标志的上升沿产生的中断的处理程序中实现这个过程。只接收模式(BIDIMODE=0 并且 RXONLY=1)下连续传输时,RXNE 变化示意图如下:

单向接收过程(BIDIMODE=1 并且 BIDIOE=0)

在此模式下,操作过程类似于只接收模式,不同的是:在使能 SPI 模块之前,需要在 SPI_CR2 寄存器中设置 BIDIMODE 为'1'并清除 BIDIOE 位为'0'。

连续和非连续传输

当在主模式下发送数据时,如果软件足够快,能够在检测到每次 TXE 的上升沿(或 TXE 中断),并立即在正在进行的传输结束之前写入 SPI_DR 寄存器,则能够实现连续的通信;此时,在每个数据项的传输之间的 SPI 时钟保持连续,同时 BSY 位不会被清除。

如果软件不够快,则会导致不连续的通信;这时,在每个数据传输之间会被清除。在主模式的只接收模式下(RXONLY=1),通信总是连续的,而且 BSY 标志始终为'1'。

在从模式下,通信的连续性由 SPI 主设备决定。不管怎样,即使通信是连续的,BSY 标志会在每个数据项之间至少有一个 SPI 时钟周期为低。非连续传输发送(BIDIMODE=0 并且 RXONLY=0)时,TXE/BSY 变化示意图如下:

3 SPI初始化结构体详解

跟其它外设一样,W55MH32标准库提供了SPI初始化结构体及初始化函数来配置SPI外设。 初始化结构体及函数定义在库文件“w55mh32_spi.h”及“w55mh32_spi.c”中,编程时我们可以结合这两个文件内的注释使用或参考库帮助文档。 了解初始化结构体后我们就能对SPI外设运用自如了,见代码清单:SPI-1 :

代码清单:SPI-1 SPI初始化结构体

typedef struct
{
    uint16_t SPI_Direction;           /*设置SPI的单双向模式 */
    uint16_t SPI_Mode;                /*设置SPI的主/从机端模式 */
    uint16_t SPI_DataSize;            /*设置SPI的数据帧长度,可选8/16位 */
    uint16_t SPI_CPOL;                /*设置时钟极性CPOL,可选高/低电平*/
    uint16_t SPI_CPHA;                /*设置时钟相位,可选奇/偶数边沿采样 */
    uint16_t SPI_NSS;                /*设置NSS引脚由SPI硬件控制还是软件控制*/
    uint16_t SPI_BaudRatePrescaler;  /*设置时钟分频因子,fpclk/分频数=fSCK */
    uint16_t SPI_FirstBit;            /*设置MSB/LSB先行 */
    uint16_t SPI_CRCPolynomial;       /*设置CRC校验的表达式 */
} SPI_InitTypeDef;

这些结构体成员说明如下,其中括号内的文字是对应参数在W55MH32标准库中定义的宏:

SPI_Direction

本成员设置SPI的通讯方向,可设置为双线全双工(SPI_Direction_2Lines_FullDuplex),双线只接收(SPI_Direction_2Lines_RxOnly), 单线只接收(SPI_Direction_1Line_Rx)、单线只发送模式(SPI_Direction_1Line_Tx)。

SPI_Mode

本成员设置SPI工作在主机模式(SPI_Mode_Master)或从机模式(SPI_Mode_Slave ), 这两个模式的最大区别为SPI的SCK信号线的时序, SCK的时序是由通讯中的主机产生的。若被配置为从机模式,W55MH32的SPI外设将接受外来的SCK信号。

SPI_DataSize

本成员可以选择SPI通讯的数据帧大小是为8位(SPI_DataSize_8b)还是16位(SPI_DataSize_16b)。

SPI_CPOL和SPI_CPHA

这两个成员配置SPI的时钟极性CPOL和时钟相位CPHA,这两个配置影响到SPI的通讯模式, 关于CPOL和CPHA的说明参考前面“通讯模式”小节。

时钟极性CPOL成员,可设置为高电平(SPI_CPOL_High)或低电平(SPI_CPOL_Low )。时钟相位CPHA 则可以设置为SPI_CPHA_1Edge(在SCK的奇数边沿采集数据) 或SPI_CPHA_2Edge(在SCK的偶数边沿采集数据) 。

SPI_NSS

本成员配置NSS引脚的使用模式,可以选择为硬件模式(SPI_NSS_Hard )与软件模式(SPI_NSS_Soft ), 在硬件模式中的SPI片选信号由SPI硬件自动产生,而软件模式则需要我们亲自把相应的GPIO端口拉高或置低产生非片选和片选信号。实际中软件模式应用比较多。

SPI_BaudRatePrescaler

本成员设置波特率分频因子,分频后的时钟即为SPI的SCK信号线的时钟频率。这个成员参数可设置为fpclk的2、4、6、8、16、32、64、128、256分频。

SPI_FirstBit

所有串行的通讯协议都会有MSB先行(高位数据在前)还是LSB先行(低位数据在前)的问题,而W55MH32的SPI模块可以通过这个结构体成员,对这个特性编程控制。

SPI_CRCPolynomial

这是SPI的CRC校验中的多项式,若我们使用CRC校验时,就使用这个成员的参数(多项式),来计算CRC的值。

配置完这些结构体成员后,我们要调用SPI_Init函数把这些参数写入到寄存器中,实现SPI的初始化,然后调用SPI_Cmd来使能SPI外设。

4 SPI—DMA

4.1 编程要点

  1. 初始化通讯使用的目标引脚及端口时钟;
  2. 使能SPI外设的时钟;
  3. 配置SPI外设的模式、地址、速率等参数并使能SPI外设;
  4. 编写基本SPI按字节收发的函数;
  5. 编写对FLASH擦除及读写操作的的函数;
  6. 编写测试程序,对读写数据进行校验。

4.2 代码分析

这段代码实现了 W55MH32 微控制器的 SPI 主模式 DMA 数据传输,通过 DMA 控制器高速发送数据到 SPI 总线。

1. 整体架构

#include "w55mh32.h" // 芯片头文件
#include "delay.h"   // 延时函数

// 全局变量
USART_TypeDef *USART_TEST = USART1; // 串口1
#define SPI_BUFF_SIZE 256
uint8_t SPI_TX_BUFF[SPI_BUFF_SIZE]; // 发送缓冲区
uint8_t SPI_RX_BUFF[SPI_BUFF_SIZE]; // 接收缓冲区(未使用)

包含芯片相关头文件和自定义延时函数。

定义 SPI 缓冲区大小(256 字节),填充发送缓冲区为 0x01, 0x02, ..., 0xFF。

2. 主函数 main

int main(void)
{
    // 初始化系统时钟、串口、SPI、DMA
    delay_init();
    UART_Configuration(115200); // 串口波特率 115200
    SPI_Configuration();        // 配置 SPI1 为主模式
    DMA_Configuration();        // 配置 DMA 通道 2(接收)和 3(发送)

    // 填充发送缓冲区:0x01, 0x02, ..., 0xFF
    for (i = 0; i < SPI_BUFF_SIZE; i++) {
        SPI_TX_BUFF[i] = i + 1;
    }

    // 启动 SPI DMA 发送
    GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 拉低片选(CS)
    DMA_Cmd(DMA1_Channel3, ENABLE);    // 启用 DMA 发送通道
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC3)); // 等待传输完成
    DMA_ClearFlag(DMA1_FLAG_TC3);      // 清除传输完成标志
    DMA_Cmd(DMA1_Channel3, DISABLE);   // 禁用 DMA 通道
    GPIO_SetBits(GPIOA, GPIO_Pin_4);   // 拉高片选(CS)

    while (1); // 主循环(无操作)
}

功能:通过 SPI 主模式,使用 DMA 高速发送 256 字节数据(SPI_TX_BUFF)。

流程:初始化 → 填充缓冲区 → 片选有效 → 启动 DMA 发送 → 等待完成 → 片选无效。

3. SPI 配置 SPI_Configuration

void SPI_Configuration(void)
{
    // 使能 SPI1 和 GPIOA 时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置片选(CS)引脚(PA4)为推挽输出
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA, GPIO_Pin_4); // 初始拉高(非选中状态)

    // 配置 SPI 引脚(PA5/SCK, PA6/MISO, PA7/MOSI)为复用推挽
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 初始化 SPI1 参数
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       // 主模式
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;         // 时钟空闲高电平(模式 3)
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;        // 第二个边沿采样
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 低速(用于调试)
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1, ENABLE); // 使能 SPI1
}

SPI 参数:主模式,全双工,8 位数据。

时钟极性(CPOL):高电平(SPI 模式 3)。

时钟相位(CPHA):第二个边沿采样。

波特率:系统时钟 / 256(低速,适合调试)。

引脚分配:

PA4:片选(CS,软件控制)。

PA5:SCK(SPI 时钟)。

PA6:MISO(主输入)。

PA7:MOSI(主输出)。

4. DMA 配置 DMA_Configuration

void DMA_Configuration(void)
{
    // 使能 DMA1 时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    // 配置 DMA 通道 3(SPI1 发送)
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;       // 内存 → 外设(发送)
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR; // 外设地址:SPI 数据寄存器
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SPI_TX_BUFF; // 内存地址:发送缓冲区
    DMA_InitStructure.DMA_BufferSize = SPI_BUFF_SIZE;        // 传输大小:256 字节
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不变
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  // 内存地址递增
    DMA_Init(DMA1_Channel3, &DMA_InitStructure);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE); // 使能 SPI 发送 DMA 请求

    // 配置 DMA 通道 2(SPI1 接收,未使用)
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;       // 外设 → 内存(接收)
    DMA_Init(DMA1_Channel2, &DMA_InitStructure);
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Rx, ENABLE); // 使能 SPI 接收 DMA 请求(未启用)
}

DMA 通道 3(发送):

  • 方向:内存(SPI_TX_BUFF)→ 外设(SPI1->DR)。
  • 触发源:SPI1 发送缓冲区空(TXE 标志)。

DMA 通道 2(接收):

  • 方向:外设(SPI1->DR)→ 内存(SPI_RX_BUFF)。
  • 触发源:SPI1 接收缓冲区满(RXNE 标志)。

注意:代码中仅启用了发送通道(DMA1_Channel3),接收通道未使用。

5. 串口配置(用于打印调试信息)

void UART_Configuration(uint32_t bound) { /* 配置串口1,波特率 115200 */ }
int fputc(int c, FILE *f) { /* 重定向 printf 到串口 */ }

串口用于打印系统时钟信息(仅初始化时执行一次)。

无实际数据接收功能,仅用于调试输出。

4.3 下载验证

在电脑端打开串口调试助手, 把编译好的程序下载到开发板。在串口调试助手可看到测试的调试信息:

如果想要了解具体的执行步骤,则可以在初始化添加如下代码:

SPI_Configuration();
DMA_Configuration();

    //Tx Data Test
    printf("Start SPI DMA transmission...\n");
    GPIO_ResetBits(GPIOA, GPIO_Pin_4);
    printf("Chip select (CS) is pulled low.\n");
    DMA_Cmd(DMA1_Channel3, ENABLE);
    printf("DMA channel 3 for SPI Tx is enabled.\n");

    // 等待 DMA 传输完成并打印状态
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC3))
    {
        // 可以添加更多状态监测信息,例如 DMA 剩余传输数量
        printf("DMA transfer in progress...\n");
        delay_ms(10);
    }
    printf("DMA transfer completed.\n");

    DMA_ClearFlag(DMA1_FLAG_TC3);
    printf("DMA transfer complete flag is cleared.\n");
    DMA_Cmd(DMA1_Channel3, DISABLE);
    printf("DMA channel 3 for SPI Tx is disabled.\n");
    delay_ms(100);
    GPIO_SetBits(GPIOA, GPIO_Pin_4);
    printf("Chip select (CS) is pulled high.\n");

    // 打印发送的数据
    printf("Data sent via SPI:\n");
    for (i = 0; i < SPI_BUFF_SIZE; i++)
    {
        printf("%02X ", SPI_TX_BUFF[i]);
        if ((i + 1) % 16 == 0)
        {
            printf("\n");
        }
    }
    printf("\n");

    // 启动 DMA 接收(如果需要)
    DMA_Cmd(DMA1_Channel2, ENABLE);
    printf("DMA channel 2 for SPI Rx is enabled.\n");
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC2))
    {
        printf("DMA Rx transfer in progress...\n");
        delay_ms(10);
    }
    printf("DMA Rx transfer completed.\n");
    DMA_ClearFlag(DMA1_FLAG_TC2);
    printf("DMA Rx transfer complete flag is cleared.\n");
    DMA_Cmd(DMA1_Channel2, DISABLE);
    printf("DMA channel 2 for SPI Rx is disabled.\n");

    // 打印接收的数据
    printf("Data received via SPI:\n");
    for (i = 0; i < SPI_BUFF_SIZE; i++)
    {
        printf("%02X ", SPI_RX_BUFF[i]);
        if ((i + 1) % 16 == 0)
        {
            printf("\n");
        }
    }
    printf("\n");

以下便是程序执行过程中打印的调试信息了:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值