STMCubeMx入门教程(6) SPI 读写FLAH的应用 导语“本教程将使用CubeMX初始化SPI,使用SPI对W25Q64 FLASH进行读写操作,通过HAL库的读写应用来数据FLASH的操作细节。” 01 ------------ 系统要求----------------- •硬件 野火指南者开发板 •软件 CubeMx & MDK & 串口调试助手 •原理图 根据原理图,我们看到FLASH连接在SPI1上,我们不适用SPI1自带片选,使用PC0开进行软件片选。 02 ------第二节 CubeMx配置----------------- (1) 我们还是使用前面的USART串口(串口的配置没有变化)项目,在此基础上进行SPI1 的配置: 我们从配置的信息上看,使用了SPI1,主从全双工模式,8位数据传输,高字节在前,模式3,CRC不校验,软件控制。SPI的模式配置看下图,采样时刻是偶数边沿所以CLOCK Phase =2: (2) 完成片选信号的GPIO配置 完成上述配置后点击代码生成。 03 ------------第三节MDK 配置-------------- 将CubeMx生成的代码使用MDK打开进行应用代码编写: 在spi.h 中进行FLASH操作的指令宏定义: - //指令表5 a6 |8 R8 }& [
- #define W25X_WriteEnable 0x06
. H' C: K) Y. @+ W - #define W25X_WriteDisable 0x04 - M9 t7 |1 g/ w3 \8 p& P6 O. x
- #define W25X_ReadStatusReg 0x05
$ N8 P0 |2 y) F3 Y- q' N& ~9 A. ` - #define W25X_WriteStatusReg 0x01
9 W6 q( d8 G4 T6 q& Q3 A - #define W25X_ReadData 0x03 & T5 z( I! U& ^7 A) L% E0 L, b
- #define W25X_FastReadData 0x0B
2 Y) u3 V+ z3 |) J6 q0 ~- i - #define W25X_FastReadDual 0x3B
. o, @( w8 A) ]- r) L" Q6 M0 o& C - #define W25X_PageProgram 0x02
- t5 Y! @: v7 D5 T; I& u - #define W25X_BlockErase 0xD8 H, N# q0 u3 B3 B; }; G; d
- #define W25X_SectorErase 0x20 ' D* V$ r& i$ A: }7 E+ w
- #define W25X_ChipErase 0xC7 * p# w4 @6 \) E( U: h) Q# v4 Q* x: [
- #define W25X_PowerDown 0xB9
+ X* T" l4 i! y$ v6 E: \ - #define W25X_ReleasePowerDown 0xAB - B+ m+ u: U' F) O8 g8 ~9 ?" U
- #define W25X_DeviceID xAB " |' p2 Q3 w M7 K7 ]. `* d
- #define W25X_ManufactDeviceID 0x90 ) Z* {3 m+ N" W, }
- #define W25X_JedecDeviceID 0x9F5 S: |% S$ F) }4 {' Y
- // others defined) p6 j! C; i( D
- #define sFLASH_ID 0XEF4017
7 [" Z5 T2 Y& M8 _8 R/ q2 b$ T( k - #define Dummy_Byte 0XFF6 O+ A t& v1 A* e8 b
- #define SPI_FLASH_PageSize 256
& u* J5 w E6 i+ O A5 @4 ^ - #define SPI_FLASH_PerWritePageSize 256/ |9 Y. N* R) u, F+ S1 P+ g5 o% J
- #define SPI1_TIME_OUT
复制代码 , }; I( E$ Q$ i# j; r$ x) S1 W, q; Q
并且申明应用的操作函数: - void SPI_FLASH_SectorErase(uint32_t SectorAddr);
) X* q& j) s' T* ?0 V: @4 V - uint32_t SPI_Flash_ReadID(void);
: H6 q- T3 F" x: O+ N - void SPI_Flash_Erase_Chip(void);
# c8 m* f9 b; ^ - void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer);' _, K! A& D$ L4 |& }( }
- void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);6 Y `, r) D5 R$ D
- void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
- Y0 x& @$ l$ I5 a4 |1 T/ f! c
复制代码
7 U, g. m: O4 Z2 ~1 f. d3 u下面我们在spi.c 中实现读写FLASH的相关函数: (1) 对片选的信号引脚进行宏定义操作: - #define SPI_FLASH_CS_H() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET)
j9 C, ~6 N7 ^) J3 A/ E - #define SPI_FLASH_CS_L() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码 + z0 ~4 g: O' u: F; V" F
根据原理图使用的PC0引脚 (2)SPI 读写个字节函数 - /**function: SPI 读一个数据**/
A" c: }% Q+ e7 `' w - uint8_t SPI1_ReadByte(void)
, R# ?0 e# X4 P+ G2 a2 z - { ( n% v$ l& ]9 g% ]! j
- uint8_t RxData;
1 T+ q6 B& O6 J! i - 1 V8 |; ~# b7 T/ s' W2 n
- HAL_SPI_Receive(&hspi1, &RxData, 1, SPI1_TIME_OUT);: g+ v/ N/ y3 ?" H3 C
- 4 F0 M( k( V. l2 @
- return RxData; //返回通过SPIx接收的数据
W# m& Y/ {. l( x, E - }
复制代码 ; F* K( M6 C9 `3 @& q; S; ^9 N
0 ^' S5 P/ X+ [6 |9 W1 S3 N我们使用了HAL封装的HALSPIReceive(&hspi1,&RxData, 1, SPI1TIMEOUT)函数来实现读一个字节。 (3)写一个字节 - /**function: SPI 写一个数据**/
4 D# s B+ k3 r4 E' P8 h$ i0 b: S - void SPI1_WriteByte(uint8_t TxData)
7 h4 l K" @: c1 Z0 z2 {+ }7 H - { 2 }9 M& S+ e( ?- n! z
- HAL_SPI_Transmit(&hspi1, &TxData, 1, SPI1_TIME_OUT); //通过外设SPIx发送一个数据 1 C6 L" R- m9 O, f1 H: W4 G0 V
- }
复制代码 (4)FLASH的写使能和非使能 - /**function: SPI_FLASH写使能,将WEL置位**/7 t/ z# \4 C5 h0 a# t( D
- void SPI_FLASH_Write_Enable(void)
2 c7 }* @& f; s% o! a" i, H; ]2 b# ] Y- S - {
( {- M3 V7 p8 H$ j+ i. @/ G - SPI_FLASH_CS_L(); //使能器件 % {& K* Y8 A8 H) S1 t) v3 i
- SPI1_WriteByte(W25X_WriteEnable); //发送写使能
7 B& q$ ~6 f, ^( p) E - SPI_FLASH_CS_H(); //取消片选
3 _( G& g% J$ {$ K - } 2 @$ Z: I- h u# a: ]- D q
% M& g& X5 E9 f# m9 a# G- /**function: SPI_FLASH写禁止,将WEL清零**/9 d* J2 L B0 b% K( O
- void SPI_FLASH_Write_Disable(void) / e6 F: v0 U9 @
- {
n; z- N9 w* k: f( w - SPI_FLASH_CS_L(); //使能器件 c6 F d% \8 Z" X: s1 d
- SPI1_WriteByte(W25X_WriteDisable); //发送写禁止指令
7 Z* X) \9 F$ Y# s; P7 m# ?* R @2 _ - SPI_FLASH_CS_H(); //取消片选
& p' S; d8 `$ I" b( z* S - }
复制代码 $ q: n6 Y e; S. S4 r5 r0 e
(5) 读取SPI_FLASH的状态寄存器,通过这个函数我们可以判断FLASH的状态,一般用到的忙和空闲两种状态,这儿也实现了等待空闲函数。 - /**) l& U: H5 \$ t4 Y
- function: 读取SPI_FLASH的状态寄存器
) G3 T1 K& C4 f0 w( O6 a8 |% i: j* V - **/: y2 {9 J- I# e3 V) G2 M- @
- //- S3 w7 S3 B0 \0 d, u
- //BIT7 6 5 4 3 2 1 0) y8 S+ }3 \: ?# g, f# e6 a# K
- //SPR RV TB BP2 BP1 BP0 WEL BUSY
/ z' n9 e2 e( G5 w) Y( s% N6 i - //SPR:默认0,状态寄存器保护位,配合WP使用" ^ c9 J& M- \5 A6 A: }- H
- //TB,BP2,BP1,BP0:FLASH区域写保护设置
0 \- E" R9 @4 J/ [/ t& A6 [6 i+ N - //WEL:写使能锁定
' z4 l; v% u9 D4 h - //BUSY:忙标记位(1,忙;0,空闲)$ t. S$ [$ R5 ^6 `4 ]
- //默认:0x00
" ~% ^+ d$ e7 Z% u4 g - uint8_t SPI_Flash_ReadSR(void) 9 C0 T' _! [2 s7 A
- {
R, D e/ ^5 R _ - uint8_t byte=0;
5 C5 M9 R* L8 G( L - SPI_FLASH_CS_L(); //使能器件 ) A+ H3 c& A) s/ ^8 y# J6 h
- SPI1_WriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 ; N7 ~) {; A/ v/ {; M
- byte = SPI1_ReadByte(); //读取一个字节
+ N- e B6 X; X) H4 J - SPI_FLASH_CS_H(); //取消片选 , s- E: g. A" w5 ?6 b; f
- return byte;
% K' [& H! n1 d5 B1 I# u+ O - }
# K) B) M& ^1 ]8 b! e/ r; {9 [
) T: y- Z# I2 W+ n: ?; B- /**
1 n; k8 _0 f" a0 L# q - function: 等待空闲
# p( J8 U: \0 O" @$ w - **/$ q9 X" L/ }/ o5 H
- void SPI_Flash_Wait_Busy(void) 7 D! c) I8 y# x) \' T' ?* Z& E* q6 A
- {
7 g9 e0 q3 Q9 V% _; N: b" n - while ((SPI_Flash_ReadSR()&0x01)==0x01); // 等待BUSY位清空8 `/ R1 ^# Y, W2 n% Q
- }
复制代码 (6)读取芯片ID W25Q64的ID函数,这个函数也是一般判断是否FLASH正常的方式。查看手册可以知道ID是0XEF4017 - /**
) l) p& Q* ], [, `9 m+ Z# V* b - function: 读取芯片ID W25Q64的ID
+ z v1 w9 f S2 c; z - **/
9 B& W$ c; X( t% y! k - uint32_t SPI_Flash_ReadID(void)+ J; c& Y6 T! l6 Y, f9 W
- {
A$ v' x+ P, M0 T# A! n - uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;
8 C9 M; e" A/ N8 y. w! t& u - SPI_FLASH_Write_Enable();
?; l+ x+ D3 E9 Y' c) {9 _ - SPI_FLASH_CS_L(); 7 L1 j9 A' k/ L9 z
- SPI1_WriteByte(W25X_JedecDeviceID);//发送读取ID命令
' l* u' e) n3 H) q4 R& I. W - Temp0 = SPI1_ReadByte(); + v) I! k! G8 S' J' U2 C) @
- Temp1 = SPI1_ReadByte();
% p0 S8 }" Q1 U, d6 c G: L - Temp2 = SPI1_ReadByte(); . ?2 V6 G& Y/ B7 x- U6 L4 B
- /*把数据组合起来,作为函数的返回值*/+ w% p5 p. f' N% k+ A0 K
- Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;2 D4 M5 l) g; w3 s
- SPI_FLASH_CS_H(); . }1 N- h6 i* X$ o) D; x" K
- return Temp;
0 Y) d+ p1 J" R. h( Q: f4 @ - }
复制代码
0 p6 _& N) R, j. s$ t6 f" S(7) flash 扇区擦除函数,FLASH和EEPROM不同,在写之前一定要进行擦除,否则不能正常写入。这儿实现了扇区擦除和正片擦除两个函数: - /**
+ E4 p! L% w8 m+ C) w - function: 擦除整个芯片+ C. N# _6 H: I& T( v0 M
- **/
* r% V% h4 ~* O9 }) | - //整片擦除时间:4 L/ a0 h7 m9 t+ G3 c' O5 W3 ^, f
- //W25X16:25s
* N! C: }' |8 T9 N9 `; G! t+ H, K - //W25X32:40s 8 @0 v# B) Q) @" K$ C7 L# |% Y) I
- //W25X64:40s ! d# I2 z: I2 }4 Q/ R) E, U
- //等待时间超长...
8 ?2 s- ?8 c7 Q, ]3 Y' w6 \ - void SPI_Flash_Erase_Chip(void) ; G, `" M. F B4 x1 c/ q
- {
( B! C, O5 _7 g - SPI_FLASH_Write_Enable(); //SET WEL ; `, N' Y8 M9 \* e _
- SPI_Flash_Wait_Busy(); 8 l9 F! F* @/ q2 N% G
- SPI_FLASH_CS_L(); //使能器件
' G7 A, i: `* n) _& ~8 j! x - SPI1_WriteByte(W25X_ChipErase); //发送片擦除命令
/ @# S O4 m/ L" Q* q4 l - SPI_FLASH_CS_H(); //取消片选
5 H8 _ C0 T( Z+ `7 B - SPI_Flash_Wait_Busy(); //等待芯片擦除结束
! b! @4 [, V7 [# Q. g - } : C t& k" O1 b. T# O
- /**
4 b4 F; l$ b- {1 h. t- { - function: SPI_FLASH_SectorErase
* Q5 V! S0 [& {% v3 P) I) W& Y8 l - annotation:Flash的一个扇区是4K,所以输入的地址要和4K对其。
) R- \1 g; l: Q0 q0 `% _6 o - **/6 E* C' k$ E/ c
- void SPI_FLASH_SectorErase(uint32_t SectorAddr)& }: a$ P7 P) V0 ^7 O/ t3 G- o% f
- {' u2 e+ B# N/ f
- SPI_FLASH_Write_Enable();
6 ]# X, _! t- L# q' W - SPI_Flash_Wait_Busy(); 9 k' B( V% p5 ^9 Y* c
- SPI_FLASH_CS_L(); * a' ?0 M- a7 M2 ~
- SPI1_WriteByte(W25X_SectorErase);
5 t- Q( G( e7 H - SPI1_WriteByte((SectorAddr & 0xFF0000) >> 16); # l9 D" I+ X9 b$ S9 A4 ?+ W
- SPI1_WriteByte((SectorAddr & 0xFF00) >> 8);
" b+ s V8 K c5 |0 f9 R$ d4 O' ` - SPI1_WriteByte(SectorAddr & 0xFF);# A0 l9 ^# j4 i" q
- SPI_FLASH_CS_H(); 8 [. W, Y! U: l/ T ^2 x/ H' _- c
- SPI_Flash_Wait_Busy();
: T) u0 b. y2 z$ d - }
复制代码 (8) 读取FLASH函数,FLASH的读操作步骤很简单 - /**
8 B: o \/ u* }' \ - function: 读取SPI FLASH % }- `% V# j- x6 H0 ^
- **/) V4 z$ l# K+ j) }2 e
- //在指定地址开始读取指定长度的数据
" }1 U& U- G/ u3 k) L N - //pBuffer:数据存储区: ~: W3 Y7 ]( y* M9 I" `
- //ReadAddr:开始读取的地址(24bit)
7 P* r* e7 J1 ?; X0 T( V - //NumByteToRead:要读取的字节数(最大65535)# M! h: k, z6 K/ b i8 V
- void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer)
, E. b% L5 a- t, }, x - {
6 U. `" k$ V8 g: F, F/ o( } - uint16_t i; $ g8 U8 O* o. R7 x' r# d' a9 F& D/ g5 O6 S
- SPI_FLASH_CS_L(); //使能器件
8 L P) V! Q. Y2 W' I4 Z - SPI1_WriteByte(W25X_ReadData); //发送读取命令
7 v4 c2 F4 [8 T+ C - SPI1_WriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
; C$ R2 k$ j: _ - SPI1_WriteByte((uint8_t)((ReadAddr)>>8));
; H: }% u% S4 d; K7 G& F - SPI1_WriteByte((uint8_t)ReadAddr);: E" F7 V3 d1 {' m) s/ v1 i3 R0 c. w
- for(i=0;i
; L3 j9 g' X$ n; b( p - {
, [( A% t! \) {0 N# a - pBuffer[i] = SPI1_ReadByte(); //循环读数 , x) w0 z* x( N0 s
- } L0 o" o& {: i- w& {! f# P
- SPI_FLASH_CS_H(); //取消片选
0 F8 E4 P) f! N3 N/ ^8 i/ @ - }
复制代码
$ q1 S3 e e; J# Z3 g (9) flash 的按页写入数据,FLASH的数据的写入不是随机可以任意写入,按页写入,一页最大的数据是256个字节。 - /**
/ h P7 M t# M8 k% t- t( p - function: SPI在一页(0~65535)内写入少于256个字节的数据
% e, r: H# ?: b - annotation:一页最大256个字节$ V. }/ b! g; I6 v p3 M/ n4 V
- **/
' ` r+ D; s, m) ` - //在指定地址开始写入最大256字节的数据5 a" h) k/ t$ Y1 Z/ c3 R6 k4 q, k
- //pBuffer:数据存储区
1 |6 Y& P Q M$ _, l4 A' S - //WriteAddr:开始写入的地址(24bit)
8 d9 Z! H5 W% z& \ - //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
, r& l8 K# K, U - void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer) Z, l1 V4 G+ i
- {
4 x3 u0 Q }7 R$ S7 K4 Z, p - uint16_t i;- p9 w; a3 f/ _$ X$ j4 k
- SPI_FLASH_Write_Enable(); //SET WEL
+ F4 j+ l. j( j3 W - SPI_FLASH_CS_L(); //使能器件) q: ~2 z: `2 [9 b4 A L
- SPI1_WriteByte(W25X_PageProgram); //发送写页命令
; r( H# z6 W/ z4 m( S& y F - SPI1_WriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
, C; g8 G/ T5 @5 ^ - SPI1_WriteByte((uint8_t)((WriteAddr)>>8));/ e+ T3 G- g+ }9 n
- SPI1_WriteByte((uint8_t)WriteAddr);
K$ |: b1 W9 o2 G/ _1 m. ? - for(i=0;i<NumByteToWrite;i++) SPI1_WriteByte(pBuffer[i]); //循环写数
- \1 ~- X4 V% }- x, s- R+ K - SPI_FLASH_CS_H(); //取消片选 . g0 {( s8 b0 W% ?$ P v/ a
- SPI_Flash_Wait_Busy(); //等待写入结束 4 a% O/ ~9 Z- i1 u! R" b, a* C
- }
复制代码
/ z+ ^; J# M$ `7 ?1 Z( k0 j(10)flash 的随机的多字节写入,通过按页写入实现的函数来实现 - /**2 b- q% w+ t* Z2 d0 {
- @function 不定量的写入数据,先确保写入前擦出扇区
! q; h: [- }6 [0 q- T3 H - @annotation
9 r6 {4 ~* Z2 k* r* m - @param, V' I0 O( f4 X8 o. q9 i
- @retval
7 G0 D3 J7 t5 i+ B$ E* Z - **/
+ N7 y) b4 M) Y# _+ K7 f- a2 i( `/ w - void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)
' F2 ^1 L* M. m0 P4 r - {' |3 E; E: K+ x1 r( g; ~: \
- uint8_t NumOfPage =0, NumOfSingle=0, Addr =0, count =0, temp =0;" E/ J4 V5 H3 l# Z& K
- /*计算Addr,写入的地址是否和PageSize对齐*/
, U4 V4 S% ^" i1 T" A' E - Addr = WriteAddr % SPI_FLASH_PageSize;$ O) Q! v# S5 G$ F) U5 ^/ N0 B
- /*count 为剩余的地址*/! i2 q2 k" O! i/ I1 M$ g6 A3 {
- count = SPI_FLASH_PageSize - Addr;
3 X' W x, ~' s+ R5 X( ]( m9 D3 v - /*计算能写入多少整数页*/7 Y. }! M n( z. f1 w
- NumOfPage = NumByteToWrite % SPI_FLASH_PageSize;
3 W% {5 i5 H( x% h - /*计算不满一页的数据*/
) S6 t; Q! y. K: `7 V& k0 i - NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;6 f: u! a3 c7 M Y: R$ N9 Y
- /* Addr=0,则 WriteAddr 刚好按页对齐 aligned */% H' }. C/ g6 y% q
- if(Addr == 0)" c; j/ f/ G' x
- {* V- B4 l% Z% G+ M3 @# [3 s
- /*NumByteToWrite < SPI_FLASH_PageSize,一页能写完*/
0 E- r T0 A9 t& }/ G1 ]6 p* \ - if(NumOfPage == 0)
- b/ V" A+ c" P8 L( B - {
( e" O& d* {- y1 z n" | - SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);1 R7 f, M7 |0 l ]7 M! X
- }
) D$ C0 ?# e/ _ - else/* NumByteToWrite > SPI_FLASH_PageSize,一页写不完,先写整数页,在写剩下的 */% U% L2 d: [. y
- {. @) G* A; X. p/ }0 L( Z, B4 Z
- /*先把整数页都写了*/! e* }7 v" Z, g: p4 }- ?, d( ~1 y
- while (NumOfPage--)8 J( u! r1 P: R' u/ y/ F& Q0 z4 c1 X
- {; ^0 [ J w- D8 a2 z
- SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);1 o/ M4 P. P% R% r1 u$ k! M0 g9 m
- WriteAddr+=SPI_FLASH_PageSize; // flash 的地址加一页的大小" @% n8 |1 w+ N5 g( M0 G
- pBuffer+=SPI_FLASH_PageSize; // 写缓存数据的地址加一页的大小
- g4 t1 L9 E+ `: B$ r5 W$ T - }0 l" ? U$ p% j/ W$ n
- /*若有多余的不满一页的数据,把它写完*/& R3 z# g7 `4 ^, \5 h" w8 y
- SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);+ ~) o/ [& U1 R! H
- }
8 k1 d5 V, w5 V/ W - }
3 n& G, c# {; F- P) v - else/* 若地址与 SPI_FLASH_PageSize 不对齐 */* t, ?, N& r4 L: A
- {/ @/ p$ [. U" {. I' T/ F$ \
- /* NumByteToWrite < SPI_FLASH_PageSize */
+ c4 j; Z) D4 g: N7 ? - if (NumOfPage == 0)8 Z& c8 \% s( q$ G5 J |
- {7 Z1 Z4 j+ v3 A* @1 O, u
- /*当前页剩余的 count 个位置比 NumOfSingle 小,一页写不完*/
! [% s! [6 S+ p& o - if(NumOfSingle >count)
- P) V+ W8 i) K; ]" W6 Y - {
9 o+ F* ]- e( g1 D - temp = NumOfSingle -count;
' @( u2 y+ l; m2 w! _# ^ - /*先写满当前页*/; L7 K8 c7 C6 m9 p
- SPI_Flash_Write_Page(WriteAddr,count,pBuffer);8 ~$ |5 @# @7 L4 f
- WriteAddr += count;: [ \2 n' R) c9 T( q0 q8 w& q: A
- pBuffer += count;4 p2 B4 \" K3 Y3 t
- /*再写剩余的数据*/8 G$ @- o( h" R- Q0 q
- SPI_Flash_Write_Page(WriteAddr,temp,pBuffer);
! x( l! n6 z6 k5 v0 E( y. K5 k - }# s6 k4 A& y9 ?5 j/ F% x* V# N1 j
- else/*当前页剩余的 count 个位置能写完 NumOfSingle 个数据*/
! Q3 }; O1 E# N* X- u - {
$ t3 n4 S3 Z/ J4 X - SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);2 x5 C6 C P* k$ {+ a& x
- }
" a/ G) k0 K$ s/ }' } - }
. h Y/ b( Q7 @ M2 \; [ - else/* NumByteToWrite > SPI_FLASH_PageSize */
5 Q, u4 _* S0 Z: g* X g - {
) m, W! O1 \4 N' S1 H* Y1 Y - /*地址不对齐多出的 count 分开处理,不加入这个运算*/: m% w; q1 c8 _/ Q+ i% I
- NumByteToWrite -= count;
# _! u$ J {8 @% F% z/ ] - NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;4 f$ S4 p t5 ? |# |& [, M3 R
- NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;6 J6 S6 o0 h# h6 d* {6 i/ l, b% F
- /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */ Z5 Z( ~4 c: r9 t" o- v/ W! D
- SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
2 r7 ^4 M$ n: D) i( n5 ~ - /* 接下来就重复地址对齐的情况 */* P3 `# P4 ]; b1 g
- WriteAddr += count;
. }4 l' E8 t& Q2 q3 Q - pBuffer += count;
1 H7 \4 @9 _1 K' ^6 g - /*把整数页都写了*/
0 Q' Y% x# t7 h# T& l - while (NumOfPage--)
4 M/ I! Q. X/ { - {
% r7 }! V E# `: \5 T9 n0 M4 Q9 C. ] - SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
- T1 N3 X2 ~: h. O9 i - WriteAddr += SPI_FLASH_PageSize;
5 q' S9 c8 ]5 Z! v7 E5 w2 a - pBuffer += SPI_FLASH_PageSize;
; y- {# c1 ?. Y+ Y, R6 R/ h% _0 ^ - }
0 u1 ~* Q' s# i6 E - /*若有多余的不满一页的数据,把它写完*/* {$ H# H2 q0 W' E3 U
- if (NumOfSingle != 0)
& ]2 y- l0 E8 Z4 r6 v7 l - {
: k3 N# ?9 c8 o3 d( g& c& j - SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);
7 m$ R0 n/ @' G8 q n, y9 k - }
0 |) I) Z, H- o6 c3 j9 d. ] - }
. ^# n0 @$ a ]; H - } 2 n5 b' N/ B4 b1 f# S7 r5 H
- }
复制代码
. J A9 Q: A! a4 W$ x通过上面的函数实现,我们就可以基本的来完成FLASH的读写操作了。有些函数参考了野火标准库的操作步骤。 在main.c 的主函数中对FLASH的读写地址进行宏定义: - #define FLASH_WriteAddress 0x00000
* P5 H" C* ?9 N0 |/ A4 |# I - #define FLASH_ReadAddress FLASH_WriteAddress! s4 E+ n) H5 d8 h# T0 I1 F
- #define FLASH_SectorToErase FLASH_WriteAddress: S$ J" ~5 I$ x4 H @- _# w4 C0 d
- #define FLASH_SPI hspi1
复制代码
5 p% P: r I+ ?% c1 p$ z% c 在mian函数中定义变量: - uint32_t flash_ID = 0;% L* b0 B0 Y1 W y; ?) p. x- m7 l
- /* 获取缓冲区的长度 */. \8 I0 m/ z. h3 T: v' ?
- #define countof(a) (sizeof(a) / sizeof(*(a)))% v4 n+ |6 s2 h
- uint8_t Tx_Buffer[] = "现在进行FLASH的读写测试\r\n";; |$ f& }$ p; U8 ? u; g( U
- #define BufferSize (countof(Tx_Buffer)-1)
, W' ]& _6 R. I3 N; i2 v; G; u3 s - uint8_t Rx_Buffer[BufferSize];
复制代码 1 O7 j& R4 Y" U( D! E" P
在main函数中进行测试代码: - printf("*************this is test for coding...**********\t\n");3 d; |6 Q) s1 [! s
- printf("this is test code for spi1 read and write flash w25Q64 \r\n");" Y, J- o4 T# t5 a$ I" }
- flash_ID = SPI_Flash_ReadID();; A' M) W& [; G
- printf("\r\n flash ID is 0X%x\r\n",flash_ID);
/ ]0 g' F8 h9 f - SPI_FLASH_SectorErase(FLASH_SectorToErase);
& s" z6 a' T4 e4 E8 W - SPI_Flash_Write(FLASH_WriteAddress,BufferSize,Tx_Buffer);
! B. x# N3 v0 b( Z - printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);9 d: J [- n4 u H( E+ C" ~
- SPI_Flash_Read(FLASH_ReadAddress,BufferSize,Rx_Buffer);
5 D. }/ Y7 g& w% n( ^' s - printf("\r\n 读的数据为:%s \r\t", Rx_Buffer);
: u( R8 V7 y: D& V0 B6 I - /* 检查写入的数据与读出的数据是否相等 */
3 I; T( W7 z/ Z# Y$ F( [ S; X U) x - if(memcmp(Tx_Buffer, Rx_Buffer, BufferSize)==0)
; q( P/ {# ]$ z# W5 [* l, u8 z5 C - {
& |. s* U# H. P& b& u - printf("写入的和读出的数据是正常的!\r\n");6 @' E Q% [- D8 _; L# y" J! B* ?
- }9 h o2 s3 ^* K; Q* N
- printf("SPI 试验结束......!\r\n");
8 a: r( C2 g) k# |6 s - printf("*************this is test end....**********\t\n");& M# h' _+ _( ^8 r0 G$ N% {
复制代码
# f$ @) c9 l( O' c( H0 \$ E0 C04 ------------第四节 效果演示-------------- 我是使用串口将写入FLASH的数据进行读出来,比较数据是否一致,同时独处FLASH的ID看是否一致: 我们可以可以看到FLASH的ID是0xef4017和产品手册中W25Q64的ID是一致的,读写测试也是正常的,我们使用了C库函数,需要包含头文件<stdio.h>。 文章出处: 小鸟的早晨 |