硬體SPI(查詢方式)
以STC15W408AS單片機為例
一、硬體接線
1、普通SPI設備接線
如NRF24L01,可以直接連接IO

2、FLASH設備接線
如GD25Q80BSIG,需要加上拉電阻

二、程式撰寫
1、和SPI相關的暫存器
① SPCTL暫存器



② SPSTAT暫存器


③ SPDAT暫存器

④ 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++); //發送當前字符
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/157056.html
標籤:python
上一篇:生成dbf檔案時,出現亂碼
下一篇:WebQQ3.0協議求解
