STM32+CubeMX移植SPI協議驅動W25Q16FLash存儲器

STM32+CubeMX移植SPI協議驅動W25Q16FLash存儲器

  • SPI簡介
    • 拓撲結構
    • 時鐘相位(CPHA)和時鐘極性( CPOL)
  • W25Q16簡介
    • 什么是Flash,有什么特點?
    • W25Q16內部塊、扇區、頁的劃分
    • 引腳定義
    • 通訊方式
    • 控制指令
    • 原理圖
  • CubeMX配置
  • 驅動代碼
  • 運行結果

W25Q16 是一種常見的串行閃存(Flash)存儲器芯片,由 Winbond 公司生產。它是一種高性能、低功耗的閃存存儲器,采用的是SPI通訊協議,想要想使用W25Q16,就必須要先會SPI協議。

SPI簡介

SPI(Serial Peripheral Interface)是一種常用的串行通信協議,通常用于在數字集成電路之間進行通信,如微控制器、傳感器、存儲器、顯示器等。SPI 協議定義了一種全雙工、同步的串行數據傳輸方式,它通常由一個主設備(Master)和一個或多個從設備(Slave)組成。

SPI 協議的主要特點包括:

  • 全雙工通信:主設備和從設備可以同時發送和接收數據,這使得 SPI 通信速度較快。

  • 同步通信:通信時鐘由主設備生成,主設備控制數據傳輸的時序。這種同步方式可以提供較高的通信速率。

  • 多從設備支持:SPI 允許主設備與多個從設備進行通信,每個從設備都有一個片選信號(Chip Select),用于選擇要通信的目標設備。

  • 單主多從結構:在標準的 SPI 總線中,通常只有一個主設備,多個從設備。但是,也有一些擴展協議支持多主設備的情況。

  • 串行傳輸:數據在時鐘的邊沿進行傳輸,可以是上升沿或下降沿,取決于設備的配置。

SPI 協議通常由以下幾條信號線組成:

  • SCLK(Serial Clock):時鐘信號,由主設備產生,用于同步數據傳輸。

  • MOSI(Master Out Slave In):主設備輸出、從設備輸入的數據線。

  • MISO(Master In Slave Out):主設備輸入、從設備輸出的數據線。

  • SS/CS(Slave Select/Chip Select):片選信號,由主設備控制,用于選擇要通信的從設備。

由于本篇章的重點是W25Q16芯片介紹,所以這里不做過多的介紹SPI通訊協議,基礎內容可以瀏覽:藍橋杯單片機學習——SPI協議&DS1302實時時鐘,這里介紹以下STM32上SPI的幾個重要概念:

拓撲結構

在這里插入圖片描述

