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

【经验分享】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 x- P; m4 T* p! D3 ^) d5 }6 }
    6 X# i2 A) x' H* I% x, H2 R

    1 H, p) @( a. T: i7 |+ `
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接口区别


    % }( q# N- n; h3 j6 Y4 U

    : W4 j& s6 H' \. ^* _

    ; V( A' t* V; }+ n

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

  •   词条 SWV(Serial Wire Viewer)


    % j! U& T% {" T( ^9 a( a1 d
    # c/ S( C# O0 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 就会把一个差分的时间戳数据包插入到跟踪数据流中。跟踪捕获设备在得到了这些时间戳后,就可以找出各跟踪数据之间的时间相关信息。另外,在时间戳计数器溢出时也会发送时间戳数据包。


    : X# g% P1 y) n$ X, Z

    % P  q1 r1 ]- g( Q& Z

    : p6 ]9 i1 ^4 h1 Y7 u2 O
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调试。


    6 I) Z7 M8 R4 u$ R( [0 B

    9 O4 f4 j& @( e/ M0 a( M
    3 L- o! z& \" h7 n  Z5 c
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 {
    : v' e8 x) |% h: k+ ?9 y

  2. / i3 c! B! E- d/ W2 `. e$ n' v) X" ~
  3.   uint32_t ts;                  // Timestamp (32-bit, Toggle bit instead of MSB)# e; E5 C' e& [( k
  4. 7 z1 E$ w) F- M3 R0 R5 O
  5.   uint32_t val1;                // Value 1   (32-bit, Toggle bit instead of MSB)
    : O1 L4 O! g9 j# U6 g+ x0 Y0 `/ [# m

  6. 3 E* o4 x3 h& ]; _: |. [
  7.   uint32_t val2;                // Value 2   (32-bit, Toggle bit instead of MSB)
    - N9 i4 a/ F0 a0 ?6 Z$ c! s+ i

  8. / g& x3 s' Y$ }5 B
  9.   uint32_t info;                // Record Information
      v4 ]; X+ w" n1 v* p* a

  10. 3 X5 ]+ O9 B% \2 Q
  11.                                 //  [ 7.. 0]: Message ID (8-bit)
    * a0 V  n9 Y& l4 _+ z, I9 Z

  12. 0 ]  A0 d* s* L( S: o; D
  13.                                 //  [15.. 8]: Component ID (8-bit)
    " `( x1 L+ Y% b! q/ V

  14. " h) f* e6 o5 v& M6 b/ O  C
  15.                                 //  [18..16]: Data Length (1..8) / Event Context
    * ?/ E- }5 l  w! N

  16.   k' n% ]2 o4 G, b% y8 B9 E2 r* k% }
  17.                                 //      [19]: IRQ Flag: y$ @, u9 h' o+ ~  a) b3 J
  18. & P; L# N) n' t, ?0 A) u4 i
  19.                                 //  [23..20]: Sequence Number
    ( i4 J# w# [, q7 A
  20. 0 l& A( Q7 Q2 @- V' U- Q9 k
  21.                                 //      [24]: First Record
    * j; c* C* O7 ^) M
  22.   L+ w! t, b1 e0 X/ x
  23.                                 //      [25]: Last Record% U# K9 a. N& _5 Q# X
  24. 9 C# \0 s# t% Q5 J$ S
  25.                                 //      [26]: Locked Record$ D1 _' \' r0 v( X
  26. ' w2 z0 K- e- _" h, e: Q7 B
  27.                                 //      [27]: Valid Record
    3 r7 X1 g8 Z4 u7 a
  28. . v* \" V1 }" t+ q
  29.                                 //      [28]: Timestamp MSB
    : \; \, U% ?3 X+ u
  30. 7 L2 M+ ?- [$ J: Q9 e' h5 ^9 r
  31.                                 //      [29]: Value 1 MSB
    1 y! t8 S) F" f: f' V1 M

  32. 2 z6 M+ Z& b  M, P9 @
  33.                                 //      [30]: Value 2 MSB
    4 S( T$ ]2 h+ a

  34. 3 T& `2 j0 u! X9 T" P# k* v
  35.                                 //      [31]: Toggle bit9 |  d  i. y( }. `% s

  36. 7 Z% I+ i' S. m' H* S# }
  37. } EventRecord_t;
复制代码

