☰
STM32雕刻机源码移植与调参实战:从Grbl到三轴控制
2026/10/15 16:12:38 网站建设 项目流程

简介:这份资源是基于STM32的雕刻机源码工程,面向嵌入式开发学习者、CNC控制爱好者及需要二次开发小型数控设备的工程师。项目以STM32F103C8T6为主控,移植并适配开源Grbl库,实现G代码解析与步进电机精准运动控制,帮助读者理解从指令解析到硬件驱动的完整链路。压缩包共255个文件,约10.11MB,以C源码与头文件为核心,包含Grbl移植、步进电机驱动、运动规划、协议解析等模块,并附带Keil工程文件、编译产物与配置脚本,便于直接打开工程编译调试。目前已有1769人学习下载,适合作为嵌入式系统与CNC控制入门的实战参考。通过学习源码,可掌握GPIO、定时器、中断配置及软硬件协同方法,积累运动控制与实时任务调度的排错经验,为后续开发雕刻机、3D打印机等自动化设备打下基础。

1. 从一堆散装源码到能动的三轴:这套 STM32 雕刻机方案到底值不值得拆

前阵子帮朋友收拾一台吃灰的桌面级 CNC,控制器烧了,原厂固件闭源,换板子要等半个月。翻了一圈手头的资料,最后用一套基于 STM32 的雕刻机源码把机器救活了。这套源码的核心思路是把 Grbl 那套成熟的 G 代码解析与步进电机规划逻辑,移植到 STM32 平台上跑,常见的是 F1 系列做控制、F4 系列做带屏幕和 SD 卡脱机控制的版本。它解决的不是“从零造 CNC”的问题,而是让你在一块几十块钱的开发板上,拿到一套能直接编译、能接驱动器、能跑 G 代码的完整固件框架。适合谁?手上有 STM32 开发板、想自己攒一台三轴雕刻机或者激光机的朋友;也适合已经买了成品机但想换掉原厂控制板、自己改固件的老玩家。源码包里一般包含主控工程、步进电机驱动层、G 代码解析器、串口通信和限位/急停处理,拿到手第一件事不是急着烧录,而是先搞清楚它的引脚映射和时钟配置跟你手上的板子对不对得上。

2. 拆开源码看骨架:Grbl 移植到 STM32 后,哪些模块在干活

2.1 从 G 代码到脉冲:一条指令的完整链路

很多人以为雕刻机固件就是“收到 G 代码,输出脉冲”,实际链路要长得多。以最常见的G1 X10 Y5 F300为例,源码里的处理顺序大致是:串口接收环形缓冲区 → 主循环取出整行 → 解析器识别指令类型和参数字段 → 把目标坐标写入规划器 → 规划器做速度前瞻和加减速计算 → 步进电机模块按 Bresenham 算法把直线插补成三轴脉冲序列 → 定时器中断里翻转 IO。这条链路上任何一个环节的参数不对,表现出来的就是丢步、异响或者直接不动。

Grbl 在 AVR 上跑的时候,很多参数是写死的宏定义,移植到 STM32 后,因为主频从 16MHz 跳到 72MHz 甚至 168MHz,定时器分频和脉冲频率上限都要重新算。源码里通常有一个config.h或者settings结构体,集中放这些参数。我一般会先看三个地方:步进脉冲的定时器配置、串口波特率、以及限位开关的触发电平。这三个不对,后面全白搭。

// 典型的步进定时器配置片段(以 TIM2 为例) // 假设系统时钟 72MHz,预分频 0,目标脉冲频率上限 30kHz #define STEP_TIMER_PSC 0 #define STEP_TIMER_ARR 2399 // 72MHz / (2399+1) = 30kHz #define STEP_TIMER_IRQn TIM2_IRQn void step_timer_init(void) { TIM_TimeBaseInitTypeDef tim; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); tim.TIM_Prescaler = STEP_TIMER_PSC; tim.TIM_Period = STEP_TIMER_ARR; tim.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &tim); TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); TIM_Cmd(TIM2, ENABLE); }

这段代码的逻辑是:用 TIM2 的更新中断作为步进脉冲的时基,ARR 决定中断频率。参数怎么改?如果你用的电机驱动器需要更窄的脉冲,就把 ARR 调小、频率拉高;如果发现高速时丢步,反而要降频率或者改加减速曲线。注意这里只是时基,真正的脉冲输出在中断里根据规划器的步数逐次翻转 IO,所以中断服务函数的执行时间必须远小于中断周期,否则高频率下直接翻车。

2.2 引脚映射与驱动器接线:别等烧了板子才看数据手册

