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

【源码】STM32的printf重定向解决方案——史上最强?

[复制链接]
radio2radio 发布时间:2020-12-13 19:15
为什么要用printf,废话,不用你用啥? 我在下面的把printf换了一个名,LOG,少打3个字。您也可以换成您喜欢的字母组合,这不是重点。
/ C  L' p7 a0 G  \3 J9 U+ Q5 Y: R* Q2 S' F
说是“解决方案+史上最强”,也不过就是下面的几行代码。 ( d* L% b5 F& m7 j$ p/ [
笔者仅在CubeMX+HAL+MDK+AC5下,测试过F1和F4。 其他环境要修改后使用。. {# a' u7 n% Q+ X) r+ R; W

- w/ Y( f& j! d
  1. #include <stdio.h>3 J6 d5 s$ B, q. M$ Y1 D$ q
  2. #include <stdlib.h>
    3 B2 [1 Q/ M/ X) h  h
  3. #include <string.h>* J( {- m  T! ^% q. I
  4. 2 i5 O& V) s& M
  5. //Debug mode On/Off switch
    : J- d2 y/ t8 }4 P
  6. #define DEBUG                   1 //set=0, disable all LOG lines
    . q6 ~  |- L3 L# O9 b' y7 F

  7.   B5 j+ p. I  x* l, `6 E. |
  8. //select one of the following "printf" retarget methods+ ^: _; ?; _# Y+ i3 |
  9. #define PRINTF_UART             0 //printf using UART port, 1-use this, 0-not  K6 A$ ?" {4 w5 J) P. f( B8 a
  10. #define PRINTF_RTT              1 //printf using Segger RTT, 1-use this, 0-not( z3 U0 K5 _% q% ~  P4 p+ a- I, X( A
  11. #define PRINTF_SWO              0 //printf using SWO port, 1-use this, 0-not
    . r# A" Q& `0 L& ]' Y+ R4 p
  12. #define PRINTF_CDC              0 //printf using USBD_CDC, 1-use this, 0-not$ Y6 F8 U, t" |3 t3 e' t# S

  13. 9 p" H- e8 k. V
  14. #if ((PRINTF_UART + PRINTF_CDC + PRINTF_SWO + PRINTF_RTT) != 1)
    # ~* M4 e9 ]9 W( m5 C" g% V3 u
  15. #error "!!!!!! printf retarget function Not define or Multi-defined !!!!!!"! [% Z/ Z4 y; i3 ?6 @5 [
  16. #endif
    0 g: U4 I6 ~7 `( ?% |0 X

  17. ' s/ K1 a1 M( y2 S& \/ c: Z( Y
  18. #if DEBUG. ^# [2 R: ?- @+ @0 F6 k2 x4 F
  19.   #if PRINTF_RTT6 r1 @, c/ K9 U3 ?6 |
  20.     #include "SEGGER_RTT.h"
    : m# v# {6 O2 x( H0 c
  21.     #define LOG(format, args...)  SEGGER_RTT_printf(0, "[%s:%d] "format, __FILE__, __LINE__, ##args). o, S* B+ A0 w" p6 @. [
  22.     #define printf(format, args...)  SEGGER_RTT_printf(0, format, ##args)
    $ R, d# K2 a) F$ C6 \. p8 }
  23.    
    / a9 F, ^( Z: W; v
  24.   #else //#if PRINTF_RTT
    + c! P, J+ m  e1 ^1 d( N  m, g
  25.     #define LOG(format, args...)  printf("[%s:%d] "format, __FILE__, __LINE__, ##args)
    + ^) f2 Q# x5 x3 I, o0 N$ v! t
  26.       n+ ]( c+ ~% X1 p8 g( W& ^
  27.     #if PRINTF_CDC
      w2 S) m/ e( I5 }
  28.     #include "usbd_cdc_if.h"
      W% m# a, c" G+ g' W
  29.     #endif
      E- d! x) [3 |5 M8 l1 U% E
  30.     4 x: k  V2 m' F1 g
  31.     int fputc(int ch, FILE *f)
    1 h% J" @% j3 f  W4 T
  32.     {
    5 W/ @/ w) ^3 O: W; m& j0 C
  33.       #if PRINTF_UART2 m( M3 U9 P% l% M6 C) k
  34.         HAL_UART_Transmit(&huart3, (uint8_t*)(&ch), 1, 1000); //UART3
    . I: D! Z8 o% \6 k! U, q
  35.         / ]" T! z8 \# F/ |# [: C& K4 O
  36.       #elif PRINTF_SWO
    ! X* n# F" a, ~  x) |( n/ Y8 t  L0 u
  37.         ITM_SendChar(ch);
    0 A4 l2 R: r! O( j, |( r* ~
  38.         
    3 A4 f/ P. h$ w' E# Q' K
  39.       #elif PRINTF_CDC
    ) H* ?# v% z8 q4 c1 ^' S
  40.         uint8_t u8Temp = 0;- v. j6 |) U  O( R+ z8 q- }
  41.         while(CDC_Transmit_FS((uint8_t*)(&ch), 1) != USBD_OK)
    6 V; M2 ^3 S. a. y0 r4 f9 S% O
  42.         {
    6 k. u  t) B% c% R0 D, r) M
  43.           HAL_Delay(1);
    / {; P4 b: |. Q3 w$ X4 ?1 a+ R7 U
  44.           if (++u8Temp >= 3) break;
    ( ]. E0 ~) ?6 g5 e5 n7 l! ~
  45.         }
    8 k+ K7 I& Y: T* ?# z8 O
  46.       #endif //#if PRINTF_UART
    6 h! t' V- o& w
  47.         $ `. B/ S4 U8 j% v7 P9 w6 ~
  48.       return(ch);) a. o* ?! _( ^. u" ^
  49.     }
    7 O* v+ E) q- t
  50.   #endif //#if PRINTF_RTT4 b/ E0 m2 j4 f9 I& D
  51.   
    0 s( n2 l& _' W7 M  G6 J; L4 M0 ]  C
  52. #else //#if DEBUG == 0
    1 E; K' i9 u2 d' Z& ~
  53.   #define LOG(args...) //disable all LOGs when compiling
    ( z, t' l& x# Y; t/ L0 d. {1 Q
  54. #endif //#if DEBUG, @5 k# ]: ?3 i& K! @  g
复制代码

" X( I% v; n3 Z+ ?这里有4种方法重定向printf,必有一款适合你:7 a' ]& u/ O, J- U% @
1 _7 v5 G* A0 N$ C
1. PRINTF_UART == 1,串口方法。
, c5 R9 U$ S& ]% p0 l; R优点: 就是最多人使用的,大家也最熟悉。 配置简单,容易使用。 各种“PC终端软件”都能用。5 O) Q, K/ Q! k* E- Z$ ~+ c
缺点: 硬件上要有串口可以用,有时还需要UART转USB接口板(或者仿真器自带VCP/CDC的东东)配合。8 `- S, p) x9 i: H) ]: P* J
! V( u1 ^2 I: a2 V$ m
2. PRINTF_RTT == 1,Segger_RTT 方法。  (笔者推荐此方法,至少值得尝试一次), 要安装RTT包,简单!6 T; e, j: E$ v0 Y
优点: 不占用额外的硬件资源, 据说速度超级快。 有多快,我不知道。% |3 l' ]" E2 W& ]; z
缺点: 要使用专用的PC软件接收,既然是仿真器大佬Segger的产品,自然是Segger的驱动程序功能多多。# t- W2 Q$ b+ Y# D0 G
           使用J-LINK仿真器,就一定要用JLinkRTTViewer等终端软件。& j5 J  x: ?2 |0 ?: N' m
           使用CMSIS-DAP仿真器,可以用DRTTView终端软件, 多谢XIVN1987网友 ,不然DAP就不能用RTT了。3 q* x+ z6 T4 G% B& k; o& g
           使用STLINK仿真器,对不起,用不了。
: w- l( F0 a5 |, Z4 P, ?& |7 }
9 m+ L; [9 c8 o7 _$ a2 x  s3. PRINTF_SWO == 1,SWO的方法,这个是启动内核的ITM功能。% l) x* V# A/ D' T# j- i
优点: 对原程序流的影响最小。 标准的JTAG/SWD/STLINK连接器,都已经准备好了SWO线,也算方便。" P, ?8 K9 u4 y( U: a
缺点: 相比2线的SWD,需要硬件连接一条SWO的输出线。 不能实现双向通信(其他方法都能)。2 G* V: G3 J4 M, D( T
            软件配置SWO功能比较复杂很容易失败,重点是要做SWO速度匹配。5 ?9 \5 k7 G! ], T& L5 t" W
            SWO方法不能在JTAG模式下使用,只能是SWD模式。另外,ARM Cotex-M0/M0+也不能用。# B5 y+ |' E0 q' y0 K& x  K; q, N7 o
            MDK/Keil有内置的SWO Viewer,不过不好用,一定要进入Debug模式,好处是各种仿真器都可以用。, O% e  x$ z  g  e& |! a
            J-LINK 和 STLINK 仿真器,有自己的独立的SWO Viewer,不需要Keil进入Debug模式那么麻烦。1 a( J1 Y) l& b7 k% \
0 }" ^6 L. @; Y8 c( c
4. PRINTF_CDC == 1, CDC的方法,比较逆天的,属于玩具级别,你想用我都拦不住。
. }! x# k5 ?8 w5 {& }, H优点: 用法与UART一样,各种串口终端软件通杀。
6 i6 I2 h  n7 N7 Z           不占用串口。 利用IC本身的CDC-VCP节省了UART转USB接口板。0 ~8 M0 F. D! T, K& o( t1 s% C' i1 M
缺点: IC要有USB硬件,代码增加,软件复杂度增加。
, _: |0 I" E6 W. k: W1 T3 N7 \8 o# ^) O9 r: A7 @
实用中,还有一种方法,称为半主机模式(semihosting),据说速度慢和影响原程序运行严重,没有认真研究过。我甚至怀疑,上面的4种方法里面,有可能有后台使用了半主机模式,知道详情的朋友,请介绍一下。
/ Q' I2 G0 c8 k9 p2 D$ \* j3 C
0 Q! A9 j7 Y' k- ~' D- o) [! A毕竟printf指令对原程序流是多余的操作,或多或少都会干扰原程序流,特别是在一些速度比较慢的MCU上面。因此,printf的输出项要尽量简短,避免大量连续使用。 如果估计占用MCU时间太多,就要做需求评估。3 @. k" D/ I! |# J! d
: `+ f, ^' O9 p6 O

