在调试程序的时候经常需要使用printf’函数来打印相关调试信息,在STM103单片机中printf函数默认只能在串口1使用,有时候串口1被占用了,想要输出调试信息时会比较麻烦。现在就总结一下如何在其他串口上使用printf函数。这里使用正点原子的例程来做说明。* ~( L ?+ Y$ u- G' j! X
默认串口1初始化代码如下
1 [0 T' W: {3 v2 V
1 V9 N. M2 n5 |; e$ \, q- #if 10 E, Z8 o1 g: ^4 l3 B
- #pragma import(__use_no_semihosting)# h3 Q4 @8 B) C5 H# Z( J! _
- //标准库需要的支持函数
$ z) {" C8 i& P: h' e - struct __FILE
& W0 e5 f" E2 ], N" o8 l, `- O - {$ w9 L" ^4 j, |! Z: I! X6 L
- int handle;
8 T* M7 \8 W& y" U" w - };8 c+ U7 e' }8 r! z
- FILE __stdout;
9 x9 f; A; A( S4 `0 w2 S# q3 U - //定义_sys_exit()以避免使用半主机模式
( n+ u3 C4 R! M9 g: t$ C - _sys_exit(int x)
1 c/ P" v: \ u) p0 y6 a. M - {
, x3 H8 p$ E! J2 L6 Z& w - x = x;1 X/ O5 [- F3 j+ F
- }
+ J2 l/ ^- m+ }1 Q4 B5 @* v - //重定义fputc函数
3 O! k$ H/ O& u7 ^. i4 j" Z - int fputc(int ch, FILE *f)
. U! M: G1 `2 g6 x - {
% ~# [1 g- t b! _3 U2 r9 N - while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕. j) p7 j" B) G1 a4 y M
- USART1->DR = (u8) ch;8 |* }; u, H4 d
- return ch;
7 f& K3 L% {" ~7 j6 G - }
: c) t: J# N9 ?/ D; s( J - #endif' e% G* b5 T- d) Y O
$ \( \% m; p% {( \% p1 I0 S- void uart_init(u32 bound) {
1 f% I" x- B. q+ I - //GPIO端口设置
/ _3 P: R" B0 a3 v0 S* @, i - GPIO_InitTypeDef GPIO_InitStructure;) v: J# T4 l# l) b2 O5 B* L( D
- USART_InitTypeDef USART_InitStructure;
3 D& ?2 C8 t g# G2 V. L% U/ h - NVIC_InitTypeDef NVIC_InitStructure;# N6 q. m) H K
# K/ a) |+ E* P4 B8 V/ U4 p f- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟4 a( r& x) y2 E2 A8 K+ G
- 6 y% s9 y: E5 I! x; H$ ~5 ^ j7 u
- //USART1_TX GPIOA.94 R G) t& `' a' J
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.96 v: ^* w* Z% j6 v w
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;1 r0 `( }9 w/ I f5 O7 Z
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出! _" y/ |; }# ` g& u1 {' |+ _9 X
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
+ s" m3 m$ Q8 d8 ~# l/ K. Z
0 Y/ q6 j: M2 N. X9 O- W- //USART1_RX GPIOA.10初始化7 [8 h6 ]. m3 ^( c4 b2 T% i6 z; O
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
! G3 s+ w2 P1 N% ^ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入- U I' P X! w# T4 _
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
3 s4 `: `3 o7 v+ a+ B6 z
* c( {2 Q/ l& ~9 e" a; C" Z9 K- //Usart1 NVIC 配置
# L& q' L5 v" d6 k- H5 l - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
" l3 t8 P4 ^0 G. U& D - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3- o q6 K/ b+ j! D5 o& [% j
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级34 C! v" `/ S; g2 C* B; x1 o' J
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
' w3 N- A+ i7 h/ R - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
1 T7 [0 l5 E, X+ W1 q& d( q
E9 _) ?3 t6 ~& W) n1 n- //USART 初始化设置8 ^- l$ b) D1 f7 L. r+ b9 H% `" b
/ `" u. H) M$ i" E" \9 B& u- USART_InitStructure.USART_BaudRate = bound;//串口波特率$ a5 Z3 _5 m/ M, A: B$ ?5 c
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式' G* H& G! B" l+ m
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位' C; ?. F. c, g* d& w! h
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
. E* M$ C8 C6 J - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
# E4 o! t5 g! L/ L* g - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式* u' {7 A# _+ R; o4 A
- & B" H0 z/ I$ k& w
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
' m) |0 o. X! v0 e8 f - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
. z% n. \6 j( w5 l3 G% a - USART_Cmd(USART1, ENABLE); //使能串口12 ]5 x8 R1 `, |9 u# X( U, D
- 5 W, x3 j' Q; U
- }
- I) T+ B" k( _; c& a
复制代码
O$ t. W4 s9 b" R9 @( |" o9 r初始化函数uart_init()上面的部分就是关于printf函数的相关设置,默认情况下printf函数是通过串口1输出数据的。
* r" v# F; Y: u3 S \, N% u假如我们现在要将printf函数重新定义到串口3上,那么就可以将uart_init()上面的一部分代码剪切到串口3初始化函数前面去,同时将fputc()函数中的串口1修改为串口3就行。0 O& l" O; ~2 T5 ~# O$ R- T
重新修改后的代码如下:4 e3 {3 t) p/ w1 i4 S) _6 M* ]
串口1初始化代码
* R( b% p1 Q, G8 h; R* Y
* K( P* C. r* h8 u% ?9 X- 7 ~/ ]. X& _6 @5 M% ?% h
- void uart_init(u32 bound) {) K( c: Y+ U h @$ P
- //GPIO端口设置
& K3 e* c0 H2 [/ i6 s, l, z: z - GPIO_InitTypeDef GPIO_InitStructure;) T N' [% J6 q) B
- USART_InitTypeDef USART_InitStructure;- L) _/ U$ p* I2 B5 }6 S/ x
- NVIC_InitTypeDef NVIC_InitStructure;8 j6 D0 a- F+ `
- , o1 ~2 C4 m: Z. R9 E- g
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟& h9 ^% _0 Z3 y3 y( y) z
6 g2 N" g' [7 }; J6 h4 L; |+ ~- //USART1_TX GPIOA.9
l. H, m( ]% q" G, o; _( M - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9! K5 R! C" I0 k: W4 ~# }- |; W4 r6 N
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;/ G: M A+ l' D1 K) J
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出& ~! Q3 |5 Z% I' D
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
7 t: V6 c+ S) ]* u# R7 N2 W
8 f+ T8 D; u! n# [3 y: B: g6 n- //USART1_RX GPIOA.10初始化
8 T; l& m- H3 P* Q- ?* ~+ O0 f+ { - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
0 w8 j7 R; a/ z5 g, Q, ?7 [ - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入1 O \# m1 t, g5 Z1 f: R
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10' f+ S4 ?1 ?, f! ~
- - A- [7 S8 A1 I; q) ?9 K" v
- //Usart1 NVIC 配置
( N# s" v, `7 L5 c$ ~: [ - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
. _2 _' ]. \+ c& ]1 s - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
. L- l$ V$ F0 O. T - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级36 G M3 G1 r5 i8 o, Q y# P
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
" J7 p- R$ g/ U, l8 Y9 q - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器' [7 W- K2 X; l! @5 E
- 7 a8 T4 u, L8 T1 ?6 u2 G
- //USART 初始化设置
' E9 L2 I& O1 @8 \( Q( k - 5 E0 x/ ~% ~4 ~! W7 @0 T
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
, U# P& [3 B6 r- E4 G( Y; c - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式8 {& h1 i9 X: F5 N' |
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
9 s2 d( ]% z+ F: Q5 x' A# u* _ - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位# M9 b, @) k0 F# M6 j5 M- i) U
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制+ H4 w5 A" w* k8 l/ E4 I/ i8 z
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
( [3 s- P7 v2 Q* p! V* E
* T8 j( K [$ u B3 m6 l- USART_Init(USART1, &USART_InitStructure); //初始化串口1, i' v/ H$ R8 n' f' O
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断9 ]5 r9 J% v9 S1 X- l
- USART_Cmd(USART1, ENABLE); //使能串口13 [9 g, o+ Z4 s) N5 u
- * b& G9 |: I7 e$ {( D
- }2 w/ p! U$ x, [8 ^1 z0 E6 o
复制代码
$ H" o+ [& \) F/ m h# i串口3初始化代码3 x6 _. K; E! e- Q
: N: s! X3 K0 }* _: I- h* ~) j$ p- #if 1# m' u2 d& z/ l& M4 @% x
- #pragma import(__use_no_semihosting)
/ R; y* I. @" \+ s! U5 p/ x - //标准库需要的支持函数6 q2 S* w1 _' _: X) S
- struct __FILE
0 ?- v; `0 X* t+ `( Y - {
- Z, m$ m) t- m+ B7 n2 L, k - int handle; I3 O- ]% b! a& n! a/ ^5 e
- - ^) H* ]& W6 L) U
- };0 M2 |5 A* v1 e& n: \; ~' `
- 2 f5 L9 {2 [: X. I( \
- FILE __stdout;
6 }9 |6 K/ o, h- T - //定义_sys_exit()以避免使用半主机模式2 \* P5 M3 \: `/ \$ b3 G
- _sys_exit(int x)
8 D5 ?$ n2 R/ v) v - {5 r' B. X$ ]5 ^. K' {
- x = x;# P0 d# `* o8 U: b0 T
- }2 I( ~# V. k/ j6 E3 M' ]
- //重定义fputc函数
6 s7 `- K7 J J$ h - int fputc(int ch, FILE *f)
: s+ I" _! p3 T' ?1 ?/ M - {
& \* Q) m \* j1 b7 e' F+ M+ p( E - while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
( G3 z% Q( I$ `& v( P! F- H - USART3->DR = (u8) ch;5 p3 B% P, i0 j) B7 r1 v& J/ F
- return ch;
! R! \- p- R* A+ D1 w - }
- v( o" `$ c9 A. o# S2 \ - #endif6 g" o1 C1 E8 {7 [
# t) H% |. r7 \3 G/ H7 N
1 Q# u) \0 Y+ Z: E! `0 w4 w2 D- / K/ L8 B7 Y' J1 V; \3 X' f" L
- ; a2 t* r. v7 k' ?
- void uart3_init(u16 baud)& P. x0 [9 t2 r$ C" k
- {) H* \, b8 y0 c$ U: U. {
- GPIO_InitTypeDef GPIO_InitStructure;% @# L! O' R) _: ^5 Z" c6 U
- USART_InitTypeDef USART_InitStructure;
8 S* |; g4 a/ R! ~6 i - NVIC_InitTypeDef NVIC_InitStructure;7 H& t! S: e7 b: V- W7 S2 w
4 ?9 f/ I2 S# A \, S, Y- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);, B5 U! Q$ V9 q9 O1 j$ a% w" E
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);3 e* c9 p" V. e+ ?% c8 s5 Z
: R/ e: m/ ]9 S9 H. Q' ?5 h! F- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
8 F+ Z; i) ^ P$ V - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;. G) D7 x9 W6 v6 A& |* b1 B
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% M4 U8 G4 i- e/ o
- GPIO_Init(GPIOB, &GPIO_InitStructure);" Z( e( I7 N( j2 q& }; \9 h) K
; b/ h) P" H5 D( |5 P. N% ]; ]- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
6 _1 c3 T5 X2 M$ x - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
, v3 L4 I8 x$ P/ f2 p - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ ~5 x' e! A8 {9 k - GPIO_Init(GPIOB, &GPIO_InitStructure);' u2 L. \( S2 r
9 F, K% Z, e7 X* t1 ~8 c- NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;7 o0 ^% H; g P
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;6 z# R1 A& v! V0 S6 Z
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;. R: ^' f9 r. m# k+ D- q
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
( y+ s; o' d0 v, S4 j7 {
! W- N4 z4 y# a1 x: g: X" Q- NVIC_Init(&NVIC_InitStructure);
& A. a3 C3 b8 ?' r' l - 3 c" [( V0 S6 q- a m6 N7 [( p3 ~
- USART_InitStructure.USART_BaudRate = baud;
* I1 n6 t& b3 E# N& } - USART_InitStructure.USART_WordLength = USART_WordLength_8b;
* B+ E5 j- O, k( t - USART_InitStructure.USART_StopBits = USART_StopBits_1;0 w' ^9 Z4 |0 z* s" @. d( d: ~
- USART_InitStructure.USART_Parity = USART_Parity_No;* h4 w) F* R# Y# {9 g. W* z. p
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
! i( @" R3 |2 r - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;: L2 M5 G' U5 b$ S2 U) E
- 0 {2 p! `; v/ R4 n7 N! Y3 U
- USART_Init(USART3, &USART_InitStructure);
: Z3 r' p# e8 w+ R- ]$ ] - 0 Q' y+ L0 e8 E# k2 g
- USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);
! \" S( v; H. s/ b5 v - USART_Cmd(USART3, ENABLE);
0 Q) T) t' a( L
! f2 s/ ]1 K" C. ^, }) U9 w# q' @- }
" e7 \% x5 u& B1 y/ \% E3 J5 k
复制代码
& g# ~& I+ O0 S( U X" C9 T修改后再使用printf函数时,就可以通过串口3输出了。
9 S( F+ p- F9 E% ]关于printf函数的设置核心代码如下:1 G$ L7 ?' O& c8 {8 Y1 N* a
- ^% O6 F. m0 l+ i8 ^: q- #pragma import(__use_no_semihosting)2 d" g6 Q& ]% C- y
- //标准库需要的支持函数
& v9 r' F6 u+ S7 G9 s2 k0 ?( j9 M2 ~ - struct __FILE$ [; x6 ?/ A1 X
- {. k" u5 Y0 ?8 J& c, {" y4 x/ ?
- int handle;' [) c1 s) j% g4 s( G, X
- ! u. g4 Q& A) R6 G
- };* @6 o; b* a8 z; F' ]& [& D
- # a. F# k- J7 B l
- FILE __stdout;. V! @5 Y' p- [: e z/ |
- //定义_sys_exit()以避免使用半主机模式7 I2 y' Y! _+ @9 D% r# d; J9 q
- _sys_exit(int x)9 x% T' y( C; @0 N) M8 H. z
- {. P5 x+ V) L3 _6 T7 f
- x = x;
, Y0 q9 v/ Q S# J* I. F, ] - }% |7 S1 o6 c% C9 v# {( m
- //重定义fputc函数$ t0 l4 b2 c' f% Z. w/ y8 y, Q+ y
- int fputc(int ch, FILE *f)
5 }$ M% T7 Z" b+ F1 W) n, u! ~ - {
1 [' V+ @" D: _0 W - while((USART3->SR & 0X40) == 0); //循环发送,直到发送完毕
, B6 f5 P1 Z0 }4 b. p - USART3->DR = (u8) ch;
/ M$ M0 I/ L0 v - return ch;
; o3 n' e$ J& Q {! k9 a5 ]: `/ U - }/ b- i7 a ~' ~ C" |$ B
复制代码 5 d0 Q" y! [/ U
通过上面几个函数可以看出,要重新定义printf函数的输出串口时,只需要修改fputc()函数中关于串口的定义就行。5 ?0 {4 R# d, h: g$ a
& R# d/ _' I) q$ @1 y
' @. u5 @7 u2 f' X8 s6 I. O |