亚洲免费av电影一区二区三区,日韩爱爱视频,51精品视频一区二区三区,91视频爱爱,日韩欧美在线播放视频,中文字幕少妇AV,亚洲电影中文字幕,久久久久亚洲av成人网址,久久综合视频网站,国产在线不卡免费播放

        ?

        Windows下I2C總線的GPIO模擬

        2017-03-24 06:35:35鄒應(yīng)雙
        電腦知識與技術(shù) 2017年1期

        鄒應(yīng)雙

        摘要:簡要介紹了I2C總線操作和基于Windows內(nèi)核模式驅(qū)動的用戶態(tài)I/O端口訪問,分析了Windows平臺下GPIO管腳模擬I2C總線的可行性,講解了編程實現(xiàn)過程,連接I2C接口的安全芯片進行了驗證。

        關(guān)鍵詞:I2C總線;GPIO管腳;Windows;內(nèi)核模式驅(qū)動

        中圖分類號:TP311 文獻標(biāo)識碼:A 文章編號:1009-3044(2017)01-0100-04

        Abstract: I2C bus operations and user-mode I/O port access using Windows kernel mode driver are briefly introduced in this paper. And a feasibility analysis on simulating I2C bus with GPIO pins under Windows is made. Then we do a programming impelmentation to the simulating method, and verify the software by accessing an I2C-interfaced security chip.

        Key words: I2C bus; GPIO pins; Windows; kernel mode driver

        1 背景

        I2C總線是Philips公司推出的兩線式串行總線,用于嵌入式系統(tǒng)連接各種低速外圍設(shè)備,如RTC、EEPROM、傳感器、安全芯片等。許多單片機、嵌入式芯片等都帶有I2C主控器,其采用的操作系統(tǒng)如嵌入式Linux等均帶有I2C驅(qū)動程序,編程中可直接使用。對于不含I2C主控器的芯片,為了滿足定制系統(tǒng)設(shè)計需求,一般也有大量的GPIO管腳,可用于軟件模擬I2C總線。

        Intel公司的X86系列CPU和配套的橋接芯片,除了用于桌面PC,還廣泛用于網(wǎng)關(guān)、收銀、視頻等各種嵌入式服務(wù)器。這類服務(wù)器一般采用Windows Server操作系統(tǒng),不帶有I2C主控器或不提供I2C驅(qū)動程序。為了實現(xiàn)軟件授權(quán)和保護,這類嵌入式服務(wù)器首先會選擇管腳少、性價比高的I2C接口的安全芯片。為了滿足這種需求,一種方法是采用USB轉(zhuǎn)I2C的專用芯片,但這將增加硬件成本和軟件復(fù)雜度。

        基于I2C總線的簡單性,可選用橋接芯片的GPIO管腳來模擬I2C主控器。GPIO管腳通過X86的I/O指令即可完全控制;但普通應(yīng)用程序在Windows下無法直接訪問I/O端口,可通過內(nèi)核模式驅(qū)動程序來實現(xiàn)。在Windows平臺下通過GPIO管腳模擬I2C總線,將是一種簡單有效、低成本的解決方法。本文就這個模擬過程進行探討。

        2 I2C總線的讀寫

        I2C總線通過時鐘和數(shù)據(jù)兩根線即可實現(xiàn)完善的同步數(shù)據(jù)傳輸。當(dāng)發(fā)送數(shù)據(jù)時,一個設(shè)備作為主機,另一個設(shè)備作為從機。主設(shè)備為數(shù)據(jù)傳輸產(chǎn)生時鐘信號。I2C通訊協(xié)議要求在時鐘線(SCL, Serial Clock Line)處于低電平時,數(shù)據(jù)線(SDA, Serial Data Line)才能變化。協(xié)議中每個從設(shè)備都有一個地址,會一直監(jiān)視總線上的主設(shè)備要初始化數(shù)據(jù)傳輸時發(fā)出的地址并匹配。

        總線的工作流程如下:

        空閑:當(dāng)總線上沒有數(shù)據(jù)通訊發(fā)生時,SCL和SDA通過上拉電阻呈高電平。

        開始:SCL為高時,SDA由高變低,這時數(shù)據(jù)傳輸開始。

        地址:主設(shè)備發(fā)送地址信息,包含7位的從設(shè)備地址和1位的讀寫位(表明數(shù)據(jù)流的方向)。發(fā)送完一個字節(jié)后,從設(shè)備會發(fā)送一位的認(rèn)可位(ACK)。

        數(shù)據(jù):根據(jù)方向位,數(shù)據(jù)在主設(shè)備和從設(shè)備之間傳輸。數(shù)據(jù)一般以8位傳輸,高位在前。接收器上用一位的ACK表明一個字節(jié)收到了。傳輸可被終止或重新開始。

        停止:當(dāng)SCL為高時,SDA由低變高,這時數(shù)據(jù)傳輸結(jié)束,總線重新進入空閑狀態(tài)。

        一次完整的數(shù)據(jù)傳輸時序如圖1所示。

        標(biāo)準(zhǔn)I2C總線的傳輸速率是100KHz,通過線與邏輯實現(xiàn)慢速設(shè)備等待。I2C總線的這些特性允許主設(shè)備的功能通過兩個GPIO管腳模擬而實現(xiàn)。

        3 Windows下的I/O端口訪問

        端口I/O指令允許X86 CPU與系統(tǒng)中的其他硬件設(shè)備通信。對于硬件設(shè)備的低層次直接控制,C函數(shù)_inp()和_outp()(用X86處理器的IN和OUT指令實現(xiàn))允許從端口讀入或向一個端口寫。但在Windows應(yīng)用程序中插入_inp()或者_outp(),將導(dǎo)致特權(quán)指令異常消息,并給出終止或調(diào)試出錯應(yīng)用程序的選擇。Windows的體系結(jié)構(gòu)決定了應(yīng)用程序不能直接通過IN和OUT指令訪問硬件。否則,應(yīng)用程序可以關(guān)閉中斷、破壞顯示或驅(qū)動器等硬件設(shè)備,危及系統(tǒng)的穩(wěn)定性。所以,通過內(nèi)核模式驅(qū)動程序間接訪問I/O端口是Windows下訪問硬件資源的唯一途徑。

        實現(xiàn)對I2C主控器的模擬,只需要簡單的I/O訪問即可實現(xiàn)。如果編寫完整的內(nèi)核模式的I2C驅(qū)動程序,將涉及復(fù)雜的、花費大量時間的Windows內(nèi)核模式驅(qū)動驅(qū)動程序的開發(fā)和調(diào)試工作。編寫最簡驅(qū)動實現(xiàn)I/O端口訪問,封裝好用戶態(tài)訪問的接口,將I2C實現(xiàn)代碼放在用戶態(tài),將極大地簡化開發(fā)工作,同時增加二次開發(fā)利用的靈活性。

        這樣通過內(nèi)核模式驅(qū)動程序?qū)崿F(xiàn)I/O訪問的副作用是每一次I/O操作都要通過Windows的I/O子系統(tǒng)發(fā)送請求,需要花費數(shù)千個時鐘周期。但這個時間成本和100KHz的慢速I2C的一個位周期相當(dāng),對于數(shù)據(jù)傳輸量不大的應(yīng)用,在性能上可接受。

        4 編程實現(xiàn)

        本文的目標(biāo)硬件平臺為Intel Core i3-4330 CPU、Intel DH82H81橋接芯片、Maxim DS28C22安全芯片。橋片和安全芯片的連接如圖2所示。

        本文的目標(biāo)軟件平臺是64位的Windows Server 2008,開發(fā)平臺是Windows 10 專業(yè)版,選用WDK(Windows Driver Kit) 7.1和Visual Studio 2015專業(yè)版。通過查詢方式實現(xiàn)I2C讀寫,驅(qū)動層提供I/O端口訪問功能,pioctl.dll庫封裝驅(qū)動成類似于IN/OUT指令的接口,i2c.dll實現(xiàn)I2C讀寫函數(shù),提供給上層做應(yīng)用開發(fā)。軟件層次結(jié)構(gòu)如圖3所示。

        下面按自底向上的順序簡單介紹各層次的實現(xiàn)源碼。

        4.1 內(nèi)核模式驅(qū)動

        和Linux驅(qū)動的開發(fā)相比,Windows驅(qū)動開發(fā)的門檻要高一些,首先需安裝WDK,了解其中的核心態(tài)函數(shù),熟悉WDM、WDF等驅(qū)動程序框架。

        WDK中提供了大量的樣例驅(qū)動供驅(qū)動開發(fā)者參考??紤]到我們的驅(qū)動只需提供X86的IN和OUT指令的訪問接口,特選擇WDK樣例中源碼最簡單的src/general/ioctl/wdm為基礎(chǔ),命名為pioctl,并對驅(qū)動源碼中的函數(shù)名等做適當(dāng)重命名,添加上I/O端口訪問的代碼,即實現(xiàn)了本驅(qū)動。這個開發(fā)過程不需要對Windows驅(qū)動開發(fā)有較深入的了解。

        本驅(qū)動程序的驅(qū)動加載和卸載、設(shè)備打開和關(guān)閉等例程無新加代碼,不是本文的重點,下面僅對I/O端口操作相關(guān)的代碼做說明。

        NTSTATUS PioctlDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp)

        {

        PIO_STACK_LOCATION irpSp;

        NTSTATUS ntStatus = STATUS_SUCCESS;

        ULONG inBufLength, outBufLength;

        ULONG dataBufSize;

        PULONG pIOBuffer;

        ULONG nPort;

        irpSp = IoGetCurrentIrpStackLocation(Irp);

        inBufLength = irpSp->Parameters.DeviceIoControl.InputBufferLength;

        outBufLength = irpSp->Parameters.DeviceIoControl.OutputBufferLength;

        switch (irpSp->Parameters.DeviceIoControl.IoControlCode)

        { // 檢查用戶態(tài)參數(shù)

        case IOCTL_PIOCTL_WRITE_PORT_ULONG:

        dataBufSize = sizeof(ULONG);

        if (inBufLength < (sizeof(ULONG) + dataBufSize)) {

        ntStatus = STATUS_INVALID_PARAMETER;

        goto End;

        }

        break;

        case IOCTL_PIOCTL_READ_PORT_ULONG:

        dataBufSize = sizeof(ULONG);

        if (inBufLength != sizeof(ULONG) || outBufLength < dataBufSize) {

        ntStatus = STATUS_INVALID_PARAMETER;

        goto End;

        }

        break;

        default:

        ntStatus = STATUS_INVALID_PARAMETER;

        goto End;

        }

        pIOBuffer = (PULONG)Irp->AssociatedIrp.SystemBuffer;

        nPort = *pIOBuffer; // I/O端口號

        switch ( irpSp->Parameters.DeviceIoControl.IoControlCode )

        { // 判定I/O控制碼

        case IOCTL_PIOCTL_READ_PORT_ULONG: // IND

        *(PULONG)pIOBuffer = READ_PORT_ULONG((PULONG)((ULONG_PTR)nPort));

        pIrp->IoStatus.Information = dataBufSize; // 讀取的字節(jié)數(shù)

        break;

        case IOCTL_PIOCTL_WRITE_PORT_ULONG: // OUTD

        pIOBuffer++;

        WRITE_PORT_ULONG((PULONG)((ULONG_PTR)nPort), *(PULONG)pIOBuffer);

        Irp->IoStatus.Information = dataBufSize; // 寫的字節(jié)數(shù)

        break;

        default:

        Irp->IoStatus.Information = 0;

        ntStatus = STATUS_INVALID_DEVICE_REQUEST;

        break;

        }

        End:

        Irp->IoStatus.Status = ntStatus;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return ntStatus;

        }

        4.2 用戶態(tài)I/O端口讀寫接口

        為了應(yīng)用程序的開發(fā)方便,實現(xiàn)了pioctl.dll庫,以負(fù)責(zé)自動動態(tài)加載卸載驅(qū)動程序、提供I/O端口的用戶態(tài)訪問接口。

        1)DLL入口函數(shù)

        BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID)

        {

        switch (ul_reason_for_call)

        {

        case DLL_PROCESS_ATTACH:

        if (pioctl_init() < 0) // 利用SCM函數(shù)加載驅(qū)動; 打開設(shè)備文件

        return FALSE;

        break;

        case DLL_PROCESS_DETACH:

        pioctl_deinit(); // 關(guān)閉設(shè)備文件; 利用SCM函數(shù)卸載驅(qū)動

        break;

        }

        return TRUE;

        }

        2)端口訪問函數(shù)

        PIOCTL_API unsigned long pioctl_inpd(unsigned short port)

        {

        ULONG PortNumber = (ULONG)port;

        ULONG Data;

        ULONG bytesReturned;

        BOOL bRc;

        bRc = DeviceIoControl(s_hDevice, (DWORD)IOCTL_PIOCTL_READ_PORT_ULONG,

        &PortNumber, sizeof(PortNumber), &Data, sizeof(Data), &bytesReturned, NULL);

        if (!bRc) {

        fprintf(stderr, "Error in DeviceIoControl : %d\n", GetLastError());

        return -1;

        }

        return Data;

        }

        pioctl_outpd()類似,不再列舉出。

        4.3 I2C讀寫函數(shù)

        本文采用查詢方式實現(xiàn)I2C讀寫函數(shù)。硬件上用DH82H81的GPIO8模擬SCL、GPIO15模擬SDA,封裝成i2c.dll庫。這兩個管腳為專用GPIO腳,通過GP_IO_SEL和GP_LVL兩個端口即可控制其I/O方向和電平值。下面以I2C操作中的啟動和發(fā)送字節(jié)為例,講解其實現(xiàn)。其他操作的實現(xiàn)過程類似,不再贅述。

        1)初始化函數(shù)

        #define SCL GPIO8

        #define SDA GPIO15

        void i2c_init(void)

        {

        gpioDir = pioctl_inpd(GP_IO_SEL) & ~((1 << SCL) | (1 << SDA)); // 0:OUTPUT

        pioctl_outpd(GP_IO_SEL, gpioDir); // 設(shè)置GPIOs的I/O方向

        gpioVal = pioctl_inpd(GP_LVL) | ((1 << SCL) | (1 << SDA));

        pioctl_outpd(GP_LVL, gpioVal); // 設(shè)置GPIOs的電平

        }

        2)端口的位操作函數(shù)

        int gpio_in(int gpio_num)

        { // 讀取GPIOs輸入腳

        if ((gpioDir & (1<

        gpioDir |= (1<

        pioctl_outpd(GP_IO_SEL, gpioDir);

        }

        return (pioctl_inpd(GP_LVL) & (1<

        }

        void gpio_out(int gpio_num, int level)

        { // 寫GPIOs輸出腳

        if ( gpioDir & (1<

        gpioDir &= ~(1<

        pioctl_outpd(GP_IO_SEL, gpioDir);

        }

        gpioVal = pioctl_inpd(GP_LVL) & ~(1<

        if (level)

        gpioVal |= 1<

        pioctl_outpd(GP_LVL, gpioVal); // 設(shè)置GPIO的電平

        }

        3)I2C讀寫函數(shù)

        #define i2c_scl(level) gpio_out(SCL, level)

        #define i2c_sda(level) gpio_out(SDA, level)

        #define i2c_sda_in() gpio_in(SDA)

        void i2c_start(void)

        { // 當(dāng)SCL為高電平時,SDA發(fā)生由高到低的跳變

        i2c_scl(1);

        i2c_sda(0);

        i2c_scl(0);

        }

        int i2c_send_data(unsigned char octet)

        { // 發(fā)送一個字節(jié)

        int i, ack;

        for(i=0x80; i>0; i>>=1) {

        i2c_sda(octet & i ? 1 : 0);

        i2c_scl(1);

        i2c_scl(0);

        }

        i2c_sda(1); // 發(fā)送器件釋放SDA線

        i2c_scl(1);

        ack = i2c_sda_in(); // 讀取低電平有效的ACK位

        scl(0); // 實現(xiàn)了了ACK

        return (ack); // 返回ACK位

        }

        4.4 驅(qū)動加載和調(diào)試

        由于目標(biāo)軟件平臺為64位系統(tǒng),pioctl.sys驅(qū)動相應(yīng)編譯成64位,需要禁用Windows Server 2008的數(shù)字簽名,編譯的驅(qū)動才能加載。

        在pioctl.sys驅(qū)動中通過KdPrint()宏輸出調(diào)試信息,通過WinDbg工具捕獲調(diào)試信息,以和printf函數(shù)類似的方式調(diào)試代碼。

        5 結(jié)束語

        本文基于GPIO管腳的I2C操作模擬方法,在圖2所示的采用Windows Server 2008的目標(biāo)平臺上做測試,實現(xiàn)了和D28C22安全芯片的可靠通信,驗證了本方法的正確性。該方法的原理可供類似的采用Intel X86方案的產(chǎn)品設(shè)計參考,以有效節(jié)省采用轉(zhuǎn)接芯片的成本、降低軟件開發(fā)的難度。

        這種用戶態(tài)I/O端口訪問的硬件模擬,花費較高的CPU時間成本,對于高速的簡單I/O設(shè)備訪問,有較大的局限性。但基于本方法,可在用戶態(tài)將硬件操作代碼調(diào)試好,再直接封裝到內(nèi)核模式驅(qū)動程序中,將極大地降低開發(fā)特定Windows設(shè)備驅(qū)動的難度。

        參考文獻:

        [1] 田磊, 宋圓方. 基于Windows CE的IIC設(shè)備驅(qū)動的實現(xiàn)[J]. 西安郵電學(xué)院學(xué)報, 2008(1): 126-128.

        [2] 蔡純潔, 邢武. PIC 16/17 單片機原理與實現(xiàn)[M]. 合肥: 中國科學(xué)技術(shù)大學(xué)出版社, 1997.

        [3] 保拉?湯姆林森. Windows NT/2000編程實踐[M]. 北京: 中國電力出版社, 2001.

        [4] 張佩, 馬勇, 董鑒源. 竹林蹊徑——深入淺出Windows驅(qū)動開發(fā)[M]. 北京: 電子工業(yè)出版社, 2011.

        日本不卡一区二区高清中文| 天堂新版在线资源| 久久久国产一区二区三区四区小说 | 夜夜爽夜夜叫夜夜高潮| 亚洲av无码之国产精品网址蜜芽| 午夜免费福利在线观看| 97中文字幕一区二区| 日本人妻免费一区二区三区| 狠狠色婷婷久久综合频道日韩| 国产激情对白一区二区三区四| 日韩精品人妻少妇一区二区| 久久婷婷综合激情五月| 芒果乱码国色天香| 国产欧美乱夫不卡无乱码| 蜜桃伦理一区二区三区| 91精品久久久老熟女91精品 | 国产人妻久久精品二区三区特黄| 久久国产热精品波多野结衣av | 欧美aⅴ在线| 日本女优中文字幕在线观看| 国产天堂av在线一二三四| 色橹橹欧美在线观看视频高清| 中文字幕少妇AV| 国产一区二区三区精品成人爱| 久久黄色视频| 无码三级在线看中文字幕完整版| 亚洲VR永久无码一区| 国产高清一区二区三区三州| 久久久亚洲av成人网站 | 2019日韩中文字幕mv| 国产黄页网站在线观看免费视频| 亚洲精品熟女乱色一区| 青青草大香蕉视频在线观看| 亚洲综合精品伊人久久| av中文字幕少妇人妻| 国产91极品身材白皙| 久久久www成人免费毛片| 欧美日韩在线观看免费| 国产精品丝袜美腿诱惑| 久久精品中文字幕| 亚洲成成品网站源码中国有限公司|