编程技术分享

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

0%

简介:低成本MOSS外形触摸灯 (SGL8022W)**

关注微信公众号,提前获取相关推文

本项目是一款低成本的触摸控制LED灯,外形设计灵感来源于MOSS,旨在提供一种简洁、互动性强的照明解决方案。核心控制芯片选用SGL8022W触摸感应芯片,这是一款专为触摸控制应用设计的低功耗、高性能芯片。

1. 需求分析

在项目初期,需求分析是至关重要的环节,它直接决定了系统的功能和性能。对于这款MOSS外形触摸灯,我们可以归纳出以下核心需求:

  • 功能需求:

    • 触摸开关灯: 用户通过触摸灯的特定区域,实现灯的开关功能。
    • 亮度调节: 通过长按或连续触摸等方式,实现灯光亮度的多级调节。
    • 低功耗: 在待机状态下,系统功耗要尽可能低,延长使用寿命。
    • 稳定可靠: 系统运行稳定可靠,避免误触和功能失效。
    • MOSS外形: 产品外观需贴合MOSS的造型设计,具有一定的美观性和辨识度。
  • 非功能需求:

    • 低成本: 项目成本控制是关键,需要选用经济高效的元器件和方案。
    • 易于生产: 设计应考虑生产制造的便利性,降低生产难度。
    • 快速响应: 触摸操作响应迅速,用户体验流畅。
    • 可扩展性: 系统设计应具备一定的可扩展性,方便后续功能升级和优化。
    • 安全性: 产品使用安全可靠,符合相关安全标准。

2. 系统架构设计

为了实现上述需求,并确保系统的可靠性、高效性和可扩展性,我将采用分层架构事件驱动架构相结合的设计模式。

2.1 分层架构

分层架构将系统划分为不同的层次,每一层只与相邻层交互,降低了系统各部分之间的耦合度,提高了代码的可维护性和可复用性。本项目可以划分为以下层次:

  • 硬件层 (Hardware Layer):

    • 负责直接与硬件交互,包括MCU (微控制器)、SGL8022W触摸芯片、LED驱动电路、电源管理电路等。
    • 提供底层的硬件驱动接口,例如GPIO控制、SPI/I2C通信、PWM输出等。
  • 驱动层 (Driver Layer):

    • 封装硬件层的接口,提供更高级、易用的驱动函数,例如触摸驱动、LED驱动、电源管理驱动等。
    • 负责硬件的初始化、配置和控制,向上层提供统一的API接口。
  • 核心层 (Core Layer):

    • 实现系统的核心逻辑,包括触摸事件处理、亮度控制算法、状态管理、任务调度等。
    • 负责接收驱动层传递的硬件事件,并根据事件类型执行相应的操作。
  • 应用层 (Application Layer):

    • 构建用户应用界面和交互逻辑,例如灯光模式切换、用户配置管理等(本项目应用层相对简单,主要体现在触摸灯的控制逻辑上)。
    • 基于核心层提供的接口,实现具体的功能和特性。

2.2 事件驱动架构

事件驱动架构是一种异步编程模型,系统对外部事件做出响应。在本系统中,触摸事件是主要的驱动事件。当SGL8022W检测到触摸动作时,会产生中断或状态变化,驱动层捕获这些事件并传递给核心层,核心层根据事件类型和当前系统状态,触发相应的处理函数。

2.3 架构优势

  • 模块化: 分层架构将系统分解为独立的模块,每个模块负责特定的功能,提高了代码的可维护性和可读性。
  • 可复用性: 驱动层和核心层的代码可以在不同的项目和平台之间复用,降低开发成本和周期。
  • 可扩展性: 系统架构易于扩展和修改,方便添加新的功能或优化现有功能。
  • 解耦合: 各层之间通过明确的接口进行交互,降低了模块之间的依赖性,提高了系统的稳定性。
  • 高效性: 事件驱动架构能够及时响应外部事件,提高系统的实时性和响应速度。

3. 技术选型与方法