2 t) W# o/ _6 ]附件是F103/F407用的完整的工程,与github上传的一样,上面的4种方法都有,可以试一试实现。
4 ~  j0 E: F) [" Kgithub:https://github.com/RadioOperator/STM32_HAL_retarget_printf; k, k; _! {- z

1 U5 d+ u; S* ^) `1 y! q  h0 u5 I% n) v( x. y0 X
. T( t9 `$ i& r! T- I" ~& P

STM32_HAL_retarget_printf-master.zip

下载

1.36 MB, 下载次数: 12

评分

参与人数 1 ST金币 +10 收起 理由
子曰好人 + 10 很给力!

查看全部评分

赞 收藏 3 评论5 发布时间:2020-12-13 19:15

举报

5个回答
goyhuan 回答时间:2020-12-14 08:20:52
强大
七哥 回答时间:2020-12-14 09:14:10
纳百家之所长,长己之技能
子曰好人 回答时间:2020-12-14 09:32:46
支持老兄,点个赞
kylixyao 回答时间:2020-12-14 18:21:45
非常不错!
radio2radio 回答时间:2020-12-15 12:22:34
子曰好人 发表于 2020-12-14 09:320 \0 m- @6 v6 p; A
支持老兄,点个赞

5 s9 h0 o: ^1 G多谢您的鼓励。
- T  ]! i% |0 {我还是把F407的例程放出来了,同时也上传了github,留个记录。

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版