📄 samsung_nand.c
字号:
}
ce_inactive(); //关闭片选
return cn_all_right_verify;
}
//----读块(无ecc校验)----------------------------------------------------
//功能: 不带ecc校验从一块内读取数据,地址不能跨块边界
//参数: sector,扇区号
// offset,块内偏移地址
// data,保存读取数据的缓冲区
// size,读取的尺寸
//返回: 正确读取
//-----------------------------------------------------------------------------
uint32_t read_block_ss_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 completed = 0;
if(block >= tg_samsung_nand.block_sum)
return cn_limit_uint32;
if((size + offset) > tg_samsung_nand.block_size)
return cn_limit_uint32;
if(size == 0)
return 0;
//起始扇区号
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) //当前扇区不是最后一个扇区
read_size = cn_sector_size - start_offset;
else //当前扇区是最后一个扇区
//+1是因为end_offset本身是需要写入的
read_size = end_offset - start_offset +1;
__read_sector_2808_no_ecc(cur_sector,start_offset,
buf+completed,read_size);
completed += read_size;
start_offset = 0; //从第二个扇区开始,肯定从0开始读
}
return completed;
}
//----ECC检查并改正-----------------------------------------------------------
//功能: ecc校验一个扇区
//参数: data,待校验数据的缓冲区
// ecc,校验码
//返回:0=正确,1=被改正,2=错误且不能修正
//-----------------------------------------------------------------------------
//----------------------------------------------------------------------------
uint32_t __correct_sector(uint8_t *data,const uint8_t *ecc)
{
uint32_t i,j,result = cn_all_right_verify;
for(i = 0; i < cn_sector_size/256; i++)
{
j = ecc_corect_256(data + i*256, ecc + i*3);
if(( j == -1) || (j == 2))
return cn_ecc_error_verify; //无法修正则直接返回
if( j == 1)
result = cn_ecc_right_verify;
}
return result;
}
//----读扇区(带ecc校验)----------------------------------------------------
//功能: 带ecc校验从一扇区内读取数据,地址不能跨扇区边界
//参数: sector,扇区号
// offset,扇区内偏移地址
// data,保存读取数据的缓冲区
// size,读取的尺寸
//返回:0=正确,1=被改正,2=错误且不能修正
//-----------------------------------------------------------------------------
uint32_t __read_sector_2808_with_ecc(uint32_t sector,uint32_t offset,
uint8_t *data,uint32_t size)
{
uint32_t i;
uint32_t address,result;
uint8_t ecc[(cn_sector_size+255)/256*3]; //每256字节产生一个ECC码
address =cn_sector_size*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(i=0; i < cn_sector_size; i++)
{
pg_sector_buf[i] = *pg_nand_address; //读取数据
}
if(cn_sector_size > 256)
{
for(i = 0; i < 5; i++) //读取校验码,坏块标志前的5字节
ecc[i] = *pg_nand_address;
ecc[i] = *pg_nand_address; //坏块标志,读取并丢弃
for(; i < cn_sector_size/256*3; i++)//读取校验码,坏块标志后的部分
ecc[i] = *pg_nand_address;
}else
{
for(i = 0; i < 3; i++) //读取校验码,只有3字节
ecc[i] = *pg_nand_address;
}
ce_inactive(); //关闭片选
result = __correct_sector(pg_sector_buf,ecc);
//无论校验结果如何,均执行数据copy,即使错误,也把错误数据告诉用户,让人知道
//错在哪里。
memcpy(data, pg_sector_buf + offset, size);
return result;
}
//----读块(带ecc校验)----------------------------------------------------
//功能: 带ecc校验从一块内读取数据,地址不能跨块边界
//参数: sector,块号
// offset,块内偏移地址
// data,保存读取数据的缓冲区
// size,读取的尺寸
//返回:实际读取的数据量
//-----------------------------------------------------------------------------
uint32_t read_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 read_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;
if(size == 0)
return 0;
//起始扇区号
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) //当前扇区不是最后一个扇区
read_size = cn_sector_size - start_offset;
else //当前扇区是最后一个扇区
//+1是因为end_offset本身是需要写入的
read_size = end_offset - start_offset +1;
verify = __read_sector_2808_with_ecc(cur_sector,start_offset,
buf+completed,read_size);
if((verify == cn_all_right_verify) || (verify == cn_ecc_right_verify))
{
completed += read_size;
start_offset = 0; //从第二个扇区开始,肯定从0开始读
}else
break;
}
return completed;
}
//----读芯片状态-------------------------------------------------------------
//功能: 读芯片状态
//参数: 无
//返回:芯片状态
//-----------------------------------------------------------------------------
uint8_t __read_status_2808(void)
{
uint8_t chip_status ;
__write_command_2808(cn_2808_read_status);
chip_status = *pg_nand_address;
return chip_status;
}
//----写一个扇区(no ecc)-------------------------------------------------------------
//功能: 写一个扇区,不做ecc校验
//参数: sector,扇区号
// offset,扇区内偏移地址
// data,数据缓冲区
// size,写入的尺寸
//返回:cn_all_right_verify=正确写入,cn_ecc_error_verify=写入错误
//-----------------------------------------------------------------------------
uint32_t __write_sector_2808_no_ecc(uint32_t sector,uint32_t offset,
uint8_t *data,uint32_t size)
{
uint32_t i;
uint32_t address;
address =cn_sector_size*sector + offset; //计算实际地址
ce_active(); //激活片选
if(address & 0x100)
__write_command_2808(cn_2808_select_page1); //写入读模式命令
else
__write_command_2808(cn_2808_select_page0); //写入读模式命令
__write_command_2808(cn_2808_page_program); //启动编程命令
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 < size; i++)
{//逐个把待写入的数据写入到器件的扇区缓冲区
*pg_nand_address = data[i];
// printf("WriteFlash: data = 0x%x\n", data);
}
__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_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 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_no_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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -