手把手教你使用MDK仿真调试

news2024/11/16 8:25:04

当今的嵌入式系统开发领域中,高效的调试工具对于工程师来说至关重要。它们能够极大地减少开发周期中的错误追踪时间,并加速产品的上市时间。MDK作为业界领先的嵌入式开发工具之一,其内置的调试功能被广大开发者所赞誉。这些功能不仅提供了对代码执行的深入洞察,还允许开发者在实时环境中监控和修改系统行为。通过本文,我们将一起探索 MDK 的调试功能如何成为嵌入式开发者的得力助手,以及它如何助力我们构建更加稳定、高效的嵌入式应用。

1. 源码下载及前置阅读

  • STM32F103C8T6模板工程

链接:https://pan.baidu.com/s/1n7XHCaMYtASWdJH2uA5yDA?pwd=lw59 提取码:lw59

  • MDK5安装包

链接:https://pan.baidu.com/s/1j7USS7-rsmr7-GZ_BIIEYQ?pwd=5q3s 提取码:5q3s

  • 芯片固件包

链接:https://pan.baidu.com/s/1Td21MOEshL7qE4afCBSjtw?pwd=86uh 提取码:86uh

如果你是个零基础的小白,连 STM32 都没见过,我也给你准备了一个保姆级教程,手把手教你搭建好 STM32 开发环境,并教你如何下载程序,简直业界良心!

零基础快速上手STM32开发(手把手保姆级教程):https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html

如果你连代码都不知道怎么烧录到 STM32 的,可以参考下文,提供了 5 种代码烧录方式:

STM32下载程序的五种方法:https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html

新手小白如果连 MDK 的使用都不熟悉,那么可以通过下文先熟悉一下 MDK 的使用:

一文教你使用MDK开发工具:https://www.lxlinux.net/e/stm32/mdk-development-tool-tutorial.html

2. MDK仿真调试配置

在 MDK 仿真前,我们需要对其进行环境配置。

  • 打开 Keil5 软件,打开或者新建一个工程。

  • 点击 Options for Target,也就是我们俗称的魔法棒。

  • 选择 C/C++ 选项卡,在进行仿真时,我们需要将 MDK 的优化调成 Level 0 等级。

  • 点击 Debug 选项卡,把「Load Application at Startup」和「Run to main」勾上,

Load Application at Startup 是在启动调试时是否加载应用程序,如果此选项去掉则不会自动将程序下载到单片机,直接调试。如果此选项打勾则每次进入调试前先下载应用程序,然后进入调试。

Run to main() 可以使程序执行到 main() 函数。进入调试模式后,程序自动运行到 main 函数处。

  • 打开 Settings ,可以看到关于仿真器的设置,可以在这里配置仿真器。默认情况下,大部分都是自动配置好的,无需额外修改。

  • 我这里使用的是 ST-Link ,所以选的是 ST-Link 。如果你的 ST-Link 正常且插在电脑上了,右边 SW Device 会正常显示,表示仿真器与开发板连接成功了。

    Debug Adapter 是你下载器的型号,这里要选择你使用的下载器型号。

    Serial 是指仿真器的序列号,当你选择完仿真器之后,Serial 才会显示你下载器的序列号。

    在下面的 Version:FW 是下载器的版本号,本文使用的下载器是 ST-Link V2 所以显示的 HW 是 V2 。

    Port 是选择仿真器的调试模式,ARM 芯片有两种调试模式,分别是 SW 和 JTAG 。

    相对于 JTAG 模式,SW 占用更少的信号线,而且功能一样支持代码下载与调试,所以强烈推荐使用 SW 调试模式。

如果 ST-Link 没有插上或设备异常,则会提示 No ST-Link detected。

  • 最后打开 Utilities 选项卡将 Use Debug Driver 打勾,再点击 OK 确定一下,MDK 仿真的调试配置就完成了。

3. 各个调试按钮的作用

接着我们编译一下代码。再点击这个像放大镜一样的按钮就可以开始仿真了(这个按钮同时也可以退出仿真)。

进入仿真之后的界面如下图示。

界面左边显示寄存器的地址和程序运行时间,上方是汇编语言窗口,需要一定的汇编基础才能看得懂。界面左下方是命令窗口,这个窗口会显示一些打印信息,也可以在这个窗口输入一些命令。右下角的窗口是关于函数及变量在内存中的地址信息。

