SylixOS SPI Flash驅動移植

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》

相關文章
相關標籤/搜索