Grbl简介

Grbl是用于CNC铣削的基于串口的运动控制器,具有高性能、低成本,使用高度优化的c语言编写,接受符合标准的G代码,完全支持弧,圆和螺旋运动,以及所有其他主要g代码命令。Grbl包括全面的加速管理和前瞻性。 这意味着控制器将对未来进行多达18次的运动规划,并提前计划其速度,以实现平稳的加速和无晃动的转弯。

官方开源链接: https://github.com/grbl/grbl/

系统层面介绍

这里使用的是移植到STM32后的Grbl8.0的程序进行分析,只对核心的部分进行详细的分析,如有错位请指正。

1.’protocol’ : Accepts command lines from the serial port and passes them to ‘gcode’ for execution.
Provides status responses for each command. Also manages run-time commands set by
the serial interrupt.
接收串口命令传递给gcode执行,给命令提供应答,通过串口中断管理程序命令
2.’gcode’ : Recieves gcode from ‘protocol’, parses it according to the current state
of the parser and issues commands via ‘…_control’ modules
从1接收G代码,解析G代码
3.’spindle_control’ : Commands for controlling the spindle.
主轴控制,雕刻机的主轴带刀的轴,与XYZ无关,M3,4,5有关于主轴正反转停止命令
4.’motion_control’ : Accepts motion commands from ‘gcode’ and passes them to the ‘planner’. This module
represents the public interface of the planner/stepper duo.
从2接收运动命令将其传递给5,相当于一个发出命令的高层接口

5.’planner’ : Receives linear motion commands from ‘motion_control’ and adds them to the plan of
prepared motions. It takes care of continuously optimizing the acceleration profile
as motions are added.
从4接收线性运动的命令,并将其添加到准备运动计划中(计算的数据写入唤醒缓冲区)
随着运动不断被添加负责优化计算加速度分布图

6.’stepper’ : Executes the motions by stepping the steppers according to the plan.
执行动作,用两个定时器来控制三个轴完成相应的动作

4.1.2Supporting files:

‘config.h’ : Compile time user settings
一些全局变量的宏定义,例如MINIMUM_STEPS_PER_MINUTE最低每分钟跳动多少下,也就是最低
频率就在这里声明的

‘settings’ : Maintains the run time settings record in eeprom and makes it available
to all modules.
全局中主要的参数设置,$$命令打印出来的参数都是这里设置的
上电读取EEPROM的值,如果读取失败调用default值,都在这里

‘eeprom’ : A library from Atmel that provides methods for reading and writing the eeprom with
a small addition from us that read and write binary streams with check sums used
to verify validity of the settings record.
存放参数的作用,有关于EPROM的读写函数
‘nuts_bolts.h’ : A collection of global variable definitions, useful constants, and macros used everywhere
一些全局变量的定义

‘serial’ : Low level serial communications and picks off run-time commands real-time for asynchronous
control.
串口控制台

‘print’ : Functions to print strings of different formats (using serial)
打印不同格式的字符串函数在这里定义的
大致框架如下所示

系统初始化

    JTAG_Set(SWD_ENABLE);            //JTAG接口状态设置,只是用SWD下载,减少IO口占用
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);  
    delay_init();                     //延时初始化

    // Initialize system upon power-up.  //开机系统初始化
    serial_init();   // Setup serial baud rate and interrupts 初始化串口及串口中断
    settings_init(); // Load Grbl settings from EEPROM 从EEPROM加载Grbl设置
    stepper_init();  // Configure stepper pins and interrupt timers 配置步进电机控制引脚及定时器中断
    system_init();   // Configure pinout pins and pin-change interrupt  配置限位开关及按键中断
    
    memset(&sys, 0, sizeof(system_t));  // Clear all system variables 清除所有系统变量
    sys.abort = true;   // Set abort to complete initialization 将abort设置为完成初始化
    sei(); // Enable interrupts 使能中断
    
    // Check for power-up and set system alarm if homing is enabled to force homing cycle
    // by setting Grbl's alarm state. Alarm locks out all g-code commands, including the
    // startup scripts, but allows access to settings and internal commands. Only a homing
    // cycle '$H' or kill alarm locks '$X' will disable the alarm.
    // NOTE: The startup script will run after successful completion of the homing cycle, but
    // not after disabling the alarm locks. Prevents motion startup blocks from crashing into
    // things uncontrollably. Very bad.
    // 上电时如果开始上电回位将启用系统报警
