在CodeWarrior for S12(X)的调试器中使用控制点(断点、观测点、标记点)

来源:互联网 发布:java项目开发全过程 编辑:程序博客网 时间:2024/05/09 00:58

在调试程序的时候,控制点是个很重要的辅助工具,它可以让你随心所欲的在你需要的地方暂停MCU以观测单片机状态,或做其他你想做的事情。运用好这个工具可以大大的节省调试的时间。毕竟,没人能保证自己写的程序不出任何问题,对吧?

大部分人都知道断点是干嘛用的,但是却好像没有几个人知道观测点的作用,包括我之前也是,在使用CodeWarrior的时候这几个东西着实困扰着我:

观测点和标记点

直到最近好好研究了下HI-WAVE(即CodeWarrior使用的调试器,可以在CodeWarrior的安装目录的Prog/hiwave.exe下找到它),终于明白了它们是干什么以及怎么用的了。

本来其实是想把帮助手册整个直接翻译一遍的,但是翻译到一半就放弃了,废话太多了,几乎一样的东西反反复复的说,再加上CSDN这XX编辑器导致我翻译了一天的东西都丢了,心态崩了干不下去了,于是干脆自己写一个小教程与大家分享。


注:此为作者原创文章,作者博客(http://blog.csdn.net/lin_strong),转载请保留这条。仅供学习交流使用,请勿用于商业用途。

术语约定

首先,为了方便起见,先约定下各个对象的中文名字,方便后面讲解。

对于调试器的界面,我们这样称呼会用到的几个控件:

调试器界面控件命名
图 1. HI-WAVE 界面

比如Source这个子窗体,我们会称它源代码窗口(或者源代码控件窗口)。

对于在各控件窗口内右键弹出的那个菜单,我们称其为 XX窗口上下文菜单

对于菜单栏中的菜单项,比如Component,我们直接称其为 Component菜单

然后还会用到一个窗体,我们将其称为控制点配置窗体:

控制点配置窗体
图 2. 控制点配置窗体

打开方法在后面会讲。

这里如图2对内部的控件名字做个约定,控件的名字一般直接用原英文而不翻译为中文。比如后文可能会这么说:
勾选Breakpoints标签页的Condition组合框内的Disable勾选框。

然后是一些术语的中英对照:

中文 英文 断点 breakpoint 临时断点 temporary breakpoint 永久断点 permanent breakpoint 计数断点 counting breakpoint 条件断点 conditional breakpoint 观测点 watchpoint 读观测点 read access watchpoint 写观测点 write accesswWatchpoint 读/写观测点 read/write access watchpoint 计数观测点 counting watchpoints 条件观测点 conditional watchpoints 标记点 markpoint 标签页 tab 列表框 listbox 组合框 groupbox 编辑框 editbox 勾选框 checkbox 按钮 button

控制点

CodeWarrior for S12(X)中有三种调试器控制点:

  • 断点
    断点对应一个地址,当MCU的PC寄存器指向断点的地址的时候会触发对应的行为(如暂停)。

  • 观测点
    观测点对应一个内存地址范围,当检测到MCU的数据总线以指定方式访问这个地址范围时,会触发对应的行为(如暂停)。

  • 标记点
    标记点顾名思义就是一个地址范围的标记,可以用它来标记一个你用到的地址,然后通过它来方便的访问。(但是笔者觉得这东西蛮鸡肋的)

下面我们来分别介绍:

断点

断点是与PC(program counter)寄存器相关的控制点。一旦PC寄存器的值与断点的地址值一致,断点就有活干了。

简单地解释一下。
CPU工作的基本原理是在周期性震荡源(如晶振)的驱动下,CPU会从某个地址开始依次读取地址处的字节并将读到的0/1值作为指令来执行一些最最基本的操作;而CPU下一个要读取指令的地址就是存放在这个名叫PC的寄存器中的,默认情况下,每次CPU从PC中得到地址后PC的值就会递增1,所以看上去就是线性的一个个往后读。而调用函数的本质其实就是:在调用前,把需要保存的值、函数返回后要继续运行的指令的地址、传给函数的变量值等保存到一个地方(栈),然后设置PC的值为那个函数的起始地址;这样CPU接着就会跳那个函数那继续执行,当执行完了后再取出跳转回来的地址并放到PC中,这样CPU就调用完函数并返回了。
所以断点其实就是在监视MCU运行到了哪句代码

可以设置断点的位置

由于断点对应的是实际的CPU指令,而源代码由于代码优化等原因,不见得每个语句都会有对应的指令代码,当然,汇编代码窗口内就不存在这个问题了,每个汇编代码行都是可以设置断点的。

为了知道源代码窗口内的某句代码是否可以设置断点,在源代码窗口内右键打开上下文菜单,并点Marks项(图3)。

点击Marks选项
图 3. 点击Marks选项

这样,再次打开上下文菜单时,Marks项前面就会有个√,并且所有可以设置断点的代码行前都会有这个标志:可设断点的标志

标记出可设置断点的代码行
图 4. 标记出可设置断点位置的源代码窗口

断点类型

调试器支持4种类型的断点:

  • 临时断点:在下一次运行到断点对应指令地址时激活,并且在激活过后即被删除。
  • 永久断点:每次运行到这个断点对应指令地址时都会激活。
  • 计数断点:在指令执行一定次数之后被激活。
  • 条件断点:当运行到这个断点对应指令地址并且给点条件为真时激活。

实际上,计数断点和条件断点是可以和其他结合使用的。

断点的标志

根据设置的断点的类型,在源代码窗口和汇编代码窗口的对应位置前都会标注对应的标志:
临时断点:临时断点标志
永久断点:永久断点标志
计数断点:计数断点标志
条件断点:条件断点标志

断点标志示例
图 5. 断点标志示例

设置及删除断点

前面说过,断点是针对指令/代码的,所以可以在源代码窗口或者汇编代码窗口设置它,下面涉及主界面操作的方法都只针对源代码窗口,汇编代码窗口的操作是几乎一致的,所以不再专门说明。

有这么几种设置及删除断点的方式:

  1. 使用快捷键
  2. 使用窗口上下文菜单
  3. 通过控制点配置窗体

快捷键

1 . 鼠标左键 + T
当程序不在运行过程中时,把鼠标移至源代码窗口中需要设置断点的地方,按住鼠标左键,然后点击T键(Temporary首字母),并松开鼠标,这样就设置了一个临时断点。程序会立刻开始运行,并在遇到断点时停止。

2 . 鼠标左键 + P
把鼠标移至源代码窗口中需要设置断点的地方,按住鼠标左键,然后点击P键(Permanent 首字母),并松开鼠标,这样就设置了一个永久断点。

3 . 鼠标左键 + S
把鼠标移至源代码窗口中需要设置断点的地方,按住鼠标左键,然后点击S键(Set 首字母),并松开鼠标,调试器就会在这个位置设置一个永久断点,并立刻打开控制点配置窗体,然后你就可以在控制点配置窗体内将其配置为其他类型的断点了。

4 . 鼠标左键 + D
把鼠标移至源代码窗口中需要删除断点的地方,按住鼠标左键,然后点击D键(Delete 首字母),并松开鼠标,这样就删除了这个地方的断点。

窗口上下文菜单

源代码窗口上下文菜单
图 6. 源代码窗口上下文菜单

1 . Run to Cursor菜单项
当程序不在运行过程中时,把鼠标移至源代码窗口中需要设置断点的地方,点击鼠标右键弹出上下文菜单(图 6),选择Run to Cursor,这样就设置了一个临时断点。程序会立刻开始运行,并在遇到断点时停止。

2 . Set Breakpoint菜单项
把鼠标移至源代码窗口中需要设置断点的地方,点击鼠标右键弹出上下文菜单(图 6),选择Set Breakpoint,这样就设置了一个永久断点。

3 . Show Breakpoint菜单项
把鼠标移至源代码窗口中,点击鼠标右键弹出上下文菜单(图 6),选择Show Breakpoint,然后就可以在其中添加/更改/删除断点了。
源代码窗口上下文菜单
图 7. 源代码窗口上下文菜单

4 . Delete Breakpoint菜单项
把鼠标移至源代码窗口中需要删除断点的地方,点击鼠标右键弹出上下文菜单(图 7),选择Delete Breakpoint,这样就删除了一个断点。

实际上断点还有一种状态,就是被禁用(disable)的状态,在这种状态下,断点会被保留,但是其标志会变浅而且在运行到它的位置时不会进行任何操作,就和没有它一样。

5 . Disable Breakpoint菜单项
把鼠标移至源代码窗口中需要禁用断点的地方,点击鼠标右键弹出上下文菜单(图 7),选择Disable Breakpoint,这样就禁用了一个断点。

源代码窗口上下文菜单
图 8.源代码窗口上下文菜单

6 . Enable Breakpoint菜单项
把鼠标移至源代码窗口中需要使能断点的地方,点击鼠标右键弹出上下文菜单(图 8),选择Enable Breakpoint,这样就使能了一个被禁用的断点。

控制点配置窗体

我们看到,使用前面的方法能直接设置的只有简单的临时断点和永久断点。其他类型的断点则需要使用控制点配置窗体设置。让我们看看怎么用控制点配置窗体来配置断点。

当你通过如上快捷键或者上下文菜单的方法打开控制点配置窗体后会看到类似如下界面:
控制点配置窗体
图 9. 控制点配置窗体

默认会打开Breakpoints标签页。

在列表框内会列出当前所有的断点。默认会选中你打开窗体时的那个断点。
当选中了一个断点后,下面的控件会修改为这个断点的配置,然后你就可以用这些控件来配置这个断点了:

  • Breakpoint组合框

    Address编辑框会显示当前断点的地址,如果Hex format勾选框被勾上了,则编辑框内的地址用十六进制表示,如果没有勾上,则使用C语言的表示法表示(即直接用数字则是10进制,前缀0则是8进制,前缀0x则是16进制)。Name后面则显示这个断点所在的位置/函数名。如果勾选了Disable勾选框,则断点被禁用。如果勾选了Temporary勾选框,则断点在激活一次之后自动被删除(即临时断点)。

  • Condition组合框

    Condition编辑框内如填着任何代码,则执行到断点时,调试器会执行其中的代码,如果结果为TRUE,才会激活断点(条件断点)。如果勾选了Disable勾选框,则条件判断被禁用。

    条件语句需要使用ANSI-C语法来声明,以图9为例,其中的AD_in0是当前程序的一个全局变量名,这样的效果相当于当程序运行到断点地址时,执行了次 if(AD_in0 == 0),如果为真,则激活断点。

    修改了条件语句后,调试器会自动对语法进行检查,如语法不正确则会提示并且修改会失败,但是如果不是语法问题,则直到执行条件语句时才会发现并且提示错误。

    可以使用 $寄存器名 来代表寄存器内的值。比如以下语句会判断A寄存器内的值是不是0x10:

    $A == 0x10

  • Command组合框

    Command编辑框内如填着东西,则当断点被触发时,就会执行其中的指令然后停止程序。
    比如,如果里头填的是open Data。那么当断点被触发时,就会自动打开一个数据控件窗口。

    里头填的是单条调试器命令(在这个地方,不允许使用命令GGOSTOP),使用命令CALLCF来关联一个命令文件(比如:CF breakCmd.cmd)。

    如果勾选了Disable勾选框,则不会执行命令。
    如果勾选了Continue勾选框,则在执行了命令后不会停止程序,而是会继续执行。

  • Counter组合框

    Counter组合框与计数断点直接相关,每次经过断点地址时,首先会递减Current编辑框内的值,如果减完后Current的值为0,则进行后续步骤判断是否触发断点。

    每次Current的值减为0后,其就会重新加载Interval内的值,如此循环。

    因此,如果Interval的值为1(默认),则效果就是它不是一个计数断点。而为其他值时,效果就是经过这个计数断点Interval次后才有可能触发。

    当更改Interval内的值并确认更新后,Current会自动赋予相同的值。

    注意:虽然勾选TemporaryInterval就会被禁用,但是临时断点也可以使用计数特性,在Current减为0后才激活。

  • Save & Restore on load

    这个General组合框内的勾选框顾名思义,就是存储当前的所有断点,这样在下次打开这个项目的时候就会保留当前的所有断点。

    如果要保留断点,则调试器会创建个与项目同名的.BPT文件,在其中保存所有断点的各项信息,以及断点所在处的函数的源码以及编译后的大小,这样在下次加载的时候编译器就能够发现是否代码发生了变化并禁用这些可能有问题的断点,具体存储格式请参照帮助手册。

  • 其他几个按钮

    Add按钮:用于直接添加新的断点,不管现在选中的是哪个断点,直接对控件值进行修改,然后点击Add按钮即可添加一个新的断点。

    Update按钮:用于确认更改,选中断点后,如果进行了任何修改,Update按钮就会由禁用状态转为可用,点击它就能验证修改。

    Delete按钮:用于删除选中的断点。

    Show Location按钮:点击后会使所有能同步的控件(源代码窗口、汇编代码窗口、内存窗口)全部高亮选中的断点的位置。

    剩下的确定取消帮助按钮就不说了。

另,列表框可以多选(图 10),多选的方法有:

  • 按住Ctrl,然后依次选择你要选中的断点。
  • 单击选中第一个你要选择的断点,按住Shift,点最后一个你想选择的断点(片选)。

多选后,下面的控件也会做对应修改,这里不啰嗦了。

列表框多选
图 10. 列表框多选

结合使用多种类型断点

根据前面的描述相信你也看出了,不同的断点特性并不是完全对立的,比如我可以设置一个永久断点让CPU每4次经过它就判断一次 (counter == 1)? ,为TRUE的话就打开个窗口。

经过试验,断点工作的基本逻辑如下图:

断点工作逻辑
图 11. 断点工作逻辑

可参照此图来实现一些自己想要的断点功能(仅供参考,不保证此图完全正确,产生的任何后果作者概不负责)。

观测点

下面来讲解下观测点,由于观测点的很多内容与断点一致,所以重复的内容我就直接跳过不浪费字数了,相信聪明的读者绝对能举一反三。

前面我们说过,断点是与PC值相关的。而观测点呢,则是与一段内存范围相关的,原理是监视数据总线中对内存地址的访问,它会在MCU以指定方式访问某段内存地址的时候激活。

这有什么用呢?比如,当你不知道谁动了你的奶酪(寄存器、变量……)的时候,观测点就派上大用场了,有很多可能性导致这种情况。比如你在其他某个模块动了这个变量的值但是后来忘记删了,或者有个野指针指向了这个地址并且在你不经意的情况下被访问了…… 这个时候断点就完全派不上用场了。会用观测点则可以节省你大量的时间。

观测点的类型

调试器支持5种类型的断点:

  • 读观测点:当在指定地址范围内发生读访问时激活
  • 写观测点:当在指定地址范围内发生写访问时激活
  • 读/写观测点:当在指定地址范围内发生读访问或写访问时激活
  • 计数观测点:当在指定地址范围内发生指定次数的指定访问时激活
  • 条件观测点:当在指定地址范围内发生指定访问且给定条件为TRUE时激活

同样的,计数断点和条件断点是可以和其他结合使用的。

观测点的标志

根据设置的断点的类型,在数据窗口和内存窗口的对应位置都会标注对应的标志,但对于观测点,没有断点那么复杂,只以其是读、写还是读/写来区分标志,而且标志也很简单:

读观测点:绿色的杆
写观测点:红色的杆
读/写观测点:黄色的杆

观测点标志示例
图 12. 观测点标志示例

设置及删除观测点

由于观测点是针对地址范围的,所以可以在数据窗口或者内存窗口设置它。

和断点类似,有这么几种设置及删除观测点的方式:

  1. 使用快捷键
  2. 使用窗口上下文菜单
  3. 通过控制点配置窗体

快捷键

1 . 数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + R
把鼠标指向数据窗口中需要设置观测点的变量,按住鼠标左键;或者在内存窗口内按住左键拖选想要设置观测点的地址范围,然后点击R键(Read首字母),并松开鼠标,这样就在指定变量/地址范围设置了一个读观测点。

2 . 数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + W
同样的方式,快捷键改为W(Write首字母),这样就在指定变量/地址范围设置了一个写观测点。

3 . 数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + B
同样的方式,快捷键改为B键(Both首字母),这样就在指定变量/地址范围设置了一个读/写观测点。

4 . 数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + S
同样的方式,快捷键改为S键(Set 首字母),调试器就会在这个位置设置一个读/写观测点,并立刻打开控制点配置窗体,然后你就可以在控制点配置窗体的Watchpoints标签页内将其配置为其他类型的观测点了。

5 . 数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + D
把鼠标指向数据窗口中需要删除观测点的变量,按住鼠标左键;或者在内存窗口内按住左键拖选想要删除观测点的地址范围,然后点击D键(Delete 首字母),并松开鼠标,这样就删除了这个地方的观测点。

窗口上下文菜单

数据窗口上下文菜单
图 13. 数据窗口上下文菜单

内存窗口上下文菜单
图 14. 内存窗口上下文菜单

嘿嘿,怎么样,很熟悉吧。与断点类似,也有菜单项:

Set Watchpoint
Show Watchpoint
Delete Watchpoint
Disable Watchpoint
Enable Watchpoint

几乎就是把断点的说明中的“断点”改成“观测点”就行了。

唯一要注意的是,在内存窗口中除了Show Watchpoint,其他都需要先拖选指定范围,然后才能选。

控制点配置窗体

同样的,也可以使用控制点配置窗体设置观测点。用数据窗口内按住鼠标左键/内存窗口内拖选地址范围 + S或者Show Watchpoint菜单项打开控制点配置窗体后会直接转到Watchpoints标签页,当然也可以先打开其他标签页然后再切换到Watchpoints标签页,然后在这个标签页中对观测点进行配置。

我们来看一下这个标签页长什么样:

控制点配置窗体(Watchpoints标签页)
图 15.控制点配置窗体(Watchpoints标签页)

也没什么好说的吧,就说下不同的吧。

Watchpoint组合框内的Size编辑框指定了地址范围的大小,单位为字节。而其中右下角那个下拉框则指定了监视的是读访问还是写访问还是读写访问。

双核共享的观测点

HCS12X多核处理器的调试模型允许在CPU12X或者XGATE数据总线中设置观测点。对应的,调试器也是要不然在CPU12X数据总线中,要不然在XGATE数据总线中设置观测点。换句话说,你在一个核的总线中设置的观测点对另外一个核没有作用,即使这是个两个核共享的变量。

设置XGATE中的观测点

如果你想设置观测点到XGATE的数据总线中,有两种办法:

  • 你设置观测点的那个变量是定义在XGATE源代码内的变量。
  • 观测点直接设置在XGATE的内存区域中。
    .

除了这两种情况,所有的观测点都是设置在CPU12X数据总线中的。

设置双核共享的观测点

  1. 变量要被定义在CPU12X源代码中,否则无法设置观测点到CPU12X的数据总线中。
  2. 设置第一个观测点为这个变量,或者直接设置到全局地址或者逻辑地址。这个观测点是属于CPU12X数据总线的。
  3. 直接设置第二个观测点到XGATE地址。使用HCS12XAdrMap控件来转换地址到XGATE地址。这个观测点数据XGATE数据总线。

怎么打开HCS12XAdrMap控件?
在菜单栏中 Component->Open->Hcs12xad….

设置在共享变量上的观测点
图 16.设置在共享变量上的观测点

标记点

标记点是和源码的一行、内存或者数据范围相关的控制点。它好像真的只是给程序员做标记用的。。。。

访问标记点并不会导致程序停止。

标志点的标志

在源码窗口或者汇编窗口内的标志点会用一个蓝色的L来标记。

而数据窗口或内存窗口内的标志点则是用一个蓝色的横杠。

设置和删除标记点

标记点没有快捷键。

同样可以使用上下文菜单来设置和删除标记点。

同样可以使用控制点配置窗体来配置标记点。

控制点配置窗体(Markpoints标签页)
图 17. 控制点配置窗体(Markpoints标签页)

看一眼这个配置窗体,好像也没什么可讲的。感觉这个控制点很鸡肋。搜索了下帮助文档,好像也就trigger里头用了一下它。所以就这样跳过吧。

参考文献

[1] Freescale semiconductor. S12(X) Debugger Manual . 2010.
[2] Erich Styger. Watchpoints: Data Breakpoints. https://mcuoneclipse.com/2012/04/29/watchpoints-data-breakpoints-in-mcu10/ .2012.

阅读全文
0 0