STM32 使用 Keil MDK 中的软件逻辑分析仪参与硬件调试

1) Keil 评估软件:MDK 4.7x 和 MDK 5

MDK 5 以 Software Pack 的形式分发特定于处理器的软件、例程和中间件(middleware)。安装 MDK 5 之后,需要从网络上下载这些 Pack。这些 Pack 也可以手动导入。

MDK 4.7x 目前也是可用的。这个版本的 MDK 会包含程序运行所需要的所有文件,不使用 MDK 5 的 Software Pack。这篇文档的 MDK 4 版本可以在这个链接找到:http://www.keil.com/appnotes/docs/apnt_261.asp

Keil 网站上有 MDK 5 的 Legacy 支持软件,安装之后可以在 MDK 5 中使用 MDK 4 的工程,而不需要任何的 Software Pack。

我们建议使用 MDK 5.10 Software Pack,本文会持续跟进新的版本(译者注:并没有>_<,我在用5.17,如有区别会注明)。

Keil 针对各种 STM32 处理器有许多类似的实验手册,比如 CAN 的使用。详见www.keil.com/st

这篇文档针对 MDK 5.10 及更新版本。

2) 下载和安装 Keil 软件

  1. 从 Keil 网站上下载 MDK 5.10 或更新的版本:www.keil.com/mdk5/install Download MDK-Core

  2. 将 MDK 安装到默认路径。安装到其他路径也是可以的,本教程会以默认路径为例:C:\Keil_v5

  3. 本教程使用C:\MDK\做为例程目录,建议同样使用这个默认路径。

  4. 如果 MDK 安装在其他路径,实验中需要做对应的路径修改。

  5. (译者注:原文没有5,P.S. 上面这一段真贫)

  6. Keil 网站上可以找到 DSP5 例程。

  7. 这个实验可以用评估版本(MDK-Lite)来完成,不需要购买授权。

  8. 不需要额外的调试器,只需准备 NUCLEO-F401RE 开发板、USB 线,以及在电脑上安装 MDK 5.10 就可以了(译者注:原文使用的是 STM32F4-Discovery 开发板)。

这篇教程仅适用于板载的 ST-Link V2 调试器,后文将给出 ST-Link V2 的配置说明,及 ST-Link V2 驱动程序的测试。

4) 例程

MDK 5 Software Pack 里面有 Blinky 和 RTX_Blinky 例程,我们会用到其中的 Blinky。另外在本文最新版的网站上可以找到增强版的 RTX_Blinky5 和 DSP 例程(DSP5):http://www.keil.com/appnotes/docs/apnt_230.asp

5) MDK 5 入门

《Getting Started MDK 5》是一部很有用的手册,可以在 Keil 网站上找到:http://www.keil.com/mdk5/

6) μVision Software Pack 的下载和安装步骤

1) 启动 μVision 并打开 Pack Installer

在 MDK 刚安装完成的时候,如果电脑连接到了外网上,μVision 和 Software Pack 会自动启动。其他情况下,需要按照这里的步骤1)和2)来做。

(译者注:由于软件版本和板卡的区别,这一部分和原文略有不同。)

  1. 将电脑连接到外网上,下载 Software Pack 需要外网连接。

  2. 点击桌面上的图标 Keil图标 启动 μVision。

  3. 点击工具栏上的按钮 Pack Installer 按钮 打开 Pack Installer。如果出现 Pack Installer 欢迎界面,阅读之后关闭它。

  4. 这里应该出现下面这个窗口。在 Board 选项卡中,选择 NUCLEO-F401RE,右侧 Packs 选项卡中的列表会自动筛选。 
    Pack Installer

  5. 也可以在 Search 栏输入 NUCLEO 进行筛选。

  6. 注意:窗口右下角应该显示“ONLINE”。如果显示的是“OFFLINE”,请检查外网连接之后再继续下面的步骤。

  7. 如果在 Pack Installer 启动的时候没有连接到外网,列表中将不会显示内容。这时候需要在连接到网络后,点击菜单 Packs/Check for Updates 或者工具栏中的 Check for Updates 刷新一下。

2) 安装 STM32F4 Software Pack

  1. 点击 Packs 选项卡,可以看到 ARM::CMSIS Software Pack 是默认安装好的。

  2. 选择 Keil::STM32F4xx_DFP 和 Keil::STM32NUCLEO_BSP 并点击 Install,这两个 Software Pack 会下载并安装在默认文件夹 C:\Keil_v5\ARM\Pack\Keil 中。下载估计需要两三分钟的时间。

  3. 下载完成后,应显示“Up to date”状态:Up to Date

