关于EFM32下的w25q256的使用

首先吐槽新浪博客,粘代码上去提示我非法字符,转战CSDN。
使用的主芯片是efm32wg256,spiflash和主芯片的接口情况如下:
spiflash和主芯片的接口状况
首先进行spi通信的配置:

/* Enable clock for USART2 */
CMU_ClockEnable(cmuClock_USART2, true);
extern void USART2_enter_DefaultMode_from_RESET(void)
{

    // $[USART_InitAsync]
    // [USART_InitAsync]$

    // $[USART_InitSync]
    USART_InitSync_TypeDef initsync = USART_INITSYNC_DEFAULT;

    initsync.baudrate = 20000000;
    initsync.databits = usartDatabits8;
    initsync.master = 1;
    initsync.msbf = 1;
    initsync.clockMode = usartClockMode0;
#if defined( USART_INPUT_RXPRS ) && defined( USART_TRIGCTRL_AUTOTXTEN )
    initsync.prsRxEnable = 0;
    initsync.prsRxCh = 0;
    initsync.autoTx = 0;
#endif

    USART_InitSync(USART2, &initsync);
    // [USART_InitSync]$

    // $[USART_InitPrsTrigger]
    USART_PrsTriggerInit_TypeDef initprs = USART_INITPRSTRIGGER_DEFAULT;

    initprs.rxTriggerEnable = 0;
    initprs.txTriggerEnable = 0;
    initprs.prsTriggerChannel = usartPrsTriggerCh0;

    USART_InitPrsTrigger(USART2, &initprs);
    // [USART_InitPrsTrigger]$

}
/* Pin PB3 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 3);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE3_MASK)
        | GPIO_P_MODEL_MODE3_PUSHPULLDRIVE;
/* Pin PB4 is configured to Input enabled*/
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE4_MASK)
        | GPIO_P_MODEL_MODE4_INPUT;
/* Pin PB5 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 5);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE5_MASK)
        | GPIO_P_MODEL_MODE5_PUSHPULLDRIVE;
/* Pin PB6 is configured to Push-pull with alt. drive strength */
GPIO->P[1].DOUT |= (1 << 6);
GPIO->P[1].MODEL = (GPIO->P[1].MODEL & ~_GPIO_P_MODEL_MODE6_MASK)
        | GPIO_P_MODEL_MODE6_PUSHPULLDRIVE;

配置完通信开始编写spiflash的驱动文件:bsp_spiflash.c
首先是初始化函数:

#define W25QXX_CS(x)        x?(GPIO_PinOutSet(gpioPortB,6)):(GPIO_PinOutClear(gpioPortB,6));        //W25QXX的片选信号
void W25QXX_Init(void)
{
    W25QXX_CS(1);           //SPI FLASH不选中

    //复位SPI 到原始状态,防止上次命令死锁导致初始化失败
    uint32_t i;
    W25QXX_Wait_Busy();
    W25QXX_Write_Enable();
    W25QXX_Wait_Busy();
    W25QXX_CS(0);
    SPI1_ReadWriteByte(W25X_ResetEnable);
    W25QXX_CS(1);
    i=3904;
    while (i-- > 0) __asm__("nop");
    W25QXX_CS(0);
    SPI1_ReadWriteByte(W25X_Reset);
    W25QXX_CS(1);
    i=11904;
    while (i-- > 0) __asm__("nop");

    //此时的等待是必须的
    W25QXX_Wait_Busy();
    W25QXX_Write_Enable();
    W25QXX_Wait_Busy();
    W25QXX_CS(0);
    /* Send "Write SR3 " instruction */
    SPI1_ReadWriteByte(0x11);
    /* Enable 100% OUT PUT DRIVER STRENGTH 3 byte地址模式 */
    SPI1_ReadWriteByte(0x00);
    W25QXX_CS(1);

    //此时的等待是必须的,设置w25qXX为4byte模式
    W25QXX_Wait_Busy();
    W25QXX_Write_Enable();
    W25QXX_Wait_Busy();
    W25QXX_CS(0);
    SPI1_ReadWriteByte(W25X_4byteEnable);
    W25QXX_CS(1);
}

之后使用了原子的spiflash的读写擦除等一系列函数,只是将读写地址改为4byte模式,重定义了spi发送指令的函数

