SylixOS SPI Flash驱动移植

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huikai309/article/details/61196650

1.开发环境

    操作系统:SylixOS

    编程环境:RealEvo-IDE3.0

    开发板:MDK972

    Nor FlashEN25Q128

2.原理概述

    NOR FLASH是一种常见的存储芯片,数据掉电不会丢失,支持片内执行,因此在嵌入式系统中,常作为启动程序的存储介质。根据传输的数据位,Nor Flash可分为并行NorParallelFlash和串行(SPIFlashSPI Nor FlashParallel Flash便宜,接口简单,但速度慢。本文介绍的是SylixOS下基于NUC970SPI Flash驱动。

    通过读取或配置Nor Flash的状态寄存器,可以获得或改变Flash的当前状态,每一位的含义如图 2-1所示。

2-1 SPI Flash状态寄存器

    根据SPI的传输方式,常见的指令可分为标准SPI指令、Dual SPI指令和Quad SPI指令。常见的指令如图 2-2、图 2-3所示。

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在写操作前大多需要先擦除,常见的擦除指令可擦除4K64K以及整片擦除。本例采用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

猜你喜欢

转载自blog.csdn.net/huikai309/article/details/61196650