UE4串口通讯

UE4中实现串口通讯有多种方式,有通过插件实现的,有通过模块实现的,有通过把C++代码编译成DLL实现的,其实最简单的方式就是直接把代码转换成UE4的能支持的类,比如继承自AActor类,然后再把该类拖到UE4场景中生成该类的一个实例。这样就会避免程序版本变化时,要求重新编译,可能会编译不通过等各种问题。废话不多说,show you the code。

头文件:

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "AllowWindowsPlatformTypes.h"
#include "Windows.h"

#include "GameFramework/Actor.h"
#include "SerialUtls.generated.h"

UCLASS()
class SERIALPORTS_API ASerialUtls : public AActor
{
    GENERATED_BODY()

public: 
    // Sets default values for this actor's properties
    ASerialUtls();
    ~ASerialUtls();

    // Called when the game starts or when spawned
    virtual void BeginPlay() override;

    // Called every frame
    virtual void Tick( float DeltaSeconds ) override;

public:
    /** 初始化串口函数
    *
    *  @param:  UINT portNo 串口编号,默认值为1,即COM1,注意,尽量不要大于9
    *  @param:  UINT baud   波特率,默认为9600
    *  @param:  char parity 是否进行奇偶校验,'Y'表示需要奇偶校验,'N'表示不需要奇偶校验
    *  @param:  UINT databits 数据位的个数,默认值为8个数据位
    *  @param:  UINT stopsbits 停止位使用格式,默认值为1
    *  @param:  DWORD dwCommEvents 默认为EV_RXCHAR,即只要收发任意一个字符,则产生一个事件
    *  @return: bool  初始化是否成功
    *  @note:   在使用其他本类提供的函数前,请先调用本函数进行串口的初始化
    *      \n本函数提供了一些常用的串口参数设置,若需要自行设置详细的DCB参数,可使用重载函数
    *           \n本串口类析构时会自动关闭串口,无需额外执行关闭串口
    *  @see:
    */
    bool InitPort(UINT portNo = 2, UINT baud = CBR_9600, char parity = 'N',
        UINT databits = 8, UINT stopsbits = 1, DWORD dwCommEvents = EV_RXCHAR);

    /** 向串口写数据
    *
    *  将缓冲区中的数据写入到串口
    *  @param:  unsigned char * pData 指向需要写入串口的数据缓冲区
    *  @param:  unsigned int length 需要写入的数据长度
    *  @return: bool  操作是否成功
    *  @note:   length不要大于pData所指向缓冲区的大小
    *  @see:
    */
    bool WriteData(unsigned char* pData, unsigned int length);


private:
    /** 打开串口
    *
    *
    *  @param:  UINT portNo 串口设备号
    *  @return: bool  打开是否成功
    *  @note:
    *  @see:
    */
    bool openPort(UINT  portNo);

    /** 关闭串口
    *
    *
    *  @return: void  操作是否成功
    *  @note:
    *  @see:
    */
    void ClosePort();

private:
    /** 串口句柄 */
    HANDLE  m_hComm;
};

源文件:

// Fill out your copyright notice in the Description page of Project Settings.

#include "SerialPorts.h"
#include "SerialUtls.h"
#include <process.h>

// Sets default values
ASerialUtls::ASerialUtls()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = true;

    m_hComm = INVALID_HANDLE_VALUE;
}

ASerialUtls::~ASerialUtls()
{
    ClosePort();
}


// Called when the game starts or when spawned
void ASerialUtls::BeginPlay()
{
    Super::BeginPlay();

    InitPort(3, CBR_9600, 'N', 8U, 1U, EV_RXCHAR);
}

// Called every frame
void ASerialUtls::Tick( float DeltaTime )
{
    Super::Tick( DeltaTime );

    unsigned char outString[] = "F88F0280808000002000";

    WriteData(outString, 21);
}


