颠覆认知!打了多年的单片机调试断点,到底该怎么设置?

21ic电子网 2020-03-03 00:00

作者:鱼鹰Osprey

来源:鱼鹰谈单片机

导读:程序运行过程中,有些数据被莫名修改了,在哪里修改的?又是怎么修改的?这个代码我只想知道是否运行过,或者运行了多少次,但是不想让程序停下来,或者仅打印调试信息,怎么办?当这个变量设置成某个数据后,我想让程序自动暂停下来进行分析,怎么办?


以上问题的所有答案就在本节内容:断点窗口(KEIL)。


本节内容将颠覆你之前对断点调试的认知。这个调试技巧鱼鹰也用了半年多了,当时知道这个调试方法的时候特别兴奋,感觉发现了新大陆。而这个调试技巧也在鱼鹰接手公司项目代码的时候快速解决了不少疑难杂症,而前些天又扩展学习了这个技巧的功能,更是让鱼鹰在学会之后轻松解决了好几个一般调试方法很难解决的 BUG,相信这个技巧也将为鱼鹰之后的开发调试之旅发挥更大的作用。


我们知道常规的断点调试是在想观察哪里的问题时就在对应的代码地址设置断点,并且一旦运行到断点位置会让程序自动暂停运行,这种断点调试功能确实为开发者解决 bug 立下了汗马功劳,但是这种方式有很大的局限性,因为很多时候我们并不需要让程序停下来,而只想知道是否在这段代码运行过,或者说发生问题的位置根本不能停下来,否则就会让整个系统功能出现问题,比如中断处理函数的调试,程序一旦停下了也就失去了所有中断的后续响应;比如两个设备通信,一方采用常规断点的方式调试,肯定会打断正常的通信过程,而这可不是我们想要的,我们只想知道在收到或发送数据后得到环境快照,而并不想让程序停下来。以上这些问题可以采用打印方式解决,但是打印调试也有很多弊端:


以串口为例:

1、你必须添加必要的打印和串口驱动代码,如果你使用 printf 函数,你还得重定向(如果对空间要求高的话,你得知道使用 printf 差不多要占用 1K 大小代码空间)。


2、如果打印效率比较低,常规波特率 9600 和 115200 打印一个字符串耗时可能比较久,那么对于中断频率较高的函数就可能就不适用了。如果你使用 printf 函数,你还得考虑函数是否可重入问题。


3、在代码中引入调试代码有风险,本来程序运行没有问题的,一旦引入调试代码之后可能就出现了问题,这种情况对于拥有丰富开发经验的人来说应该见怪不怪了。原因就在于打印输出时间太久,打乱了程序运行的节奏(而这也是我推荐使用 ITM 调试的一个原因,因为它的输出效率比串口要高得多),或者打印函数本身有问题,也会导致程序运行出现问题。


4、调试完毕之后,你必须把对应的调试代码删除(不管是删除代码还是使用宏,都要进行这一步),不然会影响运行效率。而人是健忘的(也不能说健忘,可能只是因为专注于 BUG 本身,容易忘记其它细枝末节,而解决 bug 之后的欣喜更可能忘记后续处理工作了)这个时候你可以尝试用 #warnning。但是这一步还是必不可少。


而以上问题的解决方案就是 KEIL 的断点调试窗口!


首先打开数据观察点的窗口:

快捷键是 Ctrl + B。

可以看到如下窗口:

当然你也可以通过下面这种方式打开并设置:

从这里你会发现,其实这个窗口就是用来管理你设置的断点的。平常使用的设置断点方法只是其中的一种特例罢了。

首先要知道的就是,调试器支持的断点数量是有限的,具体有多少视情况而定,一旦 KEIL 警告你设置断点太多,那么就要删除一些断点了:


常规用法

1、代码位置运行次数

有些时候我们想知道某些代码的运行次数,比如进入中断处理函数的次数,寻常的断点设置方式必然会让程序停止在中断程序中,但有些时候我们并不希望它停下来。这个时候,你只需要打开该窗口,找到已有的对应断点位置,双击之后就可以看到类似下面的窗口:

此时,你将 Count 的值设置的尽可能大一些,那么就可以让程序运行多次之后才停止。


比如我们设置 Count 的值为 100 次,那么必须在该代码位置运行 100 次才会让程序暂停。当你设置完后点击【Define】后,就会询问你是否需要重新定义,你选择“是”即可。