' P; n! V! c! V$ }) S

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


    " ?& O- c, `/ @! r

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

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

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


    9 r' X2 m, S  f! N5 o" v; I) B: }

    / x5 p. i$ K) c$ r

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

8.4   Event Recorder事件记录的实现

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

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

  1. /* 初始化EventRecorder并开启 */, q/ Y, N2 I, p4 D
  2. 3 M! m7 a* T( y0 I! q0 f
  3. EventRecorderInitialize(EventRecordAll, 1U);
    . @+ y3 \4 ^$ g6 w5 g
  4. 4 o2 w5 [; M) s+ L! S& x
  5. EventRecorderStart();
复制代码

+ @* n" u/ ], z/ }2 F

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

2 o2 v$ J6 y  x% d7 d3 g+ n% v
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调试组件里面,方便图形化展示。简单的说,这两个变量仅仅起到一个传递变量数值的作用。

    ' _9 {3 D9 m' p# g2 |

    0 p; I6 Y8 d. P2 T: j# D8 Z( }! O: {

    6 J  Y0 P3 _* a0 j: Q4 E

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


    * E+ D% O1 Q; S, M' y4 n1 S$ y4 G
    2 x* N) e% A/ Z9 `. C+ t

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

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


  • & C/ Y( Y/ B* D* R' x

    1 `5 J/ k7 r0 U1 n9 N, K$ \0 Y; U
    /* 初始化EventRecorder并开启 */4 S# k- @6 ]: z
    7 ^' |4 w% m& t
  • 9 ?  i6 C9 M5 O! A6 j! f5 _- S% ]
    9 h! m" j! m8 e4 |0 x. l& S" {; S" H
    . g5 m. O* A3 F% w' L3 A+ g* }
    % |8 _& H8 R" z# [
  • 4 m+ D; e* J" G8 x
    1 T" ^: N) P; ^! W. y& f0 f
    EventRecorderInitialize(EventRecordAll, 1U);
    0 N6 x3 D( L) u) y
    / G( `" P1 \  [9 C, d2 K

  • 1 G7 p9 g' G" j' H/ R
    - c$ q4 \& C$ C2 y0 s
    4 m6 y, b* V7 J  o2 z( o9 G

    . J& X' j$ @9 Z4 t# y
  • ( B9 m2 H; {5 c8 d6 V  K+ r
    : D( s& d2 O" r
    EventRecorderStart();9 @- x8 L# P1 B" G
    9 h9 S7 Q1 m8 C" ]1 F$ O* ]6 s

    ( h8 L9 A/ s( d# S* u

    / _7 t3 i; G( Z3 B

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


  • 3 w. U* Y% g+ f- J- M7 p

    , V+ }; |  }; K" P  _& F% c
    EventStartA(0);9 D4 s& J+ _* F  i

    1 {* |! v/ n. K7 s& ?9 o" w
  • % @" y5 u; s% w$ B7 f) g9 |6 \

    4 h9 F' J7 k' j. j9 u
    9 l6 p, e; {' a# T. a
    " n7 a: m* M5 q8 H) S
  • 6 w4 d9 [5 _- i( M6 w( o  z
    3 V9 R( }. i; x3 r1 F1 Q
    //测量的代码部分  X4 d8 `1 \, x) n6 r

    . d. Y0 Y- x$ J( l5 M

  • 3 c1 c! p7 W* g9 ~
    & ]  S) S' x5 o, k3 i7 P, |

    9 |% c2 R1 _( B- Q/ c9 G4 J4 D
    : L# _1 s( [3 X5 Y0 Y, h( a, l

  • " s. ~" n  {+ T& X8 h1 f
    6 Y9 L- T: @2 D+ o* p; j
    EventStopA(0);
    $ \( F; N  s! O1 [
    ! o1 ^# H# @6 R: J3 J

    / v; G6 m& E) e6 ^/ G0 [4 ]4 X
    1 Y7 B5 v( ^+ Q0 m+ m- w

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


    $ ]8 E+ g# `  D$ ^. x, `
    9 ^" W8 A, t* M% N: {- _8 L; y

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


    6 C& ?, {& {  d0 s( G

    & O+ w! |% h4 I. B$ V/ j) d

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

    2 A: \/ u" t" I) Q6 |- V' z
    $ J+ U" _, K0 @& [3 e

8.9   JLINK配置说明

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

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

    . [4 m$ J! z5 ?0 E

    ; U- U+ b1 v5 R5 Z. S

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

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

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

    2 o( E* I  w. e' k$ e6 y

    7 S4 ?5 O) i( R: G( V

8.10 STLINK配置说明

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

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

    ' ]& e- [; N& t- U
    2 n4 e% _$ b! d; r1 p0 L2 c7 m7 p

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

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


    : U8 e7 Z% K. a  E' D

    9 H+ {+ N4 O7 o3 q

8.11 CMSIS-DAP配置说明

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

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

    / ?9 |! Y- |% z" _+ F% c: _& L

    , L* c& }2 @; p5 ~/ A7 g( g

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

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

    5 M. Q. m( N$ U6 Y* ^" q
    ' c+ N; e. F/ X  I

8.12 ULINK配置说明

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

8.13 配套例子

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

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

    4 n; [2 Y7 u- I( d1 p

    5 K- q7 K1 Z( o9 `. N4 ?

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

  1. #include "bsp.h"
    # {( a) U0 J* I3 I

  2. * D* J4 N) M; _) b. K. x4 L: c7 H
  3. #include "EventRecorder.h"
    3 ]6 y# E* V' G4 z% C8 f$ T
  4. /* 定时器频率,500Hz */) E# z3 \! A# q( _4 I; f, H
  5. #define  timerINTERRUPT_FREQUENCY    500
    % B1 c  |- M5 w. u
  6. /* 中断优先级 */
      I3 o  B! e9 h6 P! ^0 k5 E
  7. #define  timerHIGHEST_PRIORITY       10
    + d. f, e! f9 x+ L: C
  8. /*
    - G* r: l. }1 m- l
  9. *********************************************************************************************************
    . G& z: K# ~" u5 Y3 w3 {% \
  10. *    函 数 名: vEventRecorderTest
    ( m  ]: u$ X$ B6 N" |# n6 a9 Y* \
  11. *    功能说明: 创建定时器% A* [- s4 V0 i) N% j) z) F6 ~
  12. *    形    参: 无& n0 x- f- p6 |8 q5 W9 D2 C4 H+ N
  13. *    返 回 值: 无) B. \' ]' ^" B) m2 ~1 `8 k
  14. *********************************************************************************************************. ]6 Z8 `5 h0 @; T  |  W2 M! N
  15. */
    ( R6 V3 Q6 T7 y/ `" w
  16. void vEventRecorderTest(void)
    ( J+ @9 }6 [: |. x: i- {
  17. {; n# @( A: h) z& y" T% @, O
  18.      bsp_SetTIMforInt(TIM6, timerINTERRUPT_FREQUENCY, timerHIGHEST_PRIORITY, 0);, k6 w  v; J' x4 P8 f
  19.      EventStartB(0);   
    - M5 K4 ^* O. H
  20. }
    ; \7 U$ ^4 Q! x* w
  21. 6 D6 J! L& l7 X' L( H' z( U$ P
  22. /*6 e9 ]2 T0 D% S- q
  23. *********************************************************************************************************
    8 N4 R* I, J2 a# N5 E% Q  B$ a
  24. *    函 数 名: TIM6_DAC_IRQHandler
    " C/ ~& G# Q6 L* O$ V
  25. *    功能说明: TIM6中断服务程序。6 w4 I& A$ w/ G0 l  e3 W
  26. *    形    参: 无
    : v; E* K& r; R8 h! R; k6 \( p; R
  27. *    返 回 值: 无
    * e( w2 U) a0 L8 H/ c3 O+ s
  28. *********************************************************************************************************- x6 F% D  O9 H7 g4 k% \
  29. */
    & r2 \. G# n9 {- t
  30. void TIM6_DAC_IRQHandler( void )  _; n$ l7 s1 G3 Y4 z
  31. {
    + ?$ G, d  E/ I, T5 G
  32.      if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
    " d0 h: p. A- l
  33.      {
    / s6 x9 j! n1 j+ W* R" n! E, l4 G
  34.          EventStopB(0);    & S/ g2 n: Z, w. \9 V
  35.          EventStartB(0);   
    ; J7 k3 G! o% z. t
  36.          /* 清除更新标志 */& |8 n, S7 k" V) x6 U2 f
  37.          TIM6->SR = ~ TIM_FLAG_UPDATE;9 |7 ?2 t/ ]" i1 L
  38.      }* M8 D5 U2 T; I
  39. }
复制代码

6 D! Y( N& b: E$ N2 B  N

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

应用程序的设计如下:

  1. #include "bsp.h"            /* 底层硬件驱动 */5 O1 _% k$ a# t) A2 H
  2. #include "EventRecorder.h", h4 C1 N7 U1 f6 P
  3. # p" I3 W2 Y- g0 _; F; f/ R
  4. /*
    ! C9 F3 C1 C6 D, o& \5 e+ X5 w" K
  5. *********************************************************************************************************: y- W; V) ?5 ^5 C$ w
  6. *                                              函数和变量2 h1 p1 {. J/ n+ ?; ^2 r
  7. *********************************************************************************************************
    7 Z3 A3 R2 K* Y4 u- I" g
  8. */
    $ c3 L& S* g: G3 j2 z7 A3 m& e
  9. extern void vEventRecorderTest(void);) S' \" ^, \  B$ U6 D; ]) O4 ~
  10. uint8_t s_ucBuf[10] = "armfly";) [& y1 h+ j# c' p( ~
  11. & u# R& J' y$ [# G: S! y9 j
  12. /*7 I9 ]( U4 f! y0 h) W: [4 X
  13. *********************************************************************************************************  e, K4 i! o3 n6 x6 j3 l, z5 c
  14. *    函 数 名: main7 E/ w8 Y& j8 U
  15. *    功能说明: c程序入口1 ~. v3 a5 G& b% O9 q9 c5 Q
  16. *    形    参:无
    - _* z! r! F& e: h8 S3 c
  17. *    返 回 值: 错误代码(无需处理)* o$ ?2 e8 q# x9 C7 F
  18. *********************************************************************************************************. ^+ b+ ?: ~6 C, h1 A% L
  19. */
    . q% M; r! p. A1 g' Z
  20. 9 C; s  i. d% j4 r& p" b! ]. K
  21. int main(void)+ r2 z4 D- o. @7 X' A" ~# x
  22. {
    ! x3 C* ~  K1 V6 Z
  23.      uint8_t ucKeyCode;     /* 按键代码 */1 S5 ]% N7 g! U9 f- \
  24. / K8 v4 Q; ~$ p' E; `1 D6 y3 l/ @
  25.      uint32_t t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;+ u# [% v1 d0 o4 ^5 J$ J% v" l7 ?
  26. 9 k( P  ~4 \1 B* Q+ q
  27.      /* 初始化EventRecorder并开启 */
    3 k! n' y. J- M8 S# B* `3 v; D

  28. 5 t8 q  \2 ]3 c( X
  29.      EventRecorderInitialize(EventRecordAll, 1U);
    . [0 |8 a, h- }5 C3 g  }& ~
  30. % o$ q$ O9 O) @, i1 g
  31.      EventRecorderStart();
    8 A/ b* a8 E( q6 p  i9 h
  32. 4 T8 t1 b) J) V( s" v
  33.      bsp_Init();        /* 硬件初始化 */3 J( D* C3 j( J( A8 S

  34. 6 Z, u2 C2 W; {* C& q% E6 |7 x
  35.      bsp_StartAutoTimer(0, 200); /* 启动1个200ms的自动重装的定时器 */
    6 q! ]  r( w! H

  36. 4 B! j1 C( j' B( l
  37.      /* 测量中断周期 */
    3 A3 c% p" K5 d1 f
  38.      vEventRecorderTest();
    4 J. ]- {2 r" D1 j2 z

  39. 0 z, L, W! }, r2 y
  40.      /* 进入主程序循环体 */1 ^$ w* p6 u1 h6 P6 u4 x% P( D
  41.      while (1)
    / |+ R' l) C9 c' T
  42.      {( K6 ]& A6 N9 Q6 L
  43.          bsp_Idle();        /* 这个函数在bsp.c文件。用户可以修改这个函数实现CPU休眠和喂狗 */; `. t" I' F* U" k' e3 r
  44. 8 X# b/ I3 S' p7 ?9 K
  45.          /* 判断定时器超时时间 */
    ( ?' o9 X- R# @) s: @
  46.          if (bsp_CheckTimer(0))
    # B4 X( g; a6 a
  47.          {
    3 ]: R2 D' d7 X" ?
  48.               EventStartA(0);  ! X6 k9 [3 S9 M4 x9 {! Y) n5 Y
  49.               EventStopA(0);( X4 z9 l. v: |* t/ @* D
  50.               EventStartA(1);
    " s: E& l& u* b. |! Q
  51.               bsp_DelayMS(5);
    ) v% O5 h) f/ m/ v: K
  52.               EventStopA(1);6 v( z( O  K( H' O6 K- ]' P2 V0 S
  53.               EventStartA(2);
    7 \5 o. H3 b, Y7 m- f3 Z
  54.               bsp_DelayMS(30);  J1 U) [8 W* Y
  55.               EventStopA(2);
    ( E: X# {% C! W" w8 m2 ^! I

  56. ) @' e& i! C& u  N4 ]3 X; D9 Y7 X
  57.               t0++;0 C4 `0 \' [9 J2 J1 Z! k) l  v
  58.               EventStartAv(3, t0, t0);
    $ D+ }& ?9 M. w8 K* R
  59.               bsp_DelayMS(30);* B' B, z6 U& m, o, B; Y$ E
  60.               EventStopAv(3, t0, t0);
    7 S* q- @- e1 o
  61.          }
    6 R7 l9 l' q$ Y: ~4 f0 L

  62. 2 p3 W: v! S. V- [
  63.          /* 按键滤波和检测由后台systick中断服务程序实现,我们只需要调用bsp_GetKey读取键值即可。 */
    " j' @8 o3 w1 S; K/ M  Q9 F' d( o
  64.          ucKeyCode = bsp_GetKey();   /* 读取键值, 无键按下时返回 KEY_NONE = 0 */, F0 @9 G5 u+ N& o" t# u* r
  65.          if (ucKeyCode != KEY_NONE), K# ?8 V% ]4 v2 T) R0 |6 Y8 D
  66.          {
    * _9 j& \5 ?" Q! g
  67.               switch (ucKeyCode)) B* X( H( |* P$ ^- O
  68.               {: Y5 `3 m6 x4 s& ]9 K
  69.                    case KEY_DOWN_K1:           /* K1键按下 */
    / f/ r2 T3 W6 w7 \
  70.                        t1 += 1;
    0 B% r! i9 V" k9 Y
  71.                        t2 += 2;" X# D: P9 }+ {! ^/ `3 G! d# M! |
  72.                        EventRecord2(1+EventLevelAPI, t1, t2);. I. o0 j& K+ n( }" _: e7 r! K
  73.                        t3 += 3;
    # ~' h% t4 f, V2 x; y. E
  74.                        t4 += 4;
    , }) R) [& [4 K( t- c4 e0 Q0 q& j: x: p
  75.                        EventRecord4(2+EventLevelOp, t1, t2, t3, t4);
    ; C7 r2 _, V" H$ C0 F
  76.                        EventRecordData(3+EventLevelOp, s_ucBuf, sizeof(s_ucBuf));
    / A. c- W; N6 x/ g, B
  77.                        break;: e0 W! b3 D& u. n$ R+ w# w
  78. ; i: _+ C+ p& D6 a; k
  79.                    case KEY_DOWN_K2:           /* K2键按下 */0 J+ V4 O, C# M0 v. u9 \4 N8 x
  80.                        printf("K2按键按下\r\n");" _0 [$ d6 t+ B, @4 w' P* y1 w( ^
  81.                        break;
    " C, p5 R! e; ~/ V

  82.   m3 T# H5 m3 h/ K! j
  83.                    case KEY_DOWN_K3:           /* K3键按下 */
    & v. D3 m& Y  [2 V7 t7 H
  84.                        printf("K3按键按下\r\n");
    : E6 G' P' k9 M0 P( V  h  R
  85.                        break;
    . y! W+ s0 Z) B; z

  86. 2 t: Z$ l; E) N6 M+ y" E
  87.                    default:
    # C% B  y% N! G# k
  88.                         /* 其它的键值不处理 */+ y1 J; \5 Z5 h- |+ B
  89.                        break;
    ( c/ E2 o3 R( W' d# I
  90.               }
    6 o4 S7 b8 X# @
  91.          }* W  Z9 F# W+ }( X' _6 T
  92.      }( A2 S! A+ \8 A" N
  93. }
复制代码


/ [$ ^3 w4 n* X8 c/ p. q

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

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

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

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

8.14 总结

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

  T+ e0 Q' i: s
赞 收藏 评论0 发布时间:2022-1-1 18:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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