打印

GD32F105单片机与W25Q16的NOR Flash通信存储LED字库,擦除和写入会有小问题

[复制链接]
1800|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
jinyunfeng|  楼主 | 2019-4-28 14:38 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
目前需要写入250K左右的flash数据,单片机通过串口接收的数据写入flash芯片,结果1.烧写不彻底,写入时候会有几页数据出现没有写入,还是256个0xFF的情况,2.读取的时候会偶尔几位数据读取是错误的,而且还会出现错位,比如读取0x000000的地址数据0x10,0x10,0x10,结果读出来的数据是0xFF或者0x00,0x10,x10,0x10的情况,3.有时候第一次读取的是三个0x10是正确的,但读取第7次或者第10次后会出现0x10,0xFF,0x10,中间这一位数据是错的,接下来读的第八次,第九次又正确了,一直没解决掉这3个问题,第1种情况目前是通过人为程序读取后判断是否数据都是0xFF,是的话再重新写入一遍从而暂时解决写入不完全的情况。请大神帮忙指点一下,谢谢!

使用特权

评论回复
沙发
lvben5d| | 2019-4-28 19:42 | 只看该作者
本帖最后由 lvben5d 于 2019-4-28 19:45 编辑

用的不多  但没遇到过这个情况, 你看下,会不会是自己代码软件问题。 比如你串口接收的处理 是否来得及 收1个 擦1个。。应用没讲清,下面是我调试过OK的 w25 FLASH 你可以参考。
/*返回 1 表示超时 学习Linux的返回值方式 0表示成功*/
unsigned char w25qxx_wait_idle(void)
{
    unsigned char  w25q_state=0x0F;
    unsigned short int timeout=0;
    W25QXX_CS_L;
    SEND_BYTE(W25Q_ReadStatusReg);
    while((w25q_state&0x01) && (++timeout<60000))
    {         
        w25q_state = RECV_BYTE(W25Q_ReadStatusReg);
        delay_us(20);
    }  
    W25QXX_CS_H;
    if(timeout==60000)  return 1;
    else                return 0;
}
/*当用户的MCU主频过快的时候,片选时间需要注意*/
void w25qxx_write_enable(void)
{
    W25QXX_CS_L;
    SEND_BYTE(W25Q_WriteEnable);  //使能写寄存器
    W25QXX_CS_H;   
}

void w25qxx_write_disable(void)
{
    W25QXX_CS_L;
    SEND_BYTE(W25Q_WriteDisable);  //禁能写寄存器
    W25QXX_CS_H;   
}

/*芯片一次擦除的是4KB 所以我们应用需要安排好*/
void w25qxx_sector4KB_erase(unsigned long sector_addr)
{   
    //u8 state ;
    //state  = w25qxx_wait_idle();
    //if(state!=0)  return 1; //这段代码加这里 模块就不能正常擦除。见鬼
    w25qxx_write_enable();
    W25QXX_CS_L;
    SEND_BYTE(W25Q_SectorErase);
    SEND_BYTE(sector_addr >> 16);
    SEND_BYTE(sector_addr >> 8);
    SEND_BYTE(sector_addr);
    W25QXX_CS_H;
   
    //state  = w25qxx_wait_idle();
    /*
    if(state!=0)   return 1;     //超时   加这里就没事。 奇怪
    */
}

/*芯片块擦除分32KB和64KB */
void w25qxx_block32KB_erase(unsigned long block_addr)
{
    w25qxx_write_enable();
    W25QXX_CS_L;
    SEND_BYTE(W25Q_BlockErase_32KB);
    SEND_BYTE(block_addr >> 16);
    SEND_BYTE(block_addr >> 8);
    SEND_BYTE(block_addr);
    W25QXX_CS_H;
}

/*
    一般擦除一个扇区,然后从页的首地址写入信息,因为FLASH的特性决定这样应用合适!
    需要先读出这页的数据到RAM,
    编译器会自动取0x12345678  低字节0x78  如果大小端方式不同,则需要注意修改。
    注意: 用户需注意自己传递的指针p的范围,避免越界!!1~256个字节
*/
unsigned char w25qxx_write_Nbytes(unsigned long page_addr, unsigned char *p, unsigned short int m)
{
    unsigned short int  cnt;  
    cnt = w25qxx_wait_idle(); //确保上一次擦或写的操作已经完成。
    if(cnt!=0)   return 1;  //超时
    w25qxx_write_enable();   
    if(m==0)  { m=1;  return 2; };         //不能传递0个数据写入SPI FLASH
    W25QXX_CS_L;   
    SEND_BYTE(W25Q_PageProgram);
    SEND_BYTE(page_addr >> 16);
    SEND_BYTE(page_addr >> 8);
    SEND_BYTE(page_addr);
    for(cnt=0; cnt<m; cnt++) //1~256个字节
    {
        SEND_BYTE(*p++);   
    }
    W25QXX_CS_H;

    return 0;               //成功
}

/*只要CS步拉高,就可以一直读下去*/
unsigned char w25qxx_read_Nbytes(unsigned long page_addr, unsigned char *prcv_buf, unsigned short int m)
{
    unsigned short int  cnt;
    cnt = w25qxx_wait_idle();
    if(cnt!=0)   return 1;   //超时
    W25QXX_CS_L;
    SEND_BYTE(W25Q_ReadData);//W25Q_ReadData);
    SEND_BYTE(page_addr >> 16);
    SEND_BYTE(page_addr >> 8);
    RECV_BYTE(page_addr);
    for(cnt=0; cnt<m; cnt++) //1~256个字节
    {
       *prcv_buf = RECV_BYTE(0x00);
       prcv_buf++;
    }
    W25QXX_CS_H;
    return 0;
}

使用特权

评论回复
板凳
jinyunfeng|  楼主 | 2019-4-29 09:38 | 只看该作者
被网上的例程给坑死了,找了好多天问题,谁知道是发送命令写的有问题,搞个全局变量还不清0,直接不等待发送和接收状态就跳出了。
uint16_t Delay_Count=0;


GD25Q_StatusTypeDef GD25QStatus = GD25Q16CSIG_OK;

void         Delay_US(uint32_t x)
{
        uint32_t i,y;
        y=x*10;
        for(i=0;i<y;i++)
        {
                ;
        }
}


uint8_t HAL_SPI_TransmitReceive(uint8_t *Data)
{
    while(spi_i2s_flag_get(SPI0, SPI_FLAG_TBE) == RESET)
                {
      Delay_Count++;
                        if(Delay_Count>2000)
                        {
                                return 1;
                        }
                }                       
    spi_i2s_data_transmit(SPI0, Data[0]);
    while(spi_i2s_flag_get(SPI0, SPI_FLAG_RBNE) == RESET)
                {
      Delay_Count++;
                        if(Delay_Count>2000)
                        {
                                return 1;
                        }
                }
    Data[1] = spi_i2s_data_receive(SPI0);
    return 0;
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

2

主题

7

帖子

0

粉丝