![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iY2QzYkhTMkRjMwM2Y4UTYwUmMhJWMxcTM2AzMjFWY08CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
我的 github:
https://github.com/lovewinds13/stm32f013_study。
STM32F103 相關代碼均送出到此倉庫。
1. 硬體連接配接
W25Q64 将 8M 的容量分為 128 個塊(Block),每個塊大小為 64K 位元組,每個塊又分為 16個扇區(Sector),每個扇區 4K 個位元組。
W25Q64 的最少擦除機關為一個扇區,也就是每次必須擦除 4K 個位元組。操作需要給 W25Q64 開辟一個至少 4K 的緩存區,對 SRAM 要求比較高,要求晶片必須有 4K 以上 SRAM 才能很好的操作。
W25Q64 的擦寫周期多達 10W 次,具有 20 年的資料儲存期限,支援電壓為 2.7~3.6V,W25Q64 支援标準的 SPI,還支援雙輸出/四輸出的 SPI,最大 SPI 時鐘可以到 80Mhz(雙輸出時相當于 160Mhz,四輸出時相當于 320M)。
1.1 硬體連接配接
與 STM32 的引腳連接配接如下:這裡是使用SPI1配置。
STM32引腳 | 對應SPI功能 |
---|---|
PA2 | 片選CS |
PA5 | 時鐘SCK |
PA6 | MISO |
PA7 | MOSI |
STM32 的 SPI 功能很強大, SPI 時鐘最多可以到 18Mhz,支援 DMA,可以配置為 SPI 協定或者 I2S 協定(僅大容量型号支援)。
1.2 SPI 通訊的通訊時序
SPI 協定定義了通訊的起始和停止信号、資料有效性、時鐘同步等環節。
我們以讀取 FLASH 的狀态寄存器的時序圖分析一下,時序圖也是書寫軟體模拟時序的依據。
如上圖,我們知道書寫 FLASH (來自華邦 W25X 手冊)支援的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)
CS、SCK、MOSI 信号都由主機控制産生,而 MISO 的信号由從機産生,主機通過該信号線讀取從機的資料。MOSI 與 MISO 的信号隻在 CS 為低電平的時候才有效,在 SCK 的每個時鐘周期 MOSI 和 MISO 傳輸一位資料。
1.2.1. 通訊的起始和停止信号
在上圖,CS 信号線由高變低,為 SPI 通訊的起始信号。CS 是每個從機各自獨占的信号線,當從機在自己的 CS 線檢測到起始信号後,就知道自己被主機選中了,開始準備與主機通訊。當 CS 信号由低變高,為 SPI 通訊的停止信号,表示本次通訊結束,從機的選中狀态被取消。
1.2.2. 資料有效性
SPI 使用 MOSI 及 MISO 信号線來傳輸資料,使用 SCK 信号線進行資料同步。
MOSI 及 MISO 資料線在 SCK 的每個時鐘周期傳輸一位資料。資料傳輸時,MSB 先行或 LSB 先行并沒有作硬性規定,但要保證兩個 SPI 通訊裝置之間使用同樣的協定,一般都會采用圖中的 MSB 先行模式。
觀察上圖,可知模式 0 和 3 都是在上升沿讀取資料。
示例:FLASH 讀取 JEDEC_ID (0x9F),SPI 模式 0,,頻率 f = 1MHz。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLicmbw5iY2QzYkhTMkRjMwM2Y4UTYwUmMhJWMxcTM2AzMjFWY08CX0JXZ252bj91Ztl2Lc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
讀取 JEDEC_ID 時,FLASH 回複的一個字:0xC8。
1.2.3 STM32 SPI外設
STM32 的 SPI 外設可用作通訊的主機及從機,支援最高的 SCK 時鐘頻率為 f pclk / 2 (STM32F103 型号的晶片預設 f pclk1 為 72MHz,f pclk2 為 36MHz),完全支援 SPI 協定的 4 種模式,資料幀長度可設定為 8 位或 16 位,可設定資料 MSB 先行或 LSB 先行。它還支援雙線全雙工、雙線單向以及單線模式。
SPI架構:
通訊引腳 :
SPI 的所有硬體架構都從上圖中左 MOSI、MISO、SCK及 NSS 線展開的。
STM32 晶片有多個 SPI 外設,它們的 SPI 通訊信号引出到不同的 GPIO 引腳上,使用時必須配置到這些指定的引腳。
2. 軟體配置
這裡使用 STM32 的 SPI1 的主模式,SPI 相關的庫函數和定義分布在檔案 stm32f10x_spi.c 以及頭檔案 stm32f10x_spi.h 中。
2.1 配置相關引腳的複用功能
第一步就要使能 SPI1 的時鐘, SPI1 的時鐘通過 APB2ENR 的第 12 位來設定。其次要設定 SPI1 的相關引腳為複用輸出,這樣才會連接配接到 SPI1 上否則這些 IO 口還是預設的狀态,也就是标準輸入輸出口。這裡我們使用的是 PA5、 PA6、 PA7 這 3 個(SCK、 MISO、 MOSI、CS 使用軟體管理方式),是以設定這三個為複用 IO。
宏定義:
IO 配置:
2.2 初始化 SPI1,設定 SPI1 工作模式
接下來初始化 SPI1,設定 SPI1 為主機模式,設定資料格式為 8 位,然設定 SCK 時鐘極性及采樣方式。并設定 SPI1 的時鐘頻率(最大 18Mhz),以及資料的格式(MSB 在前還是 LSB 在前)。這在庫函數中是通過 SPI_Init 函數來實作。
函數原型:
void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);
第一個參數是 SPI 标号,第二個參數結構體類型 SPI_InitTypeDef 為相關屬性設定。
SPI_InitTypeDef 的定義如下:
參數 | 解釋 |
---|---|
SPI_Direction | 設定 SPI 的通信方式,可以選擇為半雙工,全雙工,以及串行發和串行收方式 |
SPI_Mode | 設定 SPI 的主從模式,主機模式 (SPI_Mode_Master),從機模式 (PI_Mode_Slave)。 |
SPI_DataSiz | 資料為 8 位還是 16 位幀格式選擇項。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位) |
SPI_CPOL | 設定時鐘極性 |
SPI_CPHA | 設定時鐘相位,也就是選擇在串行同步時鐘的第幾個跳變沿(上升或下降)資料被采樣,可以為第一個或者第二個條邊沿采集 |
SPI_NSS | 設定 NSS 信号由硬體(NSS 管腳)還是軟體控制 |
SPI_BaudRatePrescaler | 設定 SPI 波特率預分頻值也就是決定 SPI 的時鐘的參數 ,從不分頻道 256 分頻 8 個可選值 ,選擇 256 分頻值SPI_BaudRatePrescaler_256, 傳輸速度為 36M/256=140.625KHz。 |
SPI_FirstBit | 設定資料傳輸順序是 MSB 位在前還是 LSB 位在前。SPI_FirstBit_MSB (高位在前) |
SPI_CRCPolynomial | 設定 CRC 校驗多項式,提高通信可靠性,大于 1 即可 |
初始化的範例格式為:
2.3 SPI 傳輸資料
通信接口需要有發送資料和接受資料的函數,固件庫提供的發送資料函數原型為:
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
往 SPIx 資料寄存器寫入資料 Data,進而實作發送。
固件庫提供的接受資料函數原型為:
這從 SPIx 資料寄存器讀出接收到的資料。
收發單個位元組資料:
收發多個位元組資料:
2.4 檢視 SPI 傳輸狀态
在 SPI 傳輸過程中,要判斷資料是否傳輸完成,發送區是否為空等等狀态,
通過函數 SPI_I2S_GetFlagStatus 實作的,判斷發送是否完成的方法是:
3. SPI FLASH 操作
3.1 宏定義部分
3.2 中間層函數封裝
注明: 此部分函數的封裝是為了統一硬體 SPI 和軟體模拟 SPI 接口。
//--------------------------------------------------------------------------------------------------------// 函 數 名: hal_spi_send_bytes// 功能說明: SPI 發送資料,包含軟體和硬體通信方式// 形 參: mode:通信方式選擇(0:軟體SPI;1:硬體SPI)// pbdata:發送資料的首位址// send_length:發送資料長度// 返 回 值: 執行狀态(true or false)// 日 期: 2020-03-12// 備 注: 中間層封裝底層接口// 作 者: by 霁風AI//--------------------------------------------------------------------------------------------------------uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{if (mode == 0)
{for (uint16_t i = 0; i {
Spi_WriteByte(pbdata[i]);
}return true;
}else if (mode == 1)
{
spi_master_send_some_bytes(1, pbdata, send_length);// for (uint16_t i = 0; i // {// spi_master_send_recv_byte(1, pbdata[i]);// }return true;
}else
{return false;
}
}//--------------------------------------------------------------------------------------------------------// 函 數 名: hal_spi_recv_bytes// 功能說明: SPI 接收資料,包含軟體和硬體通信方式// 形 參: mode:通信方式選擇(0:軟體SPI;1:硬體SPI)// pbdata:發送資料的首位址// send_length:發送資料長度// 返 回 值: 執行狀态(true or false)// 日 期: 2020-03-12// 備 注: 中間層封裝底層接口// 作 者: by 霁風AI//--------------------------------------------------------------------------------------------------------uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{if (mode == 0)
{for (uint16_t i = 0; i {
*pbdata++ = Spi_ReadByte(); //軟體模拟SPI
} return true;
}else if (mode == 1)
{
spi_master_recv_some_bytes(1, pbdata, recv_length); //硬體SPI// for (uint16_t i = 0; i // {// *pbdata++ = spi_master_send_recv_byte(1, 0xFF);// }return true;
}else
{return false;
}
}
關于軟體模拟 SPI 部分代碼,參看:
軟體模拟SPI代碼:
(https://github.com/lovewinds13/stm32f013_study/blob/master/Driver/src/drvsfspi.c) 。
此處不再貼出。