编程技术分享

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

0%

简介:一款复刻游戏《OneShot》的大灯泡。希望大家能喜欢~

很高兴能与您一起探讨这个复刻《OneShot》大灯泡的嵌入式项目。这是一个非常有趣且具有实践意义的项目,它涵盖了嵌入式系统开发的完整生命周期。为了构建一个可靠、高效、可扩展的系统平台,我们需要深入分析需求,精心设计架构,并采用经过实践验证的技术和方法。
关注微信公众号,提前获取相关推文

项目需求分析

首先,我们需要明确项目的具体需求。虽然题目中提到的是“复刻游戏《OneShot》的大灯泡”,但我们需要更详细地了解其功能和特性。根据游戏《OneShot》的背景和灯泡的象征意义,我们可以推断出以下需求:

  1. 基本照明功能: 灯泡最基本的功能是发光,提供照明。我们需要控制灯泡的亮度,甚至可能需要控制灯泡的颜色(如果需要更高级的复刻效果)。
  2. 动画效果: 《OneShot》中的灯泡可能不仅仅是简单的发光,可能还具有一些动画效果,例如闪烁、呼吸灯、颜色渐变等。为了更逼真地复刻,我们需要考虑实现这些动画效果。
  3. 交互功能: 为了增加趣味性和互动性,我们可以考虑为灯泡添加一些交互功能。例如,可以通过按钮、触摸传感器、光线传感器等方式与灯泡进行交互,控制灯泡的开关、亮度、颜色或动画效果。
  4. 可靠性和稳定性: 作为一个嵌入式产品,可靠性和稳定性至关重要。我们需要确保灯泡能够长时间稳定运行,不易出现故障。
  5. 低功耗: 如果灯泡需要电池供电,或者对功耗有较高要求,我们需要考虑采用低功耗的设计方案。
  6. 可扩展性: 为了未来的功能扩展和升级,我们需要设计一个具有良好可扩展性的系统架构。
  7. 易维护性: 软件代码需要易于理解、维护和升级。我们需要采用清晰的代码结构、规范的编程风格和完善的文档。

系统架构设计

基于以上需求分析,我们设计一个分层式的嵌入式软件架构,这种架构具有良好的模块化、可扩展性和可维护性。我们的系统架构将分为以下几个层次:

  1. 硬件抽象层 (HAL, Hardware Abstraction Layer): HAL层是直接与硬件交互的层,它向上层提供统一的硬件接口,屏蔽底层硬件的差异。HAL层包括:

    • GPIO 驱动: 控制灯泡的开关、亮度、颜色等。
    • 定时器驱动: 实现动画效果、延时功能等。
    • ADC 驱动 (可选): 如果需要光线传感器,则需要 ADC 驱动。
    • 触摸传感器驱动 (可选): 如果使用触摸传感器进行交互,则需要触摸传感器驱动。
    • 通信接口驱动 (可选, 例如 UART, I2C, SPI): 用于调试、固件升级或与其他设备通信。
  2. 板级支持包 (BSP, Board Support Package): BSP层是针对特定硬件平台的底层软件支持,它初始化硬件平台,配置系统时钟、内存、中断等。BSP层通常由芯片厂商或开发板厂商提供,我们需要根据我们选择的硬件平台进行配置和定制。

  3. 设备驱动层: 设备驱动层构建在 HAL 层之上,它为上层提供更高级、更易用的设备接口。设备驱动层包括:

    • LED 驱动: 封装 LED 的控制逻辑,提供亮度调节、颜色控制、动画效果等接口。
    • 按钮驱动: 封装按钮的输入检测逻辑,提供按键事件处理接口。
    • 触摸传感器驱动 (可选): 封装触摸传感器的输入检测逻辑,提供触摸事件处理接口。
    • 光线传感器驱动 (可选): 封装光线传感器的读取逻辑,提供光照强度获取接口。
    • 动画效果管理器: 管理各种动画效果,例如闪烁、呼吸灯、颜色渐变等。
  4. 应用逻辑层: 应用逻辑层是系统的核心,它实现灯泡的具体功能和交互逻辑。应用逻辑层包括:

    • 状态机: 管理灯泡的各种状态,例如开/关状态、动画模式、亮度级别等。
    • 用户交互处理: 处理用户的输入,例如按钮按下、触摸事件等,并根据输入调整灯泡的状态和行为。
    • 动画效果控制: 根据当前状态和用户输入,控制动画效果管理器播放相应的动画。
    • 电源管理 (可选): 如果需要低功耗设计,则需要电源管理模块,控制系统的功耗模式。
  5. 接口层 (API, Application Programming Interface): 接口层定义了各个模块之间的交互接口,以及应用逻辑层对外提供的接口。良好的接口设计可以提高代码的模块化程度和可复用性。

