日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

stm32中spi可以随便接吗_STM32的SPI模式读写FLASH芯片全面讲解

發布時間:2023/12/8 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 stm32中spi可以随便接吗_STM32的SPI模式读写FLASH芯片全面讲解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

例程完整代碼:

SPI協議簡介

SPI協議,即串行外圍設備接口,是一種告訴全雙工的通信總線,它被廣泛地使用在ADC,LCD等設備與MCU間通信的場合。

SPI信號線

SPI包含4條總線,分別為SS,SCK,MOSI,MISO.作用如下:

1) SS:片選信號線,當有多個SPI設備和MCU相連時,每個設備的這個片選信號線是與MCU單獨的引腳相連的,而其他的SCK,MOSI,MISO線則為多個設備并聯到相同的SPI總線上,當SS信號線為低電平時,片選有效,開始SPI通信.

2) SCK:時鐘信號線,由主通信設備產生,不同的設備支持的時鐘頻率不一樣.

3) MOSI:主設備輸出/從設備輸入引腳,主機的數據從這條信號線輸出,從機由這條信號線讀入數據,即這條線上的數據方向為從主機到從機.

4)MISO:主設備輸入/從設備輸出引腳,這條線上數據是從機到主機.

SPI模式

根據時鐘極性(CPOL)和時鐘相位(CPHA)配置的不同,分為4種SPI模式。

時鐘極性是指SPI通信設備處于空閑狀態時(也可以認為這是SPI通信開始時,即SS線為低電平),SCK信號線的電平信號。CPOL=0時,SCK在空閑狀態時為低電平,CPOL=1時則相反。

時鐘相位是指數據采樣的時刻,當CPHA=0時,MOSI或MISO數據線上的信號將會在SCK時鐘線的奇數邊沿被采樣。當CPHA=1時,數據線在SCK的偶數邊沿被采樣。

下面以CPHA=0為例講解SPI時序。

首先,由主機把片選信號NSS拉低,意為主機輸出。

在NSS被拉低的時刻,SCK分為兩種情況,若我們設置CPOL=0,則SCK時序在這時為低電平,若設置為CPOL=1,則SCK在這個時刻為高電平。

無論CPOL為0還是1,因為我們配置的時鐘相位CPHA=0,在采樣時刻的時序中我們可以看到,采樣時刻都是在SCK的奇數邊沿(注意奇數邊沿有時為下降沿,有時為上升沿)。

因此,MOSI和MISO數據線的有效信號在SCK的奇數邊沿保持不變,這個信號將會在SCK奇數邊沿時被采集,在非采樣時刻,MOSI和MISO的有效信號才發生切換。

對于CPHA=1的情況也類似,只是數據信號的采樣時刻為偶數邊沿。

注意:使用SPI協議通信時,主機和從機的時序要保持一致,即兩者都選擇相同的SPI模式。

STM32的SPI特性

STM32的小容量產品有一個SPI接口,中容量有兩個,而大容量則有3個,其特征如下:

* 單次傳輸可選擇為8位或16位。

* 時鐘極性(CPOL)和相位(CPHA)可編程設置。

* 數據順序的傳輸順序可進行編程選擇,MSB在前或LSB在前。

* 可出發中斷的專用發送和接收標志。

* 可以使DMA進行數據傳輸操作。

STM32的SPI架構分析

上圖所示為STM32的架構圖,可以看到,MISO數據線接收到的信號經移位寄存器處理后把數據轉移到接收緩沖區,然后這個數據就可以由軟件從接收緩沖區讀出了。

當要發送數據時,我們把數據寫入發送緩沖區,硬件將會把它用移位寄存器處理后輸出到MOSI數據線。

SCK的時鐘信號則由波特率發生器產生,我們可以通過波特率控制位(BR)來控制它輸出的波特率。

控制寄存器CR1掌控著主控制電路,STM32的SPI模塊的協議設置(時鐘極性,相位等)就由它來制定。而控制寄存器CR2則用于設置各種中斷使能。

