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

【经验分享】STM32H7的终极调试组件Event Recorder

[复制链接]
STMCU小助手 发布时间:2022-1-1 18:00
8.1   重要提示(必读)
  •   只要是MDK支持的调试下载器,基本都支持Event Recorder,本教程测试了JLINK,STLINK和CMSIS-DAP。

  •   务必使用MDK5.25及其以上版本。

  •   使用ARM_Compiler 软件包V1.4.0及其以上版本。

  •   CMSIS软件包 要是使用V5.3.0及其以上版本,详情本教程8.3小节末尾的说明。

  •   为了实现Event Recorder组件的最高性能,最好将下载器的时钟速度设置到所支持的最大值,另外,根据需要加大EventRecorderConf.h文件中的缓冲大小,默认可以缓冲64个消息(动态更新的FIFO空间)。

  •   此调试组件不需要用到SWO引脚,使用标准的下载接口即可。以我们的开发板为例,用到VCC,GND,SWDIO,SWCLK和NRST。大家使用三线JLINK-OB也是没问题的,仅需用到GND,SWDIO和SWCLK。


    * c; o8 O; f7 l9 Q9 p

    " d7 Q7 O: s7 \8 K2 N

    5 a! j/ A0 J  j' h4 N! X
8.2   Event Recorder简介

前面的专题教程中为大家讲解了使用SEGGER的RTT功能来替代串口打印,比较方便。只是这种方法限制用户必须使用JLINK才可以。而使用Event Recorder的话,无此限制,各种LINK通吃。只要是MDK支持的即可。

Event Recorder是MDK在5.22版本的时增加的功能,到了5.25版本后,这个功能就更加完善了,增加了时间测量和功耗测量的功能。

此调试组件不需要用到SWO引脚,使用标准的下载接口即可。以我们的开发板为例,用到VCC,GND,SWDIO,SWCLK和NRST。大家使用三线JLINK-OB也是没问题的,仅需用到GND,SWDIO和SWCLK。

  •   JTAG接口和SWD接口区别

    + ?' A7 R% o; y" v% u3 M7 X6 v

    4 r% _* f$ m; l" K1 }  |$ M0 f
    # Q' F5 a* P4 W% ^

下图分别是20pin的标准JTAG引脚和SWD( Serial Wire Debug)引脚,一般SWD接口仅需要Vref,SWDIO,SWCLK,RESET和GND五个引脚即可,SWO(Serial Wire Output)引脚是可选的。有了SWO引脚才可以实现数据从芯片到电脑端的数据发送。

  •   词条 SWV(Serial Wire Viewer)


    ; v/ g5 [3 T- D5 y, Q' _5 n! b7 u
    , \6 T& D, v4 Y! y) e

