SPI通信外设(硬件)
本文详细介绍了STM32硬件SPI的工作原理及配置方法。SPI接口支持全双工通信,可配置8/16位数据帧、高低位先行模式,时钟频率由PCLK分频获得。文章分析了SPI框图结构,包括移位寄存器、控制逻辑和状态寄存器等关键部件,并阐述了主从模式切换机制。针对W25Q64芯片的SPI通信,提供了初始化配置步骤和连续/非连续传输的实现方法,重点讲解了TXE、RXNE标志位的使用和数据处理流程。最后给出了硬
STM32内部集成了硬件SPI收发电路,可以由硬件自动执行时钟生成、数据收发等功能,减轻CPU的负担可配置8位/16位数据帧、高位先行/低位先行
最常用的配置是 8 位、高位先行
时钟频率: fPCLK / (2, 4, 8, 16, 32, 64, 128, 256)
SPI 的时钟就是 PCLK(外设时钟) 分频得来的,APB1 的 PCLK 是 36MHZ,APB2 的 PCLK 是 72MHZ
支持多主机模型、主或从操作
可精简为半双工/单工通信
支持DMA(如果需要快速传输数据,使用 DMA 自动搬运数据)
兼容I2S协议
STM32F103C8T6 硬件SPI资源:SPI1、SPI2
SPI1 和 SPI2 挂载的总线是不一样的,SPI1挂载在APB2(PCLK 是 72MHZ),SPI2 挂载在APB1(PCLK 是 36MHZ)
SPI 框图

首先左上角核心部分,就是移位寄存器,数据低位一位一位地从 MOSI 移出去,然后 MISO 的数据一位一位地移入数据高位,可以看的出来是低位先行的配置,可以通过 LSBFIRST 控制位,这一位可以控制是低位先行还是高位先行(给 0 先发送 MSB 高位,给 1 先发送 LSB 低位)
接下来左上角画了个方框,把 MOSI 和 MISO 做了交叉,主要是用来进行主从模式引脚变换的,这个 SPI 外设可以作主机也可以做从机,主机和从机的输入输出模式不同,如果要切换主机和从机的话,线路就要交叉一下(作主机的话就不用交叉了)
这两个缓冲区(接收缓冲区,发送缓冲区)实际上就是数据寄存器 DR ,发送缓冲区是发送寄存器 TDR
接收缓冲区是接收寄存器 RDR,TDR 和 RDR 占用同一个地址,统一叫做 DR
左上角核心部分体现了发送和接收的执行流程
右下角是一些控制逻辑,首先的是波特率发送器用于产生 SCK 时钟的,内部主要是一个分频器,输入时钟是 PCLK(72M 或 36M),经过分频器之后输出到 SCK 引脚
然后右边 SPI_CR1 寄存器的三个位 BR0、BR1、BR2 用来控制分频系数

这三位写入上面这些值,可以对 PCLK 时钟执行 2~256 的分频,分频后就是 SCK 时钟
接着后面这些通信电路和各种寄存器,比如LSBFIRST这一位可以控制是低位先行还是高位先行,SPE 是 SPI 使能 就是 SPI_Cmd 函数配置的位,BR 配置波特率,就是 SCK 时钟,MSTR 配置主从模式(1 是主模式,0 是从模式)CPOL 和 CPHA 是用来选择 SPI 的四种模式,SR 状态寄存器,TXE 发送寄存器空,RXNE 接收寄存器非空
最后还有 NSS 引脚,就是从机选择,低电平有效所以前面加了 N,但是硬件的 NSS 设计更偏向实现多主机,总的来说这个 NSS 并不会用到,SS 引脚直接使用一个 GPIO 模拟就行
SPI 基本结构

