Linux設備驅動開發(fā)_第1頁
Linux設備驅動開發(fā)_第2頁
Linux設備驅動開發(fā)_第3頁
Linux設備驅動開發(fā)_第4頁
Linux設備驅動開發(fā)_第5頁
已閱讀5頁,還剩128頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

Linux設備驅動設計

梁紅elvin@ema-tech1整理ppt設備驅動概述設備由兩局部組成,一個是被稱為控制器的電器局部,另一個是機械局部。一組存放器組被賦予到各個控制器。I/O端口包含4組存放器,即狀態(tài)存放器,控制存放器,數(shù)據(jù)輸入存放器,數(shù)據(jù)輸出存放器。狀態(tài)存放器擁有可以被CPU讀取的(狀態(tài))位,用來指示當前命令是否執(zhí)行完畢,或者字節(jié)是否可以被讀出或寫入,以及任何錯誤提示??刂拼娣牌髂敲从糜趩右粭l命令〔指令〕或者改變設備的(工作)模式。數(shù)據(jù)輸入存放器用于獲取輸入的數(shù)據(jù)。數(shù)據(jù)輸出存放器那么向CPU發(fā)送結果。整理ppt設備驅動概述操作系統(tǒng)是通過各種驅動程序來駕馭硬件設備,它為用戶屏蔽了各種各樣的設備。設備驅動程序是操作系統(tǒng)內核和機器硬件之間的接口,系統(tǒng)調用是操作系統(tǒng)內核和應用程序之間的接口。在應用程序看來,硬件設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬件設備進行操作.整理ppt設備驅動概述驅動完成以下的功能:對設備初始化和釋放.把數(shù)據(jù)從內核傳送到硬件和從硬件讀取數(shù)據(jù).讀取應用程序傳送給設備文件的數(shù)據(jù)和回送應用程序請求的數(shù)據(jù).檢測和處理設備出現(xiàn)的錯誤.4整理ppt設備驅動概述無操作系統(tǒng)的設備驅動有操作系統(tǒng)的設備驅動ApplicationDriverHardwareApplicationLibAPISystemcallEmbeddedOSHardware不帶操作系統(tǒng)軟件結構帶操作系統(tǒng)軟件結構Driver5整理pptLinux設備驅動6整理pptLinux設備驅動用戶級的程序使用內核提供的標準系統(tǒng)調用來與內核通訊,這些系統(tǒng)調用有:open(),read(),write(),ioctl(),close()等等。Linux的內核是映射到每一個進程的高1G空間。每一個用戶進程運行時都好似有一份內核的拷貝,每當用戶進程使用系統(tǒng)調用時,都自動地將運行模式從用戶級轉為內核級,此時進程在內核的地址空間中運行。7整理pptLinux設備驅動Linux內核使用“設備無關〞的I/O子系統(tǒng)來為所有的設備效勞。每個設備都提供標準接口給內核,盡可能地隱藏了自己的特性。用戶程序使用一些根本的系統(tǒng)調用從設備讀取數(shù)據(jù)并且將它們存入緩沖的例子。我們可以看到,每當一個系統(tǒng)調用被使用時,內核就轉到相應的設備驅動例程來操縱硬件。8整理pptLinux設備驅動Linux操作系統(tǒng)把設備納入文件系統(tǒng)的范疇來管理。每個設備在Linux系統(tǒng)上看起來都像一個文件,它們存放在/dev目錄中,稱為"設備節(jié)點"。對文件操作的系統(tǒng)調用大都適用于設備文件。

整理pptLinux設備驅動Linux下設備的屬性設備的類型:字符設備、塊設備、網絡設備主設備號:標識設備對應的驅動程序。一般“一個主設備號對應一個驅動程序〞次設備號:每個驅動程序負責管理它所驅動的幾個硬件實例,這些硬件實例那么由次設備號來表示。同一驅動下的實例編號,用于確定設備文件所指的設備??赏ㄟ^ls–l“設備文件名〞命令查看設備的主次設備號,以及設備的類型。10整理pptLinux設備驅動Linux設備驅動程序是一組由內核中的相關子例程和數(shù)據(jù)組成的I/O設備軟件接口。每當用戶程序要訪問某個設備時,它就通過系統(tǒng)調用,讓內核代替它調用相應的驅動例程。這就使得控制從用戶進程轉移到了驅動例程,當驅動例程完成后,控制又被返回至用戶進程。11整理ppt一些重要的數(shù)據(jù)結構大局部驅動程序涉及三個重要的內核數(shù)據(jù)結構:文件操作file_operations結構體文件對象file結構體索引節(jié)點inode結構體12整理ppt一些重要的數(shù)據(jù)結構文件操作結構體file_operations結構體file_operations在頭文件linux/fs.h中定義,用來存儲驅動內核模塊提供的對設備進行各種操作的函數(shù)的指針。結構體的每個域都對應著驅動模塊用來處理某個被請求的事務的函數(shù)的地址。structfile_operations{ structmodule*owner; ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*); 。。。 }13整理ppt一些重要的數(shù)據(jù)結構file_operations重要的成員Structmodule*owner,指向擁有該結構體的模塊的指針。內核使用該指針維護模塊使用計數(shù)。方法llseek用來修改文件的當前讀寫位置,把新位置作為返回值返回。loff_t是在LINUX中定義的長偏移量方法read用來從設備中讀取數(shù)據(jù)。非負返回值表示成功讀取的直接數(shù)。方法write向設備發(fā)送數(shù)據(jù)。方法ioctl提供一種執(zhí)行設備特定命令的方法。14整理ppt一些重要的數(shù)據(jù)結構file_operations重要的成員unsignedint(*poll)(structfile*,structpoll_table_struct*); 系統(tǒng)調用select和poll的后端實現(xiàn),用這兩個系統(tǒng)調用來查詢設備是否可讀寫,或是否處于某種狀態(tài)。如果poll為空,那么驅動設備會被認為即可讀又可寫,返回值是一個狀態(tài)掩碼。int(*mmap)(structfile*,structvm_area_struct*);

將設備內存映射到進程地址空間

15整理ppt一些重要的數(shù)據(jù)結構file_operations重要的成員驅動內核模塊是不需要實現(xiàn)每個函數(shù)的。相對應的file_operations的項就為NULL。Gcc的語法擴展,使得可以定義該結構體: structfile_operationsfops={ read:device_read, write:device_write, open:device_open, release:device_release};沒有顯示聲明的結構體成員都被gcc初始化為NULL。16整理ppt一些重要的數(shù)據(jù)結構file_operations重要的成員標準C的標記化結構體的初始化方法: structfile_operationsfops={ .read=device_read, .write=device_write, .open=device_open, .release=device_release };

