STM32单片机应用笔记 (三) SPI通讯 stm32f103单片机SPI通讯代码: - static void bsp_GPIO_Init(void)& t1 G! |7 k0 T
- {- v4 B( ` \ k
- GPIO_InitTypeDef GPIO_InitStructure;/ C# m1 F2 ^3 B
- EXTI_InitTypeDef EXTI_InitStructure;# @, g( ^& w! z) N) M1 t
-
8 r/ W1 }3 x6 j3 w7 v, ]: q - RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); //开外设时钟
- S3 T- {+ ]+ v$ r1 J1 I - GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //关闭jtag开sw
* C+ I: \5 J8 l) s -
# O M5 v N/ ]; `8 p- i% d - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK MOSI8 z' @( K4 Y$ n
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;" O, ?# J" n6 v, K! A& Q* U
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
2 \3 L: T# G8 }) L& s& J- C+ O - GPIO_Init(GPIOA, &GPIO_InitStructure);" y8 Q! q# o+ Q% T! b
- 7 P( _: R. `/ w# I! ~' x- u
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //MISO
/ y+ c2 o% [3 q& U$ l2 a - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/ f5 C a5 [7 Y& d
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
" F/ L a: ~2 N# D6 a, g - GPIO_Init(GPIOA, &GPIO_InitStructure); # R: B- W- B1 d: w, k! _
- }1 s0 z# d2 @( S7 z3 ^* b- h
- 7 z- T+ i6 V0 u! z% F+ j4 r
- static void bsp_SPI_Init(void)! ]- C7 j$ X; ?0 f6 `8 r. A
- {0 Q( |/ S$ i! b n
- SPI_InitTypeDef SPI_InitStructure;- D, |( c# ^$ @: a. W
- ; j, I, U7 [% J P+ g
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);* K8 V1 v2 s0 U6 G/ D
- 0 R% W1 K6 T% Y2 \ P1 _8 Q
- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //工作模式(双线或者单线) k* k3 V: [: @( i
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式+ d# o! `- y; @; a, z4 q; t
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8为数据传输
7 `+ E; [/ i# b6 w - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟不工作时的状态
3 K6 p" I* D9 y* l* ?! v% B - SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //有效触发的边沿( H( j% Y+ |5 K1 O' q
- SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选软件触发$ D/ }- e2 P w' w% z% m! R2 t# s
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //spi时钟频率
! ?% ]3 g2 U1 a* Y3 H - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位优先9 ~/ S0 L5 ~& Y y0 ?3 j
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC(默认的是7)! Z* ~/ A& Y8 Z
- SPI_Init(SPI1, &SPI_InitStructure);( `5 H" R8 l9 e6 m5 P
9 c5 K1 k5 I9 x/ V$ G/ a& q- SPI_Cmd(SPI1, ENABLE);# o) R+ U7 X& v$ D5 \' F. @
- }2 n9 k" I3 l9 I' U4 |
- I! G# c; M2 `) a& t% t
- , J# f+ E9 o3 k
- u8 bsp_SPI_ReadWriteByte(u8 txdata)
% z; c# v: [7 u9 l9 I - {: a$ L c# J: ]1 \
- u8 waitcnt = 0;
2 D) Y4 o' ^9 m3 m6 H, L0 L3 K -
$ K0 V! ?8 y0 R4 Y# f - while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //等待发送区空
0 r$ v, ]5 f: K+ U9 E - SPI_I2S_SendData(SPI1, txdata); & m7 F9 T* r# y! ~
-
* |2 k0 J$ B& _' L8 Q4 K - // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
' N; P& B6 h4 W5 Q0 x - while((SPI1->SR & SPI_I2S_FLAG_RXNE) == RESET)* p/ {* s: H) V" T* e4 M
- {
6 _* c& B$ X, S. S: M. S8 d - waitcnt++;5 T& c( M" H$ S6 X7 { ^2 t* z* s0 u( O
- if(waitcnt >= 200)3 F) s+ e4 E6 v2 x% E
- return 0;
5 y: q2 @. J9 x* [ w1 {0 W - }
7 }$ Y d! n- L - ! a3 {# l" T1 I1 S4 G" p
- return SPI_I2S_ReceiveData(SPI1);
( e2 U; `/ k7 o$ q - }
复制代码 # m+ \- V# b6 P
以上代码是使用stm32自带的SPI模块功能,数据的收发使用查询的方式,没有使用中断功能。工作中使用SPI一般是控制LED灯的驱动芯片,ADC采样芯片以及DAC芯片,都属于那种定时的读写或者有某种触发条件读写的情况,因此基本没使用过SPI的中断以及SPI的DMA功能。 下面的代码是使用IO口模拟SPI功能: - /*****************************************************************
c2 p* R! L: C6 t! l+ V& D1 k - 函数名称:SPI_GPIO_Conf5 P8 w# r4 a* {
- 函数功能:spi引脚配置6 V8 r+ w9 h, G, e% ?( E2 u
- 输 入:无+ a( j1 C, J `
- 返 回:无' G" U- _# ^, o' O' `
- ******************************************************************/
8 A/ n8 d( d) G6 v( D* r, b - void SPI_GPIO_Conf(void)
. l9 H/ l# s! m3 S+ ^7 Y2 S7 F0 c( b - {- m2 B6 Y0 A( J) P( } Z2 _ O, r6 f
- GPIO_InitTypeDef GPIO_InitStructure;
$ ?5 B. ^: g! C& Z' Y4 E -
) H' b/ o3 R+ M2 ?4 J) C3 P - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;1 w: `) F1 x2 N5 K! q. j
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
5 w+ X1 r- I0 o, d r. ~ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
9 {9 v9 e# G( k& {2 T4 ?- | - GPIO_Init(GPIOB,&GPIO_InitStructure);, {! m9 e' W$ r
- - j( D: w: _# y) U" X
- SPI_OE(0); //片选-使能芯片
/ c' t1 T% Y6 f3 W( Y - SPI_CLR(1); //清除寄存器
) H$ _. y! b! R! \/ h$ W - }
0 o8 k4 n6 q6 r2 _& F' M
* u. X" W& u4 ^1 J* Z- /*****************************************************************
" O6 c4 i% K; J1 r4 Y4 y# w$ Z - 函数名称:SPI_WriteData
4 Y7 d, z8 | I6 g - 函数功能:spi写数据& }1 V0 K7 l9 B. Q
- 输 入:data:写数据8位
4 G) a& _: k* `4 p+ D; x F - 返 回:无9 f2 D' u% T" P5 \' X
- ******************************************************************/
9 J( ]3 h; B6 B. M/ W0 }) Y- S - void SPI_WriteData(u8 data)
" L! D, m" [2 `6 ?/ N4 z% R( X% g - {
( q' g# {2 @- x. U* X z - u8 i = 0;+ W3 B4 ^* v5 Z, v( E4 [/ q& S. A# j
- / P }1 I( N E2 J' ]. `( P" I
- SPI_CLK(0); //上升沿锁存,置低锁存引脚8 w! u8 M' O1 K5 w! R6 p. I
- Delay_us(1);0 `8 |. ^" B) Q9 X o# |# ^
- ; V k$ }! }8 `+ p5 J) P2 v: x
- for(i = 0;i < 8;i++)
/ n( y4 F4 o# h4 { - {
t9 g3 H2 _0 q2 B1 s F - SPI_SCK(0); //上升沿写数据,置低时钟引脚( \& D N/ K0 D2 S6 U2 E Q
- SPI_SDO(data & 0x80); //写数据值4 }% ^# t' M# b
- data <<= 1;1 E3 F$ U% }$ ^. m4 ?: f9 K
- SPI_SCK(1); //置高时钟引脚输出数据
$ a/ u2 _. [7 Z0 p v. k- B - }' d0 i) x* i* H& j" p [3 |9 k0 [- F
-
: `( H# q; R- x z. b/ Q2 b - Delay_us(1);
. X) |$ K; B `4 I. C2 i! m( @" ` - SPI_CLK(1); //置高锁存引脚,锁存数据" b% |. [+ {- h9 D3 ]
- }
复制代码
- O( }" E# |' r; g/ ]* V' t使用IO口模拟的方式实现SPI功能主要是看明白时序图,按照时序要求操作相应引脚的电平就可以了,各引脚电平变化保持有时间要求的需要通过延时实现,各电平配合不得当有可能会导致通讯失败。 以上代码只是写数据的,读数据的与之相似,工程中没有用到就没写。 注:程序中加了一些注释,工程文件中为了整洁对齐,有些注释比较靠右,开始以为不能显示,后来发现代码下面有个移动条,通过移动条可以左右滑动。 文章出处: 单片机学习点滴 + x$ s% v% F, P i
|