硬件SPI(查詢方式)
以STC15W408AS單片機爲例
1、硬件接線
一、普通SPI設備接線
如NRF24L01,能夠直接鏈接IO
編程
二、FLASH設備接線
如GD25Q80BSIG,須要加上拉電阻
函數
2、程序編寫
一、和SPI相關的寄存器
① SPCTL寄存器
② SPSTAT寄存器
③ SPDAT寄存器
④ 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++); //發送當前字符 } }