开始进入工业自动化,买的工控机带有GPIO接口,可用于直接控制继电器。
从厂家拿到接口手册一看,居然是汇编直接操作端口,基本上是IN/OUT 指令了。接口很简单,计算位移,读取;计算位移,写入。
这种接口,常见有四种办法,分别是四种语言实现,一是直接写A SM,不过要公开给C#做的应用程序调用,很不容易,另外三种是C/C++/Delphi嵌入汇编,倒是问题不大。
接口实在是小,不想大动干戈,所以想了别的办法。
第五种,用C++/CLI,这也是一个不错的主意。但是我甚至想省掉这个接口DLL,于是有了第六种办法:C#嵌入x86汇编。
C#是没办法像C/C++/Delphi那样直接嵌入x86汇编的,所以需要做点手脚。
在汇编里面,我们为了修改一个软件经常一块空白区域来写汇编代码,然后Jmp过去执行。(不明白这一句话的可以跳过,或者去看雪论坛)
但是显然要在C#代码里面这么做很不现实,即使用C/C++编译得到ob j,C#也没办法链接这个obj。(这个涉及编译的也可以跳过)
回头一想(其实不是现在想,07年就做过C#嵌入汇编),其实C#也跑在x86上,IL指令最终还是要编译成x86汇编指令的,我们应该可以这些写汇编指令,所需要的只是一块空间而已。
我们可以申请一块非托管空间嘛,于是有:
// 分配内存
var ptr = Marsha l.AllocH Globa l(code.Length);
有了空间,我们就可以把二进制的汇编指令给写进去啦:
// 写入汇编指令
Marsha l.Copy(code, 0, ptr, code.Length);
然后呢?.Net提供一个途径,让我们可以把一个内存指针转为一个委托(一直都说.Net的委托其实就是C/C++的函数指针哈):
// 转为委托
return(T)(Object)Marsha l.GetDel egate ForFu nctio nPoin ter(ptr, typeof(T));
那么,剩下的问题,就是如何把汇编转为二进制了!
这个我们是不能像C/C++/Delphi那样直接写汇编指令的,所以得走点弯路。
我的做法是用OD随便打开一个程序,在上面直接写汇编代码,然后把汇编的十六进制复制出来,放到C#代码中。
剩下的就不多说了,直接上代码吧!
usingSystem;
usingSystem.Collec tions.Generi c;
usingSystem.Text;
usingSystem.Runtim e.Intero pServ ices;
usingSystem.Diagno stics;
usingSystem.IO;
namesp ace Consol eAppl icati on19
{
汇编指令有多少个classGPIO
{
#region属性
privat e Int32_Offse t;
/// <summar y>选择位移</summar y>
public Int32Offset { get { return _Offse t; } set { _Offse t = value; } } privat e Int32_Bit;
/// <summar y>选择位</summar y>
public Int32Bit { get { return _Bit; } set { _Bit = value; } }
#endreg ion
#region构造
privat e GPIO(Int32offset, Int32bit)
{
Offset = offset;
Bit = bit;
}
privat e GPIO(Int32gpio)
{
Offset = gpio / 16;
Bit = gpio % 16;
}
#endreg ion
#region预定义针脚
public static GPIO Pin2 = new GPIO(0, 6);
public static GPIO Pin3 = new GPIO(0, 7);
public static GPIO Pin4 = new GPIO(2, 1);
public static GPIO Pin5 = new GPIO(2, 4);
public static GPIO Pin6 = new GPIO(1, 0);
public static GPIO Pin7 = new GPIO(1, 4);
public static GPIO Pin8 = new GPIO(3, 3);
public static GPIO Pin9 = new GPIO(3, 4);
public static GPIO IO6 = new GPIO(6);
public static GPIO IO7 = new GPIO(7);
public static GPIO IO17 = new GPIO(17);
public static GPIO IO20 = new GPIO(20);
public static GPIO IO8 = new GPIO(8);
public static GPIO IO12 = new GPIO(12);
public static GPIO IO27 = new GPIO(27);
public static GPIO IO28 = new GPIO(28);
#endreg ion
#region业务
/// <summar y>是否启用</summar y>
public Boolea n Enable{ get { return Read(Offset, Bit); } set { WriteB it(Offset, Bit, value); } }
/// <summar y>是否输出</summar y>
public Boolea n Output{ get { return Read(Offset+ 4, Bit); } set { WriteB it(Offset + 4, Bit, value); } }
/// <summar y>是否设置数据位</summar y>
public Boolea n Data { get { return Read(Offset+ 12, Bit); } set { WriteB it(Offset + 12, Bit, value); } }
#endreg ion
#region读取端口
constInt16BASEAD DRESS = 0x500;
Boolea n Read(Int32offset, Int32bit)
{
var d = ReadHa ndler((Int16)(BASEAD DRESS + offset));
var c = (Byte)~(1 << bit);
d &= c;
return d == c;
}
privat e static ReadFu nc _ReadH andle r;
/// <summar y>属性说明</summar y>
public static ReadFu nc ReadHa ndler{ get { return_ReadH andle r ?? (_ReadH andle r = GetRea dHand ler()); } }
//static IntPtr ptr;
static ReadFu nc GetRea dHand ler()
{
// 汇编指令
var code = new Byte[] {
0x66, 0x8B, 0x55, 0x08, //mov dx, word ptr [ebp+8]
0xEC, //in al, dx
};
return(ReadFu nc)Inject ASM<ReadFu nc>(code);
}
public delega te Byte ReadFu nc(Int16addres s);
#endreg ion
#region写入端口
void Write(Int32offset, Int32value)
{
WriteH andle r((Int16)(BASEAD DRESS + offset), (Byte)value);
}
privat e static WriteF unc _Write Handl er;
/// <summar y>属性说明</summar y>
public static WriteF unc WriteH andle r { get { return_Write Handl er ?? (_Write Handl er = GetWri teHan dler()); } }
static WriteF unc GetWri teHan dler()
{
// 汇编指令
var code = new Byte[] {
0x66, 0x8B, 0x55, 0x08, //mov dx, word ptr [ebp+8]
0x8A, 0x45, 0x0C, //mov al, byte ptr [ebp+C]
0xEE //out dx, al
};
return Inject ASM<WriteF unc>(code);
}
public delega te void WriteF unc(Int16addres s, Byte bit);
#endreg ion
#region写入端口位
void WriteB it(Int32offset, Int32bit, Boolea n value)
{
if (value)
SetBit Handl er((Int16)(BASEAD DRESS + offset), (Byte)bit);
else
ClearB itHan dler((Int16)(BASEAD DRESS + offset), (Byte)bit);
}
privat e static WriteB itFun c _SetBi tHand ler;
/// <summar y>设置位</summar y>
public static WriteB itFun c SetBit Handl er { get { return_SetBi tHand ler ?? (_SetBi tHand ler = GetSet BitHa ndler()); } }
privat e static WriteB itFun c _Clear BitHa ndler;
/// <summar y>清除位</summar y>
public static WriteB itFun c ClearB itHan dler{ get { return_Clear BitHa ndler ?? (_Clear BitHa ndler = GetCle arBit Handl er()); } }
static WriteB itFun c GetSet BitHa ndler()
{
// 汇编指令
var code = new Byte[] {
0x53, //push ebx
0x51, //push ecx
0x66, 0x8B, 0x55, 0x08, //mov dx, word ptr [ebp+8]
0x8A, 0x45, 0x0C, //mov al, byte ptr [ebp+C]
0xB3, 0x01, //mov bl, 1
0xD2, 0xE3, //shl bl, cl
0xEC, //in al, dx
0x08, 0xD8, //or al, bl
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论