Marlin关于如何接收Gcode指令的详解

article/2025/9/17 8:20:56

我研究的Marlin是1.0版本的.想用在的地方是stm32

Marlin关于接收Gcode命令最主要的函数就是: get_command()

get_command()函数说实话对于我这样的学生来说程度还是挺复杂的.
为了节省大家理清代码和阅读文章的时间, 我全文用字力求精简, 别人已经写了的知识我就不会写

先上函数全文:

void get_command(void)
{ int16_t n;char time[30];unsigned long t;int hours, minutes;while( MYSERIAL_available() > 0  && buflen < BUFSIZE){	//	LCD_ShowString(5,5,240,320,12, ".1.");	serial_char = MYSERIAL_read();
//		printf(" serial_char: %c\n\r",serial_char);if(serial_char == '\n' ||serial_char == '\r' ||(serial_char == ':' && comment_mode == false) ||serial_count >= (MAX_CMD_SIZE - 1) )//sanse 冒号{if(!serial_count)  //if empty line{comment_mode = false; //for new commandreturn;}cmdbuffer[bufindw][serial_count] = 0; //terminate stringif(!comment_mode){comment_mode = false; //for new commandfromsd[bufindw] = false;if(strchr(cmdbuffer[bufindw], 'N') != NULL)	                                                                    {strchr_pointer = strchr(cmdbuffer[bufindw], 'N');gcode_N = (strtol(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL, 10));if(gcode_N != gcode_LastN+1 && (strstr(cmdbuffer[bufindw], PSTR("M110")) == NULL) )  {                                                                          SERIAL_ERROR_START;printf(MSG_ERR_LINE_NO);printf("%ld",gcode_LastN);//Serial.println(gcode_N);FlushSerialRequestResend();serial_count = 0;return;}if(strchr(cmdbuffer[bufindw], '*') != NULL){u8 checksum = 0;u8 count = 0;while(cmdbuffer[bufindw][count] != '*') checksum = checksum^cmdbuffer[bufindw][count++];strchr_pointer = strchr(cmdbuffer[bufindw], '*');if( (u8)(strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)) != checksum){                                                                 SERIAL_ERROR_START;printf(MSG_ERR_CHECKSUM_MISMATCH);printf(" checksum: %d\n\r",checksum);count = 0;printf(" '");while(cmdbuffer[bufindw][count] != '*'){printf("%c",cmdbuffer[bufindw][count++]);}printf(" '\n\r ");checksum = 0;count = 0;while(cmdbuffer[bufindw][count] != '*'){ printf("cmdbuffer:%d;",cmdbuffer[bufindw][count]);checksum = checksum^cmdbuffer[bufindw][count++];printf(" checksum:%d \n\r",checksum);}///	printf("\n\r ");printf("%ld",gcode_LastN);FlushSerialRequestResend();serial_count = 0;return;}//if no errors, continue parsing}else{SERIAL_ERROR_START;printf(MSG_ERR_NO_CHECKSUM);printf("%ld",gcode_LastN);FlushSerialRequestResend();serial_count = 0;return;}gcode_LastN = gcode_N;//if no errors, continue parsing}else  // if we don't receive 'N' but still see '*'{if((strchr(cmdbuffer[bufindw], '*') != NULL))                                                            {SERIAL_ERROR_START;printf(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM);printf("%ld",gcode_LastN);serial_count = 0;return;}}if((strchr(cmdbuffer[bufindw], 'G') != NULL))                                                                {strchr_pointer = strchr(cmdbuffer[bufindw], 'G');switch((int)((strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)))){case 0:case 1:case 2:case 3:if(Stopped == false) { // If printer is stopped by an error the G[0-3] codes are ignored.#ifdef SDSUPPORTif(card.saving)break;#endif //SDSUPPORTprintf(MSG_OK);printf("\n");}else {printf(MSG_ERR_STOPPED);// LCD_MESSAGEPGM(MSG_STOPPED);}break;default:break;}}bufindw = (bufindw + 1)%BUFSIZE;buflen += 1;//sanse}serial_count = 0; //clear buffer}else{if(serial_char == ';') comment_mode = true;if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;}}#ifdef SDSUPPORT   //sanseif(!card.sdprinting || serial_count!=0){ //	LCD_ShowString(20,5,240,320,12, ".2.");return;}while( !card_eof()  && buflen < BUFSIZE) {	//LCD_ShowString(50,5,240,320,12, ".3.");n=card_get();serial_char = (BYTE)n;if(serial_char == '\n' ||serial_char == '\r' ||(serial_char == ':' && comment_mode == false) ||serial_count >= (MAX_CMD_SIZE - 1)||n==-1){ if(card_eof())//sanse{printf(MSG_FILE_PRINTED);printf("\n");stoptime=millis();t=(stoptime-starttime)/1000;minutes=(t/60)%60;hours=t/60/60;sprintf(time, PSTR("%i hours %i minutes"),hours, minutes);SERIAL_ECHO_START;printf("%s",time);//   lcd_setstatus(time);card_printingHasFinished();// card_checkautostart(true);}if(!serial_count){comment_mode = false; //for new commandreturn; //if empty line}cmdbuffer[bufindw][serial_count] = 0; //terminate string
//      if(!comment_mode){fromsd[bufindw] = true;//sansebuflen += 1;bufindw = (bufindw + 1)%BUFSIZE;
//      }comment_mode = false; //for new commandserial_count = 0; //clear buffer}else{if(serial_char == ';') comment_mode = true;if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char;}}#endif //SDSUPPORT}

get_command()牵涉到的主要变量为: serial_char, cmdbuffer, bufindw, serial_count, comment_mode

static char serial_char: 相当于串口数据的中转站, 依靠MYSERIAL_read()读取 从串口中断中得到的rx_buffer 来获取命令数据

static char cmdbuffer[8] [128]:存储命令的功能, 从serial_char获取命令数据

static int bufindw: 全名buffer_index_write, 指示作用, 作为cmdbuffer的下标, 在代码中会 +1

static int serial_count: 指示作用, 作为cmdbuffer的下标, 在代码中会自+

static bool comment_mode = false: 判断模式(判断为动词), 为false就执行get_command函数, 为true就结束get_command()

static int buflen:计算缓冲区字节数量的功能, 在代码中会+1

再讲个结构体, 下面有用

typedef struct ring_buffer
{unsigned char buffer[RX_BUFFER_SIZE]; //RX_BUFFER_SIZE=128int head;int tail;
}ring_buffer;
extern  ring_buffer rx_buffer;	ring_buffer rx_buffer  =  { { 0 }, 0, 0 };

我们例如要将" G1 X1000 Y1000 Z250 "录入command_buffer中, 他是这样变的:

" G1 X1000 Y1000 Z250 "先由串口进入rx_buffer, rx_buffer的结构体中的第一位unsigned char buffer[RX_BUFFER_SIZE]中,结构体的第二,三位起下标作用.

再由get_command()函数使得: cmdbuffer[0]为G1 , cmdbuffer[1]为X1000, 以此类推, 以Z250为例, 那么
cmdbuffer[3] [2]就为5了

代码结构一张思维导图即可理清 (如有纰漏, 还望不吝赐教, 在评论区提出改正):
我只画了关于串口的读取Gcode的那一段, 如果想研究从SD卡读Gcode, 道理是一样的, 不做过多解释

请添加图片描述
温馨提示: 别看左边那一列有那么多, 其实执行最多的是右面一列:
请添加图片描述

我再送上我做过注释的, 根据思维导图截取了部分的代码:

while( MYSERIAL_available() > 0  && buflen < BUFSIZE){	//	LCD_ShowString(5,5,240,320,12, ".1.");	serial_char = MYSERIAL_read();
//		printf(" serial_char: %c\n\r",serial_char);if(serial_char == '\n' ||serial_char == '\r' ||(serial_char == ':' && comment_mode == false) ||serial_count >= (MAX_CMD_SIZE - 1) )//sanse 冒号{if(!serial_count)  //if empty line{comment_mode = false; //for new commandreturn;}cmdbuffer[bufindw][serial_count] = 0; //terminate stringif(!comment_mode){comment_mode = false; //for new command 	确保处于读模式, 避免执行命令模式fromsd[bufindw] = false;	// 避免从SD卡读取if(strchr(cmdbuffer[bufindw], 'N') != NULL)	                                                                    {strchr_pointer = strchr(cmdbuffer[bufindw], 'N');		// strchr函数功能为在一个串中查找给定字符的第一个匹配之处	gcode_N = (strtol(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL, 10));	// strtol函数会将参数nptr字符串根据参数base来转换成长整型数,参数类型范围从2至36。if(gcode_N != gcode_LastN+1 && (strstr(cmdbuffer[bufindw], PSTR("M110")) == NULL) ) // strstr(str1,str2) 返回字符串中首次出现子串的地址	pstr表示一个指向字符串的指针变量{                                                                          SERIAL_ERROR_START;   // 报错printf(MSG_ERR_LINE_NO);printf("%ld",gcode_LastN);	// %ld: long int//Serial.println(gcode_N);FlushSerialRequestResend();serial_count = 0;return;}if(strchr(cmdbuffer[bufindw], '*') != NULL){u8 checksum = 0;u8 count = 0;while(cmdbuffer[bufindw][count] != '*') checksum = checksum^cmdbuffer[bufindw][count++];	// 计算从字符串起始位到'*'的checksum, cheaksum用^运算是为了尽可能地大strchr_pointer = strchr(cmdbuffer[bufindw], '*');if( (u8)(strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)) != checksum)	// strtod:将字符串转换成浮点数	一般来说checksum为0, "strchr_pointer - cmdbuffer[bufindw] + 1"为1, 不等于说明内存出问题了 呼应 ↑{                                                                 SERIAL_ERROR_START;printf(MSG_ERR_CHECKSUM_MISMATCH);printf(" checksum: %d\n\r",checksum);count = 0;printf(" '");while(cmdbuffer[bufindw][count] != '*'){printf("%c",cmdbuffer[bufindw][count++]);}printf(" '\n\r ");checksum = 0;count = 0;while(cmdbuffer[bufindw][count] != '*'){ printf("cmdbuffer:%d;",cmdbuffer[bufindw][count]);checksum = checksum^cmdbuffer[bufindw][count++];printf(" checksum:%d \n\r",checksum);}///	printf("\n\r ");printf("%ld",gcode_LastN);FlushSerialRequestResend();serial_count = 0;return;}//if no errors, continue parsing}else{SERIAL_ERROR_START;printf(MSG_ERR_NO_CHECKSUM);printf("%ld",gcode_LastN);FlushSerialRequestResend();serial_count = 0;return;}gcode_LastN = gcode_N;//if no errors, continue parsing}else  // if we don't receive 'N' but still see '*'{if((strchr(cmdbuffer[bufindw], '*') != NULL))                                                            {SERIAL_ERROR_START;printf(MSG_ERR_NO_LINENUMBER_WITH_CHECKSUM);printf("%ld",gcode_LastN);serial_count = 0;return;}}if((strchr(cmdbuffer[bufindw], 'G') != NULL))                                                                {strchr_pointer = strchr(cmdbuffer[bufindw], 'G');switch((int)((strtod(&cmdbuffer[bufindw][strchr_pointer - cmdbuffer[bufindw] + 1], NULL)))){case 0:case 1:case 2:case 3:if(Stopped == false) { // If printer is stopped by an error the G[0-3] codes are ignored.#ifdef SDSUPPORTif(card.saving)break;#endif //SDSUPPORTprintf(MSG_OK);printf("\n");}else {printf(MSG_ERR_STOPPED);// LCD_MESSAGEPGM(MSG_STOPPED);}break;default:break;}}bufindw = (bufindw + 1)%BUFSIZE;buflen += 1;//sanse}serial_count = 0; //clear buffer}else{if(serial_char == ';') comment_mode = true;if(!comment_mode) cmdbuffer[bufindw][serial_count++] = serial_char; //注意serial_count自加}}

http://chatgpt.dhexx.cn/article/To83YT7D.shtml

相关文章

Gcode文件处理和执行

流程图&#xff1a; 1:SMC_OutQueue 不带参数>>>SMC_Interpolator 插补算法 2:CMC_CNC_REF 带参数>>>smc_NCDecoder 图型解析>>>ToolCorr 刀补>>>SmoothPath 圆弧>>>checkVelocities 速度 3:File 文件.txt/.cnc/.gcode>…

GCD简介

1. 什么是GCD GCD,全称是Grand Central Dispatch,它是C语言的API. GCD的核心 : 将block(任务)添加到queue(队列)中. 官方文档的描述: Grand Central Dispatch(GCD)是异步执行任务的技术之一。一般将应用程序中记述的线程管理用的代码在系统级中实现.开发者只需定义想执行的任…

激光雕刻机的位图-GCode转换方法实践

Author:Gary Li Date:2019-5-2 1. 简介 现在有一部分3D打印机带有激光雕刻功能&#xff0c;自己组装一台专用的激光雕刻机也并不复杂成本也很低。但是当我们使用激光雕刻机时&#xff0c;激光雕刻下位机只能接受路径指令&#xff0c;所以我们需要把位图——也就是我们平时最常…

3d打印实用小工具--GCode Viewer在线预览GCODE文件

使用solidworks2018绘制了一个零件 使用simplify3d进行切片 然后点击保存了这个文件。 没想到打印的时候&#xff0c;竟然打印成了这个零件。 原因是拷贝错了文件&#xff0c;而自己切片成功之后的GCode文件也无法预览&#xff0c;最后造成了时间和材料的浪费。 那么怎么需要打…

三轴XYZ平台生成gcode文件

1. 生成gcode坐标文件 gcode文件中保存的是需要绘制图形的路径信息&#xff0c;这里我们采用开源矢量图形编辑软件 Inkscape并通过Unicorn G-Code插件来生成 gcode坐标文件。 将软件资料包\Inkscape.rar 压缩文件解压到电脑上任意磁盘&#xff0c;软件内已安装 Unicorn G-Code插…

GRBL三:gcode代码解析

GRBL三&#xff1a;gcode代码解析 1.G00X_Y_Z_ :快速定位指令&#xff0c;_代表具体数值 可以同时针对X轴Y轴Z轴移动&#xff0c;只快速定位&#xff0c;不切削加工&#xff0c;相当于快速的移动到那个点上去 数值代表绝对位置 2.G01X_Y_Z_F_:直线差补 F指定进给速度mm/min…

3D打印gcode命令大全及解析

*G0&#xff1a;快速移动 *G1&#xff1a;控制移动 坐标轴XYZE移动控制&#xff08;G0和G1一样&#xff09; 例子&#xff1a;G0 F2000 X30 Y30 Z30 E3 *G2&#xff1a;顺时针画弧 *G3&#xff1a;逆时针画弧 此命令有两种形式&#xff1a;IJ-form和R-form。I指定了X偏移…

GCode软件使用说明书

欢迎使用GCode软件&#xff0c;本软件仅为辅助生成G代码软件...... 操作说明&#xff1a; 1、打开软件GCode.exe&#xff0c; &#xff08;1&#xff09;点击“文件”即可选择“新建”、“打开”、“保存”等功能&#xff0c;或者点击主界面快捷键&#xff1b; &#xff08;…

在 VS Code 中阅读 G-code 及 3D 打印机 gcode 常用指令介绍

1. 在 VS Code 中阅读 G-code 直接以文本格式在 VS Code 中打开 .gcode 文件&#xff0c;会发现没有语法高亮。 安装插件实现语法高亮和悬浮显示命令解释。 1.1 安装插件「G-Code」 1.2 配置 settings.json 文件 1.2.1 打开 VS Code 设置(快捷键&#xff1a; Ctrl 逗号) 1…

二分图最大匹配及匈牙利、HK算法

二分图最大匹配 在二分图中&#xff0c;最大匹配是指选出尽可能多的边使得任意两边没有公共端点。 增广路 设 M M M为二分图 G G G已匹配的边的集合&#xff0c;若 P P P是图 G G G中一条连接两个未匹配顶点的路径&#xff08;起点终点分别在两个集合&#xff09;&#xff0…

用最大流解决二分图最大匹配 Bipartite Matching

有A B C三个老师&#xff0c;D E F三门课&#xff0c;A能教E, B能教D和F&#xff0c;C能教D和E。要求每个老师只能教一门课&#xff0c;求分配方案。 这是一个典型的二分图最大匹配问题&#xff0c;二分图是只graph的顶点可以分为两部分&#xff0c;每部分内部顶点直接无连接&…

二分图最大匹配-匈牙利算法

今天介绍 匈牙利算法 &#xff1a; 匈牙利算法&#xff0c;是基于Hall定理中充分性证明的思想&#xff0c;它是部图匹配最常见的算法&#xff0c;该算法的核心就是寻找增广路径&#xff0c;由匈牙利数学家Edmonds于1965年提出&#xff0c;因而得名。 先介绍一下增广路径&#x…

求解分配问题(二) 二分图最大匹配算法

我的前一篇文章介绍了对于分配问题的Kuhn-Munkre算法&#xff0c;该算法其实可以看作是邻接矩阵形式的匈牙利算法&#xff0c;如果更抽象地看这个算法&#xff0c;它可以看成是一个二分图匹配算法的变体算法&#xff0c;具体的说&#xff0c;是二分图最大权重匹配算法。我打算也…

二分图最大匹配(匈牙利算法,Dinic网络流算法)

二分图最大匹配 二分图最大匹配问题: 有两个集合A,B,两个集合间有多条边连接集合中的点,且单个集合中的点各不相连,求两集合的点能两两配对的最大匹配数. (参考:)二分图最大匹配——匈牙利算法 匈牙利算法: A集合记录各点与B集合相连的点,B集合记录某点与A集合中匹配的点.遍历…

二分图最大匹配与最大独立集

一.概念部分 1.什么是二分图&#xff1f; 通俗的说法&#xff1a;就是可以把图分成两部分&#xff0c;每一部分任意两点之间没有关系&#xff08;同一部落&#xff09;&#xff0c;两部分之间点可能存在多种关系。 2.怎么判断二分图&#xff1f; &#xff08;1&#xff09;…

二分图最大匹配及最大权匹配

二分图最大匹配学习 一.二分图的基本知识二.二分图最大匹配什么是二分图最大匹配怎么求二分图最大匹配 三.二分图最大权匹配四.例题训练三.最小点覆盖数 一位大佬的神级解释 本以为有了网络流&#xff0c;就不用再学匈牙利了&#xff0c;但在做题的过程中&#xff0c;发现有些…

二分图最大匹配问题(匈牙利算法)

什么是二分图 如果一个无向图的的顶点可以分为两个互不相交的子集A和B&#xff0c;那么它就是二分图。也就是说&#xff0c;A、B内部不存在连边&#xff0c;所有连边都一头连着A中的顶点&#xff0c;另一头连着B中的顶点。 什么是二分图最大匹配&#xff1f; 二分图最大匹配…

二分图 二分图最大匹配

首先来说一下什么是二分图。 二分图 二分图又称作二部图&#xff0c;是图论中的一种特殊模型。 设G(V, E)是一个无向图。如果顶点集V可分割为两个互不相交的子集X和Y&#xff0c;并且图中每条边连接的两个顶点一个在X中&#xff0c;另一个在Y中&#xff0c;则称图G为二分图。…

二分图最大匹配(最大流)

先举个例子&#xff0c;有N台计算机和K个任务&#xff0c;每个计算机只能执行一个任务&#xff0c;但可以执行多种任务。现在给出N和K&#xff0c;和其关系&#xff0c;求出最多能处理的任务数。 这就是典型的二分图&#xff0c;整张图被分为两半&#xff0c;一半是电脑&#…

图论总结(一)二分图最大匹配

二分图最大匹配 (一)、二分图 1、定义2、性质3、判定(二)、二分图的匹配 1、二分图的最大匹配2、 Knig定理及其证明3、最小边覆盖与最大独立集(三)、增广路径 1、定义2、性质3、寻找增广路(四)、匈牙利算法 1、找增广路经的算法2、实践3、算法分析(五)、例题 1、最小…