• 9202阅读
  • 14回复

[提问]请教各位大侠一个关于Qt串口通信的问题~谢谢 [复制链接]

上一主题 下一主题
离线wenddywong
 
只看楼主 倒序阅读 楼主  发表于: 2012-06-27
各位,由于编程需要,现在要将在VC++下编写的串口程序改写在qt平台下,但是本人对Qt串口通信着实不熟悉,鼓捣了几天还是没有成功,因此在此向各位大侠请教。
主要就是从传感器获取数据,十六进制的数据包,例如:
55 AA 5A f3 07 00 00 00 01 05 59
55是包头
07后面的00 00 00 01 05是数据块。59是校验和,也就是数据块之和。
不停的发送,而程序这边设置的每隔200ms接收一次,当然要保证数据的完整性,然后再解析成十进制的数据发送到绘图界面。大致就是这样一个功能.
下面是VC源代码:
comm.h
#pragma once

#include <iostream>
#include <fstream>

using namespace std;

#define FORMAT setiosflags(ios::fixed) << setiosflags(ios::left) << setw(19)

typedef struct _CMD_BAG1
{
    byte  byHead[3];                            // 包头:0x55、0xAA、0x5A
    byte  byCode;                                // 命令码:0x00(采集数据);0x01(停止采集)
    unsigned short  usLength;                    // 包长:包头和数据块的字节数,即3+4
    byte  byData[4];                            // 数据块
    byte  byChkUnit;                            // 校验和
}CMD_BAG1;                                        // 命令长度为11 Bytes

typedef struct _CMD_BAG2
{
    byte  byHead[3];
    byte  byData[8];
}CMD_BAG2;                                    // 命令长度为11 Bytes

typedef union _COMM_BAG
{
    CMD_BAG1 CmdBag1;
    CMD_BAG2 CmdBag2;
    byte     byData[11];            // 用sizeof获取变量类型的大小时与编译器的对齐方式有关,此处sizeof(CMD_BAG)=12,故需减1
}COMM_BAG;

class CComm
{
public:
    CComm(void);
    ~CComm(void);
    bool Init(void);
    static DWORD WINAPI ReadPortThread(LPVOID lpParameter);
    void ReadRecvData(void);
    void SearchBagHead(byte lbyData);
    void ReceiveCmdInBag(byte byRecvChar);
    void handleSampData(void);
    ULONG ObtainTotalWatt(void);

public:
    HANDLE m_hComm;
    HANDLE m_hCommRecvThread;
    bool bIsHeadArrived;
    ULONG ulRecvDataCnt;
    COMM_BAG CommInData;
    ofstream flPowerPara;
    ULONG ulSampDataArrayCnt;
    ULONG ulTotalWatt;
};

extern CComm MyComm;

comm.cpp
#include "StdAfx.h"
#include "Comm.h"

CComm MyComm;

CComm::CComm(void)
{
    m_hComm = INVALID_HANDLE_VALUE;
    m_hCommRecvThread = NULL;
    ulTotalWatt = 0;
}

CComm::~CComm(void)
{
    if (flPowerPara.is_open())
    {
        flPowerPara.close();
        //CloseHandle(flPowerPara);
    }

    if (m_hComm != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
    }
}

