摘要
本文大致介绍了F28335中SPI工作原理和大致寄存器。还有很多细节知识没有列出,需要详细了解的同学,可以参考TI官方文档(TI官网免费下载),或者可以看书籍。重点推荐符晓编写的<TMS320F28335原理、开发及应用>。重点不推荐张卿杰的<手把手教你学DSP>。研旭的开发板还可以,但是书真的编辑的很烂。参考的资料比较老旧,纸张差,有错别字,细节错误很多,翻译的也不流畅。英语好的同学可以直接阅读TI官方文档,国内的书基本上就是翻译。
本文主要通过最后的例程演示SPI传输时的一些细节问题。并详细注释了一个自发自收程序。可以通过改变参数来实现添加FIFO功能。整个工程会打包传送到CSDN。有需要的同学可以下载。若是没有积分,可以留言免费索要。
概述
SPI作为高速串行接口,常用于DSP与外设或者DSP与DSP之间数据交换。F28335只有一个SPI模块。TI官方库中对应的结构体变量名称为:SpiaRegs。SPI寄存器不受EALLOW保护。主要有四根信号线,F28335给出两组GPIO提供选择。具体如下表:
信号名称 | 功能描述 | 第一组 | 第二组 |
---|---|---|---|
SPISIMO | SPI从输入/主输出 | GPIO16 | GPIO54 |
SPISOMI | SPI从输出/主输入 | GPIO17 | GPIO55 |
SPICLK | SPI时钟 | GPIO18 | GPIO56 |
S P I S T E ‾ \overline{ \rm SPISTE} SPISTE | 从控制下发送使能控制位 | GPIO19 | GPIO57 |
SPI在F28335上的地址分配为:
SPIA : origin = 0x007040, length = 0x000010 /* SPI-A registers */
工作模式
SPI共有两种工作方式:主控制器工作方式和从控制器工作方式。
主控制器模式
- 数据从SPISIMO引脚输出,从SPISOMI引脚锁存。
- 主控制器提供整个通信的时钟SPICLK,设置时钟的波特率。
从控制器模式
- 数据从SPISOMI输出,从SPISIMO输入。
- 时钟由主控制器提供,但不能超过从控制器LSPCLK时钟频率的四分之一。
- 选通信号允许多个设备连接在一起构成网络,但是在某个时刻只能有一个从控制器被选通。
主要特点
- SPI是全双工,同步传输串行信号。
- 数据的发送方向是先高位后低位。
- 无论是主控制器还是从控制器,数据在SPICLK一个边沿发送,在另一个边沿将会被锁存。对于任意一个控制器,接收和发送数据都是同步进行的。
- 当数据长度不为16位时,发送时保持左对齐(编程控制),接收时为右对齐(DSP自动实现)。
波特率
S P I 波 特 率 = { L S P C L K S P I B R R + 1 ( 4 ≤ S P I B R R ≤ 127 ) L S P C L K S P I B R R + 1 ( S P I B R R = 0 , 1 , 2 , 3 ) SPI波特率=\left\{ \begin{aligned} \frac {LSPCLK}{SPIBRR+1} & & ( {4 \leq SPIBRR \leq 127})\\ \frac {LSPCLK}{SPIBRR+1} & & ({SPIBRR=0,1,2,3}) \end{aligned} \right. SPI波特率=⎩⎪⎪⎨⎪⎪⎧SPIBRR+1LSPCLKSPIBRR+1LSPCLK(4≤SPIBRR≤127)(SPIBRR=0,1,2,3)
中断
SPI在F28335中共占用两个中断向量,分别为:SPIRXINTA(INT6.1)和SPITXINTA(INT6.2)。当为非FIFO模式时,只使用SPIRXINTA中断。这是因为在SPI中,收发是同步的,换言之,收到一个数据的同时必然发出了一个数据。所以,在非FIFO模式,只需要一个中断即可同时表现收/发完成。
当为FIFO模式时,才能够同时使用两个中断。
具体的对应关系如下表:
FIFO选项 | FIFO使能位 | SPI中断源 | 中断标志 | 中断使能 | 中断向量 |
---|---|---|---|---|---|
非FIFO模式 | 0 | 接收溢出 | RXOVRN | OVRNINTENA | SPIRXINT |
非FIFO模式 | 0 | 正常接收完成 | SPIINT | SOIINTENA | SPIRXINT |
非FIFO模式 | 0 | 正常发送完成 | SPINT | SPINTENA | SPIRXINT |
FIFO模式 | 1 | FIFO接收完成 | RXFFIL | RXFFIENA | SPIRXINT |
FIFO模式 | 1 | FIFO发送完成 | TXFFIL | TXFFIENA | SPITXINT |
还可以用下图表示该逻辑:
数位对齐
可以通过设置寄存器规定一次传送过程中数据的长度。当数据的长度小于16时,需要保证数位对齐。
- 发送端写入SPIDAT或者SPITXBUF中的数据必须左对齐。这需要开发者编程保证左对齐。
- 接收端读取SPIRXBUF,得到的数据是右对齐的。这需要开发者屏蔽无效高比特位。
主要寄存器
struct SPI_REGS
{
union SPICCR_REG SPICCR; // 配置与控制寄存器
union SPICTL_REG SPICTL; // 运行方式控制寄存器
union SPISTS_REG SPISTS; // 状态寄存器
Uint16 rsvd1; // reserved
Uint16 SPIBRR; // 波特率寄存器
Uint16 rsvd2; // reserved
Uint16 SPIRXEMU; // 仿真缓冲寄存器
Uint16 SPIRXBUF; // 接收缓冲寄存器
Uint16 SPITXBUF; // 发送缓冲寄存器
Uint16 SPIDAT; // 串行数据寄存器
union SPIFFTX_REG SPIFFTX; // FIFO发送寄存器
union SPIFFRX_REG SPIFFRX; // FIFO接收寄存器
union SPIFFCT_REG SPIFFCT; // FIFO控制寄存器
Uint16 rsvd3[2]; // reserved
union SPIPRI_REG SPIPRI; // FIFO优先权控制寄存器
};
SPI模块的原理如下图所示:
例程
功能
1.实现SPI自发自收。
2.验证SPI发送时序。
3.验证发送数据位不足16位,左右对齐。
4.通过标志位确认是否使用FIFO,验证两种方式的中断不同!
源代码
主程序
int main(void)
{
//1.系统初始化
InitSysCtrl();
//2.初始化GPIO
InitSpiGpio(); //使用GPIO16-GPIO19
//3.中断
//3.1 关闭中断
DINT;
IER = 0x0000; //关闭CPU级中断
IFR = 0x0000; //清除中断标志
InitPieCtrl(); //关闭PIE中断
InitPieVectTable(); //初始化PIE中断向量
//3.2写入使用的中断向量
EALLOW;
PieVectTable.SPIRXINTA = &IsrSpiRx; //接收中断服务程序
#if isFIFO
PieVectTable.SPITXINTA = &IsrSpiTx; //发送中断服务程序
#endif
EDIS;
//3.3PIE中断使能
PieCtrlRegs.PIECTRL.bit.ENPIE = 1; //使能PIE中断
PieCtrlRegs.PIEIER6.bit.INTx1 = 1; //使能第六组第一位中断(SPI接收)
#if isFIFO
PieCtrlRegs.PIEIER6.bit.INTx2 = 1; //使能第六组第二位中断(SPI发送)
#endif
IER |= M_INT6; //CPU第六组中断
EINT;
//4.初始化外设
InitSpi();
//5.自定义代码
SpiaRegs.SPITXBUF=00;//启动发送功能
while (1);
}
初始化SPI
void InitSpi(void)
{
SpiaRegs.SPICCR.bit.SPISWRESET = 0; //SPI复位
//数据在时钟上升沿发送,在时钟下降沿所存输入的数据
SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //上升沿触发
SpiaRegs.SPICTL.bit.CLK_PHASE = 0; //无延时时钟
SpiaRegs.SPICCR.bit.SPILBK = 1; //内部回路使能,实现自发自收
SpiaRegs.SPICCR.bit.SPICHAR = 0xF; //发送和接收的数据长度16
SpiaRegs.SPICTL.bit.OVERRUNINTENA = 0; //关闭溢出中断
SpiaRegs.SPICTL.bit.MASTER_SLAVE = 1; //主控制器模式
SpiaRegs.SPICTL.bit.TALK = 1; //使能发送功能
SpiaRegs.SPICTL.bit.SPIINTENA = 1; //禁止SPI中断
SpiaRegs.SPIBRR = 0x63; //波特率=150M/4/100=375K
SpiaRegs.SPIPRI.bit.FREE = 1; //仿真不影响发送数据
SpiaRegs.SPICCR.bit.SPISWRESET = 1; //SPI功能恢复
#if isFIFO
SpiaRegs.SPIFFTX.bit.SPIFFENA = 1; //使能FIFO功能
SpiaRegs.SPIFFTX.bit.TXFIFO = 0;//FIFO发送功能复位
SpiaRegs.SPIFFTX.bit.TXFFIENA = 1;//使能TXFIFO中断
SpiaRegs.SPIFFTX.bit.TXFFIL = 0;//FIFO深度,若是发送FIFO中数据少于等于0个,则触发中断
SpiaRegs.SPIFFRX.bit.RXFIFORESET = 0;//FIFO接收功能复位
SpiaRegs.SPIFFRX.bit.RXFFIENA = 1;//使能FIFO接收中断
SpiaRegs.SPIFFRX.bit.RXFFIL = 1;//FIFO深度,若是接收FIFO中数据多于等于1个,则触发中断
SpiaRegs.SPIFFTX.bit.TXFIFO = 1;//FIFO发送功能恢复
SpiaRegs.SPIFFRX.bit.RXFIFORESET = 1;//FIFO接收功能恢复
#endif
}
中断服务程序
//定义全局变量
Uint16 rx = 0;
Uint16 tx = 0;
#if isFIFO
//SPI发送中断服务程序
interrupt void IsrSpiTx()
{
tx++;
SpiaRegs.SPITXBUF = tx; //发送数据
SpiaRegs.SPIFFTX.bit.TXFFINTCLR = 1; //清除发送中断
PieCtrlRegs.PIEACK.bit.ACK6 = 1; //清除第六组中断
}
#endif
//SPI接收中断服务程序
interrupt void IsrSpiRx()
{
#if isFIFO==0
tx++;
SpiaRegs.SPITXBUF = tx;
#endif
rx = SpiaRegs.SPIRXBUF; //接收数据
#if isFIFO
SpiaRegs.SPIFFRX.bit.RXFFINTCLR = 1; //清除接收中断
#endif
PieCtrlRegs.PIEACK.bit.ACK6 = 1; //清除第六组中断
}
结果
断点是打在rx = SpiaRegs.SPIRXBUF; //接收数据
处。可以总结出,读取的SPIRXBUF数据是上一次发送的数据。示波器的一号针脚连接的是SPI时钟线,二号针脚连接的是SPISIMO,即本次发送的数据值。
第一组:传送数据为16位
第二组:传送数据为5位