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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【正点原子探索者STM32F407开发板例程连载+教学】第30章 SPI通信实验

發布時間:2023/12/14 编程问答 71 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【正点原子探索者STM32F407开发板例程连载+教学】第30章 SPI通信实验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第三十章?SPI?實驗

?

[mw_shl_code=c,true]1.硬件平臺:正點原子探索者STM32F407開發板?2.軟件平臺:MDK5.1 3.固件庫版本:V1.4.0 [/mw_shl_code]

?

?

本章我們將向大家介紹STM32F4的SPI功能。在本章中,我們將使用STM32F4自帶的SPI來實現對外部FLASH(W25Q128)的讀寫,并將結果顯示在TFTLCD模塊上。本章分為如下幾個部分:

30.1 SPI?簡介

30.2?硬件設計

30.3?軟件設計

30.4?下載驗證

30.1 SPI?簡介

SPI?是英語Serial Peripheral interface的縮寫,顧名思義就是串行外圍設備接口。是Motorola首先在其MC68HCXX系列處理器上定義的。SPI接口主要應用在?EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信號處理器和數字信號解碼器之間。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便,正是出于這種簡單易用的特性,現在越來越多的芯片集成了這種通信協議,STM32F4也有SPI接口。下面我們看看SPI的內部簡明圖(圖30.1.1):

?

?

圖30.1.1 SPI內部結構簡明圖

?

SPI接口一般使用4條線通信:

MISO?主設備數據輸入,從設備數據輸出。

MOSI?主設備數據輸出,從設備數據輸入。

SCLK時鐘信號,由主設備產生。

CS從設備片選信號,由主設備控制。

從圖中可以看出,主機和從機都有一個串行移位寄存器,主機通過向它的SPI串行寄存器寫入一個字節來發起一次傳輸。寄存器通過MOSI信號線將字節傳送給從機,從機也將自己的移位寄存器中的內容通過MISO信號線返回給主機。這樣,兩個移位寄存器中的內容就被交換。外設的寫操作和讀操作是同步完成的。如果只進行寫操作,主機只需忽略接收到的字節;反之,若主機要讀取從機的一個字節,就必須發送一個空字節來引發從機的傳輸。?

SPI主要特點有:可以同時發出和接收串行數據;可以當作主機或從機工作;提供頻率可編程時鐘;發送結束中斷標志;寫沖突保護;總線競爭保護等。

SPI總線四種工作方式?SPI?模塊為了和外設進行數據交換,根據外設工作要求,其輸出串行同步時鐘極性和相位可以進行配置,時鐘極性(CPOL)對傳輸協議沒有重大的影響。如果?CPOL=0,串行同步時鐘的空閑狀態為低電平;如果CPOL=1,串行同步時鐘的空閑狀態為高電平。時鐘相位(CPHA)能夠配置用于選擇兩種不同的傳輸協議之一進行數據傳輸。如果CPHA=0,在串行同步時鐘的第一個跳變沿(上升或下降)數據被采樣;如果CPHA=1,在串行同步時鐘的第二個跳變沿(上升或下降)數據被采樣。SPI主模塊和與之通信的外設備時鐘相位和極性應該一致。

不同時鐘相位下的總線數據傳輸時序如圖30.1.2所示:

?

圖30.1.2?不同時鐘相位下的總線傳輸時序(CPHA=0/1)

STM32F4的SPI功能很強大,SPI時鐘最高可以到37.5Mhz,支持DMA,可以配置為SPI協議或者I2S協議(支持全雙工I2S)。

本章,我們將使用STM32F4的SPI來讀取外部SPI FLASH芯片(W25Q128),實現類似上節的功能。這里對SPI我們只簡單介紹一下SPI的使用,STM32F4的SPI詳細介紹請參考《STM32F4xx中文參考手冊》第721頁,27節。然后我們再介紹下SPI ?FLASH芯片。

這節,我們使用STM32F4的SPI1的主模式,下面就來看看SPI1部分的設置步驟吧。SPI相關的庫函數和定義分布在文件stm32f4xx_spi.c以及頭文件stm32f4xx_spi.h中。STM32的主模式配置步驟如下:

1)配置相關引腳的復用功能,使能SPI1時鐘。