時鐘相位(CPHA)和時鐘極性( CPOL)

  • 時鐘極性( CPOL):在沒有數據傳輸時時鐘線的電平狀態,0表示空閑時低電平,1表示空閑時高電平。
  • 時鐘相位(CPHA:時鐘線在第幾個時鐘邊沿采集數據,0表示在第一個邊沿開始采集,1表示在第二個邊沿開始采集。
  • 不同的時鐘相位和時鐘極性,可以得到不同的SPI工作頻率:
    在這里插入圖片描述

W25Q16簡介

W25Q16 是一種常見的串行閃存(Flash)存儲器芯片,由 Winbond 公司生產。W25Q16采用的是SPI通訊協議,支持STM32的SPI數據傳輸時序0(CPOL = 0 ,CPHA = 0)和模式3(CPOL = 1,CPHA = 1),數據格式是長度為8位,MSB在前,LSB在后。它是一種高性能、低功耗的閃存存儲器,具有以下特點:

  • 存儲容量:W25Q16 芯片的存儲容量為16 Megabit(Mb),相當于2 Megabyte(MB),其中1 Byte = 8 bit。

  • 接口:W25Q16 使用 SPI(Serial Peripheral Interface)串行接口進行通信,這使得它易于與各種微控制器和數字集成電路進行連接。

  • 工作電壓:典型的工作電壓為2.7V 至 3.6V,支持廣泛的供電范圍,適用于各種電源條件下的應用。

  • 工作溫度:W25Q16 在工業級溫度范圍內(-40°C 至 +85°C)可靠工作,適用于工業和商業應用環境。

  • 封裝:W25Q16 芯片通常采用表面貼裝封裝(Surface Mount Package),如8-pin SOIC(Small Outline Integrated Circuit)封裝,方便集成到電路板上。

  • 快速擦除和編程:W25Q16 支持快速擦除和編程操作,使得數據更新和存儲操作更加高效。

  • 多種保護功能:W25Q16 芯片提供了多種保護功能,如寫保護功能、全片擦除保護、寫使能鎖定等,有助于提高數據的安全性和可靠性。

其他型號的Flas芯片,主要差距體現在存儲器大小,比如W25Q32就是存儲容量為32Mb = 4MB

什么是Flash,有什么特點?

  • FLASH是常用的用于儲存數據的半導體器件,它具有容量大,可重復擦寫、按“扇區/塊”擦除、掉電后數據可繼續保存的特性。
  • FLASH是有一個物理特性:只能寫0,不能寫1,寫1靠擦除。
  • FLASH主要有NOR Flash和NAND Flash兩種類型,NOR和NAND是兩種數字門電路。
  • NOR Flash:基于字節讀寫,讀取速度快,獨立地址/數據線,無壞塊,支持XIP,常見的應用有25QXX系列芯片和存儲程序的ROM
  • NAND FLASH:基于塊讀寫,讀取速度稍慢,地址數據線共用,有壞塊,不支持XIP,常見的應用有EMMC、SSD、U盤等

XIP:一般來說,處理器都是在flash上讀取代碼,到RAM里面執行,這樣讀取效率相對會低,而XIP則是支持處理器直接在FLash上讀取程序并執行,可以提高運行效率。

W25Q16內部塊、扇區、頁的劃分

在這里插入圖片描述

  • W25Q16將內部的存儲空間分為了32個塊,每個塊大小64KB
  • 每個塊分為16個扇區,大小為4KB
  • 每個扇區分作16個頁,每個頁為256個字節,頁是Flash讀寫操作的最小單位。
  • W25Q16共有16 * 16 * 32 = 8192個頁

Flash存儲器存在壞塊的情況,當某一個頁損壞而無法讀寫時,可能會導致整個塊讀寫過程中某個數據讀寫異常而無法使用,且這種損壞是不可逆的,換句話來說,一個頁損壞,會導致整個塊無法正常讀寫,只能使用其他塊進行讀寫,導致Flash容量下降。

引腳定義

在這里插入圖片描述

  • /CS:片選信號,低電平有效,用作SPI通訊使用
  • DO(IO1):數據輸出引腳(MISO)
  • /WP(IO2):寫保護引腳,低電平有效
  • GND:接地
  • DI(IO4):數據輸入引腳(MOSI)
  • CLK:SPI時鐘引腳
  • /HOLD(IO3):暫停通訊引腳,高電平有效
  • VCC:電源引腳

通訊方式

控制指令

在對W25Q16進行操作之前,需要先發送指令,以控制W25Q16,以下是所有的指令集:

在這里插入圖片描述
在這里插入圖片描述

通常,我們對W25Q16進行讀寫操作,只需要以下幾條指令即可完成:

在這里插入圖片描述

原理圖

在這里插入圖片描述

CubeMX配置

  1. SPI配置:注意芯片引腳和原理圖對應上。
    在這里插入圖片描述

  2. GPIO配置:配置的是片選信號,使其默認高電平即可。
    在這里插入圖片描述

  3. 定時器配置:用于試下微妙延時,以實現W25Q16的底層代碼時序要求。
    在這里插入圖片描述

  4. 其他部分的配置,自行完成,由于不涉及W25Q16的驅動,所以不做展示。

驅動代碼

關于驅動代碼,這里我是采用的正點原子提供的源碼,然后修改以一部分,用作CubeMX配置移植使用,僅作個人學習使用!!!

  1. norflash.c
#include "norflash.h"
#include "spi.h"
#include "delay.h"
#include "usart.h"
#include "stm32f4xx_hal_gpio.h"
//
//本程序只供學習使用,未經作者許可,不得用于其它任何用途
//ALIENTEK NANO STM32F4開發板
//W25QXX驅動代碼
//正點原子@ALIENTEK
//技術論壇:www.openedv.com
//創建日期:2019/4/23
//版本:V1.0
//版權所有,盜版必究。
//Copyright(C) 廣州市星翼電子科技有限公司 2019-2029
//All rights reserved
//uint16_t NORFLASH_TYPE = NM25Q16; //默認就是NM25Q16//4Kbytes為一個Sector
//16個扇區為1個Block
//W25X16
//容量為2M字節,共有32個Block,512個Sector//初始化SPI FLASH的IO口
void Norflash_Init(void)
{
//    GPIO_InitTypeDef GPIO_Initure;//    __HAL_RCC_GPIOB_CLK_ENABLE();           //使能GPIOB時鐘//    GPIO_Initure.Pin = GPIO_PIN_12;         //PB12
//    GPIO_Initure.Mode = GPIO_MODE_OUTPUT_PP; //推挽輸出
//    GPIO_Initure.Pull = GPIO_PULLUP;        //上拉
//    GPIO_Initure.Speed = GPIO_SPEED_HIGH;   //高速
//    HAL_GPIO_Init(GPIOB, &GPIO_Initure);    //初始化NORFLASH_CS = 1;			              //SPI FLASH不選中
//    SPI2_Init();		   			        //初始化SPI
//    SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_4); //設置為24M時鐘,高速模式NORFLASH_TYPE = Norflash_ReadID();	      //讀取FLASH ID.
}//讀取SPI_FLASH的狀態寄存器
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:默認0,狀態寄存器保護位,配合WP使用
//TB,BP2,BP1,BP0:FLASH區域寫保護設置
//WEL:寫使能鎖定
//BUSY:忙標記位(1,忙;0,空閑)
//默認:0x00
uint8_t Norflash_ReadSR(void)
{uint8_t byte = 0;NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_ReadStatusReg);    //發送讀取狀態寄存器命令byte = SPI2_ReadWriteByte(0Xff);           //讀取一個字節NORFLASH_CS = 1;                          //取消片選return byte;
}
//寫SPI_FLASH狀態寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以寫!!!
void Norflash_Write_SR(uint8_t sr)
{NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_WriteStatusReg);   //發送寫取狀態寄存器命令SPI2_ReadWriteByte(sr);               //寫入一個字節NORFLASH_CS = 1;                          //取消片選
}
//SPI_FLASH寫使能
//將WEL置位
void Norflash_Write_Enable(void)
{NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_WriteEnable);      //發送寫使能NORFLASH_CS = 1;                          //取消片選
}
//SPI_FLASH寫禁止
//將WEL清零
void Norflash_Write_Disable(void)
{NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_WriteDisable);     //發送寫禁止指令NORFLASH_CS = 1;                          //取消片選
}
//讀取芯片ID W25X16的ID:0XEF14
uint16_t Norflash_ReadID(void)
{uint16_t Temp = 0;NORFLASH_CS = 0;SPI2_ReadWriteByte(0x90);//發送讀取ID命令SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);SPI2_ReadWriteByte(0x00);Temp |= SPI2_ReadWriteByte(0xFF) << 8;Temp |= SPI2_ReadWriteByte(0xFF);NORFLASH_CS = 1;return Temp;
}
//讀取SPI FLASH
//在指定地址開始讀取指定長度的數據
//pBuffer:數據存儲區
//ReadAddr:開始讀取的地址(24bit)
//NumByteToRead:要讀取的字節數(最大65535)
void Norflash_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{uint16_t i;NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_ReadData);         //發送讀取命令SPI2_ReadWriteByte((uint8_t)((ReadAddr) >> 16)); //發送24bit地址SPI2_ReadWriteByte((uint8_t)((ReadAddr) >> 8));SPI2_ReadWriteByte((uint8_t)ReadAddr);for (i = 0; i < NumByteToRead; i++){pBuffer[i] = SPI2_ReadWriteByte(0XFF); //循環讀數}NORFLASH_CS = 1;                          //取消片選
}
//SPI在一頁(0~65535)內寫入少于256個字節的數據
//在指定地址開始寫入最大256字節的數據
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大256),該數不應該超過該頁的剩余字節數!!!
void Norflash_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint16_t i;Norflash_Write_Enable();                  //SET WELNORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_PageProgram);      //發送寫頁命令SPI2_ReadWriteByte((uint8_t)((WriteAddr) >> 16)); //發送24bit地址SPI2_ReadWriteByte((uint8_t)((WriteAddr) >> 8));SPI2_ReadWriteByte((uint8_t)WriteAddr);for (i = 0; i < NumByteToWrite; i++)SPI2_ReadWriteByte(pBuffer[i]); //循環寫數NORFLASH_CS = 1;                          //取消片選Norflash_Wait_Busy();					   //等待寫入結束
}
//無檢驗寫SPI FLASH
//必須確保所寫的地址范圍內的數據全部為0XFF,否則在非0XFF處寫入的數據將失敗!
//具有自動換頁功能
//在指定地址開始寫入指定長度的數據,但是要確保地址不越界!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
//CHECK OK
void Norflash_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint16_t pageremain;pageremain = 256 - WriteAddr % 256; //單頁剩余的字節數if (NumByteToWrite <= pageremain)pageremain = NumByteToWrite; //不大于256個字節while (1){Norflash_Write_Page(pBuffer, WriteAddr, pageremain);if (NumByteToWrite == pageremain)break; //寫入結束了else //NumByteToWrite>pageremain{pBuffer += pageremain;WriteAddr += pageremain;NumByteToWrite -= pageremain;			 //減去已經寫入了的字節數if (NumByteToWrite > 256)pageremain = 256; //一次可以寫入256個字節else pageremain = NumByteToWrite; 	 //不夠256個字節了}};
}
//寫SPI FLASH
//在指定地址開始寫入指定長度的數據
//該函數帶擦除操作!
//pBuffer:數據存儲區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的字節數(最大65535)
uint8_t W25QXX_BUFFER[4096];
void Norflash_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{uint32_t secpos;uint16_t secoff;uint16_t secremain;uint16_t i;secpos = WriteAddr / 4096; //扇區地址 0~511 for w25x16secoff = WriteAddr % 4096; //在扇區內的偏移secremain = 4096 - secoff; //扇區剩余空間大小if (NumByteToWrite <= secremain)secremain = NumByteToWrite; //不大于4096個字節while (1){Norflash_Read(W25QXX_BUFFER, secpos * 4096, 4096); //讀出整個扇區的內容for (i = 0; i < secremain; i++) //校驗數據{if (W25QXX_BUFFER[secoff + i] != 0XFF)break; //需要擦除}if (i < secremain) //需要擦除{Norflash_Erase_Sector(secpos);//擦除這個扇區for (i = 0; i < secremain; i++)	 //復制{W25QXX_BUFFER[i + secoff] = pBuffer[i];}Norflash_Write_NoCheck(W25QXX_BUFFER, secpos * 4096, 4096); //寫入整個扇區}else Norflash_Write_NoCheck(pBuffer, WriteAddr, secremain); //寫已經擦除了的,直接寫入扇區剩余區間.if (NumByteToWrite == secremain)break; //寫入結束了else//寫入未結束{secpos++;//扇區地址增1secoff = 0; //偏移位置為0pBuffer += secremain; //指針偏移WriteAddr += secremain; //寫地址偏移NumByteToWrite -= secremain;				//字節數遞減if (NumByteToWrite > 4096)secremain = 4096;	//下一個扇區還是寫不完else secremain = NumByteToWrite;			//下一個扇區可以寫完了}}
}
//擦除整個芯片
//整片擦除時間:
//W25X16:25s
//W25X32:40s
//W25X64:40s
//等待時間超長...
void Norflash_Erase_Chip(void)
{Norflash_Write_Enable();                  //SET WELNorflash_Wait_Busy();NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_ChipErase);        //發送片擦除命令NORFLASH_CS = 1;                          //取消片選Norflash_Wait_Busy();   				   //等待芯片擦除結束
}
//擦除一個扇區
//Dst_Addr:扇區地址 0~511 for w25x16
//擦除一個山區的最少時間:150ms
void Norflash_Erase_Sector(uint32_t Dst_Addr)
{Dst_Addr *= 4096;Norflash_Write_Enable();                  //SET WELNorflash_Wait_Busy();NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_SectorErase);      //發送扇區擦除指令SPI2_ReadWriteByte((uint8_t)((Dst_Addr) >> 16)); //發送24bit地址SPI2_ReadWriteByte((uint8_t)((Dst_Addr) >> 8));SPI2_ReadWriteByte((uint8_t)Dst_Addr);NORFLASH_CS = 1;                          //取消片選Norflash_Wait_Busy();   				   //等待擦除完成
}
//等待空閑
void Norflash_Wait_Busy(void)
{while ((Norflash_ReadSR() & 0x01) == 0x01); // 等待BUSY位清空
}
//進入掉電模式
void Norflash_PowerDown(void)
{NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_PowerDown);        //發送掉電命令NORFLASH_CS = 1;                          //取消片選delay_us(3);                               //等待TPD
}
//喚醒
void Norflash_WAKEUP(void)
{NORFLASH_CS = 0;                          //使能器件SPI2_ReadWriteByte(FLASH_ReleasePowerDown);   //  send W25X_PowerDown command 0xABNORFLASH_CS = 1;                          //取消片選delay_us(3);                               //等待TRES1
}
  1. norflash.h
