当前位置: 首页 > news >正文

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

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

目录

一.CW32F030C8T6 SPI 功能

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

宏定义部分

枚举与全局变量

主函数

时钟配置函数

GPIO 配置函数注释

DMA 配置函数注释

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)

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

宏定义部分

#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)  //缓冲区大小

枚举与全局变量

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; //传输状态标志

主函数

int32_t main(void)
{RCC_Configuration();   //初始化系统时钟和外设时钟GPIO_Configuration();  //配置GPIO引脚和复用功能DMA_Configuration();   //配置DMA通道和触发源SPI_Configuration();   //配置SPI模块参数(模式、速率等)#ifdef SPI_MASTERSPIx_CS_LOW(); //主机模式下拉低CS,选中从机
#endif#ifdef SPI_SLAVESPI_FlushSendBuff(SPIx); //从机模式下清空发送缓冲区
#endifSPI_DMACmd(SPIx, SPI_DMAReq_Tx | SPI_DMAReq_Rx, ENABLE); //启用SPI DMA传输while (1){if (RxBuffer[BufferSize - 1] == 0x55) //检测接收完成标志{
#ifdef SPI_MASTERSPIx_CS_HIGH(); //主机模式下释放CS
#endifPB01_SETHIGH(); //点亮LED1指示传输完成TransferStatus = Buffercmp(RxBuffer, TxBuffer, BufferSize); //校验数据if (TransferStatus == PASSED) {PA07_SETHIGH(); //校验成功时点亮LED2}}}
}

时钟配置函数

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 配置函数注释

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 配置函数注释

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 配置函数注释

/*** @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引脚电平)
#endifSPI_Init(SPIx, &SPI_InitStructure); // 初始化SPISPI_Cmd(SPIx, ENABLE);              // 使能SPI外设
}

缓冲区比较函数注释

/*** @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灯显示传输状态(完成/校验成功)。

http://www.dtcms.com/a/267059.html

相关文章:

  • Django中关于templates目录和static目录存放位置的总结
  • 基于开源AI智能名片链动2+1模式的S2B2C商城小程序:门店私域流量与视频号直播融合的生态创新研究
  • 【51单片机】51单片机学习笔记-课程简介
  • 权电阻网络DAC实现电压输出型数模转换Multisim电路仿真——硬件工程师笔记
  • 共射级放大电路的频率响应Multisim电路仿真——硬件工程师笔记
  • 程序计数器(PC)是什么?
  • 一个简单的分布式追踪系统
  • 【AI大模型面试八股文】大模型训练中如何应对灾难性遗忘问题?
  • 快速掌握Python编程基础
  • 【Qt】事件处理、事件分发器、事件过滤器
  • Ionic 安装使用教程
  • CPU指令集权限
  • mysql基础(一)快速上手篇
  • Swift 安装使用教程
  • 百度AI文心大模型4.5系列开源模型评测,从安装部署到应用体验
  • Python区块链服务及API实现
  • 物联网软件层面的核心技术体系
  • Day51 复习日-模型改进
  • Python 的内置函数 reversed
  • 系统移植基础部分
  • Resource punkt_tab not found. NLTK
  • Docker Desktop 安装到D盘(包括镜像下载等)+ 汉化
  • JxBrowser 7.43.3 版本发布啦!
  • 数据结构---线性表理解(一)
  • 【unitrix】 4.16 类型级别左移运算实现解析(shl.rs)
  • spring-ai-alibaba 1.0.0.2 学习(十)——各种工具调用方式对比
  • Python 闭包(Closure)实战总结
  • 【网络与系统安全】强制访问控制——BLP模型
  • PortSwigger Labs SQLInjection LAB6-7
  • 汽车功能安全【ISO 26262】概述1