SPI初始化分析
KV56中SPI的初始化通过SPI_MasterInit(void)函数,在SPI.c文件下。
具体代码
代码清单1
void SPI_MasterInit(void)
{
/* Configure the SPI corresponding Pins */
PORTC_PCR4 = PORT_PCR_MUC(2); //SPI0_PCS0
PORTC_PCR5 = PORT_PCR_MUC(2); //SPI0_SCK
PORTC_PCR6 = PORT_PCR_MUC(2); //SPI0_SOUT
PORTC_PCR7 = PORT_PCR_MUC(2); //SPI0_SIN
/* SPI MCR Configure */
SPI0_MCR &= ~SPI_MCR__MDIS_MASK; //使能模式,注意此句必须是‘=’号,MDIS默认为1,此时无法设置DIS_TSF和DIS_RSF
SPI0_MCR |= SPI_MCR__HALT_MASK; //SPI Stop Transfer
SPI0_MCR |= SPI_MCR__MSTR_MASK //主机模式
| SPI_MCR_PCSIS(0x01) //PCSx 信号的空闲状态为高 Pcs Active Low(idles high)
| SPI_MCR_CLR_TXF_MASK
| SPI_MCR_CLR_RXF_MASK
| SPI_MCR_DIS_TXF(FALSE) | SPI_MCR_DIS_RXF(FALSE); //TX RX FIFO is enableed.
/* SPI CTAR0 Configure */
SPI0_CTAR0 = SPI_CTAR_FMSZ(7); //注意这句话必须是 = 号,因为fmsz默认为1111,SPI发送数据帧大小设定,实际数据位为该值+1,8bit
/* SCK baud rate f(SCK) = [f(Busclk)/PBR]*[(1+DBR)/BR],f(SCK)最大值为f(Busclk)/2,f(Busclk) = 120MHz */
SPI0_CTAR0 |= SPI_CTAR_BR(0); // BR = 0,2分频
| SPI_CTAR_BR(2); // BR = 2,5分频
| SPI_CTAR_PCSSCK(0); // PCS to SCK Prescaler value is 1
| SPI_CTAR_CSSCK(2); //CSSCK为PCSx有效到SCK有效时间间隔value is 8
| SPI_CTAR_PASC(1); //PASC为SCK无效到PCSx无效的时间间隔value is 3
| SPI_CTAR_DBR(0); //DBR is 50/50 dury cycle
/* SPI RSER Configure */
SPI_RSER = 0; //Disabled all interrupt requests
SPI0_SR |= SPI_SR_EOQF_MASK //Clear End of Queue flag
| SPI_SR_TFFF_MASK //Clear Transmit FIFO Fill flag
| SPI_SR_TCF_MASK //Clear Transmit Complete flag
SPI0_PUSHR |= SPI_PUSHR_PCS(1) //Select PCS0 signals are to be asserted for the transfer
| SPI_PUSHR_CONT_MASK;
return;
}
分析代码清单2
代码清单2
/* Configure the SPI corresponding Pins */
PORTC_PCR4 = PORT_PCR_MUC(2); //SPI0_PCS0
PORTC_PCR5 = PORT_PCR_MUC(2); //SPI0_SCK
PORTC_PCR6 = PORT_PCR_MUC(2); //SPI0_SOUT
PORTC_PCR7 = PORT_PCR_MUC(2); //SPI0_SIN
注释:
Configure the SPI corresponding Pins:配置SPI对应的管脚。
配置的是哪几个引脚?PTC4、PTC5、PTC6、PTC7。
分别对应:
SPI的PCS0、SCK、SOUT、SIN。
PCS0、SCK、SOUT、SIN是啥?因为以前没看过SPI这块,有点菜。
因为MKV56和k60都是飞思卡尔系列,所以先到K60上查一下。
k60-SPI信号线
再看KV56
KV56-SPI信号线1.png
KV56-SPI信号线2.png
原来把PTC4、PTC5、PTC6、PTC7配置了信号线。如何配置的呢?
代码清单2用的是PORT_PCR_MUC(2)。
看一下PORT_PCR_MUC(2)是如何实现的:
代码清单3-配置SPI信号线引脚
#define PORT_PCR_MUX(x) (((uint32_t)(((uint32_t)(x)) << PORT_PCR_MUX_SHIFT)) & PORT_PCR_MUX_MASK)
其中
#define PORT_PCR_MUX_SHIFT 8U
#define PORT_PCR_MUX_MASK 0xF00U。
所以PORT_PCR_MUC(2)的结果是0x10’0000‘0000。
0x10’0000‘0000代表什么意思?
真没想出来。
后来想到,既然通过PORT_PCR_MUC(2)配置了引脚,那肯定和PORT有关。
去查PTC4
KV56_PORT_PCR_MUC(2)———0x10’0000‘0000含义.png
对照上图一看,大致猜到应该是将PTC4、PTC5、PTC6、PTC7复用了SPI0。
去找GPIO->PORTx-PCRn看引脚是怎么实现复用的。
照例先看k60(中文),再看KV56(英文)。如下图,可以知道K60的PORTx-PCRn的第8-10位用来配置引脚复用。
引脚复用-k60.png
KV56的第8-11用来配置引脚复用。0x10’0000‘0000的 bit9=1,即选择ATL2
引脚复用-KV56.png
小结:
代码清单2这4句实现了2个功能
1.配置SPI信号线引脚;
2.对引脚实现功能复用(SPI)
代码清单2
/* Configure the SPI corresponding Pins */
PORTC_PCR4 = PORT_PCR_MUC(2); //SPI0_PCS0
PORTC_PCR5 = PORT_PCR_MUC(2); //SPI0_SCK
PORTC_PCR6 = PORT_PCR_MUC(2); //SPI0_SOUT
PORTC_PCR7 = PORT_PCR_MUC(2); //SPI0_SIN
分析代码清单3
这里明显是在配置SPI的MCR寄存器。
/* SPI MCR Configure */
SPI0_MCR &= ~SPI_MCR__MDIS_MASK; //使能模式,注意此句必须是‘=’号,MDIS默认为1,此时无法设置DIS_TSF和DIS_RSF
SPI0_MCR |= SPI_MCR__HALT_MASK; //SPI Stop Transfer
SPI0_MCR |= SPI_MCR__MSTR_MASK //主机模式
| SPI_MCR_PCSIS(0x01) //PCSx 信号的空闲状态为高 Pcs Active Low(idles high)
| SPI_MCR_CLR_TXF_MASK
| SPI_MCR_CLR_RXF_MASK
| SPI_MCR_DIS_TXF(FALSE) | SPI_MCR_DIS_RXF(FALSE); //TX RX FIFO is enableed.
SPI0_MCR &= ~SPI_MCR__MDIS_MASK; //使能模式,注意此句必须是‘=’号,MDIS默认为1,此时无法设置DIS_TSF和DIS_RSF
#include SPI_MCR__MDIS_MASK 0x4000U
so, SPI0_MCR = SPI0_MCR & 11 1111 1111 1111
而
#define SPI0_MCR SPI_MCR_REG(SPI0_BASE_PTR)
#define SPI_MCR_REG(base) ((base)->MCR),
#define SPI0_BASE_PTR 0x4002C000u
即 SPI0_MCR ((0x4002C000u)->MCR)
SPI0_MCR = 0x4002C000u & 11 1111 1111 1111 = 0;
SPI0_MCR |= SPI_MCR__HALT_MASK; //SPI Stop Transfer
#define SPI_MCR__HALT_MASK 0x1u,
so SPI0_MCR = 0x1
配置MCR寄存器实现'停止传输'功能.png
继续分析代码清单3
SPI0_MCR |= SPI_MCR__MSTR_MASK //主机模式
| SPI_MCR_PCSIS(0x01) //PCSx 信号的空闲状态为高 Pcs Active Low(idles high)
| SPI_MCR_CLR_TXF_MASK
| SPI_MCR_CLR_RXF_MASK
| SPI_MCR_DIS_TXF(FALSE) | SPI_MCR_DIS_RXF(FALSE); //TX RX FIFO is enableed.
SPI_MCR__MSTR_MASK 0x8000'0000U
SPI_MCR_PCSIS(0x01) 1 0000 0000 0000 0000
SPI_MCR_CLR_TXF_MASK 0x800U
SPI_MCR_CLR_RXF_MASK 0x400U
SPI_MCR_DIS_TXF(FALSE) = 0
SPI_MCR_DIS_RXF(FALSE) = 0
so
SPI0_MCR = 1000 0000 8000 0C00
=1 0000 0000 0000 0000 0000 0000 0000 1000 0000 0000 0000 0000 1100 0000 0000
其中
(3.1) SPI_MCR__MSTR_MASK
SPI_MCR__MSTR_MASK 0x8000'0000U
0x8000'0000.png
(3.2) SPI_MCR_PCSIS(0x01)
SPI_MCR_PCSIS(0x01) 1 0000 0000 0000 0000
SPI_MCR_PCSIS.png
(3.3)SPI_MCR_CLR_TXF_MASK
SPI_MCR_CLR_TXF_MASK 0x800U
CLR_TXF.png
(3.4)SPI_MCR_CLR_RXF_MASK
SPI_MCR_CLR_RXF_MASK 0x400U
CLR_RXF.png
(3.5) SPI_MCR_DIS_TXF(FALSE) | SPI_MCR_DIS_RXF(FALSE) = 0;开始传输
TXF -RXF.png
分析代码清单4
SPI0_CTAR0 = SPI_CTAR_FMSZ(7); //注意这句话必须是 = 号,因为fmsz默认为1111,SPI发送数据帧的大小设定,实际数据位为该值+1,8bit
/* SCK baud rate f(SCK) = [f(Busclk)/PBR]*[(1+DBR)/BR],f(SCK)最大值为f(Busclk)/2,f(Busclk) = 120MHz */
SPI0_CTAR0 |= SPI_CTAR_BR(0); // BR = 0,2分频
| SPI_CTAR_BR(2); // BR = 2,5分频
| SPI_CTAR_PCSSCK(0); // PCS to SCK Prescaler value is 1
| SPI_CTAR_CSSCK(2); //CSSCK为PCSx有效到SCK有效时间间隔value is 8
| SPI_CTAR_PASC(1); //PASC为SCK无效到PCSx无效的时间间隔value is 3
| SPI_CTAR_DBR(0); //DBR is 50/50 dury cycle
/* SPI RSER Configure */
SPI_RSER = 0; //Disabled all interrupt requests
(4.1)
//注意这句话必须是 = 号,因为fmsz默认为1111,SPI发送数据帧大小设定,实际数据位为该值+1,8bit
SPI0_CTAR0 = SPI_CTAR_FMSZ(7);
=111 1000 0000 0000 0000 0000 0000 0000
bit30\29\28\27为1
FMSZ.png
(4.2)
/* SCK baud rate f(SCK) = [f(Busclk)/PBR]*[(1+DBR)/BR],f(SCK)最大值为f(Busclk)/2,f(Busclk) = 120MHz */
SPI0_CTAR0 |= SPI_CTAR_BR(0); // BR = 0,2分频
| SPI_CTAR_BR(2); // BR = 2,5分频
| SPI_CTAR_PCSSCK(0); // PCS to SCK Prescaler value is 1
| SPI_CTAR_CSSCK(2); //CSSCK为PCSx有效到SCK有效时间间隔value is 8
| SPI_CTAR_PASC(1); //PASC为SCK无效到PCSx无效的时间间隔value is 3
| SPI_CTAR_DBR(0); //DBR is 50/50 dury cycle
(4.2.1)
SPI_CTAR_BR(0) ==0<<0U & FU = 0
BR.png
(4.2.2)
SPI_CTAR_PBR(2); // BR = 2,5分频
SPI_CTAR_PBR(2) == 2<<16 & 0x3'0000 = 10 0000 0000 0000 0000
bit16 = 0,bit17=1;
PBR.png
(4.2.3)
SPI_CTAR_PCSSCK(0) // PCS to SCK Prescaler value is 1
SPI_CTAR_PCSSCK(0) = 0<<22 & 0xC0'0000
PCSSCK.png
(4.2.4)
SPI_CTAR_CSSCK(2); //CSSCK为PCSx有效到SCK有效时间间隔value is 8
SPI_CTAR_CSSCK(2) ==2<<12 & 0xF000 =10 0000 0000 0000
CSSCK.png
(4.2.5)
SPI_CTAR_PASC(1); //PASC为SCK无效到PCSx无效的时间间隔value is 3
其中SPI_CTAR_PASC(1) =1<<20 &0x30'0000 = 1 0000 0000 0000 0000 0000,bit20 ==1
PASC.png
(4.2.6)
SPI_CTAR_DBR(0); //DBR is 50/50 dury cycle
SPI_CTAR_DBR(0) =0<<31 & 0x8000'0000= 0000 0000 0000 0000 0000 0000 0000 0000,bit31等于0
DBR.png
代码清单5
/* SPI RSER Configure */
SPI_RSER = 0; //Disabled all interrupt requests
SPI0_SR |= SPI_SR_EOQF_MASK //Clear End of Queue flag
| SPI_SR_TFFF_MASK //Clear Transmit FIFO Fill flag
| SPI_SR_TCF_MASK //Clear Transmit Complete flag
SPI0_PUSHR |= SPI_PUSHR_PCS(1) //Select PCS0 signals are to be asserted for the transfer
| SPI_PUSHR_CONT_MASK;
代码清单5这里就不在分析,和上面的分析是一样的。
总结
SPI的初始化,其实就是对各个寄存器的配置(各个寄存器位的值设置)。当然不是乱配置的,而是,基于我们要实现的SPI功能,进行相应的配置。
我这里只是,结合配置\初始化好的SPI程序,去学习操作手册。
没错,这篇文章,其实就是教你如何去看操作手册,以及,了解程序中是如何通过代码来设置寄存器实现SPI功能的