串口库sio的一些研究和扩展
扩展1: 获取电脑上有的那些串口端口号
有两种方式:
方式一,通过方程获取为列表,直接传递给items
var comReadFuc = function(){ import win.reg; var tab = {}; //获取串口号 var reg = win.regReader("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM\"); if( reg ){ for(name,value,t in reg.eachValue()){ table.push(tab,value); } return tab; }else { return null; } } //调用 mainForm.static.oncommand = function(id,event){ var comList = comReadFuc(); mainForm.comHao.clear(); mainForm.comHao.items = comList;//table.clone(comList); }
方式二 :通过迭代器方式获取 , 这样可以灵活的赋值给任何控件
eachComRead = function(){ import win.reg; var b = {}; //获取串口号 var reg = win.regReader("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM\"); if( reg ){ for(name,value,t in reg.eachValue()){ table.push(b,value); } var i = 0; return function(){ i = i + 1; return b[i]; }; }else { return null; } } //调用 mainForm.static.oncommand = function(id,event){ mainForm.comHao.clear(); for com in eachComRead(){ mainForm.comHao.add(com); } mainForm.comHao.selIndex = mainForm.comHao.count; }
两个串口转接: 类似跳板
使用条件:
有一个硬件串口设备 , 一个写好的软件(带串口通信), 但是他们之间的协议不匹配, 那么怎么能让这两个东西很好的通信上呢?
方法:
下载虚拟串口 , 生成一对连接的串口,比如 com3和com4 .
硬件串口设备连接com4, 软件接口连接com3, 这样com4接收到的数据就可以自动的转接到com3的软件里了. 这个中间的软件就是下面要写的这个软件了.
在这个软件里,我们通过com4接收到来自硬件的数据, 再软件里进行一系列的处理, 然后发送给com3, 软件打开com3,接收到这个处理后的数据.
import win.ui; /*DSG{{*/ mainForm = win.form(text="测试软件v20";right=550;bottom=264;border="dialog frame") mainForm.add( boTelvname={cls="static";text="波特率:";left=344;top=53;right=400;bottom=83;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=7}; button={cls="button";text="打开";left=344;top=177;right=425;bottom=201;z=4}; button2={cls="button";text="关闭";left=438;top=177;right=519;bottom=201;z=5}; comHaoname={cls="static";text="端口号:";left=344;top=23;right=400;bottom=53;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=3}; groupbox={cls="groupbox";text="硬连接端口";left=338;top=10;right=532;bottom=214;edge=1;z=1}; groupbox4={cls="groupbox";text="软连接端口";left=18;top=10;right=212;bottom=214;edge=1;z=14}; static={cls="static";text="数据位:";left=344;top=84;right=400;bottom=114;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=9}; static16={cls="static";text="端口号:";left=25;top=27;right=81;bottom=57;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=15}; static2={cls="static";text="校验位:";left=344;top=115;right=400;bottom=145;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=11}; static3={cls="static";text="停止位:";left=344;top=145;right=400;bottom=175;align="right";center=1;font=LOGFONT(h=-16);notify=1;transparent=1;z=13}; 停止位={cls="combobox";left=416;top=146;right=521;bottom=166;edge=1;items={};mode="dropdownlist";z=12}; 数据位={cls="combobox";left=416;top=87;right=521;bottom=107;edge=1;items={};mode="dropdownlist";z=8}; 校验位={cls="combobox";left=416;top=117;right=521;bottom=137;edge=1;items={};mode="dropdownlist";z=10}; 波特率={cls="combobox";left=416;top=58;right=521;bottom=78;edge=1;items={};mode="dropdownlist";z=6}; 端口号={cls="combobox";left=416;top=28;right=521;bottom=48;edge=1;items={};mode="dropdownlist";z=2}; 软连接端口号={cls="combobox";left=94;top=33;right=199;bottom=53;edge=1;items={};mode="dropdownlist";z=16} ) /*}}*/ import thread.command; import sio; import time.performance; //定义端口 var sport,RRsport; var qiangduFlag = false; var firstFlag = true; var maxdata = 0; var mindata = 0; var timedata = 0; var pretime = 0; var commandA,commandB = false,false; eachComRead = function(){ import win.reg; var b = {}; //获取电脑中串口号 var reg = win.regReader("HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM"); if( reg ){ for(name,value,t in reg.eachValue()){ table.push(b,value); } var i = 0; return function(){ i = i + 1; return b[ i ]; }; }else { return null; } } //刷新串口列表 mainForm.comHaoname.oncommand = function(id,event){ mainForm.端口号.clear(); mainForm.软连接端口号.clear(); for com in eachComRead(){ mainForm.端口号.add(com); mainForm.软连接端口号.add(com); } mainForm.端口号.selIndex = mainForm.端口号.count; mainForm.软连接端口号.selIndex = mainForm.软连接端口号.count; } var setCommList = function(){ with mainForm{ 波特率.items = { "115200"; }; 数据位.items = { "8" }; 校验位.items = { "none"; }; 停止位.items = { "1"; }; for comName in ..eachComRead(){ 端口号.add( comName ); 软连接端口号.add( comName ) } //设置默认值 端口号.selIndex = 端口号.count; 软连接端口号.selIndex = 软连接端口号.count; 波特率.selIndex = 1; 数据位.selIndex = 4; 校验位.selIndex = 1; 停止位.selIndex = 1; //其他默认设置 button2.disabled = true; 波特率.disabled = true; 数据位.disabled = true; 校验位.disabled = true; 停止位.disabled = true; } } //执行默认设置 setCommList(); //创建一个订阅对象,用于在界面上显示 var Msg = thread.command(); //设置订阅函数(接收数据处理) Msg.recDealFunc = function( v_str = "" ){ if(v_str != ""){ var datastr = string.trim(string.hex(v_str," ")); if(commandA){ RRsport.writeHex(datastr); } if(commandB) { RRsport.writeHex(datastr ++ " 11 22"); } } } Msg.RRrecDealFunc = function( v_str = "" ){ if(v_str != ""){ var datastr = string.upper(string.replace(string.hex(v_str," "),"\s","")); select(datastr) { case "A35001" { commandA = true; commandB = false; sport.writeHex("a3 50 01"); } case "A35002" { commandA = false; commandB = false; sport.writeHex("a3 50 02"); } case "A35003" { commandA = false; commandB = true; sport.writeHex("a3 50 03"); } else { } } } } //打开串口 mainForm.button.oncommand = function(id,event){ if(mainForm.端口号.text != ""){ //禁用 mainForm.button.disabled = true; mainForm.button2.disabled = false; //打开端口 sport = sio.port( mainForm.端口号.text ); RRsport = sio.port( mainForm.软连接端口号.text ); //设置串口(波特率,数据位,停止位,校验位) sport.ioctl(tonumber( mainForm.波特率.text ),tonumber( mainForm.数据位.text ),tonumber( mainForm.停止位.text ),mainForm.校验位.text); RRsport.ioctl(tonumber( mainForm.波特率.text ),tonumber( mainForm.数据位.text ),tonumber( mainForm.停止位.text ),mainForm.校验位.text); //接收到字符时响应事件,注意这个回调函数是线程回调函数 sport.termCntIrqThread(1,function(port){ import thread.command; import sio; var sport = sio.port(port); //阻塞调用跨线程数据处理命令 sleep(3); thread.command.send("recDealFunc",sport.read()); //非阻塞调用跨线程数据处理命令 //thread.command.post("recDealFunc",sport.read()); } ) RRsport.termCntIrqThread(1,function(port){ import thread.command; import sio; var RRsport = sio.port(port); //阻塞调用跨线程数据处理命令 sleep(3); thread.command.send("RRrecDealFunc",RRsport.read()); //非阻塞调用跨线程数据处理命令 //thread.command.post("recDealFunc",sport.read()); } ) }else { mainForm.msgboxErr("未发现有效串口设备!") } } //关闭串口 mainForm.button2.oncommand = function(id,event){ //禁用 mainForm.button.disabled = false; mainForm.button2.disabled = true; //关闭串口 sport.close(); RRsport.close() } mainForm.enableDpiScaling(); mainForm.show(); return win.loopMessage();
学单片机的如果刚开始接触上位机, 那么思维可能就停在单片机思路里了, 单片机里面一般是中断和超时来获取得到的数据 , 但是上位机windows是分时处理机制, 最短的轮训时间是1ms, 这里用中断处理就会出现为题: 可能会把一帧数据处理成两帧来接收\
为了避免这个问题, 就需要利用数组进行缓存接收到的数据, 类似单片机里面的环形缓冲器
下面是我实际使用中用到的一个简单的数组入栈和出栈处理串口数据:
初始化 = function(comName="COM1",zeroA=0,zeroB=0){ owner.sport = ..sio.port(comName); if(!owner.sport){ error("功率计初始化失败!请修改[电机参数设置]->[通信端口]配置!",2); return 0; }else { owner.sport.ioctl(115200); owner.sport.setReadTimeouts(50,0); ..thread.set("通道一真实功率值",0 ); ..thread.set("通道二真实功率值",0 ); ..thread.set("继续", true); ..thread.invoke( function( sport ){ import sio; var CH1Meter,CH2Meter = 0,0; var PowerTab = {}; var 存结果Tab = {}; //清空数据之前的接收和发送缓存 sport.flush(true,true); //开启连续功率井喷 sport.write('METER:SCANMODE4\r\n'); while( thread.get("继续") ){ var ret = sport.readHex(1); if(ret==null){ continue ; } var ret = string.trim(ret); var retP = string.split(ret," "); if(#retP >= 1){ for(i=1;#retP;1){ table.push(PowerTab,retP[i]); } } if(#PowerTab>4){ var retL1,retH1,retL2,retH2 = table.shift(PowerTab,4); CH1Meter = (tonumber((retH1++retL1),16))*0.01-100; CH2Meter = (tonumber((retH2++retL2),16))*0.01-100; thread.set("通道一真实功率值",CH1Meter ) thread.set("通道二真实功率值",CH2Meter ); }; }; //关闭井喷 sport.write('METER:SCANMODE0\r\n'); sport.close(); },owner.sport ); return 1; }; }
我上面的串口是只要发送一次打开操作, 下位机的单片机会一直连续不停的发送数据上来, 于是我开线程连续不停的处理取得的数据, 保证实时数据是最新的.
当然, 如果你的数据 是应答模式 , 那么每当你处理一次数据之后,就发送下一次请求指令即可.
如果你的数据是有帧头和帧尾的, 那么就在处理那个函数里面, 用select 判断帧头, 然后后面的数据做压栈到数组操作, 直到检测到帧尾, 然后把入栈的数据提取出来进行操作.
上面这样做的好处就是: 不会丢任何数据.
登录后方可回帖
利用sio库写了一个完整的串口助手: