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

本项目是一款低成本的触摸控制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. 技术选型与方法
本项目在技术选型和方法上,充分考虑了低成本、高性能和可靠性,并采用了经过实践验证的技术和方法:
4. 详细C代码实现
以下是基于上述架构和技术选型的详细C代码实现,代码将按照分层架构进行组织,并包含详细的注释。由于代码量较大,我将分模块展示,并提供完整的代码框架和关键模块的详细实现。
4.1 硬件层 (Hardware Layer) - hardware.h
和 hardware.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
| #ifndef HARDWARE_H #define HARDWARE_H
#include "stm32f0xx.h"
#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
#define TOUCH_I2C_PORT I2C1 #define TOUCH_I2C_ADDR 0x40
#define SYS_CLK_FREQ_HZ 48000000UL
void SystemClock_Config(void); void LED_GPIO_Init(void); void LED_SetBrightness(uint8_t brightness); 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.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
| #include "hardware.h"
void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
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; RCC_OscInitStruct.PLL.PREDIV = RCC_PREDIV_DIV1; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); }
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; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } }
void LED_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
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);
HAL_GPIO_WritePin(LED_GPIO_PORT, LED_GPIO_PIN, GPIO_PIN_RESET); }
void LED_SetBrightness(uint8_t brightness) { TIM_HandleTypeDef htim3; TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM3_CLK_ENABLE();
htim3.Instance = TIM3; htim3.Init.Prescaler = 48 - 1; htim3.Init.CounterMode = TIM_COUNTERMODE_UP; htim3.Init.Period = 100 - 1; 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; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); }
void Touch_INT_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
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);
HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_1_IRQn); }
void I2C1_Init(void) { I2C_HandleTypeDef hi2c1;
__HAL_RCC_I2C1_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; GPIO_InitStruct.Pull = GPIO_PULLUP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; GPIO_InitStruct.Alternate = GPIO_AF_1_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; 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(); } }
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); }
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); }
void Delay_ms(uint32_t ms) { HAL_Delay(ms); }
void Error_Handler(void) { while(1) { HAL_GPIO_TogglePin(LED_GPIO_PORT, LED_GPIO_PIN); HAL_Delay(100); } }
|
4.2 驱动层 (Driver Layer) - touch_driver.h
和 touch_driver.c
, led_driver.h
和 led_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
| #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.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
| #include "touch_driver.h"
#define SGL8022W_ADDR 0x40 #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;
void Touch_Driver_Init(void) { I2C1_Init(); Touch_INT_GPIO_Init();
}
TouchEventType Touch_Driver_GetEvent(void) { TouchEventType event = current_touch_event; current_touch_event = TOUCH_EVENT_NONE; return event; }
static uint8_t Touch_ReadStatus(void) { uint8_t status = 0; I2C1_Read(SGL8022W_ADDR, SGL8022W_REG_STATUS, &status, 1); return status; }
void EXTI0_1_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(TOUCH_INT_GPIO_PIN);
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) { 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) { current_touch_event = TOUCH_EVENT_SINGLE_CLICK; } else if (touch_duration >= 1000) { 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
| #ifndef LED_DRIVER_H #define LED_DRIVER_H
#include "hardware.h"
void LED_Driver_Init(void);
void LED_Driver_SetBrightness(uint8_t brightness);
void LED_Driver_TurnOn(void);
void LED_Driver_TurnOff(void);
#endif
|
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
| #include "led_driver.h"
void LED_Driver_Init(void) { LED_GPIO_Init(); }
void LED_Driver_SetBrightness(uint8_t brightness) { LED_SetBrightness(brightness); }
void LED_Driver_TurnOn(void) { LED_Driver_SetBrightness(100); }
void LED_Driver_TurnOff(void) { LED_Driver_SetBrightness(0); }
|
4.3 核心层 (Core Layer) - core.h
和 core.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
| #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.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
| #include "core.h"
static LightState current_light_state = LIGHT_STATE_OFF; static uint8_t current_brightness = 0;
void Core_Init(void) { Touch_Driver_Init(); LED_Driver_Init(); }
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; 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
| #include "main.h" #include "hardware.h" #include "core.h"
int main(void) { HAL_Init();
SystemClock_Config();
Core_Init();
LED_Driver_TurnOff();
while (1) { Core_Process(); Delay_ms(10); } }
void EXTI0_1_IRQHandler(void);
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
| #include "main.h" #include "stm32f0xx_it.h" #include "touch_driver.h"
void EXTI0_1_IRQHandler(void) {
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); Touch_Driver_IRQHandler(); }
|
注意:
- 上述代码框架提供了一个基本的触摸灯控制逻辑,实际应用中可能需要根据具体需求进行调整和完善。
- 代码中使用了 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 升级为例):
- 进入 Bootloader 模式: 可以通过特定的按键组合或软件命令进入 Bootloader 模式。
- 建立 UART 通信: 上位机软件通过 UART 接口与嵌入式系统建立通信。
- 传输固件数据: 上位机软件将新的固件数据通过 UART 接口传输到嵌入式系统。
- 固件擦除和写入: Bootloader 程序接收固件数据,并擦除 Flash 存储器中旧的固件,然后写入新的固件数据。
- 校验固件: Bootloader 程序对写入的固件数据进行校验,确保固件的完整性和正确性。
- 重启系统: 固件校验通过后,Bootloader 程序跳转到新的固件入口地址,重启系统,完成固件升级。
总结
本项目“低成本MOSS外形触摸灯”展示了一个完整的嵌入式系统开发流程,从需求分析、系统架构设计、技术选型、代码实现、测试验证到维护升级,涵盖了嵌入式系统开发的各个关键环节。通过采用分层架构、事件驱动架构、模块化编程、C语言开发等技术和方法,构建了一个可靠、高效、可扩展的触摸灯系统平台。 本项目代码提供了详细的注释和说明,力求清晰易懂,方便学习和参考。实际应用中,可以根据具体需求进行调整和扩展,例如添加更多灯光模式、更复杂的触摸交互、低功耗管理等功能,不断完善和优化产品。
希望这份详细的解答能够帮助您理解嵌入式系统开发流程和代码架构设计,并为您的项目开发提供参考。 如果您有任何进一步的问题或需要更深入的讨论,请随时提出。