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

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


    1 S+ p; ?& b! a+ ]& P
    : A$ N) L1 v" X" r4 V

    - E8 h, a$ o; Y9 S- M) G# r
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接口区别


    8 w1 ~  Y8 \/ T8 ]

    * T# ?0 L9 C- p5 c, C& k& E

    5 K/ }$ ^8 S7 q8 N- D6 R  }

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

  •   词条 SWV(Serial Wire Viewer)


    ) D' U3 P* G4 [" y9 L; K  ]
    $ @9 x" d. E  P8 g% u9 Y* o

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


    : p, P! c8 X9 i; ^

    0 h# o4 k" g3 T6 I+ _
    , Y- n! M# G+ {- 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& R1 P5 X3 ~! x0 d- S" Y
    / o: `+ n* N* O5 d5 [" F

    / O4 r( ^  k* h4 S, B
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 {
    5 X. ^  Q; F; y
  2.   W7 b6 Q" @* S& O
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)
    5 `! v  u  J5 z2 [" Z4 ?0 I
  4. * y6 h% t; A) N/ @% w* V( [& i0 _8 \
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)
    . x+ W0 b! g$ ]
  6. , C; r/ ]) e. m
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB), v! Z2 k7 U' o5 T* Q% c
  8. $ u/ Z# V, r- I. Q0 m- B+ E+ j
  9.   uint32_t info;                // Record Information3 O3 T. d, j  C, m7 B2 W

  10. ( C9 {% ?+ s, a# I( t4 o
  11.                                 //  [ 7.. 0]: Message ID (8-bit)
    * K/ D2 }2 ~; A5 y7 |5 S# A4 t
  12. 8 ^) P: Q5 K; u$ k) q# S
  13.                                 //  [15.. 8]: Component ID (8-bit)
    ( ]' m# [+ X- a8 d

  14. * }0 h+ {3 ?8 f* S2 t3 n
  15.                                 //  [18..16]: Data Length (1..8) / Event Context8 F2 A( j, p  L* ]0 o6 S6 G

  16. - R/ H; A& `# k. r
  17.                                 //      [19]: IRQ Flag
    % j& u& Y; E9 e- o3 {
  18. - j4 d5 `  }4 b) Q
  19.                                 //  [23..20]: Sequence Number5 ?$ l8 D9 o; A0 t- x- a1 S6 @

  20. / k# K4 Z0 P3 p7 Q5 d1 F$ {
  21.                                 //      [24]: First Record
    ; Z0 d7 W+ Y$ d
  22. 7 e! t: c0 _; z4 I7 e
  23.                                 //      [25]: Last Record
    7 N- @% ^1 C4 l  q9 Y6 r

  24.   T6 J$ }  f4 p8 `# T
  25.                                 //      [26]: Locked Record' j. d  d* {3 y' b) h

  26. ! a; ]( I( n$ j' p+ c( t: a
  27.                                 //      [27]: Valid Record
    - K0 c; q% u/ y7 M3 C2 S
  28. ' J1 x! ]+ K8 \) {
  29.                                 //      [28]: Timestamp MSB! O6 E% V3 s/ D3 j8 i
  30. % t' \$ C& q8 [: S! o/ I: l
  31.                                 //      [29]: Value 1 MSB
    & M3 k. y7 F" j0 t* A* E
  32. / b. C' S# u% w, B% O6 I5 ?* M
  33.                                 //      [30]: Value 2 MSB; E/ D7 M! w/ J+ k3 }+ [  \

  34. 9 N, J  Y$ M" h% F
  35.                                 //      [31]: Toggle bit0 p# n% i( y2 c( C

  36. ! E- T2 X# d( S: r/ `' q
  37. } EventRecord_t;
复制代码


