娇小w搡bbbb搡bbb,《第一次の人妻》,中国成熟妇女毛茸茸,边啃奶头边躁狠狠躁视频免费观看

歷史上的今天

今天是:2025年01月31日(星期五)

2019年01月31日 | 【stm32f407】SPI實驗 驅動W25Q128

發布者:吾道明亮 來源: eefocus關鍵字:stm32f407  SPI實驗  驅動W25Q128 手機看文章 掃描二維碼
隨時隨地手機看文章

一.SPI介紹

SPI 是英語SerialPeripheral interface的縮寫,顧名思義就是串行外圍設備接口。是Motorola首先在其MC68HCXX系列處理器上定義的。SPI接口主要應用在


EEPROM,FLASH,實時時鐘,AD轉換器,還有數字信號處理器數字信號解碼器之間。SPI,是一種高速的,全雙工,同步的通信總線,并且在芯片的


管腳上只占用四根線,節約了芯片的管腳,同時為PCB的布局上節省空間,提供方便,正是出于這種簡單易用的特性,現在越來越多的芯片集成了這種


通信協議,STM32F4也有SPI接口。下面我們看看SPI的內部簡明圖


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


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


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


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


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


從圖中可以看出,主機和從機都有一個串行移位寄存器,主機通過向它的SPI串行寄存器


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


SPI主要特點有:可以同時發出和接收串行數據;可以當作主機或從機工作;提供頻率可


編程時鐘;發送結束中斷標志;寫沖突保護;總線競爭保護等。


SPI總線四種工作方式 SPI 模塊為了和外設進行數據交換,根據外設工作要求,其輸出串


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


TM32F4的SPI功能很強大,SPI時鐘最高可以到37.5Mhz,支持DMA,可以配置為SPI


協議或者I2S協議(支持全雙工I2S)。


二.庫函數應用

SPI


相關的庫函數和定義分布在文件stm32f4xx_spi.c以及頭文件stm32f4xx_spi.h中。STM32的主模式配置步驟如下:


拿SPI1舉例

1)  配置相關引腳的復用功能,使能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的定義:


typedefstruct


{


uint16_tSPI_Direction;


uint16_tSPI_Mode;


uint16_tSPI_DataSize;


uint16_tSPI_CPOL;


uint16_tSPI_CPHA;


uint16_tSPI_NSS;  


uint16_tSPI_BaudRatePrescaler;


uint16_tSPI_FirstBit;


uint16_tSPI_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);


三.庫函數應用源碼

voidSPI1_Init(void)

{        

  GPIO_InitTypeDef  GPIO_InitStructure;

  SPI_InitTypeDef  SPI_InitStructure;

         

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB時鐘

  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設置為雙線雙向全雙工

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

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

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

         SPI_InitStructure.SPI_CPHA =SPI_CPHA_2Edge;   //串行同步時鐘的第二個跳變沿(上升或下降)數據被采樣

         SPI_InitStructure.SPI_NSS =SPI_NSS_Soft;               //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制

         SPI_InitStructure.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_256;            //定義波特率預分頻的值:波特率預分頻值為256

         SPI_InitStructure.SPI_FirstBit =SPI_FirstBit_MSB;         //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始

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

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

 

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

 

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

}   

//SPI1速度設置函數

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

//@refSPI_BaudRate_Prescaler:SPI_BaudRatePrescaler_2~SPI_BaudRatePrescaler_256  

//fAPB2時鐘一般為84Mhz:

voidSPI1_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:要寫入的字節

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

u8SPI1_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){} //等待接收完一個byte  

 

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

                     

}

四.W25Q128介紹

W25Q128是華邦公司推出的大容量SPI FLASH產品,W25Q128的容量為128Mb,該系列還有W25Q80/16/32/64等。


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。


五.SPI操作W25Q128

1.      Read Manufacturer / Device ID(90h)


  

程序和時序圖一一對應

程序意思為:先片選,選中W25Q128,然后發送命令和address,然后再讀出ID,再取消片選


2. Sector Erase (20h)


對應的時序圖為


程序的意思是片選25Q128,然后發送命令和地址,然后再取消片選,等待擦除完成


3.Read Data (03h)


對應的時序圖為:

只介紹這三個,可以自行參照datasheet讀源碼,后續附上源碼


六.操作W25Q128源碼

W25qxx.h


#ifndef__W25QXX_H

#define__W25QXX_H                            

#include"sys.h"  

 

//W25X系列/Q系列芯片列表          

//W25Q80  ID 0XEF13

//W25Q16  ID 0XEF14

//W25Q32  ID 0XEF15

//W25Q64  ID 0XEF16         

//W25Q128ID  0XEF17 

#defineW25Q80     0XEF13    

#defineW25Q16     0XEF14

#defineW25Q32     0XEF15

#defineW25Q64     0XEF16

#defineW25Q128    0XEF17

 

externu16 W25QXX_TYPE;                                           //定義W25QXX芯片型號                   

 

#define     W25QXX_CS             PBout(14)                //W25QXX的片選信號

 

//////////////////////////////////////////////////////////////////////////////////

//指令表

#defineW25X_WriteEnable              0x06 

#defineW25X_WriteDisable            0x04 

#defineW25X_ReadStatusReg                 0x05 

#defineW25X_WriteStatusReg                0x01 

#defineW25X_ReadData                           0x03

#defineW25X_FastReadData          0x0B 

#defineW25X_FastReadDual           0x3B 

#defineW25X_PageProgram           0x02 

#defineW25X_BlockErase                          0xD8

#defineW25X_SectorErase              0x20 

#defineW25X_ChipErase                           0xC7

#defineW25X_PowerDown                       0xB9 

#defineW25X_ReleasePowerDown        0xAB 

#defineW25X_DeviceID                    0xAB 

#defineW25X_ManufactDeviceID  0x90 

#defineW25X_JedecDeviceID                   0x9F 

 

voidW25QXX_Init(void);

u16  W25QXX_ReadID(void);                              //讀取FLASH ID

u8     W25QXX_ReadSR(void);                          //讀取狀態寄存器 

voidW25QXX_Write_SR(u8 sr);                       //寫狀態寄存器

voidW25QXX_Write_Enable(void);                 //寫使能 

voidW25QXX_Write_Disable(void);                  //寫保護

voidW25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);

voidW25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);   //讀取flash

voidW25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//寫入flash

voidW25QXX_Erase_Chip(void);                //整片擦除

voidW25QXX_Erase_Sector(u32 Dst_Addr);  //扇區擦除

voidW25QXX_Wait_Busy(void);                   //等待空閑

voidW25QXX_PowerDown(void);           //進入掉電模式

voidW25QXX_WAKEUP(void);                             //喚醒

#endif

W25qxx.c


#include"w25qxx.h" 

#include"spi.h"

#include"delay.h"      

#include"usart.h"   

u16W25QXX_TYPE=W25Q128;       //默認是W25Q128

//4Kbytes為一個Sector

//16個扇區為1個Block

//W25Q128

//容量為16M字節,共有128個Block,4096個Sector 

                                                                                                                          

//初始化SPI FLASH的IO口

voidW25QXX_Init(void)

  GPIO_InitTypeDef  GPIO_InitStructure;

 

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB時鐘

  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);//使能GPIOG時鐘

 

          //GPIOB14

  GPIO_InitStructure.GPIO_Pin =GPIO_Pin_14;//PB14

  GPIO_InitStructure.GPIO_Mode =GPIO_Mode_OUT;//輸出

  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_InitStructure.GPIO_Pin =GPIO_Pin_7;//PG7

  GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化

 

         GPIO_SetBits(GPIOG,GPIO_Pin_7);//PG7輸出1,防止NRF干擾SPI FLASH的通信 

         W25QXX_CS=1;                           //SPI FLASH不選中

         SPI1_Init();                                         //初始化SPI

         SPI1_SetSpeed(SPI_BaudRatePrescaler_4);             //設置為21M時鐘,高速模式 

         W25QXX_TYPE=W25QXX_ReadID();        //讀取FLASH ID.

}  

 

//讀取W25QXX的狀態寄存器

//BIT7  6  5   4   3  2   1   0

//SPR   RV  TBBP2 BP1 BP0 WEL BUSY

//SPR:默認0,狀態寄存器保護位,配合WP使用

//TB,BP2,BP1,BP0:FLASH區域寫保護設置

//WEL:寫使能鎖定

//BUSY:忙標記位(1,忙;0,空閑)

//默認:0x00

u8W25QXX_ReadSR(void)   