推薦使用該方法,提高移植性,方法允許對結構體成員進行重新排列。沒有顯示聲明的結構體成員同樣都被gcc初始化為NULL。指向結構體file_operations的指針通常命名為fops。17整理ppt一些重要的數(shù)據(jù)結構文件對象file結構體 文件對象file代表著一個翻開的文件。進程通過文件描述符fd與已翻開文件的file結構相聯(lián)系。進程通過它對文件的線性邏輯空間進行操作。例如:file->f_op->read();Structfile在<linux/fs.h>中定義。指向結構體structfile的指針通常命名為filp,或者file。建議使用文件指針filp。18整理ppt一些重要的數(shù)據(jù)結構文件對象file結構體的成員Structfile_operations*f_op; 與文件相關的操作結構體指針。與文件相關的操作是在翻開文件的時候確定下來的,也就是確定該指針的值。可在需要的時候,改變指針所指向的文件操作結構體。用C語言實現(xiàn)面向對象編程的方法重載。其他成員可先忽略,后面具體實例分析。因為設備驅動模塊并不自己直接填充結構體file,只是使用file中的數(shù)據(jù)。19整理ppt一些重要的數(shù)據(jù)結構索引節(jié)點inode結構文件翻開,在內存建立副本后,由唯一的索引節(jié)點inode描述。與file結構不同。file結構是進程使用的結構,進程每翻開一個文件,就建立一個file結構。不同的進程翻開同一個文件,建立不同的file結構。Inode結構是內核使用的結構,文件在內存建立副本,就建立一個inode結構來描述。一個文件在內存里面只有一個inode結構對應。20整理ppt一些重要的數(shù)據(jù)結構索引節(jié)點inode結構Inode結構包含大量描述文件信息的成員變量。但是對于描述設備文件的inode,跟設備驅動有關的成員只有兩個。Dev_ti_rdev;包含真正的設備編號。Structcdev*i_cdev;指向cdev結構體的指針。cdev是表示字符設備的內核數(shù)據(jù)結構。從inode中獲得主設備號和次設備號的宏:Unsignedintiminor(structinode*inode);Unsignedintimajor(structinode*inode);21整理pptLinux設備驅動主設備號和次設備號的內部表達:Dev_t類型用于保存設備號,稱為設備編號。/linux/types.h文件中定義。目前設備編號dev_t是一個32位的整數(shù),其中12位表示主設備號,20位表示次設備號。通過設備編號獲取主次設備號:MAJOR(dev_tdev);MINOR(dev_tdev);通過主次設備號合成設備編號:MKDEV(intmajor,intminor);Dev_t格式以后可能會發(fā)生變化,但只要使用這些宏,就可保證設備驅動程序的正確性。22整理ppt分配和釋放字符設備號編寫驅動程序要做的第一件事,為字符設備獲取一個設備號。事先知道所需要的設備編號〔主設備號〕的情況:intregister_chrdev_region(dev_tfirst,unsignedcount,constchar*name)first是要分配的起始設備編號值。first的次設備號通常設置為0。Count所請求的連續(xù)設備編號的個數(shù)。Name設備名稱,指和該編號范圍建立關系的設備。分配成功返回0。23整理ppt分配和釋放字符設備號動態(tài)分配設備編號〔主要是主設備號〕intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name)dev是一個僅用于輸出的參數(shù),它在函數(shù)成功完成時保存已分配范圍的第一個編號。baseminor應當是請求的第一個要用的次設備號,它常常是0.count和name參數(shù)跟request_chrdev_region的一樣.24整理ppt分配和釋放字符設備號不再使用時,釋放這些設備編號。使用以下函數(shù):voidunregister_chrdev_region(dev_tfrom,unsignedcount)在模塊的卸載函數(shù)中調用該函數(shù)。25整理ppt分配和釋放字符設備號新驅動程序,建議使用動態(tài)分配機制獲取主設備號,也就是使用alloc_chrdev_region()。動態(tài)分配導致無法預先創(chuàng)立設備節(jié)點??稍诜峙湓O備號后,從/proc/devices文件中獲取。為了加載后自動創(chuàng)立設備文件,可以通過編寫內核模塊加載腳本實現(xiàn)。26整理ppt字符設備的注冊內核內部使用structcdev結構表示字符設備。編寫設備驅動的第二步就是注冊該設備。包含<linux/cdev.h>頭文件。獲取一個獨立的cdev結構:structcdev*my_cdev=cdev_alloc();調用cdev_init初始化cdev結構體voidcdev_init(structcdev*cdev,structfile_operations*fops);初始化該設備的所有者字段:dev->cdev.owner=THIS_MODULE;初始化該設備的可用操作集:dev->cdev.ops=&device_fops;27整理ppt字符設備的注冊編寫設備驅動的第二步就是注冊該設備。cdev結構已建立和初始化,最后通過cdev_add函數(shù)把它告訴內核: intcdev_add(structcdev*dev,dev_tnum,unsignedintcount);dev是要添加的設備的cdev結構,num是這個設備對應的第一個設備編號,count是應當關聯(lián)到設備的設備號的數(shù)目.卸載字符設備時,調用相反的動作函數(shù):voidcdev_del(structcdev*dev);28整理ppt設備的注冊早期方法:內核中仍有許多字符驅動不使用剛剛描述過的cdev接口。沒有更新到2.6內核接口的老代碼。注冊一個字符設備的早期方法: intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major是給定的主設備號。為0代表什么?name是驅動的名字(將出現(xiàn)在/proc/devices),fops是設備驅動的file_operations結構。register_chrdev將給設備分配0-255的次設備號,并且為每一個建立一個缺省的cdev結構。從系統(tǒng)中卸載字符設備的函數(shù): intunregister_chrdev(unsignedintmajor,constchar*name);29整理pptOpen方法編寫字符設備驅動的第三步:定義設備驅動與文件系統(tǒng)的接口,file_operation結構體的函數(shù)定義。open方法int(*open)(structinode*inode,structfile*filp);驅動程序提供open方法,讓用戶進程使用設備之前,進行一些初始化的工作。檢查設備特定的錯誤。如果第一次翻開設備,那么初始化設備。如果需要,更新f_op指針,更換操作方法集。分配并填充要放進filp->private_data的任何數(shù)據(jù)結構。30整理pptOpen方法對于設備文件,inode參數(shù)只有兩個參數(shù)對設備驅動有用的。Dev_ti_rdev;包含真正的設備編號。Structcdev*i_cdev;指向cdev結構體的指針。i_cdev里面包含我們之前建立的cdev結構。但是有時候,我們需要的是包含cdev結構的描述設備的結構。使用通過成員地址獲取結構體地址的宏container_of,在<linux/kernel.h>中定義:container_of(pointer,container_type,container_field);這個宏使用一個指向container_field類型的成員的指針,它在一個container_type類型的結構中,宏通過分析他們關系,返回指向包含該成員的結構體指針.31整理pptOpen方法在myscull_open,這個宏用來找到適當?shù)脑O備結構:dev=container_of(inode->i_cdev,structscull_dev,cdev);找到myscull_dev結構后,scull在filp->private_data中存儲其指針,為以后存取使用.

