日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Linux下如何處理串口中斷?(linux串口中斷)

隨著計(jì)算機(jī)技術(shù)的不斷發(fā)展,串口接口在工業(yè)自動(dòng)化、醫(yī)療設(shè)備、消費(fèi)電子等領(lǐng)域得到了廣泛應(yīng)用。而對(duì)于Linux操作系統(tǒng)而言,如何處理串口中斷則成為了一個(gè)需要解決的問題。

串口中斷是指通過串口傳輸數(shù)據(jù)時(shí),接收端需要向發(fā)送端發(fā)送一些確認(rèn)信息,以達(dá)到數(shù)據(jù)傳輸正確的目的。而在Linux下,處理串口中斷的方法分為以下兩種:

一、軟件方式處理串口中斷

在使用Linux進(jìn)行串口通信時(shí),可以利用系統(tǒng)調(diào)用(system call)機(jī)制要求系統(tǒng)為其注冊串口中斷服務(wù)程序,并利用此程序進(jìn)行中斷處理。

具體而言,可以使用Linux內(nèi)核提供的”tty drivers”(tty驅(qū)動(dòng)程序)來完成此項(xiàng)任務(wù)。 tty驅(qū)動(dòng)程序可以從硬件中讀取串口數(shù)據(jù),接著將數(shù)據(jù)送到操作系統(tǒng)的中斷請求隊(duì)列中,由此觸發(fā)中斷服務(wù)程序進(jìn)行處理。

不過值得注意的是,由于Linux中多個(gè)進(jìn)程具有競爭關(guān)系的關(guān)系,這種方式很容易引起進(jìn)程或線程阻塞等問題,需要進(jìn)行深入的優(yōu)化。

二、硬件方式處理串口中斷

硬件方式處理串口中斷則是指,可以利用Linux內(nèi)核實(shí)現(xiàn)的interrupt(中斷)機(jī)制,直接利用操作系統(tǒng)中的工具“注冊中斷處理函數(shù)”并與串口接口聯(lián)系起來。

在進(jìn)行硬件方式處理串口中斷時(shí),也可以利用Linux內(nèi)核的I/O映射機(jī)制,將設(shè)備驅(qū)動(dòng)程序與內(nèi)核建立聯(lián)系并實(shí)現(xiàn)中斷處理。

不過這種方式需要進(jìn)行一些硬件層面的開發(fā)人員才能夠部署和調(diào)試。

無論采用軟件方式還是硬件方式,處理串口中斷對(duì)于Linux操作系統(tǒng)而言都是一個(gè)十分重要的任務(wù)。而對(duì)于工程師們而言,則需要根據(jù)具體的應(yīng)用場景選擇戰(zhàn)略,并進(jìn)行相關(guān)的程序設(shè)計(jì)和調(diào)試,以確保系統(tǒng)穩(wěn)定并能夠達(dá)到預(yù)期的數(shù)據(jù)傳輸目的。

成都網(wǎng)站建設(shè)公司-創(chuàng)新互聯(lián),建站經(jīng)驗(yàn)豐富以策略為先導(dǎo)10多年以來專注數(shù)字化網(wǎng)站建設(shè),提供企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計(jì),響應(yīng)式網(wǎng)站制作,設(shè)計(jì)師量身打造品牌風(fēng)格,熱線:028-86922220

求教,linux下網(wǎng)口虛擬串口驅(qū)動(dòng)程序

開發(fā)虛擬串口驅(qū)動(dòng)程序

虛擬串口就是當(dāng)本地并沒有對(duì)應(yīng)的串口硬件設(shè)備,而為應(yīng)用層提供串口設(shè)備一樣的系統(tǒng)調(diào)用接口,以兼容原本使用本地串口的應(yīng)用軟件的“虛”設(shè)備。本文作者給出了一種在Windows平臺(tái)上實(shí)現(xiàn)虛擬串口的方法,由此實(shí)現(xiàn)的“串口”具有真實(shí)串口完全相同的系統(tǒng)調(diào)用接口。

在很多應(yīng)用中需要用到虛擬串口,如在Modem卡出現(xiàn)之前,已經(jīng)有了接在計(jì)算機(jī)串口上的外部Modem,而且各種拔號(hào)程序也是通過串口與外部Modem通信的。為了讓已有的拔號(hào)程序不做修改,像使用外部Modem一樣使用內(nèi)置卡,就需要內(nèi)置卡的驅(qū)動(dòng)程序虛擬一個(gè)串口設(shè)備。又如當(dāng)前工業(yè)界使用的一些串口服務(wù)器,缺枯往往有8個(gè)或16個(gè)甚至更多的串口,以連接多個(gè)串口設(shè)備,再通過一個(gè)網(wǎng)卡直接連入以太網(wǎng)。與它在同一網(wǎng)絡(luò)上的計(jì)算機(jī)就通過以太網(wǎng)與串口服務(wù)器上掛接的串口設(shè)備通信。為了讓計(jì)算機(jī)中原來使用本地串口的軟件兼容,就需要在計(jì)算機(jī)上提供虛擬串口驅(qū)動(dòng)。

