|
STM32单片机应用笔记 (三) SPI通讯 stm32f103单片机SPI通讯代码: - static void bsp_GPIO_Init(void)/ c2 ?1 }7 e3 q+ _2 Y
- {- m$ @/ I. s. ~! H7 X
- GPIO_InitTypeDef GPIO_InitStructure;' P, q: G, J: S: R. ?& A3 a9 F
- EXTI_InitTypeDef EXTI_InitStructure;" {" c1 P# \! d# v% \: t" o
-
: \+ x. |; E, p+ Q1 i - RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); //开外设时钟; S8 q/ \- V S( j. s' ^
- GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //关闭jtag开sw+ b: d$ H9 u/ i* I1 ?3 Q' s
- 2 ^. Q, h7 W- Z/ [7 T$ a
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK MOSI
) N- B$ U. v D! T. Y+ a - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
$ V W8 W# [" v1 {5 H0 t7 D: ? - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; Z0 T) Q; d# n
- GPIO_Init(GPIOA, &GPIO_InitStructure);# u$ |& q; _6 D9 A1 O& A
-
" H, {1 I$ N% p" e% M* L; w - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //MISO g5 w0 ] j; h
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- Q7 A: B+ S6 J9 `
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
- J7 }" E W1 A# p - GPIO_Init(GPIOA, &GPIO_InitStructure); ) V2 y% A# P/ D. D
- }" _2 p/ b& A, U" {# M* l
$ [0 b4 W1 s8 ]8 ~& {9 w% z' l- static void bsp_SPI_Init(void)2 C3 \: j2 J m
- {
6 m6 K' D4 l& \& Q, z3 M; i - SPI_InitTypeDef SPI_InitStructure;
) u) k( T9 j8 {+ N4 }9 P# W7 S - 5 h* S8 o8 L6 w$ g
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);1 ]! }7 W7 H" t; V
-
. _6 N8 ]5 q3 a2 g @% F( R6 G - SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //工作模式(双线或者单线)3 [$ E0 P/ B) T
- SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式
- a# e& w3 y! V( e% q - SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8为数据传输
9 B6 E* I! u+ ^' w { - SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟不工作时的状态& H$ v$ F) N7 }; V8 y7 t( ]
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //有效触发的边沿
! Q6 N: V7 t* `9 g1 i$ ?% x9 Q - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选软件触发* k1 ?) J% z! {) X7 {- V1 b
- SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //spi时钟频率
% L. t/ H% j3 m - SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位优先# o. Z- X" P Z8 _1 i! e. f$ b/ r
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC(默认的是7) Y4 H9 E8 H( f" e) y
- SPI_Init(SPI1, &SPI_InitStructure);4 c/ ^4 l% v4 m. H4 Q
- $ H9 O% J7 g5 `9 p1 j, l) [$ w
- SPI_Cmd(SPI1, ENABLE);* h v- _/ t, B
- }. a) b' W H2 p4 o( J3 k* O; e6 {
- ) `( B% N- n; ^' [+ Z* A1 X
- , c5 t+ B' q; j' X: H
- u8 bsp_SPI_ReadWriteByte(u8 txdata)4 [6 i, b# l, o) ]9 c% p& r; N/ y
- {! X$ }' D8 P+ |8 I, q6 M1 h% c
- u8 waitcnt = 0;
) K! N8 e5 N. C/ O5 C8 K* K3 f - ) M2 f$ P6 [5 ~* ]7 ~
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //等待发送区空 $ i/ V$ b* W3 f$ R) E+ d, Y; V
- SPI_I2S_SendData(SPI1, txdata); 4 _1 O& q& v- i: D
-
4 ^' A3 N4 U5 \ - // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
0 j0 x# s. v: q' M! D1 h1 v& x( u - while((SPI1->SR & SPI_I2S_FLAG_RXNE) == RESET)
$ j( D& e& D4 B- p6 g - {% Y2 A2 G: r$ J# r0 Y
- waitcnt++;" u5 O- ?" B1 t, z7 }; C3 y
- if(waitcnt >= 200)
* J, d, o, S; c. F3 T# `/ q - return 0;2 y) P2 _" G+ R) e
- }- u" d7 t B6 c6 @( p$ A
- ' P% ~/ F9 n& ]3 x1 `
- return SPI_I2S_ReceiveData(SPI1);% o) }$ Z3 D/ S% g& B E( P
- }
复制代码
1 F! x: L1 G. W+ @- }以上代码是使用stm32自带的SPI模块功能,数据的收发使用查询的方式,没有使用中断功能。工作中使用SPI一般是控制LED灯的驱动芯片,ADC采样芯片以及DAC芯片,都属于那种定时的读写或者有某种触发条件读写的情况,因此基本没使用过SPI的中断以及SPI的DMA功能。 下面的代码是使用IO口模拟SPI功能: - /*****************************************************************
" l$ x3 I! h% |& [3 ~1 t8 f - 函数名称:SPI_GPIO_Conf \$ i& c% N2 g6 [2 {
- 函数功能:spi引脚配置. X( w3 u* L0 k* O) q2 ?3 ^
- 输 入:无
, q j8 V6 i5 s# ]( x. I - 返 回:无* m3 C4 d9 W4 l9 k, ~
- ******************************************************************/
+ [7 n) }' S. Q - void SPI_GPIO_Conf(void), `1 O, \5 r. S: X/ W" b
- {
' n# L! m- e2 I- T - GPIO_InitTypeDef GPIO_InitStructure;
7 R4 {% ?1 H) `) f -
5 d8 S7 Q& M7 p& [7 P" o - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;" j$ C& s9 ~# U, a& v4 v
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
8 s: l$ Y% U9 D& Y. Y4 w6 n; ^6 [ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
" r3 l+ P" K R3 D7 H& `- w - GPIO_Init(GPIOB,&GPIO_InitStructure);
3 f& L8 B: H- }# ]; h - # u' o" O% O4 `) m+ Q7 {" l( |
- SPI_OE(0); //片选-使能芯片 {0 ?7 v S) z1 o X" ]
- SPI_CLR(1); //清除寄存器
8 [8 C2 c9 D/ m: m - }$ O7 [# [2 W; v6 E
* t2 O1 ?+ Z& X `: U% s- /*****************************************************************7 y( i2 R0 d* V4 X1 e0 A0 G/ I
- 函数名称:SPI_WriteData
# b/ Q1 l1 {, o) k* z - 函数功能:spi写数据
# @! B* P& x, ]. ~8 w - 输 入:data:写数据8位 {6 G+ ~1 Z; c
- 返 回:无! ~5 m( I& |5 F2 Q5 z8 n6 \
- ******************************************************************/
1 c: h+ q/ E6 M# ^ - void SPI_WriteData(u8 data)% b' O3 q7 c) D0 K0 f( y0 L
- { x9 D. u" ^6 q* x
- u8 i = 0;$ O# e/ e \# ^9 J* o. s
- 2 ?' V- z! R2 c" }" ]
- SPI_CLK(0); //上升沿锁存,置低锁存引脚
; L6 A3 ]' M6 e5 a. G8 F) b/ s - Delay_us(1);
1 o& N( J- W) w; F -
/ z5 T/ V) K3 ]' h0 ` - for(i = 0;i < 8;i++): r+ _! d8 t( [% m9 U. P6 x3 a
- {( V1 K2 z3 y6 N+ q+ ^5 [, v
- SPI_SCK(0); //上升沿写数据,置低时钟引脚
0 i- e) S/ S1 ]; ?8 b6 k - SPI_SDO(data & 0x80); //写数据值
; H8 I5 _8 G' b# P3 \ - data <<= 1;
) L7 S8 {& R* R+ H: Q - SPI_SCK(1); //置高时钟引脚输出数据8 [8 j$ V# |. J' _
- }2 Q& R6 c0 d% P+ i6 d; J5 q/ z( _3 {
-
* O' m8 ?, q+ Y, ~& j6 v2 B" ]7 W: u - Delay_us(1);
+ Y* K$ N# r* l! |! p8 i) R/ Y - SPI_CLK(1); //置高锁存引脚,锁存数据
( R5 F" [, p. M - }
复制代码 & B1 S0 s. C6 }% N: N; \
使用IO口模拟的方式实现SPI功能主要是看明白时序图,按照时序要求操作相应引脚的电平就可以了,各引脚电平变化保持有时间要求的需要通过延时实现,各电平配合不得当有可能会导致通讯失败。 以上代码只是写数据的,读数据的与之相似,工程中没有用到就没写。 注:程序中加了一些注释,工程文件中为了整洁对齐,有些注释比较靠右,开始以为不能显示,后来发现代码下面有个移动条,通过移动条可以左右滑动。 文章出处: 单片机学习点滴 / T- `/ ?- O9 Y" W( v
|