我們要用SPI1,第一步就要使能SPI1的時鐘,SPI1的時鐘通過APB2ENR的第12位來設置。其次要設置SPI1的相關引腳為復用(AF5)輸出,這樣才會連接到SPI1上。這里我們使用的是PB3、4、5這3個(SCK.、MISO、MOSI,CS使用軟件管理方式),所以設置這三個為復用IO,復用功能為AF5。

使能SPI1時鐘的方法為:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//使能SPI1時鐘

復用PB3,PB4,PB5為SPI1引腳的方法為:

GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3復用為?SPI1

GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4復用為?SPI1

GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5復用為?SPI1

同時我們要設置相應的引腳模式為復用功能模式:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能

2)初始化SPI1,設置SPI1工作模式等。

這一步全部是通過SPI1_CR1來設置,我們設置SPI1為主機模式,設置數據格式為8位,然后通過CPOL和CPHA位來設置SCK時鐘極性及采樣方式。并設置SPI1的時鐘頻率(最大37.5Mhz),以及數據的格式(MSB在前還是LSB在前)。在庫函數中初始化SPI的函數為:

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

跟其他外設初始化一樣,第一個參數是SPI標號,這里我們是使用的SPI1。下面我們來看看第二個參數結構體類型SPI_InitTypeDef的定義:

typedef struct

{

? uint16_t SPI_Direction;

? uint16_t SPI_Mode;

? uint16_t SPI_DataSize;

? uint16_t SPI_CPOL;

? uint16_t SPI_CPHA;

? uint16_t SPI_NSS;??

? uint16_t SPI_BaudRatePrescaler;?

? uint16_t SPI_FirstBit;???

? uint16_t SPI_CRCPolynomial;

}SPI_InitTypeDef;

結構體成員變量比較多,接下來我們簡單講解一下:

第一個參數SPI_Direction是用來設置SPI的通信方式,可以選擇為半雙工,全雙工,以及串行發和串行收方式,這里我們選擇全雙工模式SPI_Direction_2Lines_FullDuplex。

第二個參數SPI_Mode用來設置SPI的主從模式,這里我們設置為主機模式SPI_Mode_Master,當然有需要你也可以選擇為從機模式SPI_Mode_Slave。

第三個參數SPI_DataSiz為8位還是16位幀格式選擇項,這里我們是8位傳輸,選擇SPI_DataSize_8b。

第四個參數SPI_CPOL用來設置時鐘極性,我們設置串行同步時鐘的空閑狀態為高電平所以我們選擇SPI_CPOL_High。

第五個參數SPI_CPHA用來設置時鐘相位,也就是選擇在串行同步時鐘的第幾個跳變沿(上升或下降)數據被采樣,可以為第一個或者第二個條邊沿采集,這里我們選擇第二個跳變沿,所以選擇SPI_CPHA_2Edge

第六個參數SPI_NSS設置NSS信號由硬件(NSS管腳)還是軟件控制,這里我們通過軟件控制NSS關鍵,而不是硬件自動控制,所以選擇SPI_NSS_Soft。

第七個參數SPI_BaudRatePrescaler很關鍵,就是設置SPI波特率預分頻值也就是決定SPI的時鐘的參數,從2分頻到256分頻8個可選值,初始化的時候我們選擇256分頻值SPI_BaudRatePrescaler_256,?傳輸速度為84M/256=328.125KHz。

第八個參數SPI_FirstBit設置數據傳輸順序是MSB位在前還是LSB位在前,,這里我們選擇SPI_FirstBit_MSB高位在前。

第九個參數SPI_CRCPolynomial是用來設置CRC校驗多項式,提高通信可靠性,大于1即可。

設置好上面9個參數,我們就可以初始化SPI外設了。初始化的范例格式為:

SPI_InitTypeDef? SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;? //雙線雙向全雙工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;??????????? //主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;? // SPI發送接收8位幀結構

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步時鐘的空閑狀態為高電平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;//第二個跳變沿數據被采樣

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;??? //NSS信號由軟件控制

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //預分頻256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;? //數據傳輸從MSB位開始

SPI_InitStructure.SPI_CRCPolynomial = 7;?????? //CRC值計算的多項式

SPI_Init(SPI2, &SPI_InitStructure);? //根據指定的參數初始化外設SPIx寄存器

3)使能SPI1

