Introduction
- Features
- 引脚信号
- 时钟源
- 其它不常用功能
Pricinple & Mechinism
- 基于FIFO的命令和数据管理机制
- 接收数据匹配功能
- Application Information
Conclusion
- 关于SPI外设中移位器的一些思考
- 关于SPI从机的使用情况
- 关于SPI外设中实现多个PCS引脚的正确打开方式的思考
Introduction
Features
- 支持配置SCLK的极性和相位
- 动态可配置的通信帧尺寸。SPI定义片选信号硬件保持生效期间的数据位流被称为SPI的通信帧。
- 使用FIFO管理发送和接收的数据,分别对应发送FIFO和接收FIFO。
- 支持单线模式,即仅发送和仅接收。实际上,SPI的发送数据信号线和接收数据信号线是相互独立的,对软件使用来说这个功能同正常使用交换数据模式(同步数据模式)没啥区别。
- 支持主机模式和从机模式。
- 可以产生触发信号送至DMA控制器,以支持DMA传输。
- 支持多路片选。这个SPI模块引出了四根片选线,
PCS[3..0]
,但在一个时刻做多仅能选中一路片选信号。但实际应用场景非常受限。
SPI外设的电路系统有框图,如图x所示。
引脚信号
SPI通信外设同外界交互的信号线包括:
* 时钟线`SCK`
* 输出串行数据线`SOUT`
* 输入串行数据线`SIN`
* 每个SPI外设实例都有自己的四根片选信号线`PCS[3..0]`
这四根片选信号线将由软件配置SPI外设模块的寄存器SPI_TXCFG[PCS]
,选择其中一个作为本数据帧的片选信号。
片选信号线PCS在Master和Slave模式下的数据数据方向是不同的:
* 若为Master模式,`PCS`为输出信号。可配置使用SPI外设自动控制。
* 若为Slave模式,`PCS`为输入信号,仅当被选中后才能从接收来自总线上的数据。
时钟源
以YTM32B1LE05
微控制器芯片为例,SPI外设的时钟源来来自于IPC,访问SPI外设寄存器的时钟信号来自于SLOW_BUS_CLK
,如图x所示。
图中说明,SPI模块的外设功能时钟是可选的。如图x所示。SPI模块的外设功能时钟,将用于驱动SPI外设工作,产生通信波特率等。
其它不常用功能
- 支持SOUT和SIN引脚信号交换,为电路设计提供更宽的容错性,
SPI_CTRL[PINCFG]
- SPI在正常情况下使用SCLK信号对串行数据流进行采样(获得数字信息),也可以使用SCLK再延后一点的时间点进行采样,
SPI_CTRL[SPDEN]
Pricinple & Mechinism
在SPI传输的过程中,Master模式是提供通信时钟的一方,先发数据再收数据;Slave使用SPI总线上的时钟信号同步数据,先收数据再发数据。SPI的Master模式和Slave模式,相当于是SPI模块的两个影子分身,使用同一个SPI外设时,二者只能选其一。虽然大部分的电路功能都是复用的,但在具体的使用场景中,针对不同的工作模式,表现出来的行为模式也各自不同。
基于FIFO的命令和数据管理机制
YTM的SPI外设的设计中,将SPI通信属性分类为收发器外设
相关和通信帧
相关两类:
- 收发器外设相关的属性,例如波特率、中断模式等等,配置好之后,在整个通信过程中基本就不再改动了。这类配置大多被安置在
SPI_CTRL
以及专门的配置时钟波形参数的SPI_CLK
寄存器中。 - 通信帧相关的属性,例如帧数据长度,本帧结束后是否放开片选线等,是可能随着每一帧发生发生变化的。这类配置大多被安置在
SPI_TXCFG
寄存器中。SPI_TXCFG
寄存器可以以常规访问寄存器的方式读写(必须是32位写操作),但实际上,SPI外设会自动将用户写入SPI_TXCFG
寄存器的命令字送入发送FIFO中,或者更确切地说,向SPI_TXCFG
寄存器写数,将会立即作用于发送FIFO的工作方式。
YTM的SPI外设将SPI的通信过程看做是通信帧的过程:
- 每次要发送数据流时,首先需要先向发送FIFO写入命令字(写
SPI_TXCFG
寄存器),SPI外设会识别本次写入的命令字,自动配置好发送FIFO的工作模式。然后用户需要向发送FIFO写即将要发送的数据字(写SPI_DATA
寄存器),写数据字过程中,每次写入操作是32位的,意味着最多可以连续发送一个32位的数据字(也可以配置SPI外设仅识别其中的N位数据,仅将这N位的数据送上总线),这个数据字就是所谓的数据帧,每次连续发送的位长度就是“帧长度”(Frame Bit Count)。 - 随着将发送FIFO中的数据送上总线,SPI外设同时从总线上捕获下来数据流并存入接收FIFO,数据以LSB方式存放,用户可以通过读
SPI_DATA
的方式从接收FIFO中取数。在连续传输模式时(Continuous Transfer),只有当发送FIFO中有数将要发送,接收FIFO才能接收数据,即接收和发送过程是配对的。当配置为仅发送模式时(SPI_TXCFG[MSKRX]=1
),SPI外设从总线上捕获下来的数不会送入接收FIFO。 - 当在传输命令字中配置启用连续传输模式(
SPI_TXCFG[CONT]=1
),接下来向发送FIFO写入数据字的启动传输数据完成后,SPI外设将继续保持PCS的片选选中状态,并在后续的传输中仍使用最近一次生效的命令字。否则,在每个命令字发起的数据传输过程之后,SPI外设会自动释放PCS的片选信号,并停止发送数据,结束通信过程。在新的命令字到来之时,SPI外设不会再发送任何数据,此时,哪怕向发送FIFO中写数也发不出去。 - 在启用连续传输模式的同时,若设定
SPI_TXCFG[CONTC]=1
,则意味着在当前传输帧的过程当中,可以临时修改SPI_TXCFG
寄存器的值(即命令字),修改后在当前一个数据字传输完成后,将使用新的命令字开始新的传输。若设定SPI_TXCFG[CONTC]=0
,临时修改SPI_TXCFG
寄存器的写操作,将会在当前一个数据字传输完成后,终止当前的通信帧过程。(实际在应用场景中,通信过程是由应用软件确保其通信帧的完整性,所以此处启用硬件的这个允许临时插入命令字的功能在)
由此可以看出,SPI的传输过程,是由向发送FIFO中写命令字和数据字的操作完成的。这里重点看下命令字和数据字的格式。
SPI外设的命令字是写入到SPI_TXCFG
寄存器,从RM手册中可以查看该寄存器的字段定义。如图x所示。
SPI_TXCFG
寄存器中的具体字段可以顾名思义,或者查阅手册。这里特别说明其中的几个要点。
锁定配置(CONTC字段)
手册中在描述SPI_TXCFG[CONTC]
寄存器时,提到了Continuous Command
,如图x所示。
但手册中并没有展开这个Continuous Command
是什么功能。实际上,这个寄存器位的作用在于锁定SPI_TXCFG
寄存器的高8位,这8位的区域里包含了CPOL
、CPHA
、PRESCALE
和PCS
等4个字段。
* 当`CONTC=0`时,可以正常写入包括高8位的在内的整个`SPI_TXCFG`寄存器。
* 当`COUTC=1`时,高8位被锁定,向其写入不生效。
因此,为了确保每次向SPI_TXCFG
寄存器的写入操作都能生效(像写入正常的寄存器一样),需要显式地清零CONTC
控制位后,再重新写完整的SPI_TXCFG
配置值。至于写入后是否再写CONTC=1
锁住配置,可写可不写,实际意思不大。
NOTE:默认情况下CONTC=0
时,可以正常写入SPI_TXCFG
寄存器,一次写操作完成发送配置。但若CONTC=1
,先做一步解锁操作(写COUTC=0
),再写SPI_TXCFG
寄存器,实际上执行了对SPI_TXCFG
寄存器的两次写操作。手册上曾有描述,每次向SPI_TXCFG
寄存器写数,SPI外设硬件会向发送与命令FIFO中送一个配置命令,那前次命令会不会误生效?笔者之前一直有这样的顾虑,但经过调试之后发现实际是没有影响的:无论清零还是写1,在软件上都是使用“读改写”的操作为这一位赋值,在改写CONTC
的操作中,哪怕是重新写入别的字段,但也是同之前相同的值,不会改变之前的配置。另外,哪怕出现不一致的情况,总是最后的配置起作用。在前一次配置与第二次配置之间,因没有向发送数据FIFO中写数,就没有驱动SPI外设发送数据到总线,即使前次配置在电路中生效,也不会影响到第二次配置起作用后再进行的数据通信过程中SPI总线上的信号。
帧长度概念与设定(FRAMESZ字段)
手册中描述SPI_TXCFG
寄存器的FRAMESZ
字段,指定了一个通信帧包含若干个bit位。有如下说明。
这里的帧,具体是个什么概念呢?它实际就是指原始SPI通信过程中片选信号开始和结束之间的数据位流的长度。这里的“原始SPI通信”,就是每发送一个单元数据(如果帧长度设定为8比特的1字节)前后,都做一次片选的激活和释放。但实际使用SPI通信时,经常需要在连续发送多个单元数据过程中保持片选,那这个多个单元数据构成一个逻辑上的通信帧。这一个单元数据也是帧,多个单元数据也是帧?是的,抓住SPI帧的基本概念,片选信号的开始和释放之前都是一帧。
但实际操作时,多个单元数据的帧可以不同的方式展现出来,SPI外设可以支持最基本的用法,也可以用连续传输模式和保持片选功能实现。其中的区别主要在于对发送和接收数据寄存器的使用,受限于数据寄存器的长度是32比特。
- 使用SPI外设硬件发送多单元数据的帧。32个比特长度以内的通信帧(例如4个连续的字节),可以通过一次32位写操作送入SPI外设的移位器。如果设定
FRAMESZ
字段发送超出32比特的通信帧,可以通过连续多次写入32位的数据寄存器(送入TX FIFO)。之后SPI外设自动激活片选,并按位发送数据流,当完成一帧的传输之后,片选信号自动释放,如此完成一帧的通信。
使用SPI外设硬件帧的方式的通用性不够好,需要拼接字节流。如果是发送常规的字节流,需要每4个字节拼成32位数才能向Tx FIFO中写一次数,如果发送数据不是4字节的整数倍,为了凑整就要补一些空白。发送过程尚且可控实际发送字节的长度,在接收过程中,按照4字节一批收入Rx FIFO,最后补白的部分,没有任何判定依据可以确认其是有效通信数据还是补白区域。如果不是按照8的整数倍的数据单元发送,还得自行计算和找补对齐数据寄存器及关注特别的写入方式。
- 使用连续传输模式和保持片选功能,更接近常规的用法。例如当发送任意长度的字节流时,可以指定硬件帧的长度为8,作为多数据通信帧中单元数据的长度,这样对应读写FIFO的数据也可以以单元数据的长度方式操作。按照SPI外设硬件控制片选信号的机制,此时发送一个单元数据后就要释放片选信号,此时,可以利用保持片选的功能(SPI_TXCFG[CONT]寄存器位),让片选信号保持激活,然后软件连续向Tx FIFO中写入多个单元数据,驱动SPI外设通过移位器向总线上送数。当发送出所有的单元数据后,再设定
SPI_TXCFG[CONT]=0
,释放片选线信号。此时,一个包含多个单元数据的逻辑帧,实际包含了多个硬件帧,其中每个单元数据对应一个硬件帧。
图x中展现了在使用连续传输和保持片选功能发送一个包含多个单元数据的逻辑帧的过程中,开启SPI_TXCFG[CONT]寄存器位与否对应的实际信号波形。
SPI外设硬件管理PCS引脚
在通信帧的概念中,已经介绍了对片选信号的用法。这里汇总SPI外设管理PCS引脚的机制。
SPI外设设计了4个PCS信号,但每个通信帧只能使用其中1个。
SPI_CTRL[CSPOL]
寄存器字段,配置了PCS信号的极性,低电平选中高电平释放,还是高电平选中低电平释放。SPI_TXCFG[PCS]
寄存器字段,配置了当前使用哪个PCS信号做片选。
在应用中,很多开发人员使用GPIO模拟片选控制(将MCU上PCS信号对应的引脚设定成GPIO功能),但实际上,PCS信号是绑定在SPI通信外设中的,伴随着通信过程始终发生作用。可以通过SPI_TXCFG[CONT]
寄存器位灵活地将多个硬件帧连接在一次形成一个逻辑帧(所谓的“连续传输模式”)。这里需要注意的是,写入SPI_TXCFG[CONT]
寄存器对PCS信号的控制是配合移位器(相或)生效的:
- 若写入
SPI_TXCFG[CONT]=1
: - 在不发数的情况下不起作用;
- 发数过程中,移位器坚持片选,
CONT
配置不干涉; - 当发送数据到SPI总线上后,移位器想要释放片选,但
CONT
配置要保持片选,合在一起作用就是保持片选。 - 若写入
SPI_TXCFG[CONT]=0
: - 在不发数的情况下,移位器想要释放片选状态,
CONT
配置也不坚持片选,合在一起作用就是释放片选; - 发数过程中,移位器坚持片选,
CONT
配置不干涉; - 发数完成后,移位器想要释放片选,
CONT
配置也不坚持片选,合在一起作用就是释放片选。
这种相或的配合控制逻辑,就导致了一个很有趣的使用方式,开发者在使用SPI硬件片选发送一个多数据单元的逻辑帧时,就要特别注意:
- 在向Tx FIFO写数之前,设置
SPI_TXCFG[CONT]=1
,要等到第一个单元数据被移位器送到SPI总线上后才生效,持续选中PCS。 - 在向Tx FIFO写入所有的数据后,再设置
SPI_TXCFG[CONT]=0
,则立即生效,片选释放。
这样看下来,软件控制SPI_TXCFG[CONT]
就像是向控制PCS引脚发送请求一样,在发数之前请求片选,在发数完成后请求释放片选,但由移位器在合适的时间点生效。
在SPI外设作为Slave时,对PCS信号的依赖就更加明显。SPI外设仅在PCS信号被选中时,才能激活移位器从总线上搬运数据到内部,否则就是视而不见。但这里还有个SPI_CTRL[AUTOCS]
寄存器位:
- 当
SPI_CTRL[AUTOCS]=0
时,使用外部Master提供的片选信号控制PCS,外部Master会协调好在传输有效数据期间选中PCS。 - 当
SPI_CTRL[AUTOCS]=1
时,手册上的描述是可以自动产生一个PCS信号。实际上,此时是在芯片外设内部启用了额外的一小块电路,检测PCLK信号线上的变化,自动产生一个片选信号送给SPI外设,可以在没有收到外部片选控制信号的情况下,仅在总线上有时钟信号驱动的情况下就开始从总线上搬数。
另外,还有SPI_CTRL[CSDEN]
寄存器字段,可以选择将PCS2
和PCS3
设置成四线SPI传输中的DATA2
和DATA3
信号。
计算通信波特率
SPI波特率的时钟源自于IPC模块的外设功能时钟,在IPC中选择一个可用的SPI的时钟源后,经过多个分频器,得到一个基本的时基单元。
IPC_CTRL[DIV]
- IPC为具体外设的功能时钟源设定的分频因子。SPI_TXCFG[PRESCALE]
- SPI功能时钟经过PRESCALE
分频得到时基单元。
然后,在SPI_CLK
寄存器的字段设置每个硬件帧内部触发信号变化的时间点:
SPI_CLK[DIV]
- SCK时钟信号的周期为DIV+2
个时基单元。SPI_CLK[FMDLY]
- 连续传输模式时,多个硬件帧之间的长度为FMDLY+1
个时基单元。SPI_CLK[PCSCLK]
- 在开始片选时,片选信号PCS到时钟信号CLK边沿的时长为PCSCLK
个时基单元。SPI_CLK[SCKPCS]
- 在释放片选时,时钟信号CLK边沿到片选信号PCS的时长为SCKPCS+1
个时基单元。
其中,SCK时钟信号的频率就对应着SPI通信的波特率。
在实际的信号图上可以标注,有图x所示。
在SPI_CTRL[SPDEN]
寄存器中设定了SPI外设从总线上采样数据的时间点。
- 当
SPI_CTRL[SPDEN]=0
时,在SCK时钟信号的边沿执行采样。 - 当
SPI_CTRL[SPDEN]=1
时,在SCK时钟信号的边沿稍微晚一点(等待信号稳定)执行采样。
另外,关于时钟和相位的配置,可见寄存器字段SPI_TXCFG[CPOL]
和SPI_TXCFG[CPHA]
。
接收数据匹配功能
匹配数据可用于实现应用逻辑层面上通信帧的帧开始判断,也可以当做SPI从机节点的地址,使用硬件的自动检测机制,激活数据通信过程,硬件仅接收预先设定的有效数据内容。
- 若设置为仅发送模式时(
SPI_TXCFG[MSKRX]=1
),SPI总线上的数据不会送入接收FIFO,接收数据匹配的功能无法生效的。接收数据匹配的执行逻辑设计在接收FIFO中。 - 当启用仅在匹配数据功能(
SPI_CTRL[RXMO]=1
)后接收数据的模式时,仅在触发匹配事件后,接收FIFO将才开始接收后续的数据。匹配事件之前的数据不会送入接收FIFO,将被抛弃。 - 为了在匹配事件之后开始接收接下来的数据(将匹配数据字作为应用程序的数据帧开始),需要人为在匹配接收事件的中断服务中清接接收匹配事件标志位(
SPI_STS[MATIF]
)的同时,还需要停用匹配接收功能(SPI_CTRL[RXMO]=0
)。之后,接收后续数据就不需要经过匹配判断而直接送入接收FIFO中。
Application Information
在SDK设计的spi_master_basic
样例工程中,将spi_sin
和spi_sout
引脚通过杜邦线短接在一起,然后配置spi master发送从0x0
到0xF
的16个数。使用逻辑分析仪观察SPI的四根信号线,可以验证SPI的通信数据内容、通信时钟波特率、片选信号等工作情况。开发者建立实验环境后,可以微调样例工程中对spi驱动的配置项,以观察对信号及时序的对应关系。
spi_master_basic
样例工程分别以不同的控制方式实现的发送数据流和接收数据流,发送过程使用轮询方式,接收过程使用中断。
Conclusion
关于SPI外设中移位器的一些思考
在使用SPI外设时,要特别注意移位器的存在。寄存器接口上的标志位,绝大多数都是监查FIFO的状态,CPU可以通过这些状态标志位“卡点”,进行对应的操作。而这个移位器对于用户是隐藏在电路内部的,唯一相关的标志位就是SPI_STS[BUSY]
标志位。例如,在软件上向发送FIFO中写数后,SPI并不是直接将数送上总线,而是先送入移位器,驱动移位器工作后送数上线的同时,从总线上取数到移位器,之后才是将数送入接收FIFO供软件可读。这有一个执行的过程和结构,而不是写数到发送FIFO送走之后就能立刻从接收FIFO中读数的。另外,接收移位器相当于是接收FIFO的前级,接收匹配逻辑也会比较在移位器中缓存的接收数据,然后才可能送入接收FIFO或者直接抛弃掉。
这也是在spi_master_basic的样例工程中,为什么没有实现同步的轮询收发,而在接收过程中用了中断方式。实际上,如果使用SPI_STS[BUSY]
标志位取代SPI_STS[TXIF]
和SPI_STS[RXIF]
的使用,也可以实现发送过程和接收过程的同步,但这相当于弱化了FIFO的作用,通信的数据会出现不连贯的情况(需要等到移位器停止工作后才重新装载数据,中间等待软件操作的过程中,SPI总线上,多个数据单元之间会出现时间空隙)。
关于SPI从机的使用情况
SPI Slave的应用也比较典型,将当前芯片作为辅助核心的方式同主芯片配合工作,其中还涉及到输入Match功能的应用,下文再表。
关于SPI外设中实现多个PCS引脚的正确打开方式的思考
若将多个SPI从机连到一个SPI主机上,在程序运行过程中,使用时分复用多个PCS对这些从机分别通信,这种应用的场景看似功能强大,但限制颇多。多个SPI从机芯片共用同一组SIN、SOUT和SCK信号线:
- SOUT和SCK信号线的方向是主机发向所有的从机,单个主机控制信号线上的电平,所有从机接收,互不影响;
- 但SIN信号线是多个从机连在一起送向主机,但从机之间没有协调,容易冲突。
在多SPI从机连接系统中,若当其中一个从机的发送数据信号线为高电平而另一个从机为低电平时,就会出现局部短路,进而损坏电路。此时,如果仍要使用这种方式连接一主多从,可有两种解法:
- 限制每个从机的输出数据信号线必须在不传输数据的时候设定为高阻状态,通过各自的片选线激活其为输出方向。
- 限制每个从机的输出数据信号线必须在不传输数据的时候设定为开漏输出,类似于I2C总线解决多设备共享物理介质总线的思路。
但SPI从机设备的生产厂商各自不同,不能保证每家都有这个设计。临时凑在一起,总归会有比较大的风险。
因此,实际上绝大多数用户宁可多用几组引脚,为每个SPI从机设备专门分配接线的引脚。而多PCS设计的作用更多在于PCB布线的方便,从多个可选的PCS引脚中就近选择一个可用的PCS接到SPI从机设备上,对应在软件上,一旦确定下来某个SPI外设使用确定的PCS引脚,在整个程序的运行周期中就不再改动了。
作者:安德鲁苏
来源:安德鲁的设计笔记本
推荐阅读
欢迎大家点赞留言,更多Arm技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。