风声竹影 的个人资料听风竹轩的书架日志列表网络 工具 帮助

日志


8月17日

scanf()函数的用法和实践

scanf()函数的用法和实践

daizh

摘要:

本文阐述了基于ANSI,Win 95,Win NT上的 C/C++语言中scanf()函数的用法,以及在实际使用中常见错误及对策。

关键词:

scanf()

一、             序言

在CSDN论坛的C/C++版块,我时常见到“对于scanf()函数的用法、及出现的各种错误而迷惑”的帖子,萌发了我写这篇文章的念头。文中结合自身在学习和编程中对它的认识和体会,以具体示例阐述问题,目的在于使得初学者能够正确使用scanf()函数,少走不必要的弯路。

二、             scanf()函数的用法

scanf()函数是格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。

其调用格式为:      scanf("<格式化字符串>",<地址表>);

格式化字符串包括以下三类不同的字符;

1、  格式化说明符:

格式化说明符与printf()函数中的格式说明符基本相同。但和printf()函数中格式字符串的用法有一些小区别。我们来看下面这个表。

格式字符
说明
%d
从键盘输入十进制整数
%o
从键盘输入八进制整数
%x
从键盘输入十六进制整数
%c
从键盘输入一个字符
%s
从键盘输入一个字符串
%f
从键盘输入一个实数
%e
与%f的作用相同

附加格式说明字符表

字符
说明
L
输入"长"数据
H
输入"短"数据
M
指定输入数据所占宽度
*
空读一个数据

2、    空白字符: 空白字符会使scanf()函数在读操作中略去输入中的一个或多个空白字符。

3、    非空白字符: 一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。

地址表是需要读入的所有变量的地址,而不是变量本身。这与printf()函数完全不同,要特别注意。各个变量的地址之间同","分开。

例如:

#include <stdio.h>

void main()

{

       int i, j;

       printf("i, j=?\n");

       scanf("%d, %d", &i, &j);

}

上例中的scanf()函数先读一个整型数,然后把接着输入的逗号剔除掉,最后读入另一个整型数。如果","这一特定字符没有找到,scanf()函数就终止。若参数之间的分隔符为空格,则参数之间必须输入一个或多个空格。

说明:

(1)          对于字符串数组或字符串指针变量,由于数组名和指针变量名本身就是地址,因此使用scanf()函数时,不需要在它们前面加上"&"操作符。

例如:

#include <stdio.h>

void main()

{

       char *p, str[20];

                p = new char[20];

          scanf("%s", p); /*从健盘输入字符串*/

          scanf("%s", str);

          printf("%s\n", p); /*向屏幕输出字符串*/

          printf("%s\n", str);

}

(2)          可以在格式化字符串中的"%"各格式化规定符之间加入一个整数,表示任何读操作中的最大位数。

如上例中若规定只能输入10字符给字符串指针p,则第一条scanf() 函数语句变为:scanf("%10s", p);

程序运行时一旦输入字符个数大于10,p就不再继续读入,而后面的一个读入函数即scanf("%s", str)就会从第11个字符开始读入。

(3)             scanf()函数中没有精度控制。

