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

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


    , M$ C- U2 h. V$ n0 w

    " x0 M, E( g3 V
    + i& p' L6 S- d/ t/ v  U# z; S# a% e
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接口区别


    % a' u6 S1 T0 N  n( ?, Z% m7 A9 R
    ) M! O( g- c: Y  r

    5 F- `+ G' M2 `' V; Y2 D

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

  •   词条 SWV(Serial Wire Viewer)

    ) U+ z3 \9 x1 a
    3 r3 P0 m) u; `' K/ `4 W

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

    7 A$ ^# G- n2 j% R7 E$ h2 z
    ; B( j: [) \. o$ X
    " W4 j! q+ f9 N( [1 S  L
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调试。


    7 X9 u5 Y1 d+ ?# }/ h: ]

    3 j( {1 \; S3 S" ~$ r; G2 r

    $ {  ]! u; f; `; e; c6 T1 h' O
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 {; Y- p' o* o. v2 a
  2. 1 N( M, E! U) A5 X
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)2 u4 S  L  U! V) }4 a  H. H

  4. 2 k, {( I+ w0 q
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)2 ~2 a) V" G1 }; _3 y

  6. * G2 J4 r/ M5 P
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)
    - t, Z' Y$ P1 H$ @; U
  8. * T( u, m" ^* c- i. O# |
  9.   uint32_t info;                // Record Information( N& p9 N- {% i/ `/ e
  10. # E+ x( d% m  [8 I& F- v, m8 X3 N- N
  11.                                 //  [ 7.. 0]: Message ID (8-bit)
    % v; n) n4 X8 ^! a0 k( u

  12. 7 M& D; W1 {- L: a; w
  13.                                 //  [15.. 8]: Component ID (8-bit)( K( G" d% j& A) \! F; y, m
  14. / K) G/ X# c; ?/ c1 X, ?/ f$ @
  15.                                 //  [18..16]: Data Length (1..8) / Event Context4 c5 ]  G8 b5 ]' P
  16. 1 z* u1 ?2 p% C& n) ?
  17.                                 //      [19]: IRQ Flag
    ' G  ~  B: Z" i. v& N* W
  18. ) ^- N- W) y3 `7 A* y" ?- P" v
  19.                                 //  [23..20]: Sequence Number- s! w$ }, V; x

  20. : _% O5 a3 U: J7 ]4 B! ^
  21.                                 //      [24]: First Record2 T0 {8 r. R( T& j3 U

  22. 7 {* c" B) D* X9 j9 Z$ Y
  23.                                 //      [25]: Last Record0 F8 m4 ]4 J/ \* N  ~
  24. " ^' h- b# a* V9 }1 H+ F" Y
  25.                                 //      [26]: Locked Record2 u  G0 j. A$ t4 ]/ Z& F+ `, W# p
  26. : _# P8 k1 b# K# U& I
  27.                                 //      [27]: Valid Record
    8 V' ?4 E+ A: D- L# ]( n6 i( F: c
  28. 6 R% }  \2 Y6 Q* \% ?( m
  29.                                 //      [28]: Timestamp MSB
    2 h$ Y. L+ ?. l: T

  30. - O8 a( L' |& O9 C! z+ @
  31.                                 //      [29]: Value 1 MSB5 ~3 ]$ W. R8 S* n9 x, |8 o* k2 }
  32. ! o7 i- m# P, H/ \4 A
  33.                                 //      [30]: Value 2 MSB# q- S; c8 l* a, s3 z0 L9 u
  34. . ^/ q9 {: T2 F: Q9 }3 H+ _
  35.                                 //      [31]: Toggle bit& a/ _! H4 d* V5 K& O6 z

  36. 4 Z! N; u4 g/ ^" ?
  37. } EventRecord_t;
复制代码


, e' R+ O$ K: e5 N) |  E+ p9 b! f+ {

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。- l4 x! B9 x8 D3 B

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

    7 j$ _0 J, E- ?1 ~

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

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

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

    ' |1 D1 O+ f( }' E" t' N, s, C* Q) O, t

    5 Y* a9 \, q5 |5 ^

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */
    5 A& P2 b8 p# o3 X
  2. 9 A' t, M! e: K( O
  3. EventRecorderInitialize(EventRecordAll, 1U);
    . C! P: g8 M1 X) J9 z0 L* F- [8 _5 [
  4. 2 G  J: `5 q( x, ]1 H. k7 a: ]
  5. EventRecorderStart();
复制代码


- R  V5 l! E9 T3 e1 ^1 d

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


4 V) }& d  t. P% s8.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 m9 F8 R& J) j, M. |* O

    1 @0 Q8 p6 ?* Y

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

      \3 |) C( P. m5 z  k- ~  b' N3 e8 V! D

    , [$ h9 r$ k9 f) p( F8 I

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

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


  • 7 O0 \( l9 F3 |' r

    * ~! B% x, g( i+ {: H( A5 b
    /* 初始化EventRecorder并开启 */
    , M. x2 D, E' q4 T0 O

    & h5 I: B; S9 m+ @, |4 p/ m
  • / q2 P, o8 m% u. x5 P: M" P
    ' Q% V1 z) I4 l

    8 |0 O! M! d1 f1 m
    + C; y. D- ^% R
  • ' p0 S+ O1 ?' I, P

    + E# h  F' p2 H3 L8 c
    EventRecorderInitialize(EventRecordAll, 1U);
    . Y+ ^8 a4 \# C( ]5 C2 G9 i

    + N# j) [# n( a! Q
  •   i+ m  k6 q8 y$ x* y+ W. e

    ; `$ s1 C/ S+ y* ?% {
    ! m; Y$ X3 G. y8 s
    ; O9 }# |0 l& R' d! l
  • - a; M1 }7 U. f1 \! v. |$ c
    6 i' V& p3 G3 q; ~8 X- K
    EventRecorderStart();
    - ?& G: q- D! c! Q9 G

    " o6 t7 ?: q- d, ?( x7 [  o6 }
    8 a! z" S% ~. ~. Y

    % d$ x3 |8 t6 v3 U1 o; x

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


  •   R5 D1 p0 n6 Q
    ) Z8 E1 }) f4 t6 `  N) t, Y! M: U
    EventStartA(0);
    ) h9 a; y# S  Z% O, q

    1 B8 r. A1 M* B/ T7 z

  • 1 b  H$ P# K* G+ z4 j
    ) a) ?# A$ o$ V- c# N9 b" x

    : o: v, ]! r# c9 ~4 O0 q! R
    " L( ?" U, }5 n4 `; U& _6 m2 u

  • : j9 w" m, L* `; ?4 B' p5 W

    ( S" [* `1 V! t* F) a0 q" r
    //测量的代码部分1 G: B. h. [3 B+ C5 G
    , z. K5 R2 T0 ~" u) n/ A

  • ( }- n6 T7 ]* y! o1 l

    . X  m& n9 {/ e) o3 S

    / P5 k: ?7 [3 n3 ^
    9 T6 V; {8 {5 p+ p8 z, i

  • / h' L: t( q; V0 ~) X3 O# i! E8 T. q
    / L' |$ ~: n2 I; O
    EventStopA(0);4 U) T+ w, K  z. J+ d

    6 V( U6 M) Z9 a6 O& A) K6 Y1 p4 d3 X0 k; w4 f9 J; N0 g

    & E6 {- u/ q9 e) b2 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的效果:


    2 ]1 ]. Z7 V% ^; p7 U8 |5 j/ t

    5 g4 x) \$ C9 F9 u

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

    - W4 A5 Q( v; \! y/ N* _. j) ~6 Q' @

      a  ^+ o" g9 f% t

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


    / W3 R: j  _% {! H  x8 `+ {

    ) T& B6 K* d5 r3 p

8.9   JLINK配置说明

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

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

      q) l" _8 u9 }* e! {* q; z
    ; k7 Z& G" B, ]6 s: g

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

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

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


    ( I6 t, J) j- Y) V( z
    + X- H% m  h* @7 C

8.10 STLINK配置说明

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

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

    : W5 o/ _( A, A$ T8 L; u
    0 p9 y0 L) R0 R- f6 V

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

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

    / {& L. d- d5 U4 I0 v& `

    & }& I2 y1 U  [* J8 }# F* ]4 w

