4. SPI控制器硬件初始化:时钟使能、GPIO复用配置、SPI控制器寄存器初始化
SPI控制器硬件初始化是驱动开发的基础环节,直接影响后续DMA传输的稳定性和吞吐量上限。本节按照硬件初始化流程,依次讲解时钟使能、GPIO复用配置以及SPI控制器核心寄存器(波特率、数据格式、中断使能)的初始化方法。
4.1 时钟使能
时钟使能是SPI模块正常工作的前提。在VxWorks中,通常通过操作SoC的时钟管理单元(CMU/CCM)寄存器来开启SPI外设时钟。不同平台(如i.MX6、STM32MP1、Zynq)的时钟树结构不同,但操作逻辑一致:找到对应SPI实例的时钟门控位,置位使能。
| 平台 | 时钟寄存器示例 | 使能位 | 注意事项 |
|---|---|---|---|
| NXP i.MX6 | CCM_CCGRx | CG位(0b11为全使能) | 需同时使能SPI和GPIO时钟域 |
| STM32MP1 | RCC_MP_APBxENSETR | SPIxEN位 | 复位后默认关闭,需显式使能 |
| Xilinx Zynq | SLCR.SPI_CLK_CTRL | CLKACT位 | 还需配置时钟源分频 |
/* i.MX6 SPI时钟使能示例(VxWorks BSP层) */
#define CCM_CCGR1 0x020C406C
#define SPI1_CLK_ENABLE (0x3 << 20) /* CG位[21:20] */
void spiClockEnable(void)
{
UINT32 regVal;
regVal = sysIn32(CCM_CCGR1);
regVal |= SPI1_CLK_ENABLE;
sysOut32(CCM_CCGR1, regVal);
}
4.2 GPIO复用配置
SPI信号线(SCLK、MOSI、MISO、CS)必须通过GPIO复用控制器(IOMUX)配置为SPI功能模式。错误的复用配置会导致信号无法正确路由到SPI控制器引脚。
典型配置步骤:
- 查找引脚复用表:根据原理图确定SPI引脚对应的Pad和复用模式(ALT值)。
- 配置复用寄存器:将对应Pad的MUX_MODE设置为SPI功能。
- 配置电气特性:设置驱动能力(DSE)、压摆率(SRE)、上下拉(PUE/PUS)等参数。
- 配置GPIO方向:对于CS引脚,若使用软件控制,需配置为GPIO输出模式。
/* i.MX6 SPI1引脚复用配置示例 */
#define IOMUXC_SW_MUX_CTL_PAD_CSI0_DAT4 0x020E01B4
#define IOMUXC_SW_PAD_CTL_PAD_CSI0_DAT4 0x020E0468
void spiGpioMuxConfig(void)
{
/* 配置CSI0_DAT4为SPI1_SCLK (ALT2) */
sysOut32(IOMUXC_SW_MUX_CTL_PAD_CSI0_DAT4, 0x2);
/* 设置驱动强度: 0x1B0 = 100kΩ下拉, 驱动等级R0/6 */
sysOut32(IOMUXC_SW_PAD_CTL_PAD_CSI0_DAT4, 0x1B0);
/* 类似配置MOSI、MISO、CS引脚... */
}
4.3 SPI控制器寄存器初始化
SPI控制器寄存器初始化是硬件配置的核心,直接决定通信速率、数据格式和中断行为。以下以典型SPI控制器(兼容MCSPI/ECSPI)为例,讲解三个关键寄存器组。
4.3.1 波特率配置
SPI波特率由时钟源频率和分频系数共同决定。控制器通常提供预分频器和位周期计数器两级分频。
/* SPI波特率计算公式 */
SPI_CLK = CLK_SOURCE / (PRESCALER + 1) / (2 * (BIT_COUNTER + 1))
/* 示例:目标波特率10MHz,时钟源60MHz */
/* 选择PRESCALER=0(不分频),BIT_COUNTER=2 */
/* SPI_CLK = 60MHz / 1 / (2 * 3) = 10MHz */
| 寄存器字段 | 位域 | 典型值 | 说明 |
|---|---|---|---|
| PRESCALER | [15:12] | 0x0 | 预分频值,0~15对应1~16分频 |
| BIT_COUNTER | [11:8] | 0x2 | 位周期计数器,实际分频为2*(n+1) |
| CLK_POL | [6] | 0/1 | 时钟极性:0=空闲低,1=空闲高 |
| CLK_PHA | [7] | 0/1 | 时钟相位:0=第一个边沿采样,1=第二个边沿采样 |
4.3.2 数据格式配置
数据格式寄存器定义每帧的位宽、传输顺序(MSB/LSB优先)以及字对齐方式。对于DMA传输,建议将数据格式配置为与DMA传输宽度匹配(如8位、16位或32位)。
/* 数据格式配置示例:8位数据,MSB优先 */
#define SPI_CONREG_CHANNEL_MODE (0x0 << 4) /* 通道模式:主模式 */
#define SPI_CONREG_DATA_RATE (0x2 << 8) /* 波特率分频 */
#define SPI_CONREG_DATA_WIDTH (0x7 << 20) /* 8位数据宽度 */
#define SPI_CONREG_MSB_FIRST (0x0 << 23) /* MSB优先 */
#define SPI_CONREG_SPI_ENABLE (0x1 << 0) /* SPI使能 */
sysOut32(SPI1_BASE + CONREG_OFFSET,
SPI_CONREG_CHANNEL_MODE |
SPI_CONREG_DATA_RATE |
SPI_CONREG_DATA_WIDTH |
SPI_CONREG_MSB_FIRST);
4.3.3 中断使能配置
中断使能寄存器控制SPI控制器在哪些事件下产生中断。对于DMA传输模式,通常只使能错误中断(溢出、帧错误),而数据就绪中断由DMA控制器接管。
| 中断类型 | 使能位 | DMA模式建议 | 说明 |
|---|---|---|---|
| 数据就绪(RX_READY) | INTEN[0] | 关闭 | 由DMA请求信号替代 |
| 发送空(TX_EMPTY) | INTEN[1] | 关闭 | 由DMA请求信号替代 |
| 溢出错误(OVRFLOW) | INTEN[2] | 开启 | 检测DMA传输异常 |
| 帧错误(FRAME_ERR) | INTEN[3] | 开启 | 检测SPI协议错误 |
| DMA完成(DMA_DONE) | INTEN[4] | 可选 | 用于DMA传输完成回调 |
/* 中断使能配置:仅使能错误中断 */
#define SPI_INTEN_OVRFLOW_EN (0x1 << 2)
#define SPI_INTEN_FRAME_ERR_EN (0x1 << 3)
sysOut32(SPI1_BASE + INTEN_OFFSET,
SPI_INTEN_OVRFLOW_EN | SPI_INTEN_FRAME_ERR_EN);
4.4 硬件初始化完整流程
下图展示了SPI控制器硬件初始化的完整流程及各步骤的依赖关系:
4.5 初始化代码模板
以下给出一个完整的SPI控制器硬件初始化函数模板,整合了时钟、GPIO和寄存器配置:
/* spiHwInit.c - SPI硬件初始化完整实现 */
#include "vxWorks.h"
#include "sysLib.h"
/* 寄存器基地址(以i.MX6 ECSPI1为例) */
#define SPI1_BASE 0x02008000
#define SPI1_CONREG (SPI1_BASE + 0x00)
#define SPI1_CONFIGREG (SPI1_BASE + 0x04)
#define SPI1_INTEN (SPI1_BASE + 0x0C)
#define SPI1_TESTREG (SPI1_BASE + 0x10)
/* 时钟和GPIO配置宏(由BSP提供) */
#define CCM_CCGR1 0x020C406C
#define SPI1_CLK_ENABLE (0x3 << 20)
STATUS spiHwInit(UINT32 baudRateHz, UINT8 dataBits, UINT8 mode)
{
UINT32 clkDiv;
UINT32 conRegVal;
/* 步骤1:时钟使能 */
sysOut32(CCM_CCGR1, sysIn32(CCM_CCGR1) | SPI1_CLK_ENABLE);
sysMsDelay(1); /* 等待时钟稳定 */
/* 步骤2:GPIO复用配置(调用BSP函数) */
if (spiGpioMuxConfig() != OK) {
return ERROR;
}
/* 步骤3:计算波特率分频 */
clkDiv = spiCalcBaudDiv(baudRateHz);
if (clkDiv == 0) {
return ERROR;
}
/* 步骤4:配置SPI控制寄存器 */
conRegVal = (0x0 << 4) /* 主模式 */
| (clkDiv << 8) /* 波特率分频 */
| ((dataBits - 1) << 20) /* 数据宽度 */
| (0x0 << 23) /* MSB优先 */
| (mode << 6); /* 时钟极性和相位 */
sysOut32(SPI1_CONREG, conRegVal);
/* 步骤5:配置中断使能(仅错误中断) */
sysOut32(SPI1_INTEN, (0x1 << 2) | (0x1 << 3));
/* 步骤6:使能SPI控制器 */
sysOut32(SPI1_CONREG, sysIn32(SPI1_CONREG) | 0x1);
return OK;
}
4.6 初始化验证清单
完成初始化后,使用以下清单逐项验证,确保硬件配置正确:
| 验证项 | 验证方法 | 预期结果 |
|---|---|---|
| 时钟使能状态 | 读取时钟门控寄存器 | 对应SPI位为0b11 |
| GPIO复用模式 | 读取IOMUX寄存器 | MUX_MODE为SPI ALT值 |
| 波特率实际值 | 示波器测量SCLK引脚 | 误差<5% |
| 数据格式 | 发送已知数据并回环 | 接收数据与发送一致 |
| 中断使能 | 读取INTEN寄存器 | 错误中断位为1,数据中断位为0 |
通过以上步骤,SPI控制器的硬件初始化即告完成,为后续DMA传输配置奠定了稳定的硬件基础。