当前位置:首页 > 嵌入式 > 嵌入式软件
[导读]基于华邦W90P710处理器的Linux内核应用,详细介绍了Linux串口驱动的实现方法。同时对Linux文件系统操作入口函数及内核的编译做了详细的说明。

嵌入式Linux是一种很受欢迎的操作系统,具有开放源码、不存在黑箱技术、内核小、功能强大、运行稳定、效率高、易于定制裁减等特点[1],广泛应用于工控产品。很多工控产品需要和外部设备进行信息交换,而串口通信是最简单快捷的实现方法。在不同的工控产品中,由于对所选用的串口元件或者串口通信的数据格式、波特率等有不同的需求,需要对串口驱动进行开发。华邦W90P710采用ARM的ARM7TDMI微处理器核心,采用?滋CLinux-2.4.20内核,支持4组通用异步接收发送口(UART),下面基于华邦W90P710的串口驱动详细分析串口驱动的实现方法,实现嵌入式设备通过串口对外通信。

1 华邦W90P710 UART介绍

华邦W90P710支持4组UART,串口的控制主要通过以下寄存器实现[2]:
(1)行寄存器(UART_LCR):设置数据位长度、奇偶校验、停止位数。
(2)波特率除数寄存器(UART_DLL、UART_DLM):波特率发生器的公式为:BaudOut=crystal clock/16×[Divisor +2],Divisor为当前波特率。
(3)Modem控制寄存器(UART_MCR):控制RTS、CTS等信号。
(4)FIFO控制寄存器(UART_FCR):设置FIFO的长度,复位FIFO等控制。
(5)接收超时寄存器(UART_TOR):收到首个字节后接收器启动本超时,之后每收到一个字节后都会重置该值,在此超时时间内不再收到数据时,接收器会产生一个接收中断。
(6)中断控制器(UART_IER):设置接收、发送、行中断等。
在使用RXDn、TXDn前必须对GPIO进行配置,使能RXDn、TXDn,串口才可正常运行。GPIO配置对应表如表1所示。


2 Linux系统驱动介绍

设备驱动程序是操作系统内核和机器硬件之间的接口。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。同时,设备驱动程序是内核的一部分[3]。图1所示为设备驱动程序接口流程图。

Linux系统的设备分为字符设备、块设备和网络设备三种。字符设备是指存取时没有缓存的设备,只能顺序读写。典型的字符设备包括鼠标、键盘、串行口等;块设备一般都有缓存来支持,并且块设备必须能够支持随机存取。块设备主要包括硬盘设备、CD-ROM等;网络设备在Linux系统中用做专门的处理,Linux的网络系统主要是基于BSD Unix的socket机制[4]。

3 串口驱动程序详细介绍

一般来说,Linux的设备驱动程序包括驱动程序的注册和注销、设备的打开和释放、设备的读写操作、设备的控制操作、设备的中断和轮询处理等功能。下面就这些功能对串口驱动进行详细说明。

(1)串口设备的数据结构包括串口参数接收发送缓冲区等。串口参数包括波特率、数据位、数据起始位、奇偶校验、串口类型、发送缓冲区、接收缓冲区等,每个串口对应一个如下的数据结构:

typedef struct{
intbps;
intdatabits;
intstopbits;
intparity;
intsiotype;//串口参数
intopenflag;
intrecvTrigTimeout;
SIO_D_SEND_BUFFER*pSendBuf;//发送缓冲区
SIO_D_RECV_BUFFER*pRecvBuf;//接收缓冲区
struct fasync_struct *fasync_queue;
wait_queue_head_tread_wait;
}serial_dev;
static serial_dev serial_device;

(2)文件系统操作

入口函数对应文件操作函数read ()、write()、ioctl()、open()、close()。
struct file_operations serial_fops = {
owner:THIS_MODULE,
poll:serial_poll,
read:serial_read,
write:serial_write,
ioctl:serial_ioctl,
open:serial_open,
release:serial_release,
};

(3)驱动程序注册和注销。

驱动程序在应用前,需要在模块初始化时将设备注册到系统设备表中;不再使用时,将设备从系统中卸除。注册包括初始化定时器、初始化串口数据结构serial_device和字符设备注册。注销时直接调用设备注销函数[5]。
int __init topbandserial1_init(void)
{
init_timer(&timer);//初始化定时器结构
memset(&serial_device, 0, sizeof(serial_device));
result=register_chrdev(SERIAL1_MAJOR, "serial1",
&serial_fops);

}

(4)串口设备打开包括分配串口的接收发送缓冲区及中断注册[5]。
static int serial_open(struct inode *inode, struct file *filp)
{
dev->pRecvBuf = kmalloc(sizeof(SIO_D_RECV_BUFFER), GFP_KERNEL);
request_irq(INT_UART1,serial_interrupt,SA_SHIRQ,
"TopbandSerial1",&serial_device);

}

(5)串口设备释放包括释放内存空间、注销中断和删除定时器[5]。
static int serial_release(struct inode *inode, struct file *flip)
{
serial_dev *dev = flip->private_data;//释放内存空间
kfree(dev->fasync_queue);
CSR_WRITE(COM_IER_1, 0x00); /* 中断禁止 */
free_irq(INT_UART1, dev); //注销中断
del_timer(&timer);//删除定时器
MOD_DEC_USE_COUNT;
dev->openflag = 0;

}

(6)串口读数据是指返回接收缓冲区中已收到的数据。读取数据有两种方式,阻塞方式和非阻塞方式。阻塞方式[6]中用户程序执行读操作时如果没有数据可读,即让read()操作等待直到数据可读;非阻塞方式中当用户执行读操作时,不论串口是否接收到数据,设备驱动xxx_read()函数会立刻返回,read()函数系统调用也随即返回。
static int serial_read(struct file *filp, char *buf, size_t
count, loff_t *f_pos)
{
if(filp->f_flags & O_NONBLOCK)/非阻塞方式读取
retsts = serial_nonblock_read(dev,buf,count);
else/*阻塞方式读取*/
retsts = serial_block_read(dev,buf,count);

}

(7)串口写数据包括把数据存放在发送缓冲区、启动硬件发送及发送中断。当发送第一个字节后,硬件会产生发送中断,剩下的数据将在中断处理程序中发送。
static int serial_write(struct file *filp, const char *buf,
size_t count, loff_t *f_pos)
{
copy_from_user(&pSendBuf->frameData[pSendBuf->
bufWritex].data[0],buf, count);
CSR_WRITE(CMBOARD_GPIO_DATAOUT1,status1);
enable_tx_interrupt_1();

}

(8)串口控制包括设置串口波特率、奇偶校、停止位等,还可以定义其他特殊的控制。应用程序通过ioctl()调用把串口的参数传递给驱动程序,驱动程序再通过对硬件串口控制寄存器进行设置,来满足应用层用户要求。

static int serial_ioctl(struct inode *inode, struct file *flip,
unsigned int cmd, unsigned long arg)
{
switch(cmd){
case SERIAL_IOC_BPS:

break;
case SERIAL_IOC_SENDBUF:

break;
}
}

(9)中断处理包括对接收中断、发送中断、异常中断的处理。读取中断寄存器的状态,根据不同的中断类型分别处理。当收到数据时,硬件会产生接收中断,驱动程序把串口的数据读取出来,放在接收缓冲区中,直到所有数据读取完成;当发送数据时,硬件会产生发送中断,驱动程序把发送缓冲区的数据发送出去,直到所有数据发送完成;当串口接收或发送发生异常时,会产生异常中断,驱动程序根据情况把串口重新初始化,以便串口恢复正常。

static void serial_interrupt(int irq, void * dev_id,
struct pt_regs *regs)
{
status = CSR_READ(COM_IIR_1);
while(status & UART_IIR_STATUS_NO) == 0)
{
switch(status)
{
case UART_IIR_STATUS_RDA:
case UART_IIR_STATUS_TOUT:
receive_chars(dev,status);
break;
case UART_IIR_THRE:
transmit_chars(dev);
break;
}
status = CSR_READ(COM_IIR_1);
}
}

(10)定时器处理。中断接收程序只负责把数据读取到缓冲区,并没有指示缓冲区的数据可被用户使用,这时需要在超时程序中把可用标志置上,当用户调用read()函数时就可把接收缓冲区的数据返回。

static void serial_timer(unsigned long dummy)
{

serial_device.pRecvBuf->frameData
[serial_device.pRecvBuf->bufWritex].finished = 1;
mod_timer(&timer,jiffies+2);/* 20 ms 进一次 */
}

通过以上几个函数的处理,实现了串口的驱动。

4 驱动程序编译进Linux内核

以下以UART1为例,介绍驱动程序编译进Linux内核的过程,步骤如下:

(1)添加主次设备号。

主次设备号用来标识一个具体设备。主设备号用于标识设备类型,每种类型的设备需要一个对应的设备驱动程序。一个主设备可以有多个具体的设备与之对应。次设备号用于区分使用同种驱动程序的同类设备中多个不同的设备实例[7]。