{  

         u8 byte=0;   

         W25QXX_CS=0;                            //使能器件   

         SPI1_ReadWriteByte(W25X_ReadStatusReg);    //發送讀取狀態寄存器命令    

         byte=SPI1_ReadWriteByte(0Xff);             //讀取一個字節  

         W25QXX_CS=1;                            //取消片選     

         return byte;   

//寫W25QXX狀態寄存器

//只有SPR,TB,BP2,BP1,BP0(bit7,5,4,3,2)可以寫!!!

voidW25QXX_Write_SR(u8 sr)   

{   

         W25QXX_CS=0;                            //使能器件   

         SPI1_ReadWriteByte(W25X_WriteStatusReg);   //發送寫取狀態寄存器命令    

         SPI1_ReadWriteByte(sr);               //寫入一個字節  

         W25QXX_CS=1;                            //取消片選             

}   

//W25QXX寫使能   

//將WEL置位   

voidW25QXX_Write_Enable(void)   

{

         W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_WriteEnable);      //發送寫使能  

         W25QXX_CS=1;                            //取消片選             

//W25QXX寫禁止   

//將WEL清零  

voidW25QXX_Write_Disable(void)   

{  

         W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_WriteDisable);     //發送寫禁止指令    

         W25QXX_CS=1;                            //取消片選             

}                

//讀取芯片ID

//返回值如下:                                        

//0XEF13,表示芯片型號為W25Q80  

//0XEF14,表示芯片型號為W25Q16    

//0XEF15,表示芯片型號為W25Q32  

//0XEF16,表示芯片型號為W25Q64

//0XEF17,表示芯片型號為W25Q128    

u16W25QXX_ReadID(void)

{

         u16 Temp = 0;    

         W25QXX_CS=0;                                        

         SPI1_ReadWriteByte(0x90);//發送讀取ID命令       

         SPI1_ReadWriteByte(0x00);     

         SPI1_ReadWriteByte(0x00);     

         SPI1_ReadWriteByte(0x00);                               

         Temp|=SPI1_ReadWriteByte(0xFF)<<8;  

         Temp|=SPI1_ReadWriteByte(0xFF);        

         W25QXX_CS=1;                                        

         return Temp;

}                 

//讀取SPIFLASH  

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

//pBuffer:數據存儲區

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

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

voidW25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   

        u16i;                                                                                            

         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

         { 

       pBuffer[i]=SPI1_ReadWriteByte(0XFF);  //循環讀數  

    }

         W25QXX_CS=1;                                                    

}  

//SPI在一頁(0~65535)內寫入少于256個字節的數據

//在指定地址開始寫入最大256字節的數據

//pBuffer:數據存儲區

//WriteAddr:開始寫入的地址(24bit)

//NumByteToWrite:要寫入的字節數(最大256),該數不應該超過該頁的剩余字節數!!!  

voidW25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)

{

        u16i;  

    W25QXX_Write_Enable();                  //SET WEL 

         W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_PageProgram);      //發送寫頁命令   

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

   SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   

    SPI1_ReadWriteByte((u8)WriteAddr);   

   for(i=0;i

         W25QXX_CS=1;                            //取消片選 

         W25QXX_Wait_Busy();                                            //等待寫入結束

//無檢驗寫SPI FLASH

//必須確保所寫的地址范圍內的數據全部為0XFF,否則在非0XFF處寫入的數據將失敗!

//具有自動換頁功能 

//在指定地址開始寫入指定長度的數據,但是要確保地址不越界!

//pBuffer:數據存儲區

//WriteAddr:開始寫入的地址(24bit)

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

//CHECKOK

voidW25QXX_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   

{                                           

         u16 pageremain;        

         pageremain=256-WriteAddr%256; //單頁剩余的字節數                          

         if(NumByteToWrite<=pageremain)pageremain=NumByteToWrite;//不大于256個字節

         while(1)

         {           

                   W25QXX_Write_Page(pBuffer,WriteAddr,pageremain);

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

                 else //NumByteToWrite>pageremain

                   {

                            pBuffer+=pageremain;

                            WriteAddr+=pageremain;        

 

                            NumByteToWrite-=pageremain;                          //減去已經寫入了的字節數

                            if(NumByteToWrite>256)pageremain=256;//一次可以寫入256個字節

                            elsepageremain=NumByteToWrite;        //不夠256個字節了

                   }

         };           

//寫SPIFLASH  

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

//該函數帶擦除操作!

//pBuffer:數據存儲區

//WriteAddr:開始寫入的地址(24bit)                                                    

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

u8W25QXX_BUFFER[4096];             

voidW25QXX_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   

         u32 secpos;

         u16 secoff;

         u16 secremain;           

        u16i;    

         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

                   {

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

                   }

                   if(i

                   {

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

                            for(i=0;i

                            {

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

                            }

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

 

                   }elseW25QXX_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;  //下一個扇區還是寫不完

                            elsesecremain=NumByteToWrite;                   //下一個扇區可以寫完了

                   }        

         };       

}

//擦除整個芯片                

//等待時間超長...

voidW25QXX_Erase_Chip(void)   

{                                   

    W25QXX_Write_Enable();                  //SET WEL 

    W25QXX_Wait_Busy();   

       W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_ChipErase);        //發送片擦除命令  

         W25QXX_CS=1;                            //取消片選             

         W25QXX_Wait_Busy();                                        //等待芯片擦除結束

}   