// 初始化可用串口,打开数据采集线程和数据文件,成功则返回true,失败则返回false
bool CComm::Init(void)
{
    CString lcString;
    byte i;

    for (i=0; i<8; ++i)
    {                                                    // 最多搜索八个串口单元
        lcString.Format(_T("COM%d:"), i+1);
        m_hComm = CreateFile(lcString,                        // 文件名
            GENERIC_READ | GENERIC_WRITE,                    // 允许读和写
            0,                                                // 独占方式
            NULL,
            OPEN_EXISTING,                                    // 打开端口而不是创建
            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,    // 重叠方式
            NULL
            );

        if (m_hComm != INVALID_HANDLE_VALUE)
        {                                                    // 串口已打开
            DCB dcb;
            GetCommState(m_hComm, &dcb);                                // 获取串口状态并保存在dcb中

            dcb.BaudRate = 9600;                                        // 波特率为9600
            dcb.ByteSize = 8;                                            // 端口使用的数据为8bits
            dcb.Parity = NOPARITY;                                        // 无奇偶校验
            dcb.StopBits = ONESTOPBIT;                                    // 1bit停止位
            dcb.fBinary = true;                                            // 二进制方式
            dcb.fParity = false;                                        // 不允许奇偶校验
            dcb.DCBlength = sizeof(DCB);

            if (SetCommState(m_hComm, &dcb))
            {
                COMMTIMEOUTS  CommTimeouts;                                    // 用于超时设置
                GetCommTimeouts(m_hComm, &CommTimeouts);

                CommTimeouts.ReadIntervalTimeout = MAXDWORD;
                CommTimeouts.ReadTotalTimeoutMultiplier = 0;                // 读时间系数
                CommTimeouts.ReadTotalTimeoutConstant = 0;                    // 读时间常数
                CommTimeouts.WriteTotalTimeoutMultiplier = 50;                // 写时间系数
                CommTimeouts.WriteTotalTimeoutConstant = 100;                // 写时间常数

                if (SetCommTimeouts(m_hComm, &CommTimeouts))
                {
                    SetupComm(m_hComm, 1024, 1024);                                // 设置输入输出缓冲区
                    SetCommMask(m_hComm, EV_RXCHAR);                            // 通信事件(接收到一个字符,并放入输入缓冲区)
                    PurgeComm(m_hComm, PURGE_TXABORT | PURGE_RXABORT
                        | PURGE_TXCLEAR | PURGE_RXCLEAR);                        // 终止所有正在进行的字符输入/出操作和清除串口输入/出缓冲区

                    if (!flPowerPara.is_open())
                    {
                        ulSampDataArrayCnt = 0;                                    // 采集数据清零

                        //flPowerPara.open(_T("C:\\PowerPara.txt"));    // 打开数据采集文件

                        flPowerPara.open(_T("\\NAND FLASH\\PM\\PowerPara.txt"));    // 打开数据采集文件

                    }

                    if (NULL == m_hCommRecvThread)
                    {
                        m_hCommRecvThread = CreateThread(NULL, 0, ReadPortThread,
                            NULL, 0, NULL);                                        // 开启串口接收数据线程
                    }
                }
            }

            printf("COM%d打开成功!", i+1);

            return true;
        }
    }
    return false;
}

DWORD WINAPI CComm::ReadPortThread(LPVOID lpParameter)
{
    while (true)
    {
        MyComm.ReadRecvData();
    }

    return 0;
}

// 采集数据
void CComm::ReadRecvData(void)
{
    byte lbyData;

    DWORD  evtMask;
    byte * readBuf = NULL;                                                    // 读取的字节

    DWORD actualReadLen = 0;                                                // 实际读取的字节数

    DWORD willReadLen;

    DWORD dwReadErrors;
    COMSTAT cmState;

    if (WaitCommEvent(m_hComm, &evtMask, 0))
    {            
        if (evtMask & EV_RXCHAR)
        {                                                                    // 串口收到字符
            ClearCommError(m_hComm, &dwReadErrors, &cmState);

            willReadLen = cmState.cbInQue;
            if (willReadLen <= 0)
            {
                return;
            }

            readBuf = new BYTE[willReadLen];
            ReadFile(m_hComm, readBuf, willReadLen, &actualReadLen, 0);

            if (actualReadLen > 0)
            {                                                                    // 串口已接收字符
                byte *buf = readBuf;
                for (ULONG i=0; i<actualReadLen; ++i, ++buf)
                {
                    lbyData = *buf;                                            // 获取指针所指地址的值

                    if (!bIsHeadArrived)
                    {                                                            // 包头尚未到达,搜索包头
                        SearchBagHead(lbyData);
                    }
                    else
                    {                                                            // 命令包尚未到达,搜索命令包
                        if (ulRecvDataCnt > 8)
                        {
                            bIsHeadArrived = false;
                        }
                        else
                        {
                            ReceiveCmdInBag(lbyData);
                        }
                    }
                }
            }
        }
    }
    if (readBuf != NULL)
    {
        delete readBuf;
        readBuf = NULL;
    }
}