本项目在技术选型和方法上,充分考虑了低成本、高性能和可靠性,并采用了经过实践验证的技术和方法:

  • 微控制器 (MCU): 选用STM32F0系列的MCU,例如STM32F030C8T6。

    • 理由:
      • 低成本: STM32F0系列是ST公司推出的入门级32位MCU,价格低廉,适合低成本应用。
      • 高性能: 基于ARM Cortex-M0内核,性能足以满足触摸灯的控制需求。
      • 低功耗: 具有多种低功耗模式,适合电池供电应用。
      • 丰富的外设: 集成GPIO、SPI、I2C、PWM、ADC等丰富的外设,满足项目需求。
      • 成熟的生态: 拥有完善的开发工具链和丰富的技术文档,方便开发和调试。
  • 触摸芯片: 选用SGL8022W 触摸感应芯片。

    • 理由:
      • 专为触摸应用设计: 高性能、高灵敏度、抗干扰能力强。
      • 低功耗: 工作电流极低,适合低功耗应用。
      • 易于使用: 接口简单,易于集成到系统中。
      • 成本低: 价格适中,符合低成本项目需求。
  • LED驱动: 采用PWM调光技术。

    • 理由:
      • 线性调光: 通过改变PWM占空比,实现LED亮度的线性调节,调光效果平滑自然。
      • 高效节能: PWM调光效率高,功耗低。
      • 易于实现: MCU的PWM外设可以直接控制LED的亮度。
  • 软件开发方法:

    • C语言: 选用C语言作为主要的开发语言,C语言效率高、可移植性好,是嵌入式系统开发的首选语言。
    • 模块化编程: 采用模块化编程思想,将系统分解为多个模块,提高代码的可维护性和可复用性。
    • 事件驱动编程: 采用事件驱动编程模型,提高系统的实时性和响应速度。
    • 状态机: 使用状态机管理系统的不同状态,简化逻辑,提高代码的可读性和可维护性。
    • 代码版本控制: 使用Git进行代码版本控制,方便代码管理和团队协作。
    • 代码注释: 编写清晰详细的代码注释,提高代码的可读性和可维护性。

4. 详细C代码实现

以下是基于上述架构和技术选型的详细C代码实现,代码将按照分层架构进行组织,并包含详细的注释。由于代码量较大,我将分模块展示,并提供完整的代码框架和关键模块的详细实现。

4.1 硬件层 (Hardware Layer) - hardware.hhardware.c

hardware.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
// hardware.h
#ifndef HARDWARE_H
#define HARDWARE_H

#include "stm32f0xx.h" // 根据实际MCU型号选择头文件

// ** GPIO 定义 **
#define LED_GPIO_PORT GPIOA
#define LED_GPIO_PIN GPIO_PIN_5
#define TOUCH_INT_GPIO_PORT GPIOB
#define TOUCH_INT_GPIO_PIN GPIO_PIN_0

// ** SGL8022W 相关定义 **
#define TOUCH_I2C_PORT I2C1
#define TOUCH_I2C_ADDR 0x40 // SGL8022W 的 I2C 地址 (需要根据实际芯片配置)

// ** 系统时钟定义 (假设使用外部高速晶振 8MHz, PLL 48MHz)**
#define SYS_CLK_FREQ_HZ 48000000UL

// ** 函数声明 **
void SystemClock_Config(void);
void LED_GPIO_Init(void);
void LED_SetBrightness(uint8_t brightness); // 0-100 表示亮度百分比
void Touch_INT_GPIO_Init(void);
void I2C1_Init(void);
HAL_StatusTypeDef I2C1_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size);
HAL_StatusTypeDef I2C1_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size);
void Delay_ms(uint32_t ms);

#endif // HARDWARE_H

hardware.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
// hardware.c
#include "hardware.h"

// 系统时钟配置 (使用 HSE 晶振, PLL 倍频到 48MHz)
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL6; // 8MHz * 6 = 48MHz
RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler(); // 错误处理函数 (需要用户实现)
}

/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK) {
Error_Handler(); // 错误处理函数 (需要用户实现)
}

PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_I2C1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI; // I2C1 时钟源选择 HSI
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) {
Error_Handler(); // 错误处理函数 (需要用户实现)
}
}

// LED GPIO 初始化
void LED_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOA_CLK_ENABLE();

// 配置 LED GPIO
GPIO_InitStruct.Pin = LED_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_GPIO_PORT, &GPIO_InitStruct);

// 默认关闭 LED
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_PIN_RESET);
}

