void protocol_main_loop() { // Perform some machine checks to make sure everything is good to go. #ifdef CHECK_LIMITS_AT_INIT if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) { if (limits_get_state()) { sys.state = STATE_ALARM; // Ensure alarm state is active. report_feedback_message(MESSAGE_CHECK_LIMITS); } } #endif // Check for and report alarm state after a reset, error, or an initial power up. // NOTE: Sleep mode disables the stepper drivers and position can’t be guaranteed. // Re-initialize the sleep state as an ALARM mode to ensure user homes or acknowledges. if (sys.state & (STATE_ALARM | STATE_SLEEP)) { report_feedback_message(MESSAGE_ALARM_LOCK); sys.state = STATE_ALARM; // Ensure alarm state is set. } else { // Check if the safety door is open. sys.state = STATE_IDLE; // 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. // } // All systems go! system_execute_startup(line); // Execute startup script. } // --------------------------------------------------------------------------------- // Primary loop! Upon a system abort, this exits back to main() to reset the system. // This is also where Grbl idles while waiting for something to do. // --------------------------------------------------------------------------------- uint8_t line_flags = 0; uint8_t char_counter = 0; char c; for (;;) { // Process one line of incoming serial data, as the data becomes available. Performs an // initial filtering by removing spaces and comments and capitalizing all letters. while(Getc(&c) == 0) { if ((c == '\n') || (c == '\r')) { // End of line reached protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to calling function upon system abort line[char_counter] = 0; // Set string termination character. #ifdef REPORT_ECHO_LINE_RECEIVED report_echo_line_received(line); #endif // Direct and execute one line of formatted input, and report status of execution. if (line_flags & LINE_FLAG_OVERFLOW) { // Report line overflow error. report_status_message(STATUS_OVERFLOW); } else if (line[0] == 0) { // Empty or comment line. For syncing purposes. report_status_message(STATUS_OK); } else if (line[0] == '$') { // Grbl '$' system command report_status_message(system_execute_line(line)); } else if (sys.state & (STATE_ALARM | STATE_JOG)) { // Everything else is gcode. Block if in alarm or jog mode. report_status_message(STATUS_SYSTEM_GC_LOCK); } else { // Parse and execute g-code block. report_status_message(gc_execute_line(line)); } // Reset tracking data for next line. line_flags = 0; char_counter = 0; } else { if (line_flags) { // Throw away all (except EOL) comment characters and overflow characters. if (c == ')') { // End of '()' comment. Resume line allowed. if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) { line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES); } } } else { if (c <= ' ') { } else if (c == '/') { } else if (c == '(') { line_flags |= LINE_FLAG_COMMENT_PARENTHESES; } else if (c == ';') { line_flags |= LINE_FLAG_COMMENT_SEMICOLON; } else if (char_counter >= (LINE_BUFFER_SIZE-1)) { line_flags |= LINE_FLAG_OVERFLOW; } else if (c >= 'a' && c <= 'z') { // Upcase lowercase line[char_counter++] = c-'a'+'A'; } else { line[char_counter++] = c; } } } } protocol_auto_cycle_start(); protocol_execute_realtime(); // Runtime command check point. if (sys.abort) { return; } // Bail to main() program loop to reset system. } return; /* Never reached */ }
时间: 2025-07-11 19:53:29 浏览: 8
<think>好的,我现在要分析用户提供的`void protocol_main_loop()`函数代码,并尝试理解其结构和功能。首先,我需要仔细阅读代码,了解每个部分的作用,然后按照用户的要求,用中文清晰地解释这个函数。
首先,函数开头有一些预处理指令,比如`#ifdef CHECK_LIMITS_AT_INIT`,这里检查是否启用了硬限制,如果检测到限位触发,系统会进入报警状态。这部分属于初始化时的机器状态检查,确保一切正常。接下来,代码检查系统状态是否为`STATE_ALARM`或`STATE_SLEEP`,如果是,则报告报警锁定,确保系统处于报警状态。否则,系统进入空闲状态,并可能执行启动脚本。
接下来是主循环部分,注释里提到这是主循环,处理系统中断等情况会退出到main()函数。主循环内部有一个无限循环`for (;;)`,里面包含处理串口数据的逻辑。这里使用`Getc(&c)`来逐个读取字符,直到遇到换行符`\n`或`\r`,表示一行结束。处理过程中会过滤空格、注释,并将小写字母转换为大写。
当一行数据接收完毕后,会执行`protocol_execute_realtime()`进行实时命令检查,如果系统终止则返回。然后处理接收到的行数据,根据不同的情况执行相应的操作:如果是空行或注释,返回OK状态;如果是$命令,执行系统命令;如果系统处于报警或点动模式,则锁定;否则解析执行G代码。
处理完一行后,重置行标志和字符计数器,继续下一行处理。之后调用`protocol_auto_cycle_start()`可能用于自动循环启动,再次执行实时命令检查,确保系统没有终止。
在分析过程中,我需要确认每个函数和变量的作用,例如`system_execute_startup`、`gc_execute_line`等,可能需要查找相关定义,但用户提供的代码中没有这些部分,所以只能根据命名推测。例如,`gc_execute_line`可能用于执行G代码行。
需要注意到代码中的条件编译部分,如`#ifdef REPORT_ECHO_LINE_RECEIVED`,这些部分可能根据不同的配置启用或禁用某些功能,比如回显接收到的行。
另外,关于字符处理部分,代码过滤了空格和注释,小写转大写,这可能是为了统一输入的格式,确保后续处理的一致性。行溢出标志的处理也需要注意,当字符数超过缓冲区大小时会设置溢出标志,并报告错误。
在协议执行过程中,实时命令检查点可能处理如急停、重置等实时操作,确保系统响应及时。主循环的设计使得Grbl在等待任务时保持空闲,能够高效处理输入指令。
总结来说,这个函数是Grbl固件的主循环,负责初始化检查、处理输入指令、执行相应的操作(系统命令或G代码),并确保实时响应系统状态变化。需要将各部分功能分解,逐步解释,确保用户能够理解整个流程。</think>`void protocol_main_loop()` 是 Grbl 固件的主控制循环函数,负责系统初始化检查、实时指令处理及串口数据解析。以下是其核心逻辑的逐步解析:
---
### **1. 系统初始化与状态检查**
```c
#ifdef CHECK_LIMITS_AT_INIT
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
if (limits_get_state()) {
sys.state = STATE_ALARM; // 触发限位报警
report_feedback_message(MESSAGE_CHECK_LIMITS);
}
}
#endif
```
- **功能**:在初始化时检查硬件限位开关状态。
- **条件**:若启用硬限位(`BITFLAG_HARD_LIMIT_ENABLE`)且限位触发,系统进入报警状态(`STATE_ALARM`),并发送反馈消息。
---
### **2. 报警与休眠状态处理**
```c
if (sys.state & (STATE_ALARM | STATE_SLEEP)) {
report_feedback_message(MESSAGE_ALARM_LOCK);
sys.state = STATE_ALARM; // 强制锁定为报警状态
} else {
sys.state = STATE_IDLE;
system_execute_startup(line); // 执行启动脚本
}
```
- **报警状态**:若系统处于报警或休眠状态,发送报警锁定消息,并确保状态为 `STATE_ALARM`。
- **空闲状态**:否则系统进入空闲(`STATE_IDLE`)并执行启动脚本(如初始化运动参数)。
---
### **3. 主循环逻辑**
```c
for (;;) {
while(Getc(&c) == 0) {
// 处理串口输入的单个字符
if ((c == '\n') || (c == '\r')) {
// 行结束处理
protocol_execute_realtime(); // 实时命令检查
if (sys.abort) return; // 系统终止时退出
line[char_counter] = 0; // 终止符
// 根据输入内容执行不同操作
if (line_flags & LINE_FLAG_OVERFLOW) {
report_status_message(STATUS_OVERFLOW); // 行溢出错误
} else if (line[0] == 0) {
report_status_message(STATUS_OK); // 空行/注释
} else if (line[0] == '$') {
system_execute_line(line); // 执行系统命令
} else if (sys.state & (STATE_ALARM | STATE_JOG)) {
report_status_message(STATUS_SYSTEM_GC_LOCK); // 报警/点动模式下锁定
} else {
gc_execute_line(line); // 解析并执行G代码
}
// 重置行状态
line_flags = 0;
char_counter = 0;
} else {
// 字符过滤与处理
if (c == '(') line_flags |= LINE_FLAG_COMMENT_PARENTHESES; // 过滤括号注释
else if (c == ';') line_flags |= LINE_FLAG_COMMENT_SEMICOLON; // 分号注释
else if (char_counter >= (LINE_BUFFER_SIZE-1)) line_flags |= LINE_FLAG_OVERFLOW; // 行溢出
else if (c >= 'a' && c <= 'z') line[char_counter++] = c-'a'+'A'; // 小写转大写
else line[char_counter++] = c; // 存储有效字符
}
}
protocol_auto_cycle_start(); // 自动循环启动(如加工任务)
protocol_execute_realtime(); // 再次检查实时命令
if (sys.abort) return; // 终止时退出
}
```
---
### **4. 关键子流程说明**
- **实时命令检查 (`protocol_execute_realtime()`)**
- 处理紧急停止(`!`)、重置(`^X`)等实时指令。
- 确保系统及时响应高优先级事件。
- **G代码解析 (`gc_execute_line()`)**
- 将输入行解析为G代码指令(如 `G0 X10 Y20`)。
- 校验语法并生成运动规划。
- **系统命令 (`system_execute_line()`)**
- 处理以 `$` 开头的配置命令(如 `$G` 查看状态,`$H` 归零)。
- 修改系统参数或返回状态信息。
---
### **5. 错误处理与反馈**
- **行溢出错误**:当输入行超过缓冲区大小时报告 `STATUS_OVERFLOW`。
- **报警锁定**:在 `STATE_ALARM` 或 `STATE_JOG` 状态下拒绝执行新指令。
- **状态反馈**:通过 `report_status_message()` 返回执行结果(如 `STATUS_OK`)。
---
### **总结**
`protocol_main_loop()` 是 Grbl 的核心调度器,其职责包括:
1. **系统状态监控**(限位、报警、休眠)。
2. **串口数据解析**(过滤注释、格式化输入)。
3. **指令分发**(实时命令、系统命令、G代码)。
4. **实时响应**(急停、复位等中断处理)。
该循环确保 Grbl 在高效执行任务的同时,始终保持对硬件状态和用户输入的实时响应。
阅读全文
相关推荐


















