前阵子在找界面库HTMLayout时认识了aardio,小巧、不用安装、不带库,这正是我需要的。因为之前做过几年电工,知道在工控领域很多地方还在用串口、以太网的,于是决定通过写一个串口通讯库来学习aardio。 这个串口库纯aardio代码,读、写线程独立,可以实现全双工,设计成类似C#串口类的形式(方法+属性)。使用了3个标准库util.metaProperty、thread.event、thread.table。在写的过程中主要是在纯函数跟API数据类型上面吃了苦头。因为水平有限,错误在所难免,欢迎指正。
主程序
import win.ui;
import win.ui.menu;
import win.ui.statusbar;
import win.reg;
import port; //串口通讯库
/*DSG{{*/
mainForm = win.form(text="UartTest";right=599;bottom=399)
mainForm.add(
bthOpen={cls="button";text="打开串口";left=13;top=206;right=150;bottom=253;dl=1;dt=1;z=13};
btnClrCounter={cls="button";text="计数清零";left=13;top=322;right=150;bottom=369;dl=1;dt=1;z=16};
btnClrRecv={cls="button";text="清空显示";left=255;top=159;right=322;bottom=183;dl=1;dt=1;z=18};
btnSend={cls="button";text="发送数据";left=13;top=264;right=150;bottom=311;dl=1;dt=1;z=15};
chkRecvHex={cls="checkbox";text="16进制接收";left=162;top=164;right=247;bottom=182;dl=1;dt=1;z=17};
chkSendHex={cls="checkbox";text="16进制发送";left=162;top=8;right=247;bottom=26;dl=1;dt=1;z=19};
combobox1={cls="combobox";left=68;top=27;right=139;bottom=53;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=4};
combobox2={cls="combobox";left=68;top=61;right=139;bottom=87;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=6};
combobox3={cls="combobox";left=68;top=95;right=139;bottom=121;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=8};
combobox4={cls="combobox";left=68;top=128;right=139;bottom=154;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=10};
combobox5={cls="combobox";left=68;top=162;right=139;bottom=188;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=12};
groupbox1={cls="groupbox";text="初始化";left=13;top=7;right=150;bottom=197;dl=1;dt=1;edge=1;z=2};
richedit1={cls="richedit";left=162;top=27;right=588;bottom=152;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;wrap=1;z=1};
richedit2={cls="richedit";left=162;top=184;right=588;bottom=369;db=1;dl=1;dr=1;dt=1;edge=1;hscroll=1;multiline=1;vscroll=1;wrap=1;z=14};
static1={cls="static";text="串口号";left=21;top=33;right=63;bottom=53;dl=1;dt=1;transparent=1;z=3};
static2={cls="static";text="波特率";left=21;top=66;right=63;bottom=86;dl=1;dt=1;transparent=1;z=5};
static3={cls="static";text="数据位";left=21;top=99;right=63;bottom=119;dl=1;dt=1;transparent=1;z=7};
static4={cls="static";text="停止位";left=21;top=132;right=63;bottom=152;dl=1;dt=1;transparent=1;z=9};
static5={cls="static";text="校验位";left=21;top=165;right=63;bottom=185;dl=1;dt=1;transparent=1;z=11}
)
/*}}*/
var sp = port.SerialPort(); //创建一个串口对象(可以在这里指定串口参数,如果不指定则使用默认值)
var flag = false; //串口打开标志
var nRead = 0; //已读字节
var nWrite = 0; //已写字节
//串口号
var reg = win.regReader("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM\"); //获取当前计算机的串口列表
if(reg)
{
for(name,value,t in reg.eachValue())
{
mainForm.combobox1.add(value);
}
if(mainForm.combobox1.count)
mainForm.combobox1.selIndex = 1;
}
//波特率
mainForm.combobox2.add("300");
mainForm.combobox2.add("600");
mainForm.combobox2.add("1200");
mainForm.combobox2.add("2400");
mainForm.combobox2.add("4800");
mainForm.combobox2.add("9600");
mainForm.combobox2.add("14400");
mainForm.combobox2.add("19200");
mainForm.combobox2.add("38400");
mainForm.combobox2.add("56000");
mainForm.combobox2.add("57600");
mainForm.combobox2.add("115200");
mainForm.combobox2.selIndex = 6;
//数据位
mainForm.combobox3.add("4");
mainForm.combobox3.add("5");
mainForm.combobox3.add("6");
mainForm.combobox3.add("7");
mainForm.combobox3.add("8");
mainForm.combobox3.selIndex = 5;
//停止位
mainForm.combobox4.add("1");
mainForm.combobox4.add("1.5");
mainForm.combobox4.add("2");
mainForm.combobox4.selIndex = 1;
//校验位
mainForm.combobox5.add("None");
mainForm.combobox5.add("Odd");
mainForm.combobox5.add("Even");
mainForm.combobox5.add("Mark");
mainForm.combobox5.add("Space");
mainForm.combobox5.selIndex = 1;
//状态栏
status = win.ui.statusbar(mainForm);
status.addItem( "Status: Ports Closed",250);
status.addItem( "RX:0",100);
status.addItem( "TX:0",100);
//打开或关闭串口
mainForm.bthOpen.oncommand = function(id,event){
try
{
if(!flag)
{
//如果串口参数不需频繁改变,也可以在创建串口对象时指定。所有的参数都必须在打开串口前设置
sp.PortName = mainForm.combobox1.text;
sp.BaudRate = tonumber(mainForm.combobox2.text);
sp.DataBits = tonumber(mainForm.combobox3.text);
sp.StopBits = mainForm.combobox4.selIndex - 1;
sp.Parity = mainForm.combobox5.selIndex - 1;
sp.NotifyhWnd = mainForm.hwnd; //设置接收串口消息的窗口句柄
if( sp.Open()==0 )
{
flag = true;
mainForm.bthOpen.text = "关闭串口";
mainForm.statusbar.setText( "Status: " + mainForm.combobox1.text + " Opened,"
+ mainForm.combobox2.text + ","
+ mainForm.combobox5.text + ","
+ mainForm.combobox3.text + ","
+ mainForm.combobox4.text, 1 );
}
}
else
{
sp.Close();
flag = false;
mainForm.bthOpen.text = "打开串口";
mainForm.statusbar.setText( "Status: Port Closed", 1 );
}
}
catch(e)
{
mainForm.msgboxErr(e);
}
}
//发送数据
mainForm.btnSend.oncommand = function(id,event){
if(mainForm.chkSendHex.checked)
sp.WriteHex(mainForm.richedit1.text);
else
sp.Write(mainForm.richedit1.text);
}
//发送、接收字节计数清零
mainForm.btnClrCounter.oncommand = function(id,event){
nRead = 0;
nWrite = 0;
mainForm.statusbar.setText( "RX:" + nRead, 2 );
mainForm.statusbar.setText( "TX:" + nWrite, 3 );
}
//清空接收区显示
mainForm.btnClrRecv.oncommand = function(id,event){
mainForm.richedit2.text = "";
}
//发送区右键菜单
mainForm.richedit1.wndproc = function(hwnd,message,wParam,lParam){
if( message == 0x205/*_WM_RBUTTONUP*/ ){
owner.popMenu();
}
}
//接收区右键菜单
mainForm.richedit2.wndproc = function(hwnd,message,wParam,lParam){
if( message == 0x205/*_WM_RBUTTONUP*/ ){
owner.popMenu();
}
}
//窗口消息循环,处理字符串的效率会比较低
var HighByte = 0; //声明一个变量缓存汉字的高位字节,避免1个汉字拆分成“两半”
mainForm.wndproc = function(hwnd,message,wParam,lParam){
select( message )
{
case 0x8801/*_UWM_COMM_SENDEDBYTES*/
{
nWrite += wParam; //wParam存放已发送的字节数,lParam存放要发送的字节数,if(wParam != lParam) ...
mainForm.statusbar.setText( "TX:" + nWrite, 3 );
}
case 0x8701/*_UWM_COMM_RXCHAR*/
{
if(mainForm.chkRecvHex.checked) //16进制显示,不足2位在前面补0,并用空格隔开
{
mainForm.richedit2.appendText(..string.format("%02X ", lParam ));
}
else //字符显示,分两种情况:1、汉字的显示 2、英文数字等字符的显示
{
if(HighByte)
{
mainForm.richedit2.appendText( ..string.pack({HighByte;lParam}) ); //取出缓存的高位字节,组成一个完整的汉字
HighByte = 0;
}
else
{
if(lParam>127) //如果大于127,认为收到了汉字的高位字节,先缓存起来
HighByte = lParam;
//这里可能要启动一个定时器,如果一定时间内收不到后续的低位字节,则直接显示高位字节
else
mainForm.richedit2.appendText( ..string.pack(lParam) ); //英文数字等字符的显示
}
}
nRead++;
mainForm.statusbar.setText( "RX:" + nRead, 2 );
}
case 0x8901/*_UWM_COMM_FATALERR*/
{
mainForm.msgboxErr("后台线程已退出,必须关闭串口!");
}
}
}
mainForm.show()
return win.loopMessage(
/**
function(msg) //如果不指定sp.NotifyhWnd,消息将发到主线程
{
if( msg.message == 0x8701/*_UWM_COMM_RXCHAR*/ )
io.print( "串口接收到数据", msg.wParam, msg.lParam);
}
**/
);
串口库实在太长了,见附件吧。