最近金日隧道广告系统的通信系统要升级,用Moxa的DA66x设备取代原先的Moxa5630通信模块,由于DA66x内嵌了WinCE5.0系统,系统的功能可以有很大的发挥余地。
DA66x这款产品功能很强,可以说WinCE5.0在工业通信领域被用到了极致(2个10/100M的Tcp/IP通信口,16+1个高波特率串口)我更为喜欢的是,它给PC机提供了多种渠道去控制该设备,内建了Web服务器,可以通过网页进行各种配置,开启了ftp服务,可以通过ftp上传下载文件,此外我更为喜欢的是提供了Pocket CMD 5.0(如下图),可以通过超级终端进行控制(和DOS界面没有什么区别,很早的时候,我开发AB PLC的一个接口板程序时,就是用到类似的东西)。
通过Moxa提供的SDK,我还是学了不少.net的技术,比如静态构造函数的使用,以前是不知道的,所以以后直接使用静态函数的时候,知道在哪里初始化变量了(题外话:在msdn查静态构造函数的时候,意外的发现在VS2005目录里有好东西,VS2005安装目录/VC#/Specifications/2052下面有两个中文版的C#规范的word文件,很是详细,值得一看)。此外还学到了向API函数传结构体的技术,如:
[StructLayout(LayoutKind.Sequential)] //该结构体变量内存连续存储
public struct LCM_POS
{
[MarshalAs(UnmanagedType.U1)] //无符号字节变量
public byte x;
[MarshalAs(UnmanagedType.U1)]
public byte y;
}
mxdevice.LCM_POS pos = new mxdevice.LCM_POS();
pos.x = x;
pos.y = y;
IntPtr lpPos = Marshal.AllocHGlobal(Marshal.SizeOf(pos)); //分配空间
Marshal.StructureToPtr(pos, lpPos, false); //获取结构体指针
mxdevice1.IoControl(mxdevice.IOCTL_LCM_GOTO_XY, lpPos);
有时间这方面的技术,我在专门论述。
由于DA66x没有提供触摸屏,只是提供了一个16*2的LED显示屏和4个按钮,所以功能控制比较麻烦,值得提出的时,moxa公司提供的SDK还是有bug的,比如下面的一个函数:
public bool IoControl(uint code, ref byte input, ref byte output)
{
int num1 = 0;
byte num2 = 0;
if (!DeviceIoControl(hLCMPort, IOCTL_KEYPAD_GET_DOWN_STATE, (void*)0, 0, pkey, sizeof(UCHAR), &dwBackSize, NULL))
}
上面的output参数竟然没用,幸好moxa的dll没有加密可以获得源码,否则我修改起来就麻烦了。
此外DA66x系列的设备和UC7400的设置共用一个.net开发包,并且DA66x没有LCD显示和按键的控制(该需求反馈到Moxa后,据说近期就要发布相关代码),还好,moxa发过来LCM(EVC程序)的源码,可以把相关的EVC代码转换为.net代码。
Moxa最大的设计败笔(个人认为)就是按钮和LCD的驱动设备为同一个“LCM1:”,按钮和LCD就不能独立设计,否则通过线程不断检查按钮状态时会和显示冲突。没有办法,只好设计在一起了,我自己开发一个DA66x的.net驱动,效果还不错(有时间在设计一个二级菜单和多页文本显示的功能)。此外我还是看看moxa提供的开发包是怎么设计的吧,说不定又学到不少东西。
下面是我自己开发的相关代码,有类似需求的朋友可以参考一下:
//
LCD显示&按键控制
public
class
LCMKEY
{
private
mxdevice mxdevice1;
private
Thread trdKeypadlisten;
private
bool
bStop;
public
static
int
Key
=
0
;
//
按键信息
public
LCMKEY()
{
}
public
void
Init()
{
mxdevice1
=
new
mxdevice(
"
LCM1:
"
);
mxdevice1.Open();
this
.bStop
=
false
;
this
.trdKeypadlisten
=
new
Thread(
new
ThreadStart(
this
.ThreadTask));
this
.trdKeypadlisten.Priority
=
ThreadPriority.Normal;
this
.trdKeypadlisten.Start();
}
public
void
Exit()
{
mxdevice1.Close();
try
{
if
(
!
this
.bStop)
{
this
.bStop
=
true
;
}
this
.trdKeypadlisten
=
null
;
}
catch
{
return
;
}
}
~
LCMKEY()
{
Exit();
}
//
光标控制
public
void
Cursor(
bool
bFlag)
{
byte
num
=
0
;
mxdevice1.IoControl(bFlag
?
mxdevice.IOCTL_LCM_CURSOR_ON : mxdevice.IOCTL_LCM_CURSOR_OFF,
ref
num);
//
mxdevice.IOCTL_LCM_BLINK_ON
}
//
清屏
public
void
Clear()
{
byte
num
=
0
;
mxdevice1.IoControl(mxdevice.IOCTL_LCM_CLEAR,
ref
num);
}
//
光标定位
public
void
GotoXY(
byte
x,
byte
y)
{
mxdevice.LCM_POS pos
=
new
mxdevice.LCM_POS();
pos.x
=
x;
pos.y
=
y;
IntPtr lpPos
=
Marshal.AllocHGlobal(Marshal.SizeOf(pos));
Marshal.StructureToPtr(pos, lpPos,
false
);
mxdevice1.IoControl(mxdevice.IOCTL_LCM_GOTO_XY, lpPos);
}
//
文本显示
public
void
Show(
string
text,
byte
x,
byte
y)
{
int
num
=
0
;
GotoXY(x, y);
mxdevice1.WriteDev(Encoding.Default.GetBytes(text), text.Length,
out
num);
}
//
文本显示
public
void
Show(
string
text1,
string
text2)
{
//
清屏
Clear();
//
写第一行
Show(text1,
0
,
0
);
//
写第二行
Show(text2,
0
,
1
);
}
//
按钮控制
protected
virtual
void
OnKeyClick(
int
e)
{
if
(
this
.KeyClick
!=
null
)
{
this
.KeyClick(
this
, e);
Key
=
e;
}
}
//
按钮监控
private
void
ThreadTask()
{
byte
pTemp
=
0
, pKey
=
0
;
do
{
if
(mxdevice1.IoControl(mxdevice.IOCTL_KEYPAD_GET_MENU_STATE,
ref
pTemp,
ref
pKey))
{
if
(pKey
==
1
)
{
OnKeyClick(
1
);
}
}
if
(mxdevice1.IoControl(mxdevice.IOCTL_KEYPAD_GET_UP_STATE,
ref
pTemp,
ref
pKey))
{
if
(pKey
==
1
)
{
OnKeyClick(
2
);
}
}
if
(mxdevice1.IoControl(mxdevice.IOCTL_KEYPAD_GET_DOWN_STATE,
ref
pTemp,
ref
pKey))
{
if
(pKey
==
1
)
{
OnKeyClick(
3
);
}
}
if
(mxdevice1.IoControl(mxdevice.IOCTL_KEYPAD_GET_SELE_STATE,
ref
pTemp,
ref
pKey))
{
if
(pKey
==
1
)
{
OnKeyClick(
4
);
}
}
Thread.Sleep(
100
);
}
while
(
!
this
.bStop);
}
//
Events
public
event
KeyClickEventHandler KeyClick;
}