移位寄存器高位移出通过 GPIO 到 MOSI 从 MOSI 输出,移入的数据从 MISO 进来,通过 GPIO 到移位寄存器的低位,这样循环8次就能实现主机和从机交换一个字节,然后 TDR 和 RDR 的配合可以实现连续的数据流,另外,TDR 数据整体转入移位寄存器的时候置 TXE 标志位,移位寄存器数据整体转入 RDR 的时刻,置 RXNE 标志位
然后波特率发生器产生时钟输出到SCK引脚,数据控制器就看成一个管理员,控制着所有电路的运行,最后开关控制也就是 SPI_Cmd,另外 SS 从机选择引脚使用 GPIO 模拟即可
主模式全双工连续传输

输出的流程:
CPOL = 1 CPHA = 1 说明是 SPI 模式 3,所以 SCK 默认是高电平,然后在第一个下降沿 MOSI 和 MISO 移出数据,之后上升沿移入数据,依次这样来进行
第二行是 MOSI 和 MISO 输出的波形,跟随 SCK 时钟变换,数据位依次出现
第三行是 TXE 发送寄存器空标志位
第四行是发送缓冲器 TDR (发送数据寄存器)写入 SPI_DR
BSY(BUSY)是由硬件自动设置和清除的
输入的流程:
第一个是 MISO/MOSI 的输入数据
第二个是 RXNE 接收数据寄存器非空标志位
第三行是接收缓冲器 RDR (接收数据寄存器)读入 SPI_DR
分析:
首先 SS 至低电平开始时序,在刚开始时,TXE为1表示TDR空,可以写入数据开始传输,然后第1步就是软件写入0xF1至SPI_DR,0xF1就是要发送的第1个数据,可以看到写入之后,TDR变为0xF1,同时,TXE变为0表示TDR已经有数据了, TDR是等候区移位寄存器才是发送区,移位寄存器刚开始是没有数据的,所以在TDR的0xF1就会立刻转入移位寄存器开始发送,转入瞬间,至TXE标志位为0,表示发送寄存器空,然后移位寄存器有数据了,然后波形就自动开始生成,在移位产生0xF1的波形的同时,等待区TDR是空的,要把下一个数据写入到TDR里面等着
第2步操作是写入0xF1后软件等待TXE=1,然后写入0xF2至SPI_DR,之后的发送流程也是同理,0xF1数据波形产生完毕后,0xF2转入移位寄存器开始发送,这时TXE=1,我们尽快把下一个数据0xF3放到TDR里面等着
也就是第3步软件等待TXE=1,然后写入0xF3至DR,最后在这里,如果我们只想发送三个数据,0xF3转入数据后,TXE=1我们就不需要继续写入了,注意在最后一个TXE=1之后还需要等待一段时间,等0xF3的波形完整发送,等所有波形完整发送后,BUSY标志由硬件清除,这些就是发送的流程
SPI 是全双工,发送的同时还有接收,在第一个字节发送完成后,第一个字节的接收也完成了,接收到的数据一是0xA1,这时移位寄存器的数据整体转入RDR,转入的同时RXNE标志位也置1,表示收到数据了
也就是第一步软件等待 RXNE =1,表示收到数据了,然后从 SPI_DR,也就是 RDR 读出数据,接收之后,软件清除RXNE标志位,然后当下一个数据2收到的时候,RXNE重新置1,我们检测到RXNE=1时继续读出RDR
也就是第2步软件等待RXNE=1,然后从SPI_DR读出 0xA2,最后在最后一个时序完全产生之后,数据三才能收到
也就是第3步,软件等待RXNE=1,然后从SPI_DR读出0xA3,注意当一个字节收到后,移位寄存器的数据自动转入RDR会覆盖原有的数据,所以读取RDR要及时,这就是接收的流程
总结:
这个连续数据流对软件的配合要求较高,在每个标志位会产生后,数据都要及时处理,如果对传输效率有非常高的要求,就可以使用这个主模式全双工连续传输
非连续传输

