这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 活动中心 » 板卡试用 » 【分享开发笔记,赚取电动螺丝刀】移植xprintf模块

共9条 1/1 1 跳转至

【分享开发笔记,赚取电动螺丝刀】移植xprintf模块

高工
2025-05-02 10:33:41   被打赏 27 分(兑奖)     打赏

背景

     调试一颗MCU,如果适配的环境不像RTT那样把调试串口都标准化了,这个时候就需要自行实现打印函数。如果个人实现的话,就有些重复造轮子了,最近看一篇关于xprintf的分享,感觉xprintf能够挺快的适配到MCU上,因此就尝试移植xprintf。

代码分析 

源码路径

http://elm-chan.org/fsw/strf/xprintf_j.html

         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,直接在处理函数里面拿指针接着就行,稍微优化下就能满足使用。





关键词: 电动     螺丝刀     xprintf     移植     串口    

专家
2025-05-05 16:06:54     打赏
2楼

感谢分享


专家
2025-05-06 09:23:41     打赏
3楼

代码是人工写的,还是AI写的,还行,没有仿真


工程师
2025-05-09 16:25:01     打赏
4楼

常见的模块移植, 步骤清晰


院士
2025-05-10 23:50:45     打赏
5楼

楼主,您这文章看着舒服呀!多写点呗



工程师
2025-05-11 07:29:42     打赏
6楼

感谢大佬分享这么好的文章。但是我有几个问题想了解一下?

1、fputc 与fgetc是否需要微库?

2、在不同的编译环境下是移植会有出入?

3、如果我需要修改为中断接收发送,需要如何更改接口函数?

4、是否支持RTOS的移植?

5、对于其他的开源的shell是否有他的优点与缺点?比如:rt-thread的shell?


高工
2025-05-11 17:44:52     打赏
7楼

楼主的代码是重定义串口的发送和接收函数吗?


专家
2025-05-12 10:14:16     打赏
8楼

思路好,专门为串口编写专用代码。能否适用于其它厂家的开发板?


专家
2025-05-13 15:38:58     打赏
9楼

有借鉴意义,这样和pruntf感觉很像。


共9条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]