3) 安装 Blinky MDK 例程

  1. 选择 Examples 选项卡,出现如下窗口: 
    Examples

  2. 选择 Blinky (NUCLEO-F401RE)。

  3. 点击 Copy Copy

  4. Copy Example 窗口如图所示。选择 Use Pack Folder Structure,取消选择 Launch μVision。 
    Copy Example

  5. 输入 C:\MDK,点击 OK 完成 Blinky 工程的复制。

  6. Blinky 工程已经复制到 C:\MDK\Boards\ST\NUCLEO-F401RE 了。

  7. 不需要复制 CMSIS-RTOS Blinky 程序,我们会有一个更有意思的4线程版本。

    提示:复制例程时,一开始的默认路径是 C:\Users\< user >\Documents,这篇教程使用C:\MDK\ 做为默认路径。实际上可以选择任意路径。

  8. 关闭 Pack Installer。以后随时可以通过点击图标 Pack Installer 按钮 来打开。

提示:Update 按钮说明 Software Pack 有更新可以下载 Update

提示:如果找到目录C:\Keil_v5\ARM\Pack\Keil\STM32NUCLEO_BSP\1.4.0\Boards\ST\NUCLEO-F401RE,可以发现里面也有一个 Blinky。这个版本是只读的,做为备份用。请使用从 Pack Installer 的 Examples 选项卡复制出来的工程,本教程中存放在 C:\MDK

4) 从 Keil.com 上安装 RTX_Blinky5 和 DSP5 例程

  1. 从 http://www.keil.com/appnotes/docs/apnt_230.asp 上获得例程的 zip 文件。

  2. 解压缩到 C:\MDK\Boards\ST\STM32F4-Discovery\。(译者注:这个程序是对 Discovery 开发板的。)

  1. 用 USB 线将 NUCLEO 开发板连接到电脑上。

  2. 如果 ST-Link USB 驱动程序安装正确,应该可以听到正常的 USB 设备连接的系统声音。否则需要根据下一章的指示,手动安装驱动程序。

  3. 两个红色 LED 应该点亮:LD1 (COM)和 LD3(PWR)。

  4. 启动 μVision Keil图标 并选择 Project/Open Project。

  5. 选择 Blinky 工程 C:\MDK\Boards\ST\NUCLEO-F401RE\Blinky\Blinky.uvprojx

  6. 在这个地方选择 STM32F401 Flash:Project Option

  7. 点击 Target Options Target Options 或者按 ALT+F7,并选择 Debug 选项卡: 
    Debugger Selection

  8. 点击 Settings,会出现下面所示的窗口。如果显示出了 IDCODE 和 Device Name,说明 ST-Link 工作良好,可以继续下面的教程。点击两次 OK 返回 μVision 主界面。 
    Target Setup

  9. 上图中 Serial Number 框里出现一串数字说明 ST-Link 调试器已经连接到了 μVision。

  10. 如果 SW Device 框中没有显示,或者显示 error,这个问题在继续本教程之前必须要解决。请参照下一节内容:安装 ST-Link USB 驱动程序。

  11. 如果已经显示正常,ST-Link USB 驱动程序安装良好。点击两次 OK 退出 Target Options 窗口并从第9章继续。

提示:在上图 Port 框中选择 JTAG,然后在选回 SW,可以刷新 SW Device 框的显示。也可以关闭再重新打开这个窗口。

提示:ST-Link V2 和 ST-Link 的主要区别是添加了 Serial Wire Viewer (SWV) 跟踪功能。

如果上面的测试没有问题,则不需要进行这一步。

安装 ST-Link USB 驱动程序:(当上面的测试没有通过时)

  1. 先断开 NUCLEO 开发板和电脑之间的 USB 连接。

  2. 需要手动运行 stlink_winusb_install.bat 来安装 USB 驱动,这个文件存放在C:\Keil_v5\ARM\STLink\USBDriver 中。找到这个文件并双击来安装驱动程序。

  3. 连接 NUCLEO 的 USB,USB 驱动程序应该会以正常的方式安装完毕。

重要提示:ST-Link V2 固件升级程序 ST-LinkUpgrade.exe 存放在 C:\Keil_v5\ARM\STLink。如果要更新 ST-Link 固件,找到并双击这个程序,操作还是比较简单的,程序会检查并报告当前的固件版本。请注意我们需要使用 V2.J16.S0 或更高的版本来支持 SWV 操作。另外不要使用 V2.J19.S0,这一版本与最新版驱动程序不兼容。 
(译者注:V2.J19.S0 不兼容估计是个历史问题,我已经在用 MDK 5.17 和 V2.J24.M11 版本了。)

COM LED LD1 的含义:

LED 红色闪烁:电脑开始 USB 枚举,但还未完成。 
LED 红色:电脑和 ST-LINK/V2 之间已经建立连接(枚举结束),但 μVision 还没有连接到 ST-Link (比如 Debug 模式)。 
LED 绿色:μVision 以 Debug 模式连接到 ST-Link,上一个通信是成功的。 
LED 红色、绿色间隔闪烁:μVision 和目标之间正在交换数据。 
LED 熄灭,但当进入 Debug 模式或点击 RUN 时闪烁一下:μVision 的 SWV 跟踪已启动。 
LED 熄灭:ST-LINK/V2 与目标 MCU 或 μVision 之间的连接失败,需要重新连接电源以重启开发板。

9) 使用 NUCLEO 开发板运行 Blinky 例程