// LED 亮度设置 (PWM 调光)
void LED_SetBrightness(uint8_t brightness) {
TIM_HandleTypeDef htim3; // 假设使用 TIM3 PWM 输出
TIM_OC_InitTypeDef sConfigOC = {0};

// 使能 TIM3 时钟 (如果还未使能)
__HAL_RCC_TIM3_CLK_ENABLE();

htim3.Instance = TIM3;
htim3.Init.Prescaler = 48 - 1; // 预分频系数,使计数器频率为 1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 100 - 1; // PWM 周期为 100 (可以根据需要调整)
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_PWM_Init(&htim3);

sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = brightness; // 设置占空比,brightness 范围 0-100
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1); // 假设 LED 连接到 TIM3_CH1

HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动 PWM 输出
}

// 触摸中断 GPIO 初始化
void Touch_INT_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOB_CLK_ENABLE();

// 配置触摸中断 GPIO
GPIO_InitStruct.Pin = TOUCH_INT_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
HAL_GPIO_Init(TOUCH_INT_GPIO_PORT, &GPIO_InitStruct);

// 使能 EXTI 中断并设置优先级
HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(EXTI0_1_IRQn); // 使能中断
}

// I2C1 初始化 (用于与 SGL8022W 通信)
void I2C1_Init(void) {
I2C_HandleTypeDef hi2c1;

__HAL_RCC_I2C1_CLK_ENABLE(); // 使能 I2C1 时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能 GPIOB 时钟 (假设 I2C1_SCL/SDA 使用 PB6/PB7)

GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // 开漏输出,用于 I2C
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF_1_I2C1; // 复用功能选择 I2C1
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

hi2c1.Instance = I2C1;
hi2c1.Init.ClockSpeed = 100000; // 100kHz 标准模式
hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.OwnAddress2 = 0;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
if (HAL_I2C_Init(&hi2c1) != HAL_OK) {
Error_Handler(); // 错误处理函数 (需要用户实现)
}
}

// I2C1 写数据函数
HAL_StatusTypeDef I2C1_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size) {
return HAL_I2C_Mem_Write( &hi2c1, (dev_addr << 1), reg_addr, I2C_MEMADD_SIZE_8BIT, data, size, 1000); // 超时时间 1000ms
}

// I2C1 读数据函数
HAL_StatusTypeDef I2C1_Read(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t size) {
return HAL_I2C_Mem_Read( &hi2c1, (dev_addr << 1), reg_addr, I2C_MEMADD_SIZE_8BIT, data, size, 1000); // 超时时间 1000ms
}

// 毫秒级延时函数 (基于 SysTick)
void Delay_ms(uint32_t ms) {
HAL_Delay(ms); // 使用 HAL 库提供的延时函数
}

// 错误处理函数 (需要用户实现)
void Error_Handler(void) {
// 在此处添加错误处理代码,例如 LED 闪烁、重启系统等
while(1) {
// 错误指示,例如快速闪烁 LED
HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN);
HAL_Delay(100);
}
}

4.2 驱动层 (Driver Layer) - touch_driver.htouch_driver.c, led_driver.hled_driver.c

触摸驱动 - touch_driver.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// touch_driver.h
#ifndef TOUCH_DRIVER_H
#define TOUCH_DRIVER_H

#include "hardware.h"

// 触摸事件类型定义
typedef enum {
TOUCH_EVENT_NONE,
TOUCH_EVENT_SINGLE_CLICK,
TOUCH_EVENT_LONG_PRESS,
TOUCH_EVENT_DOUBLE_CLICK, // 可选,根据需要添加
// ... 可扩展更多触摸事件类型
} TouchEventType;

// 触摸驱动初始化
void Touch_Driver_Init(void);

// 获取触摸事件
TouchEventType Touch_Driver_GetEvent(void);

#endif // TOUCH_DRIVER_H

触摸驱动 - touch_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
// touch_driver.c
#include "touch_driver.h"

#define SGL8022W_ADDR 0x40 // SGL8022W I2C 地址
#define SGL8022W_REG_STATUS 0x00 // 状态寄存器地址

static volatile TouchEventType current_touch_event = TOUCH_EVENT_NONE;
static uint32_t last_touch_time = 0;
static uint8_t touch_state = 0; // 0: 未触摸, 1: 触摸中

