STC实验箱4
IAP15W4K58S4
Keil uVision V5.29.0.0
PK51 Prof.Developers Kit Version:9.60.0.0


硬件知识

       摘自《STC15系列单片机器件手册
       STC15系列单片机还提供另一种高速串行通信接口——SPI接口。SPI是一种全双工、高速、同步的通信总线,有两种操作模式:主模式和从模式。在主模式中支持高达3Mbps的速率(工作频率为12MHz时,如果CPU主频采用20MHz到36MHz,则可更高,从模式时速度无法太快,SYSclk/4以内较好),还具有传输完成标志和写冲突标志保护。
       下表总结了STC15系列单片机内部集成了SPI功能的单片机型号:
在这里插入图片描述
       上表中√表示对应的系列有相应的功能。
在这里插入图片描述
       STC15W系列与STC15F/L系列具有不同的SPI时钟频率,其中,STC15W系列单片机的SPI时钟频率选择如下表所列:

在这里插入图片描述
       表中,CPU_CLK是CPU时钟。
       STC15F/L系列单片机的SPI时钟频率选择如下表所列:
在这里插入图片描述
       表中,CPU_CLK是CPU时钟。

库函数

SPI的库函数仅在官方例程中发现,未与其他库函数放在一起,请谨慎使用。

spi.c

/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU RC Demo -----------------------------------*/
/* --- Mobile: (86)13922805190 -------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.GXWMCU.com -----------------------------------------*/
/* --- QQ:  800003751 ----------------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/


#include	"spi.h"

u8	SPI_TxRxMode;	//
u8	SPI_TxWrite;
u8	SPI_TxRead;
u8	SPI_RxCnt;
u8 	SPI_RxTimerOut;
u8 	SPI_BUF_type SPI_RxBuffer[SPI_BUF_LENTH];
u8 	SPI_BUF_type SPI_TxBuffer[SPI_BUF_LENTH];
bit 	B_SPI_TxBusy = 0;
bit 	B_SPI_RxOk;


//========================================================================
// 函数: void	SPI_Init(SPI_InitTypeDef *SPIx)
// 描述: SPI初始化程序.
// 参数: SPIx: 结构参数,请参考spi.h里的定义.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	SPI_Init(SPI_InitTypeDef *SPIx)
{
	if(SPIx->SPI_SSIG == ENABLE)			SPCTL &= ~(1<<7);	//enable SS, conform Master or Slave by SS pin.
	else									SPCTL |=  (1<<7);	//disable SS, conform Master or Slave by SPI_Mode
	if(SPIx->SPI_Module == ENABLE)			SPCTL |=  (1<<6);	//SPI enable
	else									SPCTL &= ~(1<<6);	//SPI disable
	if(SPIx->SPI_FirstBit == SPI_LSB)		SPCTL |= ~(1<<5);	//LSB first
	else									SPCTL &= ~(1<<5);	//MSB first
	if(SPIx->SPI_Mode == SPI_Mode_Slave)	SPCTL &= ~(1<<4);	//slave
	else									SPCTL |=  (1<<4);	//master
	if(SPIx->SPI_CPOL == SPI_CPOL_High)		SPCTL |=  (1<<3);	//SCLK Idle High, Low Active.
	else									SPCTL &= ~(1<<3);	//SCLK Idle Low, High Active.
	if(SPIx->SPI_CPHA == SPI_CPHA_2Edge)	SPCTL |=  (1<<2);	//sample at the second edge
	else									SPCTL &= ~(1<<2);	//sample at the first  edge
	if(SPIx->SPI_Interrupt == ENABLE)		IE2 |=  (1<<1);
	else									IE2 &= ~(1<<1);
	SPCTL = (SPCTL & ~3) | (SPIx->SPI_Speed & 3);					//set speed
	AUXR1 = (AUXR1 & ~(3<<2)) | SPIx->SPI_IoUse;	
}

//========================================================================
// 函数: void	SPI_SetMode(u8 mode)
// 描述: SPI设置主从模式函数.
// 参数: mode: 指定模式, 取值 SPI_Mode_Master 或 SPI_Mode_Slave.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	SPI_SetMode(u8 mode)
{
	SPI_TxRxMode = mode;
	if(mode == SPI_Mode_Slave)	SPCTL &= ~(1<<4);	//slave
	else						SPCTL |=  (1<<4);	//master
}


//========================================================================
// 函数: void SPI_WriteToTxBuf(u8 dat)
// 描述: SPI装载发送缓冲函数.
// 参数: dat: 要发送的值.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	SPI_WriteToTxBuf(u8 dat)	//写入发送缓冲,指针+1
{
	SPI_TxBuffer[SPI_TxWrite] = dat;
	if(++SPI_TxWrite >= SPI_BUF_LENTH)	SPI_TxWrite = 0;
}

//========================================================================
// 函数: void	SPI_TrigTx(void)
// 描述: SPI触发发送函数, 将SPI模式设置为发送模式并将发送缓冲的数据发出.
// 参数: dat: 要发送的值.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void	SPI_TrigTx(void)
{
	u8	i;
	if(SPI_TxRead == SPI_TxWrite)	
	{
		B_SPI_TxBusy = 0;
		return;
	}
	B_SPI_TxBusy = 1;
	SPI_SetMode(SPI_Mode_Master);
	SPI_SS = 0;
	i = SPI_TxBuffer[SPI_TxRead];
	if(++SPI_TxRead >= SPI_BUF_LENTH)	SPI_TxRead = 0;
	SPDAT = i;
}


//========================================================================
// 函数: void SPI_Transivion (void) interrupt SPI_VECTOR
// 描述: SPI中断函数.
// 参数: none.
// 返回: none.
// 版本: V1.0, 2012-11-22
//========================================================================
void SPI_Transivion (void) interrupt SPI_VECTOR
{
	if(SPI_TxRxMode == SPI_Mode_Slave)
	{
	//	if(!B_SPI_RxOk)
		{
			if(SPI_RxCnt >= SPI_BUF_LENTH)		SPI_RxCnt = 0;
			SPI_RxBuffer[SPI_RxCnt++] = SPDAT;
			SPI_RxTimerOut = 5;
		}
	}

	if(SPI_TxRxMode == SPI_Mode_Master)
	{
		if(SPI_TxRead != SPI_TxWrite)
		{
			SPDAT = SPI_TxBuffer[SPI_TxRead];
			if(++SPI_TxRead >= SPI_BUF_LENTH)		SPI_TxRead = 0;
		}
		else
		{
			SPI_TxRxMode = SPI_Mode_Slave;
			SPCTL &= ~(1<<4);	//slave
			SPI_SS = 1;
			B_SPI_TxBusy = 0;
		}
	}
	SPSTAT = SPIF + WCOL;	//清0 SPIF和WCOL标志
}


spi.h

/*------------------------------------------------------------------*/
/* --- STC MCU International Limited -------------------------------*/
/* --- STC 1T Series MCU RC Demo -----------------------------------*/
/* --- Mobile: (86)13922805190 -------------------------------------*/
/* --- Fax: 86-0513-55012956,55012947,55012969 ---------------------*/
/* --- Tel: 86-0513-55012928,55012929,55012966 ---------------------*/
/* --- Web: www.GXWMCU.com -----------------------------------------*/
/* --- QQ:  800003751 ----------------------------------------------*/
/* If you want to use the program or the program referenced in the  */
/* article, please specify in which data and procedures from STC    */
/*------------------------------------------------------------------*/


#ifndef	__SPI_H
#define	__SPI_H

#include	"config.h"

#define	SPI_BUF_LENTH	32
#define	SPI_BUF_type	idata


#define	SPI_Mode_Master		1
#define	SPI_Mode_Slave		0
#define	SPI_CPOL_High		1
#define	SPI_CPOL_Low		0
#define	SPI_CPHA_1Edge		1
#define	SPI_CPHA_2Edge		0
#define	SPI_Speed_4			0
#define	SPI_Speed_16		1
#define	SPI_Speed_64		2
#define	SPI_Speed_128		3
#define	SPI_MSB				0
#define	SPI_LSB				1
#define	SPI_P12_P13_P14_P15	(0<<2)
#define	SPI_P24_P23_P22_P21	(1<<2)
#define	SPI_P54_P40_P41_P43	(2<<2)

typedef struct
{
	u8	SPI_Module;		//ENABLE,DISABLE
	u8	SPI_SSIG;		//ENABLE, DISABLE
	u8	SPI_FirstBit;	//SPI_MSB, SPI_LSB
	u8	SPI_Mode;		//SPI_Mode_Master, SPI_Mode_Slave
	u8	SPI_CPOL;		//SPI_CPOL_High,   SPI_CPOL_Low
	u8	SPI_CPHA;		//SPI_CPHA_1Edge,  SPI_CPHA_2Edge
	u8	SPI_Interrupt;		//ENABLE,DISABLE
	u8	SPI_Speed;		//SPI_Speed_4,      SPI_Speed_16,SPI_Speed_64,SPI_Speed_128
	u8	SPI_IoUse;		//SPI_P12_P13_P14_P15, SPI_P24_P23_P22_P21, SPI_P54_P40_P41_P43
} SPI_InitTypeDef;



