本文最后修改時間:2022年01月08日 10:45
一、本節簡介
本節以simple_peripheral工程為例,介紹如何使用SPI讀寫W25Q80(外部flash)。
二、實驗平臺
1)CC2640R2F平臺
①協議棧版本:CC2640R2 SDK v1.40.00.45
②編譯軟件:CCS7.3.0.00019
③硬件平臺:香瓜CC2640R2F開發板
④仿真器:香瓜XDS100V3下載器
2)外設
①板載在香瓜CC2640R2F開發板底板上的芯片:W25Q80
三、版權聲明
1)作者:甜甜的大香瓜
2)聲明:喝水不忘挖井人,轉載請注明出處。
3)糾錯/業務合作:897503845@qq.com
4)香瓜BLE之CC2640R2F群:557278427
5)本文出處:原創連載資料《簡單粗暴學藍牙5》
6)完整開源資料下載地址(電腦端打開):
opengua.taobao.com
7)香瓜CC2640R2F開發板購買鏈接:
https://item.taobao.com/item.htm?spm=a1z10.1-c-s.w4023-16963296339.8.21bfc58419sWKt&id=558653143169
四、實驗前提
1、在進行本文步驟前,請先閱讀以下章節:
1)《簡單粗暴學藍牙5》的“第一章至第四章”章節?。
2、在進行本文步驟前,請先實現以下章節:
1)《簡單粗暴學藍牙5》的“第三章 軟件的安裝及使用”章節。
五、基礎知識
1、W25Q80是什么?
答: W25Q80是winbond(華邦)公司出的8M bit(也就是1MB)的flash芯片。同系列常用的型號還有W25Q16(16Mb=2MB)、W25Q32(32Mb=4MB)等。
2、W25Q80的基本信息?
答:

如上圖,W25Q80一次可寫256字節、可4K字節扇區擦除、32K或者64K塊擦除、整片擦除。
使用的是SPI接口、2.7V~3.6V電壓范圍,香瓜購買的是8-pin SOIC 208-mil封裝。

如上圖,W25Q80的廠商ID是0xEF,設備ID是0x13。這個用于初始化時識別flash芯片用。
六、硬件原理
1、W25Q80芯片引腳圖


如上圖,W25Q80一共8個腳,其中GND和VCC供電3.3V(范圍在2.7V~3.6V)即可,其他4個SPI引腳DO(輸出)、DI(輸入)、CLK(時鐘)、CS(片選)。
WP(寫保護)、HOLD(保持輸入)這兩個引腳本文并沒有用到,且“/WP”中的“/”是低電平有效的意思,所以我們本文將WP和HOLD兩引腳拉高到VCC即可。
2、W25Q80與香瓜CC2640R2F開發板的對應關系
| W25Q80引腳 | 香瓜CC2640R2F開發板引腳 |
| 引腳1(/CS) | DIO_30 |
| 引腳2(DO) | DIO_8 |
| 引腳3(/WP) | 置高到VCC |
| 引腳4(GND) | GND |
| 引腳5(DI) | DIO_9 |
| 引腳6(CLK) | DIO_10 |
| 引腳7(/HOLD) | 置高到VCC |
| 引腳6(VCC) | 3.3V |
3、硬件原理圖


