第一篇:Linux下的USB程序分析
Linux下的USB程序分析
關鍵字:USB、LINUX、設備驅動、總線 參考文獻:
LINUX 設備驅動程序 ALESSANDRO RUBINI & JONATHAN CORBET
著 魏擁明 駱剛 姜君 譯
USB 2.0 原理與工程開發 王成儒、李英偉 編著 LINUX 內核源代碼情景分析 毛德操 胡希明 著
一、USB概述 1.USB的優點
USB與計算機的接口采用的不是傳統的I/O模式,它和傳統的通信接口相比,存在以下優點:
(1)熱插拔:用戶可以把USB外設直接連接到一臺正在運行的計算機上,操作系統能自動識別,不用時可將USB在操作系統中卸載,不會損傷計算機
(2)即插即用:用戶將USB設備插入后可立即使用(3)共享式接口:不同的USB外設使用同樣的接口
(4)節省系統資源:只有USB主控制器需要使用一根IRQ線和一些I/O地址空間,對USB外設來說,它需要的僅僅是為其分配一個唯一的地址
(5)傳輸速率高;USB2.0的傳輸速率可達25MB/S以上(6)提供電源(7)兼容性強
2.USB系統描述
USB系統由USB主機和USB設備構成。
USB主機內部含有USB主控制器,負責完成主機和USB設備之間的物理數據傳輸。USB主機中還應有設備驅動程序。USB的數據驅動是基于令牌的,其所有的通信都由USB主機啟動。
二、Linux設備驅動概述 1.設備驅動程序的作用
Linux是“單塊結構”的操作系統,設備驅動作為設備控制模塊中的一部分,在整個操作系統中扮演著非常重要的角色。設備驅動使某個特定的硬件響應一個良好定義的內部編程接口,同時完全隱藏了設備工作的細節。用戶操作通過一組標準化的調用完成,而這些調用是和特定的驅動程序無關的。將這些調用映射到作用于實際硬件的設備特定的操作上,則是設備驅動程序的任務。這個編程接口能夠使得驅動程序獨立于內核的其它部分而建立,在需要的時候,可在運行時“插入”內核。
如果從另一個角度來看驅動程序,那么它可以被看作是應用和實際設備之間的一個軟件層。這種定位使驅動程序具有了“個性化”的特點:即對于相同的設備,不同的驅動程序也可以提供不同的功能。
2.Linux內核功能劃分
Linux內核從功能上分可以分為以下幾個部分;進程管理: 進程管理功能負責創建和撤銷進程以及處理它們和外部世界的連接(輸入和輸出)。不同進程之間的通信(通過信號、管道或進程見通信原語)是整個模塊的基本功能。除此之外,控制進程如何共享CPU的調度程序也是進程管理的一部分。概括地說,內核的進程管理活動就是在單個或多個CPU上實現多個進程的抽象。內存管理:
內存是計算機的主要資源之一,用來管理內存的策略是決定系統性能的一個關鍵因素。內核在有限的可用資源上為每一個進程都創建了一個虛擬尋址空間,內核的不同部分和在內存管理子系統交互時使用一套相同的系統調用,包括從簡單的malloc/free到對其它一些不常用的系統調用。文件系統:
Linux中的每個對象幾乎都可以被看作文件。內核在沒有結構的硬件上構造結構化的文件系統,所構造的文件系統抽象在整個系統中廣泛使用。另外,Linux支持多種文件系統類型,即在物理介質上組織數據的不同方式。設備控制:
除了處理器、內存以及其他很有限的幾個實體外,所有設備控制操作都由與被控制設備相關的代碼來完成。這段代碼就是設備驅動程序,內核必須為系統中的每件外設嵌入相應的驅動程序、包括硬盤驅動器、鍵盤、鼠標等。
3.設備和模塊分類
Linux系統將設備分成三種類型:字符設備、塊設備和網絡接口。每個模塊通常實現其中一種類型,相應地,模塊可分為字符模塊(char modual)、塊模塊(block modual)和網絡模塊(network modual)三種。
字符設備(character device)
字符設備是能夠象字節流(比如文件)一樣被訪問地設備,由字符設備驅動程序來實現這種特性。字符設備驅動程序通常至少需要實現open、close、read和write系統調用。字符終端(/dev/console)和串口(/dev/ttySO以及類似設備)就是字符設備的兩個例子,他們能夠用流很好地表示。字符設備可以通過文件系統節點(如/dev/tty1和/dev/lp0)來訪問,它和普通文件之間的唯一差別在于,對普通文件的訪問可以前后移動訪問指針,而大多數字符設備是只能順序訪問的數據通道。然而,也存在和數據區特性類似的字符設備,訪問它們時可前后移動訪問指針。
塊設備(block device)
和字符設備一樣,塊設備也是通過/dev目錄下的文件系統節點被訪問的。塊設備(例如硬盤)上能夠容納文件系統。Linux允許應用程序象字符設備那樣讀寫塊設備,可以一次傳遞任意多字節的數據。因此,塊設備和字符設備的區別僅僅在于內核內部管理數據的方式,也就是內核和驅動程序的接口不同。象字符設備一樣,塊設備也是通過文件系統節點被訪問的,他們之間的差異對用戶來說是透明的。塊驅動程序除了給內核提供和字符驅動程序一樣的接口以外,還提供了專門面向塊設備的接口,不過這些接口對于那些從/dev目錄下某個目錄打開塊設備的用戶和應用程序都是不可見的。另外,塊設備的接口必須支持掛裝(mount)文件系統。
網絡接口(network interface)
任何網絡事務都要經過一個網絡接口,即一個能夠和其它主機交換數據的設備。通常接口是個硬件設備,但也可能是個純軟件設備,比如回環(loopback)接口。網絡接口由內核中的網絡子系統驅動,負責發送和接收數據包,它無須了解每項事務是如何映射到實際傳送的數據包的。盡管Telnet和FTP連接都是面向流的,它們都使用了同一個設備,但這個設備看到的只是數據包,而不是獨立的流。
三、Linux下USB程序分析
USB總線的初始化和USB設備的枚舉
首先,我們先看一下USB總線本身的初始化。USB控制器(連同根集中器)連接在PCI總線上,是一個PCI設備,在PCI總線的初始化過程中會受到枚舉。PCI設備的初始化完成后,在PCI總線樹中就游樂代表著具體USB總線控制器的PCI_DEV數據結構,并已為控制器的I/O區間和RAM區間分配和設置了總線地址。
在USB總線控制器的設備驅動程序方面,則要為其準備下一個PCI_DRIVER數據結構,其類型定義在include/linux/pci.h中: struct pci_driver { struct list_head node;char *name;const struct pci_device_id *id_table;int(*probe)(struct pci_dev *dev,const struct pci_device_id *id);void(*remove)(struct pci_dev *dev);void(*suspend)(struct pci_dev *dev);void(*resume)(struct pci_dev *dev);};
這個數據結構為通用的PCI設備管理機制提供了幾個函數指針,特別是為一個通用的、一般化的PCI設備初始化過程提供了函數指針PROBE。供這個PCI設備的初始化過程叫“回叫”,以完成具體設備的初始化。對于遵循UHCI界面的USB控制器,其PCI_DRIVER數據結構為uhci_pci_driver,定義于drivers/usb/uhci.c:
static struct pci_driver uhci_pci_driver= { name: “usb-uhci”, id_table: &uhci_pci_ids [0],probe: uhci_pci_probe, remove: uhci_pci_remove, #ifdef CONFIG_PM suspend: uhci_pci_suspend, resume: uhci_pci_resume, #endif /*PM*/ };
結構中的指針id_table應該指向一個pci_device_id結構數組,表明由這個數據結構所確定的設備驅動程序適用于哪一些PCI設備。對此,drivers/usb/uhci.c中相應地定義了數組uhci_pci_ids[]: static const struct pci_device_id__devinitdata uhci_pci_ids []={{ /*handle any USBUHCI controller */ class:((PCI_CLASS_SERIAL_USB<<8)|0x00), class_mask: ~0, /* no matter who makes it */ vendor: PCI_ANY_ID, device: PCI_ANY_ID, subvendor: PCI_ANY_ID, subdevice: PCI_ANY_ID,},{/* end:all zeroes*/} };從其定義可以看出,它適用于所有類型為PCI_CLASS_SERIAL_USB,子類型為0的PCI設備,即USB控制器,而不管是由哪一家廠商提供。
準備好這些數據結構以后,就可以通過inline函數pci_module_init()向系登記具體的設備驅動程序,并對設備進行初始化。其代碼在include/linux/pci.h中。這里通過pci_register_driver()完成PCI設備驅動程序的登記和初始化,這就是前面所講的通用、一般化的PCI設備初始化過程。這個函數返回一個計數,表示在PCI總線上找到了幾個這樣的設備。一般化的PCI設備初始化過程。這個函數返回一個計數,表示在PCI總線上找到了幾個這樣的設備。一般,如果這個函數返回0就要調用pci_unregister_driver()撤消登記,但是如果PCI總線允許“熱插入”。即在加電后運行過程中帶電插入設備,而驅動程序又并非通過可以安裝模塊實現,則不應該撤消登記;因為以后熱插入此種設備仍需要執行這個驅動程序,應該保留著,以備插入此種設備之需。
如果一個設備的pci_dev結構尚未與任何驅動程序掛鉤,并且其所有地址區都尚未啟用,則pci_dev_driver()返回0。這樣的pci_dev結構需要通過pci_announce_device()加以對比(drivers/pci/pci.c)。
可想而知,所謂比對是將具體設備的類型、廠家等等在PCI枚舉階段從設備收集的信息與USB驅動程序的數組uhci_pci_ids[]進行對比,具體由pci_match_device()完成的(drivers/pci/pci.c)如果比隊結果相符,就找到了一個USB總線控制器,此時便通過驅動程序提供的函數指針probe對其進行初始化。對于遵循UHCI界面的USB總線控制器,這個函數是uhci_pci_probe(),其代碼在drivers/usb/uhci.c中。
我們在前面曾經說USB設備沒有向主機發出中斷請求的能力,而只能等待,受主機的查詢。但是這并不意味著USB控制器(在主機中)沒有向CPU發出中斷請求的能力,這是完全不同的兩回事,不能混淆。事實上,USB控制器是有能力向CPU發出中斷請求的,所以要接通它的中斷請求線。
USB控制器本身帶有微處理器,在USB總線上發送/接收的信息都由USB控制器通過DMA直接從內存讀/寫,主機的CPU只要提供緩沖區指針就可以了。而且,CPU也不需要逐次地為USB總線上的操作提供緩沖區指針,而只要把緩沖區指針紀錄在相應的交互請求,或曰交互描述塊就可以了。USB控制器自會順著交互請求隊列逐個地完成對這些緩沖區的操作。類似的DMA操作稱為“智能化DMA”。其實,USB總線控制器的DMA操作甚至比一般的智能化DMA還要復雜,因為CPU為之準備的并不只是一個緩沖區隊列,而是許多交互請求隊列,稱為一個“調度”。PCI設備(的接口)要進行DMA操作就得具有競爭成為“主總線”的能力。另一個方面,PCI設備的DMA功能還要服從CPU的統一管理,在PCI配置寄存器組的命令寄存器中有一個控制位PCI_COMMAND_MASTER,就是用來打開或關閉具體PCI設備競爭成為主總線主的能力。在完成PCI總線初始化時,所有PCI設備的DMA功能都是關閉的,所以這里要通過pci_set_master()啟用USB控制器競爭成為主總線主的能力(drivers/pci/pci.c)。
數據結構中有幾個特別重要的成分。首先是指針f1,指向一個uhci_framelist數據結構,這就是具體USB總線的“框架表”,這種數據結構也定義于drivers/usb/uhci.h: struct uhci_framelist{ __u32 frame[UHCI_NUMFRAMES];} __attribute__((aligned(4096)));
USB總線的框架表實際上是個指針數組,每個指針都指向一個等時交互隊列。常數UHCI_NUMFRAME在同一個文件中定義為1024,所以整個數組代表著1024個框架。數組的起始地址必須與4K字節邊界對齊,這樣其起始地址的低12位就全都是0。USB控制器內部有個“框架表基地址寄存器”,用來記錄這個基地址。同時,USB控制器內部還有個10位的“框架計數器”,這個計數器從0開始每過1毫秒(1/1024秒)就加一,直至0x3ff即1023,然后又變為0,如此周而復始。在框架計數的后面添上兩位0,再與框架表的基地址連在一起,就成了指向框架表中某個表項的指針。在框架表中的每個表項都指向一個uhci_td結構的隊列,每個uhci_td結構是對一個交互的描述,我們稱之為“交互描述塊”或“交互請求”。USB控制器在每個框架中首先就執行這個隊列。每個等時交互隊列的最后一個數據結構指向一個(實際上是一截)中斷交互隊列。中斷交互隊列與框架之間并不是一一對應的關系,uhci結構中uhci_td結構數組skeltd[],其中的每個元素都指向一截中斷交互請求隊列,常數UHCL_NUM_SKELTD定義為10,其中skeltd[0]是整個隊列的終點,而skeltd[9]實際上不用,所以一共有8截這樣的中斷交互請求隊列。這些中斷交互請求隊列又在鏈接在一起,成為一個總的中斷交互請求隊列。但是,鏈接在不同部位上的中斷交互請求受到執行的頻率是不一樣的。當將一個代表著中斷交互的uhci_td結構鏈入隊列時,可以根據所要求的執行周期選擇鏈入中斷交互請求隊列的不同部位。而skeltd[]中的各個元素,則起著鏈入點的作用,所以這個數組稱為中斷交互請求隊列的“骨架”(skeleton)。這些鏈入點本身也是uhci_td結構,不過是空閑的uhci_td結構,USB控制器在執行時會自動跳過。此外,雖然中斷交互請求隊列并不是與框架一一對應,二者間還是有著某種靜態的對應關系。
等時交互和中斷交互都是周期性的,在每個框架中二者的流量加在一起不超過90%。這樣,在每個框架中,USB控制器在執行完這兩種交互請求以后總是還有一些時間(至少10%),可以用來執行控制交互以及成塊交互。這兩種交互都不是周期性的,其隊列與框架沒有靜態的對應關系,USB控制器對這些隊列的執行完全是動態的,有時間就執行,沒有時間就不執行,時間多就多執行,時間少就少執行。在uhci結構中還有個uhci_qh結構數組skelqh[],數組中的每個元素都是一個隊列頭,用來維持一個“隊列的隊列”,或者說傳輸請求的隊列。如前所述,每個傳輸請求是一個交互請求的隊列。所以,這個數組是控制/成塊傳輸請求隊列的骨架,其大小是UHCI_NUM_SKELQH,在drivers/usb/uhci.h中定義為4。從邏輯上說,只要有兩個鏈入點就夠了,可是實際上USB設備有全速和低速之分,還有一個有著特殊的用途,所以共有4個。
因此,除還有其他一些成分以外,uhci數據結構實際上代表著主機CPU為一條USB總線排好的“日程表”,或者說執行程序,這就稱為一個“調度”(schedule)。不言而喻,初始化時要為USB控制器分配、建立一個uhci數據結構。這是由alloc_uhci()完成的,其代碼在drivers/usb/uhci.c中。
USB總線的根集中器總是與USB控制器集成在一起。對于UHCI界面的總線控制器,其I/O地址區間的前16個地址用于總線控制器本身,其余的就用于根集中器。根集中器的每個“端口”(port)占用兩個地址。端口的數量則取決于具體的芯片,至少兩個,最多八個。每個端口的狀態寄存器中的bit7總是1,所以代碼中通過一個循環試讀,以確定根集中器中端口的數量。
前面講過,uhci結構中的skeltd[]用于8截中斷交互隊列,是個uhci_td數據結構的數組。結構名中的“td”是“交互描述結構”(transaction descriptor)的意思。這種數據結構定義于drivers/usb/uhci.h: struct uhci_td{ /*Hardware fields*/ __u32 link;__u32 status;__32 info;__32 buffer;
/*Software fields*/ unsigned int *frameptr;struct uhci_td *prevtd, *nexttd;
struct usb_device *dev;struct urb *urb;
struct list_head list;}__attribute__((aligned(16)));
每個uhci_td數據結構代表著一個交互請求,就好象是對USB控制器的一條指令。結構中前4個32位長字的作用是由USB控制器的硬件結構所確定了的,因而不能改變;不過硬件只認開頭這4個字段,其余的字段則由軟件定義和使用。數據結構的起點必須與16字節的邊界對齊,這也是USB總線控制器的硬件結構所要求的。這4個32位長字實際上相當于一組寄存器。我們把硬件使用的這一部分稱為“交互描述塊”,以區別于整個交互描述結構。結構頭部的4個字段中,link是指向下一個uhci_td數據結構的連接指針,buffer則指向用于發送或接收的緩沖區,二者均為物理地址。此外,info就好象是命令寄存器,相當于指令中的操作碼。內核在drivers/usb/uhci.c中提供了一個inline函數uhci_fill_td(),用于設置 uhci_td數據結構頭部除link外的三個字段:
static void inline uhci_fill_td(struct uhci_td *td,__u32 status,__u32 info,__u32 buffer){ td->status = status;td->info = info;td->buffer = buffer;} 與目標設備建立連接的過程就是對目標設備的“枚舉”。枚舉過程的步驟如下:(1)為設備制訂地址(2)從設備讀入其usb_device_descriptor數據結構(3)從設備讀入其所有的“配置”描述結構(4)枚舉或改變設備的配置
USB設備的初始化
每個USB設備都有usb_driver數據結構,定義再include/linux/usb.h中: struct usb_driver{ const char * name;void *(*probe)(struct usb_device *dev, unsigned intf, const struct usb_device_id *id);void(*disconnect)(struct usb_device *,void *);struct list_head driver_list;struct file_operations *fops;int minor;struct semaphore serialize;int(*ioctl)(struct usb_device* dev,unsigned int code,void *buf);const struct usb_device_id *id_table;};
通過usb_scan_devices()掃描所有USB總線上的所有設備,讓每個USB設備驅動模塊都試著來“認領”與其對口的設備。
所謂“認領”就是使一個usb_interface_descriptor數據結構與相應的設備的usb_driver結構掛鉤。這樣,從具體設備的數據結構出發,就可以找到其設備驅動程序了。就這樣,當usb_scan_devices()完成了對所有的USB設備的掃描時,對掃描器設備的登記和初始化就完成了。最后,以次設備號中的低4位為下標的指針數組p_scn_table[]記錄著指向每個具體scn_usb_data結構的地址。
USB設備的驅動
所有的USB設備都有相同的主設備號USB_MAJOR,而根據次設備號劃分具體的設備及其類型。所以,根據設備文件節點提供的主設備號,CPU首先找到USB總線的file_operations數據結構usb_fops,從中得到用于open操作的函數指針,這個指針指向usb_open()。
Static int usb_open(struct inode * inode,struct file *file){ int minor = MINOR(inode->i_rdev);struct usb_driver *c = usb_minors[minor/16];int err =-ENODEV;struct file_operations *old_fops,*new_fops = NULL;if(!c ||!(new_fops = fops_get(c->fops)))return err;old_fops = file->f_op;file->f_op = new_fops;if(file->f_op->open)err = file->f_op->open(inode,file);if(err){ fops_put(file->f_op);file->f_op = fops_get(old);} fops_put(old_fops);return err;} 然后,進一步根據次設備號從指針數組usb_minors[]中找到掃描器的usb_driver結構。
對于USB總線上的每個傳輸,需要為之創建一個“USB傳輸請求塊”,即usb數據結構。發送控制報文的過程是:根據參數建立一個usb數據結構,把這個usb結構交給低層,讓低層據以調度相應的控制傳輸,然后睡眠等待傳輸的完成。
對于采用UHCI界面的USB總線控制器,其usb_bus結構中的指針hcpriv指向一個uhci數據結構,而uhci結構中有個次層結構rh,里面是有關根集中器的信息,這是在根集中器初始化時設置好的。如果目標設備恰好就是根集中器,那么通信的過程可以簡化,因為CPU直接就可以訪問其各個寄存器,不需要通過USB總線就能進行通信,所以此時由rh_submit_urb()完成操作。
與其他所有USB設備的通信(傳輸)都要通過USB總線上的交互來完成,因而需要為具體的傳輸調度一個或幾個交互。除等時傳輸之外,在調度中不允許同時存在對同一對象的兩個同種傳輸。
第二篇:USB Device實驗程序解析
// USB device實驗程序解析
//頭文件
#include
// SYSTICKS_PER_SECOND 每秒中斷次數 #define SYSTICKS_PER_SECOND 100 #define SYSTICK_PERIOD_MS(1000 / SYSTICKS_PER_SECOND)// 全局systick計數
volatile ui32 g_ulSysTickCount = 0;//全局變量記錄發送和接收字節數 volatile ui32 g_ulTxCount = 0;volatile ui32 g_ulRxCount = 0;#ifdef DEBUG ui32 g_ulUARTRxErrors = 0;#endif // 與debug相關的定義和聲明
// 如果在編譯時定義了DEBUG那么通過UART0進行Debug的輸出 #ifdef DEBUG //將所有Debug打印請求映射到UARTprintf輸出 #define DEBUG_PRINT UARTprintf #else // 編譯所有的Debug打印請求
#define DEBUG_PRINT while(0)((int(*)(char *,...))0)#endif // 圖形上下文(context)用于OLED屏幕顯示 tContext g_sContext;// 宏定義標志,用于在中斷時向主程序發送命令.#define COMMAND_PACKET_RECEIVED 0x00000001 #define COMMAND_STATUS_UPDATE
0x00000002 volatile ui32 g_ulFlags = 0;char *g_pcStatus;// 全局標志顯示USB設置是否完成 static volatile bool g_bUSBConfigured = false;// 錯誤處理 #ifdef DEBUG void __error__(char *pcFilename, ui32 ulLine){
UARTprintf(“Error at line %d of %sn”, ulLine, pcFilename);
while(1)
{
} } #endif // systick中斷處理 void SysTickIntHandler(void){
// 更新tick計數
g_ulSysTickCount++;}
//函數static ui32 // 功能:device接收數據并返回給主機。
// 當從主機數據發送就緒后,該程序被調用,逐個字節讀取數據,并且翻轉 //大小寫,最后回傳給主機
// 變量 psDevice 指向要處理的設備數據實例 // 變量 pcData 指向USB接收緩沖去新接收到的數據 // 變量ulNumBytes 是程序要處理的字節數。// 返回:處理的數據字節數 static ui32 EchoNewDataToHost(tUSBDBulkDevice *psDevice, ui8 *pcData,ui32 ulNumBytes){
ui32 ulLoop, ulSpace, ulCount;
ui32 ulReadIndex;
ui32 ulWriteIndex;
tUSBRingBufObject sTxRing;
// 獲取當前緩沖區信息以允許設備能直接寫入發送緩沖區。
//(變量中已經有足夠信息與接收緩沖區直接連接)
USBBufferInfoGet(&g_sTxBuffer, &sTxRing);
// 獲取發送緩沖區的剩余空間
ulSpace = USBBufferSpaceAvailable(&g_sTxBuffer);
// 判斷此次可以處理的字節數
ulLoop =(ulSpace < ulNumBytes)? ulSpace : ulNumBytes;
ulCount = ulLoop;
// 更新接收字節數
g_ulRxCount += ulNumBytes;
// 顯示Debug信息
DEBUG_PRINT(“Received %d bytesn”, ulNumBytes);
// 與USB緩沖區連接啟動字節處理
ulReadIndex =(ui32)(pcData'a')+ 'A';
}
else
{
// 判斷是否是大寫字母
if((g_pucUSBRxBuffer[ulReadIndex] >= 'A')&&
(g_pucUSBRxBuffer[ulReadIndex] <= 'Z'))
{ // 轉換為小寫字母,寫到發送緩沖區中
g_pucUSBTxBuffer[ulWriteIndex] =
(g_pucUSBRxBuffer[ulReadIndex]1;
sRect.i16YMax = 9;
GrContextForegroundSet(&g_sContext, ClrDarkBlue);
GrRectFill(&g_sContext, &sRect);
// OLED屏幕背景為白色文本
GrContextForegroundSet(&g_sContext, ClrWhite);
// 屏幕中間顯示應用名稱“usb-dev-bulk”
GrContextFontSet(&g_sContext, g_psFontFixed6x8);
GrStringDrawCentered(&g_sContext, “usb-dev-bulk”,-1,|
GrContextDpyWidthGet(&g_sContext)/ 2, 4, 0);
// 顯示當前的發送字節數、接收字節數
GrStringDraw(&g_sContext, “Tx bytes:”,-1, 0, 32, false);
GrStringDraw(&g_sContext, “Rx bytes:”,-1, 0, 42, false);
// 將GPIO外圍設備設置為GPIO功能,并且配置USB引腳
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOL);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTB_BASE, GPIO_PIN_0 GPIO_PIN_1);
ROM_GPIOPinTypeUSBAnalog(GPIO_PORTL_BASE, GPIO_PIN_6 GPIO_PIN_7);
// 使能系統節拍中斷(system tick)
ROM_SysTickPeriodSet(ROM_SysCtlClockGet()SYSTICKS_PER_SECOND);
ROM_SysTickIntEnable();
ROM_SysTickEnable();
// 顯示應用程序名稱和UART輸出
DEBUG_PRINT(“nStellaris USB bulk device examplen”);
DEBUG_PRINT(“--nn”);
// 顯示當前USB設備運行狀況
DisplayStatus(&g_sContext, “Configuring USB”);
// 初始化USB發送和接收緩沖區
USBBufferInit((tUSBBuffer *)&g_sTxBuffer);
USBBufferInit((tUSBBuffer *)&g_sRxBuffer);
// USB設備初始化
// 將USB設備信息發送給主機USB庫并且將設備連接在總線上
USBDBulkInit(0,(tUSBDBulkDevice *)&g_sBulkDevice);
// 等待初始化設置完成
DisplayStatus(&g_sContext, “Waiting for host”);
// 清除發送、接收字節數
ulRxCount = 0;
| | /
ulTxCount = 0;
// 主程序循環
while(1)
{ // 判斷是否請求更新顯示狀態
if(g_ulFlags & COMMAND_STATUS_UPDATE)
{
// 清除命令標志
g_ulFlags &= ~COMMAND_STATUS_UPDATE;
DisplayStatus(&g_sContext, g_pcStatus);
}
// 判斷是否有發送錯誤
if(ulTxCount!= g_ulTxCount)
{ // 更新最后的發送字節數
ulTxCount = g_ulTxCount;
// 通過UART更新顯示
usnprintf(pcBuffer, 16, “ %d ”, ulTxCount);
GrStringDraw(&g_sContext, pcBuffer,-1, 48, 32, true);
}
// 判斷是否有接收錯誤
if(ulRxCount!= g_ulRxCount)
{ // 更新最后的接收字節數
ulRxCount = g_ulRxCount;
// 通過UART更新顯示
usnprintf(pcBuffer, 16, “ %d ”, ulRxCount);
GrStringDraw(&g_sContext, pcBuffer,-1, 48, 42, true);
}
} }
第三篇:6程序分析
《單片機原理及應用》題庫六
(程序分析)
程序1
流水燈程序
#include
//包含8952單片機頭文件 void delaym(unsigned int t)
// 延時子程序,入口參數ms,延遲時間=t*1ms,t取值范圍0~65535 {
unsigned char j;
//j取值范圍0~255 while(t--){ for(j = 0;j < 250;j++);
//j進行的內部循環,1次延遲8us } }
void main(){
unsigned int i;
while(1)
{
unsigned char a=0xfe;
for(i=0;i<8;i++)
{
P0=a;
delaym(200);
a<<=1;
//循環左移1位
} } }
程序2 電子秒表工作程序 void main(){ TMOD=0x01;
//定時器T0工作在方式1 TH0=0xD8;
// T0裝入時間常數
TL0=0xf0;
EA=1;
//允許 中斷
ET0=1;
//允許 定時器T0中斷
TR0=1;
//啟動定時器T0
while(1)
//無條件循環
{
for(k=0;k<3;k++)
{
P0=b[k];
//送出數碼管段碼
P2=k;
//送出數碼管位選碼
delaym(3);//調用延時函數
} } } } time0_int(void)interrupt 1
//T0中斷服務程序 {
TH0=0xD8;
TL0=0xF0;
i++;
if(i==100)
//到1秒
{
i=0;
second1++;
}
if(second1==10)//到10秒
{
second2++;
second1=0;
}
if(second2==10)//到100秒
{second3++;
second2=0;
second1=0;
}
b[2]=a[second3];//轉換為段碼顯示
b[1]=a[second2];
b[0]=a[second1];
} 程序3 電子時鐘程序 void main(){ TMOD=0x01;
//定時器T0工作在方式1 TH0=0xD8;
// T0延時長度延時10mS TL0=0xf0;
EA=1;
//允許中斷
ET0=1;
//允許定時器T0中斷 TR0=1;while(1){
for(k=0;k<5;k++)
//顯示時鐘的分秒
{
P0=b[k];
P2=k;
delaym(3);
} } } time0_int(void)interrupt 1
//T0中斷服務程序 {
TH0=0xD8;
TL0=0xF0;
i++;
if(i==100)
//到1秒
{
i=0;
second++;
}
if(second==60)//到1分鐘
{second=0;
fen++;
}
w=fen/10;
x=fen%10;
y=second/10;
z=second%10;
b[4]=a[w];
//顯示分十位段碼
b[3]=a[x];
//顯示分個位段碼
b[2]=0xbf;
//數碼管顯示“-”
b[1]=a[y];
//顯示秒十位段碼
b[0]=a[z];
//顯示秒個位段碼
}
程序4
矩陣式按鍵掃描,讀取按鍵的行列編碼。unsigned char Keycan(void){
unsigned char rcode, ccode;
P1 = 0xF0;
// P1口對鍵盤輸出 鍵盤掃描信號
if((P1&0xF0)!= 0xF0)
{
delay(1);// 調用巖石函數函數實現軟件去抖動
if((P1&0xF0)!= 0xF0)
//如果不相等說明 有鍵按下
{ rcode = 0xFE;
// 逐行掃描初值
while((rcode&0x10)!= 0)
{
P1 = rcode;
// 輸出行掃描碼
if((P1&0xF0)!= 0xF0)//
{
ccode =(P1&0xF0)|0x0F;//取入列代碼
//do{;}
while((P1&0xF0)!= 0xF0);//等待鍵釋放
return((~rcode)+(~ccode));// 返回
}
else
rcode =(rcode<<1)|0x01;//行輸出代碼移1位
} }
}
return 0;// 無鍵按下,返回
}
程序5 按鍵取值計算程序
void KeyDeal(unsigned char Key)
{
if(Key!=0)
//再次判斷是否有按鍵按下
{
switch(Key)
//對按鍵的行列碼譯碼成0~15
{
case 0x11: K=0;break;
case 0x21: K=1;break;
case 0x41: K=2;break;
case 0x81: K=3;break;
case 0x12: K=4;break;
case 0x22: K=5;break;
case 0x42: K=6;break;
case 0x82: K=7;break;
case 0x14: K=8;break;
case 0x24: K=9;break;
case 0x44: K=10;break;
case 0x84: K=11;break;
case 0x18: K=12;break;
case 0x28: K=13;break;
case 0x48: K=14;break;
case 0x88: K=15;break;
default: break;
}
if(K<17)
{
c[7]=c[6];
//改變顯示位置,顯示代碼順序前移
c[6]=c[5];
c[5]=c[4];
c[4]=c[3];
c[3]=c[2];
c[2]=c[1];
c[1]=c[0];
c[0]=b[K];
//將0~15譯成段碼供顯示
}
} } 程序6 從計算機鍵盤輸入月份,通過計算機串口顯示相應的日子。#include
//包含8952單片機頭文件 #include
//包含計算機串口頭文件 #define uint unsigned int void main(){ uint month;uint day;SCON=0x50;
//串行口工作在方式1,允許接收。
TMOD=0x20;//定時器1工作在方式2
TCON=0x40;TH1=0xE8;
//裝入波特率對應的時間常數
TL1=0xE8;TI=1;TR1=1;printf(“input monthn”);//要求輸入月份
scanf(“%d”,&month);//讀取鍵盤輸入的月份數值。
switch(month)//查閱月份
{ case 1: case 3: case 5: case 7: case 8: case 10: case 12:day=31;//1、3、5、7、8、10、12上顯示31日
break;case 4: case 6: case 9: case 11:day=30;//4、6、9、11顯示30日
break;case 2:day=28;//2月份顯示28日
break;default:day=1;//都不是,日期1
} printf(“month,has dayn”,&month,&day);//在屏幕上顯示對應月份的日期
}
程序7 延時1秒子程序A ? void delay(void)
{ unsigned char m,n,s;//定義m,n,s為無符號字符型數值(0~255)
for(m=10;m>0;m--)
//m的用途控制第1重循環
for(n=100;n>0;n--)
for(s=124;s>0;s--);
//3個for指令總延時1mS×100× 10=1秒
延時子程序B void delay(unsigned char t){
while(t--)
// 由調用程序規定延時時間
{
unsigned char j;//j的取值范圍 0~255
for(j = 0;j<123;j++);//語句 延時1mS
} }
程序8 數碼顯示程序 unsigned char b[17]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff};數碼管段碼,加1個全滅段碼
unsigned char c[8];//規定顯示段碼數組為8
void display()
{ unsigned char i;
for(i=0;i<8;i++)//顯示8個數據
{
P0=c[i];//送出段碼
P2=i;//送出位選碼
delay(2);
} } 程序96 串口接收程序
#include
//規定串口工作方式1,允許接收。,PCON=0;
//SMOD=0 TMOD= 0x20;
//定時器1 工作在定時方式2
TH1= 0xe6;
//12MHz 1200波特率
//16個
TL1= 0xe6;TR1= 1;
//啟動釘子定時器1
while(1)
//不斷接收數據 { for(i=0;i<9;i++)
{
while(RI==0)
//查詢接收一幀數據完畢否
RI=0;
buf[i]=SBUF;
//從串口接收數據存入數組 buf[]
} } } 程序10 串口發送程序,發送89S51 #include
uint j;SCON= 0x40;
//規定串口工作在方式1,發送數據
PCON=0;
//SMOD=0 TMOD= 0x20;
//定時器1工作在方式2
TH1= 0xe6;
//12MHz 1200波特率 TL1= 0xe6;TR1= 1;
//啟動定時器1
while(1)
//不斷發送數據
{
i=0;
while(trdata[i]!=0x00)
//00結束 { SBUF=trdata[i];
//將數據送到串口發送器SBUF
while(TI==0);
TI=0;
i++;
}
for(j=0;j<50000;j++);//延時8μS×50000 =0.4秒
} }
第四篇:專利意見程序分析
關于專利法對公眾意見的處理程序分析
1984年專利法第41條和第42條規定了異議程序的法律效力:“第四十一條 專利申請自公告之日起三個月內,任何人都可以依照本法規定向專利局對該申請提出異議。專利局應當將異議的副本送交申請人,申請人應當在收到異議副本之日起三個月內提出書面答復;無正當理由逾期不提出書面答復的,該申請即被視為撤回。第四十二條 專利局經審查認為異議成立的,應當作出駁回申請的決定,并通知異議人和申請人。”
在1992年修改專利法時,專利異議的程序都刪去了,所以說從1992年起已經沒有異議程序了,授權前只能提“意見”并僅供參考。2010年新頒布的專利法第48條規定:“自發明專利申請公布之日起至公告授予專利權之日止,任何人均可以對不符合專利法規定的專利申請向國務院專利行政部門提出意見,并說明理由。”
新專利法審查指南中關于對公眾意見的處理的原話摘錄細則: “任何人對不符合專利法規定的發明專利申請向專利局提出的意見,應當存入該申請文檔中供審查員在實質審查時考慮。如果公眾的意見是在審查員發出授予專利權的通知之后收到的,就不必考慮。專利局對公眾意見的處理情況,不必通知提出意見的公眾。”
通過分析我國現行的專利審查公眾意見提交制度,我們不難發現:無論是專利法實施細則還是《專利審查指南》,都沒有關于公眾意見的法律地位、作用,以及社會公眾如何提供意見等更為細致的規定,尤其是對公眾意見的使用情況,審查員是沒有義務告知提出意見的公眾的,也就是說,我國目前的專利審查公眾意見提交制度中是不存在反饋機制的。
從法律層面講,關于專利意見的提交沒有明確的程序規定,具體事宜周一將和專利事務所的專家進行商討。
第五篇:程序法案例分析
程序法案例分析
案例1
2002年9月30日,河南省孟州市質量技術監督局稽查人員對河南省孟州市電業樂萬家有限責任公司經銷的商品進行執法檢查時,發現該公司銷售的“五糧液”酒防偽標簽無暗記標記,涉嫌假冒,遂當場對該公司的177瓶“五糧液”酒進行了封存,并在公證人員的現場公證下提取酒樣品,經中國宜賓五糧液股份有限公司進行鑒定為假冒五糧液產品。之后,孟州市質量技術監督局對剩余的176瓶“五糧液”酒予以登記扣押。由于此案涉及貨值金額和社會影響較大,孟州市質量技術監督局依法將此案移交給該局上級單位焦作市質量技術監督局。同年11月26日,焦作市質量技術監督局向孟州市電業樂萬家有限責任公司送達了《行政處罰告知書》,確認該公司經銷的“五糧液”酒系以假充真產品,已違反了我國《產品質量法》第39條的規定,將依據我國《產品質量法》第50條的規定給予行政處罰。要求該公司在11月28日前將陳述意見送到焦作市質量技術監督局,逾期視為放棄權利;并說明如要求公開聽證,應于收到告知書之日起3日內提出,逾期未提出的,視為放棄權利。孟州市電業樂萬家有限責任公司在收到焦作市質量技術監督局送達告知書的當天就用郵政快件郵送了陳述意見,同時提出公開聽證的申請。可是次日,即11月29日,焦作市質量技術監督局就向該公司送達了(豫焦)質技監罰字[2002]第067號《行政處罰決定書》:
1、責令停止銷售以假充真的“五糧液”酒。
2、沒收176瓶以假充真的“五糧液”酒。
3、并處該公司以假充真“五糧液”酒貨值金額二倍罰款94560元。孟州市電業樂萬家有限責任公司對此處罰決定不服,以焦作市質量技術監督局????為由,向焦作市解放區法院提起行政訴訟。
討論內容:
1.技監局的執法有無疑點?
2.孟州市電業樂萬家有限責任公司的起訴理由可以是什么?
案例2
2002年8月13日,某市星光大酒店接待了一批“特殊客人”。這些客人用他們自帶的秤稱了他們所點的海鮮后,亮出了工作證:市質量技術監督局,并指出,他們的海鮮缺斤少兩。檢查結束后,拿出一張臨時手寫的便條式的檢查證明要求店方簽字,店方覺得事態嚴重,沒有簽。店方解釋稱是廚師抓海鮮時將兩個包廂的海鮮搞混了,并讓廚師親自向檢查人員解釋。8月24日,市質量技術監督局舉行了一個新聞發布會,指出經過明查暗訪,發現多家賓館、酒樓的海鮮缺斤少兩,并指出,按銷售單價計算,星光大酒店一次克扣消費者金額最多。各大媒體對此紛紛作了報道。星光成了眾矢之的。9月15日,星光大酒店以市質量技術監督局認定自己“故意缺斤少兩、克扣消費者”的行為已構成行政侵權為由,要求法院判定其通報批評的行為違法,令其為星光恢復名譽、消除不良影響。
問:此案爭論的焦點是什么?
若是作為被告應進行怎樣的辯論?
若是原告呢?他們會從哪些方面對被告的行為進行起訴呢?
案例三簡易程序的細節
某單位執法人員在處理一件違章案件時,在證據確鑿的前提下對當事人做出處罰決定。當事人對行政處罰告知書的內容無異議,對處罰金額也無異議。并在告知書上寫下“放棄陳述和申辯權利”,并按要求簽下自己的名字,落款日期是某年某月某日。隨后,執法人員給當事人開具行政處罰決定書,當事人也在送達回證簽上名,落款的日期也是某年某月某日。表面上看,這個案件調查取證程序合法,證據確鑿,法律文書制作程序上也并無大的疏漏,案件辦理得天衣無縫。但就是這樣一起看似已辦成鐵案的案件,時過數日,當事人卻一紙訴狀將行政執法機關告上法院,聲稱執法人員在程序上違法。在法庭對質時,行政機關舉充足的證據證明當事人的行為是違法行為,且當事人已在陳述告知筆錄上表示放棄申辯權利。而當事人的辯護律師卻辯稱:?????(由學生添加)案例四執法程序錯誤行政訴訟案
一、案情簡介
1997 年9 月17 日,某省技術監督局接到群眾投訴,稱其所購買的由鴻鑫企業集團有限責任公司開發的怡園公寓商品房面積不足,要求維護購房者利益。根據群眾投訴,省局稽查大隊進行了調查,并委托省房地產計量公正站進行實地測量。測量結果表明,有住戶投訴的4 棟商品房每套實際建筑面積都少于銷售建筑面積,其計量偏差不符合供需雙方事先約定的1 %。省局擬對鴻鑫企業集團有限責任公司進行行政處罰,依照政處罰法,向該公司履行了告知程序,鴻鑫企業集團有限責任公司進行了陳述和申辯,并要求舉行聽證。省局認為符合聽證條件,依法舉行了聽證。1998 年2月23 日下發了行政處罰決定書,決定對鴻鑫企業集團有限責任公司處以28000元的罰款。
鴻鑫企業集團有限公司對上述處罰不服,于1998 年2 月28日以行政處罰認定主體錯誤為由向人民法院提起行政訴訟。值此之時,省局經核查發現,怡園公寓真正的開發商是某房地產開發有限責任公司。該公司是鴻鑫企業集團有限責任公司的集團成員,是經工商登記注冊的有獨立法人資格的企業,是獨立的民事法律主體,其經營行為應自行獨立承擔法律責任,省局主動撤銷了原行政處罰決定,鴻鑫企業集團有限責任公司自愿撤訴,法院裁定撤訴。1998 年1 月26 日,省局更換了行政處罰對象,再次下發了行政處罰決定書。
某房地產開發有限責任公司和法定代表人對第二次行政處罰決定仍然不服,向法院再次提起了行政訴訟,其訴訟理由為?