任何软件编程都需要有调试信息输出来进行,软件的调试。
在做keil51软件项目时,一般使用UART或者RS232输出调试信息。
那如何输出适合自己项目的调试信息,有下面几个方案可以参考。
首先,需要调试好uart输出接口,可以正常输出字符。
如果只需要打印字符串而不需要打印变量参数,那么就比较简单。只需要像如下这样打印:
但是,这样设计只能适用简单的设计,如果设计稍微复杂点就需要打印参数的数值。而上面的方式没有办法完成这样的需求。
那么就需要使用keil51库中支持的打印方式,通过重定向打印的接口来实现正常调用库中的printf,sprintf等函数,当然也可以自己实现printf函数。
函数名及定义
功能说明
char_getkey(void)
等待从8051串口读入一个字符并返回读入的字符,这个函数是改变整个输入端口机制时应做修改的唯一一个函数
chargetchar(void)
使用_getkey从串口读入字符,并将读入的字符马上传给putchar函数输出,其他与_getkey函数相同
char*gets(char*s,intn)
该函数通过getchar从串口读入一个长度为n的字符串并存入由s指向的数组。输入时一旦检测到换行符就结束字符输入。输入成功时返回传入的参数指针,失败时返回NULL
charungetchar(charc)
将输入字符回送到输入缓冲区,因此下次gets或getchar可用该字符。成功时返回char型值,失败时返回EOF,不能处理多个字符
charputchar(charc)
通过8051串行口输出字符,与函数_getkey一样,这是改变整个输出机制所需要修改的唯一一个函数
intprintf(constchar*fmstr[,argument]...)
以第一个参数指向字符串制定的格式通过8051串行口输出数值和字符串,返回值为实际输出的字符数
intsprintf(char*s,constchar*fmstr[,argument]...)
与printf功能相似,但数据是通过一个指针s送入内存缓冲区,并以ASCII码的形式存储
intputs(constchar*s)
利用putchar函数将字符串和换行符写入串行口,错误时返回EOF,否则返回0
intscanf(constchar*fmstr[,argument]...)
在格式控制串的控制下,利用getchar函数从串行口读入数据,每遇到一个符合格式控制串fmstr规定的值,就将它按顺序存入由参数指针argument指向的存储单元。其中每个参数都是指针,函数返回所发现并转换的输入项数,错误则返回EOF
intsscanf(char*s,constchar*fmstr[,argument]...)
与scanf的输入方式相似,但字符串的输入不是通过串行口,而是通过指针s指向的数据缓冲区
voidvprintf(constchar*s,char*fmstr,char*argptr)
将格式化字符串和数据值输出到由指针s指向的内存缓冲区内。类似于sprintf,但接受一个指向变量表的指针,而不是变量表。返回值为实际写入到输出字符串中的字符数
Cx51User'sGuide中对charputchar(charc)的描述如下
Theputcharfunctiontransmitsthecharactercusingtheserialport.
Note
Theputcharroutinereturnsthecharacteroutput,c.
现在只考虑打印调试信息,获取字符不用管。在code中加入这段代码就可以实现重定向,但是不要忘记#include
参考keil安装文件内的定义function
Keil\C51\LIB\PUTCHAR.C
如何是C99的编译器可以使用下面的方式来设计调试信息的打印,遗憾的是keil51只支持C89的编译器,不能使用__VA_ARGS__宏
或者
这个版本的DRV_PRINT(x)只能输出单变量——纯字符串,
不需要打印调试信息时,更改DRV_DEBUG宏定义
当然也可以直接这样定义
如果只需要打印一个变量,第2个参数用随意值填位,如
类似,如果有4个参数,就:
这种方式使用起来比较麻烦,而且受参数个数的限制,比较笨的方案,但是没办法:(,VxWorks5.5内核代码里就是这样干的!
通过下面这种定义,使用的时候需要两个括号,虽然两个括号使用起来有些麻烦,但是比V2的多个参数还是相对好点。
keil51的log打印方式,但是调用宏时不能换行,使用‘\’换行也不行
每种方式都有优缺点,选择适合自己项目的方式。
在处理不同的模块,不同的重要程度的信息,我们就需要实现分层的设计。下面试linux的分层级别,不过对于keil51这种级别的设计8种分层过于多,其中的大部分级别是用不到的,
定义出来,也就没有意义,只是作为参考。
linuxkernel的日志级别
#defineKERN_EMERG"<0>"/*systemisunusable*/#defineKERN_ALERT"<1>"/*actionmustbetakenimmediately*/#defineKERN_CRIT"<2>"/*criticalconditions*/#defineKERN_ERR"<3>"/*errorconditions*/#defineKERN_WARNING"<4>"/*warningconditions*/#defineKERN_NOTICE"<5>"/*normalbutsignificant*/#defineKERN_INFO"<6>"/*informational*/#defineKERN_DEBUG"<7>"/*debug-levelmessages*/
下面提供两种分层方式,使用v2--指定参数宏或者v3--参数数量可变宏都是可以的。
1.如果分层比较多的可以参考下面的设计方式来做分层设计1
2.如果认为不需要这么多分层那么可以简单的使用下面的方式。
在keil51的标准输入输出库
1.printf和sprintf都是可变参数打印
2.vprintf和vsprintf需要配合
例如
使用参考代码
3.sprintf和vsprintf使用起来比较危险,都是输出到buff中的,需要注意buff的大小,以防buff爆掉误写入其他的地方,造成程序崩掉。
对打印的内容做加一些处理,可以使用这两个函数。
例如:
可以在打印error时默认在前面加入"Error:"
一般我们在写printf时都是用'\n'来回到下一行的开始,但是在不同的系统'\n'的行为不一致。
参考下面的'\r','\n'的说明,我们需要自动在'\n'之前加入'\r'.
一般实现方式有两种。
1)通过putchar方式
参考keil51中的code
这样看起来比较清楚点
2)通过spprintf,svprintf将打印出来的字符串中的'\n'之前加入'\r'.
1、\n软回车:
在Windows中表示换行且回到下一行的最开始位置。相当于MacOS里的\r的效果。在Linux、unix中只表示换行,但不会回到下一行的开始位置。
2、\r软空格:
3、\t跳格(移至下一列)。
它们在双引号或定界符表示的字符串中有效,在单引号表示的字符串中无效。\r\n一般一起用,用来表示键盘上的回车键,也可只用\n。\t表示键盘上的“TAB”键。就像你使用enter和shift+enter的区别
4、文件中的换行符号:
linux,unix:\r\nwindows:\nMacOS:\r
5、常用转义符号的意义:\nLF或ASCII中的0x0A(10)\rCR或ASCII中的0x0D(13)\t水平制表符-HT或ASCII中的0x09(9)\\反斜杠\$美圆符\"双引号\'单引号