SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。8 G1 f' m0 k2 t) S+ W
CubeMX配置:+ N" n* X4 Y1 D. r* F
: b/ }5 u# R4 b
6 N2 h- E+ D1 y3 o/ X$ N: d
) x% J/ t$ @9 V7 S0 DSPI模式# w9 ] \; l" s! {+ }0 {
有只发送、只接收、半双工和全双工模式;
2 ~$ v& o3 u/ P" |1 y1 o. B/ E根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;
5 Q0 U. Q* B' w' _9 B+ V. G7 _. [8 @: q4 i9 d8 Z
片选信号' f" x0 D& V' Q3 n: s1 |% n/ g2 R% |
因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
! ~/ v) ?& M+ q5 a6 i& M" T1 k3 w5 ]* s3 L/ m. ]; a# A' d" w
帧格式& S+ a& ]$ K- T: ]
这里选择Motorola帧格式;
0 g3 y: O) h/ u A' a* c选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。; ^' n) E2 i" W5 \+ ^1 K! _9 i
博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
7 B, e- }0 l' v0 _7 N" s3 j博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
6 _3 f9 ?# K' Q6 ~0 V: H+ U8 R这是手册里关于TI模式的说明部分和波形图:$ C7 ^) O5 b/ U
$ A# Y, E& L, b, C6 p
* D0 x/ Q% K8 `5 Q% q
; e, S5 j; J- O8 Q6 O
/ M: Q, F, r5 s0 j8 e: ]( e F
& j5 i' G' S, }8 B+ [. }
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。9 r3 j( N6 C6 A$ \$ a
Y, e9 J2 I. P; N数据长度
5 a. `3 m4 p, t0 p1 y4 p% Q4-16bit可选,根据实际,选择8bit;
: a9 |2 b% c1 m5 {4 I/ G$ n3 e1 M* A* h" I9 L- d
数据传输模式 @6 I e- h3 |! R4 d
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
L9 Y- D: q* A( ~. M0 M" R3 h2 b. Y* C: E0 {0 T' X1 g/ q
SPI速率5 ^, f2 T% Z7 X7 n
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
6 [) e# e k: i) j8 X9 |, P3 \; @+ @ ^# p8 G! e
时钟极性和时钟相位
" T A# y9 I& w2 i* C9 F根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。9 C: q2 ?0 j' T3 ]0 y' z3 a
1 T1 h* j0 Q" j. Z" q' @" p# h/ {
CRC校验
2 p2 m" L+ c) J8 }3 |暂时还没研究到这里……
" `1 v, L) R' Y I! k
4 {# B8 G- B# P$ \0 L- C9 x: p; pNSS脉冲$ |# ]1 y& Z g) w- B8 A9 r
参考SPI的NSS 脉冲模式的作用,这里用不到;
4 J0 |( s9 m. ]% f( a/ T' |% P2 O3 J* a5 ~
NSS信号类型' W; {% F+ `& @2 A% o
因为前面禁用了硬件片选,所以这里只能设置位软件模式。8 R+ Y. D* k' ~( V& ^
/ G/ {" f! L! N9 Q
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。, q4 v/ Q% L. S; m" D
CubeMX生成的代码如下:, j1 P: ]! [7 R
4 i% A; o- H. [
- SPI_HandleTypeDef SPI3_Handler; //SPI2句柄
* t4 G+ H* Y, k3 k
& Z) S9 d8 Z* E) D/ b- void SPI3_Init(void)
' p8 F) I/ V% G/ X0 s# m7 O) J - {
6 p& G% P! K5 X9 w" Q, c - SPI3_Handler.Instance = SPI3;
2 p6 X4 z# b* G - SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
# ? m2 I. c+ l0 ? - SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
$ _0 O6 X4 A* ~* A7 Y! b - SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;$ f" Y( [2 b. E; J4 c" n
- SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;& f+ Q0 l! c8 C3 k$ P- I
- SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;9 _6 {5 _) l6 U: I9 u, B5 V
- SPI3_Handler.Init.NSS = SPI_NSS_SOFT;' u3 K( ~- U8 _
- SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;5 e2 k4 c6 J/ G4 a/ k8 _/ B% {
- SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
- S9 ?& x( ?" C/ q - SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
) r% I& ~2 Z/ M+ c; ^* ] - SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
9 _ {$ D. u# K# f) T4 s - SPI3_Handler.Init.CRCPolynomial = 7;8 {3 l' _) F: c7 N
- SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;8 n8 V7 t5 {! b! `- u* \
- SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
& ]# q% A+ \) G# n0 \, l* y8 x, K - HAL_SPI_Init(&SPI3_Handler);
& x6 p9 ^+ I' _& t
, |$ {4 T" v2 w1 u s% t5 F- __HAL_SPI_ENABLE(&SPI3_Handler);
: F1 a/ x( n, q6 V, k; g - }
% S0 C2 O7 I5 M5 h. a
# |7 Y& ?# t$ c- b- void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
3 e+ C0 W8 z- D+ |7 V - {
* C4 }3 o; B2 y' C2 N - GPIO_InitTypeDef GPIO_InitStruct = {0};& ?) ^- ~% T7 Y+ N
- if(hspi->Instance==SPI3)
( k$ _; `0 A4 F; l* P - {, h. z- R5 y7 L! d
- `% W' H8 X( X% p& h/ W- __HAL_RCC_SPI3_CLK_ENABLE();
6 X# {% J$ R- l9 z2 d0 s% d
' Q, ^, ]6 ?+ I0 z. Q. `7 L# e" M- __HAL_RCC_GPIOB_CLK_ENABLE();# k8 | R5 S# m6 d8 k
- 0 c9 H% e1 |, T: Q( W; t" k
- GPIO_InitStruct.Pin = GPIO_PIN_2;1 ^( `2 S( }& s! I
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;( u' G4 f. j e$ V
- GPIO_InitStruct.Pull = GPIO_NOPULL;- z1 K! v- v7 U
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;2 C3 o) L: N8 L
- GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
) c: Z0 @# j G: ]/ l! L3 H: Y - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
8 H: Y& b! V4 W' S - , _! M' N6 @. L8 w2 B9 Z6 q3 M' a" ^
- GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;5 D' D' C% N3 D. w; F& v
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;/ h0 G8 l# R0 i: `5 G9 z8 \
- GPIO_InitStruct.Pull = GPIO_NOPULL;
4 l+ K7 c/ x& j. E& i, Y - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;: g; G8 @4 O1 t. x+ q+ w4 u
- GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;, d2 G; e4 M7 `
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
& C8 J. K$ g$ b7 R - }
; H6 g6 a% n9 F% g - }+ i) ^8 A$ w! d) C- ~ C
- $ [; }3 ?* k9 ?7 V
1 ]2 _* O" e' L. x7 ^- static void MX_GPIO_Init(void)( O7 `; ~9 P4 V q5 G
- {; ]" k1 Q! y P7 M1 X: |
- GPIO_InitTypeDef GPIO_InitStruct = {0};
2 M; Z- w& E+ U/ V7 g
9 L) c+ }+ s3 y) N) A5 G* Q* z- /* GPIO Ports Clock Enable */. Z: R) I" `, B0 Y
- __HAL_RCC_GPIOA_CLK_ENABLE();
% k' o, _/ ^0 W W% L' {& R+ F
4 _0 G! V- Z C7 b1 i3 }- /*Configure GPIO pin Output Level */2 H; d) ^- e; Q0 n( u; |3 W
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);( T( a9 X. w4 ^& n
5 F G9 w# Z, g$ X! r# F" \- /*Configure GPIO pin : PA4 */
3 @6 r4 d$ B6 o5 E q - GPIO_InitStruct.Pin = GPIO_PIN_4;6 {4 A7 X2 L8 B7 }
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
4 [2 Y& S% y3 c) S - GPIO_InitStruct.Pull = GPIO_NOPULL;% `' r; [2 Y+ K4 C; Y* V
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;4 c1 G; R9 d. d
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
! M6 h9 W5 O* ?
[+ y* K8 k) e- }
复制代码
Y3 V0 s9 R1 `8 h6 ?* T同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:! D1 g3 X9 J, H- A y- Z
- [: `& c9 [' u( p( x! j0 Z- void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler): I: U* w+ w1 }% ^; R. W" y9 k
- {7 r# }+ m) ?% B, \/ i
- assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性6 W0 ~4 p2 O) o3 p" m1 P
- __HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI
+ k3 B) m* z; ^5 N$ `4 l2 [$ F - SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率
, b+ y. u+ w9 O% H, Y! E. m3 v - SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
0 ^" G& ? L( x1 i+ }5 f r - __HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI5 j* L1 O# m7 b. o G
* V' `+ @9 O2 h. A: f- }
复制代码 ; @8 O6 ^( Z" T6 q, B& I
因为全双工的读写是同时的,封装一个读写函数:6 f$ i+ D/ K& G# R" B
4 c9 @$ n) V: v! C! e* z: D+ [5 U
- //SPI2 读写一个字节
' Q& I" T" x3 U3 y; Q6 T - //TxData:要写入的字节
" b/ u) b' U2 c+ h - //返回值:读取到的字节6 `8 b* L5 Q( `; Z1 @- B6 X
- u8 SPI3_ReadWriteByte(u8 TxData)
6 x& p5 K/ o+ U9 |( ], C5 o - {7 N9 S+ R0 e3 b$ G6 n) |3 g
- u8 Rxdata;0 y8 O' l k3 d
- HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
, u8 j% q6 T v' ~9 Z; C! ~ - return Rxdata; //返回收到的数据
2 |8 }" y' J! k- a' j2 s5 g - }
复制代码 ' }' F1 {, l' K& j' s1 A
根据W25QXX的数据手册,写一个读ID的函数:
+ D) V6 H1 o# f$ u2 K" W/ m5 ~- //读取芯片ID& R. Z1 x6 J7 K, K$ m' Y! v
- //返回值如下:
7 t4 B3 t) w& J8 c - //0XEF13,表示芯片型号为W25Q80
, A$ J3 V) R/ `" E - //0XEF14,表示芯片型号为W25Q16 & j/ ?; G! _( p' {
- //0XEF15,表示芯片型号为W25Q32 8 g" g V4 w K* T: w, @7 F! G
- //0XEF16,表示芯片型号为W25Q64
$ o, r6 H. o3 ]) U( ~ - //0XEF17,表示芯片型号为W25Q128
0 [, l4 C8 V/ s& ?7 B5 L) H - u16 W25QXX_ReadID(void)7 R0 F( l: ]0 ]
- {
& Q' }( F% F0 e: |/ I# E6 n8 G+ J - u16 Temp = 0; ) J0 V; c: _& `( n. W
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
+ t8 m9 v4 K5 S) R' n - SPI3_ReadWriteByte(0x90);//发送读取ID命令
9 P2 i# M, l( T0 d* C+ G' I - SPI3_ReadWriteByte(0x00); - Y) S% s7 n1 N, {5 [8 W! M& x
- SPI3_ReadWriteByte(0x00); " @: Q; p! F* ?/ J% e8 K' Q
- SPI3_ReadWriteByte(0x00); 8 P+ |& t1 H# ~5 F2 X7 Q# C5 q
- Temp|=SPI3_ReadWriteByte(0xFF)<<8; 3 X o T* J( L7 n5 C/ H! X& k& v6 b2 V
- Temp|=SPI3_ReadWriteByte(0xFF); 5 u3 X' } R; B$ T
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET); ; Q/ D4 @0 h' |7 ]
- return Temp;( v" a+ r; d! s( |
- }
复制代码
A* ^; Y, i: ]6 D+ t8 s最后写一个在主函数中调用的接口函数。4 @# P2 W9 K: J$ B5 e
3 ]2 V; ^7 D: k! w- void W25QXX_Init(void)7 k3 p+ `( S+ S% [
- {
: w4 d8 A( `% Z% q& [ i( r - MX_GPIO_Init();1 E( K$ i+ V" S9 Z5 J; B$ E
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);' P/ p, V4 H4 }) r' w+ F
- SPI3_Init();5 F w* q2 J6 M F' S1 f- x. i
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
2 v) c3 q8 [% i0 B- y - uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID. 6 |7 {9 S4 n2 |; X0 x1 c$ }, X
- printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);" c4 U$ A8 u/ w J7 ^: O K7 L( f
- }
复制代码 9 T' X2 L4 i" ]/ ~
下载运行,读取到了芯片ID。- u/ X8 Q, G; W" z
& W5 G1 B( C3 S6 F) Q' M1 a7 }, z! `' g, c
8 D W8 [, W' d+ s6 c" c: d$ Z |