GRBL 1.1 移植到 STM32 (HAL库)

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

移植关键注意事项

  1. 定时器频率 :GRBL 对时间极其敏感。原版 AVR 是 16MHz,STM32 是 72/168MHz。你需要调整 config.h 中的 F_CPU 宏定义,或者在定时器预分频器中做等效换算,否则会导致 进给速度(Feed Rate)快 10 倍
  2. 中断优先级 :STM32 的中断有抢占优先级。串口中断 的优先级必须高于 定时器中断,否则在发送大量状态时可能会阻塞步进脉冲,导致丢步。
  3. 引脚电平反转 :如果你的驱动器是高电平有效,而 GRBL 默认是低电平有效(或反之),记得在 cpu_map.h 中修改宏定义,或者在 settings.c 中设置 $3 参数(方向取反)。
相关推荐
biyezuopinvip2 小时前
基于STC89C51单片机的多波形信号发生器设计与Proteus仿真
单片机·proteus·课程设计·proteus仿真·基于stc89c51单片机的·多波形·信号发生器设计
无人装备硬件开发爱好者2 小时前
STM32G474 驱动 1.54 寸三色电子墨水屏实现贪吃蛇游戏完整指南
stm32·嵌入式硬件·游戏
山木嵌入式2 小时前
FreeRTOS任务创建全解析:动态/静态创建+实战案例+参数深度剖析
stm32·freertos
项目題供诗2 小时前
STM32-定时器定时中断&定时器外部时钟(十一)
stm32·单片机·嵌入式硬件
披着假发的程序唐3 小时前
STM32 H743 MPU的配置使用方法
linux·c语言·c++·驱动开发·stm32·单片机·mcu
张健11564096483 小时前
MSP主堆栈指针
单片机
wild-civil3 小时前
解决Keil 生成的文件在 VSCode 乱码问题(自动识别,不用手动改编码)
ide·vscode·stm32·编辑器
qxl_7999155 小时前
Windows 显卡掉线无报警|模型推理全套防呆方案(实操完整版)
windows·stm32·单片机·推理显卡掉线误报警防呆
hhb_6185 小时前
Armbian 的 root 密码“总被修改”
stm32·单片机·嵌入式硬件