重工电子论坛

标题: 关于新建线程中,用MSComm控件接收数据的问题 [打印本页]

作者: 李维强-15级    时间: 2015-6-9 11:53
标题: 关于新建线程中,用MSComm控件接收数据的问题
本帖最后由 李维强-15级 于 2015-6-9 11:55 编辑

一个关于新建线程中,用MSComm控件接收数据的问题,现在情况是这样。
我在主窗口拖了一个MSComm控件到窗口里面去, 用类向导关联这个控件,并且设变量名为m_ComPort。
然后把这个m_ComPort初始化好,并且把事件响应这些函数都处理都弄好。

以下是我主窗口程序事件的定义和接收数据函数
我先在头文件里面定义一个数组 用来装数据,和之前说的MSComm控件变量

[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

CMSComm        m_ComPort;
BYTE                 rxdata[1024];


然后在CPP文件里面做了如下定义

[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

void CMyPortDlg:Init()
{
        这里里面做m_ComPort;初始化的操作主要为:
m_ComPort.SetCommPort(1); //选择COM1
m_ComPort.SetInBufferSize(1024); //接收缓冲区
m_ComPort.SetOutBufferSize(1024);//发送缓冲区
m_ComPort.SetInputLen(0);//设置当前接收区数据长度为0,表示全部读取
m_ComPort.SetInputMode(1);//以二进制方式读写数据
m_ComPort.SetRThreshold(1);//接收缓冲区有1个及1个以上字符时,将引发接收数据的OnComm事件
m_ComPort.SetSettings("9600,n,8,1");//波特率9600无检验位,8个数据位,1个停止位

….


}

void CMyPortDlg:Send(CString strData)
{
        这个里面把参数strData传进来的字符 转换成16进制数据,通过串口发出去
}
BEGIN_EVENTSINK_MAP(CSportDlg, CDialog)
    //{{AFX_EVENTSINK_MAP(CSportDlg)
    ON_EVENT(CMyPortDlg, IDC_MSCOMM1, 1 /* OnComm */, OnCommMscomm1, VTS_NONE)
    //}}AFX_EVENTSINK_MAP
END_EVENTSINK_MAP()

void CMyPortDlg::OnCommMscomm1()
{
    // TODO: Add your control notification handler code here
    VARIANT variant1;
    COleSafeArray safearray;
    LONG len,k;
    if(m_ComPort.GetCommEvent()==2)            
    {
        variant1 = m_ComPort.GetInput();
        safearray = variant1;
        len = safearray.GetOneDimSize();
        for(k=0;k<len;k++)
        {
            safearray.GetElement(&k,rxdata+k);
        }
                 然后再简单的分析数据,几行代码;
    }
}



如果用以上代码 在主窗口线程里面去发送和接收都没有问题。我发送一个数据给下位机,下位机马上就反馈数据回来给我程序了。

但是我因为程序需要,需要在新建线程里面去发送数据和接收数据,结果就带来了以下问题,把我困扰惨了。。。

我新建线程AfxBeginThread(MyThread1,this);然后把主窗口线程的this指针传给新建线程,为了就是调用主窗口的CMyPortDlg:Send()这个发送数据函数;

[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

UINT CProduceDlg::MyThread1(void *param)
{
        CMyPortDlg * Dlg =(CMyPortDlg *)param;

        …
}


下面我要在新线程里面给下位机发命令,让下位机启动一段时间,让它搜集数据,过5秒后发命令让其关闭,然后我再发命令给下位机,让它返回一个通过刚才5秒钟收集数据,然后经过处理后的数据。

然后我在新建线程MyThread1里面这样做

[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

UINT CProduceDlg::MyThread1(void *param)
{
        CMyPortDlg * Dlg =(CMyPortDlg *)param;
        Dlg->Send("FFAAAAFF");                //发送启动命令
        Sleep(5000);                                        //这里延迟5秒 ,并且让主窗口线程响应ON_EVENT事件
        Dlg->Send("FF0000FF");                //发送关闭命令
        Sleep(500);                                        //这里延迟一下,也让主窗口响应ON_EVENT
        Dlg->Send("FFBBBBFF");                //发送查询命令
        Sleep(1000);                                        //延迟1秒,留出足够的时间让下位机返回数据
        然后操作rxdata[]里面的数据       
}


我记得很早听人说 Sleep这个函数可以让出CPU执行权,在Sleep的时间里面,可以让其他线程执行程序,所以我故意在发送启动命令后延迟5秒,我以为这5秒可以让主窗口线程响应足够多的ON_EVENT事件,结果调试跟踪发现,在延迟5秒期间,主程序1次都没有响应ON_EVENT事件,而是在我发送Dlg->Send("FF0000FF ");这个关闭命令过后,主程序里面的ON_EVENT事件才来,而且来的不是m_ComPort.GetCommEvent()==2,GetCommEvent()返回的是1008,也就是“缓冲区溢出”,(因为我给下位机发送启动命令后,下位机会每3ms就发送一个数据给我电脑),而且是连续返回几个这个溢出事件,等着几个溢出事件响应完了,程序执行“Dlg->Send("FFBBBBFF");//发送查询命令”过后,新线程里面再执行Sleep(1000);,在这期间主窗口也不会响应ON_EVENT事件,所以我写在OnComm里面的那些数据处理操作根本就不执行,以至于后面我新线程里面最后 “操作rxdata[]里面的数据”,根本就是无效操作。

于是我把代码改为下面一种


[C++] syntaxhighlighter_viewsource syntaxhighlighter_copycode

UINT CProduceDlg::MyThread1(void *param)
{
        CMyPortDlg * Dlg =(CMyPortDlg *)param;
        Dlg->Send("FFAAAAFF");                //发送启动命令
        pMyThread->SuspendThread()        //把线程挂起  然后在        OnComm里面判断收到足够数据就恢复

..
}


结果 把线程一挂起,线程就永远挂起了,主窗口线程也不会响应任何一个ON_EVENT事件,也就根本就无法进入到OnComm函数,也就无法恢复。

我看MSDN里面说Sleep(0);可以让出时间片,让后我加进MyThread1里面各个地方,各种不同加法,结果还是不行,主线程都不得响应ON_EVENT,或者像我注释里面 理想的那样 去响应ON_EVENT事件,从而顺利的进入OnComm里面去操作数据。。。

我能确定在线程里面Dlg->Send("FFAAAAFF");这个命令一定起作用了的,下位机也不断向我电脑发数据,我下位机上有显示的。关键问题就是在响应ON_EVENT事件这里出问题。

作者: 李维强-15级    时间: 2015-6-10 02:23
Comm控件是需要附在对话框上面的,而且是OnEvent事件触发。这是在主线程中处理的,要用线程进行收和发处理的话,还是用 API或者CSerialPort类比较好, 毕竟效率高很多。

当初就是不懂串口 才去研究的用控件,简单的方法。。。等以后有空了再来看看用API,话说用API才是王道。。。我过后也会试验一篇API的用法

不过我昨天发帖过后,百度的时候,运气来了,看到有一个人的帖子,好像是一个留言,看到人家也是在新线程里面接收数据,也遇到问题。但是它是在新线程里面调用m_ComPort.GetInput();强制收,也就是不等那个ON_EVEN事件。因为实际情况,一般用232 或者485,给串口发数据后都有返回值,所以,我就在新线程里面 发1个数据,然后sleep(100);然后调用主窗口线程的m_ComPort.GetInput();去强制收一次,收不收得到再说。用此方法,在我Sleep(5000);的时候,根据我的情况,真的等这么久,肯定会缓冲区溢出,所以我就每隔500ms强制收一次,这样收8次 也就是5秒钟啦,
这个就是我现在想到的临时解决方法,现在用起来还可以,哈哈,用户反映不错,可惜啊,只是他看不到我背后是用怎么个糟糕的方式实现的。。。。。。。。。。。




欢迎光临 重工电子论坛 (http://cqutlab.cn/) Powered by Discuz! X3.1