在学c语言的时候,经典的hello world程序,是通过printf函数实现了。有了这个函数,就可以随意的向屏幕打印数据了。在嵌入式中,其实也是可以用printf函数的,不过需要稍微麻烦点的移植。毕竟,在嵌入式中,所有实现的都要自己来弄,不在向PC程序开发一样,很多库函数,操作系统已经搞好,就用就行了。
首先,是要去下载能实现printf的源代码。这里用的是国嵌提供的。有两个文件夹,一个include,里面一些头文件,另外一个lib,实现printf的需要的额外的程序。
中间的是inlcude中的头文件,下边是lib中的文件。
在lib中有一个Makefile。这个Makefile就是用来编译这些代码的,将所有的代码生成.o文件。供主函数链接使用。
可以发现,怎么没有printf.c文件了。那是因为这个printf.c文件是要我们自己来编写的。也就是printf函数和scanf函数要我们自己来写的,不过写的时候,可以使用lib中的一些函数来简化编写。
以上是printf的代码,使用一个数组来保存最终转化出来的字符串,大小是1024个字节。根据需要,这个大小可以更改。
函数的主要部分就是变参的处理。Printf是一个变参的函数,即函数的参数是不固定的。但是第一个参数是知道的,是一个字符串,通过这个字符串,可以知道有几个参数,从而在对这些参数进行处理。
va_start。提取字符串中的参数,即看%d,%c这些。知道有哪些参数。然后将这些参数保存在args中。
vsprintf。通过原始字符串和参数,转换后写入到新的字符串outbuf中。
va_end。这个是固定的。目前不知道有什么用。
通过上面三个函数,就实现了pritf的输入字符串的处理了。下面就调用putc函数,将转换后的字符串依次发送到串口即可了。
以上是scanf的函数。和printf函数类似,不过是先接收串口接收的数据,然后再对数据进行处理。
最后就是关键的地方了,代码写好了,怎么编译了。这个时候就要用到lib中提供的Makefile了。
这个Makefile实现了对lib文件下的各个c代码编译。最终生成lib.o供外部调用。${CFLAGS}这个是外部定义的变量。
剩下就要修改外部的总的Makefile了。
第一行:定义一些目标,就是一些.o文件。链接的时候,就链接这些文件。一个start.o,对应之前写的bootloader的start.S。main.o就对应main.c。device/dev.o这个对应外设驱动的代码集合,如LED,外部中断,串口等等这些。lib/lib.o这个就对应上面说到在lib下生成的lib.o,集合了printf和scanf。
第二行:定义一个参数CFLAGS,这个参数供调用的Makefile使用的。Makefile也是可以调用其他makefile的,这个时候,上层的makefile可以定义参数供下层的makefile使用。定义的这个参数是编译选项用的,-fno-builtin是说函数不是内建的函数,有可能我们写的函数和编译器的内建函数的名字是一样的。-I指的是搜索的头文件的目录,这里指定include目下,因为在实现printf的时候,有调用include中的头文件,所以需要告诉编译器这些头文件在什么地方。使用-I参数。$(shell pwd)这个是shell中的一些用法,调用pwd命令,返回的值就是这个$(shell pwd)的值。
第三行:将定义的参数CFLAGS导出去,这样外部的makefile就可以使用这个参数了。在lib下的makefile中是有用到这个参数的。
5-16行:实现编译链接。
18-19行: 执行device下的makefile。make –C device指的是跳转到device目录下去执行makefile。后面的all是具体执行makefile的目标
21-22行:和上面的一样,跳转到lib目录下,执行makefile命令,目标是all。
25-29行:伪目标,清除文件使用的。
以上是主目录的结构。只有main,makefile,链接脚本,start四个代码。其他代码都给弄到对应的文件下去了。
在device下,是各个外设驱动的代码。里面也有一个makefile。
以上是内容,就是将各个外设的代码给编译链接成dev.o文件,供外部使用。
这样,只需在主目录下,执行make命令。需要的.bit就生成了。是不是很方便了。
这样,就可以在程序中直接使用printf和scanf函数了。下面就来做一个简单的控制台。
上面的东西比较熟悉吧,下载程序,或者执行一些操作的时候,就有这个界面了。然后我们输入不同的选择,开发板就执行对应的操作。下面就来实现这个简单的串口控制台。
假设有5个选项。
1、 LED亮
2、 LED灭
3、 发送,hello world
4、 发送,weiqi7777
5、 发送,chinaaet
输入其他选项的话,就发送,WRONG。
执行的效果。
这是不是有点uboot的雏形了。当然这个控制台还做得比较简单。只有简单的几个命令。学到后面可以加更多的命令。
当输入不同的命令,会执行对应的操作。
这样,一个简单的控制台就做好了,同时也移植好了printf和scanf了。
对比STM32
在STM32中也是可以使用printf和scanf函数的。不过移植起来就简单多了,因为MDK将这个实现printf和scanf的库给提供了,我们只要使用即可,不过还是需要简单的修改,因为printf默认是输出到屏幕的,但是嵌入式中哪有屏幕,所以需要重定向到串口。
在STM32中,要加入stdio.h头文件,这个头文件是不是很熟悉啊,那是当然了,在c语言学习中,这个库可是基本每个程序都有了,因为里面包括了printf和scanf啊。
当然这个stdio头文件和我们学c语言的那个头文件不一样,这个是为嵌入式用的。在MDK中修改,选择使用MicroLIB。
最后,重写fputc函数。
因为printf函数,最终是调用fputc函数,将字符串发出去的,所以我们重写fputc函数,就将数据通过串口发送出去了。
至于scanf,目前没有用到过,所以还不知道。有知道的,麻烦请告知下。
以上,就实现了最重要的一个函数printf和scanf函数的移植了。对于移植,我们只需要把别人做好的代码拿来,稍微修改即可了。
上一篇:扩大ARM SoC的验证覆盖与缩短仿真时间
下一篇:基于S3C6410的ARM11学习(十七) 串口走起来
推荐阅读最新更新时间:2024-10-13 20:36
设计资源 培训 开发板 精华推荐
- 2ED300E17-SFO,评估板为 Eice DRIVER 2ED300C17-S 和 2ED300C17-ST IGBT 驱动器提供基本支持
- 使用 Analog Devices 的 LT3468ES5 的参考设计
- 使用 MaxLinear, Inc 的 AS2733T5-3.3 的参考设计
- LTC3407AEDD 演示板,双通道,同步。 DFN 中的 600mA 1.5MHz 降压转换器
- 郑师创客nfc水卡
- SP6205 低噪声、300mA 和 500mA CMOS LDO 稳压器的典型应用电路
- 烙铁改制
- LT1934IDCB 3.3V降压转换器典型应用电路
- LTC3118 的典型应用 - 具有低损耗双输入电源路径的 18V、2A 降压-升压型 DC/DC 转换器
- 使用 ON Semiconductor 的 NCP5006 的参考设计