SPI协议的原理,网上大把的资料可以找到,这里记录一下SPI的初始化过程,以即以读取W25Q16型号为例的一个简单的SPI读写过程。& z; }% Z( A, a" E* `7 E
CubeMX配置:% {& b- H/ \2 A" J7 Q1 M1 H
" i# \3 } \( a K5 L4 P; Y, B
4 M; x8 A) q, c7 ~- `0 g& O4 |/ P2 q+ T" S* f
" x/ E7 A$ u, tSPI模式
h; S" \ q( ]有只发送、只接收、半双工和全双工模式;
- _( u6 V' }- i7 q, c) o根据自己的需求,和SPI设备支持的类型,这里选择全双工的SPI;
9 S( C, C# s, z* s2 p0 a7 M5 i' M6 m) w$ ~3 h$ y
片选信号
" T v2 r) ~4 S$ C8 e h2 q因为我们这个SPI总线上会挂载多个从设备,而且只有一个主机,所以,这里禁用硬件片选,程序里面读写之前使用软件进行片选;
/ O/ p% U! k' \0 J) V" C# h* @( j6 u- n: Z7 B3 e9 X, {
帧格式5 {2 j8 \1 B; Y
这里选择Motorola帧格式;" t. ]( a& [6 F5 I4 b! v2 G
选择TI格式不能设置CPOL和CPHA,而且必须要开启硬件NSS这里才能选择TI模式。
) z; E/ |5 z' b1 f- N- v6 I博主在网上搜了一些关于TI的SSP协议相关的资料,只有简简单单的一句:SSP总线兼容SPI,SSI 和Microwire 总线的接口。
7 Y1 w5 [1 W+ I3 d( E博主又搜了以下Microwire协议,和SPI一样,只不过片选信号是高电平有效。
6 ?4 C" C9 w4 V/ m这是手册里关于TI模式的说明部分和波形图:
! l) f8 o* `; d
; m5 t) n9 ~: s3 [
2 W% n+ q, y" A* L1 R) E) j+ F8 E3 g. Z- ?( @- C& X% ^
' [# b- |, k4 R9 T _0 W
3 c0 E5 d x* N8 |" L# m6 P7 x
也不清楚这个SCK的触发信号是干嘛的,所以这里老老实实选择Motorola就行了。
% T9 H2 O+ R/ |
# f5 n7 r2 ?+ p" M1 I数据长度# m' O M e( E* t0 ?7 I2 e: ~
4-16bit可选,根据实际,选择8bit;7 A; e# ^1 u1 @9 i
$ b& \9 h% P3 e- D3 M
数据传输模式* A- f- Y- `" {' r) D/ H
第一个传输的bit是高位还是低位,这里根据设备来选择,好像MSB(先发送高位)的比较多,注意这里的会同时设置发送和接收,如果设置错了,发送的指令对方就无法识别,可能收到的就是0xFF这种值。/ U0 ~, n x% M2 a L9 y
2 T) {! z" L) F6 T' wSPI速率
2 @0 @7 W' N9 CSPI的波特率是由时钟分频得到的,CubeMX很方便的还帮我们计算出来了时钟速率,可以根据自己的需求设置。
5 T& M& g' c% ~, n0 H/ H' B) n$ h z ?
时钟极性和时钟相位
0 F- x9 \. u. r3 `# m* x' f根据极性和相位的不同,可以有4种不同的工作模式,这个根据芯片手册支持的模式选择,注意,一条总线上尽量挂载支持相通模式的设备,这样就不用每次在设备切换之前重新配置SPI外设。+ z+ s& q7 G$ b* G+ n, v! P" |! R; T
1 R- {2 g4 {6 J D0 q) S8 V% u% a) ~CRC校验
3 V. {) t5 A. z+ ^# a3 j暂时还没研究到这里……
3 z; i5 R* d0 _9 J
# P% h8 ]3 u3 K+ j+ f& q3 A6 GNSS脉冲
, s0 ~( e c/ ?3 z$ U- p5 H) K参考SPI的NSS 脉冲模式的作用,这里用不到;
5 y$ Z; R5 J4 `8 y7 o0 X
/ x0 e8 ?7 z# o1 j% [8 K( |2 [NSS信号类型
" _( U- R4 t1 ^因为前面禁用了硬件片选,所以这里只能设置位软件模式。+ C/ [1 ~3 t& ^* M
; z& I( a: s( i
除此意外,还需要把连接硬件片选信号的GPIO,设置为输出模式。$ V) a |: V: G4 _5 R$ F$ `
CubeMX生成的代码如下:5 C8 D; d: R# O: v* i
- SPI_HandleTypeDef SPI3_Handler; //SPI2句柄2 O" ]. W) M: ~. h9 `
- & z2 v8 e$ P7 d, T
- void SPI3_Init(void)
% v! K4 Y+ K5 K; q K - {
: Z3 \ ^ Z4 I& f/ @. b9 O; H - SPI3_Handler.Instance = SPI3;
, B2 Q% a+ ]. D; [0 p. O- i - SPI3_Handler.Init.Mode = SPI_MODE_MASTER;* H0 \( P; o' W+ G6 O9 E( a& w
- SPI3_Handler.Init.Direction = SPI_DIRECTION_2LINES;
2 ?( f* p5 C1 b) v+ _# r, c - SPI3_Handler.Init.DataSize = SPI_DATASIZE_8BIT;6 K% v0 l. U) u" i; \
- SPI3_Handler.Init.CLKPolarity = SPI_POLARITY_LOW;! R5 F& O8 Z* C) C8 x: k3 D
- SPI3_Handler.Init.CLKPhase = SPI_PHASE_1EDGE;; A. ^% T3 W ^4 s! S7 z2 Y3 X
- SPI3_Handler.Init.NSS = SPI_NSS_SOFT;
Z: q/ I3 @* s: H - SPI3_Handler.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_128;
3 ?8 E0 V" v3 l% U; `& ^2 o - SPI3_Handler.Init.FirstBit = SPI_FIRSTBIT_MSB;
. _7 K! }" V0 K - SPI3_Handler.Init.TIMode = SPI_TIMODE_DISABLE;' e, \' R. K& y: l: C" O& q
- SPI3_Handler.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
4 D" [4 V' j* t2 j: r! j - SPI3_Handler.Init.CRCPolynomial = 7;
# M3 D# H4 d( k+ B# }/ p5 f - SPI3_Handler.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;7 ~- I: C% e8 u
- SPI3_Handler.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;* y$ W1 n& N( v* z5 m% E
- HAL_SPI_Init(&SPI3_Handler);9 O& `% d4 {8 @7 ]* G% o2 ]: {7 i# a
- $ x8 E6 }& _6 ^, j) q7 `: L1 j
- __HAL_SPI_ENABLE(&SPI3_Handler);
( \2 o& k2 C0 O0 N3 }/ { \" X - }5 c4 j6 r+ r$ L8 Q$ _4 V
- " r6 k8 c) V( D9 K4 K/ u
- void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
0 R s$ w5 s( f+ w; d$ Q d - {! g' w8 f7 z- O6 v# M2 O( O
- GPIO_InitTypeDef GPIO_InitStruct = {0};
1 l+ _7 a5 @, S4 x - if(hspi->Instance==SPI3)
4 i4 O7 I$ z; I) t F% o. P5 r - {
' g& C1 C& m1 m3 f* c" B1 ^
, @0 ~/ {/ i' |- __HAL_RCC_SPI3_CLK_ENABLE();9 u9 P( d$ ^4 Q5 h7 [: X0 {6 k9 p
- % d- C# N' A& I- Q7 X" g! Q
- __HAL_RCC_GPIOB_CLK_ENABLE();
3 r, |0 ~2 H* }
2 d% _) h/ r" @5 U1 i! J- GPIO_InitStruct.Pin = GPIO_PIN_2;- k: \ t! V7 f: X
- GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
1 t v/ H, A: O - GPIO_InitStruct.Pull = GPIO_NOPULL;" _6 `/ w# a, j4 a" q. A2 c
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
3 }2 b* ~" r: N- j5 B! O5 f$ w9 _! i - GPIO_InitStruct.Alternate = GPIO_AF7_SPI3;) H( Z: \$ W R
- HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);8 W, {! M) C- M; w, D
/ ]8 k, ^' J) p- GPIO_InitStruct.Pin = GPIO_PIN_3|GPIO_PIN_4;
& H8 p: o! H6 M. x' k! q" | - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
) w4 n0 n- p f/ P' S; @, S - GPIO_InitStruct.Pull = GPIO_NOPULL;
9 J! G" U7 {( I2 Z) e! D% I& ^ - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;3 D) y# f2 _9 Y4 R4 }
- GPIO_InitStruct.Alternate = GPIO_AF6_SPI3;
- u+ D: C3 A( @9 K$ l. e. K9 K8 {! H - HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);7 S% j, ]5 a( v, O# l
- }
( o% e1 [# Q& ^5 ` - } w$ m+ h4 j" R! w
- & }; U [- \0 C% @
- - D/ |* @* Y: [* O" x
- static void MX_GPIO_Init(void) A: r* z. r% v) e
- { J7 \; r2 C# D' }8 z% V( F, X0 B) f/ G
- GPIO_InitTypeDef GPIO_InitStruct = {0};, ^' T+ _4 @' J! [" @: {6 A) o7 H
6 E' J, q' e/ w- y. P# H- /* GPIO Ports Clock Enable */
' v, P+ c% ?* ^, m2 e3 U - __HAL_RCC_GPIOA_CLK_ENABLE();/ k. e1 W4 D& L0 h+ O4 o5 o
- : d; g4 d! X' L3 M! S
- /*Configure GPIO pin Output Level */" k' h8 p$ z- h4 X; Y
- HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);' Y; T4 J2 S- g1 m
- 6 d% q$ c3 b9 R+ W1 v" g
- /*Configure GPIO pin : PA4 */
. x0 @, B5 j$ B w* ` - GPIO_InitStruct.Pin = GPIO_PIN_4;* ^. o+ d2 D; u) \' _: t
- GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
0 w5 Q/ L- I& D! u3 N - GPIO_InitStruct.Pull = GPIO_NOPULL;: i3 F; b7 q8 U
- GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; k3 p" T6 n$ f" J- e' L
- HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);1 L) [2 |" n7 G6 R" F' Z- A) A
( W6 h3 r2 X; v, `0 C7 [- }; e+ D4 f7 I, {
复制代码 2 H, `. G. Q- V& B: e" K& w$ i' F
同一总线上不同设备,工作在不同模式的时候,速率可能不一样,需要一个重新设置速率的函数:
L8 s% z% W! `) o6 x2 l- void SPI3_SetSpeed(u8 SPI_BaudRatePrescaler)
3 Z( ]( o# L0 ] @* S0 Q3 V& g8 \, ?/ x - {* p o2 | K% k% q" O6 a8 H
- assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性' q8 Y. I& q$ p3 ~* f* _+ [
- __HAL_SPI_DISABLE(&SPI3_Handler); //关闭SPI5 l9 K' X6 J% o! {( x4 L5 F
- SPI3_Handler.Instance->CR1 &= 0XFFC7; //位3-5清零,用来设置波特率
/ O" V$ V" W4 A6 D. [# N3 S - SPI3_Handler.Instance->CR1 |= SPI_BaudRatePrescaler; //设置SPI速度
3 X4 k& y; ~* ^+ H8 ~# k - __HAL_SPI_ENABLE(&SPI3_Handler); //使能SPI
8 L* k# P; K& ]# m - 6 M7 B" o6 e7 v1 [ q( x
- }
' B% I5 r9 ~8 b3 c% P: ~ p
复制代码
5 ?, N8 k+ T; m" V% V6 ~因为全双工的读写是同时的,封装一个读写函数:8 G! `' P5 d* t1 N6 q; \8 E
- //SPI2 读写一个字节0 ]) x( _3 P% O9 ^" K
- //TxData:要写入的字节. v; V' x3 ~4 {
- //返回值:读取到的字节
- L9 T% r# q6 m4 W - u8 SPI3_ReadWriteByte(u8 TxData) J( G* S/ I: W1 r, N0 W
- {
' ]$ `& `1 A8 z+ ?; @, z: N4 V! v - u8 Rxdata;. H6 F ?1 f+ K
- HAL_SPI_TransmitReceive(&SPI3_Handler, &TxData, &Rxdata, 1, 1000);
, c3 g8 x) r$ t) s - return Rxdata; //返回收到的数据
- p: _( J/ { _6 p2 t - }7 M7 U" Q; k6 W% _; P
复制代码
" a1 j, B y5 L% r2 Y% k( u$ L! [. e根据W25QXX的数据手册,写一个读ID的函数:$ ? B2 p8 K! D% k
- //读取芯片ID0 z/ k& |; H% q; _- F: J+ B+ x$ x
- //返回值如下:
- G, o- y- S# k9 w2 v0 @8 j - //0XEF13,表示芯片型号为W25Q80
6 Q' r' m! s0 q2 ^; H$ V- ~( o: C - //0XEF14,表示芯片型号为W25Q16 ! m* {5 [% t& f/ U+ F
- //0XEF15,表示芯片型号为W25Q32 & k! ^4 x: y* E! q5 }' K
- //0XEF16,表示芯片型号为W25Q64 % [$ S' {- j# p8 u
- //0XEF17,表示芯片型号为W25Q128 4 w$ W& F: P- |- c# N% q! y+ ?* h
- u16 W25QXX_ReadID(void)
. H! L( [; ^1 S. ?2 Z9 w - {; H5 u) p% o2 k+ {- Y& q
- u16 Temp = 0;
) F; m6 W' Y% i& k% Y - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET); 0 \! Q- i. _2 i$ A, p; o
- SPI3_ReadWriteByte(0x90);//发送读取ID命令
; v7 a( w+ E5 F - SPI3_ReadWriteByte(0x00);
$ r7 |* g$ z7 I. K) z - SPI3_ReadWriteByte(0x00); 1 Y5 \" V3 k( h' B
- SPI3_ReadWriteByte(0x00);
1 S" d6 T! C0 L - Temp|=SPI3_ReadWriteByte(0xFF)<<8;
& w% X4 N( u# k- E6 e: i) H - Temp|=SPI3_ReadWriteByte(0xFF);
+ S: t2 _0 J7 K4 o - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET); $ r6 S' K& z; z' R O
- return Temp;' ~- A3 d- @) ~. L
- } R; d l( h9 F8 ^! u
复制代码
, ~' B" @# C$ T4 F v+ y( E; s最后写一个在主函数中调用的接口函数。
* U; \' {, M. S% V5 G- v0 r9 x- void W25QXX_Init(void)
) k: [0 D$ Y1 P, x; w - {/ c8 T% I3 m; `2 Q' l7 s7 _
- MX_GPIO_Init();
/ v; e9 m) Y2 ?$ ]% y# p - HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);# y9 v( w, [3 ~* @) p4 w, d
- SPI3_Init();
8 h: I# u- L. i/ B, Z6 Z. I - SPI3_SetSpeed(SPI_BAUDRATEPRESCALER_4);, Z v& c+ C! y
- uint16_t W25QXX_TYPE=W25QXX_ReadID();//读取FLASH ID.
- G5 d) u2 r8 a% L - printf("W25QXX ID:0x%4x\r\n",W25QXX_TYPE);
" l) y$ _: B) b7 v H - }
/ r+ _8 G& G5 v2 y1 O, v
复制代码 8 h. M* X7 c1 M' q3 @
下载运行,读取到了芯片ID。7 p/ B f' @/ J( W1 a
! z3 B7 S3 z7 c) i/ j0 a
6 {+ u. t4 X) }0 ~- t% H: r$ r
, ~, ^5 p5 ?3 j! O1 K/ n————————————————1 f5 ]7 s* S; {5 u! E6 J4 g
版权声明:小盼你最萌哒/ q4 K$ R( s5 c g1 d" Z5 x
: t, F6 H$ }$ F' S3 i
! ?/ e5 ^, J# N+ |( U
|