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

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

[复制链接]
radio2radio 发布时间:2020-12-13 19:15
为什么要用printf,废话,不用你用啥? 我在下面的把printf换了一个名,LOG,少打3个字。您也可以换成您喜欢的字母组合,这不是重点。
7 x/ x9 U8 C6 ]: z4 `9 B4 V  C# Z7 u; o! j4 j0 \9 B. u) F" H8 R
说是“解决方案+史上最强”,也不过就是下面的几行代码。 0 A( Y+ [: b1 U& s$ w8 u
笔者仅在CubeMX+HAL+MDK+AC5下,测试过F1和F4。 其他环境要修改后使用。
5 J. R7 ?5 \0 B# _' g) {
- |. ?* i5 D; K
  1. #include <stdio.h>
    ! _; L3 V# o# n5 T* @+ H
  2. #include <stdlib.h>
    4 ~. Q0 {$ P6 [& i6 w& f* e: d
  3. #include <string.h>! m/ I+ _* ^/ d' Z1 Z' S: S" G
  4. ( q* B" j; z  c8 G
  5. //Debug mode On/Off switch 1 Z6 g0 C! R! T! w
  6. #define DEBUG                   1 //set=0, disable all LOG lines
    0 F3 e) U3 C) O
  7. # |5 V, }) H8 K+ d, k' I9 E. h
  8. //select one of the following "printf" retarget methods# k8 t: G8 I. }# E7 z
  9. #define PRINTF_UART             0 //printf using UART port, 1-use this, 0-not
    $ U7 o+ M) |! o
  10. #define PRINTF_RTT              1 //printf using Segger RTT, 1-use this, 0-not
      Q4 T& a9 a: `0 m3 e
  11. #define PRINTF_SWO              0 //printf using SWO port, 1-use this, 0-not
    0 {+ ]+ x0 Y- H; Z
  12. #define PRINTF_CDC              0 //printf using USBD_CDC, 1-use this, 0-not
    2 w$ C# f7 s1 l. C" [/ F; ~" j

  13. 7 o4 k" U2 ?8 D2 J4 [6 O$ S
  14. #if ((PRINTF_UART + PRINTF_CDC + PRINTF_SWO + PRINTF_RTT) != 1)
    * ]0 m) f0 M) [" P0 h6 a  Z+ |5 p
  15. #error "!!!!!! printf retarget function Not define or Multi-defined !!!!!!"
    8 p8 r9 c0 [) I4 p
  16. #endif: ?/ n  I4 k: u  Q; _7 e
  17. . [1 U, |, s$ |* F) B6 R& P
  18. #if DEBUG
    " n1 w' }# X  R4 S- z
  19.   #if PRINTF_RTT% {3 [2 Q9 P! f; x; H5 p( Z
  20.     #include "SEGGER_RTT.h"! g" p9 d7 @  n% r7 x/ Y! l
  21.     #define LOG(format, args...)  SEGGER_RTT_printf(0, "[%s:%d] "format, __FILE__, __LINE__, ##args)  _( B! w) b' l9 [# I( A- S* z
  22.     #define printf(format, args...)  SEGGER_RTT_printf(0, format, ##args)
    ' ^8 B: T* y& |1 N! d& @. v
  23.     : f# Y7 A, d* h# S0 h6 d% z2 t
  24.   #else //#if PRINTF_RTT
    2 y+ C+ ?0 F( o
  25.     #define LOG(format, args...)  printf("[%s:%d] "format, __FILE__, __LINE__, ##args)
    3 t9 D. `! ~6 d0 }# g: e1 z
  26.     ! i* Q9 q9 S: ^7 n7 A
  27.     #if PRINTF_CDC
    0 M# T! M' n  z+ q
  28.     #include "usbd_cdc_if.h"
    1 n: [* G; B( z
  29.     #endif/ ?1 \" U1 ]) a- [+ E. P2 q0 X$ v
  30.    
    0 ?% Z2 @: U6 A; l! o
  31.     int fputc(int ch, FILE *f)/ c8 _; Y, [" j
  32.     {
    ' T/ ?: G- J# ^$ L- d: B5 u* i
  33.       #if PRINTF_UART$ O- |' j/ J0 s% G5 D8 Y
  34.         HAL_UART_Transmit(&huart3, (uint8_t*)(&ch), 1, 1000); //UART3
    ( q- O- Z& M2 w/ \2 J3 ~0 S4 B  |
  35.         % e" X8 ^  m$ }6 _# W6 a  C. \' m  W
  36.       #elif PRINTF_SWO
    # D( @& j$ y/ Z- ~$ l
  37.         ITM_SendChar(ch);, j( O' I& h+ c7 a7 o2 ~/ ^
  38.         
    ! v. Y2 z7 X5 m/ ]1 B* \
  39.       #elif PRINTF_CDC
      _% t2 b, D# X; ?* u5 R
  40.         uint8_t u8Temp = 0;; v3 N8 S% K# {+ @( J
  41.         while(CDC_Transmit_FS((uint8_t*)(&ch), 1) != USBD_OK). t( x$ E, a6 `- }/ U( |- v/ p
  42.         {' z! Q) Z+ b5 o. c; C
  43.           HAL_Delay(1);1 U0 P& {$ ]" t6 X% p
  44.           if (++u8Temp >= 3) break;) `3 m4 p" S0 a  [: u
  45.         }
    ) G: g7 D' P' P4 s
  46.       #endif //#if PRINTF_UART
    5 W9 X+ \5 r4 D: c; d5 q
  47.         4 P1 J, e& `+ j8 _+ I' g
  48.       return(ch);
    3 W1 q9 i  P" P1 b- K, T  Q
  49.     }
    ! h: U0 K' E" w: z! t0 m8 g
  50.   #endif //#if PRINTF_RTT
    " |' B2 h0 v3 c3 J* s/ U: k
  51.   
    ; H  b7 a0 M1 o$ Q
  52. #else //#if DEBUG == 0
    * d3 ?2 c# s% q( [7 Q8 [. U8 L0 ?
  53.   #define LOG(args...) //disable all LOGs when compiling
    ; F7 w' l$ P% G! x4 `9 W7 c6 x4 G8 P
  54. #endif //#if DEBUG' j' J% t7 A% k8 g
复制代码

( S# g& d' c1 ]5 N, Q9 l! `这里有4种方法重定向printf,必有一款适合你:4 @- }  T& v* V: Z$ Z( Q

! `* x- s1 d( p! o' F0 C. u( C+ I1. PRINTF_UART == 1,串口方法。
) i- p( r" Q" C- J9 R/ E$ H优点: 就是最多人使用的,大家也最熟悉。 配置简单,容易使用。 各种“PC终端软件”都能用。$ J+ i; P$ D+ j  y% I
缺点: 硬件上要有串口可以用,有时还需要UART转USB接口板(或者仿真器自带VCP/CDC的东东)配合。( I  M, z8 o. l* {7 k

8 `$ }6 H  i0 I. A; w2. PRINTF_RTT == 1,Segger_RTT 方法。  (笔者推荐此方法,至少值得尝试一次), 要安装RTT包,简单!3 K7 y* @$ A; g; G
优点: 不占用额外的硬件资源, 据说速度超级快。 有多快,我不知道。( s" Z0 m3 e+ X! [
缺点: 要使用专用的PC软件接收,既然是仿真器大佬Segger的产品,自然是Segger的驱动程序功能多多。/ \* L( m- [5 m
           使用J-LINK仿真器,就一定要用JLinkRTTViewer等终端软件。: c; N$ c3 T2 Q' U! B0 ^' s
           使用CMSIS-DAP仿真器,可以用DRTTView终端软件, 多谢XIVN1987网友 ,不然DAP就不能用RTT了。
  r+ Y; ]) b% O9 I/ ], Q           使用STLINK仿真器,对不起,用不了。: L4 ]6 }: e7 \0 Z: F( l

0 A; v3 O( b# `+ s  Q3 H3. PRINTF_SWO == 1,SWO的方法,这个是启动内核的ITM功能。2 f9 r9 N8 h' H7 ]
优点: 对原程序流的影响最小。 标准的JTAG/SWD/STLINK连接器,都已经准备好了SWO线,也算方便。  A! h) p: o# c' F) |
缺点: 相比2线的SWD,需要硬件连接一条SWO的输出线。 不能实现双向通信(其他方法都能)。. h# u7 Q! `2 }
            软件配置SWO功能比较复杂很容易失败,重点是要做SWO速度匹配。7 V8 L+ m, d( S3 c7 k5 F* ~6 k
            SWO方法不能在JTAG模式下使用,只能是SWD模式。另外,ARM Cotex-M0/M0+也不能用。  v* \- |3 g, W1 }% b8 s6 d
            MDK/Keil有内置的SWO Viewer,不过不好用,一定要进入Debug模式,好处是各种仿真器都可以用。1 m! w' y/ z) \
            J-LINK 和 STLINK 仿真器,有自己的独立的SWO Viewer,不需要Keil进入Debug模式那么麻烦。) n9 H2 H5 z" b

7 }, M. l' g7 R' p6 ]% C4. PRINTF_CDC == 1, CDC的方法,比较逆天的,属于玩具级别,你想用我都拦不住。2 z- I* x+ M- }3 k# h$ o( W. D
优点: 用法与UART一样,各种串口终端软件通杀。) O5 c. ?3 p7 p  G; l3 v6 J
           不占用串口。 利用IC本身的CDC-VCP节省了UART转USB接口板。
