SPI (Serial Peripheral interface)是一种高速的,全双工,同步的通信总线,并且在芯片的管脚上只占用四根线,节约了芯片的管脚,同时为PCB的布局上节省空间,提供方便,主要应用在 EEPROM,FLASH,实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间。
8 a* ^' \) J2 v9 f
" l/ G/ z$ W) I
5 E8 G8 b+ ^& q$ E/ ]
; \0 u0 l# k! A* r今天分享下,基于STM32F103C8T6工控板上的两个SPI接口进行互相通讯,其中SPI1作为主机,SPI2作为从机。
' n0 d5 H7 o" r' B' [3 h3 X( _) u# @( F( z, D
硬件部分
3 u! w0 f$ Z. L1)某宝网上购买的STM32F103C8T6工控板,价格50¥左右;
! z6 p4 ?2 J( w' s( H2)某宝网上购买的232转USB数据线(如下图所示),价格15¥左右;; M& @6 i; X9 j* `
3)杜邦线若干,价格几乎为0¥。) @* D$ n% L/ D: S: Y5 S
, c5 D n, I* v, ?+ f
8 i5 H- y$ s3 J0 v6 A# r硬件连接8 B& ~9 {( G8 n- [
由原理图(如下所示)可知,将PA5与PB13连接,PA6与PB14连接,PA7与PB15连接即可。注意:SPI与串口的连接不同,串口的RX引脚接另一个串口TX引脚,而两个SPI连接是同名字的相连!, X4 o1 x% z( q o2 S
0 {/ i: K, b" w+ i6 F
9 D: D0 t4 j L1 L! ^' l
1 d7 F1 Y; C# o0 {7 v1 C
& t# A0 @' M* g* N* E; d$ N- x
8 p3 _9 ]6 F) t) y* y% }/ V1 b& f8 ~9 P
部分代码
, F* r G9 ?$ J$ `" Q1.spi.h头文件
! J) x( T! F6 V8 V- /*** d* m! s# ~! X3 ^# e
- ******************************** STM32F10x *********************************
/ o9 N+ c- G$ f: @ - * @文件名称: spi.h/ o6 v0 x8 W2 i4 n" v! R* e& ^# R
- * @作者名称: 闲人Ne; M$ \- d& X9 q2 k& _
- * @摘要简述: SPI头文件
Y5 `3 _: r$ \- ]8 C1 z- J' a - ******************************************************************************/1 U0 y( w( @0 k# D" x/ a9 Y
- #ifndef __SPI_H1 O' A6 K3 b+ d: c& i! b0 s
- #define __SPI_H1 S9 R) p* z E
- /* 包含的头文件 ---------------------------------------------------------------*/
( a$ L8 @/ a, e/ f$ J0 c4 f& e - #include "stm32f10x.h") T* r2 G9 p9 R5 S9 C3 U: o; y" P
- /* 函数申明 -------------------------------------------------------------------*/
; r1 W: v6 l2 n, I- @1 ~ - void SPI1_Init(void); & I# g7 C9 p; W7 q" S6 s
- void SPI2_Init(void); # j! h3 t- G6 t0 g2 V1 {6 K/ j, [
- u8 SPI1_ReadWriteByte(u8 TxData);- z- l) ]3 R, k( S% X0 k
- u8 SPI2_ReadWriteByte(u8 TxData);
; |0 Y/ M$ \' F$ K3 |# w - #endif /* __SPI_H */5 v8 @# i! y, I% H. r% k
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码 % R; w( r3 R- ~! k; C5 H& I
2.spi.c源文件! g2 v6 \' f8 k9 n
- /**) y, C* l! W/ `" H& C Y- |
- ******************************** STM32F10x *********************************0 P, n; b- |% }
- * @文件名称: spi.c
( K5 f5 p; \" C8 l" k3 Q - * @作者名称: 闲人Ne
' Z, N7 J+ q2 p7 b" P4 D/ D6 m3 M; R - * @摘要简述: SPI头文件
6 J l% Y5 A7 |1 @' o% l - ******************************************************************************/# r. x* o9 i( M9 S. b3 X
- /* 包含的头文件 ---------------------------------------------------------------*/! i: t. ^8 N5 l8 P( `0 d
- #include "spi.h"
3 n: L% L8 L; x( s - /**************************************************
& A/ u7 D. a2 W, D6 k+ }: \ - 函数名称:SPI1_Init()
' b2 ]& n$ L) b! @, Z9 }. I - 函数功能:SPI1初始化函数:配置成主机模式# t$ u" d. d0 g/ w" B; b1 y- l
- 入口参数:无" {5 ?# |" J& m+ o# m
- 返回参数:无
( C% K6 J5 g- O8 o- z - 开发作者:闲人Ne3 m. P. O; ~, [$ U- b+ D" Y
- ***************************************************/
) C$ D \/ f& v; s& M0 X2 D: [ - void SPI1_Init(void)# z. f7 C9 f; X9 V7 X
- {
0 @1 C9 ]# {5 V3 c - GPIO_InitTypeDef GPIO_InitStruct; . z' K$ Z* ~3 @2 ]0 |5 k
- SPI_InitTypeDef SPI_InitStruct; X8 i5 a* ^# g- z
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); // GPIOA时钟使能,选择SPI1,对应PA4,PA5,PA6,PA7 + J' u- x. u' d: S2 ?
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_SPI1, ENABLE ); // SPI1时钟使能
! M) W% [1 t4 j% z% z - // 初始化GPIOA,PA5/6/7都设置复用推挽输出AF_PP( ~. }# g0 T( {5 z6 n
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;4 p4 k# |0 t6 K
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出AF_PP! R' z' k* q V' ^8 ?) Y" P
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;6 L t5 H2 k' C
- GPIO_Init(GPIOA, &GPIO_InitStruct);. e$ ?) o! M* P6 }9 C% x6 R
- GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7); // PA5/6/7置高电平
' \( n& I* e: C, |3 ` b - // 初始化SPI函数
- p1 |( G' t2 ]5 O - SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或双向的数据模式:SPI设置为双线双向全双工# t: }' K. z7 H$ V
- SPI_InitStruct.SPI_Mode = SPI_Mode_Master; // SPI1设为主机# h# W G: B/ F- p! [$ [
- SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 针对SPI_CR1寄存器的DFF位,设置数据帧大小为8位1 C6 Y* x, r! O# g1 \
- SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // 针对SPI_CR1寄存器的CPOL位,串行同步时钟的空闲状态为高电平
8 B# w+ H. E7 s - SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 针对SPI_CR1寄存器的CPHA位,串行同步时钟的第二个跳变沿(即上升沿)数据被采样
- G- q8 L/ [+ f8 ~' \ - SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 针对SPI_CR1寄存器的SSM位,NSS信号由软件(使用SSI位)管理
5 n4 @) U/ O$ h - SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 针对SPI_CR1寄存器的BR位,波特率预分频值为256,最低速率 : p V" f" w' i
- SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; // 针对SPI_CR1寄存器的LSBFIRST位,数据传输从MSB位开始
; T+ h! Z9 P* g' c - SPI_InitStruct.SPI_CRCPolynomial = 7; // 针对SPI_CRCPR寄存器的CRCPOLY位,设为0x0007,为复位值
3 G/ Y3 O! \; E: G1 J - SPI_Init(SPI1, &SPI_InitStruct); ! h9 K. m9 d- R, G
- SPI_Cmd(SPI1, ENABLE); // 使能SPI外设
. }1 h0 l8 r3 C$ Y9 s5 B - }
0 R8 n9 s5 ^/ B; R - /**************************************************3 x# E% F3 p, e% z
- 函数名称:u8 SPI1_ReadWriteByte(u8 TxData)% a+ @: w7 A* j' [9 B+ A
- 函数功能:SPI1读写一个字节函数
" x( ]3 N3 U9 q: h$ W - 入口参数:TxData:要写入的字节% h0 t" R% ?( V8 P% k7 D
- 返回参数:读取到的字节. ^; h; k3 `9 j9 R7 f. l
- 开发作者:闲人Ne( ]8 z/ L, D3 P4 a# r7 D( e) G
- ***************************************************/' I! g- w z8 E: J# F" q" y
- u8 SPI1_ReadWriteByte(u8 TxData)
$ Y4 \( d; U* R0 k0 s. n0 y5 t - { 5 Q9 Z. A/ ~: R- u% G
- u8 retry=0; ) x+ a5 Z K' o" l, k5 B9 ?: {0 H
- // 检查SPI_SR寄存器的TXE位(发送缓冲为空),其值0时为非空,1时为空 3 |/ @7 ~- e& v4 H* _
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) 9 i% ], n- g, @; e
- {
- C* `2 [' u5 U% I# c: x, C - retry++; // 发送缓冲为空时,retry++
1 y' m' u8 d" I( h# Z0 T - if(retry>200)return 0;$ @' A# v$ O3 s0 N1 f
- } 6 J1 l8 E( T9 D2 S: _1 ]
- SPI_I2S_SendData(SPI1, TxData); // 通过外设SPI2发送一个数据$ f& g7 \2 P$ b
- retry=0;) }6 X- Z3 ? J w9 O% W L
- // 检查SPI_SR寄存器的RXNE位(接收缓冲为空),其值0时为空,1时为非空' x' H, o8 P; Z4 m
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
! q& S; i" ]. g* l ?9 G - {$ u% |' ~) Y. Q6 Z% q
- retry++; // 当接收缓冲为非空时,retry++
+ }+ r, Z- v, a! Q - if(retry>200)return 0;& x' ]) J' T1 o1 I/ i& x
- } % H M- S" ~1 V' j4 B, R! Y- D, n
- return SPI_I2S_ReceiveData(SPI1); // 返回通过SPIx最近接收的数据
8 q7 E& H" ~4 n: X( x - }
. X* U5 }9 t# I. u' g, R) q3 d) @ - /**************************************************
3 G7 j4 q* L4 `/ j; l6 s& c g - 函数名称:SPI2_Init()
) P4 p" \ ^& m7 o - 函数功能:SPI2初始化函数:配置成从机模式/ ?7 u* R1 O( e' s
- 入口参数:无2 {- f) o$ B. ^0 Q; a
- 返回参数:无& [; \. G4 o4 F5 F4 _+ f+ k
- 开发作者:闲人Ne+ o- y" I2 C! |+ I: Q% C
- ***************************************************/; J9 Y9 e7 s3 C) ^
- void SPI2_Init(void)
7 z4 V6 N. k& \6 \ - {
6 |) X5 z: `2 A9 @7 Q3 e& O - GPIO_InitTypeDef GPIO_InitStruct; ; X/ |( r/ H9 ~) ^( {7 n8 ^" m
- SPI_InitTypeDef SPI_InitStruct; . v8 f# }. z) X5 @! V" [
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); // GPIOB时钟使能,选择SPI2,对应PB12,PB13,PB14,PB15 1 q* Q4 L8 n e9 l6 L' f. M
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE ); // SPI2时钟使能 3 m! W. \7 {- ~
- // 初始化GPIOB,PB13/14/15都设置复用推挽输出AF_PP,PB14对应MISO,最好设为带上拉输入" c: x& a' k: Q- k
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
- s9 C3 H! W/ q! S& e - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出AF_PP
0 M, p- r7 G& X* f - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
/ [6 r3 J9 l3 z A$ n - GPIO_Init(GPIOB, &GPIO_InitStruct);
$ ?- \4 R3 L- J6 r; r - GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); // PB13/14/15置高电平
) Z0 i7 k$ l2 d/ R - // 初始化SPI函数0 ?1 J8 x$ V5 p8 Q/ n" U
- SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; // 设置SPI单向或双向的数据模式:SPI设置为双线双向全双工
0 z1 O: D0 M8 N5 h - SPI_InitStruct.SPI_Mode = SPI_Mode_Slave; // SPI2设为从机
% c1 Y K: I; | - SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; // 针对SPI_CR1寄存器的DFF位,设置数据帧大小为8位
! ^: a; ^8 v4 [$ `5 n - SPI_InitStruct.SPI_CPOL = SPI_CPOL_High; // 针对SPI_CR1寄存器的CPOL位,串行同步时钟的空闲状态为高电平- [6 I: ]6 L* O: R: X' Q& t8 Z% B
- SPI_InitStruct.SPI_CPHA = SPI_CPHA_2Edge; // 针对SPI_CR1寄存器的CPHA位,串行同步时钟的第二个跳变沿(即上升沿)数据被采样( m2 d* K, G, B) {3 z0 t
- SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; // 针对SPI_CR1寄存器的SSM位,NSS信号由软件(使用SSI位)管理
: ]" ^6 j+ K7 z9 }. H$ \- } - SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; // 针对SPI_CR1寄存器的BR位,波特率预分频值为256,最低速率# z* o2 L. K3 |1 @) ~ p) k' { X$ Y
- SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; // 针对SPI_CR1寄存器的LSBFIRST位,数据传输从MSB位开始$ H* C5 B/ N8 g' C$ X8 x
- SPI_InitStruct.SPI_CRCPolynomial = 7; // 针对SPI_CRCPR寄存器的CRCPOLY位,设为0x0007,为复位值
* r3 V% o& T+ i/ Z/ e - SPI_Init(SPI2, &SPI_InitStruct);
9 ?4 \; I3 S/ d5 E - SPI_Cmd(SPI2, ENABLE); // 使能SPI外设
1 E D, V8 x4 ^: s - }
" N6 x& V: [3 Z. z6 M - /**************************************************
- S; h& [0 F1 s Z7 I; }6 ?5 q - 函数名称:u8 SPI1_ReadWriteByte(u8 TxData)6 p) w! R3 a5 x/ e8 n
- 函数功能:SPI1读写一个字节函数+ P2 b2 U. h8 o+ v& G8 t# y
- 入口参数:TxData:要写入的字节
9 p- Z& A; K7 u: a - 返回参数:读取到的字节+ W9 I' y* V: z1 u+ ?0 V0 B5 D
- 开发作者:闲人Ne
: ]/ M. P3 ^0 Q+ ^3 u - ***************************************************/
" H4 z9 w# L7 Z* l4 s/ u% I - u8 SPI2_ReadWriteByte(u8 TxData)
& M0 Y- ^) E, \* c - { . ?2 `6 m7 a! f" L* |
- u8 retry=0;
: C; a; E% b9 U- \) G - // 检查SPI_SR寄存器的TXE位(发送缓冲为空),其值0时为非空,1时为空
% G; L- m4 ?; n$ S- O2 } - while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) 5 k# } c+ X: S$ D' s2 j$ x
- {$ G& r6 ?' S# z C% |: w& n+ A4 R
- retry++; // 发送缓冲为空时,retry++
1 ?8 ]+ X4 \' I2 | - if(retry>200)return 0;# X' @- J" |/ Z
- }
( ]; h+ Q4 I. R; k6 t - SPI_I2S_SendData(SPI2, TxData); // 通过外设SPI2发送一个数据
+ B+ y/ h |2 n4 C M, h- N7 f+ Y- ^ P - retry=0;
' y/ }+ A9 f( H& J! ^ - // 检查SPI_SR寄存器的RXNE位(接收缓冲为空),其值0时为空,1时为非空) I9 t, H+ o) @+ P; Y* d$ L
- while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) ; c& a3 U" G* X5 A
- {
6 w: j% r9 S) ` B3 V" R - retry++; // 当接收缓冲为非空时,retry++4 B6 f0 R4 W) S0 [% Z2 f) D' Q8 Q" W: E
- if(retry>200)return 0;4 q" I1 `' {, b# K' }) _
- }
( S- k. \& s) D3 J8 d - return SPI_I2S_ReceiveData(SPI2); // 返回通过SPIx最近接收的数据 : [6 u2 y6 O Z8 [. V0 V! U. j
- }2 ^/ {. B* z8 Q2 s( m0 k
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
, W: ^9 d/ V3 B* | ?, K/ u% \3.main.c主函数- c9 O- }. F: m! W/ ?+ M
- /**
3 Z2 c2 ]" A* m4 m/ f - ******************************** STM32F10x *********************************) A/ ?) ~ o8 R
- * @文件名称: main.c# ?4 Y/ @( B7 h2 k6 ~3 k
- * @作者名称: 闲人Ne3 v- G# U' |3 [4 P; B
- * @库版本号: V3.5.0
! v7 i5 K( G* D M5 ^ - * @工程版本: V1.0.0
% v: G0 N |' I" F - * @开发日期: 2021年02月17日
9 L$ ?$ J1 x/ b9 ` - * @摘要简述: 主函数% D! \* P2 y: O% m
- ******************************************************************************/
3 u% K' M* _3 z$ f8 s5 V - /* 包含的头文件 ---------------------------------------------------------------*/
9 ?9 Z/ }' `" p- M - #include "led.h" ^0 D( v/ {+ {+ Y: H
- #include "spi.h"& m U# M# A- V! V* v
- #include "serial_communication.h"2 Y2 \/ X/ G, m
- #include "sys.h"
% M5 A! d0 G. S6 W - #include "delay.h"
4 }& K7 G, N% X% I: d - #include "nvic_configuration.h". f9 V4 {0 S* m r: n% ?
- /************************************************1 r( f3 v' g( {1 m5 Z2 X3 @
- 函数名称:int main()8 e: _$ a; a1 A- Y: [( Q3 W8 A. `
- 函数功能:主函数入口" y: |# G0 g* p% I
- 入口参数:无
. T: X+ w, c3 F! o& o5 Q - 返回参数:int3 G {( | F3 O- c x% S
- 开发作者:闲人Ne
9 o4 i- v' Z- L) N+ h - *************************************************/
. p* p- M! b5 C- I4 t - u8 spi1_sand_data=0;8 A& Q) z8 Z9 R9 m
- u8 spi2_sand_data=100;
7 h( v2 Z0 d5 S/ {! v8 ?- @ - u8 spi2_receive_data;0 y3 W7 G+ s/ M: T; c/ |
- u8 spi1_receive_data;: N* c$ D; u. D8 \) i2 @; J) M
- int main(void)
5 y9 N- w+ k+ U' W( F - {& e& S% D$ A! e/ ^- v% p3 `
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
6 @6 X! E. {/ S) i! { - delay_init();
y3 b: S& y6 J6 O9 b - SPI1_Init();
) X. {/ [4 G, q0 ~* c3 G - SPI2_Init();
# ]4 b8 j# a$ I. S1 \ - My_USART1_Init();+ O1 p1 p) \; F& _
- D1=1;
3 Z5 }' L! o, G; G' ] - while(1)$ w, Z. ~$ N( J$ t
- {
2 B6 \/ M+ _& X# i" m S - spi1_receive_data=SPI1_ReadWriteByte(spi1_sand_data);7 h8 k+ J4 X8 A+ `+ K2 ~
- printf("\nSP1接收的数据为:%d\r\n",spi1_receive_data);
& @0 r, Y" y4 i* l3 S5 A - spi2_receive_data=SPI2_ReadWriteByte(spi2_sand_data); . G" S: v0 R( |; B4 k
- printf("\nSP2接收的数据为:%d\r\n",spi2_receive_data);
3 t0 V# g" ^ n% G7 \ - spi1_sand_data++; spi2_sand_data++;
- m: o4 O* D( P - delay_ms(500);7 N2 t! ^/ L- [- }
- }
% d/ g5 i6 i# p+ g - }4 z6 A% p1 R- d4 q
- /****** Copyright (C)2021 闲人Ne. All Rights Reserved ****** END OF FILE *******/
复制代码
+ q2 i5 K. Y6 u$ f8 S# @* V实验结果
: [+ r8 s3 R5 j M8 O N# q
& y. L" m2 x. G# x1 [3 F
: c* p* ~9 i7 | F' _- b. i2 ?实验结果完全符合逻辑,通讯成功!
; y7 v4 H3 r E4 Z' ?/ L9 P
& b; H8 B/ C' b3 J4 V3 [. j, N经验分享$ D1 C9 f1 \0 g8 }2 h
9 s# A4 N1 w+ q) X! X# T
该程序可作为双芯片之间SPI通讯的参考代码。
) H1 x: m5 d4 { i————————————————* ~& h2 E+ r/ Y* u
版权声明:天亮继续睡
6 l3 P# M. J c# O0 b& `: w# S/ B6 h% g5 g* e; U$ g
h/ f7 G: S; Y# l1 E |