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