请选择 进入手机版 | 继续访问电脑版

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

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


    / l5 M6 p. ?' y0 E
    , d" e& @% f% u; \! E, G3 F
      }1 R! |' J- P6 A7 J
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接口区别


    / l, |/ \# A8 X2 u7 {

    1 B, j! }. p3 I( I5 r+ U% o' n5 d
    % `4 v# H  s1 \. h3 F9 x$ |! N' U; Z

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

  •   词条 SWV(Serial Wire Viewer)


    ' ~: I1 a# y$ k7 y8 w& b. k6 {

    : c, G: |9 u8 @, F8 O* R) j0 h

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

    * Z2 {+ G! c0 r) U: O
    : K0 _7 N0 `% C, M! Y* A
    ! v6 z% `% M# O' S4 U. `
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调试。


    * w+ v+ o1 F9 P2 C! `$ \# U; z8 x8 V) }8 m
    3 M) V0 Q! b" Y, I3 v1 @, Z
      s1 h& }) n  L* v
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 {
    % D9 [; R/ ]7 ?+ j

  2. 2 T3 h$ I* _( f; ?( w" ]& |( W
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)
    + a' _% y9 u$ A0 D

  4. 0 [3 C  h. {, {  C7 r! ?
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)& }0 ]/ Q, o: E' o

  6. 4 O: E: |2 i& l* [
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)1 I- R3 w$ B9 u# n8 G! q% U

  8. 9 P4 W5 k8 z/ K" w3 i. S
  9.   uint32_t info;                // Record Information8 _8 h# J8 [* L& |

  10. 6 U9 \& J5 n* I5 ^" X
  11.                                 //  [ 7.. 0]: Message ID (8-bit)
    + W! {" ~3 G. j- v% S# |# n. z% N

  12. 3 o1 ?' V/ M- v. }2 E
  13.                                 //  [15.. 8]: Component ID (8-bit)" H( P. }+ O5 `5 Y

  14. ! ~1 e9 p6 T7 ]$ _
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    6 t# @! ^1 c* J, s' V! o

  16.   D( G- i: ~$ X# h. s1 i7 O
  17.                                 //      [19]: IRQ Flag; s% n, G  Y% \. y7 [0 {) z
  18. ) u8 s* j! a) O! l
  19.                                 //  [23..20]: Sequence Number
    & P! m) K0 O  n, v

  20. ! y  y+ V/ R( P/ f, j
  21.                                 //      [24]: First Record# K. M: ]* b+ i/ V1 f
  22. ( o) }+ `8 ]. h' a/ G5 p
  23.                                 //      [25]: Last Record
    $ S( O% n5 L0 t" f

  24. / p3 [" P- _4 l2 m& i. m- @, `1 `
  25.                                 //      [26]: Locked Record
    / t/ W5 \1 f  K9 A. I
  26. 0 y# A6 |/ j9 D& m
  27.                                 //      [27]: Valid Record- D) x: G; E$ h" Y: Q" b% X; l( I
  28. ! M( [- R. b* @, l$ J5 G
  29.                                 //      [28]: Timestamp MSB
    " F# J3 n; a9 V% u

  30. % a% A# T+ [1 ?; r8 ~5 v+ T
  31.                                 //      [29]: Value 1 MSB
    6 q7 q) g4 g/ o# ^' r! M( @
  32. - b0 ^/ D0 C% l5 f5 i
  33.                                 //      [30]: Value 2 MSB+ ?( F& `- C9 M8 s- d

  34. ( d$ n! C! S5 J* H1 d+ d# p
  35.                                 //      [31]: Toggle bit+ E' H0 S' n9 [: U! d( N; D
  36. 2 C% d' |; E5 `, q: a5 ]
  37. } EventRecord_t;
复制代码