8.11 CMSIS-DAP配置说明

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

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


    ) T' b& \! J, }; l3 O

    ( m7 a: O* r  J0 L

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

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

    $ m, l: t* {& F' c
    . S6 z2 P8 F* c( a' q" y& n) i

8.12 ULINK配置说明

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

8.13 配套例子

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

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

    $ ?; v9 _7 \" f+ h. R
    8 o4 h& E! _: o! M& h. @$ w

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

  1. #include "bsp.h"
    & |4 S. G) L% W$ a% `$ x# s

  2. / ^! _( m/ G. N7 D; R" h, l
  3. #include "EventRecorder.h"
      q; ^5 F7 B* X" r$ T5 f
  4. /* 定时器频率,500Hz */; r' L9 n2 s" A$ a  v3 N2 n% A: K
  5. #define  timerINTERRUPT_FREQUENCY    500
    : D, ~; t$ a' Y
  6. /* 中断优先级 */8 W* z5 u9 A% p8 s
  7. #define  timerHIGHEST_PRIORITY       10
    ; N( F7 ~5 H  q) \  J- k. h, b
  8. /*; E/ J) m* m/ h7 m
  9. *********************************************************************************************************
    # q0 N1 m9 \0 l
  10. *    函 数 名: vEventRecorderTest& N9 A1 |: w! i$ M! z0 e( F5 q" T
  11. *    功能说明: 创建定时器
    8 i7 e* x3 n6 j8 U' O2 a: p8 M8 W
  12. *    形    参: 无
    7 V5 @4 \! y1 s7 @* q2 w
  13. *    返 回 值: 无+ z# R' c# e4 H8 H9 b, X8 A
  14. *********************************************************************************************************
    % m, M0 n- g/ N- B
  15. */
    4 A3 Z: L! W% r5 y
  16. void vEventRecorderTest(void)
    ! `: w+ [  T) y9 H- M. L, `
  17. {
    ' O! Z0 G( H' d  J! r0 [
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);+ s3 d# J2 c5 T+ r* o2 U: |9 ?
  19.      EventStartB(0);   
    7 u* q+ f+ j4 H6 v' r! ^
  20. }4 d) E4 v. J. S6 A( `% L0 o

  21. " {9 e8 \6 T3 a+ D0 f4 c8 L& t
  22. /*8 ]' l* V4 K# L/ k" Q% G- W! o
  23. *********************************************************************************************************
    0 q+ q8 x- L* S6 [( _9 D
  24. *    函 数 名: TIM6_DAC_IRQHandler
    - ?1 ^" f4 ~. O! i9 Z
  25. *    功能说明: TIM6中断服务程序。
    : l! J5 z' `" m8 U' j* w
  26. *    形    参: 无
    / q9 P3 l% X, ]4 C
  27. *    返 回 值: 无$ H% ?# @/ S% o* S9 \# g0 N! X
  28. *********************************************************************************************************8 q, j3 T* w5 W2 I% M, H! r9 O
  29. */
    3 u" h( O% O2 j7 o: E5 f7 l
  30. void TIM6_DAC_IRQHandler( void )
    # Z7 a8 o. Y) u
  31. {
    6 h( h. a4 e6 p8 h8 v
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)0 n/ X0 Y$ I, }' ?3 I
  33.      {# Y9 z. r5 N0 z4 u8 {
  34.          EventStopB(0);    , i* H, I; [0 W( i# \
  35.          EventStartB(0);   
    % Q9 a' f: `: X* i
  36.          /* 清除更新标志 */
    ' Q- U: O+ E0 g! @
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;
    7 n! ?5 m2 x! Z/ r' C
  38.      }
    $ D  q8 }- _! [. d4 z* f# ]
  39. }
复制代码
4 |- b6 ]3 J* n4 B% D' \9 ]

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    2 q- E. P  y' G3 E" o4 ^
  2. #include "EventRecorder.h"
    1 |7 b6 T% y  m

  3. 3 `% P! t" m" G& E" N2 B/ C
  4. /*' g6 y5 L% j( d$ O
  5. *********************************************************************************************************1 {6 q4 k( U! A5 R' \* E
  6. *                                              函数和变量
    * I! X1 }* m4 ?
  7. *********************************************************************************************************
    $ ~- w) H3 }+ U1 [* o
  8. */
    5 k3 N8 u! p3 k- {
  9. extern void vEventRecorderTest(void);
    # U+ `2 P8 ~; w5 }
  10. uint8_t s_ucBuf[10] = "armfly";$ G5 U2 `( D$ V6 F+ O
  11. : _6 v! i- O) _
  12. /*
    + E% C  w" }$ O: O* v
  13. *********************************************************************************************************" m- w: S; }" |9 |8 s
  14. *    函 数 名: main
    8 D- C+ v+ q) I$ ]" P' |
  15. *    功能说明: c程序入口$ ^& Z! G. b4 a, g/ _  Y
  16. *    形    参:无' I2 p1 J8 d7 q5 U& D9 p+ V/ Z
  17. *    返 回 值: 错误代码(无需处理)$ b" i0 G! ^. m# m
  18. ********************************************************************************************************** A; A' c; [) u2 B7 U
  19. */: m% ~0 E; z3 ]# H8 t! p
  20. + P, [% y; X6 Y/ q
  21. int main(void)# L. _3 l% [8 n9 h/ b$ }
  22. {9 d, }5 u0 M# {6 M1 H0 z
  23.      uint8_t ucKeyCode;     /* 按键代码 */% s8 L& M. h0 G

  24. 5 g! H* m8 g) A0 `+ J
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;- ]; W! }9 i+ p/ e. S( U* e' Q6 g
  26. : X3 j0 ^& I: A1 I1 b/ x) L
  27.      /* 初始化EventRecorder并开启 */
    , u' U2 U. x$ r- h6 W. g$ E$ R$ f' ]% {
  28. / L9 g7 R4 y  z% N
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    + D2 ^# O+ z5 w2 D) N
  30. ' `( f) g: ]  B5 d4 Y
  31.      EventRecorderStart();
    1 b0 Z, R0 A3 B

  32. 2 L2 N! C* T- J+ @( Z& Q% V
  33.      bsp_Init();        /* 硬件初始化 */
    ( }* m+ l/ D5 D8 }

  34. 0 J, ~0 K5 {, B. U/ _1 E
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */% `* S4 O  y, N- D, p0 k

  36. : h2 b1 Z1 c) c( ]# p( L0 \! W
  37.      /* 测量中断周期 */2 W& U; }/ l2 Y2 G- a
  38.      vEventRecorderTest();
    . r  R2 {* e" J9 ?3 t

  39. 3 v3 [8 D, ?. [. ^1 S
  40.      /* 进入主程序循环体 */. W1 O2 {* u# Q/ W) W, M6 \- H
  41.      while (1)" b+ u: [7 Q( ?( R3 L
  42.      {
    . i7 e% p' i5 ]2 k8 u5 a
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
      q# P* d$ E; j  e+ P8 h

  44. ' }; k8 `- z1 e4 i5 K6 n& f. }6 K
  45.          /* 判断定时器超时时间 */6 k; q5 `, M. f6 O  c
  46.          if (bsp_CheckTimer(0))
    8 b8 K9 _1 T% J0 C. }. q3 k( X
  47.          {8 a% G+ ?3 H6 ?1 z7 b* U/ x
  48.               EventStartA(0);  
    ( i  O5 W5 H0 [; ^6 k/ e5 x
  49.               EventStopA(0);
    9 r+ k+ B% \# o
  50.               EventStartA(1);- R7 S; z2 D/ H. |' Z
  51.               bsp_DelayMS(5);; o& ^1 E5 K/ |. J. \4 F$ T
  52.               EventStopA(1);
    ) {3 W. G9 x/ g% {
  53.               EventStartA(2);
      h4 B: C. [( Z( j4 r, O# V
  54.               bsp_DelayMS(30);
    0 _7 h& t7 T1 g' N5 z
  55.               EventStopA(2);
    1 a* E# A4 {) X( o
  56. 1 N  l! |8 L" O& `4 g7 K5 c
  57.               t0++;
    . b5 t6 M; \8 C
  58.               EventStartAv(3, t0, t0);
    & l& V7 a& R0 R
  59.               bsp_DelayMS(30);2 m# \9 b# M3 v. i* S
  60.               EventStopAv(3, t0, t0);" r; E6 o: Z1 l. K$ D, s' q
  61.          }
    7 h! \" @3 J6 j$ g: |7 o7 h; l* T
  62. 5 b4 T2 @2 \. g' K1 E
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    - I* {* H6 x; V
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */
    % h4 U. `' X0 N" x' L" m# {
  65.          if (ucKeyCode != KEY_NONE)% \7 |+ J3 f9 i% G2 y0 I$ X
  66.          {8 K) D/ h! _( |! O& X
  67.               switch (ucKeyCode)
    , Z7 S4 T2 ?7 U3 R1 \
  68.               {
    9 r" W+ d5 E, z8 h
  69.                    case KEY_DOWN_K1:           /* K1键按下 */
    3 _9 M7 `6 y6 c8 w
  70.                        t1 += 1;. B2 f; D% t) O% K2 O
  71.                        t2 += 2;
    $ U; b' P& K. T0 w) b
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);
    1 Z; N- y5 ^. r2 \7 g
  73.                        t3 += 3;
    ( E- K$ `. H6 r- J/ @# m8 _: s
  74.                        t4 += 4;' E) N$ v# E$ n  k
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);0 P2 Z; N- H5 x+ f: Q
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));( _& \2 j3 w( X2 k
  77.                        break;( M2 z9 {' U- ?2 m- b  e; _

  78. - a$ Y: f+ q0 f6 c
  79.                    case KEY_DOWN_K2:           /* K2键按下 */  o- b% _' r1 H. Q3 V
  80.                        printf("K2按键按下\r\n");
    6 i; y1 g8 ~0 d( H- z: H0 G2 o
  81.                        break;
    9 H* Q7 b' n0 ?9 \4 R
  82. ) x$ E  _4 @; l; P
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    3 d, F9 M) ~% l* L$ }' Z7 w
  84.                        printf("K3按键按下\r\n");
    8 ]. P9 B, e* I
  85.                        break;1 p7 f# g, `# k& H
  86. ' z4 Y. W) A# P* z! _3 x
  87.                    default:' \+ l; m" H$ s7 A- q
  88.                         /* 其它的键值不处理 */
    ( }7 f% [  O% S# I) r; {2 N6 Q% f7 [
  89.                        break;6 p8 C5 ^4 j: K8 x5 z
  90.               }! m/ }9 z5 ^* D/ p3 T
  91.          }" i2 S& w8 O0 o, P! H4 e
  92.      }
    8 v5 v; G" i& _0 g# `
  93. }
复制代码


9 O; \" x; e+ g3 V# X, p

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

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

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

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

8.14 总结

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


6 p& Z" K7 {8 w5 o0 J! f' u. M, }7 ]
赞 收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版