大家好,欢迎来到IT知识分享网。
作者:junziyang
(注:如非特別声明,以下笔记内容均针对STM32F103ZET6而言。不同型号,细节可能存在差别。)
在头条发技术贴是最没成就感的,为了看到更多有营养的资讯,碰到自己喜欢的技术贴请一定记得,收藏+转发+点赞。
一、SPI接口简介
SPI是Serial Peripheral Interface的缩写,即串行外设接口。通过SPI接口可以与外设进行半/全双工、同步、串行通信。SPI支持双向通信且最多只需要四根线,有利于节省芯片管脚和PCB板上的空间。与USART异步通信不同,SPI是一种同步通信方式,由主机提供通信时钟(SCK),主从机在SCK同步下工作。SPI支持CRC校验,以提高通信的可靠性。SPI通信常用于主机与多个从机间的同步通信。
注意:
- 在STM32的high-density、XL-density和Connectivity line系列芯片中,SPI接口可支持SPI协议或I2S音频协议。默认情况下实现的是SPI协议,但通过软件可以将该接口切换到I2S。
- 由于JTAG调试接口占用了SPI3/I2S3的部分引脚,配置SPI3/I2S3之前需要停用JTAG接口,如需调试接口,可以改为SWD接口。
二、SPI工作原理
任何外设的功能实现都是通过操作专用寄存器实现的。STM32为SPI配置了9个32bit的寄存器,寄存器名称、地址分配及复位值表如图1所示。在后续的学习中会经常提到相关的配置位,可参考图1.
图1. SPI寄存器地址分配与复位值表
这9个寄存器中,用于SPI接口的实际只有7个,最后两个用来配置I2S的功能。
2.1 原理框图
图2. SPI原理框图
SPI框图如图2所示。最左侧是SPI的4个引脚,用来连接外部设备,各引脚功能如下:
- MISO(Master In / Slave Out):主入从出数据引脚。主模式下作为数据输入(接收)端,从模式下作为数据输出(发送)端。
- MOSI(Master Out / Slave In):主出从入数据引脚。主模式下作为数据输出(发送)端,从模式下作为数据输入(接收)端。
- SCK(Serial Clock):同步时钟引脚。SPI是一种同步通信方式,主模式下,由此引脚输出通信时钟;从模式下,由此引脚输入通信时钟。
- NSS(Slave Select):片选引脚。这是一个可选引脚,可输入和输出,通过SPI_CR2的SSOE位配置。当有多个从机时,主机和所有从机的NSS引脚连接在一起,主机通过NSS引脚选择与之单独通信的从机,以避免数据线上的冲突。通过将主机SPI的SSOE置1,可将其NSS引脚电平拉低。这样,所有连到该引脚的其他设备的NSS引脚就会收到低电平。其中被配置为NSS硬件模式(SPI_CR1的SSM=0)的设备,NSS引脚收到低电平后会变为从机。NSS被配置为输入的主模式SPI(MSTR=1且SSOE=0),如果NSS被拉低,则进入主模式故障状态:MSTR位被清除,也会自动由主模式切换为从模式。
图3. 单主机单从机互联示意图
图3所示是单个主机与单个从机互联示意图。同名引脚连接到一起(输入接输出),这样可以实现主从设备间数据的串行传输。若NSS由软件管理,则无需连接。
通信由主机发起,通过MOSI引脚将数据发送到从机,从机则通过MISO引脚进行应答。这样,在主机SCK引脚提供的通信时钟信号的同步下,实现了数据的同时接收和发送,即全双工通信。在不用NSS的情况下,SPI仅需3条线,即可实现全双工通信。
2.2 从机选择(NSS)
在存在多个从机的情况下,从机选择可以通过硬件或软件方式实现,通过SPI_CR1寄存器的SSM(Software Slave Management )位配置。
1. 软件NSS管理(SSM=1)
软件模式下,通过设置SPI_CR1寄存器SSI位的值来选择从机。软件模式下NSS引脚闲置,可以用于其他目的。
2. 硬件NSS管理(SSM=0)
硬件模式下,根据NSS输出配置的不同(即SSOE位的值),有两种方案:
(1) NSS输出使能(SSM=0,SSOE=1)
此方案仅适用于处于主模式的设备。开始通信时,主机将NSS引脚被拉低并一直保持,直至通信结束。这样NSS引脚与主机NSS引脚相连的所有设备都被设为从机。
(2) NSS输出关闭(SSM=0,SSOE=0)
此方案对处于主模式和从模式的设备均适用,但有不同的影响:
- 对处于主模式的设备,赋予其多主能力;
- 对于处于从模式的设备,NSS引脚仍执行典型的NSS输入,即NSS为低电平时,从机被选中;NSS为高电平时,从机被取消选中。
2.3 时钟相位与极性
SPI传输数据是在时钟的同步下实现的,每个时钟脉冲传输1个bit。为了适应不同设备SPI接口的收发规则,STM32的SPI接口可以通过软件配置出四种可能的时序关系。配置方法是设置SPI_CR1寄存器的CPOL和CPHA位:
- CPOL(clock polarity)位控制无数据传输时SCK引脚的电平(闲时电平)。若CPOL=0,空闲时SCK引脚为低电平;若CPOL=1,空闲时SCK引脚为高电平。该位在主模式和从模式下均有效。
- CPHA(clock phase)位控制在时钟脉冲的第几个边沿进行数据采样。若CPHA=1,在时钟信号的第2个边沿(CPOL=0时为下降沿,CPOL=1时为上升沿)启动MSB位捕获。即,SCK引脚出现第2次时钟电平转换时锁存数据;若CPHA=0,则在时钟信号的第1个边沿(CPOL=0时为上升沿,CPOL=1时为下降沿)启动MSB位捕获。即,SCK引脚出现第1次时钟电平转换时,锁存数据。
图4. SPI四种时序关系示意图
图4所示为4种时序关系的示意图。实践中需要按照SPI从设备的时钟要求进行配置,因为从设备的协议在出厂时就设置好了,且一般不可配置。通信双方在时钟脉冲的一个边缘发送,另一个边沿接收。
2.4 数据帧格式
数据从SPI_DR寄存器中移出时,既可以MSB优先,也可以LSB优先。通过SPI_CR1寄存器的LSBFRST位进行设置:LSBFRST=0,高位优先;LSBFRST=1,低位优先。图4所示为高位优先的时序。
数据帧的长度可以为8位或16位,由SPI_CR1寄存器的DFF位进行设置。数据帧格式选定后,发送和接收都按此格式。
三、SPI工作模式配置
SPI不仅支持主/从模式、全/半双工模式,主、从机还可只接收或只发送。根据需要可以配置出多达8种工作模式。如图5所示。
图5. STM32CubeIDE中SPI可模式和参数设置选项
不同的模式是通过配置SPI_CR1寄存器来实现的。下面分别说明这些模式的设置方法和工作过程。
3.1 将SPI配置为从模式
在从模式下,SPI在主机时钟的同步下工作,从SCK引脚接收来自主机的时钟。数据传输速率由主机中的设置决定,从机的数据速率设置(SPI_CR1寄存器BR[2:0]位)无效。从模式下,数据从MOSI引脚输入,从MISO引脚输出。
注意事项:
- 为了避免出现数据传输错误,建议在主机发送时钟前先使能从机。
- 在通信时钟的第一个边沿到来前,或正在进行的通信结束前,从机的数据寄存器都需要一定的准备时间。从机和主机使能前,通信时钟的极性必须稳定。
1. 配置步骤
按下述步骤配置SPI_CR1寄存器,可将SPI配置为从模式:
- 设置DFF位,选择数据格式:8位或16位;
- 设置CPOL和CPHA,配置时钟极性和相位。从机设置必须与主机一致;
- 设置SPI_CR1的LSBFIRST,配置帧格式(MSB/LSB优先)。从机设置必须与主机一致;
- 硬件模式下(SSM=0),在传输序列结束字节传输期间,NSS引脚必须为低电平;软件模式下,SPI_CR1寄存器中设置:SSM=1,SSI=0;
- SPI_CR1寄存器中设置:MSTR=0,SPE=1。前者设为从模式,后者使能SPI。
2. 发送过程
- 一个写周期中数据被并行载入Tx缓存(Tx buffer,参见图3左上角)。
- 第一个数据位发送出去后(从设备收到时钟信号,MOSI引脚上出现第一个数据位),剩余数据位被并行载入移位寄存器(Shift register)。
- 数据被移走后Tx缓存为空,SPI_SR寄存器的TXE (Tx buffer empty)被置1。如果设置了SPI_CR2中的TXEIE=1,则会产生中断。
3. 接收过程
对接收器,数据传输结束时:
- 移位寄存器中的数据被转移到Rx缓存中,SPI_SR寄存器的RXNE位被置1;
- 如果设置了RXNEIE位,则会产生中断。
接收器在接收到最后一个取样时钟边沿时,将移位寄存器中收到的数据移入Rx缓存。读SPI_DR寄存器时,返回的是Rx缓存中的数据,读取后会自动清除RXNE位。
3.2 将SPI配置为主模式
在主模式配置下,SCK引脚上发出串行时钟。主模式下,数据从MISO引脚输入,从MOSI引脚输出。
1. 配置步骤
按下述步骤配置SPI_CR1寄存器,可将SPI配置为主模式:。
- 设置SPI_CR1寄存器的BR[2:0]位,定义串行时钟SCK的波特率。波特率为fPCLK/2^(n+1),其中n为BR位的值,n越大,波特率越低。fPCLK是SPI挂载到的外设总线的时钟频率;
- 设置CPOL和CPHA位,定义串行时钟极性和相位;
- 设置DFF位,定义数据格式:8位或16位;
- 设置SPI_CR1的LSBFIRST,定义帧格式(MSB/LSB优先);
- 硬件模式下,如果输入模式中需要NSS引脚,在传输结束期间将NSS拉高;软件模式下,在SPI_CR1寄存器中设置:SSM=1,SSI=1。如果输出模式中需要NSS引脚,只需设置SSOE=1即可。
- SPI_CR1寄存器中设置:MSTR=1,SPE=1。如果NSS引脚连接到高电平,这两个位始终保持置1状态。
2. 发送过程
- 数据字节被写入Tx缓存;
- 在第1个bit发送期间,数据字节经由内部总线被并行载入移位寄存器,然后串行移出到MOSI引脚。高位优先还是低位优先由LSBFIRST位设置。
- 数据从Tx缓存被移入移位寄存器后,TXE位被置1。如果设置了TXEIE位,则会产生中断。
3. 接收过程
对接收器,数据传输结束时:
- 移位寄存器中的数据被转移到Rx缓存中,RXNE位被置1;
- 如果设置了RXNEIE位,则会产生中断。
接收器在接收到最后一个取样时钟边沿时,设置RXNE位,并将移位寄存器中收到的数据移入Rx缓存。读DR寄存器时,返回的是该缓存中的数据。读DR寄存器会自动清除RXNE位。
总结:
可以看出,主、从机发送和接收过程是一模一样的。配置过程也非常类似,只是主机需要配置波特率,而从机不用;从模式MSTR和SSI均为0,而主模式二者均为1。
发送一旦启动,只要有待发送数据被移入Tx缓存,发送过程会一直持续下去。需要注意的是,每次准备写入Tx缓存前,应确认TXE标志位是1。
注意事项:
当一个主机正在与多个从机通信时,如果需要在两次传输之间断开从机,可以配置一个GPIO来充当从机的NSS引脚并通过软件操控该引脚的电平进行切换。
3.3 将SPI配置为半双工通信
SPI可以工作于半双工模式,有两种配置方法:
- 1时钟线+1双向数据线
- 1时钟线+1单向数据线(单接收或单发送)
1. 1时钟线+1双向数据线(BIDIMODE=1)
将SPI_CR1寄存器的BIDIMODE位置1,即可开启这种模式。SCK引脚作为时钟,主机的MOSI或从机的MISO引脚作为数据通道。传输方向由SPI_CR1寄存器的BIDIOE位设置。BIDIOE=1,数据线作为输出端,反之则作为输入端。
2. 1时钟线+1单向数据线(BIDIMODE=0)
这种模式下,SPI可工作于单向发送或单向接收模式,由RXONLY位进行进一步配置。
1) 单向发送模式(BIDIMODE=0,RXONLY=0)
此模式与全双工模式类似:数据由发送引脚(主机的MOSI或从机的MISO)发送,闲置的接收引脚(主机的MISO或从机的MOSI)可用作GPIO。配置为单向发送模式的SPI,Rx缓存中没有发送的值,程序中忽略它即可。
2) 单向接收模式(BIDIMODE=0,RXONLY=1)
程序中将SPI的RXONLY位置1,将会关闭其输出功能。闲置的输出引脚(主机的MOSI或从机的MISO)可作为GPIO,用作其他目的。
按如下步骤配置和使能SPI,以启动单向接收模式通信:
- 主模式下,SPI使能后通信立即开始,清除SPE位后通信立即停止。该模式下无需读取SPI_SR寄存器的BSY标志位,来判断SPI是否正忙。只要SPI通信正在进行,该位一定为1。
- 从模式下,只要NSS被拉低(或软件模式下SSI被清除)且SCK仍在运行,SPI就会持续接收。
总结:
通过配置BIDIMODE、MSTR、BIDIOE和RXONLY这4个位,可配置出8中SPI工作模式:
- 全双工主机(BIDIMODE=0,RXONLY=0,MSTR=1)
- 全双工从机(BIDIMODE=0,RXONLY=0,MSTR=0)
- 半双工主机(BIDIMODE=1,BIDIOE=1,MSTR=1),双向模式-发送
- 半双工从机(BIDIMODE=1,BIDIOE=0,MSTR=0),双向模式-接收
- 单向发送主机(BIDIMODE=0,RXONLY=0,MSTR=1)
- 单向发送从机(BIDIMODE=0,RXONLY=0,MSTR=0)
- 单向接收主机(BIDIMODE=0,RXONLY=1,MSTR1)
- 单向接收从机(BIDIMODE=0,RXONLY=1,MSTR=0)
STM32CubeIDE中的配置界面如图5所示。
四、数据发送和接收过程
4.1 Rx和Tx缓存
如图3所示,SPI的收、发端内部各有一个缓存寄存器。传输过程中,接收端将接收到的数据先存入Rx缓存,发送端数据在被发送前也是先被存入Tx缓存。这两个内部缓存对外部程序不可见,程序中对SPI_DR的读操作,返回的是Rx缓存中的值;而对SPI_DR的写操作,则是将数据存入Tx缓存。
4.2 主模式下的启动顺序
1. 全双工模式(BIDIMODE=0,RXONLY=0)
- 数据写入SPI_DR寄存器(Tx buffer)时,开始传输。
- 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MOSI引脚串行移出。
- 与此同时,MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)
2. 单向接收模式(BIDIMODE=0, RXONLY=1)
- 当SPE=1时,即可开始传输。
- 只有接收器被激活,MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。
3. 双向模式-发送(BIDIMODE=1,BIDIOE=1)
- 数据写入SPI_DR寄存器(Tx buffer)时,即可开始传输。
- 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MOSI引脚串行移出。
- 无数据被接收
4. 双向模式-接收(BIDIMODE=1,BIDIOE=0)
- 当SPE=1时,即可开始传输。
- MISO引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。
- 因发送器未被激活,所以MOSI引脚上无串行数据移出。
4.3 从模式下的启动顺序
1. 全双工模式(BIDIMODE=0,RXONLY=0)
- 从设备收到时钟信号且数据的第1个bit出现在MOSI引脚上时,开始传输。剩余bits被载入移位寄存器。
- 与此同时(在第1个bit传输期间),数据被从Tx buffer并行加载到移位寄存器,随后从MISO引脚串行移出。在SPI主设备启动传输前,待发送数据必须已由软件写入完毕。
2. 单向接收模式(BIDIMODE=0, RXONLY=1)
- 从设备收到时钟信号且数据的第1个bit出现在MOSI引脚上时,开始传输。剩余bits被载入移位寄存器。
- 因发送器未被激活,所以MISO引脚上无串行数据移出。
3. 双向模式-发送(BIDIMODE=1,BIDIOE=1)
- 从设备收到时钟信号且Tx buffer中的第1个bit从MISO引脚上被发送出去时,开始传输。
- 在第1个bit传输期间,数据被从Tx buffer并行加载到移位寄存器,随后从MISO引脚串行移出。在SPI主设备启动传输前,待发送数据必须已由软件写入完毕。
- 无数据被接收
4. 双向模式-接收(BIDIMODE=1,BIDIOE=0)
- 从设备收到时钟信号且第1个bit数据出现在MOSI引脚上时,开始传输。
- MOSI引脚上接收到的数据被串行移入移位寄存器,然后并行载入SPI_DR寄存器(Rx buffer)。
- 因发送器未被激活,所以MISO引脚上无串行数据移出。
总结:
SPI通信总有一个主设备和一个从设备,一方发送,另一方接收。从主设备来看,执行的就是“主模式下的启动顺序”,而从配对的从设备来看,则执行的是“从模式下的启动顺序”。只是观察的角度不同而已。
4.4 发送和接收数据的处理
发送过程中,当数据从Tx buffer被移入移位寄存器后,TXE标志位被置1。这说明Tx buffer已准备好接收下一次数据载入。如果设置了TXEIE位,会产生相应的中断。程序中拟向Tx buffer写入数据前,应确认TXE=1,否则会覆盖上一次写入的数据。向SPI_DR寄存器写入数据时(实际写入的是Tx buffer),会自动清除TXE位。
接收过程中,当数据从移位寄存器移入Rxbuffer时,在最后一个取样时钟边沿RXNE被置1。这说明,SPI_DR寄存器中的数据已经可以读取。如果设置了RXNEIE位,会产生相应的中断。读SPI_DR寄存器,即可清除RXNE位。
对某些配置,在最后一个数据传输期间,可以通过等待BSY标志位来确认传输完成。下面分不同的传输模式进行详述:
1. 主/从模式下的在全双工发送和接收过程(BIDIMODE=0,RXONLY=0)
程序中必须遵循如下步骤来发送和接收数据:
- 设置SPE=1,使能SPI。
- 将第1个待发送数据项写入SPI_DR寄存器(这会清除TXE)。
- 等待TXE=1,写入下一个数据项。然后等待RXNE=1,读SPI_DR,得到第一个接收到的数据项(这会清除RXNE)。重复此操作,直至第n-1个接收到的数据。
- 等待RXNE=1,读取最后1个接收的数据。
- 等待TX=1,然后等待BSY=0,关闭SPI。
这个过程也可以用专门的中断回调函数来实现。开启中断后,在每个RXNE或TXE上升沿,中断函数都会被触发。
全双工模式下,主机和从机的时序图分别如图6和图7所示。
图6. 主机全双工模式收发时序图
图7. 从机全双工模式收发时序图
2. 单向发送过程(BIDIMODE=0,RXONLY=0)
该模式下,可简化为如下步骤:
- 设置SPE=1,使能SPI。
- 将第1个待发送数据项写入SPI_DR寄存器(这会清除TXE)。
- 等待TXE=1,写入下一个数据项。对每个数据项重复此操作。
- 将最后1个数据项吸入SPI_DR寄存器后,等待TXE=1。然后等待直至BSY=0。
这个过程也可以用专门的中断回调函数来实现。开启中断后,在每个TXE上升沿,中断函数都会被触发。
注意事项:
- 在不连续通信期间,写SPI_DR和BSY被置1之间有2个APB时钟周期的延迟。因此,在单发送模式下,写入最后一个数据后,必须首先等待TXE=1,然后等待至BSY=0。
- 单发送模式下,发送两个数据项后OVR标志位会被置1,这种模式下不会读取数据寄存器。
单发送模式下,主机和从机的时序图分别如图8和图9所示。
图8. 主机单发送模式时序图
图9. 从机单发送模式时序图
3. 双向发送过程(BIDIMODE=1,BIDIOE=1)
除了使能SPI前要置BIDIMODE=1和BIDIOD=1之外,此模式下其余步骤与单向发送模式完全相同。
4. 单向接收过程(BIDIMODE=0,RXONLY=1)
此模式下,工作步骤简化简化如下:
- 设置RXONLY=1。
- 设置SPE=1,使能SPI。主模式下,这会立即激活SCK时钟和数据串行接收,直至SPI关闭(SPE=0);从模式下,SPI主机将NSS拉低并产生SCK时钟后,开始接收数据。
- 等待RXNE=1,读取SPI_DR寄存器中收到的数据(这会清除RXNE)。对每个接收到的数据项重复此操作。
注意:最后一个数据传输完毕欧,要关闭SPI。上述流程也可以基于专门的中断回调函数来实现。开启中断后,每个RXNE上升沿都会触发。单向接收模式的时序图如图10所示。
图10. 单向接收模式时序图
5. 双向接收过程(BIDIMODE=1,BIDIOE=0)
除了使能SPI前要置BIDIMODE=1和BIDIOD=0之外,此模式下其余步骤与单向接收模式完全相同。
6. 连续和间断传输
以主模式发送数据时,如果软件足够快,能够检测到TXE的每个上升沿(或TXE中断),并且在正在进行中的数据传输结束前迅速写入SPI_DR寄存器,这种传输就可称为连续传输。这种情况下,在每个数据项之间SPI时钟没有中断,BSY位在每个数据传输之间也不会被清除。
反之,如果软件不够快,就会导致通信过程出现间断。这种情况下,在每个数据项传输之间,BSY位被清除。
在主机单向接收模式下(RXONLY=1),可以保持连续通信,BSY始终为1。
在从模式下,通信的连续性取决于主机的SPI始终。即使通信是连续的,每次传输之间BSY位至少有一个SPI始终周期会被拉低。
出现间断发送情况下,TXE和BSY位的时序如图11所示。
图11. 间断传输情况下TXE和BSY位时序图
五、标志位
5.1 状态标志位
通过SPI_SR寄存器中的3个状态标志位可以监控SPI总线的状态。
1. 发送缓存为空 – TXE
TXE=1时,表明发送缓存为空,下一个待发送数据可以载入。写SPI_DR寄存器可以清除该位。
2. 接收缓存非空 – RXNE
RXNE=1时,表明接收缓存非空,数据已成功接收正待读取。读SPI_DR寄存器可以清除该位。
3. 忙碌 – BSY
BSY位的设置和清除由硬件管理,软件写入会被忽略。BSY位用来指示SPI通信层的状态。当BSY=1时,表明SPI正忙于通信。例外情况是,在主模式/双向接收模式(MSTR=1、BIDIMODE=1、BDOE=0)中,在接收期间BSY始终为0。
BSY为常用来检查传输是否结束。在关闭SPI并进入停机状态(关闭外设时钟)前,必须严格执行这一检查,否则可能会破坏最后传输的数据。
BSY标志位还可以用来避免多主机系统中的写入冲突。
除了主模式/双向接收模式外,传输开始BSY位即被置1。当发生如下情况时,BSY被清除:
- 传输结束(主模式连续通信除外)
- SPI被关闭
- 发生主模式错误(MODF=1)
当非连续通信时,BSY在每次通信之间为0。当连续通信时:
- 主模式下,传输过程中BSY保持为1
- 从模式下,没次传输之间BSY有一个SPI时钟周期为0
注意:
不要用BSY来处理每次数据发送或接收,应该用TXE和RXNE标志位。
5.2 错误标志位
1. 主模式失效(MODF)
当主设备的NSS引脚被拉低(NSS硬件模式)或SSI被置0(NSS软件模式)时,会出现主模式失效。此时,MODF位会被自动置1。主模式失效会对SPI模块产生如下影响:
- MODF位被置1,如果ERRIE=1,会产生SPI中断
- SPE位被清除。当前设备的所有输出被阻断,SPI接口被关闭。
- MSTR位被清除,当前设备被强制进入从模式。
要清除MODF位,软件中需要执行如下步骤:
- MODF=1时,读或写一次SPI_DR寄存器
- 接着,写SPI_CR1寄存器(去重新配置或使能SPI)
在一个由多个MCU构成的系统中,为了避免多个从机间的冲突,在执行上述清除MODF位的过程中,NSS引脚必须被拉高。清除程序完成后,可将SPE和MSTR位恢复其原来的状态。作为一项安全措施,在MODF=1期间,硬件会禁止对SPE和MSTR位的设置。
从设备不会出现主模式失效,所以从设备的MODF位不能被置1。但是,在多主配置中,设备可以在MODF=1的情况下处于从模式。这种情况下,MODF=1表明系统控制过程中可能出现过多主冲突(multimaster conflict)。可以用中断程序来执行一次复位或恢复默认状态来清理这种状态。
2. 溢出错误(OVR)
主机已经发送数据,但从机没有清除上次接收数据后的RXNE位,就会出现溢出错误。出现溢出错误时,OVR为被置1,如果ERRIE=1,则会产生中断。
出现这种情况时,接收缓存中的内容不会被主机新接收到的数据更新。读SPI_DR寄存器仍可读取缓存中的内容,但OVR=1期间发送的字节就都丢失了。
读SPI_DR寄存器后接着读一次SPI_SR寄存器,即可清除OVR。HAL库中__HAL_SPI_CLEAR_OVRFLAG宏函数即是用来完成这个任务的。其代码如下:
#define __HAL_SPI_CLEAR_OVRFLAG(__HANDLE__) \ do{ \ __IO uint32_t tmpreg_ovr = 0x00U; \ tmpreg_ovr = (__HANDLE__)->Instance->DR; \//读DR tmpreg_ovr = (__HANDLE__)->Instance->SR; \//读SR UNUSED(tmpreg_ovr); \ } while(0U)
3. CRC错误(CRCERR)
通过SPI_CR1寄存器中CRCEN=1使能CRC后,可通过CRCERR位来确认接收到的值的有效性。如果移位寄存器中接收到的CRC与SPI_RXCRCR中的值不匹配,CRCERR位就会被置1。
5.3 SPI中断
SPI共有5个标志位可触发3个中断。如下表所示:
|
中断事件 |
标志位 |
使能位 |
|
发送缓存为空 |
TXE |
TXEIE |
|
接收缓存非空 |
RXNE |
RXNEIE |
|
主模式失效 |
MODF |
ERRIE |
|
溢出 |
OVR |
|
|
CRC校验错误 |
CRCERR |
以上5个标志位均在SPI_SR寄存器中,硬件设置,软件复位;3个使能位在SPI_CR2寄存器中设置。
六、关闭SPI
传输完成后,应用程序中可以关闭SPI模块来停止通信。这可以通过清除SPE位来实现。在某些配置下,如果传输正在进行中,就关闭SPI并进入停机模式,可能导致当前的传输被破坏,并可能使BSY标志位变得不可信。为避免这类情况,建议关闭SPI时严格执行如下步骤:
1. 主/从全双工 (BIDIMODE=0, RXONLY=0)
- 等待RXNE=1,接收最后一个数据
- 等待TXE=1
- 等待BSY=0
- 关闭SPI(SPE=0),最终进入停机模式(关闭外设时钟)
2. 主/从单向发送(BIDIMODE=0,RXONLY =0)或双向发送(BIDIMODE=1,BIDIOE=1)
- 等待TXE=1
- 等待BSY=0
- 关闭SPI(SPE=0),最终进入停机模式(关闭外设时钟)
3. 主单向接收(MSTR=1,BIDIMODE=0,RXONLY=1)或双向接收(MSTR=1,BIDIMODE=1, BIDIOE=0)
此模式下必须进行特殊处理,防止SPI启动新的传输。
- 等待倒数第二(n-1)次RXNE=1
- 等待一个SPI时钟周期(软件中循环)再关闭SPI(SPE=0)
- 等待最后一次RXNE=1,然后进入停机模式(关闭外设时钟)
注意:在主双向接收模式(MSTR=1,BIDIMODE=1, BIDIOE=0),传输期间始终BSY=0。
4. 从单向接收(MSTR=0,BIDIMODE=0,RXONLY=1)或双向接收(MSTR=0,BIDIMODE=0,BIDIOE=1)
- 可随时关闭SPI(SPE=0):当前传输会在SPI被关闭前完成
- 然后,如果想进入停机模式,必须首先等待BSY=0.。
七、CRC计算
为实现可靠通信,SPI中为发送数据和接收数据分别设置了一个单独的CRC(Cyclic Redundancy Check,循环冗余校验)计算器。通过设置SPI_CR1寄存器中的CRCEN=1来开启。开启后,在每个数据取样时钟边沿,都会基于一个可编程多项式对每个bit串行计算CRC值。由于SPI支持8-bit和16-bit两种数据长度,CRC计算时会采取与之对应的CRC8(8-bit数据)和CRC16(16-bit数据)两种标准。
开启CRC计算的同时会将SPI_RXCRCR和SPI_TXCRCR两个寄存器复位。此后由根据收/发数据进行CRC计算得到的值填充。
CRC校验过程分为两步:
- 在发送端,数据发送过程中逐位计算CRC并存入SPI_TXCRCR。在最后一个数据发送完成后,SPI_TXCRCR中的值被发送出去。
- 在接收端,数据接收过程中逐位计算CRC并存入SPI_RXCRCR。在最后一个数据接收完成后,会接收到来自发送端的CRC值。将之与SPI_RXCRCR中的值进行比较,如果不匹配,则说明数据在传输过程中被损坏,CRCERR位会被置1。
显然,发送端和接收端对CRC值不应再进行CRC计算。因此,在软件管理(CPU mode)的全双工或单向发送模式中,必须在最后一个数据写入SPI_DR后立即将CRCNEXT置1。在软件管理(CPU mode)的单向接收模式中,必须在收到倒数第2个数据时将CRCNEXT置1。发送端或接收端在查询到CRCNEXT=1时,将关闭CRC计数器。接收端在接收完下一个数据后,会马上用之进行CRC校验。
SPI通信可以通过以下步骤使用CRC:
- 在程序中配置CPOL, CPHA, LSBFirst, BR, SSM, SSI 和MSTR的值。
- 在SPI_CRCPR寄存器中设置CRC计算所需的多项式。
- 将SPI_CR1寄存器的CRCEN位置1,使能CRC计算。这会同时清除SPI_RXCRCR和SPI_TXCRCR寄存器。
- 将SPI_CR1寄存器的SPE位置1,使能SPI。
- 开始并保持通信,直至最后一个数据(字节或半字)被发送或接收前:软件管理的全双工或单向发送模式下,在最后一个数据被写入时设置CRCNEXT,以表明最后一个字节传输完毕后将发送CRC;单向接收模式下,在收到倒数第2个数据后,设置CRCNEXT,以便SPI做好准备在接收完最后1个数据后进入CRC阶段。CRC传输期间,CRC计算被冻结。
- 最后一个数据传输完毕后,SPI进入CRC传输和校验阶段。在全双工或单向接收模式下,接收到的CRC值会与SPR_RXCRCR中的值进行比较。如果二者不匹配,SPI_SR寄存器中的CRCERR标志位会被置1。如果设置了SPI_CR2寄存器中的ERRIE位,会产生中断
注意事项:
- 当SP处于从模式时要当心,只有时钟稳定(即,时钟处于稳态)时,才可使能CRC计算。否则可能会发生错误的CRC计算。事实上,只要设置了CRCEN=1,CRC即开始对SCK输入时钟进行响应,而不管SPE位是何状态。换句话说,即使SPI还没启动,如果输入时钟抖动,也会被认为是数据bit,并对其进行CRC计算。
- 高比特率情况下,发送CRC时必须小心。CRC传输阶段所用的CPU周期该越少越好。在CRC发送过程中,严禁在程序中调用函数,以防在接收最后的数据和CRC时出错。实际上,在发送/接收最后一个数据之前,必须将CRCNEXT位置1。
- 由于对CPU的访问不可避免的会影响SPI的带宽,因此高比特率情况下,建议采用DMA模式以避免影响SPI的速度。
- 当SPI被配置为从模式,并且使用了NSS硬件模式时,NSS引脚应该在数据传输和CRC传输期间保持为低电平。
当SPI被配置为从模式且开启CRC时,即使在NSS引脚上施加高电平,仍会执行CRC计算。当主机交替地与多个从机进行通信时就可能会出现这种情况。为保持主机和从机各自CRC计算的同步,在断开一个从机(NSS拉高)到选通另一个从机中间,应清除双方的CRC值。步骤如下:
- 关闭SPI(SPE=0)
- 关闭CRC(CRCEN=0)
- 使能CRC(CRCEN=1)
- 使能SPI(SPE=1)
八、使用DMA进行SPI通信
为了达到最大通信速度,SPI通信过程中要不断的向Tx buffer中送入待发送数据,并及时读取Rx buffer中接收到的数据,否则会发生溢出。为了便于实现高速传输,SPI的DMA功能采用了一种简单的请求/应答协议。
对Tx buffer和Rx buffer的DMA访问必须单独发出,对应的使能位分别为SPI_CR2寄存器中的TXDMAEN和RXDMAEN。
- 发送时,每次TXE=1都会发出DMA请求。然后DMA写入SPI_DR寄存器(这会清除TXE)
- 接收时,每次RXNE=1都会发出DMA请求。然后DMA读取SPI_DR寄存器(这会清除RXNE)
当SPI仅用来发送数据时,可以仅使能DMA发送(TXDMAEN=1)通道。这种情况下,OVR会被置1,因为接收到的数据未被读取。不用管他即可。同理,当SPI仅用来接收数据时,可以仅使能DMA接收(RXDMAEN)通道。
在发送模式下,当DMA已经写完所有待传输数据(DMA_ISR寄存器的TCIF=1),为了避免最后的数据被损坏,关闭SPI或进入STOP模式前,必须监测BSY是否被置0。需要注意的是:在非连续通信中,写入SPI_DR和设置BSY的操作之间有2个APB时钟周期的延迟。由于这一原因,写入最后一个数据后,必须首先等待TXE=1,任何等待BSY=0。
图12和13分别为发送和接收过程DMA时序图。
图12. 发送过程DMA时序图
图13. 接收过程DMA时序图
DMA模式启用CRC
当SPI启用DMA模式且使能CRC通信时,通信结束前的CRC发送和接收是自动的,即无需使用CRCNEXT位。接收到CRC后,必须将其读入SPI_DR寄存器来清除RXNE位。如果传输过程中出现数据损耗,在数据和CRC传输结束后,SPI_SR寄存器中的CRCERR位会被置1。
九. SPI寄存器
STM32F103ZET6中与SPI相关的寄存器共有9个,地址映射与复位值表如图1所示。这些寄存器必须按按半字(16 bits)或全字(32 bits)访问。
1. SPI_CR1寄存器
该寄存器主要管理SPI工作模式、时钟极性和相位、波特率、数据格式,以及使能SPI。
|
位置 |
名称 |
功能说明 |
|
15 |
BIDIMODE |
Bidirectional data mode enable 0:选择双线单向模式;1:选择单线双向模式。 注: I2S模式下不使用。 |
|
14 |
BIDIOE |
Output enable in bidirectional mode 0:输出禁止(只收模式);1:输出使能(只发模式)。 与BIDIMODE位一起决定在单线双向模式下数据的传输方向 注: I2S模式下不使用;主模式下,用MOSI,从模式下用MISO。 |
|
13 |
CRCEN |
Hardware CRC calculation enable 0:禁止CRC计算;1:启动CRC计算。 注:只有在SPE=0时,才能写该位,否则出错; I2S模式下不使用 |
|
12 |
CRCNEXT |
CRC transfer next 0:数据阶段;1:下一次发送CRC(CRC阶段) 注意:全双工或单发模式,在最后一个数据写入DR寄存器后必须立即设置该位;单收模式,必须收到倒数第2个数据时设置该位;DMA模式下应保持该位为0; I2S模式下不使用。 |
|
11 |
DFF |
Data frame format 0:8位格式;1:16位格式 注意:只有当SPE=0时,才能写该位,否则出错; I2S模式下不使用 |
|
10 |
RXONLY |
Receive only 0:全双工(收&发);1:单收模式 该位与BIDIMODE位一起决定在双线双向模式下的传输方向。也常用于多从设备系统,该位为1的从设备不会输出,避免数据线上的冲突。 注:I2S模式下不使用 |
|
9 |
SSM |
Software slave management 0:禁用软件从设备管理;1:启用软件从设备管理。 当SSM被置1时, NSS引脚上的电平由SSI位的值决定。 注: I2S模式下不使用。 |
|
8 |
SSI |
Internal slave select 该位仅在SSM=1时有效。此位的值决定NSS引脚的电平,NSS引脚上的输入输出值被忽略。 注: I2S模式下不使用。 |
|
7 |
LSBFIRST |
Frame format 0:先发送MSB;1:先发送LSB 注:当通信正在进行时,不要改变该位的值;I2S模式下不使用。 |
|
6 |
SPE |
SPI enable 0:禁用SPI;1:使能SPI。 注: I2S模式下不使用;禁用SPI时,必须按所需步骤操作。 |
|
5:3 |
BR[2:0] |
Baud rate control xxx=000-111对应波特率=FPCLK/(2^(xxx+1)) 注意:通信正在进行时,不能修改这些位; I2S模式下不使用。 |
|
2 |
MSTR |
Master selection 0:配置为从设备;1:配置为主设备。 注:当通信正在进行的时候,不能修改该位;I2S模式下不使用。 |
|
1 |
CPOL |
Clock polarity 0: 闲时SCK保持低电平;1: 闲时SCK保持高电平。 注:当通信正在进行的时候,不能修改该位;I2S模式下不使用。 |
|
0 |
CPHA |
Clock phase 0: 从第1个时钟边沿开始数据采样;1:第2个时钟边沿开始采样。 注:当通信正在进行的时候,不能修改该位; I2S模式下不使用。 |
2. SPI_CR2寄存器
这个寄存器主要管理SPI中断、片选引脚以及收、发DMA的使能。
|
位置 |
名称 |
功能说明 |
|
7 |
TXEIE |
Tx buffer empty interrupt enable 0:屏蔽TXE中断;1:使能TXE中断。TXE标志位被置1时产生中断。 |
|
6 |
RXNEIE |
RX buffer not empty interrupt enable 0:屏蔽RXNE中断;1:使能RXNE中断。RXNE标志位被置1时产生中断。 |
|
5 |
ERRIE |
Error interrupt enable 0:屏蔽错误中断;1:使能错误中断。 此位控制出现错误时是否产生中断。响应的错误包括SPI模式下的CRCERR,OVR,MODF,以及I2S模式下的UDR和OVR。 |
|
2 |
SSOE |
SS output enable 0:禁用主模式下的SS输出;1:启用主模式下的SS输出。 注:此bit在I2S模式下无效。 |
|
1 |
TXDMAEN |
Tx buffer DMA enable 0:禁用Tx缓存DMA;1:使能Tx缓存DMA。 当该位为1时,置TXE=1,立即会产生DMA请求。 |
|
0 |
RXDMAEN |
Rx buffer DMA enable 0:禁用Rx缓存DMA;1:使能Rx缓存DMA。 当该位为1时,置RXNE=1,立即会产生DMA请求。 |
3. SPI_SR寄存器
该寄存器管理SPI的工作状态和错误标志。
|
位置 |
名称 |
功能说明 |
|
7 |
BSY |
Busy flag 0:SPI空闲;1:SPI工作中,或者发送缓冲非空。 该位由硬件置位或者复位。 注:使用这个位时需要特别小心 |
|
6 |
OVR |
Overrun flag 0:没有出现溢出错误;1:出现溢出错误。 该位由硬件置位,由软件序列复位。 |
|
5 |
MODF |
Mode fault 0:没有出现模式错误;1:出现模式错误。 该位由硬件置位,由软件序列复位。 注: I2S模式下不使用 |
|
4 |
CRCERR |
CRC error flag 0:收到的CRC值和SPI_RXCRCR寄存器中的值匹配; 1:收到的CRC值和SPI_RXCRCR寄存器中的值不匹配。 该位由硬件置位,由软件写0复位。 注: I2S模式下不使用。 |
|
3 |
UDR |
Underrun flag 0:未发生下溢;1:发生下溢。 该标志位由硬件置,由一个软件序列清0。 注:在SPI模式下不使用。 |
|
2 |
CHSIDE |
Channel side 0:需要传输或者接收左声道;1:需要传输或者接收右声道。 注:在SPI模式下不使用。在PCM模式下无意义。 |
|
1 |
TXE |
Transmit buffer empty 0:发送缓存非空;1:发送缓存为空。 |
|
0 |
RXNE |
Receive buffer not empty 0:接收缓冲为空;1:接收缓冲非空。 |
4. SPI_DR寄存器
这是一个16位的数据寄存器。存储接收或待发送数据。数据寄存器被分为2个缓存。写入时写到Tx 缓存,读取时返回的是Rx缓存中的值。
注意:(仅对SPI模式)根据DFF位设置,数据可以为8或16-bit。该选择必须在使能SPI前完成。对8-bit,仅使用低位,接收时MSB置0。
5. SPI_CRCPR寄存器
该寄存器包含了CRC计算时用到的多项式。其复位值为0x0007,根据应用可以设置其他数值。
注:在I2S模式下不使用
6. SPI_RXCRCR寄存器
启用CRC时,此寄存器存储依据接收的字节使用CRCPR中的多项式计算出的CRC值。当CRCEN=1时,该寄存器被复位。
对8位数据帧,仅低8位参与计算,执行CRC8的标准;对16位数据帧,该寄存器中的16位均参与计算,执行CRC16的标准。
注:BSY=1时可能读到错误的值。I2S模式不使用。
7. SPI_TXCRCR寄存器
启用CRC时,此寄存器存储依据发送的字节使用CRCPR中的多项式计算出的CRC值,并作为最后一个数据发送出去。当CRCEN=1时,该寄存器被复位。
对8位数据帧,仅低8位参与计算,执行CRC8的标准;对16位数据帧,该寄存器中的16位均参与计算,执行CRC16的标准。
注:BSY=1时可能读到错误的值。I2S模式不使用。
8. SPI_I2SCFGR寄存器
该寄存器主要进行I2S的配置。对SPI,仅用到I2SMOD位,将该位设为0时,外设为SPI接口,设为1时,则为I2S接口。
9. SPI_I2SPR寄存器
该寄存器设置I2S预分频参数。
十、HAL库SPI相关函数
HAL库中USART相关的常规库函数有29个,在stm32f1xx_hal_spi.h中声明,stm32f1xx_hal_spi.c中定义。如果基于STM32CubeIDE编程,大多函数都可以自动生成。只需适当添加代码和编写所需的*Callback即可。仅将函数名称罗列如下,大多可以从名称看出功能。
/* Initialization/de-initialization functions */ HAL_SPI_Init(); HAL_SPI_DeInit(); HAL_SPI_MspInit(); HAL_SPI_MspDeInit(); /* I/O operation functions / HAL_SPI_Transmit(); HAL_SPI_Receive(); HAL_SPI_TransmitReceive(); HAL_SPI_Transmit_IT(); HAL_SPI_Receive_IT(); HAL_SPI_TransmitReceive_IT(); HAL_SPI_Transmit_DMA(); HAL_SPI_Receive_DMA(); HAL_SPI_TransmitReceive_DMA(); HAL_SPI_DMAPause(); HAL_SPI_DMAResume(); HAL_SPI_DMAStop(); /* Transfer Abort functions / HAL_SPI_Abort(); HAL_SPI_Abort_IT(); /* Request and Callcack functions / HAL_SPI_IRQHandler(); HAL_SPI_TxCpltCallback(); HAL_SPI_RxCpltCallback(); HAL_SPI_TxRxCpltCallback(); HAL_SPI_TxHalfCpltCallback(); HAL_SPI_RxHalfCpltCallback(); HAL_SPI_TxRxHalfCpltCallback(); HAL_SPI_ErrorCallback(); HAL_SPI_AbortCpltCallback(); /* Peripheral State and Error functions / HAL_SPI_GetState(); HAL_SPI_GetError();
附:血泪教训
不要忽视SPI的极性和相位,如果一个SPI接口的器件,写入寄存器的值和读取的值出现错误,很可能是极性和相位设置有问题。主从机的极性和相位要相反!比如,主机在时钟的下降沿发送,从机在时钟的上升沿接收。
数据bit错位先怀疑相位,数据全错或收不到,先检查时钟极性。
读SPI接口器件的说明书时,一定要搞清它的极性和相位。从时钟和读/写时序图就可看出。
例子:ADS1256的SPI主机设置
从机:ADS1256在SCLK的下降沿从DIN引脚接收数据,在上升沿向DOUT引脚发送数据
主机:在SCLK的下降沿从MOSI引脚接收数据(极性为0时,第2个边沿),在上升沿发送数据。
这解释了为什么CPHA设为1Edge时,读取的寄存器数据左移一位(少读了1位)。若设为CPOL=1,CPHA=1,则上升沿读不到数。CPOL=1,CPHA=0时能行吗?
ADS1256说明书中关于数据时序的描述
简而言之:发送和接收数据应该在时钟脉冲的前后边沿,前一边沿发送,下一边沿接收。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/97306.html