Linux下如何处理串口中断?(linux串口中断)

随着计算机技术的不断发展,串口接口在工业自动化、医疗设备、消费电子等领域得到了广泛应用。而对于Linux操作系统而言,如何处理串口中断则成为了一个需要解决的问题。

串口中断是指通过串口传输数据时,接收端需要向发送端发送一些确认信息,以达到数据传输正确的目的。而在Linux下,处理串口中断的方法分为以下两种:

一、软件方式处理串口中断

在使用Linux进行串口通信时,可以利用系统调用(system call)机制要求系统为其注册串口中断服务程序,并利用此程序进行中断处理。

具体而言,可以使用Linux内核提供的”tty drivers”(tty驱动程序)来完成此项任务。 tty驱动程序可以从硬件中读取串口数据,接着将数据送到操作系统的中断请求队列中,由此触发中断服务程序进行处理。

不过值得注意的是,由于Linux中多个进程具有竞争关系的关系,这种方式很容易引起进程或线程阻塞等问题,需要进行深入的优化。

二、硬件方式处理串口中断

硬件方式处理串口中断则是指,可以利用Linux内核实现的interrupt(中断)机制,直接利用操作系统中的工具“注册中断处理函数”并与串口接口联系起来。

在进行硬件方式处理串口中断时,也可以利用Linux内核的I/O映射机制,将设备驱动程序与内核建立联系并实现中断处理。

不过这种方式需要进行一些硬件层面的开发人员才能够部署和调试。

无论采用软件方式还是硬件方式,处理串口中断对于Linux操作系统而言都是一个十分重要的任务。而对于工程师们而言,则需要根据具体的应用场景选择战略,并进行相关的程序设计和调试,以确保系统稳定并能够达到预期的数据传输目的。

成都网站建设公司-创新互联,建站经验丰富以策略为先导10多年以来专注数字化网站建设,提供企业网站建设,高端网站设计,响应式网站制作,设计师量身打造品牌风格,热线:028-86922220

求教,linux下网口虚拟串口驱动程序

开发虚拟串口驱动程序

虚拟串口就是当本地并没有对应的串口硬件设备,而为应用层提供串口设备一样的系统调用接口,以兼容原本使用本地串口的应用软件的“虚”设备。本文作者给出了一种在Windows平台上实现虚拟串口的方法,由此实现的“串口”具有真实串口完全相同的系统调用接口。

在很多应用中需要用到虚拟串口,如在Modem卡出现之前,已经有了接在计算机串口上的外部Modem,而且各种拔号程序也是通过串口与外部Modem通信的。为了让已有的拔号程序不做修改,像使用外部Modem一样使用内置卡,就需要内置卡的驱动程序虚拟一个串口设备。又如当前工业界使用的一些串口服务器,缺枯往往有8个或16个甚至更多的串口,以连接多个串口设备,再通过一个网卡直接连入以太网。与它在同一网络上的计算机就通过以太网与串口服务器上挂接的串口设备通信。为了让计算机中原来使用本地串口的软件兼容,就需要在计算机上提供虚拟串口驱动。

虚拟串口的设计关键在于,该“串口”实现后必须具有与真实串口完全相同的系统调用接口。要做到这点,从已有的串口设备驱动程序上做修改是更佳捷径。下文就介绍以Windows NT上的串口驱动程序为基础,开发可运行于Windows NT、Windows 2023、Windows XP的各个版本虚拟串口驱动程序。

串口驱动中使用的几个链表

由于串口是双工设备,在一个读请求发出来还没有完成之前,同时可以发出写请求,加上在驱动程序层所有I/O请求都要求异步完成,即前一个请求尚没有完成,下一个相同的请求可能又来了。为此,串口驱动程序需要使用多个双向链表数据结构来处理各种IRP(I/O Request Packet,I/O请求包)。当收到一个IRP,先判断是否可立即完成,可以马上处理并返回,如果不允许则将IRP插在相应链表尾,在适当的时候如设备有空闲时处理,这时往往会产生一个硬件中断,激发DPC(Deferred Procedure Call,暂缓过程调用)过程,由DPC处理函数逐个从链表头取出IRP并试着完成它。串口驱动中有以下几个链表和DPC(在serial.h中有定义):

ReadQueue 和 CompleteReadDpc

用于保存Read IRP的链表和用于调度的DPC,与DPC对应的处理函数是SerialCompleteRead,它在read.c文件中,该函数的主要任务就是从ReadQueue中提取下一个IRP,并试着完成它。

WriteQueue 和 CompleteWriteDpc

用于保存Write IRP的链表和对应的DPC,与DPC对应的函数是SeriaCompleteWrite,它的实现在write.c中,该函数负责从WriteQueue中提取IRP,并试着完成它。

