1.開發環境編程
操做系統:SylixOS框架
編程環境:RealEvo-IDE3.0函數
開發板:MDK972ui
Nor Flash:EN25Q128spa
2.原理概述操作系統
NOR FLASH是一種常見的存儲芯片,數據掉電不會丟失,支持片內執行,所以在嵌入式系統中,常做爲啓動程序的存儲介質。根據傳輸的數據位,Nor Flash可分爲並行Nor(Parallel)Flash和串行(SPI)Flash。SPI Nor Flash較Parallel Flash便宜,接口簡單,但速度慢。本文介紹的是SylixOS下基於NUC970的SPI Flash驅動。接口
經過讀取或配置Nor Flash的狀態寄存器,能夠得到或改變Flash的當前狀態,每一位的含義如圖 2-1所示。ip
圖 2-1 SPI Flash狀態寄存器開發
根據SPI的傳輸方式,常見的指令可分爲標準SPI指令、Dual SPI指令和Quad SPI指令。常見的指令如圖 2-二、圖 2-3所示。flash
圖 2-2 SPI Flash讀指令
圖 2-3 SPI Flash常見指令
3.技術實現
SPI Flash的驅動主要實現對Flash的擦除、讀和寫功能,並根據具體硬件填充相應信息後掛載YAFFS文件系統便可。其代碼框架如程序清單 3-1所示。
程序清單 3-1掛載文件系統
pNor->nor_base = NOR_BASE_ADDR; pNor->block_size = NOR_ERASE_SIZE; pNor->chunk_size = 512 + 16; pNor->bytes_per_chunk = 512; pNor->spare_per_chunk = 16; pNor->chunk_per_block = pNor->block_size / pNor->chunk_size; pNor->block_per_chip = NOR_BLOCK_PER_CHIP; pNor->nor_erase_fn = __spiNorErase; pNor->nor_read_fn = __spiNorRead; pNor->nor_write_fn = __spiNorWrite; pNor->nor_initialise_fn = __spiNorInitialise; pNor->nor_deinitialise_fn = __spiNorDeinitialise; pParam->name = pcName; pParam->total_bytes_per_chunk = pNor->bytes_per_chunk; pParam->chunks_per_block = pNor->chunk_per_block; pParam->n_reserved_blocks = 4; pParam->start_block = (ulOffsetBytes / NOR_BLOCK_SIZE) + 1; pParam->end_block = NOR_BLOCK_PER_CHIP - 1; pParam->use_nand_ecc = 0; pParam->disable_soft_del = 1; pParam->n_caches = 10; pDrv->drv_write_chunk_fn = ynorWriteChunk; pDrv->drv_read_chunk_fn = ynorReadChunk; pDrv->drv_erase_fn = ynorEraseBlock; pDrv->drv_initialise_fn = ynorInitialise; pDrv->drv_deinitialise_fn = ynorDeinitialise; pDev->driver_context = &pNorDev; yaffs_add_device(pDev); /* 增長yaffs設備 */ yaffs_mount(pcName); /* 掛載yaffs文件系統 */
3.1建立SPI設備
根據硬件上SPI Flash掛載的SPI總線建立SPI設備,並設置爲模式0。具體實現如程序清單 3-2所示
程序清單 3-2建立設備
pSpi_Nor->Spi_NorDev = API_SpiDeviceCreate(pSpiBusName, SPI_NOR_DEVNAME); iError = API_SpiDeviceBusRequest(pSpi_Nor->Spi_NorDev); /* 申請SPI總線 */ if (iError == PX_ERROR) { NOR_DEBUG("Spi Request Bus error\n"); return (PX_ERROR); } API_SpiDeviceCtl(pSpi_Nor->Spi_NorDev, SPI_MODE_SET, SPI_MODE_0); /* 設置SPI模式爲MODE 0 */ API_SpiDeviceBusRelease(pSpi_Nor->Spi_NorDev); /* 釋放SPI總線 */
3.2申請片選線
申請相應的GPIO做爲片選線,並默認拉高,在後續操做時可經過調用操做系統GPIO相關的API接口改變片選線狀態。具體實現如程序清單 3-3所示。
程序清單 3-3 GPIO初始化
static INT __ssGpioInit (UINT uiSSPin) { INT iRet; iRet = API_GpioRequestOne(uiSSPin, LW_GPIOF_OUT_INIT_HIGH, "SS0"); /* 申請片選線並默認拉高 */ if (iRet < 0) { printf("API_GpioRequestOne error \n"); } _G_spiNorObj.uiSSPin = uiSSPin; return (ERROR_NONE); }
3.3擦除功能
Nor Flash在寫操做前大多須要先擦除,常見的擦除指令可擦除4K、64K以及整片擦除。本例採用4K擦除,命令爲20h,其時序如圖 3-1所示。
圖 3-1 SPI Flash擦除時序
擦除操做屬於寫操做,所以在擦除以前須要設置狀態寄存器寫使能位,並等待寫操做完成,擦除以後也須要等待擦除操做完成。代碼實現如程序清單 3-4所示。
程序清單 3-4擦除函數
static INT __flashErase (ULONG uladdr, UINT uiBlockSize) { INT iError; UINT8 ucTxCmd[4]; PSPI_NOR_OBJ pSpi_Nor = &_G_spiNorObj; LW_SPI_MESSAGE spiCmdMessage = { .SPIMSG_uiLen = 4, .SPIMSG_pucWrBuffer = ucTxCmd, .SPIMSG_pucRdBuffer = NULL, }; ucTxCmd[0] = ERASE_4K; ucTxCmd[1] = uladdr >> 16; ucTxCmd[2] = uladdr >> 8; ucTxCmd[3] = uladdr >> 0; iError = API_SpiDeviceBusRequest(pSpi_Nor->Spi_NorDev); /* 申請SPI總線 */ if (iError == PX_ERROR) { NOR_DEBUG("Spi Request Bus error\n"); return (PX_ERROR); } __flashWriteEnable(); /* 寫使能 */ __flashWaitForIdle(); SPI_ENABLE_SS(); API_SpiDeviceTransfer(_G_spiNorObj.Spi_NorDev, &spiCmdMessage, 1); /* 開始擦除 */ SPI_DISABLE_SS(); __flashWaitForIdle(); API_SpiDeviceBusRelease(pSpi_Nor->Spi_NorDev); /* 釋放SPI總線 */ return (ERROR_NONE); }
3.4寫功能
根據不一樣的模式,SPI Flash有多種寫指令,其寫速度也相應不一樣。本例使用標準SPI指令02h,其時序如圖 3-2所示。
圖 3-2 SPI Flash寫時序
與擦除相同,在寫以前須要寫使能,寫以後須要等待寫操做完成,其代碼實現如程序清單 3-5所示。
程序清單 3-5寫函數
static INT __pageWrite(const VOID *pvdata, ULONG ulAddr, UINT uiLen) { PSPI_NOR_OBJ pSpi_Nor = &_G_spiNorObj;; UCHAR *pucBuf = (UINT8 *)pvdata; INT32 iRemainLen = uiLen; UINT8 ucTxCmd[4]; INT iError; LW_SPI_MESSAGE spiCmdMessage = { .SPIMSG_uiLen = 4, .SPIMSG_pucWrBuffer = ucTxCmd, .SPIMSG_pucRdBuffer = NULL, }; ucTxCmd[0] = PAGE_PRO; ucTxCmd[1] = ulAddr >> 16; ucTxCmd[2] = ulAddr >> 8; ucTxCmd[3] = ulAddr >> 0; LW_SPI_MESSAGE spiRdMessage = { .SPIMSG_uiLen = iRemainLen, .SPIMSG_pucWrBuffer = pucBuf, .SPIMSG_pucRdBuffer = NULL, }; iError = API_SpiDeviceBusRequest(pSpi_Nor->Spi_NorDev); /* 申請SPI總線 */ if (iError == PX_ERROR) { NOR_DEBUG("Spi Request Bus error\n"); return (PX_ERROR); } __flashWriteEnable(); /* 寫使能 */ __flashWaitForIdle(); SPI_ENABLE_SS(); API_SpiDeviceTransfer(_G_spiNorObj.Spi_NorDev, &spiCmdMessage, 1); /* 發送寫指令和地址 */ API_SpiDeviceTransfer(_G_spiNorObj.Spi_NorDev, &spiRdMessage, 1); /* 進行數據傳輸 */ SPI_DISABLE_SS(); SPI_ENABLE_SS(); WRITE_DISABLE(); /* 關閉寫使能 */ SPI_DISABLE_SS(); __flashWaitForIdle(); API_SpiDeviceBusRelease(pSpi_Nor->Spi_NorDev); /* 釋放SPI總線 */ return (ERROR_NONE); }
3.5讀操做
與寫操做相同,在不一樣的SPI模式下,讀操做有不一樣的指令,讀速度也不一樣。本例採用標準SPI指令03h,其時序圖如圖 3-3所示。
圖 3-3 SPI Flash讀時序
具體代碼實現如程序清單 3-6所示。
程序清單 3-6讀函數
static INT __flashRead (ULONG ulAddr, UINT uiLen, VOID *pvData) { INT iError; UINT8 ucTxCmd[4]; PSPI_NOR_OBJ pSpi_Nor = &_G_spiNorObj; LW_SPI_MESSAGE spiCmdMessage = { .SPIMSG_uiLen = 4, .SPIMSG_pucWrBuffer = ucTxCmd, .SPIMSG_pucRdBuffer = NULL, }; ucTxCmd[0] = READ; ucTxCmd[1] = ulAddr >> 16; ucTxCmd[2] = ulAddr >> 8; ucTxCmd[3] = ulAddr >> 0; LW_SPI_MESSAGE spiRdMessage = { .SPIMSG_uiLen = uiLen, .SPIMSG_pucWrBuffer = NULL, .SPIMSG_pucRdBuffer = pvData, }; iError = API_SpiDeviceBusRequest(pSpi_Nor->Spi_NorDev); /* 申請SPI總線 */ if (iError == PX_ERROR) { NOR_DEBUG("Spi Request Bus error\n"); return (PX_ERROR); } SPI_ENABLE_SS(); API_SpiDeviceTransfer(_G_spiNorObj.Spi_NorDev, &spiCmdMessage, 1); /* 發送讀指令和地址 */ API_SpiDeviceTransfer(_G_spiNorObj.Spi_NorDev, &spiRdMessage, 1); /* 進行數據傳輸 */ SPI_DISABLE_SS(); API_SpiDeviceBusRelease(pSpi_Nor->Spi_NorDev); /* 釋放SPI總線 */ return (ERROR_NONE); }
4.參考資料
《EN25Q128》