filp->private_data=dev;

32整理pptrelease方法release方法做open相反的工作釋放open分配給filp->private_data的內存空間。在最后一次的關閉操作時,關閉設備。不是每個close系統(tǒng)調用引起調用release方法。33整理pptRead和Write方法Read的任務,就是從設備拷貝數(shù)據(jù)到用戶空間。Write的任務,那么從用戶空間拷貝數(shù)據(jù)到設備。ssize_tread(structfile*filp,char__user*buff,size_tcount,loff_t*offp);ssize_twrite(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp);filp是文件對象指針,count是請求的傳輸數(shù)據(jù)大小.buff參數(shù)對write來說是指向持有被寫入數(shù)據(jù)的緩存,對read那么是放入新數(shù)據(jù)的空緩存.offp是指向一個“l(fā)ongoffsettype〞的指針,它指出用戶正在存取的文件位置.返回值是“signedsizetype〞類型;34整理pptRead和Write方法read和write方法的buff參數(shù)是用戶空間指針,不能被內核代碼直接解引用。__user字符串只是形式上的說明,說明是用戶空間地址。驅動必須能夠存取用戶空間緩存以完成它的工作。內核如何解決這個問題?為平安起見,內核提供專用的函數(shù)來完成對用戶空間的存取。這些專用函數(shù)在<asm/uaccess.h>中聲明。 unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount); unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);大多數(shù)讀寫函數(shù)都會調用這兩個函數(shù),用于跟應用程序空間交流信息。35整理pptRead和Write方法典型的Read函數(shù)對參數(shù)的使用。36整理pptllseek函數(shù)llseek函數(shù)用于對設備文件訪問定位。驅動接口loff_t(*llseek)(structfile*,loff_t,int);庫函數(shù)off_tlseek(intfiledes,off_toffset,intwhence);

參數(shù)offset的含義取決于參數(shù)whence:如果whence是SEEK_SET,文件偏移量將被設置為offset。如果whence是SEEK_CUR,文件偏移量將被設置為cfo加上offset,offset可以為正也可以為負。如果whence是SEEK_END,文件偏移量將被設置為文件長度加上offset,offset可以為正也可以為負。SEEK_SET、SEEK_CUR和SEEK_END是SystemV引入的,是0、1和2。37整理pptioctl

進行超出簡單的數(shù)據(jù)傳輸之外的操作,進行各種硬件控制操作.ioctl方法和用戶空間版本不同的原型: int(*ioctl)(structinode*inode,structfile*filp,

unsignedintcmd,unsignedlongarg)不管可選的參數(shù)arg是否由用戶給定為一個整數(shù)或一個指針,它都以一個unsignedlong的形式傳遞。返回值 POSIX標準規(guī)定:如果使用了不適宜的ioctl命令號,應當返回-ENOTTY。這個錯誤碼被C庫解釋為“不適宜的設備ioctl。-EINVAL也是相當普遍的。38整理ppt結構化設備驅動程序設備結構體把與某設備相關的所有內容定義為一個設備結構體其中包括設備驅動涉及的硬件資源、全局軟件資源、控制〔自旋鎖、互斥鎖、等待隊列、定時器等〕在涉及設備的操作時,就僅僅操作這個結構體39整理pptLinux設備驅動的并發(fā)控制40整理ppt設備驅動的并發(fā)控制在驅動程序中,當多個線程同時訪問相同的資源時,可能會引發(fā)“競態(tài)〞,必須對共享資源進行并發(fā)控制。并發(fā)和競態(tài)廣泛存在。并發(fā)控制的目的: 使得線程訪問共享資源的操作是原子操作。原子操作: 在執(zhí)行過程中不會被別的代碼路徑所中斷的操作。驅動程序中的全局變量是一種典型的共享資源。41整理ppt考慮一個非常簡單的共享資源的例子:一個全局整型變量和一個簡單的臨界區(qū),其中的操作僅僅是將整型變量的值增加1:i++該操作可以轉化成下面三條機器指令序列:得到當前變量i的值并拷貝到一個存放器中將存放器中的值加1把i的新值寫回到內存中原子操作42整理ppt原子操作內核任務1內核任務2獲得i(1)增加i(1->2)寫回i(2)

獲得i(2)

增加i(2->3)

寫回i(3)內核任務1內核任務2獲得i(1)增加i(1->2)

獲得i(1)

增加i(1->2)寫回i(2)寫回i(2)可能的實際執(zhí)行結果:期望的結果43整理pptLinux內核的并發(fā)控制在內核空間的內核任務需要考慮同步內核空間中的共享數(shù)據(jù)對內核中的所有任務可見,所以當在內核中訪問數(shù)據(jù)時,就必須考慮是否會有其他內核任務并發(fā)訪問的可能、是否會產生競爭條件、是否需要對數(shù)據(jù)同步。44整理ppt確定保護對象找出哪些數(shù)據(jù)需要保護是關鍵所在內核任務的局部數(shù)據(jù)僅僅被它本身訪問,顯然不需要保護。如果數(shù)據(jù)只會被特定的進程訪問,也不需加鎖大多數(shù)內核數(shù)據(jù)結構都需要加鎖:假設有其它內核任務可以訪問這些數(shù)據(jù),那么就給這些數(shù)據(jù)加上某種形式的鎖;假設任何其它東西能看到它,那么就要鎖住它。Linux內核的并發(fā)控制45整理pptLinux內核的并發(fā)控制并發(fā)控制的機制中斷屏蔽,原子數(shù)操作,自旋鎖和信號量都是解決并發(fā)問題的機制。中斷屏蔽很少被單獨使用,原子操作只能針對整數(shù)來進行。因此自旋鎖和信號量應用最為廣泛。

