美文网首页
TM4C系列操作w25q128flash

TM4C系列操作w25q128flash

作者: 唐超1994 | 来源:发表于2018-01-04 11:42 被阅读0次

    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; //下一个扇区可以写完了

            }

        }

    }

    相关文章

      网友评论

          本文标题:TM4C系列操作w25q128flash

          本文链接:https://www.haomeiwen.com/subject/zuxrnxtx.html