代码实现 (C 语言)

下面我们用 C 语言来实现上述架构,并提供详细的代码注释。由于代码量较大,我们将分模块进行展示,并逐步完善。

1. HAL 层 (Hardware Abstraction Layer)

  • hal_gpio.h: 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
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

#include <stdint.h>
#include <stdbool.h>

// 定义 GPIO 引脚配置结构体
typedef struct {
uint32_t pin; // GPIO 引脚号
uint32_t mode; // GPIO 模式 (输入/输出/复用功能)
uint32_t pull; // 上拉/下拉/浮空
uint32_t speed; // 输出速度 (如果为输出模式)
} GPIO_InitTypeDef;

// GPIO 初始化函数
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct);

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(uint32_t pin, bool pinState);

// 读取 GPIO 引脚输入电平
bool HAL_GPIO_ReadPin(uint32_t pin);

#endif // HAL_GPIO_H
  • hal_gpio.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
#include "hal_gpio.h"

// 假设我们使用 STM32 微控制器,以下代码为示例,需要根据实际硬件平台修改
#include "stm32fxxx_hal.h" // 包含 STM32 HAL 库头文件 (根据具体型号选择)

// GPIO 初始化函数
void HAL_GPIO_Init(GPIO_InitTypeDef *GPIO_InitStruct) {
GPIO_InitTypeDef GPIO_Config;

GPIO_Config.Pin = GPIO_InitStruct->pin;
GPIO_Config.Mode = GPIO_InitStruct->mode;
GPIO_Config.Pull = GPIO_InitStruct->pull;
GPIO_Config.Speed = GPIO_InitStruct->speed;

HAL_GPIO_Init(GPIOA, &GPIO_Config); // 假设 GPIO 都在 GPIOA 端口,需要根据实际情况修改
}

// 设置 GPIO 引脚输出电平
void HAL_GPIO_WritePin(uint32_t pin, bool pinState) {
HAL_GPIO_WritePin(GPIOA, pin, pinState ? GPIO_PIN_SET : GPIO_PIN_RESET); // 假设 GPIO 都在 GPIOA 端口
}

// 读取 GPIO 引脚输入电平
bool HAL_GPIO_ReadPin(uint32_t pin) {
return HAL_GPIO_ReadPin(GPIOA, pin) == GPIO_PIN_SET; // 假设 GPIO 都在 GPIOA 端口
}
  • 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
24
25
26
27
28
#ifndef HAL_TIMER_H
#define HAL_TIMER_H

#include <stdint.h>

// 定义定时器配置结构体
typedef struct {
uint32_t timerInstance; // 定时器实例 (例如 TIM1, TIM2 等)
uint32_t prescaler; // 预分频系数
uint32_t period; // 计数周期
} TIMER_InitTypeDef;

// 定时器初始化函数
void HAL_TIMER_Init(TIMER_InitTypeDef *TIMER_InitStruct);

// 启动定时器
void HAL_TIMER_Start(uint32_t timerInstance);

// 停止定时器
void HAL_TIMER_Stop(uint32_t timerInstance);

// 设置定时器 PWM 输出占空比 (用于 LED 亮度调节)
void HAL_TIMER_PWM_SetDutyCycle(uint32_t timerInstance, uint32_t channel, uint32_t dutyCycle);

// 获取当前定时器计数值
uint32_t HAL_TIMER_GetCounter(uint32_t timerInstance);

#endif // HAL_TIMER_H
  • hal_timer.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
#include "hal_timer.h"

// 假设我们使用 STM32 微控制器,以下代码为示例,需要根据实际硬件平台修改
#include "stm32fxxx_hal.h" // 包含 STM32 HAL 库头文件 (根据具体型号选择)