在左上方有一排关于调试的小按钮:

它们的功能从左到右分别是:复位、全速运行、停止、进入函数、执行过此函数、跳出函数、执行到光标处、显示下一个将运行的代码

  • 复位:重新执行程序;
  • 全速运行:开始执行程序;(快捷键为 F5 )
  • 停止:停止执行程序;
  • 进入函数:进入当前行代码中;(快捷键为 F11 )
  • 执行过此函数:执行当行代码;(快捷键为 F10 )
  • 跳出函数:跳出当前程序代码;(快捷键为 Ctrl+F11 )
  • 执行到光标处:自动执行代码至蓝色光标处;(快捷键为 Ctrl+F10 )
  • Show Next Statement:显示下一行即将要执行的程序。

这里我以流水灯作为案例来详细的说明一下各个按钮的使用方法,使用的单片机型号为 STM32F103C8T6 最小系统板。连接 ST-Link 上电,接线图如下:

ST-Link V2STM32
SWCLKSWCLK
SWDIOSWDIO
GNDGND
3.3V3V3

在 STM32F103C8T6 最小系统板上有个自带的小灯,这颗 LED 灯连接在 PC13 管脚上。

首先我们需要写一个小灯的初始化程序,这里可以不用封装,直接写在 main.c 里面。

#define LED_CLK()       __HAL_RCC_GPIOC_CLK_ENABLE()
#define LED_GPIO        GPIOC
#define LED_PIN         GPIO_PIN_13

void led_init(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    LED_CLK();                                              /* IO口时钟使能 */

    gpio_initstruct.Pin = LED_PIN;                          /* LED0引脚 */
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;             /* 推挽输出 */
    gpio_initstruct.Pull = GPIO_PULLUP;                     /* 上拉 */
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;           /* 高速 */
    HAL_GPIO_Init(LED_GPIO, &gpio_initstruct);              /* 初始化LED0引脚 */
}

主函数代码如下:

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    delay_init(72);                     /* 延时初始化 */
    led_init();                         /* LED初始化 */
    while(1)
    { 
        HAL_GPIO_WritePin(LED_GPIO,LED_PIN,GPIO_PIN_SET);  /*使LED灭掉*/
        delay_ms(500);
        HAL_GPIO_WritePin(LED_GPIO,LED_PIN,GPIO_PIN_RESET);/*使LED亮起*/
        delay_ms(500); 
    }
}

点击放大镜进入仿真界面,然后再点击左上角的开始按钮(或者使用快捷键 F5 )开始执行代码。

可以看到 STM32F103C8T6 最小系统板上的 PC13 小灯在不断闪烁。这时候我们在 19 行打上一个断点,再点击全速运行按钮:

可以看到当前程序运行到了第 19 行然后暂停了,STM32F103C8T6 最小系统板上的 PC13 小灯则会保持长亮。

(需要注意的是,调试停在中断处的时候,内核会停止,但是外设将会继续运行)

接着我们在 21 行再打上一个断点,然后再点击全速运行按钮。

会发现代码执行到了 21 行就停下了,这时 STM32F103C8T6 最小系统板上的 PC13 小灯会灭掉。

我们点击右上角复位的按钮,然后选中 led_init(); 这行代码,点击执行到光标处按钮(或者快捷键 Ctrl+F10 ),将代码执行至这一行。

(需要注意必须要退出调试模式再重新进入调试模式,否则代码会一直在死循环当中执行,导致代码无法执行到你想要执行的那行)

这时候我们点击进入函数按钮(或者快捷键 F11 ),则进入到 LED 的初始化函数当中。

我们可以看到当前程序执行到了 LED 的初始化函数中。这时,点击「执行过此函数按钮」(或者快捷键 F10 ),如下图示。

点击一下上图按钮,程序将执行一行代码。而且,如果当前行是函数,将不进入此函数内部代码,直接执行完函数代码并进入到下一行。

如果你不想查看 led_init() 函数里的代码了,你可以点击「跳出函数按钮」(或者快捷键 Ctrl+F11 ),可以按钮可以直接跳出当前代码程序,准备执行下一行。

(跳出函数的话会默认执行完当前程序代码,然后跳到下一行)

