2. ARM处理器SPI控制器硬件原理

各位同学,今天我们来啃一块硬骨头——SPI控制器的硬件原理。说实话,很多做驱动开发的朋友,上来就对着寄存器手册写代码,结果遇到时序问题抓瞎了。我个人习惯是,先搞懂硬件怎么工作的,再动手写代码。这样出了问题,你心里有底。

SPI控制器说白了,就是处理器和外部设备之间的一条「串行高速公路」。这条路上有四个关键角色:时钟、数据输入、数据输出和片选。但控制器内部到底是怎么把数据发出去的?又是怎么收数据的?我们一层层剥开来看。

2.1 SPI控制器内部架构

一个典型的ARM SPI控制器,内部主要由三大模块构成:移位寄存器、时钟分频器和FIFO。我画了个框图,大家先有个整体印象。

SPI控制器内部架构框图 CPU总线接口 AHB/APB 控制寄存器组 CR0/CR1/SPIEN CPOL/CPHA/DFS 时钟分频器 PCLK → SCK 移位寄存器 8/16/32位 MOSI/MISO FIFO 发送FIFO / 接收FIFO 深度: 8/16/32级 状态寄存器 BUSY/TXE/RXNE OVR/MODF 数据寄存器 DR 外部引脚 SCK / MOSI / MISO CS(片选) 数据流向: CPU → FIFO → 移位寄存器 → 外部引脚

2.1.1 移位寄存器——SPI的核心

移位寄存器是SPI的心脏。它的工作方式特别有意思:每次时钟脉冲到来,寄存器里的数据就「挪动」一位。发送时,最高位先被推出去;接收时,进来的数据从最低位挤进来。

我记得第一次调SPI驱动时,死活读不到数据。后来发现是移位寄存器的位序搞反了——有些控制器默认MSB first,有些可以配成LSB first。你想想看,如果发送方和接收方位序不一致,数据就全乱了。

关键点:移位寄存器的位宽决定了单次传输的数据长度。常见的有8位、16位和32位。配置时一定要和从设备的数据格式匹配。

2.1.2 时钟分频器——决定传输速度

时钟分频器负责把系统时钟(比如PCLK)分频成SPI的SCK时钟。说白了,就是决定数据能传多快。

分频系数通常是一个偶数,从2到256不等。比如系统时钟100MHz,分频系数设为100,SCK就是1MHz。我建议刚开始调试时,先把时钟设慢一点,比如1MHz以下。等通信稳定了,再慢慢往上提。我曾经在某个项目里,一上来就设了30MHz,结果示波器一看,波形都变形了。

经验之谈:时钟频率不是越高越好。要考虑PCB走线长度、从设备支持的最大频率、信号完整性等因素。保守的做法是:先看从设备数据手册,取它支持频率的80%作为上限。

2.1.3 FIFO——缓解CPU压力

FIFO是个好东西。没有FIFO的SPI控制器,每收发一个字节CPU都要中断一次。有了FIFO,你可以一次塞进去好几个字节,然后该干嘛干嘛去。

FIFO的深度各厂家不一样。有些是8级,有些是16级,高端点的有32级。发送FIFO和接收FIFO通常是独立的。我习惯的做法是:配置FIFO阈值,比如发送FIFO空到一半时触发中断,这样CPU可以批量填充数据。

注意:FIFO不是万能的。如果FIFO深度和DMA配合不好,还是会有数据溢出或欠载的问题。尤其是高速传输时,要算好FIFO的填充和读取节奏。

2.2 SPI时序参数

时序参数是SPI驱动开发中最容易踩坑的地方。CPOL、CPHA、时钟频率,这三个参数配不对,设备就是不理你。

2.2.1 CPOL和CPHA——时钟极性与相位

CPOL决定SCK空闲时的电平:0是低电平空闲,1是高电平空闲。CPHA决定数据在哪个时钟沿采样:0是第一个沿采样,1是第二个沿采样。

组合起来就是四种模式。我整理了个表格,大家对照着看:

模式 CPOL CPHA 空闲SCK电平 数据采样沿 数据输出沿
模式0 0 0 低电平 上升沿 下降沿
模式1 0 1 低电平 下降沿 上升沿
模式2 1 0 高电平 下降沿 上升沿
模式3 1 1 高电平 上升沿 下降沿

最常用的是模式0和模式3。很多SPI Flash和传感器默认就是模式0。我建议你拿到从设备后,第一件事就是看数据手册里的时序图,确认它工作在哪个模式。

避坑指南:我曾经在一个项目里,主控和从设备都是模式0,但就是通信失败。查了两天,最后发现是片选信号拉低后,主控立刻发时钟,而从设备需要片选拉低后等几个微秒才能准备好。这不是CPOL/CPHA的问题,而是片选建立时间的问题。

2.2.2 时钟频率——不是越快越好

时钟频率的选择要考虑三个因素:

  • 从设备支持的最大频率——这是硬上限
  • PCB走线的信号完整性——频率高了,反射和串扰就来了
  • 系统负载——SPI时钟频率越高,CPU中断频率也越高

我一般这样选:先看从设备手册,取最大频率的一半作为起始值。调通了再慢慢往上加,直到出现误码为止。然后回退一档,作为稳定工作频率。

2.3 ARM SPI寄存器详解

搞清楚了硬件架构和时序,接下来就是和寄存器打交道了。不同厂家的ARM芯片,SPI寄存器布局略有差异,但核心寄存器就那么几个。我以最常见的三件套为例:控制寄存器、数据寄存器、状态寄存器。

2.3.1 控制寄存器(CR0/CR1)

控制寄存器负责配置SPI的工作模式。通常包含以下关键位:

位域 名称 功能描述
[1:0] CPOL/CPHA 配置时钟极性和相位,对应四种模式
[5:4] DFS 数据帧大小:00=8位,01=16位,10=32位
[7:6] SCR 时钟分频系数,SCK = PCLK / (2*(SCR+1))
[8] SPIEN SPI使能位,1使能,0禁能
[9] LSB 位序:0=MSB first,1=LSB first
[10] MSTR 主从模式选择:1=主机,0=从机

配置时有个顺序要注意:先配好所有参数,最后再使能SPI。如果先使能再改参数,可能会产生错误的时钟脉冲。

2.3.2 数据寄存器(DR)

数据寄存器是CPU和移位寄存器之间的桥梁。写DR就是把数据送进发送FIFO,读DR就是从接收FIFO取数据。

这里有个细节:有些芯片的DR是16位的,但只用了低8位或低16位。读的时候要注意掩码。我见过有人读32位寄存器,结果把高16位的垃圾数据也读进来了。

/* 以STM32为例,发送和接收数据 */
/* 发送:检查TXE标志,然后写DR */
while (!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = tx_data;

/* 接收:检查RXNE标志,然后读DR */
while (!(SPI1->SR & SPI_SR_RXNE));
rx_data = SPI1->DR;

2.3.3 状态寄存器(SR)

状态寄存器是SPI控制器的「晴雨表」。几个关键标志位:

  • BUSY:SPI正在忙,正在传输数据
  • TXE:发送FIFO空,可以写入新数据
  • RXNE:接收FIFO非空,有数据可读
  • OVR:溢出错误,接收FIFO满了还被写入
  • MODF:模式错误,多主机冲突

重要提醒:读状态寄存器时,有些标志位是「读清除」的。比如OVR标志,读SR寄存器就会自动清零。如果你在中断服务函数里先读SR判断错误,然后又读SR取数据,可能就把错误标志清掉了。正确的做法是:一次读取SR,保存到局部变量,然后判断。

2.4 小结

这一章我们聊了SPI控制器的内部架构、时序参数和关键寄存器。移位寄存器是核心,时钟分频器决定速度,FIFO提升效率。CPOL/CPHA四种模式要记牢,寄存器配置顺序不能乱。