TM4C系列操作w25q128flash

2018-01-04  本文已影响0人  唐超1994

1.flash说明

1.W25Q64 是华邦公司推出的大容量SPIFLASH 产品,W25Q64 的容量为 64Mb,W25Q128的容量为128Mb。W25Q64 的擦写周期多达 10W 次,具有 20 年的数据保存期限,支持电压为 2.7~3.6V。

2.操作时序。

    根据手册说明,发送指令需要将CS拉低。指令发送完毕在将CS拉高。指令有可能是单字节有可能是多字节。

下图为写指令时序示意图。

2.SPI操作说明


   1.SPI每发送一个数据的同时会接收到一个字节的数据

   2.SPI有4条线,MISO,MOSI,SCLK三条数据线,还有片选线CS,片选线对于SPI接口的从设备是低电平有效,主机输出一个低电平从机就被选中。这样就方便一个主机可以连接多个从设备,只需要使用不同的片选线。 

3.TM4C SPI初始化

根据W25Q时序要求有两种方式处理

1.配置为普通SPI。初始化时将FSS配置为普通IO口。根据需求拉高或者拉低。

2.官方的高级SPI模式,

源码:

SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2);//使能SPI2时钟

SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOD);//使能GPIOD时钟

    GPIOPinConfigure(GPIO_PD0_SSI2XDAT1);//SPI2 IO 口定义

    GPIOPinConfigure(GPIO_PD1_SSI2XDAT0);

    GPIOPinConfigure(GPIO_PD2_SSI2FSS);

    GPIOPinConfigure(GPIO_PD3_SSI2CLK);

    GPIOPinTypeSSI(GPIO_PORTD_BASE, GPIO_PIN_0 | GPIO_PIN_1 |GPIO_PIN_2|

                      GPIO_PIN_3);

#if defined(TARGET_IS_TM4C129_RA0) ||                                        \

    defined(TARGET_IS_TM4C129_RA1) ||                                        \

    defined(TARGET_IS_TM4C129_RA2)

    SSIConfigSetExpClk(SSI2_BASE, ui32SysClock, SSI_FRF_MOTO_MODE_0,

                      SSI_MODE_MASTER, 1000000, 8); //设置SSI 时钟 模式

    SSIAdvModeSet(SSI2_BASE,SSI_ADV_MODE_READ_WRITE);

#else

    SSIConfigSetExpClk(SSI2_BASE, SysCtlClockGet(), SSI_FRF_MOTO_MODE_0,

                      SSI_MODE_MASTER, 1000000, 8);

#endif

    SSIAdvFrameHoldEnable(SSI2_BASE);

    SSIEnable(SSI2_BASE);//使能SPI2

4.相关操作函数源码

在往某个地址写之前必须确保这个地址上的值是0xFF,否则说明这个地址以前被写过数据,还没有被擦除。W25Q64擦除的最小单位是Sector也就是4k个字节,也就是说如果要想往某个地址写一个值,如果这个地址上的值不是0xFF,那么就要把整个扇区都擦除,然后在写。

  给W25Q64开辟一个4k的缓存,比如定义一个4k的数组,然后在写数据之前先判断如果这个地址上的数据不是0xFF,就先把这个地址所在的Sector里的数据全部保存在4k缓存中,再擦除这个扇区,再把缓存中对应的地址上的数据更新,再把这个4k缓存区的所有数据一次性的写入到这个Sector中。

相关源码

void delayms(uint16_t ms) //延时函数

{

uint8_t i;

while(ms--)

for(i = 0; i < 120; i ++);

}

void Spi_WriteByte(uint8_t TxData)//写入单字节

{

    SSIDataPut(SSI2_BASE,(uint32_t) TxData);

    SSIDataGet(SSI2_BASE,&SpiReviceBuf[0]);

}

void Spi_Flash_Write_Enable(void)//写使能

{

    SSIAdvDataPutFrameEnd(SSI2_BASE,W25X_WriteEnable);

    while(SSIBusy(SSI2_BASE))

    {

    }

    SSIDataGet(SSI2_BASE,&SpiReviceBuf[0]);

}

void Spi_Flash_Write_Disable(void)//写禁止

{

    SSIAdvDataPutFrameEnd(SSI2_BASE,W25X_WriteDisable);

    while(SSIBusy(SSI2_BASE))

    {

    }

    SSIDataGet(SSI2_BASE,&SpiReviceBuf[0]);

}

void W25QXX_Erase_Sector(uint32_t Dst_Addr)//擦除扇区

{

    Dst_Addr*=4096;

    Spi_Flash_Write_Enable();

    W25QXX_Wait_Busy();

    Spi_WriteByte(W25X_SectorErase);

    Spi_WriteByte((uint8_t)((Dst_Addr)>>16));

    Spi_WriteByte((uint8_t)((Dst_Addr)>>8));

    SSIAdvDataPutFrameEnd(SSI2_BASE,(uint8_t)((Dst_Addr)>>0));

    while(SSIBusy(SSI2_BASE))

    {

    }

    SSIDataGet(SSI2_BASE,&SpiReviceBuf[0]);

    W25QXX_Wait_Busy();

}