# l) o7 Y$ r: y+ I  o缺点: IC要有USB硬件,代码增加,软件复杂度增加。8 g3 X$ b% b" D) B4 b) v

* t$ r4 r: t$ I实用中,还有一种方法,称为半主机模式(semihosting),据说速度慢和影响原程序运行严重,没有认真研究过。我甚至怀疑,上面的4种方法里面,有可能有后台使用了半主机模式,知道详情的朋友,请介绍一下。( L, D5 H( d2 z5 C( I* ~, g

% r5 P' x. v% i+ {0 O! ]毕竟printf指令对原程序流是多余的操作,或多或少都会干扰原程序流,特别是在一些速度比较慢的MCU上面。因此,printf的输出项要尽量简短,避免大量连续使用。 如果估计占用MCU时间太多,就要做需求评估。3 [& ^4 d, C" \7 Q) y
/ T' x% Z4 |1 X- j  a: B6 {
0 ^6 |& N" U6 L! p% a. I2 Q8 F  C
附件是F103/F407用的完整的工程,与github上传的一样,上面的4种方法都有,可以试一试实现。; ~$ w2 R  t/ J8 ]
github:https://github.com/RadioOperator/STM32_HAL_retarget_printf& M% x; l! p# j0 `6 T& d, t
; }, G7 R$ e$ m" g* E9 C3 k

" S8 C5 @( N/ j. n2 a$ _. {( V
8 J  g: G% O/ K( _) a. h) i1 Y6 x

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:328 b3 I* T( M$ F  g4 X' k3 j
支持老兄,点个赞

# Y! i- i) W! `0 ]/ ?& E多谢您的鼓励。) `( P: B, A5 U7 O  x
我还是把F407的例程放出来了,同时也上传了github,留个记录。

所属标签

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