一、原理图, u, e0 d- x, a2 g+ m( ~* w: p1 `2 t
3 I4 |1 p$ z( k) |7 B$ q
& b. s4 D8 [0 g1 F9 k2 e, O
* B% `. O; m) j, e8 p4 j# {二、 CubeMX配置7 h9 ?0 E/ _- c$ } f
2 j1 y/ w; _; P: B% q, K" U
Step1.打开 STM32CubeMX,点击“New Project”,选择芯片型号,STM32F103VETx。
7 I7 m4 V0 m4 N1 I* h/ m) q4 _ a9 U& X# a+ o
( S- Y% n6 V J \; l6 z# w( j
g" D$ b# j- W Step2.选择时钟源,并配置时钟树。选择Crystal/Ceramic Resonator,并配置系统时钟为72M。
) h& o. ?7 g' j3 }: H3 m% {( B1 n( U! D7 B
: l; r; R: V, [- x1 @( Z* t0 S6 _. n6 B! O5 N; J
* w& v2 o* `9 l$ L% z3 M0 Q5 o: H/ O6 j+ a% k: s! I
Step3.配置SYS,我们这里选择的是Serial Wire。(正常情况配置不配置不影响,debug可以使用。但是你不可以把这两个引脚用于其他复用功能,如果用于其他复用功能,debug就不起作用了。)( q$ O2 R# F1 x/ `
9 f# ~' y) ^" R* S* f. j
2 n1 g! i; i W3 }* k2 v7 P8 X. P7 ^
Step4.串口配置(主要为了在串口调试助手显示读写数据),因为没有用到中断和DMA所以我们就不过多讲解。& t2 H, J% S+ o% r! @! V7 T6 o
# N+ {% _; j8 t0 O: Z/ G
( y5 s( s7 J/ S8 g* V4 T( N
/ j9 w- x3 O' v' S% R7 O
step5.SPI外设配置,这里选用的SPI1。因为没有使用到中断和DMA所以只需要把硬件SPI基本参数配置好就行。
/ A) S! g# J- ^) {7 a8 `( f* g9 `' V: Z) u' V% J. k$ B
9 ~5 P) a- R: K }' d" Z
9 i2 w' T2 F9 N4 @ M y# m) g' ?
step6.因为是采用软件NSS,所以还需要配置相应的IO口。0 S( I: f; _5 t6 R5 ?
$ u: S: F+ b8 K# ^/ K0 U
! A" y6 K W$ `" I+ x$ U' ^
6 `) ]8 F; h" t5 k4 F9 ?8 n {. k 到这里关于硬件IIC参数配置基本已经完成,只需要根据之前文章《STM32Cube HAL:GPIO输入/输出(一)》Step4-Step8,设置相关工程参数和生成代码。5 R6 N; \ l3 [* n
( E- Y1 r8 D( Z- F# \- Y( w
三、添加功能代码5 B7 `$ u' U) t' h
8 l* { U! d% U1 x% t T8 E+ S
1、我们等会会向串口调试助手发送数据,进行实验结果的验证。 发送数据我们采用printf函数,所有需要重定向c库函数printf到串口。注意使用时需要在keil设置中勾选微库(use mircolib),同时需要添加头文件#include <stdio.h>。2 [2 A# @3 e; f7 f- V( a' g2 s. p
& j; Z2 j' x+ W: o. Q- //重定向c库函数printf到串口DEBUG_USART,重定向后可使用printf函数/ A! D' C. S7 H6 c$ y: w
- int fputc(int ch, FILE *f)
$ A5 z1 T: B, w3 g# C - {8 F5 U; F* ?& Q8 f) x0 a
- /* 发送一个字节数据到串口DEBUG_USART */$ _2 q# ?2 k' X. h; ` @( C
- HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
: I( {, C* ]6 y - 8 t1 N2 w6 q- G2 I
- return (ch);
[8 P5 X/ T6 T, @( R! ] w - }
8 M1 K% t+ P) X2 h5 I& W& t - . O( R& L4 d! ]& X* T2 o! e& e
- //重定向c库函数scanf到串口DEBUG_USART,重写向后可使用scanf、getchar等函数
* r3 k+ u' O7 z) E6 f - int fgetc(FILE *f)
* {( S8 { |$ ] - {
$ a/ K7 ^( n! J+ X; g* }, M- k& F - int ch;/ K% F9 q( e! ^2 v3 [* h1 z
- HAL_UART_Receive(&huart1, (uint8_t *)&ch, 1, 1000);
( t9 X' X9 ^% S# U8 V - return (ch);5 D# V9 [1 d$ k5 g8 S; n; V
- }
复制代码 ' t8 o; Q& ~+ w, Y0 B/ e0 K
8 w% N: j4 g5 L2、在gpio.h文件中添加相关的宏定义。因为是使用软件控制NSS,下面宏定义通过IO口模拟NSS信号。(NSS低电平控制,NSS高电平释放)
& I% H' B, |7 ~7 f" R3 ]. I- u2 V* `# N+ ?
- #define NSS_HIGH() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET)6 O# |3 `& r5 E9 ?* } N
- #define NSS_LOW() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码
. S/ ~. K: Q- L1 W1 C9 z/ B3、在spi.h添加宏定义,和函数的声明。方便移植和提高可读性。
6 `7 t5 J5 N' L2 C3 y. w0 d' w! [" I6 O- j7 F( N
- #define FLASH_PAGESIZE 256 //W25Q64的页面大小 ) w! A: E" a7 }: D5 b9 n9 `
- #define _Flash_ID 0xEF4017
复制代码- extern SPI_HandleTypeDef hspi1;
, U7 C8 }& X( ^$ c+ H' }' U - void MX_SPI1_Init(void);
" ~! N: G$ [8 M" p - uint32_t SPI_FLASH_ReadID(void);
2 _- C5 e8 ?' ? - void SPI_FLASH_WriteEnable(void);
) f t+ N/ a! X0 V8 U3 _! N - void SPI_FLASH_WaitForWriteEnd(void);
9 E O; r, E4 O( y. h6 y- o - void SPI_FLASH_SectorErase(uint32_t SectorAddr);
. }7 n7 o' l- M - void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);
" q! I8 i# N" L* V7 S# n - void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite);
" Y1 W. P, Q# n4 Q3 C+ G - void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t ReadAddr,uint16_t NumByteToWrite);
复制代码 ; h' ?; N' c. E2 D3 o
然后在spi.c中实现声明函数的具体功能。$ F9 Q; N! F$ N. Q* x# o0 q4 r
+ e4 B$ f5 a* ]( \+ `- /*读取制造商和设备ID*/; }/ B% Y) ]4 i
- uint32_t SPI_FLASH_ReadID(void)$ s; e7 a& I* f4 l% X3 ^
- {6 y& u3 r. Q2 y- R% V
- uint8_t W25X_JEDEC_ID=0X9F;) r; w1 E7 S. Z, V4 L
- uint8_t temp0[3];
8 [7 w7 j6 |4 T! S, j - uint32_t temp;1 L2 b( D8 U$ I! R% E B
-
, D# w! F& \, h* h; ^ - NSS_LOW();//选择FLASH:NSS低电平
9 V R8 N9 j6 ?: d* `* }: a - 7 e! u9 W$ N# Z! `; L4 w% o
- HAL_SPI_Transmit(&hspi1,&W25X_JEDEC_ID,1,10);//发送指令' a1 H3 M" [6 A4 v9 A
-
% {' ]/ j* F- s. |" u$ e$ J* ~& @5 I - HAL_SPI_Receive(&hspi1,temp0,3, 10);//读取制造商和设备ID: m- j% h( _8 ^. x( f; p
-
! \- k; W/ t( {# B - NSS_HIGH();//停止信号 FLASH:NSS高电平$ P4 E: m+ k: u' Y/ Z3 W5 Z! u- S
- & ~. Z* j# c" i" |" @0 F4 Q5 \
- temp=(temp0[0])<<16|(temp0[1]<<8)|temp0[2];//把数据组合起来,作为函数的返回值; e5 F+ D" [4 t |
: _8 X C" ^ u5 B; O$ v- return temp;7 |# O3 x4 Y& G8 X2 v1 {6 S4 s8 C
- }9 _+ E$ X0 f: q
4 T6 B7 t8 Q! l# Q) z- /*写使能*/4 B' x9 j6 D5 ~8 O$ U
- void SPI_FLASH_WriteEnable(void)! q/ i; P! g- I0 `7 ?8 e- l- M
- {) q3 g S9 O& }3 n& Y& F
- uint8_t W25X_WriteEnable=0x06;
( d! @0 \6 ?6 q% Q" i6 s; t3 l4 { -
+ S& c: i5 k' J+ K - NSS_LOW();//选择FLASH:NSS低电平
1 h r: D/ b# s; Z5 c; P -
& {$ o) K8 D" z" V - HAL_SPI_Transmit(&hspi1,&W25X_WriteEnable,1,10);//发送指令, e( o! f) }$ K1 `
-
7 c m! J. W9 } - NSS_HIGH();//停止信号 FLASH:NSS高电平! G; X9 R6 ?( [! \/ m5 a( B
- }
& l& e" q, q; s, I$ Z
& f3 b; y. `( t# l1 `9 M- /*扇区擦除*/% B* }1 u6 j8 o: W0 X2 N3 [ P( d
- void SPI_FLASH_SectorErase(uint32_t SectorAddr)
$ ~5 E# F5 x: M4 }: [( s/ l - {
z$ k. }& |! P - uint8_t W25X_SectorErase=0x20;3 N4 w; T; Q# P" g/ R) [
- uint8_t temp1,temp2,temp3;6 C) x& G# z6 m# Y- b( Y
- * ]9 d( m/ T) X! {9 b, K6 _
- SPI_FLASH_WriteEnable();//写使能
8 L% O) c; K% I9 h9 G - , t. X5 Q6 b, ]$ A3 L: }
- NSS_LOW();//选择FLASH:NSS低电平
1 D0 e8 e3 Z: c! V- {$ k -
# l% v0 a6 ]0 }9 y2 G - HAL_SPI_Transmit(&hspi1,&W25X_SectorErase,1,10);//发送指令
. a: l( d2 Q2 O, y. R7 E% F6 j& \( N - ( i3 G7 s; u6 P* H
- temp1=(SectorAddr&(0xFF0000))>>16;//发送地址5 R) z) L1 m9 r2 K( \3 I) K' s
- HAL_SPI_Transmit(&hspi1,&temp1,1,10);. C% z( Q b6 a9 _- u
- temp2=(SectorAddr&(0x00FF00))>>8;9 ]3 ^- \4 e5 e7 x) @; U
- HAL_SPI_Transmit(&hspi1,&temp2,1,10);
! C/ M/ e: [2 [3 W* r4 ^8 N/ n - temp3=(SectorAddr&(0x0000FF));
m' |+ ~+ a* i) ]/ {, T- F6 b - HAL_SPI_Transmit(&hspi1,&temp3,1,10);0 Y9 c) [8 k" w: |: i o
- NSS_HIGH();//停止信号 FLASH:NSS高电平5 D+ p9 W- J' I1 _
- " m/ u# C* c7 T+ T
- SPI_FLASH_WaitForWriteEnd();//等待擦除完毕
0 ]3 x ~, {0 ?; h* v! x4 v - }
5 n7 m8 } a/ D; K - ' Z7 A" t4 {( Y' H& l
- /*等待 WIP(BUSY) 标志被置 0,即等待到 FLASH 内部数据写入完毕*/4 l: a4 ]1 x3 d8 c, y
- void SPI_FLASH_WaitForWriteEnd(void)
- W. u- V4 G" P - {
5 x$ k8 j; z1 { s - uint8_t W25X_ReadStatusReg=0x05;
( K2 u" J6 Q2 M* M. n - uint8_t temp;) b) V _/ G/ f+ w K) I, g
-
3 R; J* B& h u# r6 E- F* H - NSS_LOW();//选择FLASH:NSS低电平" q# T+ {. c- ] n0 `
- 3 b) P3 i% F }+ O1 o
- HAL_SPI_Transmit(&hspi1,&W25X_ReadStatusReg,1,10);//发送指令
$ N6 S. ~$ @7 \' q' ?: R - do
1 s/ K/ o" r2 i. A# q4 l! u ` - {
6 e% K2 B, f/ F0 I- K - HAL_SPI_Receive(&hspi1,&temp,1,10);// 读取 FLASH 芯片的状态寄存器4 [6 D3 u5 {7 r9 o, _9 f, p7 Y
- }
2 R9 s) @; T S: b. T* F - while((temp&0x01)==1);. Y+ ~1 Q. H; Q, p
-
" o. ^9 H c- z( g a6 M - NSS_HIGH();//停止信号 FLASH:NSS高电平
4 a8 O! G3 F% x$ H/ f* ~5 s - }
- C' T8 c7 M: Q) t/ G5 \ - * w: K+ p# T1 z- d! l; D
- /*在FLASH的一个写循环中可以写多个字节,但一次写入$ S' n/ a9 I+ ]9 K
- 的字节数不能超过FLASH页的大小,W25Q64每页有256个字节*/
8 ~, u# k7 U) R: P - void SPI_FLASH_PageWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite)
2 f! l! P1 w( |3 i5 G. V0 S - {
- ~ G7 R, @' U, [( Q - uint8_t W25X_PageProgram=0x02;
8 |8 m9 Z1 H- N% t - uint8_t temp1,temp2,temp3;1 ]) I+ w, b+ d
-
3 ~- r7 F& [# G4 t, Y4 J - SPI_FLASH_WriteEnable();//写使能
* C3 s& o2 p; k, A# C - ; p* o4 I3 q1 G, G$ |& c: Z
- NSS_LOW();//选择FLASH:NSS低电平
( n- |. Z: b2 Q* _. V -
! O+ \. S8 F8 {* h7 w' n+ E& n) F - HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令
8 C( Q; D' W3 g! d( B - d+ D$ S7 T: p2 w. t- o
- temp1=(WriteAddr&(0xFF0000))>>16;//发送地址
3 ?- R" _8 _7 @' r - HAL_SPI_Transmit(&hspi1,&temp1,1,10);8 i8 p8 S: f+ a& A
-
+ Z" j& D: H. C' [; C - temp2=(WriteAddr&(0x00FF00))>>8;2 ?6 k* e6 L$ g. t! g. U
- HAL_SPI_Transmit(&hspi1,&temp2,1,10);' A) e7 O0 m3 l3 T# R; q
-
# N* g/ `: r: i - temp3=(WriteAddr&(0x0000FF));6 P7 r9 _ B3 S: {5 ^
- HAL_SPI_Transmit(&hspi1,&temp3,1,10);/ R& c* u' U: J6 h; ^; `
- $ v! C3 U3 s0 c2 I( L ^3 [' v
- HAL_SPI_Transmit(&hspi1,pBuffer,NumByteToWrite,10);//发送数据
/ s: v7 O8 O7 `4 F -
% g# ]4 g+ H G+ i - NSS_HIGH();//停止信号 FLASH:NSS高电平
2 u" }0 m8 _ f- F* O - 9 F; z2 i3 ] Z. V5 w6 C
- SPI_FLASH_WaitForWriteEnd();//等待写入完毕+ p% e5 l! H2 Z4 b
- }
# R& x7 N t, g: b - . h: p4 q9 T! |& X) F
- /*不定量数据写入*/. f4 y1 e( O- ]. g2 L4 Q
- void SPI_FLASH_BufferWrite(uint8_t * pBuffer, uint32_t WriteAddr,uint16_t NumByteToWrite)8 z0 O: {6 G! v3 x( w1 O
- {2 e7 l: `8 y! G
- uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;' B1 Q F1 n( X- T, g. L) D5 R+ W
# M) s% h( S. i2 E" s L- Addr = WriteAddr % FLASH_PAGESIZE;//判断写入的首地址是否与EEPROM页的首地址对齐,0为对齐
- P( V+ t" r2 B/ _$ X, W - count = FLASH_PAGESIZE - Addr;//计算从写入的首地址需要写多少数据才能填满当前页
$ s i, {$ x1 F9 f - NumOfPage = NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页(地址对齐的情况)
! A2 c8 R; T( F* l/ n& G0 w - NumOfSingle = NumByteToWrite % FLASH_PAGESIZE;//计算写完完整页剩下的数据个数(地址对齐的情况)
; m1 v$ ~! e7 h- X S& ~* J - + J* ?9 T5 g: a$ r. D+ U
- if(Addr == 0) //判断写入的首地址是否与页地址对齐
& _; W% ?6 g; W' \ - {! z! F- |2 d5 P" ^
- if(NumOfPage == 0) //如果页对齐,判断数据是否不满一页) Y9 W6 D, F9 y2 ]
- {. R% N3 w0 b9 U9 O1 F) m; `) z" X
- SPI_FLASH_PageWrite(pBuffer,WriteAddr,NumOfSingle);//如果不满一页,直接写入数据
1 \: r# b3 o: a k - }' [: } b5 @4 f% r
- else //在数据满一页的情况下,通过地址自增方式,循环写入数据(页写入的形式)
* [# g! M5 N- F - {4 u% G! B3 d! O2 Q+ x" w
- while(NumOfPage--)//循环写入数据:先写入完整页
* |; F/ B6 |5 p2 |) V6 K: ^% e - {" o: @, m& H) `6 N
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
2 v4 m5 P+ @3 h9 n8 b4 U - WriteAddr += FLASH_PAGESIZE;6 T+ @# i" k4 g) C
- pBuffer += FLASH_PAGESIZE;4 n- P' c$ P j' y( x# g- s
- }
+ y0 S4 W& P2 a! |0 P1 q- q
0 t1 {: `! y9 P) Z" V% [- if(NumOfSingle!=0)//循环写入数据:再写入不满一页的数据
# @( p3 ?% U& `1 n1 r/ w# d( \ - {$ r# E! p7 W8 k" n) M* x7 A& u+ p
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);
9 H7 e6 J( W2 e" V& ~2 q - }7 T4 Q! S" |6 I3 |& ~1 L
- }) g8 O& e. h4 l- K1 a) R
- }5 Q/ c5 V4 N2 d" U
- else
) Z; K5 Y5 S7 k' `. c8 J - {
# z- c' |! i2 h* { - if(NumOfPage== 0) //如果页不对齐,判断数据是否不满一页: j4 I; H l: z( L
- {3 u4 P9 k1 K- o5 V
- if(NumOfSingle<=count)//如果不满一页,判断数据是否跨页
- w9 S. Q, `+ }$ K d8 ^ - {
/ y5 B0 g! C# |% k- J, r, L - SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//如果不跨页,直接写入数据
Y: `: i+ }" Q - }
; v0 }# e/ p, ?$ a' h+ K0 V, q - else
0 ?; {5 s; R% h; l - {( e/ ~# ] ?; N! C8 E
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//如果跨页,先写首页数据,再写次页数据
) z% O6 {0 f& d - SPI_FLASH_PageWrite(pBuffer+count, WriteAddr+count, NumOfSingle-count);& F. Q5 x: x& d) E$ U
- }6 n: d3 S% O: z+ O1 r8 m8 \& t
- }
F" S! X/ L+ R" Z - 2 {$ M1 m: o1 V v; C
- else
3 ~/ `$ d' e/ w: v - {! _9 t n8 B- s
- /*如果数据满一页,对数据进行分离*/
- }$ U4 l0 A& y7 @ - NumByteToWrite -= count;//扣除第一页数据个数/ I# c0 |! A; l9 |3 |% w1 k
- NumOfPage = NumByteToWrite / FLASH_PAGESIZE;//计算写入数据需要写几个完整页# q' t, m( C: }$ P
- NumOfSingle = NumByteToWrite % FLASH_PAGESIZE; //计算写完完整页剩下的数据个数! \* t! |& T9 ^; p# G, F
) z2 P* H/ q: L F& t- Y- if(count != 0)6 B H( f" _: }& G! T6 N
- { 3 j6 |5 Z) z! e, G" V
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);//写入首页数据. t" z. b- h* R# ^- f4 D
- WriteAddr += count;//写地址自增6 b1 p% X& T0 z5 f6 u: U% K0 _
- pBuffer += count;//缓冲区指针自增/ l& L* g4 Q* t, [+ v7 x
- }
7 c. G7 R4 c0 ? - 5 _( o, c: G/ `. }7 H; q* s6 o
- while(NumOfPage--)//依次写入完整页的数据
) [. _$ w& W5 K; ^! P# W - {; r) N! k ?. I, t* J
- SPI_FLASH_PageWrite(pBuffer, WriteAddr, FLASH_PAGESIZE);
2 G& z f- D4 b( u& I# j& h - WriteAddr += FLASH_PAGESIZE;//写地址自增
3 B) _5 J3 K' n8 ]6 B v4 y- K - pBuffer += FLASH_PAGESIZE; //缓冲区指针自增 ' |6 }# a0 ~5 D p: [3 z0 G2 T5 O
- }
4 [) _- V3 o/ H+ @5 k& x; f" q - if(NumOfSingle != 0)//判断最后一页的数据是否是填满完整一页的1 k& G& Z, h. Q w0 H, f, J
- {
4 n3 z0 E( |0 T7 s% o) C' R6 | - SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写入最后一页的数据) @, v. |: [" [" U9 q
- }
/ z9 P! G* m2 P, i1 j2 m% U - }
$ s0 E0 s8 Z6 S6 P" | - }
; H. k. v! ?% B0 { - }
1 f8 l9 ^. C9 G' B% A3 S
( p6 `( ?; L: z6 b4 u! h% g- /*读取FLASH数据,读取的数据量没有限制*/
$ z2 S5 |8 V2 ~2 _" \7 V - void SPI_FLASH_BufferRead(uint8_t * pBuffer, uint32_t ReadAddr,uint16_t NumByteToWrite)% f- t5 j2 W6 p0 u% c9 f# t
- {
8 ^: q" U8 V# U3 a8 e/ }! ? - uint8_t W25X_ReadData=0x03;6 z9 \2 _- p0 v1 P3 p: l$ l
- uint8_t temp1,temp2,temp3;
) {* ]" }" U/ W0 g6 e! D - ( M* O% d3 C! i! c# @/ e
- SPI_FLASH_WriteEnable();//写使能6 b: ]4 U8 j( Z
- - h6 e) j7 B) p. d. R! [1 ^
- NSS_LOW();//选择FLASH:NSS低电平
Y! h. J4 ?2 Y( {, J - & n$ l7 q+ Y) L
- HAL_SPI_Transmit(&hspi1,&W25X_ReadData,1,10);//发送指令
9 j$ E- ] v' X, t+ ^! W% S0 f7 n - + }- c! b( a( @: o% i; L) X# n1 ~: S
- temp1=(ReadAddr&(0xFF0000))>>16;//发送地址
/ o; G; O; l C% g7 a! q' q9 y - HAL_SPI_Transmit(&hspi1,&temp1,1,10);
4 t( L( q1 I/ x9 X9 r+ _5 l - 4 B/ R% _3 B: g6 k0 r! a8 t* s1 ~! K
- temp2=(ReadAddr&(0x00FF00))>>8;# j# `. {6 I: O* T# F
- HAL_SPI_Transmit(&hspi1,&temp2,1,10);
, c' z* m/ S8 ^) t$ A7 M -
0 g, _: m* h- m - temp3=(ReadAddr&(0x0000FF));' ^1 a6 f" G# ?- P% Z- o
- HAL_SPI_Transmit(&hspi1,&temp3,1,10);
' U2 S( q0 |! D9 ?2 W& m - 3 r( w/ q f3 Y" r! W9 R
- HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);//这里超时时间不能设置为1,否则会读取错误
& f" a& t( ~) ?9 s8 a5 z -
, w; y, p2 i: d* c$ P - NSS_HIGH();//停止信号 FLASH:NSS高电平1 V7 _3 f9 L* v% L4 r8 w# @+ @% c$ K
- }
+ |2 b% P/ p2 x- a7 y& ~
复制代码 2 g: }. f4 D0 T) U, i7 O' v; ]
3、最后在main.c文件中,编写测试代码进行验证。: x! h8 @* L& i
+ B* p G3 e J3 W6 D6 s- /*测试代码涉及到变量,宏定义*/
: y+ c8 f* U, I% Y - /* 获取缓冲区的长度 */: I% s- y! p0 ^/ O
- 6 Y% A4 }- m1 w3 L4 A$ d4 I
- /*sizeof():数组占用字节除以数组类型所占字节,结果为数组元素个数*/
, K! T" n6 W' @8 Q& U - /*使用方法:sizeof(数组名)/ sizeof(数组类型名) */- Y c' D& j/ y6 r: t5 R
- #define countof(a) (sizeof(a) / sizeof(*(a)))0 v7 I1 |' k, C0 f) J! L4 w
- #define BufferSize (countof(Tx_Buffer)-1)( _- d' b4 Q) l6 j: V# v3 @7 [4 N) a
0 m8 |" V7 [, D- #define FLASH_WriteAddress 0x000006
/ k; n; f& L% T- M1 r n/ b2 [ - #define FLASH_ReadAddress FLASH_WriteAddress& g2 Y7 [9 t3 T& G9 a& L) n
- #define FLASH_SectorToErase FLASH_WriteAddress9 w- y; z. D4 p# O
- " M8 b6 j( q2 A, ^1 ?
- uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength);
9 ^: x5 U3 Y8 o0 r! r4 _ - / H l% {& q7 Z' l' U) D+ V
- uint32_t Flash_ID;- T$ J5 V* U; z/ G
- uint8_t CMP_RES;
# r+ e* ]+ I9 x! q: X& n - uint8_t Tx_Buffer[] =
8 G9 Q, y* f1 X4 n: @. P2 N - "采薇采薇,薇亦作止。曰归曰归,岁亦莫止。靡室靡家,猃狁之故。不遑启居,猃狁之故。\
" p+ T1 |& O3 l& R4 A! {" M3 H/ s - 采薇采薇,薇亦柔止。曰归曰归,心亦忧止。忧心烈烈,载饥载渴。我戍未定,靡使归聘。\! O1 }4 K# O0 `% w, V$ h
- 采薇采薇,薇亦刚止。曰归曰归,岁亦阳止。王事靡盬,不遑启处。忧心孔疚,我行不来!\& p. @7 r4 l& e: x7 g) |
- 彼尔维何?维常之华。彼路斯何?君子之车。戎车既驾,四牡业业。岂敢定居?一月三捷。\; K# v; _# R) q
- 驾彼四牡,四牡骙骙。君子所依,小人所腓。四牡翼翼,象弭鱼服。岂不日戒?猃狁孔棘!\
5 A1 U4 W* y0 r& |( K - 昔我往矣,杨柳依依。今我来思,雨雪霏霏。行道迟迟,载渴载饥。我心伤悲,莫知我哀!";5 T B& f4 m% e7 } a' H
- uint8_t Rx_Buffer[BufferSize];
* i: I- {" y' q' [, g% l9 E
复制代码- /*比较函数,用于比较写入和读取的数据是否一致*/
& A0 W, }( k7 k6 c0 U! X/ z& l - uint8_t Buffercmp(uint8_t* pBuffer1, uint8_t* pBuffer2, uint16_t BufferLength). l) j' z0 ^: `# W
- {9 A( R& z& G5 ^, @! V' `
- while(BufferLength--)
8 I8 f/ I% _/ c" O, H - {8 V3 T8 R( e4 s1 s& C$ z
- if(*pBuffer1 != *pBuffer2)$ `, S# ?0 K# C& k+ i
- {
6 U, |1 ]; _( Z& n9 |, t, e - return 0;5 s6 l5 z+ L: L/ W
- }
/ s4 L$ Q. \% Y7 e l
6 E8 f, e3 R9 C- i7 ?( `9 r3 k- pBuffer1++;
9 I4 h9 T5 L; Q8 X - pBuffer2++;5 d( f* I! O4 U& k
- }% k* D, h8 ] G9 h7 q, B, y- S7 Q* U
- return 1;6 h3 a- n8 a8 ^, _4 S
- }
9 O7 L/ m: C- P' @1 |* } - /*在主函数编写测试代码:ID验证,读写比较*/
, x' p% e% C3 N' i3 E - Flash_ID=SPI_FLASH_ReadID();//读取Flash ID
; I7 n1 `6 j9 y" i. e - if (Flash_ID == _Flash_ID) //判断读取的ID和数据手册的ID是否一致7 m! v9 s6 z2 Z8 b( w
- { ( i" q7 J& n1 u0 ^0 I* c
- printf("\r\n系统检测到SPI FLASH W25Q64 ! FlashID:0x%X\r\n",Flash_ID);6 S! r7 {4 |6 P6 ^* t4 h# Z
-
O% C F& x) y. U& k8 H) d - /* 擦除将要写入的 SPI FLASH 扇区,FLASH写入前要先擦除 */
2 n( P: v, \3 L$ r3 P" Z - SPI_FLASH_SectorErase(FLASH_SectorToErase); 8 o$ `- U5 ?* j: Z
-
* { U2 {3 P; a$ B - /* 将发送缓冲区的数据写到flash中 *///
* y& D7 c u. [0 J) U' C - SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);; g* R8 M/ V" E3 S
- printf("\r\n写入的数据为:\r\n%s", Tx_Buffer);" x8 j) @/ V# k4 A, |6 l, A8 d
-
0 h8 v+ L8 ~, S8 s5 W - /* 将刚刚写入的数据读出来放到接收缓冲区中 */5 ]# ~& ^+ b1 g" i
- SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);! A0 R$ A- R& i6 N
- printf("\r\n读出的数据为:\r\n%s", Rx_Buffer);
7 \% h5 q. E5 \7 f, z -
# E! e! t, R3 H: {! g& K. I - /* 检查写入的数据与读出的数据是否相等 */
! d! `& \) Y3 g" @1 ?1 n, [ - CMP_RES= Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);7 c0 R) n: z6 H/ h; C; `! n1 X3 d, T9 f
- 9 {) S- b5 X/ V6 g6 M9 S
- if(CMP_RES)/ m0 W7 Z1 l: T6 p2 o- `3 G0 P
- { , q, n1 [$ {! Y# d; Z3 j
- printf("\r\n16M串行flash(W25Q64)测试成功!\n\r");
* S8 ^3 q* H+ z6 y - }
; e3 H I( V! B- W, [2 g - else) |# D" T- J7 l$ ]4 s: o
- {
4 i" u9 U# N& `; h; ~" S! C3 [ - printf("\r\n16M串行flash(W25Q64)测试失败!\n\r");* v0 O4 t( S" @
- }) M5 N( B' e! ^: ?! P% [5 `% N
- }// if (FlashID == sFLASH_ID)' J4 n% i, O) @' u
- else
% ] _" s, P: e. a1 Y - { ' L, w' o: T: G" J) n
- printf("\r\n获取不到 W25Q64 ID!\n\r");
: m* I) _- ~/ ^% Y7 k, y: `8 ~ - }
复制代码
2 E) G- e6 P4 n( H" `2 J- p; P9 m) m, a9 c
调试过程碰到几个问题,也是比较无脑的。( q% G! B; L6 |& N
1、编写页写入的时候,常规流程:写使能->NSS拉低->发送页编程指令->发送地址->写入数据。
0 p7 ^9 f3 C& P. }6 D& O我忘记写发送页编程指令,导致了每次读取的时候,数据前面总是多出一些空白。(数据内容:; F" {5 _+ ?5 R- F& q7 W
空格(发送内容越多,空格越多)+数据)。刚开始在论坛看到了,一个网友也出现类似情况,评论区,都是说等待函数加个延时就可以了,试了下还是不行,后来才发现是指令忘记写了。* @; e$ D5 Y3 X* j1 A. s
0 C8 h, R; s/ h; L( c
- HAL_SPI_Transmit(&hspi1,&W25X_PageProgram,1,10);//发送指令
复制代码
! r( P) r) b; s! @# J0 g2、还出现一个问题,读取大量数据的时候(超过一页),后面的内容总是读不全。刚开始以为是自己本身写入就有问题,后来用了例程读取我写入的数据,可以正常读取。基本可以锁定是读取函数的问题,看了一下整个读取的流程没有问题,唯一需要更改就是库函数自带的接收函数的参数:超时时间的设置。果然把原先设置的1改成10就可以正常读写了。(因为这个原因,我索性把发送的超时时间也都设置为10,直接设置为1也会正常的就是了。)" s1 G4 p7 x0 _% \( Y
- r7 e* Y% _/ X
- HAL_SPI_Receive(&hspi1,pBuffer,NumByteToWrite, 10);
复制代码 1 u. d4 m( V0 x) z& j
3 `( p ^/ p3 o: a1 @
: L) T) M/ K9 H7 @+ Z! N8 ?* B: G |