你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32F103自定义的printf函数的实现

[复制链接]
STMCU小助手 发布时间:2022-3-18 22:21
 在单片机中使用最多的通信接口基本就是串口了,说起串口就不得不提串口中最常用的一个函数就是打印函数printf()函数,通常使用这个函数都是直接调用库函数来实现的,在单片机中如何要使用printf()函数一般都是在串口中进行重映射。如要在串口1中使用printf()函数,可以使用下面的代码进行重映射。
" w5 z9 v0 E6 q1 d, n  F9 z& }/ T& e8 h$ q  e* z, y+ V$ g  v
  1. //加入以下代码,支持printf函数,而不需要选择use MicroLIB8 n4 e4 [0 w) v3 {9 A: T
  2. #if 1& G7 y. a0 M8 l8 N
  3. #pragma import(__use_no_semihosting)
    4 s( o% o& ]( u2 T
  4. //标准库需要的支持函数4 z$ j* O, u8 R1 z0 W
  5. struct __FILE+ l! z, |3 x% Q+ G5 y
  6. {
    ; Q% j+ h# [$ i2 d. O: }
  7.     int handle;* E8 M+ p9 y6 G6 l: G4 \4 _( b: ^
  8. };. b) D) Q/ O9 R# v
  9. FILE __stdout;
    9 v: ^2 c9 l/ F  w2 A
  10. //定义_sys_exit()以避免使用半主机模式
    & o' f9 {  {6 a& P: Y+ t( G1 ]
  11. _sys_exit(int x)
    6 V4 U  a$ o  R( W6 q! h
  12. {
    3 b, Q: I4 f( W5 O$ ?+ @' O
  13.     x = x;4 ^+ G* R0 a/ D1 w& E: v- \5 j
  14. }
      a4 E4 ^' j7 Q
  15. //重定义fputc函数0 e0 B5 n0 Z& I: @& `
  16. int fputc(int ch, FILE *f)
      A: F# I$ W. _: n2 c! C2 `
  17. {
    : \3 N$ n7 x: K1 Q
  18.     while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
    3 }1 M# p# a. |3 r1 P
  19.     USART1->DR = (u8) ch;
    6 z1 _+ ^( C) f! j  Q9 W+ S
  20.     return ch;
    ( L  {% \( g3 `
  21. }
    + S6 W. d. H0 x0 r* }1 g
  22. #endif
复制代码
' e; Y  G0 \3 V6 z) [5 k+ u! C
  在串口1的c文件中,添加上面的代码后,使用printf()函数时,就可以通过串口1来打印了。那么不通过库函数的话,自己能不能实现printf()函数的功能呢?当然是可以的,下面就通过串口2来演示,如何在串口2上直接实现printf()函数的功能。
2 `: I8 t. w& N2 t, f6 g: a0 Z; f
/ [) d1 A7 M3 r0 F6 y& i+ a
  1. void UART2_Init(u32 bound)4 c8 p/ R4 a. {
  2. {
    1 e3 O" y& |) R1 h, X* E! T, b
  3.     GPIO_InitTypeDef GPIO_InitStructure;
    9 y7 b7 O$ V; N$ k' d& m5 J
  4.     USART_InitTypeDef USART_InitStructure;  `  C+ j) L. I0 N8 h+ D
  5.     NVIC_InitTypeDef NVIC_InitStructure;$ a6 b1 z. E* @; e! D6 \6 z$ T( D

  6. & q% d! |' T( z& n$ ]  s
  7.     // 1、串口时钟使能 GPIO时钟使能
    4 X3 T1 R' L3 h
  8.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    3 H* C7 A- J# e% r, m) h2 p
  9.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    7 ~. `, l- B1 P% Z3 ]' ?( y* W

  10. , n9 q  q! W7 h) }9 W8 E
  11.     // 2、串口复位
    , i9 s8 s8 @* W% |& A) t5 ?
  12.     USART_DeInit(USART2);8 V5 i# o: ^$ {  @4 G4 ~5 S

  13. . G3 F6 y) z' ^% ]" k
  14.     // 3、GPIO端口设置/ t1 _/ F# E% |# H' R
  15.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                         //PA2 TX' u! R7 o7 s. M* Y( a( B$ _
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出& A- ]! f, \5 ?2 j) b
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      U* @/ j1 O( O% m8 r
  18.     GPIO_Init(GPIOA, &GPIO_InitStructure);! o# B0 R- Y6 Z. u- p
  19. " A" n  c' M8 T$ W# |* m) }3 L/ t
  20.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;                               //PA3 RX
    9 p: |) C+ [; N$ ~' q, I
  21.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
    ' ~: b) b" V& S  B! J
  22.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;3 s+ O" X* _/ J# v2 J
  23.     GPIO_Init(GPIOA, &GPIO_InitStructure);
    ) N; I% H  T7 F6 u

  24. 2 }5 o, {7 d  z  s
  25.     // 4、串口参数初始化
    + d1 _1 T# N. ?2 a
  26.     USART_InitStructure.USART_BaudRate = bound;$ j* [5 u) q; d$ n6 Y4 V! N
  27.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;/ I( h) ?" N' y" c: n( s
  28.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
    4 R0 `2 T2 R' o5 a
  29.     USART_InitStructure.USART_Parity = USART_Parity_No;: C" x) k5 W# T$ K6 H! M
  30.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    * V# g3 c" ?: C) Q3 }$ J
  31.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;5 M% ], X# `, O# Y" G0 j1 |
  32.     USART_Init(USART2, &USART_InitStructure);! k7 G0 h1 F" n1 F2 A
  33. - }7 ^6 x" f7 R6 g( f6 B
  34.     // 5、初始化NVIC
    ! ~1 L3 x/ q7 m+ J/ t( R8 D. f
  35.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    ; P: A! z+ W( w  a( S- z
  36.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;  c" Q$ v  m6 x
  37.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    ; ~2 X4 ]" U$ g. h
  38.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;5 I* s7 Z( \9 l  i4 T4 |
  39.     NVIC_Init(&NVIC_InitStructure);* y: a# q) D& @. F! h) E; ]

  40. 2 d9 A0 i2 n; X8 E4 H
  41.     // 6、开启中断
    7 v# L7 R5 W8 H0 F
  42.     USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);- r( x* T5 y1 h. i  D
  43. ( w& I0 ]5 k! O; v2 X) G3 K
  44.     // 7、使能串口+ r2 Y. [- @6 n6 Q
  45.     USART_Cmd(USART2, ENABLE);# j  Q1 L5 z3 D+ I
  46. 2 ~: [- G6 T) z8 a
  47. }
    , @+ D6 A- n5 c; Y' G& X
  48. void USART2_IRQHandler(void)
    . H: L3 N& U9 y! d. E( |" u/ |# k
  49. {' X1 ^+ C2 M( }/ K. D; g! i
  50.     u8 res;
    2 q7 O4 I* C2 C1 g5 {/ f* Z
  51.     if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
    " K" l" L4 A+ t
  52.     {+ Q- I+ M+ k! D& M
  53.         res = USART_ReceiveData(USART2);
    * u+ E; d5 H5 ?# h
  54.         USART_SendData(USART2, res);                        //把接收到的数据发送出去
    - {0 |- H. O: w6 A1 [, ]; t8 _" X9 N
  55.     }
    * D" M# ^8 r2 y$ I. s7 M
  56. }
复制代码

" s% L- a. u0 b" c  首先初始化串口2,初始化方式和正常情况下一样,初始化完成之后开始自定义一个函数来实现printf()函数的功能。
* [2 E2 w; _6 Y$ P3 _7 e9 k% E
5 m" n0 j9 {9 N& }
  1. //自定义串口2 的printf 函数7 A6 Y6 w2 w% Q8 i* S
  2. char UART2_TX_BUF[200];
    # d9 i1 T# t# R8 b+ M) P  ~
  3. void u2_printf(char* fmt, ...)    //无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
    ( ~4 A! Z% y5 _* {  K9 P, b
  4. {: H& M. o+ `: y4 ^; n4 n
  5.     u16 i, j;3 E: n9 o7 b1 f: z0 ~( n( o
  6.     va_list ap;          //va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
    + [3 w9 a7 P* M5 w$ A
  7.     va_start(ap, fmt);   //va_start函数来获取参数列表中的参数,使用完毕后调用va_end()结束' A/ R9 Q& v" P
  8.     vsprintf((char*)UART2_TX_BUF, fmt, ap);        // 把生成的格式化的字符串存放在这里& L; i. X/ A! E, z3 v
  9.     va_end(ap);. k! P! u( Y# M: Y) r' Q. m- l3 S* e
  10.     i = strlen((const char*)UART2_TX_BUF);              //此次发送数据的长度
    0 L! x) A& D  q5 F, J2 W
  11.     for(j = 0; j < i; j++)                                                    //循环发送数据
    6 t/ ]: \1 o2 M% [7 A
  12.     {0 A: N9 q- J  C) g
  13.         while((USART2->SR & 0X40) == 0);                    //循环发送,直到发送完毕
    ; P4 J/ t$ [4 s; O" j( M
  14.         USART2->DR = UART2_TX_BUF[j];) F. L6 V3 f. g; _+ I
  15.     }2 F' ~/ J, P) E9 g6 O$ a, b1 I% x
  16. }
复制代码

$ t1 c/ `  g' c' C- z& A- L( w  这个函数名定义为 :u2_printf(char* fmt, …) 使用省略号表示当前参数为可变参数。接下里就可以直接使用这个函数来打印数据了。在主函数中通过一段代码来测试串口1和串口2 printf函数的功能。
" R; t, b% A/ L0 t8 q. d, i% a8 e
+ v( @7 t4 o+ y& x
  1. int main(void)2 X- n2 e9 F$ ]$ Q  U( }
  2. {
    " }1 m. G) c0 h# y  n2 _
  3.     u8 t;: e2 g; x5 F- h. e6 K0 r
  4.     u8 len;
    7 Q0 O) O  X! M. @+ R
  5.     u16 times = 0;/ a* ~7 T1 }: a/ u% r$ u  P1 Z
  6.     delay_init();       //延时函数初始化
    + G! `: d/ N5 k- Y+ n8 S
  7.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);- q- v* {' S( T% ^- H
  8.     uart_init(115200);' ~8 o8 l$ l/ @
  9.     LED_Init();
    5 [7 {* J  a! A% m  {' f+ T5 C
  10.     UART2_Init(115200);
    ! j" I& u. h+ f! l1 m
  11.     printf("USART1 AND USART2 TEST!!!");
    - [* l+ f" _; v( |- v: s
  12.     while(1)9 _' g/ z" m) M# g
  13.     {! H6 w" t3 f1 g
  14.         if(USART_RX_STA & 0x8000)
    6 S- k* i5 R9 j. Z, Y$ j
  15.         {7 |7 Z4 E& h. T
  16.             LED1 = !LED1;
    : @. p6 e/ ]( B* E% V# e! u0 \5 |
  17.             len = USART_RX_STA & 0x3fff;                //获取本次接收数据长度
    3 R- l* I& Q2 }6 h9 l0 ]5 y# o3 J( x
  18.             printf("\r\n您发送的消息为:\r\n");
    : w1 D0 j3 ?" }- }
  19.             for(t = 0; t < len; t++)& H, ]: u( K7 b$ M  K. C8 U: R
  20.             {* L+ K& p; O- {9 v# k; {9 z$ R; c$ O+ D
  21.                 USART_SendData(USART1, USART_RX_BUF[t]); //向串口1发送数据
    $ T. W2 H6 {9 E$ n3 t( J" g9 }7 b1 d% r
  22.                 while(USART_GetFlagStatus(USART1, USART_FLAG_TC) != SET); //等待发送完成" n& n0 v% W" I/ ^& J+ K: \5 Z$ N2 K7 f: W
  23.             }
    6 K: l9 A" l, a
  24.             printf("\r\n");
    / q2 y7 ~7 g9 `( _8 Q
  25.             USART_RX_STA = 0;
    4 C0 m  u1 A) [7 y0 l2 ^# n
  26.         }
    # W/ `* i5 d' M* Q
  27.         else
    9 {4 E; q& A8 q/ x- y
  28.         {
    9 F' _( o9 b7 M1 D7 e5 B4 r
  29.             times++;0 c0 @7 B" h! p7 z5 `3 v! y0 h& K& b
  30.             if(times % 5000 == 0)# c& {! h. I  b( I* a) b" p+ i: e
  31.             {
    , O: W3 e3 i- ^: c4 }0 b' N
  32.                 printf("串口实验\r\n");' Z' Y+ w- @2 g' r: q, _
  33.                 u2_printf("串口实验\r\n");                                                                        //调用串口2 printf 函数; ^$ G1 }* Q# A; T
  34.             }5 R2 t' Y7 I+ y
  35.             if(times % 300 == 0): I9 B; l! M# F, ?
  36.             {' G$ e. q) e% C8 W* R
  37.                 printf("请输入数据,以回车键结束\r\n");( }( Q2 t* t5 E* C. G- O- I5 P: ]% Q
  38.                 u2_printf("请输入数据,以回车键结束\r\n");                //调用串口2 printf 函数
    # K1 }. _4 X* ?- z) ?! \/ N
  39.             }
    ! E. e/ b+ s& O. X! ~: q
  40.             if(times % 30 == 0)/ T/ T  K9 \! l$ [, [8 }
  41.                 LED0 = !LED0;- p4 u) g+ O! y! _2 X
  42.             delay_ms(10);3 _: L/ q- |8 j* q- t' L5 e
  43. + ^5 e8 x+ j# d3 l2 D/ k4 D
  44.         }
    1 k8 h+ s4 \( I; R' G% a
  45. 6 y5 {4 S1 c4 B; P6 r
  46.     }
    % t3 l8 G5 b# @; |
  47. }
复制代码

# r( V/ N4 _  h) }1 g  通过串口1和串口2输出同样的提示信息,然后用串口助手分别给串口1和串口2发送数据,当串口1和串口2接收到数据后就会通过串口打印出来。串口1和串口2输出的提示信息都是用printf函数输出的。
9 O- X/ p6 `# q
  a7 x1 Q9 L; K0 y8 u! @ D5QCO6U0BN45)`L8H2)O0PQ.png ; N* f* |* E" H) X

6 ~0 e; Z! ~4 [: D" f1 m  可以看出串口2的printf函数和功能和串口1的printf函数功能一样,可以正常输出字符串。也可以使用串口2的printf函数打印变量值,比如在代码中增加一句变量打印的功能。1 ^3 {0 L6 }- p9 T2 N

4 \# s- a: ^* n) E) j A_X8ZPYP8FCJ}{ZV0Q3C6%6.png
: W9 I1 j' J& ^. e  i7 @; ?0 \2 H) ?& l% i# S% `% E$ f
  每次输出提示信息的时候,顺便打印一下变量times的值。
- S* l+ g$ O1 z. `4 q$ _. p! ]2 T7 l& Z, P
DKY43H1Y}ZZ`{BJH__9V8)M.png
* H) Z9 u" P1 Z; g4 S1 e( T9 X1 U* C  R) w1 t
  可以看到变量times的值也可以正常打印。2 ^" ^) z8 K; t, ]

$ G& B  P: r, L) k2 L! w) J" ?  i
; s4 t5 N( k/ f! p* |
收藏 评论0 发布时间:2022-3-18 22:21

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版