// SGL8022W 初始化
void Touch_Driver_Init(void) {
I2C1_Init(); // 初始化 I2C1
Touch_INT_GPIO_Init(); // 初始化触摸中断 GPIO

// 初始化 SGL8022W (可以根据 SGL8022W 数据手册配置寄存器,例如灵敏度等)
// 这里假设使用默认配置,实际应用中可能需要根据需求进行配置
}

// 获取触摸事件
TouchEventType Touch_Driver_GetEvent(void) {
TouchEventType event = current_touch_event;
current_touch_event = TOUCH_EVENT_NONE; // 清空事件标志
return event;
}

// 读取 SGL8022W 状态寄存器
static uint8_t Touch_ReadStatus(void) {
uint8_t status = 0;
I2C1_Read(SGL8022W_ADDR, SGL8022W_REG_STATUS, &status, 1);
return status;
}

// 触摸中断处理函数 (需要在 stm32f0xx_it.c 中实现 EXTI0_1_IRQHandler)
void EXTI0_1_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(TOUCH_INT_GPIO_PIN); // 调用 HAL 库中断处理函数

if (__HAL_GPIO_EXTI_GET_IT(TOUCH_INT_GPIO_PIN) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(TOUCH_INT_GPIO_PIN); // 清除中断标志

uint8_t current_state = Touch_ReadStatus();

if (current_state & 0x01) { // 检测到触摸 (假设 SGL8022W 状态寄存器 bit0 为触摸标志)
if (touch_state == 0) { // 之前未触摸,现在开始触摸
touch_state = 1;
last_touch_time = HAL_GetTick(); // 记录触摸开始时间
}
} else { // 未检测到触摸
if (touch_state == 1) { // 之前触摸中,现在释放触摸
touch_state = 0;
uint32_t touch_duration = HAL_GetTick() - last_touch_time;

if (touch_duration < 500) { // 短按 (500ms 阈值可调整)
current_touch_event = TOUCH_EVENT_SINGLE_CLICK;
} else if (touch_duration >= 1000) { // 长按 (1000ms 阈值可调整)
current_touch_event = TOUCH_EVENT_LONG_PRESS;
}
}
}
}
}

LED 驱动 - led_driver.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// led_driver.h
#ifndef LED_DRIVER_H
#define LED_DRIVER_H

#include "hardware.h"

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

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

// 开灯
void LED_Driver_TurnOn(void);

// 关灯
void LED_Driver_TurnOff(void);

#endif // LED_DRIVER_H

LED 驱动 - 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
// led_driver.c
#include "led_driver.h"

// LED 驱动初始化
void LED_Driver_Init(void) {
LED_GPIO_Init(); // 初始化 LED GPIO
}

// 设置 LED 亮度
void LED_Driver_SetBrightness(uint8_t brightness) {
LED_SetBrightness(brightness); // 调用硬件层函数设置亮度
}

// 开灯 (最大亮度)
void LED_Driver_TurnOn(void) {
LED_Driver_SetBrightness(100);
}

// 关灯 (亮度 0)
void LED_Driver_TurnOff(void) {
LED_Driver_SetBrightness(0);
}

4.3 核心层 (Core Layer) - core.hcore.c

核心层头文件 - core.h

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

#include "touch_driver.h"
#include "led_driver.h"

// 灯光状态类型
typedef enum {
LIGHT_STATE_OFF,
LIGHT_STATE_LOW,
LIGHT_STATE_MEDIUM,
LIGHT_STATE_HIGH,
// 可扩展更多状态
} LightState;

// 系统核心初始化
void Core_Init(void);

// 系统主循环处理函数
void Core_Process(void);

#endif // CORE_H

核心层实现 - core.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
// core.c
#include "core.h"

static LightState current_light_state = LIGHT_STATE_OFF;
static uint8_t current_brightness = 0; // 当前亮度值 (0-100)

// 系统核心初始化
void Core_Init(void) {
Touch_Driver_Init(); // 初始化触摸驱动
LED_Driver_Init(); // 初始化 LED 驱动
}

// 设置灯光状态和亮度
static void SetLightState(LightState state) {
current_light_state = state;
switch (state) {
case LIGHT_STATE_OFF:
current_brightness = 0;
LED_Driver_SetBrightness(current_brightness);
break;
case LIGHT_STATE_LOW:
current_brightness = 30; // 低亮度
LED_Driver_SetBrightness(current_brightness);
break;
case LIGHT_STATE_MEDIUM:
current_brightness = 60; // 中亮度
LED_Driver_SetBrightness(current_brightness);
break;
case LIGHT_STATE_HIGH:
current_brightness = 100; // 高亮度
LED_Driver_SetBrightness(current_brightness);
break;
default:
break;
}
}

