51单片机实战教程(21 同步串行外围通信接口SPI)

        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的功能框图如下:

51单片机实战教程(21 同步串行外围通信接口SPI)

    STC15系列单片机的SPI相关特殊功能寄存器如下:

51单片机实战教程(21 同步串行外围通信接口SPI)

51单片机实战教程(21 同步串行外围通信接口SPI)

51单片机实战教程(21 同步串行外围通信接口SPI)

51单片机实战教程(21 同步串行外围通信接口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。

上一篇:leetcode392.判断子序列


下一篇:Qt实战总结(一)QTextEdit