// 搜索包头
void CComm::SearchBagHead(byte lbyData)
{
    CommInData.CmdBag1.byHead[0] = CommInData.CmdBag1.byHead[1];    // 移动包头缓冲区
    CommInData.CmdBag1.byHead[1] = CommInData.CmdBag1.byHead[2];
    CommInData.CmdBag1.byHead[2] = lbyData;

    if ((CommInData.CmdBag1.byHead[0] == 0x55) && (CommInData.CmdBag1.byHead[1] == 0xaa) && (CommInData.CmdBag1.byHead[2] == 0x5a))
    {
        bIsHeadArrived = true;                                            // 设置包头到达标志
        ulRecvDataCnt = 0;                                                // 复位接收数据计数器
    }
}

// 获取采集数据
void CComm::ReceiveCmdInBag(byte byRecvChar)
{
    byte lbyChekSum;

    CommInData.CmdBag2.byData[ulRecvDataCnt] = byRecvChar;        // 数据移入缓冲区

    if (ulRecvDataCnt > 2)
    {
        if (CommInData.CmdBag1.usLength == ulRecvDataCnt)
        {                                                            // 包长接收完毕
            bIsHeadArrived = false;                                    // 包头到达标志复位,允许下次包头接收
            lbyChekSum = 0;

            for (byte i=0; i<ulRecvDataCnt+3; i++)
            {
                lbyChekSum += CommInData.byData;                            // 计算校验和
            }

            if (lbyChekSum == CommInData.CmdBag2.byData[ulRecvDataCnt])
            {                                                            // 接收正确
                handleSampData();                                        // 处理采集数据
            }
        }
    }

    ++ulRecvDataCnt;
}

// 解析数据
void CComm::handleSampData(void)
{
    CString lcsChar, lcsString;
    ULONG lulData;

    lulData = (ULONG)CommInData.CmdBag1.byData[0]*1000 + (ULONG)CommInData.CmdBag1.byData[1]*100
                + (ULONG)CommInData.CmdBag1.byData[2]*10 + (ULONG)CommInData.CmdBag1.byData[3];
}
离线roywillow

只看该作者 1楼 发表于: 2012-06-27
Re:请教各位大侠一个问题~谢谢
建议你最好先说一下是什么样的问题,这么多代码看起来很头疼的……
专业维修核潜艇,回收二手航母、二手航天飞机,大修核反应堆,拆洗导弹发动机更换机油,无人侦察机手动挡改自动,航天飞机保养换三滤,飞碟外太空年检 ,各型号导弹加装迎宾踏板,高空作业擦洗卫星表面除尘、打蜡及抛光,东风全系列巡航导弹。并提供原子对撞机。量大从优,有正规发票。
离线itstudy
只看该作者 2楼 发表于: 2012-06-27
#include <windows.h>
#program(lib xxxx)应该就能移植到VC平台下了吧?
我在Qt中就能调用win32 API
离线wenddywong
只看该作者 3楼 发表于: 2012-06-27
回 1楼(roywillow) 的帖子
主要就是从传感器获取数据,十六进制的数据包,例如:
55 AA 5A f3 07 00 00 00 01 05 59
55是包头
07后面的00 00 00 01 05是数据段。
不停的发送,而程序这边设置的每隔200ms接收一次,当然要保证数据的完整性,然后再解析成十进制的数据发送到绘图界面。大致就是这样一个功能
离线wenddywong
只看该作者 4楼 发表于: 2012-06-27
回 2楼(itstudy) 的帖子
请问:itstudy
这样是怎么实现的呢?我是将VC++下的程序移植到Qt下面去呢
离线itstudy
只看该作者 5楼 发表于: 2012-06-27
回 4楼(wenddywong) 的帖子
你用QtCreater或者VS建立一个QT工程 将你VC工程中的类移植到QT中  注意包含所需要的头文件与lib(dll)库就可以了
离线wenddywong
只看该作者 6楼 发表于: 2012-06-27
回 5楼(itstudy) 的帖子
谢谢你啊,但是我这不行啊,调用了很多MFC的东西,在Qt里识别不出来
离线roywillow

