windows c++ 串口通讯,G代码控制3轴电机

基于某品牌智能写字机做二次开发,使用RS232进行通讯,使用G-code进行控制。


​G代码(G-code,又称RS-274),是最为广泛使用的数控(numerical control)编程语言,有多个版本,主要在计算机辅助制造中用于控制自动机床。G代码有时候也称为G编程语言。
G代码是数控程序中的指令。一般都称为G指令。使用G代码可以实现快速定位、逆圆插补、顺圆插补、中间点圆弧插补、半径编程、跳转加工。


常见的 G 代码命令

G00 – 快速定位

G00 命令以最大行驶速度将机器从当前位置移动到指定点或命令指定的坐标。机器将同时移动所有轴,以便它们同时完成行程。这会导致直线移动到新的位置点。

G00 G代码命令 - 线性快速定位
G00是一种非切割运动,其目的是将机器快速移动到所需位置以开始某种工作,例如切割或打印。

G01 – 线性插值

G01 G 代码命令指示机器以设定的进给速率或速度直线移动。我们使用 XY 和 Z 值指定结束位置,并使用 F 值指定速度。机器控制器计算(插值)要通过的中间点以获得该直线。虽然这些G代码命令很简单,理解起来非常直观,但在它们背后,机器控制器每秒执行数千次计算,以便进行这些运动。

G01 - 线性插值 - 最常见的 G 代码命令

与仅用于定位的 G00 命令不同,G01 命令在机器执行其主要作业时使用。在车床或铣床的情况下,直线切割材料,在3D打印机的情况下,以直线挤出材料。
虽然从名字上看,G0叫做“快速直线移动”,而G1叫做“直线移动”,但实际上在Repetier-firmware里面,G0和G1指令是完全等价的,没有任何区别。移动是否快速,完全是靠参数F来决定的。这条指令的作用也很简单,就是将挤出头线性移动到一个特定的位置。这条指令带有不少参数,完整的形式是这样的:
G0 Xnnn Ynnn Znnn Ennn Fnnn Snnn
或者

G1 Xnnn Ynnn Znnn Ennn Fnnn Snnn
使用时,不需要所有的参数全部存在,但至少要有一个参数。其中,

  • Xnnn表示X轴的移动位置;
  • Ynnn表示Y轴的移动位置;
  • Znnn表示Z轴的移动位置;
  • Ennn表示E轴(挤出头步进电机)的移动位置;
  • Fnnn表示速度,单位是毫米/每分钟;
  • Snnn表示是否检查限位开关,S0不检查,S1检查,缺省值是S0;

代码:

serial.h
#ifndef _WZSERIALPORT_H
#define _WZSERIALPORT_H
#include <string>
class WZSerialPort{
public:
    WZSerialPort();
    ~WZSerialPort();

    //打开串口,成功返回true,失败返回false
    //portname(串口名): 在Windows下是"COM1""COM2"等,在Linux下是"/dev/ttyS1"等
    //baudrate(波特率): 9600、19200、38400、43000、56000、57600、115200
    //parity(校验位): 0为无校验,1为奇校验,2为偶校验,3为标记校验
    //databit(数据位): 4-8,通常为8位
    //stopbit(停止位): 1为1位停止位,2为2位停止位,3为1.5位停止位
    //synchronizable(同步、异步): 0为异步,1为同步

    bool open(const char* portname, int baudrate = 115200, char parity = 0, char databit = 8, char stopbit = 1, char synchronizeflag = 1);

    //关闭串口,参数待定
    void close();

    //发送ASCII数据或写数据,成功返回发送数据长度,失败返回0
    int send(std::string dat);


    //发送hex数据或写数据,成功返回发送数据长度,失败返回0
    int sendHex(std::string dat);

    //接受数据或读数据,成功返回读取实际数据的长度,失败返回0
    std::string receive();

private:
    int pHandle[16];
    char synchronizeflag;
};

#endif
serial.cpp
#include "serial.h"
#include <WinSock2.h>