46整理ppt中斷屏蔽單CPU系統(tǒng)中,防止竟態(tài)的一種簡單方式保證正在執(zhí)行的內核執(zhí)行路徑不被中斷處理程序所搶占,防止竟態(tài)條件的發(fā)生。Local_irq_disable() //關中斷…Criticalsection //臨界區(qū)…Local_irq_enable() //開中斷中斷對內核非常重要,長時間屏蔽中斷非常危險!只適合短時間的關閉對SMP多CPU引發(fā)的竟態(tài)無效47整理ppt鎖機制可以防止競爭狀態(tài)正如門鎖和門一樣,門后的房間可想象成一個臨界區(qū)。在一段時間內,房間里只能有一個內核任務存在,當一個任務進入房間后,它會鎖住身后的房門;當它結束對共享數(shù)據(jù)的操作后,就會走出房間,翻開門鎖。如果另一個任務在房門上鎖時來了,那么它就必須等待房間內的任務出來并翻開門鎖后,才能進入房間。加鎖機制48整理ppt任何要訪問臨界資源的代碼首先都需要占住相應的鎖,這樣該鎖就能阻止來自其它內核任務的并發(fā)訪問:

任務1

試圖鎖定隊列成功:獲得鎖訪問隊列…為隊列解除鎖…任務2試圖鎖定隊列失?。旱却却却晒Γ韩@得鎖

訪問隊列…

為隊列解除鎖加鎖機制49整理ppt原子數(shù)操作整型原子數(shù)操作原子變量初始化 atomic_ttest=ATOMIC_INIT(i);設置原子變量的值 voidatomic_set(atomic_t*v,inti)獲得原子變量的值 atomic_read(v)原子變量加 voidatomic_add(inti,atomic_t*v)原子變量減 voidatomic_sub(inti,atomic_t*v)50整理ppt原子數(shù)操作整型原子數(shù)操作原子變量的自增操作 voidatomic_inc(atomic_t*v)原子變量的自減操作 voidatomic_dec(atomic_t*v)操作并測試〔測試其是否為0,0為true,否為false〕atomic_inc_and_test(atomic_t*v)atomic_dec_and_test(atomic_t*v)intatomic_sub_and_test(inti,atomic_t*v)操作并返回〔返回新值〕intatomic_add_return(inti,atomic_t*v)intatomic_sub_return(inti,atomic_t*v)51整理ppt原子數(shù)操作原子位操作設置位 voidset_bit(intnr,volatileunsignedlong*addr)去除位 voidclear_bit(intnr,volatileunsignedlong*addr)改變位 change_bit(nr,p)測試位 test_bit(intnr,constvolatileunsignedlong*p)測試并操作位 test_and_set_bit(nr,p)52整理ppt自旋鎖自旋鎖是專為防止多處理器并發(fā)而引入的一種鎖,它在內核中大量應用于中斷處理等局部。而對于單處理器來說,防止中斷處理中的并發(fā)可簡單采用關閉中斷的方式,不需要自旋鎖。自旋鎖最多只能被一個內核任務持有,假設一個內核任務試圖請求一個已被持有的自旋鎖,那么這個任務就會一直進行忙循環(huán),也就是旋轉,等待鎖重新可用。自旋鎖可以在任何時刻防止多于一個的內核任務同時進入臨界區(qū),因此這種鎖可有效地防止多處理器上并發(fā)運行的內核任務競爭共享資源。53整理ppt自旋鎖自旋鎖的初衷就是: 在短期間內進行輕量級的鎖定。一個被爭用的自旋鎖使得請求它的線程在等待鎖重新可用的期間進行自旋〔特別浪費處理器時間〕,所以自旋鎖不應該被持有時間過長。如果需要長時間鎖定的話,最好使用信號量。54整理ppt自旋鎖自旋鎖防止在不同CPU上的執(zhí)行單元對共享資源的同時訪問,以及不同進程上下文互相搶占導致的對共享資源的非同步訪問。在單CPU且不可搶占的內核下,自旋鎖的所有操作都是空操作。

自旋鎖不允許任務睡眠。55整理ppt自旋鎖自旋鎖的根本形式如下: spin_lock(&mr_lock); /*臨界區(qū)*/ spin_unlock(&mr_lock);56整理ppt自旋鎖自旋鎖原語要求包含文件是<linux/spinlock.h>.鎖的類型是spinlock_t.鎖的兩種初始化方法:spinlock_tmy_lock=SPIN_LOCK_UNLOCKED;voidspin_lock_init(spinlock_t*lock);進入一個臨界區(qū)前,必須獲得需要的lock。voidspin_lock(spinlock_t*lock);自旋鎖等待是不可中斷的。一旦你調用spin_lock,將自旋直到鎖變?yōu)榭捎?。釋放一個鎖:voidspin_unlock(spinlock_t*lock);57整理ppt自旋鎖關中斷的自旋鎖Spin_lock_irq()Spin_unlock_irq()Spin_lock_irqsave()Spin_unlock_irqrestore()58整理ppt信號量Linux中的信號量是一種睡眠鎖。如果有一個任務試圖獲得一個已被持有的信號量時,信號量會將其推入等待隊列,然后讓其睡眠。當持有信號量的進程將信號量釋放后,在等待隊列中的一個任務將被喚醒,從而便可以獲得這個信號量。信號量的睡眠特性,使得信號量適用于鎖會被長時間持有的情況;信號量的操作信號量支持兩個原子操作P()和V(),前者做測試操作,后者叫做增加操作。Linux中分別叫做down()和up()。

59整理ppt信號量down()和up()。down()操作通過對信號量計數(shù)減1來請求獲得一個信號量。如果結果是0或大于0,信號量鎖被獲得,任務就可以進入臨界區(qū)了。如果結果是負數(shù),任務會被放入等待隊列,處理器執(zhí)行其它任務。相反,當臨界區(qū)中的操作完成后,up()操作用來釋放信號量,增加信號量的計數(shù)值。如果在該信號量上的等待隊列不為空,處于隊列中等待的任務在被喚醒的同時會獲得該信號量。60整理ppt信號量61整理ppt信號量62整理pptLinux信號量的實現(xiàn)內核代碼必須包含<asm/semaphore.h>,才能使用信號量。相關的類型是structsemaphore信號量的定義structsemaphore{

atomic_tcount;

intsleepers;

wait_queue_head_twait;

}

