STC15系列单片机SPI使用教程(一)

2020/10/05 10:23
阅读数 144

硬件SPI(查询方式)

以STC15W408AS单片机为例

一、硬件接线

1、普通SPI设备接线

如NRF24L01,可以直接连接IO
普通SPI设备接线



2、FLASH设备接线

如GD25Q80BSIG,需要加上拉电阻
FLASH设备接线

二、程序编写

1、和SPI相关的寄存器


① SPCTL寄存器

SPCTL寄存器
位描述
在这里插入图片描述




② SPSTAT寄存器

SPSTAT寄存器
在这里插入图片描述



③ SPDAT寄存器

SPDAT寄存器


④ AUXR1/P_SW1寄存器

AUXR1/P_SW1寄存器


2、自定义寄存器,数据类型重定义

sfr P_SW1  = 0xA2;		//外设功能切换寄存器1
sfr SPSTAT = 0xCD;		//SPI状态寄存器
sfr SPCTL  = 0xCE;		//SPI控制寄存器
sfr SPDAT  = 0xCF;		//SPI数据寄存器

#ifndef uchar
	#define uchar unsigned char
#endif
#ifndef uint
	#define uint  unsigned int
#endif

3、寄存器相关位宏定义, CS引脚定义

#define SPI_S0	0x04
#define SPI_S1	0x08

#define SPIF	0x80	//SPSTAT.7
#define WCOL	0x40	//SPSTAT.6

#define SSIG	0x80	//SPCTL.7
#define SPEN	0x40	//SPCTL.6
#define DORD	0x20	//SPCTL.5
#define MSTR	0x10	//SPCTL.4
#define CPOL	0x08	//SPCTL.3
#define CPHA	0x04	//SPCTL.2
#define SPDHH	0x00	//CPU_CLK/4
#define SPDH	0x01	//CPU_CLK/16
#define SPDL	0x02	//CPU_CLK/64
#define SPDLL	0x03	//CPU_CLK/128

sbit SS_1 = P1^2;	//SPI_1的CS脚
sbit SS_2 = P2^4;	//SPI_2的CS脚

4、SPI初始化代码

void InitSPI_1(void)
{
   
    
	uchar temp;
	temp = P_SW1;                                //切换到第一组SPI
	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=0 SPI_S1=0
	P_SW1 = temp;                                //(P1.2/SS, P1.3/MOSI, P1.4/MISO, P1.5/SCLK)

//	temp = P_SW1;                                //切换到第二组SPI
//	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=1 SPI_S1=0
//	temp |= SPI_S0;                              //(P2.4/SS_2, P2.3/MOSI_2, P2.2/MISO_2, P2.1/SCLK_2)
//	P_SW1 = temp;  

//	temp = P_SW1;                                //切换到第三组SPI
//	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=0 SPI_S1=1
//	temp |= SPI_S1;                              //(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
//	P_SW1 = temp;  
	
	
    SPDAT = 0;                  //初始化SPI数据
    SPSTAT = SPIF | WCOL;       //清除SPI状态位
    SPCTL = SPEN | MSTR | SSIG | SPDLL;        //主机模式
}

void InitSPI_2(void)
{
   
    
	uchar temp;
//	temp = P_SW1;                                //切换到第一组SPI
//	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=0 SPI_S1=0
//	P_SW1 = temp;                                //(P1.2/SS, P1.3/MOSI, P1.4/MISO, P1.5/SCLK)

	temp = P_SW1;                                //切换到第二组SPI
	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=1 SPI_S1=0
	temp |= SPI_S0;                              //(P2.4/SS_2, P2.3/MOSI_2, P2.2/MISO_2, P2.1/SCLK_2)
	P_SW1 = temp;  

//	temp = P_SW1;                                //切换到第三组SPI
//	temp &= ~(SPI_S0 | SPI_S1);                  //SPI_S0=0 SPI_S1=1
//	temp |= SPI_S1;                              //(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
//	P_SW1 = temp;  
	
	
    SPDAT = 0;                  //初始化SPI数据
    SPSTAT = SPIF | WCOL;       //清除SPI状态位
    SPCTL = SPEN | MSTR | SSIG | SPDLL;        //主机模式
}

5、SPI数据交换代码

