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

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

    6 a8 p, O7 J4 F7 Q) ]& h4 @

    5 P$ _; m* p' |& m0 H8 ]7 Q
    9 y0 `  X2 r  j# n) H$ h8 _) D! d
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接口区别


    2 E( ~  r# @7 {: G' `. k' D' c* n
    + P# o$ B6 J* b1 l/ c% E' L5 \
    + q/ G0 s/ g) h

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

  •   词条 SWV(Serial Wire Viewer)

    4 V- [( v3 ?& B# T

    / C* a% [" o' u; }' S7 {

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

    ! p1 h  V+ d+ m0 `

    , i$ B: n+ [' N3 F; z
    % L& c# F0 L% J1 r" V& x
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调试。


    3 z7 Y( g1 j" j% N! h3 s' a9 I

    ( N  p/ o$ y8 y& Y5 K4 w8 b1 p
    0 J) d3 r9 m! i+ z' g) ^2 G' L0 _
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 {" {6 N$ B; F$ Z! S+ P0 ^2 G& T5 W

  2. 6 |/ J/ a0 s8 t8 f* U
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)# |' ~, |2 P9 g+ o5 ^3 W" H

  4. ) v/ v; V  U6 ~4 L6 Q
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)1 P! c1 L9 I" a  H

  6. / t+ W# b" s9 ]$ X4 S; w
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)' z" N% j0 q$ O) D; R
  8. ) n1 B) }- ]# a: v
  9.   uint32_t info;                // Record Information% ^: d& x( Q$ Q" \& [* t8 M1 C6 `
  10. 0 {, @! ?7 P/ q1 \$ |# ?8 q0 f
  11.                                 //  [ 7.. 0]: Message ID (8-bit)
    ! U* N" l+ S& E$ @- m
  12. 2 n1 [5 C% k* K+ }& Y) F
  13.                                 //  [15.. 8]: Component ID (8-bit)  i7 q9 r" r, Q: o

  14. " A  H3 y; h- O4 h
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    ! x5 e, ^0 P! s/ c; d: E" p

  16. ( P% }2 @- B/ ~8 L
  17.                                 //      [19]: IRQ Flag1 c* p7 n# e! B5 E" J  p* k' N$ {

  18. 1 x$ c6 N) z+ \- X; p1 @
  19.                                 //  [23..20]: Sequence Number
    4 r( S, h( Y# j

  20. 8 H) i: A3 h; g, o* s2 O0 ?
  21.                                 //      [24]: First Record* e$ n, T. Z9 x1 o0 M

  22. 2 b7 l' t9 P' P5 ^
  23.                                 //      [25]: Last Record, y2 g( N) f+ Y0 Y$ ^
  24. $ D8 y" B6 R' d; ?6 l
  25.                                 //      [26]: Locked Record
    2 A$ n1 I+ |4 h0 D' X: X& f( G. x
  26. " ]/ x3 k' z- _( q
  27.                                 //      [27]: Valid Record$ X; ]  C. |$ x1 E% {  g2 B
  28. - p1 K* m& ^8 ]' K6 ^; R
  29.                                 //      [28]: Timestamp MSB
    7 q) E, a" ]: B; u4 a
  30. 9 ]% j. J2 q4 o6 D+ E/ H
  31.                                 //      [29]: Value 1 MSB0 X& Z) h1 ]& A7 X* k! W4 B

  32. 6 t; k* A* q1 K% P  o. E
  33.                                 //      [30]: Value 2 MSB& j1 l* |5 [2 s
  34. * C& ~. U% C7 v6 P
  35.                                 //      [31]: Toggle bit
    : q# z) d+ U/ F: B% }
  36. + o' T6 }) a% |
  37. } EventRecord_t;
复制代码