七、實驗步驟
1、編寫并添加自定義的W25Q80驅動
1)寫一個驅動GUA_ExtFlash.c(存放在“……
\simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\GUA“路徑下)
| //********************************************************************** //name:???????? GUA_ExtFlash.c //introduce:??? 外部flash驅動 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** #include “string.h” #include <ti/devices/cc26x0r2/driverlib/Ssi.h> #include <ti/drivers/spi/SPICC26XXDMA.h> #include <ti/drivers/dma/UDMACC26XX.h> #include <ti/drivers/SPI.h> #include “GUA_ExtFlash.h” /*********************宏定義************************/ //SPI引腳 #define GUA_CC2640R2_SPI0_MISO????????????? IOID_8 #define GUA_CC2640R2_SPI0_MOSI????????????? IOID_9 #define GUA_CC2640R2_SPI0_CLK?????????????? IOID_10 #define GUA_CC2640R2_SPI0_CSN?????????????? PIN_UNASSIGNED #define GUA_Spi_flash_CS??????????????????? IOID_30 //SPI片選開關宏 #define GUA_FLASH_CS_ON???????????????????? 0 #define GUA_FLASH_CS_OFF??????????????????? 1 /* Instruction codes */ #define GUA_BLS_CODE_PROGRAM??????????????? 0x02 /**< Page Program */ #define GUA_BLS_CODE_READ?????????????????? 0x03 /**< Read Data */ #define GUA_BLS_CODE_READ_STATUS??????????? 0x05 /**< Read Status Register */ #define GUA_BLS_CODE_WRITE_ENABLE?????????? 0x06 /**< Write Enable */ #define GUA_BLS_CODE_SECTOR_ERASE?????????? 0x20 /**< Sector Erase */ #define GUA_BLS_CODE_MDID?????????????????? 0x90 /**< Manufacturer Device ID */ #define GUA_BLS_CODE_DP???????????????????? 0xB9 /**< Power down */ #define GUA_BLS_CODE_RDP??????????????????? 0xAB /**< Power standby */ /* Erase instructions */ #define GUA_BLS_CODE_ERASE_4K?????????????? 0x20 /**< Sector Erase */ #define GUA_BLS_CODE_ERASE_32K????????????? 0x52 #define GUA_BLS_CODE_ERASE_64K????????????? 0xD8 #define GUA_BLS_CODE_ERASE_ALL????????????? 0xC7 /**< Mass Erase */ /* Bitmasks of the status register */ #define GUA_BLS_STATUS_SRWD_BM????????????? 0x80 #define GUA_BLS_STATUS_BP_BM??????????????? 0x0C #define GUA_BLS_STATUS_WEL_BM?????????????? 0x02 #define GUA_BLS_STATUS_WIP_BM?????????????? 0x01 #define GUA_BLS_STATUS_BIT_BUSY???????????? 0x01 /**< Busy bit of the status register */ /* Part specific constants */ #define GUA_BLS_PROGRAM_PAGE_SIZE?????????? 256 #define GUA_BLS_ERASE_SECTOR_SIZE?????????? 4096 /* Manufacturer IDs */ #define GUA_MF_MACRONIX???????????????????? 0xC2 #define GUA_MF_WINBOND????????????????????? 0xEF typedef enum GUA_CC2640R2_SPIName { GUA_CC2640R2_SPI0 = 0, GUA_CC2640R2_SPICOUNT } GUA_CC2640R2_SPIName; SPICC26XXDMA_Object GUA_spiCC26XXDMAObjects[GUA_CC2640R2_SPICOUNT]; //SPI速度4M #define GUA_SPI_BIT_RATE??????????????????? 4000000 //flash每頁大小4K #define GUA_EXT_FLASH_PAGE_SIZE???????????? 4096 /*********************內部變量************************/ const SPICC26XXDMA_HWAttrsV1 GUA_spiCC26XXDMAHWAttrs[GUA_CC2640R2_SPICOUNT] = { { ??????? .baseAddr?????????? = SSI0_BASE, ??????? .intNum???????????? = INT_SSI0_COMB, ??????? .intPriority??????? = ~0, ??????? .swiPriority??????? = 0, ??????? .powerMngrId?????? ?= PowerCC26XX_PERIPH_SSI0, ??????? .defaultTxBufValue? = 0, ??????? .rxChannelBitMask?? = 1<<UDMA_CHAN_SSI0_RX, ??????? .txChannelBitMask?? = 1<<UDMA_CHAN_SSI0_TX, ??????? .mosiPin??????????? = GUA_CC2640R2_SPI0_MOSI, ??????? .misoPin??????????? = GUA_CC2640R2_SPI0_MISO, ??????? .clkPin???????????? = GUA_CC2640R2_SPI0_CLK, ??????? .csnPin???????????? = GUA_CC2640R2_SPI0_CSN } }; const SPI_Config GUA_SPI_config[GUA_CC2640R2_SPICOUNT] = { { ???????? .fxnTablePtr = &SPICC26XXDMA_fxnTable, ???????? .object????? = &GUA_spiCC26XXDMAObjects[GUA_CC2640R2_SPI0], ???????? .hwAttrs???? = &GUA_spiCC26XXDMAHWAttrs[GUA_CC2640R2_SPI0] }, }; const uint_least8_t GUA_SPI_count = GUA_CC2640R2_SPICOUNT; static PIN_Config GUA_BoardFlashPinTable[] = { GUA_Spi_flash_CS | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MIN, /* Ext. flash chip select */ PIN_TERMINATE }; static PIN_Handle GUA_hFlashPin = NULL; static PIN_State GUA_pinState; // Supported flash devices static GUA_ExtFlashInfo_t GUA_flashInfo[] = { { ??????? .manfId = GUA_MF_MACRONIX,? // Macronics ??????? .devId = 0x15,????????????? // MX25R1635F ??????? .deviceSize = 0x200000????? // 2 Mbyte (16 Mbit) }, { ??????? .manfId = GUA_MF_MACRONIX,? // Macronics ??????? .devId = 0x14,????????????? // MX25R8035F ??????? .deviceSize = 0x100000????? // 1 Mbyte (8 Mbit) }, { ??????? .manfId = GUA_MF_WINBOND,?? // WinBond ??????? .devId = 0x13,????????????? // W25Q80DV ??????? .deviceSize = 0x100000????? // 1 Mbyte (8 Mbit) }, { ??????? .manfId = GUA_MF_WINBOND,?? // WinBond ??????? .devId = 0x12,????????????? // W25X40CL ??????? .deviceSize = 0x080000????? // 512 Kbyte (4 Mbit) }, { ??????? .manfId = GUA_MF_WINBOND,?? // WinBond ??????? .devId = 0x11,????????????? // W25X20CL ??????? .deviceSize = 0x040000????? // 256 Kbyte (2 Mbit) }, { ??????? .manfId = 0x0, ??????? .devId = 0x0, ??????? .deviceSize = 0x0 } }; // Flash information static GUA_ExtFlashInfo_t *pGUA_flashInfo = NULL; static GUA_U8 GUA_infoBuf[2]; // SPI interface static SPI_Handle GUA_spiHandle = NULL; static SPI_Params GUA_spiParams; /*********************內部函數************************/ static int GUA_Spi_write(const GUA_U8 *pGUA_buf, size_t GUA_length); static int GUA_Spi_read(GUA_U8 *pGUA_buf, size_t GUA_length); static bool GUA_Spi_open(GUA_U32 GUA_bitRate); static void GUA_Spi_close(void); static void GUA_Spi_flash(void); static void GUA_extFlashSelect(void); static void GUA_extFlashDeselect(void); static bool GUA_extFlashPowerDown(void); static bool GUA_extFlashPowerStandby(void); static bool GUA_ExtFlash_readInfo(void); static bool GUA_extFlashVerifyPart(void); static int GUA_ExtFlash_waitReady(void); static int GUA_ExtFlash_powerDown(void); static int GUA_ExtFlash_writeEnable(void); //********************************************************************** //name:???????? GUA_Spi_write //introduce:??? 寫SPI設備 //parameter:??? pGUA_buf:寫緩存 //????????????? GUA_len:數據長度 //return:?????? 如果操作正常,返回0;反之返回1 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static int GUA_Spi_write(const GUA_U8 *pGUA_buf, size_t GUA_len) { SPI_Transaction masterTransaction; masterTransaction.count? = GUA_len; masterTransaction.txBuf? = (void*)pGUA_buf; masterTransaction.arg??? = NULL; masterTransaction.rxBuf? = NULL; return SPI_transfer(GUA_spiHandle, &masterTransaction) ? 0 : -1; } //********************************************************************** //name:???????? GUA_Spi_read //introduce:??? 讀SPI設備 //parameter:??? pGUA_buf:讀緩存 //????????????? GUA_len:數據長度 //return:?????? 如果操作正常,返回0;反之返回1 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static int GUA_Spi_read(GUA_U8 *pGUA_buf, size_t GUA_len) { SPI_Transaction masterTransaction; masterTransaction.count = GUA_len; masterTransaction.txBuf = NULL; masterTransaction.arg = NULL; masterTransaction.rxBuf = pGUA_buf; return SPI_transfer(GUA_spiHandle, &masterTransaction) ? 0 : -1; } //********************************************************************** //name:???????? GUA_Spi_open //introduce:??? 打開SPI //parameter:??? GUA_bitRate:要設置的SPI速度 //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static bool GUA_Spi_open(GUA_U32 GUA_bitRate) { /*? Configure SPI as master */ SPI_Params_init(&GUA_spiParams); GUA_spiParams.bitRate = GUA_bitRate; GUA_spiParams.mode = SPI_MASTER; GUA_spiParams.transferMode = SPI_MODE_BLOCKING; /* Attempt to open SPI. */ GUA_spiHandle = SPI_open(GUA_CC2640R2_SPI0, &GUA_spiParams); return GUA_spiHandle != NULL; } //********************************************************************** //name:???????? GUA_Spi_close //introduce:??? 關閉SPI //parameter:??? none //return:?????? none //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static void GUA_Spi_close(void) { if (GUA_spiHandle != NULL) { ??????? // Close the RTOS driver ??????? SPI_close(GUA_spiHandle); ??????? GUA_spiHandle = NULL; } } //********************************************************************** //name:???????? GUA_Spi_flash //introduce:??? 把沒用的垃圾數據處理掉 //parameter:??? none //return: ??????none //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static void GUA_Spi_flash(void) { /* make sure SPI hardware module is done? */ while(SSIBusy(((SPICC26XXDMA_HWAttrsV1*)GUA_spiHandle->hwAttrs)->baseAddr)) { }; } //********************************************************************** //name:???????? GUA_extFlashSelect //introduce:??? 片選引腳選擇外部flash //parameter:??? none //return:?????? none //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static void GUA_extFlashSelect(void) { PIN_setOutputValue(GUA_hFlashPin, GUA_Spi_flash_CS, GUA_FLASH_CS_ON); } //********************************************************************** //name:???????? GUA_extFlashDeselect //introduce:??? 取消片選引腳選擇外部flash //parameter:??? none //return:?????? none //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static void GUA_extFlashDeselect(void) { PIN_setOutputValue(GUA_hFlashPin, GUA_Spi_flash_CS, GUA_FLASH_CS_OFF); } //********************************************************************** //name:???????? GUA_extFlashPowerDown //introduce:??? 讓設備進入低功耗模式,不能訪問數據,只能讀取寄存器狀態 //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static bool GUA_extFlashPowerDown(void) { GUA_U8 GUA_cmd; bool GUA_success; GUA_cmd = GUA_BLS_CODE_DP; GUA_extFlashSelect(); GUA_success = GUA_Spi_write(&GUA_cmd, sizeof(GUA_cmd)) == 0; GUA_extFlashDeselect(); return GUA_success; } //********************************************************************** //name:???????? GUA_extFlashPowerStandby //introduce:??? 退出低功耗模式 //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static bool GUA_extFlashPowerStandby(void) { GUA_U8 GUA_cmd; bool GUA_success; GUA_cmd = GUA_BLS_CODE_RDP; GUA_extFlashSelect(); GUA_success = GUA_Spi_write(&GUA_cmd, sizeof(GUA_cmd)) == 0; GUA_extFlashDeselect(); if(GUA_success) { ??????? if(GUA_ExtFlash_waitReady() != 0) ??????? { ??????????? GUA_success = false; ??????? } } return GUA_success; } //********************************************************************** //name:???????? GUA_ExtFlash_readInfo //introduce:??? 讀flash信息(廠商ID和設備ID) //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static bool GUA_ExtFlash_readInfo(void) { const GUA_U8 GUA_wbuf[] = {GUA_BLS_CODE_MDID, 0xFF, 0xFF, 0x00}; GUA_extFlashSelect(); int GUA_ret = GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf)); if(GUA_ret) { ??????? GUA_extFlashDeselect(); ??????? return false; } GUA_ret = GUA_Spi_read(GUA_infoBuf, sizeof(GUA_infoBuf)); GUA_extFlashDeselect(); return GUA_ret == 0; } //********************************************************************** //name:???????? GUA_extFlashVerifyPart //introduce:??? 校驗flash芯片是否是驅動支持的flash //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static bool GUA_extFlashVerifyPart(void) { if(!GUA_ExtFlash_readInfo()) { ??????? return false; } pGUA_flashInfo = GUA_flashInfo; while(pGUA_flashInfo->deviceSize > 0) { ??????? if(GUA_infoBuf[0] == pGUA_flashInfo->manfId && GUA_infoBuf[1] == pGUA_flashInfo->devId) ??????? { ??????????? break; ??????? } ??????? pGUA_flashInfo++; } return pGUA_flashInfo->deviceSize > 0; } //********************************************************************** //name:???????? GUA_ExtFlash_waitReady //introduce:??? 等待當前擦除、編程的操作結束 //parameter:??? none //return:?????? 如果操作正常,返回0;反之返回非0 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static int GUA_ExtFlash_waitReady(void) { const GUA_U8 GUA_wbuf[1] = {GUA_BLS_CODE_READ_STATUS}; int GUA_ret; /* Throw away all garbage */ GUA_extFlashSelect(); GUA_Spi_flash(); GUA_extFlashDeselect(); for(;;) { ??????? GUA_U8 GUA_buf; ??????? GUA_extFlashSelect(); ??????? GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf)); ??????? GUA_ret = GUA_Spi_read(&GUA_buf, sizeof(GUA_buf)); ??????? GUA_extFlashDeselect(); ??????? if(GUA_ret) ??????? { ??????????? /* Error */ ??????????? return -2; ??????? } ??????? if(!(GUA_buf & GUA_BLS_STATUS_BIT_BUSY)) ??????? { ??????????? /* Now ready */ ??????????? break; ??????? } } return 0; } //********************************************************************** //name:???????? GUA_ExtFlash_powerDown //introduce:??? 等待進入低功耗模式 //parameter:??? none //return:?????? 如果操作正常,返回0;反之返回非0 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static int GUA_ExtFlash_powerDown(void) { GUA_U8 GUA_i; GUA_i = 0; while(GUA_i < 10) { ??????? if(!GUA_ExtFlash_readInfo()) ??????? { ??????????? return 0; ??????? } ??????? GUA_i++; } return -1; } //********************************************************************** //name:???????? GUA_ExtFlash_writeEnable //introduce:??? 啟動寫操作 //parameter:??? none //return:?????? 如果操作正常,返回0;反之返回非0 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** static int GUA_ExtFlash_writeEnable(void) { const GUA_U8 GUA_wbuf[] = {GUA_BLS_CODE_WRITE_ENABLE}; GUA_extFlashSelect(); int GUA_ret = GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf)); GUA_extFlashDeselect(); if(GUA_ret) { ??????? return -3; } return 0; } //********************************************************************** //name:???????? GUA_ExtFlash_open //introduce:??? 初始化flash //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** bool GUA_ExtFlash_open(void) { bool GUA_f; GUA_hFlashPin = PIN_open(&GUA_pinState, GUA_BoardFlashPinTable); if(GUA_hFlashPin == NULL) { ??????? return false; } /* Initialise SPI. Subsequent calls will do nothing. */ SPI_init(); /* Make sure SPI is available */ GUA_f = GUA_Spi_open(GUA_SPI_BIT_RATE); if (GUA_f) { ??????? /* Put the part is standby mode */ ??????? GUA_f = GUA_extFlashPowerStandby(); ??????? if(GUA_f) ??????? { ??????????? /* Verify manufacturer and device ID */ ??????????? GUA_f = GUA_extFlashVerifyPart(); ??????? } ??????? if(!GUA_f) ??????? { ??????????? GUA_ExtFlash_close(); ??????? } } return GUA_f; } //********************************************************************** //name:???????? GUA_ExtFlash_close //introduce:??? 關閉flash //parameter:??? none //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** void GUA_ExtFlash_close(void) { if(GUA_hFlashPin != NULL) { ??????? // Put the part in low power mode ??????? GUA_extFlashPowerDown(); ??????? if(pGUA_flashInfo->manfId == GUA_MF_WINBOND) ??????? { ??????????? GUA_ExtFlash_powerDown(); ??????? } ??????? /* Make sure SPI lines have a defined state */ ??????? GUA_Spi_close(); ??????? PIN_close(GUA_hFlashPin); ??????? GUA_hFlashPin = NULL; } } //********************************************************************** //name:???????? GUA_ExtFlash_read //introduce:??? 讀flash //parameter:??? GUA_offset:偏移地址 //????????????? GUA_length:數據長度 //????????????? pGUA_buf:接收緩存區 //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** bool GUA_ExtFlash_read(size_t GUA_offset, size_t GUA_length, GUA_U8 *pGUA_buf) { GUA_U8 GUA_wbuf[4]; /* Wait till previous erase/program operation completes */ int GUA_ret = GUA_ExtFlash_waitReady(); if(GUA_ret) { ??????? return false; } /* SPI is driven with very low frequency (1MHz < 33MHz fR spec) * in this temporary implementation. * and hence it is not necessary to use fast read. */ GUA_wbuf[0] = GUA_BLS_CODE_READ; GUA_wbuf[1] = (GUA_offset >> 16) & 0xff; GUA_wbuf[2] = (GUA_offset >> 8) & 0xff; GUA_wbuf[3] = GUA_offset & 0xff; GUA_extFlashSelect(); if(GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf))) { ??????? /* failure */ ??????? GUA_extFlashDeselect(); ??????? return false; } GUA_ret = GUA_Spi_read(pGUA_buf, GUA_length); GUA_extFlashDeselect(); return GUA_ret == 0; } //********************************************************************** //name:???????? GUA_ExtFlash_write //introduce:??? 寫flash //parameter:? ??GUA_offset:偏移地址 //????????????? GUA_length:數據長度 //????????????? pGUA_buf:發送緩存區 //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** bool GUA_ExtFlash_write(size_t GUA_offset, size_t GUA_length, const GUA_U8 *pGUA_buf) { GUA_U8 GUA_wbuf[4]; while(GUA_length > 0) { ??????? /* Wait till previous erase/program operation completes */ ??????? int GUA_ret = GUA_ExtFlash_waitReady(); ??????? if(GUA_ret) ??????? { ??????????? return false; ??????? } ??????? GUA_ret = GUA_ExtFlash_writeEnable(); ??????? if (GUA_ret) ??????? { ??????????? return false; ??????? } ??????? size_t GUA_ilen; /* interim GUA_length per instruction */ ??????? GUA_ilen = GUA_BLS_PROGRAM_PAGE_SIZE – (GUA_offset % GUA_BLS_PROGRAM_PAGE_SIZE); ??????? if (GUA_length < GUA_ilen) ??????? { ??????????? GUA_ilen = GUA_length; ??????? } ??????? GUA_wbuf[0] = GUA_BLS_CODE_PROGRAM; ??????? GUA_wbuf[1] = (GUA_offset >> 16) & 0xff; ??????? GUA_wbuf[2] = (GUA_offset >> 8) & 0xff; ??????? GUA_wbuf[3] = GUA_offset & 0xff; ??????? GUA_offset += GUA_ilen; ??????? GUA_length -= GUA_ilen; ??????? /* Up to 100ns CS hold time (which is not clear ??????? * whether it’s application only in between reads) ??????? * is not imposed here since above instructions ??????? * should be enough to delay ??????? * as much. */ ??????? GUA_extFlashSelect(); ??????? if (GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf))) ??????? { ??????????? /* failure */ ??????????? GUA_extFlashDeselect(); ??????????? return false; ??????? } ?? ?????if (GUA_Spi_write(pGUA_buf, GUA_ilen)) ??????? { ??????????? /* failure */ ??????????? GUA_extFlashDeselect(); ??????????? return false; ??????? } ??????? pGUA_buf += GUA_ilen; ??????? GUA_extFlashDeselect(); } return true; } //********************************************************************** //name:???????? GUA_ExtFlash_erase //introduce:??? 擦除flash //parameter:??? GUA_offset:偏移地址 //????????????? GUA_length:數據長度 //????????????? pGUA_buf:發送緩存區 //return:?????? 如果操作正常,返回true;反之返回false //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** bool GUA_ExtFlash_erase(size_t GUA_offset, size_t GUA_length) { /* Note that Block erase might be more efficient when the floor map * is well planned for OTA but to simplify for the temporary implementation, * sector erase is used blindly. */ GUA_U8 GUA_wbuf[4]; size_t GUA_i, GUA_numsectors; GUA_wbuf[0] = GUA_BLS_CODE_SECTOR_ERASE; { ??????? size_t GUA_endoffset = GUA_offset + GUA_length – 1; ??????? GUA_offset = (GUA_offset / GUA_BLS_ERASE_SECTOR_SIZE) * GUA_BLS_ERASE_SECTOR_SIZE; ??????? GUA_numsectors = (GUA_endoffset – GUA_offset + GUA_BLS_ERASE_SECTOR_SIZE – 1) / ??????????? GUA_BLS_ERASE_SECTOR_SIZE; } for(GUA_i = 0; GUA_i < GUA_numsectors; GUA_i++) { ??????? /* Wait till previous erase/program operation completes */ ??????? int GUA_ret = GUA_ExtFlash_waitReady(); ??????? if(GUA_ret) ??????? { ??????????? return false; ??????? } ??????? GUA_ret = GUA_ExtFlash_writeEnable(); ??????? if(GUA_ret) ??????? { ??????????? return false; ??????? } ??????? GUA_wbuf[1] = (GUA_offset >> 16) & 0xff; ??????? GUA_wbuf[2] = (GUA_offset >> 8) & 0xff; ??????? GUA_wbuf[3] = GUA_offset & 0xff; ??????? GUA_extFlashSelect(); ??????? if(GUA_Spi_write(GUA_wbuf, sizeof(GUA_wbuf))) ??????? { ??????????? /* failure */ ??????????? GUA_extFlashDeselect(); ??????????? return false; ??????? } ??????? GUA_extFlashDeselect(); ??????? GUA_offset += GUA_BLS_ERASE_SECTOR_SIZE; } return true; } //********************************************************************** //name:???????? GUA_ExtFlash_info //introduce:??? 獲取當前連接的外部flash的信息 //parameter:??? none //return:?????? ExtFlashInfo_t(包含設備內存大小、廠商ID、設備ID) //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** GUA_ExtFlashInfo_t *GUA_ExtFlash_info(void) { return pGUA_flashInfo; } |
2)寫一個驅動頭文件GUA_ExtFlash.h(存放在“……
\simplelink_cc2640r2_sdk_1_40_00_45\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\src\app\GUA“路徑下)
| //********************************************************************** //name:???????? GUA_ExtFlash.h //introduce:??? 外部flash驅動的頭文件 //author:?????? 甜甜的大香瓜 //email:??????? 897503845@qq.com //QQ group:???? 香瓜BLE之CC2640R2F(557278427) //shop:???????? opengua.taobao.com //changetime:?? 2021.08.02 //********************************************************************** #ifndef GUA_EXT_FLASH_H #define GUA_EXT_FLASH_H //頭文件 #include <stdlib.h> #include <stdbool.h> /*********************宏定義************************/ //類型宏 #ifndef GUA_C typedef char GUA_C; #endif #ifndef GUA_U8 typedef unsigned char GUA_U8; #endif #ifndef GUA_8 typedef signed char GUA_8; #endif #ifndef GUA_U16 typedef unsigned short GUA_U16; #endif #ifndef GUA_16 typedef signed short GUA_16; #endif #ifndef GUA_U32 typedef unsigned long GUA_U32; #endif #ifndef GUA_32 typedef signed long GUA_32; #endif #ifndef GUA_U64 typedef unsigned long long GUA_U64; #endif #ifndef GUA_64 typedef signed long long GUA_64; #endif /*********************外部變量************************/ //設備信息結構體 typedef struct { GUA_U32 deviceSize; // bytes GUA_U8 manfId;????? // manufacturer ID GUA_U8 devId;?????? // device ID } GUA_ExtFlashInfo_t; /*********************函數聲明************************/ extern bool GUA_ExtFlash_open(void); extern void GUA_ExtFlash_close(void); extern bool GUA_ExtFlash_read(size_t GUA_offset, size_t GUA_length, GUA_U8 *pGUA_buf); extern bool GUA_ExtFlash_write(size_t GUA_offset, size_t GUA_length, const GUA_U8 *pGUA_buf); extern bool GUA_ExtFlash_erase(size_t GUA_offset, size_t GUA_length); extern GUA_ExtFlashInfo_t *GUA_ExtFlash_info(void); #endif |
3、工程中添加驅動文件GUA_ExtFlash.c、GUA_ExtFlash.h
注:拖拽至CCS工程的Application文件夾下

