编程技术分享

分享编程知识,探讨技术创新

0%

简介:4轴CNC主板,3D打印主板,支持蓝牙和Wifi,支持脱机,支持Grbl和Marlin。

好的,作为一名高级嵌入式软件开发工程师,我很乐意为您详细阐述这款4轴CNC/3D打印主板的嵌入式系统软件架构设计,并提供相应的C代码示例。
关注微信公众号,提前获取相关推文

项目背景与需求分析

首先,我们来回顾一下这款嵌入式产品的核心需求:

  • 硬件平台: 基于4轴CNC/3D打印主板,具备蓝牙、WiFi通信能力。
  • 核心功能:
    • CNC控制: 支持4轴联动,实现精确的数控加工。
    • 3D打印控制: 支持熔融沉积成型(FDM) 3D打印工艺。
    • 脱机运行: 支持从SD卡或本地存储读取G代码并执行,无需上位机实时控制。
    • 协议兼容: 兼容Grbl和Marlin固件协议,方便用户使用和扩展。
    • 无线通信: 通过蓝牙和WiFi实现远程控制、数据传输和固件升级等功能。
  • 软件目标:
    • 可靠性: 系统必须稳定可靠,确保长时间运行不出错,尤其是在高精度运动控制场景下。
    • 高效性: 代码执行效率要高,保证实时性,响应速度快,满足运动控制的实时性要求。
    • 可扩展性: 软件架构要易于扩展和维护,方便后期添加新功能或适配新的硬件平台。
    • 易维护性: 代码结构清晰,模块化设计,方便调试、修改和升级。

系统架构设计

为了满足上述需求和目标,我将采用分层架构的设计思想,并结合模块化设计事件驱动机制,构建一个清晰、高效、可扩展的嵌入式软件系统。

分层架构概述

分层架构将系统划分为多个逻辑层,每一层都有明确的职责,层与层之间通过定义好的接口进行交互。这种架构的优点在于:

  • 职责分离: 每一层专注于完成特定的功能,降低了系统的复杂性。
  • 可维护性: 修改某一层的功能不会影响到其他层,提高了系统的可维护性。
  • 可重用性: 某些层可以被其他项目复用,提高了代码的重用率。
  • 可扩展性: 可以在不影响其他层的情况下,轻松地扩展或替换某一层的实现。

针对这款CNC/3D打印主板,我将系统架构划分为以下几个主要层次,从底层到高层依次为:

  1. 硬件抽象层 (HAL - Hardware Abstraction Layer):

    • 职责:直接与硬件交互,封装底层硬件细节,向上层提供统一的硬件接口。
    • 模块:GPIO驱动、定时器驱动、PWM驱动、UART驱动、SPI驱动、I2C驱动、ADC驱动、电机驱动接口、传感器接口、存储接口(SD卡、Flash)、通信接口(蓝牙、WiFi)等。
    • 作用:屏蔽硬件差异,使得上层代码可以独立于具体的硬件平台进行开发。
  2. 板级支持包 (BSP - Board Support Package):

    • 职责:针对具体的硬件平台进行初始化配置,包括时钟配置、中断配置、外设初始化等。
    • 模块:系统初始化模块、时钟管理模块、中断管理模块、内存管理模块、电源管理模块等。
    • 作用:为操作系统和应用层提供硬件平台的基础支持。
  3. 操作系统层 (OS - Operating System) (可选,但强烈推荐):

    • 职责:提供任务调度、资源管理、同步机制、通信机制等操作系统服务,提高系统的并发性和实时性。
    • 技术选型:FreeRTOS (轻量级、实时性好、开源、成熟稳定,非常适合资源受限的嵌入式系统)。
    • 模块:任务管理模块、内存管理模块、队列/信号量/互斥锁等同步机制、定时器服务、中断管理服务等。
    • 作用:简化并发编程,提高系统实时性,增强系统可靠性。
  4. 核心服务层 (Core Services Layer):

    • 职责:实现核心的系统服务功能,为应用层提供基础服务。
    • 模块:
      • 运动控制模块 (Motion Control Module): G代码解析器、运动规划器、电机控制驱动器、轴控制模块、插补算法(直线插补、圆弧插补等)、加减速控制、位置反馈处理等。
      • 通信管理模块 (Communication Management Module): 蓝牙通信协议栈、WiFi通信协议栈、串口通信协议、USB通信协议、数据解析与打包、命令处理、远程控制接口、数据传输接口等。
      • 文件系统模块 (File System Module): SD卡文件系统驱动 (如FatFS)、G代码文件读取、配置文件读取、数据存储管理等。
      • 配置管理模块 (Configuration Management Module): 系统参数配置、电机参数配置、打印参数配置、用户配置管理、配置数据存储与加载等。
      • 错误处理模块 (Error Handling Module): 错误检测、错误日志记录、错误上报、错误恢复机制等。
      • 任务调度管理模块 (Task Scheduling Management Module): 任务优先级管理、任务创建与删除、任务间同步与通信管理等。
      • 看门狗模块 (Watchdog Module): 防止系统死机,提高系统可靠性。
  5. 应用层 (Application Layer):

    • 职责:实现具体的应用功能,如CNC控制、3D打印控制、脱机运行、远程控制等。
    • 模块:
      • CNC控制应用 (CNC Control Application): CNC模式下的用户界面、CNC控制逻辑、G代码执行流程管理、运动参数设置、实时状态显示等。
      • 3D打印控制应用 (3D Printing Control Application): 3D打印模式下的用户界面、3D打印控制逻辑、G代码执行流程管理、打印参数设置、温度控制、挤出控制、层高控制等。
      • 脱机控制应用 (Offline Control Application): SD卡/本地存储G代码文件管理、文件选择、脱机运行控制、运行状态显示等。
      • 远程控制应用 (Remote Control Application): 蓝牙/WiFi远程控制界面、远程命令接收与处理、数据反馈、固件升级等。
      • 用户界面 (User Interface) (如果需要): 本地显示屏UI (如LCD或OLED)、按键/旋钮输入处理、信息显示、菜单操作等。

