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

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

    ; H$ O4 W  Q6 e

    + k) P" ]* W" q$ c

    + J  n: {% _3 X
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, o1 ]+ u1 P8 r( A
    1 [- E1 v5 J1 [2 U6 [( Q) y
    & {# V. w) f; O' v' e

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

  •   词条 SWV(Serial Wire Viewer)

    ) N. I5 @( s) y3 q
    ( `7 Q# D# D$ K- ^

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


    : q- s. d5 @# J# o* L

    ! w) x- X# X6 A' N
    * c/ C& S( R4 \% a/ I! r8 d
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调试。

    / t: j' w9 F4 B. u
    8 }2 {3 Q+ d' h! b) }

    . f7 c6 }; L: m0 ^$ j3 a
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 {2 p/ n1 _  H/ C& p

  2. 7 x0 D& K4 J) ]: Y1 ^! ?) K; W6 t
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)
    ' _' h; T1 r; u2 W
  4. ' F- C! A6 @  _' V
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)2 l$ O3 N  z* t0 w$ E: J' E

  6. 2 p* X2 W- ?$ G/ _) f/ n$ q% X
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)
    ) E. T' y- K; u. d

  8. & N1 N6 X7 ~; F3 l' i; {
  9.   uint32_t info;                // Record Information, N7 X* z5 N. _% }( k
  10. 9 ?: r" i  U2 m* Y0 C4 K
  11.                                 //  [ 7.. 0]: Message ID (8-bit)! ?/ c; E; g2 G
  12. + d4 A% {, u1 A' M# @6 a2 y9 F! P
  13.                                 //  [15.. 8]: Component ID (8-bit)+ x' x3 c, e! t3 M" b; P& c/ A* X
  14. 9 [: D) m% Y4 {
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    7 x; O2 i: f% j, R

  16. ' g& O! N' K5 \+ P+ g$ L
  17.                                 //      [19]: IRQ Flag. \! V0 E; S: H7 p' f+ @0 u& ~

  18. 6 X# \! C' f; {& w* q3 W
  19.                                 //  [23..20]: Sequence Number
    : E6 |8 u9 ^. R5 S: {/ S
  20. & l* ~. ]3 d" v5 b+ Y
  21.                                 //      [24]: First Record: J- ^1 R) ^3 b9 R2 |- p
  22. ' Q0 x" l0 V/ L% ?
  23.                                 //      [25]: Last Record6 z( i5 f- `- w# j: g
  24. 6 D$ l$ E0 ^9 Y, M- @( @
  25.                                 //      [26]: Locked Record
    8 _1 l0 H9 D- F: R& E: S

  26. 2 J* S( G9 I5 I$ R3 B  l
  27.                                 //      [27]: Valid Record1 _4 y9 H( M% k; s: y) V6 p

  28. ( A( L8 e3 b$ m" N/ {
  29.                                 //      [28]: Timestamp MSB
    5 J! T5 E3 U8 m# T; ?

  30. * S7 H  d& W1 M; c( J
  31.                                 //      [29]: Value 1 MSB
    + d* O3 R& t3 c0 Z+ q! ?/ ?7 R0 M  s

  32. ! H2 d( ]5 x: r0 M( G
  33.                                 //      [30]: Value 2 MSB7 K& A' n! m& o, c$ z& N

  34. & A) z/ q4 t. ^, j# O. B
  35.                                 //      [31]: Toggle bit
    3 V& \8 H( D! |% V  T; G5 W! [

  36. % H" h: \3 a5 x& F6 p, G
  37. } EventRecord_t;
复制代码

. i8 b! D$ z* z  d1 d

其中参数成员info最重要,也就是前面说的32位ID,这里的说明与前面的说明稍有不同。这里是经过处理后,实际存储到Event Recorder缓冲里面的数据。" G. E# r' ^+ C5 e

对于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 b2 z/ W2 g+ S9 H4 A

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

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

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


    8 }8 i- `' {, {6 `! N9 B
    * i- S  k' W; Y  w8 h, R

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */
    6 W' M( O! Q( F* p

  2.   x% T% U3 ]9 a% a7 N! _
  3. EventRecorderInitialize(EventRecordAll, 1U);) A% b+ \% e* w/ ^  S' o; L# v- k
  4. 6 z( [% v" J! n2 s
  5. EventRecorderStart();
复制代码


* P+ ^8 P# X# D2 B( X+ n

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

% E+ Y. U  E  U9 P2 R, q) ^9 I
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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。

    1 Q; e2 |; e6 M( O7 o

    7 Y% w( V3 h! S0 n0 ~% y

    % h$ \) u! V' g9 g+ f% J2 Y

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

    ' W: p5 z+ {( s! @5 Y9 w3 g
    0 m2 c) C6 h9 Q% H7 z+ R3 X

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

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


  • , {. _* y; _4 i

    # }: j; ~: K5 C7 Z: J7 e
    /* 初始化EventRecorder并开启 */7 r2 b3 i/ f/ q5 I- z+ u0 B

    / \% P4 s; a. l& c) Q* C+ S, o# h
  • 9 e* P, c6 ]' f9 p5 D" U
    ( w# w6 A$ E. V9 a
    & F2 F+ e' L: U% s( ~" y9 ?- x

    9 I8 H6 h6 S4 X9 X# c& W

  • ; h' x6 G7 u' {* L2 O5 o
    ( s& p- |( v9 `+ g
    EventRecorderInitialize(EventRecordAll, 1U);+ A+ V3 x! W! o) e3 C( M) s

    * G* r* i( n& j- k! k9 t( s
  • $ x& R- }1 {% y# \

    : r3 t! e$ j" O7 V; J

    9 J9 ^% i; L2 J- `

    6 i& C6 Q- U! E7 b# r! @

  • 3 B' O/ b4 C1 h8 Q6 M* Z  G

    , a" W& }0 F2 b6 I. T  m
    EventRecorderStart();& M" l" h* B* v
    7 B7 s% H- h5 N
    ' d' a$ `6 L& @0 _6 g

    ! r  Q1 }; ]2 W1 d

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


  • ! l! _0 v) M; i- _% x

    * v; Y! o& @0 e) V7 O
    EventStartA(0);: G# E4 f; N; {
    1 W) O- F! ^' ]) e

  • - q7 ^, L5 Q: B& t
    : _' O) @2 L' B+ k* N' r9 g% n
    : m5 I& ^0 J; Q. P! P* V5 W. T- K

    # P* a9 X* y- `; O
  • # P- Q* M6 _7 ]% R  A4 V

    9 ^( P3 q& c/ W7 {, [* j' h! r( j+ I
    //测量的代码部分
    " L. D! P8 x) T( `" Q- V5 b

    5 _2 f5 y# P3 s3 U* Y, i' a8 x
  • ( w3 F+ Q8 [4 C. u/ z4 t; K2 t

    3 K/ B1 |8 ^- j

    " q9 L. ~8 n. _7 S9 ~! r# b' `
      i2 D1 u( O) l" ], |4 B
  • % d8 O0 K& u1 Z" x3 c
    ' N3 P- b. Z9 o0 q9 v; v+ m
    EventStopA(0);
    . g& R- ?. W" ~9 Y8 u8 g! b
    / n7 Q* S% S2 V, A; V

    , W) s& s. |0 Y
    - @* k+ I% }1 o8 S% c, e( E

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


    % _; o; V7 ?8 c7 ~* C2 q$ h
    ' x' X5 H5 n9 v& M, z/ R+ j# e% Q# \$ [

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

    ( m% _: u: V+ G5 c
    - L, R) e- v  E) S8 j6 ?, P

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


    + `- s- P* n. |" t" p
    * r) k+ v* Z6 U3 d9 _3 i, I3 L. Z5 P

8.9   JLINK配置说明

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

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

    # P8 r* W) n) z5 T  c- V  @

    5 |1 o0 o2 B; [- x$ X( y9 s

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

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

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


    % |. N4 w7 L( M" Q
    3 K6 K# \) x% u8 E! E, L1 s$ r6 h8 a

8.10 STLINK配置说明

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

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

    & q" O+ H$ x2 K/ ^" N2 c! N

    0 S3 ^) t, r  h; X

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

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


    1 J. V1 n/ I; U1 C2 |

      w0 }3 Y6 G, e( X. x5 t

8.11 CMSIS-DAP配置说明

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

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


    4 B$ }6 J  C9 T6 K; d9 s9 I" y

    ' V, z: Y/ [3 M" p' `  J4 f0 }4 [% S) _

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

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

    " t" _; |& |9 T+ J0 U6 s# {) K
    - {3 @3 Y5 z' }# v. p

8.12 ULINK配置说明

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

8.13 配套例子

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

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


    . M' Y# O* R! O3 _
    4 f3 s' v% ?" i' o* c/ }" c! H

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

  1. #include "bsp.h"6 H, D8 m& E! B8 p" b7 M2 x
  2. ; U- v# I% r8 ^  b: ~7 r7 S. B
  3. #include "EventRecorder.h"2 A3 d% m8 R  r6 V: e8 S9 Z
  4. /* 定时器频率,500Hz */
    # j3 {/ {$ g6 ^; y5 n% b# E: P: {
  5. #define  timerINTERRUPT_FREQUENCY    500
    / E9 `9 ]4 D) b7 |
  6. /* 中断优先级 */
    5 s8 k% j7 \3 u* R
  7. #define  timerHIGHEST_PRIORITY       10
    + D' [0 p" Y8 @8 a2 K
  8. /*
    8 k' e, c# n2 i6 i9 d7 ~
  9. *********************************************************************************************************
    4 {. c; `( C9 U! P- t% D+ H7 u
  10. *    函 数 名: vEventRecorderTest
    * W0 r. J8 R" V+ j7 R! E# ]
  11. *    功能说明: 创建定时器% a" s! @0 t) M
  12. *    形    参: 无
    ) L5 S; R+ p! Q+ g: o9 d
  13. *    返 回 值: 无
    9 I* ?% J7 L; t* `
  14. *********************************************************************************************************
    2 y/ r$ r0 s  B* o# L8 l
  15. */, C/ ~* s  E" `  Y. z: l
  16. void vEventRecorderTest(void)  M+ p6 g! X8 u. C# ?
  17. {
    * [9 q, L" Q) d% A, ~2 C. Y
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);
    % M+ ]$ O; @6 z3 y# p1 i3 f7 m
  19.      EventStartB(0);   ! D, x" g' ]/ |5 ?1 K
  20. }) T! d$ N2 y( a/ e+ Z0 r: T6 l' P
  21. 4 N  ?+ N) m5 e3 Y) A& K7 b! d% H
  22. /*
    0 |% d/ g# B( w+ J3 [
  23. *********************************************************************************************************0 T4 j, N* Y$ f5 d8 K2 |- t
  24. *    函 数 名: TIM6_DAC_IRQHandler# Q( |. ]$ u$ r2 C) L8 U
  25. *    功能说明: TIM6中断服务程序。  ?7 C* F2 L% t1 E: G5 A+ U
  26. *    形    参: 无% }. c) F2 H8 G: |* E$ b5 X" u
  27. *    返 回 值: 无/ @; U: f# S& J5 F6 q
  28. *********************************************************************************************************, v1 S% N( r3 ]! a
  29. */
    4 E3 P# T0 z7 w5 V5 r4 Z8 o5 r6 D
  30. void TIM6_DAC_IRQHandler( void )+ B% C! @# n( U- c1 g# ^
  31. {
    1 q) @2 u5 I+ [
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    ( n4 a0 P2 P; Z  j3 e' L. ]
  33.      {
    & Z0 L* H, F3 G' [: e% `+ F
  34.          EventStopB(0);    & Y/ R" r! P& x% [* O; {
  35.          EventStartB(0);   ' u+ B0 ]4 Z  \7 _
  36.          /* 清除更新标志 */! ]3 F* ~7 Z7 [% k) C
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;
    2 O# z6 @1 f4 F9 t0 c$ o
  38.      }
    , j& n4 ]% v$ A, |2 |: w( W
  39. }
复制代码
( s# }% `) u$ l$ G; }0 e2 j& O0 k

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */
    3 m9 f: U; r$ t& }4 z, V
  2. #include "EventRecorder.h"
    1 e2 R7 W% _. N' Q

  3. , `4 Q$ P5 z6 @) l6 ~# m8 u$ i
  4. /*; k7 t& o, C8 [" N
  5. *********************************************************************************************************
      [0 ?9 O4 ~: h5 {+ C  Z3 I
  6. *                                              函数和变量
      P. r9 e% g* D# M6 E
  7. *********************************************************************************************************: r( x8 Y' m! M
  8. */5 K0 h5 U* F. \/ ^# I
  9. extern void vEventRecorderTest(void);
    $ ]2 e+ Z. [/ j) u  N
  10. uint8_t s_ucBuf[10] = "armfly";0 P# \- |2 {+ W8 }- j

  11. : ]2 D2 A- y9 }8 I
  12. /*
    4 ^/ l+ `7 m; \/ j- r2 j# c# k
  13. *********************************************************************************************************( q% U7 ?) f& j8 Q
  14. *    函 数 名: main0 m( P: e& \9 h; S* d" o
  15. *    功能说明: c程序入口- C& q2 Z6 r  d3 ]; e
  16. *    形    参:无3 g& W3 ?! c. \, O/ [; X/ ]. U
  17. *    返 回 值: 错误代码(无需处理)
    & H% v1 I3 M, E/ y! x+ K4 |7 X% d
  18. *********************************************************************************************************
    7 L* ?# e' C  d! i5 O5 P
  19. */
    : n; w: v2 x* ~
  20. / Y" J3 ?  k! X0 `' X
  21. int main(void)
    3 f# H) F' t% L5 U, c; I
  22. {
    2 @/ \: b+ ?- T; f
  23.      uint8_t ucKeyCode;     /* 按键代码 */
    ! V, n  k3 _- Y; U& T
  24. ) w" Z) f; p) V$ c/ {& }! i" L
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
    & C9 G. p; _+ c6 O, E1 R8 ~

  26. : Z1 }  w0 ^( b3 V! E
  27.      /* 初始化EventRecorder并开启 */
    ' C' g& y- i! U* j' ~" X

  28. % r7 {  L( n) B5 z- f( R) Z
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    : q& Y3 Y5 g) t# I" Q

  30. . c8 T; b% b' q
  31.      EventRecorderStart();
    % @. o/ O" F/ U( W7 ?: D7 Q+ H

  32. ; V' \3 t+ a8 Y! x2 {0 R4 H: F" Z
  33.      bsp_Init();        /* 硬件初始化 */  b! v  k6 L* V

  34. : I3 f3 \: V: B% |* p
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */6 @" M4 R/ d7 ^. ]
  36. # `0 U. ?' Z8 g& Y/ B3 y
  37.      /* 测量中断周期 */6 I! G/ G+ v- k9 m* R; N' O5 N) o
  38.      vEventRecorderTest();
    $ i$ b3 w& z1 s

  39. 6 N' c& U/ |+ |
  40.      /* 进入主程序循环体 */
    / r. z4 g5 x5 v; z& X1 _
  41.      while (1)  L. T0 Y! h- T$ T8 l  G+ I
  42.      {
    7 t6 j/ Z+ S  ~8 s. L$ ?  w! ~
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */
    & @/ A2 t, K# W' {
  44. 0 K1 C( B9 G- t$ ^. O
  45.          /* 判断定时器超时时间 */
    ( }, t( e. q0 g
  46.          if (bsp_CheckTimer(0))
    , I% I9 ?5 P6 T* s$ H
  47.          {+ w& q1 x, @/ l+ f
  48.               EventStartA(0);  
    % C, @0 ~* O0 o' q( _5 Q
  49.               EventStopA(0);' y5 y/ ^) j: |; J
  50.               EventStartA(1);
    5 z2 `- Z- t* H
  51.               bsp_DelayMS(5);# d- e# M) T+ i) \
  52.               EventStopA(1);* r) ~! n  L8 Z, h0 f& m: b
  53.               EventStartA(2);
    ) H- ?2 f- M7 H0 \( |
  54.               bsp_DelayMS(30);
    " T$ g8 K* J  ^( M% Y
  55.               EventStopA(2);( ^1 w: T0 \  d- u* e
  56. ( e& _* x/ M5 x4 s1 i
  57.               t0++;3 H1 F! B  I. o
  58.               EventStartAv(3, t0, t0);& R8 U- a2 W/ H% W5 J+ U; C! H
  59.               bsp_DelayMS(30);
    8 i) U2 l$ Q0 K/ e/ B* J4 _+ n6 a! L9 L% G
  60.               EventStopAv(3, t0, t0);6 M, R" P$ ?5 `% j) Y
  61.          }
    , \0 z1 n8 C  S# c

  62. ; s2 {6 L' \# m8 x" `
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */: O( w( h% t7 `0 h& S1 v; e$ t+ w
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */9 Z' v" E- _. t3 |3 n
  65.          if (ucKeyCode != KEY_NONE)
    ! |; N/ O/ u5 V; |1 V. W
  66.          {# I  x, i& ]9 I  X" F# U
  67.               switch (ucKeyCode)$ u( w0 T/ D. r, e+ ]3 k, `
  68.               {0 s) r. b& E9 U' J/ F
  69.                    case KEY_DOWN_K1:           /* K1键按下 */& ?) |2 M4 T$ h1 o( @: j
  70.                        t1 += 1;. K0 s( X3 a. y7 p$ i! }4 R
  71.                        t2 += 2;
    . U  y4 P6 |+ D( l6 @
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);
    7 U* u* X9 @! W
  73.                        t3 += 3;, K: @% o0 @; Y( Z- }* g! s
  74.                        t4 += 4;
    : k5 a, `) e8 S  t% e. L
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
    ) t; o! d3 r4 {( ?9 T4 j
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    ; ?, X% H6 d3 {+ [
  77.                        break;6 T) M7 f: L! ?) r0 q

  78. ; d3 L3 s) J9 |& ?! \9 t6 S" k2 P' G9 E
  79.                    case KEY_DOWN_K2:           /* K2键按下 */* A0 k2 e' |: |1 E4 |  T  l
  80.                        printf("K2按键按下\r\n");4 r: r9 {- I; C4 E
  81.                        break;& J/ q' o+ a) c9 @1 S7 w
  82. 2 B! W1 g& c& @0 I
  83.                    case KEY_DOWN_K3:           /* K3键按下 */" P+ z: i+ C7 l' ]1 e
  84.                        printf("K3按键按下\r\n");! a  r5 i. W6 `+ m6 N+ R5 v" n
  85.                        break;( B/ I5 M( H( t& x  g, Y
  86. 8 v$ w# t' D" k2 K" R+ Z! f" V0 Y
  87.                    default:
    * K( M$ i# g* v& ]
  88.                         /* 其它的键值不处理 */
    : }# ^3 L- ~* x6 R$ e
  89.                        break;
    " B) Z4 o  Y. @3 J2 S# S
  90.               }" {1 F! F( F! L; m5 @
  91.          }! ~3 O$ G& X( I  [9 h
  92.      }7 E# n: W8 }6 g- A5 a) J/ k) S
  93. }
复制代码

7 z! k' `; S& g/ @. r: o# b

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

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

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

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

8.14 总结

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


5 ^' h: p7 p' H
收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似分享

官网相关资源

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