在网上看到一片文章,很好,转了过来。
前些天本人苦于vs2005下开发pwm的控制软件,经过几天的努力,现已成功写出了pwm的控制软件,现总结如下
开发环境:mini2440+vs2005+vs2005 sp1+mini2440 SDK
没有安装wince6.0及PB
一、学习相关的知识
1、流驱动的相关知识
1.1 流驱动的主要接口
Wince流驱动方式与c++中开发dll很相相似,主要有以下几个接口:
流接口函数 |
功能描述 |
调用流接口对象 |
XXX_Init() |
初始化设备 |
系统设备管理器 |
XXX_Denit() |
卸载设备 |
系统设备管理器 |
XXX_Open() |
打开设备进行读写操作 |
文件API函数CreateFile() |
XXX_Close() |
关闭设备 |
文件API函数CloseHandle() |
XXX_Read() |
读取设备数据 |
文件API函数ReadFile() |
XXX_Write() |
向设备写数据 |
文件API函数WriteFile() |
XXX_IOControl() |
对设备进行各种操作 |
文件API函数DeviceIOControl() |
XXX_Seek() |
移动设备数据的指针位置 |
文件API函数SetFilePointer() |
XXX_PowerDown() |
使设备休眠 |
系统电源管理器 |
XXX_PowerUp() |
恢复设备电源 |
系统电源管理器 |
图1 流驱动主要接口
1.2 流驱动工作及调用过程
1)加载驱动。在当系统启动时,设备管理器搜寻注册表的HKEY_LOCAL_MACHINE\Driver\BuiltIn键下面的子键,并逐一加载子键下的每个驱动,此过程叫BusEnum。
2)设备管理器从注册表的dll键值中获取驱动程序所在的DLL文件名。
3)设备管理器调用LoadDriver()函数把DLL加载到自己的虚拟地址空间内。
4)设备管理器在注册表的HKEY_LOCAL_MACHINE\Driver\Active下面,记录所有已经加载的驱动程序。
5)设备管理器调用驱动中的XXX_Init()函数。
6)在XXX_Init()中,通常对硬件进行一些基本的初始化操作。通过以上6步,流接口驱动被成功加载。
7)应用程序使用该设备。首先它调用CreateFile()打开设备。CreateFile()是在FileSys.exe中实现的。但是FileSys.exe只作简单判断,如果发现打开的设备驱动程序而不是一个文件,那么就重新把主动权交还给设备管理器。
8)设备管理器调用驱动程序中的XXX_Open()函数打开设备。在XXX_Open()中,驱动程序可能会对硬件进行一些额外的初始化工作,使硬件进入工作状态。
9)XXX_Open()函数把打开设备的结果返回给设备管理器。
10)设备管理器把XXX_Open()返回的结果,再返回给应用程序的CreateFile()函数调用。通过7-10步,设备已被成功打开,至此就可以对设备进行读写和控制操作。
11)应用程序使用第7步CreateFile()函数返回的句柄作为 ReadFile() / WriteFile()的第一个参数,向设备发送读请求。同样ReadFile() / WriteFile()要经过FileSys.exe转发给设备管理器。
12)设备管理器调用驱动程序中的XXX_Read() / XXX_Write() 函数,读取设备的数据信息或向设备写信息。
13)在流驱动程序中,XXX_Read() / XXX_Write() 函数可与硬件交互,从硬件中读取必要的信息或向硬件写必要的信息。然后返回给设备管理器,再返回给应用程序。
当应用程序不再使用该设备时,它可调用CloseHandle()将设备关闭。当系统不再使用设备时,应用程序可调用DeactivateDevice()函数把该驱动程序卸载
通过以上的过程我们已知道了流驱动相关函数的调用过程及wince的一些硬件控制知识
下面我们再了解一下 wince的系统架构
2、Windows Embedded CE 6.0的系统架构的模块组成
图2 WINCE6.0 系统架构
从这个架构图可以发现wince的应用程序(windows CE Application)及其他的一些服务都是通过win32 CE API函数来支持,通过API函数与内核空间对话,达到控制硬件的目的。
我们发现win32 CE APIS,中有coredll,winsock,commctrl ,commdlg,其中coredll就是我们驱动PWM及一些其他的硬件所要用到的。
3、了解coredll.dll的相关知识
Coredll对于wince来说,就相当于win32中的kernel32.dll,我们要调用驱动中的一些:函数,就得通过他来调用。
Coredll有很多函数,但我们驱动PWM只用以下几个:
coredll接口函数 |
功能描述 |
CreateFile() |
打开设备进行读写操作 |
CloseHandle() |
关闭设备 |
ReadFile() |
读取设备数据 |
WriteFile() |
向设备写数据 |
DeviceIOControl() |
对设备进行各种操作 |
SetFilePointer() |
移动设备数据的指针位置 |
图3 coredll中的API函数
4、综合分析
通过以上3部分学习,我们知道了设备是通过流驱动来操作,而流驱动测通过调用coredll中的一些函数来操作,主要操作就是图3中的一些函数。
二、c#中编写PWM操作程序
1、程序流程
1.1声明coredll中的相关函数
[DllImport("coredll.dll")]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("coredll.dll")]
public static extern bool DeviceIoControl(
IntPtr hDevice,
UInt32 dwIoControlCode,
Byte[] lpInBuffer,
UInt32 nInBufferSize,
Byte[] lpOutBuffer,
UInt32 nOutBufferSize,
UInt32 lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("coredll.dll")]
public static extern bool CloseHandle(IntPtr hDevice);
以上声明了CreateFile,DeviceIoControl,CloseHandle三个函数,注意要用coredll还要声明引用空间
using System.Runtime.InteropServices;
还有一些常数
// Constant of
const UInt32 OPEN_EXISTING = 3;
const UInt32 GENERIC_READ = 0x80000000;
const UInt32 GENERIC_WRITE = 0x40000000;
const Int32 INVALID_HANDLE_VALUE = -1;
还有一些操作的指令,这些指令可查看c:\WINCE600\platform\mini2440\src\driver\pwm\pwm.h,这是BSP中的源程序文件
const UInt32 PWM_STOP = 1;
const UInt32 PWM_SET_FEQ = 2;
1.2应用以上函数来控制PWM
private IntPtr hPort;
hPort = CreateFile("PWM1:", GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); //打开设备
DeviceIoControl(hPort, PWM_SET_FEQ, sBuf, 4, null, 0, 0, IntPtr.Zero); //设置PWM频率
CloseHandle(hPort); //关闭PWM
用以上3个函数就可以进行PWM的常规操作了
2、完整的程序
2.1程序功能说明
本程序是一个可设置PWM频率,可控制PWM开关时间程序
2.2程序界面
2.3程序
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Threading;
namespace pwm_test
{
public partial class Form1 : Form
{
private IntPtr hPort;
// Constant from PWM Driver
//查看c:\WINCE600\platform\mini2440\src\driver\pwm\pwm.h
const UInt32 PWM_STOP = 1;
const UInt32 PWM_SET_FEQ = 2;
const UInt32 PWM_SET_DUTY = 3;
[DllImport("coredll.dll")]
public static extern IntPtr CreateFile(
String lpFileName,
UInt32 dwDesiredAccess,
UInt32 dwShareMode,
IntPtr lpSecurityAttributes,
UInt32 dwCreationDisposition,
UInt32 dwFlagsAndAttributes,
IntPtr hTemplateFile);
[DllImport("coredll.dll")]
public static extern bool DeviceIoControl(
IntPtr hDevice,
UInt32 dwIoControlCode,
Byte[] lpInBuffer,
UInt32 nInBufferSize,
Byte[] lpOutBuffer,
UInt32 nOutBufferSize,
UInt32 lpBytesReturned,
IntPtr lpOverlapped);
[DllImport("coredll.dll")]
public static extern bool CloseHandle(IntPtr hDevice);
// Constant of
const UInt32 OPEN_EXISTING = 3;
const UInt32 GENERIC_READ = 0x80000000;
const UInt32 GENERIC_WRITE = 0x40000000;
const Int32 INVALID_HANDLE_VALUE = -1;
public bool pwm_ena = true;
public int tpwmon;
public int tpwmoff;
public bool pwm_level=false ;
//函数:FreqSet 设置PWM频率
private void FreqSet(UInt32 value)
{
byte[] sBuf = new byte[4];
UInt32 sInput;
// Check Open Device already
if (hPort != (IntPtr)INVALID_HANDLE_VALUE)
{
sInput = 50;
// Convert sInput(UInt32) to sBuf(Array of Byte)
BitConverter.GetBytes(sInput).CopyTo(sBuf, 0);
// Set Duty[0-99] to PWM Device
DeviceIoControl(hPort, PWM_SET_DUTY, sBuf, 4, null, 0, 0, IntPtr.Zero);
sInput = value;
// Convert sInput(UInt32) to sBuf(Array of Byte)
BitConverter.GetBytes(sInput).CopyTo(sBuf, 0);
// Set Freq to PWM Device
DeviceIoControl(hPort, PWM_SET_FEQ, sBuf, 4, null, 0, 0, IntPtr.Zero);
}
}
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (pwm_ena)
{
button1.Text = "Stop";
pwm_ena = false;
tpwmoff =Convert .ToInt32 (offtime.Value);
tpwmon =Convert .ToInt32 ( ontime.Value);
timer1.Interval = tpwmon;
timer1.Enabled = true;
FreqSet(Convert.ToUInt32(pwm_freq.Value ));
pwm_level = true;
}
else
{
button1.Text = "Start";
FreqSet (Convert .ToUInt32 (0));
timer1 .Enabled =false ;
pwm_ena =true ;
}
}
private void Form1_Closed(object sender, EventArgs e)
{
if (hPort != (IntPtr)INVALID_HANDLE_VALUE)
// Close PWM Device
CloseHandle(hPort);
}
private void Form1_Load(object sender, EventArgs e)
{
// Open PWM Device
hPort = CreateFile("PWM1:", GENERIC_READ | GENERIC_WRITE, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero);
if (hPort == (IntPtr)INVALID_HANDLE_VALUE)
{
MessageBox.Show("Open PWM1 Driver Fail");
}
}
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Enabled = false;
if (pwm_level)
{
pwm_level = false;
FreqSet(Convert.ToUInt32(0 ));
timer1.Interval = tpwmoff ;
timer1.Enabled = true;
}
else
{
pwm_level = true;
FreqSet(Convert.ToUInt32(pwm_freq.Value ));
timer1.Interval = tpwmon ;
timer1.Enabled = true;
}
}
}
}