如: scanf("%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。

(4)             scanf中要求给出变量地址,如给出变量名则会出错

如 scanf("%d",a);是非法的,应改为scnaf("%d",&a);才是合法的。

(5)          在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格,TAB或回车作间隔。

C编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。

(6)          在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。

例如:scanf("%c%c%c",&a,&b,&c);

输入为:

d e f

则把'd'赋予a, ' (空格)'赋予b,'e'赋予c。因为%c 只要求读入一个字符,后面不需要用空格作为两个字符的间隔,因此把' '作为下一个字符送给b。

只有当输入为:def 时,才能把'd'赋于a,'e'赋予b,'f'赋予c。 如果在格式控制中加入空格作为间隔,

如 scanf ("%c %c %c",&a,&b,&c);则输入时各数据之间可加空格。

我们用一些例子来说明一些规则:

#include <stdio.h>

void main()

{

       char a,b;

       printf("input character a,b\n");

       scanf("%c%c",&a,&b);  /*注意两个%c之间没有任何符号*/

       printf("%c%c\n",a,b);

}

由于scanf函数"%c%c"中没有空格,输入M N,结果输出只有M。而输入改为MN时则可输出MN两字符,见下面的输入运行情况: input character a,b

MN    (你输入的值)

MN    (屏幕上显示的值)

#include <stdio.h>

void main()

{

       char a,b;

       printf("input character a,b\n");

       scanf("%c %c",&a,&b); /*注意两个%c之间有个空格*/

       printf("\n%c%c\n",a,b);

}本例表示scanf格式控制串"%c %c"之间有空格时, 输入的数据之间可以有空格间隔。

(7)             如果格式控制串中有非格式字符则输入时也要输入该非格式字符。

例如:

scanf("%d,%d,%d",&a,&b,&c); 其中用非格式符“ , ”作间隔符,故输入时应为: 5,6,7 (与scanf 双引号之间的格式必须一样)

又如: scanf("a=%d,b=%d,c=%d",&a,&b,&c);

则输入应为 a=5,b=6,c=7

如输入的数据与输出的类型不一致时,虽然编译能够通过,但结果将不正确。

#include <stdio.h>

void main()

{

       int a;

       printf("input a number");

       scanf("%d",&a);

       printf("%ld",a);

}

由于输入数据类型为整型, 而输出语句的格式串中说明为长整型,因此输出结果和输入数据不符。输出并不是输入的值。

如将Scanf("%d",&a); 语句改为 scanf("%ld",&a);

输入数据为长整型,输入输出数据才相等。

三、             常见错误及对策

问题1:

我初学C程序,所以提的问题很浅,希望您不要见笑。我自己编了一个程序,但运行的结果与我预期的不一样。

#include<stdio.h>

void  main()

{

       static int a[2][3]={{1,3,4},{7,9,6}};

       int i,j,k;

       for(k=1;k<=2;k++)

       {printf("Please input num:");

       scanf("%d %d",&i,&j);

       if(i<2&&j<3)

              printf("num=%d\n",a[i][j]);

       else printf("Input is error,\n");

       }

       printf("programm is complete.\n");

}

我想将第8行改为

scanf("i=%d j=%d",&i,&j);

则程序运行结果变成

please input num:i=1 j=2

num=6

num=6(我原本希望能重复第一行再让我输入)

Programm is complete.

为什么第二次不能输入?

答复:

我使用Turbo C 2.0证实存在你说的问题。象scanf("i=%d j=%d",&i,&j);这样的输入方式比较特别,TC 2.0显然在第一次输入后没有象正常情况一样清楚输入缓冲区,这样第二次执行scanf时,程序并没有让你输入而是直接读入上次输入的结果。如果你一定要这么做,应该在scanf之前加上:

fflush(stdin);

这样清楚掉键盘缓冲区。

问题2:

为什么要把scanf("%c",varname)一定改成scanf(" %c")才可以正确接收字符?

答复:

类似上题,在%c的前面必须有一个空格,否则系统会将你前面输入别的值之后键入的回车符读入该变量,造成死循环。当然,如果scanf("%c",&varname)是第一条读入语句,就可以不需要空格。

问题3:(输入变量时忘记加地址运算符“&”)

int a,b;

scanf("%d%d",a,b);

答复:

这是不合法的。Scanf函数的作用是:按照a、b在内存的地址将a、b的值存进去。“&a”指a在内存中的地址。

问题4:(输入数据的方式与要求不符)

①scanf("%d%d",&a,&b);

输入时,不能用逗号作两个数据间的分隔符,如下面输入不合法:

3,4

输入数据时,在两个数据之间以一个或多个空格间隔,也可用回车键,跳格键tab。

②scanf("%d,%d",&a,&b);

C规定:如果在“格式控制”字符串中除了格式说明以外还有其它字符,则在输入数据时应输入与这些字符相同的字符。下面输入是合法的:

3,4

此时不用逗号而用空格或其它字符是不对的。

3 4 3:4

又如:

scanf("a=%d,b=%d",&a,&b);

输入应如以下形式:

a=3,b=4

问题5:(输入字符的格式与要求不一致)

在用“%c”格式输入字符时,“空格字符”和“转义字符”都作为有效字符输入。

scanf("%c%c%c",&c1,&c2,&c3);

如输入a b c

字符“a”送给c1,字符“ ”送给c2,字符“b”送给c3,因为%c只要求读入一个字符,后面不需要用空格作为两个字符的间隔。

问题6:(.输入输出的数据类型与所用格式说明符不一致)

例如,a已定义为整型,b定义为实型

a=3;b=4.5;

printf("%f%d\n",a,b);

编译时不给出出错信息,但运行结果将与原意不符。这种错误尤其需要注意。

问题7:(输入数据时,企图规定精度)

scanf("%7.2f",&a);

这样做是不合法的,输入数据时不能规定精度。

问题8:(在不应加地址运算符&的位置加了地址运算符)

scanf("%s",&str);

C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应改为:scanf("%s",str);

四、             结论

本文主要讲述了C/C++中的scanf()函数的用法,重点阐述使用scanf()函数过程中出现的常见错误及对策。当然,文中某些解决方法,均可以采用其他函数和方法来更好地解决,但本文仅限讨论scanf()函数本身。文中难免存在一些不足之处,欢迎读者批评指正。

8月15日

Nandflash读取操作源码

共三个文件:s3c2410.h, nand_flash.h, nand_flash.c。

1.s3c2410.h

#ifndef _S3C2410_H
#define _S3C2410_H
// watchdog register
#define rWTCON  (*(volatile unsigned int *)0x53000000)
//interrupt register
#define rSRCPND  (*(volatile unsigned int *)0x4A000000)
#define rSUBSRCPND (*(volatile unsigned int *)0x4A000018)
#define rINTMOD  (*(volatile unsigned int *)0x4A000004)
#define rINTMSK  (*(volatile unsigned int *)0x4A000008)
#define rINTSUBMSK (*(volatile unsigned int *)0x4A00001C)
#define rPRIORITY (*(volatile unsigned int *)0x4A00000C)
#define rINTPND  (*(volatile unsigned int *)0x4A000010)
#define rINTOFFSET (*(volatile unsigned int *)0x4A000014)
// bank ctrl register
#define rBWSCON  (*(volatile unsigned int *)0x48000000)
#define rREFRESH (*(volatile unsigned int *)0x48000024)
// NAND flash register
#define rNFCONF  (*(volatile unsigned int *)0x4e000000) //NAND Flash configuration
#define rNFCMD  (*(volatile unsigned char *)0x4e000004) //NADD Flash command
#define rNFADDR  (*(volatile unsigned char *)0x4e000008) //NAND Flash address
#define rNFDATA  (*(volatile unsigned char *)0x4e00000c) //NAND Flash data
#define rNFSTAT  (*(volatile unsigned int *)0x4e000010) //NAND Flash operation status
#define rNFECC  (*(volatile unsigned int *)0x4e000014) //NAND Flash ECC
#define rNFECC0  (*(volatile unsigned char  *)0x4e000014)
#define rNFECC1  (*(volatile unsigned char  *)0x4e000015)
#define rNFECC2  (*(volatile unsigned char  *)0x4e000016)

// I/O PORT register
#define rGPACON  (*(volatile unsigned int *)0x56000000) //Port A control
#define rGPADAT  (*(volatile unsigned int *)0x56000004) //Port A data
                       
#define rGPBCON  (*(volatile unsigned int *)0x56000010) //Port B control
#define rGPBDAT  (*(volatile unsigned int *)0x56000014) //Port B data
#define rGPBUP    (*(volatile unsigned int *)0x56000018) //Pull-up control B
                       
#define rGPCCON  (*(volatile unsigned int *)0x56000020) //Port C control
#define rGPCDAT  (*(volatile unsigned int *)0x56000024) //Port C data
#define rGPCUP  (*(volatile unsigned int *)0x56000028) //Pull-up control C
                       
#define rGPDCON  (*(volatile unsigned int *)0x56000030) //Port D control
#define rGPDDAT  (*(volatile unsigned int *)0x56000034) //Port D data
#define rGPDUP  (*(volatile unsigned int *)0x56000038) //Pull-up control D
                       
#define rGPECON  (*(volatile unsigned int *)0x56000040) //Port E control
#define rGPEDAT  (*(volatile unsigned int *)0x56000044) //Port E data
#define rGPEUP  (*(volatile unsigned int *)0x56000048) //Pull-up control E
                       
#define rGPFCON  (*(volatile unsigned int *)0x56000050) //Port F control
#define rGPFDAT  (*(volatile unsigned int *)0x56000054) //Port F data
#define rGPFUP  (*(volatile unsigned int *)0x56000058) //Pull-up control F
                       
#define rGPGCON  (*(volatile unsigned int *)0x56000060) //Port G control
#define rGPGDAT  (*(volatile unsigned int *)0x56000064) //Port G data
#define rGPGUP  (*(volatile unsigned int *)0x56000068) //Pull-up control G
                       
#define rGPHCON  (*(volatile unsigned int *)0x56000070) //Port H control
#define rGPHDAT  (*(volatile unsigned int *)0x56000074) //Port H data
#define rGPHUP  (*(volatile unsigned int *)0x56000078) //Pull-up control H

//UART register
#define rULCON0  (*(volatile unsigned int *)0x50000000) //UART 0 Line control
#define rUCON0  (*(volatile unsigned int *)0x50000004) //UART 0 Control
#define rUFCON0  (*(volatile unsigned int *)0x50000008) //UART 0 FIFO control
#define rUMCON0  (*(volatile unsigned int *)0x5000000C) //UART 0 Modem control
#define rUTRSTAT0  (*(volatile unsigned int *)0x50000010) //UART 0 Tx/Rx status
#define rUERSTAT0 (*(volatile unsigned int *)0x50000014) //UART 0 Rx error status
#define rUFSTAT0  (*(volatile unsigned int *)0x50000018) //UART 0 FIFO status
#define rUMSTAT0 (*(volatile unsigned int *)0x5000001C) //UART 0 Modem status
#define rUBRDIV0 (*(volatile unsigned int *)0x50000028) //UART 0 Baud rate divisor

#define rUTXH0  (*(volatile unsigned char *)0x50000020) //UART 0 Transmission Hold
#define rURXH0  (*(volatile unsigned char *)0x50000024) //UART 0 Receive buffer
#define WrUTXH0(ch) (*(volatile unsigned char *)0x50000020)=(unsigned char)(ch)
#define RdURXH0() (*(volatile unsigned char *)0x50000024)


// CLOCK & POWER MANAGEMENT
#define rLOCKTIME    (*(volatile unsigned int *)0x4c000000) //PLL lock time counter
#define rMPLLCON     (*(volatile unsigned int *)0x4c000004) //MPLL Control
#define rUPLLCON     (*(volatile unsigned int *)0x4c000008) //UPLL Control
#define rCLKCON      (*(volatile unsigned int *)0x4c00000c) //Clock generator control
#define rCLKSLOW     (*(volatile unsigned int *)0x4c000010) //Slow clock control
#define rCLKDIVN     (*(volatile unsigned int *)0x4c000014) //Clock divider control

// LCD register
#define rLCDCON1 (*(volatile unsigned int *)0x4D000000)
#define rLCDCON2 (*(volatile unsigned int *)0x4D000004)
#define rLCDCON3 (*(volatile unsigned int *)0x4D000008)
#define rLCDCON4 (*(volatile unsigned int *)0x4D00000c)
#define rLCDCON5 (*(volatile unsigned int *)0x4D000010)
#define rLCDSADDR1 (*(volatile unsigned int *)0x4D000014)
#define rLCDSADDR2 (*(volatile unsigned int *)0x4D000018)
#define rLCDSADDR3 (*(volatile unsigned int *)0x4D00001c)
#define rREDLUT  (*(volatile unsigned int *)0x4D000020)
#define rGREENLUT (*(volatile unsigned int *)0x4D000024)
#define rBLUELUT  (*(volatile unsigned int *)0x4D000028)
#define rDITHMODE (*(volatile unsigned int *)0x4D00004c)
#define rTPAL   (*(volatile unsigned int *)0x4D000050)
#define rLCDINTPND  (*(volatile unsigned int *)0x4D000054)
#define rLCDSRCPND  (*(volatile unsigned int *)0x4D000058)
#define rLCDINTMSK (*(volatile unsigned int *)0x4D00005c)
#define rLPCSEL  (*(volatile unsigned int *)0x4D000060)

// Timer register
#define rTCFG0  (*(volatile unsigned int *)0x51000000)
#define rTCFG1  (*(volatile unsigned int *)0x51000004)
#define rTCON  (*(volatile unsigned int *)0x51000008)
#define rTCNTB0  (*(volatile unsigned int *)0x5100000C)
#define rTCMPB0  (*(volatile unsigned int *)0x51000010)
#define rTCNTO0  (*(volatile unsigned int *)0x51000014)
#define rTCNTB1  (*(volatile unsigned int *)0x51000018)
#define rTCMPB1  (*(volatile unsigned int *)0x5100001C)
#define rTCNTO1  (*(volatile unsigned int *)0x51000020)
#define rTCNTB2  (*(volatile unsigned int *)0x51000024)
#define rTCMPB2  (*(volatile unsigned int *)0x51000028)
#define rTCNTO2  (*(volatile unsigned int *)0x5100002C)
#define rTCNTB3  (*(volatile unsigned int *)0x51000030)
#define rTCMPB3  (*(volatile unsigned int *)0x51000034)
#define rTCNTO3  (*(volatile unsigned int *)0x51000038)
#define rTCNTB4  (*(volatile unsigned int *)0x5100003C)
#define rTCNTO4  (*(volatile unsigned int *)0x51000040)
#endif

2.nand_flash.h

#ifndef _NAND_FLASH_H
#define _NAND_FLASH_H

#define TACLS  0  //1clk(0ns)
#define TWRPH0  3  //3clk(25ns)
#define TWRPH1  0  //1clk(10ns)  //TACLS+TWRPH0+TWRPH1>=50ns


//send command
#define NF_CMD(cmd) {rNFCMD=cmd;}
//set address
#define NF_ADDR(addr) {rNFADDR=addr;}
//NAND Flash Memory chip enable
#define NF_nFCE_L() {rNFCONF&=~(1<<11);}
//NAND Flash Memory chip disable
#define NF_nFCE_H() {rNFCONF|=(1<<11);}
//Initialize ECC
#define NF_RSTECC() {rNFCONF|=(1<<12);}
//read data
#define NF_RDDATA() (rNFDATA)
//write data
#define NF_WRDATA(data) {rNFDATA=data;}
//get status
#define NF_WAITRB() {while(!(rNFSTAT&(1)));}

#endif

3.nand_flash.c

/*
 www.another-prj.com
 
 author: caiyuqing
 
 本代码只属于交流学习,不得用于商业开发
*/
#include "s3c2410.h"
#include "nand_flash.h"
static unsigned char seBuf[16]={0xff};
//--------------------------------------------------------------------------------------
unsigned short nf_checkId(void)
{
 int i;
 unsigned short id;
 NF_nFCE_L();  //chip enable
 
 NF_CMD(0x90);  //Read ID
 NF_ADDR(0x0);
 for(i=0;i<10;i++); //wait tWB(100ns)
 
 id=NF_RDDATA()<<8; // Maker code(K9S1208V:0xec)
 id|=NF_RDDATA(); // Devide code(K9S1208V:0x76)
 
 NF_nFCE_H();  //chip enable
 return id;
}
//--------------------------------------------------------------------------------------
static void nf_reset(void)
{
 int i;
 NF_nFCE_L();  //chip enable
 NF_CMD(0xFF);  //reset command
 for(i=0;i<10;i++);   //tWB = 100ns.
 NF_WAITRB();   //wait 200~500us;
 NF_nFCE_H();  //chip disable
}
//--------------------------------------------------------------------------------------
void nf_init(void)
{
 rNFCONF=(1<<15)|(1<<14)|(1<<13)|(1<<12)|(1<<11)|(TACLS<<8)|(TWRPH0<<4)|(TWRPH1<<0); 
 //      1   1     1      1    1      xxx     r xxx,      r xxx       
 //      En  r r       ECCR    nFCE=H tACLS   tWRPH0      tWRPH1
 nf_reset();
}
//--------------------------------------------------------------------------------------

void nf_read(unsigned int src_addr,unsigned  char *desc_addr,int size)
{
 int i;
 unsigned int column_addr=src_addr%512;   // column address
 unsigned int page_address=(src_addr>>9);  // page addrress
 unsigned char *buf=desc_addr;
 while((unsigned int)buf<(unsigned int)(desc_addr)+size)
 {
  NF_nFCE_L();     // enable chip
  if(column_addr>255)    // 2end halft
  {
   NF_CMD(0x01);    // Read2 command.   cmd 0x01: Read command(start from 2end half page)
  }
  else
  {
   NF_CMD(0x00);    // 1st halft?
  }
  NF_ADDR(column_addr&0xff);       // Column Address
  NF_ADDR(page_address&0xff);   // Page Address
  NF_ADDR((page_address>>8)&0xff);  // ...
  NF_ADDR((page_address>>16)&0xff);  // ..
  for(i=0;i<10;i++);    // wait tWB(100ns)/////??????
  NF_WAITRB();     // Wait tR(max 12us)
 
  // Read from main area
  for(i=column_addr;i<512;i++)
  {
   *buf++=NF_RDDATA();
  }
  NF_nFCE_H();     // disable chip
  column_addr=0;
  page_address++;
 }
 return ;
}

k9f2808 NandFlash调试的过程与心得

调试的过程与心得交流

硬件平台:ARM7 44b0x
软件调试平台:ADS1.2
目的:调试一型号为:三星公司的k9f2808 容量为16M *8Bit 的NandFlash
文中:NF表示的是NandFlash

当把k9f2808 焊好后,便开始调试了,但是在其中遇到很多问题

我是第一次接触NandFlash,刚开始还是很陌生

<1>可以读出ID号(非常顺利, 这一点是我也没有想到的,认为基本上就搞定了)
<2>我试着写擦除代码  写页(我试着写0X5),   读页。 结果是返回来的全部是0XFF, 这时就出现了好多的疑点了:
我分析如下:
第一种可能:没有擦除到NF,也就不可能写了,返回的是错误的(怀疑擦除的时序有问题)
第二种可能:擦除对了,写页不对,读页也不对
第三种可能:擦除对了,写页对了,读页不对,返回的全部是错误的。
第四种可能:硬件连接不对包括有:(ALE 、CLE、CS、WR、RD等等)
第五种可能:硬件的焊接有问题
第六种可能:NandFlash是坏的

那时我也不知是什么问题而且很快进入了混乱之中。朋友们,当看到这里的时候,你认为是哪里出问题?
接着我是这样做的,我现在也觉得挺有意思
不断地调试代码(可以说是这样:脑子里想到这里有可能,就调一下,那里有可能就那里调一下)(因为我太想搞出来,想一下子把它搞定)
搞了三天还是不行,这时自己又停下来思索了,究竟是哪里出问题了???,就开始去网上找代码,整个GOOGLE都给我找翻了,但是还是没有答案。很是郁闷。

因 为看到网上好多的开发板都是把控制线接到GPIO那里去的,而我的是接到地址总线上去的,于是我也就把问题归结的硬件连接的问题上去了。这时的我又是安慰 自己,本来快没有信心的了,开始又有了信心了(我想搞开发的都会有这种感觉)。很快把控制线连到GPIO上,接着是改代码。结果呢?充满信心的我一下也就 像没气的气球了,真是烦啊!

究竟是哪里出错了???很是郁闷啊!!我真的是累了,决定先放一放先,打了二天的星际争霸游戏,调节一下心情。

过了二天,我就告诉自己一定要彻底静下心来认真思考,鼓励自己要有耐心才能成功。于是我进入了第二阶段:

首先我还是把控制线接回地址总线那里去(因为我对这个比较有信心,起码可以读出ID)

我开始思考:我可以读出ID 来,就证明:
1,    写命令是正确的(否则的话就读不回ID,焊接是没问题的,线据线是没有连错的)
2,    读NF ID 的时序是没有问题的。ID =0XEC73



于是我就想:我写命令是对的了,那么我发擦除块的命令也是对的,于是我又联想到,我所有写指令(包括写地址,写命令)都有对的(这点是很重要的,也是至关重要的)。读数据的指令也是对的了(因为我能够把ID都读回来了(这点是错的,因为到最后就是卡住我了))

现在的我又很有信心了。
现在我要证明我所分析是对的,于是我开始调代码了:

我 试着擦除NF,然后往0  block 0  Page页写(0x05)(时序是不能马虎的),再来就是试着读刚才写的页,结果在串口超级终端返回的又是OXFF,我这时好像要崩溃了,但是我又 马上意识的,我分析的应是对的,我怀疑时序,本来我是不会用逻辑分析仪的,我开始进入学习逻辑分析仪了,搞了二天终于会用那玩意了。我看到时序是有的,我 脑子里突然有一种想法,读多几次看怎么样?于是我连着读好多次那页,结果有读到第三次的时候,出现不是0xff,我开心极了,因为都读回来了 (0x05),我上了次厕所(忙到忘了上厕所了),为了确认我再重新运行程序,又不行了。那是的我真是好难受啊心里面。我又试了好多次,有时能读回来,有 时又不能读回来,我又想是不是写没有写好,于我又往那页写不是同样的数据,写了0,1,2,3…….511, 就这样我再读,结果只能读回0,1,下面的全部是OXFF,这时我为了验证是不是没有写上,于是我又开始读(不是从0区的第一个字节读,我换了其它位置去 读,结果读到了8,9)这时我明白了上点,那就是:写进去的了,问题就是出在读上面(这是非常重要的一点),于是我用逻辑分析仪进行分析,结果呢?读的时 序是有的,但是写的时序也是有的;这时我又明白了一点;但是不应出现这样的现象的啊!!


最终的问题就是出现在这里了:

       我用的44B0X的BANK是BANK5(PORTB10), 这个BANK的控制寄存器没有设好(我本来想都没去想这里会出错了,板子说明是BANK5没有使用)没有想到问题就是出现在这里;我换了其它的BANK试了一下,一切都是OK!!!


感想:从这次调NF 可以学到很多很多的东东。对我以后的发展以及经验的积累起到了很大的作用。我同时也感觉很幸运,为什么呢?假如一开始就用其它的BANK那我可能就少走很多的调试的路了,但是正因为这样,让我积累了更多的东东。这也是收获吧!!

ECC程序中nand_ecc_precalc_table表的生成原理

ECC程序中nand_ecc_precalc_table表的生成原理

作者:龙林 EMAIL:dragon_hn@sohu.com WEB:www.dragon-2008.com

  在上文的《NAND FLASH ECC校验原理与实现》中贴出了ECC算法源程序,在ECC算法源程序中有个nand_ecc_precalc_table,用于快速生成ECC校验和, 该表实际上是按照《NAND FLASH ECC校验原理与实现》表中的ECC原理生成的,理解了ECC校验和生成原理,实际上生成该表也就不存在任何困难了。下面是生成该表的源程序:   #define   BIT0(x)   ((x)&0x01)
  #define   BIT1(x)   (((x)&0x02)>>1)
  #define   BIT2(x)   (((x)&0x04)>>2)
  #define   BIT3(x)   (((x)&0x08)>>3)
  #define   BIT4(x)   (((x)&0x10)>>4)
  #define   BIT5(x)   (((x)&0x20)>>5)
  #define   BIT6(x)   (((x)&0x40)>>6)
  #define   BIT7(x)   (((x)&0x80)>>7)
  
  void MakeEccTable()
  {
    int i,m;
    BYTE xData;
    m=0;
    for(i=0;i<256;i++)
    {
      xData=0;
      if(BIT0(i)^BIT2(i)^BIT4(i)^BIT6(i)) xData|=0x01;
      if(BIT1(i)^BIT3(i)^BIT5(i)^BIT7(i)) xData|=0x02;
  
      if(BIT0(i)^BIT1(i)^BIT4(i)^BIT5(i)) xData|=0x04;
      if(BIT2(i)^BIT3(i)^BIT6(i)^BIT7(i)) xData|=0x08;
  
      if(BIT0(i)^BIT1(i)^BIT2(i)^BIT3(i)) xData|=0x10;
      if(BIT4(i)^BIT5(i)^BIT6(i)^BIT7(i)) xData|=0x20;
  
      if(BIT0(i)^BIT1(i)^BIT2(i)^BIT3(i)^BIT4(i)^BIT5(i)^BIT6(i)^BIT7(i))
        xData|=0x40;
      if(m==15)
      {
        TRACE("0x%02X,\n",xData);
        m=0;
      }
      else
      {
        TRACE("0x%02X,",xData);
        m++;
      }
    }
  }

NAND FLASH ECC校验原理与实现

ECC简介
  由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。为了检测数据的可靠性,在应用NAND Flash的系统中一般都会采用一定的坏区管理策略,而管理坏区的前提是能比较可靠的进行坏区检测。
  如果操作时序和电路稳定性不存在问题的话,NAND Flash出错的时候一般不会造成整个Block或是Page不能读取或是全部出错,而是整个Page(例如512Bytes)中只有一个或几个bit出错。
  对数据的校验常用的有奇偶校验、CRC校验等,而在NAND Flash处理中,一般使用一种比较专用的校验——ECC。ECC能纠正单比特错误和检测双比特错误,而且计算速度很快,但对1比特以上的错误无法纠正,对2比特以上的错误不保证能检测。

ECC原理
  ECC一般每256字节原始数据生成3字节ECC校验数据,这三字节共24比特分成两部分:6比特的列校验和16比特的行校验,多余的两个比特置1,如下图所示:

  ECC的列校验和生成规则如下图所示:

 



  用数学表达式表示为:
    P4=D7(+)D6(+)D5(+)D4  P4`=D3(+)D2(+)D1(+)D0
    P2=D7(+)D6(+)D3(+)D2  P2`=D5(+)D4(+)D1(+)D0
    P1=D7(+)D5(+)D3(+)D1  P1`=D6(+)D4(+)D2(+)D0
  这里(+)表示“位异或”操作
  
  ECC的行校验和生成规则如下图所示

  用数学表达式表示为:
    P8 = bit7(+)bit6(+)bit5(+)bit4(+)bit3(+)bit2(+)bit1(+)bit0(+)P8
    ……………………………………………………………………………………
  这里(+)同样表示“位异或”操作
 
  当往NAND Flash的page中写入数据的时候,每256字节我们生成一个ECC校验和,称之为原ECC校验和,保存到PAGE的OOB(out-of-band)数据区中。
  当从NAND Flash中读取数据的时候,每256字节我们生成一个ECC校验和,称之为新ECC校验和。
  校验的时候,根据上述ECC生成原理不难推断:将从OOB区中读出的原ECC校验和新ECC校验和按位异或,若结果为0,则表示不存在错(或是出现了 ECC无法检测的错误);若3个字节异或结果中存在11个比特位为1,表示存在一个比特错误,且可纠正;若3个字节异或结果中只存在1个比特位为1,表示 OOB区出错;其他情况均表示出现了无法纠正的错误。

ECC算法的实现
  static const u_char nand_ecc_precalc_table[] =
  {
    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
  };

  // Creates non-inverted ECC code from line parity
  static void nand_trans_result(u_char reg2, u_char reg3,u_char *ecc_code)
  {
    u_char a, b, i, tmp1, tmp2;

    /* Initialize variables */
    a = b = 0x80;
    tmp1 = tmp2 = 0;

    /* Calculate first ECC byte */
    for (i = 0; i < 4; i++)
    {
      if (reg3 & a)    /* LP15,13,11,9 --> ecc_code[0] */
        tmp1 |= b;
      b >>= 1;
      if (reg2 & a)    /* LP14,12,10,8 --> ecc_code[0] */
        tmp1 |= b;
      b >>= 1;
      a >>= 1;
    }

    /* Calculate second ECC byte */
    b = 0x80;
    for (i = 0; i < 4; i++)
    {
      if (reg3 & a)    /* LP7,5,3,1 --> ecc_code[1] */
        tmp2 |= b;
      b >>= 1;
      if (reg2 & a)    /* LP6,4,2,0 --> ecc_code[1] */
        tmp2 |= b;
      b >>= 1;
      a >>= 1;
    }

    /* Store two of the ECC bytes */
    ecc_code[0] = tmp1;
    ecc_code[1] = tmp2;
  }

  // Calculate 3 byte ECC code for 256 byte block
  void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
  {
    u_char idx, reg1, reg2, reg3;
    int j;

    /* Initialize variables */
    reg1 = reg2 = reg3 = 0;
    ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;

    /* Build up column parity */
    for(j = 0; j < 256; j++)
    {

      /* Get CP0 - CP5 from table */
      idx = nand_ecc_precalc_table[dat[j]];
      reg1 ^= (idx & 0x3f);

      /* All bit XOR = 1 ? */
      if (idx & 0x40) {
        reg3 ^= (u_char) j;
        reg2 ^= ~((u_char) j);
      }
    }

    /* Create non-inverted ECC code from line parity */
    nand_trans_result(reg2, reg3, ecc_code);

    /* Calculate final ECC code */
    ecc_code[0] = ~ecc_code[0];
    ecc_code[1] = ~ecc_code[1];
    ecc_code[2] = ((~reg1) << 2) | 0x03;
  }

  // Detect and correct a 1 bit error for 256 byte block
  int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
  {
    u_char a, b, c, d1, d2, d3, add, bit, i;

    /* Do error detection */
    d1 = calc_ecc[0] ^ read_ecc[0];
    d2 = calc_ecc[1] ^ read_ecc[1];
    d3 = calc_ecc[2] ^ read_ecc[2];

    if ((d1 | d2 | d3) == 0)
    {
      /* No errors */
      return 0;
    }
    else
    {
      a = (d1 ^ (d1 >> 1)) & 0x55;
      b = (d2 ^ (d2 >> 1)) & 0x55;
      c = (d3 ^ (d3 >> 1)) & 0x54;

      /* Found and will correct single bit error in the data */
      if ((a == 0x55) && (b == 0x55) && (c == 0x54))
      {
        c = 0x80;
        add = 0;
        a = 0x80;
        for (i=0; i<4; i++)
        {
          if (d1 & c)
            add |= a;
          c >>= 2;
          a >>= 1;
        }
        c = 0x80;
        for (i=0; i<4; i++)
        {
          if (d2 & c)
            add |= a;
          c >>= 2;
          a >>= 1;
        }
        bit = 0;
        b = 0x04;
        c = 0x80;
        for (i=0; i<3; i++)
        {
          if (d3 & c)
            bit |= b;
          c >>= 2;
          b >>= 1;
        }
        b = 0x01;
        a = dat[add];
        a ^= (b << bit);
        dat[add] = a;
        return 1;
      }
      else
      {
        i = 0;
        while (d1)
        {
          if (d1 & 0x01)
            ++i;
          d1 >>= 1;
        }
        while (d2)
        {
          if (d2 & 0x01)
            ++i;
          d2 >>= 1;
        }
        while (d3)
        {
          if (d3 & 0x01)
            ++i;
          d3 >>= 1;
        }
        if (i == 1)
        {
          /* ECC Code Error Correction */
          read_ecc[0] = calc_ecc[0];
          read_ecc[1] = calc_ecc[1];
          read_ecc[2] = calc_ecc[2];
          return 2;
        }
        else
        {
          /* Uncorrectable Error */
          return -1;
        }
      }
    }     /* Should never happen */
    return -1;
  }

NAND Flash的坏块

1.为什么会出现坏块
  由于NAND Flash的工艺不能保证NAND的Memory Array在其生命周期中保持性能的可靠,因此,在NAND的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,不能将某些位拉高,这 会造成Page Program和Block Erase操作时的错误,相应地反映到Status Register的相应位。

2.坏块的分类
  总体上,坏块可以分为两大类
  (1) 固有坏块
    这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将坏块第一个page的spare area的第6个byte标记为不等于0xff的值。
  (2) 使用坏块
    这是在NAND Flash使用过程中,如果Block Erase或者Page Program错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个page的 spare area的第6个Byte标记为非0xff的值。

3.坏块管理
  根据上面的这些叙述,可以了解NAND Flash出厂时在spare area中已经反映出了坏块信息,因此,如果在擦除一个块之前,一定要先check一下spare area的第6个byte是否是0xff,如果是就证明这是一个好块,可以擦除;如果是非0xff,那么就不能擦除。
当然,这样处理可能会犯一个错误―――“错杀伪坏块”,因为在芯片操作过程中可能由于电压不稳定等偶然因素会造成NAND操作的错误。但是,为了数据的可 靠性及软件设计的简单化,我们就要奉行“蒋委员长”的“宁可错杀一千,也决不放过一个”的宗旨。

4.补充
  (1)需要对前面由于Page Program错误发现的坏块进行一下特别说明。如果在对一个块的某个page进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把其他好的page里面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。
当然,这可能会犯“错杀”之误,一个补救的办法,就是在进行完页备份之后,再将这个块擦除一遍,如果Block Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个“戳”吧!
  (2)可能有人会问,为什么要使用spare area的第六个byte作为坏块标记。这是NAND Flash生产商的默认约定,你可以看到Samsung,Toshiba,STMicroelectronics都是使用这个Byte作为坏块标记的。

NAND和NOR FLASH技术综述

NAND和NOR FLASH技术设计师在使用闪存时需要慎重选择

M-Systems公司 Arie TAL

  NOR和NAND是现在市场上两种主要的非易失闪存技术。Intel于1988年首先开发出NOR flash技术,彻底改变了原先由EPROM和EEPROM一统天下的局面。紧接着,1989年,东芝公司发表了NAND flash结构,强调降低每比特的成本,更高的性能,并且象磁盘一样可以通过接口轻松升级。但是经过了十多年之后,仍然有相当多的硬件工程师分不清NOR 和NAND闪存。相“flash存储器”经常可以与相“NOR存储器”互换使用。许多业内人士也搞不清楚NAND闪存技术相对于NOR技术的优越之处,因为大多数情况 下闪存只是用来存储少量的代码,这时NOR闪存更适合一些。而NAND则是高数据存储密度的理想解决方案。 NOR的特点是芯片内执行(XIP, eXecute In Place),这样应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。NOR的传输效率很高,在1~4MB的小容量时具有很高的成 本效益,但是很低的写入和擦除速度大大影响了它的性能。NAND结构能提供极高的单元密度,可以达到高存储密度,并且写入和擦除的速度也很快。应用NAND的困难在于flash的管理和需要特殊的系统接口。

性能比较
  flash闪存是非易失存储器,可以对称为块的存储器单元块进行擦写和再编程。任何flash器件的写入操作只能在空或已擦除的单元内进行,所以大多 数情况下,在进行写入操作之前必须先执行擦除。NAND器件执行擦除操作是十分简单的,而NOR则要求在进行擦除前先要将目标块内所有的位都写为0。由于擦除NOR器件时是以64~128KB的块进行的,执行一个写入/擦除操作的时间为5s,与此相反,擦除NAND器件是以8~32KB的块进行的,执行相同的操作最多只需要4ms。执行擦除时块尺寸的不同进一步拉大了NOR和NADN之间的性能差距,统计表明,对于给定的一套写入操作(尤其是更新小文件时),更多的擦除操作必须 在基于NOR的单元中进行。这样,当选择存储解决方案时,设计师必须权衡以下的各项因素:
  ● NOR的读速度比NAND稍快一些。
  ● NAND的写入速度比NOR快很多。
  ● NAND的4ms擦除速度远比NOR的5s快。
  ● 大多数写入操作需要先进行擦除操作。
  ● NAND的擦除单元更小,相应的擦除电路更少。

接口差别
  NOR flash带有SRAM接口,有足够的地址引脚来寻址,可以很容易地存取其内部的每一个字节。NAND器件使用复杂的I/O口来串行地存取数据,各个产品或厂商的方法可能各不相同。8个引脚用来传送控制、地址和数据信息。NAND读和写操作采用512字节的块,这一点有点像硬盘管理此类操作,很自然地,基于NAND的存储器就可以取代硬盘或其他块设备。

容量和成本
  NAND flash的单元尺寸几乎是NOR器件的一半,由于生产过程更为简单,NAND结构可以在给定的模具尺寸内提供更高的容量,也就相应地降低了价格。NOR flash占据了容量为1~16MB闪存市场的大部分,而NAND flash只是用在8~128MB的产品当中,这也说明NOR主要应用在代码存储介质中,NAND适合于数据存储,NAND在CompactFlash、 Secure Digital、PC Cards和MMC存储卡市场上所占份额最大。

可靠性和耐用性
  采用flahs介质时一个需要重点考虑的问题是可靠性。对于需要扩展MTBF的系统来说,Flash是非常合适的存储方案。可以从寿命(耐用性)、位交换和坏块处理三个方面来比较NOR和NAND的可靠性。

寿命(耐用性)
  在NAND闪存中每个块的最大擦写次数是一百万次,而NOR的擦写次数是十万次。NAND存储器除了具有10比1的块擦除周期优势,典型的NAND块 尺寸要比NOR器件小8倍,每个NAND存储器块在给定的时间内的删除次数要少一些。

位交换
  所有flash器件都受位交换现象的困扰。在某些情况下(很少见,NAND发生的次数要比NOR多),一个比特位会发生反转或被报告反转了。一位的变化可能不很明显,但是如果发生在一个关键文件上,这个小小的故障可能导致系统停机。如果只是报告有问题,多读几次就可能解决了。当然,如果这个位真的改变了,就必须采用错误探测/错误更正(EDC/ECC)算法。位反转的问题更多见于NAND闪存,NAND的供应商建议使用NAND闪存的时候,同时使用EDC/ECC算法。这个问题对于用NAND存储多媒体信息时倒不是致命的。当然,如果用本地存储设备来存储操作系统、配置文件或其他敏感信息时,必须使用EDC/ECC系统以确保可靠性。

坏块处理
  NAND器件中的坏块是随机分布的。以前也曾有过消除坏块的努力,但发现成品率太低,代价太高,根本不划算。NAND器件需要对介质进行初始化扫描以发现坏块,并将坏块标记为不可用。在已制成的器件中,如果通过可靠的方法不能进行这项处理,将导致高故障率。

易于使用
   可以非常直接地使用基于NOR的闪存,可以像其他存储器那样连接,并可以在上面直接运行代码。由于需要I/O接口,NAND要复杂得多。各种NAND器件的存取方法因厂家而异。在使用NAND器件时,必须先写入驱动程序,才能继续执行其他操作。向NAND器件写入信息需要相当的技巧,因为设计师绝不能向坏块写入,这就意味着在NAND器件上自始至终都必须进行虚拟映射。
软件支持
   当讨论软件支持的时候,应该区别基本的读/写/擦操作和高一级的用于磁盘仿真和闪存管理算法的软件,包括性能优化。在NOR器件上运行代码不需要任何的软件支持,在NAND器件上进行同样操作时,通常需要驱动程序,也就是内存技术驱动程序(MTD),NAND和NOR器件在进行写入和擦除操作时都需要MTD。使用NOR器件时所需要的MTD要相对少一些,许多厂商都提供用于NOR器件的更高级软件,这其中包括M-System的TrueFFS驱动,该驱动被 Wind River System、Microsoft、QNX Software System、Symbian和Intel等厂商所采用。驱动还用于对DiskOnChip产品进行仿真和NAND闪存的管理,包括纠错、坏块处理和损耗平衡。