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

【经验分享】基于STM32使用HAL库W25Q128驱动程序

[复制链接]
STMCU小助手 发布时间:2022-4-9 23:03
在头文件中简单修改一下片选引脚IO即可使用。8 \) ]- P4 {. a5 O) t  L

8 `% @9 l. M; {/ u6 Q源文件
, {* \6 Z+ N7 w

# g; H& g3 T1 ]% f2 f' P
  1. /**' k( M. {! [" L  X3 c
  2. * @file  w25qxx.c
      h8 S, F3 q. n. ?& r1 T& ^
  3. *
    , X, s5 P4 i3 ]* e. ?2 I3 ^
  4. * @brief Create by AnKun on 2020/6/18+ z+ ]0 u& r/ [) ]6 Q' D& i
  5. *+ H4 ?1 J1 W7 d  p4 J
  6. */, \5 ~. T7 f' F  D' B
  7. - V3 t. ?0 {. @
  8. #include "w25qxx.h"
    ' @+ c) r0 d6 E# v
  9. #include "spi.h"2 _: p1 t2 J: ?' k

  10. 2 y  h2 M* G& O: ]/ m& r- E
  11. uint16_t W25QXX_TYPE = 0;7 s  N1 Q; @/ V: A
  12. uint32_t W25QXX_SIZE = 0;
    4 C) w7 p2 o" }- H* b6 G. _# t' q
  13. uint8_t  W25QXX_UID[8];3 C5 m' A. y8 ]$ W% Z7 v
  14. # ~+ E2 w7 X- |. g5 f# ~9 i
  15. static void delay_us(uint32_t us)& d8 b, r% e) ]8 I# l  `; x' t7 F- W
  16. {" k% U6 R/ G& R; Z* H7 x
  17.     uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
    ' [5 k( p+ c* q  e# B/ e: y* \+ p* C* n% A
  18.     while (delay--)+ ^( b: d3 ]& A9 B& t/ }
  19.     {. S, `) B) g8 t/ I/ H+ @2 ~0 i8 Q. ^
  20.         ;
    8 _2 e: s! k! G2 f/ b1 B1 l; G$ N
  21.     }( M# l, _$ p: W$ C* \! K
  22. }- P% K. c# u+ V) `; S7 p

  23. 9 d2 F7 X- H, x  N. b# `, V
  24. //SPI读写一个字节( a/ ?/ a9 w7 `
  25. //TxData:要写入的字节
    1 r2 T* {% t, Q: k2 W
  26. //返回值:读取到的字节/ p( _. A1 }1 _/ p
  27. static uint8_t W25QXX_SPI_ReadWriteByte(uint8_t TxData)  k! t6 Y  ~8 z
  28. {" {+ K+ j9 t8 |( F
  29.         uint8_t RxData = 0X00;9 x: J& ~# O8 Q! d2 O+ [$ g( A
  30.         if(HAL_SPI_TransmitReceive(W25QXX_SPI_Handle, &TxData, &RxData, 1, 10) != HAL_OK)/ }# L: l) ~9 O5 x" d' e' o
  31.         {
    % O# N# Z- I$ G/ x2 R
  32.                 RxData = 0XFF;3 e5 ~- |& u, f, @! y
  33.         }
    : a  c4 x3 `5 r# [
  34.         return RxData;
    + y3 [" ?7 j- [, a5 |
  35. }
    ( u1 g3 U7 O: I/ L+ h7 _* e# _4 \
  36. : z3 i' S' |! \5 t. {: _( s
  37. //4Kbytes为一个Sector
    6 x" M2 i% U* `" K0 m; Q
  38. //16个扇区为1个Block1 `6 j) Q, u" u( g! r4 M
  39. //W25Q1289 M: l* z8 h7 d. ~
  40. //容量为16M字节,共有128个Block,4096个Sector
    ) \( x5 Y3 h* A- [( J0 x

  41. & D# U4 y; _9 q- V" u
  42. //初始化SPI FLASH的IO口
    5 {( g& c, K8 E, M9 y% }
  43. int W25QXX_Init(void)0 k) n  [/ o4 D' Q
  44. {
    6 M- S/ u; S: L# b
  45.         MX_SPI1_Init();
    , I+ y+ T- n/ v1 ]0 {
  46.     W25QXX_CS_L(); /* 拉低选中 */
    , L8 `* Z1 i/ s( w
  47.     W25QXX_SPI_ReadWriteByte(0XFF);$ I3 k6 R8 |% c5 E( A* w
  48.     W25QXX_CS_H(); /* 拉高取消 */
    # ~3 o$ q4 ^1 d& g% t# U0 z
  49.     W25QXX_TYPE = W25QXX_ReadID();          // 读取FLASH ID.
      z' N# O3 R8 x! Z8 k% k4 E
  50.         W25QXX_SIZE = W25QXX_ReadCapacity();    // 读取容量
    * Y  y* t, r# h" o0 ?7 p
  51.         W25QXX_ReadUniqueID(W25QXX_UID);        // 读取唯一ID
    ! b1 F4 V9 F5 e2 ^
  52.         if((W25QXX_TYPE & 0XEF00) != 0XEF00)
    7 j% y% X; ?4 h7 Z: e1 D
  53.         {
    % h- @1 R$ Q7 c6 p$ A% z( R9 }
  54.                 return -1;$ t0 {3 e9 f$ G5 H6 D$ E
  55.         }3 H* R7 E4 V/ V9 v. O6 k
  56.         return 0;' F+ R% `7 n  A0 d0 r$ r$ a5 w
  57. }
    ( c, M' U) z$ g! o
  58. ' J0 |" L: }6 _7 ]- K5 T
  59. //读取W25QXX的状态寄存器
    / h9 C: z3 @1 @$ Q. Q' x* r
  60. //BIT7  6   5   4   3   2   1   0
    % C0 O, ^$ `9 x
  61. //SPR   RV  TB BP2 BP1 BP0 WEL BUSY% }6 H7 V& |- S- r; H
  62. //SPR:默认0,状态寄存器保护位,配合WP使用
    6 S+ L& `- _( s# p
  63. //TB,BP2,BP1,BP0:FLASH区域写保护设置3 F, j: X: [5 K
  64. //WEL:写使能锁定
    1 q# q: W) d  J& P
  65. //BUSY:忙标记位(1,忙;0,空闲). N* t7 g4 `6 G
  66. //默认:0x00( {, }3 ?9 `& Q$ c# i
  67. uint8_t W25QXX_ReadSR(void)
    2 p8 {! f# Q9 m3 Z# k+ J
  68. {* B! w9 t3 a" M7 K* M- r
  69.     uint8_t byte = 0;8 u2 L6 }1 b, z; _
  70.     W25QXX_CS_L(); //使能器件1 i4 ], d" V9 y6 D. L! T
  71.     W25QXX_SPI_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令% z. E! X: e7 [" P' D
  72.     byte = W25QXX_SPI_ReadWriteByte(0Xff);          //读取一个字节" i6 _7 |4 O# i8 ~5 D: N
  73.     W25QXX_CS_H();  //取消片选) C0 u9 y. G, m' _4 p
  74.     return byte;
    2 n. d( Q% |5 I7 U% K
  75. }3 D0 }8 m7 \8 d$ o  _* R
  76. //写W25QXX状态寄存器
    + s* U& I. q1 l
  77. //只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!* m7 i: G) y, ~. S' b6 H5 o
  78. void W25QXX_Write_SR(uint8_t sr)
    : |; y$ s; h  Y& [0 w( t
  79. {
    $ K% m" m; u/ j
  80.     W25QXX_CS_L(); //使能器件
    0 R! H- |/ p# q! C' R8 {# C
  81.     W25QXX_SPI_ReadWriteByte(W25X_WriteStatusReg);                 //发送写取状态寄存器命令
    6 v& Z, y7 O0 M3 |& q
  82.     W25QXX_SPI_ReadWriteByte(sr);                       //写入一个字节5 [! X( ^$ h0 O5 e8 G  S
  83.     W25QXX_CS_H();  //取消片选
    * s" i3 [1 @: ~& U
  84. }( T+ w: U. b' |" A+ z2 _0 }8 o8 b
  85. //W25QXX写使能8 R* s; v2 B5 c( R* _  S- v
  86. //将WEL置位
    - L# e# S5 f4 U% O+ F/ e& [
  87. void W25QXX_Write_Enable(void)6 L' L) j. s4 r9 k( G. F
  88. {8 V9 F# m5 V& b5 D/ S
  89.     W25QXX_CS_L(); //使能器件" n' I6 ]! l+ P5 {5 n& ^& a& A9 M
  90.     W25QXX_SPI_ReadWriteByte(W25X_WriteEnable);         //发送写使能$ C6 c, M9 G9 P% L' f4 O9 q' J
  91.     W25QXX_CS_H();  //取消片选0 S, H" _* X7 f( ^6 N* V# t
  92. }
    " O/ G' ]. t. k7 |
  93. //W25QXX写禁止
    ( M' q( J( e- N
  94. //将WEL清零2 N8 _5 a/ a! q  s
  95. void W25QXX_Write_Disable(void)
      r. N5 I, m0 A( e5 T; U- E3 R- d
  96. {
    ) y7 [+ f" R7 [) e
  97.     W25QXX_CS_L(); //使能器件" S7 F, I8 U3 c
  98.     W25QXX_SPI_ReadWriteByte(W25X_WriteDisable);  //发送写禁止指令9 n: f* B; E3 t) m* j- K
  99.     W25QXX_CS_H();  //取消片选) \3 v5 Z0 ~  g2 m* e
  100. }
    8 O8 S6 p5 b( G8 S
  101. //读取芯片ID
    9 B  x8 d7 a* e" C
  102. //返回值如下:# l. i, ^- B* G& s
  103. //0XEF13,表示芯片型号为W25Q80/ G: i3 b. H' H) k: E
  104. //0XEF14,表示芯片型号为W25Q16
    " H1 }2 f3 L% K0 F! T
  105. //0XEF15,表示芯片型号为W25Q327 c8 N4 `. P0 q9 O" l! B9 X
  106. //0XEF16,表示芯片型号为W25Q64! M; X( Z0 f1 B) ~1 J/ ?
  107. //0XEF17,表示芯片型号为W25Q128: O& E. G- @3 V5 L) C- P# F9 ]
  108. uint16_t W25QXX_ReadID(void)
    : B. B4 ]: Z6 m6 C$ p6 y. h
  109. {
    . T: W. T7 Y0 i3 O/ @' O9 h
  110.     uint16_t Temp = 0;
    5 u9 v  n7 z! `; T, n0 t
  111.     W25QXX_CS_L();
    . G' m  D1 _0 B, f
  112.     W25QXX_SPI_ReadWriteByte(0x90);                            //发送读取ID命令" d! y: a3 E# j- V) V
  113.     W25QXX_SPI_ReadWriteByte(0x00);
    ! e! S$ K" T6 G- {* l# Q
  114.     W25QXX_SPI_ReadWriteByte(0x00);) O& r& R. F/ i* k" A/ t  @$ n
  115.     W25QXX_SPI_ReadWriteByte(0x00);7 x" W, Q% a; C4 p  t( f: z1 C
  116.     Temp |= W25QXX_SPI_ReadWriteByte(0xFF) << 8;
    3 t" q2 N" O! R3 P* ~5 s, ]
  117.     Temp |= W25QXX_SPI_ReadWriteByte(0xFF);
    4 z' T4 i! g+ }; S7 E8 m: v; y
  118.     W25QXX_CS_H();
    3 u2 y: w! s/ i2 v- w
  119.     return Temp;
    , ?! ?+ G3 T6 Y; O2 U
  120. }
    1 X0 i/ t7 x' f

  121. ! L, M# D9 T# q& s+ T- j
  122. uint32_t W25QXX_ReadCapacity(void)$ ?0 D% C( W7 {: f# C( ^! A4 {
  123. {9 Y( s  E. I* ?: v' [
  124.         int i = 0;
    ) k. `0 j0 i3 T6 ]: T. n
  125.         uint8_t arr[4] = {0,0,0,0};5 N8 b9 d" ~8 t5 s7 ]: X* C3 h
  126.     W25QXX_CS_L();
    , |: S( p7 L: R9 w, r6 q/ }
  127.     W25QXX_SPI_ReadWriteByte(0x5A);- W/ N' ]) i& l7 T1 R9 x
  128.     W25QXX_SPI_ReadWriteByte(0x00);# X/ m$ D6 m* y) |% h: M1 y2 i
  129.     W25QXX_SPI_ReadWriteByte(0x00);
    * M' A, ?$ N/ G6 l6 j6 o) [6 W
  130.     W25QXX_SPI_ReadWriteByte(0x84);: q; o4 w- }% X* w! s2 {
  131.         W25QXX_SPI_ReadWriteByte(0x00);
    2 ]9 w' y& Y! _  g
  132.         for(i = 0; i < sizeof(arr); i++)
    * z3 M7 x9 N" z; t$ n% b
  133.         {
    5 C3 ~# B- z% t, c- M3 {
  134.                 arr<i> = W25QXX_SPI_ReadWriteByte(0xFF);" C# C6 L- W1 N, D! Q+ X9 B$ p
  135.         }
    9 b4 \" J8 k; d2 g- x5 u
  136.     W25QXX_CS_H();
    - M0 n; E( s3 O0 u' b* k4 Z: g$ D
  137.     return ((((*(uint32_t *)arr)) + 1) >> 3);+ v* Y) z9 h/ D
  138. }4 r( {) T5 V+ ~; m5 ~! T+ n3 e

  139. 4 J. m% P6 V  d" k5 \: E! Z! R. }
  140. void W25QXX_ReadUniqueID(uint8_t UID[8])
    ; c7 U7 a- Y( J. n( H
  141. {1 M' \/ D: W- [' S
  142.         int i = 0;
    ' i0 `: u! ?2 _" t
  143.         W25QXX_CS_L();
    ( k  k1 B1 g# J1 E3 M
  144.     W25QXX_SPI_ReadWriteByte(0x4B);
    9 W) V0 b) K+ p! T! Y1 L$ g2 n& A! K
  145.     W25QXX_SPI_ReadWriteByte(0x00);
    - L" T' R/ j" r+ g4 K3 u
  146.     W25QXX_SPI_ReadWriteByte(0x00);1 m/ M/ N7 t) O* N9 I
  147.     W25QXX_SPI_ReadWriteByte(0x00);
    , b! `+ X( E$ q7 x
  148.         W25QXX_SPI_ReadWriteByte(0x00);
    ! U7 Z0 t1 F1 |; V3 F& u
  149.     for(i = 0; i < 8; i++)/ x  C" H" a+ @6 g
  150.         {
    : j( O/ `1 L+ i
  151.                 UID<i> = W25QXX_SPI_ReadWriteByte(0xFF);. P6 Z5 V  m0 `/ |- Y
  152.         }% K4 c, {! e/ `0 o: z* x/ A* N" w
  153.         W25QXX_CS_H();7 S1 g* [  ]6 [  v
  154. }: f& |9 O  `! p( j

  155. + K5 `7 h9 j8 T1 E) o2 K
  156. //读取SPI FLASH
    5 t8 [) v( @/ R# Z
  157. //在指定地址开始读取指定长度的数据
    + P: t, ~5 ~& i; M" b1 }0 O! ]  a
  158. //pBuffer:数据存储区
    6 s/ V) U% _- z5 d( O
  159. //ReadAddr:开始读取的地址(24bit)
    $ I0 L0 {) w/ L& y. j: w
  160. //NumByteToRead:要读取的字节数(最大65535)% I( n  C; L2 s* I+ T0 @$ q
  161. void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
    : P1 G# F* g5 `2 L) l. n
  162. {# ~* m6 a3 v- {& z5 T6 e0 r
  163.     uint16_t i;7 ?8 w/ L2 ~& j- _) L
  164.     W25QXX_CS_L(); //使能器件! n' b3 W" x7 `' }
  165.     W25QXX_SPI_ReadWriteByte(W25X_ReadData);                 //发送读取命令
    $ o+ B" A: }+ d& q8 ?: v
  166.     W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 16));          //发送24bit地址
    ; b, V% P% p" o+ j
  167.     W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 8));0 M0 `3 M) z: Q& Z- d; r
  168.     W25QXX_SPI_ReadWriteByte((uint8_t)ReadAddr);' t/ O9 G6 F7 ~' s2 r8 H% e
  169.     for (i = 0; i < NumByteToRead; i++)
    & {" N9 `0 f6 `3 Q$ P" _+ a) g& ?
  170.     {
      B& H( o: Y$ n5 P" H" r
  171.         pBuffer<i> = W25QXX_SPI_ReadWriteByte(0XFF);           //循环读数7 ?9 Y1 d; C7 V$ |  S) C
  172.     }
    5 _, w' B  O5 G: I) e/ l/ S1 {
  173.     W25QXX_CS_H();2 l' b. C# Q" y2 a) e7 q
  174. }) x' S% T# k' Q$ a
  175. //SPI在一页(0~65535)内写入少于256个字节的数据
    0 o% Z6 E  A* r8 }4 I
  176. //在指定地址开始写入最大256字节的数据
    8 Y. B$ }& T2 h. a+ f
  177. //pBuffer:数据存储区# c/ I! \* q; V( ]
  178. //WriteAddr:开始写入的地址(24bit)
    0 [. q" k- ]+ u2 }- \
  179. //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!* R. {  {+ i! m) }; C( l' Z
  180. void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    9 `% ]! j3 ^* e" o3 f
  181. {; y" ~7 Z7 I& l
  182.     uint16_t i;
    4 D( n& ~+ D# q' d* Q
  183.     W25QXX_Write_Enable();                          //SET WEL
    7 r  T0 C1 v+ @5 i( k
  184.     W25QXX_CS_L(); //使能器件( V$ j* S& l% a/ T) }+ u
  185.     W25QXX_SPI_ReadWriteByte(W25X_PageProgram);              //发送写页命令: |, S" V+ Y9 a1 Z! E
  186.     W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 16));         //发送24bit地址9 U( c, m4 |( O$ e6 X" }" y
  187.     W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 8));) y. G- l1 S4 \; Y. C* I
  188.     W25QXX_SPI_ReadWriteByte((uint8_t)WriteAddr);* P) u9 g5 Y$ ^0 W7 w+ I
  189.     for (i = 0; i < NumByteToWrite; i++)$ Z% l+ B0 o4 Q  @; J  I5 N
  190.         W25QXX_SPI_ReadWriteByte(pBuffer<i>); //循环写数0 y6 A* l, }- {, j! b
  191.     W25QXX_CS_H();  //取消片选
    / B4 n& ~0 [/ B0 x: [/ s+ Z
  192.     W25QXX_Wait_Busy();                                                           //等待写入结束; y, ^* v$ w" t; r, m4 h
  193. }: g0 O$ w: \: R  r  S
  194. //无检验写SPI FLASH1 O* B0 N: T2 H: F; c
  195. //必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!  p1 O) o( R' k# b) O5 B5 c
  196. //具有自动换页功能. ]9 s" ?3 I3 x0 M1 U  w
  197. //在指定地址开始写入指定长度的数据,但是要确保地址不越界!$ T9 {1 i3 j2 c( m" ^( n0 H' N
  198. //pBuffer:数据存储区1 G* s0 g2 \' o9 M7 P
  199. //WriteAddr:开始写入的地址(24bit)9 r; q! j4 Q2 _9 M9 x# f4 I
  200. //NumByteToWrite:要写入的字节数(最大65535)! r! C5 k) N; x- ]6 L
  201. //CHECK OK5 Q. D$ O1 E+ u7 e9 O- J2 d% T
  202. void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    4 o, X/ U" r: R6 `6 O
  203. {
    8 S5 n) ]% V! k/ _
  204.     uint16_t pageremain;- u" I! ?$ S0 z1 J/ Q) B0 M, m# h
  205.     pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
    3 H% m2 v) X! D* a7 W# _% l/ P
  206.     if (NumByteToWrite <= pageremain)+ c- `$ F" ^/ V9 L
  207.         pageremain = NumByteToWrite; //不大于256个字节6 P: b6 c  }; O& g  W% b. U% q
  208.     while (1)
    / E  R- V# V- O! J. J! X
  209.     {) p7 ?5 [* i1 f/ X5 k
  210.         W25QXX_Write_Page(pBuffer, WriteAddr, pageremain);  T! Y6 H" X9 y4 C9 o2 X
  211.         if (NumByteToWrite == pageremain)& ]9 \! c( G$ [* s0 a9 v: d. W- S
  212.             break; //写入结束了
    : l6 U  o6 _* z/ _$ {2 I! W6 q
  213.         else //NumByteToWrite>pageremain" h0 y5 Q$ y8 Q1 d- T9 l
  214.         {0 ?) Z6 V; i+ [
  215.             pBuffer += pageremain;
    5 r8 M' x+ n, M1 P7 t# l4 g' n
  216.             WriteAddr += pageremain;1 V% y" y( R! V
  217. " L! M# ~$ ^' @$ L  P; g8 e
  218.             NumByteToWrite -= pageremain;                          //减去已经写入了的字节数+ d: `4 T7 o- z! @
  219.             if (NumByteToWrite > 256)
    4 m# }: A$ V# I; }* B
  220.                 pageremain = 256; //一次可以写入256个字节
    ( U1 M3 M/ M, |* ^# V  s  }
  221.             else
    . F1 _' {) b/ W! C5 U
  222.                 pageremain = NumByteToWrite;           //不够256个字节了" _* n; j6 v5 Y* b9 o4 o! e
  223.         }
    ! u: ]/ h) S. y8 y9 m# f
  224.     };
    7 S3 J  G$ ]4 r4 T
  225. }5 T% Y# Q& W' K9 u
  226. //写SPI FLASH2 W# F. ]/ ^4 g
  227. //在指定地址开始写入指定长度的数据
    4 T/ s. H  c0 Z% A  R% x. H
  228. //该函数带擦除操作!& [  t8 `1 [: O/ K) j  W! {6 }
  229. //pBuffer:数据存储区- B) x! m0 ~! o) }7 ?
  230. //WriteAddr:开始写入的地址(24bit)
    " v/ f- Y* a$ ~/ j* |
  231. //NumByteToWrite:要写入的字节数(最大65535)
    * u6 S1 y- \* b0 |
  232. uint8_t W25QXX_BUFFER[4096];; R  p4 R$ _7 x# ^; I6 F8 i5 q$ B/ R6 v
  233. void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
    . |8 S2 Y0 I2 k
  234. {
    ! P! h4 l$ _0 ]2 z2 R
  235.     uint32_t secpos;
    & U7 e( W+ h2 h
  236.     uint16_t secoff;
    7 \- K, A( T6 T# }! Q  M
  237.     uint16_t secremain;1 W; |. o, V+ ]* P( B; W
  238.     uint16_t i;* U& O$ A- s" l# `" y! H1 A
  239.     uint8_t *W25QXX_BUF;
    ! H: _8 u9 I" f' J. N
  240.     W25QXX_BUF = W25QXX_BUFFER;
    # E* m3 U* L0 \2 |3 G
  241.     secpos = WriteAddr / 4096;           //扇区地址3 K, w; @! U. O1 ?3 T2 D3 Q
  242.     secoff = WriteAddr % 4096;           //在扇区内的偏移/ M: N, W4 q+ N5 R( H. f+ c+ A
  243.     secremain = 4096 - secoff;           //扇区剩余空间大小
    ( b0 \; I- j0 d2 x/ d/ J4 @
  244.     if (NumByteToWrite <= secremain)
    7 p) B  E6 \- O/ W6 M3 _: q
  245.         secremain = NumByteToWrite;           //不大于4096个字节
    ) S7 _- `% I% ?
  246.     while (1)
    5 U, ]8 t5 @* e# o5 P  s1 T
  247.     {9 ]0 p5 n& e( P7 [1 p. C( O
  248.         W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096);           //读出整个扇区的内容
    " ~& g# q  }* Z9 R) A' w% ?
  249.         for (i = 0; i < secremain; i++) //校验数据
    1 K6 k- g9 B7 r1 k9 y
  250.         {5 ~) p8 V1 R1 I
  251.             if (W25QXX_BUF[secoff + i] != 0XFF)) c% d) c5 A3 z% ^
  252.                 break; //需要擦除
    . J: c/ |- F4 r7 n
  253.         }
    ( @1 [$ I' q7 M. ^2 F9 y; I: k
  254.         if (i < secremain) //需要擦除5 P+ H& Q4 p7 j  z
  255.         {( V  Y# o! x2 R/ w3 I& b1 g3 Z
  256.             W25QXX_Erase_Sector(secpos);                //擦除这个扇区6 r+ T9 b$ q* ^5 v( H9 b( E& U" X6 }
  257.             for (i = 0; i < secremain; i++)                           //复制
    ' |7 P/ w" a' p/ D% n' C
  258.             {* a/ O7 P. t) {# y7 w+ s3 o
  259.                 W25QXX_BUF[i + secoff] = pBuffer<i>;- @8 r9 ]9 K8 a, i9 h* v2 y
  260.             }; D& `/ p9 @, P0 H: m7 n3 z+ V0 j$ G* |
  261.             W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096);                   //写入整个扇区& L# Q8 W3 ~) ~0 X6 U

  262. : S: n  k* U' g' v! l& M
  263.         }1 X. U4 l- e1 l6 ?2 F6 y
  264.         else9 O$ w3 x7 A4 F4 c9 \5 f
  265.             W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.3 h' ^2 X. b: s& \$ l( e. M& d
  266.         if (NumByteToWrite == secremain)% K4 E9 {/ ^6 y& i* h* a
  267.             break; //写入结束了/ A; _# I# Z, ^, K$ u$ C
  268.         else //写入未结束2 n1 v" R$ L) r, r, J0 r, I
  269.         {
    $ y: m- z" T- l1 s' i" C& u
  270.             secpos++; //扇区地址增1
    $ m' M: q7 J7 B, r1 U
  271.             secoff = 0; //偏移位置为0/ ~3 N. H+ `3 J: Y% ]0 a4 a

  272. & [4 @2 ]0 @' ]& H+ U7 C
  273.             pBuffer += secremain;                                  //指针偏移
    ( [9 ^; O+ [# ]* `' L
  274.             WriteAddr += secremain;                                //写地址偏移+ H8 _) `2 a0 V0 e1 F
  275.             NumByteToWrite -= secremain;                        //字节数递减+ X) L- t4 e. r0 M) Y
  276.             if (NumByteToWrite > 4096)! ~8 N9 g% M: B/ _+ D: g  g5 g
  277.                 secremain = 4096;                        //下一个扇区还是写不完
    5 t4 o7 H3 ]4 j) i  z# o
  278.             else
    & c6 Z1 j4 S9 E4 C, C) O7 P
  279.                 secremain = NumByteToWrite;                //下一个扇区可以写完了, W1 o2 _) h1 N/ U( a
  280.         }
    3 d% @- C  ~* }
  281.     };3 E) X, @1 o; g
  282. }: ?' T4 n8 l3 U/ X; M% O) _

  283. 2 J- N6 E" V) L, f' J1 I" @) r
  284. //擦除整个芯片9 |# D2 E/ q0 u3 S( }
  285. //等待时间超长...
    / z# J- _# \8 C( b) y# P) e% N3 Y
  286. void W25QXX_Erase_Chip(void)
    0 E  X5 `7 E. [1 A1 }
  287. {' h; W# _  t& M$ k  L- `. M* u9 @
  288.     W25QXX_Write_Enable();                                  //SET WEL
    1 {; X+ F9 P( \! }9 \  C3 D
  289.     W25QXX_Wait_Busy();
    3 ]& ?6 R5 V3 W# @
  290.     W25QXX_CS_L(); //使能器件) Q, z2 p. P/ l. j7 P* i
  291.     W25QXX_SPI_ReadWriteByte(W25X_ChipErase);                //发送片擦除命令
    5 ~! u+ G( X, f1 G6 i& n
  292.     W25QXX_CS_H();  //取消片选
    % u) c; @- F& t% N' f. u
  293.     W25QXX_Wait_Busy();                                                      //等待芯片擦除结束# E. a/ Z: ?' N/ t  m4 T6 _
  294. }2 h7 m8 f3 @- l2 i, Q& L$ k+ _
  295. //擦除一个扇区
    ! _6 V* T! V" |; ?- c3 ~* c
  296. //Dst_Addr:扇区地址 根据实际容量设置
    6 j- I" d* m5 Q9 u: A/ M- ?
  297. //擦除一个山区的最少时间:150ms
      d# f* \* u8 L3 S+ w) q& B3 I
  298. void W25QXX_Erase_Sector(uint32_t Dst_Addr)
    9 d% G/ ]+ r/ e* Z3 b# J! H
  299. {  K# @6 F+ m  e
  300.     //监视falsh擦除情况,测试用0 U8 X; }5 n! M' a
  301.     Dst_Addr *= 4096;% v0 V3 x) C  x
  302.     W25QXX_Write_Enable();                          //SET WEL+ ?, t7 Q- E% Z8 i' C2 k$ c
  303.     W25QXX_Wait_Busy();
    / M+ H. @. \8 Z% Z, I( w. W
  304.     W25QXX_CS_L(); //使能器件
    1 h0 `% Y- {8 ^' d) y
  305.     W25QXX_SPI_ReadWriteByte(W25X_SectorErase);              //发送扇区擦除指令
    " U) D$ ?  {9 a, ^3 {
  306.     W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 16));          //发送24bit地址6 W8 }0 }# g+ ^9 ]
  307.     W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 8));
    % |+ q$ h$ m7 A7 g
  308.     W25QXX_SPI_ReadWriteByte((uint8_t)Dst_Addr);
    2 |6 t2 b6 W4 N$ U5 I) {5 w
  309.     W25QXX_CS_H();  //取消片选( G( a/ D  p# N/ p, S0 X6 X9 ^
  310.     W25QXX_Wait_Busy();                                                      //等待擦除完成5 b# I1 R1 D5 C
  311. }  I- @* C4 M5 t3 _
  312. //等待空闲" y) A1 M* S7 M% }# z% s7 A
  313. void W25QXX_Wait_Busy(void)
    . ~, p& ?" {! B' `
  314. {
    5 p4 _" h) b6 d8 I0 y6 {) E7 d
  315.     while ((W25QXX_ReadSR() & 0x01) == 0x01);                  // 等待BUSY位清空
    ! T3 b+ m2 n' p, f% x" B  f
  316. }
    " D0 s6 L* @2 D$ r
  317. //进入掉电模式
    7 s3 q) [0 i- U7 z: U6 \+ `
  318. void W25QXX_PowerDown(void)) c$ h( I8 d1 F1 M; Z
  319. {
    5 p" e4 K1 f, M- m+ q5 a2 D
  320.     W25QXX_CS_L(); //使能器件
    3 k2 d$ V" M) U1 _! G2 _) e
  321.     W25QXX_SPI_ReadWriteByte(W25X_PowerDown);        //发送掉电命令
    ) ?" Y) @% [0 ?( M& \/ G5 b. _8 |' O
  322.     W25QXX_CS_H();  //取消片选) M# Z2 H! T1 F' o9 S
  323.     delay_us(3);                               //等待TPD
    ( _' x7 ?9 l7 Q* [1 g& ]
  324. }8 f  J6 ^/ d. j( p8 [$ }
  325. //唤醒: S& w: c$ R% s- z2 e( R
  326. void W25QXX_WAKEUP(void)* z  o8 f9 X. H0 H$ {7 M* A0 v
  327. {
    6 J$ N' J9 F8 v1 D# ?# t
  328.     W25QXX_CS_L(); //使能器件$ b- Y% P( Q$ q( T& C- z
  329.     W25QXX_SPI_ReadWriteByte(W25X_ReleasePowerDown); //  send W25X_PowerDown command 0xAB; Q% c4 K; P, I
  330.     W25QXX_CS_H();  //取消片选
    $ Z) S1 R/ Z: ?  Z
  331.     delay_us(3);                                    //等待TRES1/ R1 [9 P) Z9 H9 m
  332. }</i></i></i></i></i>
复制代码

/ x4 X; e  }! }2 Z" Z' W( |" Y* p6 i' `  q' e* ]* e! v' y
头文件3 o# ~9 E6 t1 r- b3 |
  1. #ifndef __W25QXX_H7 S. T+ w* J: y( A8 k
  2. #define __W25QXX_H8 e$ G; `* Y4 G- a0 W7 H5 i; W

  3. : A* N+ ]- m' g* X4 r$ `- @) G( ]$ h
  4. #include <main.h>& e( ]. R5 z" m

  5. & i( d# e" b1 c5 t9 B
  6. #define W25QXX_SPI_Handle (&hspi1)
    3 _  a* S" d* O7 x! D6 g
  7. 9 b( t6 O& h8 l% t5 L
  8. //W25X系列/Q系列芯片列表1 Q% Q7 ~$ q3 u' C
  9. //W25Q80  ID  0XEF13
    * t# U% H* b$ y- O
  10. //W25Q16  ID  0XEF148 l6 r: c' s0 z- _
  11. //W25Q32  ID  0XEF15
    , e% W9 F# X# A2 `( l$ ^5 x4 \; Y
  12. //W25Q64  ID  0XEF168 w5 E9 y# c" x6 |
  13. //W25Q128 ID  0XEF172 Z* U1 u4 r& r* J$ L
  14. #define W25Q80         0XEF13  [" ~; @( _! ?3 ]
  15. #define W25Q16         0XEF14
    ( S9 ^8 u: w( h. X! H* H4 m$ u) B
  16. #define W25Q32         0XEF15
    % k5 S1 o. W8 X1 ^6 o: ]& M; I- j+ s
  17. #define W25Q64         0XEF16, p9 y- o: T; ~; z+ j: e+ b# ^5 G3 L
  18. #define W25Q128        0XEF17" B5 h8 G; `1 ^8 V* h
  19. 6 x$ r) w0 A' a: P7 ]1 c& S
  20. ( q' L7 G4 y2 K, w
  21. #define W25QXX_CS_L()  HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_RESET)/ U  L5 I) G- H& l2 {
  22. #define W25QXX_CS_H()  HAL_GPIO_WritePin(W25QXX_CS_GPIO_Port, W25QXX_CS_Pin, GPIO_PIN_SET)7 o/ m4 \3 W. Y% \1 d
  23. ! L" B* a. E7 [- P, m! H# M
  24. 4 T' S4 g! H3 @  ^* |! @1 N+ Q$ ?# K
  25. extern uint16_t W25QXX_TYPE;
    # R: u- P' b1 v, L) y8 G% W
  26. extern uint32_t W25QXX_SIZE;( a3 U; R7 z& {- }; {$ C
  27. extern uint8_t  W25QXX_UID[8];
    : B9 y. M" ]  h3 u
  28. + X* }" k  K9 e- t0 S

  29. 5 i$ \/ m. v% s) P1 i2 X; T' Q3 i3 k7 L
  30. //1 _, K5 r) H. ~$ a( d, y6 Z$ e
  31. //指令表
    3 j2 j+ h* u4 h% Z3 y7 D! N
  32. #define W25X_WriteEnable                0x06
    ' o8 L6 n: ^, E! ^: |- u
  33. #define W25X_WriteDisable                0x043 \, n* t  c, A+ m( _7 n2 Q. z3 g
  34. #define W25X_ReadStatusReg                0x05
    9 {5 W- P+ f/ O9 x" K0 m
  35. #define W25X_WriteStatusReg                0x01/ s! h9 P4 A5 w2 [2 E+ B
  36. #define W25X_ReadData                        0x03  \6 K, `4 V4 Z- I/ g9 j% q
  37. #define W25X_FastReadData                0x0B8 M+ T" J9 Z4 d$ Z
  38. #define W25X_FastReadDual                0x3B" U) C0 @& i) F0 h
  39. #define W25X_PageProgram                0x02
    $ L; |* t2 t2 ~) o6 ], j: j* D+ ~
  40. #define W25X_BlockErase                        0xD88 v; z. U; U/ x' {1 v# r
  41. #define W25X_SectorErase                0x204 v; b% L" S- A
  42. #define W25X_ChipErase                        0xC7
    ' ^3 J" X9 I- y* ]5 z0 P5 l
  43. #define W25X_PowerDown                        0xB95 {9 s* {4 G& A
  44. #define W25X_ReleasePowerDown        0xAB
    ( Q, r9 R1 K6 c3 ^
  45. #define W25X_DeviceID                        0xAB! W5 t% `$ n. i! w% P& @
  46. #define W25X_ManufactDeviceID        0x908 W7 A1 J) I+ g1 q; Z9 B8 k
  47. #define W25X_JedecDeviceID                0x9F/ M" g1 j( P0 o' {
  48.   ^# q1 p% N7 S4 \, h: [
  49. int W25QXX_Init(void);
    ( D5 E* @! P& A: l% G9 r
  50. void W25QXX_ReadUniqueID(uint8_t UID[8]);& e% t4 X; ~4 i4 P9 j+ {1 n% B
  51. uint16_t  W25QXX_ReadID(void);                              //读取FLASH ID
    ) F" v$ ~: o  M  x
  52. uint8_t         W25QXX_ReadSR(void);                        //读取状态寄存器. Q$ @* f( D! r3 {* J& L3 ~
  53. void W25QXX_Write_SR(uint8_t sr);                          //写状态寄存器1 p0 f# `+ s/ h' y
  54. void W25QXX_Write_Enable(void);                  //写使能+ Z# N9 c+ u: g) Z/ N
  55. void W25QXX_Write_Disable(void);                //写保护
    ; N& w; i: ~/ v5 c& E  Y4 {
  56. void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);% \$ X5 J( B: J- h
  57. void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);   //读取flash
    - Y/ m4 c& o7 `+ A- E, a; Z( @
  58. void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);//写入flash) U: \8 R5 d5 j
  59. void W25QXX_Erase_Chip(void);                      //整片擦除
    & z- V; C; G+ B2 R" f* K  }$ K
  60. void W25QXX_Erase_Sector(uint32_t Dst_Addr);        //扇区擦除$ q' T( G% v3 ~
  61. void W25QXX_Wait_Busy(void);                   //等待空闲
    6 _# }/ G8 Y5 t1 S3 o( X$ w# o% u
  62. void W25QXX_PowerDown(void);                //进入掉电模式: W4 o8 O: _5 d- v) G
  63. void W25QXX_WAKEUP(void);                                //唤醒# l" j. y/ x1 y, b& t
  64. uint32_t W25QXX_ReadCapacity(void);
    + n& I- }- R; f1 B
  65. 3 T& i' _3 ?! @9 r6 y
  66. #endif
    / z) ^; E0 ^. w& F5 [

  67. & ~% U' T6 S# b5 b' U) M4 B

  68. + J& h4 ?- @  J4 h5 t( M; `! A
  69. $ ]$ \3 l( x3 G
复制代码

- G% n$ s# l0 {' M! b3 n
赞 收藏 1 评论0 发布时间:2022-4-9 23:03

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版