SWV是由仪器化跟踪宏单元ITM(Instrumentation Trace Macrocell)和SWO构成的。SWV实现了一种从MCU内部获取信息的低成本方案,SWO接口支持输出两种格式的跟踪数据,但是任意时刻只能使用一种。两种格式的数据编码分别是UART(串行)和Manchester(曼彻斯特)。当前JLINK仅支持UART编码,SWO引脚可以根据不同的信息发送不同的数据包。当前M3/M4可以通过SWO引脚输出以下三种信息:

  • ITM支持printf函数的debug调用(工程需要做一下接口重定向即可)。ITM有32个通道,如果使用MDK的话,通道0用于输出调试字符或者实现printf函数,通道31用于Event Viewer,这就是为什么实现Event Viewer需要配置SWV的原因。

  • 数据观察点和跟踪DWT(Data Watchpoint and Trace)可用于变量的实时监测和PC程序计数器采样。

  • ITM 还附带了一个时间戳的功能:当一个新的跟踪数据包进入了ITM的FIFO 时,ITM 就会把一个差分的时间戳数据包插入到跟踪数据流中。跟踪捕获设备在得到了这些时间戳后,就可以找出各跟踪数据之间的时间相关信息。另外,在时间戳计数器溢出时也会发送时间戳数据包。


    1 ?" M" B& o% h4 |) F% J3 m
    " W# D: l, V' n. u, ~2 W7 F9 G

    2 g- E! ^& J! z+ y/ C& `
8.2.1        Event Recorder的特色

Event Recorder的特色主要有以下几点:

  • 提升应用程序动态执行期间的检测能力。

  • 支持的事件类型滤除机制,比如运行错误、API调用、内部操作和操作信息的区分。

  • 可以在任务中、RTOS内核中和中断服务程序中任意调用。

  • 对于带ITM功能的Cortex-M3/M4/M7/M33内核芯片,执行记录期间,全程无需开关中断操作。对于不带ITM功能的Cortex-M0/M0+/M23,是需要开关中断的。

  • 支持printf重定向。

  • 各种link通吃,支持SWD接口或者JTAG接口方式的JLINK、STLINK、ULINK和CMSIS-DAP。

  • 对于带DWT时钟周期计数器功能的Cortex-M3/M4/M7/M33内核芯片,创建时间戳时,可以有效降低系统负担,无需专用定时器来实现。

  • Event Recorder执行时间具有时间确定性,即执行的时间是确定的,而且执行速度超快,因此,实际产品中的代码依然可以带有这部分,无需创建debug和release两种版本。

  • RTX5及其所有中间件都支持Event Recorder调试。


    ; K* U9 e0 s) U! V# g- V

    6 b* M, M" o1 |

    ; f' l/ Y, a* q$ i8 e$ T4 ^
8.2.2        Event Recorder是如何工作的

首先来看下面这张图:

在截图的左下角有个Memory内存区,在这个内存区里面有一个缓冲Event Buffer,其实就是一个大数组。MDK通过访问这个数组实现消息的图形化展示。为了正确的图形化展示,数组缓冲里面的数据就得有一定的数据格式。而这个数据格式就是通过左侧截图里面的Event Recorder和Event Filter来实现的。Event Recorder的API实现数据记录和整理,Event Filter的API实现数据的筛选,从而可以选择哪些数据可以在MDK的Event Recorder调试组件里面展示出来。

这就是Event Recorder的基本工作流程。

8.2.3  Event Statistics时间测量功能

Event Statistics提供的时间测量功能简单易用,在测试代码前后加上测量函数即可:

在本章教程程的8.6小节为大家详细进行了讲解。通过这个时间测量功能,用户可以方便测试代码的执行时间,从而根据需要,进行合理的优化,提高代码执行效率。

8.2.4  Event Statistics功耗测量功能

Event Statistics提供的功耗测量功能,当前只有KEIL的ULINKplus支持此功能,由于ULINKplus价格不便宜,一套5000多,大家作为了解即可,实际效果如下:

8.2.5  Event Recorder的实现原理

每条Event Recorder消息是由16字节的数据组成,32位的ID,32位的时间戳,两个32位的数据,共计16个字节。其中32位ID最重要,格式如下:

Level指定消息分类,主要用于消息筛选:

Component number指定事件消息所属的软件组件,也可用于过滤:

看了下Event Recorder的源码,每条消息大体是一样的:

  1. typedef struct {# D1 s  j8 O1 U% R- y
  2. ! }+ U9 d9 r% b7 p% a0 v
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)
      ^  G  [2 `: w# {/ e; `

  4. / o8 d. w9 X/ ?" w  Y& t' W  V
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB), N6 X0 @7 q9 Y

  6.   d$ B: v2 y, O% L( J% W
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)
    & c+ _; ^$ Q2 ?

  8. ! h; a1 v5 W0 z* M; C; o7 H% @
  9.   uint32_t info;                // Record Information
    ; w+ X8 @' H  H+ Y) l. T9 ^
  10. 5 A7 j; ?  m9 y4 |/ k
  11.                                 //  [ 7.. 0]: Message ID (8-bit)" G3 P; W' D) {7 c& Q2 T+ D
  12. ! q2 D# A, f  _9 P5 g# G* [5 e
  13.                                 //  [15.. 8]: Component ID (8-bit)( \' R' A! o7 k

  14. * |$ ~5 |& ~: V; [/ e! W
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    0 y% D" D' m- c  S
  16. " A: B; B" o3 m) }8 c" c+ d
  17.                                 //      [19]: IRQ Flag9 S% n% R0 U# `
  18. 5 ~( [& Q- }+ P- c! a/ n7 q9 r
  19.                                 //  [23..20]: Sequence Number7 s3 f$ P$ L: p% f' d

  20. $ d& r8 H. O- A' Z
  21.                                 //      [24]: First Record
    - m4 I1 U; t" F* Y0 Q/ {
  22. / l! h  k9 R7 ?- C1 |' G4 D
  23.                                 //      [25]: Last Record6 S: w' ~6 k$ ~- d8 S  |
  24. - X- E6 j3 v/ v! y  A/ n
  25.                                 //      [26]: Locked Record: L6 Q/ v9 ^: R( T0 Y- z) b3 g
  26. ! A6 g: s' d2 @/ I6 l- j
  27.                                 //      [27]: Valid Record# J- b# t) a' L# I9 F

  28. $ B+ j; N: J7 @% K4 I% Z+ `
  29.                                 //      [28]: Timestamp MSB
    & g9 S0 s- U2 ^( L9 f9 }

  30. : v$ c- w( z6 k' f! R
  31.                                 //      [29]: Value 1 MSB9 p& J' U/ O- S" U9 V0 p) j
  32. : r2 l0 h2 k7 |6 a
  33.                                 //      [30]: Value 2 MSB
    3 u+ k0 m- H3 }2 z# C
  34. - g' c6 G1 U# t/ {9 b6 |- O
  35.                                 //      [31]: Toggle bit. P' Y- ~- I* S- C' T* B( x
  36. 9 V; ~% k8 x- N% X- y9 W9 Z8 L
  37. } EventRecord_t;
复制代码


2 \4 {1 p9 b- _) S! d. k3 M

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。
# k7 l2 s% u1 o

对于Event Recorder,大家了解了这些知识点基本就够用了。

8.3   创建工程模板和注意事项

Event Recorder工程的创建比较简单,这里分步为大家做个介绍。

  第1步:准备好一个使用MDK5.25或以上版本创建的工程模板。

  第2步:安装ARM_Compiler V1.4.0或以上版本(如果有最新版,直接安装最新的),详情见帖子:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=87175 。

  第3步:打开MDK5.25或以上版本创建的RTE环境。

  第4步:通过RTE环境,为工程添加Event Recorder功能。

  第5步:为了实现printf重定向,我们需要将STDOUT的输出方式改为Event Recorder,即选项里面的EVR。

  第6步:打开通过RTE环境为工程添加的文件EventRecorderConf.h,配置如下:

这里主要设置方框里面的两个参数。

Number of Records:表示Event Recorder缓冲可以记录的消息条数。

Time Stamp Source:表示时间戳来源,有如下四种可以选择,我们这里使用DWT时钟周期计数器。

由于选择的是DWT,因此EventRecorderCong.h文件中的Systick Configuration配置就不用管了。

==========================

通过上面的6步就完成了Event Recorder功能的添加,效果如下:

添加完成后,还有非常重要的两点要特别注意:

  •   第1点:一定要使用当前最新的CMSIS软件包,当前是V5.4.0(随着时间的推移,如果升级了新版本,直接使用新版即可)。


    6 T1 W& H& A! z* c

下载并导入到MDK后,需要大家更新自己现有工程CMSIS文件里面的头文件,可以直接将CMSIS文件夹中Include文件里面的所有文件全部删掉,替换为MDK安装目录如下路径里面的所有头文件:

ARM\PACK\ARM\CMSIS\5.4.0\CMSIS\Include。保证头文件都是最新的5.4.0版本。

  • 第2点:由于使能了printf重定向,大家的工程里面一定不要再做重定向了,比如fpuc,fgetc。另外当前选择了微库MicroLib:

    & o/ y' }1 s5 j: n% `

    ( J7 |0 x) A& \) c5 W! o