最后為NSS引腳,這個引腳扮演著SPI協議中的SS片選信號線的角色,如果我們把NSS引腳配置為硬件自動控制,SPI模塊能夠自動判別它能否成為SPI的主機,或自動進入SPI從機模式。但實際上我們用的更多的是由軟件控制某些GPIO引腳單獨作為SS信號,這個GPIO引腳可以隨便選擇。

SPI接口讀取FLASH實例分析

本文章以STM32通過SPI讀寫FLASH的例程來逐步講解STM32的SPI配置及FLASH芯片的普遍驅動方式,盡量做到講解精細易懂。

本實驗使用STM32的SPI2,采用主模式,全雙工通信,通過查詢發送數據寄存器和接收數據寄存器狀態確保通信正常。操作的FLASH芯片型號為W25Q16。

SPI2與芯片引腳連接為:PB12--CS,PB14--SO,PB13--CLK,PB15--SI.

本試驗沒有使用中斷,采用輪詢標志位的方式來確保SPI正常通信。

本例程完整代碼地址:

https://pan.baidu.com/s/1_bQI2V0YToQe1GJlac0rFQ?pan.baidu.com

main文件:

配置好所需的庫文件之后,我們就從main函數開始

int main(void)

{

USART1_Config();

SPI_FLASH_Init()

DeviceID=SPI_FLASH_ReadDeviceID();

Delay(200);

FlashID=SPI_FLASH_ReadID();

printf(“\r\n FlashID is 0x%X, Manufacturer Device ID is 0x%X \r\n”,FlashID,DeviceID);

if(FlashID==sFLASH_ID)

{

printf("\r\n檢測到串行flash W25Q16\r\n");

SPI_FLASH_SectorErase(FLASH_SectorToErase);

SPI_FlASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress , BufferSize );

printf("\r\n寫入的數據 :%s \r\n" , Tx_Buffer );

SPI_FLASH_BufferRead(Rx_Buffer,FLASH_ReadAddress, BufferSize );

printf("\r\n讀出的數據:%s \r\n" ,Rx_Buffer );

TransferStatus1=Buffercmp(Tx_Buffer, Rx_Buffer , BufferSize);

if(PASSED == TransferStatus1 )

{

printf("\r\n 測試成功 \r\n");

}

else

{

printf("\r\n 測試失敗 \r\n");

}

}

else

{

printf( "\r\n 獲取不到W25X16 ID \r\n" );

}

SPI_Flash_PowerDown();

while(1);

}

本實驗中,main函數調用的所有函數都是用戶函數:

1)調用USART1_Config()初始化串口。

2)調用SPI_FLASH_Init()初始化SPI模塊。

3)調用SPI_FLASH_ReadDeviceID()讀取Flash器件生產廠商的ID信息。

4)調用SPI_FLASH_ReadID()讀取器件的設備ID信息。

讀取器件的ID信息可以讓我們知道設備與主機是否能夠正常工作,也便于我們區分不同的器件。

5)若讀取得到的ID正確,則調用SPI_FLASH_SectorErase()把Flash的內容擦除,擦除后調用SPI_FLASH_BufferWrite()向FLASH寫入數據,然后再調用SPI_FLASH_BufferRead()從剛剛寫入的地址中讀出數據。最后調用Buffercmp()函數對寫入的數據與讀取的數據進行比較,若寫入的數據與讀出的數據相同,則把標志變量TransferStatus1賦值為PASSED。

6)最后調用SPI_FLASH_PowerDown()函數關閉Flash設備的電源,因為數據寫入到Flash后并不會因斷電而丟失,我們使用它時才重新開啟Flash電源。

接下來我們詳細分析main函數中調用的以上用戶函數是怎樣編寫的。

這里USART的配置不做說明,主要就是通過電腦串口打印實驗信息,跟這篇文章所涉及的主要內容不搭邊。

SPI初始化

SPI_FLASH_Init()函數初始化了SPI復用的GPIO引腳,啟動了GPIO及SPI1外設的時鐘,并初始化了SPI的模式。

void SPI_FLASH_Init()

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);

RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2,ENABLE);

//PB13 SPI2-SCK

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_13;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;

GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;

GPIO_Init(GPIOB,&GPIO_InitStructure);

