MFC通过win32 API方式实现串口通信

参考:

(76条消息) MFC+Modbus-Tcp协议实现温湿度传感器采集-CSDN博客

(76条消息) MFC串口通信的两种方法及注意事项_lintec mfc通讯方式_iwilldoitx的博客-CSDN博客

(76条消息) MFC 串口的读写数据_mfc中读写串口用法_SunSachin的博客-CSDN博客

(75条消息) VC_MFC串口通信编程详解_mfc 串口_贝壳紫铃的博客-CSDN博客

(76条消息) 串口通信程序中十六进制格式发送和接收实现_路和前方的博客-CSDN博客

(76条消息) MFC edit编辑框无法实时更新数据的问题解决_mfc设置编辑框实时显示数据_柠檬有点酸的博客-CSDN博客

(76条消息) 关于Modbus数据16进制转10进制问题_modbus数据转换_ASWaterbenben的博客-CSDN博客

一、基于win32 API的串口读写

  1. 串口通信流程

(1)打开串口

Win32 系统把文件的概念进行了扩展.无论是文件、通信设备、命名管道磁盘、还是控制台,都是用 API 函数 CreateFile来打开或创建的。

HANDLE CreateFile( LPCTSTR lpFileName,
                   DWORD dwDesiredAccess,
                   DWORD dwShareMode,
                   LPSECURITY_ATTRIBUTES lpSecurityAttributes,
                   DWORD dwCreationDistribution,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile);
/*
•   lpFileName:将要打开的串口逻辑名,如“COM1”; 
•   dwDesiredAccess: 指定串口访问的类型,可以是读取、写入或二者并列;一般设为:GENERIC_READ | GENERIC_WRITE 
•   dwShareMode:指定共享属性,由于串口不能共享,该参数必须置为 0//独占方式; 
•   lpSecurityAttributes:引用安全性属性结构,缺省值为 NULL; 
•   dwCreationDistribution:创建标志,对串口操作该参数必须置为 OPEN_EXISTING// 打开而不是创建; 
•   dwFlagsAndAttributes:属性描述,用于指定该串口是否进行异步操作,该值 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,重叠方式; 0,表示同步 I/O ;
•   hTemplateFile:对串口而言该参数必须置为 NULL; 
*/

(2)配置串口

在打开通讯设备句柄后,常需要对串口进行一些初始化配置工作。这需要通过一个DCB 结构来进行。DCB 结构包含了诸如波特率、数据位数、奇偶校验和停止位数等信息。

①用 CreateFile打开串口后,调用 GetCommState函数来获取串口的初始配置。

②修改串口的配置----修改 DCB 结构。

③再调用 SetCommState 函数设置串口。

④设置 I/O 缓冲区的大小和超时。Windows 用 I/O 缓冲区来暂存串口输入和输出的数据。如果通信的速率较高,则应该设置较大 的缓冲区。调用 SetupComm 函数可以设置串行口的输入和输出缓冲区的大小。

⑤ 超时问题:超时的作用是在指定的时间内没有读入或发送指定数量的字符,ReadFile 或 WriteFile 的操作仍然会结束。要查询当前的超时设置应调用 GetCommTimeouts 函数,调用 SetCommTimeouts 可以用某一个 COMMTIMEOUTS 结构的内容 来设置超时.

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 位停止位
//ONE5STOPBITS 1.5 位停止位
  } DCB;
//在 winbase.h 文件中定义了以上用到的常量.如下所示:
#define NOPARITY 0
#define ODDPARITY 1
#define EVENPARITY 2
#define ONESTOPBIT 0
#define ONE5STOPBITS 1
#define TWOSTOPBITS  2
#define CBR_110      110
#define CBR_300      300
#define CBR_600      600
#define CBR_1200     1200
#define CBR_2400     2400
#define CBR_4800     4800
#define CBR_9600     9600
#define CBR_14400   14400
#define CBR_19200   19200
#define CBR_38400   38400
#define CBR_56000   56000
#define CBR_57600   57600
#define CBR_115200   115200
#define CBR_128000   128000
#define CBR_256000 256000

 BOOL OpenComm(CString strComm)方法:

if (NULL == m_hComm)
    {//若为null,才能打开串口相当于创建文件
        m_hComm = CreateFile((TCHAR*)(LPCTSTR)strComm,//COM口  
            GENERIC_READ | GENERIC_WRITE, //允许读和写  
            0, //独占方式  
            NULL,
            OPEN_EXISTING, //打开而不是创建  
            0, //同步方式  
            NULL);
        if (INVALID_HANDLE_VALUE == m_hComm)//无效句柄类型
        {
            int nError = GetLastError();//返回错误类型
            m_hComm = NULL;
            AfxMessageBox(_T("串口打开失败"));
            return FALSE;
        }else
        {
            AfxMessageBox(_T("串口打开成功"));
            BOOL bRet = FALSE;
​
            //输入缓冲区和输出缓冲区的大小都是100
            bRet = ::SetupComm(m_hComm,100,100); 
            if(!bRet) AfxMessageBox(_T("设置串口输入输出缓冲区失败!"));
            
            //设定读超时
            COMMTIMEOUTS TimeOuts;   
            TimeOuts.ReadIntervalTimeout=MAXDWORD;  
            TimeOuts.ReadTotalTimeoutMultiplier=0;  
            TimeOuts.ReadTotalTimeoutConstant=0;  
            
            //设定写超时  
            TimeOuts.WriteTotalTimeoutMultiplier=100;  
            TimeOuts.WriteTotalTimeoutConstant=500;  
            bRet = SetCommTimeouts(m_hComm,&TimeOuts);
            if(!bRet) AfxMessageBox(_T("设置读写操作超时失败!"));
​
            //配置串口属性
            DCB dcb;  
            //获取串口的初始配置
            bRet = ::GetCommState(m_hComm, &dcb); 
            if (!bRet)//不成功,关闭句柄
            {
                if (m_hComm)
                {
                    CloseHandle(m_hComm);
                    m_hComm = NULL;
                    AfxMessageBox(_T("关闭句柄1"));
                }
                return FALSE;
            }else
            {
                //修改串口的配置
                dcb.BaudRate=9600; //波特率 
                dcb.ByteSize=8; //每个字节有8位  
                dcb.Parity=NOPARITY; //奇偶校验位  
                dcb.StopBits=0; //1个停止位  
                //设置串口
                bRet = ::SetCommState(m_hComm, &dcb); 
                if (!bRet)
                {
                    AfxMessageBox(_T("配置串口属性失败!"));
                    if (m_hComm)
                    {
                        CloseHandle(m_hComm);
                        m_hComm = NULL;
                        AfxMessageBox(_T("关闭句柄2"));
                    }
                    return FALSE;
                }
                
​
            }
            
            //在读写串口之前,还要用 PurgeComm()函数清空缓冲区
            bRet = ::PurgeComm(m_hComm, PURGE_RXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);  
            if (!bRet) AfxMessageBox(_T("无法清除串口的错误状态!"));
            
            return TRUE;
        }

(3)读写串口

int ReadData(HANDLE handler, char* buffer)
{
    char readBuffer[512];//读取的内容,读取到的是16进制ASCII码形式
    memset(readBuffer, 0, 512);
    DWORD wCount= 512;//读取的字节数
    BOOL bReadStat;
    
    while(1)
    {
        Sleep(200);
        bReadStat = ReadFile(handler, readBuffer, wCount, &wCount, NULL);
        if(wCount!=0) break;
    }
 
    if(!bReadStat)
    {
        AfxMessageBox("读串口失败!");
        return -1;
    }
    
    //ASCII转换成十六进制字符串
    CString str;
    CString str_HEX;
    for (int i=0;i<9;i++)
    {
        str.Format("%02X ", readBuffer[i]);
        str_HEX = str_HEX +str;
    }

    strcpy(buffer, readBuffer);  
    PurgeComm(handler, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR);    
    return 0;
}
int WriteData(HANDLE handler, char* buffer)//buffer:写入串口的内容
{
    unsigned long dwBytesWrite;
    COMSTAT ComStat;
    DWORD dwErrorFlags;
    BOOL bWriteStat;
    ClearCommError(handler, &dwErrorFlags, &ComStat);
    dwBytesWrite = strlen(buffer);
    bWriteStat=WriteFile(handler, buffer, dwBytesWrite, &dwBytesWrite, NULL);
    if(!bWriteStat)
    {
        AfxMessageBox("写串口失败!");
        return -1;
    }
    return 0;
}

(4)关闭串口

void SerialPort::CloseComm()
{
    if (m_hComm)
    {
        CloseHandle(m_hComm);
        m_hComm = NULL;
        AfxMessageBox(_T("成功关闭串口"));
    }
}

二、CRC校验

const unsigned char m_auchCRCHi[] =
{
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
    0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
    0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
​
const unsigned char m_auchCRCLo[] =
{
    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
    0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
    0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
    0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
    0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
    0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
    0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
    0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
    0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
    0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
    0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
    0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
    0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
    0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
    0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
    0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
    0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
    0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
    0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
​
///计算CRC_ModBus 校验码
unsigned short  SerialPort::CRC_Modbus(unsigned char *puchMsg, unsigned short usDataLen)
{
    unsigned char uchCRCHi = 0xFF;
    unsigned char uchCRCLo = 0xFF;
    unsigned short uIndex;
​
    while (usDataLen--)
    {
        uIndex = uchCRCHi^*puchMsg++;
        uchCRCHi = uchCRCLo^m_auchCRCHi[uIndex];
        uchCRCLo = m_auchCRCLo[uIndex];
    }
    return(uchCRCHi << 8 | uchCRCLo);
}

你可能感兴趣的:(上位机,c++,mfc)