#ifndef __NORFLASH_H
#define __NORFLASH_H  
#include "main.h"
//	 
//本程序只供學習使用,未經作者許可,不得用于其它任何用途
//ALIENTEK NANO STM32F4開發板
//W25QXX驅動代碼	   
//正點原子@ALIENTEK
//技術論壇:www.openedv.com
//創建日期:2019/4/23
//版本:V1.0
//版權所有,盜版必究。
//Copyright(C) 廣州市星翼電子科技有限公司 2019-2029
//All rights reserved									  
////W25X系列/Q系列芯片列表
#define W25Q80 	    0XEF13          /* W25Q80   芯片ID */
#define W25Q16 	    0XEF14          /* W25Q16   芯片ID */
#define W25Q32 	    0XEF15          /* W25Q32   芯片ID */
#define W25Q64 	    0XEF16          /* W25Q64   芯片ID */
#define W25Q128	    0XEF17          /* W25Q128  芯片ID */
#define NM25Q16     0X6814          /* NM25Q16  芯片ID */
#define NM25Q64     0X5216          /* NM25Q64  芯片ID */
#define NM25Q128    0X5217          /* NM25Q128 芯片ID */
#define BY25Q64     0X6816          /* BY25Q64  芯片ID */
#define BY25Q128    0X6817          /* BY25Q128 芯片ID */extern uint16_t NORFLASH_TYPE;//定義我們使用的flash芯片型號#define	NORFLASH_CS PBout(12)  //W25QXX的片選信號 extern uint8_t W25QXX_BUFFER[4096];//指令表
#define FLASH_WriteEnable       0x06
#define FLASH_WriteDisable      0x04
#define FLASH_ReadStatusReg     0x05
#define FLASH_WriteStatusReg    0x01
#define FLASH_ReadData          0x03
#define FLASH_FastReadData      0x0B
#define FLASH_FastReadDual      0x3B
#define FLASH_PageProgram       0x02
#define FLASH_BlockErase        0xD8
#define FLASH_SectorErase       0x20
#define FLASH_ChipErase         0xC7
#define FLASH_PowerDown         0xB9
#define FLASH_ReleasePowerDown  0xAB
#define FLASH_DeviceID          0xAB
#define FLASH_ManufactDeviceID  0x90
#define FLASH_JedecDeviceID     0x9Fvoid Norflash_Init(void);
uint16_t  Norflash_ReadID(void);  	      //讀取FLASH ID
uint8_t	 Norflash_ReadSR(void);         //讀取狀態寄存器
void Norflash_Write_SR(uint8_t sr);  	  //寫狀態寄存器
void Norflash_Write_Enable(void);   //寫使能
void Norflash_Write_Disable(void);  //寫保護
void Norflash_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead); //讀取flash
void Norflash_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite); //寫入flash
void Norflash_Erase_Chip(void);    	   //整片擦除
void Norflash_Erase_Sector(uint32_t Dst_Addr);//扇區擦除
void Norflash_Wait_Busy(void);           //等待空閑
void Norflash_PowerDown(void);           //進入掉電模式
void Norflash_WAKEUP(void);			   //喚醒#endif
  1. spi.c:在spi.c中添加以下內容。同時注意在spi.h中聲明該函數