uint8_t SPI_Flash_ReadSR(void)//读取flash状态

{

    uint32_t data;

    Spi_WriteByte(W25X_ReadStatusReg);

    SSIAdvDataPutFrameEnd(SSI2_BASE,0xFF);

    while(SSIBusy(SSI2_BASE))

    {

    }

    SSIDataGet(SSI2_BASE,&data);

    return (uint8_t)data;

}

void W25QXX_Wait_Busy()//等待flash

{

    uint8_t Status;

    do{

        Status = SPI_Flash_ReadSR();

        Status &= 0x01;

    }while(Status != 0);

}

void W25QXX_Read(uint8_t *pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead)//读取

{

    uint16_t i;

    uint32_t data;

    Spi_WriteByte(W25X_ReadData);

    Spi_WriteByte((uint8_t)((ReadAddr)>>16));

    Spi_WriteByte((uint8_t)((ReadAddr)>>8));

    Spi_WriteByte((uint8_t)((ReadAddr)>>0));

    while(SSIDataGetNonBlocking(SSI2_BASE, &SpiReviceBuf[0])) //清除缓冲区

    {

    }

    for(i=0;i < NumByteToRead;i++)

    {

       if(i == NumByteToRead - 1)

        {

            SSIAdvDataPutFrameEnd(SSI2_BASE,0xFF);

            while(SSIBusy(SSI2_BASE))

            {

            }

        }

        else

            SSIDataPut(SSI2_BASE,0XFF);  //循环读数,只需要不停的发送0xff,就可以读出数据

        SSIDataGet(SSI2_BASE,&data);

        pBuffer[i] = (uint8_t)data;

    }

}

void W25QXX_Write_Page(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)//写一页

{

    uint16_t i;

    Spi_Flash_Write_Enable();

    Spi_WriteByte(W25X_PageProgram);

    Spi_WriteByte((uint8_t)((WriteAddr)>>16));

    Spi_WriteByte((uint8_t)((WriteAddr)>>8));

    Spi_WriteByte((uint8_t)((WriteAddr)>>0));

    for(i = 0;i < NumByteToWrite;i++)

    {

        if(i == NumByteToWrite - 1)

        {

            SSIAdvDataPutFrameEnd(SSI2_BASE,pBuffer[i]);

            while(SSIBusy(SSI2_BASE))

            {

            }

            SSIDataGet(SSI2_BASE,&SpiReviceBuf[0]);

        }

        else

            Spi_WriteByte(pBuffer[i]);

    }

    W25QXX_Wait_Busy();

}

void W25QXX_Write_NoCheck(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)//写,但不校验是否已擦除

{

    uint16_t pageremain;

    pageremain=256-WriteAddr%256;

    if(NumByteToWrite<=pageremain)

    pageremain=NumByteToWrite;

    while(1)

    {

        W25QXX_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个字节了

        }

    }

}

void W25QXX_Write(uint8_t *pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite)//写

{

    uint32_t secpos;

    uint16_t secoff;

    uint16_t secremain;

    uint16_t i;

    uint8_t * W25QXX_BUF;

    W25QXX_BUF=W25QXX_BUFFER;

    secpos=WriteAddr/4096;//算出来扇区地址

    secoff=WriteAddr%4096;//取余数,算出在扇区内的偏移

    secremain=4096-secoff;//扇区剩余空间大小

    if(NumByteToWrite<=secremain) //没有跨扇区

        secremain=NumByteToWrite;

    while(1)

    {

        W25QXX_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容,保存在Buffer中。

        for(i=0;i < secremain;i++)  //校验数据

        {

            if(W25QXX_BUF[secoff+i]!=0XFF)

                break;//如果有不等于0xFF的数据,就需要擦除

        }

        if(i < secremain)//需要擦除

        {

            W25QXX_Erase_Sector(secpos); //擦除这个扇区

            for(i=0;i < secremain;i++)  //更新缓存中的数据

            {

                W25QXX_BUF[ secoff + i ]=pBuffer[i];  //这里的pBuffer中是我们要写的数据,把这些数据更新到缓存中对应的                                                              位置

            }

            W25QXX_Write_NoCheck(W25QXX_BUF,secpos*4096,4096);//重新写入整个扇区

        }

        else

            W25QXX_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间.

        if(NumByteToWrite==secremain)

            break;//写入结束了

    else//写入未结束

        {

            secpos++;//扇区地址增1

            secoff=0;//偏移位置为0

          pBuffer+=secremain;  //指针偏移

          WriteAddr+=secremain; //写地址偏移

          NumByteToWrite-=secremain; //字节数递减

        if(NumByteToWrite>4096)

                secremain=4096;//下一个扇区还是写不完

        else

            secremain=NumByteToWrite; //下一个扇区可以写完了

        }

    }

}

上一篇下一篇

猜你喜欢

热点阅读