1.结构
5744的SPI模块支持全双工三线同步传输,可运行在主机或从机模式,分别含有深度为5的FIFO发送和接收缓存区。其结构如下图。SPI配置允许模块发送和接收串行数据,同时也支持带FIFO缓存区的的进行扩展队列操作的数据传输。模块接收和发送的数据存放在独立的FIFO内,CPU或DMA控制器从接收FIFO读取数据,写入数据到Tx FIFO内进行发送。
2、传输过程
1)发送过程
发送数据时CPU先查询寄存器SR内TFFF的状态,若TFFF为为0则表明TX FIFO已满,继续写入无效;当TFFF为1则表明TX FIFO未满,可以继续写入。CPU通过写数据到寄存器PUSHR将要发送的数据添加到TX FIFO内,TX FIFO根据先入先出的顺序将数据移入移位寄存器进行发送,只要TX FIFO内有有效的数据,则一直进行发送。在每一次成功传输后,寄存器SR内的TCF被置位表明一次成功的传输。寄存器SR的TXCTR值阈计数TX FIFO内的有效数据数,当有数据被压入或被传送到移位寄存器时,TXCTR随之更新。当PUSHR寄存器中EOQ==1的数据被传输完成后,寄存器SR中的EOQF置位表明数据队列被全部发送完毕,可用于指示多个数据的发送完毕。
2)接收过程
当移位寄存器内的数据接收完成后,接收到的数据被添加到RX FIFO内,CPU通过读POPR寄存器将数据从RX FIFO内取出。当RX FIFO 不为空,寄存器SR中的RFDF被置位,当读取数据时,CPU先查询寄存器SR内RFDF的状态,若RFDF为0则表明RX FIFO为空,继续读取数据无效;当RFDF为1则表明RX FIFO不为空,可通过读POPR寄存器来读取RX FIFO内的数据。相应地,寄存器SR的RXCTR值阈计数RX FIFO内的有效数据数,当有数据被移入到RX FIFO 或被CPU通过POPR取出时,RXCTR随之更新。
3、传输模式
SPI传输可分为主机模式和从机模式,也有经典模式和改进模式。本文以经典主机模式为例。主机模式下SCK、PCS、SOUT为输出,SIN为输入。
1)CPHA=0
如下图CPHA=0时,数据捕捉在SCK的奇数跳边沿,数据变化在SCK的偶数跳边沿。与之相对应地若CPOL=0,SCK高电平有效,则数据捕捉在SCK的上升沿,数据变化在SCK的下降沿;若CPOL=1,SCK低电平有效,则数据捕捉在SCK的下降沿,数据变化在SCK的上升沿。
2)CPHA=1
如下图所示,当CPHA=1,数据捕捉在SCK的偶数跳边沿,数据变化在SCK的奇数跳边沿。相应地若CPOL=0,SCK高电平有效,则数据捕捉在SCK的下降沿,数据变化在SCK的上升沿;若CPOL=1,SCK低电平有效,则数据捕捉在SCK的上升沿,数据变化在SCK的下键沿。
3)持续片选模式和非持续片选模式
持续片选模式是指在两个数据帧之间片选信号PCS一直保持有效为低电平;非持续片选模式是指在两个数据帧之间片选信号PCS变为无效,二者区别如下图所示。持续片选和非持续片选模式有寄存器PUSHR[CONT位决定,PUSHR[CONT]=0选择非持续片选,PUSHR[CONT]=1选择持续片选。具体选择哪种方式要根据所要驱动的外设来确定,由于该模式有PUSHR寄存器选择,故在发送数据时要求连续数据持续片选和非持续片选要保持一致,不可混合使用,否则会造成传输错误。
4.波特率和时序延时
SPI中协议时钟即是外设桥时钟,也就是在时钟模块所配置的时钟频率,文中缩写为fp。
1)波特率
波特率即是SCK的频率。SPI的波特率由寄存器CTARn中DBR、PBR和BR决定,计算公式如下。其中Prescaler由PBR决定,Scaler由BR决定,对应关系如下表。
2)tcsc
tcsc是指从PCS有效到SCK第一个跳变沿的时间,由CTARn寄存器中PCSSCK和CSSCK决定。
3)tasc
tasc时钟从SCK最后一个跳边沿到PCS无效的时间,由CTARn寄存器中PASC和ASC决定。
4)tdt
tdt是指本次传输PCS无效到下次传输PCS有效的时间。
各个延时值如下图所示:
5.初始化
5744初始化步骤如下所示:
1)运行模式选择;
2)管脚复用功能配置;
1)使能SPI模块,停止传输以进行寄存器的配置;
2)配置CTARn以进行数据长度、传输模式、波特率和延时的设置,每个CTAR可有不同的配置,PUSHR写入数据到TX FIFO时可根据需要选择不同的CATR寄存器。
3)启动传输过程。
示例代码如下:
/******************************************************
* 函数名 SPI_SPI0_Init
* 功能 对SPI0模块进行初始化
* 输入参数 无
* 返回值 无
* 示例 SPI_SPI0_Init();//初始化SPI0
******************************************************
*/
void SPI_SPI0_Init()
{
//运行模式选择
MC_ME.PCTL99.B.RUN_CFG=0; //选择运行模式0
//管脚多路复用配置
SIUL2.MSCR[36].B.SSS=1; //PC4,选择作为SPI0_PCS0
SIUL2.MSCR[36].B.OBE=1;
SIUL2.MSCR[36].B.SRC=3;
SIUL2.MSCR[37].B.SSS=1; //PC5,选择作为SPI0_SCK
SIUL2.MSCR[37].B.OBE=1;
SIUL2.MSCR[37].B.SRC=3;
SIUL2.MSCR[38].B.SSS=1; //PC6,选择作为SPI0_SOUT
SIUL2.MSCR[38].B.OBE=1;
SIUL2.MSCR[38].B.SRC=3;
SIUL2.IMCR[41].B.SSS=1; //PC7,选择作为SPI0_SIN
SIUL2.MSCR[39].B.IBE=1;
//SPI配置
SPI_0.MCR.B.MDIS=0; //使能SPI模块
SPI_0.MCR.B.HALT=1; //停止传输
SPI_0.MCR.B.MSTR=1; //选择主机模式
SPI_0.MCR.B.PCSIS=1; //PCS0无效状态为高
//CTAR[0]寄存器
SPI_0.MODE.CTAR[0].B.FMSZ=7; //配置数值长度为8=FMSZ+1
//以下两位要根据对应外设选择,否则SPI通信会失败!!!
SPI_0.MODE.CTAR[0].B.CPHA=1; //数据捕捉在偶数跳变沿
SPI_0.MODE.CTAR[0].B.CPOL=1; //SCK低电平有效
SPI_0.MODE.CTAR[0].B.PCSSCK=2; //tcsc=5*8/45mhz=0.875us
SPI_0.MODE.CTAR[0].B.CSSCK=2;
SPI_0.MODE.CTAR[0].B.PASC=2; //tacs=5*8/45mhz=0.875us
SPI_0.MODE.CTAR[0].B.ASC=2;
//波特率=(fP/PBR)x[(1+DBR)/BR]=45mhz/5*(1+0)/4=2.25MHz
SPI_0.MODE.CTAR[0].B.PBR=2;
SPI_0.MODE.CTAR[0].B.BR=1;
SPI_0.MODE.CTAR[0].B.PDT=2;
SPI_0.MODE.CTAR[0].B.DT=13; //tdt=5*32768/45mhz=3.64ms
SPI_0.MCR.B.HALT=0; //开始传输
}
6.数据发送
发送数据通过将数据写入PUSHR寄存器发送。其实PUSHR寄存器除数据部分外,还有命令部分,可根据需要进行选择。
- CONT选择是否为持续片选模式
- CTAS用于选择初始化中设置的CTAR寄存器用以选择用于传输的数据长度、传输模式、波特率和延时等。
- EOQ用于标识是否为数据队列的最后一帧数据
- PCS用于选择片选引脚。
发送步骤为:
1)将数据写入PUSHR寄存器;
2)清空SR寄存器EOQF位开始发送;
3)等待发送完成;
4)清除发送完成标志位;
代码如下:
/******************************************************
* 函数名 SPI_SPI0_Write
* 功能 通过SPI0发送指定数据
* 输入参数
* data
* 要发送的数据
* 返回值 无
* 示例 SPI_SPI0_Write(tempData);//通过SPI发送数据tempData
******************************************************
*/
void SPI_SPI0_Write(uint32_t data)
{
uint32_t temp_data;
temp_data=0x08010000|data; //置位EOF标志为最后一帧数据,选择PCS0为片选引脚
SPI_0.PUSHR.PUSHR.R=temp_data;
SPI_0.SR.B.EOQF=1; //开始传送
while(!SPI_0.SR.B.TCF); //等待传送完成
SPI_0.SR.B.TCF=1; //清除发送标志位
}
7.数据接收
数据接收时需要等待RX FIFO不为空,也即是SR寄存器RFDF为1。若RFDF为1,则通过读取POPR寄存取来读取数据,然后清空对应标志位。示例代码如下:
/******************************************************
* 函数名 SPI_SPI0_Read
* 功能 通过SPI0读取数据
* 输入参数 无
* 返回值 通过SPI0所读取到的数据
* 示例 uint32_t tempData=SPI_SPI0_Read();//通过SPI0读取数据到tempData
******************************************************
*/
uint32_t SPI_SPI0_Read()
{
uint32_t recv_data=0;
while (SPI_0.SR.B.RFDF != 1); //等待Rx FIFO不为空
recv_data= SPI_0.POPR.R; //读取接收到的数据
SPI_0.SR.R = 0xFCFE0000; //清空相应标志位
return recv_data;
}