char* HexStrFilter(char* charStr, char* fiterStr) {
    if (charStr == NULL || fiterStr == NULL) {
        return NULL;
    }

    int len = strlen(charStr);
    char* pTemp = fiterStr;
    for (int i = 0; i < len; i++) {
        if (((charStr[i] >= '0') && (charStr[i] <= '9')) ||
                ((charStr[i] >= 'A') && (charStr[i] <= 'F')) ||
                ((charStr[i] >= 'a') && (charStr[i] <= 'f')))
        {
            *fiterStr++ = charStr[i];
        }
    }
    return pTemp;
}

char CombineHexChar(char charH, char charL) /// CombineHexChar(A,B) result=1011;
{
    char result;

    if (charH >= '0' && charH <= '9') {
        result = (charH - '0');
    }
    else if (charH >= 'a' && charH <= 'f') {
        result = (charH - 'a' + 10);
    }
    else if (charH >= 'A' && charH <= 'F') {
        result = (charH - 'A' + 10);
    }
    else {
        result = 0;/// need to fiter non-hex character
    }

    result <<= 4;
    if (charL >= '0' && charL <= '9') {
        result += (charL - '0');
    }
    else if (charL >= 'a' && charL <= 'f') {
        result += (charL - 'a' + 10);
    }
    else if (charL >= 'A' && charL <= 'F') {
        result += (charL - 'A' + 10);
    }
    else {
        result += 0;
    }
    return result;
}

int Char2Hex(char* charStr, char* hexStr)/// character to hex, return value is hexStr length
{
    if (charStr == NULL || hexStr == NULL)
    {
        return 0;
    }

    int hexStrCount = 0;

    char* fiterStr = NULL;
    fiterStr = new char[strlen(charStr) + 1];
    memset(fiterStr, 0, strlen(charStr) + 1);

    HexStrFilter(charStr, fiterStr);///filter non-hex character

    int len = strlen(fiterStr);

    // warn: if charStr length not even, the last charactor will lost
    for (int i = 0; i < len / 2; i++)
    {
        *hexStr++ = CombineHexChar(fiterStr[i * 2], fiterStr[i * 2 + 1]);
        hexStrCount++;
    }

    if (fiterStr)
    {
        delete[] fiterStr;
        fiterStr = NULL;
    }

    return hexStrCount;
}



WZSerialPort::WZSerialPort()
{

}

WZSerialPort::~WZSerialPort()
{

}