uchar SPISwap(uchar dat) 
{
   
    
    SPDAT = dat;                //触发SPI发送数据
    while (!(SPSTAT & SPIF));   //等待发送完成
    SPSTAT = SPIF | WCOL;       //清除SPI状态位
    return SPDAT;               //返回SPI数据
}

6、NRF24L01读写例程

//NRF24L01相关宏定义
#define NOP			0xFF	//空操作
#define READ_REG    0x00 
#define WRITE_REG   0x20
#define TX_ADDR     0x10

sbit CE  = P2^5;
sbit IRQ = P3^2;	//INT0

//SPI写寄存器
//reg:指定寄存器地址
//value:写入的值
uchar SPI_RW_Reg(uchar reg, uchar value)
{
   
    
	uchar status;
	SS_2 = 0;              // 使能SPI传输
	status = SPISwap(reg); //返回从MISO读出的数据,status应为上次向该寄存器内写的value
	SPISwap(value);        //写入寄存器的值
	SS_2 = 1; 		// 禁止SPI传输
	return status;       // 返回状态值
}
//读取SPI寄存器值
//reg:要读的寄存器
uchar SPI_Read(uchar reg)
{
   
    
	uchar reg_val;
	SS_2 = 0; 		     // 使能SPI传输
	SPISwap(reg);         // 发送寄存器号
	reg_val = SPISwap(NOP); // 读取寄存器内容
	SS_2 = 1;             // 禁止SPI传输
	return reg_val;     // 返回状态值
}
//在指定位置写指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//bytes:数据长度
//返回值,此次读到的状态寄存器值
uchar SPI_Write_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
   
    
	uchar status, byte_ctr;
	SS_2 = 0; // 使能SPI传输
	status = SPISwap(reg);// 发送寄存器值(位置),并读取状态值
	for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++){
   
     // 写入数据	 
		SPISwap(*pBuf++);
	}
	SS_2 = 1;//关闭SPI传输
	return status; // 返回读到的状态值
}

//在指定位置读出指定长度的数据
//reg:寄存器(位置)
//*pBuf:数据指针
//bytes:数据长度
//返回值,此次读到的状态寄存器值 
uchar SPI_Read_Buf(uchar reg, uchar *pBuf, uchar bytes)
{
   
    
	uchar status, byte_ctr;
	SS_2 = 0; // 使能SPI传输
	status = SPISwap(reg); // 发送寄存器值(位置),并读取状态值   	   
	for(byte_ctr = 0; byte_ctr < bytes; byte_ctr++){
   
    
		pBuf[byte_ctr] = SPISwap(NOP); // 读出数据
	}
	SS_2 = 1; // 关闭SPI传输
	return status; // 返回读到的状态值
}

//检测24L01是否存在
//返回值:0,成功;1,失败
uchar NRF24L01_Check(void)
{
   
    
	uchar buf[5] = {
   
    0xA5, 0xA5, 0xA5, 0xA5, 0xA5};
	uchar buf1[5];
	uchar i;
	
	CE = 0;
	SPI_Write_Buf(WRITE_REG + TX_ADDR, buf, 5);
	SPI_Read_Buf(TX_ADDR, buf1, 5); //读出写入的地址
	CE = 1;	
	for(i = 0; i < 5; i++)
		if(buf1[i] != 0xA5)
			break;	 							   
	if(i != 5)
		return 1;//检测24L01错误	
	return 0;	//检测到24L01
}
//主函数
void main(void)
{
   
    
	Init_Uart();
	EA = 1;			//开总中断

	InitSPI_2();
	NRF24L01_Check();    //切换SPI后需要读多几次,等待SPI稳定
	NRF24L01_Check();
	if(!NRF24L01_Check()){
   
    
		SendString("NRF24L01 Checked OK!\r\n");
	}
	else{
   
    
		SendString("NRF24L01 Checked Fail!\r\n");
	}
	while(1);
}

7、GD25Q80BSIG读写例程

//GD25Q80BSIG相关宏定义
#define NOP							    0xFF	//空操作
#define Write_Enable					0x06	//写使能
#define Write_Disable					0x04	//写禁能
#define Read_Status_Register			0x05	//读前八位状态寄存(S7-S0)
#define Read_Status_Register_1			0x35	//读后八位状态寄存(S15-S8)
#define Read_Data						0x03	//读数据
#define Page_Program					0x02	//页面编程,256字节
#define Chip_Erase_1					0xC7	//芯片擦除命令1
#define Chip_Erase_2					0x60	//芯片擦除命令2
#define Read_Identification				0x9F	//读取标识命令允许读取8位制造商标识,然后是两个字节的设备标识。