// 定时器初始化函数
void HAL_TIMER_Init(TIMER_InitTypeDef *TIMER_InitStruct) {
TIM_HandleTypeDef htim;

htim.Instance = TIMER_InitStruct->timerInstance;
htim.Init.Prescaler = TIMER_InitStruct->prescaler;
htim.Init.CounterMode = TIM_COUNTERMODE_UP;
htim.Init.Period = TIMER_InitStruct->period;
htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;

HAL_TIM_Base_Init(&htim); // 初始化定时器基准
HAL_TIM_PWM_Init(&htim); // 初始化 PWM 功能 (如果需要 PWM 输出)
}

// 启动定时器
void HAL_TIMER_Start(uint32_t timerInstance) {
HAL_TIM_Base_Start(timerInstance);
}

// 停止定时器
void HAL_TIMER_Stop(uint32_t timerInstance) {
HAL_TIM_Base_Stop(timerInstance);
}

// 设置定时器 PWM 输出占空比 (用于 LED 亮度调节)
void HAL_TIMER_PWM_SetDutyCycle(uint32_t timerInstance, uint32_t channel, uint32_t dutyCycle) {
TIM_OC_InitTypeDef sConfigOC;

sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = dutyCycle; // 设置占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(timerInstance, &sConfigOC, channel);
HAL_TIM_PWM_Start(timerInstance, channel); // 启动 PWM 输出
}

// 获取当前定时器计数值
uint32_t HAL_TIMER_GetCounter(uint32_t timerInstance) {
return __HAL_TIM_GET_COUNTER(timerInstance);
}

2. BSP 层 (Board Support Package)

  • bsp.h: BSP 头文件
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
#ifndef BSP_H
#define BSP_H

#include <stdint.h>

// 系统时钟配置 (根据实际硬件平台修改)
#define SYS_CLOCK_FREQ_HZ 72000000 // 72MHz 系统时钟频率

// LED 相关引脚定义 (根据实际硬件连接修改)
#define LED_PIN_RED GPIO_PIN_0
#define LED_PIN_GREEN GPIO_PIN_1
#define LED_PIN_BLUE GPIO_PIN_2

// 按钮相关引脚定义 (根据实际硬件连接修改)
#define BUTTON_PIN GPIO_PIN_13

// 定时器实例定义 (根据实际硬件平台修改)
#define TIMER_INSTANCE_LED_PWM TIM1 // 用于 LED PWM 调光的定时器

// 初始化 BSP
void BSP_Init(void);

// 初始化 LED 相关硬件
void BSP_LED_Init(void);

// 初始化按钮相关硬件
void BSP_Button_Init(void);

// 获取系统时钟频率
uint32_t BSP_GetSysClockFreq(void);

#endif // BSP_H
  • bsp.c: BSP 源文件 (示例代码,需要根据具体的硬件平台进行修改)
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 "bsp.h"
#include "hal_gpio.h"
#include "hal_timer.h"

// 初始化 BSP
void BSP_Init(void) {
// 初始化系统时钟 (示例代码,需要根据实际硬件平台修改)
// SystemClock_Config(); // 调用系统时钟配置函数 (通常由芯片厂商提供)

// 初始化 LED 相关硬件
BSP_LED_Init();

// 初始化按钮相关硬件
BSP_Button_Init();

// ... 其他硬件初始化 ...
}

// 初始化 LED 相关硬件
void BSP_LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
TIMER_InitTypeDef TIMER_InitStruct;

// 初始化 LED 引脚为 PWM 输出模式
GPIO_InitStruct.pin = LED_PIN_RED | LED_PIN_GREEN | LED_PIN_BLUE;
GPIO_InitStruct.mode = GPIO_MODE_AF_PP; // 复用推挽输出
GPIO_InitStruct.pull = GPIO_NOPULL;
GPIO_InitStruct.speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(&GPIO_InitStruct);

// 初始化定时器用于 PWM 输出
TIMER_InitStruct.timerInstance = TIMER_INSTANCE_LED_PWM;
TIMER_InitStruct.prescaler = (BSP_GetSysClockFreq() / 1000000) - 1; // 预分频,使定时器频率为 1MHz
TIMER_InitStruct.period = 1000; // PWM 周期为 1ms (1kHz PWM 频率)
HAL_TIMER_Init(&TIMER_InitStruct);
}

// 初始化按钮相关硬件
void BSP_Button_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;