模块化设计

在分层架构的基础上,每个层内部再进行模块化设计,将功能进一步细化,划分为更小的、独立的模块。模块之间通过定义良好的接口进行交互,降低模块之间的耦合度,提高代码的可重用性和可维护性。

事件驱动机制

系统内部采用事件驱动机制,各个模块之间通过事件进行通信和协作。当某个模块发生特定事件时,会发布相应的事件,其他模块可以订阅感兴趣的事件并做出响应。事件驱动机制可以降低模块之间的耦合度,提高系统的灵活性和响应速度。

技术选型与实践验证

本项目中采用的技术和方法都是经过实践验证的,成熟可靠:

  • 操作系统: FreeRTOS (实时性、稳定性、易用性)
  • 微控制器: 选择高性能的ARM Cortex-M系列微控制器,例如STM32系列 (性能、资源、生态)
  • 通信协议: 蓝牙 (BLE低功耗蓝牙)、WiFi (IEEE 802.11 b/g/n)、串口 (UART)、USB (CDC/MSC)
  • 文件系统: FatFS (成熟、稳定、开源、支持多种存储介质)
  • G代码解析: 采用成熟的G代码解析库或自行开发高效的解析器 (兼容Grbl/Marlin格式)
  • 运动控制算法: 直线插补、圆弧插补、S型加减速、PID控制 (保证运动平稳性和精度)
  • 电机驱动: 步进电机驱动器、伺服电机驱动器、电流环/位置环控制 (精确控制电机运动)
  • 开发语言: C语言 (高效、底层控制能力强、成熟的生态)
  • 开发工具: Keil MDK、IAR Embedded Workbench、GCC (成熟的IDE和编译器)
  • 调试工具: J-Link/ST-Link调试器、串口调试助手、网络调试助手、逻辑分析仪 (高效调试工具)

C代码实现 (示例 - 核心框架及关键模块)

为了展示系统架构和关键模块的实现思路,以下提供部分核心模块的C代码示例。由于代码量庞大,无法在短时间内提供完整的3000行代码,这里重点展示系统框架、HAL层、BSP层、操作系统层、核心服务层中运动控制模块和通信管理模块的关键代码片段。