$ h8 N! \* P$ k+ i4 A) t

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。+ J8 g  H# z6 Q4 S, C! @

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

    # @: z) w4 Q! l+ z! \- o

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

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

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

    * c+ y5 T/ x( {. e4 k* x8 y9 ~+ g
    0 h8 @( P( k6 f) K! o% L% _) P

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */
    7 l" b* a  l0 |* j9 ?4 B5 X" @
  2. * Y8 H6 s5 t& }6 |7 R5 r; F& U* g
  3. EventRecorderInitialize(EventRecordAll, 1U);
    / c: u3 u3 U+ |+ f- f
  4.   E" M0 Q. U2 Z
  5. EventRecorderStart();
复制代码


; r, {0 g. q: f2 D% ~# y- M# t! |

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


" R" f- M. |( c1 N% D1 h# b8 A7 w8.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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    - K0 s% r% l; H$ ?0 X/ w' [

    ; D9 U& H2 E' i
    % s9 x+ e( t: M+ h" ], l0 w

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

    : n" [" t# ]* X: v7 W" r/ a
    ; {) V5 t0 `# W3 L  g- @4 `" Z

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

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


  • ; F0 v5 Y! e5 o9 E

    2 |* ]9 {' O; v7 P/ a8 ^
    /* 初始化EventRecorder并开启 */( F/ ?! \% q& k- D8 c$ @+ |
    * Y) d6 Z3 D8 J' s

  • ( H; v! ?& ^: _% T' p4 e

    4 ^& `; z1 y2 ?9 r( w& o

    ' U6 Y( E- H6 ~) e' Y
    2 G2 Q! S; k+ W4 s8 E

  • : U* S" a: s6 s- J4 s% g2 Q, _% ]

    5 T0 Q. a5 F& o2 A1 t
    EventRecorderInitialize(EventRecordAll, 1U);9 Q( X" F7 j# ?6 [5 D3 [, _
    , o" [+ ?1 B1 f8 N& e5 g" \6 u
  • ) w5 M4 v  X3 p& F# X

    ) D% m% \6 t( i: s: A7 b* \: `& P
    ( T" F' u: d/ f: x% n
    1 p/ X8 Q6 s/ z+ w: I
  • ; b2 r0 W) l" l: l2 g
    7 u$ e- q) I5 Z* N9 D
    EventRecorderStart();
    6 f! \; q# ~( Q$ n4 @
    1 F( I4 C1 h/ {7 x* k
    / v1 j2 \* F6 y6 }0 K3 ^. ~/ d

    6 C4 L0 i" @+ l& _. x. Q

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


  • ) W$ V  {' y! N

    ! u2 N9 h' T0 [: N
    EventStartA(0);
    6 j6 j$ J4 ^. d) k
    & T) s: y9 Y! a0 o  s1 \4 d
  • ( ^8 A0 k4 h$ }- A4 |

    3 i6 V& R7 x, o
    - I  m5 y( V) @, b8 ?0 V

      x2 y6 Y4 H0 A  Z% f/ d& z
  • 8 \$ A2 A# @% ]5 |0 b

    + ]& d1 q0 Q/ A5 g; g0 ~+ S
    //测量的代码部分
    $ K$ K5 `) l- x5 I- s+ A

    7 b) K; ]! p. u4 e
  • # v: c" H/ T- s& r5 d6 B

    ; f1 p- R$ @  X6 i2 w1 \! h" t

    - q7 U" {6 L3 S9 [" m- \6 T
    / W! E0 h4 D" ]. a9 p  @& p( z
  • 1 U$ Q. r. N& V: T0 K& \. d
    * D; P5 s# t, z# D  G
    EventStopA(0);
    ; v3 ?- G' P2 r# b0 w  i* P

    + ~* H* r' Z( c/ E, G* k* Y
    3 p5 a# d7 o  H
    ! v% k2 V  `8 X1 u& p# K/ V: d

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


    / z. p, G2 j- a" R* ?! ^
    1 k$ ^: q  Z3 c* S$ t0 O  j

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


    8 w! X5 i* R9 I& t1 J

    , i" c+ f; S; W! ^+ S

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

    ' Q; Y; g/ }: k$ V. z! n9 O2 b
    ; `' l( Q* K; {

8.9   JLINK配置说明

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

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

    : W1 p6 A4 R' _
    % i) G) f' C/ u4 d$ s% _/ ~( L/ g

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

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

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


    5 C; A/ M# u6 T+ M% m9 ]2 Y

    : l5 s* b4 P9 e$ h

8.10 STLINK配置说明

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

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

    % T- y5 G$ A( c* K/ r0 c
    % v- N8 x; L1 l: ?1 U% m0 e

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

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


    + X+ R- X* `  |
    . X$ U5 u7 n3 R* ?

8.11 CMSIS-DAP配置说明

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

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

    7 h3 G, G) n" h

    + v9 D% o' |  v; Y" w* T) }

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

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


    6 V5 `; T. K( H6 _+ e
    - P6 K0 g& _1 w( ^2 P- T

8.12 ULINK配置说明

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

8.13 配套例子

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

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

    , i9 b6 b" Q( R7 C  W
    1 P5 S* v  [  n+ J+ u% ?

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

  1. #include "bsp.h"
    # c/ r0 ]2 `5 R! Y

  2. : H9 J" E% I* [! q" F
  3. #include "EventRecorder.h"3 ^5 m! k+ E! }. P1 v9 H' F
  4. /* 定时器频率,500Hz */
    $ U0 ^9 E- J9 {$ O! z( P
  5. #define  timerINTERRUPT_FREQUENCY    5003 K8 ^, z1 \4 ]3 M2 Q' Z+ S4 n
  6. /* 中断优先级 */, [- p/ `9 e  J
  7. #define  timerHIGHEST_PRIORITY       10& l. x/ z( O) Q7 U6 H, o
  8. /*
    , ]. Q" N) y, e- n- r
  9. *********************************************************************************************************
    ( @7 t! m- @2 g5 D# n5 a
  10. *    函 数 名: vEventRecorderTest( @/ o' O1 e, A' F4 C6 {+ A
  11. *    功能说明: 创建定时器
    2 J: {' ^9 O  }2 `# R7 w
  12. *    形    参: 无
    2 s& ^! m) y8 a# t! I! V
  13. *    返 回 值: 无6 J% _1 Y( m6 Y3 z
  14. *********************************************************************************************************# h! R7 ?. \( c  [! I8 W3 q8 Q
  15. */9 d  e, z5 }$ ]" G4 c9 y2 Y, T
  16. void vEventRecorderTest(void)
    7 k4 H% i$ f1 R: F- p: E( M1 r
  17. {) L' c+ W0 m( J; C5 s
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
    $ V4 {0 s7 o% H& |9 t- X
  19.      EventStartB(0);   0 f# Q7 [- }5 C  N7 o
  20. }
    ; b9 v+ p3 V/ @$ X& r! c) ?

  21. & X$ j; P- ~# o+ H/ n& L* n
  22. /*
    ( Q: F: r  b0 h$ }
  23. *********************************************************************************************************
    * [. E6 y* \5 Y! n8 O
  24. *    函 数 名: TIM6_DAC_IRQHandler4 Q# h( C& o: ]8 ?  U
  25. *    功能说明: TIM6中断服务程序。
    3 c5 U- X0 g- w2 ]! Y! R8 F
  26. *    形    参: 无. D4 @# L8 B+ G6 @4 v% K2 R
  27. *    返 回 值: 无
    6 P. V' V; |7 v3 n$ v/ y  `8 g
  28. *********************************************************************************************************5 H6 R, g' I& h+ C
  29. */
    , D+ U4 t4 m; k: V! T0 F
  30. void TIM6_DAC_IRQHandler( void ). n6 |# X9 d! R" [+ p5 p
  31. {- k1 w5 Y+ g+ `3 U
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    , x" r# A( r  \6 E
  33.      {7 T% \8 m1 s' e
  34.          EventStopB(0);   
    ; Y- L# u5 K7 y( i# o5 C3 a* G
  35.          EventStartB(0);   , N  k6 W9 I) p5 R+ `
  36.          /* 清除更新标志 */
    $ m6 {" `& `. O
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;9 _# c# d, d  R* ^
  38.      }0 r; @  f% {5 ]3 C
  39. }
复制代码

5 c* Y7 S, L4 b# X3 b

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    % E! K& U3 _' s$ l8 j6 i4 k6 K
  2. #include "EventRecorder.h"
    ( @; G  D# S' k1 U9 a
  3. 8 B1 w( M5 D+ D3 `0 r0 o0 P9 n
  4. /*
    / K4 z% J- _6 y$ ]
  5. *********************************************************************************************************& b' z5 N3 i/ ?6 ]# y7 p; C
  6. *                                              函数和变量
    , Z8 U1 R$ ]+ a
  7. *********************************************************************************************************
    5 r; S) X6 K: X4 M, U8 i0 y- e
  8. */3 n6 w/ y5 I; I( n
  9. extern void vEventRecorderTest(void);
    # B0 r7 F2 y; f
  10. uint8_t s_ucBuf[10] = "armfly";1 h1 _7 J# f: D7 _% T: ^, {! p
  11. : C+ Q' V( [/ c2 ]
  12. /*
    6 t' _1 U: G2 f1 t" t: G
  13. *********************************************************************************************************
    * e7 ^) o4 }9 e' E' u/ \! a
  14. *    函 数 名: main
    7 t3 h& q2 H8 P5 o* `# O
  15. *    功能说明: c程序入口
      ~. o! O/ |/ L
  16. *    形    参:无
    * T  ~4 {( @) F) T" u+ p9 z# I
  17. *    返 回 值: 错误代码(无需处理)
    ! ]% a& e+ U4 m
  18. *********************************************************************************************************, L% b9 E3 }2 V  {  ^2 Y' i
  19. */% p  m7 [1 E* ^6 T5 }( t) U7 J0 A

  20. + }9 f; G! X0 J: ^' B" j1 @4 W& H4 I
  21. int main(void)' M1 |; H% {7 x/ A# P5 p* q7 o
  22. {
    0 k; v$ t; h* V' D' o  S
  23.      uint8_t ucKeyCode;     /* 按键代码 */
    - v% D6 \- B9 a! `$ m8 J+ _: T

  24. ; O) ^% b0 q. u9 l" l- q
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
    0 b. G# z9 q8 v( C! d! @

  26. 1 B0 |" T  O6 G
  27.      /* 初始化EventRecorder并开启 */$ R, c& t5 O5 N1 d
  28. 5 ~& y% r1 y* I( @) a
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    . S& H; s8 k% E# T* X& q
  30. - c; Y; m6 x3 V9 ~
  31.      EventRecorderStart();
    ) E9 V# n1 o6 D& P, h7 e
  32. # F5 Z$ _! w3 t
  33.      bsp_Init();        /* 硬件初始化 */0 K! V  M- O0 F7 \, R

  34.   C* L9 W1 t3 t, z5 N3 E9 M
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */. X3 O8 a& g5 b! t
  36. 5 ]$ Z* Y; r& x) F/ D, x. ?
  37.      /* 测量中断周期 */# D- [! d( }' @2 q/ H! V
  38.      vEventRecorderTest();
    4 D8 H3 `% }6 R6 q: Q: r% p4 j
  39. 8 N+ R( f/ c0 S. z& M8 X( r7 i( D
  40.      /* 进入主程序循环体 */
    & z- ~  N! H& r  v: \4 I
  41.      while (1)
    ' A+ o# F. n( [3 U% T+ ?" B7 s4 ^
  42.      {$ D# Q2 |, X( [! ?( B% z
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    2 m; q$ |# [2 K3 h( T- {8 H

  44. 5 F1 k% K, C  N0 x/ X
  45.          /* 判断定时器超时时间 */
    / Z& u8 ^# _  C# i
  46.          if (bsp_CheckTimer(0))
    - p- O, d- t7 E8 p: U, h, w( |9 w/ G
  47.          {4 N4 z% M) b* Q7 _6 j
  48.               EventStartA(0);  1 u( H* G, w# ]- o1 H7 E
  49.               EventStopA(0);
    6 c. M, c# l8 S/ c0 ~9 _. R6 I, R
  50.               EventStartA(1);7 z1 R5 V8 g( x* B2 `1 k) D: m
  51.               bsp_DelayMS(5);
    4 c0 U# R. G; b! j$ q! E" X$ O
  52.               EventStopA(1);" `1 q$ }7 ]( ]: O% H/ V
  53.               EventStartA(2);' z8 h# P+ W- H% G. I% E
  54.               bsp_DelayMS(30);& p; R" m: h& `
  55.               EventStopA(2);$ g: M) B, R, m( d: @& d( ?

  56.   G0 [; x: m" }1 ]; w
  57.               t0++;
    ( ?- _8 N: Q4 I4 y) U3 R7 u
  58.               EventStartAv(3, t0, t0);
    5 F9 q  S9 y" |6 D) f# z
  59.               bsp_DelayMS(30);
    % k, v0 i& |$ ^# l1 k
  60.               EventStopAv(3, t0, t0);8 q% B8 b8 {  e4 {( d
  61.          }
    1 _3 ]! c1 ~) k& }2 o

  62. ' z" _* n5 Z2 K& O4 ], ~
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    % l' @# @* r5 M$ b# i! r1 c
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    4 V  ]! Q/ ^2 s' I0 G8 Z5 `5 S
  65.          if (ucKeyCode != KEY_NONE)
    5 i; i7 {7 X( H2 v% K! |; i: M0 i
  66.          {
    * e0 Q; o0 ~4 ]6 Z
  67.               switch (ucKeyCode)
      N/ y( y3 g. Z; k! E
  68.               {  h% \6 T9 M$ B
  69.                    case KEY_DOWN_K1:           /* K1键按下 */
    # j3 v, ]3 A+ }
  70.                        t1 += 1;
    ' u- ]$ M. n! [2 n4 w
  71.                        t2 += 2;
    * R  [9 @" i1 _0 c
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);& \7 D" e5 q4 I3 p. P: r
  73.                        t3 += 3;
    0 A3 D/ I( T( a. r7 Q; @: [
  74.                        t4 += 4;6 n9 A% W! C9 R) y5 ?
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
    : k  q7 `( z" u. O$ u/ ?4 O8 Z
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    + h- }; N# e' K+ N2 x+ h
  77.                        break;
    1 H3 o, g% j1 J
  78. 8 z; R  h9 Y+ Z7 w- X9 Z
  79.                    case KEY_DOWN_K2:           /* K2键按下 */, a& z# K; B  f, r: G3 I
  80.                        printf("K2按键按下\r\n");5 Q2 m& }1 A: E9 M3 i
  81.                        break;
    3 S. d2 k: x9 C; m( y' `2 N

  82. 7 Q" O  a* O/ [$ w7 E/ j
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    ; `  q1 e7 J* c7 @) a0 [0 s0 H
  84.                        printf("K3按键按下\r\n");
    & D' l6 P( d" k2 b9 V
  85.                        break;
    / s' h- n! R& j

  86. 9 W, }8 j+ L; r2 J
  87.                    default:+ r/ H6 O$ q6 K- I* k9 t
  88.                         /* 其它的键值不处理 */8 m4 ^8 a( B% z& e" P
  89.                        break;- _5 c- U7 A: K7 L- [. O
  90.               }
    7 i6 c/ ]# D* H! v
  91.          }0 m' f% o$ |" f5 Y! ?
  92.      }0 q/ M1 A% X% g9 i1 i
  93. }
复制代码


" _/ }+ j% q) E( o$ G

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

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

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

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

8.14 总结

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


& n( }1 n6 D: ~% H' e
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版