一、GRBL
GRBL 是一款高性能、开源的嵌入式固件,它让廉价的 Arduino 开发板(如 Arduino Uno)能够控制 CNC 机床、激光雕刻机、绘图仪等机器的运动。
但是,搜罗网上各种教程,并没有讲怎么样移植的内容,所以再制作写字机的过程中,写下此篇记录自己的成长。
本内容只会讲怎么移植,不涉及具体内容,如算法等。
二、代码详解
虽然我也不知道算算详解,应该也没有人能看到。
关于创建工程和导入文件不再赘述。
初始工程如下:

创建一个grbl 的文件夹,将从github上下载的grbl源码导入进去。
1、map_cpu.h
map_cpu.h文件为 ATmega328P(Arduino Uno)处理器提供了默认的引脚映射。它是 GRBL 固件的核心配置文件之一,确保固件知道哪个功能使用哪个引脚。
(1)步进脉冲输出引脚
#define STEP_DDR DDRD
#define STEP_PORT PORTD
#define X_STEP_BIT 2 // Uno Digital Pin 2
#define Y_STEP_BIT 3 // Uno Digital Pin 3
#define Z_STEP_BIT 4 // Uno Digital Pin 4
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) // All step bits
先了解下原本(AVR)的控制模型
DDRx(Data Direction Register):为方向控制,配置引脚为输入或者输出,就好比STM32的输入输出(MODE),模拟输入,推挽输出等。
PORTx:输出控制,当引脚设为输出时,控制其电平,类似STM32的ODR(输出数据)寄存器
PINx:这个就不必说了吧,选择某个引脚
关键的点在于,对于原本的 ATmega328P(Arduino Uno)来说,不需要手动”开启时钟”,因为它的时钟系统非常简单且大部分外设默认就是开启的。而要移植到STM32上,那就必须要手动开启时钟了。
我选择将PA0、PA1、PA2作为步进脉冲输出引脚
更改如下:
// Define step pulse output pins. NOTE: All step bit pins must be on the same port.
//定义步进脉冲输出引脚。注:所有的步进脉冲引脚必须定义在同一个端口上。
#define STEP_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE() //如果使用CubeIDE,按理说 //不用,但还是写上吧
#define STEP_GPIO_PORT GPIOA
#define X_STEP_Pin GPIO_PIN_1 // GPIOA
#define Y_STEP_Pin GPIO_PIN_2 //
#define Z_STEP_Pin GPIO_PIN_3 //
#define STEP_MASK (X_STEP_PIN | Y_STEP_PIN | Z_STEP_PIN)// All step bits
// 兼容原GRBL的位定义
#define X_STEP_BIT 2
#define Y_STEP_BIT 3
#define Z_STEP_BIT 4
#define STEP_MASK ((1<<X_STEP_BIT)|(1<<Y_STEP_BIT)|(1<<Z_STEP_BIT)) //此处为11100
(2)串口相关
// Define serial port pins and interrupt vectors.
#define SERIAL_RX USART_RX_vect // 串口接收完成中断向量
#define SERIAL_UDRE USART_UDRE_vect // 串口数据寄存器空中断向量
额,怎么说呢,跟中断相关,直接删去也行。
因为AVR的中断跟STM32的中断机制不一样,所以不使用这个也没关系,当要保证中断要处理相同的东西即可。后面会在serial.c这个文件里面详细说说。
对了,我打算使用串口1(USART1),对应的引脚是PA9和PA10吧。
(3)步进电机方向控制引脚
// Define step direction output pins. NOTE: All direction pins must be on the same port.
//定义方向电平输出引脚。注:所有的方向引脚必须定义在同一个端口上。
#define DIRECTION_DDR DDRD
#define DIRECTION_PORT PORTD
#define X_DIRECTION_BIT 5 // Uno Digital Pin 5
#define Y_DIRECTION_BIT 6 // Uno Digital Pin 6
#define Z_DIRECTION_BIT 7 // Uno Digital Pin 7
// 兼容原GRBL的位定义
#define X_DIRECTION_BIT 5
#define Y_DIRECTION_BIT 6
#define Z_DIRECTION_BIT 7
#define DIRECTION_MASK ((1<<X_DIRECTION_BIT)|(1<<Y_DIRECTION_BIT)|(1<<Z_DIRECTION_BIT))
与步进脉冲输出引脚类似的配置方式,不过这个是控制步进电机旋转方向的。
我打算分配PA4、PA5、PA6给X、Y、Z。
修改如下:
// Define step direction output pins. NOTE: All direction pins must be on the same port.
//定义方向电平输出引脚。注:所有的方向引脚必须定义在同一个端口上。
#define DIRECTION_GPIO_CLK_ENABLE() __HAL_RCC_GPIOA_CLK_ENABLE()
#define DIRECTION_GPIO_PORT GPIOA
#define X_DIRECTION_PIN GPIO_PIN_4 // PA4
#define Y_DIRECTION_PIN GPIO_PIN_5 // PA5
#define Z_DIRECTION_PIN GPIO_PIN_6 // PA6
#define DIRECTION_MASK (X_DIRECTION_PIN | Y_DIRECTION_PIN | Z_DIRECTION_PIN) // All direction bits
(4)步进电机驱动器的使能/禁用控制引脚
// Define stepper driver enable/disable output pin.
#define STEPPERS_DISABLE_DDR DDRB
#define STEPPERS_DISABLE_PORT PORTB
#define STEPPERS_DISABLE_BIT 0 // Uno Digital Pin 8
#define STEPPERS_DISABLE_MASK (1<<STEPPERS_DISABLE_BIT)
一眼丁真,使能电机驱动模块。没什么好说的。我用的是TMC2209模块,低电平使能。且两个驱动模块共用同一个IO口以保持协调一致。
初始电平为高电平,同时
修改如下:
// Define stepper driver enable/disable output pin.
//定义步进电机驱动器使能引脚
#define STEPPERS_DISABLE_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define STEPPERS_DISABLE_GPIO_PORT GPIOB
#define STEPPERS_DISABLE_Pin GPIO_PIN_0 //PB0
// 兼容原GRBL的位定义
#define STEPPERS_DISABLE_BIT 0
#define STEPPERS_DISABLE_MASK (1 << STEPPERS_DISABLE_BIT)
(5)限位开关(硬限位/归位开关)的硬件配置
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL).
#define LIMIT_DDR DDRB
#define LIMIT_PIN PINB
#define LIMIT_PORT PORTB
#define X_LIMIT_BIT 1 // Uno Digital Pin 9
#define Y_LIMIT_BIT 2 // Uno Digital Pin 10
#ifdef VARIABLE_SPINDLE // Z Limit pin and spindle enabled swapped to access hardware PWM on Pin 11.
#define Z_LIMIT_BIT 4 // Uno Digital Pin 12
#else
#define Z_LIMIT_BIT 3 // Uno Digital Pin 11
#endif
#if !defined(ENABLE_DUAL_AXIS)
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
#endif
#define LIMIT_INT PCIE0 // Pin change interrupt enable pin
#define LIMIT_INT_vect PCINT0_vect
#define LIMIT_PCMSK PCMSK0 // Pin change interrupt register
限位开关用于机器行程的极限位置检测,以及归零操作。使用STM32的外部中断即可,注意要能产生中断的IO口。
2025.12.22:目前来讲,在我能正常运行grbl时,完全没使用过限位开关这一功能(暂时),简单来说,先让代码不报错吧。
修改如下:
// Define homing/hard limit switch input pins and limit interrupt vectors.
// NOTE: All limit bit pins must be on the same port, but not on a port with other input pins (CONTROL).
//定义原点/硬限位开关输入引脚和限位中断向量
//注意:所有的限位引脚必须使用同一个中断处理函数。
#define LIMIT_GPIO_CLK_ENABLE() __HAL_RCC_GPIOC_CLK_ENABLE()
#define LIMIT_PORT GPIOC
#define X_LIMIT_PIN GPIO_PIN_1 // PC1
#define Y_LIMIT_PIN GPIO_PIN_2 // PC2
#define Z_LIMIT_PIN GPIO_PIN_3 // PC3
#define LIMIT_INT_PORTSOURCE GPIO_PortSourceGPIOC //中断源端口
#define X_LIMIT_INT_PINSOURCE GPIO_PinSource1 //中断源引脚 注:这三个为标准库风格
#define Y_LIMIT_INT_PINSOURCE GPIO_PinSource2 //中断源引脚
#define Z_LIMIT_INT_PINSOURCE GPIO_PinSource3 //中断源引脚
#define X_LIMIT_INT_LINE EXTI_LINE_1 //中断线
#define Y_LIMIT_INT_LINE EXTI_LINE_2 //中断线
#define Z_LIMIT_INT_LINE EXTI_LINE_3 //中断线
#define X_LIMIT_EXTI_IRQn EXTI1_IRQn //中断请求号
#define Y_LIMIT_EXTI_IRQn EXTI2_IRQn
#define Z_LIMIT_EXTI_IRQn EXTI3_IRQn
// 物理引脚掩码(用于GPIO操作)
#define LIMIT_PINS_MASK (X_LIMIT_PIN | Y_LIMIT_PIN | Z_LIMIT_PIN)
// 中断优先级(必须定义)
#define LIMIT_IRQ_PRIORITY 0 // 最高优先级
// 触发边沿(必须定义)
#define LIMIT_EXTI_TRIGGER EXTI_TRIGGER_FALLING // 下降沿触发
// 兼容原GRBL的位定义
#define X_LIMIT_BIT 1
#define Y_LIMIT_BIT 2
#define Z_LIMIT_BIT 3
#define LIMIT_MASK ((1<<X_LIMIT_BIT)|(1<<Y_LIMIT_BIT)|(1<<Z_LIMIT_BIT)) // All limit bits
(6)用户控制按钮(操作面板) 的硬件配置
// Define user-control controls (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_DDR DDRC
#define CONTROL_PIN PINC
#define CONTROL_PORT PORTC
#define CONTROL_RESET_BIT 0 // Uno Analog Pin 0
#define CONTROL_FEED_HOLD_BIT 1 // Uno Analog Pin 1
#define CONTROL_CYCLE_START_BIT 2 // Uno Analog Pin 2
#define CONTROL_SAFETY_DOOR_BIT 1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT1_vect
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_MASK ((1<<CONTROL_RESET_BIT)|(1<<CONTROL_FEED_HOLD_BIT)|(1<<CONTROL_CYCLE_START_BIT)|(1<<CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
用户控制按钮(操作面板) 的硬件配置,包括循环启动、复位、进给保持等功能按键。这是 CNC 系统的人机交互接口。是不是更复杂了?刚好和LVGL联动?
其实就是就是按某个按键执行某个函数而已,应该是复位/急停、暂停/继续、安全门、专用循环启动。
修改如下:
// Define user-control controls (cycle start, reset, feed hold) input pins.
// NOTE: All CONTROLs pins must be on the same port and not on a port with other input pins (limits).
#define CONTROL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define CONTROL_GPIO_PORT GPIOB
#define CONTROL_RESET_PIN GPIO_PIN_4 // PB4
#define CONTROL_FEED_HOLD_PIN GPIO_PIN_5 // PB5
#define CONTROL_CYCLE_START_PIN GPIO_PIN_6 // PB6
#define CONTROL_SAFETY_DOOR_Pin GPIO_PIN_7 // PB7
#define CONTROL_INT_PORTSOURCE GPIO_PortSourceGPIOB //中断源端口
#define RESET_INT_PINSOURCE GPIO_PinSource4 //中断源引脚:复位
#define FEED_HOLD_INT_PINSOURCE GPIO_PinSource5 //中断源引脚:给进保持
#define CYCLE_START_INT_PINSOURCE GPIO_PinSource6 //中断源引脚:循环开始
#define SAFETY_DOOR_INT_PINSOURCE GPIO_PinSource7 //中断源引脚:紧急停车
#define RESET_INT_LINE EXTI_LINE_4 //中断线:复位
#define FEED_HOLD_INT_LINE EXTI_LINE_5 //中断线:给进保持
#define CYCLE_START_INT_LINE EXTI_LINE_6 //中断线:循环开始
#define SAFETY_DOOR_INT_LINE EXTI_LINE_7 //中断线:紧急停车
// 中断请求号
#define RESET_EXTI_IRQn EXTI4_IRQn // EXTI线4独立中断
#define FEED_HOLD_EXTI_IRQn EXTI9_5_IRQn // EXTI线5-9共享中断
#define CYCLE_START_EXTI_IRQn EXTI9_5_IRQn // 同上
#define SAFETY_DOOR_EXTI_IRQn EXTI9_5_IRQn // 同上
// 兼容原GRBL的位定义
#define CONTROL_RESET_BIT 0 // Uno Analog Pin 0
#define CONTROL_FEED_HOLD_BIT 1 // Uno Analog Pin 1
#define CONTROL_CYCLE_START_BIT 2 // Uno Analog Pin 2
#define CONTROL_SAFETY_DOOR_BIT 3 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_MASK ((1<<CONTROL_RESET_BIT)|(1<<CONTROL_FEED_HOLD_BIT)|(1<<CONTROL_CYCLE_START_BIT)|(1<<CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
(7)探针(Probe)开关的输入引脚
// Define probe switch input pin.
//定义探测开关输入引脚
#define PROBE_DDR DDRC
#define PROBE_PIN PINC
#define PROBE_PORT PORTC
#define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT)
其实我的写字机应该用不上,但万一用到呢?所以还是写上了。、
2025.12.22:确实没用上
如下:
// Define probe switch input pin.
//定义探测开关输入引脚
#define PROBE_GPIO_CLK_ENABLE() __HAL_RCC_GPIOB_CLK_ENABLE()
#define PROBE_Pin GPIO_PIN_1
#define PROBE_GPIO_Port GPIOB
// 兼容原GRBL的位定义
#define PROBE_BIT 5 // Uno Analog Pin 5
#define PROBE_MASK (1<<PROBE_BIT)
最后,再添加一段写字机专用的宏:
// 写字机专用:抬笔/落笔控制
#define PEN_SERVO_PWM_GPIO_PORT GPIOA
#define PEN_SERVO_PWM_PIN GPIO_PIN_8 // PA8,定时器1通道1
#define PEN_UP_POSITION 1500 // 舵机角度:抬笔
#define PEN_DOWN_POSITION 1000 // 舵机角度:落笔
2025.12.22:也没用上
先来总结一下IO口的分配
| GPIO | EXTI | MODE | 宏定义 |
|---|---|---|---|
| PA1 | 输出 | X_STEP_Pin | |
| PA2 | 输出 | Y_STEP_Pin | |
| PA3 | 输出 | Z_STEP_Pin | |
| PA4 | 输出 | X_DIRECTION_PIN | |
| PA5 | 输出 | Y_DIRECTION_PIN | |
| PA6 | 输出 | Z_DIRECTION_PIN | |
| PA7 | 输出 | PEN_SERVO_PWM_PIN | |
| PB0 | 输出 | STEPPERS_DISABLE_Pin | |
| PB1 | 输出 | PROBE_Pin | |
| PB4 | EXTI_LINE_4 | 下拉输入 | CONTROL_RESET_PIN |
| PB5 | EXTI_LINE_5 | 下拉输入 | CONTROL_FEED_HOLD_PIN |
| PB6 | EXTI_LINE_6 | 下拉输入 | CONTROL_CYCLE_START_PIN |
| PB7 | EXTI_LINE_7 | 下拉输入 | CONTROL_SAFETY_DOOR_Pin |
| PC1 | EXTI_LINE_1 | 下拉输入 | X_LIMIT_PIN |
| PC2 | EXTI_LINE_2 | 下拉输入 | Y_LIMIT_PIN |
| PC3 | EXTI_LINE_3 | 下拉输入 | Z_LIMIT_PIN |
到此,宏定义的修改就差不多了,接下来到各函数的修改。
2、coolant_control.c
对于写字机项目,冷却液(coolant)功能不需要。报错就删掉。
3、eeprom.c
EEPROM 操作函数,用于读写非易失性存储器(存储 GRBL 的配置参数)。
暂时用STM32的Flash来模拟EEPROM。
重构unsigned char eeprom_get_char( unsigned int addr )和void eeprom_put_char( unsigned int addr, unsigned char new_value )即可。
2025.12.22:就EEPROM而言,目前还没搞好,使用Flash来模拟EEPROM的话,要考虑到STM32中,要擦除某一段数据,就需要整个扇区进行擦除,很麻烦,现在而言只不过让代码不报错而已,后面应该会换成专用的EEPROM芯片,如24C64等。
如下:
void Flash_WriteData( unsigned int addr, unsigned char new_value )
{
HAL_FLASH_Unlock();
uint32_t PageError = 0;
__disable_irq(); //关闭全局中断
if(HAL_FLASHEx_Erase(&EraseInitStruct, &PageError) == HAL_OK) //擦除
{
}
__enable_irq(); //开全局中断
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr, new_value);
HAL_FLASH_Lock();
}
uint_8 Flash_ReadData( unsigned int addr )
{
return *(__IO uint16_t*)addr;
}
unsigned char eeprom_get_char( unsigned int addr )
{
unsigned char data;
data = Flash_ReadData(addr);
return data;
}
void eeprom_put_char( unsigned int addr, unsigned char new_value )
{
Flash_WriteData(addr, new_value);
}
4、gcode.c
整个GRBL中的核心文件,用来解析G代码命令,负责将文本形式的 G 代码转换为机器能够执行的内部指令。
不需要修改。
5、jog.c
这个文件处理 手动控制移动 功能,允许用户通过手动控制(如按钮、摇杆)或 G 代码命令($J=)来移动机器,而不需要执行完整的 G 代码程序。不需要修改。
6、limits.c
唉,应该是改动量最大的一个文件了?
2025.12.22:目前为止,使用的是软限位,还未添加硬件限位功能,
limits.c 是 GRBL 的限位开关和归位(Homing)功能的核心实现文件。这个文件非常重要,负责机器的安全保护和自动零点定位。
接下来分点来说说要修改的函数
(1)void limits_init()
这个函数其实就是对引脚的初始化配置,如果用的STM32CubeIDE,应该是自动配置完成了,就是要注意初始电平是什么。
不只是要配置引脚,还要对中断进行配置。
void limits_init()
{
if (bit_istrue(settings.flags,BITFLAG_HARD_LIMIT_ENABLE)) {
HAL_NVIC_EnableIRQ(X_LIMIT_EXTI_IRQn);
HAL_NVIC_EnableIRQ(Y_LIMIT_EXTI_IRQn);
HAL_NVIC_EnableIRQ(Z_LIMIT_EXTI_IRQn);
} else {
limits_disable();
}
//需要去抖?
//注意:中断配置已经在CubeIDE配置好了,这里主要是使能外部中断和关闭中断
// 如果不是使用STM32CubeIDE或STM32CubeMX,需要自己配置中断
}
(2)void limits_disable()
关闭中断,没什么好说的
// Disables hard limits.
void limits_disable()
{
HAL_NVIC_DisableIRQ(X_LIMIT_EXTI_IRQn);
HAL_NVIC_DisableIRQ(Y_LIMIT_EXTI_IRQn);
HAL_NVIC_DisableIRQ(Z_LIMIT_EXTI_IRQn);
}
(3)uint8_t limits_get_state()
这是一个 GRBL 中获取限位开关状态 的函数,用于检测限位开关的状态,并返回一个表示哪个轴触发了限位的位掩码。
uint8_t limits_get_state()
{
uint8_t limit_state = 0;
uint8_t pin = (uint8_t)(((HAL_GPIO_ReadPin(LIMIT_PORT, X_LIMIT_PIN) << X_LIMIT_BIT) | \
(HAL_GPIO_ReadPin(LIMIT_PORT, Y_LIMIT_PIN) << Y_LIMIT_BIT) | \
(HAL_GPIO_ReadPin(LIMIT_PORT, Z_LIMIT_PIN) << Z_LIMIT_BIT)) & LIMIT_MASK);
#ifdef INVERT_LIMIT_PIN_MASK
pin ^= INVERT_LIMIT_PIN_MASK;
#endif
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { pin ^= LIMIT_MASK; }
if (pin) {
uint8_t idx;
for (idx=0; idx<N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx)) { limit_state |= (1 << idx); }
}
#ifdef ENABLE_DUAL_AXIS
if (pin & (1<<DUAL_LIMIT_BIT)) { limit_state |= (1 << N_AXIS); }
#endif
}
return(limit_state);
}
(4)ISR(LIMIT_INT_vect)
AVR的中断服务例程(ISR),用于处理限位引脚变化中断。
需要改造成适配STN32的中断
如下:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
switch(GPIO_Pin)
{
case X_LIMIT_Pin:
if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
break;
case Y_LIMIT_Pin:
if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
break;
case Z_LIMIT_Pin:
if (sys.state != STATE_ALARM) {
if (!(sys_rt_exec_alarm)) {
#ifdef HARD_LIMIT_FORCE_STATE_CHECK
// Check limit pin state.
if (limits_get_state()) {
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
}
#else
mc_reset(); // Initiate system kill.
system_set_exec_alarm(EXEC_ALARM_HARD_LIMIT); // Indicate hard limit critical event
#endif
}
}
break;
}
//暂未消抖
}
7、motion_control.c
这个文件包含了运动控制的核心算法,比如直线插补、圆弧插补等。由于这些算法通常是数学计算,不直接依赖于硬件,因此该代码无需修改。
8、nuts_bolts.c
主要包含 GRBL 的通用工具函数和宏定义,里面有几个延时函数需要替换为STM32的。
9、planner.c
主要涉及数学计算和数据结构操作,通常不需要修改
10、print.c
主要是格式化输出逻辑,需要修改的在serial.c这个文件。
修改void printPgmString(const char *s)如下即可:
// Print a string stored in PGM-memory
void printPgmString(const char *s)
{
printString(s);
}
11、prode.c
这是 GRBL 的探针功能文件,其实就是初始化GPIO的一些代码,对于我的写字机来说暂时不需要,删掉报错的就好。
12、protocol.c
这个文件是 GRBL 的 G 代码解析和通信协议处理核心,通常大部分不需要修改。
13、report.c
这个文件是 GRBL 的状态报告和消息发送模块
在标准C中,PSTR是一个宏,通常用于AVR编程,它将字符串字面量放入程序存储器(Flash)中,并通过指针访问。
定义以下宏即可
#define PSTR(str) (str)
14、serial.c
该文件直接处理串口通信硬件层,需要修改
(1)serial_init()
串口初始化函数,如果使用STM32CubeIDE或MX可能方便点,如果是Keil就得自己手动配置,还要有中断配置
(2)void serial_write(uint8_t data)
将AVR的串口中断使能换成STM32的即可,如下:
// Writes one byte to the TX serial buffer. Called by main program.
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_RING_BUFFER) { next_head = 0; }
// Wait until there is space in the buffer
while (next_head == serial_tx_buffer_tail) {
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
if (sys_rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
}
// Store data and advance head
serial_tx_buffer[serial_tx_buffer_head] = data;
serial_tx_buffer_head = next_head;
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE); //
}
记得引入stm32的头文件
(3)中断相关函数
没使用回调函数。因为回调函数是在中断完成时调用,与grbl 的逻辑不符。
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE) && //发�??
__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE))
{
uint8_t tail = serial_tx_buffer_tail;
USART1->DR = serial_tx_buffer[tail]; //将数据传给发送寄存器
tail++;
if (tail == TX_RING_BUFFER) { tail = 0; } //当数据下标为105时,重置下标
serial_tx_buffer_tail = tail;
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
if (tail == serial_tx_buffer_head) { __HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE); } //发�?�完成就关闭中断
}
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_RXNE) && //接收
__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE))
{
uint8_t data = USART1->DR; //读取�??个数据并赋给data
//HAL_UART_Transmit(&huart1, &data, 1, 1000);
uint8_t next_head;
switch (data)
{
case CMD_RESET: mc_reset(); break; // Call motion control reset routine.
case CMD_STATUS_REPORT: system_set_exec_state_flag(EXEC_STATUS_REPORT); break; // Set as true
case CMD_CYCLE_START: system_set_exec_state_flag(EXEC_CYCLE_START); break; // Set as true
case CMD_FEED_HOLD: system_set_exec_state_flag(EXEC_FEED_HOLD); break; // Set as true
default :
if (data > 0x7F) { // Real-time control characters are extended ACSII only.
switch(data) {
case CMD_SAFETY_DOOR: system_set_exec_state_flag(EXEC_SAFETY_DOOR); break; // Set as true
case CMD_JOG_CANCEL:
if (sys.state & STATE_JOG) { // Block all other states from invoking motion cancel.
system_set_exec_state_flag(EXEC_MOTION_CANCEL);
}
break;
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
case CMD_FEED_OVR_COARSE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_MINUS); break;
case CMD_FEED_OVR_FINE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_PLUS); break;
case CMD_FEED_OVR_FINE_MINUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_FINE_MINUS); break;
case CMD_RAPID_OVR_RESET: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_RESET); break;
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
#ifdef ENABLE_M7
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
// Write data to buffer unless it is full.
if (next_head != serial_rx_buffer_tail) {
serial_rx_buffer[serial_rx_buffer_head] = data;
serial_rx_buffer_head = next_head;
}
}
}
}
/* USER CODE END USART1_IRQn 1 */
}
15、settings.c
把__flash删掉。
基本不需要修改。
16、spindle_control.c
是 GRBL 的主轴控制模块,负责控制 CNC 主轴(铣床/雕刻机的主轴电机)。
可以替换为舵机进行修改。
2025.12.22:最后使用Z轴映射为舵机
17、stepper.c
对于 stepper.c 文件,这是 GRBL 中最关键、最复杂、实时性要求最高的文件。需要大量修改,因为它直接控制步进电机的精确脉冲时序。
(1)st_wake_up()
唤醒空闲状态下的步进电机系统,使其准备执行新的运动命令
void st_wake_up()
{
// Enable stepper drivers. 使能步进驱动
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE))
{
//STEPPERS_DISABLE_GPIO_PORT |= (1<<STEPPERS_DISABLE_BIT); //
HAL_GPIO_WritePin(STEPPERS_DISABLE_GPIO_PORT, STEPPERS_DISABLE_Pin, 0);
}
else
{
//STEPPERS_DISABLE_GPIO_PORT &= ~(1<<STEPPERS_DISABLE_BIT);
HAL_GPIO_WritePin(STEPPERS_DISABLE_GPIO_PORT, STEPPERS_DISABLE_Pin, 0);
}
// Initialize stepper output bits to ensure first ISR call does not step.
st.step_outbits = step_port_invert_mask;
// Initialize step pulse timing from settings. Here to ensure updating after re-writing.
#ifdef STEP_PULSE_DELAY
// Set total step pulse time after direction pin set. Ad hoc computation from oscilloscope.
st.step_pulse_time = -(((settings.pulse_microseconds+STEP_PULSE_DELAY-2)*TICKS_PER_MICROSECOND) >> 3);
// Set delay between direction pin write and step command.
OCR0A = -(((settings.pulse_microseconds)*TICKS_PER_MICROSECOND) >> 3);
#else // Normal operation
// Set step pulse time. Ad hoc computation from oscilloscope. Uses two's complement.
st.step_pulse_time = -(((settings.pulse_microseconds-2)*TICKS_PER_MICROSECOND) >> 3);
#endif
// Enable Stepper Driver Interrupt
HAL_TIM_Base_Start_IT(&htim2);
}
(2)void st_go_idle()
使步进电机系统进入空闲状态,停止当前的运动
void st_go_idle()
{
// Disable Stepper Driver Interrupt. Allow Stepper Port Reset Interrupt to finish, if active.
HAL_TIM_Base_Stop_IT(&htim2); // //禁用定时器2中断
busy = false;
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
bool pin_state = false; // Keep enabled. 保持启用状态
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm || sys.state == STATE_SLEEP) && sys.state != STATE_HOMING) {
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
MyDelay_ms(settings.stepper_idle_lock_time);
pin_state = true; // Override. Disable steppers. 手动干预:禁用步进电机
}
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { pin_state = !pin_state; } // Apply pin invert. 引脚取反
if (pin_state) { HAL_GPIO_WritePin(STEPPERS_DISABLE_GPIO_PORT, STEPPERS_DISABLE_Pin, 1); }
else { HAL_GPIO_WritePin(STEPPERS_DISABLE_GPIO_PORT, STEPPERS_DISABLE_Pin, 1); }
}
(3)ISR(TIMER1_COMPA_vect)
这个就是Arduino Uno的中断,是步进电机脉冲生成的核心中断服务程序,由AVR单片机的Timer1比较匹配A中断触发,负责生成精确的步进脉冲和方向控制信号。
需要替换为STM32的中断
先写一个在中断操作的函数:
void stepper_timer_interrupt(void)
{
// 防重入检查(与原始代码相同)
if (busy) { return; }
// 在步进脉冲引脚前设置方向引脚(提前几个纳秒)
// 原始: DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT, X_DIRECTION_PIN,
(st.dir_outbits & (1<<X_DIRECTION_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT, Y_DIRECTION_PIN,
(st.dir_outbits & (1<<Y_DIRECTION_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT, Z_DIRECTION_PIN,
(st.dir_outbits & (1<<Z_DIRECTION_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 然后产生步进脉冲
// 原始: STEP_PORT = (STEP_PORT & ~STEP_MASK) | st.step_outbits;
HAL_GPIO_WritePin(STEP_GPIO_PORT, X_STEP_Pin,
(st.step_outbits & (1<<X_STEP_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEP_GPIO_PORT, Y_STEP_Pin,
(st.step_outbits & (1<<Y_STEP_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(STEP_GPIO_PORT, Z_STEP_Pin,
(st.step_outbits & (1<<Z_STEP_BIT)) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 测试
// HAL_GPIO_WritePin(DIRECTION_GPIO_PORT, X_DIRECTION_PIN, 1);
//
// HAL_GPIO_WritePin(STEP_GPIO_PORT, X_STEP_Pin,
// GPIO_PIN_SET);
// HAL_GPIO_WritePin(STEP_GPIO_PORT, Y_STEP_Pin,
// GPIO_PIN_SET);
// HAL_GPIO_WritePin(STEP_GPIO_PORT, Z_STEP_Pin,
// GPIO_PIN_SET);
// 启用步进脉冲复位定时器(TIM2),在settings.pulse_microseconds微秒后复位信号
//HW_TIM_PortResetInterrupt_ValueConfig(8, st.step_pulse_time);
//__HAL_TIM_SET_AUTORELOAD(&htim3, st.exec_segment->cycles_per_tick);
__HAL_TIM_SET_COUNTER(&htim3, 0); //设置定时器3的初值为0
HAL_TIM_Base_Start_IT(&htim3); //使能定时器3,在10us后拉低步进引脚
busy = true; //原为true
__enable_irq(); // 重新启用中断,允许脉冲宽度定时器中断准时触发
//舵机
if(st.steps[X_AXIS]==0 && st.steps[Y_AXIS]==0 && st.steps[Z_AXIS]!=0)
{
if((st.dir_outbits & (1<<Z_DIRECTION_BIT)) != 0)
{
//启动舵机?
Servo_SetPWM(45);
}
if((st.dir_outbits & (1<<Z_DIRECTION_BIT)) == 0)
{
//启动舵机?
Servo_SetPWM(130);
}
}
// else
// {
// Servo_SetPWM(45);
//
// }
// 如果没有步进段,尝试从步进缓冲区弹出一个
if (st.exec_segment == NULL) {
if (segment_buffer_head != segment_buffer_tail) {
// 初始化新的步进段
st.exec_segment = &segment_buffer[segment_buffer_tail];
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// 禁用AMASS时,为慢步进频率(<250Hz)设置定时器预分频
// 原始: TCCR1B = (TCCR1B & ~(0x07<<CS10)) | (st.exec_segment->prescaler<<CS10);
uint32_t prescaler;
switch (st.exec_segment->prescaler) {
case 1: prescaler = 1; break;
case 8: prescaler = 8; break;
case 64: prescaler = 64; break;
case 256: prescaler = 256; break;
case 1024: prescaler = 1024; break;
default: prescaler = 8; break;
}
// 转换到STM32: 2MHz时钟,保持相同频率
HW_TIM_DriverInterrupt_ValueConfig(prescaler, st.exec_segment->cycles_per_tick);
#else
// 启用AMASS
//HW_TIM_DriverInterrupt_ValueConfig(8, st.exec_segment->cycles_per_tick>>1);
//__HAL_TIM_SET_AUTORELOAD(&htim2, st.exec_segment->cycles_per_tick - 1);
__HAL_TIM_SET_AUTORELOAD(&htim2, st.exec_segment->cycles_per_tick - 1);
__HAL_TIM_SET_COUNTER(&htim2, 0);
#endif
st.step_count = st.exec_segment->n_step;
// 如果新段开始一个新的规划块,初始化步进变量和计数器
if (st.exec_block_index != st.exec_segment->st_block_index) {
st.exec_block_index = st.exec_segment->st_block_index;
st.exec_block = &st_block_buffer[st.exec_block_index];
// 初始化Bresenham线和距离计数器
st.counter_x = st.counter_y = st.counter_z = (st.exec_block->step_event_count >> 1);
}
// 这里更新方向位!
st.dir_outbits = st.exec_block->direction_bits ^ dir_port_invert_mask;
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// 启用AMASS时,根据AMASS级别调整Bresenham轴增量计数器
st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.exec_segment->amass_level;
st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.exec_segment->amass_level;
st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.exec_segment->amass_level;
#endif
} else {
// 段缓冲区空,关闭
st_go_idle(); //结束,会关闭定时器2中断
system_set_exec_state_flag(EXEC_CYCLE_STOP);
return;
}
}
// 检查探测状态
if (sys_probe_state == PROBE_ACTIVE) {
probe_state_monitor();
}
// 复位步出位
st.step_outbits = 0;
// 通过Bresenham线算法执行步进位移轮廓
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_x += st.steps[X_AXIS];
#else
st.counter_x += st.exec_block->steps[X_AXIS];
#endif
if (st.counter_x > st.exec_block->step_event_count) {
st.step_outbits |= (1<<X_STEP_BIT);
st.counter_x -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) {
sys_position[X_AXIS]--;
} else {
sys_position[X_AXIS]++;
}
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_y += st.steps[Y_AXIS];
#else
st.counter_y += st.exec_block->steps[Y_AXIS];
#endif
if (st.counter_y > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Y_STEP_BIT);
st.counter_y -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) {
sys_position[Y_AXIS]--;
} else {
sys_position[Y_AXIS]++;
}
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_z += st.steps[Z_AXIS];
#else
st.counter_z += st.exec_block->steps[Z_AXIS];
#endif
if (st.counter_z > st.exec_block->step_event_count) {
st.step_outbits |= (1<<Z_STEP_BIT);
st.counter_z -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) {
sys_position[Z_AXIS]--;
} else {
sys_position[Z_AXIS]++;
}
}
// 回零周期中,锁定并防止所需轴移动
if (sys.state == STATE_HOMING) {
st.step_outbits &= sys.homing_axis_lock;
}
// 减少步事件计数
st.step_count--;
if (st.step_count == 0) {
// 段完成,丢弃当前段并推进段索引
st.exec_segment = NULL;
if (++segment_buffer_tail == SEGMENT_BUFFER_SIZE) {
segment_buffer_tail = 0;
}
}
// 应用步端口反转掩码
st.step_outbits ^= step_port_invert_mask;
busy = false;
}
该函数需要放在中断函数里面
(4)ISR(TIMER0_OVF_vect)
该函数是是定时器0溢出中断服务程序。
其实就是在stepper_timer_interrupt()(原ISR(TIMER1_COMPA_vect))函数执行时,启动定时器1来产生一个脉冲,然后在定时器1的中断程序中拉高对应引脚的电平,同时使能定时器0,定时器0在10us后产生中断,将引脚拉低 ,以此来产生脉冲信号。
对应的中断操作函数如下:
使用定时器的回调函数,这里我是用定时器2来代替AVR中的定时器1,定时器3代替AVR中的定时器0
//定时器回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM2)
{
// TIM2更新中断 - 步进定时
stepper_timer_interrupt();
}
else if (htim->Instance == TIM3)
{
//10us已到 拉低引脚
HAL_GPIO_WritePin(STEP_GPIO_PORT, X_STEP_Pin, 0);
HAL_GPIO_WritePin(STEP_GPIO_PORT, Y_STEP_Pin, 0);
// HAL_GPIO_WritePin(STEP_GPIO_PORT, Z_STEP_Pin, 0);
HAL_TIM_Base_Stop_IT(&htim3); //关闭定时器3
}
}
(5)void st_reset(void)
用于复位步进电机控制系统
复位倒没啥好说的,如下:
void st_reset()
{
// Initialize stepper driver idle state.
st_go_idle();
// Initialize stepper algorithm variables.
memset(&prep, 0, sizeof(st_prep_t));
memset(&st, 0, sizeof(stepper_t));
st.exec_segment = NULL;
pl_block = NULL; // Planner block pointer used by segment buffer
segment_buffer_tail = 0;
segment_buffer_head = 0; // empty = tail
segment_next_head = 1;
busy = false;
st_generate_step_dir_invert_masks();
st.dir_outbits = dir_port_invert_mask; // Initialize direction bits to default.
// Initialize step and direction port pins.
// STEP_PORT = (STEP_PORT & ~STEP_MASK) | step_port_invert_mask;
HAL_GPIO_WritePin(STEP_GPIO_PORT,X_STEP_Pin,bit_istrue(step_port_invert_mask,bit(X_STEP_BIT)));
HAL_GPIO_WritePin(STEP_GPIO_PORT,Y_STEP_Pin,bit_istrue(step_port_invert_mask,bit(Y_STEP_BIT)));
HAL_GPIO_WritePin(STEP_GPIO_PORT,Z_STEP_Pin,bit_istrue(step_port_invert_mask,bit(Z_STEP_BIT)));
// DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | dir_port_invert_mask;
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT,X_DIRECTION_PIN,bit_istrue(dir_port_invert_mask,bit(X_DIRECTION_BIT)));
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT,Y_DIRECTION_PIN,bit_istrue(dir_port_invert_mask,bit(Y_DIRECTION_BIT)));
HAL_GPIO_WritePin(DIRECTION_GPIO_PORT,Z_DIRECTION_PIN,bit_istrue(dir_port_invert_mask,bit(Z_DIRECTION_BIT)));
#ifdef ENABLE_DUAL_AXIS
st.dir_outbits_dual = dir_port_invert_mask_dual;
STEP_PORT_DUAL = (STEP_PORT_DUAL & ~STEP_MASK_DUAL) | step_port_invert_mask_dual;
DIRECTION_PORT_DUAL = (DIRECTION_PORT_DUAL & ~DIRECTION_MASK_DUAL) | dir_port_invert_mask_dual;
#endif
}
(6)void stepper_init(void)
嗯,步进电机初始化,如果是用STM32CubeMX或STM32CubeIDE,生成的代码自动初始化,没什么必要,但还是写上吧
// Initialize and start the stepper motor subsystem
void stepper_init()
{
MX_GPIO_Init();
MX_TIM2_Init();
#ifdef STEP_PULSE_DELAY
TIMSK0 |= (1<<OCIE0A); // Enable Timer0 Compare Match A interrupt
#endif
}
18、system.c
负责管理整个CNC控制器的系统状态、初始化、主循环以及系统级别的功能。
SREG是AVR单片机(如ATmega328P)中的状态寄存器(Status Register)。它是一个8位的寄存器,包含了当前处理器的状态信息,如全局中断使能标志、进位标志、零标志等。
前面基本不需要修改,后面获取中断状态需要修改为STM32的。
void system_set_exec_state_flag(uint8_t mask) {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_state |= (mask);
__set_PRIMASK(primask);
}
void system_clear_exec_state_flag(uint8_t mask) {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_state &= ~(mask);
__set_PRIMASK(primask);
}
void system_set_exec_alarm(uint8_t code) {
// uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_alarm = code;
__set_PRIMASK(primask);
}
void system_clear_exec_alarm() {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_alarm = 0;
__set_PRIMASK(primask);
}
void system_set_exec_motion_override_flag(uint8_t mask) {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_motion_override |= (mask);
__set_PRIMASK(primask);
}
void system_set_exec_accessory_override_flag(uint8_t mask) {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_accessory_override |= (mask);
__set_PRIMASK(primask);
}
void system_clear_exec_motion_overrides() {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_motion_override = 0;
__set_PRIMASK(primask);
}
void system_clear_exec_accessory_overrides() {
//uint8_t sreg = SREG;
uint32_t primask = __get_PRIMASK();
sei();
sys_rt_exec_accessory_override = 0;
__set_PRIMASK(primask);
}
至此,grbl的初步移植算是完成,等组装好机器后,就开始调试,后面的话还有一些参数需要修改。