//PB14 SPI2-MISO

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14;

GPIO_Init(GPIOB,&GPIO_InitStructure);

//PB15 SPI2-MOSI

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;

GPIO_Init(GPIOB,&GPIO_InitStructure);

//PB12 SPI2-CS

GPIO_InitStructure.GPIO_Pin=GPIO_Pin_12;

GPIO_InitStructure.GPIO_Mode=GPIO_Mode_Out_PP;

GPIO_Init(GPIOB,&GPIO_InitStructure);

SPI_FLASH_CS_HIGH();

//SPI2配置

SPI_InitStructure.SPI_Direction=SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode=SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize=SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL=SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA=SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS=SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler=SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit=SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial=7;

SPI_Init(SPI2,&SPI_InitStructure);

SPI_Cmd(SPI2,ENABLE);

}

SPI_FLASH_Init()分為兩部分,一部分為GPIO的配置,一部分為SPI模式的配置。

這里GPIO的配置及為SPI2相應引腳根據數據手冊配置成相應模式即可。

對STM32的SPI初始化配置,是根據將要與之通信的Flash設備的SPI特性來制定的,初始化時,有如下相關結構體成員:

1)SPI_Mode:STM32的SPI設備可以工作于主機模式(SPI_Mode_Master)或從機模式(SPI_Mode_Slave),這兩個模式的最大區別為SPI的SCK信號線的時序,SCK的時序是由通信中的主機產生的。若被配置為從機模式,STM32的SPI模塊將接收外來的SCK信號。

2)SPI_DataSize:這個成員可以選擇SPI每次通信的數據大小為8位還是16位。從FLASH的數據手冊我們可以查到,本FLASH通信的數據幀大小為8位,STM32的SPI模塊設置要與之相同。

3)SPI_CPOL和SPI_CPHA:這兩個成員配置SPI的時鐘極性和時鐘相位,這兩個配置影響到SPI的通信模式,該設置要符合將要互相通信的設備的要求.CPOL分別可以取SPI_CPOL_High(SPI通信空閑時SCK為高電平)和SPI_CPOL_Low(SPI通信空閑時SCK為低電平)。CPHA則可以取SPI_CPHA_1Edge(在SCK的奇數邊沿采集數據)和SPI_CPHA_2Edge(在SCK的偶數邊沿采集數據)。

查閱FLASH的使用手冊,可以了解到這個FLASH支持以SPI的模式0和模式3通信。即在SPI空閑時,SCK為低電平,奇數邊沿采樣(模式0);也可以在SPI空閑時,SCK為高電平,偶數邊沿采樣(模式3),即無論CPOL的狀態是什么,Flash的數據采樣時刻為SCK的上升沿。

我們在本實驗中配置使用它的模式3,即把CPOL賦值為SPI_CPOL_High;CPHA賦值為SPI_CPHA_2Edge。

4)SPI_NSS:本成員配置NSS引腳的使用模式,可以選擇為硬件模式(SPI_NSS_Hard)與軟件模式(SPI_NSS_Soft),在硬件模式中的SPI片選信號由硬件自動產生,而軟件模式則需要我們親自把相應的GPIO端口拉高或拉低來產生非片選和片選信號。如果外界條件允許,硬件模式還會自動將STM32的SPI設置為主機。

5)SPI_BaudRatePrescaler:本成員設置波特率分頻值,分頻后的時鐘即為SPI的SCK信號線的時鐘頻率。

6)SPI_FirstBit:所有串行的通信協議都會有MSB(高位數據在前)還是LSB先行(低位數據在前)的問題,而STM32的SPI模塊可以通過這個結構體成員,對這個特性編程控制。據Flash的通信時序,我們向這個成員賦值為MSB先行(SPI_FirstBit_MSB).

7) SPI_CRCPolynomial:這是SPI的CRC校驗中的多項式,若我們使用CRC校驗時,就使用這個成員的參數(多項式)來計算CRC的值。由于本實驗的Flash不支持CRC校驗,所以我們向這個結構體成員賦值為7實際上是沒有意義的。

控制FLASH的命令