虛擬串口的設(shè)計(jì)關(guān)鍵在于,該“串口”實(shí)現(xiàn)后必須具有與真實(shí)串口完全相同的系統(tǒng)調(diào)用接口。要做到這點(diǎn),從已有的串口設(shè)備驅(qū)動(dòng)程序上做修改是更佳捷徑。下文就介紹以Windows NT上的串口驅(qū)動(dòng)程序?yàn)榛A(chǔ),開發(fā)可運(yùn)行于Windows NT、Windows 2023、Windows XP的各個(gè)版本虛擬串口驅(qū)動(dòng)程序。

串口驅(qū)動(dòng)中使用的幾個(gè)鏈表

由于串口是雙工設(shè)備,在一個(gè)讀請求發(fā)出來還沒有完成之前,同時(shí)可以發(fā)出寫請求,加上在驅(qū)動(dòng)程序?qū)铀蠭/O請求都要求異步完成,即前一個(gè)請求尚沒有完成,下一個(gè)相同的請求可能又來了。為此,串口驅(qū)動(dòng)程序需要使用多個(gè)雙向鏈表數(shù)據(jù)結(jié)構(gòu)來處理各種IRP(I/O Request Packet,I/O請求包)。當(dāng)收到一個(gè)IRP,先判斷是否可立即完成,可以馬上處理并返回,如果不允許則將IRP插在相應(yīng)鏈表尾,在適當(dāng)?shù)臅r(shí)候如設(shè)備有空閑時(shí)處理,這時(shí)往往會(huì)產(chǎn)生一個(gè)硬件中斷,激發(fā)DPC(Deferred Procedure Call,暫緩過程調(diào)用)過程,由DPC處理函數(shù)逐個(gè)從鏈表頭取出IRP并試著完成它。串口驅(qū)動(dòng)中有以下幾個(gè)鏈表和DPC(在serial.h中有定義):

ReadQueue 和 CompleteReadDpc

用于保存Read IRP的鏈表和用于調(diào)度的DPC,與DPC對(duì)應(yīng)的處理函數(shù)是SerialCompleteRead,它在read.c文件中,該函數(shù)的主要任務(wù)就是從ReadQueue中提取下一個(gè)IRP,并試著完成它。

WriteQueue 和 CompleteWriteDpc

用于保存Write IRP的鏈表和對(duì)應(yīng)的DPC,與DPC對(duì)應(yīng)的函數(shù)是SeriaCompleteWrite,它的實(shí)現(xiàn)在write.c中,該函數(shù)負(fù)責(zé)從WriteQueue中提取IRP,并試著完成它。

MaskQueue 和 CommWaitDpc

這一對(duì)鏈表用于處理Windows串口驅(qū)動(dòng)的一個(gè)特性:事件驅(qū)動(dòng)機(jī)制。它允許應(yīng)用程序預(yù)設(shè)一個(gè)事件標(biāo)志,而后等待與標(biāo)志對(duì)應(yīng)事件發(fā)生。DPC所調(diào)用的函數(shù)是SerialCompleteWait,它實(shí)現(xiàn)在Waitmask.c文件中,該函數(shù)也是試著從MaskQueue中提取IRP并完成它。

PurgeQueue

該鏈表與前面幾個(gè)稍有不同伏鍵洞,它沒有與之相對(duì)應(yīng)的DPC機(jī)制,而是在每次收到Purge請求時(shí)從PurgeQueue中逐個(gè)提取IRP并試著完成,因某種原因不能完成時(shí)則插入鏈表。相應(yīng)的函數(shù)是purge.c文件中的SerialStartPurge。

以上機(jī)制是串口驅(qū)動(dòng)程序的重要實(shí)現(xiàn)方法,在虛擬串口驅(qū)動(dòng)中需要保留,但不同的是,硬件串口驅(qū)動(dòng)中是ISR(中斷服務(wù)程序)根據(jù)收、發(fā)或MODEM中斷來激發(fā)相應(yīng)的DPC,而在虛擬串口驅(qū)動(dòng)中將因?qū)嶋H情況不同會(huì)有不同的激發(fā)機(jī)制。