1. 系统框架 (main.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include "bsp.h"
#include "os.h"
#include "motion_control.h"
#include "communication_manager.h"
#include "config_manager.h"
#include "error_handler.h"
#include "user_interface.h" // 可选

// 任务堆栈大小定义
#define TASK_STACK_SIZE_DEFAULT 128
#define TASK_STACK_SIZE_MOTION 256
#define TASK_STACK_SIZE_COMM 256
#define TASK_STACK_SIZE_UI 256

// 任务句柄
TaskHandle_t xMotionControlTaskHandle = NULL;
TaskHandle_t xCommunicationTaskHandle = NULL;
TaskHandle_t xUITaskHandle = NULL;

// 任务函数声明
void vMotionControlTask( void *pvParameters );
void vCommunicationTask( void *pvParameters );
void vUITask( void *pvParameters );

int main( void )
{
// 1. BSP 初始化
BSP_Init();

// 2. 系统配置加载
ConfigManager_LoadConfig();

// 3. 错误处理模块初始化
ErrorHandler_Init();

// 4. 运动控制模块初始化
MotionControl_Init();

// 5. 通信管理模块初始化
CommunicationManager_Init();

// 6. 用户界面模块初始化 (可选)
// UserInterface_Init();

// 7. 创建任务
BaseType_t xTaskCreateResult;

xTaskCreateResult = xTaskCreate( vMotionControlTask, "MotionTask", TASK_STACK_SIZE_MOTION, NULL, OS_PRIORITY_NORMAL, &xMotionControlTaskHandle );
if( xTaskCreateResult != pdPASS ) { ErrorHandler_HandleError(ERROR_TASK_CREATE_MOTION); }

xTaskCreateResult = xTaskCreate( vCommunicationTask, "CommTask", TASK_STACK_SIZE_COMM, NULL, OS_PRIORITY_NORMAL, &xCommunicationTaskHandle );
if( xTaskCreateResult != pdPASS ) { ErrorHandler_HandleError(ERROR_TASK_CREATE_COMM); }

// 如果需要用户界面,则创建UI任务
// xTaskCreateResult = xTaskCreate( vUITask, "UITask", TASK_STACK_SIZE_UI, NULL, OS_PRIORITY_NORMAL, &xUITaskHandle );
// if( xTaskCreateResult != pdPASS ) { ErrorHandler_HandleError(ERROR_TASK_CREATE_UI); }

// 8. 启动 FreeRTOS 调度器
vTaskStartScheduler();

// 正常情况下不会执行到这里
return 0;
}

// 运动控制任务
void vMotionControlTask( void *pvParameters )
{
while(1)
{
MotionControl_TaskHandler(); // 运动控制任务处理函数
vTaskDelay(pdMS_TO_TICKS(1)); // 适当延时,降低CPU占用率
}
}

// 通信任务
void vCommunicationTask( void *pvParameters )
{
while(1)
{
CommunicationManager_TaskHandler(); // 通信任务处理函数
vTaskDelay(pdMS_TO_TICKS(10)); // 适当延时
}
}

// 用户界面任务 (可选)
void vUITask( void *pvParameters )
{
while(1)
{
// UserInterface_TaskHandler(); // 用户界面任务处理函数
vTaskDelay(pdMS_TO_TICKS(50)); // 适当延时
}
}

2. 硬件抽象层 (HAL - 示例: hal_gpio.h, hal_gpio.c)

hal_gpio.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include "stdint.h"

// GPIO 端口定义 (根据具体硬件平台定义)
typedef enum {
GPIO_PORT_A,
GPIO_PORT_B,
GPIO_PORT_C,
GPIO_PORT_D,
// ...
GPIO_PORT_MAX
} GPIO_Port_TypeDef;

// GPIO 引脚定义 (根据具体硬件平台定义)
typedef enum {
GPIO_PIN_0 = (1 << 0),
GPIO_PIN_1 = (1 << 1),
GPIO_PIN_2 = (1 << 2),
GPIO_PIN_3 = (1 << 3),
GPIO_PIN_4 = (1 << 4),
GPIO_PIN_5 = (1 << 5),
GPIO_PIN_6 = (1 << 6),
GPIO_PIN_7 = (1 << 7),
GPIO_PIN_8 = (1 << 8),
GPIO_PIN_9 = (1 << 9),
GPIO_PIN_10 = (1 << 10),
GPIO_PIN_11 = (1 << 11),
GPIO_PIN_12 = (1 << 12),
GPIO_PIN_13 = (1 << 13),
GPIO_PIN_14 = (1 << 14),
GPIO_PIN_15 = (1 << 15),
GPIO_PIN_ALL = 0xFFFF
} GPIO_Pin_TypeDef;

// GPIO 工作模式定义
typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF_PP, // 复用推挽输出
GPIO_MODE_AF_OD // 复用开漏输出
} GPIO_Mode_TypeDef;

// GPIO 输出类型定义
typedef enum {
GPIO_OTYPE_PP, // 推挽输出
GPIO_OTYPE_OD // 开漏输出
} GPIO_OType_TypeDef;

// GPIO 上下拉电阻定义
typedef enum {
GPIO_PUPD_NONE,
GPIO_PUPD_PULLUP,
GPIO_PUPD_PULLDOWN
} GPIO_PuPd_TypeDef;

// GPIO 初始化结构体
typedef struct {
GPIO_Port_TypeDef Port;
GPIO_Pin_TypeDef Pin;
GPIO_Mode_TypeDef Mode;
GPIO_OType_TypeDef OType;
GPIO_PuPd_TypeDef Pull;
uint32_t Speed; // 输出速度 (可选)
uint32_t Alternate; // 复用功能选择 (可选)
} GPIO_InitTypeDef;

// 函数声明
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);
void HAL_GPIO_WritePin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin, uint8_t PinState);
uint8_t HAL_GPIO_ReadPin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin);

#endif /* HAL_GPIO_H */

hal_gpio.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include "hal_gpio.h"
// 包含具体硬件平台的头文件,例如 "stm32f4xx_hal.h" 或 "gd32f4xx.h" 等
#include "stm32f4xx_hal.h" // 示例,根据实际平台修改

void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct)
{
GPIO_InitTypeDef GPIO_Config;

// 映射 HAL GPIO 结构体到 STM32 HAL 结构体 (示例,根据实际平台修改)
GPIO_Config.Pin = GPIO_InitStruct->Pin;
if (GPIO_InitStruct->Mode == GPIO_MODE_INPUT) {
GPIO_Config.Mode = GPIO_MODE_INPUT;
} else if (GPIO_InitStruct->Mode == GPIO_MODE_OUTPUT) {
GPIO_Config.Mode = GPIO_MODE_OUTPUT_PP; // 默认推挽输出
} else if (GPIO_InitStruct->Mode == GPIO_MODE_AF_PP) {
GPIO_Config.Mode = GPIO_MODE_AF_PP;
} else if (GPIO_InitStruct->Mode == GPIO_MODE_AF_OD) {
GPIO_Config.Mode = GPIO_MODE_AF_OD;
}
if (GPIO_InitStruct->OType == GPIO_OTYPE_PP) {
GPIO_Config.Pull = GPIO_InitStruct->Pull;
GPIO_Config.Speed = GPIO_SPEED_FREQ_HIGH; // 示例速度
GPIO_Config.Alternate = GPIO_InitStruct->Alternate; // 示例复用功能
} else if (GPIO_InitStruct->OType == GPIO_OTYPE_OD) {
GPIO_Config.Pull = GPIO_InitStruct->Pull;
GPIO_Config.Speed = GPIO_SPEED_FREQ_HIGH; // 示例速度
GPIO_Config.Alternate = GPIO_InitStruct->Alternate; // 示例复用功能
} else {
GPIO_Config.Pull = GPIO_InitStruct->Pull;
}

// 使能 GPIO 时钟 (示例,根据实际平台修改)
if (GPIO_InitStruct->Port == GPIO_PORT_A) {
__HAL_RCC_GPIOA_CLK_ENABLE();
} else if (GPIO_InitStruct->Port == GPIO_PORT_B) {
__HAL_RCC_GPIOB_CLK_ENABLE();
} // ... 其他端口时钟使能

// 调用 STM32 HAL 库 GPIO 初始化函数 (示例,根据实际平台修改)
HAL_GPIO_Init((GPIO_TypeDef *)((GPIOA_BASE + (GPIO_InitStruct->Port * 0x400))), &GPIO_Config);
}

void HAL_GPIO_WritePin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin, uint8_t PinState)
{
// 调用 STM32 HAL 库 GPIO 输出函数 (示例,根据实际平台修改)
HAL_GPIO_WritePin((GPIO_TypeDef *)((GPIOA_BASE + (Port * 0x400))), Pin, (GPIO_PinState)PinState);
}

uint8_t HAL_GPIO_ReadPin(GPIO_Port_TypeDef Port, GPIO_Pin_TypeDef Pin)
{
// 调用 STM32 HAL 库 GPIO 输入函数 (示例,根据实际平台修改)
return (uint8_t)HAL_GPIO_ReadPin((GPIO_TypeDef *)((GPIOA_BASE + (Port * 0x400))), Pin);
}

3. 板级支持包 (BSP - 示例: bsp.h, bsp.c)

bsp.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef BSP_H
#define BSP_H

#include "stdint.h"
#include "hal_gpio.h"
#include "hal_timer.h"
#include "hal_uart.h"
// ... 其他 HAL 头文件

// 系统时钟频率定义 (根据实际硬件平台定义)
#define SYS_CLK_FREQ_HZ 168000000 // 168MHz 示例

// 函数声明
void BSP_Init(void);
void BSP_DelayMs(uint32_t ms);
uint32_t BSP_GetSysTick(void);

// ... 其他 BSP 函数声明

#endif /* BSP_H */

bsp.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include "bsp.h"
#include "FreeRTOS.h"
#include "task.h"
#include "systick.h" // FreeRTOS SysTick 配置头文件

// 系统滴答定时器频率 (FreeRTOS 使用)
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 ) // 1ms 滴答

// SysTick 计数器变量 (FreeRTOS 提供)
extern volatile uint32_t ulTaskSwitchedInTick;

void BSP_Init(void)
{
// 1. 初始化系统时钟 (根据具体硬件平台配置)
SystemClock_Config(); // 示例函数,需要根据具体平台实现

// 2. 初始化 HAL 驱动
HAL_GPIO_Init(/* ... */);
HAL_TIM_Init(/* ... */);
HAL_UART_Init(/* ... */);
// ... 初始化其他 HAL 驱动

// 3. 初始化 SysTick (FreeRTOS 使用)
SysTick_Config(SYS_CLK_FREQ_HZ / configTICK_RATE_HZ);

// 4. 初始化其他板级外设 (如SD卡、蓝牙、WiFi等)
// SDCard_Init();
// Bluetooth_Init();
// WiFi_Init();
}

void BSP_DelayMs(uint32_t ms)
{
vTaskDelay(pdMS_TO_TICKS(ms)); // 使用 FreeRTOS 延时函数
}

