你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32接口篇之SPI接口和W25Q16初始化详解  

[复制链接]
bb123 发布时间:2016-8-25 15:48
" c: {1 t+ t, x- [( C9 B2 n0 _

0 ]" j7 h/ T! a2 R; [7 `2 c
STM32接口篇之SPI接口
                                                        ---SPI接口操作FLASH存储芯片W25Q16初始化代码详细分析6 ^7 f8 y) J7 w: i, q/ m

5 T( q- a  M3 a5 w# [4 X; |+ A我的其它两篇帖子请参考:. e$ w1 d5 v! _0 d' }- V" E
STM32库函数注释翻译,每个函数使用说明及使用示例
: [& {% [7 \( i3 {4 {, k% C
STM32库函数翻译,每个函数说明及示例-RCC(时钟)篇(上)
9 ^+ {& ~  K: _) ], J' F, j" P

: H9 J9 Y) h) A7 u9 T& Q: Z% B) ?; j- u4 R( J# M
文档目的:
学习STM32F4SPI接口和FLASH存储芯片W25Q16,通过对初始化代码分析,更好的了解STM32SPI接口编程中的一些关键知识点。

* r, o' b* i6 N4 d, E
学习素材:
l  W25Q16数据手册
l  电路原理图
l  STM32F4应用手册
针对W25Q16SPI接口初始化代码1 [: C! O7 L- h1 E

, z3 Z4 P9 s1 O) x$ F/ `4 o
7 D# A& H5 P1 C: U6 @
1 硬件电路说明
(1)=关键原理图
& W7 P) L% x( \3 o3 U! K) `  a$ d9 R
FLASH芯片接口图
$ c# B+ e" x; `& _0 i- f$ `' f
1.jpg
8 q- x9 r; D: p7 N& g

# @" ^" \4 a+ m8 R$ M% y. {
GPIO接口图
2.jpg
6 }, h. o3 g. U2 @! \4 p
3.jpg 9 X4 z" U; V( C
(2)   原理图说明
首先我们看到上面第一个图,第一个图展示了W25Q16的原理图信息,我们配合W25Q16的数据手册(第六页 见下图),我们分析具体IO的连接和功能,分析结果见下表
4.jpg
5.jpg # G7 O! K& }0 N# B3 A
4 z/ u: p8 c. j, J% C- s: z3 H
  
管脚号
  
名称
连接的io或者信号
功能
  
1
  
CS
PB0(不复用)
片选信号 该信号低有效  即当该信号为低电平的时候对W25Q16的操作是有效的
  
2
  
DO
PB4(复用SPI1_MOSI或者SPI3_MOSI
数据输出信号
  
3
  
WP
3.3V
写保护输入 当该信号为低电平是开启写保护  无法写入数据 从图中我们可以看出 这个管脚直接接在高电平3.3V上面 始终不开启写保护(不过做一个上拉应该会更好)
  
5
  
DI
PB5(复用SPI1_MISO或者SPI3_MISO
数据输入信号
  
6
  
CLK
PB3(复用SPI1_CLK或者SPI3_CLK)
时钟信号
注意:配合(1)中的GPIO接口图,我们可以得到具体IO的复用方法,在上表中已经给出。
$ \2 D; @+ G3 r. e4 V
(3)   编程需求
通过上表的分析,我们可以得出如下编程需求:
l  PB3(CLK),PB4(MOSI),PB5(MISO)初始化为复用功能(可以使用复用SPI接口或者SPI3接口,因为SPI1和SPI3控制器的相关引脚都在这三个IO上,见GPIO接口图)
l  PB0初始化为普通IO,该IO的功能是在操作SPI接口的时候,拉低该IO,操作结束以后,拉高该IO。
l  初始化配置SPI控制器,该控制器的初始化要配合W25Q16芯片手册进行。
+ A8 ?; l; \8 v; n0 Y

9 o9 X- `+ u) i, @7 r4 c- Q  d
2 初始化程序分析(1)   代码展示9 C9 L  H( _% n: S# l
void SPI1_Init(void)! G0 T( _: i0 U8 Q# G  k
{                 
8 w6 b: b9 E" ^# Y  \- ]1 cGPIO_InitTypeDef  GPIO_InitStructure;         * U. `8 K  \' @4 k# E/ C! b, G
SPI_InitTypeDef  SPI_InitStructure;         % |1 q! L, C' n5 d
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB时钟        * X) Y- S. c- v+ U
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//使能SPI1时钟         7 n9 `1 Q( N& A$ ]4 W/ b+ h! G1 O2 c& S
//GPIOFB3,4,5初始化设置         
8 \$ ~$ h; v' h* }/ jGPIO_InitStructure.GPIO_Pin= GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;        
( C  u* m% \: _GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF;//复用功能        / q! r! `: F8 e$ p) o
GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;//推挽输出         
9 o9 a$ x, \! ]' c& K: A# IGPIO_InitStructure.GPIO_Speed= GPIO_Speed_100MHz;//100MHz        # ?! z8 T& ^9 J
GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;//上拉         + r1 E8 ?) t: V" @- d3 V# G9 {
GPIO_Init(GPIOB,&GPIO_InitStructure);//初始化        
# N6 _6 o2 h' E  u( L7 O GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1);//PB3复用为 SPI1         / q# t7 x9 }9 Y( m, p
GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);//PB4复用为 SPI1        7 f7 P4 t) g4 [# f
GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);//PB5复用为 SPI1              
. @2 V$ ~, k9 J. |" C( |6 P RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);//复位SPI1        5 C* x. w4 g4 G4 o
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);//停止复位SPI1         
/ I$ f3 a! F. B$ ZSPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;           " @+ w) A) |! }8 Y7 m+ N  q
SPI_InitStructure.SPI_Mode= SPI_Mode_Master;                   //设置SPI工作模式:设置为主SPI         3 j' d: h/ o* m4 n# `- [
SPI_InitStructure.SPI_DataSize= SPI_DataSize_8b;                          
- M. @/ F5 ]$ Q% f% nSPI_InitStructure.SPI_CPOL= SPI_CPOL_High;                          
/ y* _5 T: k+ Y$ p9 k* }SPI_InitStructure.SPI_CPHA= SPI_CPHA_2Edge;   //串行同步时钟的第二个跳变沿(上升或下降)数据被采样         : P3 r) X0 F- c. l5 R
SPI_InitStructure.SPI_NSS= SPI_NSS_Soft;               //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制         SPI_InitStructure.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_256;            //定义波特率预分频的值:波特率预分频值为256         SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;         //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始         SPI_InitStructure.SPI_CRCPolynomial= 7;      //CRC值计算的多项式           ~& i  ^0 c8 a; U
SPI_Init(SPI1,&SPI_InitStructure);        
. a; j' o7 c( @/ }7 a. c0 G  XSPI_Cmd(SPI1, ENABLE); //使能SPI外设         
2 a+ V3 z; k. i" a5 E/ R. dSPI1_ReadWriteByte(0xff);//启动传输            
4 h' {1 I3 Z0 c" B}     2 d* m; j: u- V7 S
+ J$ R/ |2 m' g% W) v7 e' x
void W25QXX_Init(void){     0 w: J8 z8 m/ z4 q9 ~  \/ `
GPIO_InitTypeDef  GPIO_InitStructure;    . q$ I# L# `+ q. a- S9 O
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB时钟
  d! q+ y2 @; ]% Q3 [//GPIOB0    GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0;//PB0   
9 k0 x; Y# ]. g1 o8 H2 }GPIO_InitStructure.GPIO_Mode =GPIO_Mode_OUT;//输出   
$ [- F/ G4 j3 A/ iGPIO_InitStructure.GPIO_OType =GPIO_OType_PP;//推挽输出    6 k2 m8 z) f* }/ O/ V
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_100MHz;//100MHz    8 R2 G# o* A0 n% K8 y! |" j) z5 `
GPIO_InitStructure.GPIO_PuPd =GPIO_PuPd_UP;//上拉   
- i5 a- [( ]  M, g0 s+ O GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化         
2 h* B8 t( ^* q+ S; PW25QXX_CS=1;                           //SPI FLASH不选中        
( C2 M" w$ I/ @$ e2 S$ K SPI1_Init();                                         //初始化SPI         
/ v+ [8 [7 B6 ^3 z$ DSPI1_SetSpeed(SPI_BaudRatePrescaler_2);             //设置为42M时钟,高速模式         
9 D* _0 I# l& ^, y0 x' P* @ W25QXX_TYPE=W25QXX_ReadID();        //读取FLASH ID.  b/ b* K6 g" l2 ~, y$ c
}  ! A; ?9 D1 C6 U5 ]) p0 f/ q2 i# T
(2)   代码分析1.  初始化代码总体分析由展示的代码,我们可以看出,对W25Q16的初始化主要使用了两个函数,主要调用函数void W25QXX_Init(void)对W25Q16进行初始化,而该函数又调用了SPI1_Init()函数对SPI1接口进行初始化。代码主要流程图如下: 6.jpg 6 I- X: z% L. ]8 L
5 ?' Q3 c- V6 O! e
通过以上流程图,我们可以看出对W25Q16的初始化,主要是初始化SPI接口。下面对代码具体说明。 & W' J' B  }% ]6 o# P" U
2.  代码详细分析(我们主要分析SPI1接口的初始化函数)我们先分析函数voidSPI1_Init(void),对该函数里面的没一行代码进行详细说明l  GPIO_InitTypeDef GPIO_InitStructure;* ?& ^( N# ~5 i
SPI_InitTypeDef  SPI_InitStructure;- P% `& {% g8 @3 n8 G) T+ b# G
代码分析:首先定义了两个结构体变量,这两个结构体的类型定义在GPIO和SPI的库函数中,主要作为GPIO和SPI初始化调用的参数,我们一会详细分析。
  H# i1 d" F/ a9 ul  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟
0 i& H4 ]! l# ^0 @5 o) ZRCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);//使能SPI1时钟
- M8 H+ E7 {. N0 F  L7 Q代码分析:STM32有着强大且细分的时钟系统,我们可以单独的对每一个控制器的时钟进行使能,这样可以更好的降低功耗。因此,这两句分别使能GPIOB和SPI1的时钟。关于时钟库函数的说明,请看我的《STM32库函数说明及示例-RCC篇》
% v7 ?8 l$ U6 h7 D$ Nl  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3|GPIO_Pin_4|GPIO_Pin_5;% C4 ?# v2 y# p; _
GPIO_InitStructure.GPIO_Mode= GPIO_Mode_AF;//复用功能         : k: }) z$ }8 Y/ M# r! p
GPIO_InitStructure.GPIO_OType= GPIO_OType_PP;//推挽输出         
2 x4 T4 A& m6 L& WGPIO_InitStructure.GPIO_Speed= GPIO_Speed_100MHz;//100MHz         $ p" Z9 B! ~7 r$ P4 g
GPIO_InitStructure.GPIO_PuPd= GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB,&GPIO_InitStructure);
# ]) o: `4 i, k. i
//初始化代码分析:这几行代码主要是初始化结构体参数GPIO_InitStructure,该参数指定了GPIO的一些具体的配置,注意第二行代码,我们把PB3,PB4和PB5设置成复用功能。第三行我们采用推挽输出,可以得到更大的驱动能力。第四行我们把IO的最大翻转速度配置成100MHZ,因为考虑PB3作为SPI的时钟口,最高的频率可达42MHZ。其它关于GPIO库函数GPIO_Init(GPIOB, &GPIO_InitStructure)的详细解释,请见我的《STM32库函数说明及示例-GPIO篇》。
5 e7 |/ x9 V5 w' N" K- Q
l  GPIO_PinAFConfig(GPIOB,GPIO_PinSource3,GPIO_AF_SPI1); //PB3复用为 SPI1         GPIO_PinAFConfig(GPIOB,GPIO_PinSource4,GPIO_AF_SPI1);//PB4复用为
, K0 c9 j4 B5 e! F  [0 G  {. y SPI1GPIO_PinAFConfig(GPIOB,GPIO_PinSource5,GPIO_AF_SPI1);//PB5复用为 SPI1
2 ~2 g% `1 J; E, \% K2 v" A
代码分析:以上三行实现管脚复用功能的具体配置,我们分别把PB3,PB4和PB5配置成SPI1接口的复用功能。关于该函数的详细说明,请见我的《STM32库函数说明及示例-GPIO篇》。
4 j. L, A8 T2 g) ^3 a9 x8 n
l  RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,ENABLE);        8 \( N0 a) [8 G& N7 @' D2 }
RCC_APB2PeriphResetCmd(RCC_APB2Periph_SPI1,DISABLE);         对SPI1接口的时钟进行复位,以便进行SPI接口的初始化操作l  SPI_InitStructure.SPI_Direction =SPI_Direction_2Lines_FullDuplex;           
( M- y7 Q! h; ?- p9 b0 cSPI_InitStructure.SPI_Mode= SPI_Mode_Master;                   //设置SPI工作模式:设置为主SPI        7 a$ ^  i6 ]0 Y7 `& q; i
SPI_InitStructure.SPI_DataSize= SPI_DataSize_8b;                          
! p& A7 p0 M+ T+ K: `7 J* vSPI_InitStructure.SPI_CPOL= SPI_CPOL_High;                          6 P9 U6 b( [* b& p8 v/ W+ @0 A: ?
SPI_InitStructure.SPI_CPHA= SPI_CPHA_2Edge;            
9 {% U8 X" |4 x5 `* nSPI_InitStructure.SPI_NSS= SPI_NSS_Soft;                       ( w" H) [0 z: K" n
SPI_InitStructure.SPI_BaudRatePrescaler= SPI_BaudRatePrescaler_256;                           9 f# z6 z5 y! n7 U* p; \& c8 B; Y
SPI_InitStructure.SPI_FirstBit= SPI_FirstBit_MSB;                  
! H" u) @5 R+ USPI_InitStructure.SPI_CRCPolynomial= 7;      //CRC值计算的多项式         
& j9 ?' j8 ^  B7 O, L3 @3 ^3 T2 sSPI_Init(SPI1,&SPI_InitStructure);   
   
$ v& q, c! B" a/ P2 ]代码分析:这一段代码是SPI接口初始化的关键代码,在该代码中,对结构体变量SPI_InitStructure里面的成员变量进行了初始化。以下分别进行说明:
! N$ Q# u2 c+ t  b  z' U8 XØ  第一行使用宏SPI_Direction_2Lines_FullDuplex将SPI接口配置成双线全双工模式,因为硬件接口使用了输入和输出两个IO,所以应该是双线模式,而且W25Q16的数据是在收发两个方向同时传输,所以是全双工模式。
8 Q1 J/ H5 E( e$ sØ  第二行设置STM32的SPI接口工作在主模式(SPI_Mode_Master)。因为读写数据的请求都是由STM32主动发出,所以是主设备。这里,大家要搞清楚主从设备的区别,通信的发起方就是主设备。, |" `% ?  V" M, i8 D% g% p
Ø  第三行设置数据长度为8位(SPI_DataSize_8b),因为W25Q16的最小传输数据单位(或者说每个地址对应的数据是1个字节)是1个字节。1个字节是8位,所以选择8位。7 w! N2 F( r* _
Ø  第四行和第五行分别设置极性和相位为SPI_CPOL_High和SPI_CPHA_2Edge。SPI_CPOL_High表示时钟的初始电平位高电平,SPI_CPHA_2Edge表示在第二个边沿进行数据采样(见下图1)。这样的组合是SPI数据传输模式中的模式3。根据W25Q16的数据手册,W25Q16支持MODE0(MODE0是SPI的初识电平为低电平,在第一个边沿进行数据采样配置组合是SPI_CPOL_Low,SPI_CPHA_1Edge)和MODE3。请见图2黄色部分。因此这里我们选择MODE3,当然选择MODE0也是可以的。
8.jpg
图1:SPI MODE3时序图
$ Q$ y  c3 W3 K, z: `) j' x
7.jpg
! w4 [" w1 `2 n4 r图2

; {  m* ~8 ~# _5 }: c  Q( XØ  第六行设置SPI_NSS信号,由软件产生。因为我们没有使用硬件NSS信号,所以选择由软件产生NSS信号。大家注意我们的CS管脚是连接到普通IO上面的,因此没有使用硬件NSS。- `: {; b$ e. a+ E* k6 b' ]
Ø  第七行配置SPI时钟的分频值为256(SPI_BaudRatePrescaler_256),因此实际的时钟值是APB2的时钟/256 Ø  第八行配置SPI的第一位是最高有效位(SPI_FirstBit_MSB)。这个也是和W25Q16相关的。请见下图红色箭头部分。
+ ~; D! T& T3 z$ K3 r3 w 9.jpg
5 N. J; A0 C% a3 i      ) o' [* k# P, o- \, v6 v
Ø  第九行配置SPI的CRC校验多项式为7
* U$ A& s5 K. B: Z$ u( xØ  第十行调用SPI_Init(SPI1, &SPI_InitStructure);对SPI1接口进行初始化。% T8 }3 y- M) l; o9 l( g& k
l  SPI_Cmd(SPI1, ENABLE); //使能SPI外设SPI1_ReadWriteByte(0xff);//启动传输  9 l; ~! \1 A' B9 Q  Y% j
以上第一行使能SPI接口,第二行使用SPI接口写入一个0XFF,以启动数据的传输。相当于启动数据发送器。

1 j/ f; r2 {7 H8 ^0 s9 V! ]5 h% ?* w3 h
+ L0 O# H1 {( f. @3 E6 ~' X

基于STM32的SPI接口操作W25Q16初始化代码详细分析.pdf

下载

760.51 KB, 下载次数: 995

STM32-SPI-W25Q16

评分

参与人数 1 ST金币 +10 收起 理由
zero99 + 10

查看全部评分

1 收藏 7 评论25 发布时间:2016-8-25 15:48

举报

25个回答
samyang13 回答时间:2019-3-4 20:19:29
我有个疑问哈,以上芯片资料上的截图中,写操作在时钟上升沿,读操作在时钟下降沿。楼主的配置中SPI_InitStructure.SPI_CPOL= SPI_CPOL_High;                          
* {6 h3 t) I' ?SPI_InitStructure.SPI_CPHA= SPI_CPHA_2Edge;   . H) v" d% r1 p0 s9 l5 n
这个是上升沿有效,那么怎么同时保证读和写两个操作都可行的呢?
bb123 回答时间:2016-8-25 15:52:13
本帖最后由 bb123 于 2016-8-25 15:53 编辑 5 j1 H6 V; V7 _9 y
9 ]+ i$ Z, A7 e* R! a& r, q
发新贴了 SPI接口和W25Q16 请大家一起学习分享 有什么问题请及时提出
. E6 q& E% @2 Q4 i我的其它两篇帖子请参考  }+ Z7 r( D- S4 }
STM32库函数注释翻译,每个函数使用说明及使用示例7 m7 v6 [1 o, ^: V6 v6 ~
https://www.stmcu.org.cn/module/forum/thread-607584-1-1.html
6 b3 P3 h9 N* O0 V4 ~' p2 tSTM32库函数翻译,每个函数说明及示例-RCC(时钟)篇(上)9 T6 d* l5 S) m' @
https://www.stmcu.org.cn/module/forum/thread-607608-1-2.html) H9 j6 j5 m! L/ {; v
1 U. x: ?0 x, x7 e/ a6 ]* ~' K* D
youyouxiaojin86 回答时间:2019-9-4 13:21:05
以前用STM25P32没出现过问题,现在改用W25Q32,居然出问题了,偶尔初上电时读数据错误。再等闲学习下。
beebird 回答时间:2016-8-26 23:03:58
好文章,正准备学STM32的SPI的使用
disheng4688 回答时间:2016-8-27 08:58:56
谢谢分享。
kunchen 回答时间:2017-2-6 09:25:16
谢谢分享
yimao111 回答时间:2017-5-16 10:55:21
楼主,你的2.5 引脚的MOSI和MISO 弄反了吧
yimao111 回答时间:2017-5-16 10:56:26
楼主,你的2.5 引脚的MOSI和MISO 弄反了吧
lanmanck 回答时间:2017-5-22 11:39:18
yimao111 发表于 2017-5-16 10:553 O7 s& }' ^7 T; u' P
楼主,你的2.5 引脚的MOSI和MISO 弄反了吧

, P2 y0 G8 y$ a/ `5 a他用io模拟,你管得着!
langtaosha 回答时间:2017-6-10 10:45:30
多谢楼主,辛苦了
艾汇思 回答时间:2017-6-14 10:22:59
楼主,您把整个源代码发出来给我们学习好不好?谢谢了!
samhong 回答时间:2017-7-28 20:41:03
楼主,艾汇思朋友,晚上好!建议个人努力把代码努出来,发给楼主点评,相互交流,相互进步!!我们一起来...
361741352 回答时间:2017-8-31 10:57:19
楼主为什么我用模式0,就不能读啦呢。
lelefj 回答时间:2018-3-25 20:07:31
只能回一个下一个了、
王希瑞 回答时间:2018-4-10 15:58:34
yimao111 发表于 2017-5-16 10:55
( M: Z0 L9 N/ Y% ~楼主,你的2.5 引脚的MOSI和MISO 弄反了吧

: B8 ?8 |$ e, K. c/ _" v1 P4 m没有反,是正确的
% _$ j. V1 C& h" v
星光月 回答时间:2018-4-11 11:34:30
谢谢分享,资料很有用
1 y" x: n2 w7 R& {1 U
12下一页

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版