⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 samsung_nand.c

📁 新一代基于事件的嵌入式操作系统dyos在三星的s3c44b0的arm芯片上的完整移植代码
💻 C
📖 第 1 页 / 共 4 页
字号:
}


//----写一个扇区(with ecc)-------------------------------------------------------------
//功能: 写一个扇区,带ecc校验
//参数: sector,扇区号
//      offset,扇区内偏移地址
//      data,数据缓冲区
//      size,写入的尺寸
//返回:cn_all_right_verify=正确写入,cn_ecc_error_verify=写入错误
//-----------------------------------------------------------------------------
uint32_t __write_sector_2808_with_ecc(uint32_t sector,uint32_t offset,
                                      uint8_t *data,uint32_t size)
{
    uint32_t i;
    uint32_t address;
    uint8_t ecc[(cn_sector_size+255)/256*3];          //每256字节产生一个ECC码
    if((offset != 0) || (size != cn_sector_size))
    {
        __read_sector_2808_with_ecc(sector,0,pg_sector_buf,cn_sector_size);
        //执行ECC校验,但是不判断校验结果,因为扇区写入前可能是随机数据,校验错
        //误并不能说明发生了错误
    }
    memcpy(pg_sector_buf+offset,data,size);
    __make_sector_ecc(pg_sector_buf, ecc);               //计算ecc代码
    ce_active();
    __write_command_2808(0x00);

    __write_command_2808(cn_2808_page_program); //启动编程命令
    address=cn_sector_size*sector;  //写入起始地址
    address_start();    //启动写入地址时序
    *pg_nand_address = (uint8_t)address;         // A0 ~ A7
    *pg_nand_address = (uint8_t)(address >> 9);  // A9 ~ A16
    *pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
    address_end();      //结束写入地址时序
    __wait_ready_2808( );  //等待芯片内部操作完成

    for(i=0; i<(cn_sector_size); i++)
    {//逐个把待写入的数据写入到器件的扇区缓冲区
        *pg_nand_address = pg_sector_buf[i];
       // printf("WriteFlash: data = 0x%x\n", data);
    }
    if(cn_sector_size > 256)
    {
        for(i = 0; i < 5; i++)              //写入校验码,坏块标志前的5字节
            *pg_nand_address = ecc[i];
        *pg_nand_address = 0xff;            //坏块标志,写0xff相当于保持原值
        for(; i < cn_sector_size/256*3; i++)//写入校验码,坏块标志后的部分
            *pg_nand_address = ecc[i];
    }else
    {
        for(i = 0; i < 3; i++)              //写入校验码,只有3字节
            *pg_nand_address = ecc[i];
    }
    __write_command_2808(cn_2808_startup_write);  //启动芯片内部写入过程
    __wait_ready_2808_slow(cn_wait_page_write);  //等待芯片内部操作完成

    if(__read_status_2808() & cn_2808_failure)
    {
        ce_inactive();
        return cn_ecc_error_verify;
    }
    ce_inactive();
    return cn_all_right_verify;
}

