7651|20

79

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

100求助各位高手:关于串口收发程序! [复制链接]

EVC下串口通信原程序如下:
/**********************************
函数名称: OpenPort
描    述: 打开串口
输入参数: LPCTSTR Port: 串口名,如"COM0:","COM1:"
                  int BaudRate: 波特率
                  int DataBits: 数据位, 取值为7或8
                  int StopBits: 停止位
                  int Parity  : 奇偶校验位
返    回: FALSE: 失败;    TRUE: 成功
*********************************/
BOOL CSerialPortDlg::OpenPort(LPCTSTR Port, int BaudRate, int DataBits, int StopBits, int Parity)
{
        COMMTIMEOUTS CommTimeOuts;

        // 打开串口
        m_hComm = CreateFile(Port, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0);
        if(m_hComm == INVALID_HANDLE_VALUE)
        {
                MessageBox(_T("无法打开端口或端口已打开!请检查是否已被占用."));
                return FALSE;
        }
        GetCommState(m_hComm, &dcb);                        /* 读取串口的DCB */
        dcb.BaudRate = BaudRate;                       
        dcb.ByteSize = DataBits;
        dcb.Parity = Parity;
        dcb.StopBits = StopBits;       
        dcb.fParity = FALSE;                                /* 禁止奇偶校验 */
        dcb.fDtrControl = 0;                                /* 禁止流量控制 */
        dcb.fRtsControl = 0;
        dcb.fOutX = 0;
        dcb.fInX = 0;
        dcb.fTXContinueOnXoff = 0;
       
        //设置状态参数
        SetCommMask(m_hComm, EV_RXCHAR);                        /* 串口事件:接收到一个字符 */       
        SetupComm(m_hComm, 16384, 16384);                                        /* 设置接收与发送的缓冲区大小 */
        if(!SetCommState(m_hComm, &dcb))                                        /* 设置串口的DCB */
        {
                MessageBox(_T("无法按当前参数配置端口,请检查参数!"));
                ClosePort();
                return FALSE;
        }
               
        //设置超时参数
        GetCommTimeouts(m_hComm, &CommTimeOuts);               
        CommTimeOuts.ReadIntervalTimeout = 100;                                /* 接收字符间最大时间间隔 */
        CommTimeOuts.ReadTotalTimeoutMultiplier = 1;               
        CommTimeOuts.ReadTotalTimeoutConstant = 100;                /* 读数据总超时常量 */
        CommTimeOuts.WriteTotalTimeoutMultiplier = 0;
        CommTimeOuts.WriteTotalTimeoutConstant = 0;               
        if(!SetCommTimeouts(m_hComm, &CommTimeOuts))
        {
                MessageBox(_T("无法设置超时参数!"));
                ClosePort();
                return FALSE;
        }
               
        PurgeComm(m_hComm, PURGE_TXCLEAR | PURGE_RXCLEAR);         /* 清除收/发缓冲区 */               
        return TRUE;               
}
/***************************************
函数名称: CSerialPortDlg::ClosePort
描    述: 关闭串口
************************************/
BOOL CSerialPortDlg::ClosePort(void)
{
        if(m_hComm != INVALID_HANDLE_VALUE)
        {
                SetCommMask(m_hComm, 0);               
                PurgeComm(m_hComm, PURGE_TXCLEAR | PURGE_RXCLEAR);        /* 清除收/发缓冲 */
                CloseHandle(m_hComm);                                                                /* 关闭串口操作句柄 */
                m_hComm = INVALID_HANDLE_VALUE;
                return TRUE;
        }

        return FALSE;
}
/************************************
函数名称: CALLBACK CSerialPortDlg::OnCommRecv
描    述: 串口接收数据成功回调函数

返    回: FALSE: 失败;    TRUE: 成功
************************************/
void CALLBACK CSerialPortDlg::OnCommRecv(CWnd* pWnd, char *buf, int buflen)
{
        CString tmp;

        CSerialPortDlg * pDlg = (CSerialPortDlg*)pWnd;
        CEdit *pRecvStrEdit = (CEdit*)pDlg->GetDlgItem(IDC_REC_DISP);
                                                                                /* 取得控件指针 */
        for (int i = 0; i < buflen; i++, buf++)
        {
                tmp.Format(_T("%c"), *buf);                                                /* 将字符转换为字符串 */
                pDlg->m_strRecDisp += tmp;
        }

        pRecvStrEdit->SetWindowText(pDlg->m_strRecDisp);        /* 显示在窗口上 */
}
/************************************
函数名称: CSerialPortDlg::CommRecvTread
描    述: 串口接收线程

输入参数: LPVOID lparam: 线程参数,创建线程时传入
返    回: 0: 线程退出, 返回值没特殊含义
*************************************/
DWORD CSerialPortDlg::CommRecvTread(LPVOID lparam)
{
        DWORD dwLength;
        char *recvBuf = new char[1024];
        CSerialPortDlg *pDlg = (CSerialPortDlg*)lparam;

        while(TRUE)
        {                                                                                                                                /* 等待线程退出事件 */
                if (WaitForSingleObject(pDlg->m_ExitThreadEvent, 0) == WAIT_OBJECT_0)
                        break;       

                if (pDlg->m_hComm != INVALID_HANDLE_VALUE)
                {                                                                                                                        /* 从串口读取数据 */
                        BOOL fReadState = ReadFile(pDlg->m_hComm, recvBuf, 1024, &dwLength, NULL);
                        if(!fReadState)
                        {
                                //MessageBox(_T("无法从串口读取数据!"));
                        }
                        else
                        {
                                if(dwLength != 0)
                                        OnCommRecv(pDlg, recvBuf, dwLength);                        /* 接收成功调用回调函数 */
                        }
                }
        }               

        delete[] recvBuf;
        return 0;
}
// 定义串口设置参数表格
const CString PorTbl[6] = {_T("COM1:"),_T("COM2:"),_T("COM3:"),_T("COM4:"),_T("COM5:"), _T("COM6:")};
const DWORD BaudTbl[6] = {4800, 9600, 19200, 38400, 57600,115200};       
const DWORD DataBitTbl[2] = {7, 8};
const BYTE  StopBitTbl[3] = {ONESTOPBIT, ONE5STOPBITS, TWOSTOPBITS};
const BYTE  ParityTbl[4] = {NOPARITY, ODDPARITY, EVENPARITY, MARKPARITY};
/*****************************************
函数名称: CSerialPortDlg::OnOpenCom
描    述: "打开端口" 按键单击事件代码
******************************************/
void CSerialPortDlg::OnOpenCom()
{
        DWORD IDThread;
        HANDLE hRecvThread;                                                                                                /* 接收线程句柄 */
        UpdateData(TRUE);

        CString strPort = PorTbl[m_ComboPort.GetCurSel()];                                /* 查表获取参数值 */
        DWORD baud                = BaudTbl[m_ComboBaud.GetCurSel()];
        DWORD databit   = DataBitTbl[m_ComboData.GetCurSel()];
        BYTE stopbit    = StopBitTbl[m_ComboStop.GetCurSel()];
        BYTE parity                = ParityTbl[m_ComboParity.GetCurSel()];

        BOOL ret = OpenPort(strPort, baud, databit, stopbit, parity);        /* 打开串口 */
        if (ret == FALSE)
                return;

        m_ExitThreadEvent = CreateEvent(NULL, TRUE, FALSE, NULL);                /* 创建串口接收线程退出事件*/

        // 创建串口接收线程
        hRecvThread = CreateThread(0, 0, CommRecvTread, this, 0, &IDThread);
        if (hRecvThread == NULL)
        {
                MessageBox(_T("创建接收线程失败!"));
                return;
        }       
        CloseHandle(hRecvThread);

        m_ButOpen.EnableWindow(FALSE);                                                        /* 打开端口按键禁止 */                               
        m_ButClose.EnableWindow(TRUE);                                                        /* 关闭端口按键使能 */       
        MessageBox(_T("打开") + strPort + _T("成功!"));
}
/****************************************
函数名称: CSerialPortDlg::OnOpenCom
描    述:"关闭端口" 按键单击事件代码
*****************************************/
void CSerialPortDlg::OnCloseCom()
{
        if (m_ExitThreadEvent != NULL)
        {
                SetEvent(m_ExitThreadEvent);                        /* 通知线程退出 */
                Sleep(1000);
                CloseHandle(m_ExitThreadEvent);
                m_ExitThreadEvent = NULL;
        }

        m_ButOpen.EnableWindow(TRUE);                                /* 打开端口按键禁止 */                               
        m_ButClose.EnableWindow(FALSE);                                /* 关闭端口按键使能 */       
        ClosePort();
}
/***********************************
函数名称: OnSend()
描    述: 发送数据按键
***************************************/
void CSerialPortDlg::OnSend()
{
        DWORD dwactlen;

        if (m_hComm == INVALID_HANDLE_VALUE)
        {
                MessageBox(_T("串口未打开!"));
                return;
        }

        UpdateData(TRUE);
        int len = m_strSendEdit.GetLength();                                 /* 取得输入字符串长度 */
        char *psendbuf = new char[len];

        for(int i = 0; i < len;i++)
                psendbuf = (char)m_strSendEdit.GetAt(i);                 /* 转换为单字节字符 */

        WriteFile(m_hComm, psendbuf, len, &dwactlen, NULL);         /* 从串口发送数据 */
       
        delete[] psendbuf;
}
/*******************************************
函数名称: CSerialPortDlg::OnClearSend
描    述: "清除发送缓冲区" 按键单击事件代码
*********************************************/
void CSerialPortDlg::OnClearSend()
{
        m_strSendEdit = _T("");                                /* 清除发送区字符 */
        UpdateData(FALSE);
}
/********************************
函数名称: CSerialPortDlg::OnClearRec
描    述: "清除接收缓冲区" 按键单击事件代码
**************************************/
void CSerialPortDlg::OnClearRec()
{
        m_strRecDisp = _T("");                                       
        SetDlgItemText(IDC_REC_DISP,m_strRecDisp);        /* 清除接收区字符 */
}
/***********************************
函数名称: CSerialPortDlg::OnDestroy
描    述: 窗口销毁事件处理函数
**********************************/
void CSerialPortDlg::OnDestroy()
{
        CDialog::OnDestroy();
       
        if (m_ExitThreadEvent != NULL)
        {
                SetEvent(m_ExitThreadEvent);                                /* 通知接收线程退出 */
                Sleep(1000);
                CloseHandle(m_ExitThreadEvent);                                /* 关闭接收线程退出事件句柄 */
                m_ExitThreadEvent = NULL;
        }

        ClosePort();                                                                        /* 关串口 */
}
问题:
1、程序中回调函数OnCommRecv在整个程序中它的作用是什么?为什么要有这样一个回调函数呢??
2、程序中SetCommMask(m_hComm, 0);这条是不是当系统接收到一个字符就触发串口事件m_hComm
   这个函数不是跟WaitCommEvent共同使用吗?如:WaitCommEvent(m_hComm,&dwMask,0){switch(dwMask){...}}
   可是在本程序中没出现WaitCommEvent,那么调用SetCommMask的意义是什么呢?
