串口这节的资料真的是少的可怜啊~~~国嵌这节讲的真心不敢恭维,网上的资料基本都是一个样子,不是说说s3c6400.c和samsung.c这两个文件就是说说驱动的结构。都是些意义不大的东西。
首先,我先说说本节的学习资料:
1.<linux设备驱动开发详解> 宋宝华 这本说里面对uart的基本驱动结构讲的是比较细致的是深入tty设备驱动讲解的,但是缺乏从驱动到应用的一贯性。
2.国嵌视频和ppt 主要是将驱动的框架进行了大致讲解,即终端设备的基本概念
3.<精通linux设备驱动程序开发> 宋宝华译 有一个详细的uart驱动程序 可以和s3c6400.c和samsung.c文件进行对比学习
4.linux内核源码包括s3c6400.c samsung.c samsung.h serial.c termbits.h serial_core.c文件等
5.<linux高级程序设计> 杨宗德 第七章 这本说的是对于终端串口的应用开发。可以把底层驱动和应用进行联系。
6.<嵌入式linux应用开发完全手册> 韦东山 我觉得这节对终端的概念讲解还是挺好的 ,但是对于串口驱动讲的挺一般的 因为主要是讲8250的(可以不看)
本节的学习目的:
1.能够使用ok6410上面的串口进行通信,即应用程序的开发。
2.理解uart驱动结构和tty驱动的结构。这个结构很复杂理解就可以了~~~~~~
本节保留问题:
最大的问题就是下面的问题一!!!
本节知识点:
1.本节最大的知识点就是uart的驱动结构,我们就先入手对结构的分析(分析的是linux中串口驱动的结构)。
第一:首先要知道在linux中串口驱动是终端设备的一种,终端设备是字符设备,所有串口驱动是字符设备驱动,是通过dev目录下的文件节点对设备进行操作的。这样我们就有了一点初步的从驱动到应用的想法。
第二:我们看下s3c6400.c samsung.c 6410串口驱动文件。这个两个文件是让我最迷糊的两个文件,初一看跟什么uart_driver uart_op uart_port一点关系都没有。那是因为好多samsung的片子共用一套串口驱动,什么2440,6410等,所以就在这个层次上有进行了一次封装,就有了s3c6400.c这个文件了。
第三:对于s3c6400.c samsung.c具体的我还不是很懂,但是大体上我可以猜一下,应该是在s3c6400.c中通过类似s3c6400_uart_inf中的type变量,告诉驱动类型应该是s3c6410的,然后改了uart各种寄存器的基地址,让这些寄存器地址变成了6410的,并且也调用了6410的uart_port和uart_driver。因为uart_op的功能都是一样的,所以共用同一套op。
第四:其实自己也可以写这个结构的uart驱动,步骤为
1.
填写uart_port 和uart_driver 填写uart_driver主要的目的是为了在注册uart_driver的时候能够创建出字符设备其实就是注册一个uart的字符设备 填写uart_port主要是为了在往对应uart驱动中添加port的时候能够跟uart寄存器(即基地址),中断,fifo建立联系,但是同时也产生了
问题一:
因为在s3c6400.c文件中,对这一步进行了封装,我没有找到对应6410的uart_driver和uart_port赋值,不知道基地址应该填写那个基地址,是否进行ioremap映射,也不知道什么console ,tty_driver什么都进行赋值。
2.
uart_op结构填写,然后uart_register_driver()对uart_driver进行注册,uart_add_one_port()对uart_port进行注册,这里出现了
问题二:
uart_op中的函数都分别有对应的功能,这些函数应该是给上层tty驱动调用的。但是具体都是怎么调用的,都在那里调用就不知道了,是通过什么联系到应该程序中的write和read函数和tcsetattr等终端函数 的也不知道。这些函数那些有用,那些需要填写,因为不知道上面是怎么调用的 所以也不知道怎么写。
3.
但是有一个问题就是 字符设备的注册是在uart_register_driver的时候进行的,uart的初始化是在uart_add_one_port的时候进行的,我觉得应该是系统完成的,包括uart初始化,fifo模式,uart中断设置等应该都不用我们自己设置,中断的初始化是在uart_op->startup函数调用的时候完成的,这个函数是在应用程序打开uart设备节点的时候调用的。
4.
根据datasheet 根据uart_op中函数的功能 根据上层tty驱动都是怎么一个调用关系来写op中的所有函数。这里体现了tty体系的主要功能,不去考虑终端设备具体是什么,对于这一类设备都用一套统一的接口给应用程序。
问题三:也不知道uart这个具体的终端设备怎么跟tty体系建立关系,还是uart这个驱动的结构本身就是tty驱动体系的一个封装啊。
第五:上面第四点已经说明了,我没有写出这个结构的串口驱动的最重要的三点原因,其实samsung.c文件,也是按照第四点的那几步进行的,主要是他把uart_driver和uart_port进行了封装,没有找到。然后我按照裸机的uart驱动,写了一个利用ioremap,没有fifo,没有中断,轮询判断标志位的uart驱动。但是也没有运行成功,原因是samsung.c这个uart驱动,一次设置了s3c6410的4个uart,也就是说它先初始化了fifo,初始化了各种乱七八糟的uart寄存器,我觉得其实ioremap的这个方式一定可以的,如果想成功就把其他不用的uart寄存器都写入初始值就可以了。
2.比较一下利用ioremap的uart驱动和linux这个终端模式的uart驱动:
第一:linux的这个驱动虽然层次多,麻烦,难以理解,但是这是一个tty终端驱动框架,我们是应该了解的。
第二:linux的这个驱动框架,不用我们自己去配置uart初始化的寄存器,尤其是fifo啊 uart中断啊 这些比较麻烦的寄存器,这些步骤应是在系统移植的过程的时候完成的。
第三:linux的这个uart驱动,层次很多,但是能完成把所有tty终端驱动(键盘,显示器等)不管硬件是什么的情况,给终端应用留出统一的接口。其实也是无形的减少了中间的代码量。如果是自己写uart驱动,还得匹配write和read函数。
第四:linux的uart驱动,在tty体系中,里面是一套完整的接受体系,linux写好的首发缓存区要稳定一些。
3.对于uart驱动我想说,最重要的还是来了解下uart驱动和tty驱动的驱动结构,然后能学会写终端应用程序能够控制串口就好了,想写一个uart驱动还是有困难的,因为这个tty驱动结构的层次有点多。并且tty驱动和硬件相连的部分(即带有fifo的uart初始化)也不知道是在linux源码的那个地方(那个文件)完成的,这还涉及到了linux系统移植了。
4.宋宝华在<linux设备驱动开发详解>中提到,uart设备驱动完全可以遵循tty驱动来设计,也就是其实仅仅利用tty终端驱动也能写出uart驱动,就是把uart当成一个普通的tty_driver结构,根据终端的operation函数集(即终端的读与写),把uart的硬件功能添加到operation函数集中去,就是对应fifo啊,中断啊这些寄存器配置得自己写了。其实linux已经完成把tty_operation到uart_op的过程,tty_regiser到uart_register的过程了,叫做串口核心层 在serial_core.c文件中。其实就是把tty驱动封装成了uart驱动。这就变相的解决了上面的问题二和问题三,对于问题三可以说uart本身就是tty是在serial_core.c文件中封装的,应该是不用建立什么关系的,对于问题二,找到了uart_op是怎么被调用的了,是跟tty_operation对应的,具体tty_operation是怎么到read,write的,那是tty驱动体系的事情,我们在下面进行分析。对于中断啊,fifo啊在那里初始化的,可以去找uart_add_one_port函数,在serial_core.c文件里面,应该是在这个函数中对uart进行的硬件初始化的。
5.终端是一类字符设备的统称:控制台,串口,伪终端
控制台:打印内核信息,能映射到一个真正的终端上。
伪终端:成对出现类似telnet
串口:就是真实的串口设备,一般映射到真正的终端上。
6.终端体系结构:tty核心层,tty线路规程,tty驱动层
tty_operation结构在tty驱动层,其中一部分函数是给tty核心层调用的,一部分是像字符设备驱动一样通过vfs虚拟文件系统留给应用程序的接口的。所有到这里就明白了uart_op到底是怎么到应用程序的。
tty核心层主要负责在用户层接受数据,tty线路规程的作用是来格式化的,来修改协议的,比如说键盘输入中的tab按键问题,tty驱动层是负责硬件控制的。
如图:
两条路:1.串口驱动层----->tty驱动层-------->tty核心层------->tty线路规程--------->tty核心层------------->用户空间
2.串口驱动层----->tty驱动层-------->tty核心层------->用户空间
相反依然,这节tty驱动不是重点,所以不再多说了,对于tty驱动的学习我觉得最好的还是,宋宝华的<linux设备驱动开发详解>讲的很详细。
7.uart应用程序详解:
第一:打开设备文件,国嵌内核的串口1设备文件是/dev/s3c2410_serial1
第二:设置tio.c_cflag=B115200|CS8|CREAD|CLOCAL 设置的是波特率
第三:通过这个终端控制函数tcsetattr(fd,TCSANOW,&tio) 把波特率设置给串口1
第四:通过read和write进行读写设备文件,即对串口进行读写 ,其实这里是对终端进行读写。
第五:关闭设备文件
这样就对串口进行了控制,可以实现gps,gsm等设备了。
对于终端应用程序的细节应该看,杨宗德的<linux高级程序设计>
本节代码:
本节代码就是一个uart_test.c的一个串口应用程序,驱动是内核里面的,内核是国嵌2.6.36版本的,实现了在波特率115200的情况下,串口收到一个数据,然后再发送出这个数据。
对于驱动程序设计,我们可以仅仅了解模式,但是对于应用程序设计,我们要学会使用它,什么波特率的设置啊,串口模式的设置啊,由于驱动中对应的设置函数都是固定的所以也不是太用我们去考虑,模式很固定,尽管换了板载换了驱动,我么依然可以使用差不多的应用程序,但是有一个东西是很值得注意的,就是设备节点的名字,我们要学会在驱动中找到这个名字。在samsung.c文件中 static struct uart_driver s3c24xx_uart_drv这个结构中的dev_name元素 就是设备节点的名字。还要补充一点就是对于uart的移植一般改的应该就是这个文件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <error.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <time.h>
#include <termios.h>
int main()
{
char *buf;
int fd;
int i;
struct termios tio;
fd=open("/dev/s3c2410_serial1",O_RDWR|O_NDELAY|O_NOCTTY);
tio.c_cflag=B115200|CS8|CREAD|CLOCAL;
tcsetattr(fd,TCSANOW,&tio);
while(1)
{
i=read(fd,buf,1);
if(i>0)
write(fd,buf,1);
}
close(fd);
return 0;
}
所有评论(0)