社区
非技术区
帖子详情
一个串口,两个窗体在用。咋办?
f1305147
2011-05-11 04:40:22
SerialPort comm = new SerialPort();
这句话写在哪里?
两个窗体中都需要用到,可不可以写在一个可以公用的地方?
两个窗体中接受,发送分别分开就可以吗》
...全文
145
4
打赏
收藏
一个串口,两个窗体在用。咋办?
SerialPort comm = new SerialPort(); 这句话写在哪里? 两个窗体中都需要用到,可不可以写在一个可以公用的地方? 两个窗体中接受,发送分别分开就可以吗》
复制链接
扫一扫
分享
转发到动态
举报
AI
作业
写回复
配置赞助广告
用AI写文章
4 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
f1305147
2011-05-12
打赏
举报
回复
[Quote=引用 3 楼 zhulong1111 的回复:]
两个窗口不能同时进行访问 必须是一个访问完关闭 另一个在继续打开访问
[/Quote]
有个问题请教。
比如拨号,比如查询等等用到接受事件,接到的数据咋区分呢?
zhulong1111
2011-05-11
打赏
举报
回复
两个窗口不能同时进行访问 必须是一个访问完关闭 另一个在继续打开访问
zhulong1111
2011-05-11
打赏
举报
回复
[Quote=引用楼主 f1305147 的回复:]
SerialPort comm = new SerialPort();
这句话写在哪里?
两个窗体中都需要用到,可不可以写在一个可以公用的地方?
两个窗体中接受,发送分别分开就可以吗》
[/Quote]以前我做短信猫也遇到过 用线程 lock 来锁住这个SerialPort 串口操作类 一个窗口访问完就关闭串口
Bo217
2011-05-11
打赏
举报
回复
在中小型电站系统就地控制中,比如水电站中如果我们要进行各种设备控制的话,串口数量就可能比较多了,有的地方加上载波甚至可以达到10个以上,很多的解决方法是将某些功能设备并行接到一个串口上面尽量减少串口的数量,然后进行数据采集的时候采取环的方法进行。但是工业控制要求实时性比较高,比如报警和各种控制,如果不能在尽可能短的时间里面进行处理可能引发大的后果,我们觉得还是应该将各种不同设备接入不同的串口,比如水电站中间各个机组的PLC和机组的调速器通讯等就接入不同串口。如果某个相同设备数量很多,如温度装置,有的1个发电机组可能超过20个温度点,我们可以采用接入2个或者多个串口的方法处理。
为了使初学者能够更容易看懂串口通讯的处理过程,我采用援助非洲刚果(布)姆古古鲁水电站的温度表为实例进行程序的分析。在我们这个项目中有4台发电机组,每个机组温度表有20个点。由于这个与上位机通讯串口安排极多,我们只能将20个温度表并行接入串口进行通讯。在进行硬件通讯之前我们首先要看懂改硬件的通讯协议。
通讯协议就是上位机向改外围设备进行读取数据和进行某种功能控制时候的一系列指令和外围设备返回上位机的各数据位代表的意思。比如那个位是控制码,哪个位是数据,是什么数据等。
首先启动VC新建一个给予SDI的工程,然后加入SerialPort类。由于要进行多串口通讯,我们需要对SerialPort进行一些简单的修改,由于在与硬件通讯过程中一般通讯协议都采用BYTE类型数据传送,我们可以将改类中间的发送和接收数据类型修改成为BYTE类型。我修改了下面部分内容,详细改动请见附录提供的SERIALPORT类。
//
// Write a string to the port
//
void CSerialPort::WriteToPort(BYTE bWriteBuffer[],int nWriteBufferSize)
{
assert(m_hComm != 0);
int nSize = sizeof(bWriteBuffer)/sizeof(BYTE);
m_nWriteBufferSize = nWriteBufferSize;
for(int i = 0 ; i < nWriteBufferSize ; i ++)
m_bWriteBuffer[i] = bWriteBuffer[i];
// set event for write
SetEvent(m_hWriteEvent);
}
。。。。。
由于我们改串口接入了20台温度设备,在进行通讯的时候是通过发送某个地址的设备命令进行读取数据。我们首先对硬件设置相应的地址,这里我们设置0到19号地址。采集的时候采用循环的方式从0号地址向19号地址进行读取数据。当收到相应的数据包的时候我们进行相应的地址的数据解包处理。然后发送下一个地址的要数据命令。当地址为最后一台设备的时候我们将地址清0处理就可以了。但是如果我们这个20台设备中间某一个或者多个设备由于故障或者电源没开的话,上述通讯就会出现问题,我们发送没有运行的地址设备就会收不到相应的报文,我们就不会发送下一个地址的要数据命令,这是程序就会不走下去了。解决方法可以是我们从外部去判断是否对当前地址的发送要数据命令和收到数据命令是否超时。如果超时就进行跳过然后发送下一个地址要数据命令。当出现规定几个循环的时候进行该设备的采集参数清0等工作这个就可以随自己定义考虑了。具体实现如下:
定义SERIALPORT类对象,创建线程进行通讯。
CSerialPort m_Ports;
int nColtAddr,这个用来存放当前采集设备地址。nColts;这个用来存放当前缓冲区收到的字节数目
HANDLE m_pThread;外部控制线程
BYTE m_RecBuff[1000];接收缓冲区
float fVal[20];处理解包内容,这里可以根据实际情况进行定义。
启动串口监视线程和外部控制线程
nColtAddr = 0 ;
nColts = 0;
if(m_Ports.InitPort(this,1,4800,'N',8,1,EV_RXCHAR|EV_RXFLAG,1024))
{
this->m_Ports.StartMonitoring();启动监视线程
SetCommVal();发送第一台设备数据命令
}
下面是启动外部控制线程
unsigned int nDummy;
m_pThread=(HANDLE) _beginthreadex(NULL,0,CommThread,this,CREATE_SUSPENDED,&nDummy);//开辟外部控制线程
ResumeThread(m_pThread); 运行线程
外部控制线程控制当前设备发送要数据命令和收到数据报文是否超时
UINT C××××View::CommThread(LPVOID pParam)
{
C××××View *pView = (C××××View *)pParam;
while(1)
{
CTime cNowTime = CTime::GetCurrentTime();
tNow = cNowTime.GetTime();
struct _timeb timebuffer;
_ftime(&timebuffer);
int nNowMillSecond = timebuffer.millitm;
///
tLast = cLastColtTime[0].GetTime();
if((tNow - tLast)*1000 + (nNowMillSecond - nMillSecond[0]) > 800)
pView->SetCommVal();发送下一台设备要数据命令或者进行其他的相关处理
Sleep(100);
}
}
发送串口数据命令,这里要根据外部设备的制定的通讯协议来进行。这次温度表采用的是ASCII的形式通讯。
void C××××View::SetCommVal()
{
int HAddr,LAddr,m_Xnh;
int nHAdd,nLAdd;
nHAdd = ExchangeAscII((nColtAddr>>4)&0x0f);
nLAdd = ExchangeAscII(nColtAddr&0x0f);
m_Xnh = nHAdd^nLAdd^0x52^0x44;
HAddr = ExchangeAscII((m_Xnh>>4)&0x0f);
LAddr = ExchangeAscII(m_Xnh&0x0f);
BYTE OutBuff[8] = {0x40,nHAdd,nLAdd,0x52,0x44,HAddr,LAddr,0x0d};
m_Ports.WriteToPort(OutBuff,8);
cLastColtTime = CTime::GetCurrentTime();
nColtAddr++;
if(nColtAddr > 19)//19 define max addr numbers
nColtAddr = 0;
}
ASCII码的一些简单变换,我们进行一下简单的封装,方便调用:
BYTE C××××View::ExchangeAscII(BYTE bInput)
{
BYTE bRef = 0;
if(bInput > 9)
bRef = bInput+0x37;
else
bRef = bInput+0x30;
return bRef;
}
BYTE C××××View::ExchangeAscIItoNormal(BYTE bInput)
{
BYTE bRef = 0;
if(bInput > 0x39)
bRef = bInput-0x37;
else
bRef = bInput-0x30;
return bRef;
}
LONG C×××View::OnCommunication(WPARAM ch, LPARAM port)进行数据处理,WPARAM,LPARAM类型是多态性数据(polymorphic data type),在WIN32中为32位,支持多种数据类型,根据需要自动适应,这样程序就有很强的适应性。再次我们这里理解成为BYTE类型(与外围设备通讯协议保持一致,方便解包)。每当串口接收缓冲区内有一个字符的时候,就会产生一个WM_COMM_RXCHAR消息,触发 OnCommunication函数,下面我们可以根据我们的需要进行解包处理了;
LONG CMy11View::OnCommunication(WPARAM ch, LPARAM port)
{
if(port == 1)
{
m_RecBuff[nColts] += (BYTE)(char *)(ch);
nColts++;
if(nColts == 24)这里根据通讯协议规定的发送定制要数据命令就会上传24个字节的数据报文内容。这里可以根据不同外部设备进行不同的设置
{
DataProcessTemp(m_RecBuff);处理解包
nColts = 0;缓冲区指针清0,准备接收下一台设备数据
ResetBuffVal();清空缓冲区内容
SetCommVal(); 发送下一台设备内容
}
}
return 0;
}
数据解包处理,这里就必须根据外部设备定义的通讯协议来处理了。
void CMy11View::DataProcessTemp(BYTE m_Inbuff[])
{
int nTempAddr = nColtAddr - 1;
if(nTempAddr < 0)
nTempAddr = 19;
int nHAdd,nLAdd;
nHAdd = ExchangeAscII((nTempAddr>>4)&0x0f);
nLAdd = ExchangeAscII(nTempAddr&0x0f);
if(m_Inbuff[0] == 0x40)
{
if(m_Inbuff[1] == nHAdd && m_Inbuff[2] == nLAdd)
{
if(m_Inbuff[3] == 0x52 && m_Inbuff[4] == 0x44)
{
int nzTemp[5];
float fTemp;
nzTemp[0] = m_Inbuff[7];
nzTemp[1] = m_Inbuff[8];
nzTemp[2] = m_Inbuff[9];
nzTemp[3] = m_Inbuff[10];
for(int i = 0 ; i < 4; i ++)
{
if(nzTemp[i] > 0x39)
nzTemp[i] -= 0x37;
else
nzTemp[i] -= 0x30;
}
fTemp=float(nzTemp[1]+(nzTemp[0]<<4)+(nzTemp[3]<<8)+(nzTemp[2]<<12))/10;
fVal[nTempAddr] = fTemp;
RedrawWindow();
}
}
}
}
void CMy11View::ResetBuffVal()
{
for(int i=0;i<1000;i++)
m_RecBuff[i] = 0;
}
至此,基本的通讯外围程序基本完成,如果我们要扩充多个串口多线程的话,我们可以做如下修改:
CSerialPort m_Ports[20];
BYTE m_RecBuff[20][1000];
BYTE m_SendBuff[5][1000];
int nColts[20];
int nZBKType[24];
int nWrongCount[20][20];
int nColtAddr[20];
HANDLE m_pThread;
//Protect Device
if(this->m_Ports[0].InitPort(this,2,9600,'N',8,1,EV_RXCHAR|EV_RXFLAG,1024))
{
this->m_Ports[0].StartMonitoring();
SetComBufferVal(0);
}
//Diandu Device
if(this->m_Ports[1].InitPort(this,4,1200,'E',8,1,EV_RXCHAR|EV_RXFLAG,1024))
{
this->m_Ports[1].StartMonitoring();
SetComBufferVal(1);
}
我们对各种发送命令函数进行载入形参的方法来解决。
iotthingsgraph-jvm-0.17.4-beta-sources.jar
iotthingsgraph-jvm-0.17.4-beta-sources.jar
rolesanywhere-jvm-1.1.15-sources.jar
rolesanywhere-jvm-1.1.15-sources.jar
基于PDFjs和html2canvasjs的在线PDF签名处理工具-实现PDF文件上传展示与签名板手写功能-支持签名图片去底色处理与拖拽定位-通过截取HTML元素保持高清晰度导.zip
stm32基于PDFjs和html2canvasjs的在线PDF签名处理工具_实现PDF文件上传展示与签名板手写功能_支持签名图片去底色处理与拖拽定位_通过截取HTML元素保持高清晰度导.zip
开源免费且支持 Docker 的 Java 博客系统所用框架介绍
【源码预览】:https://renmaiwang.cn/s/yiwoi 一款开源且免费的 Java 博客系统,运用了 spring-boot、spring-data-jpa、shiro、freemarker、bootstrap 等框架进行构建,同时对 Docker 提供支持。
iOS全局自动化 代码混淆 工具!支持cocoapod组件代码一并 混淆,完美避开hardcode方法、静态库方法和系统库方法!
【源码预览】:https://renmaiwang.cn/s/ez8f3 (最新版、最全版本)iOS全局自动化 代码混淆 工具!支持cocoapod组件代码一并 混淆,完美避开hardcode方法、静态库方法和系统库方法!
非技术区
7,774
社区成员
63,115
社区内容
发帖
与我相关
我的任务
非技术区
.NET技术 非技术区
复制链接
扫一扫
分享
社区描述
.NET技术 非技术区
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章