在W90P710-?滋Clinux/?滋Clinux-distlinux-2.4.x/include/
linux目录下的major.h中定义主设备号,添加如下代码:
#define SERIAL1_MAJOR230
在W90P710-?滋Clinux/?滋Clinux-dist/vendors/Winbond/W90P710目录下的makefile中建立设备主次设备号(主设备号为230,次设备号为1),添加如下代码:
serial1,c,230,1

(2)在W90P710-?滋Clinux/?滋Clinux-dist/linux-2.4.x/drivers/char目录下的makefile中添加如下代码:

obj-$(CONFIG_TOPBAND_SERIAL1)+=w90p710_serial_1.o

(3)在W90P710-?滋Clinux/?滋Clinux-dist/linux-2.4.x/drivers/char目录下的config.in字符设备段中添加如下代码:

#if [ "$CONFIG_TOPBAND_SERIAL1" = "y" ]; then
bool ‘Topband serial1 support‘ CONFIG_TOPBAND_
SERIAL1
#fi

(4)在W90P710-?滋Clinux/?滋Clinux-dist目录下运行make menuconfig,在menuconfig的字符设备选项中可以看见刚刚添加的“CONFIG_TOPBAND_SERIAL1”选项,选上该项。使用make dep、 make clean、make三个命令编译Linux内核,生成内核文件linux.bin[8]。

(5)在W90P710-?滋Clinux/romdisk/dev目录下创建设备文件,输入命令:
mknod serial1 c 230 1
生成设备文件“serial1”,应用程序通过使用“/dev/ serial1”这个设备文件名就可对串口进行操作。

最后编写简单的串口测试程序,编译生成镜像文件;再把镜像文件romfs.img和内核文件linux.bin下载到开发板,把开发板的串口和PC机相连,PC机端使用串口调试工具发送测试数据,开发板能正确收发数据。

本文按驱动程序的功能详细介绍了W90P710微处理器实现串口驱动的方法,串口驱动程序是很典型的字符设备驱动程序,其他字符设备驱动和串口的实现方法是相同的,这对开发其他字符设备驱动程序有一定的借鉴作用。

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

随着汽车软件数量爆发式的增长,整个行业都需要重新思考汽车产品的开发流程。为此,Arm推出了丰富的硬件IP、新的系统IP,以及全新的汽车计算与计算子系统产品路线图,旨在为各种汽车应用实现性能、功能安全、可扩展等方面的支持。

关键字: ARM 汽车电子

双系统将是下述内容的主要介绍对象,通过这篇文章,小编希望大家可以对双系统的相关情况以及信息有所认识和了解,详细内容如下。

关键字: 双系统 Windows Linux

知名移动芯片设计公司ARM最近迈出重要一步,它正式推出汽车芯片设计。ARM推出的芯片设计方案名叫Neoverse,随同芯片一起推出的还有面向汽车制造商、汽车供应商的新系统。

关键字: ARM 汽车芯片 芯片

随着通用人工智能的发展,数据中心的计算需求逐步提高。针对多模态数据、大模型的推理和训练需要更高的算力支持,而随着算力提升与之而来的还需更关注在功耗方面的优化。对于头部云计算和服务厂商而言,针对专门用例提高每瓦性能变得至关...

关键字: ARM 服务器 AI Neoverse CSS

一直以来,riscv架构都是大家的关注焦点之一。因此针对大家的兴趣点所在,小编将为大家带来riscv架构的相关介绍,详细内容请看下文。

关键字: riscv ARM riscv架构

安装Linux操作系统并不复杂,下面是一个大致的步骤指南,以帮助您完成安装。1. 下载Linux发行版:首先,您需要从Linux发行版官方网站下载最新的ISO镜像文件。

关键字: Linux 操作系统 ISO镜像

计算机是由一堆硬件组成的,为了有限的控制这些硬件资源,于是就有了操作系统的产生,操作系统是软件子系统的一部分,是硬件基础上的第一层软件。

关键字: Linux 操作系统 计算机

Linux操作系统是一套免费使用和自由传播的类Unix操作系统,通常被称为GNU/Linux。它是由林纳斯·托瓦兹在1991年首次发布的,并基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。Lin...

关键字: Linux 操作系统

所谓进程间通信就是在不同进程之间传播或交换信息,它是一组编程接口,让程序员能够协调不同的进程,使之能在一个操作系统里同时运行,并相互传递、交换信息;还可以让一个程序能够在同一时间里处理许多用户的需求。

关键字: Linux 进程通信 编程接口

串口通信作为一种最传统的通信方式,在工业自动化、通讯、控制等领域得到广泛使用。

关键字: Linux 串口通信 通讯
关闭
关闭