MaskQueue 和 CommWaitDpc

这一对链表用于处理Windows串口驱动的一个特性:事件驱动机制。它允许应用程序预设一个事件标志,而后等待与标志对应事件发生。DPC所调用的函数是SerialCompleteWait,它实现在Waitmask.c文件中,该函数也是试着从MaskQueue中提取IRP并完成它。

PurgeQueue

该链表与前面几个稍有不同伏键洞,它没有与之相对应的DPC机制,而是在每次收到Purge请求时从PurgeQueue中逐个提取IRP并试着完成,因某种原因不能完成时则插入链表。相应的函数是purge.c文件中的SerialStartPurge。

以上机制是串口驱动程序的重要实现方法,在虚拟串口驱动中需要保留,但不同的是,硬件串口驱动中是ISR(中断服务程序)根据收、发或MODEM中断来激发相应的DPC,而在虚拟串口驱动中将因实际情况不同会有不同的激发机制。

DriverEntry的实现

DriverEntry是驱动程序的入口函数,相当于应用程序C语言中的main函数,开发一个虚拟串口驱动亮闷首先要修改的就是它。它的函数实体在initunlo.c文件中。只是在虚拟串口驱动中由于不与具体的硬件打交道,就不存在硬件资源分析、硬件初始化、判断其工作状态等处理,只需要为虚拟串建立设备对象、符号链接和初始化数据结构。一个典型函数实现大体如下:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

{

/*填写DriverObject->MajorFunction数组*/

/*建立设备对象*/

/*初始化SERIAL_DEVCIE_EXETENSION数据结构*/

Status = IoCreateDevice(DriverObject, sizeof(SERIAL_DEVICE_EXTENSION), &uniNameString, FILE_DEVICE_SERIAL_PORT, 0,TRUE,&deviceObject);

//初始化所有链表

InitializeListHead(&extension->ReadQueue);

InitializeListHead(…);

…;

//初始化所有DPC

KeInitializeDpc(&extension->CompleteReadDpc,SerailCompleteRead,extension);

KeInitializeDpc(…);

/*建立符号链接*/

SerialSetupExternalNaming(extension);

return Status;

}

SerialRead和SerialCompleteRead的实现

函数SerailRead和SerialCompleteRead决定了对Read IRP的响应策略,它们都存于read.c中。以串口服务器要用的虚拟串口为例,当串口服务器收到来自外部数据时将通过网络发至计算机,计算机则产生相应的网络中断并进行协议数据处理。网络接收线程缓存新收到的数据并激活CompleteReadDpc,从而SerialCompleteReadIrp得到调用,它再调用CompleteReadIrp对每个IRP进行处理。它们的实现大体如下:

NTSTATUS SerialRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)

{

/*此处略去变量声明和初始化*/

/*提取IRP中相关的数据*/

stack = IoGetCurrentIrpStackLocation(Irp);

ReadLen = stack->Parameters.Read.Length;

/*先看本地缓冲有数据否?有的话先读取*/

if(Extension->InCounter > 0 )

{ //注意这里要加锁,以防数据访问冲突

KeAcquireSpinLock(&Extension->

ReadBufferLock,&lIrql);

FirstRead = (ReadLen>Extension->

InCounter)? Extension->InCounter: ReadLen;

RtlCopyMemory(Irp->AssociatedIrp.

SystemBuffer,Extension->pInBuffer,FirstRead);

Extension->InCounter -= FirstRead;

ReadLen -= FirstRead;

KeReleaseSpinLock(&Extension->

ReadBufferLock,lIrql);//释放锁

}

/*是否已读到足够数据?是的话则完成该IRP*/

if( 0 == ReadLen)

{

status=STATUS_SUCCESS;

Irp->IoStatus.Status = status;

Irp->IoStatus.Information = FirstRead;

IoCompleteRequest(Irp,0);

return status;

}

/*没有则将IRP插入队列中,通过网络向串口服务器发出读数据请求*/

IoMarkIrpPending(Irp);

InsertWaitList(Extension->ReadQueue,Irp);

status = TdiSendAsync(Extension->ComChannel,pAckPacket,PacketLen(pAckPacket),(PVOID)ReadAckComplete,Irp);

/*返回PENDING,表示该IRP尚没有完成*/

return STATUS_PENDING;

}

Void CompleteReadIrp(IN PSERIAL_DEVICE_EXTENSION extension,IN PIRP Irp,IN PUCHAR pInData,IN ULONG Length )

{

/*此处略去变量声明和初始化*/

/*读取新数据*/

ReadLen = (ReadLen > Length)? Length : ReadLen;

if(ReadLen != 0)

{

RtlCopyMemory(pReadAsync->

pReadBuffer,pInData,ReadLen);

pReadAsync->pReadBuffer += ReadLen;

pReadAsync->ReadAlready += ReadLen;

extension->PerfStats.ReceivedCount +=

ReadLen;

}

else

{

/*因为串口服务器端只有在已经有了相应的数据或超过时间(此时,Length=0)才会发来应答并激活本DPC过程,所以此时已经超时,为了便于结束本IRP,这里有意改变TotalNeedRead,造成接收完毕的假象*/

pReadAsync->TotalNeedRead =

pReadAsync->ReadAlready;

}

if(pReadAsync->TotalNeedRead == pReadAsync->ReadAlready)

{

/*该IRP是否已经接收完毕,是的话则结束该

IRP*/

EndReadIrp(Irp);

/*从ReadQueue中取下一个IRP*/

}

/*本IRP没有完成也没有超时,则继续等待本DPC下次被激活,注意此时要判断IRP是否被要求取消*/

}

SerialWrite和SerailCompleteWrite的实现

SerialWrite和SerailCompleteWrite决定了Write IRP的实现。在SerialWrite中调用了网络发送函数TdiSendAsync,当该发送完成后将激活CompleteWriteDpc,调度SerialCompleteWrite函数,而它主要就是取出当前的WriteIRP,设置已经发送的数据数量,调用CompleteWriteIrp做该IRP的进一步处理。它们大体如下:

NTSTATUS SerialWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)

{

/*此处略去变量声明和初始化*/

/*从IRP中提取有关数据*/

stack=IoGetCurrentIrpStackLocation(Irp);

SendLen = stack->Parameters.Write.Length;

/*为网络发送和异步操作分配缓冲,在CompleteWrite中全部数据发送完后释放*/

pWriteAsync = ExAllocatePool(NonPagedPool,

SendLen+PACKET_HEADER_LEN+sizeof(WRITE_ASYNC));

if(pWriteAsync == NULL)

{

//错误处理

}

//保存异步数据

//设置网络发送数据包

BuildDataPacket(pPacket,WRITE,(USHORT)SendLen,pWriteAsync->pWriteBuffer);

/*先将IRP暂时阻塞并插入队列,在CompleteWrite中完成*/

IoMarkIrpPending(Irp);

InsertWaitList(extension->WriteQueue, Irp);

/*将写请求和相关数据通过网络发向串口服务器,由它负责将数据传到具体串口设备*/

status = TdiSendAsync(Extension->ComChannel,pPacket,PacketLen(pPacket),(PVOID)CompleteWriteIrp,Irp);

//统计数据累加

Extension->PerfStats.TranittedCount += SendLen;

return STATUS_PENDING;

}

NTSTATUS CompleteWriteIrp(IN PDEVICE_OBJECT deviceobject,IN PIRP pIrp,IN PVOID context)

{

/*此处略去变量声明和初始化*/

SendLen=pWriteAsync->TotalNeedWrite – pWriteAsync->WroteAlready;

if(SendLen == 0)//全部数据发送完毕

{

EndWaitWriteIrp(pWriteIrp,STATUS_SUCCESS,

pWriteAsync->WroteAlready,pWriteAsync);

//从WriteQueue中取下一个IRP;

}

else //发送剩余数据

{

if(pWriteIrp->Cancel)

{

//IRP被要求取消,完成WriteIrp

EndWaitWriteIrp(pWriteIrp,STATUS_CANCELLED,

pWriteAsync->WroteAlready,pWriteAsync);

return STATUS_CANCELED;

}

else

{

//再次设置网络数据包并发送

BuildDataPacket(…);

status = TdiSendAsync(…);

//统计数据累加

Extension->PerfStats.TranittedCount +=

SendLen;

return STATUS_MORE_PROCESSING_REQUIRED;

}

}

}

其他几个接口函数的实现

除Read/Write外,SerialUnload、SerialCreateOpen、 SerialClose、SerialCleanup、SerailFlush等调用接口是硬件相关性比较弱的接口函数,基本不要修改,直接删除原来操作硬件的部分即可。复杂一点就是SerialIoControl,该接口函数包含有大量设置、读取串口硬件状态的处理,可建立一个本地数据结构随时保存虚拟串口的当前硬件状态。同时为了保证串口服务器端的真实串口状态和上层软件要求的一致,需要将所有设置请求通过网络发送到服务器端,由它负责改变真实硬件的状态。

关于linux 串口中断的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

创新互联【028-86922220】值得信赖的成都网站建设公司。多年持续为众多企业提供成都网站建设,成都品牌建站设计,成都高端网站制作开发,SEO优化排名推广服务,全网营销让企业网站产生价值。

本文题目:Linux下如何处理串口中断?(linux串口中断)
文章起源:http://www.shufengxianlan.com/qtweb/news7/320357.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联