uint32_t BSP_GetSysTick(void)
{
return xTaskGetTickCount(); // 使用 FreeRTOS 获取系统滴答计数
}

// 示例系统时钟配置函数 (需要根据具体硬件平台实现)
void SystemClock_Config(void)
{
// ... 根据硬件平台配置系统时钟,例如使用 HSE 外部高速晶振,配置 PLL 倍频等
// ... 确保系统时钟频率达到 SYS_CLK_FREQ_HZ
}

4. 操作系统层 (OS - FreeRTOS 配置 - 示例: FreeRTOSConfig.h)

FreeRTOSConfig.h (部分关键配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* -----------------------------------------------------------
* Application specific definitions.
*
* These definitions should be adjusted for your particular hardware and
* application requirements.
*
* THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
* FreeRTOS API DOCUMENTATION.
* ---------------------------------------------------------- */

/* 系统时钟频率 (与 BSP_Init 中配置的系统时钟频率一致) */
#define configCPU_CLOCK_HZ ( SYS_CLK_FREQ_HZ )

/* 滴答定时器频率 (1ms 滴答) */
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )

/* 任务堆栈大小 (默认值) */
#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 128 )

/* 总共可创建的任务数量上限 */
#define configMAX_PRIORITIES ( 5 ) // 示例优先级数量

/* 定义空闲任务的优先级 */
#define configIDLE_TASK_PRIORITY ( tskIDLE_PRIORITY )

/* 定义时间片轮转调度是否开启 */
#define configUSE_PREEMPTION 1

/* 定义是否使用时间片轮转调度 */
#define configUSE_TIME_SLICING 1

/* 定义是否使用软件定时器 */
#define configUSE_TIMERS 1

/* 定义软件定时器任务的优先级 */
#define configTIMER_TASK_PRIORITY ( OS_PRIORITY_LOW )

/* 定义软件定时器任务的堆栈大小 */
#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE )

/* 定义钩子函数 (可选) */
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCHECK_FOR_STACK_OVERFLOW 2 // Stack overflow checking method

/* ... 其他 FreeRTOS 配置项 */

#endif /* FREERTOS_CONFIG_H */

5. 核心服务层 - 运动控制模块 (motion_control.h, motion_control.c - 示例: 步进电机控制)

motion_control.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#ifndef MOTION_CONTROL_H
#define MOTION_CONTROL_H

#include "stdint.h"

// 轴定义
typedef enum {
AXIS_X,
AXIS_Y,
AXIS_Z,
AXIS_A,
AXIS_MAX
} Axis_TypeDef;

// 电机方向定义
typedef enum {
MOTOR_DIR_CW, // 顺时针
MOTOR_DIR_CCW // 逆时针
} Motor_Dir_TypeDef;

// 电机控制结构体
typedef struct {
Axis_TypeDef axis;
uint32_t step_pin;
uint32_t dir_pin;
uint32_t enable_pin;
// ... 其他电机参数
} Motor_Config_TypeDef;

// 运动控制初始化
void MotionControl_Init(void);

// 设置电机方向
void MotionControl_SetMotorDirection(Axis_TypeDef axis, Motor_Dir_TypeDef dir);

// 电机步进脉冲输出 (单步)
void MotionControl_StepPulse(Axis_TypeDef axis);

// 设置电机使能状态
void MotionControl_SetMotorEnable(Axis_TypeDef axis, uint8_t enable);

// G代码解析与执行 (简化示例)
void MotionControl_ProcessGCode(const char *gcode_line);

// 运动控制任务处理函数 (在任务中周期性调用)
void MotionControl_TaskHandler(void);

#endif /* MOTION_CONTROL_H */

motion_control.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#include "motion_control.h"
#include "hal_gpio.h"
#include "bsp.h"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"

// 电机配置 (示例,根据实际硬件连接配置)
Motor_Config_TypeDef MotorConfig[AXIS_MAX] = {
{AXIS_X, GPIO_PIN_10, GPIO_PIN_11, GPIO_PIN_12}, // X轴
{AXIS_Y, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15}, // Y轴
{AXIS_Z, GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_7}, // Z轴
{AXIS_A, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_4} // A轴
};

// 运动状态变量 (示例)
volatile uint32_t current_position[AXIS_MAX] = {0}; // 当前位置 (步数)
volatile uint32_t target_position[AXIS_MAX] = {0}; // 目标位置 (步数)
volatile uint32_t speed[AXIS_MAX] = {0}; // 运动速度 (步/秒)

// 运动控制初始化
void MotionControl_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;

