0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

英创信息技术嵌入式系统设备驱动接口的C#编程

英创信息技术 来源:英创信息技术 作者:英创信息技术 2020-01-15 11:29 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

英创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------------------------------

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 嵌入式主板
    +关注

    关注

    7

    文章

    6107

    浏览量

    36933
收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    ARM嵌入式这样学

    工程师的的终点只有一个,就是真正的技术专家。 嵌入式技术大体上可分为编程语言,内核技术,操作系统
    发表于 12-04 07:48

    CW32嵌入式软件开发的必备知识

    。 能够进行操作系统的任务设计、任务管理等,以满足特定应用的需求。 5 、硬件接口与外设 熟悉常用的硬件接口,如GPIO、串口、SPI、I2C等,并能编写相应的
    发表于 11-28 07:48

    C语言在嵌入式开发中的应用

    语言可以确保实时系统中的任务能够在规定的时间内完成,保证系统的稳定性和可靠性。 4、嵌入式网络编程 随着物联网技术的飞速发展,
    发表于 11-21 08:09

    嵌入式和FPGA的区别

    开发中做出更明智的技术选择。 基本概念解析 嵌入式系统(Embedded System)是一种专用计算机系统,通常包含微处理器/微控制器、存储器和专用外围
    发表于 11-19 06:55

    嵌入式需要掌握哪些核心技能?

    /网络设备)、中断底半部处理,适用于高端嵌入式设备。 企业需求:RTOS是60%岗位的核心要求,Linux开发需求随项目复杂度增长。 4)通信协议与接口 基础协议:UART、SPI、
    发表于 10-21 16:25

    盘点嵌入式就业所需要的技能有哪些?

    ,把握未来的职业机遇。 1.智能汽车行业: - 熟悉嵌入式编程语言,如C/C++、Python等。 - 掌握嵌入式
    发表于 08-11 15:43

    入行嵌入式应该怎么准备?

    知识: 一、C/C++编程C/C++是嵌入式系统
    发表于 08-06 10:34

    嵌入式开发入门指南:从零开始学习嵌入式

    特定功能的计算机系统,广泛应用于智能家居、工业控制、医疗设备、车载系统等领域。 2. 学习嵌入式开发的前置知识熟悉C语言
    发表于 05-15 09:29

    ElfBoard嵌入式教育科普|ADC接口全面解析

    当代信息技术体系中,嵌入式系统接口作为数据交互的核心基础设施,构成了设备互联的神经中枢。基于标准化通信协议与
    的头像 发表于 04-30 09:34 1888次阅读
    ElfBoard<b class='flag-5'>嵌入式</b>教育科普|ADC<b class='flag-5'>接口</b>全面解析

    嵌入式编程设计模式

    嵌入式编程设计模式,介绍如何使用设计模式为嵌入式系统创建高效且优化的C语言设计。 纯分享贴,有需要可以直接下载附件获取完整资料! (如
    发表于 04-15 14:47

    如何成为一名嵌入式软件工程师?

    体系 1)编程语言与工具 C/C++:嵌入式软件开发中最常用的编程语言,因其高效性和对硬件的直接控制能力而备受青睐。 RTOS(实时操
    发表于 04-15 14:37

    嵌入式教育科普|SPI接口全面解析

    在现代科技领域,嵌入式系统接口作为信息交互的核心枢纽,扮演着不可或缺的角色。
    的头像 发表于 03-24 16:45 2038次阅读
    <b class='flag-5'>嵌入式</b>教育科普|SPI<b class='flag-5'>接口</b>全面解析

    嵌入式教育科普|GPIO接口全面解析

    在现代科技领域,嵌入式系统接口作为信息交互的核心载体,发挥着至关重要的作用,各类接口通过预定义的通信协议与标准化规范,可以实现
    的头像 发表于 03-05 11:11 2729次阅读
    <b class='flag-5'>嵌入式</b>教育科普|GPIO<b class='flag-5'>接口</b>全面解析

    为什么嵌入式驱动开发工程师可以拿高薪?

    。 为什么嵌入式驱动开发工程师可以拿高薪? 嵌入式驱动开发工程师属于技术密集型工作,不仅需要深入了解硬件的工作原理,还需掌握各种
    发表于 01-07 16:56

    新手怎么学嵌入式?

    基本的概念。嵌入式系统是一种将计算机技术嵌入到特定设备中的系统,它通常具有特定的功能和有限的资源
    发表于 12-12 10:51