63整理pptLinux信號量的實現(xiàn)信號量的聲明和初始化直接創(chuàng)立一個信號量structsemaphore*sem;接著使用sema_init來初始化這個信號量: voidsema_init(structsemaphore*sem,intval);互斥模式的信號量聲明,內核提供宏定義.DECLARE_MUTEX(name); 信號量初始化為1DECLARE_MUTEX_LOCKED(name); 信號量初始化為064整理pptLinux信號量的實現(xiàn)動態(tài)分配的互斥信號量聲明voidinit_MUTEX(structsemaphore*sem); 信號量初始化為1voidinit_MUTEX_LOCKED(structsemaphore*sem); 信號量初始化為065整理pptLinux信號量的實現(xiàn)信號量的P操作voiddown(structsemaphore*sem); down減小信號量的值,并根據(jù)信號量的值決定是否等待。不可中斷的等待。intdown_interruptible(structsemaphore*sem); 操作是可中斷的。intdown_trylock(structsemaphore*sem); 信號量在調用時不可用,down_trylock立刻返回一個非零值.66整理pptLinux信號量的實現(xiàn)信號量的V操作voidup(structsemaphore*sem); 通過down操作進入臨界區(qū)的進程,再退出的時候都需要調用一個up操作,釋放信號量。67整理pptLinux信號量的實現(xiàn)信號量根本使用形式為:staticDECLARE_MUTEX(mr_sem);//聲明互斥信號量…if(down_interruptible(&mr_sem))/*可被中斷的睡眠,當信號來到,睡眠的任務被喚醒*//*臨界區(qū)…*/up(&mr_sem);操作配套使用68整理pptLinux設備驅動調試

69整理ppt內核調試選項內核開發(fā)者在內核自身中構建了多個調試特性。這些特性會產生額外的輸出并降低性能,Linux發(fā)行版的內核為了提高性能,去除這些調試特性。用來開發(fā)的內核應當激活的調試配置選項,是在“kernelhacking〞菜單中。70整理ppt通過打印調試Printkprintk通過附加不同的消息優(yōu)先級在消息上,對消息的嚴重程度進行分類。在<linux/kernel.h>定義了8個loglevel。DEFAULT_MESSAGE_LOGLEVEL為默認級別(printk.c)當消息優(yōu)先級小于console_loglevel,信息才能顯示出來。而console_loglevel的初值為DEFAULT_CONSOLE_LOGLEVEL。通過對/proc/sys/kernel/printk的訪問來改變console_loglevel的值。該文件包含四個數(shù)字:當前的loglevel、默認loglevel、最小允許的loglevel、引導時的默認loglevel。echo1>/proc/sys/kernel/printkecho8>/proc/sys/kernel/printk71整理ppt通過打印調試翻開和關閉消息通過封裝printk函數(shù),快速翻開調試信息或者關閉調試信息。 #definePDEBUG(fmt,args...)\ printk(KERN_DEBUG“myscull:"fmt,##args)通過在Makefile里面定義調試開關變量去決定調試信息是否翻開。72整理ppt通過查詢調試獲取相關信息的最好方法:在需要的時候才去查詢系統(tǒng)信息,而不是持續(xù)不斷地產生數(shù)據(jù)。/proc文件系統(tǒng)是一種特殊的、由軟件創(chuàng)立的文件系統(tǒng),內核使用他向外界導出信息。/proc下面的每個文件都綁定于一個內核函數(shù),用戶讀取其中的文件時,該函數(shù)動態(tài)的生成文件的內容。例如/proc/devices73整理ppt通過查詢調試包含<linux/proc_fs.h>在驅動中定義跟proc文件綁定的內核函數(shù)read_proc,在函數(shù)里面定義要輸出的信息。在初始化函數(shù)中調用creat_proc_read_entry函數(shù)將/proc入口文件和read_proc函數(shù)聯(lián)系起來。卸載模塊時調用remove_proc_entry撤銷proc入口。74整理ppt通過查詢調試read_proc函數(shù)int(*read_proc)(char*page,char**start,off_toffset,intcount,int*eof,void*data);page是輸出數(shù)據(jù)的緩存內存頁。進程讀取/proc文件時,內核會分配一個內存頁,read_proc將數(shù)據(jù)通過這個內存頁返回到用戶空間。start是這個函數(shù)用來說有關的數(shù)據(jù)寫在頁中哪里eof,當沒有數(shù)據(jù)可返回時,驅動設置這個參數(shù)。Data是提供給驅動的專用數(shù)據(jù)指針。75整理ppt通過查詢調試intsprintf(char*buf,constchar*fmt,...)將數(shù)據(jù)打包成字符流的形式。內核很多象printk函數(shù)一樣,通過庫函數(shù)的形式提供給內核開發(fā)者的函數(shù),以滿足內核開發(fā)中的一些簡單的需要。void*memset(void*s,charc,size_tcount);void*memcpy(void*dest,constvoid*src,size_tcount);

76整理ppt通過查詢調試creat_proc_read_entry函數(shù) structproc_dir_entry*create_proc_read_entry(constchar*name,mode_tmode,structproc_dir_entry*base,read_proc_t*read_proc,void*data);name是要創(chuàng)立的proc文件名,mod是文件的訪問掩碼(缺省0),base指出要創(chuàng)立的文件的目錄;如果base是NULL,文件在/proc根下創(chuàng)立。read_proc是實現(xiàn)文件內容的read_proc函數(shù),data被內核忽略(傳遞給read_proc).77整理ppt查看Oops信息大多數(shù)bug通常是因為廢棄了一個NULL指針或者使用了錯誤的指針值。這類bug導致的結果通常是一條oops消息。一條oops消息能夠顯示發(fā)生故障時CPU的狀態(tài),以及CPU存放器的內容和其他看似難以理解的信息。78整理ppt查看Oops信息例如訪問一個NULL指針。因為NULL不是一個可訪問的指針值,所以會引發(fā)一個錯誤,內核會簡單地將其轉換為oops消息并顯示。然后其調用進程會被殺死。

UnabletohandlekernelNULLpointerdereferenceatvirtualaddress00000000