添加文件過程中,選項選擇如下


4、應用層調用
1)添加頭文件(simple_peripheral.c中)
| //GUA #include “GUA_ ExtFlash.h” //GUA |
2)添加測試代碼(simple_peripheral.c的SimpleBLEPeripheral_init函數末尾中)
| //GUA GUA_U32 GUA_offset = 0x012233; GUA_U8 GUA_write_buf[7] = {‘o’,’p’,’e’,’n’,’g’,’u’,’a’}; GUA_U8 GUA_read_buf[7] = {0}; bool GUA_flash_Ret; //初始化flash GUA_flash_Ret = GUA_ExtFlash_open(); if(GUA_flash_Ret == true) { ??????? //擦除flash ??????? GUA_flash_Ret = GUA_ExtFlash_erase(GUA_offset, sizeof(GUA_write_buf)); ??????? if(GUA_flash_Ret == true) ??????? { ??????????? //寫flash ??????????? GUA_flash_Ret = GUA_ExtFlash_write(GUA_offset, sizeof(GUA_write_buf), GUA_write_buf); ??????????? if(GUA_flash_Ret == true) ??????????? { ??????????????? //讀flash ??????????????? GUA_flash_Ret = GUA_ExtFlash_read(GUA_offset, sizeof(GUA_read_buf), GUA_read_buf); ??????????????? if(GUA_flash_Ret == true) ??????????????? { ??????????????????? //讀正確后關flash ??????????????????? GUA_ExtFlash_close(); ??????????????? } ??????????? } ??????? } } //GUA |
上述代碼,先將0x012233地址擦除7個字節,再寫入7個字節“opengua”,再讀出7個字節,判斷是否讀出的是“opengua”,以此來判斷是否有正常寫入。
八、注意事項
1、W25Q80的內存大小是1M,也就是0x100000,所以讀、寫、擦除的范圍就是0~0x100000。
2、在寫入之前,需要先擦除,否則會寫入不成功。
3、本文驅動會與《香瓜CC2640R2F之LCD》沖突,因為都是使用SPI。如果只是想檢測flash是否良好,可以在oled啟動之前,測試一下flash再關閉。思路如下
| //flash測試代碼 //…… //oled初始化代碼 //…… //GUA //用oled顯示flash的好壞 if(GUA_flash_Ret == true) { ??????? GUA_LCD_WriteString(“flash OK”, GUA_LCD_LINE7); } else { ??????? GUA_LCD_WriteString(“flash error”, GUA_LCD_LINE7); } //GUA |
九、實驗結果
仿真并設置斷點,查看讀出的flash數據

可見讀出的是“opengua”,與寫入的數據一樣,表示讀寫正常。
因此,實驗成功。