// 初始化电机控制 GPIO
for (int i = 0; i < AXIS_MAX; i++) {
// STEP 引脚
GPIO_InitStruct.Port = GPIO_PORT_A; // 示例端口,根据实际修改
GPIO_InitStruct.Pin = MotorConfig[i].step_pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT;
GPIO_InitStruct.OType = GPIO_OTYPE_PP;
GPIO_InitStruct.Pull = GPIO_PUPD_PULLDOWN;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(GPIO_PORT_A, MotorConfig[i].step_pin, GPIO_PIN_RESET); // 初始低电平

// DIR 引脚
GPIO_InitStruct.Pin = MotorConfig[i].dir_pin;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(GPIO_PORT_A, MotorConfig[i].dir_pin, GPIO_PIN_RESET); // 初始方向

// ENABLE 引脚 (可选,如果使用使能控制)
GPIO_InitStruct.Pin = MotorConfig[i].enable_pin;
HAL_GPIO_Init(&GPIO_InitStruct);
HAL_GPIO_WritePin(GPIO_PORT_A, MotorConfig[i].enable_pin, GPIO_PIN_SET); // 初始失能 (高电平使能)
}

// ... 初始化运动控制相关的定时器、中断等
}

// 设置电机方向
void MotionControl_SetMotorDirection(Axis_TypeDef axis, Motor_Dir_TypeDef dir)
{
GPIO_Port_TypeDef gpio_port = GPIO_PORT_A; // 示例端口,根据实际修改
uint8_t pin_state = (dir == MOTOR_DIR_CW) ? GPIO_PIN_SET : GPIO_PIN_RESET; // 顺时针高电平,逆时针低电平 (示例)

HAL_GPIO_WritePin(gpio_port, MotorConfig[axis].dir_pin, pin_state);
}

// 电机步进脉冲输出 (单步)
void MotionControl_StepPulse(Axis_TypeDef axis)
{
GPIO_Port_TypeDef gpio_port = GPIO_PORT_A; // 示例端口,根据实际修改

HAL_GPIO_WritePin(gpio_port, MotorConfig[axis].step_pin, GPIO_PIN_SET); // 输出高电平
BSP_DelayMs(1); // 短延时,产生脉冲宽度 (需要根据电机驱动器要求调整)
HAL_GPIO_WritePin(gpio_port, MotorConfig[axis].step_pin, GPIO_PIN_RESET); // 输出低电平

// 更新位置计数器 (根据方向判断增减)
// ...
}

// 设置电机使能状态
void MotionControl_SetMotorEnable(Axis_TypeDef axis, uint8_t enable)
{
GPIO_Port_TypeDef gpio_port = GPIO_PORT_A; // 示例端口,根据实际修改
uint8_t pin_state = enable ? GPIO_PIN_RESET : GPIO_PIN_SET; // 低电平使能,高电平失能 (示例)

HAL_GPIO_WritePin(gpio_port, MotorConfig[axis].enable_pin, pin_state);
}

// G代码解析与执行 (简化示例 - 仅处理 G00 快速定位和 G01 直线插补)
void MotionControl_ProcessGCode(const char *gcode_line)
{
char command[10];
float x = 0, y = 0, z = 0, a = 0, f = 0; // 位置坐标和进给速度
int num_params = 0;

// 解析 G 代码 (简化解析,实际应用需要更完善的解析器)
sscanf(gcode_line, "%s", command);

if (strcmp(command, "G00") == 0 || strcmp(command, "G01") == 0) {
num_params = sscanf(gcode_line, "%s X%f Y%f Z%f A%f F%f", command, &x, &y, &z, &a, &f);
if (num_params >= 2) { // 至少要解析到 X 坐标
target_position[AXIS_X] = (uint32_t)(x * STEPS_PER_MM_X); // 毫米转换为步数 (需要定义 STEPS_PER_MM_X 等常量)
if (num_params >= 3) target_position[AXIS_Y] = (uint32_t)(y * STEPS_PER_MM_Y);
if (num_params >= 4) target_position[AXIS_Z] = (uint32_t)(z * STEPS_PER_MM_Z);
if (num_params >= 5) target_position[AXIS_A] = (uint32_t)(a * STEPS_PER_MM_A);
if (num_params >= 6) speed[AXIS_X] = speed[AXIS_Y] = speed[AXIS_Z] = speed[AXIS_A] = (uint32_t)f; // 设置进给速度 (步/分钟转换为步/秒)

// 执行运动规划和控制 (简化示例,仅单轴运动)
if (strcmp(command, "G00") == 0) {
// 快速定位 (G00) - 简单步进驱动
for (int i = 0; i < AXIS_MAX; i++) {
if (target_position[i] > current_position[i]) {
MotionControl_SetMotorDirection(i, MOTOR_DIR_CW); // 正方向
} else {
MotionControl_SetMotorDirection(i, MOTOR_DIR_CCW); // 反方向
}
while (current_position[i] != target_position[i]) {
MotionControl_StepPulse(i);
if (target_position[i] > current_position[i]) current_position[i]++;
else current_position[i]--;
BSP_DelayMs(1); // 控制步进速度 (需要更精细的定时器控制)
}
}
} else if (strcmp(command, "G01") == 0) {
// 直线插补 (G01) - 需要更复杂的插补算法和运动控制
// ... (此处省略直线插补算法实现) ...
// 示例:简单地按轴顺序步进
for (int i = 0; i < AXIS_MAX; i++) {
// ... 类似 G00 的步进驱动,但需要考虑进给速度 f 和插补算法
}
}
}
}
// ... 处理其他 G 代码指令
}


