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

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

[复制链接]
radio2radio 发布时间:2020-12-13 19:15
为什么要用printf,废话,不用你用啥? 我在下面的把printf换了一个名,LOG,少打3个字。您也可以换成您喜欢的字母组合,这不是重点。$ m: H( i6 ?9 \' S

% q2 \& Q  \9 M9 h' H说是“解决方案+史上最强”,也不过就是下面的几行代码。 2 f7 A' g# x1 d* W* f) w* V
笔者仅在CubeMX+HAL+MDK+AC5下,测试过F1和F4。 其他环境要修改后使用。) K2 ~& q! `( R! c6 @5 W
8 Q: N; b! p1 M: [, a
  1. #include <stdio.h>- f: ]4 R$ ~4 J% R) n
  2. #include <stdlib.h>
    2 m, V, W% @- R: b5 P
  3. #include <string.h>- C' x* J1 O- g6 r" g- U

  4. ! }8 @3 t) B) q( T
  5. //Debug mode On/Off switch ( }  I* C' ^) U  N
  6. #define DEBUG                   1 //set=0, disable all LOG lines* C* d0 H7 K. R" g  \+ {$ P4 E
  7. ! J( n! ?! j: L2 ~
  8. //select one of the following "printf" retarget methods
    + z$ a0 ^5 S) L4 h0 y
  9. #define PRINTF_UART             0 //printf using UART port, 1-use this, 0-not
    % D" G; K+ Q9 K1 H  b, ~
  10. #define PRINTF_RTT              1 //printf using Segger RTT, 1-use this, 0-not% _5 ]2 k+ g3 {1 K# p% K. |
  11. #define PRINTF_SWO              0 //printf using SWO port, 1-use this, 0-not
    4 M2 h9 j  P' A
  12. #define PRINTF_CDC              0 //printf using USBD_CDC, 1-use this, 0-not; i! m2 n6 Z1 ]* Z1 J

  13.   i) f! Y) W, Y1 M6 t7 T$ c
  14. #if ((PRINTF_UART + PRINTF_CDC + PRINTF_SWO + PRINTF_RTT) != 1)
    * |! O8 }5 R1 o( @9 a* ~. o4 o
  15. #error "!!!!!! printf retarget function Not define or Multi-defined !!!!!!"
    - g4 k$ y% \) C4 Q. [' v% {1 a- G
  16. #endif) K" o! D( u7 ~/ \9 y

  17. % W( a8 }  |- x6 D  S3 {$ ^0 L5 z( d
  18. #if DEBUG
    . V' V$ Y# v# h- N% O
  19.   #if PRINTF_RTT
    5 g- h' L% Y3 O7 s2 z) A
  20.     #include "SEGGER_RTT.h"
      Z, z' V) L; A6 F4 Y
  21.     #define LOG(format, args...)  SEGGER_RTT_printf(0, "[%s:%d] "format, __FILE__, __LINE__, ##args)7 {# a$ a; r/ B" X+ [
  22.     #define printf(format, args...)  SEGGER_RTT_printf(0, format, ##args)# w2 S' x+ b) X8 M9 y, u# Z
  23.    
    1 O( j! Q) x9 H- ^# F2 p1 c
  24.   #else //#if PRINTF_RTT
    ) ]* c* q$ c7 @! G7 L0 {
  25.     #define LOG(format, args...)  printf("[%s:%d] "format, __FILE__, __LINE__, ##args)* t5 l8 I# _) H7 g
  26.    
    5 \+ w" U) a4 v) I; U- \
  27.     #if PRINTF_CDC7 c' O5 ^; |; U9 m
  28.     #include "usbd_cdc_if.h"
    ( N* W6 A) r- Q4 a  z
  29.     #endif0 R; t1 |; ]& N; h1 i
  30.    
    - x: r0 q" ?  M: {8 y4 ^) M8 j, O
  31.     int fputc(int ch, FILE *f)6 A  k9 z3 }2 S% C
  32.     {  Q9 v3 X5 t, ~* i0 X, \6 [
  33.       #if PRINTF_UART6 s+ v) a7 l7 s6 G* P, c
  34.         HAL_UART_Transmit(&huart3, (uint8_t*)(&ch), 1, 1000); //UART32 E% i8 R# I" O. o- o" v
  35.         # R4 \- ]7 K& B; C( `- e
  36.       #elif PRINTF_SWO
    9 o8 P) L9 y2 s( l9 G0 x0 ~3 ]
  37.         ITM_SendChar(ch);+ ]8 W; G/ E( ^: k/ W" G
  38.         
    ' z: H% H5 h2 j2 F& A, e6 U1 K
  39.       #elif PRINTF_CDC% \! h4 e7 |$ C: j  I) ?" N
  40.         uint8_t u8Temp = 0;
    % t% n* J/ u; L2 \
  41.         while(CDC_Transmit_FS((uint8_t*)(&ch), 1) != USBD_OK)- a% q( w: V1 M
  42.         {
    5 K1 {! S' e: z# {
  43.           HAL_Delay(1);. y# ]+ c: O7 P) l
  44.           if (++u8Temp >= 3) break;# k' N; o% Y" T
  45.         }
    7 j9 }5 s# ?1 ?1 i. [0 e3 c
  46.       #endif //#if PRINTF_UART
    & B4 p% @, G' h# Z% l8 ~* N( u
  47.         
    , g8 Y' B3 H' s2 t2 T
  48.       return(ch);- r0 g+ {$ g: c$ I2 y8 k! [
  49.     }; l. t  r3 z$ X1 U* ^& L
  50.   #endif //#if PRINTF_RTT6 [3 ]& O5 T  b' g3 ?
  51.   
    3 q! ~/ a1 d7 Y# c2 q6 G" k0 R$ Y) n
  52. #else //#if DEBUG == 0
    . g5 S/ I" d6 X; @5 e1 ?
  53.   #define LOG(args...) //disable all LOGs when compiling' u) F9 v& Q5 P# U
  54. #endif //#if DEBUG4 O# o" a( m9 ?, S
复制代码
" T, e4 ]) m& Y! u  M0 R
这里有4种方法重定向printf,必有一款适合你:$ B  d1 {1 `& b' e! H) Q7 O% A3 X2 S# O

. y" |9 \5 |3 y) V+ T3 t% b7 {% k+ o7 k1. PRINTF_UART == 1,串口方法。 + ^& U7 s! Z& N( E2 }; L  m0 x
优点: 就是最多人使用的,大家也最熟悉。 配置简单,容易使用。 各种“PC终端软件”都能用。
$ D1 n0 ?( d5 q5 |  l缺点: 硬件上要有串口可以用,有时还需要UART转USB接口板(或者仿真器自带VCP/CDC的东东)配合。
& O: I# y+ _0 _3 [6 J8 H. G0 j1 J3 w2 j. j' U% _
2. PRINTF_RTT == 1,Segger_RTT 方法。  (笔者推荐此方法,至少值得尝试一次), 要安装RTT包,简单!6 s" C3 R, d/ s1 A5 t) e8 p* p4 m) [
优点: 不占用额外的硬件资源, 据说速度超级快。 有多快,我不知道。
* ~* n5 B, E5 C, h" q缺点: 要使用专用的PC软件接收,既然是仿真器大佬Segger的产品,自然是Segger的驱动程序功能多多。
. x% X2 d) _$ B% R$ Z6 _           使用J-LINK仿真器,就一定要用JLinkRTTViewer等终端软件。6 v5 q, w1 e# ?: P
           使用CMSIS-DAP仿真器,可以用DRTTView终端软件, 多谢XIVN1987网友 ,不然DAP就不能用RTT了。1 m" G5 s" j7 s! Y9 F
           使用STLINK仿真器,对不起,用不了。4 M3 b7 N4 p% L$ }  Q4 z6 c
( r' O4 l4 n0 V7 R1 B; F8 Y
3. PRINTF_SWO == 1,SWO的方法,这个是启动内核的ITM功能。- W/ d4 ?$ K/ f' {6 s% e
优点: 对原程序流的影响最小。 标准的JTAG/SWD/STLINK连接器,都已经准备好了SWO线,也算方便。
( g# k& Y+ B5 w缺点: 相比2线的SWD,需要硬件连接一条SWO的输出线。 不能实现双向通信(其他方法都能)。
, b5 ^* o8 d: F7 ?0 z5 _: A            软件配置SWO功能比较复杂很容易失败,重点是要做SWO速度匹配。
+ ^7 n) c& w# u            SWO方法不能在JTAG模式下使用,只能是SWD模式。另外,ARM Cotex-M0/M0+也不能用。
; b# ]2 e0 f; O  O            MDK/Keil有内置的SWO Viewer,不过不好用,一定要进入Debug模式,好处是各种仿真器都可以用。3 G: i" V4 q& Q! m8 ~
            J-LINK 和 STLINK 仿真器,有自己的独立的SWO Viewer,不需要Keil进入Debug模式那么麻烦。
% C4 {, R& L! V" o- o* w
) [' {  P+ n$ l! V; Y4. PRINTF_CDC == 1, CDC的方法,比较逆天的,属于玩具级别,你想用我都拦不住。
  U% u9 v) l0 _  K3 X8 w+ m优点: 用法与UART一样,各种串口终端软件通杀。, T, c" S) r3 q2 v3 n, h! Q
           不占用串口。 利用IC本身的CDC-VCP节省了UART转USB接口板。
2 E5 i# k# g0 |& p( ]: ?4 ]3 Q缺点: IC要有USB硬件,代码增加,软件复杂度增加。2 f: @8 s* v0 k$ A$ b/ Y9 A

: ^0 q+ c6 \! X- n: O$ d实用中,还有一种方法,称为半主机模式(semihosting),据说速度慢和影响原程序运行严重,没有认真研究过。我甚至怀疑,上面的4种方法里面,有可能有后台使用了半主机模式,知道详情的朋友,请介绍一下。; ]3 ^  k# A6 y( }# e& `

/ Q4 c) [( B" K; N# v) d  j毕竟printf指令对原程序流是多余的操作,或多或少都会干扰原程序流,特别是在一些速度比较慢的MCU上面。因此,printf的输出项要尽量简短,避免大量连续使用。 如果估计占用MCU时间太多,就要做需求评估。8 |: N/ W) q+ g! w: G

8 A+ n! {& k; h3 l& c  m4 i/ ]" s7 Y% _9 h5 f+ D" t
附件是F103/F407用的完整的工程,与github上传的一样,上面的4种方法都有,可以试一试实现。" I  k  v. \# n7 s6 x
github:https://github.com/RadioOperator/STM32_HAL_retarget_printf+ m' \1 b, b1 O# J
; s# q) f0 s+ T% V6 r$ k( ~

9 w6 j& d% e1 x. p$ s$ r
- r6 h+ P  z; _3 S

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:325 g# K  k8 }- [0 y- u& b$ Z
支持老兄,点个赞
8 b1 R9 Q0 X1 [1 _
多谢您的鼓励。
& e. T! k3 Y2 U9 D* Z+ q5 |- S3 ^我还是把F407的例程放出来了,同时也上传了github,留个记录。

所属标签

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