通过串口与上位机通信是经常用到的调试方法。
) s$ ]0 x4 f5 i, e) g& z9 f, ^( w# }. _) L- J( ^6 ?
STM32上外设USART引脚配置
; K; C+ V' [ \6 Q+ a0 |1 [TX(默认PA9):复用推挽输出
- n, I% R3 }9 |3 P6 [RX(默认PA10):浮空输入或上拉输入- R8 t. U" p% Z2 W8 Y' b
8 V+ _/ p& M' F4 R0 w+ @
在写代码前需要检查硬件是否满足要求,使用串口通信时一般需要安装CH340驱动或者CP210x等,这取决于你的电平转换芯片是什么。3 X& g5 p8 q @# B
( p( f0 _5 x: M串口设置的步骤一般为:
: v4 H9 F% R6 r1.使能串口时钟,使能GPIO时钟;- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA);
复制代码
5 C( U/ \9 r, r0 k需要同时打开GPIO和外设时钟。0 d$ Y( y1 Y9 c1 A9 z! X
2. 设置GPIO端口模式;. ]1 w4 k) G: P( w) S
$ r# p# S) v+ u" N- GPIO_InitTypeDef GPIO_InitStructure;, v" n/ I2 |2 n2 C, n0 {" Y
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;. T8 @0 F, ~ F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
# M8 B M) D& X7 X; R - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
6 J9 o- f) U2 O& [0 G; F3 [9 @ - GPIO_Init(GPIOA, &GPIO_InitStructure);
5 z) x. ^8 _) i' z6 ^' w% D' ?
! t. U% X% ~# `6 k( {( K2 F- w- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
! Y# @- j3 j6 N - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; 7 a3 P1 K3 L/ ]/ @. M
- GPIO_Init(GPIOA, &GPIO_InitStructure);
复制代码
% {4 {: C) e# K7 ~- ~& M3 \PA9:复用推挽模式 PA10:浮空输入1 O, V& {! }$ {" e8 p$ q/ |+ |
' O# I( t' l; E( \% a9 H! o# ^3. 初始化串口参数;; z( k6 _1 r; V) [6 O; i/ M
: H4 @# `# X/ a- USART_InitTypeDef USART_InitStructure;
& t/ J) Y. ]& n* h. x h - USART_InitStructure.USART_BaudRate = 115200; //设置波特率;
9 P& K, a1 e8 e) w6 Q4 x - USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式& }. e/ J" T% N* n) ]
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //1位停止位+ U6 j* @& e* D. l& F
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
F; b& j; ^+ [6 Y2 c) R - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
4 S$ b6 {" M) ^ {: X9 l) |9 R - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式+ e9 E/ x2 t! ?0 O3 q9 e% j: _) N. y
- USART_Init(USART1, &USART_InitStructure); //初始化串口
复制代码 ) ^" `9 g, s& m# [' D" F
4. 开启中断并初始化NVIC(如果需要开启中断才需要这个步骤);
, ]+ X, c4 v9 q9 Q6 r1 Y8 P/ @6 F
! p2 b8 s* [* P* O$ [- NVIC_Init(&NVIC_InitStructure);
* q2 v4 ]- z2 C1 h0 A; _9 o" e9 |# L - USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);
) }( G& z. ^. r- Y - USART_ITConfig(USART1, USART_IT_TXE,ENABLE);
复制代码 5 Q: z$ x( Y B" C
RXNE是“准备好读取到的数据”事件标志,TXE是“发送数据寄存器为空”事件标志。
/ Z6 y( ~, e2 Z- a9 V5. 使能串口;
; O4 F1 O( i! y: C/ x$ D- S/ w, `2 u
m6 N9 X" W1 H- USART_Cmd(USART1, ENABLE);
复制代码
' X9 h1 V% L/ ?4 q, k, V2 O6 E6. 编写中断处理函数;) F& w' t7 @8 W' a1 `* [% C: d. g! `
4 e5 T* g% q2 [) {* g6 u' V
- void USART1_IRQHandler(void)$ c# a) X/ R& M- _- U9 ^7 p8 C
- {
3 Z4 s ?1 q% x5 o. d: I* c7 m. u; @. P - if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)% q# r% E5 [2 C% u$ F% \8 q
- {8 h4 t5 W* e+ w
- //编写处理程序
1 Q/ A+ X$ y! O - }
" r8 Y" C; A% R% z4 v - }
复制代码 , c# y7 N9 b# l3 d! P
可以根据特定的中断事件,比如判断是否发生串口发送完成中断(如代码所示)。2 D! W* H8 ~, q) w- A0 ~% t# `
: f" {4 x' c& Q2 X: T4 w" K
7. 发送接收数据。$ i. N: a6 D4 w2 o7 |
; F! l6 ~( X. }. f
利用发送数据函数USART_SendData(USARTx,ch); 可以发送一个字节到串口,并利用USART_GetFlagStatus() 读取发送数据寄存器的状态来 等待发送寄存器将数据成功发送。* L# d8 J+ {8 V+ U% t1 s8 H- f/ L
7 H/ O, s& Z" S/ |8 O
- void USART1_IRQHandler(void). D) l! N6 T( `' u, K
- {
. T B( w" l0 Y1 b* M# e+ z: k - if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
) P) v( i/ `, D6 U N) p - {9 i& w6 y% f- {$ D. y; ^
- /* 发送一个字节数据到USART */ 9 H: Q, ^" o* y8 d4 v/ k" U$ d
- USART_SendData(USART1,ch);
) D6 I2 [5 q w7 v - /* 等待发送数据寄存器为空 */
^& A' {# f; B2 M) _" J: Q - while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);. q1 X( o8 @3 {5 d$ n
- }
+ p* h) x' a* \! z! ] - }
复制代码
" C# Z# i/ A( d) {8 ]0 R接收数据利用函数USART_ReceiveData(); 从DR寄存器读取接收到的数据;在中断处理函数中通过if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET)判断是否是接收中断,如果是,则读取串口接收到的数据:Result=USART_ReceiveData(USART1);
. W2 n, Z" S4 P( }9 I* c) e; l: a
- void USART1_IRQHandler(void)
9 \7 u: P" a; x. G1 V - {) O1 H( s3 [9 Y$ ~/ \7 @) _5 v6 K
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET), G' ~3 Z' T; K0 s
- {9 ]- }3 G I/ k
- Result = USART_ReceiveData(USART1,ch); 7 k2 _) c7 G: ?6 L, @
- while (USART_GetFlagStatus(USART1, USART_IT_RXNE) == RESET);
8 c- V# E0 h7 {. h& S2 o - }
% w3 D3 ?* s- d - }
复制代码 0 }, T* Z7 i6 X+ n8 z* _; k, s
根据USART_SendData(USARTx,ch);和USART_ReceiveData();两个函数,每次发送或接收一个字节数据,以此为基础,可编写一次发送或接收一个字符串的函数,但这样还是不够方便,为了能像C语言那样使用printf语句输出,需要重定向printf函数发送字符串。
( N7 {1 _8 T: t# z( Nprintf()函数实际上是一个宏,最终调用的是 fputc()这个函数来执行输出的,所以如果重新定义了这个函数,就能使函数向串口输出。
0 u0 Q" l ^$ S; y4 q! p0 q' M& w6 U ]% M. v* d- J5 E- E
- /* 重定向printf函数 */) O o% }+ ^# @2 t' a% g; S9 P
- int fputc(int ch, FILE *f)
" o& e- u( {# B' r - {
8 |6 K& ]# ^) M: D - USART_SendData(USART1, (uint8_t) ch);
# X3 k' e* t6 ? - /* 等待发送完毕 */ . ~$ p3 q$ @1 {. O# C6 M/ }
- while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
# I$ H0 U- j/ |/ r+ W - return ch;
+ O, B& o$ T0 v* u5 h* A4 |$ u& Z - }
复制代码
9 R2 S+ @6 \, c% e$ r同时也可以重定向scanf()函数接收字符串:
2 y& X' P+ D( N
{' m' e% B1 _7 W3 R/ @6 Z2 n6 z: l- int fgetc(FILE *f)
2 n5 f* ~; U2 a - { 2 Z/ E( Z+ W* m9 h* [8 U$ V* i
- /* 等待串口输入数据 */+ U' k3 `/ I, ]* m: e ?
- while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);' i* F# ^' E& v7 D
- return (int)USART_ReceiveData(USART1); ! J L' i H$ }) @
- }
复制代码
9 U Z6 i1 s8 k! G6 C, P) D6 j————————————————+ q8 s8 `& @) G' _& `
版权声明:云缙
0 N8 X! h6 n9 w6 E+ \2 {; m5 x9 ?& ]7 M1 S' \3 C b" L- q
/ E9 o" S& y! Z% K7 v
|