// 运动控制任务处理函数 (在任务中周期性调用)
void MotionControl_TaskHandler(void)
{
// 检查是否有新的 G 代码指令需要执行
// ... 从 G 代码队列或文件读取 G 代码行
// char *gcode_line = GetNextGCodeLine(); // 示例函数
// if (gcode_line != NULL) {
// MotionControl_ProcessGCode(gcode_line);
// }

// ... 执行运动规划、插补、加减速控制等实时任务
// ... 定时生成步进脉冲,驱动电机运动
}

6. 核心服务层 - 通信管理模块 (communication_manager.h, communication_manager.c - 示例: 串口通信)

communication_manager.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#ifndef COMMUNICATION_MANAGER_H
#define COMMUNICATION_MANAGER_H

#include "stdint.h"

// 通信协议类型定义
typedef enum {
COMM_PROTOCOL_SERIAL,
COMM_PROTOCOL_BLUETOOTH,
COMM_PROTOCOL_WIFI,
COMM_PROTOCOL_USB_CDC,
COMM_PROTOCOL_MAX
} Comm_Protocol_TypeDef;

// 通信状态定义
typedef enum {
COMM_STATE_IDLE,
COMM_STATE_CONNECTING,
COMM_STATE_CONNECTED,
COMM_STATE_DISCONNECTED,
COMM_STATE_ERROR
} Comm_State_TypeDef;

// 通信管理初始化
void CommunicationManager_Init(void);

// 启动/停止通信协议
void CommunicationManager_StartProtocol(Comm_Protocol_TypeDef protocol);
void CommunicationManager_StopProtocol(Comm_Protocol_TypeDef protocol);

// 发送数据
uint32_t CommunicationManager_SendData(Comm_Protocol_TypeDef protocol, const uint8_t *data, uint32_t len);

// 接收数据处理 (回调函数或消息队列方式)
void CommunicationManager_ReceiveDataHandler(Comm_Protocol_TypeDef protocol, const uint8_t *data, uint32_t len);

// 获取通信状态
Comm_State_TypeDef CommunicationManager_GetState(Comm_Protocol_TypeDef protocol);

// 通信任务处理函数 (在任务中周期性调用)
void CommunicationManager_TaskHandler(void);

#endif /* COMMUNICATION_MANAGER_H */

communication_manager.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#include "communication_manager.h"
#include "hal_uart.h"
#include "bsp.h"
#include "string.h"
#include "stdio.h"

// 串口配置 (示例,根据实际硬件连接配置)
UART_InitTypeDef SerialConfig = {
UART_PORT_1, // 示例串口端口
115200, // 波特率
UART_WORDLENGTH_8B,
UART_STOPBITS_1,
UART_PARITY_NONE,
UART_HWCONTROL_NONE
};

// 通信状态变量 (示例)
Comm_State_TypeDef CommState[COMM_PROTOCOL_MAX] = {COMM_STATE_IDLE};

// 接收缓冲区 (示例)
#define SERIAL_RX_BUFFER_SIZE 256
uint8_t SerialRxBuffer[SERIAL_RX_BUFFER_SIZE];
uint32_t SerialRxIndex = 0;

// 通信管理初始化
void CommunicationManager_Init(void)
{
// 初始化串口
HAL_UART_Init(&SerialConfig);

// ... 初始化蓝牙、WiFi、USB 等通信模块
}

// 启动通信协议
void CommunicationManager_StartProtocol(Comm_Protocol_TypeDef protocol)
{
if (protocol == COMM_PROTOCOL_SERIAL) {
// 启动串口接收中断 (示例)
HAL_UART_Receive_IT(&SerialConfig, SerialRxBuffer, SERIAL_RX_BUFFER_SIZE);
CommState[COMM_PROTOCOL_SERIAL] = COMM_STATE_CONNECTED; // 假设串口默认连接
}
// ... 启动其他协议
}

// 停止通信协议
void CommunicationManager_StopProtocol(Comm_Protocol_TypeDef protocol)
{
if (protocol == COMM_PROTOCOL_SERIAL) {
HAL_UART_AbortReceive(&SerialConfig); // 关闭串口接收
CommState[COMM_PROTOCOL_SERIAL] = COMM_STATE_DISCONNECTED;
}
// ... 停止其他协议
}

// 发送数据
uint32_t CommunicationManager_SendData(Comm_Protocol_TypeDef protocol, const uint8_t *data, uint32_t len)
{
if (protocol == COMM_PROTOCOL_SERIAL) {
return HAL_UART_Transmit(&SerialConfig, data, len, 100); // 100ms 超时
}
// ... 处理其他协议的发送
return 0;
}

