printf重定向介绍7 g* u" m' O a, s9 t5 j
在学C语言的时候,会经常用标准库<stdio.h>中printf(), scanf()这两个函数, 来实现数据的输入与输出. 格式化输入输出的功能还是挺强大的, 那已经有了现成的轮子, 就要寻思一下如何进一步在单片机上使用它.
; ^; c9 R$ y. a. ?* p1 A下面是ARM Compiler6文档中对printf的介绍:
( y& k, P" [) k3 T3 R' K: S
u1 A' K# s+ q8 i# x6 h/ m( YThe printf family consists of _printf(), printf(), _fprintf(), fprintf(), vprintf(), and vfprintf().
3 f5 i8 M+ W; f/ j% r2 ]All these functions use __FILE opaquely and depend only on the functions fputc() and ferror(). The functions _printf() and _fprintf() are identical to printf() and fprintf() except that they cannot format floating-point values.. B( [- K! ?7 W4 G/ p7 V; I; U. C
The standard output functions of the form _printf(…) are equivalent to:
Z8 }/ ?8 _5 V, v+ Z; U# u" I! sfprintf(& __stdout, …)where __stdout has type __FILE.* g; l8 j0 S t
, u: C& j- P) T7 G/ `; Y/ L0 |大致是说printf家族有这么些函数<_printf(), printf(), _fprintf(), fprintf(), vprintf(), and vfprintf()>, 这些函数都隐式的使用__FILE<文件流>, 并且只依靠fputc(), ferror()这两个函数.1 p7 R6 z2 a4 y- Q I) T
6 p+ @: Q' i. I0 }( R: h, U9 GIf you define your own version of __FILE, your own fputc() and ferror() functions, and the __stdout object, you can use all of the printf() family, fwrite(), fputs(), puts() and the C++ object std::cout unchanged from the library., y4 _1 }/ ]" Z E& W; ~. F. k p( q
0 @$ b- O4 x1 N" Q) Y/ U( q6 u重新定义__FILE, fputc(), ferror(), __stdout, 就可以使用printf()家族的各种函数了.
. P$ v3 U" }$ w% i4 O& Q4 q8 N
( Z+ u: d: [1 m7 Z7 XSTM32中printf重定向' v0 H5 }* @$ l9 { O; m
下面是官方给出的重映射模板:
+ h) q% X0 I( R& {9 ?7 v) x' J* K<遇到不会的先看看文档往往能得到最直接的答案>
7 ]4 B! I8 J0 _+ i, X, C% j7 P2 u, f# L; W5 s. P
- #include <stdio.h>* F3 c+ M, P7 u* F6 g% _
- struct __FILE% \8 v) Q# v9 b2 `) @8 ^
- {
4 c- [( i* g" j6 y3 n - int handle;7 C; T9 d% O h1 S% T) F
- /* Whatever you require here. If the only file you are using is */8 k2 ]. |7 h9 U- ~& y9 b
- /* standard output using printf() for debugging, no file handling */( g$ Y3 e# w2 ? {5 I; B8 H
- /* is required. */
6 U( y( }; k% O9 y2 ]4 w* D( | - };
9 f+ g4 q# {) e - /* FILE is typedef’d in stdio.h. */
{3 Y) B% m* k- C - FILE __stdout;
* h7 T$ [8 K; h3 q5 m( c/ z6 T - int fputc(int ch, FILE *f)
' n {; L/ }; y$ u - {) u) M3 q* K- e7 L! q& I. k
- /* Your implementation of fputc(). */$ ^# c0 T1 d; M6 V3 j ?
- return ch;4 c! F1 ^; ^; {, M2 L( f- t# D
- }
1 Q5 O$ w% { w& J1 B( V - int ferror(FILE *f)
" M% k+ }4 X* m( m, \9 o - {- ~6 h) l: M$ a" e) w- i+ [
- /* Your implementation of ferror(). */
8 u! ~; e9 ^# p) [ - return 0;
+ T( e/ |7 z: N) M3 b - }
复制代码
4 Z! H8 t4 i; o0 Q5 n+ \# f. M要将printf()重定向到STM32中的串口, 只需要重新实现 fputc() 函数即可, 在fputc()中通过串口发送一个char. 在上一次的工程上进行修改, 将printf()重定向到串口3.9 B4 v9 z0 s# p! [( U2 B9 b
) ^3 v) i: x$ `: P
- #include <stdio.h>- I) L& E2 T$ V1 ]9 n4 W
- struct __FILE; g" T4 l& u$ T5 \
- {+ W5 o- v2 s& j/ C3 W7 q
- int handle;
. T* M, U: s4 M$ Z8 f/ i' e' g - /* Whatever you require here. If the only file you are using is */6 v$ j! `6 |1 j2 B: b# v
- /* standard output using printf() for debugging, no file handling */3 s$ z8 \9 v$ V8 S6 q# q! ^! ~
- /* is required. */$ [) v7 q- m+ C' [+ K
- };/ [- t2 m( @( H4 }6 R4 a
- /* FILE is typedef’d in stdio.h. */- L' X" b) c( F' t
- FILE __stdout;
; s3 T" i% [ c8 j+ A& j: W( n - int fputc(int ch, FILE *f) / ?1 l8 E0 W) t/ t) ?6 M
- {
* b% |' S/ [( p& H8 [ - /* Your implementation of fputc(). */
: p1 N4 \) E6 U* k" x, ^ - HAL_UART_Transmit( &huart3, ( uint8_t* )&ch, 1, 0xFFFF );/ P3 @7 `1 J. \% |) P
- return ch;
6 u- b( v0 o8 S% D - }
- W% A# a, r% @. [ - int ferror(FILE *f)
1 B# u' v0 A+ B; G0 y9 d - {
1 r1 P+ U* y- r0 } - /* Your implementation of ferror(). */6 \* M. L7 q! z1 j' A5 A! A4 s
- HAL_UART_Transmit( &huart3, ( uint8_t* )"Printf Error\n", 13, 0xFFFF );// 可选
( t0 a) T0 G6 k7 v; y6 e/ a" C: p - return 0;
; ]2 e" m+ E, Q/ b - }
复制代码
5 J) e! m0 m1 Y1 [2 n& z1 p写个简单的程序试一试:
3 c& h' N2 L6 [2 S# Z9 f( A( ^8 z. V4 Z# M w
- int main(void)
* F. S$ f4 G6 a3 Z+ R - {
. J6 u9 G0 l0 T - MPU_Config();
0 |& C P3 J4 I, w9 B% K: U% ~ - SCB_EnableICache();
9 K0 T( f2 U1 o' W8 G3 m/ h$ k9 K - SCB_EnableDCache();' }) w3 a9 Z& u# Q2 c' E8 @" d2 j
- HAL_Init();1 @- W; b6 v9 m8 Q* r
- SystemClock_Config();3 R3 m$ Y8 @2 b, p+ q. F
- MX_GPIO_Init();; f! O4 }6 G# m9 u% j
- MX_ETH_Init();; [- z7 `5 ]/ |* C+ {0 M' @
- MX_USART3_UART_Init();
* c1 ?$ q; o; D/ r7 M! V- w5 d9 C - /* USER CODE BEGIN 2 */ C6 r1 {5 h" t: b
- double d = 1.234;
* x+ m/ r2 J9 ^6 v% ^; r - int i = 60;
Q, R- A* e) U4 g# k$ Z - char str[] = "Do not be lazy.\n";
% C# q3 a" I. {2 @: Y& z - printf("Hello World\n");
, ], I# u5 [' P4 D( }0 e4 B - printf("d = %lf\ni = %d\n", d, i);1 S/ M6 k/ g; p4 c3 o# M3 l8 [ z4 T
- printf("this is a string : %s", str);
9 R# b5 v- p& V, A, v
' k- x% C4 D) B& B- while (1){}6 E, l1 \/ Q& n8 k- z( h7 z3 s) P6 p
- }
复制代码
1 }: l8 F! Y7 M输出结果:
% s C7 G6 z, c) T0 u! k M9 g' q2 h1 {& D4 G$ c
" y# d- R0 L2 C5 f+ X _可以开始愉快的使用printf()了.
* }3 D4 y& C6 ~% v, p4 j. C2 x3 _5 n
) g7 @+ Q9 k1 x# X0 q5 s& m) d
& c3 b/ i7 ]/ j. F/ P |