STC15系列單片機SPI使用教程(一)

硬件SPI(查詢方式)

以STC15W408AS單片機爲例

1、硬件接線

一、普通SPI設備接線

如NRF24L01,能夠直接鏈接IO
普通SPI設備接線



編程

二、FLASH設備接線

如GD25Q80BSIG,須要加上拉電阻
FLASH設備接線
函數

2、程序編寫

一、和SPI相關的寄存器


① SPCTL寄存器

SPCTL寄存器
位描述
在這裏插入圖片描述




② SPSTAT寄存器

SPSTAT寄存器
在這裏插入圖片描述



③ SPDAT寄存器

SPDAT寄存器


④ AUXR1/P_SW1寄存器

AUXR1/P_SW1寄存器


二、自定義寄存器,數據類型重定義

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

三、寄存器相關位宏定義, 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腳

四、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;        //主機模式
}

五、SPI數據交換代碼

uchar SPISwap(uchar dat) 
{ 
    SPDAT = dat;                //觸發SPI發送數據
    while (!(SPSTAT & SPIF));   //等待發送完成
    SPSTAT = SPIF | WCOL;       //清除SPI狀態位
    return SPDAT;               //返回SPI數據
}

六、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);
}

七、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);
}

八、串口代碼

//寄存器和宏定義
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++);         //發送當前字符
    }
}
相關文章
相關標籤/搜索