bool WZSerialPort::open(const char* portname,
                        int baudrate,
                        char parity,
                        char databit,
                        char stopbit,
                        char synchronizeflag)
{
    this->synchronizeflag = synchronizeflag;
    HANDLE hCom = NULL;
    if (this->synchronizeflag)
    {
        //同步方式
        hCom = CreateFileA(portname, //串口名
                           GENERIC_READ | GENERIC_WRITE, //支持读写
                           0, //独占方式,串口不支持共享
                           NULL,//安全属性指针,默认值为NULL
                           OPEN_EXISTING, //打开现有的串口文件
                           0, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
                           NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    }
    else
    {
        //异步方式
        hCom = CreateFileA(portname, //串口名
                           GENERIC_READ | GENERIC_WRITE, //支持读写
                           0,       //独占方式,串口不支持共享
                           NULL,  //安全属性指针,默认值为NULL
                           OPEN_EXISTING, //打开现有的串口文件
                           FILE_FLAG_OVERLAPPED, //0:同步方式,FILE_FLAG_OVERLAPPED:异步方式
                           NULL);//用于复制文件句柄,默认值为NULL,对串口而言该参数必须置为NULL
    }

    if (hCom == (HANDLE)-1)
    {
        return false;
    }

    //配置缓冲区大小
    if (!SetupComm(hCom, 1024, 1024))
    {
        return false;
    }

    // 配置参数
    DCB p;
    memset(&p, 0, sizeof(p));
    p.DCBlength = sizeof(p);
    p.BaudRate = baudrate; // 波特率
    p.ByteSize = databit; // 数据位

    switch (parity) //校验位
    {
    case 0:
        p.Parity = NOPARITY; //无校验
        break;
    case 1:
        p.Parity = ODDPARITY; //奇校验
        break;
    case 2:
        p.Parity = EVENPARITY; //偶校验
        break;
    case 3:
        p.Parity = MARKPARITY; //标记校验
        break;
    }

    switch (stopbit) //停止位
    {
    case 1:
        p.StopBits = ONESTOPBIT; //1位停止位
        break;
    case 2:
        p.StopBits = TWOSTOPBITS; //2位停止位
        break;
    case 3:
        p.StopBits = ONE5STOPBITS; //1.5位停止位
        break;
    }

    if (!SetCommState(hCom, &p))
    {
        // 设置参数失败
        return false;
    }

    //超时处理,单位:毫秒
    //总超时=时间系数×读或写的字符数+时间常量
    COMMTIMEOUTS TimeOuts;
    TimeOuts.ReadIntervalTimeout = 1000; //读间隔超时
    TimeOuts.ReadTotalTimeoutMultiplier = 500; //读时间系数
    TimeOuts.ReadTotalTimeoutConstant = 5000; //读时间常量
    TimeOuts.WriteTotalTimeoutMultiplier = 500; // 写时间系数
    TimeOuts.WriteTotalTimeoutConstant = 2000; //写时间常量
    SetCommTimeouts(hCom, &TimeOuts);

    PurgeComm(hCom, PURGE_TXCLEAR | PURGE_RXCLEAR);//清空串口缓冲区

    memcpy(pHandle, &hCom, sizeof(hCom));//保存句柄

    return true;
}

void WZSerialPort::close() {
    HANDLE hCom = *(HANDLE*)pHandle;
    CloseHandle(hCom);
}

int WZSerialPort::send(std::string dat)
{
    HANDLE hCom = *(HANDLE*)pHandle;

    if (this->synchronizeflag)
    {
        // 同步方式
        DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
                                    (char*)dat.c_str(), //数据首地址
                                    dwBytesWrite, //要发送的数据字节数
                                    &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
                                    NULL); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            return 0;
        }
        return dwBytesWrite;
    }
    else
    {
        //异步方式
        DWORD dwBytesWrite = dat.length(); //成功写入的数据字节数
        DWORD dwErrorFlags; //错误标志
        COMSTAT comStat; //通讯状态
        OVERLAPPED m_osWrite; //异步输入输出结构体

        //创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
        memset(&m_osWrite, 0, sizeof(m_osWrite));
        m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, "WriteEvent");

        ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
                                    (char*)dat.c_str(), //数据首地址
                                    dwBytesWrite, //要发送的数据字节数
                                    &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
                                    &m_osWrite); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在写入
            {
                WaitForSingleObject(m_osWrite.hEvent, 1000); //等待写入事件1秒钟
            }
            else
            {
                ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osWrite.hEvent); //关闭并释放hEvent内存
                return 0;
            }
        }
        return dwBytesWrite;
    }
}

int WZSerialPort::sendHex(std::string dat)
{
    // 先将string格式的dat转为十六进制的hexStr
    char* hexStr = NULL;
    int len = dat.length();
    hexStr = new char[len + 1];
    memset(hexStr, 0, len + 1);

    int hexStrLen = 0;
    char* dat1 = (char*)dat.data();
    hexStrLen = Char2Hex(dat1, hexStr);

    HANDLE hCom = *(HANDLE*)pHandle;

    if (this->synchronizeflag)
    {
        // 同步方式
        DWORD dwBytesWrite = hexStrLen; //成功写入的数据字节数
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
                                    hexStr, //数据首地址
                                    dwBytesWrite, //要发送的数据字节数
                                    &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
                                    NULL); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            return 0;
        }
        return dwBytesWrite;
    }
    else
    {
        //异步方式
        DWORD dwBytesWrite = hexStrLen; //成功写入的数据字节数
        DWORD dwErrorFlags; //错误标志
        COMSTAT comStat; //通讯状态
        OVERLAPPED m_osWrite; //异步输入输出结构体

        //创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
        memset(&m_osWrite, 0, sizeof(m_osWrite));
        m_osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, "WriteEvent");

        ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
        BOOL bWriteStat = WriteFile(hCom, //串口句柄
                                    hexStr, //数据首地址
                                    dwBytesWrite, //要发送的数据字节数
                                    &dwBytesWrite, //DWORD*,用来接收返回成功发送的数据字节数
                                    &m_osWrite); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bWriteStat)
        {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在写入
            {
                WaitForSingleObject(m_osWrite.hEvent, 1000); //等待写入事件1秒钟
            }
            else
            {
                ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osWrite.hEvent); //关闭并释放hEvent内存
                return 0;
            }
        }
        return dwBytesWrite;
    }
}