4. 查看程序段/函数执行的时间

在代码中,我们经常会使用延时函数。那么,我们如何确定延时函数真的延时了我们所设置的时间呢(比如 500ms)?

我们可以通过代码调试功能来确定延时时间的准确性。

首先打开魔法棒,在 Debug 中打开 Settings 选项。

再点击 Trace 选项卡,可以找到时钟频率。

默认情况下的时钟频率是 8MHz ,这里我们改成 72MHz 。

我们选中 20 行,然后使用「执行到光标处按钮」(快捷键为 Ctrl+F10 )运行至 20 行。如下:

在左下方的 Sec 表示程序运行了多久,单位是秒( s ),我们使用「执行过此函数」(快捷键为 F10 )执行一下延时函数,看看延时函数是否准确。结果如下:

使用此结果减去初始的时间,可以看到大约延时了 0.5s 左右,也就是 500ms ,可以认为延时函数的时间是非常准确的。

5. 工具栏常用窗口按钮介绍

在调试按钮的左侧有一排窗口按钮,用于打开各种调试窗口(也可以通过菜单栏的 View 打开)。

  • 第一个 Command Window 按钮,可以打开 Command 窗口,这个窗口可以显示一些打印的信息,还能输入命令。

  • 第二个按钮是 Disassembly Window ,这个窗口显示了汇编语言。你可能需要一些汇编语言的基础才能看得懂,这个窗口显示的光标表示下一条即将运行的汇编语言是什么。

  • 第三个按钮是 Symbols Window ,这是符号窗口,用于查看一些符号类型。本文暂时不对这个窗口进行深入学习。

  • 第四个按钮是 Registers Window ,这是一个寄存器窗口。这个窗口可以直观的查看一些内核的寄存器,还有程序运行时间等等。

  • 第五个按钮是 Call Stack Window ,它是用于查看函数调用关系 & 局部变量。本文学习的是仿真工具一些基础的使用,所以不对这个窗口进行深入学习。

  • 第六个按钮是 Watch Windows ,用于查看函数首地址以及变量的值。

Watch 窗口还可以设置变量在被读或写后自动停止运行,对于代码调试非常有帮助。可以定义一个函数,将该函数的名称输入到窗口中,然后向函数传入一个数,在这个窗口你可以看到该函数的寄存器首地址,传完数之后你可以看到该数的类型是什么。

点击该按钮,弹出的界面如下:

举个栗子来演示一下这个过程。例如,在程序中定义一个全局变量 int temp; ,在 while 循环中执行 temp++ 。右击 temp ,选择 Add "temp" to... 添加到 Wacth 1 。

在左下方的 Watch 1 窗口就可以看到 temp 的首地址和定义类型了。如下图:

这里我们使用「执行过此函数按钮」(快捷键为 F10 ),执行一遍程序,会发现 temp 的值发生了改变,执行了一次 temp++ 。如下图:

Wacth 窗口不止这些功能,Wacth 窗口还可以设置变量在被读或写后会自动停止运行。这个功能在调试行数比较多的项目非常有用,因为在很多场景下,有个变量有可能在几十上百处被修改,如果没有这个功能,排查变量被修改将变得非常困难。

那么如何设置这个功能呢?

首先右击 temp ,会出现一个列表,点击 Set Access Breakpoint at "temp" 。如下图:

这时候会弹出一个窗口,在这个窗口你可以设置函数是否要在被读或者被写的自动停止运行。设置完之后点击 Define 就行了。

Wacth 窗口对于我们代码调试还是有非常大的作用,大家可以好好掌握。

  • 第七个按钮是 Memory Windows ,可用于查看内存的地址与值。界面如下图示:

同样的,我们举一个例子。

首先我们在程序中定义一个 uint8_t 类型的数值 temp[10] ,然后给数组的前五个数赋值 {1,2,3,4,15} ,其他元素默认值为 0 。在循环中我们进行 temp[0]++ ,只对第一个元素自增。

编译一下,打开仿真工具,再打开Memory 窗口,在窗口中输入 temp ,这样我们就可以看到 temp 数组在内存中的地址与值。前面我们给数组赋值 {1,2,3,4,15} ,在 Memory 窗口可以看到 temp 数组被十六进制表示出来 “01 02 03 04 0F” 。如下图:

(注意,M3/M4/M7内核是小端模式,内存的值要倒着读)

Memory 窗口可以帮助我们快速的查看变量在内存中的地址和值,便于我们调试程序。

  • 第八个按钮是 Serial Windows ,这是用于查看串口的窗口。

  • 下面这个按钮是 Peripheral 窗口,于查看寄存器的值。Core Peripherals 是内核寄存器,其他的都是外设寄存器。如下图:

    打开之后是这个样子的(我这里打开的是 GPIOC )。如下图:

    在这里你可以看到一些寄存器的值,如果你想看某个值,你可以点击左边的 + 号,在 + 号中有更详细的信息。

    通过这个窗口,可以查看到自己配置寄存器是否正确,确保外设能按照你的预期运行。

6. 小结

总的来说,想要将单片机学的更好,MDK 仿真工具就必不可少。MDK 仿真工具不仅功能强大,而且操作起来的难度也不高,对新手也十分的友好,最重要的是它能够帮助我们更好地理解和调试程序代码,学好使用 MDK 仿真工具,可以让你的技术更上一层楼。


另外,想进大厂的同学,一定要好好学算法,这是面试必备的。这里准备了一份 BAT 大佬总结的 LeetCode 刷题宝典,很多人靠它们进了大厂。

刷题 | LeetCode算法刷题神器,看完 BAT 随你挑!

有收获?希望老铁们来个三连击,给更多的人看到这篇文章

推荐阅读:

  • 程序员必备编程资料大全
  • 程序员必备软件资源

欢迎关注我的博客:良许嵌入式教程网,满满都是干货!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1408448.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PostgreSQL 是不是大小写敏感

如果你踩过 MySQL 的大坑的话就知道:MySQL 在 Windows 下不区分大小写,但在 Linux 下默认是区分大小写。 如果你稍加不注意就会出现在本机开发的程序运行一切正常,发布到服务器行就出现表名找不到的问题。 这是我们前一个项目遇到的巨大问题…

Python中函数的参数有哪些?

目录 ①必选参数 ②默认参数 ③可变参数 ④关键字参数 ⑤命名关键字参数 学习python函数部分的时候,发现除了正常定义的必选参数外,还使用默认参数、可变参数、关键字参数和命名关键字参数,在这里理清楚这些参数具体是怎么回事吧&#x…

nvm 配置淘宝镜像失效,以及安装node后 npm-v 无效

win11 nvm版本 1.1.4 和1.1.7和1.1.12(目前最新版本24年 一月二十三日) 以上nvm版本都会出现一下问题, 从https://github.com/coreybutler/nvm-windows/releases 下载nvm安装包如下图 傻瓜式安装后,不用去配置环境变量&#…

动态IP代理与静态IP代理:详细区别与比较全析

动态代理IP和静态代理IP在跨境业务中具有非常广泛的实用性,但仍然有非常多小白选手并不清楚什么场景适合用哪一类IP,哪一中代理IP类型更适合你?其实他们各有其优点和缺点,为了使您的网络营销、社媒推广、跨境电商运营、网络抓取尽…

C#用DateTime.DaysInMonth(Int32, Int32) 获取指定月的天数

目录 一、DateTime.DaysInMonth(Int32, Int32) 方法 二、实例 1.实例1 2.实例2 3.方法3 一、DateTime.DaysInMonth(Int32, Int32) 方法 返回指定年和月中的天数。 public static int DaysInMonth (int year, int month);参数 year Int32 年。month Int32 月&#…

「JavaSE」抽象类接口2

🎇个人主页:Ice_Sugar_7 🎇所属专栏:快来卷Java啦 🎇欢迎点赞收藏加关注哦! 抽象类&接口2 🍉接口间的继承🍉接口的应用🍉总结 🍉接口间的继承 和类的继承…

Java--异常处理

文章目录 主要内容一.练习11.源代码代码如下(示例): 2.结果 二.练习21.源代码代码如下(示例): 2.结果 三.练习31.源代码代码如下(示例): 2.结果 总结 主要内容 一.练习1 编写程序,定义一个 cir…

FRRouting学习(一) 配置日志文件

以配置isis event事件日志为例 1、在配置之前,/var/log/frr路径下是没有文件的: 2、在vtysh config之下输入:log file /var/log/frr/isisd.log debugging 后面的debugging表示日志级别,可以根据自己修改 3、配置好了之后&#xf…