// 系统主循环处理函数
void Core_Process(void) {
TouchEventType event = Touch_Driver_GetEvent();

switch (event) {
case TOUCH_EVENT_SINGLE_CLICK:
// 单击事件处理:切换灯光状态
if (current_light_state == LIGHT_STATE_OFF) {
SetLightState(LIGHT_STATE_LOW); // 默认从低亮度开始
} else if (current_light_state < LIGHT_STATE_HIGH) {
SetLightState(current_light_state + 1); // 切换到下一个亮度级别
} else {
SetLightState(LIGHT_STATE_OFF); // 最高亮度后切换到关闭
}
break;

case TOUCH_EVENT_LONG_PRESS:
// 长按事件处理:循环调节亮度 (可选功能)
if (current_light_state != LIGHT_STATE_OFF) {
if (current_brightness < 100) {
current_brightness += 10; // 每次长按亮度增加 10% (可调整)
if (current_brightness > 100) current_brightness = 100;
} else {
current_brightness = 10; // 到达最高亮度后,从最低亮度开始循环
}
LED_Driver_SetBrightness(current_brightness);
}
break;

default:
break; // 无触摸事件
}

// 其他核心逻辑可以添加到此处,例如定时任务、低功耗管理等
}

4.4 应用层 (Application Layer) - main.c

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
// main.c
#include "main.h"
#include "hardware.h"
#include "core.h"

int main(void) {
// HAL 库初始化
HAL_Init();

// 系统时钟配置
SystemClock_Config();

// 核心层初始化 (包含触摸和 LED 驱动初始化)
Core_Init();

// 初始灯光状态为关闭
LED_Driver_TurnOff();

// 主循环
while (1) {
Core_Process(); // 核心层处理函数,处理触摸事件和灯光控制
// 其他应用层逻辑可以添加到此处
Delay_ms(10); // 适当延时,降低 CPU 占用
}
}

// 中断回调函数 (需要在 stm32f0xx_it.c 中实现,这里只给出函数声明)
void EXTI0_1_IRQHandler(void); // 触摸中断处理函数声明 (在 touch_driver.c 中实现)

// 错误处理回调函数 (在 hardware.c 中实现)
void Error_Handler(void);

4.5 stm32f0xx_it.c (中断服务例程)

需要在 stm32f0xx_it.c 文件中添加触摸中断服务例程,并调用 touch_driver.c 中定义的 EXTI0_1_IRQHandler 函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// stm32f0xx_it.c
#include "main.h"
#include "stm32f0xx_it.h"
#include "touch_driver.h" // 包含触摸驱动头文件

// EXTI0_1 中断服务例程
void EXTI0_1_IRQHandler(void)
{
/* USER CODE BEGIN EXTI0_1_IRQn 0 */

/* USER CODE END EXTI0_1_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); // 假设触摸中断引脚为 GPIO_PIN_0 (需要根据实际配置修改)
/* USER CODE BEGIN EXTI0_1_IRQn 1 */
Touch_Driver_IRQHandler(); // 调用 touch_driver.c 中实现的触摸中断处理函数
/* USER CODE END EXTI0_1_IRQn 1 */
}

注意:

  • 上述代码框架提供了一个基本的触摸灯控制逻辑,实际应用中可能需要根据具体需求进行调整和完善。
  • 代码中使用了 HAL 库,需要根据实际的 STM32 开发环境进行配置和编译。
  • 代码中的 SGL8022W 寄存器地址和触摸事件判断逻辑是假设的,需要参考 SGL8022W 的数据手册进行配置和实现。
  • 错误处理函数 Error_Handler() 需要根据实际需求进行完善,例如添加错误日志、重启系统等功能。
  • stm32f0xx_it.c 文件是 STM32CubeIDE 等开发工具自动生成的,需要根据实际项目进行修改和添加中断服务例程。

5. 测试与验证

