zoukankan      html  css  js  c++  java
  • 14. 串口控制台建立

    14. 串口控制台建立

    串口控制台建立这一节的主要有三个内容:

    1.控制台框架搭建

    1.1控制台的分类介绍:

        1.1.1菜单型控制台:就是选中设置好的数字或者字母选项后执行相应功能的控制台:

    例如刚进入uboot之后的界面,就是菜单型控制台:

    等待我们输入命令,来执行相应的操作。例如上面,如果此时我们输入1,就是进行Format the nand Flash的操作:

        1.1.2解析型控制台:在上面的菜单型控制台里,选择5:Exit to command line:后会出现:

    就进入了解析型控制台:

    我们输入help后,控制台会去解析这一命令是不是该控制台所支持的,如果是,它会去调用相应的help函数来运行。会列出这个uboot所在的解析型控制台所支持的命令:

    由于解析型控制台实现起来会很复杂,今天就选择相对简单的菜单型控制台,来实现一个相对简单的自己的uboot控制台。

        菜单型控制台:

    可以看到一个菜单型控制台,第一件事就是先把菜单打印出来,而且会循环打印,首先想到的就是使用printf来打印菜单信息。代码:

    上面就是uboot里的提示信息,根据用户的选项来执行对应的功能函数,通过switch来选择实现。上面就是菜单型框架。

    在上面的菜单型控制台的实现里,最主要的两个函数是scanf和printf。

    2. scanf和printf的实现

    首先先实现printf函数,在实现一个函数的时候,第一考虑的是它的功能,printf的功能是打印信息的,printf在PC机里打印信息是打印到显示器里的。然而在开发板里,printf的信息是打印到串口终端的。接着就是要了解该函数的参数。要了解它的参数需要在命令行执行:man 3 printf:

    可以看到第一个就是我们的printf函数,只是它的参数是采用的变参的方式,变参用三个点表示,就是该函数的参数是不一定的。可以为零,可以为一个,可以为很多个。后面变参的形式由前面的const char *format来决定的。就是如果const char *format指定的是一个整形参数,那么。。。就得是整形参数,如果const char *format是字符型参数,那么。。。就得是字符型参数。

    实现思路:将传进来的参数转化为字符串型的buffer,然后字符串型的buffer通过一个while循环,利用上一节实现的void putc(unsigned char ch)函数输出到串口终端。

    实现思路的难点是怎么把传进来的参数转化为字符串型的buffer。

    1)在上一节的uboot工程里创建一个printf.c:

    2)接着把printf.c加入到Makefile里面去:

    3)printf.c的实现:(1)将变参转化为字符串。(2)打印字符串到串口。

        对于(2)打印字符串到串口。起始实现起来很方便,因为前面已经实现了putc()函数:

        有难度的是(1)将变参转化为字符串。这里需要用到C语言中已经实现好的一些宏。关于变参处理的一些宏(函数)。来帮我们完成。(1)将变参转化为字符串。实现步骤:

    上面就实现了将变参转化为字符串。但是问题又来了。上面的三个宏(函数)又得我们实现,而且实现起来是非常复杂的,而且是很标准的。这里就可以采用移植的方法。

    我们去实现这种嵌入式的软件,并不是说,所有的代码都需要自己去编写。比如后面还有实现tftp,tftp还需要ip协议栈,对于ip协议栈,我们是无法实现的。所以就是在实现嵌入式的软件的时候,很大时候需要做的是移植。

    我们实现移植的地方有两个,一个是Linux内核里,或是C库。这里谈到移植,对于现阶段的我们来说,难度太大了。所以现在我们只要知道,这三个函数的功能是什么,和会使用这些从Linux内核提取好的一些文件,集成到自己的uboot工程里面使用就可以了。

    下面是两个提取好的文件目录:

    把这两个文件夹拷贝到原先的工程目录下:

    接下来对工程文件夹下的Makefile进行如下修改:

    修改前:

    修改:

    1. 把printf.c放到lib文件夹下,在lib文件夹里一起编译得lib.o,则需要包含lib/lib.o。所以把printf.o换成lib/lib.o.
    2. 然后,gboot.bin是用过gboot.elf得到的。

    1. gboot.elf是通过链接上面的*.o文件得到的。

    1. 为了得到lib/lib.o则需要进入到lib文件夹进行编译,make的结果是获得all:

    all是在lib文件夹下make后得到的文件:

    1. 还要进入lib文件夹进行clean:

    最后修改的文件为:

    lib下的Makefile:

    顶层的Makefile为:

    修改好了之后进行编译make,会报下面的错:

    上面的ctype.c文件是在lib文件夹下的,这里报错说明已经把lib文件夹下的文件已经用进了gboot.bin工程,只是报错了。错误信息是在ctype.c有很多未定义的符号。接下来打开ctype.c,看看是怎么回事:

    上面看到ctyp.c里使用了这些符号,make为什么会报这些符号没定义呢?可以看到它包含了ctype.h头文件,打开:

    可以看到include/ctype.h里已经定义了这些符号了。可以看到ctype.h已经定义了这些符号。问题出在在编译的时候,arm-linux-gcc这编译器默认寻找头文件的路径,并没有找到我们现在工程里include/ctype.h头文件。这就需要我们在编译的时候,指定编译器arm-linux-gcc在编译的时候除了要去默认路径需找头文件,还要去我们指定的路径寻找头文件。自己指定头文件路径是通过-I参数去指定寻找头文件路径。

    打开lib目录下的Makefile:

    这里,我们需要在arm-linux-gcc命名后面加-I来指定还要需找头文件的路径。但是可以看到,已经有CFLAGS变量了,它就是来保存一些编译的参数的。CFLAGS是在工程文件夹下的Makefile定义的:

    打开顶层的Makefile加入CFLAGS的定义:

    该CFLAGS变量的值是一个路径,通过shell命令pwd获得当前的相对路径,包含该路径下的include文件夹。为了底层的目录能够用到该文件,需要将变量export:

    修改了之后,make,先前的问题解决了:

    可以看到前面的问题解决了,但是又出现了新的问题:printf.c里的第3行的va_list没有定义。打开printf.c:

    上面这个类型va_list是在lib/vsprintf.h文件里定义的:

    所以要解决这个错误,只需要将lib/vsprintf.h包含到printf.c即可解决:

    加入之后编译make,会有以下错误:

    第一个警告,uart.c:29: warning: conflicting types for built-in function 'putc'然后打开uart.c的29行,发现根本没有什么内敛函数。起始这是因为在Makefile里少了一个参数:-fno-builtin

    再把该变量CFLAGS加到该Makefile里编译c文件的地方:

    重新编译:

    会发现警告没了。又有错误。就是在编译lib/lib.o的时候发现未定义的vaprintf。哦哦,这是我们的函数写错了,应该是vsprintf。修改之后:

    修改后再make:还是原来的错误:

    按照道理修改了之后应该是不会再出现这样的错误的。再说现在工程里已经没有vaprintf的影子。怎么还是这个错误。无论怎能make clea后make,还是那个错误。搞到无奈,求救百度,百度无解。最后make clean之后进入lib文件夹,发现lib文件夹里生成的东西没有被清除。就在lib文件夹下进行make clean后退到原来工程目录,make clean,奇迹出现了:

    但是还是有一个错误,这是我们使用了为定义和实现的scanf函数导致的:

    这是我们先把它注释掉:

    修改之后,重新编译成功了:

    生成了gboot.bin:

    烧写到开发板,发现串口终端的内容输出了:

    上面就实现了printf函数了,接下来就是实现scanf函数了。在Redhat 6.4执行命令:man 3 scanf:

    可以看到返回值是int,第一个参数是const char *format,第二个参数还是变参。该函数实现的过程跟printf是相反的。Scanf函数是:1)先获取输入的字符串。2)获得字符串进行格式转换,传递给系统。实现代码:

    编译成功:

    到这里,printf和scanf都实现好了,编译并烧写到开发板:

    可以看到我们要的输入效果出现了。我们输入1,没有反应,输入4,会输出Error:wrong

    Selection。在后面的操作里将实现switch选择所对应的处理方法。到这里控制台的基本搭建完成了。

     

    3.程序结构的优化

    可以看到,现在目录下的文件已经很乱了,随着工程的深入,文件会越来越多,为了管理的方便,这里,我们把有关设备驱动的C文件放到dev文件夹里:

    这样,我们的工程文件的布局就合理多了。当然,dev文件夹下也需要Makefile文件,从lib文件夹下拷贝过去再进行修改:

    最后dev下的Makefile的内容为:

    这样就修改好了dev目录下的Makefile,注意,我们顶层的Makefile也需要改:

    做了上面的相应修改后,make:

    到这里,我们的基本工程框架已经实现了,结构也比较合理了。为以后跟多功能的加入搭建好了平台。

  • 相关阅读:
    基于ZYNQ SOC视频处理的常规设计
    Alinx黑金没有实现利用一个VDMA完成视频流读写的例程
    Vitis软件平台、vitis实例、裸机SOC(SDK)程序移植
    ZYNQ PS端IIC接口使用-笔记
    用信号量实现生产者&消费者模型
    C语言 -- 内存对齐
    排序算法---希尔排序
    排序算法---直接插入排序
    网络套接字编程介绍---UDP通信
    C++实现__搜索二叉树
  • 原文地址:https://www.cnblogs.com/FORFISH/p/5188823.html
Copyright © 2011-2022 走看看