|
|
EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在基础实验成功的基础上,对串口的调试方法进行实践。硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中。* F [5 d/ U9 Z4 K! W
b) 初始化函数定义:( u# G: b5 x4 F3 ^
void USART_Configuration(void); //定义串口初始化函数
+ s! f, U" w( w$ U5 t) j, X c) 初始化函数调用:% [' Z% S8 B$ l4 G
void UART_Configuration(void); //串口初始化函数调用
* V8 Q2 Y. v- S 初始化代码:: v; ^' a" e! Q' B: s
! f5 F2 K9 I9 m4 z" I- void USART_Configuration(void) //串口初始化函数9 B+ s, R+ G; Z8 A( H% A/ G
2 J6 `1 b% r4 s5 }- {
4 P$ N) I2 s( S, w) v4 f - - O7 T% i5 U4 n5 u2 m6 \
- //串口参数初始化7 V+ ]4 K$ ^. g% e5 J }7 q2 _* X& k" U
' x! v D7 T7 x# N1 L6 y5 p0 t" P- USART_InitTypeDef USART_InitStructure; //串口设置恢复默认参数- w, E; r0 r* x/ X9 w# J0 n
- , {( y/ x- ?, A+ D) R! ^5 b6 U
- //初始化参数设置
7 Z! ^% R2 T1 C
& B3 s# i; H, |2 S: \- USART_InitStructure.USART_BaudRate = 9600; //波特率9600$ k( c8 E! |$ D+ w c& l y4 o
$ R e9 \- y8 X0 v3 q- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长8位$ p1 W: A! n4 B: q X* y+ M7 C7 w" U2 Z
- 1 S) ~$ x( U5 y# u. K3 O$ G
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止字节
7 O! |. V3 F, [ - 4 L; N- u! N% y$ T6 O$ D
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验8 f. e% G0 `% c4 f7 J
6 w" [3 O0 ] h" ?. }- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无流控制$ f( l8 M! I5 ~6 h7 x( M! B, a) `
- + _2 Y$ y; j% A. \5 Y$ k" L. l/ a
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//打开Rx接收和Tx发送功能! H. R- }1 h$ W7 ?! O/ a& I
- 2 T" L8 ?! V2 C$ B, W
- USART_Init(USART1, &USART_InitStructure); //初始化
! o# a3 W# _) _; j. y& f2 i- a
6 [9 U; m' a) ] V5 I0 M |6 O% \% o- USART_Cmd(USART1, ENABLE); //启动串口
" z) L7 p2 ~1 z. d
' G- f7 v) w5 l G' |+ D) E0 y- }
复制代码
6 c" J6 p' }! j9 k RCC中打开相应串口5 i4 f7 a8 s- o" Z7 q5 V
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 , ENABLE);6 j$ v+ ~. I" c' s
GPIO里面设定相应串口管脚模式$ n* t" W7 Z1 ^. Z) l% s
9 h4 m3 J2 k$ e& V% W9 ]& P2 A
- //串口1的管脚初始化2 J/ `6 A* `$ F0 o1 y8 g; F9 C
- ) j5 I& J( Q, q, S( ~9 h9 ~* |
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //管脚95 {% u4 j1 I* D: `1 [" }
+ }; `5 Y, s0 G. x' c0 I( Z' _- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
! m0 m1 w# S" n5 ] e7 _
6 J! W9 j$ ], w( e5 U q) F- GPIO_Init(GPIOA, &GPIO_InitStructure); //TX初始化
, j4 h5 r5 M0 o! p+ ~9 F4 v - ' t g7 w. }( T* h
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //管脚10
3 X: ]# c* p4 s - 4 F7 } D+ ]& d9 U% B' q
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
% q5 C s d0 t# r9 X- H1 u4 R/ s. a4 ~3 H - 5 |& ?3 }) }2 H9 L4 u, h( B
- GPIO_Init(GPIOA, &GPIO_InitStructure); //RX初始化
复制代码
9 s I3 H, y' ]( o d) 简单应用: Z: G8 C* O+ y! V
发送一位字符* E: q1 R# j1 E9 W9 q
USART_SendData(USART1, 数据); //发送一位数据0 c% \2 e; i2 E$ U
% }) r+ D) T7 }8 `/ ^' G
- while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待发送完毕
复制代码
& w7 g- a$ @+ R! o 接收一位字符
. o- F7 q5 J X. S# {
1 r' d" Z; ?5 I9 F# B- while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET){} //等待接收完毕
复制代码
, G# W2 S, d+ }+ J* v! x% f; x0 S 变量= (USART_ReceiveData(USART1)); //接受一个字节
% a( K0 N' p# L( Y4 R% O 发送一个字符串
& ]5 Z8 `# U. z6 O1 Y8 {4 J 先定义字符串:char rx_data[250];) G4 d0 H# O5 i% w+ x# M
然后在需要发送的地方添加如下代码
8 j% y/ e; n: ^: R* s# ^) ~' b) I/ @# |
- int i; //定义循环变量+ Z* _0 n* O, q" }
" r! H2 V2 r# H# S; u% s: S3 p7 H- while(rx_data!='\0') //循环逐字输出,到结束字'\0'
7 q# Y! A- g: ~# r
' W6 z8 G7 z( ]: g2 ~" Q% U& w0 g- {USART_SendData(USART1, rx_data); //发送字符) R" ~! Y: Y- @* t
* T- X8 Q& m! [/ |% d- while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待字符发送完毕
6 b) n6 h. p7 [! F( {1 ?0 W' o
* R2 y% M& U7 U2 F k7 u0 Z. T- i++;}
复制代码
$ I5 w* o! N& D* {2 z" ^9 s e) USART注意事项:
( l/ f6 P% R. i; p 发动和接受都需要配合标志等待。
' C! c; V" H$ i: M6 t6 t* f 只能对一个字节操作,对字符串等大量数据操作需要写函数
% B m" o2 P; K 使用串口所需设置:RCC初始化里面打开RCC_APB2PeriphClockCmd
5 X: S# ~$ a- A7 r; X: o0 J (RCC_APB2Periph_USARTx);GPIO里面管脚设定:串口RX(50Hz,IN_FLOATING);串口TX(50Hz,AF_PP);3 Y7 I0 ~9 m5 H
f) printf函数重定义(不必理解,调试通过以备后用)6 |( ? W3 O0 i/ [, m \
(1) 需要c标准函数:/ ?( ]. B. W/ L8 t
; ?$ m- w8 v! p8 J
2 Y4 @( n+ C0 _0 z; x E1 U+ j) | (2) 粘贴函数定义代码
* D# u. q3 [# [" x0 q# E! m #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) //定义为putchar应用* V; R: Z p1 o* y" A$ n
(3) RCC中打开相应串口' Y% G& q) U7 g" H! I% j; W
(4) GPIO里面设定相应串口管脚模式
$ a2 K/ r1 ~/ ]5 h& H (6) 增加为putchar函数。5 e/ J% \8 [- S+ B9 W
int putchar(int c) //putchar函数9 b8 f$ ^7 L9 |
% _( w9 N2 m3 [4 G% ]- <p> {</p>6 ~* Y# L$ B7 X/ I* @5 n
- <p> if (c == '\n'){putchar('\r');} //将printf的\n变成\r</p>! Q4 u7 I' s6 |7 J3 A6 [
- <p> USART_SendData(USART1, c); //发送字符</p>+ n- X1 q2 [6 E! Y4 q$ Q
- <p> while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){} //等待发送结束</p>8 f0 w; c) q1 C. W
- <p> return c; //返回值</p>
7 O, |5 k0 b0 W! q1 J# F7 R$ ]8 {2 r- K - <p> }</p>
复制代码
8 K1 i9 y2 M, k. t9 M6 ~4 ~' U+ X# T (8) 通过,试验成功。printf使用变量输出:%c字符,%d整数,%f浮点数,%s字符串,/n或/r为换行。注意:只能用于main.c中。2 [. \1 h& y% _
3、 NVIC串口中断的应用
) n& c( [+ @; X9 U* k a) 目的:利用前面调通的硬件基础,和几个函数的代码,进行串口的中断输入练习。因为在实际应用中,不使用中断进行的输入是效率非常低的,这种用法很少见,大部分串口的输入都离不开中断。$ y I) G: w% _3 e4 Q+ ~1 h8 N' ]
b) 初始化函数定义及函数调用:不用添加和调用初始化函数,在指定调试地址的时候已经调用过,在那个NVIC_Configuration里面添加相应开中断代码就行了。
1 I- b$ R O' Y/ V3 Q/ P1 u! r4 m c) 过程:3 m" t3 q) B2 N: N* }$ ^% e
i. 在串口初始化中USART_Cmd之前加入中断设置:
, ~, Q) P2 L$ U; u* W0 G8 b- c/ Y7 L, x+ t
- USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//TXE发送中断,TC传输完成中断,RXNE接收中断,PE奇偶错误中断,可以是多个。" L0 G H# f- z& V( c6 p9 C
& J# i; O- ~, o- d/ C- ii. RCC、GPIO里面打开串口相应的基本时钟、管脚设置
& v( H# n ~/ k" H( j& @7 |# @9 r
4 O; E, C: o$ x9 ?3 T" K- iii. NVIC里面加入串口中断打开代码:' y# I9 K' Q, h$ ~; x5 i& T; a
- / {/ m" _! l7 t( [
- NVIC_InitTypeDef NVIC_InitStructure;//中断默认参数- N7 Q! `6 j+ b0 [
& [/ _5 D6 U; C- O0 N; S- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;//通道设置为串口1中断0 W' N4 X* F2 Q! A. L" K8 c$ J
- 5 }9 b" x# {1 j: C0 l+ }! p
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //中断占先等级0
5 u# t" h/ P) }1 N$ d& a! O; ~: D - 0 S" J1 t _. k! b
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //中断响应优先级0
/ D( b/ [8 v& f# B6 B' d
( @/ i6 T. O$ ~- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //打开中断
% i$ }* v8 W5 N$ h: k% A0 Z& f4 Q
% o! N! l4 S: B) g1 ? Q3 E9 `- NVIC_Init(&NVIC_InitStructure); //初始化
复制代码
% f# X% \0 M/ ~5 a+ b$ F4 w# e iv. 在stm32f10x_it.c文件中找到void USART1_IRQHandler函数,在其中添入执行代码。一般最少三个步骤:先使用if语句判断是发生那个中断,然后清除中断标志位,最后给字符串赋值,或做其他事情。
; f, O: J0 U* S: G* D z" ^5 _0 d! {0 K
- void USART1_IRQHandler(void) //串口1中断
, c. o l4 I8 `
( V: B" [+ \! [" w" ~& a0 e- {# p6 K5 k& Z2 v: R1 j7 N4 m
- $ z8 x7 @. j6 J: j$ t b/ |
- char RX_dat; //定义字符变量
4 H8 e' T, z5 t @& q# [+ } - 0 x4 e) {$ ]1 @& g; N: w- ^
- if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //判断发生接收中断% S1 I( v9 |0 V: w; S6 S
8 n# J5 U# d7 H8 J- {USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除中断标志! E& ]4 X3 R% i6 ?
+ ?$ h. T! |/ y4 P- GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)0x01); //开始传输
8 W/ n' G& O# q0 t. D
# D2 p# V; V- l- RX_dat=USART_ReceiveData(USART1) & 0x7F; //接收数据,整理除去前两位
8 N' t2 g& G3 @' C' U! K2 R
_+ L; S0 M% G' E9 }) _- USART_SendData(USART1, RX_dat); //发送数据" }7 }+ v& b8 L+ o7 t' \. t8 ?
- 7 ~- u' I Y7 v$ U4 L9 c
- while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET){}//等待发送结束
, A/ r7 _ X% ~& W9 }* \
4 F+ q/ i! y0 i9 z- }
/ \" J; {5 i! o. \. z
. U% J1 v D2 U P! _. r" _- }
复制代码 , S b# W3 D8 p0 P
d) 中断注意事项:
. f9 p D% t* i; p9 G 可以随时在程序中使用USART_ITConfig(USART1, USART_IT_TXE, DISABLE);来关闭中断响应。" e- X; S' B- X( F. Y5 h
NVIC_InitTypeDef NVIC_InitStructure定义一定要加在NVIC初始化模块的第一句。
; l$ d1 `" z5 M7 | 全局变量与函数的定义:在任意.c文件中定义的变量或函数,在其它.c文件中使用extern+定义代码再次定义就可以直接调用了。4 P. b B, l c0 X+ o0 L$ p+ }% g
STM32笔记之九:打断它来为我办事,EXIT (外部I/O中断)应用. i d1 y1 t. @* Q8 P/ ]2 H0 i# Y
a) 目的:跟串口输入类似,不使用中断进行的IO输入效率也很低,而且可以通过EXTI插入按钮事件,本节联系EXTI中断。, ?1 A3 X3 B3 b1 ?9 _9 L, m
b) 初始化函数定义:' d5 J$ S+ _ h3 M- \* d# A. P$ G
0 m5 F% z3 z1 Z, L' C- void EXTI_Configuration(void); //定义IO中断初始化函数
复制代码
' }% _( H8 @. G% A8 u c) 初始化函数调用:
! F! R8 R6 \; C6 C
1 {8 M! W3 ]; w# L- EXTI_Configuration();//IO中断初始化函数调用简单应用:
复制代码 ; k! w) Z) c1 c4 P, K* m
d) 初始化函数:+ @/ x" d1 i, I% O9 N
z9 W- D. {# I$ T
- void EXTI_Configuration(void); V- k6 A1 O" N9 g% c
; {3 Q, t$ q! i* s7 N- { EXTI_InitTypeDef EXTI_InitStructure; //EXTI初始化结构定义
/ B/ e9 \- ~8 | - 8 @: D8 ]/ f! w
- EXTI_ClearITPendingBit(EXTI_LINE_KEY_BUTTON);//清除中断标志( n, x' a9 e. a. r5 C; |
# C% O' o9 |+ y1 u2 w. A- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource3);//管脚选择
* C" C% y5 d8 h/ k4 N/ @+ D! Y
* B7 n+ J# w8 V: `+ W; @- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource4);9 J. K1 Q1 @2 ], P; |
- 4 o$ `- k5 x) c K- ~6 v
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource5);9 a, I4 Q: K* c7 s+ t& H
- * @. ?' X! i* L6 e0 L- E) ?
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource6);! A. q4 G, X$ P# I: s$ u* b8 |% y/ B
- 5 B4 T9 [" O' x0 F' L3 X/ v
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//事件选择
1 r& K1 P8 M. S* y8 N( M - 6 @/ e7 O1 v- ?4 o" U
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//触发模式- n& G6 C* y9 H
- 1 Z' V0 Y" G* `( ]! z3 [
- EXTI_InitStructure.EXTI_Line = EXTI_Line3 | EXTI_Line4; //线路选择. P; s: l8 {5 g! a! N
1 I) { i. r; \. V9 y. W2 j. ^- EXTI_InitStructure.EXTI_LineCmd = ENABLE;//启动中断- J$ K+ Y) M9 o% Z8 f% T3 V/ i* g
" t. E7 \5 L" s! z- EXTI_Init(&EXTI_InitStructure);//初始化4 s6 }5 H/ x6 R1 B8 i
- # \$ d0 {: N9 s) |6 G2 i ~! e, A7 X1 L
- }
复制代码 5 Z% h. v1 m/ n: i( o% T" r! `
e) RCC初始化函数中开启I/O时钟$ {+ @3 o8 Z" I! V$ W
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);7 c; }# x, n( V/ [ _
GPIO初始化函数中定义输入I/O管脚。. c J& j2 |0 y8 V" l2 i/ Y# l
# j/ Z2 L! L8 n3 j5 ~0 |% V) ]
- //IO输入,GPIOA的4脚输入
% }4 Q! X* o. Q4 x
2 w4 I, K& X5 H3 F" d- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;1 G4 \% _4 s& u# R
- * H- a2 ^; s" [9 x0 {+ w0 K
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
6 a9 D3 u1 t5 w0 C, k - 6 v1 O$ I2 H' ]% b! \, t) l9 k
- GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化$ m. Y3 y( Z8 ~4 ]; S2 g# I& m7 J
- ; A, u, G! E' V& j
- f) 在NVIC的初始化函数里面增加以下代码打开相关中断:
; z- k2 M, D1 F# o/ Z3 y$ }$ O - 1 F* D% N l1 d5 R
- NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel; //通道/ ~2 t0 [0 P% s7 o- [* i
) ?0 X5 U& C: y3 i- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//占先级
8 g) X7 b. l% k& `5 _& Y+ J* U
8 r* L4 y# l6 e" i: t8 A8 i- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //响应级+ b# S1 H* ^! P5 Y& U
- 8 ^0 f1 H, x# X. N7 ]* J* W
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //启动
, U5 F0 m( i' O4 E( H7 l - 8 Q% f" W B2 Q A* ?% @/ ^
- NVIC_Init(&NVIC_InitStructure); //初始化
复制代码
' a! \* W" V1 J g) 在stm32f10x_it.c文件中找到void USART1_IRQHandler函数,在其中添入执行代码。一般最少三个步骤:先使用if语句判断是发生那个中断,然后清除中断标志位,最后给字符串赋值,或做其他事情。7 m; k: @( j( c7 N4 q
# P* t- M# U9 m9 N5 \+ L9 ~# I- if(EXTI_GetITStatus(EXTI_Line3) != RESET) //判断中断发生来源$ C0 [) _- V: E) @5 y
4 {% V5 [* h3 f, M$ u8 K* k- { EXTI_ClearITPendingBit(EXTI_Line3); //清除中断标志1 e; |# @" t- b* P
% R* C3 ?& M# y& w/ Y; D- USART_SendData(USART1, 0x41); //发送字符“a”1 F8 _. B* s! x8 s0 L, }$ m
0 O; p! X3 [& a% ?' p- GPIO_WriteBit(GPIOB, GPIO_Pin_2, (BitAction)(1-GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_2)));//LED发生明暗交替9 U! M, \( ]2 u8 e: ^
- . A1 O3 {7 v% O5 N% ?9 ~) n; P
- }
复制代码
5 B0 T6 `. z0 W) Y4 T h) 中断注意事项:5 A; Z3 L" R' W2 y! S; a9 Y+ j3 S
中断发生后必须清除中断位,否则会出现死循环不断发生这个中断。然后需要对中断类型进行判断再执行代码。& i4 a" [# q8 m8 f. J' E
使用EXTI的I/O中断,在完成RCC与GPIO硬件设置之后需要做三件事:初始化EXTI、NVIC开中断、编写中断执行代码。' L: I8 p# l. A9 p9 _% o
-----------------------------------------------------------------------------------* v! }$ O$ ] A* ~$ X0 B
补充. U# `" y) u, A% l
上边的不足之处就是无法发送char类型数据,是u16类型的,会报错
5 I" h0 N' i! m4 @; k" [. X 后续可以更换为3.5的固件库,; ~. G/ n; K* W4 q/ }
d) 简单应用中
+ J" c. j y! L G9 J' q, E- \ 超级简单应用只可以发送u16类型数据
& X! b* H6 r% W8 A2 W7 v 下面发送char数组时,9 Q/ q4 u8 X, Z5 R2 w
“{USART_SendData(USART1, rx_data);、、” 中rx_data需要改为
+ h# ^: s: A8 [+ S) S0 d" z! o rx_data才可以,不然就会报错数据类型不匹配,4 Z+ `" ~1 @9 l$ l/ E+ q% t7 Z
`! w8 M6 c& ]! g- j5 N( E* E( F
# E& H( f& o: L% ?7 f: s/ a* Y& X ^' _1 W6 J+ I
% \8 t" `/ K# n$ o( D
6 ^) F7 z# T2 b | G o* `3 x |
|