std::string WZSerialPort::receive()
{
    HANDLE hCom = *(HANDLE*)pHandle;
    std::string rec_str = "";
    char buf[1024];
    if (this->synchronizeflag)
    {
        //同步方式
        DWORD wCount = 1024; //成功读取的数据字节数
        BOOL bReadStat = ReadFile(hCom, //串口句柄
                                  buf, //数据首地址
                                  wCount, //要读取的数据最大字节数
                                  &wCount, //DWORD*,用来接收返回成功读取的数据字节数
                                  NULL); //NULL为同步发送,OVERLAPPED*为异步发送

        for (int i = 0; i < 1024; i++)
        {
            //if (buf[i] != -52) {
            if (buf[i]) {
                rec_str += buf[i];
                printf("%c\n",buf[i]);
            }
            else
                break;
        }
        return rec_str;
    }
    else
    {
        //异步方式
        DWORD wCount = 1024; //成功读取的数据字节数
        DWORD dwErrorFlags;  //错误标志
        COMSTAT comStat;     //通讯状态
        OVERLAPPED m_osRead; //异步输入输出结构体

        //创建一个用于OVERLAPPED的事件处理,不会真正用到,但系统要求这么做
        memset(&m_osRead, 0, sizeof(m_osRead));
        m_osRead.hEvent = CreateEvent(NULL, TRUE, FALSE, "ReadEvent");

        ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误,获得设备当前状态
        //if (!comStat.cbInQue)
        // return 0;  //如果输入缓冲区字节数为0,则返回false

        //std::cout << comStat.cbInQue << std::endl;
        BOOL bReadStat = ReadFile(hCom,     //串口句柄
                                  buf, //数据首地址
                                  wCount, //要读取的数据最大字节数
                                  &wCount, //DWORD*,用来接收返回成功读取的数据字节数
                                  &m_osRead); //NULL为同步发送,OVERLAPPED*为异步发送
        if (!bReadStat)
        {
            if (GetLastError() == ERROR_IO_PENDING) //如果串口正在读取中
            {
                //GetOverlappedResult函数的最后一个参数设为TRUE
                //函数会一直等待,直到读操作完成或由于错误而返回
                GetOverlappedResult(hCom, &m_osRead, &wCount, TRUE);
            }
            else
            {
                ClearCommError(hCom, &dwErrorFlags, &comStat); //清除通讯错误
                CloseHandle(m_osRead.hEvent); //关闭并释放hEvent的内存
                return 0;
            }
        }
        for (int i = 0; i < 1024; i++)
        {
            if (buf[i] != -52)
                rec_str += buf[i];
            else
                break;
        }
        return rec_str;
    }
}
main.cpp
#define _CRT_SECURE_NO_WARNINGS
#include "serial.h"

using namespace std;
int main(){
    WZSerialPort w;
    w.close();
    if (w.open("COM4",115200)) {
        printf("com is opened!\n");
        string str = "G01 X50Y50F1000\r";
        // 发送的是十六位的信号, 若发送ASCII的串口信号,则用w.send(str)
        int a = w.send(str);
        printf("a:%d\n", a);

        str = "G01 X100Y50\r";
        a = w.send(str);
        printf("a:%d\n", a);

        str = "G00 X100Y100\r";
        a = w.send(str);
        printf("a:%d\n", a);

        str = "G00 X50Y100\r";
        a = w.send(str);
        printf("a:%d\n", a);

        str = "G01 X50Y50\r";
        a = w.send(str);
        printf("a:%d\n", a);

        w.close();
    }
    return 0;
}