//擦除一個扇區

//Dst_Addr:扇區地址 根據實際容量設置

//擦除一個山區的最少時間:150ms

voidW25QXX_Erase_Sector(u32 Dst_Addr)   

{  

         //監視falsh擦除情況,測試用   

        printf("fe:%x\r\n",Dst_Addr);   

        Dst_Addr*=4096;

    W25QXX_Write_Enable();                  //SET WEL           

    W25QXX_Wait_Busy();   

       W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_SectorErase);      //發送扇區擦除指令 

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

   SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   

    SPI1_ReadWriteByte((u8)Dst_Addr);  

         W25QXX_CS=1;                            //取消片選             

    W25QXX_Wait_Busy();                                        //等待擦除完成

}  

//等待空閑

voidW25QXX_Wait_Busy(void)   

{   

         while((W25QXX_ReadSR()&0x01)==0x01);   // 等待BUSY位清空

}  

//進入掉電模式

void W25QXX_PowerDown(void)   

       W25QXX_CS=0;                            //使能器件   

    SPI1_ReadWriteByte(W25X_PowerDown);        //發送掉電命令  

         W25QXX_CS=1;                            //取消片選             

    delay_us(3);                               //等待TPD  

}   

//喚醒

voidW25QXX_WAKEUP(void)   

{  

       W25QXX_CS=0;                            //使能器件   

   SPI1_ReadWriteByte(W25X_ReleasePowerDown);   // send W25X_PowerDown command 0xAB   

         W25QXX_CS=1;                            //取消片選             

    delay_us(3);                               //等待TRES1

}   


關鍵字:stm32f407  SPI實驗  驅動W25Q128 引用地址:【stm32f407】SPI實驗 驅動W25Q128

上一篇:【stm32f407】CAN總線
下一篇:【stm32f407】I2C實驗

推薦閱讀

日本面板廠JDI開發了一款玻璃材質電容式指紋傳感器,但產業分析師認為,盡管其TFT指紋傳感器與硅芯片解決方案相較有一些特定優勢,該公司可能太晚切入該市場、投入的資源也太少。為了尋找智能手機以外的新市場,日本面板廠Japan Display Inc. (JDI)開發了一款以透明玻璃為基礎材質的電容式指紋傳感器。對于這家由索尼(Sony)、東芝(Toshiba)與日立(Hitac...
一.內存管理原理內存管理,是指軟件運行時對計算機內存資源的分配和使用的技術。其最主要的目的是如何高效,快速的分配,并且在適當的時候釋放和回收內存資源。內存管理的實現方法有很多種,他們其實最終都是要實現2個函數:malloc和free;malloc函數用于內存申請,free函數用于內存釋放。我們介紹一種比較簡單的辦法來實現:分塊式內存管理。下面我們介...
背景遇到的問題最近在做關于Contiki操作系統在TelosB節點上的一些實驗,但是在編譯一些Contiki樣例的時候遇到了".text"和".rodata"段出現重合無法成功編譯的情況,查閱資料顯示需要更新Instant Contiki中自帶的msp430-gcc來解決這個問題。本文將我的操作過程做一個記錄,希望對讀者有所幫助。硬軟件環境Instant Contiki3.0(Ubuntu 14.04LTS)1. 安裝...
蘋果今天宣布,從 iOS/iPadOS/tvOS 14 的下個 Beta 版本開始,應用追蹤透明度隱私保護策略將會生效。這項要求原定于去年 9 月生效,但蘋果為了給開發者提供更多的準備而推遲了。  在該策略生效之后,所有 iPhone、iPad 和 Apple TV 應用開發者都需要得到用戶的許可,才能在其他應用和網站上追蹤他們的活動,并獲取他們設備的隨機廣...

史海拾趣

問答坊 | AI 解惑

09元器件清單

本帖最后由 paulhyde 于 2014-9-15 09:02 編輯 2009年全國大學生電子設計競賽 基本儀器和主要元器件清單  …

查看全部問答∨

程序中的變量名在編譯之后成了什么?

我一直有個疑惑,我們都知道內存中的數據是分段存儲的,但是我就一直不能理解,程序編譯之后,源程序中的變量名是否也編譯成為數據值了,而且這個變量的指針是由誰指向的,當變量名也被編譯為值的情況下,必然會導致內存的占用,是否就是說,從節約 ...…

查看全部問答∨

得勛章了,高興,新的一年,繼續努力,進著有分!!!!

在eeworld里面有兩個月了,認識了好多朋友,也學到了很多,多謝eeworld的網友們,新的一年,繼續努力------- 宇帆嵌入式交流群1,氣氛很好,能給大家提供一個技術交流群,很高興,嘿嘿!現在正式推出宇帆嵌入式交流群2, 群號61273439,歡迎大家多 ...…

查看全部問答∨

DSP優化

從在DSP上做智能視頻以來,看到很多優化的文檔, 受益非淺,在這根據俺這短短的經驗, 也總結一二。優化分很多方面, 或者說不同的層次,不同方面的考慮的問題和優化方式完全不同, 俺稍微懂點的會慢慢展開, 不懂的繼續學習 ...1)系統2)算法3) ...…

查看全部問答∨

請教定時器TimerB延時問題

void delay_5us(void) { TBCTL = TBSSEL_2 + TBCLR; /*SMCLK, SMCLK為8M, up mode*/ TBCCTL0 = CCIE; TBCCR0=39; /*8,000,000/200,000-1=CCRO。用來確定 PWM周期5us ...…

查看全部問答∨

電動汽車控制器(新能源項目)

國內電動汽車(電動客車)制造商使用的驅動電機主要有三款,1.直流有刷電機(串勵電機和他勵電機)。2.直流永磁無刷電機。3.交流感應電機。三者向比較:直流有刷電機,優點是扭矩大,驅動控制相對簡單,國內的控制器廠家也有不少,缺點是體積 ...…

查看全部問答∨

VxWorks 5.5和6.5區別?

大家好,誰知道VxWorks 5.5 和6.5 之間有啥區別啊?…

查看全部問答∨

求助一個模擬電路分析

     我發的是一個基于ST7538的電力線載波通信模塊的電路圖。     圖上有幾處看的不懂,還請大神們不吝賜教!     如圖:            1.左上部分圖上標的數 ...…

查看全部問答∨
小廣播
設計資源 培訓 開發板 精華推薦

最新單片機文章
何立民專欄 單片機及嵌入式寶典

北京航空航天大學教授,20余年來致力于單片機與嵌入式系統推廣工作。

 
EEWorld訂閱號

 
EEWorld服務號

 
汽車開發圈

 
機器人開發圈

電子工程世界版權所有 京ICP證060456號 京ICP備10001474號-1 電信業務審批[2006]字第258號函 京公網安備 11010802033920號 Copyright ? 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
主站蜘蛛池模板: 布尔津县| 施甸县| 葫芦岛市| 阿克陶县| 吴忠市| 赣榆县| 东乌珠穆沁旗| 梓潼县| 安庆市| 会泽县| 宝应县| 石屏县| 南京市| 商丘市| 开平市| 遵化市| 桃园市| 阿勒泰市| 桂林市| 乌兰察布市| 东至县| 青岛市| 出国| 自治县| 长沙市| 临沂市| 达孜县| 延边| 雷波县| 彩票| 辽宁省| 湘西| 仁寿县| 无极县| 茌平县| 黄石市| 大渡口区| 怀化市| 句容市| 邯郸市| 双城市|