嵌入式系統裝置驅動介面的C#編程

來源:互聯網
上載者:User

       英創ARM9系列嵌入式主板,均預裝了WinCE 5.0、WinCE 6.0作業系統,使用者可使用標準的C/C++或C#進行應用程式的開發。英創的嵌入式板卡一大特色就是提供了豐富的通訊介面,並實現了相應的驅動程式,使用者只需直接調用相應的介面函數即可實現。

 

        在使用C#進行應用程式開發時,由於C#無法使用C++的靜態庫函數,對於一些流式驅動裝置,比如ISA,GPIO,WDT,CAN,SPI,IRQ等,沒有封裝好的庫函數可操作,如果希望控制它們,一種辦法是使用C編寫com組件,在com組件中調用英創提供的相應主板SDK包裡的靜態庫函數;另一種方法是直接調用API函數來訪問裝置驅動。

 

        對於大多數的流式驅動裝置,應用程式使用的一般過程:
        1、通過CreatFile開啟裝置,獲得裝置控制代碼。
        2、使用ReadFile讀取資料,使用WriteFile發送資料,以及使用DeviceIoControl對裝置驅動進行設定、讀寫等操作。
        3、使用完畢,用CloseHandle關閉裝置。

 

        在一般情況下,裝置的專用功能都是通過DeviceIoControl來實現的,因此如何通過C#編程來操作DeviceIoControl尤為重要。本文以英創ARM9嵌入式主板EM9170為例,介紹如何使用DeviceIoControl來操作主板的ISA介面進行讀寫。GPIO,WDT,CAN,SPI,IRQ等其他流式裝置的操作請參看英創開發光碟片內提供的相關常式。

 

1、DeviceIoControl函數的定義
        在wince中核心庫為coredll.dll,相當於window的kernel32.dll和user32.dll。要調用核心庫,首先需要在代碼中添加引用:

        using System.Runtime.InteropServices;

 

        在c#中DeviceIoControl及CreatFiles,CloseHandle函式宣告,及相關變數定義方法如下(該寫法並不固定)。

 

[DllImport('coredll.dll', EntryPoint = 'CreateFile', CharSet = CharSet.Unicode)] 
private static extern int CreateFile(String lpFileName, 
        uint dwDesiredAccess, 
        uint dwShareMode, 
        int lpSecurityAttributes, 
        int dwCreationDisposition, 
        int dwFlagsAndAttributes, 
        int hTemplateFile);

 

[DllImport('coredll.dll', EntryPoint = 'CloseHandle')] 
private static extern int CloseHandle(int hObject);

 

[DllImport('coredll.dll', EntryPoint = 'DeviceIoControl')]
private static extern bool DeviceIoControl(int hDevice,
        uint dwIoControlCode,
        byte[] lpInBuf,
        uint nInBufSize,
        byte[] lpOutBuf,
        uint nOutBufSize,
        ref uint lpBytesReturned,
        uint lpOverlapped);

 

        DeviceIoControl函數參數分析:

參數 定義
hDevice

 

裝置控制代碼
Handle類型
c#中可以對應成int或uint或IntPtr
通過CreatFiles獲得

 

dwIoControlCode

 

驅動控制命令
DWORD類型
C#中可以對應成int或uint

不同裝置該值定義不同,根據該參數lpInBuffer,nInBufSize,lpOutBuf,nOutBufSize參數定義也不同,在c中,該值的宏定義為
#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) )

根據DeviceType, Function, Method, Access轉換而來
在前面代碼中為英創嵌入式主板EM9170的ISA及GPIO的CTL_CODE值定義,比如GPIO輸出控制的CTL_CODE定義
private const uint GPIO_IOCTL_OUT_ENABLE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | ( 3900<< 2) | (METHOD_BUFFERED);

更多的dwIoControlCode值定義和輸入輸出參數定義請參看英創相關常式

 

lpInBuffer

 

輸入BUFFER
LPVOID類型
C#中可以對應成byte[]或ref Struct等,也可以使用unsafe傳遞指標

應用程式傳遞給驅動的資料指標
一般用byte[]的通用性好些,對於需要傳遞結構體指標的地方,可以使用Marshal類的方法拷貝結構體到byte數組內,或者直接根據struct成員值修改byte數組的相應位

 

nInBufSize

 

輸入BUFFER長度
DWORD類型

如果DeviceIoControl操作無輸出BUFFER,一般lpInBuffer為null,nInBufSize為0

 

lpOutBuf

 

輸出BUFFER
LPVOID類型

 

nOutBufSize

 

輸入BUFFER長度
DWORD類型

 

lpBytesReturned

 

操作程式實際返回的位元組數指標
LPDWORD類型
C#中一般對應為ref uint

 

lpOverlapped

 