//SPI 读写一个字节
//Tx Data:要写入的字节
//返回值:读取到的字节
uint8_t SPI1_ReadWriteByte(uint8_t data)
{
    return USART_SpiTransfer(USART2,data);
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(32bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
    u16 i;
    W25QXX_CS(0);                            //使能器件
    SPI1_ReadWriteByte(W25X_ReadData);         //发送读取命令
    SPI1_ReadWriteByte((u8)((ReadAddr)>>24));  //发送32bit地址
    SPI1_ReadWriteByte((u8)((ReadAddr)>>16));
    SPI1_ReadWriteByte((u8)((ReadAddr)>>8));
    SPI1_ReadWriteByte((u8)ReadAddr);
    for(i=0;i<NumByteToRead;i++)
    {
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);   //循环读数
    }
    W25QXX_CS(1);
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(32bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
    u16 i;
    W25QXX_Write_Enable();                  //SET WEL
    W25QXX_CS(0);                            //使能器件
    SPI1_ReadWriteByte(W25X_PageProgram);      //发送写页命令
    SPI1_ReadWriteByte((u8)((WriteAddr)>>24));  //发送32bit地址
    SPI1_ReadWriteByte((u8)((WriteAddr)>>16));
    SPI1_ReadWriteByte((u8)((WriteAddr)>>8));
    SPI1_ReadWriteByte((u8)WriteAddr);
    for(i=0;i<NumByteToWrite;i++)SPI1_ReadWriteByte(pBuffer[i]);//循环写数
    W25QXX_CS(1);                            //取消片选
    W25QXX_Wait_Busy();                    //等待写入结束
}

原子的spiflash写函数自带4k区擦除功能,使用很方便。
现在公司要求管理w25q256,每秒向里面写入1帧解析后的GPS数据,掉电后从上次写入的地址继续往下写。我暂定每帧数据为64字节,实现的逻辑是:每次写入1帧数据之后,将本次写入的地址保存在flash最后的4k区内,每个地址占4字节。每次写入数据之前,先从最后的4k区查询到上次写入的地址,然后地址偏移继续往后写。查询上次地址的方法为,从最后4k区第一个地址往后查询,查询到第一个空的帧,往前偏移1帧的地址,即为上次flash写入的地址,当最后4k区写满之后,进行一次扇区擦除。

//SPIflash写GPS数据的任务
void add_data_to_flash (void *p)
{
    static uint32_t last_time=1;
    if (last_time == (time_speed.time)) return;

    uint8_t data[64];
    uint8_t addr[4];
    uint32_t address=0;
    uint32_t i;

    //处理4k地址区
    for(i=0;i<4096;i+=4)
    {
        W25QXX_Read(addr,SPIFLASH_SIZE-4096+i,4);//从地址4K区读取地址
        if ((addr[0] == 0xFF)&&(addr[1] == 0xFF)&&(addr[2] == 0xFF)&&(addr[3] == 0xFF))//读取到本次需要写入的4K地址区地址
        {
            if(i==0)//如果4K地址区为空
            {
                address = 0;
                addr[0] = 0x00;
                addr[1] = 0x00;
                addr[2] = 0x00;
                addr[3] = 0x00;
                break;
            }
            else
            {
                i-=4;
                W25QXX_Read(addr,SPIFLASH_SIZE-4096+i,4);//读取到上次flash写到的地址
                address = (((uint32_t)addr[0]))+(((uint32_t)(addr[1]))<<8)+(((uint32_t)(addr[2]))<<16)+(((uint32_t)(addr[3]))<<24);
                i+=4;
                break;
            }
        }
        else if(i == 4092)
            address = (((uint32_t)addr[0]))+(((uint32_t)(addr[1]))<<8)+(((uint32_t)(addr[2]))<<16)+(((uint32_t)(addr[3]))<<24);
    }

    //处理得到的上次写入的地址
    if (i > 0)
    {
        address = address+(flash_frame_size);
        addr[0] = (uint8_t)(address);
        addr[1] = (uint8_t)(address>>8);
        addr[2] = (uint8_t)(address>>16);
        addr[3] = (uint8_t)(address>>24);
    }
    if (address >= SPIFLASH_SIZE-4096)//如果数据区已写满
    {
        address = 0;
        addr[0] = 0;
        addr[1] = 0;
        addr[2] = 0;
        addr[3] = 0;
    }

    //把新数据写进下一帧地址
    sprintf (data,"%d %d %d %d %d %d %d\r\n"
            ,(uint32_t)(pGPS_Data.Hour+8)*10000+(uint32_t)pGPS_Data.Min*100+(uint32_t)pGPS_Data.Sec
            ,(uint32_t)(time_speed.speed*100)
            ,time_speed.latitude_int
            ,time_speed.longitude_int
            ,time_speed.count_satellite
            ,pGPS_Data.PUBX_data.avg_cno
            ,hr);
    W25QXX_Write(data,address,flash_frame_size);

    last_time = time_speed.time;

    if(i>=4092)//如果4K地址区已经写满
    {
        W25QXX_Erase_Sector((SPIFLASH_SIZE-4096)/4096);//擦除4k地址扇区
        i=0;
    }
    W25QXX_Write(addr,SPIFLASH_SIZE-4096+i,4);//将本次地址写入4K地址区
}

这个函数在主函数中以时间片的形式调用,周期为300ms。使用RTOS的话加入系统延时函数即可。

展开阅读全文

没有更多推荐了,返回首页