實際上,編寫設備的驅動都有一定的規律可循。

首先我們要確定設備使用的是什么通信協議。如EEPROM使用的是I2C協議。本章的Flash使用的是SPI,那么我們就根據它的通信協議,選擇好STM32的硬件模塊,并進行相應的I2C或SPI模塊初始化。

因為不同的設備都會相應的有不同的指令,如EEPROM中會把第一個數據解釋為存儲矩陣的地址(實質就是指令)。而FLASH則定義了更多的指令,有寫指令、讀指令、讀ID指令等。

對主機來說,這些指令只是它遵守最基本的通信協議發送出的數據。但設備把這些數據解釋為不同的意義(指令編碼),所以才成為指令。在我們配置好STM32的協議模塊后,想要控制設備,就要遵守相應設備所定義的命令規則。

綜上所述,驅動的編寫原理:確定通信協議模塊,通過協議收發命令、數據,進而驅動設備。

下圖為Flash的各種命令和命令解釋時序

指令表中的A0~A23指地址,M0~M7為器件的制造商ID,D0~D7為數據。

在命令列表中可以了解到讀取設備ID的命令(Device ID)編碼為ABh、dummy、dummy、dummy。表示此命令由這四個字節組成,其中dummy意為任意編碼,表示這些編碼可以發送任意數據。命令列表中帶括號的字節數據表示由Flash返回給主機的響應,可以看到Release Power down or HPM/DeviceID命令的第5個字節為從機返回的響應。(ID1-ID0)即返回設備的ID號。

讀Device指令時序的編程

下圖為讀Device指令的時序圖

以看到主機首先通過MOSI線(即Flash的DI線)發送第一個字節為ABh編碼,緊接著三個字節的dummy編碼(即3個字節的偽數據),然后Flash就忽略DI線上的信號,通過MISO線(即Flash的DO線)把它的Flash設備ID發送給主機。

了解了Device ID命令及其時序,我們分析SPI_FLASH_ReadDeviceID()函數來說明驅動編寫原理。

這個函數實現了讀取Flash的ID 的功能

u32 SPI_FLASH_ReadDeviceID(void)

{

u32 Temp=0;

SPI_FLASH_CS_LOW();

//發送4字節指令

SPI_FLASH_SendByte(W25X_DeviceID);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

//讀取Flash返回的第5字節數據

Temp=SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_CS_HIGH();

return Temp;

}

這個函數的代碼流程嚴格遵從DeviceID命令的時序:

1)SPI_FLASH_CS_LOW(),拉低CS線,片選FLASH,以使能FLASH設備。

2)利用SPI_FLASH_SendByte()向Flash發送第一個命令字節編碼W25X_DeviceID,該宏展開后為0xAB.

3)根據指令表,發送完這個指令后,后面緊跟著三個字節的dummy byte,我們把Dummy_Byte宏定義為0xFF,實際上改成其它編碼都可以,無影響。

4)完整的命令在前面已經發送完畢,根據時序,在第5個字節,Flash通過DO端口輸出它的器件ID,我們調用函數SPI_FLASH_SendByte()接收返回的數據,并賦值給Temp變量。SPI_FLASH_ReadDeviceID()函數的返回值即為讀取得到的器件ID。

5)拉高片選信號,結束通信。

這就完成了讀FLASH的ID。在這個讀FlashID 函數中多次調用了一個相對底層的用戶函數,它實現了利用SPI發送和接收數據的功能:

u8 SPI_FLASH_SendByte(u8 byte)

{

while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_TXE)==RESET);

SPI_I2S_SendData(SPI2,byte);

while(SPI_I2S_GetFlagStatus(SPI2,SPI_I2S_FLAG_RXNE)==RESET);

return SPI_I2S_ReceiveData(SPI2);

}

1)調用庫函數SPI_I2S_GetFlagStatus()等待發送寄存器清空。

2)發送數據寄存器準備好后,調用庫函數SPI_I2S_SendData()向從機發送數據。

3)調用庫函數SPI_I2S_GetFlagStatus()等待接收數據寄存器非空。

