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

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


    . L( h2 U: U2 \- U1 F$ F
    * u4 C6 u; T( w* Z- K

    + [6 U6 _4 O/ E7 C% U' ]
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接口区别

    $ ?9 E% |  a/ F8 }$ j; ?

    0 U, a" `! T/ h* K/ R2 J

    / }! x, \3 [7 S4 K  O5 ]. h) z

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

  •   词条 SWV(Serial Wire Viewer)


    . Q: L% d! D. }# d

    ) M, d3 h: L% f% t/ `

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# M4 R, R3 {$ Y

    ' ?: ?, I# T0 s7 c3 I6 k! ?4 M4 m

    / e6 F  C2 i/ O$ p
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调试。

    & }: Q+ B; s: \" r3 J
    2 u$ B- \! s4 J# o
    % h$ E1 J8 d" r( U$ {  q+ \
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 v. w: t% u0 @% @! t

  2. - ^8 |- F' M4 M' u
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)
    & t: ]+ F% |8 ?$ t7 U& P' O7 o

  4. 8 l* W6 T  ?: {* |
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)& w6 i$ e, A  v# K/ \$ }" \" C- T

  6. & r+ }5 }* ~; q! m, `8 M# f3 |; V
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)
    $ ]5 q% K; y# ]. g  [& Z; x

  8. - }! a( d  o0 q" X5 a* Q5 T
  9.   uint32_t info;                // Record Information8 F' D! x. R& P  J9 G! c2 L# n6 d- a
  10. . o  ^: K" @' {3 s# E: L/ [2 l# n
  11.                                 //  [ 7.. 0]: Message ID (8-bit)0 _2 Z' c8 A7 K0 h/ a0 |6 i3 X6 g9 D8 Q
  12. * z. ~) C7 E  y
  13.                                 //  [15.. 8]: Component ID (8-bit)  l* L( l/ ]% a
  14. - U. [1 y* v# U/ V1 U, s
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    4 v+ k' n9 g  v/ W2 f- e) ^  {* T, y
  16. & g7 N) M( ~6 B
  17.                                 //      [19]: IRQ Flag
    $ ?+ W! W' v" R% E3 i

  18. & n4 h8 Y# y4 j7 J! d
  19.                                 //  [23..20]: Sequence Number; P2 c6 g* u+ E6 ?! y" }
  20. ' y4 w3 Z3 v2 `# T' y& \
  21.                                 //      [24]: First Record
    * m, A, U9 v8 k" O% ^% E' E  `
  22. 1 g* D7 g! j' }
  23.                                 //      [25]: Last Record
    1 y3 {  H' c: C% Q' g) y: B

  24. 3 a" P9 }, O' m9 S
  25.                                 //      [26]: Locked Record6 u- K4 Y# r) ]4 }

  26. & \' G) c! k+ I7 y$ l; S% M3 ?, D
  27.                                 //      [27]: Valid Record+ I4 W: C  M/ v/ A$ L# z4 X

  28. ( R' p- @" W7 c3 j# ]
  29.                                 //      [28]: Timestamp MSB
    0 G1 Q" L7 _9 t0 X  e& e1 f

  30. : w2 g6 s/ b  q* E1 X
  31.                                 //      [29]: Value 1 MSB! R5 |) i( g- S# l" Y" G$ V

  32. 2 B. w# Z  Y# o' b6 \" c3 n( K
  33.                                 //      [30]: Value 2 MSB* J9 P3 j( L8 g8 M* [! ?

  34. 9 Q- w  `/ ~% T6 L- }
  35.                                 //      [31]: Toggle bit; C0 T! C/ |0 H( {6 A: _( p4 x- E

  36. 4 ~$ f+ z2 Q  z( `: V2 k
  37. } EventRecord_t;
复制代码

8 E! t# W- Z" a$ C" c1 W0 `

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。: _' d$ e% c3 f, n

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


    9 w" r5 g0 g- f$ m, T4 ^

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

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

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

    , N! o( P* ^3 q9 {
    1 O" V/ f7 y" L2 ^" I8 Z; v# G

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */5 }/ Z2 h( w. |) l' l

  2. 0 D% [7 b0 P3 t. b9 V1 P+ n
  3. EventRecorderInitialize(EventRecordAll, 1U);
    6 L) _2 ^% ], P  |7 u
  4. " d2 a2 m0 T& C9 `7 s# h0 r/ n
  5. EventRecorderStart();
复制代码


" z( @5 F" \# p: s1 p( z

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


: Z6 X+ w' G; i1 Q( N5 t: N8.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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。


    " f8 x, e5 X9 N2 V1 S. o
    3 T& h' x/ c# |; }! ]7 d

    $ T" b$ w1 Z- t5 C

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

    ' u: j9 n" K# h* n2 \, [
    0 N- J6 W) w* d! |2 e

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

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

  • * ^: z. Z$ W% J8 w/ v. T
    8 t7 k# ~/ N0 B
    /* 初始化EventRecorder并开启 */
    4 x  i/ K' n! U; h' ]
    : v" R; B* w0 t( Z' C2 F2 E
  • : P: K- P  D  D! m* x
    * p6 q- Y. w# v* h+ c

    & s: W' O# I& S9 F7 O
    : _3 x- s8 T$ g- U4 ?* ^
  • 7 y" L8 i( l( d  v- T

    " E* {7 m' w) N. {
    EventRecorderInitialize(EventRecordAll, 1U);
    8 V/ ^' h: D4 k; f' o& I
    9 ~$ H9 X+ m/ J% s( s0 N) y
  • # D2 K( u8 [: {
    # [2 N0 g& q9 T+ Z3 @. ?. E

    ; f$ f" A1 u. B0 c7 k; ]1 O

    " f' N  c8 ^% ?1 s, G9 [( e8 m4 Y
  • * a  P4 Q% f2 V3 w
    ) S% S- r; u4 a1 c
    EventRecorderStart();
    ! j% X+ `1 |( t+ M6 y! n5 ^; w
    2 e4 j, B7 l( p5 h# |

    % X2 Y& n. E; Z8 \
    % |5 `! V. M6 b, N

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


  • 9 d, z, _6 w- A) e% N
    6 s, g6 k9 I; k( _; F3 G
    EventStartA(0);
    * ]: ~9 S5 c3 S4 x9 t/ @* J
    ; F3 p- Z# }+ F( N

  • ' l. o" o; q4 h7 w# A+ D

    , J. }$ z5 j$ V' D+ Q2 n
    2 h: h( H8 ^  Q4 ]. W
    % u& ?# w2 B% h0 E: S* r, r% B

  • , O0 M4 F3 d. |) s

    * F% `/ Q' F$ y6 o1 a1 I# m" w
    //测量的代码部分  v8 Q; W" w  R2 s7 V
    8 j$ R" a) J  \9 g) L4 {

  •   b8 W3 K% i$ o4 v5 Y

    5 D! W/ P2 ]* L: t  L' l* q

    ; P8 B5 P" G  q: ~
    # S. F7 x- H2 V4 k" F+ @

  • 5 P" G9 p  Y, v. L
    , [2 P9 v! r% q
    EventStopA(0);+ F" d' C+ f& [, m: |

    . Y9 ^# X) O8 t5 A% e  ?1 R
    1 a1 q/ o  t( p% T5 ]
    - b0 x$ o: c$ g2 j! d: L; 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的效果:


    / H, S9 A" B, \6 P. x- j

    5 e3 p1 d9 T0 X( h( ?* x  a

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


    / v9 O4 B4 q4 V$ P/ T/ N
    + v3 ?5 Z, q$ q1 L

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


    ! n) a0 a& g' Y8 y/ m5 v
    ' n, `! \5 }7 G: _+ K( k

8.9   JLINK配置说明

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

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

    % Y. O; C; X/ K; [$ f# ^/ u: d

    $ l7 a! Z- v1 l, N; ?9 r" }

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

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

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


    0 ~) c& R7 I8 {: {2 M0 k8 g
    5 C0 i1 {% {( ?4 x& k2 J: f

8.10 STLINK配置说明

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

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

    6 }# M: e9 ?7 J
    + q4 Y: y9 m9 h- g0 F: w# D* A

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

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


    $ y* n: h# }' N

    * R; {. T+ g- s) E' y7 i

8.11 CMSIS-DAP配置说明

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

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


    3 m9 m! ~3 l" e) U+ T7 \

      e2 Z1 O( J$ a8 K* N

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

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


    - J% o% U) _. ~3 U7 w+ b6 z
    5 C# `8 V1 Y. `/ @, |; ^

8.12 ULINK配置说明

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

8.13 配套例子

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

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


      B. x1 |2 E! n! G8 r
    0 M0 \# `9 F" X5 L! T% S

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

  1. #include "bsp.h"' @8 ^; j- m- R. d% |; E; `3 H
  2. ) R& W8 {6 X. u# K2 Z7 Q  M
  3. #include "EventRecorder.h"
    # L5 V- H* m1 m' q2 S4 r
  4. /* 定时器频率,500Hz */8 v0 p, {# T# k) j
  5. #define  timerINTERRUPT_FREQUENCY    500& c& u# P- S/ Z
  6. /* 中断优先级 */. [6 i; m% I! [9 d8 U7 g
  7. #define  timerHIGHEST_PRIORITY       10. u3 B& _$ A- C" _/ P  P% [
  8. /*# b9 G* d4 @3 K5 S7 @/ y, P0 u
  9. *********************************************************************************************************! |/ P- t/ ~* K
  10. *    函 数 名: vEventRecorderTest, y' f2 t" S; h% c
  11. *    功能说明: 创建定时器
    7 r- w! z1 f/ a2 f- r! i: D
  12. *    形    参: 无
    0 u* x9 g- S4 {/ b5 }
  13. *    返 回 值: 无
    5 @6 d3 Q- \9 f8 F: s! ]
  14. *********************************************************************************************************/ D* w" a/ _" t! w( y& L# D# |
  15. */
    . U( L- [* C: U/ i1 V* z( M
  16. void vEventRecorderTest(void)
      L) z6 \' U2 I1 ^  `( g
  17. {
    ; O- e. [$ j3 ]
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);+ T# o' f  J& Z" R  S! Z+ N5 \- N
  19.      EventStartB(0);   ( ~  l( i* }& ]7 P7 z
  20. }# ]$ g7 P# }( G! F% A+ p' b' I
  21. 5 D# b. }) R6 X+ Q* j2 O0 h
  22. /*
    ! k" `. Z4 h/ o3 R/ E" A/ b0 g
  23. *********************************************************************************************************+ p7 T0 B- v/ V; G0 q
  24. *    函 数 名: TIM6_DAC_IRQHandler6 s3 _( F6 G: p8 H4 v( Y, z
  25. *    功能说明: TIM6中断服务程序。
    : `4 {( o% d# R; N, _
  26. *    形    参: 无# o" X: o% E% Q: w) f" _+ \& X
  27. *    返 回 值: 无
    : a1 j0 H. g+ X" q' H+ z6 x0 [
  28. *********************************************************************************************************
    / i( R9 a2 N& A/ ?, o3 m' x
  29. */1 |2 c* c. q0 x* C5 k+ g* l
  30. void TIM6_DAC_IRQHandler( void )
    9 ]3 Y+ D, Z* e+ D
  31. {" k- ^5 u6 Z6 @9 x. Y
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    ( s  j& F7 w6 a1 U) s
  33.      {
    ' d  C! P: R4 D' V2 t: _
  34.          EventStopB(0);    , m/ U, k0 `9 e- h+ ]  n- P
  35.          EventStartB(0);   
    - Y5 y1 g9 `! d; p% e+ y
  36.          /* 清除更新标志 */
    5 E3 S( f) _3 |
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;
    * ]3 k8 o0 o/ z2 a3 V
  38.      }# `$ C2 }8 Q) f; H! O4 Y! T
  39. }
复制代码
% Q- [# g% S$ j, z: J% X# E, [

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    + o* c2 v" o. A! Z6 O* O5 I+ B7 ?
  2. #include "EventRecorder.h"
    ) i2 r1 y+ P" A' u+ }/ p

  3.   s0 X" y( U. ^. G- r. G
  4. /*
    & A4 p9 p* s* L" {1 p0 ~3 F
  5. *********************************************************************************************************
    6 ]: t' E+ j9 ]3 Y0 d% c
  6. *                                              函数和变量
    5 [1 x5 @8 V4 R3 ]3 ]
  7. *********************************************************************************************************4 m% ~# \& _5 v" S) r
  8. */) {  t9 L& a! `3 w
  9. extern void vEventRecorderTest(void);. j( {* \+ n/ U! w& H
  10. uint8_t s_ucBuf[10] = "armfly";  I6 ?1 w4 O$ ?+ d

  11. 5 s& u. x. C. ?/ d+ y- ~; v7 P. X
  12. /*5 o6 C$ F! R& e- K
  13. *********************************************************************************************************. V5 R" X2 H3 Z9 B
  14. *    函 数 名: main
    ) V  l( E4 U1 K
  15. *    功能说明: c程序入口% ~4 N0 k7 r9 _5 X3 s! O
  16. *    形    参:无8 N( B# }( V8 r! p
  17. *    返 回 值: 错误代码(无需处理)
    8 U  O) I5 a; M5 N3 F% j2 ?
  18. *********************************************************************************************************
    ' |5 \; v3 z5 t  j) o
  19. */# ^# j8 W0 U- N/ o9 e

  20. ! x# s9 y2 w  O; d7 m
  21. int main(void)
    9 |: I1 _$ ^& h- E* b1 s! Y  v
  22. {
    ' ]! i4 B. N5 Z, t9 k; e6 P8 l
  23.      uint8_t ucKeyCode;     /* 按键代码 */9 W: B# S! l3 O+ u/ K5 B. M
  24. & e: O- M. X4 ^' ^$ F; [
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;7 m  ?; X+ @9 y/ e3 D# E
  26. / g; P7 N: \3 Q& q
  27.      /* 初始化EventRecorder并开启 */$ }" n0 u1 J5 @! N7 A0 b# n

  28. % \) v0 d; f5 B2 @4 q5 b
  29.      EventRecorderInitialize(EventRecordAll, 1U);4 Y' a3 K$ n+ p/ h+ e$ z
  30. # Q. `6 ?& {5 w2 o
  31.      EventRecorderStart();
    # D. ^5 r+ Z3 U9 j. d8 G
  32. # x) w5 k+ Q$ a0 n0 M
  33.      bsp_Init();        /* 硬件初始化 */
    0 ~: s' k) l( c, [5 N
  34. : C9 O: b' B  B4 y
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */
    + b; p' s" }" h1 q( }  z5 h( U

  36. ! {; n( `) n% c7 I: x6 l. `  P
  37.      /* 测量中断周期 */
    ) X0 N  i# g- C# m& Z
  38.      vEventRecorderTest();" j( I7 a" G% B2 g0 E
  39. 1 v# U9 P0 A. \$ m
  40.      /* 进入主程序循环体 */
    2 S0 ~* |3 H! @& B3 ~. b
  41.      while (1)
    2 N4 S, g! E# \9 P
  42.      {
    6 G. K' d" \3 E2 o& s3 ]
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 *// l; x; M4 ~# I
  44. " H# H& I# T$ L# ]
  45.          /* 判断定时器超时时间 */
    + U5 u3 i) y8 s6 b- s  E2 j% [
  46.          if (bsp_CheckTimer(0))
    % P, F0 ]1 O/ Q0 M4 @0 S9 p
  47.          {
    6 a2 D2 D$ L2 g0 }- P+ ~
  48.               EventStartA(0);  
    ' M; f' F8 U- R8 a6 i- Z
  49.               EventStopA(0);
    ! ^$ i# h$ V) \
  50.               EventStartA(1);
    1 s. b6 @# {; o) D
  51.               bsp_DelayMS(5);
    6 O2 a# ^  b0 w; S( X% }
  52.               EventStopA(1);0 t1 d# m8 @" e" r3 C5 x) L. [
  53.               EventStartA(2);3 k2 g# `' }, V( X$ z& @" M
  54.               bsp_DelayMS(30);
    , _: m7 l/ S0 V
  55.               EventStopA(2);2 }3 H# [: B2 X8 d; {
  56. 5 V: |0 V: E8 |- a
  57.               t0++;
    " o# D& n' \4 ?* v) V3 d
  58.               EventStartAv(3, t0, t0);
      U4 H! A7 i+ u+ X7 [; T
  59.               bsp_DelayMS(30);
    + O9 {* ?7 P4 h4 x$ W: J# z4 u
  60.               EventStopAv(3, t0, t0);
    0 X( i& Q& {5 z3 v; C- ~8 H
  61.          }
    2 C: i! l5 g& ^, C" \3 s; @
  62. 1 m2 t/ V- S9 K- B
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    ; `2 a# B# Z8 r  t% G
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */6 R2 [( H* J2 @. s  T! B$ Q7 T$ S
  65.          if (ucKeyCode != KEY_NONE). U+ O. ^" B5 J5 @. l  ~0 P& n  d
  66.          {
    : @8 F6 B& ^; r8 s  D6 \8 O
  67.               switch (ucKeyCode)% g3 s& p5 T+ o+ d1 }
  68.               {
    " _8 b6 k9 z) D$ Q- R! j4 i: i6 S" D
  69.                    case KEY_DOWN_K1:           /* K1键按下 */1 `& ]' s) |7 d  z
  70.                        t1 += 1;
    ; D3 c* a5 g4 O
  71.                        t2 += 2;8 K& w* ^8 }, D% M) \
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);: o7 r/ z5 e5 h5 l5 `1 t( K% B
  73.                        t3 += 3;
    ! {0 O5 I- ^, R) d5 ^% D
  74.                        t4 += 4;
    : y5 v+ }; J. X* A2 z
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
    8 g, M! c: y) _6 F. U. h" l
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    - Q1 Y- B, P4 G' ?
  77.                        break;5 |& e% P) g& P! o. k
  78. , B/ L& f/ r" ^3 F/ R  K
  79.                    case KEY_DOWN_K2:           /* K2键按下 */
    1 L; Z5 ~9 L" Y( L  \
  80.                        printf("K2按键按下\r\n");
    1 f( n3 K2 }: c0 c
  81.                        break;
    * g& C. C$ L/ d1 `+ ?% c8 B9 G$ w  Q& g

  82. 2 _1 i& z+ X+ o; c; @% X) i
  83.                    case KEY_DOWN_K3:           /* K3键按下 *// ~9 q2 |. T! S0 K: J! |
  84.                        printf("K3按键按下\r\n");8 e( E+ m+ @. {3 p3 T4 A% d
  85.                        break;
    7 C$ I! R4 A& u# @* v
  86. % E4 _3 w! Y! n0 t0 I
  87.                    default:
    ) @1 ?3 V* N0 M
  88.                         /* 其它的键值不处理 */( l6 j- x' V# Y7 F" ~  n- V
  89.                        break;
    9 M8 ~- E, Y# F8 @! ^3 J' Y3 l
  90.               }
    4 _! }) j, f% G6 j$ z
  91.          }, y  p6 w& C; H, T) y
  92.      }  g& _0 X. N  a. {  [6 S. h+ k
  93. }
复制代码

2 o# m3 J4 t& d" T# ]1 p

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

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

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

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

8.14 总结

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

4 {/ h7 W! M* P: a( M5 T7 h6 v
赞 收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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