// 初始化按钮引脚为输入模式,并启用上拉
GPIO_InitStruct.pin = BUTTON_PIN;
GPIO_InitStruct.mode = GPIO_MODE_INPUT;
GPIO_InitStruct.pull = GPIO_PULLUP;
HAL_GPIO_Init(&GPIO_InitStruct);
}

// 获取系统时钟频率
uint32_t BSP_GetSysClockFreq(void) {
return SYS_CLOCK_FREQ_HZ;
}

3. 设备驱动层 (Device Driver Layer)

  • led_driver.h: LED 驱动头文件
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

#include <stdint.h>

// 初始化 LED 驱动
void LED_Driver_Init(void);

// 设置 LED 亮度 (0-100%)
void LED_Driver_SetBrightness(uint8_t brightness);

// 设置 LED 颜色 (RGB 颜色值)
void LED_Driver_SetColor(uint8_t red, uint8_t green, uint8_t blue);

// 关闭所有 LED
void LED_Driver_Off(void);

// 开启所有 LED 并设置为白色
void LED_Driver_White(void);

#endif // LED_DRIVER_H
  • led_driver.c: LED 驱动源文件
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
#include "led_driver.h"
#include "bsp.h"
#include "hal_timer.h"

// 初始化 LED 驱动
void LED_Driver_Init(void) {
BSP_LED_Init(); // 初始化 LED 相关硬件
}

// 设置 LED 亮度 (0-100%)
void LED_Driver_SetBrightness(uint8_t brightness) {
uint32_t dutyCycle = (brightness * 1000) / 100; // 计算 PWM 占空比 (0-1000)

// 设置 RGB LED 的亮度 (假设 RGB LED 连接到定时器的不同通道)
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_1, dutyCycle); // 红色 LED (通道 1)
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_2, dutyCycle); // 绿色 LED (通道 2)
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_3, dutyCycle); // 蓝色 LED (通道 3)
}

// 设置 LED 颜色 (RGB 颜色值)
void LED_Driver_SetColor(uint8_t red, uint8_t green, uint8_t blue) {
// 将 RGB 颜色值转换为 PWM 占空比 (假设 LED 为共阳极,占空比越大亮度越低)
uint32_t redDutyCycle = 1000 - ((red * 1000) / 255);
uint32_t greenDutyCycle = 1000 - ((green * 1000) / 255);
uint32_t blueDutyCycle = 1000 - ((blue * 1000) / 255);

// 设置 RGB LED 的颜色
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_1, redDutyCycle); // 红色 LED (通道 1)
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_2, greenDutyCycle); // 绿色 LED (通道 2)
HAL_TIMER_PWM_SetDutyCycle(TIMER_INSTANCE_LED_PWM, TIM_CHANNEL_3, blueDutyCycle); // 蓝色 LED (通道 3)
}

// 关闭所有 LED
void LED_Driver_Off(void) {
LED_Driver_SetColor(0, 0, 0); // 设置为黑色,即关闭所有 LED
}

// 开启所有 LED 并设置为白色
void LED_Driver_White(void) {
LED_Driver_SetColor(255, 255, 255); // 设置为白色
}
  • button_driver.h: 按钮驱动头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#ifndef BUTTON_DRIVER_H
#define BUTTON_DRIVER_H

#include <stdint.h>
#include <stdbool.h>

// 定义按键事件类型
typedef enum {
BUTTON_EVENT_NONE, // 无事件
BUTTON_EVENT_PRESSED, // 按下事件
BUTTON_EVENT_RELEASED, // 释放事件
BUTTON_EVENT_LONG_PRESSED // 长按事件
} ButtonEventType;

// 初始化按钮驱动
void Button_Driver_Init(void);

// 获取按键事件
ButtonEventType Button_Driver_GetEvent(void);

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

#define BUTTON_DEBOUNCE_TIME_MS 50 // 按键消抖时间 (50ms)
#define BUTTON_LONG_PRESS_TIME_MS 1000 // 长按时间 (1 秒)

static ButtonEventType currentEvent = BUTTON_EVENT_NONE; // 当前按键事件
static bool buttonPressed = false; // 按键是否被按下
static uint32_t lastPressTime = 0; // 上次按键按下时间

// 初始化按钮驱动
void Button_Driver_Init(void) {
BSP_Button_Init(); // 初始化按钮相关硬件
}

// 获取按键事件
ButtonEventType Button_Driver_GetEvent(void) {
ButtonEventType event = currentEvent;
currentEvent = BUTTON_EVENT_NONE; // 清空事件

// 读取按键状态
bool buttonState = !HAL_GPIO_ReadPin(BUTTON_PIN); // 假设按钮按下时引脚为低电平

// 按键消抖和事件检测
if (buttonState && !buttonPressed) { // 按键按下
if (HAL_TIMER_GetCounter(HAL_TIMER_GET_SYSTICK_INSTANCE()) - lastPressTime > BUTTON_DEBOUNCE_TIME_MS) {
buttonPressed = true;
lastPressTime = HAL_TIMER_GetCounter(HAL_TIMER_GET_SYSTICK_INSTANCE());
currentEvent = BUTTON_EVENT_PRESSED;
}
} else if (!buttonState && buttonPressed) { // 按键释放
buttonPressed = false;
currentEvent = BUTTON_EVENT_RELEASED;
} else if (buttonPressed) { // 按键持续按下
if (HAL_TIMER_GetCounter(HAL_TIMER_GET_SYSTICK_INSTANCE()) - lastPressTime > BUTTON_LONG_PRESS_TIME_MS) {
currentEvent = BUTTON_EVENT_LONG_PRESSED;
buttonPressed = false; // 长按事件只触发一次
}
}

return event;
}
  • animation_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
#ifndef ANIMATION_MANAGER_H
#define ANIMATION_MANAGER_H

#include <stdint.h>
#include <stdbool.h>

// 定义动画效果类型
typedef enum {
ANIMATION_NONE, // 无动画
ANIMATION_BLINK, // 闪烁
ANIMATION_BREATHING, // 呼吸灯
ANIMATION_COLOR_FADE // 颜色渐变
} AnimationType;

// 初始化动画效果管理器
void Animation_Manager_Init(void);

// 设置当前动画效果
void Animation_Manager_SetAnimation(AnimationType animationType);

// 更新动画效果 (在定时器中断或主循环中周期性调用)
void Animation_Manager_Update(void);

#endif // ANIMATION_MANAGER_H
  • animation_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
#include "animation_manager.h"
#include "led_driver.h"
#include "hal_timer.h"

#define BLINK_INTERVAL_MS 500 // 闪烁间隔 (500ms)
#define BREATHING_PERIOD_MS 2000 // 呼吸灯周期 (2 秒)
#define COLOR_FADE_PERIOD_MS 5000 // 颜色渐变周期 (5 秒)

static AnimationType currentAnimation = ANIMATION_NONE; // 当前动画效果
static uint32_t lastAnimationUpdateTime = 0; // 上次动画更新时间

// 初始化动画效果管理器
void Animation_Manager_Init(void) {
currentAnimation = ANIMATION_NONE;
}

// 设置当前动画效果
void Animation_Manager_SetAnimation(AnimationType animationType) {
currentAnimation = animationType;
}

// 更新动画效果 (在定时器中断或主循环中周期性调用)
void Animation_Manager_Update(void) {
uint32_t currentTime = HAL_TIMER_GetCounter(HAL_TIMER_GET_SYSTICK_INSTANCE());

switch (currentAnimation) {
case ANIMATION_BLINK:
if (currentTime - lastAnimationUpdateTime > BLINK_INTERVAL_MS) {
lastAnimationUpdateTime = currentTime;
static bool ledState = false;
ledState = !ledState;
if (ledState) {
LED_Driver_White();
} else {
LED_Driver_Off();
}
}
break;

case ANIMATION_BREATHING:
if (currentTime - lastAnimationUpdateTime > 20) { // 每 20ms 更新一次亮度
lastAnimationUpdateTime = currentTime;
uint32_t timeInPeriod = (currentTime % BREATHING_PERIOD_MS);
uint8_t brightness = (uint8_t)((100.0 * sin(2 * M_PI * timeInPeriod / BREATHING_PERIOD_MS - M_PI / 2) + 100) / 2); // 计算呼吸灯亮度
LED_Driver_SetBrightness(brightness);
}
break;

case ANIMATION_COLOR_FADE:
if (currentTime - lastAnimationUpdateTime > 50) { // 每 50ms 更新一次颜色
lastAnimationUpdateTime = currentTime;
uint32_t timeInPeriod = (currentTime % COLOR_FADE_PERIOD_MS);
uint8_t red = (uint8_t)(127.5 * sin(2 * M_PI * timeInPeriod / COLOR_FADE_PERIOD_MS) + 127.5);
uint8_t green = (uint8_t)(127.5 * sin(2 * M_PI * timeInPeriod / COLOR_FADE_PERIOD_MS + 2 * M_PI / 3) + 127.5);
uint8_t blue = (uint8_t)(127.5 * sin(2 * M_PI * timeInPeriod / COLOR_FADE_PERIOD_MS + 4 * M_PI / 3) + 127.5);
LED_Driver_SetColor(red, green, blue);
}
break;

case ANIMATION_NONE:
default:
break;
}
}