只看该作者 7楼 发表于: 2012-06-28
回 6楼(wenddywong) 的帖子
只要有头文件和库就没问题
另外creator不识别不代表编译器不识别
专业维修核潜艇,回收二手航母、二手航天飞机,大修核反应堆,拆洗导弹发动机更换机油,无人侦察机手动挡改自动,航天飞机保养换三滤,飞碟外太空年检 ,各型号导弹加装迎宾踏板,高空作业擦洗卫星表面除尘、打蜡及抛光,东风全系列巡航导弹。并提供原子对撞机。量大从优,有正规发票。
离线wenddywong
只看该作者 8楼 发表于: 2012-06-28
回 7楼(roywillow) 的帖子
编译通不过怎么办?
离线roywillow

只看该作者 9楼 发表于: 2012-06-28
回 8楼(wenddywong) 的帖子
怎么个不通?错误是啥?
专业维修核潜艇,回收二手航母、二手航天飞机,大修核反应堆,拆洗导弹发动机更换机油,无人侦察机手动挡改自动,航天飞机保养换三滤,飞碟外太空年检 ,各型号导弹加装迎宾踏板,高空作业擦洗卫星表面除尘、打蜡及抛光,东风全系列巡航导弹。并提供原子对撞机。量大从优,有正规发票。
离线dxfans

只看该作者 10楼 发表于: 2012-06-29
Re:回 1楼(roywillow) 的帖子
引用第3楼wenddywong于2012-06-27 17:44发表的 回 1楼(roywillow) 的帖子 :
主要就是从传感器获取数据,十六进制的数据包,例如:
55 AA 5A f3 07 00 00 00 01 05 59
55是包头
07后面的00 00 00 01 05是数据段。
不停的发送,而程序这边设置的每隔200ms接收一次,当然要保证数据的完整性,然后再解析成十进制的数据发送到绘图界面。大致就是这样一个功能


把问题描述清楚,是你这个功能不会做还是在做的过程中有问题需要解决??
离线wenddywong
只看该作者 11楼 发表于: 2012-06-29
继续顶,有人能帮忙吗?
离线roywillow

只看该作者 12楼 发表于: 2012-06-29
回 11楼(wenddywong) 的帖子
现在想帮你的人最想知道的就是你编译的时候究竟碰到了什么错误……
专业维修核潜艇,回收二手航母、二手航天飞机,大修核反应堆,拆洗导弹发动机更换机油,无人侦察机手动挡改自动,航天飞机保养换三滤,飞碟外太空年检 ,各型号导弹加装迎宾踏板,高空作业擦洗卫星表面除尘、打蜡及抛光,东风全系列巡航导弹。并提供原子对撞机。量大从优,有正规发票。
离线wenddywong
只看该作者 13楼 发表于: 2012-06-30
哪位能帮帮忙呢?
离线wenddywong
只看该作者 14楼 发表于: 2012-06-30
回 10楼(dxfans) 的帖子
谢谢你的回复,准确说是在Qt下实现这个功能的时候遇到问题,不知道怎样去进行数据包头搜索,数据完整性校验。
也谢谢roywillow,按照你说的把VC下面写的类直接放到qt工程下编译,但是没有成功,出现一大堆编译错误,不知道怎样去操作。。。
快速回复
限100 字节
 
上一个 下一个