// 串口接收中断回调函数 (示例 - 需要在 HAL UART 驱动中调用)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == SerialConfig.port) {
// 处理接收到的数据
CommunicationManager_ReceiveDataHandler(COMM_PROTOCOL_SERIAL, SerialRxBuffer, SERIAL_RX_BUFFER_SIZE - huart->RxXferCount);

// 重新启动接收中断 (循环接收)
HAL_UART_Receive_IT(&SerialConfig, SerialRxBuffer, SERIAL_RX_BUFFER_SIZE);
}
}


// 接收数据处理 (示例 - 串口接收处理)
void CommunicationManager_ReceiveDataHandler(Comm_Protocol_TypeDef protocol, const uint8_t *data, uint32_t len)
{
if (protocol == COMM_PROTOCOL_SERIAL) {
// 在这里处理接收到的串口数据,例如解析 G 代码命令
for (uint32_t i = 0; i < len; i++) {
if (data[i] == '\n' || data[i] == '\r') { // 行结束符
if (SerialRxIndex > 0) {
SerialRxBuffer[SerialRxIndex] = '\0'; // 字符串结束符
// 处理完整的 G 代码行
MotionControl_ProcessGCode((const char *)SerialRxBuffer);
SerialRxIndex = 0; // 重置接收索引
}
} else if (SerialRxIndex < SERIAL_RX_BUFFER_SIZE - 1) {
SerialRxBuffer[SerialRxIndex++] = data[i]; // 存入接收缓冲区
}
}
}
// ... 处理其他协议的接收数据
}

// 获取通信状态
Comm_State_TypeDef CommunicationManager_GetState(Comm_Protocol_TypeDef protocol)
{
return CommState[protocol];
}

// 通信任务处理函数 (在任务中周期性调用)
void CommunicationManager_TaskHandler(void)
{
// 检查各通信协议的状态,处理连接、断开、数据接收等事件
// ... 例如:蓝牙连接状态检测、WiFi 数据包处理等
}

7. 其他模块 (简要说明)

  • 文件系统模块 (File System Module): 使用 FatFS 文件系统库,实现 SD 卡的挂载、文件读取、目录操作等功能,用于读取脱机 G 代码文件和配置文件。
  • 配置管理模块 (Configuration Management Module): 负责系统参数、电机参数、打印参数等配置数据的加载、保存和管理。可以使用 Flash 存储配置数据,并提供 API 接口供其他模块访问和修改配置。
  • 错误处理模块 (Error Handling Module): 负责系统错误的检测、记录和处理。可以使用错误代码定义不同类型的错误,并通过串口或日志文件输出错误信息,方便调试和维护。
  • 用户界面模块 (User Interface Module): 如果需要本地显示屏,则需要实现 UI 驱动和界面逻辑,处理用户输入,显示系统状态和操作菜单。

测试验证和维护升级

测试验证:

  • 单元测试: 针对每个模块进行单元测试,验证模块功能的正确性。
  • 集成测试: 将各个模块集成起来进行测试,验证模块之间的协作和接口的正确性。
  • 系统测试: 进行全面的系统功能测试,模拟实际应用场景,验证系统的整体功能和性能。
  • 可靠性测试: 进行长时间运行测试,验证系统的稳定性和可靠性。
  • 性能测试: 测试系统的实时性、响应速度、运动控制精度等性能指标。

维护升级:

  • 模块化设计: 方便对系统进行局部修改和升级,不影响其他模块。
  • 固件升级: 预留固件升级接口,可以通过串口、USB 或 OTA (Over-The-Air) 方式进行固件升级,方便修复 Bug 和添加新功能。
  • 日志记录: 完善的日志记录功能,方便在系统出现问题时进行故障排查和分析。

总结

以上详细阐述了这款4轴CNC/3D打印主板嵌入式系统软件架构设计,并提供了关键模块的C代码示例。整个系统采用分层架构、模块化设计和事件驱动机制,旨在构建一个可靠、高效、可扩展的嵌入式软件平台。通过 FreeRTOS 操作系统、HAL 硬件抽象层、BSP 板级支持包以及核心服务层和应用层的协同工作,实现 CNC/3D 打印的运动控制、通信管理、脱机运行和远程控制等核心功能。

请注意,以上代码示例仅为框架和思路展示,实际项目开发中需要根据具体的硬件平台、功能需求和性能指标进行详细的设计和实现,并进行充分的测试和验证。 完整的3000行代码实现需要更详细的模块代码,例如更完善的G代码解析器、插补算法实现、蓝牙/WiFi 通信协议栈的集成、文件系统操作、配置管理、错误处理、用户界面等等。 这些模块的完整代码实现将远超 3000 行,这里提供的示例代码旨在帮助您理解整体架构和关键模块的设计思路。

欢迎关注我的其它发布渠道