我们将使用板载 ST-Link V2 调试器连接 Keil MDK 开发平台和真实的目标硬件。

  1. 点击桌面图标 Keil图标 启动 μVision,用 USB 线将 NUCLEO 开发板的 CN1 连接到电脑上。

  2. 选择 Project/Open Project,打开文件 C:\MDK\Boards\ST\NUCLEO-F401RE\Blinky\Blinky.uvprojx

  3. ST-Link 会默认被选择。第一次运行 μVision 和 NUCLEO 开发板的时候,可能需要安装 USB 驱动程序,详见上面章节。

  4. 点击 Rebuild 图标 Rebuild 编译源文件。也可以用旁边的 Build 图标 Build

  5. 点击 Load 图标 Load 对 STM32 的 flash 进行编程。Output 窗口中会显示这个过程。

  6. 点击 Debug 图标 Debug 进入 Debug 模式,如果出现 Evaluation Mode 对话框,点击 OK。 
    注意: 当下载到 flash 时才需要使用 Load 图标,如果选择的是 RAM 运行则不需要。

  7. 点击 RUN 图标 RUN。注意:点击 STOP 图标 STOP 可以停止程序运行。

NUCLEO 开发板上的绿色 LED 会开始闪烁。 
按下开发板上蓝色的 USER 按键会暂停闪烁。

至此你已经了解如何编译工程、下载到 STM32 处理器的 flash 中、运行并停止程序!

注意: Blinky 程序已经永久的烧写到 flash 中了,开发板可以独立运行这个程序,直到下一次被烧写。

10) 硬件断点

STM32F4 共有六个硬件断点,可以在程序运行过程中随时设置或取消。

  1. 在 Blinky 程序运行过程中,打开 Blinky.c 文件,点击 main() 函数中 for 循环里的某一行左侧边缘深灰色区域。

  2. 会出现一个红色的圆形标志,程序会停止运行。

  3. 请注意断点同时显示在源代码窗口和反汇编窗口,如下图。 
    断点

  4. 不论是反汇编窗口还是源代码窗口,左侧边缘显示深灰色方形的区域表示这些代码行存在汇编指令,可以在这里设置断点。

  5. 每次点击 RUN 图标 RUN,程序会运行到下一次遇到断点。

  6. 可以尝试点击 Single Step(Step In)Step In 、Step OverStep Over 和 Step OutStep Out 。

提示: 如果单步调试(Step In)不工作,点击 Disassembly 窗口使它成为焦点,可能需要点击一行反汇编代码。这样操作表示想要汇编级别的单步运行,而不是 C 语言代码级别。

提示: ARM CoreSight 的断点是 no-skid 的,硬件断点发生在被设置断点的指令执行之前(译者注:有 skid 的断点的意思是,程序停止在断点设置的指令甚至后面几个指令执行之后)。另外 flash 中烧写的指令不会被替代或修改,这的特性对于高效率软件开发有重要意义。

完成这个实验后,再次点击这些断点以删除它们,为后面的实验做准备。

提示: 可以通过点击断点,或者选择 Debug/Breakpoints(或按 Ctrl+B)并选择 Kill All 来删除。

提示: 可以通过选择 Debug/Breakpoints 或按 Ctrl+B 来查看所有断点的设置。

11) Call Stack + Locals 窗口

局部变量

Call Stack + Locals 窗口被合并在一个集成窗口中,每当程序停止时会显示调用栈和当前函数的所有局部变量。

如果可能,局部变量的值会显示,否则显示 < not in scope >。菜单中的 View/Call Stack Window 用来切换 Call Stack + Locals 窗口显示或隐藏。

  1. 运行并停止 Blinky,点击 Call Stack + Locals 选项卡。

  2. 下图展示了Call Stack + Locals 窗口。 
    Call Stack + Locals 
    窗口中显示了当前活动函数的名称和局部变量列表。随同每个函数的名字会显示它被哪个函数或中断/异常调用。 
    当函数退出时,会从列表上移除。 
    最早调用的函数会出现在列表的底端。 
    这个列表只有在程序停止运行时有效。

  3. 点击 Step In 图标 Step In 或按 F11 键。

  4. 当单步运行到不同的函数时,观察它们在窗口上显示的变化。如果陷入到 Delay 函数的循环当中,可以用 Step Out Step Out 或 Ctrl+F11 键快速退出。

  5. 点击几次 Step In,观察其他函数。

  6. 右键点击一个函数名,尝试 Show Callee Code 和 Show Caller Code 选项。 
    Right Click Function Name

  7. 点击 Step Out 图标 Step Out 退出所有函数,返回 main()。

提示: 如果单步调试(Step In)不工作,点击 Disassembly 窗口使它成为焦点,可能需要点击一行反汇编代码来执行汇编级别的单步运行。如果焦点在源代码窗口上,则是执行 C 语言代码级别的单步运行。

提示: 可以在程序停止运行时,通过 Call Stack + Locals 窗口来修改变量的值。

提示: 上述是标准的“Stop and Go”调试过程。ARM Coresight 调试技术还可以做很多更强大的事情,比如在程序运行中显示并实时更新全局或静态变量,而不需要修改程序。由于局部变量通常存储在 CPU 寄存器中,不能在程序运行时实时显示,需要转换成全局或静态变量使得作用域不会消失。