参考:

1. 百度百科

2. G 代码解释|最重要的 G 代码命令列表

3. windows c++ 串口通讯

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
and debugged. Its widespread availability allows PCB professionals to exchange image drill and route securely and efficiently. The RS-274X format is simple, compact and unequivocal. It is easy to interpret. It describes an image with very high precision. It is complete: one single file describes an image. It is portable and easy to debug by its use of printable 7-bit ASCII characters. A well-constructed RS-274X file precisely defines the PCB image data and the functions of the different image elements. Unfortunately, poorly constructed or simply erroneous RS-274X files also circulate, sometimes leading to unjustified criticism of the format itself. Errors may be due to a misunderstanding of the format. With more than 25 years experience in CAM software we at Ucamco know which areas are most often misunderstood. This revision of the RS-274X specification explains these areas more clearly. Other files are not invalid but poorly constructed. Especially troublesome are painted or stroked pads and copper planes. Poorly constructed files take longer to process, require more manual work and increase the risk of errors. This revision of the RS-274X specification recommends constructions to make RS-274X files safer and more efficient, and hence fabrication more reliable, faster and cheaper. A few words must be said about RS-274-D or Standard Gerber. This format was developed to drive NC machine tools and was used for Gerber vector plotters in the 1960s and 1970s. It is not an image description format. It is amazing that it is still used. It is like using teletype paper tape to transfer text documents. We call on industry experts and professional organizations to discourage the use of the obsolete RS-274-D format. Although other data transfer formats have come into the market, they have not displaced RS-274X. The reason is simple. More than 90% of the problems in data transfer are due not to limitations in the RS-274X format but to poor practices and, worse, the use of RS-274
以下是一个简单的 C++ 串口钩子通讯代码示例,演示了如何使用钩子来实现串口通讯: ```c++ #include <iostream> #include <Windows.h> // 定义串口句柄 HANDLE hSerial = NULL; // 串口钩子函数 LRESULT CALLBACK SerialPortHook(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) { // 读取串口数据 char szBuf[1024]; DWORD dwBytesRead; if (ReadFile(hSerial, szBuf, sizeof(szBuf), &dwBytesRead, NULL)) { // 处理串口数据 // ... // 修改串口数据 // ... // 发送串口数据 DWORD dwBytesWritten; if (WriteFile(hSerial, szBuf, dwBytesRead, &dwBytesWritten, NULL)) { // 发送成功 // ... } else { // 发送失败 // ... } } // 返回下一个钩子处理程序的返回值 return CallNextHookEx(NULL, nCode, wParam, lParam); } else { // 如果不是 HC_ACTION,则调用下一个钩子处理程序 return CallNextHookEx(NULL, nCode, wParam, lParam); } } int main() { // 打开串口 hSerial = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hSerial == INVALID_HANDLE_VALUE) { std::cerr << "Failed to open serial port!" << std::endl; return 1; } // 配置串口 DCB dcb; GetCommState(hSerial, &dcb); dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; SetCommState(hSerial, &dcb); // 安装钩子 HHOOK hHook = SetWindowsHookEx(WH_KEYBOARD_LL, SerialPortHook, NULL, 0); // 消息循环 MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // 卸载钩子 UnhookWindowsHookEx(hHook); // 关闭串口 CloseHandle(hSerial); return 0; } ``` 以上代码是一个示例,打开指定的串口并配置串口参数,然后安装钩子并在钩子函数中读取和修改串口数据,最后关闭串口并卸载钩子。注意,在使用钩子进行串口通讯时,需要保证钩子处理程序的运行速度不会影响串口数据的实时传输。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值