//----写块(带ecc校验)----------------------------------------------------
//功能: 带ecc校验把缓冲区写入块内,地址不能跨块边界
//参数: sector,块号
//      offset,块内偏移地址
//      data,保存读取数据的缓冲区
//      size,读取的尺寸
//返回:实际写入的数据量,cn_limit_uint32表示出错
//-----------------------------------------------------------------------------
uint32_t write_block_ss_with_ecc(uint32_t block,uint32_t offset,
                                 uint8_t *buf,uint32_t size)
{
    uint32_t start_sector;  //首扇区号
    uint32_t start_offset;  //首地址在首扇区的偏移量
    uint32_t end_sector;    //结束地址所在扇区
    uint32_t end_offset;    //结束地址在扇区中的偏移量
    uint32_t cur_sector;    //当前正在写的扇区
    uint32_t write_size;     //从当前扇区写入的数据量
    uint32_t completed = 0;
    uint32_t verify;

    if(block >= tg_samsung_nand.block_sum)
        return cn_limit_uint32;
    if((size + offset) > tg_samsung_nand.block_size)
        return cn_limit_uint32;

    //起始扇区号
    start_sector = offset / cn_sector_size + u32g_sectors_per_block * block;
    //起始地址在起始扇区号中的偏移量
    start_offset = offset % cn_sector_size;
    //结束扇区号
    end_sector =(offset + size-1)/cn_sector_size+u32g_sectors_per_block*block;
    //结束地址在结束扇区中的偏移量
    end_offset = (offset + size -1) % cn_sector_size;
    for(cur_sector = start_sector; cur_sector <= end_sector; cur_sector++)
    {
        if(cur_sector != end_sector)    //当前扇区不是最后一个扇区
            write_size = cn_sector_size - start_offset;
        else    //当前扇区是最后一个扇区
            //+1是因为end_offset本身是需要写入的
            write_size = end_offset - start_offset +1;
        verify = __write_sector_2808_with_ecc(cur_sector,start_offset,
                                  buf+completed,write_size);
        if((verify == cn_all_right_verify) || (verify == cn_ecc_right_verify))
        {
            completed += write_size;
            start_offset = 0;   //从第二个扇区开始,肯定从0开始写
        }else
            break;
    }
    return completed;
}


//----擦除一块-------------------------------------------------------------
//功能: 擦除一块
//参数: block_no,目标块号
//返回: true = 成功擦除,false = 坏块
//-----------------------------------------------------------------------------
bool_t erase_block_2808(uint32_t block_no)
{
    uint32_t page_no;
    page_no = block_no * u32g_sectors_per_block;

    ce_active();

    __write_command_2808(cn_2808_block_erase);

    address_start();
    *pg_nand_address = (uint8_t)page_no;         // A9 ~ A16
    *pg_nand_address = (uint8_t)(page_no>>8);    // A17 ~ A22
    address_end();

    __write_command_2808(cn_2808_startup_erase);

    __wait_ready_2808_slow(cn_wait_block_erase);  //等待芯片内部操作完成

    ce_inactive();
    return true;
//    if(readStatus() & cn_2808_failure)
//      return flWriteFault;
}

//----擦除整个芯片-------------------------------------------------------------
//功能: 擦除整个芯片。
//参数: 无
//返回: true = 成功擦除,false = 坏块
//-----------------------------------------------------------------------------
bool_t __erase_all_2808(void)
{
    uint32_t i;
    for(i=0; i<tg_samsung_nand.block_sum; i++)
    {
        erase_block_2808(i);
    }
    return true;
}

//说明: 以下三个查询是否需要擦除的函数,基于flash的特征:无论块是否空,只要是1
//      的位就可以改为0,而flash的寿命由擦除次数决定。
//----查询是否需要擦除(with ecc)-----------------------------------------------
//功能: 查询在block的offset处写入buf中的size长度数据前是否需要擦除块。由于擦除
//      既慢又容易磨损flash,故本函数对加快速度和延长flash寿命很有效,在不同情况
//      下,呈现的效果并不一致。
//      1、norflash,多次写入少量数据时非常有效。
//      2、nandflash做高可靠应用,由于存在扇区ecc的问题,需要写入sector边界对齐
//        的数据才有效。
//      3、nandflash做媒体应用,音视频媒体应用可不做ecc,效果与norflash一样。
//      4、故nandflash只判断是否全FF