重疊操作結構
沒有使用,定為uint傳0,或者IntPtr傳IntPtr.Zero

 

 

        在C#調用外部dll時,可以用uint來對應DWORD,用ref uint來對應LPDWORD,用IntPtr來對應各種指標,更通用的,可以直接使用byte[]來對應各種指標。

 

2、DeviceIoControl函數的操作執行個體
        以英創嵌入式主板EM9170的ISA操作為例,以下程式碼封裝括了CreatFile需要的參數定義,ISA操作的IOCTL定義,和ISA的DeviceIoControl操作傳入參數結構體的定義,和封裝DeviceIoControl的兩個ISA函數定義。

 

        主函數操作程式開啟ISA裝置,並操作ISA輸出輸入,最後關閉裝置。

 

private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const int OPEN_EXISTING = 3;
private const int FILE_FLAG_RANDOM_ACCESS = 0x10000000;

//------------------------bsp_drivers----------------------------------

//winioctl
private const uint FILE_DEVICE_BUS_EXTENDER = 0x0000002a;
private const uint METHOD_BUFFERED = 0;
private const uint FILE_ANY_ACCESS = 0;

// ISA IO Control Codes
private const uint ISA_IOCTL_READ_WRITE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3910 << 2) | (METHOD_BUFFERED);

private const uint ISA_IOCTL_BUS_RESET = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3911 << 2) | (METHOD_BUFFERED);

//------------------------bsp_drivers_end------------------------------


[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct ISA_BUS_ACCESS
{
        [FieldOffset(0)]public uint dwCmd; // = 0: Read, = 1: Write

        [FieldOffset(4)]public uint dwSeg; // = 0: ISA_CS0, = 1: ISA_CS1

        [FieldOffset(8)]public uint dwOffset;
        [FieldOffset(12)]public uint dwValue; // only lower byte valid

}

public static int OpenISA_DIO(String DevName)
{
        int handle; 
        handle = CreateFile(DevName,
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, // sharing mode

        0, // security attributes (ignored)

        OPEN_EXISTING, // creation disposition
        FILE_FLAG_RANDOM_ACCESS, // flags/attributes

        0);
        return handle;
}

public static bool CloseISA_DIO(int hISA_DIO)
{
        if (hISA_DIO != 0)
        {
                if (CloseHandle(hISA_DIO) != 0)
                {
                        return false;
                }
        }
        return true;
}

public static bool ISA_Read(int hISA_DIO, int nSeg, uint nOffset, ref byte pRdValue)

{
        ISA_BUS_ACCESS isabus;
        uint lpBytesReturned = 0;

        isabus.dwCmd = 0;
        isabus.dwSeg = (uint)nSeg;
        isabus.dwOffset = nOffset;
        isabus.dwValue = 0;

        int size = Marshal.SizeOf(typeof(ISA_BUS_ACCESS));
        byte[] lpIntBuf = new byte[size];
        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        {
                Marshal.StructureToPtr(isabus, buffer, false);
                Marshal.Copy(buffer, lpIntBuf, 0, size);
        }
        finally
        {
                Marshal.FreeHGlobal(buffer);
        }

        byte[] lpOutBuf = new byte[sizeof(byte)];
        if (!DeviceIoControl(hISA_DIO, // file handle to the driver

                ISA_IOCTL_READ_WRITE, // I/O control code

                lpIntBuf, // in buffer
                (uint)size, // in buffer size
                lpOutBuf, // out buffer
                sizeof(byte), // out buffer size

                ref lpBytesReturned, // pointer to number of bytes returned

                0)) // ignored (=NULL)
        {
                return false;
        }
        pRdValue = lpOutBuf[0];
        return true;

}

public static bool ISA_Write(int hISA_DIO, int nSeg, uint nOffset, byte ucWrValue)

{
        ISA_BUS_ACCESS isabus;
        uint lpBytesReturned = 0;

        isabus.dwCmd = 1;
        isabus.dwSeg = (uint)nSeg;
        isabus.dwOffset = nOffset;
        isabus.dwValue = (uint)ucWrValue;

        int size = Marshal.SizeOf(typeof(ISA_BUS_ACCESS));
        byte[] lpIntBuf = new byte[size];
        IntPtr buffer = Marshal.AllocHGlobal(size);
        try
        {
                Marshal.StructureToPtr(isabus, buffer, false);
                Marshal.Copy(buffer, lpIntBuf, 0, size);
        }
        finally
        {
                Marshal.FreeHGlobal(buffer);
        }

        if (!DeviceIoControl(hISA_DIO, // file handle to the driver

                ISA_IOCTL_READ_WRITE, // I/O control code

                lpIntBuf, // in buffer
                (uint)size, // in buffer size 
                null, // out buffer
                0, // out buffer size
                ref lpBytesReturned, // pointer to number of bytes returned

                0)) // ignored (=NULL)
        {
                return false;
        }
        return true;
}
static void Main(string[] args)
{
        const int ISA_CS1 = 1;
        int hISA;

        hISA = OpenISA_DIO('ISA1:');

        if( hISA == -1 )
        {
                Console.Write('Open ISA_DIO device fail!');
                return;
        }

        bRet = ISA_Write(hISA, ISA_CS1, 0, b);
        bRet = ISA_Read(hISA, ISA_CS1, 0, ref b);

        CloseISA_DIO(hISA);
        return;
}

 

3、傳遞結構體的操作說明
        該常式的ISA_Read,ISA_Write和函數中,DeviceIoControl的參數lpIntBuf需要傳遞一個結構體指標,關於結構體的操作需要按以下步驟。

 

        所以首先需要定義一個和介面定義相同的結構體,在C#中定義與C介面的結構體需要使用StructLayout欄位聲明結構體的大小,和對齊規則。

 

        在每個成員前面用FieldOffset欄位設定該成員在結構體中的位移。

 

[StructLayout(LayoutKind.Explicit, Size = 16)]
public struct ISA_BUS_ACCESS
{
        [FieldOffset(0)]public uint dwCmd; // = 0: Read, = 1: Write

        [FieldOffset(4)]public uint dwSeg; // = 0: ISA_CS0, = 1: ISA_CS1

        [FieldOffset(8)]public uint dwOffset;
        [FieldOffset(12)]public uint dwValue; // only lower byte valid

}

 

        然後設定結構體各成員值,將結構體拷貝到byte[]中,在C#中將結構體拷貝到byte數組需要使用Marshal類。

 

        首先計算結構體大小
        int size = Marshal.SizeOf(typeof(ISA_BUS_ACCESS));

 

        建立一個byte[]
        byte[] lpIntBuf = new byte[size];

 

        用AllocHGlobal申請一塊非託管空間,並獲得該空間的指標
        IntPtr buffer = Marshal.AllocHGlobal(size);

 

        然後用StructureToPtr將結構體拷貝到指標位置,再用Copy將指標位置資料拷貝到byte數組。因為之前申請了記憶體空間,使用try-finally確報用FreeHGlobal釋放空間

try
{
        Marshal.StructureToPtr(isabus, buffer, false);
        Marshal.Copy(buffer, lpIntBuf, 0, size);
}
finally
{
        Marshal.FreeHGlobal(buffer);
}

 

        以上操作包括空間申請與釋放,效率並不高,為提高效率,應當將結構體成員變數值直接賦值到byte數組相應位置,如: 
        ISA_BUS_ACCESS isabus;
        isabus.dwCmd = 1;

 

        修改為
        lpIntBuf[3] = 1; 

 

        注意整數的低位在高地址。

 

4、英創EM9170其他使用DeviceIoControl操作的裝置dwIoControlCode值定義

//------------------------bsp_drivers----------------------------------

//winioctl
private const uint FILE_DEVICE_BUS_EXTENDER = 0x0000002a;
private const uint METHOD_BUFFERED = 0;
private const uint FILE_ANY_ACCESS = 0;

// GPIO IO Control Codes
private const uint GPIO_IOCTL_OUT_ENABLE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | ( 3900<< 2) | (METHOD_BUFFERED);

private const uint GPIO_IOCTL_OUT_DISABLE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3901 << 2) | (METHOD_BUFFERED);