DriverEntry的實(shí)現(xiàn)

DriverEntry是驅(qū)動(dòng)程序的入口函數(shù),相當(dāng)于應(yīng)用程序C語言中的main函數(shù),開發(fā)一個(gè)虛擬串口驅(qū)動(dòng)亮悶首先要修改的就是它。它的函數(shù)實(shí)體在initunlo.c文件中。只是在虛擬串口驅(qū)動(dòng)中由于不與具體的硬件打交道,就不存在硬件資源分析、硬件初始化、判斷其工作狀態(tài)等處理,只需要為虛擬串建立設(shè)備對(duì)象、符號(hào)鏈接和初始化數(shù)據(jù)結(jié)構(gòu)。一個(gè)典型函數(shù)實(shí)現(xiàn)大體如下:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)

{

/*填寫DriverObject->MajorFunction數(shù)組*/

/*建立設(shè)備對(duì)象*/

/*初始化SERIAL_DEVCIE_EXETENSION數(shù)據(jù)結(jié)構(gòu)*/

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(…);

/*建立符號(hào)鏈接*/

SerialSetupExternalNaming(extension);

return Status;

}

SerialRead和SerialCompleteRead的實(shí)現(xiàn)

函數(shù)SerailRead和SerialCompleteRead決定了對(duì)Read IRP的響應(yīng)策略,它們都存于read.c中。以串口服務(wù)器要用的虛擬串口為例,當(dāng)串口服務(wù)器收到來自外部數(shù)據(jù)時(shí)將通過網(wǎng)絡(luò)發(fā)至計(jì)算機(jī),計(jì)算機(jī)則產(chǎn)生相應(yīng)的網(wǎng)絡(luò)中斷并進(jìn)行協(xié)議數(shù)據(jù)處理。網(wǎng)絡(luò)接收線程緩存新收到的數(shù)據(jù)并激活CompleteReadDpc,從而SerialCompleteReadIrp得到調(diào)用,它再調(diào)用CompleteReadIrp對(duì)每個(gè)IRP進(jìn)行處理。它們的實(shí)現(xiàn)大體如下:

NTSTATUS SerialRead(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)

{

/*此處略去變量聲明和初始化*/

/*提取IRP中相關(guān)的數(shù)據(jù)*/

stack = IoGetCurrentIrpStackLocation(Irp);

ReadLen = stack->Parameters.Read.Length;

/*先看本地緩沖有數(shù)據(jù)否?有的話先讀取*/

if(Extension->InCounter > 0 )

{ //注意這里要加鎖,以防數(shù)據(jù)訪問沖突

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);//釋放鎖

}

/*是否已讀到足夠數(shù)據(jù)?是的話則完成該IRP*/

if( 0 == ReadLen)

{

status=STATUS_SUCCESS;

Irp->IoStatus.Status = status;

Irp->IoStatus.Information = FirstRead;

IoCompleteRequest(Irp,0);

return status;

}

/*沒有則將IRP插入隊(duì)列中,通過網(wǎng)絡(luò)向串口服務(wù)器發(fā)出讀數(shù)據(jù)請求*/

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 )

{

/*此處略去變量聲明和初始化*/

/*讀取新數(shù)據(jù)*/

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

if(ReadLen != 0)

{

RtlCopyMemory(pReadAsync->

pReadBuffer,pInData,ReadLen);

pReadAsync->pReadBuffer += ReadLen;

pReadAsync->ReadAlready += ReadLen;

extension->PerfStats.ReceivedCount +=

ReadLen;

}

else

{

/*因?yàn)榇诜?wù)器端只有在已經(jīng)有了相應(yīng)的數(shù)據(jù)或超過時(shí)間(此時(shí),Length=0)才會(huì)發(fā)來應(yīng)答并激活本DPC過程,所以此時(shí)已經(jīng)超時(shí),為了便于結(jié)束本IRP,這里有意改變TotalNeedRead,造成接收完畢的假象*/

pReadAsync->TotalNeedRead =

pReadAsync->ReadAlready;

}

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

{

/*該IRP是否已經(jīng)接收完畢,是的話則結(jié)束該

IRP*/

EndReadIrp(Irp);

/*從ReadQueue中取下一個(gè)IRP*/

}

/*本IRP沒有完成也沒有超時(shí),則繼續(xù)等待本DPC下次被激活,注意此時(shí)要判斷IRP是否被要求取消*/

}

SerialWrite和SerailCompleteWrite的實(shí)現(xiàn)