7 o# D% U2 [( ^: Y9 K2 z0 w5 k

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。7 E, [( W0 d; k

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

    8 a, F/ ~! b2 b) n1 {& w

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

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

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

    7 W; N$ c' O% w* N$ u4 w/ M
    / H5 j8 Z2 n" I+ I. B

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 *// h+ u1 o5 Q4 L" a0 b2 w; b

  2. ) P% g$ m* ?: b
  3. EventRecorderInitialize(EventRecordAll, 1U);8 q; S! G3 j5 J$ K' C

  4. 9 B) r' `& z  G; f5 @( o! Z5 W7 e
  5. EventRecorderStart();
复制代码


' x3 T, b- l) z! s

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


5 b* d% j% c, ]$ p: G, A" `9 ?8.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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    4 M7 |2 E% x& q/ v- r
    4 N, I  A8 |: t2 ?' V

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


    7 F8 ]. B  E9 I4 T. r9 q1 v
    9 I: E# A6 j* A( T

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

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

  • 6 x8 Z9 s( x( P2 T' x- f+ y

    ' q, f& s% Z9 D# G9 A7 G5 y  h
    /* 初始化EventRecorder并开启 */
    2 L2 }( i# ?4 p' Q" w

    - ^/ l3 o# D2 }$ \2 X# u9 R. S
  • , b' A! ]' r+ y6 j% I

    ) a0 ?  A# Q& _: _6 y: ^# p
    / {3 F) y/ T6 V

    / y" y5 l. [! K7 F1 k0 |

  • " r# x* Z' \. U" Y0 ~
    ! V! s: C8 z3 x! d3 W( D
    EventRecorderInitialize(EventRecordAll, 1U);0 X2 _- r" k3 U# q! i+ B9 ?
    : j; g* J7 m/ C0 R; H
  • / [; a0 l1 V0 b6 R( k" i9 d. B4 b
    5 U. C% A; y) g* Q
    " }/ d- x  u+ _

    * t! ]" e+ z  `8 S. e

  • 8 z! M! n* ^! N% K7 G
    9 N/ `" D. k. X$ Y
    EventRecorderStart();8 G6 o, @6 }3 V, I
    2 z1 h9 W4 g6 g5 w
    ; [0 J& n) V* `" b# M
    & k6 n5 R7 r% t9 I& Y

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


  • ! L" S3 G1 _5 b5 g' z) O# c
    ; Z# W9 ^1 ^4 R9 X; t1 h
    EventStartA(0);* a( {8 S0 q9 V) }2 S
    % v) s% B+ M/ {% X  P  Y

  • 7 p% k$ s( }$ \/ @
    9 y+ T* X: {- @; n6 q! M
    6 h$ t( \# N: X6 H% t# X; y

    . G$ ?  _7 p* F7 k
  • # ]9 G2 k6 d  W7 ^7 n) {" N: G, t

    ; S( h6 D% Q7 Q" g6 Q5 B1 Y; t4 l+ R
    //测量的代码部分
    * B: D9 P; f! V0 X+ e# ?* `

    8 s# a8 D; `9 ?8 x) F

  •   H4 ?& G$ f# {3 \4 m/ G- D

    # h; M8 v6 q1 A, V2 W% L$ Z6 \' W

      j6 J2 }# m! S' {+ a/ k# ~3 _" B. w
    ; o. V% I/ I" L. n5 ~

  • 4 a9 T. _2 z: T+ Q7 w6 @4 N; m

    . `; t: E) f2 g: N- q; n1 v" O
    EventStopA(0);
    , R- F" H9 @& u7 L) B, r8 F2 g
    ; C3 C3 H, J+ S  D
    . Q: T& ]8 D$ g9 R6 Z' V4 ^- P9 }
    4 U$ i3 c' }4 J. H% c

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


    3 ]7 z3 |7 }9 h# s' j7 G& K& Q
    % x! c. l: y/ [- `' D

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


    % g  a' H5 U6 G2 ]" n2 T" p: Q
    . Y; T9 j1 _+ o) o9 h

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

    ) q0 p- A' Y9 U" m6 }/ q$ @6 c
    2 W! d. U7 m8 A9 N0 Z

8.9   JLINK配置说明

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

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

    ! R" ^9 u8 _1 p( Z
    2 Q' K& z. I1 `$ `5 P- @

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

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

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

    % h( y. U! S0 n5 t$ g, _1 t' w

    3 o) \, F5 |8 i) H7 x9 a# ~! p1 \

8.10 STLINK配置说明

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

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

    5 Q# U0 p+ E+ v: u
    + ^# c' C  y& q; V

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

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

    ( ?7 [; [# Q; q# h) C. P( f9 {: ?
    ; B0 [: s( k4 `/ S2 T

8.11 CMSIS-DAP配置说明

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

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


    , i, y6 \$ {  d0 Q4 r) _

    # N- u+ H6 l5 t. b  j

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

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

    # Q% R8 y  F* I& q/ p; {

    6 {: q4 ^+ A7 J. T

8.12 ULINK配置说明

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

8.13 配套例子

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

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

    2 ~5 U2 [" g, s6 P+ y- ^6 z
    8 ~( l3 H. U( p* m. @5 c; d* j

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

  1. #include "bsp.h"4 x* ?/ A3 b! l! @# I' P

  2. * o  j  w% n* Q, X: x: a
  3. #include "EventRecorder.h"7 l) u: K5 j6 e3 |7 A$ B: ?
  4. /* 定时器频率,500Hz */
    7 K7 u* h1 P8 }' o9 B) A
  5. #define  timerINTERRUPT_FREQUENCY    5003 {% Q$ b" h) p- V, j" s
  6. /* 中断优先级 */
    ) J( W0 Z4 o1 I1 Z
  7. #define  timerHIGHEST_PRIORITY       10! Y* o! \' J- l  R. G
  8. /*8 ~, {# z8 g) ~5 Z
  9. *********************************************************************************************************8 {9 P& E( Y% [) I
  10. *    函 数 名: vEventRecorderTest
    ' }/ ]: I& l# ?3 Z# E8 G7 M* Q  @
  11. *    功能说明: 创建定时器: |7 \' i! |- t& f( X6 n( `4 S
  12. *    形    参: 无
    + T8 g' ~$ x, P3 G3 T& {9 j! P: O) H
  13. *    返 回 值: 无6 R- X4 W) k7 s9 ~
  14. *********************************************************************************************************
    4 y' ?4 f$ k" W  d$ S" B) V
  15. */
    + H9 q4 a9 I1 I( {! j" _  ^
  16. void vEventRecorderTest(void)
    5 Z" X6 Q1 f( r, S
  17. {  R; }% Q6 ~3 C! p+ O+ u: N
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
    0 [4 M  w# u- N8 g. R% x
  19.      EventStartB(0);   $ w0 e0 R+ d% v
  20. }  ^( Q2 _3 B1 d

  21. % X; b1 }3 ^6 W; o
  22. /*
    ' X/ {4 o3 e! w7 H
  23. *********************************************************************************************************1 X- y+ {7 J2 r! u: M6 H. X
  24. *    函 数 名: TIM6_DAC_IRQHandler
    5 S/ Y% d/ o, ~. _1 y0 F" p
  25. *    功能说明: TIM6中断服务程序。
    7 W4 R( _! a* T
  26. *    形    参: 无8 ~6 t* b! E# B/ s2 V
  27. *    返 回 值: 无9 U6 b4 I( V# |- T! \2 I
  28. *********************************************************************************************************# r2 `$ r! f4 V  E
  29. */- A) A) _# E( g7 F& @
  30. void TIM6_DAC_IRQHandler( void )5 _  w+ [6 j% y2 F( `+ l! O
  31. {
    , q  L  P3 A  d7 n
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)+ s9 x  |  z: C& O
  33.      {% i2 S8 y* P7 t( N
  34.          EventStopB(0);      b) N+ d, v/ k
  35.          EventStartB(0);   
    ( W+ r; w) k% Q8 R" ?, l
  36.          /* 清除更新标志 */
    0 _( ^. E  y* X: R# V: |7 q. w
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;; W' l- w2 M) c* w
  38.      }0 H2 h' w! {1 c
  39. }
复制代码
6 l# _' Z% T" \4 C+ |# `" l& F2 ~

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    : c1 L: d& b" H' G9 f
  2. #include "EventRecorder.h"
    : h' r- H& Y/ R+ J9 y
  3. 6 p  ]2 T" }5 L' W3 b( `0 s; t* g
  4. /*
    3 q( F4 x5 Y5 t# r, `
  5. *********************************************************************************************************
    ) x- R/ M+ H8 A2 c' G" \
  6. *                                              函数和变量  E: ~" L$ _2 m; K( u; E
  7. *********************************************************************************************************: i% C. s- V3 P. |
  8. */
    & R8 a- ~  m/ \* @1 T1 s! |
  9. extern void vEventRecorderTest(void);
    5 T7 u) s1 ?! ?8 K  G
  10. uint8_t s_ucBuf[10] = "armfly";
    ( e8 ?+ [8 Y/ w* h/ C8 `

  11. 4 u, c: ~+ x1 Z9 G; J
  12. /*. z6 @7 ?, w7 y" g( x
  13. *********************************************************************************************************
    $ Q9 I. [" X' G* J( U7 y
  14. *    函 数 名: main
    + H) i$ \* K, ~$ D" \
  15. *    功能说明: c程序入口
    1 T5 T% R3 m4 @3 V, a& K
  16. *    形    参:无4 Z  A* R! B, x) G2 \$ W' S. N
  17. *    返 回 值: 错误代码(无需处理): x6 V# w7 d' M( N% r
  18. *********************************************************************************************************
    + F$ O( A: K( v/ b
  19. */
    2 J! [4 i4 _% L! l

  20. . t! b5 x1 S' ]) M2 q
  21. int main(void)) N$ `6 H; R3 \9 c0 P% `
  22. {
    7 Z' V6 K3 E1 q% a4 `( N, R/ |* H
  23.      uint8_t ucKeyCode;     /* 按键代码 */7 \" [' L% P( ]) S$ V; R
  24. ! N7 ]: t  j  P) T
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
    $ r/ I  X. T. ?& W

  26. 1 L1 Y) C4 d# {' V) G) i
  27.      /* 初始化EventRecorder并开启 */
    ; @8 o! d* n" e, ?( I: B

  28. 7 z) }, o# J8 ^. T
  29.      EventRecorderInitialize(EventRecordAll, 1U);% C& {7 Z' o% [- s7 A& b# _9 O

  30. ( U' x0 ]# }' [9 A' _+ Z8 x
  31.      EventRecorderStart();
    1 h  `) X+ N/ a; i' I

  32. 0 b/ m! Y+ j# v* {
  33.      bsp_Init();        /* 硬件初始化 */% |' {' e# f1 U& p9 s
  34. 7 b! x( z+ g( \* u& F1 m+ |
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 *// w1 ]" V3 h. |

  36. $ u, ?! p+ z5 C
  37.      /* 测量中断周期 */  n9 ^. L& e, o5 r( {4 R& m
  38.      vEventRecorderTest();# J  E0 D# g( c: t) O

  39. & c: C" ~  M3 T& E& \1 x
  40.      /* 进入主程序循环体 */
    ( o0 X+ Q8 [5 k9 `. f, X: M' B
  41.      while (1)8 [; n4 G8 E  n" l1 @$ @, |6 r$ a
  42.      {2 g6 t8 y4 Z/ {, C! U, G) e
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */; }/ U: L/ I4 i) I* }+ z
  44. 9 j! p6 u+ ?* x* T
  45.          /* 判断定时器超时时间 */
    0 |7 {& A; ?- J; n' {* p
  46.          if (bsp_CheckTimer(0))# g! y3 V6 I/ [) i0 }+ i
  47.          {
    1 _! Y; q& s9 [5 c( W
  48.               EventStartA(0);  0 b; J4 D9 d1 @) O: B! m; p  ]
  49.               EventStopA(0);& d$ u$ [, ]; q1 D: ]) [* i2 r. ~* O
  50.               EventStartA(1);0 J5 b. S; i3 I) J. D9 \; k9 w
  51.               bsp_DelayMS(5);+ B4 J1 @5 N/ s" W4 d, L8 y
  52.               EventStopA(1);  I# f+ x+ z! _
  53.               EventStartA(2);: D' A% U  x1 C" b
  54.               bsp_DelayMS(30);: T) Q1 b8 @' A* a  |/ Q$ J1 Q
  55.               EventStopA(2);7 N4 _( g; H3 V- m; M

  56. 3 v* X& ^( G7 V) f
  57.               t0++;
    5 o! }5 F. m( r2 `/ `
  58.               EventStartAv(3, t0, t0);
      B: k2 B. K( A  J, w8 o, z
  59.               bsp_DelayMS(30);
    * m! [6 o- p; @1 y3 ]
  60.               EventStopAv(3, t0, t0);6 G) P' f) y4 _2 p1 Q: K
  61.          }
    9 N0 y. \7 m5 P# f! N% Q8 y9 i
  62. # ~. Q- `3 Z: h" E$ r( i: e1 L
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */  d: Q+ p5 ?/ N) x/ S
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */4 `5 p0 W6 M5 u/ A  O# E0 \& y
  65.          if (ucKeyCode != KEY_NONE)
    . m- {) M# N; J* \) r+ K4 B
  66.          {7 ^; {9 A. ~9 U, g. `
  67.               switch (ucKeyCode), z1 x1 |& T9 Q4 M  A
  68.               {8 M. Z& `+ s7 [1 W5 s/ b* a2 S
  69.                    case KEY_DOWN_K1:           /* K1键按下 */* Z# Z8 j/ ]$ \" l) [6 |
  70.                        t1 += 1;
    1 W2 q( I0 [  K3 o2 }, z
  71.                        t2 += 2;
    9 l8 _8 o% B  U9 B6 O9 B5 k
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);2 q& H9 U+ [* N" Z
  73.                        t3 += 3;& O, M, I$ k* i0 Z; }3 G: ~
  74.                        t4 += 4;: X. g+ ^* g; u: M
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);% j  E. P9 g+ R6 s- o
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));9 Q* {# u9 z; i$ l& ?
  77.                        break;3 k+ ^' |- P/ t# X$ E& S
  78. 7 J3 q# c5 m" V9 F9 Z) }
  79.                    case KEY_DOWN_K2:           /* K2键按下 */
    8 W+ O# ?! p: _! A: F/ }7 x) H
  80.                        printf("K2按键按下\r\n");0 d# n( Q: ]' C' ?1 A" U  A
  81.                        break;
    & I: ^5 [# F& ?( v; u$ D. H( `8 m' p

  82. 7 x; b3 }9 ~0 Q3 k! D4 D* H
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    ; F8 M, b2 r/ E& E  N
  84.                        printf("K3按键按下\r\n");
      f8 }- W: g6 T& L" @: v" B
  85.                        break;$ F  n$ h: p2 F+ f4 d4 z1 H
  86. ( Z& L# i( A2 T. t2 o/ x3 R- E9 S
  87.                    default:0 g5 a7 \" ~) s2 h% a
  88.                         /* 其它的键值不处理 */) @$ r/ x7 ^: o" K: U  n$ a5 g
  89.                        break;; }; R3 ~5 C3 \( M% J0 B) m0 S
  90.               }
    3 t/ X" m1 `  E' K6 ^
  91.          }
    ( ~5 q3 G) d/ O7 x4 d$ v- a
  92.      }
    1 i$ Y  U" g1 E# t9 v0 L+ a
  93. }
复制代码


2 Z; m# R0 B; D4 @) \' R

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

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

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

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

8.14 总结

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

( c, _" h$ I  G* ]# [) N
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版