3、SetupComm(m_hComm, 16384, 16384);这个函数在书中都提示说“并不是很有用、只是个推荐值、并不是串行驱动必须的...”
   为什么我看到的几个串口应用程序都用到这个函数,它有什么特殊意义吗??是不是可以象书中介绍的不用这个函数???
4、本程序中用到一些事件、线程如:请问整个程序的流程大概是什么样的呢,特别是事件、线程那比较混乱...
先谢谢各位高手啦!

最新回复

我现在试了两个串口程序 都是可以打开串口,但是发送数据时程序死掉,郁闷! 但是板子(X86的)在XP下用超级终端还没问题 程序移植到VC++下,重新编译在PC上用也没问题   详情 回复 发表于 2008-7-29 10:51
点赞 关注

回复
举报

58

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
问题:
1、程序中回调函数OnCommRecv在整个程序中它的作用是什么?为什么要有这样一个回调函数呢??
>>>A:从函数名就可以判断出OnCommRecv是串口接收处理的功能。至于为什么是回调,LZ可以试试将回调属性拿掉,看看是否对结果有影响!!!
2、程序中SetCommMask(m_hComm, 0);这条是不是当系统接收到一个字符就触发串口事件m_hComm
>>>A:SetCommMask()函数的参数,去看帮助吧。EVC4的帮助上说:A value of zero disables all events. 这句话LZ不会看不懂吧!
  这个函数不是跟WaitCommEvent共同使用吗?如:WaitCommEvent(m_hComm,&dwMask,0){switch(dwMask){...}}
>>>A:是和WaitCommEvent共同使用的。Set了哪个事件,就可以Wait这个事件。
  可是在本程序中没出现WaitCommEvent,那么调用SetCommMask的意义是什么呢?
>>>A:应该在接收线程中调用 WaitCommEvent
3、SetupComm(m_hComm, 16384, 16384);这个函数在书中都提示说“并不是很有用、只是个推荐值、并不是串行驱动必须的...”
  为什么我看到的几个串口应用程序都用到这个函数,它有什么特殊意义吗??是不是可以象书中介绍的不用这个函数???
>>>A:只能说你看到的几个串口程序都有问题!可以不使用它。
4、本程序中用到一些事件、线程如:请问整个程序的流程大概是什么样的呢,特别是事件、线程那比较混乱...
>>>A:建议去看资料 ,网上很多的。search一下吧!
 
 

回复

61

帖子

0

TA的资源

一粒金砂(初级)

板凳
 
1.OnCommRecv是在系统收到数据是会调用的回调函数,
2.waitcommevent是等待串口消息(无论是接收还是发送),并且阻塞调用该命令所在的进程.在SET之后继续.
3.在这段程序当中是需要的setup是做初始化的,可以不用.
4.串口的例程很多吧,GOOGLE一下.
 
 
 

回复