如果借助 ULINK pro 和 ETM 跟踪,可以记录所有指令的执行情况。Disassambly 和 Source 窗口是按编写的顺序显示代码的,而 ETM 跟踪可以按执行的顺序显示。另外 ETM 还提供 Code Coverage、Performance Analysis 及 Execution Profiling 等功能。

把局部变量转换成全局或静态变量通常意味着把它从 CPU 寄存器移动到 RAM 上,CoreSight 在程序运行中可以观察 RAM,但不能观察 CPU 寄存器。

调用栈

如上面可以看到的,当程序停止运行时,函数按栈的方式显示在列表中。当想要了解栈中有哪些函数被调用、存储的返回值是什么的时候,这个功能就很有用。

提示: 可以在程序停止运行时,通修改局部变量的值。

提示: 点击菜单 Debug/Breakpoints 或按 Ctrl+B 键可以查看 Hardware Breakpoint 列表,同时这也是配置 Watchpoint (观察点,也叫 Access Point)的地方。在这个列表里可以临时屏蔽某些项目。点击 Debug/Kill All Breakpoints 会删除断点,但不会删除观察点。

12) Watch 和 Memory 窗口及其使用方法

Watch 和 Memory 窗口实时显示变量的值,这是通过 ARM CoreSight 调试技术实现的,这项技术是包含在 Cortex-M 处理器中的一部分。同时,也可以在这些存储器地址上实时地“put”或插入数值。这两个窗口都可以通过拖拽变量名,或者手动输入来添加变量。

Watch 窗口

添加全局变量: 除非程序停止在局部变量所在的函数,否则 Watch 和 Memory 窗口不能观察局部变量。

  1. 停止运行处理器 STOP 并退出 Debug 模式 Debug

  2. 在 Blinky.c 的第24行左右,声明一个全局变量(这里变量名叫做value):unsigned int value = 0;

  3. 在第104行左右添加语句 value++; 和 if (value > 0x10) value = 0;,如图: 
    value Codes

  4. 选择菜单 File/Save All 或点击 Save All 。

  5. 点击 Rebuild Rebuild ,点击 Load Load 下载到 flash。

  6. 进入 Debug 模式 Debug ,点击 RUN RUN ,提示:可以在程序运行中设置 Watch 和 Memory 窗口。

  7. 在 Blinky.c 中,右键点击变量 value 并选择 Add value to … 及 Watch 1,Watch 1 窗口会打开并显示value 如下图。

  8. value会实时增加到0x10。 
    Watch 1 
    提示: 也可以框选 value,单击并拖拽到 Watch 或 Memory 窗口。 
    提示: 请确认菜单 View/Periodic Window Update 在选中状态。

  9. 也可以在 Name 下面区域双击或按 F2,再手动输入或复制粘贴变量名。或是打开菜单 View/Symbols Window 来输入变量。 
    提示: 如果要拖拽到一个非活动的选项卡,选中变量按住鼠标并移动至希望打开的选项卡名字上,等待其打开并拖拽鼠标到窗口内释放。

Memory 窗口

  1. 右键点击 value 并选择添加到 Memory 1,或手动添加 value 到 Memory 1。如果需要的话,选择菜单 View/Memory 来打开 Memory 1 窗口。

  2. 注意 value 被当作了一个指针,其值做为地址显示在了 Memory 1 上。这个操作在想要看一个指针指向的地址的时候很有用,但不是我们现在想看到的。

  3. 在变量名前面加一个“&”符号并按回车键,改为显示变量的物理地址(0x2000000C)。

  4. 右键点击 Memory 1 窗口并选择 Unsigned/Int。

  5. value 的值现在以32位的形式显示了。

  6. Watch 和 Memory 窗口都是实时更新的。

  7. 在 Memory 窗口中,鼠标移动到数据区域右键点击,选择 Modify Memory,可以修改对应地址的值。 
    Memory

提示: 这些操作通常不会占用 CPU 周期。关于 DAP 是如何运行的,详见下一章“原理”。

提示: 在 Debug 模式中选择菜单 View/Symbol Window,可以打开 Symbol 窗口查看变量和各自的位置。

上面展示的 Memory 和 Watch 窗口的操作并不需要配置 Serial Wire Viewer (SWV),这些机制使用的是 SWV 之外的另一个 CoreSight 特性。CoreSight Debug Access Port (DAP)通过 Serial Wire Debug (SWD)或 JTAG 连接来处理读写操作,从而实现实时在线的存储器访问。

13) 如何在 Watch 和 Memory 窗口中观察局部变量