力扣hot100 两两交换链表中的节点 双指针

Problem: 24. 两两交换链表中的节点 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Code /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { …

力扣hot100 环形链表 快慢指针 哈希 数学公式

Problem: 142. 环形链表 II 文章目录 思路Code 思路 👨‍🏫 参考题解 Code ⏰ 时间复杂度: O ( n ) O(n) O(n) 🌎 空间复杂度: O ( 1 ) O(1) O(1) /** /*** Definition for singly-linked list.* class ListNode {* int val;* …

微信小程序如何做到高效引流?-数灵通

随着微信小程序制作门槛的降低,大量的小程序如雨后春笋般涌现,其中小程序商城类型尤为众多。然而,由于微信自身的流量有限,对于大多数小程序商城而言,获取足够的流量成为了一大挑战。 针对这一问题,我们可以…

如何在Windows 10/11中设置IPv4?这里有详细步骤

在Windows上,路由器的动态主机配置协议(DHCP)服务器(通常)负责将动态传输控制协议/互联网协议(TCP/IP)配置分配给网络中的每个设备,包括运行Windows 11或Windows 10的计算机。 尽管…

链表OJ----相交链表找交点

https://leetcode.cn/problems/intersection-of-two-linked-lists/description/ 1、长链表先走,然后二者一起走 由于两个链表可能不一样,就不好控制移动。那么我们可以让长的链表先走二者的长度差的长度,使剩下部分和短链表一样长。然后二者一…

操作无法完成,因为文件已在Windows资源管理器中打开,如何解决?以及如何将哔哩哔哩下载好的视频导出到电脑中播放?— 以vivo手机为例

前言 想删除流氓软件的时候,提示操作无法完成,因为文件已在Windows资源管理器中打开,但打开任务管理器,似乎又没有符合的正在执行的程序,更别说打开让人看到头疼的资源监视器了,本文将用一招解决如上问题 …

【RF FILTER 仿真】滤波器 Ansys Electronics not ADS

第一,声明 全网搜索,用这个HFSS继承的介绍非常少,并且没有什么指导意义。所以有必要写一下,就像之前的xpedition,总要挑战一下吧。本文仅仅和大家学习研究,对比ADS体会一下差别。 第二,记录直接开始&…

微信小程序如何解决botton按钮对齐问题

如果botton不在其他控件下层&#xff0c;或者上层控件类型不为flex&#xff0c;可以用float调整botton显示位置&#xff0c;如floatright&#xff0c;则botton显示在右侧。 <button type"primary" style"float: right;">测试</button><vie…

什么是JMeter?我们为什么要用JMeter做性能测试

什么是JMeter&#xff1f;我们为什么要用JMeter做性能测试 什么是JMeter&#xff1f;为什么选择JMeterJMeter的优点JMeter是如何工作的 什么是JMeter&#xff1f; Apache JMeter TM是纯Java开源软件&#xff0c;最初由Apache软件基金会的Stefano Mazzocchi开发&#xff0c;旨在…

运维神器Ansible的常用模块

引言&#xff1a;话不多说&#xff0c;今天分享一下Ansible的常用模块&#xff0c;建议收藏哦 1、ping模块 ping模块可以进行主机连通性测试 命令格式 ansible 主机或主机组 -m ping 例&#xff0c;成功显示如下&#xff1a; 2、command 模块 command模块可以直接在远程主机…

条件语句及if语句,case语句

文章目录 条件语句一、测试1、概述格式 2、文件测试2.1 格式2.2 常用操作符 3、比较整数数值3.1 格式3.2 常用操作符 4、字符串比较4.1 格式4.2 常用操作符 5、逻辑测试&#xff08;短路运算&#xff09;5.1 格式5.2 常用操作符 6、双中括号6.1 格式6.2 说明 二、if语句1、单分…

DA14531平台secondary_bootloade工程修改笔记

DA14531平台secondary_bootloade工程修改笔记 1.支持在线仿真 初始时加入syscntl_load_debugger_cfg(); 表示可以重复Jlink连接调试仿真 2.支持串口烧录&#xff0c;和支持单线线写 utilities\secondary_bootloader\includes\bootloader.h /************** 2-wire UART supp…