你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F407 FSMC驱动MT29F4G08A NAND FLASH源代码分享

[复制链接]
STMCU小助手 发布时间:2022-4-21 21:00
一、MT29F4G08A概述" d8 Y5 \$ j7 B+ f2 j2 T, Y
   MT29F4G08是一颗 512MB 的 NAND FLASH 芯片相对于 SPI FLASH( W25Q256)和 SD 卡等存储设备,NAND FLASH 采用 8 位并口访问,具有访问速度快的优势。  A7 |6 m% B$ N% o
1、NAND FLASH信号线
) G9 t8 M3 D9 H8 e: f2 i# u9 c' j. ~4 t9 }9 j
$KO2IP4KQQM{LB11{M{ZF90.png
6 h' h; t6 ~6 n+ D/ O& A- {. A
- y& A8 _  W  E  G2、NAND FLASH 存储阵列
3 q* R- ~% d  a' u$ ~- p' b S8C}8~@YXSWD_0WIHK62_NY.png
, f; I- l$ o" O7 Q4 s# m: E1 `/ j. K5 @; ~
由图可知: MT29F4G08 由 2 个 plane 组成,每个 plane 有 2048 个 block,每个 block 由 64个 page 组成,每个 page 有 2K+64 字节( 2112 字节)的存储容量。所以, MT29F4G08 的总容量为: 2204864*( 2K+64) = 553648128 字节( 512MB)。其中, plane、 block、 page 等的个数根据 NAND FLASH 型号的不同,会有所区别,大家注意查看对应 NAND FLASH 芯片的数据
: u9 I& L5 t. m手册。! J# Y2 l& g2 Z, H
NAND FLASH 的最小擦除单位是 block,对 MT29F4G08 来说,是( 128+4) K 字节, NANDFLASH 的写操作具有只可以写 0,不能写 1 的特性,所以,在写数据的时候,必须先擦除 block(擦除后, block 数据全部为 1),才可以写入。NAND FLASH 的 page 由 2 部分组成:数据存储区( data area)和备用区域( spare area),对 MT29F4G08 来说,数据存储区大小为 2K 字节,备用区域大小为 64 字节。我们存储的有效数据,一般都是存储在数据存储区( data area)。备用区域( spare area),一般用来存放 ECC( ErrorChecking and Correcting)校验值,在工程中,我们将利用这个区域,来实现 NAND FLASH 坏块管理和磨损均衡。2 I9 k4 k5 K7 K5 l
NAND FLASH 的地址分为三类:块地址( Block Address)、页地址( Page Address)和列地址( Column Address)。以 MT29F4G08 为例,这三个地址,通过 5 个周期发送。: v. A3 f8 D5 U+ E) b- c

: d8 A. A* i9 ]2 y# Z; W" ?9 q! j二、部分参考代码
# c: h% ]6 T$ \1 {" g( v( ]' U+ ?  z8 L/ w: Y
  1. FSMC_NANDInitTypeDef NAND_Handler;    //NAND FLASH句柄
    & F- M; J2 Y  _3 k$ I4 }4 v
  2. nand_attriute nand_dev;             //nand重要参数结构体# k5 J# W* S6 r
  3. - x: j6 Q1 D8 X! P  P) u
  4. //初始化NAND FLASH
    $ J3 r) E: N, T' F
  5. u8 NAND_Init(void)
    6 x# f/ z% m  G
  6. {3 g9 Y$ V/ g4 l) _
  7.         GPIO_InitTypeDef GPIO_InitStructure;/ m) D" h3 O# x( m- w8 [! B5 }( w
  8.     FSMC_NAND_PCCARDTimingInitTypeDef ComSpaceTiming,AttSpaceTiming;
    0 D0 L% H( W2 z: u
  9. 6 E4 ]0 T5 D# {" x! J! [; }
  10.         
    5 T4 S% F3 Q7 v$ u2 c) ~) t0 ~
  11.         ; G" G# R& w3 e% b& U2 G
  12.         RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟  
    & |% _" H) f$ A2 D# m
  13.         RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟  
    % u, ~1 v, p, h3 u
  14.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);+ i) ?) Z  o' b3 ^% ^& D

  15. + X9 h) e- F7 C  b

  16. / v: @2 D2 k1 v* o- p
  17.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_14|GPIO_Pin_15;
    $ v% ^8 R5 g/ ^" E# t; I
  18.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
    4 `6 L/ y* Q+ u# P
  19.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
    8 G8 j$ M, k: ?; g
  20.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    ( ~8 }  `7 G: W& r6 i
  21.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉* ~+ @: p2 p+ u' n4 F
  22.         GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化  ' b4 Y; ~+ ?. h; ^* Q2 `
  23. ; J4 Z) C+ u( V$ O3 x7 S6 ~
  24. / Q5 X1 R1 l1 z: I
  25.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10;
    : r) J& ^: ~& N* F% Q9 L9 Y
  26.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出* c7 J* A- E7 }6 S2 v: x
  27.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出- L! q3 c5 K# _: i
  28.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
    + ~4 q& }" {5 f) U  I6 q
  29.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉& v9 ~9 b7 D, ~9 k
  30.         GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化- U3 U) O. c7 G) Z" g3 i4 r& J" A

  31. + W+ o7 ]' E; v* T  N, G" T
  32. ; x0 ]- l! K% I0 B2 p
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    1 n) S6 ^# K' N' z+ v2 w
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
    $ Y. p( e: }% v
  35.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出  [0 D8 L# V0 n5 w0 Z
  36.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz) B7 ]. H/ W! _+ U. w" u
  37.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉+ q- T7 k. @" A
  38.         GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化# n& C6 A1 M4 H; F" x6 `
  39.         
    * e+ x2 @, p1 r- h. {
  40.         4 [' }$ L5 t0 w6 x0 u
  41.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; " p5 z4 G2 u9 z. w0 ~) F4 U' v
  42.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;: D9 ]" ]3 Y$ w+ z6 ^3 L2 Y: |; V
  43.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;1 t/ {1 E; t$ ]( i" |# |8 @- |
  44.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    / _5 N$ \: K9 P
  45.         GPIO_Init(GPIOD, &GPIO_InitStructure);
    ; n: e& Q. I0 @% M
  46.         ! M& |3 E# |8 O* u" P) r
  47.         
    7 Z: I) P% N  p
  48.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    1 w+ ?& y+ Y- r
  49.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    7 b$ ?3 \) w  t# ]: [3 G
  50.         GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;6 b8 a% ]& N6 V$ t# q
  51.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
      |; S  @' L. ~" F; _4 R
  52.         GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    : C: L' ?6 R* H) [+ o! v( j
  53.         GPIO_Init(GPIOD, &GPIO_InitStructure);
    0 \0 ^) ?) k/ W/ k" h! U' u" {* n" Z
  54.         GPIO_SetBits(GPIOD,GPIO_Pin_2);3 w6 j; |+ h' R& s
  55. 0 ^+ i" X5 k, v( V: ^9 t
  56.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12! e9 l7 d6 n1 d1 y$ i8 q: |8 l2 T
  57.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12" ^0 G3 C+ a! z' q: c; S
  58.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);//PD0,AF12
    * {  M. e, d+ b8 K) [2 J6 _, r3 ?
  59.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);//PD1,AF12        
    ( r3 s; g2 [2 S; S
  60.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
    # o1 f8 s: W, R/ \! `9 F
  61.         GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
    7 [9 K! ^& B) |/ s0 \
  62.         6 w5 u# a! q* y- a6 I$ h4 p4 i3 v- k3 ^
  63.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource3,GPIO_AF_FSMC);//PE7,AF12" ~/ D7 x$ l% @+ m* B- R) I  e6 [
  64.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource4,GPIO_AF_FSMC);: B7 a$ Y, j2 {: }& X" ^
  65.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
    , p6 N, O7 @  i
  66.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
    $ R3 \+ c4 r1 Z6 L8 P
  67.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
    2 s2 z' g$ Z1 f! h+ J2 k$ i
  68.         GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
    1 w$ z5 f0 ]- ]" ^
  69. / h  c6 L7 |0 |, V
  70.         GPIO_PinAFConfig(GPIOG,GPIO_PinSource9,GPIO_AF_FSMC);        
    1 V5 a, k, @9 l; p- ]. c+ Q
  71.         1 m3 h" A# ^) _* F
  72.         
    6 C4 O) M0 f( ~. z% d2 u) |5 k! G
  73.         6 ~5 R6 X3 g% M* \
  74.         
    ( k/ V2 S- y% \# |7 S8 a
  75.         ComSpaceTiming.FSMC_SetupTime=2;         //建立时间
    ) [& `) T1 p4 W7 f2 O, x  [
  76.     ComSpaceTiming.FSMC_WaitSetupTime=3;     //等待时间
    ) C& F2 W% w  N* B( F
  77.     ComSpaceTiming.FSMC_HoldSetupTime=2;     //保持时间; V) @3 ?2 j. R$ h
  78.     ComSpaceTiming.FSMC_HiZSetupTime=1;      //高阻态时间/ O( q# e+ r8 @1 G% i7 O

  79. / j9 `' a3 `- q. A3 G4 p: g" z' n
  80.     AttSpaceTiming.FSMC_SetupTime=2;         //建立时间# q5 e8 |1 V; l
  81.     AttSpaceTiming.FSMC_WaitSetupTime=3;     //等待时间
    3 [4 a" K$ P# E5 P1 v9 a
  82.     AttSpaceTiming.FSMC_HoldSetupTime=2;     //保持时间$ I0 q  w9 Q  t; ]- G+ z
  83.     AttSpaceTiming.FSMC_HiZSetupTime=1;      //高阻态时间        
    # L0 \8 ?% f7 t2 D& U9 |
  84. - D( L3 M6 y$ s  D2 M7 J9 W
  85.         
    / S- y& J5 L2 Y
  86.     NAND_Handler.FSMC_Bank=FSMC_Bank3_NAND;                          //NAND挂在BANK3上1 s) ]' V' `% U. K
  87.     NAND_Handler.FSMC_Waitfeature=FSMC_Waitfeature_Disable;                    //关闭等待特性3 P" _7 }' A. W2 n9 h0 B2 n1 R
  88.     NAND_Handler.FSMC_MemoryDataWidth=FSMC_MemoryDataWidth_8b;     //8位数据宽度
    1 y- a! v- n( o
  89.     NAND_Handler.FSMC_ECC=FSMC_ECC_Disable;              //不使用ECC' `5 I) j+ R, m) \. u
  90.     NAND_Handler.FSMC_ECCPageSize=FSMC_ECCPageSize_2048Bytes;      //ECC页大小为2k: H: A6 h, v& t  \3 u* \
  91.     NAND_Handler.FSMC_TCLRSetupTime=0;                                  //设置TCLR(tCLR=CLE到RE的延时)=(TCLR+TSET+2)*THCLK,THCLK=1/180M=5.5ns1 W& E9 J" h9 y9 |# u
  92.     NAND_Handler.FSMC_TARSetupTime=1;                                   //设置TAR(tAR=ALE到RE的延时)=(TAR+TSET+2)*THCLK,THCLK=1/180M=5.5n。   
    7 p* z! s! ~2 ^% V( }! L
  93.         NAND_Handler.FSMC_CommonSpaceTimingStruct=&ComSpaceTiming;
    2 P" F2 Z& j, i; Z4 j8 ?  U
  94.         NAND_Handler.FSMC_AttributeSpaceTimingStruct=&AttSpaceTiming;               
    1 {: t2 ^% Y" w( ^1 i1 A6 Y1 R  c  k
  95.         $ e' @$ X; x9 y
  96.         
    / j/ ~) |: C! q( r2 ^& g+ s" J# X
  97.         
    3 `/ A  k2 R; f5 x
  98.     FSMC_NANDInit(&NAND_Handler);0 Y5 N0 y( [/ z
  99.     FSMC_NANDCmd(FSMC_Bank3_NAND,ENABLE);
    $ g- M+ _. C% W9 K
  100.          ) A- I7 t8 `- M% x* f0 m5 e4 O/ F
  101.     NAND_Reset();                               //复位NAND  X" ^. j8 L$ x" `/ @$ Z
  102.     delay_ms(100);
      Q: F" q$ M! o5 ]2 R
  103.     nand_dev.id=NAND_ReadID();                //读取ID( k, Y+ L2 v( I" Z1 C: ?2 q
  104.         NAND_ModeSet(4);                                //设置为MODE4,高速模式 7 r% ~" Y0 T5 A) [3 x# K+ Y) o2 M
  105.         if(nand_dev.id==MT29F4G08ABADA)//NAND为MT29F4G08ABADA
      l0 P3 a/ j. ^# i  j8 q5 ~' F
  106.     {' y; \  e. a8 G& g
  107.         nand_dev.page_totalsize=2112;        //nand一个page的总大小(包括spare区)# C, l  q* N4 U+ M: Z0 `
  108.         nand_dev.page_mainsize=2048;         //nand一个page的有效数据区大小
    5 y' U" e7 T: z; o( ^; T! L
  109.         nand_dev.page_sparesize=64;                //nand一个page的spare区大小
      S! b+ Q$ y# J. m3 p7 ~
  110.         nand_dev.block_pagenum=64;                //nand一个block所包含的page数目3 ?: O7 w7 x9 o* E+ X' H( m5 L
  111.         nand_dev.plane_blocknum=2048;        //nand一个plane所包含的block数目! C# C% p4 Z2 x$ p- z
  112.         nand_dev.block_totalnum=4096;         //nand的总block数目/ U8 p" f1 c/ o  `5 D/ Z. i
  113.     }else return 1;        //错误,返回
    1 L% ?  K. p4 @# x- t
  114.     return 0;: A; U8 ]0 Q  K3 w
  115. }
    & \/ g2 [5 @; U

  116. 5 ?  \3 M6 b* N9 v8 W

  117.   U6 j( d- i1 G* U0 ^1 t
  118. //读取NAND FLASH的ID
    " O+ |1 U( V) {% h9 d! i
  119. //返回值:0,成功;
      R( A4 [$ S- k% d. D) m4 Z1 W
  120. //    其他,失败
    & Q! n7 ^  k1 e9 p: ~8 _8 X
  121. u8 NAND_ModeSet(u8 mode)- J* f5 |8 c- B( b
  122. {   / K. H2 y( F' _1 [! {! R
  123.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_FEATURE;//发送设置特性命令) a% i8 |0 ~" x$ U+ F
  124.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=0X01;                //地址为0X01,设置mode
    ( z- Y; F. v! y+ ?5 F6 `! |
  125.          *(vu8*)NAND_ADDRESS=mode;                                        //P1参数,设置mode1 I7 Y0 e8 o4 M; a0 }
  126.         *(vu8*)NAND_ADDRESS=0;, [8 p& L+ ^$ d5 u
  127.         *(vu8*)NAND_ADDRESS=0;
    . n8 z2 e% ^( m/ X- M
  128.         *(vu8*)NAND_ADDRESS=0; / O/ ^; Q9 D- k9 n5 i; O8 u* ]
  129.     if(NAND_WaitForReady()==NSTA_READY)return 0;//成功
    & n" P$ @) H( I; G
  130.     else return 1;                                                                //失败
    ; ^5 p9 v+ H1 f8 i# k! ~2 _: c
  131. }: S& ^# `8 V& s1 |+ D0 a8 |' T0 ?
  132. 0 ?9 @" a9 g9 N
  133. //读取NAND FLASH的ID  u! J6 a+ j* C3 X- w
  134. //不同的NAND略有不同,请根据自己所使用的NAND FALSH数据手册来编写函数
    + R* P" s8 w. I8 y, g
  135. //返回值:NAND FLASH的ID值
    0 `& |" H  G5 p: Y6 T
  136. u32 NAND_ReadID(void)5 U3 }$ p+ y! `$ h+ X
  137. {
    0 R: b) c: n* d7 O) f9 J1 p$ x. |
  138.     u8 deviceid[5];
      [0 ?& v) E" v4 W7 u# x
  139.     u32 id;  
    : i+ s( U; _: B7 _. f! g3 v
  140.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_READID; //发送读取ID命令7 S3 x. U) h% {* q! i9 b- Y
  141.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=0X00;2 k  e! Z7 o8 `" d, [
  142.         //ID一共有5个字节- F/ w4 `$ e' q2 \) W
  143.     deviceid[0]=*(vu8*)NAND_ADDRESS;      # T# b. N9 L1 O9 @# L4 H. @
  144.     deviceid[1]=*(vu8*)NAND_ADDRESS;  
    * g& E1 y7 X! K' K! m* g
  145.     deviceid[2]=*(vu8*)NAND_ADDRESS; % b0 w2 F/ N  g
  146.     deviceid[3]=*(vu8*)NAND_ADDRESS;
    5 k# C* d  v  P! }- W9 p
  147.     deviceid[4]=*(vu8*)NAND_ADDRESS;  
    " Q- l+ s7 W" J( d, J; C6 f; S+ Q
  148.     //镁光的NAND FLASH的ID一共5个字节,但是为了方便我们只取4个字节组成一个32位的ID值
    # k( I2 F: d% `8 W1 q
  149.     //根据NAND FLASH的数据手册,只要是镁光的NAND FLASH,那么一个字节ID的第一个字节都是0X2C
    # l/ T' F3 |" z1 ?+ c6 z5 [- L
  150.     //所以我们就可以抛弃这个0X2C,只取后面四字节的ID值。
    7 Z% g7 i, t' r. }
  151.     id=((u32)deviceid[1])<<24|((u32)deviceid[2])<<16|((u32)deviceid[3])<<8|deviceid[4];
    " X6 j+ z% @, q5 a. G6 M
  152.     return id;' C/ M2 j1 @3 L- y' z8 F
  153. }  
    5 b6 u% x  @& I9 e
  154. //读NAND状态
    6 w* K% X& L/ I, k/ @5 s
  155. //返回值:NAND状态值( t- @6 y7 G1 @5 s2 b" V
  156. //bit0:0,成功;1,错误(编程/擦除/READ)/ C! }, l+ x1 j2 ]; a
  157. //bit6:0,Busy;1,Ready! ^, D* @3 p/ `/ j" d
  158. u8 NAND_ReadStatus(void)
    * S( c! ^4 V7 L
  159. {* W! T* N& K, S3 \
  160.     vu8 data=0; $ j9 @' b7 K2 O1 }0 }6 E
  161.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_READSTA;//发送读状态命令
    / K+ ]* D. g/ B  ^9 ?2 ]3 p
  162.     NAND_Delay(NAND_TWHR_DELAY);        //等待tWHR,再读取状态寄存器
    / \7 q# H! t% o0 e
  163.          data=*(vu8*)NAND_ADDRESS;                        //读取状态值
    ! W% R6 b% W4 q7 x5 a! y. ^  \
  164.     return data;( u4 |$ z' d" k. Z! I
  165. }1 O6 Z, K2 t6 `" z
  166. //等待NAND准备好2 T5 i! b+ S3 i- C* m1 p) l; U
  167. //返回值:NSTA_TIMEOUT 等待超时了
    6 r: Y  B0 D% L3 c' X/ w- c; _- e
  168. //      NSTA_READY    已经准备好8 d, ]1 _. m5 t% W
  169. u8 NAND_WaitForReady(void)1 ~: F: [" {( S+ r  y/ g
  170. {
    5 S4 c: @8 ^, x# F3 |
  171.     u8 status=0;
    ( L7 d& n( \& D0 J( z& t) n" m. x
  172.     vu32 time=0; 7 r3 l0 t( A& N- Q+ {7 p+ B
  173.         while(1)                                                //等待ready
    3 q8 d6 A0 P! ~
  174.         {! h5 Q/ R1 y# \' ?, f( \% [* y0 O. ]
  175.                 status=NAND_ReadStatus();        //获取状态值
    3 d& O) N# J) F! Z' L: {/ g" D
  176.                 if(status&NSTA_READY)break;
    1 e# N4 @: Y1 p7 G* p2 p
  177.                 time++;2 [& I! U8 p. h
  178.                 if(time>=0X1FFFFFFF)return NSTA_TIMEOUT;//超时
    1 ]# t- d0 z$ ]" f  u0 ^
  179.         }  % L& e8 J+ H2 Q+ K' b3 h; C
  180.     return NSTA_READY;//准备好) D& K. g5 o) ]
  181. }  
    ! C* r. w2 i7 C, b8 U2 K
  182. //复位NAND
    $ R& `+ h( `" L" P
  183. //返回值:0,成功;
    8 f9 q& [0 C0 U
  184. //    其他,失败
    & \# G; [5 o4 y) D4 ]! f6 R0 s
  185. u8 NAND_Reset(void)
    . I- C! p" u1 u
  186. {
    ( C# I5 Z5 H, Q- F0 r) Z( r9 ]
  187.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_RESET;        //复位NAND" f8 u/ x0 ^+ j. B6 |9 T
  188.     if(NAND_WaitForReady()==NSTA_READY)return 0;//复位成功
    2 _1 S) a2 O% U* c2 s
  189.     else return 1;                                                                //复位失败5 I4 {# w* ^1 G1 Y# z3 s! z
  190. }
    0 Z* m6 T: {) p5 z
  191. //等待RB信号为某个电平; k" a+ J+ Y3 y3 o0 R" S. k5 `, A
  192. //rb:0,等待RB==0
    " Z/ ]8 [/ N+ @2 A" i4 U
  193. //   1,等待RB==1! W+ J' }2 C# B' P8 Q% F* z
  194. //返回值:0,成功( c+ f) T3 E' Y
  195. //       1,超时
    # t0 n) _- e, p1 i, ?, d
  196. u8 NAND_WaitRB(vu8 rb)7 w4 Z  G7 g8 L3 Q
  197. {
    4 _% N  L1 a! T) l
  198.     vu32 time=0;  
    8 l1 ~8 }( F1 X) E4 R  B
  199.         while(time<0X1FFFFFF)
    4 [* J" @8 H5 U" R7 Z1 q
  200.         {
    8 q9 S& r8 o* K
  201.                 time++;# s) V; ?' U) J
  202.                 if(NAND_RB==rb)return 0;
    , Y- m- D5 _  G  k: r5 y
  203.         }
    & R! y7 e7 i) ^3 y
  204.         return 1;
    - t* X: h+ p: C2 [2 g. ]# T
  205. }4 Q) y! v* L1 N
  206. //NAND延时
    2 ~0 U0 @& {" x3 j- Q9 l9 M
  207. //一个i++至少需要4ns3 z4 g& _6 P# E! u) A. }6 C
  208. void NAND_Delay(vu32 i)
    ' S/ R, M1 Y( j$ y& t! \0 q8 r
  209. {
    1 m/ x6 F; t3 @) O& ?1 t# y' C$ Z: E
  210.         while(i>0)i--;5 d9 n8 l& X- k& A6 N
  211. }# r6 x& w' Q* q+ O* ]. Y
  212. //读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数)
    ! n7 v4 L: f! l3 D) F
  213. //PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
    7 v: {9 }, ]( a' E: {" j% w
  214. //ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)4 h  g9 ]$ `# v6 D7 r
  215. //*pBuffer:指向数据存储区+ J9 b1 s; {/ |& a
  216. //NumByteToRead:读取字节数(不能跨页读)
    6 u  u* d; r+ `$ w  X. Y: n$ C
  217. //返回值:0,成功
    # x# t1 `% E  h! K8 I2 e
  218. //    其他,错误代码
    9 \4 f" K" P- y
  219. u8 NAND_ReadPage(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToRead)6 z/ @+ w& x0 N& H
  220. {  G# ]" G! D$ Z$ z* M
  221.     vu16 i=0;
    0 J2 f( Z" S: r% s7 p3 y/ D7 [
  222.         u8 res=0;
    . n) P; D+ p7 a7 q/ t, _
  223.         u8 eccnum=0;                //需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc. b; ?  W2 b: L- ]8 _9 V6 H
  224.         u8 eccstart=0;                //第一个ECC值所属的地址范围. i  ?6 H" J8 e* w6 ?
  225.         u8 errsta=0;
      w, e- f# f! {6 B7 D6 G
  226.         u8 *p;
    6 e5 z4 ~; D( D1 i
  227.      *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
    . [3 F' z3 k- X( P. Z* s& v  S6 ^
  228.     //发送地址
    , M, o3 |2 W$ V
  229.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;; A- I3 ?  ]6 f" v3 o  t. d! M( a* I
  230.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);' ?, X3 Y* h) L0 L
  231.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;# M7 s; |7 U) J; X; U/ `( T
  232.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);
    5 M7 l9 }4 J1 C+ I# G! a5 {
  233.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);
    $ w/ X" Z* m: j/ x7 K. `: W# D
  234.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;9 v2 P; Y* S! I5 O
  235.     //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过0 i0 A$ S# C0 H# f- v
  236.     //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备. }" ^: W7 d) u9 m9 n
  237.     //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
    + }0 U" q! h: S2 z2 U4 w7 [
  238.     //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行: o, N) i: {3 m2 r3 T7 H2 S
  239.     //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。  S+ ?! Q9 e( D
  240.         res=NAND_WaitRB(0);                        //等待RB=0 $ n3 p& Q1 h( U9 q
  241.     if(res)return NSTA_TIMEOUT;        //超时退出, Z, c2 q" O. |2 t6 a2 a
  242.     //下面2行代码是真正判断NAND是否准备好的
    % o+ M  C; H2 S& n+ U
  243.         res=NAND_WaitRB(1);                        //等待RB=1 # n8 y( P. s6 i* n/ c/ P4 U
  244.     if(res)return NSTA_TIMEOUT;        //超时退出8 `/ ], R* j8 D3 M" J
  245.         if(NumByteToRead%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验. `% ~3 L* a) P2 U) G2 q5 C$ T
  246.         { 5 `- K; L2 l' Q
  247.                 //读取NAND FLASH中的值2 g, Q: t& G' o  ^. ]6 A
  248.                 for(i=0;i<NumByteToRead;i++)
    ; m* h2 V) O% ^: x6 D
  249.                 {
    * c& M% ]4 Y- P* `  u9 n! p& f/ z
  250.                         *(vu8*)pBuffer++ = *(vu8*)NAND_ADDRESS;
    ' X, M* I( L3 Q0 c9 I- @
  251.                 }
      ~3 [% D9 l- w# M7 I, g( R
  252.         }else
    - [+ y; e. p7 Q4 r# a1 d; E
  253.         {
    " j- s/ }7 c" J6 [- n0 P, _
  254.                 eccnum=NumByteToRead/NAND_ECC_SECTOR_SIZE;                        //得到ecc计算次数( d2 A. w7 [! C
  255.                 eccstart=ColNum/NAND_ECC_SECTOR_SIZE;! B, ~# a2 ]3 F. N
  256.                 p=pBuffer;
    9 U3 d7 X' h3 {! _
  257.                 for(res=0;res<eccnum;res++)
    3 w' k+ b5 p6 F
  258.                 {2 n! Q6 z; y% k) a% d( B% k: @
  259.                         FSMC_Bank3->PCR3|=1<<6;                                                //使能ECC校验
    + I& f* p' M. i/ e6 U( U
  260.                         for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)                                //读取NAND_ECC_SECTOR_SIZE个数据7 T, x+ \* H! l0 F
  261.                         {, _4 l# P' s3 c/ c( r
  262.                                 *(vu8*)pBuffer++ = *(vu8*)NAND_ADDRESS;
    8 e3 B+ [! Q$ n  J
  263.                         }               
    ; q( D3 Z6 b4 L3 f
  264.                         while(!(FSMC_Bank3->SR3&(1<<6)));                                //等待FIFO空        
    8 P3 [* Z% u* a: q. j/ {& r4 X
  265.                         nand_dev.ecc_hdbuf[res+eccstart]=FSMC_Bank3->ECCR3;//读取硬件计算后的ECC值& c4 t9 x! v' D) M3 o8 H  B) Y/ B, v
  266.                         FSMC_Bank3->PCR3&=~(1<<6);                                                //禁止ECC校验0 P) a% o- |+ X) s' I- ~
  267.                 }
    7 p% ~0 v" `+ v6 t) ?  X
  268.                 i=nand_dev.page_mainsize+0X10+eccstart*4;                        //从spare区的0X10位置开始读取之前存储的ecc值
    4 s- }; g5 Q4 y! v+ b2 Z6 Q
  269.                 NAND_Delay(NAND_TRHW_DELAY);//等待tRHW , U" C4 C. m9 Y& v2 m( m* q* n  B
  270.                 *(vu8*)(NAND_ADDRESS|NAND_CMD)=0X05;                                //随机读指令4 x, a" \0 y- Y& D
  271.                 //发送地址+ b' w# l) E% g0 Y9 v( j' a
  272.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)i;8 i. ~: P& k8 L% A7 a
  273.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(i>>8);
    9 y7 Q- Y! S' y$ A% t7 u0 P+ ]
  274.                 *(vu8*)(NAND_ADDRESS|NAND_CMD)=0XE0;                                //开始读数据
    ' E; D+ S* v+ y
  275.                 NAND_Delay(NAND_TWHR_DELAY);//等待tWHR
      Z8 R0 [4 j# q) A
  276.                 pBuffer=(u8*)&nand_dev.ecc_rdbuf[eccstart];* h  y; y0 U* x
  277.                 for(i=0;i<4*eccnum;i++)                                                                //读取保存的ECC值' b2 A$ z, i( M% l! T5 X
  278.                 {1 w  @  x3 Z3 f
  279.                         *(vu8*)pBuffer++= *(vu8*)NAND_ADDRESS;+ w6 U+ j- S$ S3 U8 r, O
  280.                 }                        # c; l; ^: h9 S3 V' l, r
  281.                 for(i=0;i<eccnum;i++)                                                                //检验ECC
    ! C: `* X; ~9 j% F
  282.                 {
    8 z& u4 ~3 k* h/ K5 N4 k  S1 v
  283.                         if(nand_dev.ecc_rdbuf[i+eccstart]!=nand_dev.ecc_hdbuf[i+eccstart])//不相等,需要校正
    * k: F4 l9 s( C7 B
  284.                         {, Z6 M0 S/ E* }6 \( e3 E
  285.                                 printf("err hd,rd:0x%x,0x%x\r\n",nand_dev.ecc_hdbuf[i+eccstart],nand_dev.ecc_rdbuf[i+eccstart]);
    - T* f0 f! M) }1 L* {, m# T& u
  286.                                  printf("eccnum,eccstart:%d,%d\r\n",eccnum,eccstart);        8 Q) e4 O9 w% x  t$ |
  287.                                 printf("PageNum,ColNum:%d,%d\r\n",PageNum,ColNum);        
    # o) r  ^# w( Y
  288.                                 res=NAND_ECC_Correction(p+NAND_ECC_SECTOR_SIZE*i,nand_dev.ecc_rdbuf[i+eccstart],nand_dev.ecc_hdbuf[i+eccstart]);//ECC校验; N' W, F  X: o! g
  289.                                 if(res)errsta=NSTA_ECC2BITERR;                                //标记2BIT及以上ECC错误" g  V, E& a9 o7 S) }& ?
  290.                                 else errsta=NSTA_ECC1BITERR;                                //标记1BIT ECC错误
    4 |9 i0 w5 X2 p$ o1 p/ K
  291.                         }
    . U/ r/ q% i7 B* ~
  292.                 }                 ; c3 `! v" r0 X" l" H
  293.         }) P& T; t  g8 ~1 q
  294.     if(NAND_WaitForReady()!=NSTA_READY)errsta=NSTA_ERROR;        //失败
    : i. D6 ]9 N+ V0 z* R' S
  295.     return errsta;        //成功   / w) m0 K/ u- t' N5 h& s
  296. }
    ; D. F1 Y' w6 |, m: P0 U$ m# u
  297. //读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数),并对比(FTL管理时需要)
    ; ]+ I7 V" k5 Y# a- u) T3 `
  298. //PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
    6 y# E% Y: ~0 @8 ~" \; z4 f+ N8 `
  299. //ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)' S$ H% u) k2 X$ p) U
  300. //CmpVal:要对比的值,以u32为单位
    6 \, ^% S1 @' y$ K; p* a
  301. //NumByteToRead:读取字数(以4字节为单位,不能跨页读)6 ?1 `# @) p& e; k
  302. //NumByteEqual:从初始位置持续与CmpVal值相同的数据个数
    5 x" S* Y  V$ e8 I
  303. //返回值:0,成功2 g8 v4 D- |1 \& D2 l
  304. //    其他,错误代码
    2 a: X4 q( ~! D6 U
  305. u8 NAND_ReadPageComp(u32 PageNum,u16 ColNum,u32 CmpVal,u16 NumByteToRead,u16 *NumByteEqual)
    - k2 g& k7 J8 y. ]2 ~
  306. {
    + H6 m8 D6 d5 l! f8 l: v
  307.     u16 i=0;
    9 ]% a6 j; `; Z
  308.         u8 res=0;
    9 t) E  m2 P1 n3 x) T" S
  309.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
    6 u, S( v9 u8 l. h: F
  310.     //发送地址
    $ j; r* \; W7 J" C+ e
  311.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;
    " |- f, n' u) \4 X# R% e
  312.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);
    8 p! ?$ D' X& R% G9 U( V, \# ~
  313.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;
    7 ?* Y1 F1 |' I* E, j
  314.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);" z$ o* P& l% B( q) H- r( u
  315.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);& Q+ C; L( C! `4 ^5 s
  316.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;3 U1 r) L' l& U" X/ u5 n
  317.     //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过- G4 V- m1 _  i$ x0 b7 e
  318.     //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备9 d  T4 J% ~' V
  319.     //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙, V& p5 }* i5 y
  320.     //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
    ! W" \1 K5 I3 z9 Z
  321.     //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
    ! `$ b$ p6 F1 v% O. u7 Z
  322.         res=NAND_WaitRB(0);                        //等待RB=0 + W' r  G" u% f4 j/ q
  323.         if(res)return NSTA_TIMEOUT;        //超时退出& |! l! O; Z* ~
  324.     //下面2行代码是真正判断NAND是否准备好的3 \" a9 @& q. {5 ^& s5 T, c4 ?4 k
  325.         res=NAND_WaitRB(1);                        //等待RB=1
    ( n. F% j8 W1 W( F
  326.     if(res)return NSTA_TIMEOUT;        //超时退出  
    3 U4 o! m# t. ], v0 k
  327.     for(i=0;i<NumByteToRead;i++)//读取数据,每次读4字节; m% d4 v- d* w# u& m# f6 C, u) P
  328.     {
    . I8 p5 q0 N- \- L
  329.                 if(*(vu32*)NAND_ADDRESS!=CmpVal)break;        //如果有任何一个值,与CmpVal不相等,则退出.
    ! Q4 K9 c6 {: g( {- v( r$ d, ]" A
  330.     }8 ]3 ?1 c* r9 w2 v. w
  331.         *NumByteEqual=i;                                        //与CmpVal值相同的个数; H9 N" d9 G4 @5 E" a
  332.     if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
    : s* M: t* k+ Q4 d* e
  333.     return 0;        //成功   1 h, r# t  t* f5 h% `; [/ H5 t
  334. } ( ~* P5 {7 I+ D2 n5 j8 i' a& w
  335. //在NAND一页中写入指定个字节的数据(main区和spare区都可以使用此函数)
    8 Z/ W0 w# t2 K- a, Z" X2 D+ c! M/ v
  336. //PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
    % ?3 n7 j! \, B/ g, s( j, I: n4 v# g
  337. //ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)( a9 x. ]6 _) l7 h  `* @
  338. //pBbuffer:指向数据存储区) f9 `( s" T+ J  d
  339. //NumByteToWrite:要写入的字节数,该值不能超过该页剩余字节数!!!$ |& H# |# r; ~/ [: O  H# t2 u
  340. //返回值:0,成功 . G, k5 ?+ e. n
  341. //    其他,错误代码1 |% A6 I  `; ^& k3 F5 Z1 c: d
  342. u8 NAND_WritePage(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToWrite)
    $ z1 Y+ `0 U3 X' b: J
  343. {* e1 R7 K) k6 Z0 j" W
  344.     vu16 i=0;  
    * c8 I( r0 [) u# |7 x
  345.         u8 res=0;0 |& e, [. t6 @5 _) R9 b
  346.         u8 eccnum=0;                //需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc/ F" w7 p2 x! g; _9 E
  347.         u8 eccstart=0;                //第一个ECC值所属的地址范围
    ) z& c& K4 Y* [' ^  r
  348.         
    + ~3 \% \$ p4 K- ]8 }: O
  349.         *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
    + f* J0 m) V5 J: u6 J9 X" `8 j
  350.     //发送地址
    ' t/ R& q3 |$ E6 v; V
  351.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;2 O% |9 x" e1 o9 s( t, ]
  352.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);
    6 w2 X; B- i* b! A
  353.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;; E' [  d- @9 p$ D7 b5 z
  354.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);3 u" ~7 S) c% i; ?" |
  355.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);1 T- B' E9 v- ~, L- q, Q
  356.         NAND_Delay(NAND_TADL_DELAY);//等待tADL 5 f# t& U  C- p* b  ?
  357.         if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
    , V$ Z, ]0 m2 Z: h9 Y3 v
  358.         {  : {2 E: D" R9 R  O# C* }+ o
  359.                 for(i=0;i<NumByteToWrite;i++)                //写入数据
    & l4 n0 V( s& l0 X9 W% j4 A( R
  360.                 {* c3 J/ L6 P" [( U
  361.                         *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
    ; _! k. b4 P4 j1 u
  362.                 }
    & {: H" Q5 u  Q, v. G( b
  363.         }else' p# g) L! c6 f" W# S" c. `
  364.         {
    " S, H; t$ k; m# Q+ ^2 M
  365.                 eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;                        //得到ecc计算次数
    6 @3 |$ J) P; k+ `. s/ U8 l
  366.                 eccstart=ColNum/NAND_ECC_SECTOR_SIZE; 9 [2 K% k/ T- E5 {8 N: o% d* P; e
  367.                  for(res=0;res<eccnum;res++)
    ' e& g! J4 f1 A0 ?* N, }
  368.                 {
    - y5 {* D) v! K7 O& V
  369.                         FSMC_Bank3->PCR3|=1<<6;                                                //使能ECC校验
    6 J/ ~1 P; F, W9 ^9 a( t. G
  370.                         for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)                                //写入NAND_ECC_SECTOR_SIZE个数据
      X( t) M7 B% m7 c1 D
  371.                         {1 o8 @6 ^6 {2 d9 g) I
  372.                                 *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
    # T- l# u$ A; H5 K$ d( i( g+ u
  373.                         }                . [& d' }+ r( ^* M
  374.                         while(!(FSMC_Bank3->SR3&(1<<6)));                                //等待FIFO空        
    ' d0 C/ _$ {5 g; d7 o
  375.                         nand_dev.ecc_hdbuf[res+eccstart]=FSMC_Bank3->ECCR3;        //读取硬件计算后的ECC值
    ; @# N' T( _) Y2 j  L1 T: X) b
  376.                           FSMC_Bank3->PCR3&=~(1<<6);                                                //禁止ECC校验. U& _; g# o  }( d$ o
  377.                 }  
    5 @0 o$ G. s3 c  f6 |: d3 B" F  M
  378.                 i=nand_dev.page_mainsize+0X10+eccstart*4;                        //计算写入ECC的spare区地址3 h, v8 \7 W' @" U
  379.                 NAND_Delay(NAND_TADL_DELAY);//等待tADL
    0 T, s( e/ v4 q8 |$ X
  380.                 *(vu8*)(NAND_ADDRESS|NAND_CMD)=0X85;                                //随机写指令
    ' I# i8 V1 z( d! k3 V8 e% b& F
  381.                 //发送地址
    ; J3 |! V% f9 c
  382.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)i;+ C( k/ n. [, [+ t% Q& \0 H( U
  383.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(i>>8);
      `+ }  ~; e# k' i
  384.                 NAND_Delay(NAND_TADL_DELAY);//等待tADL
    . k: `8 ]8 y8 H4 N* Z
  385.                 pBuffer=(u8*)&nand_dev.ecc_hdbuf[eccstart];
    6 p/ ^  d3 m5 v. I
  386.                 for(i=0;i<eccnum;i++)                                        //写入ECC
    5 U4 y2 m  {) k% b, @$ z/ ^! c; m
  387.                 { 2 m" T* N; \- G' ~5 `, p
  388.                         for(res=0;res<4;res++)                                 
    / k/ B" `' N4 Q" e+ W5 X, W
  389.                         {3 |9 p) ]7 W/ W5 f) x
  390.                                 *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
    8 n* @' O" i9 N
  391.                         }; R; D1 X  T% q
  392.                 }                 " e5 B  Y8 s; m; d
  393.         }
    ' n( y6 r  j2 T5 f5 w/ D
  394.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1; - q! ^3 a- R. z" V+ M5 N8 q& T8 s
  395.          delay_us(NAND_TPROG_DELAY);        //等待tPROG. y1 ]* V! T' Q. x2 T
  396.         if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败! j* K7 |7 k& q) X) v6 l: X
  397.     return 0;//成功   ( c+ X$ E# p; w0 ^5 H  P
  398. }: H+ J& h% D. S* m5 ?  r+ ]
  399. //在NAND一页中的指定地址开始,写入指定长度的恒定数字
    8 u* u/ d; K, L6 n, y" I
  400. //PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
    ' A  O/ A7 }  t5 r6 h+ C, C6 w9 K) h
  401. //ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)# I. t+ ]; A" K! D% u$ _0 @/ {7 m
  402. //cval:要写入的指定常数
    5 ?' R0 d7 y2 O
  403. //NumByteToWrite:要写入的字数(以4字节为单位). [. z5 X' ~, l% r
  404. //返回值:0,成功 5 S1 n) l$ K; r1 V( P' n9 ~! x
  405. //    其他,错误代码
    ) H" T" {4 O+ _8 E/ I) G
  406. u8 NAND_WritePageConst(u32 PageNum,u16 ColNum,u32 cval,u16 NumByteToWrite)
    4 ?$ k+ H9 T! O$ V3 S( e, O0 f
  407. {# _" L3 U8 ~$ |5 N3 h
  408.     u16 i=0;  
    4 u  G0 W6 ^( i" m7 ?% D$ R' D
  409.         *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
    % V4 a; V! d6 O
  410.     //发送地址0 M2 o: }4 P$ N, W4 `
  411.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;8 d" O% b" v+ o% ~1 K9 b( c. `
  412.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);
    - v: s9 r7 ~  p
  413.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;. ?- {3 X6 w  B9 @
  414.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);% ?; {' B% ~7 I7 M! L
  415.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);
      a1 I8 ?) M' U5 m) \
  416.                 NAND_Delay(NAND_TADL_DELAY);//等待tADL
    / c0 f& L( S# a- n
  417.         for(i=0;i<NumByteToWrite;i++)                //写入数据,每次写4字节
    5 E; ^+ |  ^6 E5 p# N; g
  418.         {' j3 a. _, r- H7 x
  419.                 *(vu32*)NAND_ADDRESS=cval;9 `6 k7 a2 [  T+ C7 @
  420.         } . j/ \, ^7 @8 \1 c
  421.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1;
    % n/ F/ E$ _# W2 l- L
  422.          delay_us(NAND_TPROG_DELAY);        //等待tPROG* @$ ?- a) q1 Y: u; `
  423.     if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
    . U: `: C, c" T, R) p( p* {3 J, z. x
  424.     return 0;//成功   & N; J6 y( [) d1 c4 p& C" v
  425. }( z' R8 a  B6 x/ ]! O
  426. //将一页数据拷贝到另一页,不写入新数据8 Y; V- D2 H7 X! f# x
  427. //注意:源页和目的页要在同一个Plane内!
    % b! R/ E2 ]4 B2 R5 }7 V7 o8 k8 g
  428. //Source_PageNo:源页地址,范围:0~(block_pagenum*block_totalnum-1)
    + O7 O0 ]) {# U: h) Q1 Q2 r1 x
  429. //Dest_PageNo:目的页地址,范围:0~(block_pagenum*block_totalnum-1)  . O) Y7 f  w1 p0 L& I: d
  430. //返回值:0,成功
    & ]$ w, T9 X9 O
  431. //    其他,错误代码; x" c/ Y& i+ C; O3 `4 S
  432. u8 NAND_CopyPageWithoutWrite(u32 Source_PageNum,u32 Dest_PageNum)
    1 s1 R( \  @/ f6 o0 h0 g. m7 e
  433. {* |9 ^% w, [' g* r
  434.         u8 res=0;
    * `1 ?: J# s3 M- P$ X
  435.     u16 source_block=0,dest_block=0;  . d# O  s$ \0 @- m1 {
  436.     //判断源页和目的页是否在同一个plane中4 |+ x1 \! `0 i) X; _3 W- [
  437.     source_block=Source_PageNum/nand_dev.block_pagenum;# q  V6 R. _1 u# d! {. `
  438.     dest_block=Dest_PageNum/nand_dev.block_pagenum;
    6 O2 ^: i$ y1 _
  439.     if((source_block%2)!=(dest_block%2))return NSTA_ERROR;        //不在同一个plane内
    - M& h: \0 y5 |2 @: e
  440.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD0;        //发送命令0X00
    % A. M7 F0 V- G$ Q, j- }
  441.     //发送源页地址
    # @3 d& n, f! @2 s# I
  442.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;0 ?0 `- v3 U: b# w9 B, k- h5 p
  443.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;/ H+ n, N( C% l( C& A
  444.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)Source_PageNum;
    1 R* c2 Y4 \$ b' X/ R% t
  445.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Source_PageNum>>8);
    8 p2 }8 d" T5 w7 W4 n
  446.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Source_PageNum>>16);
    . H4 W2 w# K+ J; N' D$ x% w, r! X) w2 |
  447.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD1;//发送命令0X35
    - H, D7 i% f% j, W2 P. u
  448.     //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过5 R6 k. D$ V# t; M0 y5 c0 c
  449.     //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备" S5 _, ]7 {& R9 `3 [0 H
  450.     //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
    1 |2 c; P: X! x# |$ X/ Q# {& w6 X/ y
  451.     //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行# v0 g8 I9 z# m9 w. W, d
  452.     //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。) l4 F/ }4 e9 C% B
  453.         res=NAND_WaitRB(0);                        //等待RB=0
    ; _6 M: O2 f8 _8 s4 N6 X. ~* }' N
  454.         if(res)return NSTA_TIMEOUT;        //超时退出
    4 N9 g: Z5 G) t3 t: k' m
  455.     //下面2行代码是真正判断NAND是否准备好的
    3 U; |3 C& J! z0 l" C/ E" i
  456.         res=NAND_WaitRB(1);                        //等待RB=1 1 R, }+ L9 [2 M8 `4 A4 {, S
  457.     if(res)return NSTA_TIMEOUT;        //超时退出
    0 K. a: I; J6 l1 L
  458.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD2;  //发送命令0X85
    9 K; C5 l) ~* i/ s  [
  459.     //发送目的页地址
    6 h6 l: p1 k' \( w& i4 ?+ X
  460.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;0 c* o( M0 }, i" e" B
  461.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;
    0 ]+ O; @2 @( a: y
  462.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)Dest_PageNum;4 s3 v+ P# t  m6 `' ^" n( j7 J
  463.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Dest_PageNum>>8);
    2 \2 N0 y+ b; b( f
  464.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Dest_PageNum>>16);1 w! J- \& Z. i" J
  465.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;        //发送命令0X10 # x' t' b6 Z0 P' V
  466.         delay_us(NAND_TPROG_DELAY);        //等待tPROG! [: b% B5 u; h
  467.     if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;        //NAND未准备好
    ! h9 v( z2 ?! W
  468.     return 0;//成功   
    , h1 F! K- b, T; D6 a& M3 e
  469. }( ]! E* h' Q% A9 ^

  470. - W( |8 x* ?# t( o3 V5 S
  471. //将一页数据拷贝到另一页,并且可以写入数据
      \; R$ Y4 X- C1 d
  472. //注意:源页和目的页要在同一个Plane内!' v% [. b# O: n0 I
  473. //Source_PageNo:源页地址,范围:0~(block_pagenum*block_totalnum-1)
    ' R/ Y- l0 @: L/ O; w
  474. //Dest_PageNo:目的页地址,范围:0~(block_pagenum*block_totalnum-1)  " n7 C/ A4 S& V% e5 D  E
  475. //ColNo:页内列地址,范围:0~(page_totalsize-1)
    & |) q/ H5 i/ e
  476. //pBuffer:要写入的数据5 J* b5 F' t6 O, I
  477. //NumByteToWrite:要写入的数据个数, C+ L0 _$ ?9 a9 k
  478. //返回值:0,成功
    ' i& ^" z% d. Z  I* i
  479. //    其他,错误代码
    * b6 ]  s4 [# [1 p( |
  480. u8 NAND_CopyPageWithWrite(u32 Source_PageNum,u32 Dest_PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToWrite)/ O9 c, J) F: ]: ?
  481. {2 U) J( s# ^/ N0 }: w+ `) s* N
  482.         u8 res=0;, I3 Z, f9 s" `: A
  483.     vu16 i=0;) z; ?7 d# [! R
  484.         u16 source_block=0,dest_block=0;  
    * Y8 b, c; y$ X0 W% k; R
  485.         u8 eccnum=0;                //需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc' o9 v8 U/ M, K0 T, f
  486.         u8 eccstart=0;                //第一个ECC值所属的地址范围
    ' q7 T4 M9 B+ f6 V6 o/ |
  487.     //判断源页和目的页是否在同一个plane中
      O* E1 B; \$ |2 W- X5 K% l
  488.     source_block=Source_PageNum/nand_dev.block_pagenum;0 X; q- B# b$ \+ N5 V6 b- _9 ~5 Z
  489.     dest_block=Dest_PageNum/nand_dev.block_pagenum;
    3 ]' a" z& _* v
  490.     if((source_block%2)!=(dest_block%2))return NSTA_ERROR;//不在同一个plane内
    , f; Z! c5 [, F1 x: G
  491.         *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD0;  //发送命令0X00, r" ^* B- l. @% N- @3 [6 h9 d! E
  492.     //发送源页地址
    ( V# ?5 ?1 }1 [- q% N
  493.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;
    ) V; k* K$ I+ V# X0 W7 G1 n
  494.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)0;
    $ \  x  @  y0 e5 _! _* E
  495.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)Source_PageNum;
    / _# s% \4 H  O3 ^. d2 r# M
  496.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Source_PageNum>>8);
    ! V, e6 Z  ~. e9 l+ t% \9 ~; h3 Z
  497.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Source_PageNum>>16);
    - B0 z/ H7 t- [7 e8 h; G
  498.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD1;  //发送命令0X35
    ; O! O" R+ j" p# x( O0 w

  499. ! T5 T! b( i+ G- R4 ^' t
  500.     //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过+ x& ?% w$ [% A
  501.     //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备1 J$ b0 U2 N) z$ M5 ~7 ~6 ]: _) d
  502.     //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
    ! R# ]5 U6 d2 u3 [
  503.     //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行3 `# b& V: l& P/ H1 u" k0 J. k
  504.     //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。/ {6 Q/ g, z7 o5 C8 v0 q4 M+ A
  505.         res=NAND_WaitRB(0);                        //等待RB=0
    3 ]  ?8 O5 |8 ?1 H! E. H
  506.         if(res)return NSTA_TIMEOUT;        //超时退出
    0 p3 o6 x9 B# ^6 {# m# L
  507.     //下面2行代码是真正判断NAND是否准备好的
    ! q7 k! b5 e; y
  508.         res=NAND_WaitRB(1);                        //等待RB=1
    / s* r2 e# l" L$ f
  509.     if(res)return NSTA_TIMEOUT;        //超时退出
    / I# \. R' u9 n- q, s; ^
  510.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD2;  //发送命令0X85
    ) D  K7 b7 x: J1 q: [
  511.     //发送目的页地址
    6 @/ ?' `" H2 [8 l+ [0 n& l
  512.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;# a" p% Z3 x2 C9 A
  513.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);! r$ L% G5 o( l6 g+ `  Y. S% H
  514.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)Dest_PageNum;
    5 o5 O9 `1 |/ S9 {) R3 t2 ^
  515.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Dest_PageNum>>8);
    7 a7 b( O+ }( r& O! X7 U: W
  516.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(Dest_PageNum>>16); ( U" M. P4 P! n& a. U& X
  517.     //发送页内列地址3 J6 c) Y& C( n8 Z  K9 B8 |0 X; w
  518.         NAND_Delay(NAND_TADL_DELAY);//等待tADL
      Q& t! }/ E( y, \
  519.         if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验* {/ s3 d9 o3 b* e7 U% z& S
  520.         {  ' J" }5 O9 H9 d% Q; J
  521.                 for(i=0;i<NumByteToWrite;i++)                //写入数据
    3 z( F; ?8 O! s" M  b4 v
  522.                 {
    ' S* @4 m7 j& d# V$ L$ L0 ~% t
  523.                         *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;3 H) Z$ u5 t5 d; l- H' N- |
  524.                 }: h+ T( q+ \0 U8 L
  525.         }else& ^. a" w; L% A2 \( t) q( f
  526.         {
    5 F& v: v" M6 ]' G
  527.                 eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;                        //得到ecc计算次数9 \7 \9 y+ ^0 g
  528.                 eccstart=ColNum/NAND_ECC_SECTOR_SIZE;
    " e9 |, Q) ?% t4 F  s( |1 {
  529.                  for(res=0;res<eccnum;res++)
    ! f/ ~6 G. x. ^3 _
  530.                 {
    2 y' M$ i5 ~: U2 D( r4 O* z
  531.                         FSMC_Bank3->PCR3|=1<<6;                                                //使能ECC校验 - R. z- X! |- y0 q1 L
  532.                         for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)                                //写入NAND_ECC_SECTOR_SIZE个数据, I0 E! o( Z7 I9 B& d' W8 L
  533.                         {
    4 H5 n, h& W# G6 S5 d; _
  534.                                 *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;* K$ U3 j+ u9 p8 t) N
  535.                         }               
    0 w, h/ i( Y) k) B" `
  536.                         while(!(FSMC_Bank3->SR3&(1<<6)));                                //等待FIFO空        7 h6 B2 i6 r. M
  537.                         nand_dev.ecc_hdbuf[res+eccstart]=FSMC_Bank3->ECCR3;        //读取硬件计算后的ECC值1 k9 T1 I. c( V9 @& Q6 j
  538.                          FSMC_Bank3->PCR3&=~(1<<6);                                                //禁止ECC校验( `: E5 d5 U+ l) ?1 [
  539.                 }  
    : }8 \5 o7 S) d
  540.                 i=nand_dev.page_mainsize+0X10+eccstart*4;                        //计算写入ECC的spare区地址
    : i- @$ L( Q# ?7 a0 k. u: Y
  541.                 NAND_Delay(NAND_TADL_DELAY);//等待tADL
    # f  s9 `1 F8 [( C) V7 \* J0 o
  542.                 *(vu8*)(NAND_ADDRESS|NAND_CMD)=0X85;                                //随机写指令3 O- e. H5 M  V/ |7 k7 Y; q2 j) r
  543.                 //发送地址  {3 m5 G& y; k  @* q
  544.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)i;
    & R; ]' u4 d& F7 y2 M
  545.                 *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(i>>8);7 S: d3 o, U3 N4 d
  546.                 NAND_Delay(NAND_TADL_DELAY);//等待tADL
    + b5 e) w9 u# ?. x/ J9 A
  547.                 pBuffer=(u8*)&nand_dev.ecc_hdbuf[eccstart];
    3 N6 b0 L5 ]8 _% q
  548.                 for(i=0;i<eccnum;i++)                                        //写入ECC2 |8 f+ j' }8 \& Z
  549.                 { - @! W1 c7 `" g
  550.                         for(res=0;res<4;res++)                                 
    2 T; ?1 G8 v5 ]" I7 U0 f
  551.                         {0 R+ ?& C0 I  k$ T5 V
  552.                                 *(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
    7 ], H3 C/ J; ]/ X5 [
  553.                         }% n2 R$ Y1 x  g+ e
  554.                 }                 
    ; F- s4 I# a5 w. Z$ P* Z/ O
  555.         }
    5 G) i" m' D7 ~. L. P
  556.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;        //发送命令0X10
    3 F% A' q3 ~# _" I" h$ ]* b. n
  557.          delay_us(NAND_TPROG_DELAY);                                                        //等待tPROG
    # ^2 M$ r' Q7 ?: C: r
  558.     if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;        //失败
    0 s- g( d; F. P- |' X9 ?! @
  559.     return 0;        //成功   
    2 c* b2 Y* h0 r5 W* o
  560. } 5 e0 b" P) L( q4 @' v
  561. //读取spare区中的数据5 V3 }: G: H' V
  562. //PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
    2 o8 i" V& V7 E5 D. F
  563. //ColNum:要写入的spare区地址(spare区中哪个地址),范围:0~(page_sparesize-1)
    3 L7 @* H0 }; j! b' y0 P
  564. //pBuffer:接收数据缓冲区
      y2 y& T$ w0 E2 }0 A9 _; ^
  565. //NumByteToRead:要读取的字节数(不大于page_sparesize)" }5 X$ |, i# [7 `& H
  566. //返回值:0,成功8 P6 a$ c9 g( [5 ?; p% C
  567. //    其他,错误代码
    ; L$ J, R$ _& D
  568. u8 NAND_ReadSpare(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToRead)  x  ?$ Q4 {, a; E* N) Z& p/ C4 d
  569. {
    & @  e  j/ U& S4 V' A2 K8 t
  570.     u8 temp=0;6 T  o0 ?- Y0 q- H  e
  571.     u8 remainbyte=0;; S6 Y* n2 U2 N  m
  572.     remainbyte=nand_dev.page_sparesize-ColNum;
    ( e0 r/ ^/ n/ o) {  r" |$ |
  573.     if(NumByteToRead>remainbyte) NumByteToRead=remainbyte;  //确保要写入的字节数不大于spare剩余的大小( z- r9 L* t$ M1 f; G
  574.     temp=NAND_ReadPage(PageNum,ColNum+nand_dev.page_mainsize,pBuffer,NumByteToRead);//读取数据
    ; R2 Q3 R5 s8 _0 v" f
  575.     return temp;
    3 P% V; C+ t/ ]( n% m
  576. } : C  d5 u" v# O5 ?
  577. //向spare区中写数据
    . a( W& l* v% r9 U/ P
  578. //PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1). W3 l) j; z3 l) y9 ~3 Q
  579. //ColNum:要写入的spare区地址(spare区中哪个地址),范围:0~(page_sparesize-1)  & c. `' q+ a+ c, {
  580. //pBuffer:要写入的数据首地址
    & k$ F" s2 p. `5 A) b
  581. //NumByteToWrite:要写入的字节数(不大于page_sparesize)
    ; v/ K( j4 H/ ~% \/ X7 G2 p
  582. //返回值:0,成功/ M2 K! ?! |1 Y  O) ]3 F
  583. //    其他,失败
    6 ?1 ?$ b' I' N& s
  584. u8 NAND_WriteSpare(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToWrite)
    4 ~4 ?$ G3 a6 \  D" V# G. ~
  585. {
    ' T" V2 c% E5 u# G1 l4 y
  586.     u8 temp=0;
    9 b9 _4 b5 m2 s' S; N
  587.     u8 remainbyte=0;6 k. e  W7 }: i% d
  588.     remainbyte=nand_dev.page_sparesize-ColNum;7 U  h7 d- x  ^5 T# _0 u
  589.     if(NumByteToWrite>remainbyte) NumByteToWrite=remainbyte;  //确保要读取的字节数不大于spare剩余的大小% c$ I/ b4 o5 i  D3 A/ J4 _+ _
  590.     temp=NAND_WritePage(PageNum,ColNum+nand_dev.page_mainsize,pBuffer,NumByteToWrite);//读取3 @/ W3 k' C4 |9 i4 Q, d
  591.     return temp;& y6 n$ U' y* z5 l
  592. } ' s) F/ ^6 M3 \- l- ?: ]
  593. //擦除一个块& {9 F# b$ z8 B5 U4 g
  594. //BlockNum:要擦除的BLOCK编号,范围:0-(block_totalnum-1)
    - _- H4 `6 \* M) z) d/ L( E7 Q- j" E
  595. //返回值:0,擦除成功, l! X4 l2 t* C* i. u+ s) v4 E
  596. //    其他,擦除失败
    1 }9 C0 I; U% U; V2 m/ |4 r
  597. u8 NAND_EraseBlock(u32 BlockNum)6 E3 X; W$ u6 ]# c" T; q
  598. {
    ( X) ?$ K& E, O3 E) m5 w- a7 E
  599.         if(nand_dev.id==MT29F16G08ABABA)BlockNum<<=7;          //将块地址转换为页地址
    ) q# m0 o$ P2 ~& @' A
  600.     else if(nand_dev.id==MT29F4G08ABADA)BlockNum<<=6;
    ( f: _) R: i# C4 |8 e
  601.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_ERASE0;
    " W6 Z1 {8 E8 V8 z8 S* P
  602.     //发送块地址
    , `4 o* r5 f7 ?* D$ \
  603.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)BlockNum;/ H1 D! R- N. X5 s, l
  604.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(BlockNum>>8);
    " X) Z- m8 Z; @" q2 K
  605.     *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(BlockNum>>16);+ c, h. }+ Q- ?7 I8 U+ J
  606.     *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_ERASE1;2 ?9 }% X' _+ ]4 X5 z# C
  607.         delay_ms(NAND_TBERS_DELAY);                //等待擦除成功
    - s7 a0 k9 m* Z
  608.         if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
    4 Y" G7 F  I9 x1 |" w" C3 _+ C2 c
  609.     return 0;        //成功   
    1 N9 i& O" k: [$ }8 B( g- Y  x
  610. }
      M" c/ v  Q! M' B6 b6 {1 F
  611. //全片擦除NAND FLASH
    . i) J: {# N/ E' h1 Q' Q& T
  612. void NAND_EraseChip(void)
    + [1 S" e( ~; f9 s
  613. {- H: }/ r/ ]3 ~& Q3 C
  614.     u8 status;' M  @0 [* V8 t3 w' `; r( Z: U) ?  D
  615.     u16 i=0;
      h( k* D: R" V0 s5 @
  616.     for(i=0;i<nand_dev.block_totalnum;i++) //循环擦除所有的块
    ( R3 q( l+ z7 ^1 F  m( g
  617.     {3 H7 b: N( i& }0 b4 t
  618.         status=NAND_EraseBlock(i);
    - Q6 K$ \# L* B
  619.         if(status)printf("Erase %d block fail!!,错误码为%d\r\n",i,status);//擦除失败
    ! h2 Z& X. {5 R8 t7 c
  620.     }. x+ y1 ]9 R. m+ O" h
  621. }  r; l+ E; U/ L( H! I8 ]/ h7 E

  622. " m3 j- c. z4 U: C! p2 y
  623. //获取ECC的奇数位/偶数位  ?& ~9 w( P+ s# {
  624. //oe:0,偶数位. i$ x- C1 G# i- q0 w; {4 i% R
  625. //   1,奇数位/ t; i$ w& L9 u8 Q4 _
  626. //eccval:输入的ecc值
    4 J/ F* f! F# h6 W# z. {
  627. //返回值:计算后的ecc值(最多16位)
    , p! ]* q4 b) \3 D7 J: t  q
  628. u16 NAND_ECC_Get_OE(u8 oe,u32 eccval)
    5 M/ F+ o& |9 P
  629. {
    6 J! E# e& C: @
  630.         u8 i;7 x: U% Q: k$ D! H* u4 _3 F7 k
  631.         u16 ecctemp=0;
    , E1 ~- w, p4 q1 Q9 O
  632.         for(i=0;i<24;i++)' i) u& |1 w, V& ]5 a
  633.         {
    4 I4 ]& `3 \  F2 N4 ?. W: F+ l' ~
  634.                 if((i%2)==oe)
    3 a) o$ s8 E; f: x
  635.                 {
    ) V$ a& d0 J# D/ u2 {$ @2 L: j) F$ D7 F0 }
  636.                         if((eccval>>i)&0X01)ecctemp+=1<<(i>>1);
    ' x+ g2 r6 N! M
  637.                 }
    - v2 H* Z/ {- A
  638.         }$ @, o8 w+ @: s; i5 M
  639.         return ecctemp;
    - |2 E( h; G: G! V% S! q" k5 \
  640. }
    ! P' M3 t; T1 v5 Q: h
  641. //ECC校正函数+ O* O" m5 f' V, j1 ^/ {
  642. //eccrd:读取出来,原来保存的ECC值
    : |( K$ d  }5 q* K7 q
  643. //ecccl:读取数据时,硬件计算的ECC只
      H4 k) |( W. O% J" J' ~7 {! p7 |% O
  644. //返回值:0,错误已修正
    . H% {: X9 T" L# L; K
  645. //    其他,ECC错误(有大于2个bit的错误,无法恢复)3 [; w4 e9 }! Y+ t
  646. u8 NAND_ECC_Correction(u8* data_buf,u32 eccrd,u32 ecccl)
    2 Y1 D/ P6 G% @  c: X& Q
  647. {1 n6 Z" e# ~. t# v2 S* j7 H
  648.         u16 eccrdo,eccrde,eccclo,ecccle;
    ; _; V7 v2 ?$ e
  649.         u16 eccchk=0;4 l/ t( u+ h+ A, {% y
  650.         u16 errorpos=0; & c# J7 u9 x( D# U* K" e- p
  651.         u32 bytepos=0;  
    1 }, o8 m( c2 J3 v
  652.         eccrdo=NAND_ECC_Get_OE(1,eccrd);        //获取eccrd的奇数位
    3 H( U! b& ?3 f; }/ C
  653.         eccrde=NAND_ECC_Get_OE(0,eccrd);        //获取eccrd的偶数位- W, Z: l. u( i  ^7 O2 ~5 g* b1 g
  654.         eccclo=NAND_ECC_Get_OE(1,ecccl);        //获取ecccl的奇数位
    7 c2 }6 ^& _( B* Q! ]6 ~. I
  655.         ecccle=NAND_ECC_Get_OE(0,ecccl);         //获取ecccl的偶数位- R2 }/ z& z; j! d
  656.         eccchk=eccrdo^eccrde^eccclo^ecccle;6 r/ L) G& d2 W1 ~! O  K, {. l
  657.         if(eccchk==0XFFF)        //全1,说明只有1bit ECC错误, P8 m+ Q: v, a9 |
  658.         {
    3 J( a. x: z% d6 x1 r4 R! X$ a
  659.                 errorpos=eccrdo^eccclo;
    5 v+ w: X) Q/ Y$ u/ ?9 A5 i
  660.                 printf("errorpos:%d\r\n",errorpos);
    * H0 d& x1 \2 y' y- F5 S0 j
  661.                 bytepos=errorpos/8;
    / p: r) T( O( q
  662.                 data_buf[bytepos]^=1<<(errorpos%8);
    " h8 W# m5 @6 k  ~9 [7 {( A
  663.         }else                                //不是全1,说明至少有2bit ECC错误,无法修复
    , A! y4 U: I% F# a
  664.         {( ^/ O* c, L! V$ v
  665.                 printf("2bit ecc error or more\r\n");/ H$ y8 u* \: g, j) g4 y
  666.                 return 1;
    % z/ S& j3 x( W& z7 ?! K
  667.         }
      x) T9 x7 w4 x; a" _- o5 ?) K
  668.         return 0;
    & g* n% d) M0 R+ Z& K+ [& K
  669. }
复制代码
  1. int main(void)8 f, {" @4 e" O) n7 y
  2. {        % ^1 r  x4 l. A% x" u: k3 ?
  3.         u8 *buf;' d% A0 Y, t- k1 B/ E: N' d
  4.         u8 *backbuf;& \- [6 K7 Z7 [
  5.     u8 res;        7 I$ }3 @) s# W9 h
  6.         u16 i;
    1 G, j9 v* c9 O0 V0 r
  7. ) s. l' e. I6 A- }, R: z
  8.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
    8 r  \8 m" \9 u' D4 g/ x- d4 i
  9.         delay_init(168);  //初始化延时函数
    : }4 {9 e" |3 T. g% O( N' ~
  10.         uart_init(115200);                //初始化串口波特率为115200  W! m, J# e' i5 D' U7 T% m& s
  11.         led_init();                                        //初始化LED 8 ~7 {& `7 q3 t
  12.          FSMC_SRAM_Init();                        //初始化外部SRAM  
    ) S$ d$ l8 ?* o" a+ p- \  ?
  13.         
    1 T7 Z4 `" ]7 C" y& o3 ^
  14.         my_mem_init(SRAMIN);                //初始化内部内存池
    $ R6 _* S3 n" X; S0 x* Y
  15.         my_mem_init(SRAMEX);                //初始化外部内存池
    - E! X7 p. ?, M2 B9 W9 w4 w
  16.         my_mem_init(SRAMCCM);                //初始化CCM内存池
    ! u$ V2 g4 {2 }
  17.         
    / \) N/ T, W* o7 p. J2 i9 `1 ?! ^& x, `0 W
  18.          while(FTL_Init())                            //检测NAND FLASH,并初始化FTL4 X1 r& T, m) a! y. V4 m
  19.         {
    ( _+ C3 @) b$ s. N
  20.                 printf("NAND Error!  Please Check\r\n");         
    7 v8 S9 B" r4 g' |# P3 W! R
  21.                 delay_ms(500);        & Z' Y1 d3 z, q% u$ K, n
  22.         }        
    / R8 _: l5 D, e
  23.         backbuf=mymalloc(SRAMIN,NAND_ECC_SECTOR_SIZE);        //申请一个扇区的缓存8 N- V& h  e7 w/ c6 }7 g2 R
  24.         buf=mymalloc(SRAMIN,NAND_ECC_SECTOR_SIZE);                //申请一个扇区的缓存, V. l# s6 I$ g9 e
  25.         sprintf((char*)buf,"NAND Size:%dMB",(nand_dev.block_totalnum/1024)*(nand_dev.page_mainsize/1024)*nand_dev.block_pagenum);4 a4 S7 h/ g$ m5 |- p, c
  26.         printf("%s\r\n",buf);        //显示NAND容量  0 ~% A  O1 L- J4 P! V
  27.         2 j2 r4 l0 R8 q' N4 v) `2 t8 {
  28.         9 u5 u* m# o0 q- R) T; O
  29.         for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)( V& n. T! c% U' _
  30.         {$ c/ @# F8 h. u: J
  31.                 buf<i>=i;" A; ~* Z+ ^5 M( P% |: r) T
  32.         }
    % T2 g1 q; A2 _/ W1 U
  33.         printf("Writing data to sector..\r\n");
    5 R- A; w: F1 `" N
  34.         res=FTL_WriteSectors(buf,2,NAND_ECC_SECTOR_SIZE,1);//写入扇区7 r. P4 ^/ c9 O0 w
  35.         # Y- R# c2 {" B, W
  36.         if(res==0)1 W7 ]# l% I- A3 U1 o& k
  37.                 printf("Write data successed\r\n");//写入成功8 k8 M* _, h6 [6 ~5 w/ M% l1 |
  38.         else 2 y8 A' u3 I8 a% ]: h5 p* Z# X
  39.         {
    2 u; g+ D' \/ x% P2 Q, }: ~$ G6 U
  40.                 while(1)4 U/ k+ G# i) E* u: T1 l% f" W
  41.                 {; `! x9 H( R( ?6 z2 F0 g/ s
  42.                         printf("Write data failed\r\n");//写入失败% {' Y3 }9 z( i8 F* J/ w9 Q
  43.                         delay_ms(500);        & V; d4 J+ Q7 v/ w/ b! H8 J
  44.                 }, e) t# \+ O% A3 r5 y
  45.         }
    3 @$ B% _6 R) S% O  ^
  46.         # {/ x* b2 n, S! i
  47.         ' `" j' w* @9 i! f1 D) ~) ~, A
  48.         FTL_ReadSectors(backbuf,2,NAND_ECC_SECTOR_SIZE,1);//预先读取扇区0到备份区域,防止乱写导致文件系统损坏.
    $ {9 G; p* t2 ^: R* r
  49.         
    % t5 r  h$ U* N% g0 I: g8 H4 j
  50.         res=FTL_ReadSectors(buf,2,NAND_ECC_SECTOR_SIZE,1);//读取扇区; X* y0 U: w0 F: |% z* z' Z
  51.         " ?! q7 I- J7 K* a  J
  52.         if(res==0)//读取成功) F2 N5 ~  ]  x6 N2 R* W7 }. l
  53.         {
    - W/ ?- z3 c& E' l9 {" n2 u+ Y
  54.                 printf("Sector 2 data is:\r\n");
    $ G# q" J6 V$ F2 t' m) `
  55.                 for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)
    , q& D) b' o0 M' j
  56.                 {# o! y0 A! T/ `" z* n
  57.                         printf("%x ",buf<i>);//输出数据/ O' o/ W0 l+ B1 g- V4 z5 M, l' I( h( V
  58.                 }
    . s' q* Y+ |( `, d
  59.                 printf("\r\ndata end.\r\n");0 `& Z1 u* I* ?+ n6 V: R* H- Q: w
  60.                 printf("USART1 Send Data Over!  \r\n"); 8 Q" B: ?' @( I8 U4 g
  61.         }
    . [& c* o3 E1 ~+ F7 |6 G
  62.         
    0 l0 D7 g7 R/ S
  63.         
      d& p0 B" k0 j# U7 g* J- M
  64.         : w/ B% A, S6 j( X" |
  65.          while(1)
    3 W3 B" c8 E; u: B8 t
  66.         {        6 U% z/ V( y: [8 u" e; i7 r- ]
  67.                 turn_prog_led();2 T2 h! T1 ]3 C+ T3 v7 U% {
  68.                 delay_ms(10);   ; ]( F" ^8 U- T' P( M
  69.         }           
    9 I3 T/ ]& q4 G2 v
  70. }$ P7 d6 |% r  u* r

  71. ' B8 ~, z) i' `! @' z/ ]! J
  72. / n' R9 _& e# _8 ~) Y1 Y) \9 b  S( F# v
  73. </i></i>
复制代码

! ]3 @6 x8 N: Q/ h" E7 c+ b+ ?+ J( G2 X/ i) {- u
收藏 评论0 发布时间:2022-4-21 21:00

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版