国产MCU学习Day7——CW32F030C8T6 SPI主从通信详解

每日更新教程,评论区答疑解惑,小白也能变大神!"

目录

[一.CW32F030C8T6 SPI 功能](#一.CW32F030C8T6 SPI 功能)

二.SPI主从通信示例(带DMA)

宏定义部分

枚举与全局变量

主函数

时钟配置函数

[GPIO 配置函数注释](#GPIO 配置函数注释)

[DMA 配置函数注释](#DMA 配置函数注释)

[SPI 配置函数注释](#SPI 配置函数注释)

缓冲区比较函数注释

关键功能说明

一.CW32F030C8T6 SPI 功能

  • 串行外设接口(SPI)是一种同步串行数据通信接口,常用于 MCU 与外部设备之间进行同步串行通信。 CW32x030 内部集成 2 个串行外设 SPI 接口,支持双向全双工、单线半双工和单工通信模式,可配置 MCU 作为 主机或从机,支持多主机通信模式,支持直接内存访问(DMA)。

1.1主要特性

  • 支持主机模式、从机模式
  • 支持全双工、单线半双工、单工
  • 可选的 4 位到 16 位数据帧宽度
  • 支持收发数据 LSB 或 MSB 在前
  • 可编程时钟极性和时钟相位
  • 主机模式下通信速率高达 PCLK/4
  • 从机模式下通信速率高达 PCLK/4
  • 支持多机通信模式
  • 8 个带标志位的中断源
  • 支持直接内存访问 (DMA)

1.2功能框图

1.3SPI 引脚选择

1.4端口配置

1.5通信时序

1.6数据帧格式

  • 数据帧宽度由控制寄存器 SPIx_CR1 的 WIDTH 位域配置,可设置 4 ~ 16bit 任意数据位宽。 数据的大小端由控制寄存器 SPIx_CR1的 LSBF位域配置,可选择最高有效位在前(MSB)或最低有效位在前(LSB)。

1.7时钟频率

  • 同步串行时钟 SCK 信号由 SPI 主机控制产生,其时钟来源是 PCLK,通过配置控制寄存器 SPIx_CR1 的 BR 位域 来设置分频因子,可选择 2 ~ 128 分频。对于 SPI 从机,配置 SPIx_CR1.BR 无影响。

1.8时钟极性、时钟相位

  • 时钟极性 CPOL,指设备处于没有数据传输的空闲状态时,SCK 串行时钟线的电平状态。通过控制寄存器 SPIx_CR1 的 CPOL 位域进行配置:设置 SPIx_CR1.CPOL 为 0,SCK 时钟线在空闲时为低电平;设置 SPIx_CR1.CPOL 为 1, SCK 时钟线在空闲时为高电平。
  • 时钟相位 CPHA,指数据的采样和移位时刻。通过控制寄存器 SPIx_CR1 的 CPHA 进行配置:设置 SPIx_CR1. CPHA 为 0,在 SCK 的前边沿(SCK 由空闲状态变为非空闲状态的时钟边沿)采样、后边沿(SCK 由非空闲状态 变为空闲状态的时钟边沿)移位;设置 SPIx_CR1.CPHA 为 1,在 SCK 的前边沿移位、后边沿采样。
  • 根据时钟极性 CPOL 和时钟相位 CPHA 的不同配置,SPI 可设置 4 种电平模式,主机和从机需要配置成相同的电 平模式才能保证正常通信。

1.9 CS引脚

1.10工作模式-全双工模式

1.10工作模式-单线半双工模式

1.11工作模式-单工模式

1.11多机通讯

1.12状态标志

1.13SPI 中断

1.14 SPI DMA

1.15 编程示例

二.SPI主从通信示例(带DMA)

以下是为代码添加的详细注释,以增强可读性和理解性:

宏定义部分

c 复制代码
#define  SPI_MASTER //主机模式
//#define  SPI_SLAVE  //从机模式(当前注释掉,表示未启用)

// SPI模块配置
#define  SPIx                           CW_SPI2  //使用SPI2模块
#define  SPIx_CLK                       RCC_APB1_PERIPH_SPI2  //SPI2时钟源
#define  SPIx_APBClkENx                 RCC_APBPeriphClk_Enable1  //APB时钟使能函数

// SPI引脚配置(SCK、MISO、MOSI、CS)
#define  SPIx_SCK_GPIO_CLK              RCC_AHB_PERIPH_GPIOA  //SCK引脚时钟
#define  SPIx_SCK_GPIO_PORT             CW_GPIOA  //SCK引脚端口
#define  SPIx_SCK_GPIO_PIN              GPIO_PIN_2  //SCK引脚号
#define  SPIx_SCK_AF()                  PA02_AFx_SPI2SCK()  //SCK复用功能配置

#define  SPIx_MISO_GPIO_CLK             RCC_AHB_PERIPH_GPIOA  //MISO引脚时钟
#define  SPIx_MISO_GPIO_PORT            CW_GPIOA  //MISO引脚端口
#define  SPIx_MISO_GPIO_PIN             GPIO_PIN_0  //MISO引脚号
#define  SPIx_MISO_AF()                 PA00_AFx_SPI2MISO()  //MISO复用功能配置

#define  SPIx_MOSI_GPIO_CLK             RCC_AHB_PERIPH_GPIOA  //MOSI引脚时钟
#define  SPIx_MOSI_GPIO_PORT            CW_GPIOA  //MOSI引脚端口
#define  SPIx_MOSI_GPIO_PIN             GPIO_PIN_1  //MOSI引脚号
#define  SPIx_MOSI_AF()                 PA01_AFx_SPI2MOSI()  //MOSI复用功能配置

#define  SPIx_CS_GPIO_CLK               RCC_AHB_PERIPH_GPIOA  //CS引脚时钟
#define  SPIx_CS_GPIO_PORT              CW_GPIOA  //CS引脚端口
#define  SPIx_CS_GPIO_PIN               GPIO_PIN_3  //CS引脚号
#define  SPIx_CS_AF()                   PA03_AFx_SPI2CS()  //CS复用功能配置

// CS引脚电平控制
#define  SPIx_CS_LOW()                  PA03_SETLOW()  //拉低CS(选中从机)
#define  SPIx_CS_HIGH()                 PA03_SETHIGH() //拉高CS(取消选中)

// DMA配置
#define  SPIx_RX_DMACHANNEL             CW_DMACHANNEL1  //RX DMA通道
#define  SPIx_TX_DMACHANNEL             CW_DMACHANNEL2  //TX DMA通道
#define  SPIx_DMA_RxTrigSource          DMA_HardTrig_SPI2_RXBufferNE  //RX触发源
#define  SPIx_DMA_TxTrigSource          DMA_HardTrig_SPI2_TXBufferE  //TX触发源
#define  BufferSize                     ARRAY_SZ(TxBuffer)  //缓冲区大小

枚举与全局变量

c 复制代码
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus; //测试状态枚举

// 发送和接收缓冲区
uint8_t TxBuffer[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
                      0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
                      0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
                      0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C,
                      0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x55}; //发送数据(末尾0x55为结束标志)
uint8_t RxBuffer[BufferSize]; //接收缓冲区
volatile TestStatus TransferStatus = FAILED; //传输状态标志

主函数

c 复制代码
int32_t main(void)
{
    RCC_Configuration();   //初始化系统时钟和外设时钟
    GPIO_Configuration();  //配置GPIO引脚和复用功能
    DMA_Configuration();   //配置DMA通道和触发源
    SPI_Configuration();   //配置SPI模块参数(模式、速率等)

#ifdef SPI_MASTER
    SPIx_CS_LOW(); //主机模式下拉低CS,选中从机
#endif

#ifdef SPI_SLAVE
    SPI_FlushSendBuff(SPIx); //从机模式下清空发送缓冲区
#endif

    SPI_DMACmd(SPIx, SPI_DMAReq_Tx | SPI_DMAReq_Rx, ENABLE); //启用SPI DMA传输

    while (1)
    {
        if (RxBuffer[BufferSize - 1] == 0x55) //检测接收完成标志
        {
#ifdef SPI_MASTER
            SPIx_CS_HIGH(); //主机模式下释放CS
#endif
            PB01_SETHIGH(); //点亮LED1指示传输完成

            TransferStatus = Buffercmp(RxBuffer, TxBuffer, BufferSize); //校验数据
            if (TransferStatus == PASSED) 
            {
                PA07_SETHIGH(); //校验成功时点亮LED2
            }
        }
    }
}

时钟配置函数

c 复制代码
void RCC_Configuration(void)
{
    // 使能GPIO、DMA、FLASH等外设时钟
    RCC_AHBPeriphClk_Enable(SPIx_SCK_GPIO_CLK | SPIx_MISO_GPIO_CLK | SPIx_MOSI_GPIO_CLK | 
                           SPIx_CS_GPIO_CLK | RCC_AHB_PERIPH_DMA | RCC_AHB_PERIPH_GPIOA | 
                           RCC_AHB_PERIPH_GPIOB | RCC_AHB_PERIPH_FLASH, ENABLE);
    SPIx_APBClkENx(SPIx_CLK, ENABLE); //使能SPI时钟

    // 配置系统时钟为64MHz(HSI->PLL)
    RCC_HSI_Enable(RCC_HSIOSC_DIV6);
    RCC_PLL_Enable(RCC_PLLSOURCE_HSI, 8000000, RCC_PLL_MUL_8);
    FLASH_SetLatency(FLASH_Latency_3); //Flash等待周期设为3(频率>48MHz时需要)
    RCC_SysClk_Switch(RCC_SYSCLKSRC_PLL); //切换系统时钟到PLL
}

GPIO 配置函数注释

c 复制代码
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; // GPIO初始化结构体
    
    // 配置SPI引脚复用功能(SCK/MISO/MISO需复用为SPI功能)
    SPIx_SCK_AF();  // SCK引脚复用为SPI功能
    SPIx_MISO_AF(); // MISO引脚复用为SPI功能
    SPIx_MOSI_AF(); // MOSI引脚复用为SPI功能

#ifdef SPI_MASTER
    // 主机模式配置:SCK、MOSI、CS为推挽输出,MISO为浮空输入
    GPIO_InitStructure.Pins = SPIx_SCK_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;    // 高速输出
    GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.Pins = SPIx_MOSI_GPIO_PIN;
    GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.Pins = SPIx_CS_GPIO_PIN;
    GPIO_Init(SPIx_CS_GPIO_PORT, &GPIO_InitStructure);

    // MISO引脚配置为浮空输入
    GPIO_InitStructure.Pins = SPIx_MISO_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStructure);

    SPIx_CS_HIGH(); // 拉高CS引脚(默认不选中从机)
#endif

#ifdef SPI_SLAVE
    // 从机模式配置:CS引脚复用,MISO为推挽输出,其余为输入
    SPIx_CS_AF();   // CS引脚复用为SPI功能

    GPIO_InitStructure.Pins = SPIx_MISO_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; // MISO推挽输出
    GPIO_InitStructure.Speed = GPIO_SPEED_HIGH;
    GPIO_Init(SPIx_MISO_GPIO_PORT, &GPIO_InitStructure);

    // SCK、MOSI、CS配置为输入
    GPIO_InitStructure.Pins = SPIx_SCK_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
    GPIO_Init(SPIx_SCK_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.Pins = SPIx_MOSI_GPIO_PIN;
    GPIO_Init(SPIx_MOSI_GPIO_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.Pins = SPIx_CS_GPIO_PIN;
    GPIO_InitStructure.Mode = GPIO_MODE_INPUT_PULLUP; // CS带上拉输入
    GPIO_Init(SPIx_CS_GPIO_PORT, &GPIO_InitStructure);
#endif

    // 配置LED控制引脚(PB1和PA7)
    GPIO_InitStructure.Pins = GPIO_PIN_1;
    GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_Init(CW_GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.Pins = GPIO_PIN_7;
    GPIO_Init(CW_GPIOA, &GPIO_InitStructure);

    // 初始化LED状态为灭
    PB01_SETLOW(); // PB1输出低电平
    PA07_SETLOW(); // PA7输出低电平
}

DMA 配置函数注释

c 复制代码
void DMA_Configuration(void)
{
    DMA_InitTypeDef DMA_InitStructure; // DMA初始化结构体

    // 配置SPI发送DMA通道(内存到外设)
    DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK;           // 块传输模式
    DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_8BIT; // 8位数据宽度
    DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Increase; // 内存地址自增
    DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Fix;      // 外设地址固定
    DMA_InitStructure.TrigMode = DMA_HardTrig;             // 硬件触发
    DMA_InitStructure.HardTrigSource = SPIx_DMA_TxTrigSource; // SPI发送触发源
    DMA_InitStructure.DMA_TransferCnt = BufferSize;        // 传输数据量
    DMA_InitStructure.DMA_SrcAddress = (uint32_t)TxBuffer; // 源地址(发送缓冲区)
    DMA_InitStructure.DMA_DstAddress = (uint32_t)&SPIx->DR; // 目标地址(SPI数据寄存器)
    DMA_Init(SPIx_TX_DMACHANNEL, &DMA_InitStructure);      // 初始化DMA通道
    DMA_Cmd(SPIx_TX_DMACHANNEL, ENABLE);                   // 使能DMA通道

    // 配置SPI接收DMA通道(外设到内存)
    DMA_InitStructure.DMA_Mode = DMA_MODE_BLOCK;
    DMA_InitStructure.DMA_TransferWidth = DMA_TRANSFER_WIDTH_8BIT;
    DMA_InitStructure.DMA_SrcInc = DMA_SrcAddress_Fix;      // 外设地址固定
    DMA_InitStructure.DMA_DstInc = DMA_DstAddress_Increase; // 内存地址自增
    DMA_InitStructure.TrigMode = DMA_HardTrig;
    DMA_InitStructure.HardTrigSource = SPIx_DMA_RxTrigSource; // SPI接收触发源
    DMA_InitStructure.DMA_TransferCnt = BufferSize;
    DMA_InitStructure.DMA_SrcAddress = (uint32_t)&SPIx->DR;  // 源地址(SPI数据寄存器)
    DMA_InitStructure.DMA_DstAddress = (uint32_t)RxBuffer;   // 目标地址(接收缓冲区)
    DMA_Init(SPIx_RX_DMACHANNEL, &DMA_InitStructure);
    DMA_Cmd(SPIx_RX_DMACHANNEL, ENABLE);
}

SPI 配置函数注释

c 复制代码
/**
 * @brief 配置SPI为16Mbps通信速率
 */
void SPI_Configuration()
{
    SPI_InitTypeDef SPI_InitStructure; // SPI初始化结构体

    // 配置SPI工作参数
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 双线全双工模式
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;                      // 主机模式
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;                  // 8位数据帧
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;                         // 时钟空闲时为低电平
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;                       // 第一个时钟边沿采样
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;                          // 软件控制片选
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; // PCLK/4分频(16MHz)
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;                 // MSB优先
    SPI_InitStructure.SPI_Speed = SPI_Speed_High;                      // 高速模式

#ifdef SPI_SLAVE
    // 从机模式特殊配置
    SPI_InitStructure.SPI_Mode = SPI_Mode_Slave;  // 从机模式
    SPI_InitStructure.SPI_NSS = SPI_NSS_Hard;     // 硬件控制片选(通过CS引脚电平)
#endif

    SPI_Init(SPIx, &SPI_InitStructure); // 初始化SPI
    SPI_Cmd(SPIx, ENABLE);              // 使能SPI外设
}

缓冲区比较函数注释

c 复制代码
/**
 * @brief 比较两个缓冲区内容是否相同
 * 
 * @param pBuffer1 : 第一个缓冲区指针
 * @param pBuffer2 : 第二个缓冲区指针
 * @param BufferLength : 需要比较的长度
 * @return TestStatus 
 *     @arg PASSED: 缓冲区内容完全相同
 *     @arg FAILED: 缓冲区内容存在差异
 */
TestStatus Buffercmp(uint8_t *pBuffer1, uint8_t *pBuffer2, uint16_t BufferLength)
{
    while (BufferLength--)
    {
        if (*pBuffer1 != *pBuffer2) // 逐字节比较
        {
            return FAILED; // 发现不一致立即返回失败
        }
        pBuffer1++;
        pBuffer2++;
    }
    return PASSED; // 全部比较通过返回成功
}

关键功能说明

  • SPI模式:通过宏定义选择主机或从机模式,当前配置为主机。
  • DMA传输:使用DMA通道1和2分别处理SPI的接收与发送,降低CPU负载。
  • 数据校验 :通过Buffercmp函数比较发送和接收缓冲区数据的一致性。
  • 硬件指示:通过GPIO控制LED灯显示传输状态(完成/校验成功)。
相关推荐
小宋同学在不断学习21 分钟前
stm32-掌握SPI原理(一)
stm32·单片机·spi
学不动CV了1 小时前
数据结构---链表结构体、指针深入理解(三)
c语言·arm开发·数据结构·stm32·单片机·链表
工业互联网专业5 小时前
汇编与接口技术:8259中断实验
汇编·单片机·嵌入式硬件·8259中断实验
brave and determined5 小时前
国产MCU学习Day6——CW32F030C8T6: I2C功能详解与应用案例
单片机·eeprom·i2c·cw32f030c8t6·cw32·cw32f030·中断读取eeprom
梁山1号6 小时前
【ESP32】3.串口的发送与接受
单片机·物联网
KaiGer6666 小时前
AUTOSAR进阶图解==>AUTOSAR_SWS_V2XFacilities
单片机·汽车·嵌入式·autosar
desssq8 小时前
SPI通信协议
单片机·嵌入式硬件
星辰pid10 小时前
STM32实现四自由度机械臂(SG90舵机)多功能控制(软件篇freertos)
stm32·单片机·嵌入式硬件·机械臂
森焱森15 小时前
水下航行器外形分类详解
c语言·单片机·算法·架构·无人机
小殷学长17 小时前
【单片机毕业设计17-基于stm32c8t6的智能倒车监测系统】
stm32·单片机·课程设计