|
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操作的指令宏定义: - //指令表7 w% U. w# J" G: h
- #define W25X_WriteEnable 0x06 8 H9 `0 n0 I, l1 C% c' S8 Z0 N
- #define W25X_WriteDisable 0x04
' A* l- _6 T. y* w0 O4 I& A - #define W25X_ReadStatusReg 0x05 0 i( _+ o% K7 l% R a( x: \) A* f
- #define W25X_WriteStatusReg 0x01 $ x& q0 m: O* o8 k8 \4 d7 X
- #define W25X_ReadData 0x03 + I4 y, O h2 [ D, _; @& W" J
- #define W25X_FastReadData 0x0B
8 h4 n8 ~, r" H v. ^( x - #define W25X_FastReadDual 0x3B
3 K( T u/ w" l) k, @! S' J - #define W25X_PageProgram 0x02 2 W( |! y5 R3 ]1 h2 n2 {. @3 z' J& R
- #define W25X_BlockErase 0xD8 ) e0 Q+ ?+ f' g' [& w1 j5 Z
- #define W25X_SectorErase 0x20 ( V8 I! C: Q. r9 U1 c
- #define W25X_ChipErase 0xC7 7 R, U6 g2 ]1 [$ z( v2 ]9 v; |
- #define W25X_PowerDown 0xB9 + A$ t0 F1 w' X1 d) i
- #define W25X_ReleasePowerDown 0xAB
7 W7 g4 d( Y5 |5 Z! h0 t9 F - #define W25X_DeviceID xAB
/ o8 f7 P3 b- `9 H; d - #define W25X_ManufactDeviceID 0x90
4 I2 X( Y( G0 w - #define W25X_JedecDeviceID 0x9F
8 K" t' f' w$ ~; ?7 D - // others defined
( O! s2 T! u) d8 L6 y& \6 @ - #define sFLASH_ID 0XEF4017" e1 w& U# _4 t! X) M
- #define Dummy_Byte 0XFF
; g* E4 J- k# l, I' a: v - #define SPI_FLASH_PageSize 256$ W* i, F0 p) Z; h9 S
- #define SPI_FLASH_PerWritePageSize 256" W3 M5 ^% Z, D6 e; k
- #define SPI1_TIME_OUT
复制代码
" Y" `% U. \ W) C% m* J并且申明应用的操作函数: - void SPI_FLASH_SectorErase(uint32_t SectorAddr);$ y2 ]: C) \8 Z2 z3 s, t$ I
- uint32_t SPI_Flash_ReadID(void);
) {; X: f8 d4 Y( a% R: h2 t - void SPI_Flash_Erase_Chip(void);
$ [. C( N! E. x0 P1 W - void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer);
1 j! W8 }& R7 u( t - void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);0 S4 q& z4 K: s+ x) j
- void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer);
7 b1 x! ~* x+ s& h0 V9 d) Y1 M
复制代码
% o Z3 r, r$ W下面我们在spi.c 中实现读写FLASH的相关函数: (1) 对片选的信号引脚进行宏定义操作: - #define SPI_FLASH_CS_H() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_SET) $ p( J1 G/ X' m. {
- #define SPI_FLASH_CS_L() HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0, GPIO_PIN_RESET)
复制代码 * Y/ A1 V8 {2 p
根据原理图使用的PC0引脚 (2)SPI 读写个字节函数 - /**function: SPI 读一个数据**/
5 U+ w+ H- C3 i5 f. }9 N% W* L! [ - uint8_t SPI1_ReadByte(void)
% f+ L9 K5 {/ N4 d6 H - { , S, I! p* {& \0 @; a2 ^; [
- uint8_t RxData;
9 E- j; A' l4 i: c0 ^ -
4 S: g! _. ?) R* F8 u. |* m# O( x - HAL_SPI_Receive(&hspi1, &RxData, 1, SPI1_TIME_OUT);
3 M( T. p9 o: O# i0 e2 y5 n - - G S- u- g6 m# J: Q3 Y5 c
- return RxData; //返回通过SPIx接收的数据 8 h3 ?! |0 K. ^+ x; w2 z, G" Q+ c) K
- }
复制代码 ) t2 X5 l& _4 ?
8 Q* D# D6 u8 x* z+ n7 \' X我们使用了HAL封装的HALSPIReceive(&hspi1,&RxData, 1, SPI1TIMEOUT)函数来实现读一个字节。 (3)写一个字节 - /**function: SPI 写一个数据**/
. O3 Z) O+ X' V1 }, P - void SPI1_WriteByte(uint8_t TxData)) w7 m, C% K3 S
- {
% H; P; p9 h3 L+ E - HAL_SPI_Transmit(&hspi1, &TxData, 1, SPI1_TIME_OUT); //通过外设SPIx发送一个数据
# n$ B; _0 k% J7 N4 a4 I; Q+ g/ t - }
复制代码 (4)FLASH的写使能和非使能 - /**function: SPI_FLASH写使能,将WEL置位**/
+ B9 v2 G1 J6 k - void SPI_FLASH_Write_Enable(void) * G+ j0 E6 s" b4 \+ r" Y8 |3 N$ L
- {
9 t+ t; Q" N/ b+ b5 [ - SPI_FLASH_CS_L(); //使能器件 4 X8 z7 I* Y6 z- o/ G- b
- SPI1_WriteByte(W25X_WriteEnable); //发送写使能
& ~# p4 U2 n9 N. j0 B1 Y( r - SPI_FLASH_CS_H(); //取消片选 / j( `2 ]: J: l3 F& T1 q2 F7 N8 |
- } 2 w8 O2 f& _; V2 a; }' Q @
( H+ r9 U% G; v* F1 l: J2 I7 s5 |5 ?7 T- /**function: SPI_FLASH写禁止,将WEL清零**/3 o6 b. E, P( @ w3 x
- void SPI_FLASH_Write_Disable(void)
5 H; q/ x+ U$ h! \1 ^ - {
7 N) D* h' T/ ?# @/ z1 g I# L - SPI_FLASH_CS_L(); //使能器件 ( |. P5 B/ b" w, W- D5 o
- SPI1_WriteByte(W25X_WriteDisable); //发送写禁止指令
; p2 `; }: L$ j* c - SPI_FLASH_CS_H(); //取消片选
* i6 S& ?- P, D' Y1 d s. f% D - }
复制代码 2 u7 T& q) w+ F" w+ k* b
(5) 读取SPI_FLASH的状态寄存器,通过这个函数我们可以判断FLASH的状态,一般用到的忙和空闲两种状态,这儿也实现了等待空闲函数。 - /**
( t( c; a! z3 ~) k - function: 读取SPI_FLASH的状态寄存器
5 `' ]2 ^- O- f$ [2 O7 R - **/
! w6 M. ?6 \8 x4 j. x0 j3 I - //
4 K9 l1 |6 A, P8 n/ l - //BIT7 6 5 4 3 2 1 02 p# ?4 _! P& }# m% C5 c9 G
- //SPR RV TB BP2 BP1 BP0 WEL BUSY* N/ _& u6 @, p' t( H: v
- //SPR:默认0,状态寄存器保护位,配合WP使用& _$ {& o8 O; z* j+ ?
- //TB,BP2,BP1,BP0:FLASH区域写保护设置# O. W }, ]* _2 K* D0 n% \6 A# s
- //WEL:写使能锁定
' k+ S4 c$ Q+ s+ ]( U - //BUSY:忙标记位(1,忙;0,空闲)
$ w& y$ p5 g9 B5 _* j" f - //默认:0x00
9 x/ K& i3 {" l! c - uint8_t SPI_Flash_ReadSR(void) ; Q$ @2 @) K- `% K
- { 5 H1 y( l: P) E5 b
- uint8_t byte=0; ! G: w3 p6 G. m1 F+ R
- SPI_FLASH_CS_L(); //使能器件 `5 Z9 b# `; L: Z$ L9 O% U8 m* |7 \
- SPI1_WriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令 $ u6 E- U8 G. F n
- byte = SPI1_ReadByte(); //读取一个字节
3 U- n2 H- K6 d; r' L- ^ - SPI_FLASH_CS_H(); //取消片选 ( ? P3 [8 v( b
- return byte; . N6 ?; q% S& s9 n; e
- }
# j) t( j- U& m# ?, L7 J
6 z9 R" ^$ K3 M9 t! q5 [6 R- /**
a3 u6 n5 m2 ]; s: a0 [8 Q; v L" p - function: 等待空闲
$ } n2 m: j- Q0 ~5 m - **/
! w6 Q/ N0 q% v+ ^ - void SPI_Flash_Wait_Busy(void)
, x& {/ C w5 ?% N1 w, K% G+ ] - {
$ {' X* E* ]2 W1 |; B5 n, N - while ((SPI_Flash_ReadSR()&0x01)==0x01); // 等待BUSY位清空* _; @+ r% T- N1 d: E5 X4 O
- }
复制代码 (6)读取芯片ID W25Q64的ID函数,这个函数也是一般判断是否FLASH正常的方式。查看手册可以知道ID是0XEF4017 - /**! c) V% _+ Y/ ]9 o
- function: 读取芯片ID W25Q64的ID' {9 t& p. O9 ]
- **/
& S6 z6 f2 k# d8 E' I, O& d# L - uint32_t SPI_Flash_ReadID(void)
9 t: h" g; `# X1 a) S1 H0 f1 u' i" m - {
$ h1 M5 d5 m! ?% z$ y - uint32_t Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; - x I7 z1 T' K6 N
- SPI_FLASH_Write_Enable();
' O( D: i( Y* H1 n' d+ \ - SPI_FLASH_CS_L();
# [" N R: A9 c& ]; k; u+ g - SPI1_WriteByte(W25X_JedecDeviceID);//发送读取ID命令
y+ J% j5 o- I" s6 Q - Temp0 = SPI1_ReadByte();
/ I9 y8 }$ f7 \ - Temp1 = SPI1_ReadByte(); , x, `( i* Z) y( D, M
- Temp2 = SPI1_ReadByte(); + Q9 v& b, j0 ]- J4 ?) l
- /*把数据组合起来,作为函数的返回值*/
& H" B3 H1 V \* m/ D - Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2;
) r( j+ u8 v9 [4 K6 q+ j - SPI_FLASH_CS_H(); 9 E! a7 F& v8 I6 ^+ g
- return Temp;
2 U U$ D$ k2 g7 V5 M% W# D - }
复制代码 3 p) P4 ^5 A3 T
(7) flash 扇区擦除函数,FLASH和EEPROM不同,在写之前一定要进行擦除,否则不能正常写入。这儿实现了扇区擦除和正片擦除两个函数: - /**
k, w5 _' | j8 j" A; n6 \ - function: 擦除整个芯片# A$ }% W' x) n
- **/
- a: Q- m1 {5 {) U! y% E - //整片擦除时间:
' a/ b$ o, b8 R6 r8 E - //W25X16:25s 9 t& f. L0 c$ ^+ Q) U d, d% @, m
- //W25X32:40s ; m* u/ y8 m1 L" D, d% Z
- //W25X64:40s / g; l! X% L* C a7 v3 C# H1 Y5 F
- //等待时间超长...
/ G, P6 v( K9 \% n - void SPI_Flash_Erase_Chip(void)
' }5 W4 q1 p, I - { 2 N8 F% m' j1 O+ |% o
- SPI_FLASH_Write_Enable(); //SET WEL
' ]8 \/ y; U0 a2 w - SPI_Flash_Wait_Busy();
3 P" y# V# A: f* h: k - SPI_FLASH_CS_L(); //使能器件 7 o1 {. y* J ]4 H S: i
- SPI1_WriteByte(W25X_ChipErase); //发送片擦除命令 + j$ D# m' J* L+ ^" q; T3 E
- SPI_FLASH_CS_H(); //取消片选
- W+ ^7 W' d9 ^ - SPI_Flash_Wait_Busy(); //等待芯片擦除结束" v' `/ n* [0 q" t: G1 R
- }
7 k& o' _% b/ ~5 L) g9 W9 w - /**
/ ?5 c, S# A6 t1 a# s - function: SPI_FLASH_SectorErase
% D, C- F; z( w) u% A' o1 V# S& N - annotation:Flash的一个扇区是4K,所以输入的地址要和4K对其。5 u. x& f9 D$ }3 Z, r
- **/
1 M4 {: N" K- y z: W - void SPI_FLASH_SectorErase(uint32_t SectorAddr)& L6 c4 ]; n; H! h
- {
, N6 p/ g) R6 N) A- P/ l3 S3 U5 ]9 J - SPI_FLASH_Write_Enable();
5 V+ g! P4 O$ h1 V; V& q, d - SPI_Flash_Wait_Busy();
' v% W6 i2 D) Q7 f7 L% H$ O - SPI_FLASH_CS_L();
" [2 r; C: M$ p/ D/ E: B! t, D - SPI1_WriteByte(W25X_SectorErase);
3 h0 T& J& C+ O+ r6 T/ s) D - SPI1_WriteByte((SectorAddr & 0xFF0000) >> 16);
5 Q* w5 i8 y$ }" C! D) V% W- Z - SPI1_WriteByte((SectorAddr & 0xFF00) >> 8);
, ~3 K7 k6 R8 ~3 o7 D" ` - SPI1_WriteByte(SectorAddr & 0xFF);
/ K, {# @# J8 r" h8 |! a6 j8 k - SPI_FLASH_CS_H();
* w4 }/ F! o0 m: i6 ?! p* }( ]# C( t5 \ - SPI_Flash_Wait_Busy();
% z9 x$ |4 x; _; O( f. n - }
复制代码 (8) 读取FLASH函数,FLASH的读操作步骤很简单 - /**1 T+ k2 X( K8 _
- function: 读取SPI FLASH
: E* P1 B' o7 v - **/
1 H, z3 v% {/ U! ? - //在指定地址开始读取指定长度的数据 }$ P: K% Z% C7 v4 C5 c$ K
- //pBuffer:数据存储区 [; b8 ^+ J O' R
- //ReadAddr:开始读取的地址(24bit)
5 X9 J! M: x! C, W; Q% C: o - //NumByteToRead:要读取的字节数(最大65535)8 }1 Z H( R( H
- void SPI_Flash_Read(uint32_t ReadAddr,uint16_t NumByteToRead,uint8_t* pBuffer) ' I9 O! p; Q, d0 ^$ O. K7 f9 q
- { ; s1 T" b8 E; w& x8 i5 D, x
- uint16_t i; ( V0 W3 b8 p" _% S0 L7 t7 ~1 \
- SPI_FLASH_CS_L(); //使能器件
: m9 W; t+ {3 S0 C; L9 \& `, ]% r - SPI1_WriteByte(W25X_ReadData); //发送读取命令
/ t) S/ N3 h3 U" q6 m9 p - SPI1_WriteByte((uint8_t)((ReadAddr)>>16)); //发送24bit地址
) h) }& ~; D3 p4 l Y/ t - SPI1_WriteByte((uint8_t)((ReadAddr)>>8));; B( h2 ~: I, e0 r5 M: Z7 m
- SPI1_WriteByte((uint8_t)ReadAddr);& f2 Q/ ?4 q/ G) A- G# E; B
- for(i=0;i
2 K) S$ I4 \0 L# C2 B: r/ c( w - { 6 `7 y$ U' u, q( E
- pBuffer[i] = SPI1_ReadByte(); //循环读数 & U0 Z7 ~+ H5 l
- }
! @! |/ j, a/ J$ D3 n - SPI_FLASH_CS_H(); //取消片选* G1 p' Y: } y3 h' u; M
- }
复制代码
: u4 U/ u8 W8 b, u1 } (9) flash 的按页写入数据,FLASH的数据的写入不是随机可以任意写入,按页写入,一页最大的数据是256个字节。 - /** x6 w2 @& c, G( x
- function: SPI在一页(0~65535)内写入少于256个字节的数据
2 J8 W9 T& G6 k3 Z - annotation:一页最大256个字节' ~5 Y9 O" b! j6 {
- **/. y3 }8 n6 ?; k1 U6 u5 X
- //在指定地址开始写入最大256字节的数据
, p, o& e" e. l o% N8 X+ j - //pBuffer:数据存储区
$ X/ i, ^9 k! o6 c - //WriteAddr:开始写入的地址(24bit)
4 E; H, c( g o - //NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!8 ]: t" ~( q7 ]6 U" [# j4 h
- void SPI_Flash_Write_Page(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)# N* w( l5 E5 @: b+ K, }
- {
3 \: S0 P) y4 x9 o# j& Z - uint16_t i;* J3 k Z' ~: Q" q: Q9 r
- SPI_FLASH_Write_Enable(); //SET WEL9 Y4 y* X( x4 C
- SPI_FLASH_CS_L(); //使能器件
7 Y7 G! D( s9 I0 _" B, j - SPI1_WriteByte(W25X_PageProgram); //发送写页命令' ?4 ^$ V, |; Y' y P% Z
- SPI1_WriteByte((uint8_t)((WriteAddr)>>16)); //发送24bit地址
3 D* A$ Y2 G' ~- @' c4 \ - SPI1_WriteByte((uint8_t)((WriteAddr)>>8));
9 O, b+ T6 ~( ?7 T$ G5 G - SPI1_WriteByte((uint8_t)WriteAddr);
# d9 _+ X2 p! ]8 d( K - for(i=0;i<NumByteToWrite;i++) SPI1_WriteByte(pBuffer[i]); //循环写数; t2 ~9 h% _+ ^* K9 k' @% j
- SPI_FLASH_CS_H(); //取消片选
8 u. h" g% }6 P/ D) L# a/ \; [ - SPI_Flash_Wait_Busy(); //等待写入结束
0 U) ]3 I. l* }" G; y7 ?% [- d5 n - }
复制代码 * }5 [1 U' r# ]9 e. P
(10)flash 的随机的多字节写入,通过按页写入实现的函数来实现 - /**
& P! F+ C% ~( ^8 q2 T" a! H - @function 不定量的写入数据,先确保写入前擦出扇区1 |4 i2 u" F7 K! B+ u6 P6 Z" N
- @annotation; M( T% z6 B% s, r. D
- @param% L! `4 L$ N- n
- @retval1 e* p/ ^" Z% Z1 v2 C* A
- **/
, Q B, K; K# b: ~ - void SPI_Flash_Write(uint32_t WriteAddr,uint16_t NumByteToWrite,uint8_t* pBuffer)! R' O' x1 R: Q7 H" J
- {; p- G# X) B# Z
- uint8_t NumOfPage =0, NumOfSingle=0, Addr =0, count =0, temp =0;
* j" P- Q# x9 H4 n - /*计算Addr,写入的地址是否和PageSize对齐*/
7 N8 F7 v# q' }8 `+ `! W; j, r - Addr = WriteAddr % SPI_FLASH_PageSize;6 U# H2 a' U8 m$ h' w' e w' x& P
- /*count 为剩余的地址*/9 l: m: N9 [- r4 O6 R. Z! c+ Q
- count = SPI_FLASH_PageSize - Addr;8 u Z/ w4 D$ P4 ^ {
- /*计算能写入多少整数页*/
/ Y$ S5 t8 d7 D - NumOfPage = NumByteToWrite % SPI_FLASH_PageSize;
8 x- n' y# [. @ - /*计算不满一页的数据*/
6 F& X4 Y* s. l. S- P6 v - NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;' M0 p5 N; a! s$ J
- /* Addr=0,则 WriteAddr 刚好按页对齐 aligned */
" J3 k& n6 `# M& Z0 E. C - if(Addr == 0)
i7 G# t& `! |" p7 y0 S9 z; J - {. U$ k2 E, p0 B" y# j/ s
- /*NumByteToWrite < SPI_FLASH_PageSize,一页能写完*/
5 B2 i3 ?# C' } - if(NumOfPage == 0)
3 M/ s$ ]- ], i' v2 ]$ p8 X2 T. a" R - {
) \1 ~4 C, i l$ W2 h8 s' C. M6 h' I - SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
! m) c: j0 a) G6 g - }, y v. {4 Y P" K6 R
- else/* NumByteToWrite > SPI_FLASH_PageSize,一页写不完,先写整数页,在写剩下的 */
6 |) b2 _ S/ R7 T - {+ ^- S; V6 o( D
- /*先把整数页都写了*/
3 N. X5 m- ~" e L - while (NumOfPage--)1 v3 u' L/ U4 {3 l
- {2 d, R/ Z5 l' ~: ]5 j8 ?
- SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);
3 ^7 n/ ?# u# r3 v" J6 n/ l - WriteAddr+=SPI_FLASH_PageSize; // flash 的地址加一页的大小
, s( I9 V, y1 o5 p7 u5 B) }- h+ r - pBuffer+=SPI_FLASH_PageSize; // 写缓存数据的地址加一页的大小- W3 {( E& _" \
- }3 ?2 B2 n2 o9 Z4 i7 {% M
- /*若有多余的不满一页的数据,把它写完*/4 p' h4 ]; x/ k5 Z
- SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);8 B6 U) ?" L6 A- c- {
- }
+ K9 J- y) k. ?4 R' B F# N- E - }
! m" a/ D, s$ _$ m( T - else/* 若地址与 SPI_FLASH_PageSize 不对齐 */. L5 s+ M o! ?2 p: l) v
- {
# p5 J8 a. ?1 z5 b3 p- Z - /* NumByteToWrite < SPI_FLASH_PageSize */6 a; e9 P3 V, D$ b
- if (NumOfPage == 0)
' K( m2 j4 i" ^' z - {
! z3 O0 j) C, L, z7 O' c - /*当前页剩余的 count 个位置比 NumOfSingle 小,一页写不完*/3 ]; J3 f, A0 S R7 V
- if(NumOfSingle >count) \; i5 y: b: e- E3 [) P6 O
- {. I8 j! B% i5 }" Z
- temp = NumOfSingle -count;
; B5 n" m( d5 E5 J# [% b+ Z7 t6 m - /*先写满当前页*/: A( l& g" y) R7 ]
- SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
. h! _0 c5 d9 W$ ^- T& b5 r - WriteAddr += count;
7 C! j+ q( m0 y# e - pBuffer += count;
6 J, j8 ^- a; `9 _& @# q - /*再写剩余的数据*/6 A8 t T$ i3 p' E0 J
- SPI_Flash_Write_Page(WriteAddr,temp,pBuffer);
3 [; o: {% U7 `: J- P+ Z - }
! k+ D. Z9 N8 H$ h* A - else/*当前页剩余的 count 个位置能写完 NumOfSingle 个数据*/( W0 P( w3 J% i) z0 X4 m
- {+ @3 i* }( @2 {- d- v8 F
- SPI_Flash_Write_Page(WriteAddr,NumByteToWrite,pBuffer);
# b0 B; O1 z, e5 S- b' o( {: r - }! k& }' w& i8 L, f: U
- }& S) w. u9 q* O) F* g
- else/* NumByteToWrite > SPI_FLASH_PageSize */5 `% j4 B- W# z: r+ H2 h/ g" r9 G
- {; U G' z( m( w, n H9 Q! n
- /*地址不对齐多出的 count 分开处理,不加入这个运算*/
- j- e0 s/ E1 c$ d8 Y5 M2 o - NumByteToWrite -= count;
B" X! T0 y# W1 R, o1 l( r - NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;3 n; I( o) g. A7 e
- NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;5 C1 G( x$ q- S
- /* 先写完 count 个数据,为的是让下一次要写的地址对齐 */# u4 d9 F( a6 a, ^; H9 ^3 }/ \
- SPI_Flash_Write_Page(WriteAddr,count,pBuffer);
+ ` g" m8 I) g8 x- f7 c - /* 接下来就重复地址对齐的情况 */
$ G1 y+ B, G* A% h. J - WriteAddr += count;
. u, c, s' I( b" N) m - pBuffer += count;
# _- _ T" ?1 d8 A- p9 B - /*把整数页都写了*/
2 c' f$ \+ V2 b8 ]5 p, Z. u* k - while (NumOfPage--)3 c: G4 q5 j# X) V8 Q
- {
& y# |" B; i8 g, k- P. L. e& i" O - SPI_Flash_Write_Page(WriteAddr,SPI_FLASH_PageSize,pBuffer);' D( j2 A3 }. z9 V; I* \. I: ^
- WriteAddr += SPI_FLASH_PageSize;
( F0 ~6 H9 J# K' d# n - pBuffer += SPI_FLASH_PageSize;
; K/ l7 l4 H* {, K - }
- V% s& ^# i6 A) I - /*若有多余的不满一页的数据,把它写完*/
& b, w9 C) [5 ^7 G Y - if (NumOfSingle != 0)
3 n& w( X2 N. X( c+ k: E - {
( Z* z0 Z( E; H u8 [' ? - SPI_Flash_Write_Page(WriteAddr,NumOfSingle,pBuffer);
) T3 g, l6 I: x - }3 P5 \; A% b0 B2 e5 |
- }! Y! M( [' R7 v6 T1 x
- } 0 e1 h1 d( u+ V
- }
复制代码 / u4 g1 {2 l$ \5 a% K( g: t
通过上面的函数实现,我们就可以基本的来完成FLASH的读写操作了。有些函数参考了野火标准库的操作步骤。 在main.c 的主函数中对FLASH的读写地址进行宏定义: - #define FLASH_WriteAddress 0x00000- T" T, ?& R7 b+ V+ T3 c
- #define FLASH_ReadAddress FLASH_WriteAddress, k# B" R2 U. i1 a$ C6 j; n
- #define FLASH_SectorToErase FLASH_WriteAddress
( W6 u, H. q9 o+ P$ L - #define FLASH_SPI hspi1
复制代码 . e: W% y' V5 d8 E; w6 K S$ L
在mian函数中定义变量: - uint32_t flash_ID = 0;
a2 I I. W' o# H! V$ y2 d1 ~9 v - /* 获取缓冲区的长度 */
" m+ D. B# ^6 u- H - #define countof(a) (sizeof(a) / sizeof(*(a)))& z; u# y8 Z( V9 }0 H& o0 M' D( m
- uint8_t Tx_Buffer[] = "现在进行FLASH的读写测试\r\n";
9 R E. q+ n5 H2 B - #define BufferSize (countof(Tx_Buffer)-1), N* e4 V' m, i0 C$ |+ U# t9 n. `" l
- uint8_t Rx_Buffer[BufferSize];
复制代码
! [! W4 V/ K Q( A& j 在main函数中进行测试代码: - printf("*************this is test for coding...**********\t\n");; m( {5 |7 d4 P) q: U$ `% n& K1 O
- printf("this is test code for spi1 read and write flash w25Q64 \r\n");- \$ x4 ^3 a2 [
- flash_ID = SPI_Flash_ReadID();: W7 v* C, Y5 R g- |. X
- printf("\r\n flash ID is 0X%x\r\n",flash_ID);
l* O' Y# m8 C1 B7 G" e) X - SPI_FLASH_SectorErase(FLASH_SectorToErase);
8 o; q8 M: [3 i) F0 G - SPI_Flash_Write(FLASH_WriteAddress,BufferSize,Tx_Buffer);0 W& {: i/ A( b5 I0 e- D1 {. b2 [
- printf("\r\n 写入的数据为:%s \r\t", Tx_Buffer);
; i# \& j3 P) n: q' f8 S - SPI_Flash_Read(FLASH_ReadAddress,BufferSize,Rx_Buffer);$ p/ T! n3 O0 Y& T
- printf("\r\n 读的数据为:%s \r\t", Rx_Buffer);$ H- V' ^3 v4 ^
- /* 检查写入的数据与读出的数据是否相等 */8 R: P6 h$ e$ e J% U- S- j
- if(memcmp(Tx_Buffer, Rx_Buffer, BufferSize)==0)0 ^8 F9 @* q: [2 H
- {( z1 z3 x) W- A* W
- printf("写入的和读出的数据是正常的!\r\n");0 @: ]5 D* ~8 i) @% I
- }" B. Z: i1 p# y% h, k: r8 ?) O) ?
- printf("SPI 试验结束......!\r\n");
7 y7 F( C4 I: H+ S/ C - printf("*************this is test end....**********\t\n");' y2 m* A# Q! L8 i+ a% j! b
复制代码 8 B* ?5 I( v! s* _1 t& z3 w J
04 ------------第四节 效果演示-------------- 我是使用串口将写入FLASH的数据进行读出来,比较数据是否一致,同时独处FLASH的ID看是否一致: 我们可以可以看到FLASH的ID是0xef4017和产品手册中W25Q64的ID是一致的,读写测试也是正常的,我们使用了C库函数,需要包含头文件<stdio.h>。 文章出处: 小鸟的早晨 |