這一步通過SPI1_CR1的bit6來設置,以啟動SPI1,在啟動之后,我們就可以開始SPI通訊了。庫函數使能SPI1的方法為:

SPI_Cmd(SPI1, ENABLE); //使能SPI1外設

4)SPI傳輸數據

通信接口當然需要有發送數據和接受數據的函數,固件庫提供的發送數據函數原型為:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

這個函數很好理解,往SPIx數據寄存器寫入數據?Data,從而實現發送。

固件庫提供的接受數據函數原型為:

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx)?;

這個函數也不難理解,從SPIx數據寄存器讀出接受到的數據。

5)查看SPI傳輸狀態

在SPI傳輸過程中,我們經常要判斷數據是否傳輸完成,發送區是否為空等等狀態,這是通過函數SPI_I2S_GetFlagStatus實現的,這個函數很簡單就不詳細講解,判斷發送是否完成的方法是:

SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);

SPI1的使用就介紹到這里,接下來介紹一下W25Q128。W25Q128是華邦公司推出的大容量SPI FLASH產品,W25Q128的容量為128Mb,該系列還有W25Q80/16/32/64等。ALIENTEK所選擇的W25Q128容量為128Mb,也就是16M字節。

W25Q128將16M的容量分為256個塊(Block),每個塊大小為64K字節,每個塊又分為16個扇區(Sector),每個扇區4K個字節。W25Q128的最小擦除單位為一個扇區,也就是每次必須擦除4K個字節。這樣我們需要給W25Q128開辟一個至少4K的緩存區,這樣對SRAM要求比較高,要求芯片必須有4K以上SRAM才能很好的操作。

W25Q128的擦寫周期多達10W次,具有20年的數據保存期限,支持電壓為2.7~3.6V,W25Q128支持標準的SPI,還支持雙輸出/四輸出的SPI,最大SPI時鐘可以到80Mhz(雙輸出時相當于160Mhz,四輸出時相當于320M),更多的W25Q128的介紹,請參考W25Q128的DATASHEET。

30.2?硬件設計

本章實驗功能簡介:開機的時候先檢測W25Q128是否存在,然后在主循環里面檢測兩個按鍵,其中1個按鍵(KEY1)用來執行寫入W25Q128的操作,另外一個按鍵(KEY0)用來執行讀出操作,在TFTLCD模塊上顯示相關信息。同時用DS0提示程序正在運行。

所要用到的硬件資源如下:

1)??指示燈DS0

2)??KEY_UP和KEY1按鍵

3)?TFTLCD模塊

4)?SPI

5)?W25Q128

這里只介紹W25Q128與STM32F4的連接,板上的W25Q128是直接連在STM32F4的SPI1上的,連接關系如圖30.2.1所示:

?

圖30.2.1?STM32F4與W25Q128連接電路圖

這里,我們的F_CS是連接在PB14上面的,另外要特別注意:W25Q128和NRF24L01共用SPI1,所以這兩個器件在使用的時候,必須分時復用(通過片選控制)才行。

30.3?軟件設計

打開我們光盤的SPI實驗工程,可以看到我們加入了spi.c,flash.c文件以及頭文件spi.h和flash.h,同時引入了庫函數文件stm32f4xx_spi.c文件以及頭文件stm32f4xx_spi.h。

打開spi.c文件,看到如下代碼:

//以下是SPI模塊的初始化代碼,配置成主機模式?????????????????????????????????????????? ??

//SPI口初始化

//這里針是對SPI1的初始化

void SPI1_Init(void)

{????

? GPIO_InitTypeDef? GPIO_InitStructure;

? SPI_InitTypeDef? SPI_InitStructure;

??????

? RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);//使能GPIOA時鐘

? RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);//?使能SPI1時鐘

?

? //GPIOFB3,4,5初始化設置:?復用功能輸出??

? GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;//PB3~5

? GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//復用功能

? GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽輸出

? GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz

? GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉

? GPIO_Init(GPIOB, &GPIO_InitStructure);//?初始化

?

? //配置引腳復用映射

GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3復用為?SPI1

GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1); //PB4復用為?SPI1

GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1); //PB5復用為?SPI1

?

//這里只針對SPI口初始化

RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//復位SPI1

RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止復位SPI1

?

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;? //設置SPI全雙工

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;??????? //設置SPI工作模式:主SPI

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;???? //設置SPI的數據大小: 8位幀結構

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;//串行同步時鐘的空閑狀態為高電平

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //數據捕獲于第二個時鐘沿

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; ? //NSS信號由硬件管理

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;??? //預分頻256

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //數據傳輸從MSB位開始

SPI_InitStructure.SPI_CRCPolynomial = 7;??? //CRC值計算的多項式

SPI_Init(SPI1, &SPI_InitStructure); //根據指定的參數初始化外設SPIx寄存器

?

SPI_Cmd(SPI1, ENABLE); //使能SPI1

SPI1_ReadWriteByte(0xff);//啟動傳輸??????????

}??

//SPI1速度設置函數

//SPI速度=fAPB2/分頻系數

//入口參數范圍:@ref SPI_BaudRate_Prescaler

//SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256

//fAPB2時鐘一般為84Mhz:

void SPI1_SetSpeed(u8 SPI_BaudRatePrescaler)

{

???assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判斷有效性

?????? SPI1->CR1&=0XFFC7;//位3-5清零,用來設置波特率

?????? SPI1->CR1|=SPI_BaudRatePrescaler;?? //設置SPI1速度

?????? SPI_Cmd(SPI1,ENABLE); //使能SPI1

}

//SPI1?讀寫一個字節

//TxData:要寫入的字節

//返回值:讀取到的字節

u8 SPI1_ReadWriteByte(u8 TxData)

{??????????? ?????????????????? ?

?

? while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET){}//等待發送區空?

SPI_I2S_SendData(SPI1, TxData); //通過外設SPIx發送一個byte??數據

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET){} //等待接收完

return SPI_I2S_ReceiveData(SPI1); //返回通過SPIx最近接收的數據??????

???????????? ???

}

此部分代碼主要初始化SPI,這里我們選擇的是SPI1,所以在SPI1_Init函數里面,其相關的操作都是針對SPI1的,其初始化步驟和我們上面介紹的一樣。在初始化之后,我們就可以開始使用SPI1了,這里特別注意,SPI初始化函數的最后有一個啟動傳輸,這句話最大的作用就是維持MOSI為高電平,而且這句話也不是必須的,可以去掉。

在SPI1_Init函數里面,把SPI1的頻率設置成了最低(84Mhz,256分頻)。在外部函數里面,我們通過SPI1_SetSpeed來設置SPI1的速度,而我們的數據發送和接收則是通過SPI1_ReadWriteByte函數來實現的。

接下來我們來看看w25qxx.c文件內容。由于篇幅所限,詳細代碼,這里就不貼出了。我們僅介紹幾個重要的函數,首先是W25QXX_Read函數,該函數用于從W25Q128的指定地址讀出指定長度的數據。其代碼如下:? ???

//讀取SPI FLASH?

//在指定地址開始讀取指定長度的數據

//pBuffer:數據存儲區

//ReadAddr:開始讀取的地址(24bit)

//NumByteToRead:要讀取的字節數(最大65535)

void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)??

{

????? u16 i;?? ??????????????????????????????????????????????????????????????? ????

?????? W25QXX_CS=0;???????????????????????? //使能器件??

??? SPI1_ReadWriteByte(W25X_ReadData);????? //發送讀取命令??

??? SPI1_ReadWriteByte((u8)((ReadAddr)>>16));? //發送24bit地址???

??? SPI1_ReadWriteByte((u8)((ReadAddr)>>8));??

??? SPI1_ReadWriteByte((u8)ReadAddr);??

??? for(i=0;i<NumByteToRead;i++)

?????? {

??????? pBuffer=SPI1_ReadWriteByte(0XFF);?? //循環讀數?

??? }

?????? W25QXX_CS=1;? ??????????????????????????? ?????????? ??????

}

由于W25Q128支持以任意地址(但是不能超過W25Q128的地址范圍)開始讀取數據,所以,這個代碼相對來說就比較簡單了,在發送24位地址之后,程序就可以開始循環讀數據了,其地址會自動增加的,不過要注意,不能讀的數據超過了W25Q128的地址范圍哦!否則讀出來的數據,就不是你想要的數據了。