//      对第二种情况,一个改进方法是每次写零碎数据时均新开一个扇区,待扇区用完以
//      后才整体压缩擦除,但太过麻烦,以后再说吧。
//参数: sector,块号
//      offset,块内偏移地址
//      data,数据缓冲区
//      size,数据尺寸
//返回: flase = 需要擦除,true = 已准备好,不需要擦除
//-----------------------------------------------------------------------------
bool_t query_block_ready_ss_with_ecc(uint32_t block,uint32_t offset,
                              uint8_t *buf,uint32_t size)
{
    uint32_t start_sector;  //首扇区号
    uint32_t end_sector;    //结束地址所在扇区
    uint32_t cur_sector;    //当前正在读的扇区
    uint32_t address;
    uint32_t loop;
    uint8_t data;

    if(block >= tg_samsung_nand.block_sum)
        return false;
    if((size + offset) > tg_samsung_nand.block_size)
        return false;
    if(size == 0)
        return true;
    if(buf == NULL)
        return false;

    //起始扇区号
    start_sector = offset / cn_sector_size + u32g_sectors_per_block * block;
    //结束扇区号
    end_sector =(offset + size-1)/cn_sector_size+u32g_sectors_per_block*block;
    address =cn_sector_size*start_sector;     //计算实际地址
    for(cur_sector = start_sector; cur_sector <= end_sector; cur_sector++)
    {
        ce_active();                        //激活片选
        __write_command_2808(cn_2808_select_page0);    //写入读模式命令
        address_start();                    //开始写入地址
        *pg_nand_address = (uint8_t)address;         // A0 ~ A7
        *pg_nand_address = (uint8_t)(address >> 9);  // A9 ~ A16
        *pg_nand_address = (uint8_t)(address >> 17); // A17 ~ A22
        address_end();                      //完成写入地址

        __wait_ready_2808( );  //等待芯片内部操作完成

        for(loop=0; loop < cn_sector_size+cn_oob_size; loop++)
        {
            data = *pg_nand_address;    //读取数据
            if(data != 0xff)            //只要有1字节非oxff,即返回错误
            {
                ce_inactive();          //关闭片选
                return false;
            }
        }
        ce_inactive();                      //关闭片选
        address += cn_sector_size;
    }
    return true;
}

//----查询是否需要擦除(no ecc)-----------------------------------------------
//功能: 同上一个函数,不带ecc
//参数: sector,块号
//      offset,块内偏移地址
//      data,数据缓冲区
//      size,数据尺寸
//返回: flase = 需要擦除,true = 已准备好,不需要擦除
//-----------------------------------------------------------------------------
bool_t query_block_ready_2808_no_ecc(uint32_t block,uint32_t offset,
                              uint8_t *buf,uint32_t size)
{
    uint32_t start_sector;  //首扇区号
    uint32_t start_offset;  //首地址在首扇区的偏移量
    uint32_t end_sector;    //结束地址所在扇区
    uint32_t end_offset;    //结束地址在扇区中的偏移量
    uint32_t cur_sector;    //当前正在读的扇区
    uint32_t read_size;
    uint32_t address;
    uint32_t completed = 0;
    uint32_t loop;
    uint8_t data;

    if(block >= tg_samsung_nand.block_sum)
        return false;
    if((size + offset) > tg_samsung_nand.block_size)
        return false;
    if(size == 0)
        return true;
    if(buf == NULL)
        return false;

    //起始扇区号
    start_sector = offset / cn_sector_size + u32g_sectors_per_block * block;
    //起始地址在起始扇区号中的偏移量
    start_offset = offset % cn_sector_size;
    //结束扇区号
    end_sector =(offset + size-1)/cn_sector_size+u32g_sectors_per_block*block;
    //结束地址在结束扇区中的偏移量
    end_offset = (offset + size -1) % cn_sector_size;
    address = block * cn_block_size + offset;
    for(cur_sector = start_sector; cur_sector <= end_sector; cur_sector++)
    {
        if(cur_sector != end_sector)    //当前扇区不是最后一个扇区
            read_size = cn_sector_size - start_offset;
        else    //当前扇区是最后一个扇区
            //+1是因为end_offset本身是需要写入的
            read_size = end_offset - start_offset +1;
        if(address & 0x100)
            __write_command_2808(cn_2808_select_page1);    //写入读模式命令
        else
            __write_command_2808(cn_2808_select_page0);    //写入读模式命令
        address_start();                    //开始写入地址
        *pg_nand_address = (uint8_t)address;         // A0 ~ A7

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -