SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
8 C& u+ a' z. m% nCubeMX配置:
* n; u9 V; `. j- U0 i( y5 |* u+ c8 F- z
. \" } P: [3 V k3 X: v) y$ G* r/ h" H) f1 O
SPI模式
f: O& j+ ?& q2 X8 R! L有只发送、只接收、半双工和全双工模式;4 l: ^8 P, N# Z2 V) T
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;. s4 M7 A( p& f6 v& x
% g, J2 c$ ~8 K
片选信号
]9 [4 [% C( p" h2 h5 y" N( V因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
7 W* `4 u# r5 a
$ M3 z i3 W; y; g* Z0 x3 N3 ^( o帧格式2 }; W ~% `. P( |4 |$ S! S+ g
这里选择Motorola帧格式;
- s7 V! K% x4 F# n8 U: b选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
+ Y2 m0 Z( i4 h: i博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。2 ?* u# V: ^4 \, A5 J, c9 F
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
\5 d% w- K' J! D这是手册里关于TI模式的说明部分和波形图:
7 o$ {& {0 B0 |8 i4 t' Q$ Q0 U
. F6 n6 z# e/ ^2 W: j# ^6 [. I. ^9 G
0 R6 ]" y' H+ o
$ C! g/ n9 T, G& j6 g7 x" }0 R& L; Y$ ^5 L# [/ a4 v; e& q
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。4 A$ b% K6 }! w! ~
% k1 J' n& |" P+ v6 _
数据长度/ e) q% V5 I+ ^# ]" X' J
4-16bit可选,根据实际,选择8bit;
. Q* N$ X6 E9 p4 K# v* D* u
: S1 f( b) @/ i9 F+ o数据传输模式
- j+ K6 e1 O- P! F: N! ~第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
8 x/ B, h2 j& ~3 p1 p- _' r
2 t9 ?1 p8 F1 a5 cSPI速率
' m/ A, l$ t' K. ]& o+ h0 G0 c8 ySPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。8 l$ y6 P2 U/ G$ G4 C
2 s& ~9 f/ A5 v: K* K时钟极性和时钟相位& c; }0 ^" x. i! Y2 r" |
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。' X: I# R- ]% n! W! N
4 ?! Y, O- m' f9 tCRC校验, S1 c& b3 m2 ]8 z
暂时还没研究到这里……
6 A. g @: X0 j* t! W( K) F( Z3 M1 W' K
NSS脉冲7 Q1 @+ X" R; h- a
参考SPI的NSS 脉冲模式的作用,这里用不到;
. h0 G+ d2 T+ A/ x9 s( j3 u1 s) H, Y5 R
NSS信号类型
4 t8 I' X. {) m' t& F4 @, M! O因为前面禁用了硬件片选,所以这里只能设置位软件模式。: s7 r% d; @5 _
7 m9 S+ X' D8 f, }7 B) g
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。 R' W1 X+ g& W! d, r, s; s* U' }/ ]4 ]1 `
CubeMX生成的代码如下:
. S) F5 _0 \# |) N2 t4 X8 ^0 ?* R) J+ V0 @3 _( C: b1 x
- SPI_HandleTypeDef SPI3_Handler; //SPI2句柄
]) J. m* I# z; c. q9 G/ ] - , ~) B# g( w+ \' b7 Y7 X
- void SPI3_Init(void)
8 K1 k& z+ \$ @4 b, l8 S4 s - {' |2 r7 w5 D0 B* l* [
- SPI3_Handler.Instance = SPI3;
+ P4 u. L0 ]7 L% m4 D, P - SPI3_Handler.Init.Mode = SPI_MODE_MASTER;& H y& S8 M2 B9 \' H% e
- SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
- V3 w5 i y) t% _ - SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
: d% u* r, `3 ]' e6 ?. k - SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;9 s& s2 L9 ^# o7 H' F' ?1 H
- SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
0 K' J ^4 _, C - SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
" I3 c1 U# o6 u& G: Q - SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;* N9 V. l* _8 d
- SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
8 |, ?, ]- e0 J2 N - SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
* |( i" v* b& U2 S$ a4 m% B* q - SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
: \: j/ f7 V" n+ E/ G0 i3 C - SPI3_Handler.Init.CRCPolynomial = 7;
! H& _: w4 `& E: }- f9 K8 `1 x - SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
5 t1 ?6 W, d G9 p4 \2 L - SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
9 N8 S# s# M. }! @8 o8 X - HAL_SPI_Init(&SPI3_Handler);
9 v8 }- j& M* p4 q, N% D - ' {0 N& C# J- c2 |, G
- __HAL_SPI_ENABLE(&SPI3_Handler);
! [- o6 I4 q' d& v. n; C, P - }4 D+ H9 D- X0 W( ?1 g( w( k9 q) Y
- `; N( U' b# X7 o; q- |/ T- void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)5 V* Y. ]3 A. E4 r# I/ z0 K6 p
- {" X0 \- i* M, R3 ?" a9 V
- GPIO_InitTypeDef GPIO_InitStruct = {0};
2 l- d' H' e6 l - if(hspi->Instance==SPI3)( N3 q( B& j* O5 @2 F
- {- r# B7 d$ @5 X" P* P
- a/ Y% L. e. ?0 @7 `/ j) S6 m# d
- __HAL_RCC_SPI3_CLK_ENABLE();
1 _0 ^ d+ W, l: X! y t/ j - , ^( R. M8 k8 Z. }4 L; P% m$ D& J; b
- __HAL_RCC_GPIOB_CLK_ENABLE();
3 s E# e, f$ |2 ` - 8 E* w8 i# c" b% |- _
- GPIO_InitStruct.Pin = GPIO_PIN_2;
* G' i; ~) H- `9 u - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;7 j7 }9 C; ^* B; z( [5 [
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- H) L! h! |& k1 j0 `9 ]7 Y* r - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;. s/ x& C$ u; y! ~0 Q) Y0 d0 V- \
- GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;5 w [# s+ n9 Y3 p
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);# m( }8 R. T; x) j
- $ T2 ~& _* s" _% N
- GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
) |9 m* x5 L" y2 ~4 V' w# w6 ~* e - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;8 B, }7 D& ?) F. W& J! N
- GPIO_InitStruct.Pull = GPIO_NOPULL;
# ~, E4 P6 O7 e7 Z - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
* X7 e/ v; K7 T$ q" d; H3 U - GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
9 u) {8 L1 n1 t; r/ j2 l8 q. r. D - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
% i: R2 R/ D' x - }5 {9 I( B% d1 |: K8 N
- }4 O& h, L/ K, b1 f5 B
* D( q/ ]* `* Y4 t7 e: x
N+ F2 f3 E* _8 M) D, t0 H0 V- static void MX_GPIO_Init(void)9 L# T/ B/ ?6 A5 \* S% _# J
- {1 c* k5 J6 b7 C& u% q3 M
- GPIO_InitTypeDef GPIO_InitStruct = {0};
4 p% i( }* R& o: v: U
6 {8 L- T1 I2 H3 E; @- /* GPIO Ports Clock Enable */+ b1 A. x+ v# c; c8 V
- __HAL_RCC_GPIOA_CLK_ENABLE();
- f$ B) |' E9 g2 \3 z; ]. Y
+ A1 L$ P$ x1 e2 n% O- /*Configure GPIO pin Output Level */
$ g& b( ?( k* ~9 l/ m; C6 G - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);$ |1 g" U% \& ?. u- k4 f
) }9 d+ v$ b1 z$ A' Q/ L" \5 T S9 K- /*Configure GPIO pin : PA4 */
0 o4 |0 r) K/ C$ R6 a6 H6 W - GPIO_InitStruct.Pin = GPIO_PIN_4;
# L, ~) C" S' a - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
1 X9 c, E: ^) M$ s0 v& ~% o - GPIO_InitStruct.Pull = GPIO_NOPULL;. a( [& c$ o6 \6 h& L5 }( E- f
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;5 O! e p" T% ?! s2 T) }
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
3 h- ^ }6 F! f* a
4 k+ |4 r t p. _/ N+ \9 Z- }
复制代码
0 B \8 F5 f, |3 D! F1 b同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:, z+ y1 x8 H1 e, z8 c9 e
# o2 G7 q2 y) x: {
- void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)( s# O$ c# t* F% y: D* \0 A/ Y
- {2 x. z- j3 ` C# }0 e- B2 ]
- assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性9 V3 q2 F* e+ {8 V; U. V# Z, L( v/ A
- __HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI
! [6 s. H0 m# c$ D9 f t& t - SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率
6 _) e( O5 T8 R* }! T m - SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
1 V- f5 s2 n4 ]6 T" W - __HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI; ]1 O9 D- X L/ [/ W
- 6 |! R& H* Y- |% u
- }
复制代码 7 e; L8 x/ a6 q' \* N
因为全双工的读写是同时的,封装一个读写函数:
9 U/ o1 R, n- e7 {
# Y% x1 }* v; A. K- //SPI2 读写一个字节0 ^. ?2 ]3 O A! H) B
- //TxData:要写入的字节
4 i9 F7 D. b* p0 m% I - //返回值:读取到的字节! y: P! F) c4 a C6 h
- u8 SPI3_ReadWriteByte(u8 TxData)0 z0 G6 T0 }, e7 \! `6 V z0 t
- {7 ^& }/ A: |2 t1 }
- u8 Rxdata;1 y4 V0 T7 x t
- HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);9 L, N7 a# M8 i* b+ K$ h
- return Rxdata; //返回收到的数据, }/ Q2 _% `8 B1 h/ ], c
- }
复制代码 + E/ u* J% \# U" Z- B/ j
根据W25QXX的数据手册,写一个读ID的函数:& [, T3 M; N f2 J4 e) }
- //读取芯片ID$ p/ C% f% J2 B1 x$ p- }
- //返回值如下:
% }0 Q" U5 h8 h - //0XEF13,表示芯片型号为W25Q80
1 B9 s: X# Q$ L; y. E - //0XEF14,表示芯片型号为W25Q16
& {" c' u. X' S1 y- t5 ? - //0XEF15,表示芯片型号为W25Q32 , F/ Q& \ H- D- r# p! q. D
- //0XEF16,表示芯片型号为W25Q64
4 N9 l! [' J. e - //0XEF17,表示芯片型号为W25Q128
6 V* @ P5 \2 K5 d - u16 W25QXX_ReadID(void)0 Z, {* P' F7 L
- {3 j$ ~0 c+ k7 c
- u16 Temp = 0;
- J4 }$ k$ B1 p* T2 _% y - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); $ U- `1 ^8 Q7 h7 q8 o
- SPI3_ReadWriteByte(0x90);//发送读取ID命令 & o: U A; A' ^2 W, i
- SPI3_ReadWriteByte(0x00);
) d P( \/ U) Q' t: I - SPI3_ReadWriteByte(0x00);
2 J6 l1 f- H1 `3 m8 T - SPI3_ReadWriteByte(0x00); % e2 p6 L. t8 I7 u
- Temp|=SPI3_ReadWriteByte(0xFF)<<8; # q7 A" Y3 x/ N! m; l* H
- Temp|=SPI3_ReadWriteByte(0xFF);
8 `8 U1 e) e9 v% r; a7 | - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
) y u2 p. B, s5 B+ h; ]7 j - return Temp;
+ L3 Y8 k) g- w7 p( d( ? - }
复制代码 . D) j* l: N$ [# P' X+ X' r
最后写一个在主函数中调用的接口函数。
! d Q- X8 w9 F% R5 q9 z# p+ X2 l
' q( ^# L& R* ^6 w+ a4 O: b' j- void W25QXX_Init(void)
( `" I, O7 W7 F6 ~2 P! n: Q - {
: d) |1 |. Z2 S* h" I) w, L+ [ v - MX_GPIO_Init();2 v8 z1 a- K0 V( b# ?+ s7 u
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
6 l* E z! L9 ?, I# l# W4 C - SPI3_Init();2 l1 E! @+ h6 U
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
. z; n$ i) _% P8 y& ?( A% t - uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.
( n: u6 D$ ~$ G6 x9 h, F - printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
3 U# g! |7 n5 ` - }
复制代码 ) S/ i* x, @# |7 v7 y: t$ L
下载运行,读取到了芯片ID。
+ y a4 I2 ?5 h
' b$ ]) f U- y- x; {* B i2 e* o( p, \) Q* O
! v; O, O6 G! T R |