源码里最容易被忽略、也最容易出人命的就是引脚定义。不同作者写的 STM32 雕刻机源码,引脚分配差异很大。常见做法是 X/Y/Z 各占一个 STEP 和一个 DIR,另外加一个 EN 统一使能。但具体落到 PA0 还是 PB6,得看源码里的pin_map.h或者直接在main.c里找GPIO_Pin_x。

我一般会先画一张表,把源码里的引脚和手头板子的可用 IO 对一遍,再决定是改源码还是改接线。改源码更稳妥,因为改接线容易在插拔时接错。下面是一个典型的引脚映射表,你可以照着这个格式把自己源码里的整理出来:

功能源码默认引脚可替换引脚注意事项
X_STEPPA0PA8 / PB0避开 JTAG 占用脚
X_DIRPA1PA9 / PB1方向电平可软件反转
Y_STEPPA2PA10 / PB2同上
Y_DIRPA3PA11 / PB3同上
Z_STEPPA4PA12 / PB4同上
Z_DIRPA5PA15 / PB5同上
ENPA6PB6低电平使能常见
限位 XPB12任意带中断脚内部上拉,常闭到地

这张表不是让你照抄,而是提醒你:源码里的引脚不一定适配你的板子,尤其是 PA13/PA14 是 SWD 调试口,PA15/PB3/PB4 默认是 JTAG 功能,不关掉复用直接配 GPIO 会没反应。血泪经验是,先把调试口留出来,别把 STEP 配到 PA13 上,否则烧录一次就要拔一次线。

2.3 编译环境与烧录:Keil、Makefile 还是 PlatformIO

源码包通常带的是 Keil MDK 工程,因为国内做 STM32 的大多用这个。但如果你不想装 Keil,也可以用 Makefile + arm-none-eabi-gcc,或者 PlatformIO。三条路我都走过,说下区别:Keil 最省事,打开工程直接编译,但工程文件里的器件型号要跟你板子一致,否则下载算法不对;Makefile 最干净,适合放进版本管理,但启动文件和链接脚本要自己配;PlatformIO 介于两者之间,库管理方便,但有些老源码的寄存器操作跟 HAL 库混用,编译时容易报重复定义。

# 用 arm-none-eabi-gcc 编译的典型命令 arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -O2 \ -I./inc -I./cmsis \ -T stm32f103c8t6.ld \ src/main.c src/stepper.c src/gcode.c src/serial.c \ -o firmware.elf arm-none-eabi-objcopy -O binary firmware.elf firmware.bin

参数说明:-mcpu=cortex-m3对应 F1 系列,F4 要改成cortex-m4;-O2是常用优化等级,但步进中断服务函数如果被优化出问题,可以单独给它加__attribute__((optimize("O0")));链接脚本决定 Flash 和 RAM 的起始地址,F103C8T6 是 64KB Flash、20KB RAM,换芯片必须换脚本。烧录用 st-flash 或者 OpenOCD 都行,命令不复杂,关键是接线:SWDIO、SWCLK、GND、3.3V 四根线,别接错。

3. 参数调不对,机器就跟你对着干:步进细分、加速度与回零配置

3.1 步进细分与脉冲当量:算错一个数,雕出来全是废品

步进驱动器的细分设置和固件里的脉冲当量必须匹配,这是最容易出错的地方。假设你用的是 1.8 度步进电机,驱动器设 16 细分,丝杠导程 2mm,那么电机转一圈需要 200×16=3200 个脉冲,走 2mm,所以脉冲当量是 2/3200=0.000625mm/脉冲。这个数要写进固件的settings里,Grbl 系通常叫$100、$101、$102(分别对应 X/Y/Z 每毫米脉冲数)。

源码里如果用的是浮点计算,直接填 1600 就行(1/0.000625=1600)。但有些 STM32 移植版为了省 CPU,用的是定点数或者整数,这时候要注意单位换算。我见过一个版本把脉冲当量放大 1000 倍存成整数,结果填参数时忘了乘,机器走 10mm 实际只走了 0.01mm,排查了一下午。

// 常见的 settings 结构体片段 typedef struct { float steps_per_mm[3]; // X/Y/Z 每毫米脉冲数 float max_rate[3]; // 最大速度 mm/min float acceleration[3]; // 加速度 mm/s^2 uint8_t step_pulse_us; // 脉冲宽度微秒 } cnc_settings_t; cnc_settings_t settings = { .steps_per_mm = {1600.0f, 1600.0f, 1600.0f}, .max_rate = {3000.0f, 3000.0f, 1000.0f}, .acceleration = {500.0f, 500.0f, 200.0f}, .step_pulse_us = 3 };

