linux+UART串口驱动开发文档Word下载.docx
- 文档编号:8365132
- 上传时间:2023-05-11
- 格式:DOCX
- 页数:14
- 大小:53.70KB
linux+UART串口驱动开发文档Word下载.docx
《linux+UART串口驱动开发文档Word下载.docx》由会员分享,可在线阅读,更多相关《linux+UART串口驱动开发文档Word下载.docx(14页珍藏版)》请在冰点文库上搜索。
√串口物理内存映射到虚存的时机:
依据上面所介绍的两条执行路径,再看内核的内存初始化的调用时期,只有完成这个初始化后才能进行物理内存到虚存的映射,内存的初始化主要是在start_kernel中调用的mem_init,这个调用明显在uart_console_init之后,在fbmem_init之后,到此就全部说明了为何不能在对串口使用ioremap进行物理内存的映射了。
那么究竟要在什么时机用什么方法进行串口物理内存的映射呢?
√串口物理内存的映射方式:
参考ep93xx的板载I/O的映射处理,它的处理方式是一次性将所有的物理I/O所在的内存空间映射到虚存空间,映射的基址是IO_BASE_VIRT,大小是IO_SIZE.
/*WhereinvirtualmemorytheIOdevices(timers,systemcontrollers
*andsoon).Thisgetsusedinarch/arm/mach-ep93xx/mm.c.*/
#defineIO_BASE_VIRT0xFF000000//VirtualaddressofIO
#defineIO_BASE_PHYS0x80000000//PhysicaladdressofIO
#defineIO_SIZE0x00A00000//Howmuch?
完成映射的函数是ep93xx_map_io,所有要进行映射内存都在ep93xx_io_desc结构当中描述,我们的串口映射也加在这个地方,基址分别如下:
文件:
linux-2.4.21/include/asm-arm/arch-ep93xx/regmap.h
#defineIO_W83697_UART_BASE0x20000000
#defineIO_W83697_UART_SIZE0x1000
#defineIO_W83977_UART_BASE0x30000000
#defineIO_W83977_UART_SIZE0x1000
#defineIO_SIZE_2(IO_SIZE+0x100000)
#defineIO_BASE83697_VIRTIO_BASE_VIRT+IO_SIZE
#defineIO_BASE83977_VIRTIO_BASE_VIRT+IO_SIZE_2
ep93xx_map_io完成是在arch初始化中赋值给structmachine_descmdesc这个机器描述结构体,主要由位于mach-ep93xxarch.c文件中如下宏完成此结构的初始化:
MACHINE_START(EDB9302,"
edb9302"
)
…..
MAPIO(ep93xx_map_io)//初始化.map_io=ep93xx_map_io….
MACHINE_END
最终这个函数在调用路径如下:
start_kernel→setup_arch→paging_init→(mdesc->
map_io())
至此完成串口物理内存的映射,这个过程在console_init调用之前,因此不会有问题,此种方法建立映射是直接创建物理内存页与虚存页的对应,大小为4k一页,最终调用的是create_mapping(),建立页表映射是与具体的平台相关的,位于mach_ep93xx/mm/proc-arm920.S文件中提供了与具体平台相关的页表建立函数,其中包括TLB表操作/Cache操作/页表操作等:
在上层的start_kernel→setup_arch→setup_processor调用下,会在proc-arm920.S文件中查找"
.proc.info"
节的__arm920_proc_info,并从中找到配置的process相关的操作函数,具体的arm页表建立的详情须要参看ARM内存管理的相关手册.
.section"
#alloc,#execinstr
.type__arm920_proc_info,#object
__arm920_proc_info:
.long0x41009200
……
.longarm920_processor_functions
.size__arm920_proc_info,.-__arm920_proc_info
在arm920_processor_functions中包含的页表操作如下:
/*pgtable*/
.wordcpu_arm920_set_pgd
.wordcpu_arm920_set_pmd
.wordcpu_arm920_set_pte
2.与串口硬件相关的宏主.
如下,下面将详术如下,并指出其具体被使用的环境上下文:
<
1>
.读写数据.
#defineUART_GET_CHAR(p)((readb((p)->
membase+W83697_UARTDR))&
0xff)
#defineUART_PUT_CHAR(p,c)writeb((c),(p)->
membase+W83697_UARTDR)
2>
.接收发送状态.
#defineUART_GET_RSR(p)((readb((p)->
membase+W83697_UARTRSR))&
#defineUART_PUT_RSR(p,c)writeb((c),(p)->
membase+W83697_UARTRSR)
3>
.发送及接收中断状态.
#defineUART_GET_CR(p)((readb((p)->
membase+W83697_UARTCR))&
#defineUART_PUT_CR(p,c)writeb((c),(p)->
membase+W83697_UARTCR)
#defineUART_GET_INT_STATUS(p)((readb((p)->
membase+W83697_UARTIIR))&
4>
.以及其它的中断使能设置等,在传送时打开传送中断即会产生传送中断.
#defineUART_PUT_ICR(p,c)writeb((c),(p)->
membase+W83697_UARTICR)
5>
.FIFO的状态,是否读空/是否写满;
每次读时必须读至FIFO空,写时必须等到FIFO不满时才能写(要等硬件传送完).
接收中断读空FIFO的判断:
status=UART_GET_FR(port);
while(UART_RX_DATA(status)&
&
max_count--){
}
发送中断写FIFO:
当发送缓冲区中有数据要传送时,置发送中断使能,随后即产生传送中断,此时FIFO为空,传送半个FIFO大小的字节,如果发送缓冲区数据传完,则关闭发送中断.
6>
.传送时可直接写串口数据口,而不使用中断,但必须等待检测FIFO的状态
do{
}while(!
UART_TX_READY(status));
//waitfortxbuffernotfull...
3.串口驱动的参数配置
串口的参数主要包括如下几个参数,全部都记录在uart_port结构上,为静态的赋值,本串口驱动支持6个设备,所以驱动中就包括了6个port,一个串口对应一个port口,他们之间除了对应的中断号/寄存器起始基址/次设备号不同之外,其它的参数基本相同.
√串口对应中断,这里六个串口,其中有3个串口使用的系统外部中断0/1/2,其中另外几个中断用提GPIO中断,具体有关GPIO中断的内容可参见EP93XX芯片手册,GPIO中断共享一个系统中断向量,涉及中断共享的问题,后面将详述LINUX中的中断共享支持.
√串口时钟,串口时钟用来转换计算须要设置到配置寄存器当中的波特率比值,其计算方法为:
quot=(port->
uartclk/(16*baud));
baud为当前设置的波特率,可为115200等值,取决于所选的串口时钟源,quot即为要设置到寄存器当中的比值.
√串口基址,即串口所有配置寄存器基础址.
√串口次设备号(由驱动中的最低次设备号依次累加)
前面已经讲过了六个串口中断,这里详细列出对应情况如下,方便查找:
w83697的三个串口对应中断如下:
∙uart1:
读写数据寄存器偏移为00x3F8,对应系统外部中断INT_EXT[0].
∙uart2:
读写数据寄存器偏移为00x2F8,对应系统外部中断INT_EXT[1].
∙uart3:
读写数据寄存器偏移为00x3e8,对应系统外部中断INT_EXT[2].
∙uart4:
读写数据寄存器偏移为00x3e8,对应EGPIO[8].
w83977的两个串口对应中断如下:
读写数据寄存器偏移为00x3F8,对应EGPIO[1].
读写数据寄存器偏移为00x2F8,对应EGPIO[2].
下面列出其中一个具体的串口port的定义如下:
{
.port={
.membase=(void*)W83697_UART4_BASE,
.mapbase=W83697_UART4_BASE,
.iotype=SERIAL_IO_MEM,
.irq=W83697_IRQ_UART4,//串口中断号
.uartclk=1846100,//uart时钟,默认.
.fifosize=8,//硬件fifo大小.
.ops=&
amba_pops,//底层驱动的硬件操作集,如开关中断等.
.flags=ASYNC_BOOT_AUTOCONF,
.line=3,//串口在次设备数组中的索引号,须注意从0计起…
},
.dtr_mask=0,
.rts_mask=0,
4.串口驱动的底层接口函数
驱动文件:
linux-2.4.21/drivers/serial/Ep93xx_w83697.c
相关文件:
linux-2.4.21/drivers/serial/core.c下面详述.
函数:
w83697uart_rx_chars(structuart_port*port,structpt_regs*regs)
描述:
串口接收数据中断,此函数中应当注意的要点如下:
∙接收数据时,要注意判断FIFO是否读空(参见上述2点中说明).
∙接收数据放入flip缓冲区,此缓冲区专供缓存中断中接收到的数据,是最原始的串口数据,为更上一层中各种终端处理模式的原始数据,可以进行各种加工处理。
∙接收数据到flip缓冲区中时,须根据硬件接收状态,置每一个接收到的字符的接收标志,放在flag_buf_ptr当中,标志类型有TTY_NORMAL/TTY_PARITY/TTY_FRAME等,分别表示正常/校验出错/帧出错(无停止位)等.
∙每接收数据之后,会通过在退出中断前调用tty_flip_buffer_push()来往tq_timer任务列表中加一个队列任务,串口的队列任务主要是负责将中断接收到flip缓冲区中的数据往上传输至终端终冲区,队列任务的机制将在后面介绍,它是一种异步执行机制,在软中断中触发执行.
staticvoidw83697uart_tx_chars(structuart_port*port)
串口发送数据中断,发送中断中要做的事比较少,比起接收中断简单了好多,注意事项如下:
∙当上层要发送数据时,就会打开串口发送中断,此时FIFO为空,传送半个FIFO大小数据即退出,通常打开中断是通过更上一层的uart_flush_chars()调用,最终调用的是w83697uart_start_tx().
∙检测当没有数据要传输的时候,关闭传送中断,在传送之前与传送完之后都有检测.
∙最重要的一点是如果传送缓冲区当中的字符数已经小于WAKEUP_CHARS,则可以唤醒当前正在使用串口进行传送的进程,这里是通过tasklet机制来完成,这也是一异步执行机制.
顺带介绍开关中断接口:
staticvoidw83697uart_start_tx(structuart_port*port,unsignedinttty_start)
staticvoidw83697uart_stop_tx(structuart_port*port,unsignedinttty_stop)
staticvoidw83697uart_int(intirq,void*dev_id,structpt_regs*regs)
中断处理函数,为3个使用系统外部中断的的串口的中断入口,其中必须处理的中断状态分为如下几种,注意必须在处理中断时根据手册中的说明来清除中断,通常是读或写某些寄存器即可。
∙接收中断.
∙传送中断.
∙FIFO超时中断.
∙其它不具体处理的中断,必须读相应寄存器清中断.
staticvoidw83697uart_int2(intirq,void*dev_id,structpt_regs*regs)
中断处理函数,为另外几个使用串口使用的GPIO中断入口,GPIO中断共享同一个系统中断向量,必须根据GPIO的中断状态寄存器的相应位来判断对应的中断是属哪一个串口的,从而进行相应的处理,其实这个判断也是无所谓的,因为中断产生时传进来的参数已经含有了相应串口的参数,在判断完中断产生的GPIO口后立即调用w83697uart_int2完成具体的中断处理.
staticintw83697uart_startup(structuart_port*port)
串口开启后的初始化函数,主要完成初始化配置,以及安装中断处理了函数,初始化配置包括打开中断使能标志。
staticvoidw83697uart_shutdown(structuart_port*port)
串口关闭函数,清除配置,半闭中断.
staticvoidw83697uart_change_speed(structuart_port*port,unsignedintcflag,unsignedintiflag,unsignedintquot)
配置函数,经由上次调用下来,主要配制串口的波特率比,以及各种容错处理,在串口打开初始化时会被调用,在必变串口波特率/校验方式/停止位/传送位数等参数时会被调用.
5.串口驱动与上层的接口关联
linux-2.4.21/drivers/serial/core.c
这一层接口是串口驱动中的共用部分代码,核心结构为structuart_driver.这一层上承TTY终端,下启串口底层,串口底层主要处理了与串口硬件相关的部分,并向上提供uart中间层向下的接口.Uartcoar向下与底层驱动的接口,通过一个staticstructuart_opsamba_pops结构完成?
这个结构直接赋值给串口structuart_amba_portamba_ports的.ops成员,最后将串口的port加入到uart_driver当中完成关联,通过uart_add_one_port加入.
staticint__initw83697uart_init(void)
intret,i;
ret=uart_register_driver(&
amba_reg);
if(ret==0){
for(i=0;
i<
UART_NR;
i++)
uart_add_one_port(&
amba_reg,&
amba_ports[i].port);
returnret;
前面讲到了有6个串口,除了w83697中的前三个串使用的是独立的系统外部中断之外,其它的在个串口是共享一个系统中断向量的,现在我们来看看多个中断是如何挂在一个系统中断向量表当中的,共享中断到底是什么样的一种机制?
进行分析代码可知,linux下的中断采用的是中断向量的方式,每一个中断对应一个中断描述数组当中的一项,结构为structirqdesc,其当中对应一成员结构为structirqactionr的成员action,这个即表示此中断向量对应的中断处理动作,这里引用从网上下载的一幅图讲明中断向量表与中断动作之间的关系:
摘自IBMdeveloperWorks中国
structirqaction{
void(*handler)(int,void*,structpt_regs*);
unsignedlongflags;
unsignedlongmask;
constchar*name;
void*dev_id;
structirqaction*next;
};
从上面的结构体与图当中,我们就可以很清楚的看到,一个中断向量表可以对应一个irqaction,也可能对应多个由链表链在一起的一个链表irqaction,这当中主要在安装中断的时候通过中断的标志位来决定:
∙安装中断处理,不可共享:
retval=request_irq(port->
irq,w83697uart_int,0,"
w83697_uart3"
port);
∙安装中断处理,可共享:
irq,w83697uart_int2,SA_SHIRQ,"
w83977_uart5"
由上即可知,安装共享中断时,只须指定安装的中断标志位flag为SA_SHIRQ,进入分析安装中断的处理可知,在安装时,会检测已经安装的中断是否支持共享中断,如果不支持,则新的中断安装动作失败;
如果已经安装的中断支持共享中断,则还必须检测将要安装的新中断是否支持中断共享,如果不支持则安装还是会失败,如果支持则将此新的中断处理链接到此中断向量对应的中断动作处理链表当中.
在产生中断时,共享中断向量中对应的中断处理程序链表中的每一个都会被调用,依据链表的次序来,这样处理虽然会有影响到效率,但是一般情况下中断传到用户的中断处理服务程序中时,由用户根据硬件的状态来决定是否处理中断,所以能常情况下都是立即就返回了,效率的影响不会是大的问题.
前面已经简单讲过了LINUX下的硬中断处理机制,其实硬中断的处理都由LINUX底层代码具体完成了,使用者一般在处理硬中断时是相当简单的,只须要用request_irq()简单的挂上中断即可,这里我们进一步介绍一下LINUX下的软中断机制,软中断机制相比起硬中断机制稍微复杂一些,而且在LINUX内核本身应用非常的广,它作为一种软性的异步执行机制,只有深入理解了它才能灵活的运用.
之所以提到内核的softirq机制,主要是因为在串口中断也使用了这些机制,理解了这些机制就能更加明白串口驱动一些问题,现在先提出几个问题如下:
∙前面提供到中断接收后数据,先放到flip缓冲区当中,这样让人很容易进一步想知道,中断处理的缓冲区的数据,用户进程读取串口时如何读到的?
很明显中断处于内核空间,用户读取串口输入进程是在用户空间,中断缓冲区中的数据如何被处理到终端缓冲区中,供用户读取的?
∙另外写串口时,是向终端缓冲区当中写入,那么上层的写操作如何知道下层缓冲区中的的数据是否传送完成?
用户空间的写串口进程处于什么样的状态?
如果是写完缓冲区就睡眠以保证高效的CPU使用率,那么何时才应该醒过来?
由谁负责醒过来?
1.往tq_timer任务队列中添加一项任务.
根据以上这两个问题,我们来深入代码分析,首先看接收缓冲区中的数据如何上传,前面已经提到过,接收中断处理完成后,会调用tty_flip_buffer_push(),这个函数完成的功能就是往一系统定义的任务队列当中加入一个任务,下面我们将详细的分析加入的任务最终是如何执行起来的.[任务:
这里所讲的任务可以直接理解成为一个相应的回调函数,LINUX下术语称作tasklet]
voidtty_flip_buf
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux UART 串口 驱动 开发 文档