在上一章我们实现了一个最简单的串口接口,本章我们就基于该接口来实现一个属于你自己的功能强大的调试接口。
为什么要提供这个接口?
在嵌入式系统中,我们如果使用系统的printf接口,在调试完成后,无法自动关闭,需要逐一删除调试语句,非常不方便。但是printf的功能强大,属实是一个调试信息输出的利器,那么怎么办?能否鱼和熊掌兼得呢?答案是肯定的,我们实现一个和printf功能一模一样的接口,但是却能克服printf的缺陷,并且提供更方便的控制。那就是STOpen系统中的DebugPrintf接口,在使用完成后,只需要关闭DEBUG_ENABLE宏就可以去掉全部调试代码,简单易用,功能强大,而且输出重定向也相当方便。
调试接口定义:
用DebugPrintf调用代替printf,参数也是可变参数,功能完全一样。
/***************************************************************************
函数名称: DebugPrintf
功能描述: 提供和系统printf功能一样的调试接口
输入参数: 可变参数
输出参数: 无
返回值 :
使用注意:
***************************************************************************/
#ifdef DEBUG_ENABLE
SYS_EXTERN void DebugPrintf(SINT8* str,...);
#else
#define DebugPrintf(...)
#endif
#define DEBUG_ENABLE //如果定义这个变量,enable debug接口
调试相关宏定义:
#define DEBUG_BUFFER_LEN 100
该宏定义DebugPrintf使用的输出buffer长度。
为了减少ram使用量(在嵌入式系统中,RAM可以宝贝),被Printf格式化后的数据是保存到栈上的。一般这个值不要定义太大,默认值大多数情况都够用了。这个值大小,限制了使用DebugPrintf格式化后的输出字符串的总长度,一旦超过这个长度,会爆栈。
SYS_EXTERN void Usart1TxAlways(UINT8 *pData,UINT32 len);
#define DebugPutByte(p,len) Usart1TxAlways(p,len)
//输出指定长度的数据到终端,默认从串口usart1输出
//pData : 指向要输出的数据
//len : 要输出数据的长度
我们用宏DebugPutByte代替了函数,你可以实现一个参数一致的函数来像上面一样,替代Usart1TxAlways(p,len)即可。
DebugPrintf的使用步骤:
1.打开DEBUG_ENABLE宏定义(不用了关闭该定义即可)
2.定义DEBUG_BUFFER_LEN的值为合适的值。
3.提供DebugPutByte(p,len)的实现函数
下面是DebugPrintf函数的实现:
#ifdef DEBUG_ENABLE
void DebugPrintf(SINT8* str,...)
{
SINT8 txt[DEBUG_BUFFER_LEN];
va_list args;
va_start(args,str);
vsprintf(txt,str,args);
va_end(args);
DebugPutByte((UINT8*)txt,strlen(txt));
}
#endif
串口的输出重定向函数:
void Usart1TxAlways(UINT8 *pData,UINT32 len)
{
UINT32 timeout = 0xFFFF;
while(len--)
{
while((!((USART1->SR)&(1 << 7))) && (timeout--)); //防止硬件死机
USART1->DR= *pData++;
}
}
该函数的实现及其简单,高效,而且方便实现输出重定向。
如何重定向输出流?
只要提供不同的DebugPutByte(p,len)实现,就可以把调试信息输出到屏幕,串口,文件等地方。如果你在DebugPutByte(p,len)实现函数里面进一步加强功能,甚至可以动态的切换DebugPrintf输出的终端。
测试代码,实现了字符串,格式的十六进制和十进制的数据输出:
int main(void)
{
SystemInitialize();
Usart1Init();
SetTimer(TIMER_ID1,1000,TRUE); //start
// SetTimer(TIMER_ID2,100,TRUE);
while(1)
{
MSGTYPE msg;
if(GetMessage(&msg) == FALSE)
{
//没有任何事情需要处理,可以进入休眠状态
__WFI(); //任何中断发生后唤醒继续执行
continue;
}
switch(msg.type)
{
case MSG_TIMER:
{
if(msg.wParam == TIMER_ID1)
{
static UINT32 time = 1;
time++;
DebugPrintf("counter:0x%X,%d\n",time,time);
}
}
break;
default: break;
}
}
}
下图是测试代码输出的信息:
下图了对比打开和关闭调试信息的代码量,我们发现因为使用printf的功能,需要花费大约6KB的代码空间,所以在使用完成出发行版本之前,请关闭到调试信息,一方面提高程序的运行速度(调试信息是阻塞方式输出,耽搁CPU的时间),另外一方面可以降低ROM的使用量。
原创文章,欢迎转载,请注明来源,未经书面允许,请勿用于商业用途。