/SPI2 讀寫一個字節
//TxData:要寫入的字節
//返回值:讀取到的字節
u8 SPI2_ReadWriteByte(u8 TxData)
{u8 Rxdata;HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);       return Rxdata;          		    //返回收到的數據		
}
  1. delay.c
// Core\Src\delay.c
#include "tim.h"
#include "delay.h"
/*** @brief    微秒延時* @param    Delay_us  —— 指定延遲時間長度,單位為微秒。* @retval   None*/
void delay_us(uint32_t Delay_us) 
{__HAL_TIM_SetCounter(&htim11, 0);__HAL_TIM_ENABLE(&htim11);while(__HAL_TIM_GetCounter(&htim11) < Delay_us);/* Disable the Peripheral */__HAL_TIM_DISABLE(&htim11);
}
  1. delay.h
// Core\Inc\delay.h
#ifndef __DELAY_H__
#define __DELAY_H__#include "main.h"
#include "tim.h"void delay_us(uint32_t Delay_us);#endif /* __DELAY_H__ */
  1. main.h:添加以下代碼
//定義一些常用的數據類型短關鍵字 
typedef int32_t  s32;
typedef int16_t s16;
typedef int8_t  s8;typedef const int32_t sc32;  
typedef const int16_t sc16;  
typedef const int8_t sc8;  typedef __IO int32_t  vs32;
typedef __IO int16_t  vs16;
typedef __IO int8_t   vs8;typedef __I int32_t vsc32;  
typedef __I int16_t vsc16; 
typedef __I int8_t vsc8;   typedef uint32_t  u32;
typedef uint16_t u16;
typedef uint8_t  u8;typedef const uint32_t uc32;  
typedef const uint16_t uc16;  
typedef const uint8_t uc8; typedef __IO uint32_t  vu32;
typedef __IO uint16_t vu16;
typedef __IO uint8_t  vu8;typedef __I uint32_t vuc32;  
typedef __I uint16_t vuc16; 
typedef __I uint8_t vuc8;  //位帶操作,實現51類似的GPIO控制功能
//具體實現思想,參考<<CM3權威指南>>第五章(87頁~92頁).
//IO口操作宏定義
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14     #define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 //IO口操作,只對單一的IO口!
//確保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //輸出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //輸入 #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //輸出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //輸入 #define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //輸出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //輸入 #define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //輸出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //輸入 
  1. main.c