有讀的函數,當然就有寫的函數了,接下來,我們介紹W25QXX_Write這個函數,該函數的作用與W25QXX_Flash_Read的作用類似,不過是用來寫數據到W25Q128里面的,代碼如下:

//寫SPI FLASH?

//在指定地址開始寫入指定長度的數據

//該函數帶擦除操作!

//pBuffer:數據存儲區? WriteAddr:開始寫入的地址(24bit)????????????????????????????????????????

//NumByteToWrite:要寫入的字節數(最大65535)??

u8 W25QXX_BUFFER[4096];???????????

void W25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)??

{

?????? u32 secpos;

?????? u16 secoff; u16 secremain; u16 i;

?????? u8 * W25QXX_BUF;??? ?

?? ? W25QXX_BUF=W25QXX_BUFFER; ????

????? secpos=WriteAddr/4096;//扇區地址?

?????? secoff=WriteAddr%4096;//在扇區內的偏移

?????? secremain=4096-secoff;//扇區剩余空間大小??

????? //printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//測試用

????? if(NumByteToWrite<=secremain)secremain=NumByteToWrite;//不大于4096個字節

?????? while(1)

?????? {????

????????????? W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//讀出整個扇區的內容

????????????? for(i=0;i<secremain;i++)//校驗數據

????????????? {

???????????????????? if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除? ?? ??

????????????? }

????????????? if(i<secremain)//需要擦除

????????????? {

???????????????????? W25QXX_Erase_Sector(secpos);//擦除這個扇區

???????????????????? for(i=0;i<secremain;i++)?????? ?? //復制

???????????????????? {

??????????????????????????? W25QXX_BUF[i+secoff]=pBuffer;? ?

???????????????????? }

???????????????????? W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//寫入整個扇區

????????????? }else W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//已擦除的,直接寫?????? ??

????????????? if(NumByteToWrite==secremain)break;//寫入結束了

????????????? else//寫入未結束

????????????? {

???????????????????? secpos++;?????????????????????????????????? //扇區地址增1

???????????????????? secoff=0;??????????????????????????????????? //偏移位置為0 ??? ?

????????????? ?? ? pBuffer+=secremain;? ??????????????? //指針偏移

???????????????????? WriteAddr+=secremain;?????????????? //寫地址偏移? ??

????????????? ?? ? NumByteToWrite-=secremain;????? //字節數遞減

???????????????????? if(NumByteToWrite>4096)secremain=4096; //下一個扇區還是寫不完

???????????????????? else secremain=NumByteToWrite;?????????????? //下一個扇區可以寫完了

????????????? }????

?????? };

}

該函數可以在W25Q128的任意地址開始寫入任意長度(必須不超過W25Q128的容量)的數據。我們這里簡單介紹一下思路:先獲得首地址(WriteAddr)所在的扇區,并計算在扇區內的偏移,然后判斷要寫入的數據長度是否超過本扇區所剩下的長度,如果不超過,再先看看是否要擦除,如果不要,則直接寫入數據即可,如果要則讀出整個扇區,在偏移處開始寫入指定長度的數據,然后擦除這個扇區,再一次性寫入。當所需要寫入的數據長度超過一個扇區的長度的時候,我們先按照前面的步驟把扇區剩余部分寫完,再在新扇區內執行同樣的操作,如此循環,直到寫入結束。這里我們還定義了一個W25QXX_BUFFER的全局變量,用于擦除時緩存扇區內的數據。

其他的代碼就比較簡單了,我們這里不介紹了。對于頭文件w25qxx.h,這里面就定義了一些與W25Q128操作相關的命令和函數(部分省略了),這些命令在W25Q128的數據手冊上都有詳細的介紹,感興趣的讀者可以參考該數據手冊。

最后,我們看看main函數,代碼如下:

//要寫入到W25Q128的字符串數組

const u8 TEXT_Buffer[]={"Explorer STM32F4 SPI TEST"};

#define SIZE sizeof(TEXT_Buffer)?????

int main(void)

