serial 收到 G代码命令后,要通过 protocal 来处理
protocol.h
#ifndef protocol_h
#define protocol_h
//行执行串行输入流的缓冲区大小。 线缓存器大小来自于可执行串口输入流
//注意:不是一个问题,除了极端的情况下,但线缓冲区大小太小了和G代码块可以截断。正式,G代码标准支持多达256
//字符。在未来的版本中,这将会增加,当我们知道多少额外
//内存空间我们可以投资或重写刀位点解析器没有这个
//缓冲区。
#ifndef LINE_BUFFER_SIZE
#define LINE_BUFFER_SIZE 80
#endif
//开始Grbl主循环。它处理所有从串口输入的字符并执行
//他们完成。它还负责完成初始化过程。
void protocol_main_loop();
//检查和执行实时命令在主程序的各种停止点
void protocol_execute_realtime();
.
// void protocol_cycle_start(); //通知步进子系统开始执行刀位点程序缓冲区。
// Reinitializes the buffer after a feed hold for a resume.
// void protocol_cycle_reinitialize(); //重新启动缓冲后饲料保持一份简历。
// void protocol_feed_hold(); //启动一个提要的正在运行的程序
//执行自动循环功能,如果启用。
void protocol_auto_cycle_start();
//阻塞,直到所有缓冲的步骤执行
void protocol_buffer_synchronize();
#endif
_____________________________________________________________________________________________
实现代码实际上是一个封装代码没有具体实现代码
预先解析定义不同的注释类型。
#define COMMENT_NONE 0
#define COMMENT_TYPE_PARENTHESES 1
#define COMMENT_TYPE_SEMICOLON 2
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated. G代码缓存
//指导和执行一行从protocol_process格式化的输入。虽然大多
//输入流刀位点,这也指导和执行Grbl内部命令,
//设置等发起寻的周期,和切换开关状态
static void protocol_execute_line(char *line)
{
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort 保释调用函数时系统中止
#ifdef REPORT_ECHO_LINE_RECEIVED //选择打印接收路线
report_echo_line_received(line);
#endif
if (line[0] == 0) { // 第一个字节等于0表示空行或者是注释行
// Empty or comment line. Send status message for syncing purposes.
// 空或注释行。发送状态信息进行同步。
report_status_message(STATUS_OK);
} else if (line[0] == '$') { //$表示系统命令,执行sys_execute_line()并打印报告信息
// Grbl '$' system command
report_status_message(system_execute_line(line));
} else if (sys.state == STATE_ALARM) {
// Everything else is gcode. Block if in alarm mode.
report_status_message(STATUS_ALARM_LOCK);
} else {
// Parse and execute g-code block!
report_status_message(gc_execute_line(line)); // 执行G代码解析
}
}
___________________________________________________________________________________________
下面是grbl 循环
void protocol_main_loop()
{
//完成初始化过程在一个电或复位。
// Print welcome message
report_init_message();
// 检查和报告报警状态重置后,错误,初始化上电。
if (sys.state == STATE_ALARM) {
report_feedback_message(MESSAGE_ALARM_LOCK);
} else {
// All systems go! But first check for safety door.
if (system_check_safety_door_ajar()) {
bit_true(sys.rt_exec_state, EXEC_SAFETY_DOOR);
protocol_execute_realtime(); // Enter safety door mode. Should return as IDLE state.
} else {
sys.state = STATE_IDLE; // Set system to ready. Clear all state flags.
}
system_execute_startup(line); // Execute startup script.
}
// ---------------------------------------------------------------------------------
// Primary loop! Upon a system abort, this exits back to main() to reset the system.
// ---------------------------------------------------------------------------------
uint8_t comment = COMMENT_NONE;
uint8_t char_counter = 0;
uint8_t c;
for (;;) {
//一行输入的串行数据的过程,作为数据变得可用。执行一个
//初始过滤去除空间和评论和利用所有的信件。
//注意:评论时,空间和块删除(如果支持的话)处理技术
//在刀位点解析器,它有助于压缩到Grbl传入的数据
//线缓冲区,这是有限的。刀位点标准实际上州一行不行
//超过256个字符,但是Arduino Uno没有内存空间。
//有更好的处理器,它会很容易把这个初步解析的
//分离任务共享的刀位点解析器和Grbl系统命令。
while((c = serial_read()) != SERIAL_NO_DATA) {
if ((c == 'n') || (c == 'r')) { // 回车
line[char_counter] = 0; // 设置结束
protocol_execute_line(line); // Line is complete. Execute it!
comment = COMMENT_NONE;
char_counter = 0;
} else {
if (comment != COMMENT_NONE) {
// Throw away all comment characters 去掉注释
if (c == ')') { //c==41
// End of comment. Resume line. But, not if semicolon type comment.
if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; }
}
} else {
if (c <= ' ') {
// c<20去掉空格和控制字符
} else if (c == '/') {
//块删除不支持。忽略字符。
//注意:如果支持,只需要检查系统是否启用了块删除。
} else if (c == '(') {
// Enable comments flag and ignore all characters until ')' or EOL.
// NOTE: This doesn't follow the NIST definition exactly, but is good enough for now.
// In the future, we could simply remove the items within the comments, but retain the
// comment control characters, so that the g-code parser can error-check it.
comment = COMMENT_TYPE_PARENTHESES;
} else if (c == ';') {
// NOTE: ';' comment to EOL is a LinuxCNC definition. Not NIST.
comment = COMMENT_TYPE_SEMICOLON;
// TODO: Install '%' feature
// } else if (c == '%') {
// Program start-end percent sign NOT SUPPORTED.
// NOTE: This maybe installed to tell Grbl when a program is running vs manual input,
// where, during a program, the system auto-cycle start will continue to execute
// everything until the next '%' sign. This will help fix resuming issues with certain
// functions that empty the planner buffer to execute its task on-time.
} else if (char_counter >= (LINE_BUFFER_SIZE-1)) {
// Detect line buffer overflow. Report error and reset line buffer.
report_status_message(STATUS_OVERFLOW);
comment = COMMENT_NONE;
char_counter = 0;
} else if (c >= 'a' && c <= 'z') { // 小写转大写
line[char_counter++] = c-'a'+'A';
} else {
line[char_counter++] = c;
}
}
}
}
// If there are no more characters in the serial read buffer to be processed and executed,
// this indicates that g-code streaming has either filled the planner buffer or has
// completed. In either case, auto-cycle start, if enabled, any queued moves.
protocol_auto_cycle_start(); //系统开始循环
protocol_execute_realtime(); // Runtime command check point. 运行实时命令
if (sys.abort) { return; } // Bail to main() program loop to reset system.
}
return;
}
____________________________________________________________________________________________
protocol_execute_realtime()函数主要是是设置各种状态
void protocol_execute_realtime()
{
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
do { // If system is suspended, suspend loop restarts here.
// Check and execute alarms.
rt_exec = sys.rt_exec_alarm; //拷贝全局各自报警标志位变量到rt_exec
if (rt_exec) { // 如果rt_exec为真进入
//系统报警。一切都已经关闭了已经严重错误的东西。报告
//用户错误的来源。如果重要,Grbl禁用通过输入死循环直到系统重置/中止。
sys.state = STATE_ALARM; // 设置系统为报警状态
if (rt_exec & EXEC_ALARM_HARD_LIMIT) {
report_alarm_message(ALARM_HARD_LIMIT_ERROR); //报告报警信息为接近极限值
} else if (rt_exec & EXEC_ALARM_SOFT_LIMIT) { //报告执行软件限位报警
report_alarm_message(ALARM_SOFT_LIMIT_ERROR); //报告出现软件限位报警
} else if (rt_exec & EXEC_ALARM_ABORT_CYCLE) { //执行停止循环报警
report_alarm_message(ALARM_ABORT_CYCLE); //出现终止循环报警
} else if (rt_exec & EXEC_ALARM_PROBE_FAIL) { //执行探查失败报警
report_alarm_message(ALARM_PROBE_FAIL); //出现探查失败报警
} else if (rt_exec & EXEC_ALARM_HOMING_FAIL) { //执行返回原点失败报警
report_alarm_message(ALARM_HOMING_FAIL); //出现返回原点失败报警
}
// Halt everything upon a critical event flag. Currently hard and soft limits flag this.
// 停止一切在一个关键事件标志。目前硬和软限制旗帜。
if (rt_exec & EXEC_CRITICAL_EVENT) { //如果系统是循环实际进入
report_feedback_message(MESSAGE_CRITICAL_EVENT); //报告反馈信息
bit_false_atomic(sys.rt_exec_state,EXEC_RESET); //清除目前的复位状态
do {
// Nothing. Block EVERYTHING until user issues reset or power cycles. Hard limits
// typically occur while unattended or not paying attention. Gives the user time
// to do what is needed before resetting, like killing the incoming stream. The
// same could be said about soft limits. While the position is not lost, the incoming
// stream could be still engaged and cause a serious crash if it continues afterwards.
// TODO: Allow status reports during a critical alarm. Still need to think about implications of this.
// if (sys.rt_exec_state & EXEC_STATUS_REPORT) {
// report_realtime_status();
// bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
// }
} while (bit_isfalse(sys.rt_exec_state,EXEC_RESET)); //((x & mask) == 0)
}
bit_false_atomic(sys.rt_exec_alarm,0xFF); // 清除所有报警标志
}
// Check amd execute realtime commands /校验和执行实时命令
rt_exec = sys.rt_exec_state; //拷贝全局各自报警标志位变量到rt_exec
if (rt_exec) { // Enter only if any bit flag is true
// Execute system abort.
if (rt_exec & EXEC_RESET) {
sys.abort = true; // Only place this is set true.
return; // Nothing else to do but exit.
}
// Execute and serial print status
if (rt_exec & EXEC_STATUS_REPORT) {
report_realtime_status();
bit_false_atomic(sys.rt_exec_state,EXEC_STATUS_REPORT);
}
// Execute hold states.
// NOTE: The math involved to calculate the hold should be low enough for most, if not all,
// operational scenarios. Once hold is initiated, the system enters a suspend state to block
// all main program processes until either reset or resumed.
//执行保持状态。
//注意:所涉及的数学计算持有应该足够低对大多数人来说,即使不是全部,
//操作场景。一旦启动,系统进入暂停状态主程序流程,直到重置或恢复。
//如果全局各自报警标志位其中(执行取消动作) | (执行进给保持) | (执行安全门)任意一位为真进入
if (rt_exec & (EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)) {
// TODO: CHECK MODE? How to handle this? Likely nothing, since it only works when IDLE and then resets Grbl.
//待办事项:检查模式?如何处理呢?可能没有,因为它只会在空闲,然后重置Grbl。
// State check for allowable states for hold methods. 状态检查容许状态的方法。
//系统为闲着状态, 开始循环状态, 回原点状态, 控制取消状态, 开始保持状态, 开始安全门状态时进入
if ((sys.state == STATE_IDLE) || (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_MOTION_CANCEL | STATE_HOLD | STATE_SAFETY_DOOR))) {
// If in CYCLE state, all hold states immediately initiate a motion HOLD.
//如果在循环状态,所有保持状态立即启动运动。
if (sys.state == STATE_CYCLE) { //如果是循环状态执行暂停状态
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.通知持有减速步进模块来验算。
sys.suspend = SUSPEND_ENABLE_HOLD; // Initiate holding cycle with flag.
}
// If IDLE, Grbl is not in motion. Simply indicate suspend ready state.
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_ENABLE_READY; }
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
//执行和标志和减速运动取消并返回空闲。主要由探测使用周期停止和取消剩余的运动。
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. If so, only flag that motion cancel is complete.
// 动作取消只发生在一个周期中,但是,安全门可能事先被启动循环。如果是这样,只有运动标志取消已经完成。
if (sys.state == STATE_CYCLE) { sys.state = STATE_MOTION_CANCEL; }
sys.suspend |= SUSPEND_MOTION_CANCEL; // Indicate motion cancel when resuming. Special motion complete.
}
// Execute a feed hold with deceleration, only during cycle.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR state from prematurely changing back to HOLD.
if (bit_isfalse(sys.state,STATE_SAFETY_DOOR)) { sys.state = STATE_HOLD; }
}
// Execute a safety door stop with a feed hold, only during a cycle, and disable spindle/coolant.
// NOTE: Safety door differs from feed holds by stopping everything no matter state, disables powered
// devices (spindle/coolant), and blocks resuming until switch is re-engaged. The power-down is
// executed here, if IDLE, or when the CYCLE completes via the EXEC_CYCLE_STOP flag.
if (rt_exec & EXEC_SAFETY_DOOR) {
report_feedback_message(MESSAGE_SAFETY_DOOR_AJAR);
// If already in active, ready-to-resume HOLD, set CYCLE_STOP flag to force de-energize.
// NOTE: Only temporarily sets the 'rt_exec' variable, not the volatile 'rt_exec_state' variable.
if (sys.suspend & SUSPEND_ENABLE_READY) { bit_true(rt_exec,EXEC_CYCLE_STOP); }
sys.suspend |= SUSPEND_ENERGIZE;
sys.state = STATE_SAFETY_DOOR;
}
}
bit_false_atomic(sys.rt_exec_state,(EXEC_MOTION_CANCEL | EXEC_FEED_HOLD | EXEC_SAFETY_DOOR)); //清除(执行取消动作)(执行进给保持)(执行安全门)标志位
}
// Execute a cycle start by starting the stepper interrupt to begin executing the blocks in queue.
// 执行一个循环开始启动步进开始执行中断队列的街区
if (rt_exec & EXEC_CYCLE_START) { //循环开始状态进入
// Block if called at same time as the hold commands: feed hold, motion cancel, and safety door.
// Ensures auto-cycle-start doesn't resume a hold without an explicit user-input.
//块如果在同时举行的命令:保持进给,运动取消,和安全的门。 //确保auto-cycle-start没有简历没有显式的用户输入。
if (!(rt_exec & (EXEC_FEED_HOLD | EXEC_MOTION_CANCEL | EXEC_SAFETY_DOOR))) { //状态机如果不是保持进给,运动取消,和安全的门。
// Cycle start only when IDLE or when a hold is complete and ready to resume.
// NOTE: SAFETY_DOOR is implicitly blocked. It reverts to HOLD when the door is closed.
//循环开始时只有当闲置或持有完成并准备简历。
//注意:SAFETY_DOOR是隐式地屏蔽。它返回的时候门是关闭的。
// 如果系统状态为闲着状态,系统状态为开始进给或运动取消,暂停标志为位重新开始
if ((sys.state == STATE_IDLE) || ((sys.state & (STATE_HOLD | STATE_MOTION_CANCEL)) && (sys.suspend & SUSPEND_ENABLE_READY))) {
// Re-energize powered components, if disabled by SAFETY_DOOR.
// 由SAFETY_DOOR重振组件供电,如果禁用。
if (sys.suspend & SUSPEND_ENERGIZE) {
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
//延迟任务:重新启动主轴和冷却剂,延迟升高,然后恢复周期。
if (gc_state.modal.spindle != SPINDLE_DISABLE) { //主轴模式不是失能进入
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); //设置状态和速度
delay_ms(SAFETY_DOOR_SPINDLE_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
if (gc_state.modal.coolant != COOLANT_DISABLE) {
coolant_set_state(gc_state.modal.coolant);
delay_ms(SAFETY_DOOR_COOLANT_DELAY); // TODO: Blocking function call. Need a non-blocking one eventually.
}
// TODO: Install return to pre-park position.
}
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
//开始循环只有排队运动存在于运动没有取消和绘图缓冲器。
//只有在队列马达存在规定的缓冲,并且动机没有让取消,才会循环
if (plan_get_current_block() && bit_isfalse(sys.suspend,SUSPEND_MOTION_CANCEL)) {
sys.state = STATE_CYCLE;
st_prep_buffer(); // Initialize step segment buffer before beginning cycle.初始化步开始循环之前
st_wake_up();
} else { // Otherwise, do nothing. Set and resume IDLE state.否则,什么也不做,设置和复位空闲模式
sys.state = STATE_IDLE;
}
sys.suspend = SUSPEND_DISABLE; // Break suspend state.
}
}
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_START);
}
// Reinitializes the cycle plan and stepper system after a feed hold for a resume. Called by
// realtime command execution in the main program, ensuring that the planner re-plans safely.
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
//重新启动后循环计划和步进系统的进给保持简历。通过实时命令执行主程序,确保安全计划重新计划。
//注意:画线算法变量仍保持通过规划师和步进
//循环仅。步进路径应该继续,好像什么都没发生一样。
//注意:EXEC_CYCLE_STOP由步进子系统周期或进给保持时完成。
if (rt_exec & EXEC_CYCLE_STOP) { //如果是循环停止状态进入
if (sys.state & (STATE_HOLD | STATE_SAFETY_DOOR)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
//保存完整。设置为指示准备简历。继续持有或门状态,直到用户
//已发布了一份简历命令或重置。
if (sys.suspend & SUSPEND_ENERGIZE) { // De-energize system if safety door has been opened. 如果安全的门已被打开。断开系统
spindle_stop();
coolant_stop();
// TODO: Install parking motion here. 安装停车动作。
}
bit_true(sys.suspend,SUSPEND_ENABLE_READY);
} else { // Motion is complete. Includes CYCLE, HOMING, and MOTION_CANCEL states. 电机完成,循环,回原点,,MOTION_CANCEL
sys.suspend = SUSPEND_DISABLE;
sys.state = STATE_IDLE;
}
bit_false_atomic(sys.rt_exec_state,EXEC_CYCLE_STOP);
}
}
// Overrides flag byte (sys.override) and execution should be installed here, since they
// are realtime and require a direct and controlled interface to the main stepper program.
//重写标志字节(sys.override)和执行应该安装在这里,因为他们
//实时和需要直接和控制接口的主要步进程序。
// Reload step segment buffer 重新加载步段缓冲
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_MOTION_CANCEL | STATE_SAFETY_DOOR | STATE_HOMING)) { st_prep_buffer(); }
// If safety door was opened, actively check when safety door is closed and ready to resume.
// NOTE: This unlocks the SAFETY_DOOR state to a HOLD state, such that CYCLE_START can activate a resume.
//如果安全的门被打开,积极检查当安全门关闭,准备简历。
//注意:这解锁SAFETY_DOOR状态保持状态,这样CYCLE_START可以激活一个简历。
if (sys.state == STATE_SAFETY_DOOR) { //安全门状态进入
if (bit_istrue(sys.suspend,SUSPEND_ENABLE_READY)) {
if (!(system_check_safety_door_ajar())) {
sys.state = STATE_HOLD; // Update to HOLD state to indicate door is closed and ready to resume. 更新保存状态指示门关闭,准备简历。
}
}
}
} while(sys.suspend); // Check for system suspend state before exiting.
}
__________________________________________________________________________________________
//阻塞,直到所有缓冲的步骤执行或循环状态。与进给保持
//在一个同步调用,如果它会发生。同时,等待清洁周期结束。
void protocol_buffer_synchronize()
{
// If system is queued, ensure cycle resumes if the auto start flag is present.
//如果排队系统,确保循环简历如果汽车开始标记。
protocol_auto_cycle_start();
do {
protocol_execute_realtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
} while (plan_get_current_block() || (sys.state == STATE_CYCLE));
}
分享: