在调试程序的时候经常需要使用printf’函数来打印相关调试信息,在STM103单片机中printf函数默认只能在串口1使用,有时候串口1被占用了,想要输出调试信息时会比较麻烦。现在就总结一下如何在其他串口上使用printf函数。这里使用正点原子的例程来做说明。
( G) x4 P3 f4 I5 ?默认串口1初始化代码如下5 _, k/ Q* m4 W/ Q
+ H/ B: | ^% @/ c9 ^4 Q
- #if 1
# G) D! M, v9 @! `# \ - #pragma import(__use_no_semihosting)
$ ~7 d# O, ~/ i' x. a7 }9 T+ x; L+ F - //标准库需要的支持函数6 V+ n) i7 o! I
- struct __FILE
1 J. P" l" Q7 j$ M. b. i6 K$ L `8 r - {* T8 y% b( ?# g+ H
- int handle;
) X! `. h) g" O0 u' R - };
1 H) j; ?2 C! ]% [( s - FILE __stdout;
0 }1 k+ E& Y8 K a& z3 r - //定义_sys_exit()以避免使用半主机模式
* q* A9 p8 ^2 X. B' O3 Z - _sys_exit(int x)
4 Q+ E! s& m- C5 H' h - {- w- m( P) T- B4 b
- x = x;6 a1 R+ {$ t3 h# `2 h4 [/ D8 A4 B
- }7 c q1 y8 K( v0 `; M" @- r$ k) r
- //重定义fputc函数
$ e* L2 V3 W1 O* [& a( }2 [ K - int fputc(int ch, FILE *f)2 }. I n$ P6 u! {( s
- {
& b' A+ w- K: `* r* O4 n2 S4 n - while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕+ C8 J; ?- w5 i% ~8 C1 i9 S2 h+ o
- USART1->DR = (u8) ch;
3 `5 l/ p4 R- B6 p% k - return ch;! m U- @5 \% B6 y# W! [
- }: \3 P4 x' ?* U! \6 ~/ J/ Q
- #endif
$ D, ?9 |) ~" @( |9 v. ^/ x - * m" }$ }4 r; s" Z( Y$ I7 k6 ~
- void uart_init(u32 bound) {
7 P2 L* X3 |' _ - //GPIO端口设置 t1 O4 f. R$ s4 M4 s
- GPIO_InitTypeDef GPIO_InitStructure;) }( B. d# R7 m/ m; `; C
- USART_InitTypeDef USART_InitStructure;0 w9 c: y f+ }% S, f0 P
- NVIC_InitTypeDef NVIC_InitStructure;7 r& x5 ~4 t/ y( k' J
) I) w( g2 P7 q- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
Y2 T1 }) W7 }; }) k
0 \( m$ _! V- y4 N3 d, _- //USART1_TX GPIOA.9
, n5 n5 W' ^7 h; U" ` - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9' Q3 H" x) o( Y+ f$ ?
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;) p% Y0 M4 }) ^5 Y* s9 @* R) N
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
! \( \$ K$ H1 N5 i: a* o - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9& _ k! D: k4 @$ Q
& s# i! p. B# a S1 g% S- //USART1_RX GPIOA.10初始化2 U2 G& z3 o6 \4 M, m1 [4 H) C0 D
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10$ ]. l8 |+ x7 I5 Y
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入0 r* n- Y+ V0 }5 |1 g* P5 z
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
( j1 f( P/ P: D9 g! a - 2 O1 X3 U% p4 n1 j% F# e
- //Usart1 NVIC 配置
; I& C% w+ @, a" Q7 g0 u# L, r - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
7 j" E$ S! g6 L - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
0 W6 l( Y, w; y9 h. r - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3) Z# g" L! g" e) {
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能( h. Z g1 x) G8 @# S+ M* D; j5 _4 K
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器8 b8 g1 g2 p9 m: {) M
9 y4 w1 ?# p/ ?# h- //USART 初始化设置7 I& K9 p: Y1 {) j; E! e g) V
9 n* S& @1 {6 }3 K; r3 m, |) j- USART_InitStructure.USART_BaudRate = bound;//串口波特率* K/ M2 A( ?$ E: O3 g) E* g/ J
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
- A& m/ ^3 |; G1 I* j - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
0 l1 j5 V. [. x5 m/ s, D3 v# g2 h - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位% N% v& e; S# a S1 J0 b" w3 _
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制9 F2 T: [5 `% P2 t4 }. A
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
: z5 D' g% w) L0 I - " n0 q* O* ~$ d& S
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
4 _3 E- l( Q& Y' c( a5 ?. @ - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
3 Z5 N* R. R; b7 o+ e. T - USART_Cmd(USART1, ENABLE); //使能串口1" F, C9 z) m( y% g; k( J
$ R4 G- K1 i1 _- }
5 _! }, Q' N* s% {3 B4 N/ I+ v7 Y
复制代码
$ ^, }3 R3 `5 n0 F' O* ]; _1 q. \初始化函数uart_init()上面的部分就是关于printf函数的相关设置,默认情况下printf函数是通过串口1输出数据的。, P% c0 U, p& E$ U" b7 Q i7 y6 Q% A
假如我们现在要将printf函数重新定义到串口3上,那么就可以将uart_init()上面的一部分代码剪切到串口3初始化函数前面去,同时将fputc()函数中的串口1修改为串口3就行。) [6 t% _4 i4 L0 [
重新修改后的代码如下:) X5 ^* a' f6 H8 s( ]9 ^, e, Q
串口1初始化代码( y. ?. D4 d. F+ V0 ~
- h0 R. B* V0 }* m9 L; y
- [& W/ b* W* F0 F% m" I$ j- void uart_init(u32 bound) {
7 {$ a% b1 I6 |' y g4 `( b3 }1 m8 [ - //GPIO端口设置) r9 ~$ ~3 o/ S! [% B/ c# T
- GPIO_InitTypeDef GPIO_InitStructure;
% R6 A9 T* B: F - USART_InitTypeDef USART_InitStructure;
) d i( W- c K$ B$ d F; w" U - NVIC_InitTypeDef NVIC_InitStructure;: \6 g/ M8 ?2 E4 W; d6 {
8 F: r. ?# Y% [$ r) M% D6 d! }- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟3 a& `* `) h+ j( i0 I
( C" x5 r. i2 N& H& z! S& n6 M- //USART1_TX GPIOA.9% X9 l \$ N5 e! H8 ^' Y, h0 ^
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.95 }- P$ o" ?" n4 S; [& [: o' Y
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
* r1 u5 A% y6 |" j0 a; w+ Q! a- A - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
8 n( y( L$ ^. t - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
% O R; R4 J$ ], v4 q% N
, _' h9 t3 d! b9 b$ L- //USART1_RX GPIOA.10初始化' L( \- r; y- v( Q' J1 u
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA109 ?) v; W( ]0 m& N' H
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入% W: g( X4 F s: \3 `5 H# S7 |
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10! o N. p* v" d2 L1 }: e+ K
- 4 S; p3 {) b) q2 {0 M7 P: [
- //Usart1 NVIC 配置% J$ s& w; k; g- m- o
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
7 P: ?- r; `5 T9 o9 z$ c - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
4 K" ~. l8 ^1 w+ n4 c6 p ? - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3' G) p1 W% Y1 F, m' c1 l
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能3 J9 k4 _) T- b- ]4 J1 P s! n
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器9 ]! a5 Y! L0 L5 x* E6 T7 Y0 P, } N+ C
- , {6 N% y9 J4 B" Z5 S! U! Z" C
- //USART 初始化设置/ t, O! ~" v6 d8 L" y4 Z$ }
; n' u1 b C q# Y8 j/ B C- USART_InitStructure.USART_BaudRate = bound;//串口波特率
?: B* ]6 d0 X - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
# `0 @) b+ Q* w; p8 a$ e - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位/ u# h# j6 j4 m/ m0 K8 v, z* i
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
9 f% J0 b( X/ P6 P - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
; t9 X( [: u: y0 `+ ~ - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式* d6 t ^. F4 O) B2 ~
7 ]2 F, p) `1 b- USART_Init(USART1, &USART_InitStructure); //初始化串口10 a% w; I/ @$ o) h+ `
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断/ h* ^+ L4 P' z. S! p' z
- USART_Cmd(USART1, ENABLE); //使能串口1/ k' @$ ` E1 T0 p. }
- " u! s' R! q* u
- }; x& ?2 ^; F3 Y; u# B$ k
复制代码
; M8 B8 K. m3 R- m4 t1 Y0 ]串口3初始化代码, e& u& ^ v2 U9 a$ I
0 C) `1 \7 P( I. [- #if 1/ ?; m# c% j+ W3 D" ~, a0 h7 E
- #pragma import(__use_no_semihosting)
+ E% @7 [$ S* n# ^$ I - //标准库需要的支持函数
4 @5 ` g9 C' e z) q/ w - struct __FILE
. ?: G3 B/ [! v v3 N1 a - {' q3 @$ _# Z) g* R" D4 F# n1 z, `
- int handle;
0 b/ c3 u7 X" r, r9 D, }' {4 c - ; q- c; a) H% s; m e( k* j
- };- V' e0 x/ b/ \0 x2 x0 Y8 \
- ! D- n: R8 [: {2 c' Y$ I4 a
- FILE __stdout;$ o% S: |+ C. ~7 ]6 A+ m, `* U3 y
- //定义_sys_exit()以避免使用半主机模式2 {: B9 }: a0 ?( G0 I( @- A9 A
- _sys_exit(int x)
, _/ C2 [' j6 j6 W( Q5 i - {
0 h/ o u+ F+ E1 n - x = x;
2 Z& y) L1 ]- f+ O3 M1 M9 L% {# h; s* o - }
/ W- m# g6 J9 R8 Q. Z - //重定义fputc函数7 {3 M, m* t8 I- j
- int fputc(int ch, FILE *f)( |3 @* m3 z8 M. V# F9 R; z
- {) u/ t9 Q4 c1 i/ A
- while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
; n& |% U; R' x - USART3->DR = (u8) ch;
: a. x+ B% b0 C2 ^& ` - return ch;; u3 T2 W1 I/ Q# c2 i2 ~
- }
' D3 P' A) T7 [/ T, h' Q - #endif
- V! Y/ |+ Z, [8 x$ y S& `3 X - " A7 e/ G9 q2 X2 x( a8 y
- * H8 p# I" d b$ F% [6 H7 u8 U
8 r) C* k0 X- @ @
0 {% d4 h7 v6 J+ X+ z- void uart3_init(u16 baud)
6 g& G/ }+ E7 y# Q D - {
4 b# {0 m+ \. U( z1 g - GPIO_InitTypeDef GPIO_InitStructure;
- Z0 ~0 b$ j+ z - USART_InitTypeDef USART_InitStructure;# |+ x7 a2 v+ \6 C: C+ x: H
- NVIC_InitTypeDef NVIC_InitStructure;* N5 R o' |* h$ @* z2 n# A4 {
- , Z$ r# g0 t0 c0 S. o1 {& A
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
( o; g. b% d5 D0 l7 M, c. B - RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE); q: u$ s' m/ ^2 S) P
1 j7 _0 j% q0 ~4 t2 R2 z1 m# d: K- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;# n) ?9 h9 k% Y' W* @) F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;4 s% _+ i6 d. J: `/ \; z9 w- e% N
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;4 s0 J( I) H; D7 Z* ]2 H# w6 F7 S
- GPIO_Init(GPIOB, &GPIO_InitStructure);
9 j# A- Q0 H/ o$ g( Z1 b
0 n8 p' e3 h+ q- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
, x, v( k6 F0 M1 I - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
5 }/ N: I+ x1 B- z2 u7 h - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
" v$ m4 n1 V, y( g - GPIO_Init(GPIOB, &GPIO_InitStructure);: F5 x, {! _ C
- 0 `4 g3 m* x1 G ^
- NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
; V+ G/ w& P# H; O) e - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
: y. y* O9 P) P9 N' W7 a4 u - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
6 R: b" T* h9 q* P) D( b - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;! N5 a% v; c/ w; d* }; E a
& s) K6 e/ c' P% m( d- NVIC_Init(&NVIC_InitStructure);
: w* L% }4 ?; ?: @& q8 N- d, g - $ X0 n; x0 X3 C4 d
- USART_InitStructure.USART_BaudRate = baud;
0 t5 A1 O! _# S2 @) u; g0 O - USART_InitStructure.USART_WordLength = USART_WordLength_8b;
+ R" [ `( O8 @5 t* {! p1 _5 c - USART_InitStructure.USART_StopBits = USART_StopBits_1;
; N! B: A' x. S5 x# b6 R; }- Y x - USART_InitStructure.USART_Parity = USART_Parity_No;
/ W4 l0 f1 \$ e: i - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
t1 \ |9 S: L( F1 \! ^* \& j - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;) c$ ]) m* W3 N' O! r
- & [! [* o% Q# {# s' E
- USART_Init(USART3, &USART_InitStructure);4 G% e) j# M9 C0 f% a
- % c C6 u2 }: f& g$ Z) [' U4 X
- USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);2 ~( M# G3 W5 A( O; w$ l
- USART_Cmd(USART3, ENABLE);4 j, {3 ?- x, k/ S
- {) i; }0 }; {/ @6 G- Y: ~4 c5 d
- }- \0 z/ \' i5 h
复制代码
# Z; t8 a/ q: E- G; e' e9 t2 W修改后再使用printf函数时,就可以通过串口3输出了。8 r. b9 J0 x/ ~+ J7 R
关于printf函数的设置核心代码如下:
: u/ p- e1 I% J! K8 W0 M$ f
) Y; E' N2 o! u- #pragma import(__use_no_semihosting). Q* n! O" e D, P" ~) B+ ]+ O
- //标准库需要的支持函数; \; i+ f) F9 V% u. c6 y" g' Y
- struct __FILE
: [ s4 j' }( W. H# ~ - { W3 l$ S$ n9 P, D; ~; U
- int handle;3 F+ F3 P: ?' s: Y7 s* d
- " e/ ?7 p( j4 w
- };8 D: g) m) I6 F$ G! d/ B
- F1 i2 D% w1 [" O% w/ T7 E- FILE __stdout;
- f& D, X5 B( l: r7 ?+ F8 } - //定义_sys_exit()以避免使用半主机模式
4 Q+ F2 Y4 p2 E - _sys_exit(int x)
+ }# P! g/ e/ C2 n8 p+ F; Q* Z - {
3 w: L* S. H! L( w - x = x;6 Z: K6 \. r* j2 e6 A; H
- }
4 y |/ S! ]6 a* C, }' e6 s Y - //重定义fputc函数, a5 [1 `% j. y& f4 m; p+ a* M
- int fputc(int ch, FILE *f) _3 v) e/ h. c# Q2 P, [
- {
9 F. C% Q: n5 G x7 y$ [8 q - while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
2 |, H7 x1 \2 `9 n- ?' ?! U0 C: c* l - USART3->DR = (u8) ch;
$ T$ ~7 P% Y/ B4 p" X8 g - return ch; K( I- C I. G) [! u4 C- x
- }
" x. x1 Z" T N6 C9 o
复制代码 : h& u o8 s% g6 |& I i, z; h
通过上面几个函数可以看出,要重新定义printf函数的输出串口时,只需要修改fputc()函数中关于串口的定义就行。' K/ A3 H0 ^( B; w9 @
" C/ t t% x/ M$ s, ~
, F$ {! _6 N* D7 T4 d2 f, Z" }7 J7 K |