嵌入式开发输出调试和日志信息的几种方式转载本文请联系强黄公众号。基于MCU的嵌入式软件开发在某些情况下可能没有额外的存储空间,因此调试和日志信息无法有效保存在本地。这时候通过某种方式输出调试(Debug)和日志(Log)信息就有意义了。下面说一下嵌入式开发中输出调试和日志信息的几点。1、标准库printf直接输出在单片机嵌入式开发中,最常见的方式是通过UART串口printf输出调试和日志信息。在Linux、Windows等大型系统中,使用标准C库没有问题,但在MCU等资源有限的平台上,通常使用微库。1、使用微库配置方法在使用的??IDE中,如Keil、IAR,都需要在项目选项中进行配置,才能正常使用微库。Keil使用微库:Project->OptionsforTarget->Target,勾选“UseMicroLIB”IAR使用微库:Project->OptionsforNode->GeneralOptions->LibraryConfiguration,选择“Full”进行补充说明,IAR有使用库有四个选项:None:无Normal:选择常规配置的运行库Full:选择完整配置的运行库Custom:选择自定义运行库2.重新定义函数如果要输出信息,必须有路径没错,就是UART或者CAN。因此,需要重新定义函数。以UART串口为例,最常见的方式是:#includeintfputc(intch,FILE*f){DEBUG_SendByte((uint8_t)ch);returnch;}intfgetc(FILE*f){while(USART_GetFlagStatus(DEBUG_COM,USART_FLAG_RXNE)==RESET);return(int)USART_ReceiveData(DEBUG_COM);}当然具体在串口中的实现和你的底层有关。通过以上配置,就可以直接使用printf函数了。2、自定义printf输出有些情况下,需要指定调试或日志信息的输出,需要自定义输出格式。例1:例如:我想设置一个“DBUGE开关”。在开发和测试阶段,我需要打开开关。产品量产后,我不需要打开调试信息输出。#defineDEBUG(Type,...)if(DEBUG_EN(Type))\{\printf(__VA_ARGS__);\}说明:__VA_ARGS__是一个可变参数的宏,表示左边宏中“...”的内容为按原样复制到__VA_ARGS__所在的右侧。例2:再如:输出日志信息,加上“时间戳”:#defineDEBUG(Type,...)if(DEBUG_EN(Type))\{\printf("%s:",GetTimeStr());\printf(__VA_ARGS__);\}更多定制:实际项目中可能会有很多需求,例如:针对不同类型的传感器,输出带有“传感器编号”日志信息的日志。因此,项目需求和复杂度不同,可能会有不同的调试或日志信息输出方式。实施与否,需要综合评估。3、SWO输出类似于UART串口输出,SWO(SerialWireOutput)串口线输出也是其中之一,但是需要MCU支持SWO功能(目前很多MCU都支持)。在线调试时可以输出到IDE界面,例如:也可以离线输出到一些工具界面,例如:4.CLI调试输出信息以上方法都是纯粹输出调试/日志信息,相对高级一点方法是通过CLI(CommandLineInterface)命令行获取调试信息。相信大家都不知道命令行的模式。做嵌入式开发的都知道,linux这样的终端就是命令行,但是我们这里说的命令行比linux终端要简单的多。但原理是类似的,你可以通过输入命令来查看数据或执行一个动作。1、与printf相比,可以通过CLI随时查看指定信息和执行某些操作。2、相对于printf的劣势,需要在代码中集成CLI组件,以及相应的(查看、执行动作等)代码,消耗资源较多。(当然也可以使用轻量级的CLI,不占用资源。)比如我之前用的估计不到1K的Flash空间:其实很多RTOS都集成了CLI组件,并且实际上不可能使用它们。有想象中的复杂,类似于移植一个RTOS,甚至更简单。由于时间和空间有限,我就说到这里了。关于CLI的内容还是很多的。如果后面还有时间,我会尝试分享更多相关的内容。