一:SPI简介
SPI ( Serial Peripheral lnterface,串行外围设备接口)通讯协议,是摩托罗拉公司提出的一种同步串行接口技术,是一种高速、全双工、同步通信总线,在芯片中只占用四根管脚用来控制及数据传输。SPI相比IIC和UART来说传输速度更快,但是由于协议本身缺少相应的应答和校验操作,在数据的可靠性上有一定缺陷!
1.1:SPI物理线路说明;
标准的SPI包括四根线,分别是CS(片选线),SCK(同步时钟线),MOSI(主机输出从机输入线),MISO(主机输入从机输出线),在通信时一般将主机和从机对应线连接起来就好,如下图所示:
1.2:SPI传输模式说明;
在说传输模式前,有必要先简单说一下SPI中的时钟极性(CPOL)和时钟相位(CPHA)。时钟极性可以理解成SPI时钟线(SCK)在空闲时的电平状态,例如CPOL=0,那么在SPI空闲时时钟线(SCK)就是低电平,当CPOL=1时,那么在SPI空闲时时钟线就为高电平;时钟相位(CPHA)可以理解成SPI采集数据的时刻,CPHA=0时代表数据在第一个时钟沿采样,CPHA=1时代表数据在第二个时钟沿采样,例如当CPOL=0,CPHA=0时,数据的变化和采样如下图(绿线处变化,橙线处采样)所示:
模式0 CPOL=0,CPHA=0—>SCK空闲电平为低电平,数据在第一个时钟沿采样;
模式1 CPOL=0,CPHA=1—>SCK空闲电平为低电平,数据在第二个时钟沿采样;
模式2 CPOL=1,CPHA=0—>SCK空闲电平为高电平,数据在第一个时钟沿采样;
模式3 CPOL=1,CPHA=1—>SCK空闲电平为高电平,数据在第二个时钟沿采样;
二:CS88F003 SPI模块简要介绍和使用;
2.1:CS88F003 SPI介绍;
cs88f003总共包含一个spi模块,支持主从模式,并且支持可编程的时钟频率。由于在使用中很多情况都只使用到单纯的发送和接收,所以本教程教程也只简单的介绍SPI 发送、SPI接收,别的功能其实也大差不差本文就不再赘述!
2.2:CS88F003 SPI主模式中断检测发送数据;
实验目的: 在主函数中检测SPI是否在发送数据,如果上一个数据已发送完(或刚准备发送第一个数据),则继续发送数据0x0f,最终通过逻辑分析仪可以抓取主机SPI的MOSI信号线一直在发送0x0f;
实验代码:
/*******************************************main.c***********************************************/
void main(void)
{
Sys_Clk_Set_IRCH(SYS_CLK_8M); //芯片默认校准为16M,分频后系统主频8M
SysClk_Delay(50);
WDT_OFF();
/*1:第一步开启引脚复用功能,例如PT15-CS,PT10-SCK,PT00-MOSI,PT01-MISO*/
PT0_MUX1 |= (0x02|0x01);//0x01;
PT1_MUX1 |= (0x20|0x01);//0x01;
/*2:第二步打开SPI时钟*/
SPI_SPE |= 0x02;
/*3:第三步SPI控制寄存器复位-官方例程是写了这个步骤*/
SPI_CR0 = 0x00;
SPI_CR1 = 0x00;
/*4:第四步设置SPI的通信速率-例如将频率设置为1M*/
SPI_CR0 |= (0x01<<5);
/*5:第五步设置SPI主从模式,例如将设置SPI为主机模式*/
SPI_CR0 |= 0x04;
/*6:第六步设置SPI工作模式,例如设置为模式0 CPOL=0,CPHA=0*/
SPI_CR0 &= ~(0x03);
/*7:第七步开启主模式下的CS输出*/
SPI_CR1 |= 0x40;
/*8:第八步选择SPI中断输出口,例如选择INT0输出*/
IRQ_SEL0 |= 0x40;
/*9:第九步使能SPI相关中断,例如开启SPI发送中断*/
SPI_CR1 |= 0x02;
/*10:第十步开启全局中断和INT0*/
IE |= 0x81;
/*11:第十一步使能SPI*/
SPI_SPE |= 0x01;
//中断相关函数请查看interrupt.c......
while(1)
{
/*等待SPI发送完成*/
if(spi_flag)
{
/*标志位置0,准备发送数据*/
spi_flag = 0;
SPI_DR = 0x0f;
}
}
}
/*******************************************interrupt.c***********************************************/
void IRQ_0() interrupt 0
{
/*判断SPI是否发送完成*/
if(SPI_SR & 0x02)
{
/*标志位置1*/
spi_flag = 1;
}
}
实验结果: 逻辑分析仪抓取数据如下图所示:
2.3:CS88F003 从模式中断接收数据;
实验目的: 当从机SPI接收到5个数据后,在中断中将spi_flag标志位置1,并且在轮询系统中通过uart1将收到的5个字节发送出去。此实验需要注意主机端和从机端SPI模式(CPOL和CPHA)要配置一致!
实验代码:
/*******************************************main.c***********************************************/
/*uart1配置函数*/
void uart1_send_init(void)
{
PT1_MUX0 |= 0x40;
UART1_CLKS = 0x00;
/*设置波特率115200*/
UART1_CLKDIVH = 0x00;
UART1_CLKDIVL = 0x45;
/*数据格式为10位帧格式*/
UART1_CTRL &= ~(0x06);
/*使能uart1*/
UART1_CTRL |= 0x01;
}
void main(void)
{
Uint_8U i = 0;
Sys_Clk_Set_IRCH(SYS_CLK_8M); //芯片默认校准为16M,分频后系统主频8M
SysClk_Delay(50);
WDT_OFF();
/*1:第一步开启引脚服用功能,例如PT15-CS,PT10-SCK,PT00-MOSI,PT01-MISO*/
PT0_MUX1 |= (0x02|0x01);//0x01;
PT1_MUX1 |= (0x20|0x01);//0x01;
/*2:第二步打开SPI时钟*/
SPI_SPE |= 0x02;
/*3:第三步SPI控制寄存器复位*/
SPI_CR0 = 0x00;
SPI_CR1 = 0x00;
/*4:第四步设置SPI的通信速率,例如设置成1M*/
SPI_CR0 |= (0x01<<5);
/*5:第五步设置SPI为从机模式*/
SPI_CR0 &= ~(0x04);
/*6:第六步设置SPI的工作模式,例如CPOL=0,CPHA=0*/
SPI_CR0 &= ~(0x03);
/*7:第七步选择SPI的中断输出口,例如选择INT0*/
IRQ_SEL0 |= 0x40;
/*8:第八步开启SPI接收中断*/
SPI_CR1 |= 0x01;
/*9:第九步使能全局中断和INT0*/
IE |= 0x81;
/*10:第十步使能SPI*/
SPI_SPE |= 0x01;
//具体操作请查看interrupt.c......
/*UART模块配置*/
uart1_send_init();
while(1)
{
/*等待SPI接收完成*/
if(spi_flag)
{
/*标志位置0,串口准备发送spi接收到的数据数据*/
spi_flag = 0;
for(i = 0; i<5; i++)
{
/*将要发送的数据存入发送数据缓存区*/
UART1_TXD = receive_data[i];
/*等待数据发送完成*/
while(UART1_STATE & 0x40);//注意,这里只有检测TBSY1位才能确保数据发送完成,千万不要尝试TC1位!
/*清除发送完成标志位*/
UART1_IRQCLR |= 0x40;//官方demo写了,所以才加,实际测试不加也没事。
}
}
}
}
/*******************************************interrupt.c***********************************************/
void IRQ_0() interrupt 0
{
/*判断SPI是否接收完成*/
if(SPI_SR & 0x01)
{
/*读取寄存器数据,同时也清标志位*/
receive_data[rece_cnt++] = SPI_DR;
/*当收到五个字节时*/
if(rece_cnt == 5)
{
/*复位相应的标志位*/
spi_flag = 1;
rece_cnt = 0;
}
}
}
实验结果: 逻辑分析仪抓取数据如下图所示:
从逻辑分析仪抓取的数据来看,串口发出的数据集和SPI收到的数据一致,证明从机接收实验成功!
三:CS88F003 SPI模块使用注意事项;
①:如果是三线模式发送(cs,sck,mosi),在实际测试时你可能会发现miso没开启复用功能也会有波形,这时候最好的方法就是把miso引脚设置为输出模式默认低电平。
②:有些时候主机甚至都不用三线模式,只用双线(sck,mosi),这个其实也只需要把NSS引脚复用给关了就行了。
③:如果用死等的方式,检测数据是否发完成用BSY和TXE标志都可以。
④:这个龟卵东西定义变量要特别注意,反正如果要在函数内部定义数组,最好用xdata修饰给弄到XRAM中去,不然万一出了问题(开了中断就有可能出问题😂)那真的是找到你心碎😥!
⑤:其实官方例程是在中断里发送数据的,因为给SPI_DR寄存器写数据就可以清中断标志,但是我个人不太习惯,所以此例程相当于没清中断标志,但是单芯片单功能实测没问题。
*本例程仅为个人使用记录,具体问题还请参考原厂文档和demo!