SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。
' d8 h/ V' v% g+ \: s: M- u! P, mCubeMX配置:0 T3 I) |$ m1 O+ G1 Z' M
: p# Z, ]: a4 f
: R4 B- B& T) b9 V; _/ r
( I* ?7 J1 w& q) w
SPI模式2 {# r' P! m% A9 @" C
有只发送、只接收、半双工和全双工模式;5 q! { @" i, m; F& [
根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;8 ~2 f; ?5 I- a8 t. q+ J% T0 _7 E
7 E! ^, s) L5 t' l
片选信号: t3 F) m2 D2 ^0 m6 c$ i; r; |
因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
$ Y, O+ z. L$ H" F' d! H; v
5 S+ c( F# {* p! L# ]/ o* W& }6 `帧格式9 w- Z8 T' `' k9 a% W6 s7 a7 G
这里选择Motorola帧格式;) H8 J8 P; Q7 r
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
2 [8 `7 m8 n, S3 @: @博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。; w/ E- |0 ^. r$ X( R3 j
博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。& ]: x: y, X# Y0 D$ M/ p
这是手册里关于TI模式的说明部分和波形图:- N( ^" J3 q7 D& r' {. U
; _$ n. r1 R* r$ Q8 i0 h8 u! K
6 t+ |$ I. {1 z% h- `' i3 }
* N7 i) @) B+ p
! G5 Y. @& Z5 ~& n6 i" T' k1 ]7 f' G+ V( [4 ?
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。
O3 P! q& d. o( L2 x8 p7 Q4 b7 s
2 m" F t8 f* B v8 A% K, D数据长度6 n( i0 L7 C6 i
4-16bit可选,根据实际,选择8bit;
6 g4 }0 U7 w) J$ i+ w% C. N6 `6 w* A( C1 V8 p, a s S6 \
数据传输模式 t# K$ V' a+ L: \
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。
( `7 J, b( [; o, w! @3 h7 B& M# [+ |. E6 V. o" H9 q
SPI速率 P, p& M- g2 ^0 s) `8 q! h
SPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。, e' t5 W6 O( x2 u$ u" R/ J8 V. A( v
& _; d1 H( M* r: v# i/ w
时钟极性和时钟相位6 A6 s p: }' [) Z% D2 R
根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。; X, }! F. A# ?
7 x: k+ m( ]" R! K! F' SCRC校验$ d3 M+ ]) ^- k( ]# m# j* S
暂时还没研究到这里……8 b- ]8 a, Z# _8 i/ L& J1 g& B( @3 l8 |
4 {# {9 o! M9 |4 K. z' UNSS脉冲
1 j5 b3 o, l+ [4 q参考SPI的NSS 脉冲模式的作用,这里用不到;
% N6 k G9 H0 F* n3 g& F% F
9 b+ V; Y7 i( _NSS信号类型2 U5 `( C4 i0 e7 w5 Q. I* t* V
因为前面禁用了硬件片选,所以这里只能设置位软件模式。" k6 N9 n6 J/ j
f ?9 e, S$ e9 t除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。
7 D' O# q: a' \4 w3 x1 jCubeMX生成的代码如下:- c& \. m* E% U7 h; l. p+ F
1 \7 r4 a$ g& ]( b6 `% n
- SPI_HandleTypeDef SPI3_Handler; //SPI2句柄+ g) w9 S% c) V* X7 m0 r
* w' T' e: K4 L, W" r; y+ e1 c- void SPI3_Init(void)
0 L; s' O' f- [) g7 V8 P - { W j! l) f& q# b4 D( {
- SPI3_Handler.Instance = SPI3;
' _& G9 \1 l8 H; \8 ?) A9 W, T - SPI3_Handler.Init.Mode = SPI_MODE_MASTER;
* ?8 D! ]' L8 E( c - SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
5 r3 i) f9 _* }9 x1 v2 k - SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;
2 y8 G& |: z# U- C3 h - SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;
# G" v) B) q; M { - SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;
% x8 v0 |% W1 j2 s - SPI3_Handler.Init.NSS = SPI_NSS_SOFT;3 U M$ h l/ `# \
- SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;5 j0 B7 K7 \1 M0 z+ h" A$ D
- SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;7 b% ^1 K) G2 o* ?+ i
- SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;
D- s& |8 }6 o8 l n& j+ E/ x7 R - SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
# s+ P4 z, b( w" ~4 h3 b7 m& R/ ? - SPI3_Handler.Init.CRCPolynomial = 7;* d, R. e6 n B5 H9 ]
- SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
4 d+ n& a+ z/ w) w - SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
+ B1 s9 w& k6 x - HAL_SPI_Init(&SPI3_Handler);
' i6 r3 {0 ^. j - + V' Q+ n9 B) l4 u) T
- __HAL_SPI_ENABLE(&SPI3_Handler); , A" @1 q @8 ? {
- }
" ?/ x6 Z. x# D$ F: N% T
* j/ ^! W5 N1 e. p- void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
+ y8 p- N# s/ u# f M5 o - {6 M! k/ _7 t6 d4 [2 ^8 N
- GPIO_InitTypeDef GPIO_InitStruct = {0};
" D; e I% r* w& h- l* s; [$ f - if(hspi->Instance==SPI3)
, s* ]( C, ?2 v8 ]6 d - {9 p0 {6 H& Q9 N% ~! k; P8 A
8 a' u" U6 N+ |: X2 j- __HAL_RCC_SPI3_CLK_ENABLE();
, v2 E3 m& Z/ g+ {7 Y - 0 ~8 j% w- ~ q0 f4 @' {! n- _
- __HAL_RCC_GPIOB_CLK_ENABLE();2 N3 N& ` |8 u9 b
; L" q/ g3 H7 `- GPIO_InitStruct.Pin = GPIO_PIN_2;
, V- O; n. ^+ F a) W - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;% E# N8 N w( [& l5 Q
- GPIO_InitStruct.Pull = GPIO_NOPULL;8 R% U2 @/ s* j4 [" M
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;1 `! Q {6 r1 F: |% v0 ?
- GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;
; O% c, D! X& D7 D4 ^' E - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
, m. U) Z! d6 x7 Z5 x c- C+ _
8 X6 Y" Z* O' z6 p* b! \' ^1 u# Q- GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
, Z+ v9 D$ k- u* V& l - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
. I( ?* ~ N5 j - GPIO_InitStruct.Pull = GPIO_NOPULL; V0 a. n6 K3 _$ O# o- n
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;9 q$ B% X" J+ Y/ I& d2 a
- GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;0 A7 _* }' \+ P' s# ]+ q2 \/ C
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
# X' d1 N/ N% }/ ? - }/ X1 P" ~* b1 ^; t' \* ]
- }
q) }( v% l4 L9 l - ) j- S4 H" [& z& g7 S8 G) i0 ^
- # i ^$ ^( x7 D3 u
- static void MX_GPIO_Init(void)! e$ B) @3 @1 S: ^2 ~- W2 w% M" @
- {% q2 X: ]7 ?2 ~* ]5 ?4 m
- GPIO_InitTypeDef GPIO_InitStruct = {0};; o$ U- P* g5 e7 b: Y$ b
- 6 X% `; Z1 A% ]' f/ u! S: a
- /* GPIO Ports Clock Enable */
/ A, f. E \7 G6 _" u& j4 M - __HAL_RCC_GPIOA_CLK_ENABLE();3 I( K' v5 g# h( m ~+ P
- , n6 H# O' c; }8 N0 ^
- /*Configure GPIO pin Output Level */
1 o* _& [2 Q2 N+ v - HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
( L, B8 A4 K) U* _6 t1 M - ) Y% K; G+ W- R% }3 V3 Y
- /*Configure GPIO pin : PA4 */8 I/ e2 ^6 N) v7 T- M
- GPIO_InitStruct.Pin = GPIO_PIN_4;
L- v {& t: H+ _& G: } - GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;# o. ]( C8 T% r, y
- GPIO_InitStruct.Pull = GPIO_NOPULL; j- n+ m2 E) J8 R( V5 D8 `4 I
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;' {& ], G% d* r5 s0 k0 l0 h( }+ L
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);6 m4 ?8 Z( E+ O$ H
- ' c3 ]0 R) H! g$ `! q
- }
复制代码 9 ?7 t; b! _1 x- Q# H5 g! L
同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:0 e' \4 E! ?4 A
( i! Q R: j, E, q8 D- void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)6 m W W5 i. X- G0 E. \+ G+ u
- {
1 Q' a* |: {, \! e - assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性
! x. T- u- h. o9 k - __HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI! t& u- U6 n1 }: _, ]5 L
- SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率$ Z ^# S; K' @0 S
- SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
/ [7 J" u0 _9 o' L; c - __HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI
, q a. Z* a- j0 m. R' S; [; o - ; c; l3 q8 W! L
- }
复制代码 3 U3 i" H) c- u% `
因为全双工的读写是同时的,封装一个读写函数:
8 W9 I3 B1 n; B z1 U7 W, M$ |0 K6 Z$ F/ u6 `' J3 W) T
- //SPI2 读写一个字节4 k$ t4 x' U7 h. ?
- //TxData:要写入的字节
$ t0 M: B9 Z9 f2 b& T# e - //返回值:读取到的字节
4 n, I3 {3 c. t: p7 F. G - u8 SPI3_ReadWriteByte(u8 TxData)' L; e7 m4 ~" P. v3 f) O
- {2 z4 |% \% D c, q5 K
- u8 Rxdata;
1 K" D' m5 U1 w4 V+ o# p5 ?1 w" [ - HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);; [; b0 n1 x( {. |6 E
- return Rxdata; //返回收到的数据
O0 I4 y; ~6 F2 _/ x3 h - }
复制代码 # Y9 ^: C+ P' K, R% _
根据W25QXX的数据手册,写一个读ID的函数:
4 M& y7 j. ~) w# l6 f- //读取芯片ID% _0 S' ~1 y6 m: @: A) k3 A* W
- //返回值如下:
& H2 `* O' c- |! n - //0XEF13,表示芯片型号为W25Q80
! f5 {! n1 `9 f - //0XEF14,表示芯片型号为W25Q16
& |+ f4 F$ @9 g e0 o6 S6 U2 \1 e - //0XEF15,表示芯片型号为W25Q32
! T$ n# S" b/ g8 J - //0XEF16,表示芯片型号为W25Q64
0 W% F# i' p" k1 M ]) Y - //0XEF17,表示芯片型号为W25Q128 9 q x+ j; v( {8 e: u
- u16 W25QXX_ReadID(void)2 B0 t) l, P% ]4 X4 r- K6 c F
- {! K) h. p0 J! ~% R
- u16 Temp = 0;
2 D# {7 \. e: [& f: f - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); * B5 u9 x4 z1 e4 g' ]: Q7 ~! e
- SPI3_ReadWriteByte(0x90);//发送读取ID命令 4 }( |) V. b, f
- SPI3_ReadWriteByte(0x00);
$ L1 Z9 M) h6 W+ w2 B1 v. O- _ - SPI3_ReadWriteByte(0x00);
5 Y7 I2 j9 q( L5 X+ U - SPI3_ReadWriteByte(0x00); 1 r7 k; |& O* ]- }* e6 |7 d
- Temp|=SPI3_ReadWriteByte(0xFF)<<8; & Y* M* C! w9 ^- N8 `/ c
- Temp|=SPI3_ReadWriteByte(0xFF); # d, t/ ], x7 L" q& g' }) u9 W
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
* U8 R( q( g0 K( T K7 `5 M - return Temp;
4 `. ]2 _# A4 Z3 k1 {- h+ Y- |3 _ - }
复制代码
7 L% o- [* i6 S最后写一个在主函数中调用的接口函数。( g3 b% f5 d% R ]$ i9 p( h0 ?
2 f: q+ q I* ?; B
- void W25QXX_Init(void)
5 v2 D+ q* ^" A3 ^& j7 L - {
7 i" f0 t) l- k k0 E5 n5 \ - MX_GPIO_Init();/ m" x+ h O- C5 p5 K% D0 E
- HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
" Z; ]' C5 u. V$ ~3 i, s* ?8 t - SPI3_Init();- F# h; a& R% o9 C. d7 U @
- SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);
) l4 X. C) ]7 V) x3 j5 T; U+ G - uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.
# `1 h! K8 Z5 u' d1 Q7 u8 N/ l - printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
; N% T& Y- y7 L6 d6 Q0 l3 z" A - }
复制代码 2 h' w! J: [* L# C: i( V) R) \5 l# c
下载运行,读取到了芯片ID。
( b' @! z5 ~; t Q* C$ I5 c4 J6 `" b) g3 c7 ]- Y" t
0 e2 |: t- V1 h/ n7 B6 B1 D
4 a- r* o( W: Z$ g0 i5 o
|