背景
调试一颗MCU,如果适配的环境不像RTT那样把调试串口都标准化了,这个时候就需要自行实现打印函数。如果个人实现的话,就有些重复造轮子了,最近看一篇关于xprintf的分享,感觉xprintf能够挺快的适配到MCU上,因此就尝试移植xprintf。
代码分析
源码路径
xprintf代码挺简单,就两个文件,xprintf.h和xprintf.c两个文件。对于前期适配,除非遇到问题,否则我们并不关心.c内部的实现,仅仅需要关心.h文件提供的接口和参考的实现即可。
.h文件结构
从结构上看,.h文件主要提供了三类接口,第一类是xprintf需要开关的模块,即:
#define XF_USE_OUTPUT 1 /* 1: 使能串口输出 */ #define XF_CRLF 0 /* 1: 在输出时,将 \n 转化成 \r\n */ #define XF_USE_DUMP 0 /* 1: 使能put_dump功能 */ #define XF_USE_LLI 0 /* 1: 使能长整型输出 */ #define XF_USE_FP 1 /* 1: 使能浮点输出 */ #define XF_USE_INPUT 0 /* 1: 使能串口输入 */ #define XF_INPUT_ECHO 0 /* 1: 在xgets函数中使能回传功能 */
如果开启了串口输出,则有如下接口可使用:
// 这两个接口应该是 #define xdev_out(func) xfunc_output = (void(*)(int))(func) extern void (*xfunc_output)(int); // 这类接口时调用xdev_out指定了串口输出实现后,串口打印调用接口 void xputc (int chr); void xputs (const char* str); void xprintf (const char* fmt, ...); void xsprintf (char* buff, const char* fmt, ...); // 如果没调用xdev_out指定串口输出函数,或者需要临时使用其他串口输出,则可以使用这些函数输出信息 void xfputc (void (*func)(int), int chr); void xfputs (void (*func)(int), const char* str); void xfprintf (void (*func)(int), const char* fmt, ...); // 使能put_dump后可调用的函数,实际功能是按照width指定宽度dump buff字串(十六进制), // 其中addr是起始地址(不知为何,实际代码中仅仅是打印这个地址,并未使用), // len是dump长度,实际buffer长度为len * width void put_dump (const void* buff, unsigned long addr, int len, size_t width);
如果开启了串口输入,则有如下接口可使用
// 类似于串口输出,用于指定串口输入实现接口 #define xdev_in(func) xfunc_input = (int(*)(void))(func) extern int (*xfunc_input)(void); // 获取输入信息接口,获取字符串 int xgets (char* buff, int len); //字符串转整数,转浮点数 int xatoi (char** str, long* res); int xatof (char** str, double* res);
代码移植
移植的第一步就是添加代码,这步没啥好说的,无非就是拷贝代码至工程目录,之后将代码添加至工程。
代码适配
这里基于stm32F412-nucleo平台(去年不知道谁给我寄的板子,一直放那吃灰,刚好五一有假期,拿出来玩这个)。
开启宏
#define XF_USE_OUTPUT 1 /* 1: Enable output functions */ #define XF_CRLF 0 /* 1: Convert \n ==> \r\n in the output char */ #define XF_USE_DUMP 0 /* 1: Enable put_dump function */ #define XF_USE_LLI 1 /* 1: Enable long long integer in size prefix ll */ #define XF_USE_FP 1 /* 1: Enable support for floating point in type e and f */ #define XF_DPC '.' /* Decimal separator for floating point */ #define XF_USE_INPUT 1 /* 1: Enable input functions */ #define XF_INPUT_ECHO 1 /* 1: Echo back input chars in xgets function */
接口编写
编写输出一个字符实现
void fputc(int data) { if (HAL_UART_Transmit(&huart1, (uint8_t *)&data, 1, 5000) != HAL_OK) { Error_Handler(); } } int fgetc(void) { int data = '\n'; while (HAL_UART_Receive(&huart1, (uint8_t *)&data, 1, 5000) != HAL_OK) { ; } return data; }
测试代码编写
int main(void) { /* USER CODE BEGIN 1 */ char buff[256], *pbuff; long lval; double fval; pbuff = buff; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_USART1_UART_Init(); /* USER CODE BEGIN 2 */ xprintf("%d\n\r", 1234); xprintf("%6d,%3d%%\n\r", -200, 5); xprintf("%-6u\n\r", 100); xprintf("%ld\n\r", 12345678); xprintf("%llu\n\r", 0x100000000); xprintf("%lld\n\r", -1LL); xprintf("%04x\n\r", 0xA3); xprintf("%08lX\n\r", 0x123ABC); xprintf("%016b\n\r", 0x550F); xprintf("%*d\n\r", 6, 100); xprintf("%s\n\r", "String"); xprintf("%5s\n\r", "abc"); xprintf("%-5s\n\r", "abc"); xprintf("%-5s\n\r", "abcdefg"); xprintf("%-5.5s\n\r", "abcdefg"); xprintf("%-.5s\n\r", "abcdefg"); xprintf("%-5.5s\n\r", "abc"); xprintf("%c\n\r", 'a'); xprintf("%12f\n\r", 10.0); xprintf("%.4E\n\r", 123.45678); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ xprintf("Input:"); if (xgets(pbuff, 256)) { if (pbuff[0] == '\0') { continue; } if (xatoi(&pbuff, &lval)) { xprintf("long val %ld\n\r", lval); } pbuff = buff; if (xatof(&pbuff, &fval)) { xprintf("float val %f\n\r", fval); } else { pbuff = buff; xprintf("string: %s\n\r", pbuff); } pbuff = buff; } } /* USER CODE END 3 */ }
运行结果
1234 -200, 5% 100 12345678 4294967296 -1 00a3 00123ABC 0101010100001111 100 String abc abc abcdefg abcde abcde abc a 10.000000 1.2346E+02 Input:12.1 float val 12.100000 Input:22222 long val 22222 float val 22222.000000 Input:.1 float val 0.100000 Input:123.s string: 123.s Input:sssa string: sssa Input:sq.1 string: sq.1 Input:
总结
从测试的结果看,xprintf的output功能挺稳定,但是input功能有些坑爹,atoi和atof函数,会直接改变输入参数缓存的位置,直接导致循环中处理完毕后,不得不重新规整数据。但这属于小bug,直接在处理函数里面拿指针接着就行,稍微优化下就能满足使用。