4)接收寄存器非空時,調用SPI_I2S_ReceiveData()獲取接收寄存器中的數據并作為函數的返回值,這個數據即由從機發回給主機的數據。

這是最底層的發送數據和接收數據的函數,利用庫函數的標識檢測確保通信正常。

讀取廠商ID

對于其他函數,編寫的方法是類似的,如讀取廠商ID 的函數SPI_FLASH_ReadID()。

u32 SPI_FLASH_ReadID(void)

{

u32 Temp=0,Temp0=0,Temp1=0,Temp2=0;

SPI_CS_LOW();

SPI_FLASH_SendByte(W25X_JedecDeviceID);

Temp0=SPI_FLASH_SendByte(Dummy_Byte);

Temp1=SPI_FLASH_SendByte(Dummy_Byte);

Temp2=SPI_FLASH_SendByte(Dummy_Byte);

Temp=(Temp0<<16)|(Temp1<<8)|Temp2;

return Temp;

}

這里的W25X_JedcDeviceID的宏定義為0x9F.

這個函數根據命令流程,發送一個字節的命令代碼(9F)后,從機就通過DO線返回廠商ID即0~16位的設備ID 。

FLASH芯片的讀寫以及擦除

1.扇區擦除

根據Flash的存儲原理,在寫入數據前要先對存儲區域進行擦除。

所以執行SPI_FLASH_SectorErase()函數對要寫入的扇區進行擦除,也稱為預寫。

void SPI_FLASH_SectorErase(u32 SectorAddr)

{

SPI_FLASH_WriteEnable();

SPI_FLASH_WaitForWriteEnd();

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_SectorErase);

SPI_FLASH_SendByte((SectorAddr & 0xFF0000)>>16);

SPI_FLASH_SendByte((SectorAddr & 0xFF00)>>8);

SPI_FLASH_SendByte(SectorAddr & 0xFF);

SPI_FLASH_CS_HIGH();

SPI_FLASH_WaitForWriteEnd();

}

先忽略SPI_FLASH_WriteEnable()和SPI_FLASH_WaitForWriteEnd()函數。其余為純粹的關于FLASH擦除操作,扇區擦除命令時序如下圖:

其中第一個字節為扇區擦除命令編碼(20h),緊跟其后的為要進行擦除的24位起始地址。

根據Flash的說明,它把整個存儲矩陣分為塊區和扇區,每塊(Block)的大小為64KB,每個扇區(Sector)的大小為4KB,對存儲矩陣進行擦除時,最小的單位為扇區。

2.寫使能

根據Flash的讀寫要求,在進行寫入、扇區擦除、塊擦除、整片擦除及寫狀態寄存器前,都需要發送寫使能命令。

在SPI_FLASH_SectroErase()函數中我們調用了SPI_FLASH_WriteEnable(),具體實現如下:

void SPI_FLASH_WriteEnable(void)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_WriteEnable);

SPI_FLASH_CS_HIGH();

}

本函數比較簡單,就是根據寫使能命令的時序,發送寫使能命令write enable(06h)。

3.讀Flash狀態

在擦除函數SPI_FLASH_SectorErase()中,還調用了用戶函數SPI_FLASH_WaitForWriteEnd()來確保在Flash不忙碌的時候,才發送命令與數據。

這個函數通過讀取Flash的狀態寄存器來獲知它的工作狀態。

void SPI_FLASH_WaitForWriteEnd(void)

{

u8 FLASH_Status=0;

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadStatusReg);

do

{

FLASH_Status=SPI_FLASH_SendByte(Dummy_Byte);

}

while((FLASH_Status & WIP_Flag) == SET );

SPI_FLASH_CS_HIGH();

}

本函數實質是不斷檢測Flash狀態寄存器的Busy位,直到Flash的內部寫時序完成,從而確保下一通信操作正常,這里WIP_Flag宏定義為0x01。

主機通過發送讀狀態寄存器命令Read Status Register(05h),返回它的8位狀態寄存器值。

本函數檢測的就是狀態寄存器的Bit0位,即BUSY位。Flash在執行內部寫時序的時候,除了讀狀態寄存器的命令,其他一切命令都會忽略,并且BUSY位保持為1,即我們需要等待BUSY位為0的時候,再向FLASH發送其他指令。

4.向FLASH寫入數據

對Flash寫入數據,最小單位是256字節,廠商把這個單位稱為頁。

寫入時,一般也只有頁寫入的方式。

因而我們為了方便地把一個很長的數組寫入Flash中,一般需要進行轉換,把數組按頁分好,再寫入Flash中,如同I2C通信中對EEPROM的頁寫入一樣,只是頁的大小不同而已。

void SPI_FLASH_BufferWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

u8 NumOfPage=0,NumOfSingle=0,Addr=0,count=0,temp=0;

Addr=WriteAddr % SPI_FLASH_PageSize;

count=SPI_FLASH_PageSize-Addr;

NumOfPage=NumByteToWrite / SPI_FLASH_PageSize;

NumOfSingle=NumByteToWrite % SPI_FLASH_PageSize;

if (Addr == 0) /* WriteAddr is SPI_FLASH_PageSize aligned */

{

if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

}

else /* NumByteToWrite > SPI_FLASH_PageSize */

{

while (NumOfPage--)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

else /* WriteAddr is not SPI_FLASH_PageSize aligned */

{

if (NumOfPage == 0) /* NumByteToWrite < SPI_FLASH_PageSize */

{

if (NumOfSingle > count) /* (NumByteToWrite + WriteAddr) > SPI_FLASH_PageSize */

{

temp = NumOfSingle - count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);

}

else

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

}

}

else /* NumByteToWrite > SPI_FLASH_PageSize */

{

NumByteToWrite -= count;

NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;

NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

while (NumOfPage--)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

if (NumOfSingle != 0)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

}

}

在SPI_FLASH_BufferWrite()中,對數組進行分頁后,它調用了用戶函數SPI_FLASH_PageWrite來對數據進行頁寫入。

void SPI_FLASH_PageWrite(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

SPI_FLASH_WriteEnable();

SPI_FLASH_CS_LOW();

//頁編程指令

SPI_FLASH_SendByte(W25X_PageProgram);

//發送高8位地址

SPI_FLASH_SendByte((WriteAddr&0xFF0000)>>16);

//發送中8位地址

SPI_FLASH_SendByte((WriteAddr&0xFF00)>>8);

//發送低8位地址

SPI_FLASH_SendByte(WriteAddr&0xFF);

if(NumByteToWrite>SPI_FLASH_PerWritePageSize)

{

NumByteToWrite=SPI_FLASH_PerWritePageSize;

}

while(NumByteToWrite--)

{

SPI_FLASH_SendByte(*pBuffer);

pBuffer++;

}

SPI_FLASH_CS_HIGH();

SPI_FLASH_WaitForWriteEnd();

}

頁寫入時序如下圖,發送完頁寫入指令PageProgram(02h)及地址后,可以連續寫入最多256字節數據,在發送完數據之后,我們調用SPI_FLASH_WaitForWriteEnd()等待Flash內部寫時序完成再退出函數。

5.從Flash讀取數據

對于讀取數據,發出一個命令后,可以無限制的把整個Flash的數據都讀取完,若認為讀取數據的數據量足夠了,可以拉高片選信號以表示讀取數據結束。

void SPI_FLASH_BufferRead(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadData);

SPI_FLASH_SendByte((ReadAddr&0xFF0000)>>16);

SPI_FLASH_SendByte((ReadAddr&0xFF00)>>8);

SPI_FLASH_SendByte(ReadAddr&0xFF);

while(NumByteToRead--)

{

*pBuffer=SPI_FLASH_SendByte(Dummy_Byte);

pBuffer++;

}

SPI_FLASH_CS_HIGH();

}

以上為讀數據的時序圖,SPI_FLASH_BufferRead()函數首先發送讀數據指令ReadData(03h)。

緊接著發送24位讀數據起始地址,STM32再通過DO線接收數據,并把他們使用指針的方式記錄起來。

總結

以上是生活随笔為你收集整理的stm32中spi可以随便接吗_STM32的SPI模式读写FLASH芯片全面讲解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。