在代码开发完成后,需要进行全面的测试和验证,以确保系统的功能和性能符合需求。测试阶段可以分为以下几个方面:

  • 单元测试: 对每个模块(例如触摸驱动、LED驱动、核心逻辑)进行单独测试,验证模块的功能是否正常。可以使用单元测试框架(例如 CMocka)编写测试用例,自动化测试过程。
  • 集成测试: 将各个模块组合起来进行测试,验证模块之间的接口和协同工作是否正常。例如,测试触摸驱动和核心逻辑的集成,验证触摸事件是否能够正确触发灯光状态的切换。
  • 系统测试: 对整个系统进行全面测试,验证系统的功能、性能、稳定性、可靠性等是否符合需求。系统测试可以包括:
    • 功能测试: 测试触摸开关灯、亮度调节等基本功能是否正常。
    • 性能测试: 测试触摸响应速度、亮度调节平滑度、功耗等性能指标是否满足要求。
    • 稳定性测试: 长时间运行系统,观察系统是否出现异常或崩溃。
    • 可靠性测试: 进行各种异常情况测试,例如电源波动、干扰等,验证系统的抗干扰能力和容错能力。
    • 用户体验测试: 邀请用户体验产品,收集用户反馈,评估产品的易用性和用户满意度。

测试工具和方法:

  • 万用表: 测量电压、电流等参数,用于功耗测试和电路故障排查。
  • 示波器: 观察信号波形,用于调试硬件接口和分析信号时序。
  • 逻辑分析仪: 抓取和分析数字信号,用于调试 I2C 通信等数字接口。
  • 调试器 (例如 ST-Link): 在线调试代码,查看变量值、单步执行、断点调试等,用于软件调试。
  • 测试用例: 编写详细的测试用例,覆盖各种功能和场景,确保测试的全面性。
  • 自动化测试脚本: 编写自动化测试脚本,提高测试效率和重复性。

6. 维护与升级

嵌入式系统的维护和升级是产品生命周期中不可或缺的环节。为了方便后续的维护和升级,需要在系统设计阶段就考虑以下方面:

  • 模块化设计: 模块化设计可以方便代码的维护和升级,当需要修改或添加功能时,只需要修改相应的模块,而不会影响其他模块。
  • 清晰的代码注释: 编写清晰详细的代码注释,方便后续开发人员理解和维护代码。
  • 预留升级接口: 在硬件设计上预留升级接口(例如 UART、USB 等),方便进行固件升级。
  • 固件升级方案: 设计可靠的固件升级方案,例如 OTA (Over-The-Air) 在线升级或 UART 离线升级。
  • 版本控制: 使用代码版本控制工具(例如 Git)管理代码,方便代码的版本管理和回溯。
  • 日志记录: 添加必要的日志记录功能,方便在产品运行过程中记录错误信息和运行状态,用于故障排查和性能分析。

固件升级流程 (以 UART 升级为例):

  1. 进入 Bootloader 模式: 可以通过特定的按键组合或软件命令进入 Bootloader 模式。
  2. 建立 UART 通信: 上位机软件通过 UART 接口与嵌入式系统建立通信。
  3. 传输固件数据: 上位机软件将新的固件数据通过 UART 接口传输到嵌入式系统。
  4. 固件擦除和写入: Bootloader 程序接收固件数据,并擦除 Flash 存储器中旧的固件,然后写入新的固件数据。
  5. 校验固件: Bootloader 程序对写入的固件数据进行校验,确保固件的完整性和正确性。
  6. 重启系统: 固件校验通过后,Bootloader 程序跳转到新的固件入口地址,重启系统,完成固件升级。

总结

本项目“低成本MOSS外形触摸灯”展示了一个完整的嵌入式系统开发流程,从需求分析、系统架构设计、技术选型、代码实现、测试验证到维护升级,涵盖了嵌入式系统开发的各个关键环节。通过采用分层架构、事件驱动架构、模块化编程、C语言开发等技术和方法,构建了一个可靠、高效、可扩展的触摸灯系统平台。 本项目代码提供了详细的注释和说明,力求清晰易懂,方便学习和参考。实际应用中,可以根据具体需求进行调整和扩展,例如添加更多灯光模式、更复杂的触摸交互、低功耗管理等功能,不断完善和优化产品。

希望这份详细的解答能够帮助您理解嵌入式系统开发流程和代码架构设计,并为您的项目开发提供参考。 如果您有任何进一步的问题或需要更深入的讨论,请随时提出。

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