前阵子在找界面库HTMLayout时认识了aardio,小巧、不用安装、不带库,这正是我需要的。因为之前做过几年电工,知道在工控领域很多地方还在用串口、以太网的,于是决定通过写一个串口通讯库来学习aardio。 这个串口库纯aardio代码,读、写线程独立,可以实现全双工,设计成类似C#串口类的形式(方法+属性)。使用了3个标准库util.metaProperty、thread.event、thread.table。在写的过程中主要是在纯函数跟API数据类型上面吃了苦头。因为水平有限,错误在所难免,欢迎指正。
主程序
codelayui.code
- 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);
- }
- **/
- );
串口库实在太长了,见附件吧。