1、总体概述
首先通过rs274ngc_init()函数进行初始化,初始化内容包括读取rs274ngc.var和rs274ngc.tool_default文件,坐标,G模态组,M模态组以及其他G代码运行需要的状态量。然后就可以通过rs274ngc_read()函数进行读取G代码,然后进行语法和逻辑判断,最后通过rs274ngc_execute()函数进行G代码执行。
2、rs274ngc_read()函数的解读
首先判断当前G代码解释器是否正在执行探头测距,确保测距过程的完整(LinuxCNC内才有相应的配套逻辑),然后判断录入的当前行G代码内容是否非空,文件指针为空的情况,并通过CHK宏调用,实现错误代码的返回。
接下来执行read_text()函数,对传入的文本行逐字进行处理,处理后只保留小写和数字组成的紧凑G代码文本,其他空格,大写,tab键,注释等去掉。
接下来通过parse_line()函数进行逻辑检查,内部执行了四个函数:init_block()(初始化开始进入G代码分析时的状态,比如将R的状态量设置为-1,如果字符串中有R则将状态量修改为1等),read_items()(内部定义了一个函数指针数组,根据G代码的首字母,调用数组内的read_x函数,以sscanf函数进行转换),enhance_block()(对模态运动的逻辑检查和判定),check_items()(内部实现了3个函数check_g_codes判断G代码是否符合要求,check_m_codes判断M代码是否数量超过4个,check_other_codes判断其他类似于I被录入了,但确实G2,G3的模态等)。
3、rs274ngc_execute()函数的解读
首先处理以#xxx=xxx形式G代码对内部参数(1~5400)的设定,主要是MDI模式用于设定参数。
接下来主要执行了4个函数:execute_block(),write_g_codes(),write_m_codes(),write_settings()。
- execute_block()函数
这个函数实现了G代码的所有动作,包括
打印注释,进给模式设置(G99 G98) 进给率 主轴转速 换刀,同时还对换刀M代码 主轴转向M代码 冷却M代码,还有一个feed override M48 M49处理
同时还处理了G04暂停 G17,G18,G19等坐标平面 G20,G21单位制 G40,G41,G42左右刀补
刀具偏置值 G44,G49 (G44 Hxx 长度补偿类似偏置值只有两位数表示)G54~G59.3坐标系
G61 G64控制模式 G90,G91坐标类型 G98,G99进给模式 G10 坐标系设置 G28,G30回原点 G92坐标系偏移
模态运动的处理 G0 G1 G2 G3 G80到G90间的循环代码处理等。
以上所有操作都仅是输出字符串,没有关联的硬件操作过程。 - write_g_codes()函数
内部模态G码的保存和更新。 - write_m_codes()函数
内部模态M码的保存和更新。 - write_settings()函数
其他模态值的更新 行号 进给率 主轴速度等更新。
根据独立的G代码解释器并没有规定G代码的执行顺序,但是在LinuxCNC的execute_block()函数对G代码的执行顺序做了规定:
G-codes are are executed in the following order.
1. mode 0, G4 only - dwell. Left here from earlier versions.
2. mode 2, one of (G17, G18, G19) - plane selection.
3. mode 6, one of (G20, G21) - length units.
4. mode 15 one of (G07,G08) - lathe diameter mode
5. mode 7, one of (G40, G41, G42) - cutter radius compensation.
6. mode 8, one of (G43, G49) - tool length offset
7. mode 12, one of (G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3)
- coordinate system selection.
8. mode 13, one of (G61, G61.1, G64, G50, G51) - control mode
9. mode 3, one of (G90, G91) - distance mode.
10. mode 4, one of (G90.1, G91.1) - arc i,j,k mode.
11. mode 10, one of (G98, G99) - retract mode.
12. mode 0, one of (G10, G28, G30, G92, G92.1, G92.2, G92.3) -
13. mode 1, one of (G0, G1, G2, G3, G38.2, G80, G81 to G89, G33, G33.1, G76) - motion or cancel.
G53 from mode 0 is also handled here, if present.
Some mode 0 and most mode 1 G-codes must be executed after the length units
are set, since they use coordinate values. Mode 1 codes also must wait
until most of the other modes are set.
4、G代码的处理
简单分析一下read_g()函数。为了简化G代码的处理,将G代码的数值进行放大10被,在rs274ngc.hh文件中有声明,比如G1用10表示。从中间数组的内容通过sscanf函数获取回一个dobule类型的数据,放大10倍后通过调用fix函数进行取整,以该整数为下标对所有支持G代码数组(1000个,-1表示不支持的G代码,其他有效值表示隶属的G代码组编号)进行取值,判定是否为-1,-1表示不支持的G代码操作,返回错误代码。同时判定当前的G代码是否是已经被同组的G代码赋值过了(init_block()函数会将初始状态设置为-1),出现同组的其他G代码赋值,返回错误代码。
根据rs274ngc.hh描述支持的G代码很少只有52个,为了丰富G代码翻译,LinuxCNC拓展到了92个,可以参考LinuxCNC的代码拓展需要的G代码到G代码解释器中。
尝试将G77固定循环车削指令(C类G代码)添加到G代码解释器中。动作分解是,①快速向下到定位点②横向进刀③竖向切削④快速回到开始点。
- 第一步
在rs274ngc.hh中添加G77的宏
#define G_77 770
- 第二步
在read_g()函数中找到_gees[1000]数组里面的第770个位置,将-1改为1号(设置为1号模组,关于模组分类可以看_gees[]数组源码位置的注释)。 - 第三步
在check_g_codes()函数中作一些逻辑判断,比如要确保开始坐标点和目标坐标点不在一个点上。
int mode1 SET_TO block->g_modes[1];
if(mode1 IS G_77){
CHK((block->x_flag IS ON) AND (block->z_flag IS ON), NCE_G77_error1);
if(settings->distance_mode IS MODE_ABSOLUTE){
CHK((fabs(block->x_number - settings->currentt_x) > 0.001) AND (fabs(block->z_number - settings->currentt_z) > 0.001), NCE_G77_error2);
}else if(settings->distance_mode IS MODE_INCREMENTAL){
CHK((fabs(block->x_number) > 0.001) AND (fabs(block->z_number) > 0.001), NCE_G77_error2);
}
}
- 第四步
将两个错误码的宏填到rs274ngc_return.hh文件中,同时将错误代码对应的文本填入到_rs274ngc_errors[]数组中,并且修改数组的长度值RS274NGC_MAX_ERROR到199
#define NCE_G77_error1 198
#define NCE_G77_error2 199
_rs274ngc_errors[]添加错误提示内容
/* 198 */ "G77 XZ coordinates must be specified simultaneously", // G77 value
/* 199 */ "G77 The target position and start point position cannot be the same", // G77 pos
- 第5步
在G代码执行函数rs274ngc_execute()->execute_block()->convert_g()->convert_motion()添加一个分支。使G代码解释器执行G77程序。
else if(motion IS G_77){
CHP(convert_cycle_g7x(motion, block, settings));
settings->motion_mode SET_TO motion;
}
- 第6步
实现convert_cycle_g7x()函数
static int convert_cycle_G7x( /* ARGUMENTS */
int motion, /* a g-code between G_71 and G_78, a canned cycle */
block_pointer block, /* pointer to a block of RS274 instructions */
setup_pointer settings) /* pointer to machine settings */
{
static char name[] SET_TO "convert_cycle_g7x";
CANON_MOTION_MODE save_mode;
int status;
if(motion IS G_77){
// 路径1
STRAIGHT_TRAVERSE(block->x_number, settings->current_y, settings->current_z
#ifdef AA
, settings->current_a
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef BB
, settings->current_b
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef CC
, settings->current_c
#else
#ifdef ALL_AXES
, 0
#endif
#endif
);
// 路径2
STRAIGHT_FEED(block->x_number, settings->current_y, block->z_number
#ifdef AA
, settings->current_a
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef BB
, settings->current_b
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef CC
, settings->current_c
#else
#ifdef ALL_AXES
, 0
#endif
#endif
);
// 路径3
STRAIGHT_FEED(settings->current_x, settings->current_y, block->z_number
#ifdef AA
, settings->current_a
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef BB
, settings->current_b
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef CC
, settings->current_c
#else
#ifdef ALL_AXES
, 0
#endif
#endif
);
// 路径4
STRAIGHT_TRAVERSE(settings->current_x, settings->current_y, settings->current_z
#ifdef AA
, settings->current_a
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef BB
, settings->current_b
#else
#ifdef ALL_AXES
, 0
#endif
#endif
#ifdef CC
, settings->current_c
#else
#ifdef ALL_AXES
, 0
#endif
#endif
);
}
return RS274NGC_OK;
}
- 测试
测试效果如图所示。
5、G代码解释器和运动控制库的连接
最终G代码执行的动作都是在canon.cc内进行实现,只需要将canon.cc内定义的基本函数,以队列的形式将数据传递到运动控制库通讯队列,发送数据到运动控制库,即可完成电机的动作。