4. 应用逻辑层 (Application Logic Layer)

  • app_logic.h: 应用逻辑层头文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef APP_LOGIC_H
#define APP_LOGIC_H

#include <stdint.h>
#include <stdbool.h>

// 灯泡状态类型
typedef enum {
LAMP_STATE_OFF,
LAMP_STATE_ON,
LAMP_STATE_ANIMATION
} LampStateType;

// 初始化应用逻辑
void App_Logic_Init(void);

// 运行应用逻辑 (在主循环中周期性调用)
void App_Logic_Run(void);

#endif // APP_LOGIC_H
  • app_logic.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
#include "app_logic.h"
#include "led_driver.h"
#include "button_driver.h"
#include "animation_manager.h"

static LampStateType currentLampState = LAMP_STATE_OFF; // 当前灯泡状态
static AnimationType currentAnimationMode = ANIMATION_NONE; // 当前动画模式

// 初始化应用逻辑
void App_Logic_Init(void) {
LED_Driver_Init();
Button_Driver_Init();
Animation_Manager_Init();

currentLampState = LAMP_STATE_OFF;
LED_Driver_Off(); // 初始状态关闭灯泡
}

// 运行应用逻辑 (在主循环中周期性调用)
void App_Logic_Run(void) {
ButtonEventType buttonEvent = Button_Driver_GetEvent();

switch (currentLampState) {
case LAMP_STATE_OFF:
if (buttonEvent == BUTTON_EVENT_PRESSED) {
currentLampState = LAMP_STATE_ON;
LED_Driver_White(); // 开启灯泡
}
break;

case LAMP_STATE_ON:
if (buttonEvent == BUTTON_EVENT_PRESSED) {
currentLampState = LAMP_STATE_ANIMATION;
currentAnimationMode = ANIMATION_BLINK; // 切换到闪烁动画
Animation_Manager_SetAnimation(currentAnimationMode);
} else if (buttonEvent == BUTTON_EVENT_LONG_PRESSED) {
currentLampState = LAMP_STATE_OFF;
LED_Driver_Off(); // 关闭灯泡
}
break;

case LAMP_STATE_ANIMATION:
if (buttonEvent == BUTTON_EVENT_PRESSED) {
if (currentAnimationMode == ANIMATION_BLINK) {
currentAnimationMode = ANIMATION_BREATHING; // 切换到呼吸灯动画
} else if (currentAnimationMode == ANIMATION_BREATHING) {
currentAnimationMode = ANIMATION_COLOR_FADE; // 切换到颜色渐变动画
} else if (currentAnimationMode == ANIMATION_COLOR_FADE) {
currentAnimationMode = ANIMATION_NONE; // 关闭动画
currentLampState = LAMP_STATE_ON; // 恢复常亮状态
LED_Driver_White();
}
Animation_Manager_SetAnimation(currentAnimationMode);
} else if (buttonEvent == BUTTON_EVENT_LONG_PRESSED) {
currentLampState = LAMP_STATE_OFF;
LED_Driver_Off(); // 关闭灯泡
}
break;

default:
break;
}

// 更新动画效果 (如果当前状态为动画模式)
if (currentLampState == LAMP_STATE_ANIMATION) {
Animation_Manager_Update();
}
}

