硬件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++); //发送当前字符 } }