SPI是Sysnchronous Serial Peripheral Interface 的缩写。SPI是一种全双工、高速、同步的通信总线,有两种操作模式:主模式和从模式。所谓全双工是指SPI主器件在向从器件发送数据的同时,也读取(或者接收从器件)的数据,从器件接收主器件数据的同时也向主器件发送数据。STC15W系列部分单片机内部集成了SPI总线接口。SPI总线在嵌入式系统中非常重要,如多片单片机间通信,SPI flash存储芯片的数据读写等。SPI总线由4条线组成,分别为:MOSI、MISO、SCLK、SS。下面分别对这4条线做下说明。
MOSI(Master Out Slave In)主器件输出从器件输入,用于主器件到从器件的串行数据传输。当SPI器件做主器件时是信号输出线,当SPI器件做从器件时是信号输入线。在时钟的前半周期,主机将数据放在MOSI信号线上,从机在该边界处获取数据。
MISO(Master In Slave Out):主器件的输入,从器件的输出,用于实现从器件到主器件的数据传输。当SPI器件做主器件时是信号输入线,当SPI器件做从器件时是信号输出入线。SPI规范中一个主机可以连接多个从机,主机的MISO线会连接到多个从机上,当主机与其中一个从机通信时,因将其他从机MISO脚驱动置为高阻状态(将SS脚拉高)。
SCLK(SPI Clock):串行时钟信号,用于同步主器件和从器件之间在MOSI及MISO线上的数据传输。当主器件启动一次数据传输时,自动产生8个SCLK时钟周期信号给从机。在SCLK的每个跳变处(上升沿或下降沿)移处一位数据。因此,一次数据传输可以传输一个字节的数据。
SS(Slave Select):从机选择信号(线),可以说是片选(CS)信号,从机选择信号是一个输入信号,低电平有效,故STC15单片机说明书中,在SS上加了一横线。当器件的SS脚未必设置成忽略时,当SPI器件的SS脚被拉低时,该机就被设置成从器件。
STC15系列单片机SPI的功能框图如下:
STC15系列单片机的SPI相关特殊功能寄存器如下:
有了对SPI寄存器的了解,就可以来编写SPI的库函数。建两个文件分别以stcspior.h, stcspi.c存入C51 Template文件夹下Library文件夹中。完成后的头文件如下:
/*stcspi.h
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 12/26/2021
*/
#ifndef __STCSPI_H__
#define __STCSPI_H__
#include "mtype.h"
#include "stc15w4k.h"
#include "stcint.h"
#include "delay.h"
extern FSYSCLOCK FSCLK; //FSCLK was defined in mian.h, FSYSCLOCK was defined in mtype.h
//***********************************************************
typedef enum
{
SPI_PG1 = 0, //P1.2/SS,P1.3/MOSI,/P1.4/MISO,P1.5SCLK
SPI_PG2, //P2.4/SS_2,P2.3/MOSI_2,P2.2/MISO_2,P2.1/SCLK_2
SPI_PG3 //5.4/SS_3,P4.0/MOSI_3,P4.1/MISO_3,P4.3/SCLK_3
}SPI_PINGROUP;
//***********************************************************
typedef enum
{
SPI_FCLK0 = 0, //cpu fclk/4
SPI_FCLK1, //cpu fclk/8
SPI_FCLK2, //cpu fclk/16
SPI_FCLK3, //cpu fclk/32
}SPI_FCLK; //frequency of SPI clk
/***********************************************************
Function: SPI_PinSel(SPI_PINGROUP mSel);
Return value: void
Discription: configure SPI pins
Example:
SPI_PinSel(SPI_PG1); //P1.2/SS,P1.3/MOSI,/P1.4/MISO,P1.5SCLK
***********************************************************************/
void SPI_PinSel(SPI_PINGROUP mSel);
/***********************************************************
Function: SPI_ClkSel(SPI_FCLK mSel);
Return value: void
Discription: configure SPI clock
Example:
SPI_PinSel(SPI_FCLK0); //cpu fclk/4
***********************************************************************/
void SPI_ClkSel(SPI_FCLK mSel);
/***********************************************************
Function: SPI_SSPin(BOOL mEnable);
Return value: void
Discription: configure SPI SS pin enable/disable
Example:
SPI_SSPin(0); //SPI SS pin is ignored
***********************************************************************/
void SPI_SSPin(BOOL mEnable);
/***********************************************************
Function: SPI_Enable(BOOL mEnable);
Return value: void
Discription: configure SPI enable/disable
Example:
SPI_Enable(1); //SPI enable
SPI_Enable(0); //SPI disable
***********************************************************************/
void SPI_Enable(BOOL mEnable);
/***********************************************************
Function: SPI_DataOrder(BOOL fLSB);
Return value: void
Discription: configure transmit data order for SPI transmitting
Example:
SPI_DataOrder(1); //begin from LSB
***********************************************************************/
void SPI_DataOrder(BOOL fLSB);
/***********************************************************
Function: SPI_MasterEnable(BOOL bMaster);
Return value: void
Discription: configure SPI device master/slave
Example:
SPI_MasterEnable(1); //set this device as master
***********************************************************************/
void SPI_MasterEnable(BOOL bMaster);
/***********************************************************
Function: SPI_IdleClkPol(BOOL bHigh);
Return value: void
Discription: configure SPI idle clk voltage level
Example:
SPI_IdleClkPol(1); //SPI idle clk voltage level: high
***********************************************************************/
void SPI_IdleClkPol(BOOL bHigh);
/***********************************************************
Function: SPI_ClkPhase(BOOL bSOT); // SOT:sampling on the trailing edge
Return value: void
Discription: configure SPI clk phase, sampling on the trailing edge/sampling on the leading edge
Example:
SPI_ClkPhase(1); //sampling on the trailing edge
***********************************************************************/
void SPI_ClkPhase(BOOL bSOT);
/***********************************************************
Function: SPI_Init(BOOL mSSable,SPI_PINGROUP mSel, SPI_FCLK mSelClk, BOOL bMaster);
Return value: void
Discription: configure SPI device
Example:
SPI_Init(1, SPI_PG1, SPI_FCLK0, 1);//SS pin enable, SPI pin select group 0, SPI clock, mcu clock/4, master device
***********************************************************************/
void SPI_Init(BOOL mSSable,SPI_PINGROUP mSel, SPI_FCLK mSelClk, BOOL bMaster);
/***********************************************************
Function: SPI_SendChar(i8 mdata);
Return value: void
Discription: master SPI device send char
Example:
SPI_SendChar(123);
***********************************************************************/
void SPI_SendChar(i8 mdata);
/***********************************************************
Function: SPI_SendStringAndEof(i8* str);
Return value: void
Discription: master SPI device transmit string and end of flag
Example:
SPI_SendStringAndEof("Hello world!");
***********************************************************************/
void SPI_SendStringAndEof(i8* str);
/***********************************************************
Function: SPI_RecChar(i8* mdata);
Return value: char
Discription: SPI slave device receive char
Example:
char tem;
SPI_RecChar(&tem);
***********************************************************************/
char SPI_RecChar(i8* mdata);
/***********************************************************
Function: SPI_SendAndRecChar(i8 mdata);
Return value: void
Discription: master SPI device transmit char and receive char
Example:
char tem = SPI_SendAndRecChar(i8 mdata);
***********************************************************************/
char SPI_SendAndRecChar(i8 mdata);
/***********************************************************
Function: SPI_RecStringTillEof(i8* str);
Return value: void
Discription: SPI slave device receive string till end of flag
Example:
char str[20];
SPI_RecStringTillEof(str);
***********************************************************************/
void SPI_RecStringTillEof(i8* str);
#endif
在写源代码时需要特别注意的是要清除SPSTAT中的SPIF及WCOL需写入1,有点类似STM32部分寄存器的操作,前面在写中断库函数时,就没注意到这点,现已修改过来。完成后的源代码如下:
/*stcspi.c
Designed by Bill Liu
Version 0.0
Modified last by Bill Liu on 12/26/2021
*/
#include "stcspi.h"
//***********************************************************
void SPI_PinSel(SPI_PINGROUP mSel)
{
ui8 tem = P_SW1;
SetBits(&tem,3,2,mSel);
P_SW1 = tem;
}
//End of SPI_PinSel(SPI_PINGROUP mSel)
//***********************************************************
void SPI_ClkSel(SPI_FCLK mSel)
{
ui8 tem = SPCTL;
SetBits(&tem,1,0,mSel);
SPCTL = tem;
}
//End of SPI_ClkSel(SPI_FCLK mSel)
//***********************************************************
void SPI_SSPin(BOOL mEnable)
{
if(mEnable)
SPCTL &= 0x7F;
else
SPCTL |= 0x80;
}
//End of SPI_SSPin(BOOL mEnable)
//***********************************************************
void SPI_Enable(BOOL mEnable)
{
if(mEnable)
SPCTL |= 0x40;
else
SPCTL &= 0xBF;
}
//End of SPI_Enable(BOOL mEnable)
//***********************************************************
void SPI_DataOrder(BOOL fLSB)
{
if(fLSB)
SPCTL |= 0x20;
else
SPCTL &= 0xDF;
}
//End of SPI_DataOrder(BOOL fMSB)
//***********************************************************
void SPI_MasterEnable(BOOL bMaster)
{
if(bMaster)
SPCTL |= 0x10;
else
SPCTL &= 0xEF;
}
//End of SPI_MasterEnable(BOOL bMaster)
//***********************************************************
void SPI_IdleClkPol(BOOL bHigh)
{
if(bHigh)
SPCTL |= 0x08;
else
SPCTL &= 0xF7;
}
//End of SPI_IdleClkPol(BOOL bHigh)
//***********************************************************
void SPI_ClkPhase(BOOL bSOT)
{
if(bSOT) //sampling on the trailing edge
SPCTL |= 0x04;
else
SPCTL &= 0xFB;
}
//End of SPI_ClkPhase(BOOL bSOT)
//***********************************************************
void SPI_Init(BOOL mSSable,SPI_PINGROUP mSel, SPI_FCLK mSelClk, BOOL bMaster)
{
SPI_SSPin(mSSable);
SPI_PinSel(mSel);
SPI_ClkSel(mSelClk);
SPI_MasterEnable(bMaster);
}
//End of SPI_Init(BOOL mSSable,SPI_PINGROUP mSel, SPI_FCLK mSelClk, BOOL bMaster)
//***********************************************************
void SPI_SendChar(i8 mdata)
{
SPI_Enable(1);
STC_ClearSPIFlag();//clear flag
SPDAT = mdata;
while(!(SPSTAT & 0x80));
SPSTAT = 0xFF; //clear flag
}
//End of SPI_SendChar(i8 mdata)
//***********************************************************
void SPI_SendStringAndEof(i8* str)
{ ui8 i = 0;
while(str[i] != 0)
{
SPI_SendChar(str[i]);
Delayxus(2,FSCLK);
i++;
}
SPI_SendChar(0xFF);
}
//End of SPI_SendStringAndEof(i8* str)
//***********************************************************
char SPI_RecChar(i8* mdata)
{
SPI_Enable(1);
STC_ClearSPIFlag();//clear flag
while(!(SPSTAT & 0x80));
STC_ClearSPIFlag();//clear flag
*mdata = SPDAT;
return *mdata;
}
//End of SPI_RecChar(i8* mdata)
//***********************************************************
char SPI_SendAndRecChar(i8 mdata)
{
i8 tem;
SPI_Enable(1);
STC_ClearSPIFlag();//clear flag
SPDAT = mdata;
while(!(SPSTAT & 0x80));
STC_ClearSPIFlag();//clear flag
tem = SPDAT;
return tem;
}
//End of SPI_SendAndRecChar(i8 mdata)
//***********************************************************
void SPI_RecStringTillEof(i8* str)
{
ui8 i = 0;
memset(str, 0, strlen(str));
SPI_Enable(1);
STC_ClearSPIFlag();//clear flag
while(str[i] != 0xFF)
{
STC_ClearSPIFlag();//clear flag
while(!(SPSTAT & 0x80));
STC_ClearSPIFlag();//clear flag
str[i] = SPDAT;
i++;
}
str[i] = 0;
}
//SPI_RecStringTillEof(i8* str)
此处不做验证示例,以后在项目实战中再做说明。本文代码及相关库文件已打包上传到CSDN,如需离线查看,可去下载,文件名为STC15 SPI Library Source Code.rar。