配置还是SPI模式3, SCK默认高电平,想发数据时,如果检测到 TXE=1 时TDR为空
也就是第1步软件写入0xF1至SPI_DR,这时候TDR的值变为0xF1,TXE变为0,目前移位寄存器也是空的状态,所以这个0xF1会立即转入移位寄存器开始发送,波形产生并且TXE置回1,表示可以把下一个数据放TDR里面了,但在非连续传输这里,TXE=1,并不着急把下一个数据写进去,而是等待,等第1个字节时序结束后,也意味着接收第1个字节也完成了,这时接收的RXNE会置1,我们等待RXNE置1后,先把第1个接收到的数据读出来,之后再写入下一个字节数据
也就是第2步,软件等待TXE=1,但是较晚写入0xF2至SPI_DR,当较晚写入TDR后,数据2开始发送,也是一样,先不急着写入数据上,而是等到时序结束后,先把接收的数据2收着,然后再写入数据3,等数据三的时序结束后,再接收数据上置换回来的数据
整体步骤就是第1步等待TXE为1,第2步写入发送的数据至TDR,第3步等待RXNE为1,第4步读取RDR接收的数据,之后交换第2个字节重复这4步,这就是非连续传输的流程
但非连续传输缺点就是,没有及时把下一个数据写进TDR候着,所以等到第1个字节时序结束后,第2个字节还没送过来,那么这个数据传输将会在这边等,会产生间隙,拖慢了整体数据传输的速度,这个间隙在SCK频率低的时候影响不大,但是在SCK频率非常高的时候,这个间隙会拖后腿
硬件 SPI 读写 W25Q64
根据前面的介绍和框图和流程来实现读取 W25Q64
SPI 常用库函数
SPI_Init 初始化
参数:
SPI_Mode 选择 SPI 的模式(决定设备是主机还是从机)
SPI_Direction 配置 SPI 裁剪引脚(最常用的就是标准模式双线全双工)
SPI_DataSize 配置数据帧
SPI_FirstBit 配置高位先行/低位先行
SPI_BaudRatePrescaler 波特率预分频器(分频系数越大,SCK 的时钟频率越小传输越慢)
SPI_CPOL 配置 SPI 模式 时钟极性
SPI_CPHA 配置 SPI 模式 时钟相位
SPI_NSS NSS模式
SPI_CRCPolynomial CRC校验的多项式(默认值 7)
SPI_I2S_DeInit 恢复缺省配置
SPI_StructInit 结构体变量初始化
SPI_Cmd 外设使能
SPI_I2S_ITConfig 中断使能
SPI_I2S_DMACmd DMA使能
SPI_I2S_SendData 写 DR 数据寄存器(TDR)
SPI_I2S_ReceiveData 读 DR 数据寄存器(RDR)
SPI_I2S_GetFlagStatus 读取标志位
SPI_I2S_ClearFlag 清除标志位
SPI_I2S_GetITStatus 读取中断标志位
SPI_I2S_ClearITPendingBit 清除中断标志位
SPI 初始化
首先第一步开启时钟,开启 SPI 和 GPIO 的时钟
第二步初始化 GPIO 口 SCK MOSI 是外设控制的所以配置为复用推挽输出,MISO 是外设输入的,可以配置为上拉输人,SS 是软件控制的输出信号,所以配置为复用推挽输出
第三步 配置 SPI 外设,结构体选参数即可,调用一下 SPI_Init 配置各种参数如:8 位/16 位数据帧、高位先行/低位先行、SPI 模式
第四步开关控制,调用 SPI_Cmd 使能
void MySPI_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
SPI_InitTypeDef SPI_InitStructure;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_128;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1,&SPI_InitStructure);
SPI_Cmd(SPI1,ENABLE);
MySPI_W_SS(1);
}
非连续传输

参考这个时序来执行,产生交换字节的时序,主要就是写 DR、读 DR 和获取状态标志位
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE != SET));
SPI_I2S_SendData(SPI1,ByteSend);
while(SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE != SET));
return SPI_I2S_ReceiveData(SPI1);
}
注意:这里硬件 SPI 必须是发送同时接收,要想接收必须得先发送,只有给 TDR 写东西,才会触发时序的生成,如果不发送只调用接收函数,那时序是不会动的
还有 TXE 和 RXNE 标志位是会自动清除的,TXE 在写入 SPI_DR 时被清除,读 SPI_DR 时,RXNE 标志位被清除
更多推荐



所有评论(0)