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

【经验分享】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。


    . {/ h6 A; ~' s6 Q( b( a

    2 @# G: g. t4 P' e0 Q
    * {" l- }' l" y% H
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接口区别


    & `# b$ w5 i# `% R. @. N  C' A3 `

    & D7 n( F. W# z5 Z* w

    : F( e- Z* ^1 S! L  X( ^/ F+ X

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

  •   词条 SWV(Serial Wire Viewer)


    0 I0 r( x8 x, k2 X
    . m( O- |9 `% ^4 l3 A4 Q5 f0 _

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 就会把一个差分的时间戳数据包插入到跟踪数据流中。跟踪捕获设备在得到了这些时间戳后,就可以找出各跟踪数据之间的时间相关信息。另外,在时间戳计数器溢出时也会发送时间戳数据包。

    ( U/ c" G1 p% K$ l0 m( v" W, C

    0 U% L6 P0 i3 t2 v% B- K7 Y
    1 ?8 R2 J1 i7 n8 }( O( G
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调试。


    % l% v2 \6 [9 w+ d8 m! w
      q# P8 B6 P* o. G8 N( H
    - H' \- N$ j) y. Z% }
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 {& M6 J9 @8 \+ ?# o/ T
  2. 9 _9 ~) s9 P  N3 d8 N
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)( w* @2 C6 P7 X! c/ ^: ~9 R
  4.   {4 R2 c9 C  f6 i
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)
    ( R9 Q6 ~( d9 ?) W6 d8 e6 h, o8 K
  6. 3 N! Y, Y" {6 o7 |( H8 Q
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB), \- N9 _' H4 g& d: P6 S& o% |
  8. " F6 T( `8 L1 M, C* c. M* v% `# x
  9.   uint32_t info;                // Record Information
    ' ?* _. q' D; M* }' P

  10. % S+ H- I: ~4 ^1 E
  11.                                 //  [ 7.. 0]: Message ID (8-bit)9 \2 Z) o0 d, P, T1 B; L5 U0 s

  12. . ]( A# V3 V8 x( V4 e
  13.                                 //  [15.. 8]: Component ID (8-bit)  @: s/ J! R( H( l! @

  14. & e5 Z; \6 T/ R/ z4 x) c
  15.                                 //  [18..16]: Data Length (1..8) / Event Context+ N' J$ R4 j. k+ b3 j

  16. 7 g0 l) `8 v0 h8 o; k6 _$ l
  17.                                 //      [19]: IRQ Flag% |" p- o8 y" c/ T
  18. - \! j; w& h: T. N- A) W# ^
  19.                                 //  [23..20]: Sequence Number
    & [3 L. ~, N- _( Y2 a" n- q. w

  20. 3 v! e& y9 F; M& ~
  21.                                 //      [24]: First Record2 w/ t' K$ t+ ]

  22. ' C/ P( H: V  t1 \
  23.                                 //      [25]: Last Record
    ; i% L8 K5 S7 h0 ~& o. t/ f# b
  24. 0 H" ]/ P7 f. Q* G  u
  25.                                 //      [26]: Locked Record* y( b% h: V/ X* |

  26. ) J( Y" [8 g0 u+ I2 P; @  Z
  27.                                 //      [27]: Valid Record
    , H9 W( j$ m8 _0 ?0 V- _5 S
  28. - X* r6 |3 A8 h% S& M0 G% l
  29.                                 //      [28]: Timestamp MSB
    + \. [& l$ t2 A7 W
  30. ) s" k, j% I0 N* _8 s: E  \: X2 d
  31.                                 //      [29]: Value 1 MSB* k" k( C. S9 f4 X0 o( \1 h3 r" o

  32. ! R. S7 P4 ~' O7 {/ P1 ~8 v
  33.                                 //      [30]: Value 2 MSB9 K2 {9 w# h2 e6 q" ]- D- o0 L" r* P

  34. # @$ l& T. z: l* b" ~
  35.                                 //      [31]: Toggle bit" m6 Z7 D. J: Y- l# W

  36. 1 j& o6 D9 D. P0 `7 l
  37. } EventRecord_t;
复制代码

3 M/ z3 T3 l+ w

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。' U: d4 I/ ?. F

对于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(随着时间的推移,如果升级了新版本,直接使用新版即可)。


    4 G9 i6 Z  w5 \/ O& ^% c) s% U

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

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

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

    + ?9 k* ]7 N- `! i

    - W: m1 r3 t& j, @: Z; Y3 Q0 X

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */% M7 e$ K) V! @4 U! N" N

  2. % Q5 {' t7 {/ u1 b% N
  3. EventRecorderInitialize(EventRecordAll, 1U);/ w; r  n4 q* V. j+ C
  4. " D) F8 `' w( N/ P! ]
  5. EventRecorderStart();
复制代码

: @9 q$ D6 e  ^$ Q' I6 a0 _

  第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引脚。


, |/ f! Q5 @" \" u9 V8.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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。

    : w; K7 ~! l& K# i# _
    3 B" d4 Q) Q% N+ ~+ c2 ]

    $ T) [8 z5 a5 e1 M5 P: `

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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    - p7 t: e2 N: |8 K# M2 @2 `& W

    * U* J) i0 g5 M8 o, S) j% @2 j

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

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

  • ( S: g  A; a% \
    + Q, a: s9 p% O' N  l8 ]3 \0 s
    /* 初始化EventRecorder并开启 */2 H3 @3 m4 e, t

    # Z8 ^: y  [' R; p4 V1 D

  • ; p& ?5 N/ k3 E$ v% h

    4 L+ P! b3 S" P9 E8 m

    % l' S; N- ?; c6 x7 m
    1 l2 y* f! t5 n( T" Q: m

  • ( M/ K! I9 v& ]/ r# x8 r
    % e7 L, T, i2 m% c  H
    EventRecorderInitialize(EventRecordAll, 1U);
    % m' ]' @  @' i# f9 W  ?

    2 l% `( M+ v0 k' m9 H4 n% v

  • $ W) |* Z  R, @. W7 I$ p
    " `( q) X4 u4 E. X+ n2 L
    ; Z2 n1 O0 B; r# t) i" N

    6 ^/ B1 w; Q$ a1 A

  • : N: ~, C6 O. V0 z$ ^& N( v

    % l$ L) M3 ~1 q( B# I6 D
    EventRecorderStart();
    7 E4 h- o0 [, j/ D( s

    . d6 W  ~! A, u! N( X/ n( ~/ b' G
    / I! G* Y4 x) I1 |

    * y& K: v) r9 k9 a# ]! s, u

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

  • % q" T# h9 B4 j4 T/ {
    9 I0 i! ~, Y" m3 r: x
    EventStartA(0);. p" z9 m. a/ z
    $ c' }# D5 F; ]; ^
  • / m! P- J4 X  ^9 \! n" m6 f
    1 ?8 J# O$ z( X3 o9 [

    $ Z3 U; {0 O% ]9 Z, v
    / C) V' b3 K& v1 N; ]2 N9 k. l& s

  • - q, s3 ~1 V: u. P
    % R. E/ p5 u* q
    //测量的代码部分6 ^# Q/ _- _, M) _$ |2 O

    % z6 q3 k; s( I& P& y

  • . Q' C8 z( i" J9 [

    3 J; Q2 u4 B/ s5 D) s9 C1 A

    % \3 E7 q! I3 }: ]! R

    : R8 l( t( Z* y: c
  • 7 @% K9 e- k6 ~1 p6 v
    - b! P: X5 i8 f, g. D4 A9 s/ ]
    EventStopA(0);
    & Q- I% ?$ c. v* v) F7 z/ r6 k
    / n1 i' G9 |7 [
    8 \; }5 m& D1 X9 z

    9 B/ K7 M3 L% m/ |7 d( J

这里是用分组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的效果:

    : P  g) M1 b6 w: Q2 e7 S) }+ Y% D
    . \$ a# }4 }1 j

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


    , E* x4 r! e9 M# C3 T! z0 F# l
    * z" f9 X, P0 K; J) `( L! |

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

    ' c! S( Z3 W+ p" Z/ i+ M% m0 J

    6 x3 ]6 q# W# F* P( y" o# _+ |

8.9   JLINK配置说明

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

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


    ' b% }. K9 C2 A6 g0 i* D3 Q
    % }+ G! a  w, `8 X, [

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

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

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

    , I& V8 f7 I4 t% X6 q* y

    , ]( T. D# E/ {) r

8.10 STLINK配置说明

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

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

    7 e) w6 r  Y7 r- O1 K
    ; p0 L' H# |! Z: p/ E; t* Q; Z0 |

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

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


    , y8 e& B9 U: K
    ( s$ J7 g4 q& Z/ D& B

8.11 CMSIS-DAP配置说明

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

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


    2 b& V8 z+ o% \) O, y
    + @# C; f4 |6 g9 b3 P

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

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

    , x. j5 u+ g6 T3 e+ a6 d

    8 w% \0 F; T; _

8.12 ULINK配置说明

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

8.13 配套例子

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

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

    0 Y9 H; f8 @& W/ n& |5 A
    6 @  c2 {/ V/ A# ]8 W

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

  1. #include "bsp.h"- D6 h$ \0 I9 }, ~2 V" c" I

  2. # |# ]9 z9 }. q4 f
  3. #include "EventRecorder.h"% K; n% A5 }, O4 O; A
  4. /* 定时器频率,500Hz */
    ; g0 ~5 `/ ?. i& Q: e) |8 ]7 L
  5. #define  timerINTERRUPT_FREQUENCY    500
    % w( Y" r( Y2 w7 ^
  6. /* 中断优先级 *// j& e2 ]  v" h8 J3 B" f
  7. #define  timerHIGHEST_PRIORITY       10
    - l0 t* ?: z$ Y9 J6 e8 i
  8. /*7 m7 u% M) J4 D' ~
  9. *********************************************************************************************************
    - w3 |. Z9 h- u/ B) G" {* Y- n
  10. *    函 数 名: vEventRecorderTest4 ~7 m4 ?0 I5 ^6 Q8 w0 c$ w: `
  11. *    功能说明: 创建定时器* _# v8 ]5 R3 w" l" [: _
  12. *    形    参: 无
    8 a4 O" H) ?, \# Z
  13. *    返 回 值: 无
    2 y3 R) }" z% x
  14. *********************************************************************************************************% q  m9 @3 t; a* ]9 \
  15. */9 h( ]+ Z5 m2 K2 ~  n" ~! e
  16. void vEventRecorderTest(void)
    ( ?, {" u+ A8 o2 f/ O
  17. {8 D/ a2 e, l: e6 I! w% t
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);3 B; g2 p5 h% R( B
  19.      EventStartB(0);   
    * V* [/ t! c& \- N& x
  20. }/ U" Q5 y, ~. d7 j- B2 C
  21. 0 _3 h2 A! u" R2 ]2 J! S
  22. /*' e; N, _9 B6 h+ z/ T0 W
  23. *********************************************************************************************************3 j- y: y% i% ~. Q# P0 j7 h
  24. *    函 数 名: TIM6_DAC_IRQHandler0 P3 m. d0 K+ d* Z! S) D/ ^& y6 R
  25. *    功能说明: TIM6中断服务程序。1 |: I, Q: h  U2 H1 |# ^( w
  26. *    形    参: 无
    0 @  u: c% `" v; W( o
  27. *    返 回 值: 无$ m( Q- z1 e1 J' l9 H
  28. *********************************************************************************************************9 q6 R" Z/ L( ^6 r
  29. */2 n$ }' Y- u- g' b; n( p# i
  30. void TIM6_DAC_IRQHandler( void )
    + |3 l$ m4 ?/ X. a: D) H
  31. {
    ! i. I# A% d6 j
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)& y1 C0 a; _( \. C5 `
  33.      {
    7 m( Q0 ]* f' G* ?' m$ E) V0 n
  34.          EventStopB(0);   
    0 t7 K- g! m) h2 }  e. U  e/ B
  35.          EventStartB(0);   
    ; {/ Y$ {" e( l* }
  36.          /* 清除更新标志 */
    0 k% K& y- @: L; @
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;
    - Z5 p: H# h* x+ r) m
  38.      }
    . ?; g4 |' ^4 ~; [3 ]+ x
  39. }
复制代码
0 P8 G1 O! J+ v" \. T! f6 x

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    ; _) u& c. C$ z3 [0 F! g
  2. #include "EventRecorder.h"
      Z% h! N2 e, M6 O/ _, C
  3. 9 G* I, M  u) \) F; i1 u4 b
  4. /*
    . \- U" }3 O' o
  5. *********************************************************************************************************
    2 {# Z/ S8 H7 h. e! U7 O* q0 Z
  6. *                                              函数和变量
    0 s% q  Z% E4 J
  7. *********************************************************************************************************
    5 d$ M  J' S' F% P; A0 V
  8. */
    / H! N" W9 }; `3 m
  9. extern void vEventRecorderTest(void);
    ( f% m0 J/ K' _5 c$ T; `* h  x
  10. uint8_t s_ucBuf[10] = "armfly";& X2 I' I4 E/ f  r2 N* ?' U7 c# T
  11. ) k* M# r4 ^6 ]. N% C
  12. /*
    4 c: R, L( B+ Y6 V
  13. *********************************************************************************************************. v/ Z, G5 Z( [6 [% x+ I/ S& q+ S
  14. *    函 数 名: main
    $ P& U! f" [; N% s& n; [
  15. *    功能说明: c程序入口; A1 [: `6 _- @* I
  16. *    形    参:无
    , L  _' L9 x$ X% G. d
  17. *    返 回 值: 错误代码(无需处理)
    & j4 t% S  L: E* W# s. Z5 D; q
  18. *********************************************************************************************************+ W' R2 M- n- ]1 A5 o
  19. */
    0 ~# V1 ^2 Y+ ^! ~' d9 ~8 H1 T
  20. 0 M- U' y4 r' X7 S) w( G
  21. int main(void); W# a3 ]9 \5 _+ x$ m  |  Z0 Q" N( ]
  22. {% I" W: o6 [% k6 I
  23.      uint8_t ucKeyCode;     /* 按键代码 */
    5 T2 @# F, y( l
  24. $ ?: j! p. r- n5 s
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;1 t  \0 m$ k# y

  26. & c+ \0 V4 K: \3 I" H# s
  27.      /* 初始化EventRecorder并开启 */* W  b) k( ]  @, f% [! Z) v/ S
  28. 4 {) e' P+ R  |4 A& i9 g9 F8 o7 P, {/ S
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    4 K' A; l% {9 h" \4 h
  30. % d; K% F1 K5 \0 |9 t
  31.      EventRecorderStart();- d# f' m; i/ C* `  G# B
  32. / W% z+ C; A! O7 `6 M+ f
  33.      bsp_Init();        /* 硬件初始化 */6 Y2 r: y/ x/ h9 N; L% _1 L

  34. 0 A) ]' o* W0 e' U7 c" S
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */, @$ e6 }% c. m  V0 T2 M

  36. # R# h: S8 R1 d) g, q2 I/ f; u; e. p
  37.      /* 测量中断周期 */9 ^+ x' C* u( D, O* E* ?/ p
  38.      vEventRecorderTest();% |' \; N# r5 B3 K1 S3 n
  39. 1 |7 U6 _( s0 E" \
  40.      /* 进入主程序循环体 */2 K; v) e% ]/ @9 L5 ?$ c# X! `, P
  41.      while (1)9 X, k' [* s/ D8 Z& q; f
  42.      {
    ) H* A0 V% h) W  i3 u+ J4 o
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */8 g+ s4 e. ?* l# P) `; R6 B9 w
  44. ' K3 M" `3 B2 c7 N
  45.          /* 判断定时器超时时间 */& [& f0 \: p1 a4 \
  46.          if (bsp_CheckTimer(0))0 l' \6 A% ^0 e# ^; t
  47.          {
    % D/ ^+ C: I# w$ b  _
  48.               EventStartA(0);  
    8 M3 b% ^. f3 b' ?$ j
  49.               EventStopA(0);* P. c$ \3 Z$ x
  50.               EventStartA(1);
    ) v2 u5 x# N% C  k
  51.               bsp_DelayMS(5);9 m  Y! M; }) w) C" p* r3 s
  52.               EventStopA(1);
    5 x6 @& ^8 D+ N
  53.               EventStartA(2);* v4 W4 Z5 G$ f0 Z
  54.               bsp_DelayMS(30);9 L) t/ O; m$ \9 [6 Z! U
  55.               EventStopA(2);
    , G( X- J4 Z. f+ J) N$ U7 E
  56. $ e6 l( c. f; u/ X& r# B3 {! g, {
  57.               t0++;0 X) P9 }2 [+ ?$ M/ r
  58.               EventStartAv(3, t0, t0);
    1 f: S3 p6 Z2 F9 T3 U4 f) L
  59.               bsp_DelayMS(30);
    1 D# Q6 [/ k3 c( ?7 m9 D
  60.               EventStopAv(3, t0, t0);9 Q+ A. j% @' L1 @: i
  61.          }
    " B+ j( l  C! j  r
  62. 3 f+ v1 c; L' ]8 t9 @# y/ _# O
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */. k5 `3 ~7 i& T9 a0 U0 s. f# k( ~2 P
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */& s2 I2 \4 J5 h, r8 U- T
  65.          if (ucKeyCode != KEY_NONE)4 m2 l% g2 R" _% u6 [1 P
  66.          {/ {* C) P, n: ^8 D0 E0 k  l+ Q* c
  67.               switch (ucKeyCode)( F2 p+ c& k( P9 Y' c
  68.               {
    / [+ y+ T$ ~$ `. ]  [0 }% r% [' [
  69.                    case KEY_DOWN_K1:           /* K1键按下 */
    " w8 Q- B9 c5 e, Z
  70.                        t1 += 1;
    ' y" h4 z% P1 M2 x
  71.                        t2 += 2;$ M& |/ D4 F' s/ J$ w/ x
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);4 L) E" ~$ M( `+ e
  73.                        t3 += 3;
    . {+ ~8 q6 q% H3 ~, ]/ \: A4 r( Q, R
  74.                        t4 += 4;
    0 B/ A7 i$ t& q1 i( z
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
    ) A+ S" d% G1 S: k7 |0 Z
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    ! V4 V4 o2 {- F# ]* Z
  77.                        break;% \- }( W# a! }+ Y: v7 v5 j. z' G

  78. " q% k  n: s3 Z+ S' C1 }5 I( s4 M
  79.                    case KEY_DOWN_K2:           /* K2键按下 */9 s# D+ @% @- b, C5 ~. d
  80.                        printf("K2按键按下\r\n");
    ! z; g+ y  r2 `, _4 ^! v: K0 p4 ]
  81.                        break;
    . w: @4 U. P/ I$ {* d

  82. , y) ]+ c4 }- _, u/ ~. a' f
  83.                    case KEY_DOWN_K3:           /* K3键按下 */3 q. S2 X2 {4 {' K
  84.                        printf("K3按键按下\r\n");+ w7 _! Q& P1 _/ D
  85.                        break;
    . M' K6 J% h. H. ~  F3 l0 M

  86. 5 s& Q- E. r; P+ m' R6 Z
  87.                    default:. L! d) q" D$ f( W, ?  G
  88.                         /* 其它的键值不处理 */
    & _! ?* }* U7 k5 D
  89.                        break;) l6 P. ?! M" D8 ?4 I
  90.               }
    4 ^0 J% b5 j1 e9 ~5 G6 Q
  91.          }
    6 S, p! ]) j  l; z& Q" m
  92.      }
    % W3 g. e; V$ U8 ~! E
  93. }
复制代码

+ E% F7 B1 r( [( b  Q

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

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

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

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

8.14 总结

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

( `8 y+ m4 @) k* x6 h/ r5 T- g* z
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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