STM32单片机应用笔记 (三) SPI通讯 stm32f103单片机SPI通讯代码: - static void bsp_GPIO_Init(void)
& i/ p* g* v( {# a* J; r! v' U - {8 {' k1 T# @3 V& [/ ^% a
- GPIO_InitTypeDef GPIO_InitStructure;
& g- {8 X! C9 \7 s2 ? - EXTI_InitTypeDef EXTI_InitStructure;: }: v: W% s% q4 g# A/ W
-
6 T7 D1 W2 S( m3 A$ q: Q0 e - RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); //开外设时钟5 n" b' a+ b2 w1 D0 b: v7 |
- GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //关闭jtag开sw
: S% S, `0 i$ g: o9 g - ) w. X4 _8 |- U* B/ R. B- W& M6 s
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7; // SCK MOSI# X. o: h, a6 h; n: f
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
) ^" r' z8 }; m8 Q* V - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
4 g' z0 a' O" i; A/ z1 o4 j - GPIO_Init(GPIOA, &GPIO_InitStructure);
. J* `1 I) ^+ `- t0 V4 p. J - + `) W8 B: S- u/ c- c2 C
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //MISO" ]6 k8 H8 G+ H% n
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ W5 I* w# D% o2 h! B- P/ M. x
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
% n# z. ~: s; ?; T! c - GPIO_Init(GPIOA, &GPIO_InitStructure); + a' s6 H8 S. q; W& i- T; J
- }; ?# L/ A, E R+ J; {! r
! z. {7 g& B+ k( R* E% E8 J, Z; j5 [: ]- static void bsp_SPI_Init(void)& K, `8 Z7 R/ h! o
- {% V+ m' e5 G* ?; x4 v
- SPI_InitTypeDef SPI_InitStructure;
! C7 _% Z6 c1 X# I* ~4 R -
9 X/ e% m! H, { - RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1,ENABLE);
) ]- f5 g0 i) d6 Z7 c0 A - 8 l4 l! z& q; [4 M c
- SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //工作模式(双线或者单线)
; B K2 Z" H; A4 w4 _ - SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //主机模式2 P9 {. M, b- v5 p$ N, J
- SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //8为数据传输9 W& v# e+ N7 Z" p
- SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //时钟不工作时的状态* E/ H6 @5 w1 E
- SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //有效触发的边沿
+ C1 R! d7 D0 a( j8 t. f9 L - SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //片选软件触发
7 c0 j8 ^5 Y# q* V - SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4; //spi时钟频率$ _) o1 F) C! G2 l8 F* f! ~6 u y* H
- SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //高位优先' b1 X4 | t' Z
- SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC(默认的是7)
* O: ~$ f; U g+ y# x$ e; f - SPI_Init(SPI1, &SPI_InitStructure);: `8 Q0 R# e: ~8 i4 K
- + b: i5 ]0 M% b ^$ `0 V( g% ~
- SPI_Cmd(SPI1, ENABLE);
; \- `! ]2 I0 K - }% @3 |7 t# s, S- y# }2 F
- # `. C1 X( ^6 a$ P8 z2 D6 O: A
+ |; D7 V n, r! {0 B; x( ]- u8 bsp_SPI_ReadWriteByte(u8 txdata)
% {: s9 f$ F3 K" ^. f0 T8 h5 ~ - {; p X7 J/ {* K5 Y( D
- u8 waitcnt = 0;3 l8 a+ F( W6 X4 ]4 b! r' D
- 7 J, u- [, r0 e' @4 {8 |. l
- while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET); //等待发送区空
$ P) I) ~0 l( `5 r/ _& y( M - SPI_I2S_SendData(SPI1, txdata); % B7 }# ~6 H3 I- q1 |0 M
- . p9 a" J4 j, K, ~# W/ l) y
- // while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)
. n6 n" X7 n% Y - while((SPI1->SR & SPI_I2S_FLAG_RXNE) == RESET)
3 h* M( g+ V5 ?) Y/ B - {+ X. y2 Z1 N$ g2 q4 |3 R# g- y
- waitcnt++;; A% D/ |6 {5 m3 G
- if(waitcnt >= 200)
; E4 _6 s/ O3 o$ Q - return 0;5 B6 {2 y, v$ d/ D( V# a
- }
5 W, x3 J2 p5 u. @" A* j -
5 l; _" S. s% _% S( k: ^ - return SPI_I2S_ReceiveData(SPI1);
; r7 @7 M6 ^7 J - }
复制代码 * x6 k# F0 s3 x0 v) t! ^; ^
以上代码是使用stm32自带的SPI模块功能,数据的收发使用查询的方式,没有使用中断功能。工作中使用SPI一般是控制LED灯的驱动芯片,ADC采样芯片以及DAC芯片,都属于那种定时的读写或者有某种触发条件读写的情况,因此基本没使用过SPI的中断以及SPI的DMA功能。 下面的代码是使用IO口模拟SPI功能: - /*****************************************************************
3 E7 {& T- \% _* D. g - 函数名称:SPI_GPIO_Conf
! C% N; ~& T T' w! Y# @0 y, v - 函数功能:spi引脚配置* W, G: V' U; P- k) K: g) n0 P& P/ ~
- 输 入:无5 W" G9 V3 ]5 u2 a7 C, Y$ Z+ Q
- 返 回:无
0 C0 r, \4 O7 N0 X! p9 X - ******************************************************************/) |6 x, ?# t/ k# _9 I
- void SPI_GPIO_Conf(void)
# m6 i @. Y5 s - {% n; R6 [7 M0 R6 N4 v2 f# y: _; N
- GPIO_InitTypeDef GPIO_InitStructure;
/ c3 z6 }# w+ I; {! y - + C- q+ i5 V3 M6 ~
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;' {. @' ?; s# s. |6 D
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
: W& J9 [7 V$ W' K7 |) p' J - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
8 e0 I8 I0 f1 x0 N$ w - GPIO_Init(GPIOB,&GPIO_InitStructure);5 s- |) b/ F, b' |) G( l
-
* X1 |( }7 S: a4 c! R& Q% P) {0 H# E - SPI_OE(0); //片选-使能芯片
4 d0 c9 U& ]. k3 w4 t - SPI_CLR(1); //清除寄存器
3 G8 ]' u3 P( ~( n; U! K4 ]2 w - }( _- l/ P: k8 @- t# N5 q. x+ ]
& X4 W8 d2 F/ b8 d! X5 T- /*****************************************************************
. W( |, K- F' e( i( O" U, _ - 函数名称:SPI_WriteData$ C' l$ `1 a' H3 ^
- 函数功能:spi写数据6 r; k! ^+ M7 @+ U
- 输 入:data:写数据8位# B: J$ C* l+ U _- \: j* B
- 返 回:无
9 Z$ r9 r8 b4 x - ******************************************************************/
3 S% {1 q: W ?3 s. g8 w! w# f - void SPI_WriteData(u8 data)6 `& B$ [( k6 o
- {# P6 F9 d- I; n8 v! @4 S" N
- u8 i = 0;7 f$ q6 \) }3 E& w6 c" O" ^$ e- r
- / S# P5 V* r* ~& [; f- w
- SPI_CLK(0); //上升沿锁存,置低锁存引脚
9 }! @1 T5 J' E$ e: F( c) S - Delay_us(1);5 F! G9 o( ~5 _* |
-
" A6 h% {* p8 m( O3 B - for(i = 0;i < 8;i++)
- r$ K o; W$ R - {& ]' {5 J/ F% [; ~$ x' f$ o% }5 ^
- SPI_SCK(0); //上升沿写数据,置低时钟引脚% l/ ^8 M2 V" w
- SPI_SDO(data & 0x80); //写数据值( p! F9 ^1 q- V- [2 W
- data <<= 1;/ Z' \1 X$ z. m0 K) L- D# Q8 b2 c
- SPI_SCK(1); //置高时钟引脚输出数据* g7 b# r B" q* {$ A% p! h
- }! ~9 M2 |5 S) l" v5 N
-
$ E7 X* z: O# }0 Z* i E - Delay_us(1);' j- c* s1 q6 O2 G# B4 ?" `" B
- SPI_CLK(1); //置高锁存引脚,锁存数据" D! h( e! w/ R
- }
复制代码
, J7 }- u! i; _$ Y7 s; H使用IO口模拟的方式实现SPI功能主要是看明白时序图,按照时序要求操作相应引脚的电平就可以了,各引脚电平变化保持有时间要求的需要通过延时实现,各电平配合不得当有可能会导致通讯失败。 以上代码只是写数据的,读数据的与之相似,工程中没有用到就没写。 注:程序中加了一些注释,工程文件中为了整洁对齐,有些注释比较靠右,开始以为不能显示,后来发现代码下面有个移动条,通过移动条可以左右滑动。 文章出处: 单片机学习点滴 8 U x$ U5 ]. e2 [0 n/ N
|