目录
- 硬件知识
- 库函数
- spi.c
- spi.h
- 测试
- main.c
- 实验现象
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_Modeif(SPIx->SPI_Module == ENABLE) SPCTL |= (1<<6); //SPI enableelse SPCTL &= ~(1<<6); //SPI disableif(SPIx->SPI_FirstBit == SPI_LSB) SPCTL |= ~(1<<5); //LSB firstelse SPCTL &= ~(1<<5); //MSB firstif(SPIx->SPI_Mode == SPI_Mode_Slave) SPCTL &= ~(1<<4); //slaveelse SPCTL |= (1<<4); //masterif(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 edgeelse SPCTL &= ~(1<<2); //sample at the first edgeif(SPIx->SPI_Interrupt == ENABLE) IE2 |= (1<<1);else IE2 &= ~(1<<1);SPCTL = (SPCTL & ~3) | (SPIx->SPI_Speed & 3); //set speedAUXR1 = (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); //slaveelse 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); //slaveSPI_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,DISABLEu8 SPI_SSIG; //ENABLE, DISABLEu8 SPI_FirstBit; //SPI_MSB, SPI_LSBu8 SPI_Mode; //SPI_Mode_Master, SPI_Mode_Slaveu8 SPI_CPOL; //SPI_CPOL_High, SPI_CPOL_Lowu8 SPI_CPHA; //SPI_CPHA_1Edge, SPI_CPHA_2Edgeu8 SPI_Interrupt; //ENABLE,DISABLEu8 SPI_Speed; //SPI_Speed_4, SPI_Speed_16,SPI_Speed_64,SPI_Speed_128u8 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_PPGPIO_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_PPGPIO_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, DISABLESPI_InitStructure.SPI_SSIG = DISABLE; //片选位 ENABLE, DISABLESPI_InitStructure.SPI_FirstBit = SPI_MSB; //移位方向 SPI_MSB, SPI_LSBSPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主从选择 SPI_Mode_Master, SPI_Mode_SlaveSPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟相位 SPI_CPOL_High, SPI_CPOL_LowSPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //数据边沿 SPI_CPHA_1Edge, SPI_CPHA_2EdgeSPI_InitStructure.SPI_Interrupt = ENABLE; //中断允许 ENABLE,DISABLESPI_InitStructure.SPI_Speed = SPI_Speed_128; //SPI速度 SPI_Speed_4, SPI_Speed_16, SPI_Speed_64, SPI_Speed_128SPI_InitStructure.SPI_IoUse = SPI_P24_P23_P22_P21; //IO口切换 SPI_P12_P13_P14_P15, SPI_P24_P23_P22_P21, SPI_P54_P40_P41_P43SPI_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函数
/**
* @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();elseOLED_DC_L();SPI_TxRead = 0;SPI_TxWrite = 0;SPI_WriteToTxBuf(dat);SPI_TrigTx();while(B_SPI_TxBusy);OLED_DC_H();OLED_CS_H();
}