玩CNC雕刻机的朋友对GRBL应该都不陌生,这个开源的运动控制固件因其简洁高效而广受欢迎。但原版GRBL运行在Arduino平台上时,72MHz主频的STM32F103C8T6显然是个更有吸引力的选择——价格与Arduino相当,性能却提升数倍。更重要的是,STM32丰富的外设资源让我们能够实现更多扩展功能,比如脱机运行、图形化交互等。
这个项目的核心目标有三个:一是将GRBL V1.1f完整移植到STM32F103C8T6平台;二是增加脱机运行能力,通过SPI Flash存储G代码文件;三是构建基于OLED和旋转编码器的人机交互界面。最终实现一个既可以通过USB联机控制,也能完全独立运行的CNC控制器。
STM32F103C8T6被称为"蓝色药丸"不是没有道理的。这款Cortex-M3内核的MCU拥有72MHz主频、64KB Flash和20KB RAM,价格却只要10元左右。相比Arduino UNO的16MHz主频和2KB RAM,性能提升显著。更重要的是,它具备:
步进电机驱动部分建议采用光耦隔离设计,典型电路如下:
code复制MCU GPIO -> 光耦(如PC817) -> 步进驱动器(如TMC2208)
↑
隔离电源供电
限位开关建议采用常闭接法,这样即使线路断开也会触发保护:
code复制VCC -> 限位开关 -> 输入引脚 -> 下拉电阻 -> GND
电源部分需要特别注意:
原版GRBL使用Arduino的Timer1产生步进脉冲,在STM32上我们需要重新设计。TIM1的高级定时器特性非常适合这个任务:
c复制void TIM1_Configuration(void) {
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
// 时基配置:72MHz/(72*100) = 10kHz脉冲频率
TIM_TimeBaseStructure.TIM_Period = 100 - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
// PWM模式配置
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 50; // 50%占空比
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM1, &TIM_OCInitStructure);
// 高级定时器需要特别使能PWM输出
TIM_CtrlPWMOutputs(TIM1, ENABLE);
}
注意:实际应用中应根据步进驱动器需求调整脉冲频率和占空比。过高的频率可能导致驱动器无法识别,一般建议在10-100kHz范围内。
GRBL对实时性要求极高,中断处理不当会导致丢步或位置偏差。STM32的NVIC提供了更灵活的中断优先级管理:
c复制void NVIC_Configuration(void) {
NVIC_InitTypeDef NVIC_InitStructure;
// 步进脉冲定时器中断(最高优先级)
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 编码器中断(次高优先级)
NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_Init(&NVIC_InitStructure);
// 串口中断(较低优先级)
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
使用FATFS文件系统管理SPI Flash中的G代码文件,关键是要正确实现底层驱动:
c复制// diskio.c 中的关键函数实现
DSTATUS disk_initialize(BYTE pdrv) {
SPI_FLASH_Init(); // 初始化SPI Flash
return RES_OK;
}
DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) {
uint32_t addr = FLASH_BASE_ADDR + sector * 512;
SPI_FLASH_Read(buff, addr, count * 512);
return RES_OK;
}
提示:SPI Flash建议使用W25Q64等常见型号,容量8MB足够存储大量G代码文件。读写前务必擦除扇区,否则写入会失败。
旋转编码器配合OLED屏实现菜单导航:
c复制void encoder_handler(void) {
static uint8_t last_state = 0;
uint8_t current_state = GPIO_ReadInputDataBit(ENC_PORT, ENC_A_PIN) |
(GPIO_ReadInputDataBit(ENC_PORT, ENC_B_PIN) << 1);
// 状态机实现编码器方向判断
if(last_state == 0x00 && current_state == 0x02) encoder_value++;
if(last_state == 0x00 && current_state == 0x01) encoder_value--;
last_state = current_state;
}
void oled_menu_update(void) {
switch(current_menu) {
case MENU_MAIN:
OLED_ShowString(0, 0, "1.Run File");
OLED_ShowString(0, 2, "2.File List");
OLED_ShowString(0, 4, "3.Settings");
break;
case MENU_FILE_LIST:
show_file_list();
break;
}
}
采用状态机模式管理系统运行状态:
c复制void main_loop(void) {
while(1) {
switch(system_state) {
case STATE_IDLE:
handle_encoder();
update_display();
check_buttons();
break;
case STATE_RUNNING:
execute_gcode_block();
update_position();
check_limits();
break;
case STATE_PAUSED:
show_pause_screen();
if(resume_pressed()) system_state = STATE_RUNNING;
break;
}
// 喂看门狗防止死机
IWDG_ReloadCounter();
}
}
GRBL的运动控制核心是stepper.c中的步进脉冲生成算法。移植时需要特别注意:
c复制void st_prep_buffer(void) {
// 计算下一个运动段的步进间隔
uint32_t dt = (uint32_t)(1000000 / sqrt(2 * accel * distance));
// 配置定时器自动重装载值
TIM1->ARR = dt - 1;
TIM1->CCR2 = dt / 2; // 50%占空比
}
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机不转 | 驱动器未使能 | 检查ENA信号电平 |
| 单方向运动 | DIR信号异常 | 检查DIR引脚连接 |
| 随机抖动 | 共地问题 | 加装光耦隔离 |
| 低速振动 | 电流设置不当 | 调整驱动器细分和电流 |
重要提示:调试时务必先断开电机电源,用示波器验证脉冲信号正常后再连接驱动器。安全第一!
运动平滑性:在planner.c中修改运动前瞻算法参数:
c复制#define BLOCK_BUFFER_SIZE 32 // 增大缓冲区减少卡顿
#define MINIMUM_PLANNER_SPEED 100 // 最低规划速度(mm/min)
脉冲频率提升:通过调整定时器分频比提高最高脉冲频率:
c复制// 72MHz/(24*30) = 100kHz
TIM1->PSC = 24 - 1;
TIM1->ARR = 30 - 1;
内存优化:STM32F103的20KB RAM需要精打细算:
这个移植项目最令人满意的部分是最终的性能表现——实测脉冲频率可达200kHz以上,足以驱动大多数步进电机实现高速高精度运动。而脱机运行功能的加入,使得这个小控制器可以完全脱离电脑独立工作,非常适合现场应用。