社区
非技术区
帖子详情
一个串口,两个窗体在用。咋办?
f1305147
2011-05-11 04:40:22
SerialPort comm = new SerialPort();
这句话写在哪里?
两个窗体中都需要用到,可不可以写在一个可以公用的地方?
两个窗体中接受,发送分别分开就可以吗》
...全文
167
4
打赏
收藏
一个串口,两个窗体在用。咋办?
SerialPort comm = new SerialPort(); 这句话写在哪里? 两个窗体中都需要用到,可不可以写在一个可以公用的地方? 两个窗体中接受,发送分别分开就可以吗》
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用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);
}
我们对各种发送命令函数进行载入形参的方法来解决。
使用C#及Visual Studio 2019写
一个
简单的
串口
助手
使用C#及Visual Studio 2019写
一个
简单的
串口
助手(萌新笔记)简单介绍设计过程功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成
一个
适合你的列表创建
一个
表格设定内容居中、居左、居右SmartyPants创建
一个
自定义列表如何创建
一个
注脚注释也是必不可少的KaTeX数学公式新的甘特图功能,丰富你的文章UML 图表FLowchart流...
C#
串口
与
窗体
应用程序的连接
这几天又被安排看了几天的书,,,,,真是乏味啊。。。。所以当boss说要我试试自己创建
一个
窗体
应用程序,实现与
串口
的连接的时候,整个人就跟打了鸡血一样,兴趣和动力立马就上来了啊。 好吧,废话不多说,直接切入正题。 第一部分 设计
窗体
不多说,直接设计成下面这个样子就行了,用的控件有GroupBox、button、TextBox。 第二部分 设置端口 既然关系到
串口
,这里我们要做
Visual Studio 2019 C# 上位机入门(2):写
一个
简单的
串口
助手
本文记录一下用Visual Studio 2019 C# 写
一个
简单的
串口
助手的过程,由于没有先从小处学习,而是直接找相关资料就开始做,免不了很多奇怪的问题花了一些时间,基于此情况,我将尽可能整理出更多细节,尤其是我遇到的坑,以便和我一样的新手小白上手。后续我还准备单独分析Visual Studio上提供的一些控件的使用方法,以加深理解。话不多说,正文开始。
C#多个
窗体
共享同
一个
SerialPort控件
在使用SerialPort控件时,能够发现虽然可以把它拖拽到窗口上,但它并不会显示在
窗体
上,而是在
窗体
的下面,这说明此控件并不是
一个
可视化的控件,类似的控件还有Timer。正是由于SerialPort的这种特性,使得我们可以不必采用拖拽的方式添加此控件,而是直接在代码中添加。 现在有
两个
窗体
,分别是Form1和Form2,其中Form1是主
窗体
,现在想要在Form1中打开
串口
,然后在Form2中进...
C# 多
窗体
共用
一个
串口
1.定义
一个
静态SerialPort1对象,初始化
串口
参数,不要用自带的SerialPort控件;//
串口
接收,可直接通过主
窗体
类名调用。2.把
串口
定义为
一个
类,其他
窗体
就可以直接调用。1.1 定义
一个
静态SerialPort1对象。1.2 初始化
串口
参数,注册
串口
接收事件。1.3 其他
窗体
调用。
非技术区
7,776
社区成员
63,113
社区内容
发帖
与我相关
我的任务
非技术区
.NET技术 非技术区
复制链接
扫一扫
分享
社区描述
.NET技术 非技术区
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章