编程技术分享

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

0%

简介:机智的kason大佬开源了胡桃摇的模型,于是想着完善一下这个工程吧。

好的,非常荣幸能参与到“胡桃摇”这个充满趣味的项目中来。作为一个高级嵌入式软件开发工程师,我将从需求分析开始,逐步深入到系统设计、代码架构、具体实现、测试验证以及未来的维护升级,力求打造一个可靠、高效、可扩展的嵌入式系统平台,让“胡桃摇”项目不仅有趣,而且技术上也经得起推敲。
关注微信公众号,提前获取相关推文

项目背景与需求分析

首先,我们来深入理解“胡桃摇”项目的背景和需求。基于“机智的kason大佬开源了胡桃摇的模型”,我们可以推断这是一个硬件模型,可能是一个小型的机械装置,模拟胡桃摇动的动作。我们的目标是为其设计并实现一套嵌入式系统,赋予这个模型“生命”,使其能够自主或受控地进行摇动,并具备一定的智能和可扩展性。

需求概要:

  1. 核心功能:胡桃摇动控制

    • 驱动机械模型,实现胡桃的摇动动作。
    • 摇动模式可以多样化,例如:匀速摇动、变速摇动、随机摇动等。
    • 摇动速度和幅度可调。
  2. 交互方式:

    • 本地控制: 通过板载按键或传感器(例如触摸、重力感应等)进行本地控制。
    • 远程控制(可选但推荐): 考虑通过蓝牙或Wi-Fi等无线通信方式,使用手机APP或其他设备进行远程控制,增加趣味性和可玩性。
  3. 状态指示:

    • 通过LED指示灯或其他显示方式,反馈系统运行状态,例如:摇动模式、速度、电量等。
  4. 电源管理:

    • 低功耗设计,延长电池续航时间。
    • 电源状态监测,低电量报警。
  5. 可扩展性:

    • 预留硬件和软件接口,方便未来增加新功能,例如:音乐播放、灯光效果、更复杂的动作模式等。
  6. 可靠性和稳定性:

    • 系统运行稳定可靠,不易崩溃。
    • 具备一定的容错能力,例如:电机过载保护、低电压保护等。
  7. 易维护性:

    • 代码结构清晰,模块化设计,易于理解和维护。
    • 预留调试接口,方便问题排查和软件升级。

系统设计架构

为了满足以上需求,并确保系统的可靠性、高效性和可扩展性,我推荐采用分层架构的设计模式,并结合事件驱动状态机的思想。这种架构在嵌入式系统开发中被广泛验证,具有良好的结构性和灵活性。

分层架构:

我们将系统软件划分为以下几个层次,由下至上依次为:

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

    • 功能: 直接与硬件交互,封装底层硬件操作细节,向上层提供统一的硬件访问接口。
    • 模块:
      • GPIO 驱动: 控制通用输入输出引脚,用于LED指示灯、按键输入、电机控制信号等。
      • 定时器驱动: 提供定时功能,用于PWM生成、周期性任务调度、时间管理等。
      • 电机驱动: 控制电机(例如步进电机或舵机)的转动,实现胡桃的摇动动作。
      • 传感器驱动 (可选): 如果使用传感器,例如触摸传感器、重力传感器等,则需要相应的驱动。
      • 通信接口驱动 (可选): 如果需要远程控制,例如蓝牙或Wi-Fi,则需要相应的通信接口驱动。
      • 电源管理驱动: 监测电压、电流,控制电源模式,实现低功耗管理。
  2. 设备驱动层 (Device Driver Layer)

    • 功能: 基于HAL层提供的接口,实现对特定设备的控制和管理,向上层提供更高级别的设备操作接口。
    • 模块:
      • LED 管理驱动: 控制LED的亮灭、闪烁等效果,用于状态指示。
      • 按键管理驱动: 检测按键事件,例如单击、双击、长按等,并向上层传递按键事件。
      • 电机控制驱动: 实现电机的精确控制,例如角度控制、速度控制、运动模式控制等。
      • 传感器管理驱动 (可选): 处理传感器数据,例如触摸事件检测、重力加速度数据读取等。
      • 通信协议栈 (可选): 实现蓝牙或Wi-Fi等通信协议,例如蓝牙BLE协议栈、TCP/IP协议栈等。
      • 电源管理模块: 实现更高级的电源管理策略,例如休眠模式、唤醒机制等。
  3. 系统服务层 (System Service Layer)

    • 功能: 提供系统级别的服务,例如任务调度、事件管理、状态管理、配置管理等,为应用层提供基础支撑。
    • 模块:
      • 任务调度器: 管理系统中的任务,例如周期性任务、事件驱动任务等。
      • 事件管理器: 负责事件的接收、分发和处理,实现事件驱动编程模型。
      • 状态管理器: 管理系统的各种状态,例如摇动模式、速度、电源状态等,并提供状态切换和查询接口。
      • 配置管理器: 管理系统的配置参数,例如摇动速度、模式参数、通信参数等,可以从配置文件或外部存储加载配置。
      • 错误处理模块: 处理系统运行过程中出现的错误,例如硬件错误、软件异常等,并进行错误日志记录和恢复。
  4. 应用层 (Application Layer)

    • 功能: 实现具体的应用逻辑,即“胡桃摇”的核心功能,包括摇动模式控制、用户交互逻辑、状态显示等。
    • 模块:
      • 摇动控制模块: 实现各种摇动模式,例如匀速摇动、变速摇动、随机摇动等,并根据用户指令控制摇动。
      • 用户交互模块: 处理用户输入,例如按键输入、远程控制指令等,并根据用户输入执行相应的操作。
      • 状态显示模块: 控制LED指示灯或其他显示设备,显示系统状态信息。
      • 电源管理应用: 根据系统状态和用户操作,调用电源管理驱动,实现低功耗运行。

事件驱动与状态机:

  • 事件驱动: 系统以事件为驱动,例如按键事件、定时器事件、传感器事件、远程控制指令事件等。当事件发生时,系统会根据事件类型调用相应的处理函数,执行相应的操作。这种模式能够提高系统的响应性和效率。

  • 状态机: 对于复杂的摇动模式控制和用户交互逻辑,可以使用状态机进行建模。状态机将系统划分为不同的状态,例如“待机状态”、“匀速摇动状态”、“变速摇动状态”等。状态之间可以进行切换,状态切换由事件触发。状态机能够清晰地描述系统的行为,并简化代码逻辑。

代码实现 (C语言)

接下来,我将用C语言逐步实现上述架构的各个层次,并给出关键模块的代码示例。由于篇幅限制,这里只给出核心代码框架和关键功能模块的实现,完整的3000行代码将包含更详细的注释、错误处理、参数配置等。

1. 硬件抽象层 (HAL)

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
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
GPIO_PIN_2,
// ... 更多引脚定义
GPIO_PIN_MAX
} gpio_pin_t;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

// 初始化 GPIO 引脚
void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode);

// 设置 GPIO 引脚输出电平
void hal_gpio_write(gpio_pin_t pin, gpio_level_t level);

// 读取 GPIO 引脚输入电平
gpio_level_t hal_gpio_read(gpio_pin_t pin);

#endif // HAL_GPIO_H

hal_gpio.c (假设基于STM32 HAL库,实际根据具体MCU平台实现)

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
#include "hal_gpio.h"
#include "stm32fxxx_hal.h" // 假设使用 STM32 HAL 库

// 映射 gpio_pin_t 到 STM32 HAL 的 GPIO_TypeDef 和 GPIO_Pin
GPIO_TypeDef* gpio_port_map[] = {GPIOA, GPIOB, GPIOC, /* ... 更多端口 */}; // 根据实际 MCU 端口定义
uint16_t gpio_pin_map[] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, /* ... 更多引脚 */}; // 根据实际 MCU 引脚定义

void hal_gpio_init(gpio_pin_t pin, gpio_mode_t mode) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_TypeDef* port = gpio_port_map[pin / 16]; // 假设每组端口 16 个引脚
uint16_t pin_num = gpio_pin_map[pin % 16];

if (mode == GPIO_MODE_OUTPUT) {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
} else { // GPIO_MODE_INPUT
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 默认上拉
}
GPIO_InitStruct.Pin = pin_num;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速即可
HAL_GPIO_Init(port, &GPIO_InitStruct);
}

void hal_gpio_write(gpio_pin_t pin, gpio_level_t level) {
GPIO_TypeDef* port = gpio_port_map[pin / 16];
uint16_t pin_num = gpio_pin_map[pin % 16];
HAL_GPIO_WritePin(port, pin_num, (level == GPIO_LEVEL_HIGH) ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

gpio_level_t hal_gpio_read(gpio_pin_t pin) {
GPIO_TypeDef* port = gpio_port_map[pin / 16];
uint16_t pin_num = gpio_pin_map[pin % 16];
GPIO_PinState state = HAL_GPIO_ReadPin(port, pin_num);
return (state == GPIO_PIN_SET) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW;
}

hal_timer.h

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

typedef enum {
TIMER_1,
TIMER_2,
// ... 更多定时器定义
TIMER_MAX
} timer_id_t;

// 初始化定时器,单位 us
void hal_timer_init(timer_id_t timer_id, uint32_t period_us);

// 启动定时器
void hal_timer_start(timer_id_t timer_id);

// 停止定时器
void hal_timer_stop(timer_id_t timer_id);

// 设置定时器回调函数
void hal_timer_set_callback(timer_id_t timer_id, void (*callback)(void));

#endif // HAL_TIMER_H

hal_timer.c (假设基于STM32 HAL库)

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
#include "hal_timer.h"
#include "stm32fxxx_hal.h" // 假设使用 STM32 HAL 库

TIM_HandleTypeDef htim_map[] = { /* ... 定时器句柄数组 */ }; // 根据实际 MCU 定时器定义
void (*timer_callback_map[TIMER_MAX])(void) = {NULL}; // 定时器回调函数数组

void hal_timer_init(timer_id_t timer_id, uint32_t period_us) {
TIM_HandleTypeDef *htim = &htim_map[timer_id];
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};

htim->Instance = TIM1 + timer_id; // 假设定时器 ID 和 STM32 TIM 编号对应
htim->Init.Prescaler = SystemCoreClock / 1000000 - 1; // 预分频,使计数器时钟为 1MHz
htim->Init.CounterMode = TIM_COUNTERMODE_UP;
htim->Init.Period = period_us - 1;
htim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim->Init.RepetitionCounter = 0;
htim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
HAL_TIM_ConfigClockSource(htim, &sClockSourceConfig);
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(htim, &sMasterConfig);
HAL_TIM_PeriodElapsedCallback(htim); // 注册中断回调函数 (需要在 STM32 HAL 库中断处理中调用)
if (HAL_TIM_Base_Init(htim) != HAL_OK) {
// 错误处理
}
}

void hal_timer_start(timer_id_t timer_id) {
HAL_TIM_Base_Start_IT(&htim_map[timer_id]); // 启动定时器中断
}

void hal_timer_stop(timer_id_t timer_id) {
HAL_TIM_Base_Stop_IT(&htim_map[timer_id]);
}

void hal_timer_set_callback(timer_id_t timer_id, void (*callback)(void)) {
timer_callback_map[timer_id] = callback;
}

// STM32 HAL 库定时器中断回调函数 (需要在 stm32fxxx_it.c 中实现)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
for (int i = 0; i < TIMER_MAX; ++i) {
if (htim->Instance == (TIM1 + i)) { // 假设定时器 ID 和 STM32 TIM 编号对应
if (timer_callback_map[i] != NULL) {
timer_callback_map[i](); // 调用用户注册的回调函数
}
break;
}
}
}

hal_motor.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
#ifndef HAL_MOTOR_H
#define HAL_MOTOR_H

typedef enum {
MOTOR_1,
MOTOR_2,
// ... 更多电机定义
MOTOR_MAX
} motor_id_t;

typedef enum {
MOTOR_DIRECTION_CW, // 顺时针
MOTOR_DIRECTION_CCW // 逆时针
} motor_direction_t;

// 初始化电机驱动
void hal_motor_init(motor_id_t motor_id);

// 控制电机步进
void hal_motor_step(motor_id_t motor_id, int steps, motor_direction_t direction);

// 设置电机速度 (步/秒)
void hal_motor_set_speed(motor_id_t motor_id, uint32_t speed);

#endif // HAL_MOTOR_H

hal_motor.c (假设使用步进电机和 GPIO 控制)

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
#include "hal_motor.h"
#include "hal_gpio.h"
#include "hal_timer.h"

// 电机控制引脚定义 (根据实际硬件连接修改)
gpio_pin_t motor_pin_map[MOTOR_MAX][4] = {
{GPIO_PIN_X1, GPIO_PIN_X2, GPIO_PIN_X3, GPIO_PIN_X4}, // MOTOR_1 引脚
{GPIO_PIN_Y1, GPIO_PIN_Y2, GPIO_PIN_Y3, GPIO_PIN_Y4}, // MOTOR_2 引脚
// ... 更多电机引脚
};

// 步进电机步序 (根据实际电机和驱动器修改)
const uint8_t step_sequence[4][4] = {
{1, 0, 0, 0}, // 步序 1
{0, 1, 0, 0}, // 步序 2
{0, 0, 1, 0}, // 步序 3
{0, 0, 0, 1} // 步序 4
};

uint32_t motor_speed_map[MOTOR_MAX] = {0}; // 电机速度 (步/秒)
timer_id_t motor_timer_map[MOTOR_MAX] = {TIMER_MAX}; // 电机定时器 ID

void hal_motor_init(motor_id_t motor_id) {
for (int i = 0; i < 4; ++i) {
hal_gpio_init(motor_pin_map[motor_id][i], GPIO_MODE_OUTPUT); // 初始化电机控制引脚为输出
}
motor_speed_map[motor_id] = 100; // 默认速度 100 步/秒
motor_timer_map[motor_id] = TIMER_1 + motor_id; // 假设电机 ID 和定时器 ID 对应
}