#ifdef HOMING_INIT_LOCK
    if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
#endif
    
    // Force Grbl into an ALARM state upon a power-cycle or hard reset. 在上电后或者复位后启用报警
#ifdef FORCE_INITIALIZATION_ALARM
    sys.state = STATE_ALARM;
#endif
    
    // Grbl initialization loop upon power-up or a system abort. For the latter, all processes
    // will return to this loop to be cleanly re-initialized.
    // 在电源启动或系统中止时进行Grbl初始化循环。对于后者,所有进程都将返回到这个循环以进行干净的重新初始化。
    for(;;) 
    {

        // TODO: Separate configure task that require interrupts to be disabled, especially upon
        // a system abort and ensuring any active interrupts are cleanly reset.
        // TODO:单独的配置任务,需要禁用中断,特别是在系统中止时,并确保所有活动中断被干净地重置。
      
        // Reset Grbl primary systems. 复位Grbl主系统
        serial_reset_read_buffer(); // Clear serial read buffer 清除串口读取缓冲区
        gc_init(); // Set g-code parser to default state 将g-code解析器设置为默认状态
        spindle_init(); // 配置主轴速度控制PWM引脚及定时器
        coolant_init(); // 配置冷却风扇控制引脚
        limits_init();  // 配置限位触发引脚
        probe_init();   // 探测开关引脚配置
        plan_reset(); // Clear block buffer and planner variables 清除块缓冲区和计划变量
        st_reset(); // Clear stepper subsystem variables. 清除步进器子系统变量。
    
        // Sync cleared gcode and planner positions to current system position.
        // 同步清除gcode和计划员位置到当前系统位置。
        plan_sync_position();
        gc_sync_position();
    
        // Reset system variables. 重置系统变量。
        sys.abort = false;
        sys_rt_exec_state = 0;
        sys_rt_exec_alarm = 0;
        sys.suspend = false;
        sys.soft_limit = false;
                  
        // Start Grbl main loop. Processes program inputs and executes them.
        // 启动Grbl主循环。处理程序输入并执行它们。
        protocol_main_loop();
    }

数据接收与处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/* 
GRBL PRIMARY LOOP:
*/
void protocol_main_loop(void)
{
uint8_t comment = COMMENT_NONE;
uint8_t char_counter = 0;
uint8_t c;
// ------------------------------------------------------------
// Complete initialization procedures upon a power-up or reset.
// ------------------------------------------------------------

// Print welcome message //打印欢迎信息
report_init_message();

// Check for and report alarm state after a reset, error, or an initial power up.
// 检查并报告报警状态后,复位,错误,或初始电源。
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.将系统设置为ready。清除所有标志。
}
system_execute_startup(line); // Execute startup script.启动脚本执行。
}

// ---------------------------------------------------------------------------------
// Primary loop! Upon a system abort, this exits back to main() to reset the system.
// 主循环!在系统中止时,退出到main()来重置系统。
// ---------------------------------------------------------------------------------

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.
// 当数据变得可用时,处理一行输入的串行数据。执行一个通过删除空格和注释并将所有字母大写来进行初始过滤。

// NOTE: While comment, spaces, and block delete(if supported) handling should technically
// be done in the g-code parser, doing it here helps compress the incoming data into Grbl's
// line buffer, which is limited in size. The g-code standard actually states a line can't
// exceed 256 characters, but the Arduino Uno does not have the memory space for this.
// With a better processor, it would be very easy to pull this initial parsing out as a
// seperate task to be shared by the g-code parser and Grbl's system commands.

while((c = serial_read()) != SERIAL_NO_DATA) {
if ((c == '\n') || (c == '\r')) { // End of line reached 到达行尾
line[char_counter] = 0; // Set string termination character.设置字符串终止字符
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 == ')') {
// End of comment. Resume line. But, not if semicolon type comment.
if (comment == COMMENT_TYPE_PARENTHESES) { comment = COMMENT_NONE; }
}
} else {
if (c <= ' ') {
// Throw away whitepace and control characters 丢弃空白和注释字符
} else if (c == '/') {
// Block delete NOT SUPPORTED. Ignore character.
// NOTE: If supported, would simply need to check the system if block delete is enabled.
} 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') { // Upcase lowercase
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; /* Never reached */
}