背景
调试一颗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,直接在处理函数里面拿指针接着就行,稍微优化下就能满足使用。
我要赚赏金