(译者注:由于板卡的区别,这一部分和原文略有不同。)

  1. 运行 Blinky.c 程序。我们将使用 main() 中的局部变量 num

  2. 在 Blinky.c 的第87行附近,main 函数的开头,找到声明这个局部变量的地方。

  3. 右键点击这个变量,把它输入到 Watch 1 窗口中。注意由于局部变量的值可能存放在 CPU 寄存器内,μVision 不能在程序运行时访问,因此会显示 < not in scope >。如果 μVision 显示无法添加变量,请尝试停止再开始 Blinky 程序。 
    Watch Local Variables

  4. 在 Blinky.c 的主循环里添加一个断点,会使程序停止,这时会出现当前变量的值。

  5. 删除这个断点。

  6. 局部变量或自动变量可能存放在 CPU 寄存器内,μVision 不能在程序运行时访问。局部变量 num 只有在main 函数运行时才会存在,在其他函数或中断/异常处理程序中是不存在的,因此 μVision 无法确定这个变量的值。

  7. 停止运行处理器 STOP 并退出 Debug 模式 Debug 。

如何实时更新局部变量:

只需把 num 改为全局变量定义在 Blinky.c 中。

  1. 把 num 的声明移动到 main() 的外面、Blinky.c 的最前面,把它改为全局变量:
<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">unsigned</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> value = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
int32_t num = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li></ul>

提示: 也可以定义成静态变量,如: static int32_t num = 0; 。 
提示: 在编辑模式和 Debug 模式都可以编辑文件,但编译只能在编辑模式进行。

  1. 点击 Rebuild 按钮编译源文件,确认没有 error 和 warning。

  2. 点击 Load 按钮 Load 下载到 flash,窗口的左下角会显示进度条。

  3. 进入 Debug Debug 模式并点击 RUN RUN 。

  4. 这时 num 变量已经可以实时更新了。

  5. 可以读(写)全局或静态的变量、结构体,以及其他以变量形式放在函数与函数之间的东西,包括外设的读写。(译者注:这一句我没翻好:You can read (and write) global, static variables and structures. Anything that stays around in a variable from function to function. This include reads and writes to peripherals.)

  6. 停止运行处理器 STOP 并退出 Debug 模式 Debug ,准备下一个练习。

提示: 菜单中的 View/Periodic Window Update 需要选中,否则变量只在程序停止时更新。

原理

μVision 使用 ARM CoreSight 技术实现不窃取 CPU 周期的情况下对存储器位置进行读写。这种操作几乎是完全非侵入的,不影响程序本身运行时序。我们知道 Cortex-M4 是哈弗架构的,具有分离的指令总线和数据总线。当 CPU 以最大速度取指令时,CoreSight 调试模块有大量的时间读写数值,并不影响 CPU 周期。

有一种罕见情况,当 CPU 和 μVision 恰好同时读写相同的内存地址时,CPU 会暂停一个时钟周期,表现出轻微的侵入性。实际上可以认为这种窃取周期的情况不会发生。

14) 使用 Logic Analyzer 图形化观察变量

这一章将在 Logic Analyzer 中显示全局变量的值。这个功能使用 Serial Wire Viewer,因此不会窃取 CPU 周期,用户代码中也不需要加入任何代码片段。

配置 Serial Wire Viewer (SWV):

  1. 停止运行处理器 STOP 并退出 Debug 模式 Debug 。

  2. 点击 Target Options Target Options 或者按 ALT+F7,并选择 Debug 选项卡,点击窗口右侧的 Settings,确认选择的是 SW 模式。ST-Link中,SWV 强制使用 SW 模式。

  3. 选择 Trace 选项卡,选择 Trace Enable,取消选择 Periodic 和 EXCTRC,设置 Core Clock 为 48 MHz。其他选项如图所示。

    译者注:CPU 时钟频率很重要,请务必按照实际值设置。Blinky 工程的时钟频率在 Abstract.txt中可以找到。如果使用的是其他板卡,请自行检查时钟频率。

  4. 点击 OK 返回 Target Options。

  5. 再次点击 OK 返回主界面。 
    Trace Setup

配置 Logic Analyzer:

  1. 进入 Debug 模式 Debug。打开菜单 View/Analysis Windows 并选择 Logic Analyzer,或者选择工具栏上的 LA 按钮 LA 按钮 。 
    提示: 可以在程序运行中配置LA。

  2. 点击 Blinky.c 选项卡,右键点击 value 变量并选择 Add value to… 及 Logic Analyzer。也可以手动拖拽来添加。

  3. 点击 LA 左上角的 Setup… 按钮进入 LA 设置窗口。

  4. 选择 value,设置 Display Range Max 为 0x15。

  5. 点击 Close。 
    LA Setup

运行程序: 注意: 可以在程序运行中配置LA。

  1. 点击 RUN RUN。点击 Zoom Out 使图形中网格大小是5秒左右。

  2. 变量 value 的值将增加到 0x10(十进制16)再重新设为 0。 
    提示: 如果没有看到波形,请退出并重新进入 Debug 模式以刷新 LA。可能还需要重新上电 NUCLEO 板卡。请确认 Core Clock 的数值正确。 
    提示: Logic Analyzer 中最多可以显示4个变量,必须是全局变量、静态变量或原始地址如*((unsigned long*)0x20000000)

  3. 请注意当 USER 按钮按下时,变量的值会随之停止增加。当然也请注意观察这个现象是如此直观。

  4. 选择 Signal Info、Show Cycles、Amplitude 及 Cursor,观察 LA 的测量功能。点击 Update Screen 栏的 Stop 按钮可以停止 LA。

  5. 停止运行处理器 STOP 。 
    LA波形