这样你的断点变成了这样:

后面的 count=100 表示剩余运行次数为 100,运行 100 次后将停止程序。前面的 00 代表断点号,E 代表这是一个执行断点,0x080016B0 代表代码地址,后面的是源码位置。


当这个断点位置运行了 2 次,重新打开该窗口(刷新数据),发现这个数变成了 98,从而可以推算出,已经运行了多少了。如果说你想让这段代码运行 2 次后停止,那么你只需要一开始设置 Count 的值为 2 即可。


2、数据访问

有些时候我们需要知道一些变量会在哪里被访问,那么你可以设置该变量的访问条件。比如鱼鹰想知道 emOspery 变量会在哪里被读取?那么你只需设置如下:

定义之后就是这样:

因为 Count 值设置为 1,所以每一次读取 emOspery 的操作都将使程序停止。比如这段代码:

还有后面的打印函数也使用 emOsprey 变量,所以也会导致程序运行停止。可能你会感到奇怪,为什么 emOsprey++这样的操作也会涉及到读取?事实上你理解了 CPU 寄存器存在的意义也就明白了。


而当你设置为写(Write)访问时,你会发现从复位程序开始运行后,程序会停止在某个地方,这是为什么?当你知道全局变量会在进入 main 函数之前被初始化时,你也就明白为什么了。


在这里我们选择使用 Objects 访问,即按整个变量对象进行访问,上面的 emOsprey 变量实际上是 uint16_t,所以 len 为 2,即字节大小。也就说,如果你设置为 Objects 访问,那么它会根据实际的情况设置访问范围。


为了更好的说明这一点,我构造一个结构体。

这个结构体大小可以看出是 6 个字节。


然后设置访问该结构体的条件:

如果我们按 Objects 访问的话,那么下面的每一条语句都会导致程序运行的停止。

这是因为这些数据都在 Osprey 结构体的范围内(从这里也可以了解到,只要在 len 的范围内的访问都会导致程序停止运行,所以你可以试试将 Size 设置得更大)。


而如果设置为 Byte 访问的话,那么就只有第一条语句才会导致程序停止运行:

实际上如果你希望只在某个结构体成员变量被访问时才停止,那么直接这么设置就可以:

你会发现设置是如此之简单。


实际上还有一种更为通用的访问方式,即按地址访问。


上面可以看出 Ospery.Ospery1 成员变量的地址为 0x20000016(由此我们知道也可以通过这个来看出一个结构体变量的地址是多少)。所以我们可以这样设置:

而代码位置的断点设置亦是如此。


断点太多,怎么知道程序因何停止?看你的命令窗口就知道了:


3、数据匹配

有些时候,我们并不关注地址访问情况,而对变量的数据内容感兴趣。比如说鱼鹰想让变量emOspery 等于 1 时停下来,怎么设置?

只要简单的设置 emOspery == 1 即可(注意必须设置访问条件,并且 Size 设置正确)。


事实上你也可以设置两个变量相等作为条件:


设置为不等也是可以的:

当然还有其它支持的运算就靠你们自己去发现了(可支持运算:&,&&,<,<=,>,>= ,==,!=)。


注意:以上内容可以组合使用,比如读、写条件,计数器计数等可以同时设置。满足条件时就会让程序运行停止。


高级用法

以上为比较常规的调试功能,现在说说鱼鹰刚学习的技能,这个技能的使用灵活性更大,而且对于解决疑难杂症更是不二之选。


首先设置一个你需要的断点:

打开断点窗口,并双击你之前设置的断点:

设置 Command 为【printf(“USRAT_Init()\n”)】(注意\n,否则可能不能输出,这个应该是 KEIL 的一个 bug)。最后【Define】

清空你之前的命令(如果你不嫌乱的话,也可以不清空):

那么你的程序每次运行到这个代码位置都会在Command 窗口输出一条信息:


但是你的程序并不会停止。


如果说你想让断点代码位置运行多次之后才输出一条信息也是可以的,只要设置Count 即可。


这里可能你会问,这 printf 不就是我们写的打印函数吗?事实上,是,也不是。


这个函数是打印函数没错,但是这是 KEIL 调用的打印函数,输出位置是 Command 窗口,和你自己写的代码没一点关系,每次触发条件时KEIL 都会调用该函数进行打印,而不会让你的程序暂停运行。事实上这个 Command 绝不仅仅只是设置 printf这么简单,如果真是这样我也不会如此推崇它了,感兴趣的可以去官网查找关于调试命令的使用方法。