参数怎么定?steps_per_mm按上面公式算;max_rate先从 1000 开始试,慢慢往上加,直到电机不丢步;acceleration对雕刻机来说 300~500 比较稳,太高会失步,太低加工效率上不去。step_pulse_us是脉冲宽度,大多数驱动器要求至少 2.5us,设 3 比较安全。注意这些参数不是一次就能调好的,我一般会拿一块废料,画一个 50mm×50mm 的方框,走一遍量实际尺寸,偏差超过 0.1mm 就回去改steps_per_mm。

3.2 加速度与前瞻:为什么你的机器拐角会“啃”出圆角

Grbl 的前瞻算法会在解析 G 代码时预读若干行,根据拐角角度和当前速度决定是否减速。STM32 版本如果 RAM 够,前瞻行数可以设大一点,比如 16 行;RAM 紧张就设 8 行。这个参数在源码里通常叫BLOCK_BUFFER_SIZE或者PLANNER_BUFFER_SIZE。

前瞻行数不够,机器在连续短线段加工时就会频繁启停,表现出来就是拐角处速度掉到零,然后重新加速,加工表面有停顿痕迹。但前瞻行数也不是越大越好,每行都要存坐标、速度、加速度等信息,RAM 吃紧的 F103 开太大直接编译不过。

// 规划器缓冲区配置 #define PLANNER_BUFFER_SIZE 16 // 前瞻行数,F103 建议 8~12,F4 可以 16~24 #define MIN_SEGMENT_TIME 0.02f // 最小段执行时间,秒 #define JUNCTION_DEVIATION 0.01f // 拐角偏差,毫米

JUNCTION_DEVIATION这个参数值得单独说:它决定机器在拐角处允许多大的速度不降为零。设太小,拐角减速明显,加工慢;设太大,拐角会被“切”掉,方框变成圆角。常见做法是从 0.01mm 开始调,如果加工出来圆角明显,就降到 0.005;如果机器在拐角抖动厉害,就加到 0.02。这个参数跟机械刚性有关,刚性差的机器设太小反而会丢步。

3.3 回零与限位:省掉这一步,撞机只是时间问题

限位开关和回零逻辑是很多 DIY 玩家偷懒的地方,觉得“我手动对刀就行”。但只要你跑一次长时间加工,中途丢步或者程序出错,没有限位保护的机器就会一路撞到机械尽头,轻则丢步,重则丝杠弯、电机烧。源码里一般有硬限位和软限位两套逻辑,硬限位靠 IO 中断,软限位靠固件里的行程范围判断。

// 限位中断处理片段 void EXTI15_10_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line12) != RESET) { // 假设 PB12 接 X 限位,低电平触发 if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) == Bit_RESET) { stepper_emergency_stop(); // 立即停止所有轴 system_state = STATE_ALARM; // 进入报警状态 } EXTI_ClearITPendingBit(EXTI_Line12); } }

这段代码的关键是stepper_emergency_stop()必须足够快,不能在里面做浮点运算或者串口打印。我一般会把急停逻辑写成直接关定时器、清步数计数器,其他善后放到主循环里做。另外,限位开关建议用常闭接法,这样断线也会触发报警,常开接法断线了反而没保护。回零顺序也有讲究:先 Z 轴抬到安全高度,再 X/Y 回零,最后 Z 回零,顺序错了可能撞刀。

4. 避坑与排查:烧录后没反应、电机只抖不走、串口连不上

4.1 烧录成功但机器没反应

现象:编译下载都正常,串口也能连上,但发 G 代码电机不动,驱动器指示灯也不亮。原因通常是使能信号没给对。很多源码里 EN 引脚默认输出高电平,而大多数驱动器是低电平使能,结果驱动器一直处于关闭状态。解决:在源码里找ENABLE_PIN的初始化,把默认电平改成低,或者在发指令前先拉低 EN。另一个可能是定时器没启动,检查step_timer_init()是否在main()里被调用,以及中断优先级是否被其他中断屏蔽。

4.2 电机只抖不走或走一步退两步

现象:电机发出嗡嗡声但不转,或者转一个角度又弹回去。原因一般是步进脉冲频率太高,超过了驱动器的响应能力,或者加速太快导致失步。解决:先把max_rate降到 500mm/min 试,如果还抖,检查steps_per_mm是否算错——算大了会导致实际速度远超预期。还有一种情况是 DIR 信号建立时间不够,在 STEP 脉冲到来之前 DIR 还没稳定,源码里如果没加延时,可以在 DIR 翻转后加几个微秒的delay_us(5)。

4.3 串口连不上或乱码

现象:电脑端软件能打开串口但收不到回复,或者收到一堆乱码。原因通常是波特率不匹配。Grbl 系默认 115200,但有些 STM32 移植版用的是 9600 或者 57600。解决:先看源码里USART_Init的USART_BaudRate值,把上位机改成一样的。如果波特率对但还是乱码,检查晶振频率——源码里SystemInit()默认按 8MHz 外部晶振算 PLL,如果你的板子是 12MHz 晶振,主频就错了,串口分频也跟着错。这种情况要么改晶振宏定义,要么换板子。

