分享一个代码模块,网上找的一个STM32的SPI2通讯的FM25CL64历程,我自己结合自己的板子改成SPI1很快成功了。1FM25CL64确实简单,通讯和使用都比W25Q64容易8 F2 N) i4 C v, N1 a
2写之前没有必要擦除 类似EEPROM5 x" @8 C3 A \3 a
先看我板子原理图8 X) M0 \$ ?: D/ D4 K
, B: l. I+ A; z
两个存储设备挂在SPI1上,CS脚铁电就是PA4
5 R* ~# b% O5 ~: r. U2 R1 i; b上代码:$ r2 o7 n6 @ w0 ]
- #ifndef __FM25CL64_H_) M+ q) b( j7 ]3 e: q4 e9 }. O6 D
- #define __FM25CL64_H_ ' ~( B7 V/ f0 j
- #include "sys.h" . H" H; \4 V$ B" S5 o
: K( D6 Z8 V) i! E: o4 s
! x2 _" d9 [8 a- #define FM25CL64_CS PAout(4) //FM25CL64片选, S8 ?# w2 y2 }- z/ `3 V) u$ `
* h- D9 w% [9 g9 ^* l* H- ////FM25CL64指令定义
3 W/ b7 j1 B7 b0 ^7 Z" h1 ` - #define FM25CL64_WREN 0x06 //使能# k. @; x9 g2 H: h8 \+ c; {
- #define FM25CL64_WRDI 0x04 //失能) C9 A- f' k4 w% `
- #define FM25CL64_RDSR 0x05 //读状态
8 h5 }/ _: O: f0 g# i c2 `3 `/ l - #define FM25CL64_WRSR 0x01 //写状态
: y' v. Q' U+ t% F5 }: _ - #define FM25CL64_READ 0x03 //读数据; h( M0 ^8 ?4 y* i3 g' L! R
- #define FM25CL64_WRITE 0x02 //写数据, t7 [2 b. t9 m" S. |
- , l6 `* L; R7 l3 U9 w5 b) @
- void fm25cl64_init(void);//初始化
- i. V1 k+ c: K4 ]( U. X- Q - uint8_t fm25cl64_read_sr(void);//读状态
3 I" p6 I; `$ x - void fm25cl4_write_sr(uint8_t sr);//写状态7 Z3 k" Z4 r; X/ t) q* n
- void fm25cl64_write_enable(void);//使能
# P |5 J4 q- l. u - void fm25cl64_write_disable(void);//失能( S% V* Z5 F3 s$ T( z
- void fm25cl6_read_nbyte(uint8_t *pbuff, uint16_t read_addr, uint16_t read_byte);//连续读取N个字节- J# P/ `3 h. }! G
- void fm25cl6_write_nbyte(uint8_t *pbuff, uint16_t write_addr, uint16_t write_byte);//连续写入N个字节
7 z; D# C$ d7 y$ g/ m% H U; T - uint8_t fm25cl6_read_1byte(uint16_t read_addr);//读取一个字节
0 e1 N6 o' B. x# l; P - void fm25cl6_write_1byte(uint16_t write_addr, uint8_t write_data);//写入一个字节
5 _, u1 R* J% T2 R L0 z- ^ - void fm25cl6_clear_data(uint16_t write_addr, uint8_t len);//清除LEN长度数据
1 a2 t: m# W. a" a6 A - #endif1 J, B" A1 T l
- * H1 n4 T7 ?1 g2 L) o
- & C2 t I- ?" Z% _
& M, V( H! J! b e
) x' Y) a9 J; v
9 t- H- y! T( u& M3 [- - p6 C2 B" O. o5 ^
- * f1 q2 E. ~; s) t. L3 e3 T- c
- $ ?8 Y9 n' I! X; T' z
- ' n$ t, c) R' w
复制代码 0 ~! u) ^& P- G" {
% {, e2 Z6 i, z* V, x) `) C, l, O& X6 @
- #include "fm25cl64.h"
! _; d& |9 t& z2 P& Z% {) E5 O: A - #include "spi.h"/ I; X8 Z0 i: u. }: [) i
- 3 N, r0 z# v# ]
* ~0 n$ z$ D3 o. m$ V
: J J! m1 h& g+ X% H3 m' \+ a
# p9 T$ C E# g& ]; @+ J% @& s- ) a6 J1 a+ d! f7 _: p& v
- /*********************************************************************************************************
8 m6 K" x$ o$ B6 U, x - **函数名称: void fm25cl64_init(void)+ e$ H' u1 Q" F: x9 _" A
- **函数功能:fm25cl64初始化
$ G9 O6 {( x4 a) k - **入口参数:无
! k. o8 p/ u3 n0 L& X: N: o5 k/ ~ - **返回值:无. C, n% j8 K. a& _
- **注释:
9 L6 m/ y. M. d' u( P% t% S - **********************************************************************************************************/
, ~; R5 d4 N' a0 E+ N - void fm25cl64_init(void)
$ {' f' U1 ^- Z$ Y7 n, T5 w5 W - {& {; r; i9 {# x# B: D
- GPIO_InitTypeDef GPIO_InitStructure;
: _+ _$ E3 j X: R% s( { - & E E+ }4 u0 O* a# I
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE );0 G' [7 W2 k$ n. g3 c
- 1 E8 ?, L9 {( L. ~
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; # j0 T( Y" `, Z, O$ c" F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
; j" J i( A& p" ^/ d% f. l - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ i- D0 b0 |0 K& e' o3 r. W
- GPIO_Init(GPIOA, &GPIO_InitStructure);
5 T7 u0 A4 S1 p, r) A. B/ Q - GPIO_SetBits(GPIOA, GPIO_Pin_4);
8 @2 {! Q' V3 ?2 ]) D
/ U a. y7 s' r) w& T- FM25CL64_CS=1; //SPI FLASH不选中
# K( a& O' B3 C6 X - SPI1_Init(); //初始化SPI! n& W s0 j1 e( S' D; m4 W
- SPI1_SetSpeed(SPI_BaudRatePrescaler_2);//设置为18M时钟,高速模式
$ n* K3 T/ I5 d - }
- V6 W- ?; K7 s! v6 I
, |% v6 b7 f- R& ]4 h1 B- /*********************************************************************************************************
! ^! {' \2 {; k C0 k - **函数名称: fm25cl64_read_sr
' t/ E/ q4 T+ @9 z9 P" _; x - **函数功能:读取fm25cl64状态 C+ |! J6 c- a9 o
- **入口参数:无
) C1 Q3 V- Z$ ^4 U0 O7 I( W s - **返回值:读取到的字节/ S: |* p3 r# P0 ]6 @: y7 \, l$ s
- **注释:
6 X0 E/ |9 D% j1 P' q# H6 @+ H% r - **********************************************************************************************************/
! E( Y3 F/ L% `3 A3 U6 k5 L5 a0 r - uint8_t fm25cl64_read_sr(void)* Z, r0 X2 m9 H. q- _% C0 {# j
- {7 F4 k/ i6 s( M, K- u- {% I
- uint8_t byte = 0;
8 S5 J; f: [" [: j. p; i1 ~3 Z - 4 \. \0 }/ |& h" q* {
- FM25CL64_CS = 0; //片选5 N" H0 U j- a! K* O% W d6 u
- SPI1_ReadWriteByte(FM25CL64_RDSR); //写入读状态命令
. Q) b. v, a; _. E - byte = SPI1_ReadWriteByte(0xFF); //写一个空字节再读数据
7 j4 w0 G3 q1 ~$ D% q - FM25CL64_CS = 1; //取消片选
$ ~5 G8 O9 N( U$ Q+ K& Z - 2 ^) ]& D# U. p M" G
- return byte; //返回数据
7 ^5 o! I+ T6 E' R; Z& L - }
) K, s1 a) W# n& E8 N0 s0 S: F
, c) J5 I0 g4 `
; ]. i; [4 _, y) b, l' C- /*********************************************************************************************************
1 k! O/ T: }9 ~3 I9 J) o. k - **函数名称: fm25cl64_write_sr: U: F# }8 j/ t
- **函数功能:写fm25cl64状态7 q _% J. y' N& r) @) M
- **入口参数:状态数据
- u$ L5 p6 g- S. }$ Z - **返回值:无: j# D; o, p5 S- b- r
- **注释:
) `2 I+ D) l# d; J. Z; s, ^ - **********************************************************************************************************/% x1 f% E6 G; e8 f
- void fm25cl4_write_sr(uint8_t sr)
* e8 p) L( d1 F7 O - {' J3 N& D( M8 t( j' d9 D. _" `6 a2 X
- FM25CL64_CS = 0; //片选: E* r0 {: G/ V) E5 a" g1 U
- SPI1_ReadWriteByte(FM25CL64_WRSR);//写入写状态命令, T( k8 e/ B2 u( }1 k' I
- SPI1_ReadWriteByte(sr); //写入状态数据6 U( n: _/ ?6 N" ~
- FM25CL64_CS = 1; //取消片选/ c7 ]% j2 D: ]" U2 k4 j
- }
4 b* H6 l, S! {. F7 Z
, Q0 Q! C5 X) e4 d. ^0 n- _: ?- ) s4 j- k3 P6 S: c1 ^
- /*********************************************************************************************************
; z5 H1 i- `; K3 m - **函数名称: fm25cl64_write_enable
1 ?( N$ j0 T& [2 O# W% o) F - **函数功能:使能FM25CL64; Y& u4 h$ ^. i5 [
- **入口参数:无# E, N0 Q! u! P0 Q* e. n
- **返回值:无2 m/ V: l: U! X7 S
- **注释:; e* M" n) R' R
- **********************************************************************************************************/
3 u$ s8 T5 e/ t0 ~3 e8 e& K" E+ @ - void fm25cl64_write_enable(void)
5 P! {$ I5 E" j" N - {, j# [1 C# _6 @* w4 m
- FM25CL64_CS = 0;//片选; t: @, g# x' A4 P
- SPI1_ReadWriteByte(FM25CL64_WREN);//写入写使能命令
8 W$ ^: D0 A" y* ^2 [5 r+ X - FM25CL64_CS = 1;//取消片选& a: h+ e) y& y; n
- }
' y: ]: v: Y N) B3 W. j0 h
8 t a W% h2 A, Y6 i5 y( j
' m6 g" l3 W# |6 j8 Q$ q( x+ ^- /*********************************************************************************************************
7 m f( u5 E1 Y/ E# @( P - **函数名称: fm25cl64_write_disable( D: O$ I- S) j: }% i6 e5 x1 _
- **函数功能:失能FM25CL64, l; D0 ]1 I2 [1 m8 b
- **入口参数:无5 a! i M5 ]3 E$ @; x# A4 e5 a
- **返回值:无$ K. k1 H+ `3 i, v9 h
- **注释:- T4 q; f* D% Y. G
- **********************************************************************************************************/
j! B& V! p5 D; r - void fm25cl64_write_disable(void)7 S7 O$ f. Z/ K' b' F( L! v
- {
# X7 ?8 f* p) W% V - FM25CL64_CS = 0;//片选- w: B# Y8 G0 U, e/ k% m
- SPI1_ReadWriteByte(FM25CL64_WRDI);//写入写失能命令5 h0 ?9 q8 p: X# {5 \9 U3 K
- FM25CL64_CS = 1;//取消片选 P' f v8 _. Y) H; s! W
- }
6 v& f2 k% [- n' W) o+ A
: Q7 i- M/ w: _' |7 w. O3 m+ k- 0 A. x) u, a3 K4 W
- /*********************************************************************************************************
( ~: L+ n8 i' j/ b7 p4 P1 C" M - **函数名称: fm25cl6_read_nbyte
; g2 ~) ~3 v& D1 h% ? - **函数功能:连续读取N字节数据8 {% ~! c: R$ C* u9 c6 G
- **入口参数:*pbuff,读取数据到指针,,read_addr,读数据首址,,read_byte,读多少字节0 L& F. j7 T e) N' O7 t: ]
- **返回值:无$ W: l9 K) r5 Z2 k8 K8 w
- **注释:
9 `. C2 Q8 x8 F/ ` - **********************************************************************************************************/3 _9 C. f3 F8 E \
- void fm25cl6_read_nbyte(uint8_t *pbuff, uint16_t read_addr, uint16_t read_byte)/ J& @' k& ]- Q, N
- {8 Q7 c; `5 D* q
- uint16_t i;; X$ P& t$ S( N! H" e& C2 }; ]+ h
- 4 a9 p6 F w3 C& M' B
- ) h% i, M' z+ }- Q/ u3 L& Q* N) N
- FM25CL64_CS = 0;//片选
2 Z" n1 f: Y9 H7 G) |9 _) k - SPI1_ReadWriteByte(FM25CL64_READ);//写入读数据命令" Y D& E& J7 F6 e* u j m/ |6 c: G
- SPI1_ReadWriteByte((uint8_t)((read_addr&0xE0)>>8));//写入高8位址,最大地址13位/ P+ g" @$ b, X3 U
- SPI1_ReadWriteByte((uint8_t)(read_addr));//写入低8位址,
- v& h% e% X7 }8 J0 c- l -
; i. @7 y, V5 ?% e - for(i=0;i<read_byte;i++)//连续读取数据# g& D0 }; U4 y: j c D! |+ `
- {
) ]/ ]5 Y5 W2 q6 ]5 ^ - pbuff[i] = SPI1_ReadWriteByte(0xFF);5 r% k8 [! Q" y; V
- }
- n0 o0 e$ a, s) G! Q - FM25CL64_CS = 1;//取消片选
, ^" F- G1 Z5 @) \
1 m: C. b' h, g# D- `-
' o8 a4 U1 n5 R+ n, @ - }( O6 O' B3 j5 v# u$ W
- k) G- W* S5 i3 }/ ?# F
- ; t! L3 K6 z) Z- [ Z
- /*********************************************************************************************************, Q6 \5 d4 L: B$ ^9 S: b* X
- **函数名称: fm25cl6_write_nbyte
4 e3 |& M; e$ ?6 l7 y5 A - **函数功能:连续写入N字节数据# i% I6 `/ m V1 G1 a1 w& C
- **入口参数:*pbuff,写入的数据指针,,write_addr,写入数据首址,,write_byte写多少字节9 n2 Y5 l$ _: Q5 {* u
- **返回值:无$ m! h9 J3 L4 b4 P
- **注释:3 n& Y6 H: G, f
- **********************************************************************************************************/, `* n& k; R7 q8 b
- void fm25cl6_write_nbyte(uint8_t *pbuff, uint16_t write_addr, uint16_t write_byte)' I$ ^( ^9 J S5 i
- {
( i1 ?9 u& E2 C4 i, o4 @6 |5 i" a - uint16_t i;
! D c, u0 Y$ F% b7 i! A$ J8 b
) v- g; {+ y+ ~5 |" ?* q% `+ l- * ^- u2 Z6 Z5 O5 ?- }$ J
- fm25cl64_write_enable();//使能写操作,一定要先使能,取消片选后再写作下面操作% Q6 V" Z4 x+ Q' W# O B3 r
- ! w9 Z) _& J- i& |$ d, `
- FM25CL64_CS = 0;//片选7 f+ X6 L2 I- ?% G$ r
-
) x; U9 p( o& g( q: J - SPI1_ReadWriteByte(FM25CL64_WRITE);//写数据命令
& S- W' x4 g% N7 f/ _" B" g - SPI1_ReadWriteByte((uint8_t)((write_addr&0xE0)>>8));//写入高8位址,最大地址13位
+ s$ o: k L& F, P8 g1 Y) V+ d. d - SPI1_ReadWriteByte((uint8_t)(write_addr));//写入低8位址,0 [( i1 V' _% S9 h! I1 F& j8 W( o
-
5 R, m* |" ?. w+ B3 [ - for(i=0;i<write_byte;i++)//连续写入数据$ P+ [% G3 ?4 U
- {
9 I6 Q* I" |0 i - SPI1_ReadWriteByte(pbuff[i]);
) t8 T% {" r I" |/ I+ R( M6 {3 } - }
+ [% U: G% r% T1 `: D - FM25CL64_CS = 1;//取消片选
! B( z) e4 z( U& M3 Z q5 e \ - }
, e' i& l. J; d6 J. Z$ F1 ] - * r4 c O4 N7 i
- * \; V' l7 L/ N( t) l: d3 V. q; C
% a6 q9 f5 H2 N( Y9 D) C S9 @ l- /*********************************************************************************************************: a, |5 C) e! G5 F
- **函数名称: fm25cl6_read_data_1byte5 k7 R. ?+ ]$ `( l+ t/ P
- **函数功能:读1字节数据
. T. W+ u) c6 H |4 c4 T/ v- { - **入口参数:read_addr,读取数据地址
2 S' ~* |/ R/ a$ J* u# X& s - **返回值:返回读取的数据
7 {8 u9 o, D. N7 G - **注释:1 |2 M$ O- f' F8 t' p
- **********************************************************************************************************/! Z: c& w3 q9 n4 b
- uint8_t fm25cl6_read_1byte(uint16_t read_addr)% j8 p5 T" I- J: B! j4 F
- {4 V% \5 x0 u: O& B8 E5 {' t
- uint8_t dat;- E/ p& ^3 C" O3 B+ [9 U
- $ c6 ~1 o1 H3 v! M3 g" j) Z
- FM25CL64_CS = 0;//片选6 y( l8 k: y$ s2 K! C- T
-
9 e: V( f) E+ O0 |) S, t& F - SPI1_ReadWriteByte(FM25CL64_READ);//写入读数据命令8 h t1 n+ P v5 J5 n
- SPI1_ReadWriteByte((uint8_t)((read_addr)>>8));//最大地址13位
: e6 o! {1 {! y; j - SPI1_ReadWriteByte((uint8_t)(read_addr));//
! G K) i. V7 T -
5 H: j7 [5 s) q6 z - dat = SPI1_ReadWriteByte(0xFF);//读一字节数据
+ _. c3 Z H; s
) g9 U4 G" y$ j5 P/ y8 U* ^3 A- FM25CL64_CS = 1;//取消片选
) O- b: Q d, @7 s+ h4 J
+ ]" G# C; m% k M4 g) X: Z$ G- return dat;7 E, i, `) x; K. u( Z* f0 o/ v
- }
1 F1 M _+ N/ c1 o4 L
6 A; A4 ?+ \' p# Q7 l8 z
/ r- x ?/ Z+ @- J v/ m3 ]1 f- /********************************************************************************************************** z8 G! C- Z- c# x6 w
- **函数名称: fm25cl6_read_data_1byte
! Y; n" A* R3 U2 `4 u6 I! ? - **函数功能:写入1字节数据6 E6 k; G7 k/ U; M
- **入口参数:write_addr,写入数据地址,,write_data,写入的数据
$ o I; s) x7 f- K- a - **返回值:无
4 S. A) z, B/ V% P U - **注释:
2 g/ e) @9 E2 ~* [ - **********************************************************************************************************/( N* }' n! ]8 [* e/ @7 s
- void fm25cl6_write_1byte(uint16_t write_addr, uint8_t write_data)
3 a. C( s8 |% ^5 n - {$ l8 w- g+ b; f+ f; `
- / F I, _: o" d3 R0 z
- 3 h$ y( @ n; }# Z3 [
- fm25cl64_write_enable();//使能写操作,一定要先使能,取消片选后再写作下面操作& g6 x- J/ U Q1 F
-
8 I- W9 Q y" w. J% d - * u/ g! \/ E+ Y3 B1 n1 O
- FM25CL64_CS = 0;//片选" {; |9 f. _# e- O8 G3 e
-
; a! N2 k" j# V8 u) o8 o - SPI1_ReadWriteByte(FM25CL64_WRITE);//写数据命令
8 B! a; M( @8 d4 p - SPI1_ReadWriteByte((uint8_t)((write_addr)>>8));//最大地址13位( B) ?- j0 i% ?. |
- SPI1_ReadWriteByte((uint8_t)(write_addr));//5 u1 K" [4 r) {7 v1 |. t
- # D9 R D) @" K: b. h& }
- 4 G& H! Q5 B/ ^$ w
- SPI1_ReadWriteByte(write_data);//写一字节数据
7 ^8 }- [. O/ _, ~9 }! Z0 y- p - 7 H4 a* T/ Y2 }) `. N8 r" I! l6 @
- FM25CL64_CS = 1;//取消片选
0 H K% O; }& M' V2 r% ` - }
- M3 S! n, t$ S
- S5 U: j' x6 J; t, {- 4 a# e, Q5 \! z" n3 O
- /*********************************************************************************************************- d$ ]5 s! w; L1 _, L
- **函数名称: fm25cl6_clear_data
6 V+ l6 }4 M4 k; E. E - **函数功能:清除指定长度数据" C; F, m9 |7 c" M! b J
- **入口参数:write_addr,清除数据地址,,len,清除的数据长度2 |0 t+ Z: [ R8 F
- **返回值:无
0 H- ]1 n4 T5 r; k' ]: K$ \ - **注释:
+ I3 j9 Z( G# b' S/ I - **********************************************************************************************************/
1 ^) h& k7 o7 s3 ^/ N( m' p - void fm25cl6_clear_data(uint16_t write_addr, uint8_t len)% l5 i$ T3 @) H0 V
- {
7 U+ k6 N4 Q; Q( k9 T7 d6 ], ]" e
- b1 E( i! W# u/ I6 t- 7 K8 k8 P2 E3 E; `4 }
- fm25cl64_write_enable();//使能写操作,一定要先使能,取消片选后再写作下面操作+ A2 \. N1 @9 a! S: K& e
-
# R8 w G9 V0 n/ E: p( V9 D5 M1 l - ) u* U" `6 c5 H5 M& E: T3 f
- FM25CL64_CS = 0;//片选1 N/ q) v1 Y6 M2 n3 |
-
& E( c/ i5 x& v - SPI1_ReadWriteByte(FM25CL64_WRITE);//写数据命令
! Z3 ^$ ?2 p0 i l - SPI1_ReadWriteByte((uint8_t)((write_addr)>>8));//最大地址13位+ S! u0 K% f, b/ b
- SPI1_ReadWriteByte((uint8_t)(write_addr));//( p1 ~' b) U; ]
-
+ P+ l U/ c6 d8 x2 n: x5 H - while(len--)
( {; U1 B# F% ~7 u3 f - {
# O% d" R4 o c- G1 ^0 ~, n - SPI1_ReadWriteByte(0x00);//- L1 a% C3 _* w
- }
4 O) ?. [7 ~ w$ Z7 D! C- E7 @ -
& {' A) X% u0 J3 V9 A - FM25CL64_CS = 1;//取消片选* u& k4 e& w A2 d+ T1 p
- }, T. k! V7 k7 ]3 V) Z9 _! v5 |
- ! M+ r4 R8 }. g8 j3 o
- 2 q3 L. {! C- q- H6 P) `0 Q
1 R* E+ g1 Z( y4 D$ y
% g6 @, [2 u, X8 V( x
; z# W& J) R8 H
# O, D& M, F. P+ O% G- `; u8 Z
( S# o: i* T/ ?' X) a- ' G( N' V9 k! d9 @( A2 J
- & ~; y) w, z. H' p9 N, j" R6 r
- & l0 M9 {! @" ~3 J+ z1 ~+ P
- s* e7 `1 u8 G' i6 G$ y! o3 l
复制代码 其实如果面向接口编程,模块调用SPI的函数SPI1_ReadWriteByte还可以抽象,以备可以切换为SPIX_ReadWriteByte。0 _4 W" s% Y/ G3 f, X
' r# l0 J/ |4 j* J
测试代码
. m. Q" [! J @- fm25cl64_init();
' F/ y7 c( Z9 j7 R6 Y - fm25cl6_write_1byte(0,0xAB);! T$ f9 R0 e( N# _ N; i! ?
- GG=fm25cl6_read_1byte(0);
2 N+ n+ L! A1 b& r - // fm25cl6_clear_data(0,1);
* V" `( t( l/ m - fm25cl6_write_1byte(0,0xCD);
* E d% e9 Y6 ?8 F - GG=fm25cl6_read_1byte(0);
复制代码 . z0 }* K. S' m( k: J) S# L* k+ {- b
6 w; D) K5 M' i, H, y, Y. A |
十多年来一ˊ直在用 24C64 的 FRAM 路过...& v0 ^! ?, U/ ]6 u9 F9 i5 n