extern	u8	SPI_TxRxMode;
extern	u8	SPI_TxWrite;
extern	u8	SPI_TxRead;
extern	u8	SPI_RxCnt;
extern	u8 	SPI_RxTimerOut;
extern	u8 	SPI_BUF_type SPI_RxBuffer[SPI_BUF_LENTH];
extern	u8 	SPI_BUF_type SPI_TxBuffer[SPI_BUF_LENTH];
extern	u8  *SPI_pTxBuffer;
extern	bit 	B_SPI_RxOk;
extern	bit 	B_SPI_TxBusy;


void	SPI_Init(SPI_InitTypeDef *SPIx);
void	SPI_SetMode(u8 mode);
void	SPI_WriteToTxBuf(u8 dat);
void	SPI_TrigTx(void);

#endif

测试

OLED驱动程序见SPI驱动0.96/1.3寸 OLED屏幕,易修改为DMA控制

main.c

#include "./Drivers/config.h"
#include "./Drivers/delay.h"

#include "./Drivers/GPIO.h"
#include "./Drivers/spi.h"
#include "./SPI_OLED/oled.h"

void GPIO_config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;					//结构定义
	GPIO_InitStructure.Mode = GPIO_OUT_PP;					//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_InitStructure.Pin  = GPIO_Pin_1 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;//指定要初始化的IO, 或操作
	GPIO_Inilize(GPIO_P2,&GPIO_InitStructure);				//初始化为推挽输出

	GPIO_InitStructure.Mode = GPIO_OUT_OD;					//指定IO的输入或输出方式,GPIO_PullUp,GPIO_HighZ,GPIO_OUT_OD,GPIO_OUT_PP
	GPIO_InitStructure.Pin  = GPIO_Pin_2;					//指定要初始化的IO, 或操作
	GPIO_Inilize(GPIO_P2,&GPIO_InitStructure);				
}

void	SPI_config(void)
{
	SPI_InitTypeDef		SPI_InitStructure;
	SPI_InitStructure.SPI_Module    = ENABLE;              //SPI启动    ENABLE, DISABLE
	SPI_InitStructure.SPI_SSIG      = DISABLE;			  //片选位     ENABLE, DISABLE
	SPI_InitStructure.SPI_FirstBit  = SPI_MSB;			  //移位方向   SPI_MSB, SPI_LSB
	SPI_InitStructure.SPI_Mode      = SPI_Mode_Master;	  //主从选择   SPI_Mode_Master, SPI_Mode_Slave
	SPI_InitStructure.SPI_CPOL      = SPI_CPOL_High;      //时钟相位   SPI_CPOL_High,   SPI_CPOL_Low
	SPI_InitStructure.SPI_CPHA      = SPI_CPHA_2Edge;	  //数据边沿   SPI_CPHA_1Edge,  SPI_CPHA_2Edge
	SPI_InitStructure.SPI_Interrupt = ENABLE;			  //中断允许   ENABLE,DISABLE
	SPI_InitStructure.SPI_Speed     = SPI_Speed_128;		  //SPI速度    SPI_Speed_4, SPI_Speed_16, SPI_Speed_64, SPI_Speed_128
	SPI_InitStructure.SPI_IoUse     = SPI_P24_P23_P22_P21; //IO口切换   SPI_P12_P13_P14_P15, SPI_P24_P23_P22_P21, SPI_P54_P40_P41_P43
	SPI_Init(&SPI_InitStructure);
	
	SPI_TxRxMode = SPI_Mode_Master;
}

void main(void)
{
	uint8_t i;
	
	GPIO_config();
	SPI_config();
	EA = 1;

	OLED_Init();
	OLED_Clear();
	OLED_Display_On();	     
	OLED_ShowString(0, 0, "STC15W4K58  OLED", 16, 0);
	OLED_ShowString(0, 2, "   2022-01-10", 6, 0);
	OLED_ShowString(0, 3, "    SPI Test", 6, 0);
	for(i = 0; i < 7; ++i)
		OLED_ShowChinese(8 + 16 * i, 6, i, 1);

	OLED_Refresh_Gram();

	while(1)
	{

	}
}

修改OLED_WR_Byte函数
在这里插入图片描述

  1. /**
    * @brief 向SSD1306写入一个字节
    * @param dat:要写入的数据/命令 cmd:数据/命令标志 0,表示命令;1,表示数据;
    * @retval None
    */
    void OLED_WR_Byte(uint8_t dat, uint8_t cmd)
    {
        OLED_CS_L();
        if (cmd)
            OLED_DC_H();
        else
            OLED_DC_L();
    
    	SPI_TxRead = 0;
    	SPI_TxWrite = 0;
    	SPI_WriteToTxBuf(dat);
    	SPI_TrigTx();
    	while(B_SPI_TxBusy);
    
        OLED_DC_H();
        OLED_CS_H();
    }
    

实验现象

在这里插入图片描述