15) 观察点:条件断点

不需要使用 Serial Wire Viewer

STM32 处理器有6个硬件断点,可以不停止 CPU 运行而在线设置。STM32 还有四个观察点。观察点可以认为是有条件的断点。观察点和逻辑分析仪共用比较器,因此要使用观察点,需要在逻辑分析仪中保证至少两个变量空位置。观察点也被称作 Access Breakpoint。

  1. 使用与前一练习相同的 Blinky 设置,停止程序运行,保持在 Debug 模式。

  2. 在 Blinky.c 中创建的全局变量 value 将用于观察点的探究。

  3. 配置观察点不需要 SWV Trace,但在这个练习中我们会用到逻辑分析仪。

  4. 延续上一个联系,变量 value 应保持在逻辑分析仪中。

  5. 在 μVision 菜单中选择 Debug 并点击 Breakpoints,或者按 Ctrl+B 键。

  6. 将 Read 和 Write 两个 Access 都选中,在 Expression 框中输入“value == 0x5”,不含引号。

  7. 点击 Define,应出现图中所示的一行。点击 Close。 
    Watchpoint Setup

  8. 如果 Watch 1 中没有 value 变量,添加一个进去。

  9. 打开菜单 Debug/Debug Settings 并选择 Trace 选项卡,选择“on Data R/W sample”,并保持 EXCTRC 不选中。

  10. 点击两次 OK 回到主界面。打开 Trace Records 窗口。 
    Trace Records 按钮

  11. 点击 RUN RUN 。

  12. 在逻辑分析仪和 Watch 窗口中可以看到 value 在变化。

  13. 当 value 等于 0x5 时,Watchpoint 会停止运行程序。

  14. 注意如下图所示的 Trace Records 窗口中的 data write 记录:0x5 在 Data 列的最后一行,另外还有写入数据的地址和写入指令对应的 PC 指针。当前使用的是 ST-Link,如果用 ULINK2 会显示相同的窗口,但如果是 ULINK pro 或 J-Link(黑盒子)显示的窗口会略有区别。 
    Trace Records

  15. 查看 Breakpoints 窗口中的 Help 按钮可以了解另一种可用的表达式格式,有些目前没有在 μVision 中实现。

  16. 点击 RUN 可以重复本次练习。

  17. 练习结束后,停止程序运行,点击 Debug 并选择 Breakpoints(或按 Ctrl+B),删除所有断点。

  18. 退出 Debug 模式。

提示: 观察点不能像硬件断点那样在程序运行中设置。

提示: 在 Breakpoints 窗口中双击观察点,它的信息会放在下面的配置区域供编辑。点击 Define 会创建另一个观察点。如要删除旧的观察点,需要选中它并点击 Kill Selected,或者尝试下一条提示:

提示: 表达式旁边的选框可以用来临时屏蔽观察点。

提示: 逻辑分析仪中也可以输入原始地址,如:*((unsigned long*)0x20000000)

图中显示了变量 value 的触发点为 0x5 时,逻辑分析仪的显示情况。这里运行了三次。

LA with watchpoint

16) 基于 Keil RTX RTOS 的 RTX_Blinky 例程

Keil 提供了一个全功能的 RTOS —— RTX,做为 Keil MDK including 源代码的一部分。本章的例子将探索 RTX RTOS 工程。MDK 可以和任何 RTOS 配合使用,实际上 RTOS 只是和你的工程一同编译的一组 C 语言函数。RTX 包含在所有版本的 MDK 中,以 BSD 开源协议的形式提供。

译者注:原文附带提供了一个专门的 RTX_Blinky 程序,是四个任务控制 Discovery 开发板上的四个 LED。这个工程使用了 STM32F4xx_DFP 2.1.0 软件包,在新版本的 Keil MDK 中是不提供的(Keil 网站上也没有这个版本的下载链接),难以完成这个实验。 
这一章翻译版中,将用新版本的 pack 中包含的 RTX_Blinky 程序替代。其他操作与原文相同。 
原文的四任务程序确实更能够体现 Keil MDK 调试模式在观察 RTX 工程上的便利性,有兴趣的话可以参考阅读。

RTX 及其所有组件放在:C:\Keil_v5\ARM\Pack\ARM\CMSIS\3.20.4\CMSIS_RTX

按照第6章的指示,安装并复制 RTX_Blinky 工程到 C:\MDK\Boards\ST\NUCLEO-F401RE\

  1. 在 μVision 的编辑模式(非 debug 模式)中选择 Project/Open Project。

  2. 打开文件 C:\MDK\Boards\ST\NUCLEO-F401RE\RTX_Blinky\RTX_Blinky.uvprojx

  3. 如果出现 Update Configuration Files 窗口,选择 Cancel。

  4. 这个工程已经为 ST-Link V2 调试器设置好了。

  5. 点击 Rebuild 图标 Rebuild 编译源文件,应该不会出现 error 或 warning。

  6. 点击 Load 图标 Load 手动编程 flash。左下角的进度条会显示这个过程。

  7. 点击 Debug 图标 Debug 进入 Debug 模式。点击 RUN 图标 RUN

  8. NUCLEO 开发板上的绿色 LED 会开始闪烁。

  9. 点击 STOP 图标 STOP 。

