VC++串口通信编程详解
- 格式:docx
- 大小:25.74 KB
- 文档页数:8
基于VC6.0MFC的简单串⼝通信软件编程(⼀)基于对话框编程基本步骤:1.新建基于对话框的⼯程;2.拖曳所需的基本控件,构成界⾯;3.使⽤类向导(CLASS WIZARD)创建与基本控件对应的成员变量、成员函数,确定与函数相应的消息或事件——即触礁发该函数执⾏的条件(如单击等)。
(⼆)使⽤mscomm控件:1.基本设置:m_com.SetCommPort(1); //选择COM1m_com.SetInBufferSize(1024); //设置输⼊缓冲区的⼤⼩,Bytesm_com.SetOutBufferSize(256); //设置输出缓冲区的⼤⼩,Bytesif(!m_com.GetPortOpen()) //打开串⼝m_com.SetPortOpen(TRUE);m_com.SetInputMode(1); //设置输⼊⽅式为⼆进制⽅式m_com.SetSettings("4800,s,8,1");m_com.SetRThreshold(1); //为1表⽰有⼀个字符引发⼀个事件 m_com.SetInputLen(0);2.似乎每次设置m_com.SetSettings("4800,s,8,1"),必须先关再开串⼝才有效。
设置的校验位似乎只对发送数据有效,对接收数据⽆效。
3.虽然m_com.SetRThreshold(1)设置接收到⼀个字符就引发⼀个事件(触发执⾏函数),但执⾏函数读取到的数据可能不⽌1Byte,如另⼀串⼝连续发送多个字节,当串接收到第⼀个字节后就触发函数,但在函数去输⼊缓冲区读取数据时可以读到8Byte。
(三)MFC的⼀些基本使⽤:1.Unsigned char 与char不同,在执⾏加减时,char型当作带符号数(可能为负)。
2.当控件对应的变量是控件类时,可能通过该类的成员函数来获取数据或执⾏操作:如m_com是mscomm控件的变量,可以执⾏m_com.SetPortOpen(FALSE)来关闭串⼝。
工业控制领域(如DCS系统),经常涉及到串行通信问题。
为了实现微机和单片机之间的数据交换,人们用各种不同方法实现串行通信,如DOS下采用汇编语言或C语言,但在Windows 环境下却存在一些困难和不足。
在Windows操作系统已经占据统治地位的情况下(何况有些系统根本不支持DOS如Windows2000)开发Windows 环境下串行通信技术就显得日益重要。
VC++6.0是微软公司于1998年推出的一种开发环境,以其强大的功能,友好的界面,32位面向对象的程序设计及Active X的灵活性而受广大软件开发者的青睐,被广泛应用于各个领域。
应用VC++开发串行通信目前通常有如下几种方法:一是利用Windows API通信函数;二是利用VC的标准通信函数_inp、_inpw、_inpd、_outp、_outpw、_outpd等直接对串口进行操作;三是使用Microsoft Visual C++的通信控件(MSComm);四是利用第三方编写的通信类。
以上几种方法中第一种使用面较广,但由于比较复杂,专业化程度较高,使用较困难;第二种需要了解硬件电路结构原理;第三种方法看来较简单,只需要对串口进行简单配置,但是由于使用令人费解的V ARIANT 类,使用也不是很容易;第四种方法是利用一种用于串行通信的CSerial类(这种类是由第三方提供),只要理解这种类的几个成员函数,就能方便的使用。
笔者利用CSerial类很方便地实现了在固定式EBM气溶胶灭火系统分区启动器(单片机系统)与上位机的通信。
以下将结合实例,给出实现串行通信的几种方法。
1 Windows API通信函数方法与通信有关的Windows API函数共有26个,但主要有关的有:CreateFile() 用“comn”(n为串口号)作为文件名就可以打开串口。
ReadFile() 读串口。
WriteFile() 写串口。
CloseHandle() 关闭串口句柄。
VC串口通讯在VC++中有两种方法可以进行串口通讯。
一种是利用Microsoft公司提供的ActiveX控件Microsoft Communications Control。
另一种是直接用VC++访问串口。
下面将简述这两种方法。
一、Microsoft Communications ControlMicrosoft公司在WINDOWS中提供了一个串口通讯控件,用它,我们可以很简单的利用串口进行通讯。
在使用它之前,应将控件加在应用程序的对话框上。
然后再用ClassWizard 生成相应的对象。
现在我们可以使用它了。
该控件有很多自己的属性,你可以通过它的属性窗口来设置,也可以用程序设置。
我推荐用程序设置,这样更灵活。
SetCommPort:指定使用的串口。
GetCommPort:得到当前使用的串口。
SetSettings:指定串口的参数。
一般设为默认参数"9600,N,8,1"。
这样方便与其他串口进行通讯。
GetSettings:取得串口参数。
SetPortOpen:打开或关闭串口,当一个程序打开串口时,另外的程序将无法使用该串口。
GetPortOpen:取得串口状态。
GetInBufferCount:输入缓冲区中接受到的字符数。
SetInPutLen:一次读取输入缓冲区的字符数。
设置为0时,程序将读取缓冲区的全部字符。
GetInPut:读取输入缓冲区。
GetOutBufferCount:输出缓冲区中待发送的字符数。
SetOutPut:写入输出缓冲区。
一般而言,使用上述函数和属性就可以进行串口通讯了。
以下是一个范例。
#define MESSAGELENGTH 100class CMyDialog : public CDialog{protected:VARIANT InBuffer; //(输入缓冲区)VARIANT OutBuffer;//(输出缓冲区)CMSComm m_Com;public:......}BOOL CMyDiaLog::OnInitDialog()CDialog::OnInitDialog(); //(onlnitDialog()是属于Cdialog)m_Com.SetCommPort(1);if (!m_Com.GetPortOpen()) { //(取得串口状态)m_Com.SetSettings("57600,N,8,1"); //(指定串口的参数)m_Com.SetPortOpen(true); //(打开或关闭串口)m_Com.SetInBufferCount(0); //(输入缓冲区中接受到的字符数)SetTimer(1,10,NULL);InBuffer.bstrVal=new unsigned short[MESSAGELENGTH];OutBuffer.bstrVal=new unsigned short[MESSAGELENGTH];OutBuffer.vt=VT_BSTR;}return true;}void CMyDiaLog::OnTimer(UINT nIDEvent){if (m_Com.GetInBufferCount()>=MESSAGELENGTH) {InBuffer=m_Com.GetInput();// handle the InBuffer.// Fill the OutBuffer.m_Com.SetOutput(OutBuffer);}CDialog::OnTimer(nIDEvent);}用该控件传输的数据是UNICODE格式。
c语言怎么写串口通信编程 -回复在C语言中进行串口通信编程,你需要了解串口的基本原理以及相关的函数和库。
串口通信是一种通过串行数据传输进行通信的方式,常用于嵌入式系统中与外部设备进行数据交互。
本文将以步骤的形式来介绍如何在C语言中进行串口通信编程。
步骤一:了解串口的基本原理在开始串口通信编程之前,你需要了解串口的基本原理。
串口是通过发送和接收数据位的序列来进行通信的。
串口通信需要考虑的一些参数包括波特率(即数据传输速率)、数据位数、校验位和停止位等。
波特率指的是每秒钟传输的位数,可以是常用的9600、19200、38400等。
步骤二:选择合适的串口库在C语言中,你可以选择使用合适的串口库来简化串口通信的编程工作。
常见的串口库包括Windows系统中的WinAPI、Linux系统中的termios库等。
选择库的时候,需根据你所使用的操作系统和开发环境进行选择。
步骤三:打开串口在开始使用串口进行通信之前,需要先打开串口。
使用串口库的函数,可以根据需要选择打开特定的串口,一般通过指定串口名称或者端口号来进行打开。
打开串口的函数可能返回一个文件描述符或者句柄,用于后续的读写操作。
步骤四:配置串口参数打开串口之后,需要进行串口参数的配置。
这包括波特率、数据位数、校验位和停止位等参数的设置。
一般通过调用相应的函数,将需要设置的参数传递给串口库,以完成参数的配置。
步骤五:读取串口数据配置完串口参数后,你可以开始读取串口数据。
通过调用读取函数,你可以从串口接收缓冲区中获取数据,并进一步进行处理。
读取函数可能会阻塞程序执行,直到有数据可读取为止。
步骤六:发送串口数据与读取串口数据相对应的是发送串口数据。
通过调用相应的发送函数,你可以将你要发送的数据写入串口发送缓冲区,等待发送。
发送函数可能会阻塞程序执行,直到数据成功发送。
步骤七:关闭串口在程序结束时,需要关闭已经打开的串口。
通过调用相应的函数,可以完成串口的关闭。
用VC实现PC机与单片机串口通讯梁伯福PC机与单片机串口通讯可以通过多种方式来实现,在这里只介绍使用MSCOMM控件进行通讯。
PC机与单片机进行串口通讯的电路如下:因为单片机输入输出的是TTL电平,而PC机串口输入输出的是RS232电平,其与TTL 电平不兼容,所以要通过RS232接口进行电平转换,这可通过集成电路MAX232来实现。
在这里,我们的通讯采用主从方式,即PC机做主机,单片机作从机,PC机控制单片机发送或者接收数据,单片机没有主动发起通讯的权力。
PC机程序。
我们首先在VC中通过appWizard生成一个基于对话框的程序,接着在对话框中添加MSCOMM控件。
方法是:右击对话框-> insert activeX control -> MSCOMM32.OCX。
添加MSCOMM控件后,我们需要在头文件中定义一个类型为CMScomm的变量。
CMSComm m_msComm;// CMSComm是添加控件后VC自动生成的类接着我们使用此变量对串口进行初始化操作(可在对话框初始化时或通讯前调用此函数)。
void InitComm(){m_msComm.SetCommPort(1); // 设置通讯的串口,可为1,2,….,N//(如你的PC机有N个串口的话)m_msComm.SetInputMode(1); //设置接收模式,0为文本,1为二进制,要想能接收//值为0的数据,一定要设置为二进制模式m_msComm.SetInputLen(0); // 设置读取方式,0为读取接收缓冲区的全部数据m_msComm.SetSettings("4800, n, 8, 1"); //设置串口的波特率为4800,//无校验位,8数据位, 1位停止位m_msComm.SetPortOpen(true); // 打开串口,准备通讯}为了方便,我们这里假定PC机一次只接收或发送一个数据。
VC++编写GPS的串口通信作者: ths. 本文可以转载,请以超链接标明原始出处和作者信息及版权声明网址: /?p=347::源代码:: GPS通信真的好多事情,这个代码也是半年前写的了,拿出来贴贴。
阅读此文之前,如果你还不知道如何编写VC++的串口通信,那么你可以先看我的这篇文章:VC++编写串口通信.GPS(Golbal Position System全球定位系统是一个在做一些项目中很常用到的仪器,它通过卫星的通信可以返回给你你当前所处的时间,时区,经纬度等等。
VC++编写GPS通信是属于典型的基本的串口通信例子,而且只用接收信息即可,不用对GPS 发送信息。
编写通信的方法的前半部分都跟我上篇文章说的串口通信一样,即注册ActiveX,建立实例及事件的映射等等。
完成上面的操作后,你就可以接收的GPS 发来的信息了。
CGPSDlg::CGPSDlg(CWnd* pParent/*=NULL*/: CDialog(CGPSDlg::IDD, pParent{m_hIcon = AfxGetApp(->LoadIcon(IDR_MAINFRAME;m_port = 1; //端口号设置为1m_strset = _T("38400, n, 8, 1";//串口参数,波特率:38400, 8为数据位,1为停止位m_inputBuffer = _T(""; //清空输入缓冲区m_str = _T("";}void CGPSDlg::OnBnClickedButton1({//---------------------------------------------------------------------------m_str = _T("";//如果端口原来打开,则需要先关闭if(m_comm.get_PortOpen(m_comm.put_PortOpen(FALSE;//设置连接参数m_comm.put__CommPort(m_port; //指定串口号m_comm.put_Settings(m_strset; //通信参数设置m_comm.put_InBufferSize(1024; //指定接收缓冲区大小m_comm.put_InBufferCount(0; //清空输入缓冲区m_comm.put_InputMode(0; //文本模式获取数据m_comm.put_InputLen(0; //读取缓冲区的全部数据m_comm.put_RThreshold(1; //设置在产生OnComm事件前要接收的字符数阈值//打开端口连接if(!m_comm.get_PortOpen(m_comm.put_PortOpen(TRUE;//---------------------------------------------------------------------------}但因为GPS发送来的信息是源源不断的帧数据,如果你直接阅读这些帧数据,你会发现根本无法阅读。
C语言实现串口通信在使用系统调用函数进行串口通信之前,需要打开串口设备并设置相关参数。
打开串口设备可以使用open(函数,设置串口参数可以使用termios结构体和tcsetattr(函数。
以下是一个简单的串口通信接收数据的示例代码:```c#include <stdio.h>#include <stdlib.h>#include <fcntl.h>#include <unistd.h>#include <termios.h>int mainint fd; // 串口设备文件描述符char buff[255]; // 存储接收到的数据int len; // 接收到的数据长度//打开串口设备fd = open("/dev/ttyS0", O_RDONLY);if (fd < 0)perror("Failed to open serial port");return -1;}//设置串口参数struct termios options;tcgetattr(fd, &options);cfsetspeed(&options, B1200); // 设置波特率为1200 tcsetattr(fd, TCSANOW, &options);//接收数据while (1)len = read(fd, buff, sizeof(buff)); // 从串口读取数据if (len > 0)buff[len] = '\0'; // 将接收到的数据转为字符串printf("Received data: %s\n", buff);}}//关闭串口设备close(fd);return 0;```这段代码首先通过open(函数打开串口设备文件"/dev/ttyS0",然后使用tcgetattr(函数获取当前设置的串口参数,接着使用cfsetspeed(函数设置波特率为1200,最后使用tcsetattr(函数将设置好的串口参数写回。
基于VC++6.0实现的串口通讯摘要:串口通讯作为一种灵活、方便、可靠的通讯方式,广泛应用于计算机与其它设备之间的通讯以及工业控制系统中,是计算机与外部设备进行数据通讯时经常使用的方式之一。
本文介绍了VC++6.0环境下利用MSComm控件实现计算机与外部设备之间的串口通讯。
关键词:串口通讯VC++6.0MSComm一、引言串口作为计算机标准配置中的对外接口,由于其简便、易行及数据传输可靠等优点,使其成为计算机与外部设备之间通讯的主要手段之一,广泛应用于工业控制系统中。
VC++6.0作为面向对象的可视化开发工具,具有良好的界面设计能力,利用具有良好运行效率和稳定性的MSComm控件,可以简单方便的实现串口通讯功能。
二、MSComm控件介绍VC++6.0是基于Windows操作系统的主流编程可视化语言。
应用VC++6.0开发串行通讯通常有如下几种方法:1.利用Windows API通讯函数。
2.利用VC的标准通讯函数_inp,_inpw,_inpd,_outp,_outpw,_outpd等直接对串口进行操作。
3.使用Microsoft Visual C++的通讯控件(MSComm)。
以上几种方法中,前两种由于使用时需要的专业程度比较高,需要了解内部接口及标准通讯函数的定义,因此比较困难;第三种方法较简单,对使用者要求不高,只需对串口进行简单配置即可。
Microsoft Communications Control(以下简称MSComm)是Microsoft公司基于组件对象模型(COM)开发的简化Windows下串口通讯编程的ActiveX控件,它为应用程序提供了通过串口接口收发数据的简便方法。
该控件屏蔽了通讯过程中的底层操作,用户只需通过设置并监视其属性和事件,即可完成串口编程,实现与被控制对象的串行通讯、数据交换,并监视或响应在通讯过程中可能发生的各种错误和事件。
具体有两种处理方法:一是事件驱动(Event-Driven)。
Vc++串口通信(加密解密以及反馈协议)一.主要功能:实现两台计算机通过串口进行数据通信。
二.软件特色:与本软件传输协议不同的串口信息接收到之后不做显示,发送过程中经过数据包加首部尾部来确定数据是否为同一个协议,之后再经过加密发送,接受时先解密,然后经过除去首部跟尾部来得到本来的数据。
三.加密原理:发送时先加首部跟尾部,然后再将所有字符串转换成字符数组,进而对数组中的每个字符进行处理,具体方法是首先获得本次发送的数据总长度,单个字符减去本次字符串总长度的数字,再将数组转换成字符串发送出去,解码时先将受到的字符串转换成字符数组,再将每个字符加上本次接收到的字符串总长度,然后除去首部跟尾部,得到实际有用的数据。
四.协议原理:在每次收到数据之后进行判断,是否为预先设定的反馈数据,如果是,则不做处理,如果不是,则进行发送反馈数据操作。
每次发送完数据对话框有提示“数据已发送“,当收到反馈数据时,提示”数据已成功接收“。
五.主要问题:单次发送的数据不能大于33个字符,否则会接收不正常。
波特率已加到程序里面固定为“115200“,如果太小了可能会出现接收不到信息或者接收速度慢等情况。
六.界面图示:1.发送完数据,但是对方未收到。
2.发送完数据,对方已经成功接收。
七.部分代码界面设计类向导中的函数设计“serilDlg.cpp”文件内部代码#include "stdafx.h"#include "seril.h"#include "serilDlg.h"#ifdef _DEBUG#define new DEBUG_NEW#undef THIS_FILEstatic char THIS_FILE[] = __FILE__;#endifint comseril;///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialog{public:CAboutDlg();// Dialog Data//{{AFX_DATA(CAboutDlg)enum { IDD = IDD_ABOUTBOX };//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CAboutDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL// Implementationprotected://{{AFX_MSG(CAboutDlg)//}}AFX_MSGDECLARE_MESSAGE_MAP()};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD){//{{AFX_DATA_INIT(CAboutDlg)//}}AFX_DATA_INIT}void CAboutDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CAboutDlg)//}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)//{{AFX_MSG_MAP(CAboutDlg)// No message handlers//}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CSerilDlg dialogCSerilDlg::CSerilDlg(CWnd* pParent /*=NULL*/): CDialog(CSerilDlg::IDD, pParent){//{{AFX_DATA_INIT(CSerilDlg)m_strRXData = _T("");m_strTXData = _T("");m_TestFlag = _T("");//}}AFX_DATA_INIT// Note that LoadIcon does not require a subsequent DestroyIcon in Win32 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);}void CSerilDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CSerilDlg)DDX_Control(pDX, IDC_OPENSERIL, m_Opensril);DDX_Control(pDX, IDC_COM, m_serilcom);DDX_Text(pDX, IDC_EDIT_RXDATA, m_strRXData);DDX_Text(pDX, IDC_EDIT_TXDATA, m_strTXData);DDX_Control(pDX, IDC_MSCOMM1, m_ctrlComm);DDX_Text(pDX, IDC_TESTFLAG, m_TestFlag);//}}AFX_DATA_MAP}BEGIN_MESSAGE_MAP(CSerilDlg, CDialog)//{{AFX_MSG_MAP(CSerilDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_CLEAR, OnClear)ON_BN_CLICKED(IDC_CLEARSEND, OnClearsend)ON_CBN_CLOSEUP(IDC_COM, OnCloseupCom)ON_BN_CLICKED(IDC_FASONG, OnFasong)ON_BN_CLICKED(IDC_OPENSERIL, OnOpenseril)//}}AFX_MSG_MAPEND_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////// CSerilDlg message handlersBOOL CSerilDlg::OnInitDialog(){CDialog::OnInitDialog();// Add "About..." menu item to system menu.// IDM_ABOUTBOX must be in the system command range.ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CString strAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// Set the icon for this dialog. The framework does this automatically// when the application's main window is not a dialogSetIcon(m_hIcon, TRUE); // Set big iconSetIcon(m_hIcon, FALSE); // Set small icon// TODO: Add extra initialization herereturn TRUE; // return TRUE unless you set the focus to a control}void CSerilDlg::OnSysCommand(UINT nID, LPARAM lParam){if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialog::OnSysCommand(nID, lParam);}}// If you add a minimize button to your dialog, you will need the code below// to draw the icon. For MFC applications using the document/view model,// this is automatically done for you by the framework.void CSerilDlg::OnPaint(){if (IsIconic()){CPaintDC dc(this); // device context for paintingSendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);// Center icon in client rectangleint cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// Draw the icondc.DrawIcon(x, y, m_hIcon);}else{CDialog::OnPaint();}}// The system calls this to obtain the cursor to display while the user drags// the minimized window.HCURSOR CSerilDlg::OnQueryDragIcon(){return (HCURSOR) m_hIcon;}BEGIN_EVENTSINK_MAP(CSerilDlg, CDialog)//{{AFX_EVENTSINK_MAP(CSerilDlg)ON_EVENT(CSerilDlg, IDC_MSCOMM1, 1 /* OnComm */, OnComm, VTS_NONE)//}}AFX_EVENTSINK_MAPEND_EVENTSINK_MAP()void CSerilDlg::OnComm(){// TODO: Add your control notification handler code hereVARIANT variant_inp;COleSafeArray safearray_inp;LONG len,k;BYTE rxdata[20480],rxtemp[20480]={0},top[20480],down[20480],jm[20480],jm1[20480]; //设置BYTE数组An 8-bit integerthat is not signed.CString strtemp,test,test1;if (m_ctrlComm.GetCommEvent() == 2) //事件值为2表示接收缓冲区内有字符{ // CString rw="rw";////////以下你可以根据自己的通信协议加入处理代码variant_inp = m_ctrlComm.GetInput(); //读缓冲区safearray_inp = variant_inp; //VARIANT型变量转换为ColeSafeArray型变量len = safearray_inp.GetOneDimSize(); //得到有效数据长度for(k = 0;k < len;k++){safearray_inp.GetElement(&k,rxdata+k);//转换为BYTE型数组}if(rxdata[0] == 'r'&&rxdata[1] == 'e'&&rxdata[2] == 'c'&&rxdata[3] == 'i'&&rxdata[4] == 'v'&&rxdata[5] == 'e'){m_TestFlag = "已被成功接收";}else{m_ctrlComm.SetOutput(COleVariant("recive"));}for (k=0;k<5;k++){top[k]=rxdata[k];}for(k=0;k<4;k++){down[k]=rxdata[len-4+k];}if(top[0] == 'b' && top[1] == 'e' && top[2] == 'g' && top[3] == 'i' && top[4] == 'n' && down[0] == 'o' && down[1] == 'v' && down[2] == 'e' && down[3] == 'r'){for(k=0;k<len-9;k++){rxtemp[k] = rxdata[k+5];}for(k=0;k<len-9;k++){jm1[k] = (rxtemp[k] -len+9);}for(k = 0;k < len-9;k++) //将数组转换为Cstring型变量{BYTE bt = *(char*)(jm1+k); //字符型strtemp.Format("%c",bt); //将字符送入临时变量strtemp存放m_strRXData += strtemp;}}}UpdateData(FALSE); //更新编辑框内容(主要是接收编辑框中的)}void CSerilDlg::OnFasong(){long siz,k;CString SendAll,sendal;UpdateData(TRUE); //读取编辑框内容siz = strlen( m_strTXData) ;char cha[20480] ;char cha1[20480];char cha2[20480];strcpy(cha, m_strTXData);for (k=0;k<siz;k++){cha1[k] = cha[k] +siz ;}CString str(cha1,siz);SendAll = "begin" + str + "over";m_ctrlComm.SetOutput(COleVariant(SendAll)); //发送数据m_TestFlag = "数据已发送";UpdateData(FALSE); //读取编辑框内容}void CSerilDlg::OnOpenseril(){// TODO: Add your control notification handler code herem_ctrlComm.SetCommPort(comseril); //选择com口if ( m_ctrlComm.GetPortOpen() ){m_ctrlComm.SetPortOpen(FALSE);// 关闭串口// AfxMessageBox("cannot open serial port");AfxMessageBox("串口已关闭");}else{m_ctrlComm.SetPortOpen(TRUE);//打开串口AfxMessageBox("串口打开成功");SetDlgItemText(IDC_OPENSERIL,"关闭串口"); //显示串口已经关闭m_ctrlComm.SetSettings("115200,n,8,1"); //波特率9600,无校验,8个数据位,1个停止位m_ctrlComm.SetInputMode(1); //1:表示以二进制方式检取数据m_ctrlComm.SetRThreshold(1); //参数1表示每当串口接收缓冲区中有多于或等于1个字符时将引发一个接收数据的OnComm事件m_ctrlComm.SetInputLen(0); //设置当前接收区数据长度为0m_ctrlComm.GetInput(); //先预读缓冲区以清除残留数据// m_TestFlag = "未测试";UpdateData(FALSE);}}void CSerilDlg::OnCloseupCom(){comseril = m_serilcom.GetCurSel();}void CSerilDlg::OnClear(){m_strRXData = "";UpdateData(FALSE); //更新编辑框内容// TODO: Add your control notification handler code here}void CSerilDlg::OnClearsend(){// TODO: Add your control notification handler code herem_strTXData = "";UpdateData(FALSE);}、Seril.h内代码// serilDlg.h : header file////{{AFX_INCLUDES()#include "mscomm.h"//}}AFX_INCLUDES#if !defined(AFX_SERILDLG_H__1B7176B4_DDC7_45B3_B636_2597E461F275__INCLUDED_) #define AFX_SERILDLG_H__1B7176B4_DDC7_45B3_B636_2597E461F275__INCLUDED_#if _MSC_VER > 1000#pragma once#endif // _MSC_VER > 1000/////////////////////////////////////////////////////////////////////////////// CSerilDlg dialogclass CSerilDlg : public CDialog{// Constructionpublic:CSerilDlg(CWnd* pParent = NULL); // standard constructor// Dialog Data//{{AFX_DATA(CSerilDlg)enum { IDD = IDD_SERIL_DIALOG };CButton m_Opensril;CComboBox m_serilcom;CString m_strRXData;CString m_strTXData;CMSComm m_ctrlComm;CString m_TestFlag;//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CSerilDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support//}}AFX_VIRTUAL// Implementationprotected:HICON m_hIcon;// Generated message map functions//{{AFX_MSG(CSerilDlg)virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg void OnClear();afx_msg void OnClearsend();afx_msg void OnCloseupCom();afx_msg void OnFasong();afx_msg void OnComm();afx_msg void OnOpenseril();DECLARE_EVENTSINK_MAP()//}}AFX_MSGDECLARE_MESSAGE_MAP()};//{{AFX_INSERT_LOCATION}}// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif// !defined(AFX_SERILDLG_H__1B7176B4_DDC7_45B3_B636_2597E461F275__INCLUDED_)日期:2013-4-10。
VC串口通讯,API函数介绍介绍工业控制领域利用串口和外围设备进行通讯。
正文前言:总所周之,利用串口进行数据通讯在在通讯通讯领域重占有着重要的地位。
利用RS232-RS485进行数据信号的采集和传递是VC编程的又一大热点。
串口通讯在通讯软件重有着十分广泛的应用。
如电话、传真、视频和各种控制等。
在各种开发工具中间,VC由于功能强大和灵活,同时也得到了Microsoft的最大支持,所以在一般进行涉及硬件操作的通讯编程中,大都推荐使用VC作为开发工具。
然而工业控制串口通讯这个又不同于一般的串口通讯程序,因为控制外围设备传送的大都是十六进制数据(BYTE类型),所以,为了提高程序的运行稳定性,我们在编写程序进行通讯时可以不考虑传送BYTE类型数据的工作。
串口通讯目前流行的方法大概有两种:一是利用Microsoft提供的CMSCOMM 控件进行通讯,不过现在很多程序员都觉应该放弃这种方式。
二是利用WINAPI 函数进行编程,这种编程的难度最高,要求你要掌握很多的API函数。
三是利用现在网络上面提供的一些串口通讯控件进行编写,比如CSerial类等。
程序实现:我在经过许多的项目的开发和实践中发现,采用WIN API函数进行串口的开发能够给程序员很大的空间,并且程序运也很稳定。
所以我将与串口接触的函数进行封装,然后在各个工程中进行调用,效果还是比较好的,现将各个函数和调用方法列举出来,希望对各位有所帮助。
一、设置串口相关工作#define MAXBLOCK 2048#define XON 0x11#define XOFF 0x13BOOL SetCom(HANDLE &m_hCom, const char *m_sPort, int BaudRate, int Databit, CString parity, CString stopbit){COMMTIMEOUTS TimeOuts; ///串口输出时间超时设置DCB dcb; ///与端口匹配的设备m_hCom=CreateFile(m_sPort, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); // 以重叠方式打开串口if(m_hCom==INVALID_HANDLE_VALUE){AfxMessageBox("设置串口部分,串口打开失败"); /////重叠方式异步通信(INVALID_HANDLE_VALUE)函数失败。
在工业控制中,工控机(一般都基于Windows平台)经常需要与智能仪表通过串口进行通信。
串口通信方便易行,应用广泛。
一般情况下,工控机和各智能仪表通过RS485总线进行通信。
RS485的通信方式是半双工的,只能由作为主节点的工控PC机依次轮询网络上的各智能控制单元子节点。
每次通信都是由PC机通过串口向智能控制单元发布命令,智能控制单元在接收到正确的命令后作出应答。
在Win32下,可以使用两种编程方式实现串口通信,其一是使用ActiveX控件,这种方法程序简单,但欠灵活。
其二是调用Windows的API函数,这种方法可以清楚地掌握串口通信的机制,并且自由灵活。
本文我们只介绍API串口通信部分。
串口的操作可以有两种操作方式:同步操作方式和重叠操作方式(又称为异步操作方式)。
同步操作时,API函数会阻塞直到操作完成以后才能返回(在多线程方式中,虽然不会阻塞主线程,但是仍然会阻塞监听线程);而重叠操作方式,API函数会立即返回,操作在后台进行,避免线程的阻塞。
无论那种操作方式,一般都通过四个步骤来完成:(1)打开串口(2)配置串口(3)读写串口(4)关闭串口1、打开串口Win32系统把文件的概念进行了扩展。
无论是文件、通信设备、命名管道、邮件槽、磁盘、还是控制台,都是用API函数CreateFile来打开或创建的。
该函数的原型为:C++代码HANDLE CreateFile( LPCTSTR lpFileName, DWORD dwDesiredAccess, DWOR D dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dw CreationDistribution, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile);lpFileName:将要打开的串口逻辑名,如“COM1”;dwDesiredAccess:指定串口访问的类型,可以是读取、写入或二者并列;dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为0;lpSecurityAttributes:引用安全性属性结构,缺省值为NULL;dwCreationDistribution:创建标志,对串口操作该参数必须置为OPEN_EXISTING;dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值为FILE_FLAG_OVERLAPPED,表示使用异步的I/O;该值为0,表示同步I/O 操作;hTemplateFile:对串口而言该参数必须置为NULL。
同步I/O方式打开串口的示例代码:C++代码重叠I/O打开串口的示例代码:C++代码2、配置串口在打开通讯设备句柄后,常常需要对串口进行一些初始化配置工作。
这需要通过一个DCB结构来进行。
DCB结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。
在查询或配置串口的属性时,都要用DCB结构来作为缓冲区。
一般用CreateFile打开串口后,可以调用GetCommState函数来获取串口的初始配置。
要修改串口的配置,应该先修改DCB结构,然后再调用SetCommState函数设置串口。
DCB结构包含了串口的各项参数设置,下面仅介绍几个该结构常用的变量:typedef struct _DCB{ ……… //波特率,指定通信设备的传输速率。
这个成员可以是实际波特率值或者下面的常量值之一: DWORD BaudRate; CBR_110,CBR_300,CBR_600,CBR_1200,CBR_2400,CBR_4800,CBR_9600,CBR_19200,CBR_38400, CBR_56000, CBR_57600, CBR_115200, CBR_128000, CBR_256000,CBR_14400 DWORD fParity; // 指定奇偶校验使能。
若此成员为1,允许奇偶校验检查… BYTE ByteSize; // 通信字节位数,4—8 BYTE Parity; //指定奇偶校验方法。
此成员可以有下列值: EVENPARITY 偶校验 NOPARITY 无校验MARKPARITY 标记校验 ODDPARITY 奇校验 BYTE StopBits; //指定停止位的位数。
此成员可以有下列值: ONESTOPBIT 1位停止位 TWOSTOPBITS 2位停止位ON 5STOPBITS 1.5位停止位GetCommState函数可以获得COM口的设备控制块,从而获得相关参数:BOOL GetCommState( HANDLE hFile, //标识通讯端口的句柄 LPDCB lpDCB //指向一个设备控制块(DCB结构)的指针 ); SetCommState函数设置COM口的设备控制块: BOOL SetCommState( HANDLE hFile, LPDCB lpDCB );除了在BCD中的设置外,程序一般还需要设置I/O缓冲区的大小和超时。
Windows 用I/O缓冲区来暂存串口输入和输出的数据。
如果通信的速率较高,则应该设置较大的缓冲区。
调用SetupComm函数可以设置串行口的输入和输出缓冲区的大小。
BOOL SetupComm( HANDLE hFile, // 通信设备的句柄 DWORD dwInQueue, // 输入缓冲区的大小(字节数) DWORD dwOutQueue // 输出缓冲区的大小(字节数) );在用ReadFile和WriteFile读写串行口时,需要考虑超时问题。
超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile或WriteFile的操作仍然会结束。
要查询当前的超时设置应调用GetCommTimeouts函数,该函数会填充一个COMMTIMEOUTS结构。
调用SetCommTimeouts可以用某一个COMMTIMEOUTS结构的内容来设置超时。
读写串口的超时有两种:间隔超时和总超时。
间隔超时是指在接收时两个字符之间的最大时延。
总超时是指读写操作总共花费的最大时间。
写操作只支持总超时,而读操作两种超时均支持。
用COMMTIMEOUTS结构可以规定读写操作的超时。
3COMMTIMEOUTS结构的定义为:typedef struct _COMMTIMEOUTS { DWORD ReadIntervalTimeout; //读间隔超时DWORD ReadTotalTimeoutMultiplier; //读时间系数DWORD ReadTotalTimeoutConstant; //读时间常量DWORD WriteTotalTimeoutMultiplier; // 写时间系数DWORD WriteTotalTimeoutConstant; //写时间常量} COMMTIMEOUTS,*LPCOMMTIMEOUTS;COMMTIMEOUTS结构的成员都以毫秒为单位。
总超时的计算公式是:总超时=时间系数×要求读/写的字符数+时间常量例如,要读入10个字符,那么读操作的总超时的计算公式为:读总超时=ReadTotalTimeoutMultiplier×10+ReadTotalTimeoutConstant可以看出:间隔超时和总超时的设置是不相关的,这可以方便通信程序灵活地设置各种超时。
如果所有写超时参数均为0,那么就不使用写超时。
如果ReadIntervalTimeout为0,那么就不使用读间隔超时。
如果ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant 都为0,则不使用读总超时。
如果读间隔超时被设置成MAXDWORD并且读时间系数和读时间常量都为0,那么在读一次输入缓冲区的内容后读操作就立即返回,而不管是否读入了要求的字符。
在用重叠方式读写串口时,虽然ReadFile和WriteFile在完成操作以前就可能返回,但超时仍然是起作用的。
在这种情况下,超时规定的是操作的完成时间,而不是ReadFile和WriteFile的返回时间。
配置串口的示例代码:SetupComm(hCom,1024,1024); //输入缓冲区和输出缓冲区的大小都是1024 COMMTIMEOUTS TimeOuts; //设定读超时TimeOuts.ReadIntervalTimeout=1000; TimeOuts.ReadTotalTimeoutMultiplier=500;TimeOuts.ReadTotalTimeoutConstant=5000; //设定写超时TimeOuts.WriteTotalTimeoutMultiplier=500;TimeOuts.WriteTotalTimeoutConstant=2000; SetCommTimeouts(hCom,&TimeOuts); //设置超时DCB dcb; GetCommState(hCom,&dcb); dcb.BaudRate=9600; //波特率为9600 dcb.ByteSize=8; //每个字节有8位dcb.Parity=NOPARITY; //无奇偶校验位dcb.StopBits=TWOSTOPBITS; //两个停止位SetCommState(hCom,&dcb); PurgeComm(hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);在读写串口之前,还要用PurgeComm()函数清空缓冲区,该函数原型:BOOL PurgeComm( HANDLE hFile, //串口句柄DWORD dwFlags // 需要完成的操作);参数dwFlags指定要完成的操作,可以是下列值的组合:PURGE_TXABORT 中断所有写操作并立即返回,即使写操作还没有完成。
PURGE_RXABORT 中断所有读操作并立即返回,即使读操作还没有完成。
PURGE_TXCLEAR 清除输出缓冲区PURGE_RXCLEAR 清除输入缓冲区3、读写串口我们使用ReadFile和WriteFile读写串口,下面是两个函数的声明:BOOL ReadFile( HANDLE hFile, //串口的句柄 // 读入的数据存储的地址, // 即读入的数据将存储在以该指针的值为首地址的一片内存区 LPVOID lpBuffer, DWORD nNumberOfBytesToRead, // 要读入的数据的字节数 // 指向一个DWORD 数值,该数值返回读操作实际读入的字节数 LPDWORD lpNumberOfBytesRead, // 重叠操作时,该参数指向一个OVERLAPPED结构,同步操作时,该参数为NULL。