//要寫入到W25Q16的字符串數組
const uint8_t TEXT_Buffer[] = {"Hello,The man who don't write code!"};
#define SIZE sizeof(TEXT_Buffer)
#define  FLASH_SIZE  2 * 1024 * 1024	//FLASH 大小為2M字節;int main(void)
{/* USER CODE BEGIN 1 */
//      uint8_t len;uint8_t datatemp[100]; //flash讀取到的內容/* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();MX_SPI2_Init();MX_TIM11_Init();/* USER CODE BEGIN 2 */HAL_UART_Receive_IT(&huart1, (unsigned char* )aRxBuffer, 1); //串口接收中斷,用作調試Norflash_Init();				    //W25QXX初始化printf("SPI TEST\r\n");NORFLASH_TYPE = Norflash_ReadID();//讀取FLASH IDprintf("id:%#x\r\n",NORFLASH_TYPE); /* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){
//       if(USART_RX_STA&0x8000)
//		{					   
//			len=USART_RX_STA&0x3fff;//得到此次接收到的數據長度
//		printf("\r\n您發送的消息為:\r\n");
//			HAL_UART_Transmit(&huart1,(uint8_t*)USART_RX_BUF,len,1000);	//發送接收到的數據
//			while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC)!=SET);		//等待發送結束
//		printf("\r\n\r\n");//插入換行
//			USART_RX_STA=0;
//		}Norflash_Write((uint8_t *)TEXT_Buffer, FLASH_SIZE - 100, sizeof(TEXT_Buffer));	//從倒數第100個地址處開始,寫入SIZE長度的數據HAL_Delay(1000);printf("Write:%s\r\n", TEXT_Buffer); //顯示讀到的字符串Norflash_Read(datatemp, FLASH_SIZE - 100, sizeof(TEXT_Buffer));	//從倒數第100個地址處開始,讀出SIZE個字節HAL_Delay(1000);printf("Read:%s\r\n", datatemp); //顯示讀到的字符串/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

以上就是所有W25Q16的驅動代碼,僅供個人學習哈,如果有哪里有誤,還請斧正。

運行結果

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/15205.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/15205.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/15205.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

iBarcoder for Mac v3.15.1中文激活版:讓條形碼生成變得如此簡單

在現代社會&#xff0c;條形碼無處不在&#xff0c;從超市商品到物流包裹&#xff0c;都離不開它的身影。iBarcoder for Mac作為一款簡單易用的條形碼生成軟件&#xff0c;讓條形碼的生成變得如此簡單。 iBarcoder for Mac v3.15.1中文激活版下載 無論你是需要為商品添加條形碼…

Scrapy框架簡單介紹及Scrapy項目編寫詳細步驟

引言 Scrapy是一個用Python編寫的開源、功能強大的網絡爬蟲框架&#xff0c;專為網頁抓取和數據提取設計。它允許開發者高效地從網站上抓取所需的數據&#xff0c;并通過一系列可擴展和可配置的組件來處理這些數據。Scrapy框架的核心組成部分包括&#xff1a; Scrapy Engine&…

aws glue配置讀取本地kafka數據源

創建連接時填寫本地私有ip地址&#xff0c;選擇網絡配置 配置任務選擇kafka作為數據源 但是執行任務時日志顯示連接失敗 文檔提到只能用加密通信 如果您希望與 Kafka 數據源建立安全連接&#xff0c;請選擇 Require SSL connection (需要 SSL 連接)&#xff0c;并在 Kafka priv…

python批發模塊的調試之旅:從新手到專家的蛻變

新書上架~&#x1f447;全國包郵奧~ python實用小工具開發教程http://pythontoolsteach.com/3 歡迎關注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目錄 一、調試技巧的重要性 二、批發模塊調試的實戰演練 1. 設置斷點 2. 逐行執行代碼 3. 觀察…

Android+SQLiteOpenHelper實現登錄記住密碼小案例

實現自動登錄&#xff0c;在數據庫中存 注冊的賬號信息 package com.example.databases_text;import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import andro…

運維行業中的堆疊交換機監控與配置管理策略

隨著信息技術的迅猛發展&#xff0c;企業網絡架構日趨復雜&#xff0c;交換機作為網絡基礎設施的核心設備&#xff0c;其穩定性和安全性對于企業業務的運行至關重要。在運維實踐中&#xff0c;堆疊交換機&#xff08;Stacked Switches&#xff09;因其高可靠性、靈活擴展性等特…

SM2258G專用SSD開卡工具(三星閃存),后附工具下載

工具下載&#xff1a; https://download.csdn.net/download/weixin_43097956/89354302

「貪心算法」檸檬水找零

力扣原題鏈接&#xff0c;點擊跳轉。 假設你的手里沒有錢。你要賣檸檬水&#xff0c;每杯5塊錢。每個顧客有可能會給你5塊錢、10塊錢或20塊錢&#xff0c;你要拿手中的錢找零。如何判斷你能否成功找零呢&#xff1f; 如果一上來就有顧客花10塊錢或20塊錢&#xff0c;你手中沒…

python中特殊的靜態方法__new__

一、關于new方法 在Python中&#xff0c;__new__方法是一個特殊的靜態方法&#xff0c;用于實例化對象。通常不需要直接調用__new__方法&#xff0c;Python會自動調用它來分配內存空間并返回一個新對象&#xff08;或者更具體地說&#xff0c;是對象的引用&#xff09;。然而&…

視頻怎么轉換成二維碼圖片?視頻做成二維碼播放的方法

怎樣在電腦上制作可以播放視頻的二維碼呢&#xff1f;很多日常生活中&#xff0c;很多的場景或者物品都會有自己的二維碼&#xff0c;其他人通過掃碼就可以獲取對應的內容。有很多場景下會把視頻轉換二維碼&#xff0c;通過掃碼在手機上查看視頻內容&#xff0c;比如產品介紹、…

水表電表遠程抄表是什么?

1.簡述&#xff1a;水表電表遠程抄表技術性 隨著時代的發展&#xff0c;傳統式手動抄表方法早已被更為高效、智能化的遠程抄表系統所替代。水表電表遠程抄表&#xff0c;說白了&#xff0c;就是利用互聯網技術完成對水表和電表讀數的遠程數據采集管理方法&#xff0c;大大提升…

效果炸裂!使用 GPT-4o 快速實現LLM OS

▼最近直播超級多&#xff0c;預約保你有收獲 —1— 什么是 LLM OS&#xff1f; 關于 LLM OS 的最初構想源自karpathy 在2023年11月11日發布的一條Twitter 動態&#xff0c;這是 LLM OS 概念的最早出處&#xff0c;如下圖所示&#xff1a; LLM OS 主要有以下5個部分組成&#x…

基于SA模擬退火優化算法的TSP問題求解matlab仿真,并對比ACO蟻群優化算法

目錄 1.程序功能描述 2.測試軟件版本以及運行結果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 基于SA模擬退火優化算法的TSP問題求解matlab仿真,并對比ACO蟻群優化算法,對比兩個算法的仿真時間&#xff0c;收斂曲線&#xff0c;以及路徑規劃的結果&#xff0…

中間件的概念及示例

什么是中間件&#xff1f; 中間件是一種軟件技術&#xff0c;它在分布式系統中起著至關重要的作用。以下是關于中間件的詳細解釋&#xff1a; 定義與位置&#xff1a; 中間件是位于應用系統和系統軟件之間的一類軟件。它使用系統軟件提供的基礎服務&#xff08;功能&#xff0…

Flask+Vue+MySQL天水麻辣燙管理系統設計與實現(附源碼 配置 文檔)

背景&#xff1a; 同學找到我期望做一個天水麻辣燙的網頁&#xff0c;想復用以前做過的課設&#xff0c;結合他的實際需求&#xff0c;讓我們來看看這個系統吧~ 項目功能與使用技術概述&#xff1a; 里面嵌入了6個子系統&#xff0c;其中餐飲系統可以進行餐館信息添加、修改…

TypeScript體操類型練習

歷史小劇場 這個世界上&#xff0c;有兩種人最痛苦&#xff0c;第一種是身居高位者&#xff0c;第二種是身居底層者&#xff0c;第一種人很少&#xff0c;第二種人很多。第一種人叫崇禎&#xff0c;第二種人叫百姓。 而最幸福的&#xff0c;就是中間那撥人&#xff0c;主要工作…

Influence blocking maximization on networks: Models, methods and applications

abstract 由于各種社會和貿易網絡的不斷出現&#xff0c;網絡影響力分析引起了研究者的極大興趣。基于不同的影響力傳播模型&#xff0c;人們提出了許多網絡影響力最大化的新模型和方法。作為傳統影響力最大化問題的延伸和擴展&#xff0c;影響力封鎖最大化問題已成為研究熱點&…

借助 CloudFlare 增強站點內容保護防采集

今天在一位站長的幫助下實測了 CloudFlare 增強站點內容保護實現防采集的功能,效果那是杠杠的,如果您的站點原創內容比較多的話,明月強烈建議試試 CloudFlare 這個內容保護,無論是 WordPress 、Typecho 都有非常好的效果,并且幾乎沒有任何誤傷,搜索引擎爬蟲蜘蛛更是不會影…

【圖論】單源最短路

前言 今天&#xff0c;我們來講最短路&#xff0c;首先看只有一個起點&#xff08;單源&#xff09;的情況。 為了書寫方便&#xff0c;我們約定以下內容&#xff1a; template<class W> using Graph vector<vector<pair<int, W>>>; // 鄰接表(ve…

集中抄表電表是什么?

1.集中抄表電表&#xff1a;簡述 集中抄表電表&#xff0c;又稱為遠程抄表系統&#xff0c;是一種現代化電力計量技術&#xff0c;為提升電力行業的經營效率和客戶服務質量。它通過自動化的形式&#xff0c;取代了傳統人工抄水表&#xff0c;完成了數據信息實時、精確、高效率…