1. 硬件映射文件 (cpu_map.h)
这个文件定义了 GRBL 抽象的引脚(如 STEP, DIR, LIMIT)对应到 STM32 的具体 GPIO 引脚。
c
// cpu_map.h
#ifndef cpu_map_h
#define cpu_map_h
#include "stm32f4xx_hal.h" // 根据你的芯片修改 (如 stm32f1xx_hal.h)
// --- 步进电机引脚定义 (X, Y, Z 轴) ---
#define STEP_PORT GPIOA
#define X_STEP_PIN GPIO_PIN_0
#define Y_STEP_PIN GPIO_PIN_1
#define Z_STEP_PIN GPIO_PIN_2
#define DIR_PORT GPIOA
#define X_DIR_PIN GPIO_PIN_3
#define Y_DIR_PIN GPIO_PIN_4
#define Z_DIR_PIN GPIO_PIN_5
#define ENABLE_PORT GPIOA
#define STEPPERS_ENABLE_PIN GPIO_PIN_6
// --- 限位开关引脚定义 ---
#define LIMIT_PORT GPIOB
#define X_LIMIT_PIN GPIO_PIN_12
#define Y_LIMIT_PIN GPIO_PIN_13
#define Z_LIMIT_PIN GPIO_PIN_14
// --- 控制引脚 (复位, 急停等) ---
#define CONTROL_PORT GPIOB
#define RESET_PIN GPIO_PIN_15
// --- 主轴控制 (PWM/继电器) ---
#define SPINDLE_PORT GPIOC
#define SPINDLE_PWM_PIN GPIO_PIN_6 // 假设使用 TIM3_CH1
// --- 宏定义:简化 GPIO 操作 ---
#define STEP_PORT_SET(x) HAL_GPIO_WritePin(STEP_PORT, x, GPIO_PIN_SET)
#define STEP_PORT_CLR(x) HAL_GPIO_WritePin(STEP_PORT, x, GPIO_PIN_RESET)
#define DIR_PORT_SET(x) HAL_GPIO_WritePin(DIR_PORT, x, GPIO_PIN_SET)
#define DIR_PORT_CLR(x) HAL_GPIO_WritePin(DIR_PORT, x, GPIO_PIN_RESET)
#endif
2. 步进定时器中断 (stepper.c 修改部分)
GRBL 的核心是一个高频定时器中断,用于计算加减速并输出脉冲。这是移植最难的部分。
需要配置一个 STM32 定时器(如 TIM1) 产生 1ms 或更高频率的中断,然后在中断里调用 GRBL 的步进函数。
c
// stepper.c (仅展示移植相关的修改)
#include "stm32f4xx_hal.h"
#include "grbl.h"
extern TIM_HandleTypeDef htim1; // CubeMX 生成的定时器句柄
// 定时器中断回调函数 (在 stm32f4xx_it.c 中会被调用)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM1) {
// 这里是 GRBL 的心跳
// 原 AVR 代码中的 ISR(TIMER1_COMPA_vect) 逻辑放在这里
st_isr(); // GRBL 内部的步进中断处理函数
}
}
// 初始化步进定时器
void st_init() {
// 使用 CubeMX 初始化 TIM1
// 配置预分频器,使得定时器频率为 1MHz (1us 计数)
// 配置周期为 1000 (即 1ms 进入一次中断)
HAL_TIM_Base_Start_IT(&htim1);
// 初始化 GPIO (调用上面的 cpu_map)
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitStruct.Pin = X_STEP_PIN | Y_STEP_PIN | Z_STEP_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(STEP_PORT, &GPIO_InitStruct);
}
3. 串口重定向 (serial.c)
GRBL 通过串口与 PC 通信。你需要将 AVR 的 USART_Receive 改为 STM32 的 UART。
c
// serial.c
#include "serial.h"
#include "usart.h" // CubeMX 生成的 USART 头文件
// 接收缓冲区
#define RX_RING_BUFFER 256
volatile uint8_t rx_buffer[RX_RING_BUFFER];
volatile uint8_t rx_head = 0;
volatile uint8_t rx_tail = 0;
// UART 接收中断回调
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
rx_buffer[rx_head] = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
rx_head = (rx_head + 1) % RX_RING_BUFFER;
}
}
// GRBL 调用的读字节函数
uint8_t serial_read() {
if (rx_head == rx_tail) {
return SERIAL_NO_DATA;
} else {
uint8_t data = rx_buffer[rx_tail];
rx_tail = (rx_tail + 1) % RX_RING_BUFFER;
return data;
}
}
// GRBL 调用的写字节函数
void serial_write(uint8_t data) {
HAL_UART_Transmit(&huart1, &data, 1, 10);
}
void serial_init() {
// CubeMX 会自动初始化 UART1
// 开启接收中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);
}
4. EEPROM 模拟 (eeprom.c)
STM32 没有硬件 EEPROM,通常使用 Flash 模拟。
c
// eeprom.c
#include "eeprom.h"
#include "stm32f4xx_hal_flash.h"
#define EEPROM_START_ADDR 0x0800F000 // 使用 Flash 最后一页
void memcpy_to_flash(uint32_t addr, void *src, uint32_t len) {
HAL_FLASH_Unlock();
uint32_t *p = (uint32_t *)src;
for(uint32_t i=0; i<len; i+=4) {
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr+i, *p++);
}
HAL_FLASH_Lock();
}
void memcpy_from_flash(void *dst, uint32_t addr, uint32_t len) {
uint8_t *p = (uint8_t *)dst;
for(uint32_t i=0; i<len; i++) {
p[i] = *(__IO uint8_t*)(addr + i);
}
}
5. 主函数 (main.c)
将 GRBL 的主循环嵌入到 STM32 的 main 函数中。
c
// main.c
#include "main.h"
#include "grbl.h"
int main(void) {
HAL_Init();
SystemClock_Config();
// CubeMX 初始化外设
MX_GPIO_Init();
MX_USART1_UART_Init();
MX_TIM1_Init(); // 步进定时器
// 初始化 GRBL 核心
serial_init();
settings_init(); // 加载参数
st_init(); // 初始化步进电机
plan_init(); // 初始化运动规划
// 启动 GRBL 协议解析器
protocol_main_loop();
while (1) {
// GRBL 已经在 protocol_main_loop 中死循环了
}
}
参考代码 GRBL 移植stm32 www.youwenfan.com/contentcsu/70177.html
移植关键注意事项
- 定时器频率 :GRBL 对时间极其敏感。原版 AVR 是 16MHz,STM32 是 72/168MHz。你需要调整
config.h中的F_CPU宏定义,或者在定时器预分频器中做等效换算,否则会导致 进给速度(Feed Rate)快 10 倍。 - 中断优先级 :STM32 的中断有抢占优先级。串口中断 的优先级必须高于 定时器中断,否则在发送大量状态时可能会阻塞步进脉冲,导致丢步。
- 引脚电平反转 :如果你的驱动器是高电平有效,而 GRBL 默认是低电平有效(或反之),记得在
cpu_map.h中修改宏定义,或者在settings.c中设置$3参数(方向取反)。