Learn lumit Step 5 : 实现 printf
Learn lumit Step 5 : 实现 printf 输出++++++++++++++++++++++++++++++++++++++++++++++++++++++
串口能够驱动起来之后,按照惯例我们就要 helloworld 了。其实格式化输出
printf 的作用在嵌入式开发中的地位远不止此,它不仅是一个非常重要的调试手段,
也是一个无法回避的技术细节。这一节对于很多做 ARM 开发的我相信都很重要,
特别是对于使用 ADS1.2 上 ARMCC 编译器的开发者来说,这里提到的一个技巧也许
能帮上你不少忙。
我在知道怎么用技巧实现 printf 输出之前,一直都是用 SkyEye 里面的一个被
起名为 skyeye_printf.c 的文件,它实际上是实现了一个自制简易可用的 printf ,
其简单实用的程度好比 Wiggler Jtag 仿真调试头。这个文件是从水木清华 BBS 上
wohaha 大侠的一篇帖子里面找出来的,虽然它在嵌入式应用场合也足够好用,不过
还是有很多不足,我把它也放到附件里面,有兴趣的朋友可以下载来看。
上面的 skyeye_printf.c 主要是自己实现了格式化输出那些 % 的输出格式,
最终通过字符输出函数 skyeye_putc 这样一个 low-level 的输出接口获得实现的。
但在 ARM ADS1.2 里面已经有了足够好的 C 库来实现格式化字符串的工作,它所缺的
仅仅就是一个底层的输出接口函数。通常这个最底层的输出接口就是通过某一个串口
把字符发送出来,在另一端用超级终端这样的工具显示出来就可以。
在 ADS1.2 的安装目录下 ( 我的机器上是 C:\Program Files\ARM\ADSv1_2\PDF )
有一篇非常好的文档教你怎么实现这个接口,文档名是 ADS_CompilerGuide_D.pdf ,
在 The C and C++ Library 章节 4-76 (page166) 位置处,这是一个经典的不能再经典
的关于 printf 实现的例子,我们所要做的就是把 fputc 实现为我们上一节里面已经做
好的 uart_sendchar ,其他还有 ferror 和 __stdout 的声明也不可少。
具体实现的代码如下:
/* retarget code of printf low-level function */
/* *************************************************************** */
#include <stdio.h>
struct __FILE { int handle; /* Add whatever you need here */};
FILE __stdout;
int fputc(int ch, FILE *f)
{
if( ch == '\n' )
{
uart_putchar( UART0_BASE, '\r' );
uart_putchar( UART0_BASE, '\n' );
}
else
uart_putchar( UART0_BASE, ch );
return ch;
}
int ferror(FILE *f)
{
return EOF;
}
/* *************************************************************** */
只要把上面这一段代码嵌入到你的 main 之上,然后在调用 printf 之前把串口
按照需要的波特率初始化好,就可以方便地使用 printf 提供的标准格式化输出功能了。
附件里面是一个现成的例子,可以在 lumit4510 的板子上烧进去,运行输出的结果
如下:
printf
hello, lumiter!
test_rw = 0x2468, test_ro = 0x1357, test_bss = 0x0
printf
hello, lumiter!
test_rw = 0x2468, test_ro = 0x1357, test_bss = 0x0
特别要说明的是,它完成了从加电启动到实现 printf 的全部过程,不需要借助
任何的系统调用,和 bootloader 无关。有点板子不需要这样实现也可以用 printf 输出,
那主要是借助了板子上 SWI 软中断接口,其实我倒觉得这个顺序应该反过来,printf 是
实现一个 bootloader 前必须经过的一个环节,不知大家是否赞同?
页:
[1]