printingeip:d083a064Oops:0002[#1]SMPCPU:0EIP:0060:[]NottaintedEFLAGS:00010246(2.6.6)

EIPisatoops_example_write+0x4/0x10[oops_example]

eax:00000000ebx:00000000ecx:00000000edx:00000000…

79整理ppt通過監(jiān)視調試strace命令可以顯示由用戶空間程序所發(fā)出的所有系統(tǒng)調用。還以符號形式顯示調用的參數(shù)和返回值。當一個系統(tǒng)調用失敗,錯誤的符號值(例如,ENOMEM)和對應的字串(Outofmemory)都顯示.strace有很多命令行選項;-t來顯示每個調用執(zhí)行的時間,-T來顯示調用中花費的時間,-e來限制被跟蹤調用的類型,-o來重定向輸出到一個文件.缺省地,strace打印調用信息到stderr.80整理pptLinux的內存分配

81整理pptkmalloc函數(shù)void*kmalloc(size_tsize,intflags);所分配到的內存在物理內存中連續(xù)且保持原有的數(shù)據(jù)〔不清零〕。size是要分配的塊的大小。Linux創(chuàng)立一系列內存對象slab,每個slab內的內存塊大小是固定。處理分配請求時,就直接在包含有足夠大內存塊的slab中分配一個整塊給請求者。內核只能分配一些預定義的、固定大小的字節(jié)數(shù)組。kmalloc能夠處理的最小內存塊是32或64字節(jié)〔體系結構依賴〕,而內存塊大小的上限隨著體系和內核配置而變化。不應分配大于128KB的內存。假設需多于幾個KB的內存塊,最好使用其他方法。82整理pptkmalloc函數(shù)void*kmalloc(size_tsize,intflags);Flags分配標志,表示如何分配空間。所有標志都定義在<linux/gfp.h>GFP_KERNEL 內存分配是代表運行在內核空間的進程執(zhí)行的,并且在空閑內存較少時把當前進程轉入休眠以等待一個頁面。GFP_ATOMIC 內核通常會為原子性的分配預留一些空閑頁。當當前進程不能被置為睡眠時,應使用GFP_ATOMIC,這樣kmalloc甚至能夠使用最后一個空閑頁。如果連這最后一個空閑頁也不存在,那么分配返回失敗。常用來從中斷處理和進程上下文之外的其他代碼中分配內存,從不睡眠。GFP_DMA 要求分配可用于DMA的內存。83整理ppt后備高速緩存

內核為驅動程序常常需要反復分配許多相同大小內存塊的情況,增加了一些特殊的內存池,稱為后備高速緩存〔lookasidecache〕。設備驅動程序通常不會涉及后備高速緩存,但是也有例外:在Linux2.6中USB和SCSI驅動。84整理ppt后備高速緩存創(chuàng)立一個新的后備高速緩存kmem_cache_createname:name和這個后備高速緩存相關聯(lián);通常設置為被緩存的結構類型的名字,不能包含空格。參數(shù)size:每個內存區(qū)域的大小。offset:頁內第一個對象的偏移量;用來確保被分配對象的特殊對齊,0表示缺省值。flags:控制分配方式的位掩碼:SLAB_NO_REAP

保護緩存在系統(tǒng)查找內存時不被削減,不推薦。SLAB_HWCACHE_ALIGN

所有數(shù)據(jù)對象跟高速緩存行對齊,平臺依賴,可能浪費內存。SLAB_CACHE_DMA

每個數(shù)據(jù)對象在DMA內存區(qū)段分配.85整理ppt后備高速緩存創(chuàng)立一個新的后備高速緩存kmem_cache_create參數(shù)constructor和destructor是可選函數(shù),用來初始化新分配的對象和在內存被作為整體釋放給系統(tǒng)之前“清理〞對象。86整理ppt后備高速緩存kmem_cache_alloc從已創(chuàng)立的后備高速緩存中分配對象:void*kmem_cache_alloc(kmem_cache_t*cache,intflags);flags和kmalloc的flags相同使用kmem_cache_free釋放一個對象:voidkmem_cache_free(kmem_cache_t*cache,constvoid*obj);當驅動用完這個后備高速緩存〔通常在當模塊被卸載時〕,釋放緩存: intkmem_cache_destroy(kmem_cache_t*cache);87整理pptget_free_page相關函數(shù)如果一個模塊需要分配大塊的內存,最好使用面向頁的分配技術。__get_free_page(unsignedintflags); 返回一個指向新頁的指針,未清零該頁get_zeroed_page(unsignedintflags);

類似于__get_free_page,但用零填充該頁__get_free_pages(unsignedintflags,unsignedintorder);分配是假設干(物理連續(xù)的)頁面并返回指向該內存區(qū)域的第一個字節(jié)的指針,該內存區(qū)域未清零flags與kmalloc的用法相同order是請求或釋放的頁數(shù)以2為底的對數(shù)。假設其值過大(沒有這么大的連續(xù)區(qū)可用),那么分配失敗88整理pptget_free_page相關函數(shù)當程序不需要頁面時,用以下函數(shù)之一來釋放 voidfree_page(unsignedlongaddr);

voidfree_pages(unsignedlongaddr,unsignedlongorder);89整理pptLinux的中斷處理

90整理ppt為什么會有中斷中斷最初是為克服對I/O接口控制采用程序查詢所帶來的處理器低效率而產生的。處理器速度一般比外設快很多用輪詢的方式來查詢設備的狀態(tài),CPU效率不高,CPU和外設不能并行工作。中斷機制讓CPU啟動設備后,就去處理其他任務,只有當外設真正完成數(shù)據(jù)傳輸?shù)臏蕚?,請求CPU效勞的時候,CPU才轉過來處理外設的請求。91整理ppt中斷和異常外部中斷: 外部設備所發(fā)出的I/O請求。隨著計算機系統(tǒng)結構的不斷改進以及應用技術的日益提高,中斷的適用范圍也隨之擴大,出現(xiàn)了所謂的內部中斷〔或叫異?!?。異常: 為解決機器運行時所出現(xiàn)的某些隨機事件及編程方便而出現(xiàn)的。92整理pptI/O中斷處理為了保證系統(tǒng)對外部的響應,一個中斷處理程序必須被盡快的完成。因此,把所有的操作都放在中斷處理程序中并不適宜Linux中把緊隨中斷要執(zhí)行的操作分為三類緊急的(critical)

一般關中斷運行。諸如對PIC應答中斷,對PIC或是硬件控制器重新編程,或者修改由設備和處理器同時訪問的數(shù)據(jù)非緊急的(noncritical)