SerialWrite和SerailCompleteWrite決定了Write IRP的實(shí)現(xiàn)。在SerialWrite中調(diào)用了網(wǎng)絡(luò)發(fā)送函數(shù)TdiSendAsync,當(dāng)該發(fā)送完成后將激活CompleteWriteDpc,調(diào)度SerialCompleteWrite函數(shù),而它主要就是取出當(dāng)前的WriteIRP,設(shè)置已經(jīng)發(fā)送的數(shù)據(jù)數(shù)量,調(diào)用CompleteWriteIrp做該IRP的進(jìn)一步處理。它們大體如下:

NTSTATUS SerialWrite(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)

{

/*此處略去變量聲明和初始化*/

/*從IRP中提取有關(guān)數(shù)據(jù)*/

stack=IoGetCurrentIrpStackLocation(Irp);

SendLen = stack->Parameters.Write.Length;

/*為網(wǎng)絡(luò)發(fā)送和異步操作分配緩沖,在CompleteWrite中全部數(shù)據(jù)發(fā)送完后釋放*/

pWriteAsync = ExAllocatePool(NonPagedPool,

SendLen+PACKET_HEADER_LEN+sizeof(WRITE_ASYNC));

if(pWriteAsync == NULL)

{

//錯(cuò)誤處理

}

//保存異步數(shù)據(jù)

//設(shè)置網(wǎng)絡(luò)發(fā)送數(shù)據(jù)包

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

/*先將IRP暫時(shí)阻塞并插入隊(duì)列,在CompleteWrite中完成*/

IoMarkIrpPending(Irp);

InsertWaitList(extension->WriteQueue, Irp);

/*將寫請求和相關(guān)數(shù)據(jù)通過網(wǎng)絡(luò)發(fā)向串口服務(wù)器,由它負(fù)責(zé)將數(shù)據(jù)傳到具體串口設(shè)備*/

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

//統(tǒng)計(jì)數(shù)據(jù)累加

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)//全部數(shù)據(jù)發(fā)送完畢

{

EndWaitWriteIrp(pWriteIrp,STATUS_SUCCESS,

pWriteAsync->WroteAlready,pWriteAsync);

//從WriteQueue中取下一個(gè)IRP;

}

else //發(fā)送剩余數(shù)據(jù)

{

if(pWriteIrp->Cancel)

{

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

EndWaitWriteIrp(pWriteIrp,STATUS_CANCELLED,

pWriteAsync->WroteAlready,pWriteAsync);

return STATUS_CANCELED;

}

else

{

//再次設(shè)置網(wǎng)絡(luò)數(shù)據(jù)包并發(fā)送

BuildDataPacket(…);

status = TdiSendAsync(…);

//統(tǒng)計(jì)數(shù)據(jù)累加

Extension->PerfStats.TranittedCount +=

SendLen;

return STATUS_MORE_PROCESSING_REQUIRED;

}

}

}

其他幾個(gè)接口函數(shù)的實(shí)現(xiàn)

除Read/Write外,SerialUnload、SerialCreateOpen、 SerialClose、SerialCleanup、SerailFlush等調(diào)用接口是硬件相關(guān)性比較弱的接口函數(shù),基本不要修改,直接刪除原來操作硬件的部分即可。復(fù)雜一點(diǎn)就是SerialIoControl,該接口函數(shù)包含有大量設(shè)置、讀取串口硬件狀態(tài)的處理,可建立一個(gè)本地?cái)?shù)據(jù)結(jié)構(gòu)隨時(shí)保存虛擬串口的當(dāng)前硬件狀態(tài)。同時(shí)為了保證串口服務(wù)器端的真實(shí)串口狀態(tài)和上層軟件要求的一致,需要將所有設(shè)置請求通過網(wǎng)絡(luò)發(fā)送到服務(wù)器端,由它負(fù)責(zé)改變真實(shí)硬件的狀態(tài)。

關(guān)于linux 串口中斷的介紹到此就結(jié)束了,不知道你從中找到你需要的信息了嗎 ?如果你還想了解更多這方面的信息,記得收藏關(guān)注本站。

創(chuàng)新互聯(lián)【028-86922220】值得信賴的成都網(wǎng)站建設(shè)公司。多年持續(xù)為眾多企業(yè)提供成都網(wǎng)站建設(shè),成都品牌建站設(shè)計(jì),成都高端網(wǎng)站制作開發(fā),SEO優(yōu)化排名推廣服務(wù),全網(wǎng)營銷讓企業(yè)網(wǎng)站產(chǎn)生價(jià)值。


分享文章:Linux下如何處理串口中斷?(linux串口中斷)
分享網(wǎng)址:http://www.dlmjj.cn/article/cccesgi.html