SPI (Serial Peripheral interface)是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。# w G- i$ G; g3 R6 R% _" {0 A
- j/ z' w( k% h
% M3 `4 v) [8 [0 C- i
9 Q* U: x7 H; S: P8 F0 M c" `今天分享下,战舰v3与C8T6工控板上的SPI通讯实验,其中战舰v3作为主机,C8T6工控板作为从机。
9 |3 K: q) C. F, I8 e/ U+ @ Y* p; y' ~1 G% u/ Q4 y
实验功能说明% O% S$ {# y& U3 k1 G1 [! y4 n
程序分别烧录战舰v3和C8T6工控板之后,通过RS232可分别获取战舰v3从C8T6读到的数据以及C8T6从战舰v3读到的数据,并且战舰v3的PB12可以控制与C8T6工控板的SPI通信状态。
; I3 l6 T2 j3 R9 `! f) u1 N1 i( i! r" k0 o: T+ k
硬件部分
- f! e$ y- a3 u# R1)正点原子战舰v3开发板(下图所示),350¥左右;
! o0 ]9 _' v9 w2 E$ v' j5 q/ N0 D% t& O0 q, T! f4 {- M! i+ r
* i+ I( O0 d' T1 Z. [4 K
! B# D/ _; ^" L9 {' d
2)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;
, X K$ } F; R, h! h! `3)某宝网上购买的232转USB数据线(下图所示),价格15¥左右;# o) ^% ?# D) }" S" B
6 G( |" U6 Q$ J% p3 J" c' Y7 ^
% r; A1 r( P P! A/ N$ X" n& M4)杜邦线若干,价格几乎为0¥。
/ U' Q" J+ z% k2 a% J+ p- H% }' U2 |3 G) C! Z5 W) x; i
硬件连接
4 x# A" a- O2 y, s0 ~2 M按照下表用杜邦线连接好即可。! h& ~4 |2 `2 X2 Y' l
, W/ ^. m* f, ~3 B
( r7 Y, n6 W4 U/ _9 Q6 G
0 S# S0 p7 M7 w* v$ y+ K5 O7 ^6 M5 O* [ H1 b. K
部分代码6 f" Z& j4 ]. ^7 y9 \
C8T6工控板的代码可参考互通讯实验中对SPI1的配置,SPI1初始化函数只有一点不同:4 d1 P6 T( R7 Z, I
+ m8 I# @; ?* D2 [- SPI_InitStruct.SPI_NSS = SPI_NSS_Hard;
复制代码 ) f5 G1 A2 w! I l t2 ]
战舰v3代码如下:
( [( R0 o$ n4 F7 `( `$ a) _1.spi头文件
5 L9 q: j0 [( J. g' k2 Y1 U8 T& F" j- /**) F$ i3 `( Y2 e1 x* F3 p
- ******************************** STM32F10x *********************************
$ l2 w/ {1 s2 S, J6 B+ v& C$ H6 y - * @文件名称: spi.h" z- ^% E! W3 e
- * @作者名称: 闲人Ne8 Z: e) ?6 n& m! m9 s! a
- * @摘要简述: SPI头文件, |4 f! z3 `, j- Q5 F
- ******************************************************************************/
( ] W& e) O3 Q0 K9 n - #ifndef __SPI_H+ j* R/ q! t0 U
- #define __SPI_H% f0 `" ^" f5 ~4 a, A F( u
- /* 包含的头文件 --------------------------------------------------------------*/; T7 Y9 O7 l% c3 v0 o# m4 \" w
- #include "stm32f10x.h"* b( s2 M- U0 d" \) l$ W9 [+ a" O
- /* 函数申明 ------------------------------------------------------------------*/0 u* h9 b% O+ g' I
- void SPI2_Init(void); // 初始化SPI2接口, r2 t1 S" w0 V5 K
- void SPI2_SetSpeed(u8 SpeedSet); // 设置SPI2速度
3 i+ M5 w: R" y - u8 SPI2_ReadWriteByte(u8 TxData); // SPI总线读写一个字节
: `7 R+ ^8 f* ]) m, c8 U - #endif /* __SPI_H */
1 O. J1 n: Y% Q0 K8 m3 _ - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/6 r5 X" i3 W2 V$ x) S' d# w
复制代码 + R! Q8 M1 i; p
2.spi源文件/ d9 d, s I4 w( n0 F$ x1 h
- /**
. C/ N+ v& n! Q - ******************************** STM32F10x *********************************
) H1 a: I7 a; G, { - * @文件名称: spi.c
# d. w9 |2 P% d% `3 Y& \ - * @作者名称: 闲人Ne8 x4 B: q8 _, q3 J" q2 d/ t+ ?6 @
- * @摘要简述: SPI源文件, ^4 } O O% M, n
- ******************************************************************************/) g7 c3 v6 o! y; P5 \ u" U
- /* 包含的头文件 ---------------------------------------------------------------*/6 @& u% h: v% K- C* o7 s
- #include "spi.h"
$ f: w Z$ x) ]2 S5 v - /**************************************************3 f8 }# p9 P4 C$ v7 k
- 函数名称:SPI2_Init()6 @% U9 b4 R3 r: A
- 函数功能:SPI2初始化函数:配置成主机模式5 P x/ Z4 J! w' ~% m
- 入口参数:无1 M% G# ^' V; F! O
- 返回参数:无
! l& {4 h6 F: B* T& f - 开发作者:闲人Ne
, Q5 c3 r' [: |, o3 A( M. c - ***************************************************/
' K! {& D" G; ]3 e/ }6 Q - void SPI2_Init(void)
8 ~ I' L' j: E" {' r. T/ k - {4 C& `' T6 X8 w% Q$ M
- GPIO_InitTypeDef GPIO_InitStruct; 0 q. r% d! L( O4 u7 d6 d
- SPI_InitTypeDef SPI_InitStruct; / w6 Y! M7 C- l A% P" k1 C
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); // GPIOB时钟使能,选择SPI2,对应PB12,PB13,PB14,PB15- y/ ^& e8 x1 C8 v0 ^0 e9 o) @
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE ); // SPI2时钟使能, P" P1 ~) T" q$ Q- D+ [6 q. R
- // 初始化GPIOB,PB13/14/15都设置复用推挽输出AF_PP,PB14对应MISO,最好设为带上拉输入 : k+ `9 S) k: A' f5 F3 P' u
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
8 W, q' U9 x0 t: T, W - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
3 F: A- A% q( \% g. K! G - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
# o" }" ^4 V- Q r9 x* n0 l' ^ - GPIO_Init(GPIOB, &GPIO_InitStruct);
5 Z; c4 j) i- ^! H$ C( h - GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); // PB13/14/15置高电平( c4 ^/ O4 l' U# T7 j" _/ H+ e
- // 初始化SPI函数
, x! j) l0 ^/ D& A - SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或双向的数据模式:SPI设置为双线双向全双工
4 ]$ d: Z+ u3 P. l - SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // 针对SPI_CR1寄存器的SSI位和MSTR为,均设置1,即SPI工作模式为主SPI5 e$ ^5 T4 V$ k
- SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 针对SPI_CR1寄存器的DFF位,设置数据帧大小为8位
$ p( }' H, c% h4 @& r4 K - SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // 针对SPI_CR1寄存器的CPOL位,串行同步时钟的空闲状态为高电平) F8 K& q2 B3 J
- SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 针对SPI_CR1寄存器的CPHA位,串行同步时钟的第二个跳变沿(即上升沿)数据被采样/ P# I" d! j# e! e) r5 @+ X- K
- SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 针对SPI_CR1寄存器的SSM位,NSS信号由软件(使用SSI位)管理
8 X7 r D5 c* p% W& H& L+ @) U! B9 j; { - SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 针对SPI_CR1寄存器的BR位,波特率预分频值为256,最低速率 0 F1 N2 e ^' S
- SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; // 针对SPI_CR1寄存器的LSBFIRST位,数据传输从MSB位开始
8 p8 \2 c% P$ v8 S - SPI_InitStruct.SPI_CRCPolynomial = 7; // 针对SPI_CRCPR寄存器的CRCPOLY位,设为0x0007,为复位值% P" L, b# J0 C. n
- SPI_Init(SPI2, &SPI_InitStruct); 3 i8 Y) g8 J1 S( a
- SPI_Cmd(SPI2, ENABLE); // 使能SPI外设
0 V) I! Y* T3 ]0 T- e - }
# c( d& }3 m* R; f k - /**************************************************
) A4 q, s: m! K: u% K2 w0 \( G - 函数名称:u8 SPI2_ReadWriteByte(u8 TxData)
" Z7 Z6 I( d& x& N - 函数功能:读写一个字节函数
8 ~* O8 D9 W% E' t: p5 g' o - 入口参数:TxData:要写入的字节1 p$ o- ?7 k' I+ \$ @* y$ x% o
- 返回参数:读取到的字节8 k, }6 v, ]5 h1 y8 _
- 开发作者:闲人Ne6 e a, w/ t6 ?9 w! g: _
- ***************************************************/
8 y9 g: u7 t5 a+ L* R - u8 SPI2_ReadWriteByte(u8 TxData)
0 ^) W8 X. x$ g: P/ t - {
, I; j$ H( Z; [; |! K) R" R8 d/ v - u8 retry=0; / o$ f) X. t5 T3 u( R
- // 检查SPI_SR寄存器的TXE位(发送缓冲为空),其值0时为非空,1时为空+ k6 C( ^7 S) t7 K$ R
- while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
. Y% K- @8 ]* X! k: K - {2 u. Z8 R1 _# a H1 ^4 K6 m: [2 `% e
- retry++; // 发送缓冲为空时,retry++
_$ W& t, U) q9 T - if(retry>200)return 0;
7 v9 p- @9 D- C6 U - }
) ^+ I% K. [# i; Y - SPI_I2S_SendData(SPI2, TxData); // 通过外设SPI2发送一个数据
' K8 K0 Y* U3 y - retry=0;
0 ?6 c! c0 n% q9 }% {/ t5 L - // 检查SPI_SR寄存器的RXNE位(接收缓冲为空),其值0时为空,1时为非空
) m8 L/ \+ @! `! S, e - while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) ! s7 R8 c: {. E B4 c7 E$ \3 {
- {9 M) y* q( S% ^
- retry++; // 当接收缓冲为非空时,retry++$ A3 g: U }! ]( C2 K
- if(retry>200)return 0;
6 Z( j8 w' Y( _ g% L8 \. E - }
' `7 i0 y1 a0 A8 Q/ K$ @ - return SPI_I2S_ReceiveData(SPI2); // 返回通过SPIx最近接收的数据 9 }, ]7 U" M0 f- r0 q% d$ D
- }
+ h) g; `7 t: I2 Y - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
4 _4 K- \3 N, m3.spi_cs头文件: ] R, H- M# s- t- A
- /**
# L* r* x2 t. T# k - ******************************** STM32F10x *********************************! m! o6 e& t" m1 C; S
- * @文件名称: spi_cs.h
5 o: F! x( C! L: N - * @作者名称: 闲人Ne
- \4 v, A. J9 j2 V - * @摘要简述: SPI_CS头文件
, ^7 G) q x0 F; x! M8 n' t - ******************************************************************************/
; O2 T& T, j/ @; R" _3 z' { - #ifndef __SPI_CS_H
- j5 n- w% O! E$ M - #define __SPI_CS_H
6 k1 y) `- P" x5 [9 Z+ f* l2 a1 Y% Y - /* 包含的头文件 ---------------------------------------------------------------*/, A [2 O2 R0 m4 i9 _5 \
- #include "sys.h"! |+ C9 f* R1 W( n+ V
- /* 位操作定义 -----------------------------------------------------------------*/
; M5 e i$ h" Q/ x - #define SPI_CS PBout(12) // PB12
6 L4 }% T0 A/ t" e6 S+ m - /* 函数申明 -------------------------------------------------------------------*/7 g. q3 B; D$ i# A7 n1 v' F# r
- void SPI_CS_Init(void); // 初始化函数
$ a/ @' v/ u6 V' B - #endif /* __SPI_CS_H */
( Z: D% P% E$ j8 l - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 3 Y: P& @7 S7 R7 _$ ~9 G4 }
4.spi_cs源文件! z5 h( D4 D2 a6 |. |
- /**1 U( @, ?4 F9 E Z' p" m4 m
- ******************************** STM32F10x *********************************+ A _6 r/ Z" F" _9 B' b
- * @文件名称: spi_cs.c( ~! W. }; d/ u7 t# {9 _
- * @作者名称: 闲人Ne2 M4 N8 `; G; {0 O A1 o
- * @摘要简述: SPI_CS源文件
) s9 s8 E( V$ Z0 R L - ******************************************************************************/
4 G( J5 D) y1 J) v! u. S - /* 包含的头文件 --------------------------------------------------------------*/- E- z" S+ q" V2 s
- #include "spi_cs.h"% b% |& J; v. R! u! ^) B. z9 ]
- #include "stm32f10x.h"7 W& h2 Q, [* \$ V; q x1 b# ^# U
- /************************************************* h; U% @1 t& q
- 函数名称:SPI_CS_Init()' k# d1 x- Z3 @# i
- 函数功能:初始化SPI_CS管脚配置! h$ { ?, o+ |. O! A& }/ [( u
- 入口参数:无
" b' l9 {' B+ M - 返回参数:无
' c/ Q; J' ?* A - 开发作者:闲人Ne
3 L0 B; T# Z# Q1 E. L% k - *************************************************/5 T' b- P! k9 I8 p
- void SPI_CS_Init(void)
! ~" c* v6 u& L8 Q! u - {
; L+ }8 H( ?3 H6 O( L1 ^ - GPIO_InitTypeDef GPIO_InitStruct; 5 {- [) w: C- D9 D2 ]0 J
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能PB端口时钟 & O: z) w* C% B" f: b) }: V8 |
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; // 选择GPIO引脚12
. r+ K! k5 K5 ]% I, \3 ]; u - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出
& F. Y. R" v0 F6 z+ |1 P - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz k5 } `& W: t7 }8 ?
- GPIO_Init(GPIOB, &GPIO_InitStruct);
0 k t1 [3 d8 h+ _# g% L8 _ - SPI_CS=0; $ i3 N! y+ Y3 J- P1 d* C
- }
# ]( d6 y' \3 g( a5 U: X - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
' r- X9 h2 Z5 F; b. ~# s5.mian主函数
/ R: p0 Z# i. h0 c7 L- /**+ Z; r5 F) `! }- x* _7 F0 y( l
- ******************************** STM32F10x *********************************0 S- L8 v* Q9 I& C% w- [4 F
- * @文件名称: main.c7 B1 [$ ?/ Y+ Q1 I- r
- * @作者名称: 闲人Ne
1 {* I+ F4 i% U0 p7 V - * @库版本号: V3.5.0' i9 T0 ~3 H! s R6 ?
- * @工程版本: V1.0.0
% x+ [8 n- n, ?6 a K: Y# u - * @开发日期: 2021年2月18日& P( {0 n( L$ L! l( Z, D) M
- * @摘要简述: 主函数
S0 Q8 ]3 F; d0 j4 z2 Q$ y1 c8 i: M - ******************************************************************************/
: K+ J- B- M& P - /*------------------------------------------------------------------------------ z+ r" O+ H& f
- * @更新日志:5 [8 t$ K3 Z& ]/ P9 y1 o, x& ^
- * @无! d& p: u/ K$ N6 E/ M7 t
- * ---------------------------------------------------------------------------*/
+ ~$ S4 w" v0 |- H - /* 包含的头文件 ---------------------------------------------------------------*/& r2 K/ B4 |+ Q$ J
- #include "led.h"% G, K ~/ }0 Y( S* ]: a' R- ?
- #include "key.h"
: }4 s( K" }& d( @7 e+ _ - #include "serial_communication.h"1 |+ p$ |, N/ y, n& C
- #include "spi.h"3 s6 `, ^0 h& M
- #include "spi_cs.h"; H' ^- C- Q5 M B: w; B, k9 ]+ A
- #include "sys.h"( |; C$ I9 }' z! n" n2 [8 a1 m5 f
- #include "delay.h"& X7 e( _9 f7 k5 e5 |4 t
- #include "nvic_configuration.h"8 F0 l5 V& b: ?
- /************************************************0 B# d. E+ o( L, D, n3 N8 [ l
- 函数名称:int main()
* M0 E6 J: t( x& y/ y - 函数功能:主函数. L7 k, V$ V2 r1 k8 J
- 入口参数:无( K5 R; k: {$ o( q
- 返回参数:int. _6 _3 d. T( X1 d* I3 p: @) X8 w
- 开发作者:闲人Ne% Z' P& N4 ~- Y: C/ V
- *************************************************/" n8 e6 I7 M8 l* ?) D& }& A6 ~
- u8 key;) a- M5 b2 j" f+ X
- u8 spi2_send_data=0;% x+ L6 U* ^7 ]0 k, t
- u8 spi2_receive_data;& b( m$ K+ k/ U9 t, B# l4 O. o
- int main(void)7 ]' U( z8 f8 d M/ w
- {
L+ Q) x) \, z3 \ - NVIC_Configuration();! ]5 L3 I6 E5 Q, O5 k0 X3 K# o
- delay_init(); 1 O f8 }3 A z9 _, b. w
- My_USART1_Init(); % Y. @1 F. ?. ?* x0 s
- LED_Init(); / h6 J% u+ R# \3 A9 C/ J$ C* k
- KEY_Init();
" B6 |! I+ w1 B - SPI2_Init();9 j; n7 z( R! l2 Z4 J2 T8 d Z; A1 n
- SPI_CS_Init();
; J3 t# @5 Y* E" o9 B& F - DS0=1;
, h! U4 c9 Z1 _ - DS1=0;1 r& h& v$ m u" J8 H, U9 N
- while(1)
# r4 ?! {9 K o6 l - {
- L( B S+ Z3 B$ U7 w4 \' I - key=KEY_Scan(0); // 有键按下时,key就不为08 U& V3 l4 O/ w" G6 K7 L
- if(key==KEY0_PRES) // KEY0按下,发送一次数据* u; q3 _+ l# t# B
- {, V8 j, C& ~- I2 H5 _
- SPI_CS=!SPI_CS;
2 i$ n* x& A8 L% z - DS1=!DS1;; r* H! Y0 x3 n; I: V* }
- }, U( W0 s) }2 f, G! m2 ]7 X
- DS0=!DS0; // LED0每隔500ms闪烁,提示系统正在运行: @8 r# Z; y2 O$ q
- spi2_receive_data = SPI2_ReadWriteByte(spi2_send_data);
) l1 t0 H) ~- j# H! h9 D- ] - printf("\n战舰v3的SPI2接收到的数据为:%d\r\n",spi2_receive_data);
4 F5 W; |7 ~) z6 W7 T( I& B - spi2_send_data++; E' B. t# f* L) Q% o: V% Y' T
- delay_ms(500);
. x; j! W3 @) v8 v8 s! }) n9 C* @ - }
" \' a1 o' x$ S% r3 B - }; d3 V3 Z' {( M7 W
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
# C; V# h* | C ^# N. D实验结果
* K- L$ ]6 h+ g1 H战舰v3实时读到的数据如下图所示,当战舰v3的PB12为0时(即CS=0时),能正确读到从C8T6工控板传来的数据,当按下KEY0键(即CS=1时),则读到的数据稳定在255(不理解,反正是读不到正确数据了)。
& Q$ k* [2 L0 H' \/ a: m* j. P" R' b& G% y7 Y& m) r9 F% |
3 u' t. D; v* d, K
0 n8 g6 V: c5 L
C8T6实时读到的数据如下图所示,当战舰v3的PB12为0时(即CS=0时),能正确读到从战舰v3传来的数据,当按下KEY0键(即CS=0时),则读到的数据为0。
" n% P0 C( s0 v5 G. h5 U/ ?4 z# C* R6 F; } p, p
" ~, V- k+ j, X* }1 k! x. g8 G+ Q% A& O4 k5 g$ |. k T
经验分享( N6 X: Q, F7 H
( ]5 n, L' d r/ v; O' ^9 H* I; @该程序可作为双芯片之间SPI通讯的参考代码。
' `" C: r( H( C2 j————————————————8 G9 @0 I* m! A" _( M2 j9 e
版权声明:天亮继续睡5 F0 X3 I7 [( ~4 p+ x, p
& X6 i$ u* A8 v9 k
$ e( R6 H2 g2 j# i6 i |