SPI (Serial Peripheral interface)是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
( v: n4 E2 }* p. D8 { N" _) w
1 p3 o( _* J& O
) N3 e5 S* t* |8 d7 M今天分享下,战舰v3与C8T6工控板上的SPI通讯实验,其中战舰v3作为主机,C8T6工控板作为从机。
# t7 D; o+ D* k; p* k `" R& b& p1 J1 n6 e# `$ y N' r
实验功能说明
|" J2 y. h6 s程序分别烧录战舰v3和C8T6工控板之后,通过RS232可分别获取战舰v3从C8T6读到的数据以及C8T6从战舰v3读到的数据,并且战舰v3的PB12可以控制与C8T6工控板的SPI通信状态。+ d! h: ?! F0 } w8 n" v0 _( p
" a& z% r1 }1 n6 I硬件部分
% N/ s) w& ` F! d9 }) G1)正点原子战舰v3开发板(下图所示),350¥左右;/ N' M2 h$ W" V+ t
3 d) w/ f3 K/ d5 r+ V- p2 Q$ J: O
9 W! D( a! P0 `3 f R/ w
& j- S) i8 `0 L) j2)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;: I: P2 q4 n8 O) b4 g
3)某宝网上购买的232转USB数据线(下图所示),价格15¥左右;
+ h8 F D4 e- E/ [7 W
. c+ c1 I* f6 k0 J2 X- ^
6 w- j( ` }$ `1 ?0 ^% z4 g$ H# A4)杜邦线若干,价格几乎为0¥。
. d, D' V3 @( z, f$ |& T |
: w; [- s% M1 h# l& k. g' {硬件连接! L" v! q+ a$ V0 e; Q) A3 R
按照下表用杜邦线连接好即可。
% D$ s2 P, _% t A, E2 R4 s# ~/ g1 ^5 A. ]1 B
* U f2 n6 e" A3 D; Q, h
. \4 I! b& N8 G/ I$ v1 Q0 D+ W' ^& u$ j8 f& F8 S: M) Y4 C
部分代码 I9 h. \* ?6 [5 n+ `4 x# I4 l
C8T6工控板的代码可参考互通讯实验中对SPI1的配置,SPI1初始化函数只有一点不同:
" f: r; {' }. T- [' h- Y- A2 @
, k* {3 S1 K4 t& G6 g, R- SPI_InitStruct.SPI_NSS = SPI_NSS_Hard;
复制代码 K8 |& W/ J* G3 c$ K" Z" b
战舰v3代码如下:
9 \( K1 {$ [$ j" e+ K0 P/ q' ^1.spi头文件- E7 h4 s2 i7 j; ?$ Z' h( v
- /**
J" X9 b2 ?/ U$ l; t - ******************************** STM32F10x *********************************0 z% }; i. `& G% r9 O8 a
- * @文件名称: spi.h& Z- Z# j1 O5 T- S) Y
- * @作者名称: 闲人Ne. H. }6 X( y+ h1 h" S
- * @摘要简述: SPI头文件
- z0 W7 V/ n4 ?8 n+ b- x$ H; I: x) } - ******************************************************************************/9 S+ v3 G+ e1 C7 r* D% B
- #ifndef __SPI_H
3 Z/ x' Q+ R. v - #define __SPI_H
0 g! x" S. c0 [0 }( o% h - /* 包含的头文件 --------------------------------------------------------------*/9 o, `) h1 `% ^/ J
- #include "stm32f10x.h"
2 B8 \6 d4 V5 H6 E4 h5 S3 K - /* 函数申明 ------------------------------------------------------------------*/
# z% j- @. P6 ^ - void SPI2_Init(void); // 初始化SPI2接口
7 b0 o/ F& o6 z7 h% s3 J) r - void SPI2_SetSpeed(u8 SpeedSet); // 设置SPI2速度 3 Y0 J6 w N G2 ]
- u8 SPI2_ReadWriteByte(u8 TxData); // SPI总线读写一个字节 ( l7 k! q( X+ z( T" V [) P8 K; u
- #endif /* __SPI_H */
# @7 M( W' P- f6 C) x - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/7 u! j) w) X0 B V
复制代码
& T/ A% |' u, Q4 z9 L$ M; u2.spi源文件6 I9 k( i: Y. r' X& x
- /**
' g, u6 u0 n: X: W4 d - ******************************** STM32F10x *********************************
8 u! z; z1 v& m! o3 D - * @文件名称: spi.c
2 \9 h x0 d' v# e - * @作者名称: 闲人Ne
" g9 k6 u6 n9 x: n+ d - * @摘要简述: SPI源文件
7 U% C3 \/ T8 F - ******************************************************************************/" h/ F" {: Q5 I$ Y0 ?0 N6 F
- /* 包含的头文件 ---------------------------------------------------------------*/
! D' F' ]+ o7 Y0 m/ c - #include "spi.h") j0 S* l& {5 w7 R" h J, b1 M- {
- /**************************************************
% {7 p, e9 [7 s4 v - 函数名称:SPI2_Init()
/ T! Z+ l9 S6 q$ ` - 函数功能:SPI2初始化函数:配置成主机模式; |( L0 ?/ \! r
- 入口参数:无
% T0 S" U: J# R( K7 j/ E, _ p: u - 返回参数:无
# }" v- \+ i4 S9 i% R% t/ P - 开发作者:闲人Ne1 t; @( n S8 S. P m+ e8 S8 Y7 `' z
- ***************************************************/
8 j+ r1 ~4 m0 b9 b7 h! f - void SPI2_Init(void)
8 R( H$ z/ O* @$ d. q# a - {
7 \( [% \! \* U, X. ]. R - GPIO_InitTypeDef GPIO_InitStruct;
$ ^% F, e" s m+ F& n2 B - SPI_InitTypeDef SPI_InitStruct;
( f( Q: L/ K! ^ - RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); // GPIOB时钟使能,选择SPI2,对应PB12,PB13,PB14,PB15 ~% b {! ?* c+ n' ~
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE ); // SPI2时钟使能
1 ~0 f* x; A S* G, t* `% D - // 初始化GPIOB,PB13/14/15都设置复用推挽输出AF_PP,PB14对应MISO,最好设为带上拉输入
; ?% A# C' F- \ - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
9 F( Z# {" P) `3 `# f/ h - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
* e; C/ ?" T: p1 M! I - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
" t6 x9 T$ s- z' D4 o - GPIO_Init(GPIOB, &GPIO_InitStruct);
' I/ m, N1 u1 Y- p* I& h6 ^ - GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); // PB13/14/15置高电平' I) y' R9 L9 ^- X& J0 m
- // 初始化SPI函数
9 B5 H3 r" `7 `. j - SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或双向的数据模式:SPI设置为双线双向全双工 8 g. F& y* V( [. d9 L
- SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // 针对SPI_CR1寄存器的SSI位和MSTR为,均设置1,即SPI工作模式为主SPI e k8 K2 I; u* y
- SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 针对SPI_CR1寄存器的DFF位,设置数据帧大小为8位
( z1 L8 o3 a/ o7 T - SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // 针对SPI_CR1寄存器的CPOL位,串行同步时钟的空闲状态为高电平% p: ?# s7 \6 ?: V- s
- SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 针对SPI_CR1寄存器的CPHA位,串行同步时钟的第二个跳变沿(即上升沿)数据被采样9 s) x9 L8 e( l# d* W# I9 Q8 f
- SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 针对SPI_CR1寄存器的SSM位,NSS信号由软件(使用SSI位)管理
* k$ E: I) K5 l* h7 g3 g1 w9 W& c/ B - SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 针对SPI_CR1寄存器的BR位,波特率预分频值为256,最低速率 ( i$ _( V0 f i6 f8 Q% ]% l
- SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; // 针对SPI_CR1寄存器的LSBFIRST位,数据传输从MSB位开始
% Y; M$ c6 t: u' |; C5 M0 J - SPI_InitStruct.SPI_CRCPolynomial = 7; // 针对SPI_CRCPR寄存器的CRCPOLY位,设为0x0007,为复位值
+ x& G, [: K* g' x' k - SPI_Init(SPI2, &SPI_InitStruct); : c1 \7 P* R& \% u- S$ E
- SPI_Cmd(SPI2, ENABLE); // 使能SPI外设
8 w9 z X. S# i1 j/ }8 Z) u0 s - }
1 i$ H4 c* t% K, w - /**************************************************
: T( w- P" Y: ~ - 函数名称:u8 SPI2_ReadWriteByte(u8 TxData)6 G' J: n7 Q3 }- S4 Z
- 函数功能:读写一个字节函数
. w% o7 j2 K3 X! b: S/ N: W - 入口参数:TxData:要写入的字节
7 P8 R- z: d- t - 返回参数:读取到的字节
" q2 [! K+ i, p - 开发作者:闲人Ne
$ {: _5 B8 l7 N3 }$ i: k - ***************************************************/
' d: s) {" g; M - u8 SPI2_ReadWriteByte(u8 TxData)5 h, n- X: w3 I' T
- {
& b; Z; o8 [* L" Z- J2 j - u8 retry=0; + Y' E6 |; U. X/ O8 \& [' a
- // 检查SPI_SR寄存器的TXE位(发送缓冲为空),其值0时为非空,1时为空" a% Q$ @1 x8 K+ e# U! D
- while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET)
: |. x3 e' _/ m0 k% Z: B4 e - {$ O$ E: ~; G) m
- retry++; // 发送缓冲为空时,retry++
0 h; @8 K4 ]9 N) q - if(retry>200)return 0;
: P L' X" M6 ]4 R& m - }
; d' X, W9 ?! n3 D& J; W& n - SPI_I2S_SendData(SPI2, TxData); // 通过外设SPI2发送一个数据
! @7 h& @" I9 | H) u - retry=0;/ }6 J7 r) T5 D4 |9 C7 \' A7 {
- // 检查SPI_SR寄存器的RXNE位(接收缓冲为空),其值0时为空,1时为非空
3 Y/ _+ h# I2 \1 f' V - while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET)
& w; O7 M$ s0 ~( u3 ~1 v/ i& Z' \ - {
- P. z. N3 W0 W$ E1 q - retry++; // 当接收缓冲为非空时,retry++ D# p, u5 G: @* _% |
- if(retry>200)return 0;
, T: r) K' E- y% a8 J - }
6 P. b3 c: Z- L - return SPI_I2S_ReceiveData(SPI2); // 返回通过SPIx最近接收的数据
: J8 f/ [# Z8 H, v - }0 S0 g, S9 Q6 U# F; X. n* e
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
& j3 j6 n& o' s/ V3.spi_cs头文件
* r. N5 | ]3 T: Y7 D' U- /**
# d8 r& p% c( o- D2 Y+ ~- ~* B& B - ******************************** STM32F10x *********************************+ A2 i- C* F$ M. G7 Y4 @
- * @文件名称: spi_cs.h+ u9 \- U+ t) V4 {; G* g/ H
- * @作者名称: 闲人Ne m- W% C5 k4 P/ N, h3 a9 B6 ~. |
- * @摘要简述: SPI_CS头文件
2 ]( l5 ^9 F8 j2 } - ******************************************************************************/1 d" U9 W) f( A8 x: n$ y
- #ifndef __SPI_CS_H3 M7 k& N! t7 `) U
- #define __SPI_CS_H3 N2 T4 F& w/ h
- /* 包含的头文件 ---------------------------------------------------------------*/
0 k% j( n& `0 m/ v5 N7 F% ~/ N - #include "sys.h"
5 V' X* L I7 z9 { - /* 位操作定义 -----------------------------------------------------------------*/
2 y3 a ?9 h) ?: C5 Y8 s - #define SPI_CS PBout(12) // PB12 U/ ~$ y0 [# K- z+ t ?+ O. t
- /* 函数申明 -------------------------------------------------------------------*/
$ b$ A% e9 Z3 T% J5 W7 U5 n - void SPI_CS_Init(void); // 初始化函数* `- g7 F/ x/ o2 Q5 ^" E
- #endif /* __SPI_CS_H */ a* s% M. p; P. Q8 Z
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 . X- Z) ]5 b( y# x8 v1 ]1 U7 }) ]
4.spi_cs源文件, q5 r$ t- Q) f& X1 e
- /**
& M9 Q2 _4 b0 | - ******************************** STM32F10x *********************************7 {, v {7 I9 N5 {& H
- * @文件名称: spi_cs.c
6 l1 Z7 X% v0 R0 j0 P - * @作者名称: 闲人Ne
: V6 v" K, d5 L9 f1 y - * @摘要简述: SPI_CS源文件
3 p- i4 Z' a7 x6 ? - ******************************************************************************/. `4 c; f0 a5 G2 e- \. O8 T' z8 s
- /* 包含的头文件 --------------------------------------------------------------*/
7 Z6 K& n: [6 i. L1 c/ J% ~ - #include "spi_cs.h"6 }& v/ ~5 E3 d; q! D+ I
- #include "stm32f10x.h"
6 Q% v/ M" _& y! m - /************************************************
$ }' c- R+ }" y6 \5 e - 函数名称:SPI_CS_Init()
$ z$ @# V/ m8 B' x- T7 t - 函数功能:初始化SPI_CS管脚配置 M. I L* J7 F3 u. ?
- 入口参数:无
" S4 H( N! v! p) L, ] S - 返回参数:无
1 @0 u- C9 P/ Q% f( Q - 开发作者:闲人Ne
: {+ D2 m; p' j; q9 x - *************************************************/
% l; _' P3 r' }# A5 O- Q - void SPI_CS_Init(void)
) p$ l1 j+ p7 [1 d2 o* N - {9 T+ D- ~/ @, F6 j' x% O
- GPIO_InitTypeDef GPIO_InitStruct; - k1 f+ m# v9 I, K* D
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // 使能PB端口时钟
; s8 z# k; _! B) B/ A9 x# Z4 ^- y0 M - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12; // 选择GPIO引脚12
6 E4 Q5 b3 x [/ k: k: l9 S6 J4 o& x - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽输出8 d$ d7 E( Z+ ?
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; // IO口速度为50MHz
& f7 |' j' o0 g! `; X. g# s - GPIO_Init(GPIOB, &GPIO_InitStruct); % j8 L1 Q; i/ I. _1 H
- SPI_CS=0; ! g# a( T' b" G" k2 y
- }: g1 X0 c) s3 V, x# M; _+ X
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
1 i K; D$ S& n0 Y5.mian主函数- c3 L) B7 q8 X( G a, Y' \, C
- /**
' P2 l3 l C8 Z - ******************************** STM32F10x *********************************
9 n; i% l) Y( {, |& h1 r - * @文件名称: main.c( `4 ^: {9 }9 b* B* ^0 _5 B. l
- * @作者名称: 闲人Ne
: o/ x8 S3 I4 |- u; u2 w - * @库版本号: V3.5.0
( m" f3 p. ?! ` - * @工程版本: V1.0.0
9 s$ `+ V5 C0 L" c8 a5 W) B/ x0 \ - * @开发日期: 2021年2月18日& J& M2 Q9 i4 K2 `
- * @摘要简述: 主函数7 u1 D9 h y" }* r3 B$ t% q
- ******************************************************************************/
9 ]# Y7 k" ]. J" ] - /*-----------------------------------------------------------------------------
+ s# R) X. ?5 ~# y' ]# k - * @更新日志:
+ O% @3 @1 k6 U. @ - * @无8 e" S8 c( I( ^5 W! R. s- h3 D
- * ---------------------------------------------------------------------------*/3 a8 ?! w! f7 n6 U
- /* 包含的头文件 ---------------------------------------------------------------*/, g9 Y7 T5 e! g, D* ]0 ]% V7 Y
- #include "led.h"3 i. Q0 N9 Q. w% n
- #include "key.h"5 W& y1 w! u; r$ s
- #include "serial_communication.h"
/ k( ~0 u4 a8 a$ V - #include "spi.h"
( Z% z3 {. \) t# z: { - #include "spi_cs.h"
7 a6 X _9 E3 I9 S* _" `' z$ R. S& m - #include "sys.h"
5 C8 ?/ e- M6 W* i - #include "delay.h"" d) C- S' R. D4 [
- #include "nvic_configuration.h"
2 q7 Z X; M' D" o - /************************************************
; U3 ]5 C) \, `. a! a6 C - 函数名称:int main()
9 ^: i) e h$ @% D' g2 ~ - 函数功能:主函数/ `' C& b; w6 g6 w+ U: o/ C) i
- 入口参数:无+ |1 ~0 S1 }" E
- 返回参数:int
, N# _- b6 W' |+ b- `! z - 开发作者:闲人Ne9 n- a6 ^- b. k4 X0 W& H
- *************************************************/& i1 _/ E! Q3 v
- u8 key;% R( M2 g. U% U- Z! B3 X
- u8 spi2_send_data=0;
1 @" p" s; z+ n4 b" [! w - u8 spi2_receive_data;
7 ^" k7 B3 f3 \# \" H5 u - int main(void)
5 M+ l! R$ ~1 x6 _# N - { & B3 n- Q4 K& x- p5 W
- NVIC_Configuration();
7 g- [( G* D" q' a" K$ B - delay_init(); 4 S1 B' O, u) y' w: v+ U
- My_USART1_Init();
/ [7 ~! g/ D6 U - LED_Init(); 1 \6 @! Q8 ~, q3 v" F! i! J
- KEY_Init();
: z- v5 Z2 K ?/ e$ y# ~ - SPI2_Init();! r. m) w( b1 p
- SPI_CS_Init();
9 p0 z8 x2 V6 K* w v - DS0=1;
; h7 f" Z' g& D8 r) ^& p! W - DS1=0;% Y- p2 _$ y ]! I
- while(1)
* S8 | `- t6 N% S# [2 o- M: h9 J - {
9 V" ^8 ]. [0 `. J0 U @ - key=KEY_Scan(0); // 有键按下时,key就不为0
7 Y8 `# o6 n$ z) ]5 Y3 t - if(key==KEY0_PRES) // KEY0按下,发送一次数据/ L5 t ?6 a, q- {2 R# v* H
- {- r1 @; |7 c( Y7 H2 C
- SPI_CS=!SPI_CS;% {: I. w( }) P8 ] g9 U9 j
- DS1=!DS1;
# _+ E: I! k" Z/ S - }# |) w, m' [. C( R, q- Q/ `
- DS0=!DS0; // LED0每隔500ms闪烁,提示系统正在运行. ?- v6 } c" l
- spi2_receive_data = SPI2_ReadWriteByte(spi2_send_data);) T( H5 y- K, @3 P
- printf("\n战舰v3的SPI2接收到的数据为:%d\r\n",spi2_receive_data);+ x* V$ ?& O" ~' D6 \
- spi2_send_data++;
& Y9 q7 r1 f6 A: ?. K - delay_ms(500);
" h/ B- e+ |# `! L2 x) b0 O - }
( v" e7 M) B9 n! u( [, Q$ g - }
/ `, D/ i* I2 Q' [. e2 R - /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 1 R& k$ x1 H1 W: K
实验结果9 b$ b7 I" U; x" [0 n
战舰v3实时读到的数据如下图所示,当战舰v3的PB12为0时(即CS=0时),能正确读到从C8T6工控板传来的数据,当按下KEY0键(即CS=1时),则读到的数据稳定在255(不理解,反正是读不到正确数据了)。
0 @0 w0 b7 ]4 J: L" L+ O% m+ q
# t+ Y p9 g) {$ l. j
4 e7 w* k$ e: H0 k: [
$ N* I) L+ [+ l5 BC8T6实时读到的数据如下图所示,当战舰v3的PB12为0时(即CS=0时),能正确读到从战舰v3传来的数据,当按下KEY0键(即CS=0时),则读到的数据为0。
" Z/ `+ b+ Z* @/ v- d1 ?0 o4 C- G5 w r' P: k8 w9 D0 f0 m
; x/ i, H+ J& I1 O6 h1 X$ u% e, ?
; O. k' s: M+ \7 n; N m经验分享
" B- ]1 M" J, V! K! B( ?" k9 i& p# I5 A
该程序可作为双芯片之间SPI通讯的参考代码。
% E5 {2 r2 j. v1 ~4 _- x————————————————8 D3 L+ w; ]1 _
版权声明:天亮继续睡$ }+ O6 N* O$ U# D( O+ y2 ~9 b
! E! m9 @# Z& z- O% v2 i& G$ N D; _, ]3 U' T2 Q& ^) v" [4 `, @
|