private const uint GPIO_IOCTL_OUT_SET = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3902 << 2) | (METHOD_BUFFERED);

private const uint GPIO_IOCTL_OUT_CLEAR = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3903 << 2) | (METHOD_BUFFERED);

private const uint GPIO_IOCTL_PIN_STATE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3904 << 2) | (METHOD_BUFFERED);

// ISA IO Control Codes
private const uint ISA_IOCTL_READ_WRITE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3910 << 2) | (METHOD_BUFFERED);

private const uint ISA_IOCTL_BUS_RESET = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3911 << 2) | (METHOD_BUFFERED);

//private const uint ISA_IOCTL_REDA_CS1 = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3912 << 2) | (METHOD_BUFFERED);

//private const uint ISA_IOCTL_WRITE_CS1 = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3913 << 2) | (METHOD_BUFFERED);

// SPI IO Control Codes
private const uint CSPI_IOCTL_EXCHANGE = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3030 << 2) | (METHOD_BUFFERED);

// IRQ IO Control Codes
private const uint IOCTL_WAIT_FOR_IRQ = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3920 << 2) | (METHOD_BUFFERED);

private const uint IOCTL_SEND_EOI = (FILE_DEVICE_BUS_EXTENDER << 16) | (FILE_ANY_ACCESS << 14) | (3921 << 2) | (METHOD_BUFFERED);

//------------------------bsp_drivers_end------------------------------

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.