bool ASerialUtls::InitPort(UINT portNo, UINT baud, char parity,
    UINT databits, UINT stopsbits, DWORD dwCommEvents)
{

    /** 临时变量,将制定参数转化为字符串形式,以构造DCB结构 */
    char szDCBparam[50];
    sprintf_s(szDCBparam, "baud=%d parity=%c data=%d stop=%d", baud, parity, databits, stopsbits);

    //** DEBUG
    FString tempString(szDCBparam);
    if (GEngine)
    {
        GEngine->AddOnScreenDebugMessage(1, 5.f, FColor::Red, tempString);
    }
    else
    {
        UE_LOG(LogTemp, Warning, TEXT("baud=%d parity=%c data=%d stop=%d"), baud, parity, databits, stopsbits);
    }

    /** 打开指定串口,该函数内部已经有临界区保护,上面请不要加保护 */
    if (!openPort(portNo))
    {
        return false;
    }

    /** 是否有错误发生 */
    BOOL bIsSuccess = TRUE;

    /** 设置串口的超时时间,均设为0,表示不使用超时限制 */
    COMMTIMEOUTS  CommTimeouts;
    CommTimeouts.ReadIntervalTimeout = 0;
    CommTimeouts.ReadTotalTimeoutMultiplier = 0;
    CommTimeouts.ReadTotalTimeoutConstant = 0;
    CommTimeouts.WriteTotalTimeoutMultiplier = 0;
    CommTimeouts.WriteTotalTimeoutConstant = 0;
    if (bIsSuccess)
    {
        bIsSuccess = SetCommTimeouts(m_hComm, &CommTimeouts);
    }

    DCB  dcb;
    if (bIsSuccess)
    {
        // 将ANSI字符串转换为UNICODE字符串
        DWORD dwNum = MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, NULL, 0);
        wchar_t *pwText = new wchar_t[dwNum];
        if (!MultiByteToWideChar(CP_ACP, 0, szDCBparam, -1, pwText, dwNum))
        {
            bIsSuccess = TRUE;
        }

        /** 获取当前串口配置参数,并且构造串口DCB参数 */
        bIsSuccess = GetCommState(m_hComm, &dcb) && BuildCommDCB(pwText, &dcb);
        /** 开启RTS flow控制 */
        dcb.fRtsControl = RTS_CONTROL_ENABLE;

        /** 释放内存空间 */
        delete[] pwText;
    }

    if (bIsSuccess)
    {
        /** 使用DCB参数配置串口状态 */
        bIsSuccess = SetCommState(m_hComm, &dcb);
    }

    /**  清空串口缓冲区 */
    PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_TXCLEAR | PURGE_RXABORT | PURGE_TXABORT);

    return bIsSuccess == TRUE;
}

bool ASerialUtls::openPort(UINT portNo)
{
    /** 把串口的编号转换为设备名 */
    char szPort[50];
    sprintf_s(szPort, "COM%d", portNo);

    /** 打开指定的串口 */
    m_hComm = CreateFileA(szPort,                       /** 设备名,COM1,COM2等 */
        GENERIC_READ | GENERIC_WRITE,  /** 访问模式,可同时读写 */
        0,                             /** 共享模式,0表示不共享 */
        NULL,                           /** 安全性设置,一般使用NULL */
        OPEN_EXISTING,                  /** 该参数表示设备必须存在,否则创建失败 */
        0,
        0);

    /** 如果打开失败,释放资源并返回 */
    if (m_hComm == INVALID_HANDLE_VALUE)
    {
        return false;
    }

    return true;
}

void ASerialUtls::ClosePort()
{
    /** 如果有串口被打开,关闭它 */
    if (m_hComm != INVALID_HANDLE_VALUE)
    {
        CloseHandle(m_hComm);
        m_hComm = INVALID_HANDLE_VALUE;
    }
}

bool ASerialUtls::WriteData(unsigned char* pData, unsigned int length)
{
    BOOL   bResult = TRUE;
    DWORD  BytesToSend = 0;

    if (m_hComm == INVALID_HANDLE_VALUE)
    {

        FString msg("failure!");

        if (GEngine)
        {
            GEngine->AddOnScreenDebugMessage(2, 5.f, FColor::Red, msg);
        }

        return false;
    }

    /** 向缓冲区写入指定量的数据 */
    bResult = WriteFile(m_hComm, pData, length, &BytesToSend, NULL);
    if (!bResult)
    {
        DWORD dwError = GetLastError();
        /** 清空串口缓冲区 */
        PurgeComm(m_hComm, PURGE_RXCLEAR | PURGE_RXABORT);
        //** LeaveCriticalSection(&m_csCommunicationSync);

        return false;
    }

    UE_LOG(LogTemp, Warning, TEXT("pData=%s length=%d"), pData, length);


    return true;
}

串口实现部分是参考的是C++串口编程实例。这里主要实现的是每帧向串口(COM3)发送字符串。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值