78

帖子

0

TA的资源

一粒金砂(初级)

4
 

SetupComm(m_hComm, 16384, 16384);
好像你不设置缺省是512,对于大部分的串口程序就足够用了,所以对于大部分情况是绝对可以不设置的,

事件和回调函数会不太容易理解,串口通信的例子很多,建议楼主多看几个。
 
 
 

回复

60

帖子

0

TA的资源

一粒金砂(初级)

5
 
sorry
第二个问题我打错了
应该是这个SetCommMask(m_hComm, EV_RXCHAR);

非常感谢楼上2位仁兄!!!
!!!!
 
 
 

回复

82

帖子

0

TA的资源

一粒金砂(初级)

6
 
是三位
 
 
 

回复

63

帖子

0

TA的资源

一粒金砂(初级)

7
 
1、这里是不是回调函数都可以。
用回调感觉怪怪的。
 
 
 

回复

68

帖子

0

TA的资源

一粒金砂(初级)

8
 

我感觉也是怪怪的
哈哈
请问回调函数有什么特殊的含义吗???
在什么情况下需要用回调函数呢?
 
 
 

回复

69

帖子

0

TA的资源

一粒金砂(初级)

9
 
所谓的回调函数只不过是不知道谁给取的一个唬人的名字,其实就是个普通的函数,把串口收到的内容显示在edit控件上,你把"回调"函数那段代码复制到调用回调函数的位置效果一样.
真正的回调函数是指 我写了一个函数但不在我的代码中调用,只是通过传递函数指针或者别的方法,让其他的模块(比如系统或者别人写的代码)来调用,当然本质上也是个普通函数
 
 
 

回复

72

帖子

0

TA的资源

一粒金砂(初级)

10
 

SetCommMask(m_hComm, EV_RXCHAR);
楼主理解的应该是对的,我写串口通信时重来不用
 
 
 

回复

55

帖子

0

TA的资源

一粒金砂(初级)

11
 
我把这个回调函数中的内容写到调用回调函数的位置
编译程序成功后
打开串口后,点发送数据
系统提示应用程序错误内存不能为读!
 
 
 

回复

85

帖子

0

TA的资源

一粒金砂(初级)

12
 

有了回调函数应该是更灵活了,比如说一个button,它由一个回调函数OnButton1(),你只要按下该按钮,就会触发这个回调函数。串口通信时也是一样,只不过是自己定什么时候响应你自己的回调函数,比如说,出错的时候,象楼主的例子,当有数据到达缓冲区的时候响应OnCommRecv。

有了这个回调函数我们不再需要开启线程,循环检测是否有数据了,只要有数据,回调函数就会有响应。
 
 
 

回复

71

帖子

0

TA的资源

一粒金砂(初级)

13
 
你把代码发来看看
 
 
 

回复

76

帖子

0

TA的资源

一粒金砂(初级)

14
 
ok,我怎么发给你
 
 
 

回复

61

帖子

0

TA的资源

一粒金砂(初级)

15
 
QQ或者MSN方便告诉我吗??
 
 
 

回复

85

帖子

0

TA的资源

一粒金砂(初级)

16
 
发到邮箱rilyyu@tom.com
 
 
 

回复

72

帖子

0

TA的资源

一粒金砂(初级)

17
 
我也觉得怪怪的, 同一个类还需要回调函数? 直接调用不好吗?

当然问题可能是其他的....
 
 
 

回复

77

帖子

0

TA的资源

一粒金砂(初级)

18
 
肯定會丟失資料(尤其是按下觸摸板時)
 
 
 

回复

81

帖子

0

TA的资源

一粒金砂(初级)

19
 

楼主的例子应该是自己做了一个串口通信类,当中把读操作也封装了,至于接收数据是靠主进程中的自定义回调函数来完成。而不是象通常的做法,先收数据,若有,判断是不是我要的数据,然后再做处理。
 
 
 

回复

61

帖子

0

TA的资源

一粒金砂(初级)

20
 
“肯定會丟失資料(尤其是按下觸摸板時)”

不解!!!
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表