在调试程序的时候经常需要使用printf’函数来打印相关调试信息,在STM103单片机中printf函数默认只能在串口1使用,有时候串口1被占用了,想要输出调试信息时会比较麻烦。现在就总结一下如何在其他串口上使用printf函数。这里使用正点原子的例程来做说明。
. `/ C2 e$ p! X$ d+ p- E! s默认串口1初始化代码如下
0 I- t/ ~" p5 ~* a: I( i! d2 i4 s% f0 v
2 Y& \1 Q+ z) n8 i! t+ l9 d- #if 11 E- |; K/ P. e. W0 k
- #pragma import(__use_no_semihosting)
' U0 E, }3 T$ o - //标准库需要的支持函数; |% p4 |2 i. {. |' r4 b
- struct __FILE: h& _% [: \: P: m
- {
7 R6 m$ Y. B: d2 R' P - int handle;4 c2 l5 E. I0 y- ~) v& a+ P
- };: J! u7 [4 c; k1 ?# a' h9 V: }
- FILE __stdout;4 W$ I+ ^! {; N
- //定义_sys_exit()以避免使用半主机模式) c3 {, L4 \' v! o, h
- _sys_exit(int x)
5 S2 T: B# `9 X i, v# O I - {8 ~( B; U) r8 h( l0 ?! N) C
- x = x;
! G3 ^2 p2 A1 w% @' D9 D3 X - }
/ o6 u$ t5 R+ t7 ?. v- Z$ d - //重定义fputc函数
" S5 e V z" \9 a) ^' T$ C - int fputc(int ch, FILE *f)( Y' }2 U0 L2 `: C' h
- {
2 ?% a( e j# f - while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
' ]4 {8 r( c4 [, ? - USART1->DR = (u8) ch;
3 _& Y5 v- d. U1 B s - return ch;
7 V$ q8 q: Y; T' L' Z/ ] - }+ z0 O8 f! Y' Q" q8 M
- #endif! J0 A; T, d4 o s! _5 |% |) p
- : x! C$ q; w m, M' W
- void uart_init(u32 bound) {/ I3 _+ M! J9 Y: X) |
- //GPIO端口设置
, G% k- n$ B6 ^7 M1 D0 a/ s( E - GPIO_InitTypeDef GPIO_InitStructure;
Y( m1 S- S) Z; } - USART_InitTypeDef USART_InitStructure;$ ^1 x! Q. h H9 t
- NVIC_InitTypeDef NVIC_InitStructure;
8 F# N3 n( |9 c
7 y/ q/ H: r1 |$ O, Y/ Q @( \- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
& z# u! ?6 V9 y - 9 \. K+ F( e3 w/ o! M' r
- //USART1_TX GPIOA.9* G( |8 X" R# u$ W/ W
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
8 j4 c& v1 T7 l - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- [; d7 s& H' H* p0 p: {$ h) k) F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
4 E6 b2 \: Z7 x, @; j - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9( m6 `! l ^/ M1 k- |2 t4 e& x
0 E# p' ~" a9 T( n6 C s& O& W- //USART1_RX GPIOA.10初始化" q2 C) l* i3 k' p2 E
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
1 m9 X( v6 O4 R- K' u: y1 t/ w - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入$ @* K: W! m9 d% ?1 t# v
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
' K$ w+ ^( [6 h# @9 \5 T; U
9 B: X% f8 ^5 _) l& j8 P- //Usart1 NVIC 配置
1 ` Z! `! Q# r& t, \& H* v+ A! v5 H - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
M4 y1 H7 b' ?( F - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3/ @8 m! V/ h! @+ F
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3% Z2 ?* ?$ P b' x
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能. Q( h% x4 K% K% \& E
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
. H! ?8 t7 y; g3 z/ i: s - . O4 l& o! e9 {( V) b3 T/ K9 ^) f
- //USART 初始化设置, E9 R& k# G' i; t3 ?2 ^5 ^
& W. ]5 F& C0 e- \- USART_InitStructure.USART_BaudRate = bound;//串口波特率
+ i1 `% Y, c" e0 o8 q9 h: p: d! S - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式: |. y: e. W) n( C3 R( U% a
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
k8 c! J; H* g7 U9 d - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
7 m9 {! a3 j, y8 K7 _ - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制 b+ {( o" d8 i$ L6 D
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式) D( _) ?# f3 ~+ N% G# Q3 z. Q
- , t' y5 p2 G' S$ m/ c! k0 R! s+ J
- USART_Init(USART1, &USART_InitStructure); //初始化串口1! c" ?/ h8 M/ u, ]9 k1 `
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
+ e G5 T. y. { - USART_Cmd(USART1, ENABLE); //使能串口19 m8 K6 S& ?1 L1 I: a
- / H2 B% k7 a5 f8 n7 l: h
- }
. n0 q8 E* C, [! U. x% M/ z ~
复制代码 : a3 U" ^% [* |8 m( ]
初始化函数uart_init()上面的部分就是关于printf函数的相关设置,默认情况下printf函数是通过串口1输出数据的。" L8 k2 Z" |7 Y* h! y
假如我们现在要将printf函数重新定义到串口3上,那么就可以将uart_init()上面的一部分代码剪切到串口3初始化函数前面去,同时将fputc()函数中的串口1修改为串口3就行。
" \( N) d; w F2 O& W重新修改后的代码如下:
) {. Q4 k W' C" Z" x串口1初始化代码: H4 C3 b: d; K) [4 Y
) O7 t7 z9 H* \( Q& @2 Z2 F* ]) Q- + z( Y3 g' g' H; @1 `6 j
- void uart_init(u32 bound) {# E& b0 B3 W' [4 q5 H. O5 s
- //GPIO端口设置9 ^& T8 w1 Y5 H* ~9 K; R
- GPIO_InitTypeDef GPIO_InitStructure;
+ N7 _! B2 o" ?# G0 F# u% h* Q: U - USART_InitTypeDef USART_InitStructure;7 ^& M4 ]5 u* _2 z/ `
- NVIC_InitTypeDef NVIC_InitStructure;% y) {, n5 H0 \+ O8 A( ~7 A! {
- 1 N* W6 |/ N& l( F) x2 r
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟) ~# |5 X4 _' G, c: I* v
4 v0 k' k% t2 F: R- //USART1_TX GPIOA.9
# q% J2 a, m' Z+ M5 u+ w @ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9. u8 q" V! y, _. X
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 k: C9 V, u i' e; e! `
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
i6 I C9 ~! J4 T) T. R# j2 Z - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
' D9 D1 {+ z% v( i; o' R8 |8 }/ [# j
# @ O$ b3 b& z: [* ?- //USART1_RX GPIOA.10初始化
l6 Z2 d# `* x6 b2 |' [ - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
* {3 _4 W6 I& [& X - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入) X7 u" a8 e0 T* J
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
: Y/ `$ c9 h/ M+ Q% Z# H! ` - 6 w7 E9 w$ L3 F/ g/ Q, g
- //Usart1 NVIC 配置 L4 k7 d1 F+ N: k1 t
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;& S6 H: g& a5 n8 b- c" C \
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
7 _! [- |- F1 _# S1 Y1 Q- G: [ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
* s& E. L* w1 _0 X- X4 H* h5 Y% H - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
8 g! X; S/ T1 f) F3 @# b% K+ U - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器7 N- U8 x, y" m' _$ ^
+ L' q% S* `) p+ l- //USART 初始化设置* @! e/ B8 ^& E5 l: X
- % f1 _" V3 N. q; Z* p
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
9 O. ?% s% c3 ^6 [$ N& f. G - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
% r* N" \/ W# a0 m }! A ]1 s1 L - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
$ Y% J4 v3 h4 e7 n: n - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位- u, I$ w5 r: n
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制$ k1 o5 {: v6 W3 Z! C
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式- W3 g% v1 i& a$ S! i. d
7 E' }; z- m8 v1 C3 f* ~# I* _7 X- USART_Init(USART1, &USART_InitStructure); //初始化串口1
% j' j" _0 E% |4 { - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
- A" _2 N$ F3 ^! p$ B( X) z - USART_Cmd(USART1, ENABLE); //使能串口13 r" ^8 p, T# N& v8 v9 K. {* V& k
0 n0 D! B2 |: {- E, i3 K- }9 U* X$ l& T5 \: N: E+ I6 e
复制代码
) l: n3 d" R) d t g串口3初始化代码
' t' Q* n8 q5 v) d0 {
0 k; O3 L/ ^$ p7 F- #if 1$ [8 |( q4 y; k
- #pragma import(__use_no_semihosting)
: h! m3 w( W3 d# Z" P - //标准库需要的支持函数
, q5 X$ H) F( t7 b8 G - struct __FILE
6 M( l# P6 ]" R* h9 V, s - {
' c+ x5 t1 n! X* F - int handle;0 Q: b- ^! `$ Z! X4 }* p& o, A
- 6 ^1 o8 Z6 n! y1 K! B0 j. |
- };9 T# M5 V5 _9 c' j# H' L! Y& \% Y$ ^
- 5 _6 I# l% L' x$ o; K
- FILE __stdout;
7 k O: |( m6 d; c- S - //定义_sys_exit()以避免使用半主机模式; ~5 Z8 p/ y* B1 n' M) E
- _sys_exit(int x)4 T! P2 w# n- u* g. V1 m3 e3 ^
- {
9 F. u" @3 M1 ^8 P$ U - x = x;
( ]6 S1 \7 D) {5 X/ L) N7 m7 L - }5 F0 `8 p M) Y# z$ x! i1 I
- //重定义fputc函数/ m7 t( r+ ?: a
- int fputc(int ch, FILE *f): U8 W( O/ I, K/ ~
- {! Z2 u3 ~% ?& @, T0 m) ~
- while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
2 ?4 v6 Y9 q: O" w - USART3->DR = (u8) ch;
/ F; w. g1 g! _: _ - return ch;" [* ]$ R% r$ l5 s( v9 A
- }
4 s9 X) ~9 h6 O4 d9 l/ e+ l - #endif
% b* P" _, }7 m/ b2 u
- E+ [2 O9 {( k/ |- 4 y# Z/ U" ]4 D# ?+ F* I- i
6 m4 Y% t8 M; b, K6 V
9 y/ d, s' W( z7 d. v; `- void uart3_init(u16 baud)
9 l; ~3 X) E8 [6 e - {: G0 [. i1 @* X. L0 }7 P# g; T/ q) |
- GPIO_InitTypeDef GPIO_InitStructure;
/ d! I1 c5 z- @ - USART_InitTypeDef USART_InitStructure;4 k0 r3 }& K& E$ [' N
- NVIC_InitTypeDef NVIC_InitStructure;
/ z5 g. Q% e o! x* P
+ T$ e2 U- h9 h6 ]! f' G4 p) l- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
& C+ S3 W, Y7 ?1 x2 f5 D8 K - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
5 V6 }. K! U- Z' c# K6 R - ) }' c" w& Q' m% W5 P% q
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;' c2 I2 j& d! }& Z; A4 L* w
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;/ y! t1 W( t* W+ {! @9 D
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;( l; W5 Z B; B5 Y
- GPIO_Init(GPIOB, &GPIO_InitStructure);
2 \% `; i1 J. Q* V1 Y - 1 E9 }: e/ l) ], `7 X3 }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;+ k, M; U. u7 u% p3 ^$ q0 L
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
0 D! x& q; ~% F7 D- w - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;; f- h2 W5 g3 E0 L8 B r' ]/ r( J# B
- GPIO_Init(GPIOB, &GPIO_InitStructure);
3 Q5 s$ s/ q5 b" _, O4 o
! @2 g# d$ t( V# U" e$ E, B+ l- NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;$ n: t5 I' x) @; @& y/ @
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;; h! g5 Q+ j1 w0 z5 J
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;3 v) d1 `& v" Y2 t9 H$ ~6 @
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;* _5 `: d3 J6 x; k6 l' u
8 | y1 x- h3 k" t- NVIC_Init(&NVIC_InitStructure);/ @1 H; J9 E% U1 g+ ~! ^; _
- ) [; a) ^) K/ a3 I6 b4 g% g
- USART_InitStructure.USART_BaudRate = baud;
9 o) a- {! Z3 c, i+ m - USART_InitStructure.USART_WordLength = USART_WordLength_8b;7 `' F0 H e) N$ \8 L3 ]* Z
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
- d% V% E- e, o/ `2 k - USART_InitStructure.USART_Parity = USART_Parity_No;
% Y4 P4 h7 C; V! Q- l( e - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
( h* N- M* b& _9 h2 f/ Z - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- X5 W# p1 G5 R- A- Z1 T - 3 x* s/ s. Q# {7 X _& V4 N8 e
- USART_Init(USART3, &USART_InitStructure);, F* e, H; w& J( Y8 I% {
- - W( u6 R! h8 S0 t( Y- i0 e
- USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
% O( |% ^" _. u7 d - USART_Cmd(USART3, ENABLE);
, ]8 r: n/ g: v - 5 Z2 [. p' i, f& c/ P. ]: z9 n
- }
1 J4 G& F, N7 W) o9 T+ R" r
复制代码 6 E$ n$ {4 _, Z: ?4 k
修改后再使用printf函数时,就可以通过串口3输出了。' w; c: W; f/ g- @6 V& f O$ T
关于printf函数的设置核心代码如下:& R/ u: d& u: I/ b% h
# V: t, q8 f( A; c! t
- #pragma import(__use_no_semihosting)
6 j9 Z9 Z) o3 Z2 c7 S, R - //标准库需要的支持函数0 N- n" C% V6 n+ k1 l' ~5 L+ U
- struct __FILE
! j: R- Q- L; O: y - {1 k1 e2 [: ? F* T* c. U% D
- int handle;
; J: P, h1 U& R+ U1 m - % B* H2 t; i0 S) ~7 X+ P
- };
3 K" u- Q5 X, f; R* S" q! @+ i' } - ( I1 o- ]8 j- x, d& S2 W8 W
- FILE __stdout;
& Y9 S1 _) U8 g. B - //定义_sys_exit()以避免使用半主机模式
' J0 a; S* Y0 _ - _sys_exit(int x)
9 H% A: W) y# D - {, B2 p- C! @. M1 X2 J* |
- x = x;
8 ?2 L* v7 M5 G/ k' t) T- l - }- }4 N. J" v5 N& c
- //重定义fputc函数
# }* L$ k* R0 Q# s/ r - int fputc(int ch, FILE *f)7 n4 _& ^" K3 C% t
- {
# D9 `0 D2 {) v7 r. y$ l& o& t$ I' W& @ - while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
9 i: ~* M9 d6 J, | - USART3->DR = (u8) ch;+ U3 a9 ], q% g
- return ch;, @4 X) i) N H4 d$ q
- }! j' `% H f# r: @* K$ ~9 ^- A! b$ `
复制代码 / p2 x* C0 N# j
通过上面几个函数可以看出,要重新定义printf函数的输出串口时,只需要修改fputc()函数中关于串口的定义就行。
: s/ j* S% q4 H9 P' d- d# H
- q) K" ?( _; J# S6 J+ f% ]4 p" u7 A p0 Z3 p+ L
|