我们将借助 Kernel Awareness 窗口来探索 RTX 的运行。

17) 使用 Serial Wire Viewer (SWV)的 RTX Kernel Awareness

用户经常需要了解当前执行的任务的编号和其他任务的状态,这些信息一般被 RTOS 存储在一个结构体或内存空间中。Keil 为 RTX 提供了 Task Aware 窗口,其他 RTOS 公司同样也提供 μVision 中的对应的 awareness 插件。

  1. 点击 RUN 图标 RUN 运行 RTX_Blinky。

  2. 打开菜单 Debug/OS Support 并选择 System and Thread Viewer,图中所示的窗口将打开。可以拖住这个窗口并移动至屏幕中间。窗口中的数值是实时更新的,使用的读写技术与 Watch 和 Memory 窗口相同。 
    重要提示: 需要选中菜单中的 View/Periodic Window Update。

  3. 打开菜单 Debug/OS Support 并选择 Event Viewer。由于还没有配置 SWV,估计不会有数据显示。

System and Thread Viewer

**RTX Viewer:配置 Serial Wire Viewer (SWV):

需要激活 Serial Wire Viewer 来使 Event Viewer 运行。

  1. 停止运行处理器 STOP 并退出 Debug 模式 Debug 。

  2. 点击 Target 选择框旁边的 Target Options Target Options ,选择 Debug 选项卡。

  3. 选择 ST-Link Debugger 旁边的 Settings。

  4. 在 Debug 窗口中,确认选择的是 SW 模式而不是 JTAG。SWV 只能运行在 SW 模式。

  5. 点击 Trace 选项卡。

  6. 设置 Core Clock 为 48 MHz,并选中 Trace Enable。

  7. 取消选择 Periodic 和 EXCTRC,如图所示。

    译者注:CPU 时钟频率很重要,请务必按照实际值设置。Blinky 工程的时钟频率在 Abstract.txt中可以找到。如果使用的是其他板卡,请自行检查时钟频率。

  8. 需要选中 ITM Stimulus Port 31,RTX Viewer 使用这个方法获得内核 awareness 信息并显示在 Event Viewer 中。这个功能有轻微的侵入性。

  9. 点击两个 OK 返回主界面。 
    Trace Setup 
    Serial Wire Viewer 配置完成!

  10. 进入 Debug 模式并点击 RUN。

  11. 选择 Task and System 选项卡,显示应该在更新。

  12. 点击 Event Viewer 选项卡。

  13. 如图所示,窗口中以图形的形式显示任务事件。可以尝试点击 Zoom 栏的 ALL、+ 和 - 来调整 Grid 到 1 秒左右,或调整到 20us 左右查看每次 LED 状态变化时的任务切换细节。 
    Event Viewer
    Event Viewer

提示: 如果 Event Viewer 不工作,打开 Trace Records 并确认 ITM 31 帧显示正常。Core Clock 是否正确?这个工程运行在 48 MHz(NUCLEO-F401RE)。

Cortex-M3 警告: 利用上面提到的读、写方式,μVision 会实时更新目标板卡的所有 RTX 信息。Event Viewer 使用 ITM,有轻微的侵入性。

数据在程序运行的时候更新,并不需要在源代码中插入专门的观察代码。你会发现这个特点非常好!记住,所有版本的 MDK 中都提供 RTX 的源代码。

提示: 这些 RTX Kernel Awareness 窗口中的功能可以使用 ULINK2、ULINK-ME、ULINK pro、ST-Link V2 及 J-Link。

18) Logic Analyzer 窗口:图形化地实时观察变量

μVision 包含图形化 Logic Analyzer 窗口,在 STM32 中可以利用 Serial Wire Viewer 实时显示多达四个变量。RTX_Blinky 使用一个任务来生成 LED 的闪烁,我们可以画出这个波形的图像。

  1. 关闭 RTX Viewer 窗口,停止运行处理器 STOP 并退出 Debug 模式 Debug 。

  2. 如图,在 RTX_Blinky.c 中添加一个全局变量 unsigned int led_status

  3. 如图,在 RTX_Blinky.c 的 blinkLED 任务中添加 2 行:led_status = 1;led_status = 0;,大约在第 70 行左右的位置(在 LED_On 和 LED_Off 函数调用的前面)。 
    RTX_Blinky.c

  4. 选择菜单 File/Save All 或点击 Save All 。

  5. Rebuild 工程 Rebuild ,烧写 flash Load 。

  6. 进入 Debug 模式 Debug 。

  7. 现在可以运行程序了 RUN 。

  8. 打开菜单 View/Analysis Windows 并选择 Logic Analyzer,或者选择工具栏上的 LA 按钮 LA 按钮 。

  9. 点击 RTX_Blinky.c 选项卡,右键点击 led_status 变量并选择 Add value to… 及 Logic Analyzer,led_status 变量将添加到 LA。 
    提示: 如果不能添加变量到 LA,请确认 Trace Config 设置正确。要添加变量到 LA 必须开启 Serial Wire Viewer。 
    Logic Analyzer 可以显示静态或全局的变量、结构体和数组。 
    不能显示局部变量:请转换为静态或全局变量。如果要观察外设寄存器,请使用变量读取或写入,然后把变量添加进 LA。

  10. 点击 LA 左上角的 Setup… 按钮,设置 Display Range Max 为 0x3。

  11. 点击 Close 回到 LA 窗口。

  12. 使用 All、Out 和 In 按钮使图形中网格大小是 0.5 秒左右。如果需要的话,移动滚动条到最右边。

  13. 选择 Signal Info 和 Show Cycles,点击以标记一个位置,移动光标得到时序信息。在波形上放置光标以获得时序和其他信息,如图。

  14. 点击 Update Screen 栏的 Stop 按钮可以停止数据采集。