注意这两点后,就可以使用Event Recorder的功能了。

8.4   Event Recorder事件记录的实现

Event Recorder的使用也比较省事,这里也分步为大家进行说明:

  第1步:初始化,仅需添加如下两行代码即可。

  1. /* 初始化EventRecorder并开启 */
    ( d% U, ]( F7 Y$ c8 v: F+ k+ `5 c; z% A

  2. 6 ?& {1 S& ~# n
  3. EventRecorderInitialize(EventRecordAll, 1U);5 _! O# n" V& ]. b
  4. 1 c$ A2 y$ X  D, K: R* X
  5. EventRecorderStart();
复制代码

8 t, V* t/ \' F

  第2步:调用Event Recorder的API就可以使用了,主要有以下三个API:

EventRecord2:可以发送两个32位数据。

EventRecord4:可以发送四个32位数据。

EventRecordData:可以发送字符串。

显然这三个函数没有printf使用方便,所以对于这三个函数,大家做个简单的了解即可。教程配套例子里面有调用到这三个函数,可以操作熟悉下。这三个API的说明是在对应的help文档中,即MDK安装目录路径:/ARM/PACK/Keil/ARM_Compiler/1.6.0/Doc/General/html/index.html。

  第3步:进入调试状态,选上周期更新:

点击全速运行:

然后将Event Recorder调试组件展示出来:

效果如下:

另外,这里有个知识点需要大家了解下,如果程序里面也调用了Event Statistics时间测量函数,那么也会在这个界面里面展示消息的,如何才能仅展示大家想看的功能呢?这就需要用到Event Recorder支持的筛选功能。使用这个功能需要大家先暂停全速运行,然后点击下面这个选项:

弹出的界面里面可以设置哪些选项显示,哪些选项不显示(勾上表示显示),我们这里取消Event Statistics的显示,设置完毕后记得点击OK按钮。

这就不展示Event Statistics的内容了。再次启动全速运行前,下面这个选项的对勾别忘了勾上。

8.5   Event Recorder实现printf重定向

实现printf输出需要用到MDK调试组件中的Debug(printf) Viewer,输出效果就跟大家使用串口调试软件一样,可以输出中文和英文。

MDK的printf调试组件使用方法跟本章8.4小节中的说明一样,点击调试,选中周期运行,然后显示Debug(printf) Viewer调试组件:

效果如下:

另外,还有一个知识点需要给大家做个补充,使用SWD接口的SWO引脚也是可以做串口打印的,并且也是通过这个调试组件Debug(printf) Viewer进行输出。只是这种方式的性能没有Event Viewer强,而且要多占用一个SWO引脚。


) W- B2 _* T6 A8.6   Event Statistics 时间测量功能的实现

时间测量功能简单易用,仅需一个起始函数,一个停止函数即可。当前支持4组,每组支持16路测量,也就是可以同时测量64路。

时间测量的API函数支持多任务和中断里面随意调用。

1、  测量起始函数:EventStartG (slot) 或者EventStartGv (slot, val1, val2)

  • 函数中的字母G是表示分组A,B,C,D,即实际调用函数为EventStartA,EventStartB,EventStartC和EventStartD。

  •   函数的第一个形参slot的范围是0-15,也就是每个分组可以测试16路。

  •   函数后面的两个形象val1和val2是32位变量,用户可以用这两个形参来传递变量数值给Event Statistics调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    6 |! L. R' ]- f6 P: A

    3 A& z1 [  a6 p/ ^( k* z. ]
    " X5 P( a9 y/ M& _

2、  测量停止函数:EventStopG (slot) 或者  EventStopGv (slot, val1, val2)

  •   函数中的字母G是表示分组A,B,C,D,即实际调用函数为EventStopA,EventStopB,EventStopC和EventStopD。

  •   函数的第一个形参slot的范围是0-15,也就是每个分组可以测试16路。

  • 函数后面的两个形象val1和val2是32位变量,用户可以用这两个形参来传递变量数值给Event Statistics调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    & q6 R+ s: C2 f8 k. y
    2 d( J' K$ i  d2 x% N8 x% E

这里也分步为大家说明Event Statistics时间测量功能的使用方法。

  第1步:初始化,仅需添加如下两行代码即可。


  • 5 [* V& D2 w* y3 L( I
      V6 G( I, Q8 E
    /* 初始化EventRecorder并开启 */
    : X, {" x1 ~, d' C5 @2 F$ A

    ) i! C6 o! I+ \1 J

  •   X( m. e/ ?  k
    1 _" g/ I: |" E$ Y& j( q- o1 T
    $ B2 B7 Z) B$ G4 F0 j3 |" |

    ; M% z! b0 [7 \7 Y; E$ z6 d& ]
  • : b* I$ J" O4 W  M' @" A
    ; V4 a5 L" H$ B; O: X
    EventRecorderInitialize(EventRecordAll, 1U);9 M' i" H4 j6 [% T

    ; e; Q; M  M8 Z% b: o, F& v

  • 8 [5 x' e: t7 }1 J; y' J
    3 x! j) E; x- [) B. u+ z

    + t7 {7 I: e7 d- i- s
    3 i3 [# ~, |, B- c. D7 M. Y

  • 1 }+ f$ z: H) Q. s6 o/ P
    8 D# S! a. z  z3 G
    EventRecorderStart();7 `! q6 x, F9 s
    9 e8 ^# n+ U( S# s" f4 \
    7 x8 |9 r) j. y1 S7 d4 T

    & g' u( @2 B8 T% Y0 U* _! {, _

  第2步:在要测量的代码前后加上起始和结束时间。

  • ' K; G2 K3 j9 C8 x8 s/ p5 a
    ) a' t* F, O$ b& A6 A1 c
    EventStartA(0);" z7 G' D: b6 f2 A6 M
    5 M1 s, Y. k4 I9 b& Q" c  C% U2 ?

  • " n: Q" s) e4 z+ m/ X- g2 V5 }

    . g! S" B) q& M" H7 l0 a

    - r, w3 z0 n* _3 h

    7 A) x; b! w* G: M  x# S( e# l6 K

  • ! H: Z* ^0 m/ w0 j  Y

    % l1 v( k6 G' Q* p8 q8 v
    //测量的代码部分
    / N# t$ N( t% e" o7 ^

    6 a  \) f2 p& Z$ F* K

  • ! U1 f5 t( w* U* U/ h

    / F2 j% L) f1 T+ o1 Y. S

    7 i/ k- r; Z. K; y; Q; c

    " y# p* _# |' q5 q3 V: j

  • ! O& C) i  A. M0 e+ w, L- Q6 w. ]
    3 U* M. f  [" i1 v8 @/ V
    EventStopA(0);
    1 b; I, h6 F5 n1 C9 B, c" I

    : z/ h* Y/ C8 u7 Z, H1 E7 z8 ~$ |  g9 G0 O6 _

    5 Z9 x( C6 `( p1 ?9 O

这里是用分组A的测量通道0。

  第3步:跟本章8.4小节讲解的一样,点击调试,选择周期更新选项,然后全速运行。

  第4步:全速运行后,显示Event Statistics调试组件。

比如我这里简单的测试了一个5ms的延迟函数,效果如下(测量时间是动态更新的):

另外要注意一点,微秒的时间单位us可能无法正常显示,这个是没有关系的:

8.7   Event Statistics 功耗测量功能的实现

当前仅KEIL自家的ULINKplus支持功耗测量功能,这款下载器不便宜,一套5000多,大家有个了解即可,我们这里就不做讲解了。

8.8   Event Recorder对RTX5及其所有中间件的支持

后面做RTX5及其所有中间件的教程时会为大家做讲解,这里让大家看下效果:

  •   RTX5组件和使用Event Recoder的效果:


    8 Q. s5 |: C  i4 Q. m2 @" n; w

    % N! c1 A% V; w

  •   网络调试组件效果展示:


    ( J% c/ ~0 ]1 q( i# ^: G

    ' H2 y4 ^3 E' K' H' y4 j# d

  •   文件系统和USB协议栈的效果展示:

    7 x" u# X% e" X- b/ X( u

    ' f" v- {$ L2 M" u$ I

8.9   JLINK配置说明

为了帮助大家更好的使用JLINK,这里将JLINK配置中关键的几个地方做个说明。

  •   下面这个地方最重要,一定要正确设置当前系统工作的主频,如果不正确,会导致Event Statistics的时间统计不正确(对于H7,Core部分要填400MHz)。

    " J  U) W0 V  e. _- n9 Z

    $ t. g# b% B: P5 A6 k8 I

注:如果大家调试状态弹出SWD配置时钟超出范围的问题,可以考虑将上面截图中的Enable选项的对勾取消掉即可,但内核时钟一定要修改为芯片的主频。

另外,进入调试状态后,右下角的时间是否正常更新都没有关系:

  •   其它选项配置如下(只要大家的工程能够正常调试,配置就是没问题的):

    $ ^7 Z5 Q9 G( N3 z
    . [3 z" z3 z+ Q) [

8.10 STLINK配置说明

为了帮助大家更好的使用STLINK,这里将STLINK配置中关键的几个地方做个说明。

  •   下面这个地方最重要,一定要正确设置当前系统工作的主频,如果不正确,会导致Event Statistics的时间统计是不正确的(对于H7,Core部分要填400MHz)。


    0 m3 Y& H+ I0 j) m
    - ^: f4 I& X' y

另外注意,进入调试状态后,右下角的时间是否正常更新都没有关系:

  • 其它选项配置如下(只要大家的工程能够正常调试,配置就是没问题的):


    4 Q. H" n, Y, x$ l# F

    6 @0 \8 i! j( p$ L

8.11 CMSIS-DAP配置说明

为了帮助大家更好的使用CMSIS-DAP,这里将CMSIS-DAP配置中关键的几个地方做个说明。

  •   下面这个地方最重要,一定要正确设置当前系统工作的主频,如果不正确,会导致Event Statistics的时间统计不正确(对于H7,Core部分要填400MHz)。

    2 R2 n9 V/ }( F' _8 H3 L* J

    3 [3 W% @! \4 G& X

另外注意,进入调试状态后,右下角的时间是否正常更新都没有关系:

  •   其它选项配置如下(只要大家的工程能够正常调试,配置就是没问题的):


    ) K) s* g1 h! E3 w# @% O2 i
    3 R& F7 P% y4 W) l0 _" t5 g

8.12 ULINK配置说明

由于手头没有ULINK,这里就不做讲解了。如果大家需要相关配置,按照前面小节三款LINK的配置照葫芦画瓢搞一下即可,或者在MDK安装目录的路径ARM\Hlp下有对应的文档说明:

8.13 配套例子

本章节教程配套了如下例程,仅MDK版本。

  •   V7-008_终极调试组件EventRecoder的使用

    ! i+ O& o2 e  r: T

    " K# p" V+ D. U( X) C7 ~9 {

具体代码实现也比较简单,以V6开发板为例,定义一个TIM6的中断,中断频率是500Hz,通过Event Statistics测量中断的执行频率。代码如下:

  1. #include "bsp.h"
    ( Y, r: t0 \) W

  2. - s  B8 b+ E5 x9 T
  3. #include "EventRecorder.h"
    % G( q+ e  q% {" O# a
  4. /* 定时器频率,500Hz */
    & h- o2 K. U% a& K, l
  5. #define  timerINTERRUPT_FREQUENCY    500* x/ k8 t/ ~, o, B+ q, H
  6. /* 中断优先级 */
    ! `, t$ k0 W( P$ S; \) C
  7. #define  timerHIGHEST_PRIORITY       10
    1 r1 x/ E6 M$ h5 d
  8. /*
    - ~' g" D$ [2 {, S, G2 L' @
  9. *********************************************************************************************************
    + A/ Y, F  _/ N* V$ h7 Y& J, @
  10. *    函 数 名: vEventRecorderTest( Z1 i# M9 ~# ?: j+ A) n6 Q
  11. *    功能说明: 创建定时器; p3 l" q5 D6 ?; Z4 {' ?
  12. *    形    参: 无
    9 [, s% `" n; c# F5 s) t
  13. *    返 回 值: 无; K1 ^$ d* o. `
  14. ********************************************************************************************************** `# O. n; |. B% r; K! ^- w
  15. */1 b/ K' d$ ?1 P/ `& z$ N; `+ E
  16. void vEventRecorderTest(void)9 E* G/ m" ^" c& R/ S( ~, o8 l
  17. {8 N" r) ?$ I9 }0 A* ^1 A
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
    6 n5 o. L8 h+ O" h) o( S
  19.      EventStartB(0);   
    * t- v" j; F4 [9 I; g: U
  20. }+ g! C4 q3 u0 B( ?2 _

  21. + x. G4 U& B2 V. a
  22. /*
    ( [9 g6 A% c. z( w3 Q: n' l; ^
  23. ********************************************************************************************************** \3 X  m$ Z. h8 D& d" f1 G1 r# p/ m
  24. *    函 数 名: TIM6_DAC_IRQHandler4 ?3 r3 ?+ i  f+ h5 k4 f6 Q
  25. *    功能说明: TIM6中断服务程序。
    6 a( ~) ]4 A3 C3 R  \+ N$ M, U7 `
  26. *    形    参: 无& F  ?' k6 ~; l, Q, o4 G
  27. *    返 回 值: 无
    * O, A1 e/ |8 M
  28. *********************************************************************************************************! R( K5 {8 ~/ w
  29. */+ K) M9 U( r* T1 b; \4 A7 T
  30. void TIM6_DAC_IRQHandler( void )* o+ ^# ^- i* v& E, \
  31. {- j* J. B* ?4 d# o) X$ W
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)4 R6 P% P. c3 z2 t6 v
  33.      {
    - ?6 e( U( J1 p6 J5 A
  34.          EventStopB(0);    0 x0 w( ^& E1 v* i$ @5 u
  35.          EventStartB(0);   
    ' \* Q# C  q7 F
  36.          /* 清除更新标志 */
    * f% O- `+ B+ N1 C
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;* |4 ~8 ], r- O
  38.      }' R" @. o1 ~1 f4 d% f( |9 ]7 b0 i* M
  39. }
复制代码
- G3 M* m/ ^! B; V# `4 `7 b5 f

效果如下,测量的平均频率是1.98ms,与我们设计的500Hz基本符合:

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */* Z( z7 k& {1 G! t( Z& q
  2. #include "EventRecorder.h"- X8 X* j" {, Q% m7 S

  3. 2 d: a3 y0 f0 w& b* q& o/ o
  4. /*  Z, K4 D5 u( s; ~6 J7 D
  5. *********************************************************************************************************5 ]: C+ U1 M: o4 d& r3 x% l8 ^
  6. *                                              函数和变量$ ]7 r& n5 j% V% y' o# m
  7. *********************************************************************************************************7 R: z  h1 H2 g8 |9 u
  8. */
    . ?  f, I# `+ x7 J5 o2 B9 g
  9. extern void vEventRecorderTest(void);
    4 @* y7 C# e( p3 t
  10. uint8_t s_ucBuf[10] = "armfly";2 D/ Q; X1 P; h/ G) u

  11. 4 I( v1 Z( B1 R2 g
  12. /*
    4 n* j! `6 c4 e: L; W1 Y+ ]8 |
  13. *********************************************************************************************************
    - m# U5 F+ I7 l% F/ o0 g# h% x( l( \
  14. *    函 数 名: main4 _" @' g; R/ B5 S* s! Q2 {
  15. *    功能说明: c程序入口
    # ^  D1 q+ q. r4 q% U6 I7 j/ i
  16. *    形    参:无* h8 N. Q8 _' o, Z
  17. *    返 回 值: 错误代码(无需处理)* E+ m$ {* i% S- ?% k' i
  18. *********************************************************************************************************& `  k+ L) S9 @( i
  19. */1 M( Y( K' G, {  w: r$ K
  20. 3 F( M0 E! I6 w3 d
  21. int main(void)0 l( c/ D9 j- t' H5 g6 M5 E
  22. {" w  E2 ^; ]# p# n+ c1 u
  23.      uint8_t ucKeyCode;     /* 按键代码 */
    $ D6 p  \" U  z# V3 b! @1 Q) ~
  24. ; b8 Z  H( c2 ~$ W. @; w
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;! A% k8 g% ^+ v

  26. : ]* l9 H/ A2 X5 a* g$ i
  27.      /* 初始化EventRecorder并开启 */
    + ?; w0 z# g5 D' l. ~" O

  28. 2 I6 Q1 U# a( g% [. a
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    # k0 l. I- U) D) Q4 z  N3 a  l

  30. 1 \9 R# ~- R) b/ _# R* O
  31.      EventRecorderStart();
    9 w. `) Q/ s4 F! m7 G. z
  32. . ~, z& V# ~: r/ _  I
  33.      bsp_Init();        /* 硬件初始化 */! Y6 y7 `/ y  o0 O
  34. 4 G( L4 R& E1 t" e
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */: s; L% [: [2 F9 w: J5 l
  36. ! x. t0 {: V8 K' @
  37.      /* 测量中断周期 */* W2 N8 W9 z4 w' r, @/ b
  38.      vEventRecorderTest();3 E- J: e1 Q& [5 z! z1 R2 p8 ?& r

  39. + D/ R8 d# }+ t7 E8 j: `& A2 B  f# I
  40.      /* 进入主程序循环体 */( a. C; U& C# j; T, U8 l
  41.      while (1)  |. x; S. H3 g7 Y  p4 W9 b/ f
  42.      {: ^- R3 D" R' K6 s; h" N) p. N
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */. u, P: S0 H/ U6 m' a! s! E

  44. , _3 t1 B6 S$ e" G  q/ `
  45.          /* 判断定时器超时时间 */- W, R+ L# j2 {+ f# r% M% N) s
  46.          if (bsp_CheckTimer(0))
    2 v  O: ]( d7 _& t. o0 q5 R% O
  47.          {: \, ]' }2 v; N/ a
  48.               EventStartA(0);  ! v* K' @, c9 M5 x" X
  49.               EventStopA(0);+ X- m1 n4 k; G* J- g
  50.               EventStartA(1);
    , {5 _  E4 R, k4 [; L, R
  51.               bsp_DelayMS(5);3 I# [  a' S; n+ s- p( x
  52.               EventStopA(1);
    7 b0 I0 ?* a' E/ R
  53.               EventStartA(2);4 s: L0 g8 t6 a; ~0 S& @, y
  54.               bsp_DelayMS(30);
    : a5 M# t8 d- W6 d, T
  55.               EventStopA(2);3 q( a* P- m! M
  56. & }9 Y" K8 h/ s. v/ u
  57.               t0++;' F) k$ W  |  a) N8 u
  58.               EventStartAv(3, t0, t0);
    8 v5 d- q- k& u% y' n. {  i2 ]$ \
  59.               bsp_DelayMS(30);
    ' B0 ~; R- {) S3 {
  60.               EventStopAv(3, t0, t0);" D% Y4 m4 Y; E9 `! T! a
  61.          }
    ) b9 C$ \4 [2 Z, ~2 N

  62. 8 _3 ~3 d! M% }8 ~' N4 t
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    % z& I* t/ U- n# i/ N' L
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */# b8 T! y/ z, V9 d
  65.          if (ucKeyCode != KEY_NONE)
    7 @. {% @$ e- I; K5 K( t0 j
  66.          {1 w' n4 R1 j$ c. M6 t
  67.               switch (ucKeyCode)1 D2 q" B; s8 b- u: T& W
  68.               {
    ( }& A1 Z6 c  m% G3 j
  69.                    case KEY_DOWN_K1:           /* K1键按下 */  ^; t  d8 w7 {
  70.                        t1 += 1;
    & `/ e# B1 A) P% I+ b
  71.                        t2 += 2;1 o- _) ]/ J8 y' W; h
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);  N* J3 e' S5 F
  73.                        t3 += 3;8 O! N* ?% P9 b% x+ Z: }
  74.                        t4 += 4;
    ) k1 `4 Y  X  t7 Q
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
      I: S7 o  ^* O. @! _# `+ R
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    , s* h8 M9 h8 h( O* b$ Q
  77.                        break;
    ( x8 Z/ L( R5 F

  78. / B$ j" W) r3 h# X; A
  79.                    case KEY_DOWN_K2:           /* K2键按下 */
    1 w7 ], W! B. a: m. }! O8 }# j) R
  80.                        printf("K2按键按下\r\n");; B0 `- T. i! m* E6 W
  81.                        break;
    ; ?8 O& \! E- v, h( ]; C

  82. $ Q3 q/ A4 v; B. N5 o5 W8 Z
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    9 L: a, Y0 l5 i, B: n
  84.                        printf("K3按键按下\r\n");
    7 p  b& }% t3 U' |& D( q- |5 Z
  85.                        break;
    0 a6 j: X) w! h' U- D9 y$ U
  86. , `7 |  Y( G. o) f: @
  87.                    default:( K) J6 G. t5 y- S. T4 _9 r8 {
  88.                         /* 其它的键值不处理 */- t/ X# m; {0 F+ [, f9 P9 `
  89.                        break;! V$ d6 R$ r* x" E9 L
  90.               }" t8 d* p: F+ Q( ?
  91.          }) W1 ]. }) C+ [( j$ g3 ~8 u8 W
  92.      }
    9 @2 y2 S' m- p& [7 t
  93. }
复制代码

* F6 O+ i9 s2 q( ]# \& r: E0 G8 [

应用程序里面主要实现了三个功能:

  1、利用测量分组A实现4路时间的测量(第1路什么也没有测量,可以用来表示这两个函数本身执行占用的时间)。每100ms测量一次时间,效果如下:

  2、利用函数EventRecord2,EventRecord4和EventRecordData发送消息事件。按下按键K1进行更新,效果如下:

  3、基于Event Recorder的printf重定向。按下按键K2或者K3会打印消息,效果如下:

8.14 总结

Event Recoder还是非常实用的,建议大家多使用几次,熟练掌握。基本用上几次就上瘾,离不开了,的确是工程调试的利器。

3 b6 ~& p  k* a" V; R, c
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

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