5. 主函数 (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
#include "bsp.h"
#include "app_logic.h"
#include "hal_timer.h" // 为了使用 HAL_TIMER_GET_SYSTICK_INSTANCE()

int main(void) {
// 初始化 BSP (包括硬件初始化)
BSP_Init();

// 初始化应用逻辑
App_Logic_Init();

// 初始化 SysTick 定时器 (用于时间管理,例如延时、按键消抖等)
HAL_TIMER_InitSysTick(1000); // 初始化 SysTick 定时器,中断频率为 1kHz (1ms 中断周期)

while (1) {
// 运行应用逻辑 (状态机处理、用户交互、动画更新等)
App_Logic_Run();

// 可以添加延时,降低 CPU 占用率 (可选)
// HAL_Delay(10);
}
}

// SysTick 定时器中断处理函数 (示例,需要根据实际硬件平台和 HAL 库进行配置)
void SysTick_Handler(void) {
HAL_IncTick(); // HAL 库提供的 SysTick 计数器递增函数
// 在这里可以添加需要在 SysTick 中断中处理的任务 (例如,定时任务、RTOS 的时间片轮转等)
}

编译和构建

将以上代码文件 (hal_gpio.h, hal_gpio.c, hal_timer.h, hal_timer.c, bsp.h, bsp.c, led_driver.h, led_driver.c, button_driver.h, button_driver.c, animation_manager.h, animation_manager.c, app_logic.h, app_logic.c, main.c) 添加到您的嵌入式开发项目中,并根据您选择的硬件平台和开发工具链进行编译和构建。

测试和验证

在代码编译完成后,将固件烧录到您的嵌入式设备上,进行测试和验证。

  1. 基本功能测试: 测试灯泡的开关功能、亮度调节功能、颜色控制功能是否正常工作。
  2. 动画效果测试: 测试各种动画效果 (闪烁、呼吸灯、颜色渐变) 是否流畅、自然。
  3. 交互功能测试: 测试按钮的按下、释放、长按事件是否能正确触发,并根据用户输入进行相应的响应。
  4. 可靠性和稳定性测试: 让灯泡长时间运行,观察是否出现异常或故障。
  5. 功耗测试 (如果需要): 使用功耗测试仪测量灯泡在不同状态下的功耗。

维护和升级

为了方便后续的维护和升级,我们应该:

  1. 代码注释: 在代码中添加详细的注释,方便理解代码逻辑。
  2. 文档编写: 编写项目文档,包括需求分析文档、设计文档、用户手册等。
  3. 版本控制: 使用版本控制系统 (例如 Git) 管理代码,方便代码的版本管理和协作开发。
  4. 固件升级机制: 预留固件升级接口 (例如 UART, USB 等),方便后续固件升级。

技术和方法总结

在这个项目中,我们采用了以下技术和方法:

  1. 分层式架构: 将系统划分为 HAL 层、BSP 层、设备驱动层、应用逻辑层和接口层,提高了代码的模块化程度、可扩展性和可维护性。
  2. 硬件抽象层 (HAL): 屏蔽底层硬件的差异,使上层代码可以独立于具体的硬件平台。
  3. 设备驱动: 封装硬件设备的控制逻辑,提供易用的设备接口。
  4. 状态机: 使用状态机管理灯泡的各种状态,使系统逻辑清晰、易于理解。
  5. 事件驱动编程: 使用事件驱动编程处理用户输入,提高系统响应速度和效率。
  6. 定时器和 PWM: 使用定时器实现动画效果和 LED 亮度调节。
  7. C 语言编程: 使用 C 语言进行嵌入式软件开发,C 语言具有高效、灵活、可移植性好等优点,是嵌入式系统开发中最常用的编程语言。
  8. 模块化设计: 将系统划分为多个模块,每个模块负责特定的功能,提高了代码的可复用性和可维护性。
  9. 代码规范: 遵循良好的代码规范,例如命名规范、注释规范、代码风格等,提高代码的可读性和可维护性。
  10. 测试驱动开发: 在开发过程中进行充分的测试和验证,确保系统的可靠性和稳定性。

总结

这个复刻《OneShot》大灯泡的嵌入式项目,从需求分析到系统实现,再到测试验证和维护升级,涵盖了嵌入式系统开发的完整流程。我们通过精心设计的软件架构和经过实践验证的技术方法,构建了一个可靠、高效、可扩展的系统平台。希望这个详细的说明和代码示例能够帮助您理解嵌入式系统开发的过程,并为您的项目提供参考。 这个项目是一个很好的实践案例,可以帮助您提升嵌入式软件开发技能。 如果您在实际开发过程中遇到任何问题,欢迎随时向我咨询。

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