RTX Logic Analyzer

提示: 也可以在 Watch 和 Memory 窗口中添加变量,实时显示或修改。

提示: 可以显示表示变量的数学运算的信号,这在物理世界中是无法测量的。

21) Serial Wire Viewer(SWV)配置窗口(供参考)

跟踪功能的配置主要是在图中所示的 Trace 选项卡中完成的。μVision 中没有全局的 SWV 选项,对每个工程、以及工程中的每个 target 设置,都需要单独配置 SWV,配置信息存储在工程中。这个窗口有两种方法进入:

A. 在编辑模式中: 选择 Target Options Target Options 或者按 ALT+F7,并选择 Debug 选项卡。点击窗口右侧的 Settings,再选择 Trace 选项卡。启动 μVision 时默认是编辑模式。 
B. 在 Debug 模式中: 选择菜单中的 Debug/Debug Settings,再选择 Trace 选项卡。Debug 模式用 Debug 按钮进入。

SWV 配置窗口

1) Core Clock: SWV 的 CPU 时钟频率。在工程的启动代码或者 Abstract.txt 中可以找到 CPU 时钟频率,通常叫 SYSCLK 或主时钟频率。除非使用的是 ULINK pro 调试器,否则这个选项必须填写正确。

2) Trace Enable: 使能 SWV 和 ITM。这个选项只能在编辑模式修改。不会影响 Watch 和 Memory 窗口的更新。

3) Trace Port: 使用 ST-Link 时,这个选项是不可更改的。

4) Timestamps: 使能时间戳,选择预分频器。预分频器默认是1。

5) PC Sampling: 对 PC 指针的采样: 
   a. Prescaler: 1024*16(默认值)指的是每 16,384 个 PC 指针会显示 1 个,其他的不会采集。 
   b. Periodic: 使能 PC 指针采样。 
   c. On Data R/W Sample: 显示造成逻辑分析仪中所列的变量的读写对应的指令地址。这个功能与数据跟踪有关,但与 PC 采样无关。

6) ITM Stimulus Ports: 使能用于在 μVision 中输出数据的类似 printf 的语句的 32 位寄存器。端口 31(a)用于 Keil RTX Viewer,实时内核识别(awareness)窗口;端口 0(b)用于 Debug (printf) Viewer;其他位在 μVision 中没有使用。 
   Enable: 显示 32 位十六进制数,代表哪些端口是使能的。 
   Privilege: Privilege 用于 RTOS 指定哪些 ITM 端口可以在用户程序中使用。

7) Trace Events: 使能各种 CPU 计数器。除了 EXCTRC 之外,其他的都是 8 位计数器。每个计数器是累积的,每 256 周期产生计数器溢出事件。Counter 窗口显示计数器的值,Instruction Trace 窗口显示计数器溢出并重新计数产生的事件。 
   a. CPI: 从第一个指令到每个指令所用的额外周期数,其中包括指令取指拖延(instruction fetch stall)。 
   b. EXC: CPU 的异常(exception)开销的累积周期数,包括入栈和返回操作,不包括花费在异常处理程序的时间。 
   c. Sleep: CPU 在睡眠模式的累积周期数,使用 FCLK 时钟。 
   d. LSU: 从第一个周期开始,花费在 load/store 上的累积周期数。 
   e. Fold: Folded 指令的累积数量。这个结果来自那些已经从流水线上移除(flush)了无用指令,从而导致零执行周期的预测分支指令。(译者注:我架构学的烂,这句不太会翻:These results from a predicted branch instruction where usused instructions are removed (flushed) from the pipeline giving a zero cycle execution time.) 
   f. EXCTRC: 异常跟踪。这一项与上面其他项有所不同,不是一个计数器。这个选项使能 Trace Exceptions 窗口中异常的显示。这个功能通常在调试中使用,用来显示异常。

提示: 计数器在单步执行是也会累加,这提供了很有用的信息。计数器是映射在存储器上的,可以在程序中读取。

  • 17
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值