因为是利用 KEIL 去执行打印任务,所以对你的程序几乎没有任何影响,并且在你设置断点后也不用担心删除代码问题,可以放心饮用。还有一个额外的好处就是,对于所有能设置调试断点的单片机都适用,因此对于调试器也就没有过多的要求了,比如说,不管你是用JLINK、ST-LINK 还是CMSIS-DAP(CMSIS-DAP 不能使用ITM,所以鱼鹰才会想着用别的方式替代。总算是找到了,而且它在某些方面更出色),都可以这么用。

现在摘录官网一些关于断点窗口的知识:


表达式定义断点类型:

§当设置标志 Read 或 Write 或两者时,访问中断(A)被定义 。发生指定的内存访问时会触发断点。以字节为单位指定内存访问窗口的大小,或者以表达式的对象大小指定。对于此断点类型,Expression 必须解析为内存地址和内存类型。允许的运算符(&,&&,<。<=。>,> =,= =和!=)在程序执行暂停或执行命令之前比较变量值 。


§当 Expression 解析为代码地址时,将执行执行中断(E)。到达指定的代码地址时触发断点。代码地址必须引用 CPU 指令的第一个字节。


§当 Expression 不能简化为地址时,定义条件中断(C)。当条件表达式变为 TRUE 时,断点将触发。在每条 CPU 指令之后重新计算条件表达式,并且会大大减慢程序执行速度。


该计数值指定的次数的断点表达式必须计算为 TRUE 断点触发之前的数目。


当命令被指定的μVision 执行语句,然后恢复执行程序。此处指定的命令可以是μVision 调试或信号功能。要从这些函数中暂停程序执行,请设置系统变量 _break_。


注意

当在模拟器中将访问断点(读或写)设置为外设寄存器(SFR)时,即使应用程序未访问外设寄存器,断点也可能触发。发生这种情况是因为μVision 模拟器在应用程序驱动和模拟器内部访问之间没有区别。


里面有一个比较关键的就是关于条件中断(C),如果你设置的表达式不是一个代码地址,也没有设置读写访问条件,那么就会被设置为条件中断,一旦设置为条件中断,那么会在每条汇编指令后计算表达式,这会影响程序正常运行速度,所以没有必要的话,不要设置为条件中断。


设置断点的一般错误总结:

当弹出以下窗口时,说明断点设置错误,需要查看命令窗口才能知道具体错误信息。

a)      断点太多

删除一些断点即可


b)     重复定义断点

这是因为之前你已经定义了这个断点,而现在你又定义了这个断点,这个时候你可以选择覆盖之前的断点或者保留之前的断点


c)      不允许对同一个资源设置不同类型断点

这个是由于对同一个资源准备设置不同断点导致的,需要删除之前的设置的断点才行。


d)     表达式错误

检查你的表达式是否正确,注意如果你使用了运算符,那么对于浮点变量的支持好像并不正常,不管你怎么设置,都说表达式错误。


到此,断点窗口(前期我叫它数据观察点,我也不知道从哪看到的这个词,后来觉得还是断点窗口比较准确)的内容就结束了。这个小节内容对于调试而言绝对是一大利器,也是鱼鹰决定写这个KEIL 调试系列文章的主要原因。但是以上所有的调试内容都有一个很大的局限性,就是它只能定格在某一刻(如果你使用Command 命令就不一样了),而这一刻前面的所有信息都无法知晓。这个时候就要了解另一个调试技能,ITM,它能将程序从出生(复位程序开始)到死亡(死循环或者断电)的大部分信息记录下来。这个章节内容早已发布,感兴趣的就去前面看一看咯。 