void hal_motor_step(motor_id_t motor_id, int steps, motor_direction_t direction) {
if (steps <= 0) return;

uint32_t step_delay_us = 1000000 / motor_speed_map[motor_id]; // 计算步进延时 (us)
static int current_step[MOTOR_MAX] = {0}; // 当前步序

for (int step_count = 0; step_count < steps; ++step_count) {
if (direction == MOTOR_DIRECTION_CW) {
current_step[motor_id] = (current_step[motor_id] + 1) % 4; // 顺时针步进
} else { // MOTOR_DIRECTION_CCW
current_step[motor_id] = (current_step[motor_id] + 3) % 4; // 逆时针步进 (等价于 -1 % 4)
}

for (int i = 0; i < 4; ++i) {
hal_gpio_write(motor_pin_map[motor_id][i], (step_sequence[current_step[motor_id]][i]) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // 设置步序
}
hal_delay_us(step_delay_us); // 延时
}
}

void hal_motor_set_speed(motor_id_t motor_id, uint32_t speed) {
motor_speed_map[motor_id] = speed;
}

// 简单的 us 延时函数 (实际应用中建议使用更精确的定时器或 RTOS 延时)
void hal_delay_us(uint32_t us) {
volatile uint32_t count = us * (SystemCoreClock / 1000000 / 3); // 粗略估算,需根据实际 MCU 时钟频率调整
while (count--);
}

2. 设备驱动层

led_driver.h

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

typedef enum {
LED_RED,
LED_GREEN,
LED_BLUE,
// ... 更多 LED 定义
LED_MAX
} led_id_t;

// 初始化 LED 驱动
void led_driver_init(led_id_t led_id);

// 控制 LED 亮/灭
void led_driver_set_state(led_id_t led_id, bool on);

// 控制 LED 闪烁
void led_driver_blink(led_id_t led_id, uint32_t on_time_ms, uint32_t off_time_ms);

#endif // LED_DRIVER_H

led_driver.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
#include "led_driver.h"
#include "hal_gpio.h"
#include "hal_timer.h"

// LED 控制引脚定义 (根据实际硬件连接修改)
gpio_pin_t led_pin_map[LED_MAX] = {
GPIO_PIN_LED_RED,
GPIO_PIN_LED_GREEN,
GPIO_PIN_LED_BLUE,
// ... 更多 LED 引脚
};

bool led_state_map[LED_MAX] = {false}; // LED 当前状态
timer_id_t led_blink_timer_map[LED_MAX] = {TIMER_MAX}; // LED 闪烁定时器 ID
uint32_t led_on_time_map[LED_MAX] = {0}; // LED 闪烁亮时长
uint32_t led_off_time_map[LED_MAX] = {0}; // LED 闪烁灭时长

void led_driver_init(led_id_t led_id) {
hal_gpio_init(led_pin_map[led_id], GPIO_MODE_OUTPUT); // 初始化 LED 引脚为输出
led_driver_set_state(led_id, false); // 默认关闭 LED
led_blink_timer_map[led_id] = TIMER_2 + led_id; // 假设 LED ID 和定时器 ID 对应
}

void led_driver_set_state(led_id_t led_id, bool on) {
led_state_map[led_id] = on;
hal_gpio_write(led_pin_map[led_id], on ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW);
hal_timer_stop(led_blink_timer_map[led_id]); // 停止闪烁定时器
}

void led_driver_blink(led_id_t led_id, uint32_t on_time_ms, uint32_t off_time_ms) {
led_on_time_map[led_id] = on_time_ms;
led_off_time_map[led_id] = off_time_ms;
led_state_map[led_id] = true; // 初始状态为亮
hal_timer_init(led_blink_timer_map[led_id], on_time_ms * 1000); // 初始化闪烁定时器,单位 us
hal_timer_set_callback(led_blink_timer_map[led_id], led_blink_callback); // 设置闪烁回调函数
hal_timer_start(led_blink_timer_map[led_id]); // 启动闪烁定时器
hal_gpio_write(led_pin_map[led_id], GPIO_LEVEL_HIGH); // 初始点亮 LED
}

// LED 闪烁定时器回调函数
void led_blink_callback(void) {
for (int i = 0; i < LED_MAX; ++i) {
if (timer_callback_map[led_blink_timer_map[i]] == led_blink_callback) { // 找到对应的 LED
led_state_map[i] = !led_state_map[i]; // 状态反转
hal_gpio_write(led_pin_map[i], led_state_map[i] ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // 设置 LED 状态
hal_timer_stop(led_blink_timer_map[i]); // 停止当前定时器
hal_timer_init(led_blink_timer_map[i], led_state_map[i] ? led_off_time_map[i] * 1000 : led_on_time_map[i] * 1000); // 重新初始化定时器,切换亮灭时长
hal_timer_start(led_blink_timer_map[i]); // 启动定时器
break;
}
}
}

button_driver.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
#ifndef BUTTON_DRIVER_H
#define BUTTON_DRIVER_H

typedef enum {
BUTTON_1,
BUTTON_2,
// ... 更多按键定义
BUTTON_MAX
} button_id_t;

typedef enum {
BUTTON_EVENT_CLICK,
BUTTON_EVENT_DOUBLE_CLICK,
BUTTON_EVENT_LONG_PRESS
// ... 更多按键事件定义
} button_event_t;

// 初始化按键驱动
void button_driver_init(button_id_t button_id);

// 设置按键事件回调函数
void button_driver_set_event_callback(button_id_t button_id, void (*callback)(button_event_t event));

#endif // BUTTON_DRIVER_H

button_driver.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
#include "button_driver.h"
#include "hal_gpio.h"
#include "hal_timer.h"

// 按键引脚定义 (根据实际硬件连接修改)
gpio_pin_t button_pin_map[BUTTON_MAX] = {
GPIO_PIN_BUTTON_1,
GPIO_PIN_BUTTON_2,
// ... 更多按键引脚
};

void (*button_event_callback_map[BUTTON_MAX])(button_event_t event) = {NULL}; // 按键事件回调函数数组
timer_id_t button_debounce_timer_map[BUTTON_MAX] = {TIMER_MAX}; // 按键消抖定时器 ID
bool button_last_state_map[BUTTON_MAX] = {true}; // 按键上次状态 (默认释放状态为高电平)
uint32_t button_click_time_map[BUTTON_MAX] = {0}; // 按键单击开始时间

#define BUTTON_DEBOUNCE_TIME_MS 20 // 按键消抖时间 20ms
#define BUTTON_LONG_PRESS_TIME_MS 500 // 长按时间 500ms
#define BUTTON_DOUBLE_CLICK_INTERVAL_MS 300 // 双击间隔 300ms

void button_driver_init(button_id_t button_id) {
hal_gpio_init(button_pin_map[button_id], GPIO_MODE_INPUT); // 初始化按键引脚为输入
button_debounce_timer_map[button_id] = TIMER_3 + button_id; // 假设按键 ID 和定时器 ID 对应
}

void button_driver_set_event_callback(button_id_t button_id, void (*callback)(button_event_t event)) {
button_event_callback_map[button_id] = callback;
}

// 按键扫描任务 (建议周期性调用,例如 10ms 周期)
void button_scan_task(void) {
for (int i = 0; i < BUTTON_MAX; ++i) {
gpio_level_t current_level = hal_gpio_read(button_pin_map[i]); // 读取按键引脚电平

if (current_level != button_last_state_map[i]) { // 电平发生变化
button_last_state_map[i] = current_level; // 更新上次状态

if (current_level == GPIO_LEVEL_LOW) { // 按键按下 (假设按下为低电平)
hal_timer_init(button_debounce_timer_map[i], BUTTON_DEBOUNCE_TIME_MS * 1000); // 初始化消抖定时器
hal_timer_set_callback(button_debounce_timer_map[i], button_debounce_callback); // 设置消抖回调函数
hal_timer_start(button_debounce_timer_map[i]); // 启动消抖定时器
}
}
}
}

// 按键消抖定时器回调函数
void button_debounce_callback(void) {
for (int i = 0; i < BUTTON_MAX; ++i) {
if (timer_callback_map[button_debounce_timer_map[i]] == button_debounce_callback) { // 找到对应的按键
hal_timer_stop(button_debounce_timer_map[i]); // 停止消抖定时器

if (hal_gpio_read(button_pin_map[i]) == GPIO_LEVEL_LOW) { // 再次确认按键仍然按下
if (button_event_callback_map[i] != NULL) {
if (button_click_time_map[i] == 0) { // 第一次点击
button_click_time_map[i] = HAL_GetTick(); // 记录点击时间
hal_timer_init(button_debounce_timer_map[i], BUTTON_LONG_PRESS_TIME_MS * 1000); // 初始化长按定时器
hal_timer_set_callback(button_debounce_timer_map[i], button_long_press_callback); // 设置长按回调函数
hal_timer_start(button_debounce_timer_map[i]); // 启动长按定时器
} else { // 第二次点击
if (HAL_GetTick() - button_click_time_map[i] < BUTTON_DOUBLE_CLICK_INTERVAL_MS) { // 双击
button_event_callback_map[i](BUTTON_EVENT_DOUBLE_CLICK); // 触发双击事件
button_click_time_map[i] = 0; // 清空点击时间
} else { // 单击 (第一次点击超时)
button_event_callback_map[i](BUTTON_EVENT_CLICK); // 触发单击事件
button_click_time_map[i] = HAL_GetTick(); // 重新记录点击时间,准备下一次双击判断
hal_timer_init(button_debounce_timer_map[i], BUTTON_DOUBLE_CLICK_INTERVAL_MS * 1000); // 启动双击超时定时器
hal_timer_set_callback(button_debounce_timer_map[i], button_click_timeout_callback); // 设置双击超时回调函数
hal_timer_start(button_debounce_timer_map[i]); // 启动双击超时定时器
}
}
}
}
break;
}
}
}

// 按键长按定时器回调函数
void button_long_press_callback(void) {
for (int i = 0; i < BUTTON_MAX; ++i) {
if (timer_callback_map[button_debounce_timer_map[i]] == button_long_press_callback) { // 找到对应的按键
hal_timer_stop(button_debounce_timer_map[i]); // 停止长按定时器
if (button_event_callback_map[i] != NULL && hal_gpio_read(button_pin_map[i]) == GPIO_LEVEL_LOW) { // 再次确认按键仍然按下
button_event_callback_map[i](BUTTON_EVENT_LONG_PRESS); // 触发长按事件
}
button_click_time_map[i] = 0; // 清空点击时间
break;
}
}
}

// 按键单击超时定时器回调函数 (用于判断双击是否超时)
void button_click_timeout_callback(void) {
for (int i = 0; i < BUTTON_MAX; ++i) {
if (timer_callback_map[button_debounce_timer_map[i]] == button_click_timeout_callback) { // 找到对应的按键
hal_timer_stop(button_debounce_timer_map[i]); // 停止超时定时器
if (button_event_callback_map[i] != NULL) {
button_event_callback_map[i](BUTTON_EVENT_CLICK); // 触发单击事件 (之前第一次点击超时)
}
button_click_time_map[i] = 0; // 清空点击时间
break;
}
}
}

3. 系统服务层

task_scheduler.h

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

typedef void (*task_func_t)(void);

typedef struct {
task_func_t task_func;
uint32_t period_ms; // 任务周期 (ms)
uint32_t last_exec_time; // 上次执行时间 (ms)
} task_t;

// 初始化任务调度器
void task_scheduler_init(void);

// 添加任务
void task_scheduler_add_task(task_t *task);

// 任务调度运行 (需要在主循环中周期性调用)
void task_scheduler_run(void);

#endif // TASK_SCHEDULER_H

task_scheduler.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
#include "task_scheduler.h"
#include "hal_timer.h" // 可以使用定时器来实现更精确的调度

#define MAX_TASKS 10 // 最大任务数量
task_t task_list[MAX_TASKS];
uint8_t task_count = 0;

void task_scheduler_init(void) {
task_count = 0;
}

void task_scheduler_add_task(task_t *task) {
if (task_count < MAX_TASKS) {
task_list[task_count++] = *task;
task->last_exec_time = HAL_GetTick(); // 初始化上次执行时间
} else {
// 任务列表已满,错误处理
}
}

void task_scheduler_run(void) {
uint32_t current_time = HAL_GetTick();
for (int i = 0; i < task_count; ++i) {
if (current_time - task_list[i].last_exec_time >= task_list[i].period_ms) {
task_list[i].task_func(); // 执行任务
task_list[i].last_exec_time = current_time; // 更新上次执行时间
}
}
}

state_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
#ifndef STATE_MANAGER_H
#define STATE_MANAGER_H

typedef enum {
SYSTEM_STATE_IDLE,
SYSTEM_STATE_ROCKING_SLOW,
SYSTEM_STATE_ROCKING_FAST,
SYSTEM_STATE_ROCKING_RANDOM,
SYSTEM_STATE_PAUSED,
// ... 更多系统状态
SYSTEM_STATE_MAX
} system_state_t;

// 获取当前系统状态
system_state_t state_manager_get_current_state(void);

// 设置系统状态
void state_manager_set_state(system_state_t new_state);

// 系统状态改变事件回调函数类型
typedef void (*state_change_callback_t)(system_state_t old_state, system_state_t new_state);

// 注册系统状态改变事件回调函数
void state_manager_register_state_change_callback(state_change_callback_t callback);

#endif // STATE_MANAGER_H

state_manager.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "state_manager.h"

system_state_t current_state = SYSTEM_STATE_IDLE; // 初始状态为 IDLE
state_change_callback_t state_change_callback = NULL; // 状态改变回调函数

system_state_t state_manager_get_current_state(void) {
return current_state;
}

void state_manager_set_state(system_state_t new_state) {
if (new_state != current_state && new_state < SYSTEM_STATE_MAX) {
system_state_t old_state = current_state;
current_state = new_state;
if (state_change_callback != NULL) {
state_change_callback(old_state, new_state); // 调用状态改变回调函数
}
}
}

void state_manager_register_state_change_callback(state_change_callback_t callback) {
state_change_callback = callback;
}

4. 应用层

hu_tao_rocking_app.h

1
2
3
4
5
6
7
8
9
10
#ifndef HU_TAO_ROCKING_APP_H
#define HU_TAO_ROCKING_APP_H

// 初始化胡桃摇应用
void hu_tao_rocking_app_init(void);

// 胡桃摇应用主循环 (需要在 main 函数中调用)
void hu_tao_rocking_app_run(void);

#endif // HU_TAO_ROCKING_APP_H

hu_tao_rocking_app.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#include "hu_tao_rocking_app.h"
#include "led_driver.h"
#include "button_driver.h"
#include "motor_driver.h" // 假设设备驱动层封装了 motor_driver
#include "state_manager.h"
#include "task_scheduler.h"

#define MOTOR_ROCKING_MOTOR_ID MOTOR_1 // 假设摇动电机 ID 为 MOTOR_1
#define LED_STATUS_LED_ID LED_RED // 假设状态 LED ID 为 LED_RED
#define BUTTON_MODE_BUTTON_ID BUTTON_1 // 假设模式切换按键 ID 为 BUTTON_1

// 摇动速度参数 (可以根据实际模型调整)
#define ROCKING_SPEED_SLOW 50 // 慢速摇动速度 (步/秒)
#define ROCKING_SPEED_FAST 100 // 快速摇动速度 (步/秒)
#define ROCKING_SPEED_RANDOM_MIN 30 // 随机摇动最小速度 (步/秒)
#define ROCKING_SPEED_RANDOM_MAX 120 // 随机摇动最大速度 (步/秒)
#define ROCKING_ANGLE 45 // 摇动角度 (度) // 需要根据电机步距角和减速比计算步数

// 摇动模式定时器周期 (ms)
#define ROCKING_TIMER_PERIOD_MS 20 // 20ms 周期,控制摇动频率

// 摇动定时器 ID
#define ROCKING_TIMER_ID TIMER_4

// 摇动任务函数
void rocking_task(void);

// 系统状态改变回调函数
void system_state_change_handler(system_state_t old_state, system_state_t new_state);

// 模式切换按键事件回调函数
void mode_button_event_handler(button_event_t event);

// 摇动定时器回调函数
void rocking_timer_callback(void);

// 随机数生成函数 (简单伪随机数,实际应用中可以使用更安全的随机数生成器)
uint32_t simple_rand(void);

void hu_tao_rocking_app_init(void) {
led_driver_init(LED_STATUS_LED_ID); // 初始化状态 LED
button_driver_init(BUTTON_MODE_BUTTON_ID); // 初始化模式切换按键
motor_driver_init(MOTOR_ROCKING_MOTOR_ID); // 初始化摇动电机

button_driver_set_event_callback(BUTTON_MODE_BUTTON_ID, mode_button_event_handler); // 设置模式切换按键事件回调
state_manager_register_state_change_callback(system_state_change_handler); // 注册系统状态改变回调

// 初始化摇动定时器
hal_timer_init(ROCKING_TIMER_ID, ROCKING_TIMER_PERIOD_MS * 1000);
hal_timer_set_callback(ROCKING_TIMER_ID, rocking_timer_callback);

// 添加摇动任务到任务调度器
task_t rocking_task_def = {rocking_task, ROCKING_TIMER_PERIOD_MS, 0};
task_scheduler_add_task(&rocking_task_def);

state_manager_set_state(SYSTEM_STATE_IDLE); // 初始状态设置为 IDLE
}

void hu_tao_rocking_app_run(void) {
task_scheduler_run(); // 运行任务调度器
button_scan_task(); // 扫描按键 (周期性调用)
// ... 其他应用层周期性任务
}

// 摇动任务函数 (根据当前系统状态控制电机摇动)
void rocking_task(void) {
system_state_t current_state = state_manager_get_current_state();
motor_direction_t direction = MOTOR_DIRECTION_CW; // 默认顺时针
uint32_t speed = 0;

switch (current_state) {
case SYSTEM_STATE_ROCKING_SLOW:
speed = ROCKING_SPEED_SLOW;
break;
case SYSTEM_STATE_ROCKING_FAST:
speed = ROCKING_SPEED_FAST;
break;
case SYSTEM_STATE_ROCKING_RANDOM:
speed = ROCKING_SPEED_RANDOM_MIN + (simple_rand() % (ROCKING_SPEED_RANDOM_MAX - ROCKING_SPEED_RANDOM_MIN + 1)); // 随机速度
direction = (simple_rand() % 2 == 0) ? MOTOR_DIRECTION_CW : MOTOR_DIRECTION_CCW; // 随机方向
break;
case SYSTEM_STATE_IDLE:
case SYSTEM_STATE_PAUSED:
default:
speed = 0; // 停止摇动
break;
}

if (speed > 0) {
motor_driver_set_speed(MOTOR_ROCKING_MOTOR_ID, speed);
motor_driver_rock_angle(MOTOR_ROCKING_MOTOR_ID, ROCKING_ANGLE, direction); // 控制电机摇动一定角度 (需要实现 motor_driver_rock_angle 函数)
} else {
motor_driver_stop(MOTOR_ROCKING_MOTOR_ID); // 停止电机
}
}

// 系统状态改变回调函数 (根据状态更新 LED 指示)
void system_state_change_handler(system_state_t old_state, system_state_t new_state) {
switch (new_state) {
case SYSTEM_STATE_IDLE:
led_driver_set_state(LED_STATUS_LED_ID, false); // 熄灭 LED
break;
case SYSTEM_STATE_ROCKING_SLOW:
led_driver_blink(LED_STATUS_LED_ID, 500, 500); // 慢速闪烁
break;
case SYSTEM_STATE_ROCKING_FAST:
led_driver_blink(LED_STATUS_LED_ID, 200, 200); // 快速闪烁
break;
case SYSTEM_STATE_ROCKING_RANDOM:
led_driver_blink(LED_STATUS_LED_ID, 100, 900); // 随机闪烁
break;
case SYSTEM_STATE_PAUSED:
led_driver_set_state(LED_STATUS_LED_ID, true); // 常亮 LED
break;
default:
led_driver_set_state(LED_STATUS_LED_ID, false); // 默认熄灭 LED
break;
}
}

// 模式切换按键事件回调函数 (切换摇动模式)
void mode_button_event_handler(button_event_t event) {
if (event == BUTTON_EVENT_CLICK) { // 单击切换模式
system_state_t current_state = state_manager_get_current_state();
system_state_t next_state;

switch (current_state) {
case SYSTEM_STATE_IDLE:
next_state = SYSTEM_STATE_ROCKING_SLOW;
break;
case SYSTEM_STATE_ROCKING_SLOW:
next_state = SYSTEM_STATE_ROCKING_FAST;
break;
case SYSTEM_STATE_ROCKING_FAST:
next_state = SYSTEM_STATE_ROCKING_RANDOM;
break;
case SYSTEM_STATE_ROCKING_RANDOM:
next_state = SYSTEM_STATE_PAUSED;
break;
case SYSTEM_STATE_PAUSED:
default:
next_state = SYSTEM_STATE_IDLE;
break;
}
state_manager_set_state(next_state); // 设置新的系统状态
} else if (event == BUTTON_EVENT_LONG_PRESS) { // 长按进入 IDLE 状态
state_manager_set_state(SYSTEM_STATE_IDLE);
}
}

// 摇动定时器回调函数 (可以用于更精细的摇动控制,例如变速摇动)
void rocking_timer_callback(void) {
// ... 可以根据需要添加更复杂的摇动控制逻辑
}

// 简单伪随机数生成函数
uint32_t simple_rand(void) {
static uint32_t seed = 12345; // 初始种子
seed = (seed * 1103515245U + 12345U);
return seed;
}

main.c 主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "main.h" // 假设包含 HAL 库初始化等
#include "hu_tao_rocking_app.h"

int main(void) {
HAL_Init(); // 初始化 HAL 库
SystemClock_Config(); // 配置系统时钟 (根据具体 MCU 平台配置)
// ... 初始化其他外设,例如 UART 用于调试

task_scheduler_init(); // 初始化任务调度器
hu_tao_rocking_app_init(); // 初始化胡桃摇应用

while (1) {
hu_tao_rocking_app_run(); // 运行胡桃摇应用
// ... 其他全局任务,例如低功耗管理
}
}

测试验证与维护升级

测试验证:

  1. 单元测试: 针对HAL层、设备驱动层和系统服务层的各个模块进行单元测试,验证其功能的正确性。例如,测试GPIO驱动能否正确控制引脚电平,定时器驱动能否产生准确的定时中断,电机驱动能否按照指令步进等。

  2. 集成测试: 将各个模块组合起来进行集成测试,验证模块之间的协同工作是否正常。例如,测试按键驱动和状态管理器能否正确联动,电机驱动和摇动控制模块能否实现预期的摇动模式等。

  3. 系统测试: 对整个嵌入式系统进行全面的系统测试,验证其功能、性能、可靠性和稳定性是否满足需求。例如,测试在各种摇动模式下是否运行正常,长时间运行是否稳定,电源管理是否有效等。

  4. 用户体验测试: 邀请用户进行实际体验测试,收集用户反馈,进一步完善产品。

维护升级:

  1. 预留升级接口: 在硬件设计上预留固件升级接口,例如UART、USB等。在软件设计上,预留固件升级程序入口,方便进行固件升级。

  2. 版本控制: 使用版本控制系统(例如Git)管理代码,方便代码的版本管理、bug修复和功能迭代。

  3. 模块化设计: 采用模块化设计,方便对系统进行局部修改和升级,降低升级风险。

  4. 日志记录: 在系统中加入日志记录功能,方便在运行过程中记录系统状态和错误信息,便于问题排查和维护。

  5. 远程升级 (可选): 如果项目包含无线通信功能,可以考虑实现远程固件升级功能,方便用户在线升级。

总结

以上代码示例和架构设计,旨在提供一个可靠、高效、可扩展的“胡桃摇”嵌入式系统平台。这套分层架构结合事件驱动和状态机的设计模式,在嵌入式系统开发中被广泛应用,能够有效地组织代码,提高开发效率,并为未来的功能扩展和维护升级打下坚实的基础。

请注意,这只是一个代码框架和示例,实际项目开发中还需要根据具体的硬件平台、电机类型、传感器选择等进行详细的调整和完善。例如,电机驱动部分需要根据具体的步进电机驱动器型号和控制方式进行调整,定时器的配置需要根据MCU的时钟频率和精度要求进行调整,等等。

希望这份详细的解答能够帮助您完善“胡桃摇”项目,打造出一个有趣又技术含量十足的嵌入式产品! 如果您有任何其他问题或需要更深入的探讨,欢迎随时提出,我会尽力提供帮助。

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