Debug工具
1、前言
在软件开发的过程中,debug(调试)是一个很重要的事情,因为没有百分之百正确的代码,一旦结果不符合预期,我们需要知道问题出在哪里了。
在PC环境下开发应用程序,我们不需要太操心,因为有各式各样的模拟器、调试器可供使用,我们可以追踪到每一行代码的执行过程和执行结果,找出问题只是时间问题而已。但在嵌入式环境下,就有些麻烦了,能用的手段,无外乎两种:
1)使用硬件仿真器定位问题。
2)使用日志输出定位问题。
对嵌入式工程师(特别是linux工程师)而言,鉴于使用硬件仿真器的诸多不便(成本高,无法保证人手一个;硬件连接复杂,需要预留特定接口;使用不方便;等等),日志输出几乎成为必备且唯一的debug手段。但是,总会有例外:
系统刚刚启动,在日志输出的通道(通常是UART接口)ready之前,怎么debug?
在不得不使用仿真器之前,我们还有一个简单的方法,就是点LED灯,本文将结合“X Project”“【任务2】启动到u-boot command line”实现的过程,对这个方法进行简单的介绍和总结。
2、 思路
从本质上讲,软件debug最高效的手段,是良好的设计、优秀的编码、细致的代码检查,当然,百密总有一疏。但大多数时候,我们只要能找到出现问题的大概位置,再辅以代码的检查,就可以百分之九十九的解决问题,这也是通过日志输出的方法定位问题的基本逻辑。
因此,在日志输出通道ready之前,我们也可以使用同样的思路,借助LED灯,实现问题的定位。大致思路如下:
1)如果系统只有一个LED灯,可以在代码中,正确执行某一个步骤之后,点亮LED,以此类推,一步一步检查代码执行的正确与否(其实“X Project”“【任务1】启动过程-Boot from USB”就是借用了这个思路)。
2)当然,如果系统有多个LED,例如四个,我们可以将这四个LED编码成一个4bit的数字(0~15),这样就可以表示更多的状态,下面是“X Project”基于bubblegum-96平台,实现的一个简单的接口(代码逻辑很简单,我就不解释了):
/* https://github.com/wowotechX/u-boot/blob/x_integration/board/actions/bub... */
/*
* A simple debug function for early debug, in which,
* we use four LEDs to display sixteen debug codes, from 0 to 15.
* Using it, we can know, at least roughly, at where out code is run.
*/
void bubblegum_early_debug(int debug_code)
{
uint8_t val;
val = debug_code & 0x1;
setbits_le32(GPIOA_OUTEN, 1 << DEBUG_LED0_GPIO);
clrsetbits_le32(GPIOA_OUTDAT, 1 << DEBUG_LED0_GPIO,
val << DEBUG_LED0_GPIO);
val = (debug_code >> 1) & 0x1;
setbits_le32(GPIOA_OUTEN, 1 << DEBUG_LED1_GPIO);
clrsetbits_le32(GPIOA_OUTDAT, 1 << DEBUG_LED1_GPIO,
val << DEBUG_LED1_GPIO);
val = (debug_code >> 2) & 0x1;
setbits_le32(GPIOF_OUTEN, 1 << DEBUG_LED2_GPIO);
clrsetbits_le32(GPIOF_OUTDAT, 1 << DEBUG_LED2_GPIO,
val << DEBUG_LED2_GPIO);
val = (debug_code >> 3) & 0x1;
setbits_le32(GPIOF_OUTEN, 1 << DEBUG_LED3_GPIO);
clrsetbits_le32(GPIOF_OUTDAT, 1 << DEBUG_LED3_GPIO,
val << DEBUG_LED3_GPIO);
}
3、一个例子
利用上面的debug接口,在调试串口驱动的时候,发挥了很大的作用,如下:
/* https://github.com/wowotechX/u-boot/blob/x_integration/drivers/serial/se... */
extern void bubblegum_early_debug(int debug_code);
static int owl_serial_probe(struct udevice *dev)
{
/* only for UART2, TODO */
bubblegum_early_debug(4);
/* pinmux */
setbits_le32(MFP_CTL2, 1 << 22);
bubblegum_early_debug(5);
/* device clock enable */
setbits_le32(CMU_DEVCLKEN1, 1 << 8);
bubblegum_early_debug(6);
/* reset de-assert */
setbits_le32(CMU_DEVRST1, 1 << 7);
bubblegum_early_debug(7);
/* set default baudrate and enable UART */
owl_serial_setbrg(dev, 115200);
/* enable uart */
setbits_le32(UART2_BASE + UART_CTL, UART_CTL_EN);
bubblegum_early_debug(8);
return 0;
}
串口驱动编写好之后,发现系统会死在owl_serial_probe中,于是添加了一系列的"点灯"操作,发现最后停留在“bubblegum_early_debug(5); ”上面(LED2和LED0亮),于是就可以确定这条语句出了问题:
setbits_le32(CMU_DEVCLKEN1, 1 << 8);
经过仔细检查,发现CMU_DEVCLKEN1寄存器定义错了(可能是抄Action的代码笔误了)。
4、总结
步骤很简单,之所以要写一篇文章,是想告诉大家,嵌入式开发其实挺简单的,只要有足够的细心和耐心,一切皆有可能。
作者:wowo
文章来源 :蜗窝科技
keil MDK也是可以借助h-jtag进行单步调试,写出来与大家一起分享一下:
keil MDK编译器使用V4.01版本,下载地址:
http://www.embedinfo.com/down-list.asp?id=714 (需要注册一下)
h-jtag使用V1.0版本(请注意,一定要用V1.0或者以上版本才可以与mdk兼容),下载地址
http://www.hjtag.com/download/H-JTAG%20V1.0%20Preview.zip
分别安装MDK与h-jtag
两个软件都安装完成后,首先,需要运行H-JTAG 安装目录下的TOOLCONF.EXE 程序,对KEIL 安装目录下的TOOLS.INI 配置文件进行更新。如下图所示。点击Config 按钮,对TOOLS.INI 进行配置,然后退出。注意在点击Config之前,要退出keil编译器。
接下来,就可以在 KEIL 下对项目进行配置。首先,重新运行KEIL,并在KEIL 中打开一个项目,然
后点击Project -> Options for Target…菜单,接下来,会弹出下图所示的Options 配置窗口。
在上图所示的Options 配置窗口中,选择Debug 设置,点击标签下的如图所示下拉按键,就可以看到H-JTAG ARM 调试驱动。如下图所示:
选中使用h-jtag硬件仿真,点击 OK 按钮,回到KEIL 的主窗口,KEIL 的设置就完成了。
然后编译程序,生成hex的目标文件,利用h-jtag的flash下载工具将目标文件下载到硬件。
下载完毕后,使用Crtl+F5进入单步调试界面。
在单片机系统中,串口(UART,通用异步收发接口)是一个非常重要的组成部分。通常使用单片机串口通过 RS232/RS485 电平转换芯片与上位机连接, 以进行上位机与下位机的数据交换、参数设置、组成网络以及各种外部设备的 连接等。RS232/RS485 串行接口总线具有成本低、简单可靠、容易使用等特点,加上其历史悠久,所以目前应用仍然非常广泛;特别对于数据量不是很大 的场合,串口通信仍然是很好的选择,有着广阔的使用前景。
在单片机编程中,串口占了很重要的地位。传统方式串口程序的调试,往往是利用专用的单片机硬件仿真器。在编写好程序后,利用仿真器来设置断点,观察变量和程序的流程,逐步对程序进行调试,修正错误。使用硬件仿真器的确是很有效的方法,但是也有一些缺点:
很多仿真器不能做到完全硬件仿真,因而会造成仿真时正常,而实际运行时出现错误的情况;也有仿真不能通过,但是实际运行正常的情况。
对于一些较新的芯片或者是表面贴装的芯片,要么没有合适的仿真器或仿真头;要么就是硬件仿真器非常昂贵,且不容易买到。
有时由于设备内部结构空间的限制,仿真头不方便接入。
有的仿真器属于简单的在线仿真型,仿真时有很多限制。例如速度不高, 实时性或稳定性不好,对断点有限制等,造成仿真起来不太方便。
1.调试前的准备工作
下面介绍一种利用的功能来实现 51 单片机用户程序的方法。使用这种方 法,无需任何硬件仿真器,甚至都不需要用户电路板。所需的只是:
①硬件。1 台普通计算机(需要带有 2 个标准串口)和1根串口线(两头都是母头,连线关系如图1所示)。
②串口软件可以是自己编写的专用调试或上下位机通信软件,也可以是通用的串口软件(如串口助手、串口调试等),主要用来收发数据。
2.基本调试命令介绍
这个串口调试方法主要是利用了 Keil 强大的软件仿真功能。在新版本(高于 6.0)的 Keil 软件中,增强了软件的仿真能力,可以利用软件仿真更多的单片机功能。在这些功能中,其中有一个很重要的功能就是利用计算机的串口来模拟单片机的串口(这不同于很多软件在仿真时使用的激励文件方式,可以直接与其他串口进行通信,更加方便、灵活)。首先要介绍仿真时需要使用的两 个命令:ASSIGN和MODE。
2.1 ASSIGN命令
将单片机的串口绑定到计算机的串口。基本使用方式为:
ASSIGN channel
其中: channel 代表计算机的串口,可以是 COM1、COM2、COM3 或 COM4; 而 inreg 和 outreg 代表单片机的串口。对于只有一个串口的普通单片机,即 SIN 和 SOUT;对于有两个或者多个串口的单片机,即 SnIN 和 SnOUT(n=0, 1,…即单片机的串口号)。
例如:ASSIGN COM1
将计算机的串口1绑定到单片机的串口(针对只有一个串口的单片机)。
ASSIGN COM2
将计算机的串口 2 绑定到单片机的串口 0(针对有多个串口的单片机,注意串口号的位置)。
需要注意的是,参数的括号是不能省略的,而outreg则是没有括号的。
2.2 MODE命令
设置被绑定计算机串口的参数。基本使用方式为:
MODE COMx baudrate, parity, databits, stopbits
其中: COMx(x = 1,2,…)代表计算机的串口号;baudrate 代表串口 的波特率;parity 代表校验方式;databits 代表数据位长度;stopbits 代表 停止位长度。
例如:MODE COM1 9600, n, 8, 1
设置串口1。波特率为9600,无校验位,8位数据,1位停止位。
MODE COM2 19200, 1, 8, 1
设置串口2。波特率为19200,奇校验,8位数据,1位停止位。
使用以上两个命令,就能够将计算机的串口模拟成单片机的串口了。在进 行软件仿真时,所有发送到被绑定的计算机串口上的数据都会转发到 Keil 模拟 的单片机串口上,用户程序可以通过中断处理程序或查询方式接收到这些数 据;同样,单片机程序中发送到单片机串口上的数据也会通过被绑定的计算机串口发送出来,可以被其他软件所接收。利用这个特点,就可以方便地仿真、 调试单片机的串口部分程序。要注意的是,这两个命令需要一起使用。
2.3 仿真步骤
首先,用串口线将计算机的两个串口连接起来(或者是两台计算机上的两 个串口)。这两个串口一个用来模拟单片机串口,另一个给调试程序使用。这 个由用户自己分配,没有特殊要求。
其次,编写好用户程序,并编译通过。
然后,设置工程文件(Project)的相关参数,如图 2 和图 3 所示。主要是 选择模式(Use Simulator)以及晶振参数。
为了不必每次进入仿真状态后,都需要输入串口参数设置命令,可以建立一个初始化文件。初始化文件是一个普通的文本文件,内容就是仿真时需要的命令,按照顺序一行输入一条。如图 3 所示,建立了一个 debug.ini 的初始化文件。这样,当每次进入仿真调试状态时,就会自动载入 debug.ini 的内容进行初始化。
为了正确仿真串口,在软件仿真调试时,在用户的 Keil 工程文件的属性中,还需要设置实际使用的晶振频率。这个参数非常重要,直接影响通信的波特率,可以按照实际使用的参数进行设置。要注意,这个参数的单位是MHz。
设置好参数后,就可以进行仿真了。单击工具栏的图标 进入 Debug(仿
真调试)状态,在 Output window 窗口中的 command 文本框(一般是在左下角)中输入上面介绍的命令。例如,将PC机的串口1设置为单片机的串口:
mode com1 9600,0,8,1
assign com1
然后设置断点,一般是在关键地方或与串口相关联的地方设置。再单击图标运行(Run)用户程序,使用户程序运转起来(不然是接收不到串口数据的)。这时再使用软件或用户调试软件,发送通信命令或者数据包,看用户程序是否进入断点,以及相关的变量是否正确。还可以有意发送带有错误数据的数据包,以观察用户程序的异常处理部分是否正常。一旦发现程序中的错误, 可以马上停止仿真调试,立即修改代码,然后再次重复上面的步骤进行仿真。 因为不需要与用户目标板联机,也不用下载代码到用户板上,所以速度非常高。以上这些步骤和使用硬件仿真器的基本一样,只不过现在使用的是软件仿真。
需要注意的是:仿真时单片机串口实际的波特率由 MODE 命令来指定,单片机程序中的 TMOD、SCON 等参数是不影响串口仿真状态的(也就是说这些参数不 影响仿真的波特率,即使它们是错误的)。但是中断的使能位(如 ES、EA 等) 还是起作用的,如果ES或EA被禁止,那么就不会进入串口中断。
因为这种方法是利用计算机的串口来仿真单片机的串口,而仿真是通过 Keil 软件来转换串口上的数据,不是直接转发数据的,所以在实际仿真时,处理速度会比实际单片机运行时稍微低一点。比方说仿真状态时 1s 只能发送/接收 10 个数据帧,但在单片机硬件上运行时可能 1s 就可以接收/发送 50 个数据 帧。这与使用的计算机的速度有关,但对仿真来说,是没有任何影响的。
对于多串口的单片机,从理论上来说,可以一次绑定多个串口,只要计算机有足够多的串口。基本上,使用这种方法需要占用计算机的串口数量是单片 机绑定串口的 2 倍。一个串口被 Keil 占用,用来模拟单片机的串口;另外一个 串口被计算机占用,用来给单片机的串口收发数据。
3.小结
这里介绍的方法对 C51 和汇编语言都是适合的。它最大的好处就是简单、 方便,容易使用,不需要使用任何电路,也没有特殊的要求;甚至可以在硬件电路制作好之前就将串口部分的程序编写、调试完毕。笔者使用这种方法已经很长时间了,事实证明这种方法确实非常有效。其实对于 51 单片机,Keil 的 仿真功能实在是太强大了,只要充分掌握其特点,能够熟练利用它,就可以解决工作中的大部分问题。很多工作都可以使用软件仿真来完成,根本无需任何 硬件仿真器;只有一些新的外部器件的时序、接口的调试才有可能需要用到硬件仿真器。目前介绍 Keil 软件仿真这方面的参考书籍很少,有些讲的还是老版本的用法,不过没有关系,Keil 的帮助文件写得很详细、很清楚,只要认真看明白就会使用了。使用熟练后,就会发现Keil的功能相当强。
对于串口编程,51 单片机有 Keil 这个功能强大的开发软件,给我们带来了极大的便利;而在其他单片机软件的开发中,目前还没有这么强大的开发工 具和方便的调试手段。这里有个变通的办法,就是可以先在 Keil 中编写并调试 好串口程序,然后将程序移植到其他单片机平台中。至于如何能够减小程序移植的工作量,使得程序具有更好的通用性,以最小的代价就可以平滑地移植到 其他单片机平台上,也是一个非常值得探讨的问题。
4.再上一层楼
说了那么多,咱们现在的电脑大多只有一个串口,最起码机箱后面只有一个串口的接口,而现在大多笔记本没有串口,即使使用 USB 转的串口,那也只 有一个。
那就不能用这种方法仿真了呗?
非也,还是有解决方法的,看看下边怎么处理。
这是什么软件?呵呵,可以虚拟串口的软件,先到 NT5 文件夹下执行那个 vsbsetup 的文件,看到一个黑窗口闪过,然后再回来运行 vspdconfig,就可以运行起来了,运行的界面看看后面图片。
怎么样?什么?英文的?别怕,就点一个按钮而已。 看到右侧那个叫“Add pair”的按钮了?点击它。
呵呵,电脑好像响了一声,好像有时查U盘的声音,有硬件变化一样。 当然,看看左侧,出来了 COM3 和 COM4 了,对呀,虚拟出了两个串口,并 且已经配对了。
到设备管理器里看看,多出来两个串口,确实已经配对。 怎么用?Keil用一个,串口调试助手用一个呗!
文章来源:百度文库