4.4 加工到一半突然停住

现象:跑了几分钟或者几十分钟后,机器突然停在原地,串口无响应。原因可能是看门狗复位,或者中断里执行了耗时操作导致主循环饿死。解决:先看源码里有没有开IWDG,如果开了但主循环里没有及时喂狗,就会复位。另外检查串口接收中断,如果用了printf重定向且没加缓冲,大量日志输出会拖慢中断。我一般会把调试输出关掉再跑长时间测试,确认是固件问题还是硬件干扰。

4.5 限位开关误触发

现象:机器正常加工时突然报警停止,检查限位开关并没有碰到。原因是限位信号线没有屏蔽,或者内部上拉太弱,电机运转时的干扰耦合进来。解决:限位线用双绞线或者屏蔽线,屏蔽层单端接地;源码里把限位中断改成上升沿和下降沿都触发,并在中断里加 10ms 的软件消抖。如果还不行,就在限位开关两端并一个 100nF 电容,硬件滤波。

5. 进阶玩法:用 SD 卡脱机跑 G 代码与自定义 M 指令

5.1 SD 卡脱机控制:摆脱电脑的束缚

带 SD 卡的 STM32 雕刻机源码通常用 SPI 模式接卡,文件系统用 FatFs。脱机跑 G 代码的逻辑是:上电后挂载 SD 卡,扫描根目录下的.nc或.gcode文件,通过按键选择文件,然后逐行读取、解析、执行。这里的关键是读取速度要跟得上加工速度,如果每读一行都重新打开文件,加工会一顿一顿的。常见做法是开一个 512 字节的缓冲区,一次读多行,解析器从缓冲区取。

// SD 卡逐行读取的简化逻辑 FATFS fs; FIL file; char line_buf[128]; UINT br; f_mount(&fs, "", 0); if (f_open(&file, "test.nc", FA_READ) == FR_OK) { while (f_gets(line_buf, sizeof(line_buf), &file)) { if (line_buf[0] == ';' || line_buf[0] == '\n') continue; // 跳过注释和空行 gcode_parse_line(line_buf); // 解析并推入规划器 while (planner_buffer_full()) { // 缓冲区满则等待 protocol_execute_runtime(); } } f_close(&file); }

参数说明:line_buf大小要能容纳最长的一行 G 代码,一般 128 字节够用;planner_buffer_full()是规划器满的判断,满了就调用protocol_execute_runtime()让规划器继续消费。注意 SD 卡的 SPI 时钟不能太高,F103 跑 18MHz 以上容易读卡失败,建议先降到 9MHz 测试。

5.2 自定义 M 指令:控制主轴、冷却和探针

标准 G 代码里的 M3/M5 是主轴启停,M8/M9 是冷却液。但 DIY 机器上你可能想用 M 指令控制激光功率、吸尘器或者气泵。源码里一般有一个M指令的分发函数,找到case 3:和case 5:的位置,照着加自己的逻辑就行。

// 自定义 M 指令示例:M10 开气泵,M11 关气泵 switch (m_code) { case 3: spindle_on(); break; case 5: spindle_off(); break; case 10: GPIO_SetBits(GPIOA, GPIO_Pin_8); break; // 气泵开 case 11: GPIO_ResetBits(GPIOA, GPIO_Pin_8); break; // 气泵关 default: break; }

这里要注意的是,M 指令的执行时机。有些 M 指令是立即执行的,有些要等规划器里的运动执行完才执行。比如换刀指令 M6 就必须等所有运动停止。源码里通常用protocol_synced()来判断是否同步。如果你加的自定义指令控制的是安全相关的设备,建议强制同步,避免运动中误动作。

5.3 验证方法:用示波器和废料说话

调完参数别急着上正式料,我一般会做三步验证。第一步,不接电机,用示波器或者逻辑分析仪看 STEP 和 DIR 波形,确认脉冲频率和方向电平跟预期一致。第二步,接上电机但不上丝杠,空转看丢不丢步,用手轻捏电机轴感受扭矩。第三步,上丝杠和废料,画一个 100mm×100mm 的方框和一个直径 50mm 的圆,用卡尺量尺寸,圆度用眼睛看就行。方框尺寸偏差超过 0.2mm 就回去改steps_per_mm,圆有棱角就改前瞻和加速度。

从那以后我每次拿到一套新的雕刻机源码,都强制走一遍“示波器看波形 → 空载试跑 → 废料试切”的流程,不再直接上正式料。这套 STM32 的源码底子不错,但参数和引脚适配得自己盯,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询