如修改那些只有處理器才會訪問的數(shù)據(jù)結構(例如按下一個鍵后讀掃描碼),這些也要很快完成,因此由中斷處理程序立即執(zhí)行,不過一般在開中斷的情況下93整理pptI/O中斷處理Linux中把緊隨中斷要執(zhí)行的操作分為三類非緊急可延遲的(noncriticaldeferrable)這些操作可以被延遲較長的時間間隔而不影響內核操作,有興趣的進程將會等待數(shù)據(jù)。內核用下半局部這樣一個機制來在一個更為適宜的時機用獨立的函數(shù)來執(zhí)行這些操作。如把緩沖區(qū)內容拷貝到某個進程的地址空間(例如把鍵盤緩沖區(qū)內容發(fā)送到終端處理程序進程)。94整理pptS3c2410的中斷中斷發(fā)生時,需要知道中斷的來源。芯片的引線有限,很難提供很多條中斷請求引線。使用中斷控制器管理中斷請求線。S3c2410將中斷控制器集成在CPU芯片中,“復用〞GPIO端口引腳,具有作為中斷請求線的功能。SRCPND存放器32位中的每一位對應著一個中斷源,每一位被設置為1,那么相應的中斷源產生中斷請求并且等待中斷被效勞。95整理ppt注冊中斷效勞例程中斷號是一個珍貴且常常有限的資源。內核維護一個中斷號的注冊表。要使用中斷,就要進行中斷號的申請,也就是IRQ(InterruptReQuirement)。只有當設備需要中斷的時候才申請占用一個IRQ,或者是在申請IRQ時采用共享中斷的方式,讓更多的設備使用中斷。96整理ppt注冊中斷效勞例程在<linux/interrupt.h>實現(xiàn)中斷注冊接口: intrequest_irq(unsignedintirq, irqreturn_t(*handler)(int,void*,structpt_regs*), unsignedlongflags, constchar*dev_name, void*dev_id ); voidfree_irq(unsignedintirq,void*dev_id);request_irq的返回值是0指示申請成功,為負值時表示錯誤碼。函數(shù)返回-EBUSY表示已經有另一個驅動占用了所要申請的中斷線。97整理ppt注冊中斷效勞例程request_irq的參數(shù)說明:unsignedintirq,要申請的中斷號。irqreturn_t(*handler)(int,void*,structpt_regs*),要安裝的中斷處理函數(shù)指針。constchar*dev_name, 用在/proc/interrupts中顯示中斷的擁有者。98整理ppt注冊中斷效勞例程request_irq的參數(shù)說明:unsignedlongflags,與中斷管理相關的位掩碼選項。Flags的每個位有不同含義SA_INTERRUPT當該位被設置時,表示這是一個“快速〞中斷??焖僦袛嗵幚砝踢\行時,屏蔽中斷。SA_SHIRQ這個位表示中斷可以在設備間共享。void*dev_id 這個指針用于共享的中斷號。做為驅動程序的私有數(shù)據(jù)區(qū)〔可用來識別那個設備產生的中斷〕。不使用共享中斷線方式時,可設置為NULL。99整理pptproc文件系統(tǒng)中的中斷信息/proc/interrupts反映系統(tǒng)的中斷信息第一列是IRQ號給出每個中斷線發(fā)生中斷的次數(shù)。給出處理中斷的可編程中斷控制器。給出在該中斷號上注冊中斷處理例程的設備名稱。100整理ppt實現(xiàn)中斷處理例程中斷處理例程特別之處:在中斷時間內運行,不能向用戶空間發(fā)送或者接收數(shù)據(jù)。不能做任何導致休眠的操作。不能調用schedule函數(shù)。無論快速還是慢速中斷處理例程,都應該設計成執(zhí)行時間盡可能短。101整理ppt實現(xiàn)中斷處理例程中斷處理函數(shù)的參數(shù)和返回值irqreturn_t(*handler)(intirq,void*dev_id,structpt_regs*regs)Irq中斷號Dev_id驅動程序可用的數(shù)據(jù)區(qū),通??蓚鬟f指向描述設備的數(shù)據(jù)結構指針。structpt_regs*regs,保存了處理器進入中斷代碼之前的cpu存放器的值。一般驅動可不要。102整理ppt實現(xiàn)中斷處理例程啟動和禁用中斷驅動禁止特定中斷線的中斷:#include<asm/irq.h>.voiddisable_irq(intirq);voidenable_irq(intirq);禁止所有中斷voidlocal_irq_save(unsignedlongflags);local_irq_save在當前處理器上禁止中斷遞交,在保存當前中斷狀態(tài)到flags。voidlocal_irq_disable(void);local_irq_disable關閉本地中斷遞交而不保存狀態(tài);103整理ppt實現(xiàn)中斷處理例程翻開中斷:voidlocal_irq_restore(unsignedlongflags); 恢復由local_irq_save存儲于flags的狀態(tài),而local_irq_enable無條件翻開中斷.voidlocal_irq_enable(void);104整理ppt頂半部和底半步中斷處理的一個主要問題是如何在處理中進行長時間的任務。響應一次設備中斷需要完成一定數(shù)量的工作,但是中斷處理需要很快完成并且不使中斷阻塞太長。Linux把中斷處理例程分兩局部:頂局部:實際響應中斷的例程。底局部:被頂局部調用,通過開中斷的方式進行。兩種機制實現(xiàn):Tasklet工作隊列workqueue105整理ppt頂半部和底半部頂半部頂半部的功能是“登記中斷〞,當一個中斷發(fā)生時,它進行相應地硬件讀寫后就把中斷例程的下半部掛到該設備的底半部執(zhí)行隊列中去。頂半部執(zhí)行的速度就會很快,可以效勞更多的中斷請求。底半部僅有“登記中斷〞是遠遠不夠的,因為中斷的事件可能很復雜。Linux引入了一個底半部,來完成中斷事件的絕大多數(shù)使命。底半部和頂半部最大的不同是底半部是可中斷的,而頂半部是不可中斷的,底半部幾乎做了中斷處理程序所有的事情,而且可以被新的中斷打斷!底半部那么相對來說并不是非常緊急的,通常還是比較耗時的,因此由系統(tǒng)自行安排運行時機,不在中斷效勞上下文中執(zhí)行。106整理ppt小任務機制tasklet內核在BH機制的根底上進行了擴展,實現(xiàn)“軟中斷請求〞〔softirq〕機制。利用軟中斷代替bottomhalfhandler的處理。tasklet機制正是利用軟中斷來完成對驅動bottomhalf的處理。tasklet會讓內核選擇某個適宜的時間來執(zhí)行給定的小任務。tasklet所對應的處理函數(shù)就是tasklet_action,這個處理函數(shù)在系統(tǒng)啟動時初始化軟中斷時,就在軟中斷向量表中注冊。Tasklet_action遍歷一個全局的tasklet_vec鏈表。鏈表中的元素為tasklet_struct結構體。107整理ppt軟中斷和tasklet的關系如以下圖:小任務機制tasklet108整理ppt小任務機制taskletksoftirqd是一個后臺運行的內核線程,它會周期的遍歷軟中斷的向量列表,如果發(fā)現(xiàn)哪個軟中斷向量被掛起了〔pend〕,就執(zhí)行對應的處理函數(shù)。tasklet所對應的處理函數(shù)就是tasklet_action,這個處理函數(shù)在系統(tǒng)啟動時初始化軟中斷時,就在軟中斷向量表中注冊。109整理ppt小任務以數(shù)據(jù)結構的形式存在:structtasklet_struct{ structtasklet_struct*next; unsignedlongstate; atomic_tcount; void(*func)(unsignedlong); unsignedlongdata;};每個結構一個函數(shù)指針func,指向自定義的函數(shù)。這就是我們要執(zhí)行的小任務函數(shù)。小任務機制tasklet110整理ppttasklet的接口DECLARE_TASKLET(name,function,data)此接口初始化一個tasklet;name是tasklet的名字,function是執(zhí)行tasklet的函數(shù);data是unsignedlong類型的function參數(shù)。staticinlinevoidtasklet_schedule(structtasklet_struct*t)調度執(zhí)行指定的tasklet。將定義后的tasklet掛接到cpu的tasklet_vec鏈表。而且會引起一個軟tasklet的軟中斷,既把tasklet對應的中斷向量掛起(pend)。小任務機制tasklet111整理pptvoidtasklet_disable(structtasklet_struct*t);這個函數(shù)禁止給定的tasklet.tasklet,但仍然可以被tasklet_schedule調度,但是它的執(zhí)行被延后直到這個tasklet被再次激活。voidtasklet_enable(structtasklet_struct*t);激活一個之前被禁止的tasklet.如果這個tasklet已經被調度,它會很快運行.一個對tasklet_enable的調用必須匹配每個對tasklet_disable的調用,因為內核跟蹤每個tasklet的"禁止次數(shù)".小任務機制tasklet112整理pptvoidtasklet_hi_schedule(structtasklet_struct*t);調度tasklet在更高優(yōu)先級執(zhí)行.當軟中斷處理運行時,它在其他軟中斷之前處理高優(yōu)先級tasklet。voidtasklet_kill(structtasklet_struct*t);這個函數(shù)確保了這個tasklet沒被再次調度來運行;它常常被調用當一個設備正被關閉或者模塊卸載時.如果這個tasklet被調度來運行,這個函數(shù)等待直到它已執(zhí)行.

