分享一个多线程异步串口通讯库
编辑时间:2017-05-14 作者:H.green 浏览量:3734 来源:cart论坛

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

主程序

codelayui.code

  1. import win.ui;
  2. import win.ui.menu;
  3. import win.ui.statusbar;
  4. import win.reg;
  5. import port;    //串口通讯库
  6. /*DSG{{*/
  7. mainForm = win.form(text="UartTest";right=599;bottom=399)
  8. mainForm.add(
  9. bthOpen={cls="button";text="打开串口";left=13;top=206;right=150;bottom=253;dl=1;dt=1;z=13};
  10. btnClrCounter={cls="button";text="计数清零";left=13;top=322;right=150;bottom=369;dl=1;dt=1;z=16};
  11. btnClrRecv={cls="button";text="清空显示";left=255;top=159;right=322;bottom=183;dl=1;dt=1;z=18};
  12. btnSend={cls="button";text="发送数据";left=13;top=264;right=150;bottom=311;dl=1;dt=1;z=15};
  13. chkRecvHex={cls="checkbox";text="16进制接收";left=162;top=164;right=247;bottom=182;dl=1;dt=1;z=17};
  14. chkSendHex={cls="checkbox";text="16进制发送";left=162;top=8;right=247;bottom=26;dl=1;dt=1;z=19};
  15. combobox1={cls="combobox";left=68;top=27;right=139;bottom=53;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=4};
  16. combobox2={cls="combobox";left=68;top=61;right=139;bottom=87;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=6};
  17. combobox3={cls="combobox";left=68;top=95;right=139;bottom=121;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=8};
  18. combobox4={cls="combobox";left=68;top=128;right=139;bottom=154;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=10};
  19. combobox5={cls="combobox";left=68;top=162;right=139;bottom=188;dl=1;dt=1;edge=1;items={};mode="dropdownlist";z=12};
  20. groupbox1={cls="groupbox";text="初始化";left=13;top=7;right=150;bottom=197;dl=1;dt=1;edge=1;z=2};
  21. 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};
  22. 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};
  23. static1={cls="static";text="串口号";left=21;top=33;right=63;bottom=53;dl=1;dt=1;transparent=1;z=3};
  24. static2={cls="static";text="波特率";left=21;top=66;right=63;bottom=86;dl=1;dt=1;transparent=1;z=5};
  25. static3={cls="static";text="数据位";left=21;top=99;right=63;bottom=119;dl=1;dt=1;transparent=1;z=7};
  26. static4={cls="static";text="停止位";left=21;top=132;right=63;bottom=152;dl=1;dt=1;transparent=1;z=9};
  27. static5={cls="static";text="校验位";left=21;top=165;right=63;bottom=185;dl=1;dt=1;transparent=1;z=11}
  28. )
  29. /*}}*/
  30. var sp = port.SerialPort(); //创建一个串口对象(可以在这里指定串口参数,如果不指定则使用默认值)
  31. var flag = false;   //串口打开标志
  32. var nRead = 0;  //已读字节
  33. var nWrite = 0; //已写字节
  34. //串口号
  35. var reg = win.regReader("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM\");   //获取当前计算机的串口列表
  36. if(reg)
  37. {
  38.     for(name,value,t in reg.eachValue()) 
  39.     {
  40.         mainForm.combobox1.add(value);
  41.     }
  42.     
  43.     if(mainForm.combobox1.count)
  44.         mainForm.combobox1.selIndex = 1;
  45. }
  46. //波特率
  47. mainForm.combobox2.add("300");
  48. mainForm.combobox2.add("600");
  49. mainForm.combobox2.add("1200");
  50. mainForm.combobox2.add("2400");
  51. mainForm.combobox2.add("4800");
  52. mainForm.combobox2.add("9600");
  53. mainForm.combobox2.add("14400");
  54. mainForm.combobox2.add("19200");
  55. mainForm.combobox2.add("38400");
  56. mainForm.combobox2.add("56000");
  57. mainForm.combobox2.add("57600");
  58. mainForm.combobox2.add("115200");
  59. mainForm.combobox2.selIndex = 6;
  60. //数据位
  61. mainForm.combobox3.add("4");
  62. mainForm.combobox3.add("5");
  63. mainForm.combobox3.add("6");
  64. mainForm.combobox3.add("7");
  65. mainForm.combobox3.add("8");
  66. mainForm.combobox3.selIndex = 5;
  67. //停止位
  68. mainForm.combobox4.add("1");
  69. mainForm.combobox4.add("1.5");
  70. mainForm.combobox4.add("2");
  71. mainForm.combobox4.selIndex = 1;
  72. //校验位
  73. mainForm.combobox5.add("None");
  74. mainForm.combobox5.add("Odd");
  75. mainForm.combobox5.add("Even");
  76. mainForm.combobox5.add("Mark");
  77. mainForm.combobox5.add("Space");
  78. mainForm.combobox5.selIndex = 1;
  79. //状态栏
  80. status = win.ui.statusbar(mainForm);
  81. status.addItem( "Status: Ports Closed",250);
  82. status.addItem( "RX:0",100);
  83. status.addItem( "TX:0",100);
  84. //打开或关闭串口
  85. mainForm.bthOpen.oncommand = function(id,event){
  86.     try
  87.     {
  88.         if(!flag)
  89.         {
  90.             //如果串口参数不需频繁改变,也可以在创建串口对象时指定。所有的参数都必须在打开串口前设置
  91.             sp.PortName = mainForm.combobox1.text;
  92.             sp.BaudRate = tonumber(mainForm.combobox2.text);
  93.             sp.DataBits = tonumber(mainForm.combobox3.text);
  94.             sp.StopBits = mainForm.combobox4.selIndex - 1;
  95.             sp.Parity = mainForm.combobox5.selIndex - 1;
  96.             sp.NotifyhWnd = mainForm.hwnd;  //设置接收串口消息的窗口句柄
  97.             
  98.             if( sp.Open()==0 )
  99.             {
  100.                 flag = true;
  101.                 mainForm.bthOpen.text = "关闭串口";
  102.                 mainForm.statusbar.setText( "Status: " + mainForm.combobox1.text + " Opened,"
  103.                                                        + mainForm.combobox2.text + ","
  104.                                                        + mainForm.combobox5.text + ","
  105.                                                        + mainForm.combobox3.text + ","
  106.                                                        + mainForm.combobox4.text, 1 );
  107.             }
  108.         }
  109.         else
  110.         {
  111.             sp.Close();
  112.             flag = false;
  113.             mainForm.bthOpen.text = "打开串口";
  114.             mainForm.statusbar.setText( "Status: Port Closed", 1 );
  115.         }
  116.     }
  117.     catch(e)
  118.     {
  119.         mainForm.msgboxErr(e);
  120.     }
  121. }
  122. //发送数据
  123. mainForm.btnSend.oncommand = function(id,event){
  124.     if(mainForm.chkSendHex.checked)
  125.         sp.WriteHex(mainForm.richedit1.text);
  126.     else
  127.         sp.Write(mainForm.richedit1.text);
  128. }
  129. //发送、接收字节计数清零
  130. mainForm.btnClrCounter.oncommand = function(id,event){
  131.     nRead = 0;
  132.     nWrite = 0;
  133.     mainForm.statusbar.setText( "RX:" + nRead, 2 );
  134.     mainForm.statusbar.setText( "TX:" + nWrite, 3 );
  135. }
  136. //清空接收区显示
  137. mainForm.btnClrRecv.oncommand = function(id,event){
  138.     mainForm.richedit2.text = "";
  139. }
  140. //发送区右键菜单
  141. mainForm.richedit1.wndproc = function(hwnd,message,wParam,lParam){
  142.     if( message == 0x205/*_WM_RBUTTONUP*/ ){  
  143.         owner.popMenu();
  144.     }
  145. }
  146. //接收区右键菜单
  147. mainForm.richedit2.wndproc = function(hwnd,message,wParam,lParam){
  148.     if( message == 0x205/*_WM_RBUTTONUP*/ ){  
  149.         owner.popMenu();
  150.     }
  151. }
  152. //窗口消息循环,处理字符串的效率会比较低
  153. var HighByte = 0;   //声明一个变量缓存汉字的高位字节,避免1个汉字拆分成“两半”
  154. mainForm.wndproc = function(hwnd,message,wParam,lParam){
  155.     select( message ) 
  156.     {
  157.         case 0x8801/*_UWM_COMM_SENDEDBYTES*/
  158.         {
  159.             nWrite += wParam;   //wParam存放已发送的字节数,lParam存放要发送的字节数,if(wParam != lParam) ...
  160.             mainForm.statusbar.setText( "TX:" + nWrite, 3 );
  161.         }
  162.         case 0x8701/*_UWM_COMM_RXCHAR*/
  163.         {
  164.             if(mainForm.chkRecvHex.checked)  //16进制显示,不足2位在前面补0,并用空格隔开
  165.             {
  166.                 mainForm.richedit2.appendText(..string.format("%02X ", lParam ));
  167.             }
  168.             else  //字符显示,分两种情况:1、汉字的显示   2、英文数字等字符的显示
  169.             {
  170.                 if(HighByte)
  171.                 {
  172.                     mainForm.richedit2.appendText( ..string.pack({HighByte;lParam}) );  //取出缓存的高位字节,组成一个完整的汉字
  173.                     HighByte = 0;
  174.                 }
  175.                 else
  176.                 {
  177.                     if(lParam>127)  //如果大于127,认为收到了汉字的高位字节,先缓存起来
  178.                         HighByte = lParam;
  179.                         //这里可能要启动一个定时器,如果一定时间内收不到后续的低位字节,则直接显示高位字节
  180.                     else
  181.                         mainForm.richedit2.appendText( ..string.pack(lParam) ); //英文数字等字符的显示
  182.                 }
  183.             }
  184.             
  185.             nRead++;
  186.             mainForm.statusbar.setText( "RX:" + nRead, 2 );
  187.         }
  188.         case 0x8901/*_UWM_COMM_FATALERR*/
  189.         {
  190.             mainForm.msgboxErr("后台线程已退出,必须关闭串口!");
  191.         }
  192.     }
  193. }
  194. mainForm.show() 
  195. return win.loopMessage(
  196. /**
  197.     function(msg)   //如果不指定sp.NotifyhWnd,消息将发到主线程
  198.     {
  199.         if( msg.message == 0x8701/*_UWM_COMM_RXCHAR*/ )
  200.             io.print( "串口接收到数据", msg.wParam, msg.lParam);
  201.     }
  202. **/
  203. );


串口库实在太长了,见附件吧。

UartTest.zip


来说两句吧
  • 请先说点什么
    热门评论
    3735人参与,1条评论www
    来自湖北省潜江市的网友 2017年05月16日 12:01
    这个串口通讯确实写的不错。