21ic电子网 即时传播最新电子科技信息,汇聚业界精英精彩视点。
评论
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 737浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 162浏览
  • 书接上回:【2022年终总结】阳光总在风雨后,启航2023-面包板社区  https://mbb.eet-china.com/blog/468701-438244.html 总结2019,松山湖有个欧洲小镇-面包板社区  https://mbb.eet-china.com/blog/468701-413397.html        2025年该是总结下2024年的喜怒哀乐,有个好的开始,才能更好的面对2025年即将
    liweicheng 2025-01-24 23:18 45浏览
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 830浏览
  • 故障现象 一辆2007款日产天籁车,搭载VQ23发动机(气缸编号如图1所示,点火顺序为1-2-3-4-5-6),累计行驶里程约为21万km。车主反映,该车起步加速时偶尔抖动,且行驶中加速无力。 图1 VQ23发动机的气缸编号 故障诊断接车后试车,发动机怠速运转平稳,但只要换挡起步,稍微踩下一点加速踏板,就能感觉到车身明显抖动。用故障检测仪检测,发动机控制模块(ECM)无故障代码存储,且无失火数据流。用虹科Pico汽车示波器测量气缸1点火信号(COP点火信号)和曲轴位置传感器信
    虹科Pico汽车示波器 2025-01-23 10:46 97浏览
  • Ubuntu20.04默认情况下为root账号自动登录,本文介绍如何取消root账号自动登录,改为通过输入账号密码登录,使用触觉智能EVB3568鸿蒙开发板演示,搭载瑞芯微RK3568,四核A55处理器,主频2.0Ghz,1T算力NPU;支持OpenHarmony5.0及Linux、Android等操作系统,接口丰富,开发评估快人一步!添加新账号1、使用adduser命令来添加新用户,用户名以industio为例,系统会提示设置密码以及其他信息,您可以根据需要填写或跳过,命令如下:root@id
    Industio_触觉智能 2025-01-17 14:14 159浏览
  • 2024年是很平淡的一年,能保住饭碗就是万幸了,公司业绩不好,跳槽又不敢跳,还有一个原因就是老板对我们这些员工还是很好的,碍于人情也不能在公司困难时去雪上加霜。在工作其间遇到的大问题没有,小问题还是有不少,这里就举一两个来说一下。第一个就是,先看下下面的这个封装,你能猜出它的引脚间距是多少吗?这种排线座比较常规的是0.6mm间距(即排线是0.3mm间距)的,而这个规格也是我们用得最多的,所以我们按惯性思维来看的话,就会认为这个座子就是0.6mm间距的,这样往往就不会去细看规格书了,所以这次的运气
    wuliangu 2025-01-21 00:15 398浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 209浏览
  • 数字隔离芯片是一种实现电气隔离功能的集成电路,在工业自动化、汽车电子、光伏储能与电力通信等领域的电气系统中发挥着至关重要的作用。其不仅可令高、低压系统之间相互独立,提高低压系统的抗干扰能力,同时还可确保高、低压系统之间的安全交互,使系统稳定工作,并避免操作者遭受来自高压系统的电击伤害。典型数字隔离芯片的简化原理图值得一提的是,数字隔离芯片历经多年发展,其应用范围已十分广泛,凡涉及到在高、低压系统之间进行信号传输的场景中基本都需要应用到此种芯片。那么,电气工程师在进行电路设计时到底该如何评估选择一
    华普微HOPERF 2025-01-20 16:50 137浏览
  • 现在为止,我们已经完成了Purple Pi OH主板的串口调试和部分配件的连接,接下来,让我们趁热打铁,完成剩余配件的连接!注:配件连接前请断开主板所有供电,避免敏感电路损坏!1.1 耳机接口主板有一路OTMP 标准四节耳机座J6,具备进行音频输出及录音功能,接入耳机后声音将优先从耳机输出,如下图所示:1.21.2 相机接口MIPI CSI 接口如上图所示,支持OV5648 和OV8858 摄像头模组。接入摄像头模组后,使用系统相机软件打开相机拍照和录像,如下图所示:1.3 以太网接口主板有一路
    Industio_触觉智能 2025-01-20 11:04 200浏览
  • 飞凌嵌入式基于瑞芯微RK3562系列处理器打造的FET3562J-C全国产核心板,是一款专为工业自动化及消费类电子设备设计的产品,凭借其强大的功能和灵活性,自上市以来得到了各行业客户的广泛关注。本文将详细介绍如何启动并测试RK3562J处理器的MCU,通过实际操作步骤,帮助各位工程师朋友更好地了解这款芯片。1、RK3562J处理器概述RK3562J处理器采用了4*Cortex-A53@1.8GHz+Cortex-M0@200MHz架构。其中,4个Cortex-A53核心作为主要核心,负责处理复杂
    飞凌嵌入式 2025-01-24 11:21 99浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 170浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