小任務機制tasklet113整理ppt工作隊列

工作隊列類似taskets,允許內核代碼請求在將來某個時間調用一個函數(shù),不同在于:tasklet在軟件中斷上下文中運行,所以tasklet代碼必須是原子的。而工作隊列函數(shù)在一個特殊內核進程上下文運行,有更多的靈活性,且能夠休眠。tasklet只能在最初被提交的處理器上運行,這只是工作隊列默認工作方式。內核代碼可以請求工作隊列函數(shù)被延后一個給定的時間間隔。tasklet執(zhí)行的很快,短時期,并且在原子態(tài),而工作隊列函數(shù)可能是長周期且不需要是原子的,兩個機制有它適合的情形。114整理ppt工作隊列structworkqueue_struct類型在workqueue.h中定義。一個工作隊列必須明確的在使用前創(chuàng)立,宏為: structworkqueue_struct*create_workqueue(constchar*name); structworkqueue_struct*create_singlethread_workqueue(constchar*name);每個工作隊列有一個或多個專用的進程("內核線程"),這些進程運行提交給這個隊列的函數(shù)。假設使用create_workqueue,就得到一個工作隊列它在系統(tǒng)的每個處理器上有一個專用的線程。在很多情況下,過多線程對系統(tǒng)性能有影響,如果單個線程就足夠那么使用create_singlethread_workqueue來創(chuàng)立工作隊列。115整理ppt工作隊列當用完一個工作隊列,可以去掉它,使用:voiddestroy_workqueue(structworkqueue_struct*queue);

116整理pptLinux內核中定義了一個timer_list結構,我們在驅動程序中可以利用定時器structtimer_list{

structlist_headlist;

unsignedlongexpires;//定時器到期時間

unsignedlongdata;//作為參數(shù)被傳入定時器處理函數(shù)

void(*function)(unsignedlong);

};117整理ppt定時器timer的API函數(shù):增加定時器 voidadd_timer(structtimer_list*timer);刪除定時器 intdel_timer(structtimer_list*timer);修改定時器的expire intmod_timer(structtimer_list*timer,unsignedlongexpires);118整理ppt定時器使用定時器的一般流程為:

〔1〕timer、編寫function;

〔2〕為timer的expires、data、function賦值;

〔3〕調用add_timer將timer參加列表;

〔4〕在定時器到期時,function被執(zhí)行;

〔5〕在程序中涉及timer控制的地方適當?shù)卣{用del_timer、mod_timer刪除timer或修改timer的expires。119整理ppt矩陣式鍵盤原理矩陣式鍵盤一般適用于按鍵數(shù)量較多的場合,它由行線和列線組成,按鍵位于行、列的交叉點上。如下圖,一個4×4的行、列結構可以構成一個有16個按鍵的鍵盤。120整理ppt矩陣式鍵盤原理按鍵設置在行、列交叉點上,行、列分別連接到按鍵開關的兩端。行線通過上拉電阻接到十5V上。平時無按鍵動作時,行線處于高電平狀態(tài);而當有健按下時,行線電平狀態(tài)將由通過此按鍵的列線電平決定:列線電平如果為低,行線電平為低;列線電平如果為高,那么行線電平亦為高。這一點是識別矩陣式鍵盤是否被按下的關鍵所在。121整理ppt矩陣式鍵盤原理矩陣鍵盤按鍵的識別方法分兩步進行:①識別鍵盤哪一行的鍵被按下。讓所有列線均為低電平,檢查各行

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論