sbit WP = P1^6;		//写保护,低电平有效

//写使能
void Write_Enable_Cmd(void)
{
   
    
	SS_1 = 0;
	SPISwap(Write_Enable);
	SS_1 = 1;
}
//写禁能
void Write_Disable_Cmd(void)
{
   
    
	SS_1 = 0;
	SPISwap(Write_Disable);
	SS_1 = 1;
}
//读状态寄存器前八位
uchar Read_Status_Register_Sta(void)
{
   
    
	uchar sta;
	SS_1 = 0;
	SPISwap(Read_Status_Register);
	sta = SPISwap(NOP);
	SS_1 = 1;
	return sta;
}
//读数据
void Read_Data_Cmd(uchar ad1, uchar ad2, uchar ad3, uchar *dat, uint len)
{
   
    
	uchar i, cmd[4];
	cmd[0] = Read_Data;
	cmd[1] = ad1;
	cmd[2] = ad2;
	cmd[3] = ad3;
	SS_1 = 0;
	for(i = 0; i < 4; i++){
   
    
		SPISwap(cmd[i]);
	}
	for(i = 0; i < len; i++){
   
    
		*dat++ = SPISwap(NOP);
	}
	SS_1 = 1;
}
//页编程,输入24位起始地址
void Page_Program_Cmd(uchar ad1, uchar ad2, uchar ad3, uchar *dat, uint len)
{
   
    
	uchar i, cmd[4];
	uint count = 0, temp = 0;
	
	cmd[0] = Page_Program;
	cmd[1] = ad1;
	cmd[2] = ad2;
	cmd[3] = ad3;
	temp = 256 - ad3;		//一次最多写256字节,超过的写进下一页
	Write_Enable_Cmd();		//写使能
	SS_1 = 0;
	for(i = 0; i < 4; i++){
   
    
		SPISwap(cmd[i]);
	}
	for(i = 0; i < temp; i++){
   
    
		SPISwap(*dat++);
	}
	SS_1 = 1;
	while(Read_Status_Register_Sta() & 0x01);	//等待写入完毕
	if(len > temp){
   
    					//需要写入的数据长度超过当前页,超过的写进下一页
		cmd[0] = Page_Program;
		cmd[1] = ad1;
		cmd[2] = ad2 + 1;			//超过的写进下一页
		cmd[3] = 0;
		temp = len - temp;
		Write_Enable_Cmd();
		SS_1 = 0;
		for(i = 0; i < 4; i++){
   
    
			SPISwap(cmd[i]);
		}
		for(i = 0; i < temp; i++){
   
    
			SPISwap(*dat++);
		}
		SS_1 = 1;
		while(Read_Status_Register_Sta() & 0x01);
	}
}
//芯片擦除
void Chip_Erase_1_Cmd(void)
{
   
    
	Write_Enable_Cmd();
	SS_1 = 0;
	SPISwap(Chip_Erase_2);
	SS_1 = 1;
	while(Read_Status_Register_Sta() & 0x01);
}
//读ID
void Read_Identification_Sta(uchar *rdid)
{
   
    
	uchar i;
	SS_1 = 0;
	SPISwap(Read_Identification);
	for(i = 0; i < 3; i++){
   
    
		*rdid++ = SPISwap(NOP);
	}
	SS_1 = 1;
}
//16进制转字符串输出
void HexToAscii(uchar *pHex, uchar *pAscii, uchar nLen)
{
   
    
    uchar Nibble[2];
    uint i,j;
    for (i = 0; i < nLen; i++){
   
    
        Nibble[0] = (pHex[i] & 0xF0) >> 4;
        Nibble[1] = pHex[i] & 0x0F;
        for (j = 0; j < 2; j++){
   
    
            if (Nibble[j] < 10){
   
    
                Nibble[j] += 0x30;
            }
            else{
   
    
                if (Nibble[j] < 16)
                    Nibble[j] = Nibble[j] - 10 + 'A';
            }
            *pAscii++ = Nibble[j];
        }               // for (int j = ...)
    }           // for (int i = ...)
    *pAscii++ = '\0';
}
//主函数
void main(void)
{
   
    
	uchar sta, dis[2], rdid[3];
	uchar write[10] = {
   
    1, 2, 3, 4, 5, 6, 7, 8, 9, 11}, read[10] = {
   
    0x00};
	uchar play[20] = {
   
    0x00};
	
	WP = 1;
	
	Init_Uart();
	EA = 1;			//开总中断

	InitSPI_1();
	
	Read_Identification_Sta(rdid);	//切换SPI后,需要多读几次,等待SPI稳定
	Read_Identification_Sta(rdid);
	Read_Identification_Sta(rdid);
	HexToAscii(&rdid[0], dis, 1);
	SendString("Manufacturer ID: 0x");
	SendString(dis);
	SendString("\r\n");
	
	HexToAscii(&rdid[1], dis, 1);
	SendString("Memory Type: 0x");
	SendString(dis);
	SendString("\r\n");
	HexToAscii(&rdid[2], dis, 1);
	SendString("Capacity: 0x");
	SendString(dis);
	SendString("\r\n");

	sta = Read_Status_Register_Sta();
	HexToAscii(&sta, dis, 1);
	SendString("GD25Q80BSIG Status Register: 0x");
	SendString(dis);
	SendString("\r\n");

	Chip_Erase_1_Cmd();	//写数据之前要先擦除数据
	Page_Program_Cmd(0x00, 0x01, 0xFA, write, 10);//写数据
	Read_Data_Cmd(0x00, 0x01, 0xFA, read, 10);//读数据
	HexToAscii(read, play, 10);
	SendString("Read Address 0x0001FA: ");
	SendString(play);
	SendString("\r\n");
	
	while(1);
}