{???????????

u8 key, datatemp[SIZE];

?????? u16 i=0;

?????? u32 FLASH_SIZE;

?????? NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//設置系統中斷優先級分組2

?????? delay_init(168);? //初始化延時函數

?????? uart_init(115200);???????? //初始化串口波特率為115200????

?????? LED_Init();???????????????????????? //初始化LED

????? LCD_Init();???????????????????????? //LCD初始化

?????? KEY_Init(); ?????????????????????? //按鍵初始化?

?????? W25QXX_Init();????????????????? //W25QXX初始化

????? POINT_COLOR=RED;

?????? LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");??????

?????? LCD_ShowString(30,70,200,16,16,"SPI TEST");??????

?????? LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");

?????? LCD_ShowString(30,110,200,16,16,"2014/5/7");??????

?????? LCD_ShowString(30,130,200,16,16,"KEY1:Write? KEY0:Read");? //顯示提示信息??????????? ?????? while(W25QXX_ReadID()!=W25Q128)????? //檢測不到W25Q128

?????? {

????????????? LCD_ShowString(30,150,200,16,16,"W25Q128 Check Failed!");

????????????? delay_ms(500);

????????????? LCD_ShowString(30,150,200,16,16,"Please Check!? ??????");

????????????? delay_ms(500);

????????????? LED0=!LED0;????????????? //DS0閃爍

?????? }

?????? LCD_ShowString(30,150,200,16,16,"W25Q128 Ready!");

?????? FLASH_SIZE=128*1024*1024;?? //FLASH?大小為2M字節

? ??? POINT_COLOR=BLUE;???????????????????? //設置字體為藍色? ?

?????? while(1)

?????? {

????????????? key=KEY_Scan(0);

????????????? if(key==KEY1_PRES)//KEY1按下,寫入W25Q128

????????????? {

???????????????????? LCD_Fill(0,170,239,319,WHITE);//清除半屏???

??????????????????? LCD_ShowString(30,170,200,16,16,"Start Write W25Q128....");

???????????????????? W25QXX_Write((u8*)TEXT_Buffer,FLASH_SIZE-100,SIZE);?????????????

//從倒數第100個地址處開始,寫入SIZE長度的數據

???????????????????? LCD_ShowString(30,170,200,16,16,"W25Q128 Write Finished!");//提示完成

????????????? }

????????????? if(key==KEY0_PRES)//KEY0按下,讀取字符串并顯示

????????????? {

??????????????????? LCD_ShowString(30,170,200,16,16,"Start Read W25Q128.... ");

???????????????????? W25QXX_Read(datatemp,FLASH_SIZE-100,SIZE);?

//從倒數第100個地址處開始,讀出SIZE個字節

???????????????????? LCD_ShowString(30,170,200,16,16,"The Data Readed Is:?? ");?????? //提示傳送完成

???????????????????? LCD_ShowString(30,190,200,16,16,datatemp);?? //顯示讀到的字符串

????????????? }

????????????? i++;

????????????? delay_ms(10);

????????????? if(i==20)

????????????? {

???????????????????? LED0=!LED0;//提示系統正在運行????

???????????????????? i=0;

????????????? }??????????? ??

?????? }??????

}

這部分代碼和IIC實驗那部分代碼大同小異,我們就不多說了,實現的功能就和IIC差不多,不過此次寫入和讀出的是SPI FLASH,而不是EEPROM。

30.4?下載驗證

在代碼編譯成功之后,我們通過下載代碼到ALIENTEK探索者STM32F4開發板上,通過先按KEY1按鍵寫入數據,然后按KEY0讀取數據,得到如圖30.4.1所示:

?

圖30.4.1 SPI實驗程序運行效果圖

伴隨DS0的不停閃爍,提示程序在運行。程序在開機的時候會檢測W25Q128是否存在,如果不存在則會在TFTLCD模塊上顯示錯誤信息,同時DS0慢閃。大家可以通過跳線帽把PB4和PB5短接就可以看到報錯了。

?

?實驗詳細手冊和源碼下載地址:http://www.openedv.com/posts/list/41586.htm?

?

正點原子探索者STM32F407開發板購買地址:http://item.taobao.com/item.htm?id=41855882779

??

?

?

探索者實驗連載

實驗25 SPI實驗.zip

?

第三十章 SPI 實驗-STM32F4開發指南-正點原子探索者STM32開發板.pdf

?

http://openedv.com/thread-43360-1-1.html

總結

以上是生活随笔為你收集整理的【正点原子探索者STM32F407开发板例程连载+教学】第30章 SPI通信实验的全部內容,希望文章能夠幫你解決所遇到的問題。

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