寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難,但我還是想去做!
本文寫于:2025.04.18
STM32開發板學習——第一節: [1-1]課程簡介第40節: [11-5] 硬件SPI讀寫W25Q64
- 前言
- 開發板說明
- 引用
- 解答和科普
- 一、硬件接線
- 二、W25Q64
- 問題
- 總結
前言
? ?本次筆記是用來記錄我的學習過程,同時把我需要的困難和思考記下來,有助于我的學習,同時也作為一種習慣,可以督促我學習,是一個激勵自己的過程,讓我們開始32單片機的學習之路。
? ?歡迎大家給我提意見,能給我的嵌入式之旅提供方向和路線,現在作為小白,我就先學習32單片機了,就跟著B站上的江協科技開始學習了.
? ?在這里會記錄下江協科技32單片機開發板的配套視頻教程所作的實驗和學習筆記內容,因為我之前有一個開發板,我大概率會用我的板子模仿著來做.讓我們一起加油!
? ?另外為了增強我的學習效果:每次筆記把我不知道或者問題在后面提出來,再下一篇開頭作為解答!
開發板說明
? ?本人采用的是慧凈的開發板,因為這個板子是我N年前就買的板子,索性就拿來用了。另外我也購買了江科大的學習套間。
? ?原理圖如下
1、開發板原理圖
2、STM32F103C6和51對比
3、STM32F103C6核心板
視頻中的都用這個開發板來實現,如果有資源就利用起來。另外也計劃實現江協科技的套件。
下圖是實物圖
引用
【STM32入門教程-2023版 細致講解 中文字幕】
還參考了下圖中的書籍:
STM32庫開發實戰指南:基于STM32F103(第2版)
數據手冊
解答和科普
一、硬件接線
默認情況下,是作為JATG調試端口使用的,都需要先接觸調試端口的復用。
SCK(PA5),MISO(PA6),MOSI(PA7),NSS(PA4或其他)。
我們的任務就是修改底層的這個MySPI.c文件,把這些初始化,和時序的執行步驟,由軟件實現變成硬件實現。所以,我們把底層的實現,由軟件改成硬件,不會影響到上層代碼的。
這個就不移出了。
第一步,開啟時鐘,開啟SPI和GPIO的時鐘;
第二步,初始化GPIO口,其中SCK、和MOSI,是由硬件外設控制的輸出信號,所以配置為復用推挽輸出,MISO是硬件外設的輸入信號,我們可以配置為上拉輸入;因為輸入設備可以有多個,不存在復用輸入這個東西,直接上拉輸入就可,普通GPIO口可以輸入,外設也可以輸入;SS引腳,是軟件控制的輸出信號,所以配置為通用推挽輸出;
第三步,配置SPI外設,這一塊,使用一個結構體選參數即可,調用一下SPI_Init,這里的各種參數就都配置好了;
第四步,開關控制,SPI_Cmd,給SPI使能。
寫DR、讀DR、獲取標志位。
庫文件
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
寫DR寄存器,寫數據到發送數據寄存器TDR.
uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx);
讀DR寄存器,接收數據寄存器RDR.
FlagStatus SPI_I2S_GetFlagStatus(SPI_TypeDef* SPIx, uint16_t SPI_I2S_FLAG);
獲取TXE和RXNE標志位的狀態;
這樣就可以控制時序產生。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode=SPI_Mode_Master; //模式:主機SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex; //單雙向模式SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b; //數據幀長度SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB; //高位先行SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128; //外設時鐘128分頻SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low; //模式0SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; //軟件SPI_InitStructure.SPI_CRCPolynomial=7; //CRC校驗7SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1); //默認不選中從機
}
SS
void MySPI_W_SS(uint8_t BitValue)
{GPIO_WriteBit(GPIOA,GPIO_Pin_4,(BitAction)BitValue);
}
void MySPI_Start(void)
{MySPI_W_SS(0);
}void MySPI_Stop(void)
{MySPI_W_SS(1);
}
第一步,等待TXE為1,第二步,寫入發送的數據至TDR,第三步,等待RXNE為1,第四步,讀取RDR接收的數據,之后交換第二個字節,重復這4步。
uint8_t MySPI_SwapByte(uint8_t ByteSend)
{while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE)!=SET);SPI_I2S_SendData(SPI1,ByteSend);while (SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_RXNE)!=SET);return SPI_I2S_ReceiveData(SPI1);}
硬件SPI初始化
void MySPI_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_4;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5|GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);SPI_InitTypeDef SPI_InitStructure;SPI_InitStructure.SPI_Mode=SPI_Mode_Master; //模式:主機SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex; //單雙向模式SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b; //數據幀長度SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB; //高位先行SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_128; //外設時鐘128分頻SPI_InitStructure.SPI_CPOL=SPI_CPOL_Low; //模式0SPI_InitStructure.SPI_CPHA=SPI_CPHA_1Edge;SPI_InitStructure.SPI_NSS=SPI_NSS_Soft; //軟件SPI_InitStructure.SPI_CRCPolynomial=7; //CRC校驗7SPI_Init(SPI1,&SPI_InitStructure);SPI_Cmd(SPI1,ENABLE);MySPI_W_SS(1); //默認不選中從機
}
#ifndef __MYSPI_H
#define __MYSPI_Hvoid MySPI_Init(void);
void MySPI_Start(void);
void MySPI_Stop(void);
uint8_t MySPI_SwapByte(uint8_t ByteSend);#endif
二、W25Q64
應用層不變
#include "stm32f10x.h" // Device header
#include "MySPI.h"
#include "W25Q64_Ins.h"
void W25Q64_Init(void)
{MySPI_Init();
}void W25Q64_ReadID(uint8_t *MID ,uint16_t *DID)
{MySPI_Start();MySPI_SwapByte(W25Q64_JEDEC_ID); //讀ID號的指令,拋玉引磚,返回值沒有意義沒用*MID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); //0XFF沒有意義,拋磚引玉,就是為了把對面有意義的數據置換過來*DID = MySPI_SwapByte(W25Q64_DUMMY_BYTE); // 讀取設備ID高8位*DID <<=8;*DID |= MySPI_SwapByte(W25Q64_DUMMY_BYTE); //獲得16位的DIDMySPI_Stop();
}void W25Q64_WriteEnable(void)
{MySPI_Start();MySPI_SwapByte(W25Q64_WRITE_ENABLE);MySPI_Stop();
}void W25Q64_WaiteBusy(void)
{uint32_t TimeOut;MySPI_Start();MySPI_SwapByte(W25Q64_READ_STATUS_REGISTER_1);TimeOut=10000;while ((MySPI_SwapByte(W25Q64_DUMMY_BYTE)& 0x01)==0x01) //用掩碼取出低位{TimeOut--;if(TimeOut==0){break;}}MySPI_Stop();
}void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count)
{uint16_t i;W25Q64_WriteEnable();MySPI_Start();MySPI_SwapByte(W25Q64_PAGE_PROGRAM);MySPI_SwapByte(Address>>16); //0x123456 變為 0x12MySPI_SwapByte(Address>>8); //0x123456 變為 0x1234 高位自動舍去就是0x34 MySPI_SwapByte(Address); //0x123456 高位自動舍去就是0x56 for(i=0;i<Count;i++){MySPI_SwapByte(DataArray[i]);}MySPI_Stop();W25Q64_WaiteBusy(); //事后等待
}void W25Q64_SectorErase(uint32_t Address)
{W25Q64_WriteEnable(); //寫使能MySPI_Start();MySPI_SwapByte(W25Q64_SECTOR_ERASE_4KB);MySPI_SwapByte(Address>>16); //0x123456 變為 0x12MySPI_SwapByte(Address>>8); //0x123456 變為 0x1234 高位自動舍去就是0x34 MySPI_SwapByte(Address);MySPI_Stop();W25Q64_WaiteBusy(); //事后等待
}
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count)
{uint32_t i;MySPI_Start();MySPI_SwapByte(W25Q64_READ_DATA);MySPI_SwapByte(Address>>16); //0x123456 變為 0x12MySPI_SwapByte(Address>>8); //0x123456 變為 0x1234 高位自動舍去就是0x34 MySPI_SwapByte(Address); for(i=0;i<Count;i++){DataArray[i]=MySPI_SwapByte(W25Q64_DUMMY_BYTE);}MySPI_Stop();}
#ifndef __W25Q64_H
#define __W25Q64_Hvoid W25Q64_Init(void);
void W25Q64_ReadID(uint8_t *MID ,uint16_t *DID);
void W25Q64_ReadData(uint32_t Address,uint8_t *DataArray,uint32_t Count);
void W25Q64_SectorErase(uint32_t Address);
void W25Q64_PageProgram(uint32_t Address,uint8_t *DataArray,uint16_t Count);#endif
#ifndef __W25Q64_INS_H
#define __W25Q64_INS_H#define W25Q64_WRITE_ENABLE 0x06
#define W25Q64_WRITE_DISABLE 0x04
#define W25Q64_READ_STATUS_REGISTER_1 0x05
#define W25Q64_READ_STATUS_REGISTER_2 0x35
#define W25Q64_WRITE_STATUS_REGISTER 0x01
#define W25Q64_PAGE_PROGRAM 0x02
#define W25Q64_QUAD_PAGE_PROGRAM 0x32
#define W25Q64_BLOCK_ERASE_64KB 0xD8
#define W25Q64_BLOCK_ERASE_32KB 0x52
#define W25Q64_SECTOR_ERASE_4KB 0x20
#define W25Q64_CHIP_ERASE 0xC7
#define W25Q64_ERASE_SUSPEND 0x75
#define W25Q64_ERASE_RESUME 0x7A
#define W25Q64_POWER_DOWN 0xB9
#define W25Q64_HIGH_PERFORMANCE_MODE 0xA3
#define W25Q64_CONTINUOUS_READ_MODE_RESET 0xFF
#define W25Q64_RELEASE_POWER_DOWN_HPM_DEVICE_ID 0xAB
#define W25Q64_MANUFACTURER_DEVICE_ID 0x90
#define W25Q64_READ_UNIQUE_ID 0x4B
#define W25Q64_JEDEC_ID 0x9F
#define W25Q64_READ_DATA 0x03
#define W25Q64_FAST_READ 0x0B
#define W25Q64_FAST_READ_DUAL_OUTPUT 0x3B
#define W25Q64_FAST_READ_DUAL_IO 0xBB
#define W25Q64_FAST_READ_QUAD_OUTPUT 0x6B
#define W25Q64_FAST_READ_QUAD_IO 0xEB
#define W25Q64_OCTAL_WORD_READ_QUAD_IO 0xE3#define W25Q64_DUMMY_BYTE 0xFF#endif
問題
總結
本節課主要是了解硬件SPI讀寫W25Q64,在硬件層面對整個SPI進行寫,主要是各種時序,標志位和事件來進行,硬件會自動開啟時序,需要進行配置。