8、串口代码

//寄存器和宏定义
sfr AUXR   = 0x8E;		//辅助寄存器
sfr P_SW1  = 0xA2;		//外设功能切换寄存器1

//STC15W408AS单片机只有定时器0和定时器2
sfr T2H    = 0xD6;               //定时器2高8位
sfr T2L    = 0xD7;               //定时器2低8位

#ifndef FOSC
	#define FOSC  24000000L		//系统频率24MHz
#endif
#define BAUD 115200             //串口波特率

#define S1_S0 0x40              //P_SW1.6
#define S1_S1 0x80              //P_SW1.7

bit busy;						//忙标志

//UART 初始化程序
void Init_Uart(void)
{
   
    
	uchar temp;
	temp = P_SW1;
    temp &= ~(S1_S0 | S1_S1);    //S1_S0=0 S1_S1=0
    P_SW1 = temp;                //(P3.0/RxD, P3.1/TxD)
    
//  temp = P_SW1;
//  temp &= ~(S1_S0 | S1_S1);    //S1_S0=1 S1_S1=0
//  temp |= S1_S0;               //(P3.6/RxD_2, P3.7/TxD_2)
//  P_SW1 = temp;  
 
//  temp = P_SW1;
//  temp &= ~(S1_S0 | S1_S1);    //S1_S0=0 S1_S1=1
//  temp |= S1_S1;               //(P1.6/RxD_3, P1.7/TxD_3)
//  P_SW1 = temp;  

    SCON = 0x50;                //8位可变波特率
	
	T2L = (65536 - (FOSC / 4 / BAUD));   //设置波特率重装值
    T2H = (65536 - (FOSC / 4 / BAUD)) >> 8;
    AUXR |= 0x14;                //T2为1T模式, 并启动定时器2
    AUXR |= 0x01;               //选择定时器2为串口1的波特率发生器
    ES = 1;                     //使能串口1中断
}
//UART 中断服务程序
void Uart() interrupt 4 using 1
{
   
    
	if(RI){
   
    
		RI = 0;                 //清除RI位
	}
	if(TI){
   
    
		TI = 0;                 //清除TI位
		busy = 0;               //清忙标志
	}
}

//发送串口数据
void SendData(uchar dat)
{
   
    
    while(busy);               //等待前面的数据发送完成
    busy = 1;
    SBUF = dat;                 //写数据到UART数据寄存器
}

//发送字符串
void SendString(uchar *s)
{
   
    
    while(*s)                  //检测字符串结束标志
    {
   
    
        SendData(*s++);         //发送当前字符
    }
}

展开阅读全文
spi
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部