# u2 W" Q' Y( P! {/ T

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。: L, [- m2 A1 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(随着时间的推移,如果升级了新版本,直接使用新版即可)。

    # h+ z' ?3 o0 q& Y5 q/ f

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

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

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


    3 N) e' P0 ~- A- J

    / r% q; t" R+ O7 U% P) H( O

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */% ]3 ?: Q  v; L
  2. / v8 o1 E4 K8 J" \
  3. EventRecorderInitialize(EventRecordAll, 1U);
    : [, @: k4 G: P, @' c% Q3 {0 s, f

  4. + E! C! [) Q" A9 {8 R4 z! c  A
  5. EventRecorderStart();
复制代码

# I7 W- s* r. N1 n2 t' I( S# J

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


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


    0 D, D+ D( A! X$ @, O( O5 {9 a4 I6 O
    + G+ z4 p2 `9 T$ c0 b

    6 k( R) \/ R# k9 x* N

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

    5 E" m  F& N6 \" p: I  ]( c

    - u% ?( s: g" W

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

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

  • + P8 [3 q( b/ Y& w
    * q) ^3 ^7 c! Y* h- J
    /* 初始化EventRecorder并开启 */
    " h, F5 X1 P1 B+ L9 r
    ( b, e0 V% B6 q4 ]7 Y, c

  • 4 s. O2 c6 |* w
    ' e; U& i" c6 S& i+ F: W- d

    $ [9 d" n2 K3 ~3 J0 o8 x9 x8 F/ m

    0 Z4 \2 `: |" h2 v6 p1 W
  • 2 s& i( [2 W. w6 \' [- A% t

    4 ?% T& f; m8 Y; u. O
    EventRecorderInitialize(EventRecordAll, 1U);$ S& G# [- |9 [

    / I3 _. _; R/ x: F" j* f$ I! v' [0 W

  • , ?3 e8 I& I, L4 l0 @8 l$ L

    ( q% t! C' h5 W# c7 B' [* P& w

    % c( i/ p# Z9 N

    6 w: z6 }, m) b: b$ {

  • * z8 }1 P( z" b" H! w) B
    , g" Y; K& F- K& T7 x
    EventRecorderStart();% `4 M0 H' W" W) Q' ~6 ^! ~

    6 ^3 G, t" v. l; f- r$ a2 N2 X+ x; D2 a% \# a

    8 j" j- _, b" C/ h: w! T; D% r4 G3 c7 b

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

  • ( l1 |8 Y4 z8 Y) {+ v
    ( I8 V* o; o/ @5 b1 F
    EventStartA(0);' t' H/ j9 t% R& G( O3 V) L

    3 o1 }  I; x1 E

  • ; l% t* l( }% u6 n# d

      H4 L) a: e) E% x4 i' I  P

    " K, F* P9 U- X% q( ^2 c3 d% b
    , O1 F( m5 }2 }0 m. L) h
  • 9 w2 V: {- e+ r& s( {

    # j* I! e$ E4 r3 D
    //测量的代码部分
    4 A7 g  ?( A5 S( Y' [; Y. P

    / [& f' f2 R9 G7 a% S

  • $ d8 a$ e1 r9 h) |. Y
    , h) ~; p5 H' p" n5 t' a
    7 v& c7 ?8 `9 f' S
    2 Z) C, `! F4 }; X" K5 ^+ x

  • & D7 \! r' {- j6 u; r

    & H2 T9 Q* ]/ ]8 _
    EventStopA(0);3 K7 F( g* r8 z( ?+ F
    1 h# j8 h3 z, x# P8 q7 F8 V( o& u
    9 n6 S9 R5 }- ?
    6 B6 s$ w* j: `0 l5 h. y  z

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


    4 i) E9 J6 P1 U, R: W
    : t7 Y% V5 |9 r: I: v$ h) p" t

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

    % k* C2 j' }8 i0 m5 Q

    " j& g, ]7 E  j$ u0 d: \

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

    4 _" C9 A4 Q6 e" H: Y" o2 Z* W

    4 v, R$ y. ?3 o. n5 |- J, n1 L

8.9   JLINK配置说明

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

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

    : U7 n; {( ^$ j
    7 u: D1 \" \0 ?1 }4 R

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

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

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

    : V4 l% |* c) G5 D2 O4 M
    . t! g/ Q, @1 q# l. ?

8.10 STLINK配置说明

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

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


    . k( m( F. S& O! @) p! s* \! {
    2 }7 f8 |2 ?# X

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

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


    3 a$ l' Z/ x2 k( {) A

    : r9 A5 ^; L6 {' J/ y% `9 p" \

8.11 CMSIS-DAP配置说明

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

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

    4 W& H/ @$ h7 K% N$ H( V$ N

    - g4 n* r' M6 l" M' X

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

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

    2 W! q; L3 i# u* n: w5 ^" Q4 h
      e* L. U' n+ r7 m# j( ~% Z& @, B

8.12 ULINK配置说明

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

8.13 配套例子

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

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


    , J$ g0 @( T* K" E
    ( A  d+ ?7 ]' X) J

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

  1. #include "bsp.h"
    + s7 y+ ^) W, O4 Y* J

  2. & e* H8 b% ?7 a. n$ [
  3. #include "EventRecorder.h"
    . |: a% ^+ s9 |6 D. d; U9 C. M! Q1 f
  4. /* 定时器频率,500Hz */8 p( F, C5 U* ]
  5. #define  timerINTERRUPT_FREQUENCY    500
    6 p7 y- M. `* x! |6 I+ `0 y' K/ S
  6. /* 中断优先级 */
    8 {( o: [. J' ^/ T8 N8 N; l6 {5 ~
  7. #define  timerHIGHEST_PRIORITY       10
    - p. ]- k; p7 ]$ I7 c
  8. /*
    3 Y# L" ?* A! [3 \) Z7 N* c, X, ]
  9. *********************************************************************************************************
    0 r5 Q! o: N0 b; \: }. ~' R
  10. *    函 数 名: vEventRecorderTest
    - b  [7 }. o* F1 q% Z) W
  11. *    功能说明: 创建定时器% Z; _) g8 U. D1 \: M- A$ K
  12. *    形    参: 无
    ; E5 o! Q. X  [2 T, v* F
  13. *    返 回 值: 无
    ; z- i1 L* M1 z
  14. *********************************************************************************************************+ S$ H+ }3 }/ n
  15. */, m, m. I$ n8 ?, Q4 b* F
  16. void vEventRecorderTest(void)
      X9 G6 n* ?; X, U" K4 a4 k
  17. {
    ( P! N+ R4 D# _# n) B( X0 S
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
    8 S, j: F( M" t: c, W
  19.      EventStartB(0);   2 t( I( o% w* ^, n" C2 V( D
  20. }3 |- w8 q  b, \  _; P+ l# f& I! j
  21. ) k) n+ X2 A6 {4 [8 D0 O9 V
  22. /*1 \! y+ b0 Q) c
  23. *********************************************************************************************************
    ' w* i  O6 i8 ]1 |, Z
  24. *    函 数 名: TIM6_DAC_IRQHandler
      d( _7 }7 U9 w4 D
  25. *    功能说明: TIM6中断服务程序。
    , R5 S9 p4 D* ]2 k9 A, x! Q, X
  26. *    形    参: 无
    & E4 N% K% T# h5 |: C4 @
  27. *    返 回 值: 无8 R% B. Z* d# L+ r- I5 r% s
  28. *********************************************************************************************************2 s0 Q+ F) {; V# j1 m3 l
  29. */& W& g' i+ V, `
  30. void TIM6_DAC_IRQHandler( void )' a( M8 _4 d9 Q! D  V# W
  31. {5 S: }# t3 w/ W) C4 r! S
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)1 ?6 B9 V/ O  v- D/ f7 y
  33.      {' }+ n2 c( `0 d  G* @: N4 p
  34.          EventStopB(0);   
    3 b  t# ^- {+ u/ {5 T; _& X
  35.          EventStartB(0);   
    " y/ q' m' E: t. E) i. B) K/ w
  36.          /* 清除更新标志 */5 K9 y, X9 \. V' u% C
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;0 V! q3 u1 l2 q- q3 F
  38.      }
    7 S. U, g6 Q& K4 t7 j
  39. }
复制代码

1 H. L  C, |$ [4 r+ y

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */  |- r) l$ d  ]) ]% a# h- I  E
  2. #include "EventRecorder.h"; n9 W' P4 w7 t/ \

  3. / O& D) D  G0 v* U1 A9 V
  4. /*
    / @6 E6 i  w* d  [( W. q
  5. *********************************************************************************************************
    * b" e% Z8 w" H  R0 o5 C9 I
  6. *                                              函数和变量/ w7 S1 x$ F0 r
  7. *********************************************************************************************************
    ! ]9 M6 B! I" Q2 c: U. s$ \
  8. */( }+ n  c, r# N8 D5 f% U2 p% S1 Q* [
  9. extern void vEventRecorderTest(void);& v+ Q2 f) g$ B1 V
  10. uint8_t s_ucBuf[10] = "armfly";; H3 C9 S6 E( K0 t
  11. / t/ v) ]% v) n& Z: }. r
  12. /*1 T. P" R9 E9 i8 p% R2 O
  13. *********************************************************************************************************( \( X, s4 H+ t4 e5 W1 q
  14. *    函 数 名: main
    # o0 T) C" V# g" E7 D" G/ J; l
  15. *    功能说明: c程序入口
    2 u: O- h. ^) i
  16. *    形    参:无
    7 A' {  Y6 k* s
  17. *    返 回 值: 错误代码(无需处理)
    7 _$ u7 B/ B$ D6 ]+ Y
  18. *********************************************************************************************************; G* A& r6 L3 E) ?
  19. */
    . O# B. ^' j( P; f+ M
  20. 7 F$ d, m  A$ S) d8 ?
  21. int main(void)
    . v( t3 h- O$ ~( X1 S
  22. {
    3 N7 h8 s9 t# V/ Z
  23.      uint8_t ucKeyCode;     /* 按键代码 */* l/ W0 ]5 w2 W) o# V

  24. " y* K+ ]' J9 \8 R4 T
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
      ~% v) Q7 \% g! l1 |/ c8 J
  26. ( e2 H2 k4 U. ^  g; |' \
  27.      /* 初始化EventRecorder并开启 */; a* e6 u7 S6 E' |+ \2 q
  28. ( l) \+ t3 Z9 }) r
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    * u7 c; [+ @# \5 }
  30. . W5 R& U$ c+ z6 J4 p8 o
  31.      EventRecorderStart();3 C4 i% Y% Y, A  |2 l/ x* c$ j' }

  32. & F8 u. {( _9 s: _. m
  33.      bsp_Init();        /* 硬件初始化 */' P& b0 x6 i# B1 k' A
  34. 3 U- ~( t) J- R
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */+ H9 H$ H  \& R" K$ k- q
  36. 2 Y! \5 j9 M+ p
  37.      /* 测量中断周期 */4 u- `+ M/ @& x6 K
  38.      vEventRecorderTest();! T  Y& y& E( T" K0 o8 J' E  W
  39. $ b; m5 I6 Z- E$ G) \+ t! W4 V9 Q
  40.      /* 进入主程序循环体 */4 Z: x' H7 O2 f# N( h" x. s+ @- @
  41.      while (1)
    ! h+ P( {6 n9 M7 W
  42.      {" A1 z& Q1 B2 a2 g. h, Q
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    + n& @5 n: p& `( q7 v+ ?( `
  44. 1 h' A: \7 Y" P& E/ J. @/ y3 C5 S" S
  45.          /* 判断定时器超时时间 */) j" e& e& @$ j; m
  46.          if (bsp_CheckTimer(0))- n$ t0 n: j1 W1 X
  47.          {
    - j) N- @5 p8 F1 _( p4 ?5 I
  48.               EventStartA(0);  : G% f( O5 c" ^! {9 V
  49.               EventStopA(0);7 F/ b2 I" z4 K: M# _9 ~- P
  50.               EventStartA(1);
      C4 _( _. F* u2 }. f  V
  51.               bsp_DelayMS(5);1 @/ J8 X9 g5 X# `- N# w. F. A' t; e
  52.               EventStopA(1);5 H7 E" E2 N" U) I& k
  53.               EventStartA(2);7 z% P2 b  L4 ^( t8 L6 C* [
  54.               bsp_DelayMS(30);- a4 L  c3 F1 J  O* i* f
  55.               EventStopA(2);
    9 D/ |; l. @' ]! u: x

  56. 4 `, q0 B8 p0 |) S
  57.               t0++;$ ~7 }0 p, m; Z5 _1 u
  58.               EventStartAv(3, t0, t0);1 v8 @: l$ R3 r: m9 B3 |
  59.               bsp_DelayMS(30);
    6 Z* }; y: k$ Y) L
  60.               EventStopAv(3, t0, t0);
    + F4 q; I& q9 A) o, D
  61.          }
    - V: x- z. {* L  U

  62. % n5 _% v+ ?5 ?( w0 P
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */7 }9 z3 [: m+ }0 }
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    - ?/ S& {& v$ c, v9 W3 x
  65.          if (ucKeyCode != KEY_NONE)9 c* u. `- l" y1 q' o
  66.          {
    7 K5 E  G2 h7 k8 o' f7 E; r- y
  67.               switch (ucKeyCode)  ]5 n% U! p6 O
  68.               {
    " u* r  \+ S3 z3 k  M* |5 j1 F
  69.                    case KEY_DOWN_K1:           /* K1键按下 */
    4 N- u: t. t5 w; [& `
  70.                        t1 += 1;: D) }: U0 m- v
  71.                        t2 += 2;4 x+ m& Y$ a, j2 _; W
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);7 ], n( i& b) B
  73.                        t3 += 3;
    * W6 u  {% {$ H2 b6 M. K- `
  74.                        t4 += 4;9 C( g4 l; d& I, U0 ?8 b. `3 t
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);8 }/ R) V1 d- R) s& Z- `
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));" S9 n$ M! E( O0 N, a- b, U% e5 g
  77.                        break;) c, }5 A$ ^- G, g7 f- j5 d3 }, n

  78. : c9 v% _8 q% _  g9 h, g/ l: |
  79.                    case KEY_DOWN_K2:           /* K2键按下 */9 Z* y' f- P$ T1 H
  80.                        printf("K2按键按下\r\n");* t8 Y' J# Y4 h# v  |# X% j
  81.                        break;* x. t6 A8 b0 M3 f4 S
  82. 0 w! W  I' p- B, T' I" n
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    7 n1 r( f2 K# v$ l& k
  84.                        printf("K3按键按下\r\n");
    2 }$ }' x6 O2 J1 U& h% Y
  85.                        break;
    & g- F: |3 B' Y% L8 r
  86. " K, S) w# q3 \* ^) @
  87.                    default:* \5 f0 u4 t  b- z" R
  88.                         /* 其它的键值不处理 */9 {8 Y# e3 ?, Q  U9 |* D) q
  89.                        break;
    : C( K( Y# J* R$ M$ R, j% h5 b2 ?
  90.               }! o/ v1 L) Z+ s
  91.          }
    1 [8 q, j7 P( q
  92.      }
    ( y/ Q. ?' |) B6 W( j1 L
  93. }
复制代码


/ a% b8 R6 o0 z) L$ D' ]4 h

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

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

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

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

8.14 总结

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


5 }* h/ Q2 Z5 G3 h8 Y5 \+ v1 N
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

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