SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。% @ ~( D) p* X C1 L" O. F( U
CubeMX配置:8 }$ y7 y% d9 d8 e8 H9 h
& X1 U* ]% F& b3 H: x2 X
( t! @+ E; T; V ~ ^( R- U; h! [ [# }1 |- i
SPI模式
; c( ^1 p, w4 T! L有只发送、只接收、半双工和全双工模式;
' P- L7 h1 F& t9 x根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;
1 K: w; e- `# s% a. H( W% v! X/ E I( Q( k) {3 ~! k
片选信号
# c" r2 w' A* Z! P3 e因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
& X A, p) B/ }% q/ P3 o' I
; x* M' z4 } p$ Q- M6 t帧格式
2 K# i! O4 Y/ K: W3 M J% Z这里选择Motorola帧格式;
: k" H3 L& G ~选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。* E# }2 @' M- g9 I7 K
博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
6 D S& Z h) a6 j2 B# ~博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
- i6 x$ ^+ S# {% b- {这是手册里关于TI模式的说明部分和波形图:
8 x' F$ s1 y' J Z! h9 y- ]. h9 H, q# H( I1 \3 i; U
; m! {1 d! i" Q t! @7 u8 D
' r. H" U3 b* ]" @7 A! B2 n7 d$ A, B' p& v# U8 S; U7 h
, j) H& j1 j) a4 ^) {# }% z也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。
$ ?5 l& ?2 Q- _+ k6 f" i% ~4 D1 O; ^: {; p+ b
数据长度
: T3 a9 L# Z4 O1 ]4 b4-16bit可选,根据实际,选择8bit;
; B( _0 {/ ]/ }; ^ l1 L7 u, k ]0 D) r
数据传输模式( ~; V" p5 W- }9 J4 O
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
" A/ v; r2 }: p! O0 s W8 e r1 D G
SPI速率& u& p/ Z0 M6 Q0 G4 T
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
" h9 c/ {9 X2 h% I) i) V
W5 C% j/ {3 o& K" o时钟极性和时钟相位/ f3 \: L$ a( ?( y8 O: E1 |9 k
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。6 N6 C s% p! W( |. \3 B Z' @ O
+ n& ?8 P* Y p1 O& y8 K9 ]CRC校验6 @- Y- ^- z2 U
暂时还没研究到这里……
/ e+ p% c# h$ c( ~ |8 K8 L0 F0 @" A" a7 X1 v, [. T
NSS脉冲8 E% ?$ |& l+ R3 E7 l) u6 i9 T
参考SPI的NSS 脉冲模式的作用,这里用不到;% @7 R4 M4 `! Q6 B5 _; Q
9 _1 J4 t9 S2 V: JNSS信号类型
a& Z3 B5 |3 `) ]5 R因为前面禁用了硬件片选,所以这里只能设置位软件模式。7 A) h7 V7 J/ l8 _ j$ M- R5 g
5 e0 e/ w# b: w2 M除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。% |8 V6 m7 H* v5 |
CubeMX生成的代码如下:" Y& Q9 c* T# c0 N
4 B1 I1 q7 d+ M, M
- SPI_HandleTypeDef SPI3_Handler; //SPI2句柄0 L i) O/ v# ]9 K' v, }1 g& z
- . j" W6 G/ \6 e3 b" B
- void SPI3_Init(void), `+ [6 Y- g8 W: V
- {: ^, N; |" F. ]; }. @5 k+ a
- SPI3_Handler.Instance = SPI3;, b, v0 S! J$ d
- SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
! s' _. P1 v. [9 V6 s! W - SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
3 A+ U' z! Z4 [& ?( [6 Q" e - SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;0 g# ]3 O. `* Q# l
- SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;1 B: d& S$ v4 _, \: A- q) N
- SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
6 j7 [' H8 s- Y$ T) k7 B - SPI3_Handler.Init.NSS = SPI_NSS_SOFT;5 U/ p) N2 u9 A, P& e5 V! s
- SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;, C* D! U8 d/ |% F5 Z
- SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;; M. i9 A- C! |* U3 V
- SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;6 _$ v% e4 d8 D9 x& j5 \9 @
- SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
! w0 w- x9 M! J) ?6 @" S - SPI3_Handler.Init.CRCPolynomial = 7;
3 D8 ~* ^) U f( [3 o - SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;( D7 [! U) |( l- w
- SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;$ e( ?; K& u; B5 f( Z) C6 @
- HAL_SPI_Init(&SPI3_Handler);9 }( h+ p: A- S8 N
3 P& N6 d/ i; H& r- o( v- __HAL_SPI_ENABLE(&SPI3_Handler); # m0 W5 U: C- U1 k4 D
- }( R& p% @ L) y4 {* t
- ( a4 x& n4 i2 I* i0 w
- void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
# m+ f/ P9 P0 k - {1 g7 U0 S3 G. g! k1 h" J
- GPIO_InitTypeDef GPIO_InitStruct = {0};& C5 s7 C& q- }( u% [1 {7 Q6 D
- if(hspi->Instance==SPI3)' w- k8 Z" _% f |
- {5 t* f1 y8 r% _
~) A6 R6 G( A. m/ S% k- __HAL_RCC_SPI3_CLK_ENABLE();
/ c2 b- J6 C H2 E - ) d# U' l6 W# X2 Z. f
- __HAL_RCC_GPIOB_CLK_ENABLE();! U( B+ S) a- ?# p
- 2 ~& V& G" K" y
- GPIO_InitStruct.Pin = GPIO_PIN_2;& ^# G% J1 y, |1 @) U. }+ u
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ r( W- A6 l, n - GPIO_InitStruct.Pull = GPIO_NOPULL;. m: m5 p# h9 j i& W* c
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;. K2 |9 k( Z: A1 b$ O7 X2 `
- GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;$ b, @2 p. F3 z9 c/ H+ v
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
5 ?2 y: d/ s6 c - / d I4 Y" j/ y* S
- GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
: h, e* s2 N% B" f% Z- } - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;$ o4 C, a1 ?7 @2 E0 N3 L
- GPIO_InitStruct.Pull = GPIO_NOPULL;
- d8 Y! E1 `- J" O! V8 z - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;, I+ [; H! y% n0 ^1 `" r
- GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
( j B* v& g" x7 T! ] - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);, k( h/ Q% H$ H, R2 L0 F
- }
8 y f8 e$ ?. k - }
/ D/ h2 d& g2 q8 X/ Z' r
, K7 `. m8 Q( R1 e7 ~/ d( M
! `" ~3 f* ]. G' e- static void MX_GPIO_Init(void)
4 t1 P1 m4 I9 c0 f+ m& }, y# X9 Q - {
5 k7 D% [$ J: |: R3 e, c - GPIO_InitTypeDef GPIO_InitStruct = {0};
9 R3 i% w8 H, Z! t9 T2 u- f
4 l2 w' b* v- ^9 |% \- /* GPIO Ports Clock Enable */
, B# Z- G; i/ }/ T+ Z - __HAL_RCC_GPIOA_CLK_ENABLE();
1 v* Z, Q4 |1 T4 v - $ s: B1 w% z5 a* A O' D4 D3 V' M
- /*Configure GPIO pin Output Level */
+ c( x. q, [+ ]4 H/ i9 R& ` - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
7 g8 p- x# }8 K3 T4 D6 @* R( U& _
: s* @( P }* x6 \3 `- /*Configure GPIO pin : PA4 */( B/ j2 D6 r+ r: M" Q. U0 [
- GPIO_InitStruct.Pin = GPIO_PIN_4;; V& } h- `2 p
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;* k; }7 o. e; W1 D) d/ I' {" K7 r
- GPIO_InitStruct.Pull = GPIO_NOPULL;+ j; Z. W: ^% l+ s4 ]# I# @, R+ M
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
% q% o5 x0 X* H/ Z: t - HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
: ?) r: f4 B3 m L5 X
% q: c& I& F3 h& L; C1 V7 _3 _- }
复制代码
: l( l% H. u; p# r' d# Q# J同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:) U! f3 g% o+ ]+ w9 R% A. z, ^
8 u' y- ]2 C' [4 _- void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)* ?) S/ a& e" [' D6 }
- {2 ]/ }% f1 p/ W2 R- s8 A& L
- assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
6 z3 j! A( q/ [+ B( _9 Z - __HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI' T n ~1 F# H( d+ A
- SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率
: t' ~7 e* P6 g0 e; J - SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
2 e* o8 f% s* b) f' \7 ` - __HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI: b D. w( M& R6 r* E& |/ c3 V: I C3 _
- / B i" C- a' |+ z7 j2 ]( C
- }
复制代码
- n' a8 ?8 D7 l1 ]; s因为全双工的读写是同时的,封装一个读写函数:
3 M3 q, c1 l% y8 R1 w4 ^" w+ Z
' w3 @/ d" x3 O9 C; V9 q- //SPI2 读写一个字节) S- B7 f5 B( g4 i. ]# m. P
- //TxData:要写入的字节
) D4 c m, ]# h' {; c - //返回值:读取到的字节 s" [5 p- k1 n2 o# K% S
- u8 SPI3_ReadWriteByte(u8 TxData). D* j$ m/ [5 O: D2 g9 p& j
- {
' R X) e; D3 _; W - u8 Rxdata;1 X( Z) r2 A& R
- HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
8 @9 ?+ A6 G; T& V$ A( M - return Rxdata; //返回收到的数据" h, [: x% x M
- }
复制代码
` ]# D! c$ p- n根据W25QXX的数据手册,写一个读ID的函数:
* z$ M2 ^" Q! c. L- //读取芯片ID" T; F7 H7 T- @3 [, N) |1 t( P
- //返回值如下:
: k" y' @* y* { \) ~) S7 N2 R, ^ - //0XEF13,表示芯片型号为W25Q80
1 W: I2 ^% e, J) G* r0 Q1 b - //0XEF14,表示芯片型号为W25Q16 & X/ i- c' i) o# t* k0 T% V8 J% u
- //0XEF15,表示芯片型号为W25Q32 : G: S6 R/ ?1 Y7 N
- //0XEF16,表示芯片型号为W25Q64
" p2 L8 x. h+ a8 S" w1 l - //0XEF17,表示芯片型号为W25Q128 $ V$ [, @' E2 W u2 \" w
- u16 W25QXX_ReadID(void)
8 c: a3 }; S+ v( O7 m0 S1 C( F - {
9 ]4 }# n! f: f8 H& P9 F7 u. A# A - u16 Temp = 0;
! \5 M- _: U5 Y4 g, H' q" i - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); $ p+ g3 w1 Y) Z- `2 g( }
- SPI3_ReadWriteByte(0x90);//发送读取ID命令
4 b" B5 Q& W# M5 \; T) V! k% E$ I7 u - SPI3_ReadWriteByte(0x00); + X$ g- F1 m& I
- SPI3_ReadWriteByte(0x00); 1 |5 a* l4 @7 e- A6 Y
- SPI3_ReadWriteByte(0x00);
4 w9 H( L, @, m& k9 h& l2 p+ S, H - Temp|=SPI3_ReadWriteByte(0xFF)<<8; 8 C X0 l/ g; p8 a( ^
- Temp|=SPI3_ReadWriteByte(0xFF); : A$ I- d9 `" W2 c: O/ w& q9 m
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET); 6 C! M* R9 H5 v% p
- return Temp;% G- v. W' c- J
- }
复制代码 2 j$ x- w- R% v
最后写一个在主函数中调用的接口函数。& p! d& W J) |- k# _: n+ q% r
. Z0 |. N$ N5 S) P- void W25QXX_Init(void), d4 i q5 j/ G6 `; V
- {/ ?, h0 Y2 S- a) e$ m0 p# U# X3 w
- MX_GPIO_Init();
4 w* x& F- Q+ S, n$ p - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
, V9 f! S* v ~ - SPI3_Init();1 z8 b9 M# X$ [9 ]) m
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
% p: q3 p. Y7 Y/ b) u - uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.
* R6 R/ U# H5 L9 ?- N" f5 m( B - printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
# N4 r# T! g$ d - }
复制代码
7 ]. L+ K8 m( L8 p下载运行,读取到了芯片ID。
5 h2 w7 z2 A- u" o \" p Y; x' p( b
5 T2 a' R* i) g
( B; U1 z4 @5 f. R
|