编程技术分享

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

0%

中文固件多管型 Wi-Fi 盖革计数器(MWGC-2T)是一款用于测量电离辐射的嵌入式设备。它采用多根盖革计数管以提高探测效率和精度,具备 Wi-Fi 连接功能,可以将数据上传至云端或本地服务器,并提供中文固件界面,方便国内用户使用。

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

2. 需求分析:

  • 核心功能:
    • 辐射探测: 使用多根盖革计数管探测电离辐射,并进行计数。
    • 数据处理: 将计数转换为剂量率 (µSv/hr, mR/hr)、累积剂量 (µSv, mR)、CPM (Counts Per Minute) 等辐射参数。
    • 数据显示: 在 LCD 屏幕上实时显示辐射参数、状态信息、设置菜单等中文界面。
    • Wi-Fi 通信: 连接 Wi-Fi 网络,将数据上传至服务器 (MQTT, HTTP 等协议可选),接收远程控制指令 (可选)。
    • 用户界面: 提供友好的中文操作界面,包括菜单导航、参数设置、数据显示等。
    • 报警功能: 当辐射剂量超过预设阈值时,发出声光报警。
    • 数据存储: 本地存储历史辐射数据,方便用户查询和分析。
    • 固件升级: 支持通过 Wi-Fi 或本地接口进行固件升级。
  • 性能需求:
    • 高精度: 准确测量辐射剂量率和累积剂量。
    • 高灵敏度: 能够探测到低剂量辐射。
    • 实时性: 实时显示辐射数据,响应用户操作。
    • 低功耗: 在电池供电模式下,具有较长的续航时间。
    • 稳定性: 系统运行稳定可靠,长时间工作无故障。
  • 可靠性需求:
    • 数据可靠性: 保证辐射数据的准确性和完整性。
    • 系统可靠性: 系统运行稳定,不易崩溃。
    • 通信可靠性: Wi-Fi 通信连接稳定,数据传输可靠。
  • 可扩展性需求:
    • 软件可扩展性: 代码架构易于扩展新功能,如支持更多传感器、通信协议、云平台等。
    • 硬件可扩展性: 预留硬件接口,方便未来扩展硬件模块,如 GPS 定位、蓝牙通信等。
  • 维护升级需求:
    • 固件可升级: 支持远程固件升级,方便 bug 修复和功能更新。
    • 日志记录: 记录系统运行日志,方便故障排查。
    • 远程维护: 通过 Wi-Fi 远程监控设备状态 (可选)。

系统架构设计

为了满足以上需求,我们采用分层架构设计,将系统划分为以下几个层次,每一层都有明确的功能和职责,层与层之间通过清晰的接口进行通信。这种架构具有良好的模块化、可维护性和可扩展性。

1. 硬件层 (Hardware Layer):

  • 核心处理器 (MCU): 选择高性能、低功耗的嵌入式微控制器,如 STM32 系列 (例如 STM32F4/F7/H7),ESP32 系列,或者其他 ARM Cortex-M 系列 MCU。 MCU 负责整个系统的控制、数据处理和外设驱动。
  • 盖革计数管 (Geiger Tubes): 多根盖革计数管并联或串联使用,提高探测灵敏度和效率。需要设计合适的信号调理电路,将盖革管产生的脉冲信号转换为 MCU 可识别的数字信号。
  • Wi-Fi 模块 (Wi-Fi Module): 集成 Wi-Fi 功能的模块,如 ESP32-WROOM-32 或其他 Wi-Fi 模块,负责 Wi-Fi 连接、数据传输和接收。
  • LCD 显示屏 (LCD Display): 用于显示辐射数据、用户界面和菜单的 LCD 屏幕,通常采用 SPI 或并行接口与 MCU 连接。
  • 蜂鸣器 (Buzzer): 用于报警提示,当辐射剂量超过阈值时发出声音报警。
  • LED 指示灯 (LED Indicators): 用于状态指示,例如电源指示、Wi-Fi 连接状态、报警状态等。
  • 按键 (Buttons): 用于用户交互,进行菜单导航、参数设置等操作。
  • 电源管理 (Power Management): 包括电源输入、稳压、电池充电管理等,确保系统供电稳定可靠,并实现低功耗管理。
  • 存储器 (Memory): 包括 Flash Memory (用于存储固件、配置参数、历史数据) 和 RAM (用于程序运行和数据缓存)。
  • 实时时钟 (RTC): 用于时间戳记录,记录辐射数据发生的时间。 (可选)
  • JTAG/SWD 调试接口 (Debug Interface): 用于程序调试和固件烧录。

2. 驱动层 (Driver Layer):

驱动层负责直接与硬件交互,提供上层软件访问硬件资源的接口。每个硬件模块都应有对应的驱动程序。

  • MCU 驱动 (MCU Driver): 包括 MCU 的基本配置 (时钟、GPIO、中断等)、外设初始化 (UART、SPI、I2C、ADC、Timer 等) 和底层硬件操作函数。
  • 盖革管驱动 (Geiger Tube Driver): 负责读取盖革管的脉冲信号,进行计数,并提供计数数据给上层应用。
  • Wi-Fi 模块驱动 (Wi-Fi Module Driver): 封装 Wi-Fi 模块的 API,提供 Wi-Fi 连接、数据发送和接收等功能。
  • LCD 驱动 (LCD Driver): 负责 LCD 的初始化、显示字符、显示图像、清屏等操作。
  • 蜂鸣器驱动 (Buzzer Driver): 控制蜂鸣器的开关。
  • LED 驱动 (LED Driver): 控制 LED 的亮灭。
  • 按键驱动 (Button Driver): 检测按键按下事件,并进行按键扫描和去抖动处理。
  • 电源管理驱动 (Power Management Driver): 控制电源模式切换,实现低功耗管理。
  • 存储器驱动 (Memory Driver): 提供 Flash 读写操作接口,用于存储配置参数和历史数据。
  • RTC 驱动 (RTC Driver): 读取和设置 RTC 时间。 (可选)

3. 中间件层 (Middleware Layer):

中间件层位于驱动层之上,提供通用的软件服务和组件,简化上层应用的开发。

  • RTOS (Real-Time Operating System): 选择一个合适的 RTOS,如 FreeRTOS, RT-Thread, uCOS 等,用于任务调度、资源管理、线程同步和通信,提高系统的实时性和并发性。
  • 文件系统 (File System): 如果需要存储大量历史数据或配置文件,可以使用文件系统,如 FATFS, LittleFS 等。
  • 网络协议栈 (Network Stack): TCP/IP 协议栈,用于 Wi-Fi 通信,例如 lwIP, FreeRTOS-Plus-TCP 等。
  • MQTT 客户端库 (MQTT Client Library): 如果使用 MQTT 协议上传数据,需要集成 MQTT 客户端库,如 Paho MQTT, MQTT-C 等。
  • JSON 解析库 (JSON Parsing Library): 如果使用 JSON 格式进行数据交换,需要集成 JSON 解析库,如 cJSON, jsmn 等。
  • UI 库 (UI Library): 用于简化用户界面开发,提供图形组件和界面管理功能,例如 LittlevGL (LVGL), emWin 等。 (如果资源有限,也可以直接操作 LCD 驱动实现简单的 UI)
  • 中文支持库 (Chinese Support Library): 提供中文编码支持,例如 GBK 或 UTF-8 编码支持,以及中文显示字体库。

4. 应用层 (Application Layer):

应用层是系统的核心层,实现盖革计数器的具体功能和业务逻辑。

  • 主任务 (Main Task): 系统的入口任务,负责系统初始化、任务创建和调度。
  • 盖革计数任务 (Geiger Counting Task): 负责读取盖革管计数数据,进行数据处理和转换,计算剂量率、累积剂量等参数。
  • 显示任务 (Display Task): 负责更新 LCD 显示,显示实时辐射数据、菜单界面、状态信息等。
  • Wi-Fi 通信任务 (Wi-Fi Communication Task): 负责 Wi-Fi 连接管理、数据上传和接收。
  • 用户界面任务 (UI Task): 处理用户按键操作,进行菜单导航、参数设置等。
  • 报警任务 (Alarm Task): 监测辐射剂量,当超过阈值时触发报警。
  • 数据存储任务 (Data Storage Task): 负责将历史辐射数据存储到 Flash 中。
  • 固件升级任务 (Firmware Update Task): 处理固件升级流程,包括接收固件、校验固件、更新固件等。
  • 配置管理模块 (Configuration Management Module): 负责读取和保存系统配置参数,例如 Wi-Fi 设置、报警阈值、显示设置等。
  • 系统监控模块 (System Monitoring Module): 监控系统状态,例如内存使用率、CPU 负载等。 (可选)
  • 日志记录模块 (Log Recording Module): 记录系统运行日志,方便故障排查。 (可选)

代码设计架构:基于 RTOS 的事件驱动架构

为了实现系统的实时性、并发性和可维护性,我们采用基于 RTOS 的事件驱动架构。

  • 任务 (Tasks): 系统功能划分为多个独立的任务,例如盖革计数任务、显示任务、Wi-Fi 通信任务等,每个任务负责特定的功能。RTOS 负责任务的调度和管理。
  • 事件队列 (Event Queues): 任务之间通过事件队列进行通信。当一个任务需要通知另一个任务发生某个事件时,就向事件队列发送一个事件消息。接收任务从事件队列中接收事件消息,并根据事件类型进行处理。
  • 消息队列 (Message Queues): 用于任务之间传递数据。一个任务可以将数据封装成消息发送到消息队列,另一个任务从消息队列中接收消息并解析数据。
  • 互斥锁 (Mutexes) 和信号量 (Semaphores): 用于任务同步和资源互斥访问。例如,多个任务需要访问共享资源 (如 LCD 显示屏) 时,可以使用互斥锁保护共享资源,避免数据竞争。

系统工作流程:

  1. 系统启动: MCU 初始化,RTOS 初始化,创建各个任务。
  2. 盖革计数任务: 循环读取盖革管计数,计算剂量率等参数,并将数据更新事件发送给显示任务和 Wi-Fi 通信任务。
  3. 显示任务: 接收到数据更新事件后,从共享数据区读取最新的辐射数据,更新 LCD 显示。同时,监听用户按键事件,处理菜单操作和参数设置。
  4. Wi-Fi 通信任务: 接收到数据更新事件后,从共享数据区读取辐射数据,并通过 Wi-Fi 将数据上传至服务器。同时,监听服务器下发的控制指令 (如果支持)。
  5. 用户界面任务: 处理用户按键事件,例如菜单导航、参数设置等,并将用户操作事件发送给相应的任务进行处理。
  6. 报警任务: 周期性检测辐射剂量,当超过阈值时,控制蜂鸣器和 LED 发出报警。
  7. 数据存储任务: 周期性将辐射数据存储到 Flash 中。
  8. 固件升级任务: 监听固件升级请求,接收固件数据,校验固件,并更新固件。

C 代码实现 (示例,约 3000+ 行)

为了满足 3000 行代码的要求,我们将提供尽可能详细的代码示例,涵盖系统架构中的各个层次和模块,并包含必要的注释。请注意,以下代码示例是基于 FreeRTOSSTM32F4 平台,您可以根据实际硬件平台和 RTOS 进行调整。

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
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
162
163
164
165
166
167
168
169
170
/* main.c */
#include "main.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#include "stdio.h"
#include "geiger_driver.h"
#include "lcd_driver.h"
#include "wifi_module.h"
#include "buzzer_driver.h"
#include "led_driver.h"
#include "button_driver.h"
#include "data_process.h"
#include "config_manager.h"
#include "display_task.h"
#include "geiger_task.h"
#include "wifi_task.h"
#include "ui_task.h"
#include "alarm_task.h"
#include "data_storage.h"
#include "firmware_update.h"

/* 任务句柄 */
TaskHandle_t xDisplayTaskHandle;
TaskHandle_t xGeigerTaskHandle;
TaskHandle_t xWifiTaskHandle;
TaskHandle_t xUITaskHandle;
TaskHandle_t xAlarmTaskHandle;
TaskHandle_t xDataStorageTaskHandle;
TaskHandle_t xFirmwareUpdateTaskHandle;

/* 事件队列句柄 */
QueueHandle_t xDataUpdateQueue;
QueueHandle_t xUIEventQueue;
QueueHandle_t xWifiEventQueue;
QueueHandle_t xAlarmEventQueue;
QueueHandle_t xFirmwareUpdateQueue;

/* 互斥锁句柄 (用于保护共享资源,例如辐射数据) */
SemaphoreHandle_t xRadiationDataMutex;

/* 全局辐射数据结构体 */
RadiationData_t g_RadiationData;

/* 系统初始化函数 */
void System_Init(void) {
HAL_Init(); // HAL 库初始化
SystemClock_Config(); // 系统时钟配置
GPIO_Init(); // GPIO 初始化
UART_Init(); // UART 初始化 (用于调试)
SPI_Init(); // SPI 初始化 (用于 LCD)
I2C_Init(); // I2C 初始化 (如果需要)
Timer_Init(); // 定时器初始化 (用于盖革计数)
ADC_Init(); // ADC 初始化 (如果需要)
LCD_Init(); // LCD 驱动初始化
Geiger_Driver_Init(); // 盖革管驱动初始化
WiFi_Module_Init(); // Wi-Fi 模块初始化
Buzzer_Driver_Init(); // 蜂鸣器驱动初始化
LED_Driver_Init(); // LED 驱动初始化
Button_Driver_Init(); // 按键驱动初始化
DataProcess_Init(); // 数据处理模块初始化
ConfigManager_Init(); // 配置管理模块初始化
DataStorage_Init(); // 数据存储模块初始化
FirmwareUpdate_Init(); // 固件升级模块初始化

// 初始化全局辐射数据
memset(&g_RadiationData, 0, sizeof(RadiationData_t));
g_RadiationData.doseRate = 0.0f;
g_RadiationData.cumulativeDose = 0.0f;
g_RadiationData.cpm = 0;
strcpy(g_RadiationData.status, "正常");

// 创建互斥锁
xRadiationDataMutex = xSemaphoreCreateMutex();
if (xRadiationDataMutex == NULL) {
printf("创建互斥锁失败!\r\n");
Error_Handler();
}

// 创建事件队列
xDataUpdateQueue = xQueueCreate(10, sizeof(DataUpdateEvent_t));
xUIEventQueue = xQueueCreate(10, sizeof(UIEvent_t));
xWifiEventQueue = xQueueCreate(10, sizeof(WifiEvent_t));
xAlarmEventQueue = xQueueCreate(10, sizeof(AlarmEvent_t));
xFirmwareUpdateQueue = xQueueCreate(5, sizeof(FirmwareUpdateEvent_t));

if (xDataUpdateQueue == NULL || xUIEventQueue == NULL || xWifiEventQueue == NULL || xAlarmEventQueue == NULL || xFirmwareUpdateQueue == NULL) {
printf("创建事件队列失败!\r\n");
Error_Handler();
}
}

/* 系统时钟配置 (示例,根据实际 MCU 和时钟源配置) */
void SystemClock_Config(void) {
// ... (省略 STM32 时钟配置代码,根据实际硬件平台配置) ...
}

/* GPIO 初始化 (示例) */
void GPIO_Init(void) {
// ... (省略 GPIO 初始化代码,根据实际硬件连接配置) ...
}

/* UART 初始化 (示例,用于调试) */
void UART_Init(void) {
// ... (省略 UART 初始化代码,配置串口参数) ...
}

/* SPI 初始化 (示例,用于 LCD) */
void SPI_Init(void) {
// ... (省略 SPI 初始化代码,配置 SPI 参数) ...
}

/* I2C 初始化 (示例,如果需要) */
void I2C_Init(void) {
// ... (省略 I2C 初始化代码,配置 I2C 参数) ...
}

/* 定时器初始化 (示例,用于盖革计数) */
void Timer_Init(void) {
// ... (省略 定时器初始化代码,配置定时器参数,例如计数模式、频率等) ...
}

/* ADC 初始化 (示例,如果需要) */
void ADC_Init(void) {
// ... (省略 ADC 初始化代码,配置 ADC 参数) ...
}

/* 错误处理函数 */
void Error_Handler(void) {
printf("系统发生错误!\r\n");
while (1) {
// 错误处理逻辑,例如闪烁 LED,重启系统等
}
}


int main(void) {
System_Init(); // 系统初始化

// 创建任务
if (xTaskCreate(DisplayTask, "DisplayTask", 256, NULL, 3, &xDisplayTaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(GeigerTask, "GeigerTask", 256, NULL, 4, &xGeigerTaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(WifiTask, "WifiTask", 512, NULL, 2, &xWifiTaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(UITask, "UITask", 256, NULL, 3, &xUITaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(AlarmTask, "AlarmTask", 128, NULL, 1, &xAlarmTaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(DataStorageTask, "DataStorageTask", 256, NULL, 2, &xDataStorageTaskHandle) != pdPASS) {
Error_Handler();
}
if (xTaskCreate(FirmwareUpdateTask, "FirmwareUpdateTask", 512, NULL, 1, &xFirmwareUpdateTaskHandle) != pdPASS) {
Error_Handler();
}

printf("任务创建完成,启动 FreeRTOS 调度器...\r\n");
vTaskStartScheduler(); // 启动 FreeRTOS 调度器

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

2. geiger_driver.cgeiger_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
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
/* geiger_driver.c */
#include "geiger_driver.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件

// 定义盖革管计数管脚 (示例,根据实际硬件连接配置)
#define GEIGER_COUNT_GPIO_PORT GPIOA
#define GEIGER_COUNT_GPIO_PIN GPIO_PIN_0

static uint32_t g_geiger_count = 0; // 盖革管计数

/* 盖革管计数中断处理函数 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == GEIGER_COUNT_GPIO_PIN) {
g_geiger_count++; // 计数加 1
}
}

/* 盖革管驱动初始化 */
void Geiger_Driver_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 示例,根据实际 GPIO 端口使能时钟

// 配置盖革管计数管脚为输入中断模式
GPIO_InitStruct.Pin = GEIGER_COUNT_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; // 上升沿触发中断
GPIO_InitStruct.Pull = GPIO_PULLDOWN; // 下拉
HAL_GPIO_Init(GEIGER_COUNT_GPIO_PORT, &GPIO_InitStruct);

// 使能 EXTI 中断 (示例,根据实际 EXTI 线配置)
HAL_NVIC_SetPriority(EXTI0_IRQn, 5, 0); // 设置中断优先级
HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能 EXTI0 中断
}

/* 获取盖革管计数 */
uint32_t Geiger_Driver_GetCount(void) {
return g_geiger_count;
}

/* 重置盖革管计数 */
void Geiger_Driver_ResetCount(void) {
g_geiger_count = 0;
}

// ... (可以添加更多驱动函数,例如配置盖革管参数,错误处理等) ...


/* geiger_driver.h */
#ifndef GEIGER_DRIVER_H
#define GEIGER_DRIVER_H

#include <stdint.h>

/* 盖革管驱动接口函数声明 */
void Geiger_Driver_Init(void);
uint32_t Geiger_Driver_GetCount(void);
void Geiger_Driver_ResetCount(void);

#endif /* GEIGER_DRIVER_H */

3. lcd_driver.clcd_driver.h (LCD 驱动)

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* lcd_driver.c */
#include "lcd_driver.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件
#include "string.h" // for memset, strcpy, strlen
#include "stdio.h" // for sprintf

// ... (LCD 驱动相关宏定义,例如 LCD 控制管脚、指令、数据等,根据实际 LCD 型号和连接方式定义) ...
// 示例:
#define LCD_CS_GPIO_PORT GPIOB
#define LCD_CS_GPIO_PIN GPIO_PIN_0
#define LCD_RS_GPIO_PORT GPIOB
#define LCD_RS_GPIO_PIN GPIO_PIN_1
#define LCD_RST_GPIO_PORT GPIOB
#define LCD_RST_GPIO_PIN GPIO_PIN_2
#define LCD_SPI_INSTANCE SPI1

/* LCD 初始化 */
void LCD_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
SPI_InitTypeDef SPI_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOB_CLK_ENABLE(); // 示例
__HAL_RCC_SPI1_CLK_ENABLE(); // 示例

// 配置 LCD 控制管脚为输出模式
GPIO_InitStruct.Pin = LCD_CS_GPIO_PIN | LCD_RS_GPIO_PIN | LCD_RST_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(LCD_CS_GPIO_PORT, &GPIO_InitStruct); // 示例

// 配置 SPI
SPI_InitStruct.Instance = LCD_SPI_INSTANCE; // 示例
SPI_InitStruct.Init.Mode = SPI_MODE_MASTER;
SPI_InitStruct.Init.Direction = SPI_DIRECTION_2LINES;
SPI_InitStruct.Init.DataSize = SPI_DATASIZE_8BIT;
SPI_InitStruct.Init.CLKPolarity = SPI_POLARITY_LOW;
SPI_InitStruct.Init.CLKPhase = SPI_PHASE_1EDGE;
SPI_InitStruct.Init.NSS = SPI_NSS_SOFT;
SPI_InitStruct.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2; // 调整波特率
SPI_InitStruct.Init.FirstBit = SPI_FIRSTBIT_MSB;
SPI_InitStruct.Init.TIMode = SPI_TIMODE_DISABLE;
SPI_InitStruct.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
SPI_InitStruct.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&SPI_InitStruct) != HAL_OK) {
Error_Handler(); // 错误处理
}

// LCD 复位
HAL_GPIO_WritePin(LCD_RST_GPIO_PORT, LCD_RST_GPIO_PIN, GPIO_PIN_RESET);
HAL_Delay(100);
HAL_GPIO_WritePin(LCD_RST_GPIO_PORT, LCD_RST_GPIO_PIN, GPIO_PIN_SET);
HAL_Delay(100);

// 初始化 LCD 控制器 (发送初始化指令序列,根据 LCD 控制器手册)
LCD_SendCommand(0x01); // Software Reset
HAL_Delay(10);
LCD_SendCommand(0x11); // Sleep Out
HAL_Delay(120);
LCD_SendCommand(0x29); // Display ON
LCD_SendCommand(0x2C); // Memory Write

LCD_Clear(LCD_COLOR_BLACK); // 清屏为黑色
}

/* 发送 LCD 命令 */
void LCD_SendCommand(uint8_t cmd) {
HAL_GPIO_WritePin(LCD_CS_GPIO_PORT, LCD_CS_GPIO_PIN, GPIO_PIN_RESET); // 片选使能
HAL_GPIO_WritePin(LCD_RS_GPIO_PORT, LCD_RS_GPIO_PIN, GPIO_PIN_RESET); // 命令模式
HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); // SPI 发送命令
HAL_GPIO_WritePin(LCD_CS_GPIO_PORT, LCD_CS_GPIO_PIN, GPIO_PIN_SET); // 片选失能
}

/* 发送 LCD 数据 */
void LCD_SendData(uint8_t data) {
HAL_GPIO_WritePin(LCD_CS_GPIO_PORT, LCD_CS_GPIO_PIN, GPIO_PIN_RESET); // 片选使能
HAL_GPIO_WritePin(LCD_RS_GPIO_PORT, LCD_RS_GPIO_PIN, GPIO_PIN_SET); // 数据模式
HAL_SPI_Transmit(&hspi1, &data, 1, HAL_MAX_DELAY); // SPI 发送数据
HAL_GPIO_WritePin(LCD_CS_GPIO_PORT, LCD_CS_GPIO_PIN, GPIO_PIN_SET); // 片选失能
}

/* 设置 LCD 窗口区域 */
void LCD_SetWindow(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd) {
LCD_SendCommand(0x2A); // Column Address Set
LCD_SendData(xStart >> 8);
LCD_SendData(xStart & 0xFF);
LCD_SendData(xEnd >> 8);
LCD_SendData(xEnd & 0xFF);

LCD_SendCommand(0x2B); // Row Address Set
LCD_SendData(yStart >> 8);
LCD_SendData(yStart & 0xFF);
LCD_SendData(yEnd >> 8);
LCD_SendData(yEnd & 0xFF);

LCD_SendCommand(0x2C); // Memory Write
}

/* 清屏 */
void LCD_Clear(uint16_t color) {
uint16_t i, j;
LCD_SetWindow(0, 0, LCD_WIDTH - 1, LCD_HEIGHT - 1);
for (i = 0; i < LCD_WIDTH; i++) {
for (j = 0; j < LCD_HEIGHT; j++) {
LCD_SendData(color >> 8);
LCD_SendData(color & 0xFF);
}
}
}

/* 画点 */
void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color) {
if (x >= LCD_WIDTH || y >= LCD_HEIGHT) return;
LCD_SetWindow(x, y, x, y);
LCD_SendData(color >> 8);
LCD_SendData(color & 0xFF);
}

/* 画线 (示例,简单水平线) */
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color) {
if (y1 == y2) { // 水平线
for (uint16_t x = x1; x <= x2; x++) {
LCD_DrawPixel(x, y1, color);
}
} // ... (可以添加其他线型,例如垂直线、斜线) ...
}

/* 显示字符 (ASCII 字符) */
void LCD_DrawChar(uint16_t x, uint16_t y, char ch, Font_t *font, uint16_t textColor, uint16_t bgColor) {
uint32_t i, j;
uint8_t charCode = ch - ' '; // 获取字符在字库中的偏移量

if (x > LCD_WIDTH - font->Width || y > LCD_HEIGHT - font->Height) return; // 超出显示区域

LCD_SetWindow(x, y, x + font->Width - 1, y + font->Height - 1);

for (i = 0; i < font->Height; i++) {
for (j = 0; j < font->Width; j++) {
if (font->Data[charCode * font->Height + i] & (1 << j)) { // 字库数据中该位为 1,显示字符颜色
LCD_SendData(textColor >> 8);
LCD_SendData(textColor & 0xFF);
} else { // 字库数据中该位为 0,显示背景颜色
LCD_SendData(bgColor >> 8);
LCD_SendData(bgColor & 0xFF);
}
}
}
}

/* 显示字符串 (ASCII 字符串) */
void LCD_DrawString(uint16_t x, uint16_t y, const char *str, Font_t *font, uint16_t textColor, uint16_t bgColor) {
while (*str) {
LCD_DrawChar(x, y, *str, font, textColor, bgColor);
x += font->Width;
str++;
}
}

/* 显示中文字符串 (需要集成中文字库和编码处理) */
void LCD_DrawChineseString(uint16_t x, uint16_t y, const char *str, Font_t *font, uint16_t textColor, uint16_t bgColor) {
// ... (中文显示实现,需要处理中文编码,例如 UTF-8 或 GBK,并使用中文字库) ...
// 示例 (简化的 UTF-8 处理,实际应用需要更完善的 UTF-8 解析)
while (*str) {
if ((*str & 0xF0) == 0xE0) { // UTF-8 三字节字符
// ... (解析 UTF-8 编码,获取中文字符,并从中文字库中获取字模) ...
// ... (根据字模显示中文字符) ...
str += 3;
} else { // ASCII 字符
LCD_DrawChar(x, y, *str, font, textColor, bgColor);
str++;
}
x += font->Width; // 假设中文字库和 ASCII 字库宽度相同
}
}


/* lcd_driver.h */
#ifndef LCD_DRIVER_H
#define LCD_DRIVER_H

#include <stdint.h>

// ... (LCD 驱动相关宏定义,例如 LCD 屏幕尺寸、颜色定义等) ...
// 示例:
#define LCD_WIDTH 240
#define LCD_HEIGHT 320
#define LCD_COLOR_BLACK 0x0000 // 黑色
#define LCD_COLOR_WHITE 0xFFFF // 白色
#define LCD_COLOR_RED 0xF800 // 红色
#define LCD_COLOR_GREEN 0x07E0 // 绿色
#define LCD_COLOR_BLUE 0x001F // 蓝色
#define LCD_COLOR_YELLOW 0xFFE0 // 黄色

// 字库结构体 (示例,需要根据实际字库格式定义)
typedef struct {
uint8_t Width; // 字符宽度
uint8_t Height; // 字符高度
const uint8_t *Data; // 字库数据指针
} Font_t;

// ... (字库数据声明,例如 ASCII 字库、中文点阵字库,可以放在单独的 font.c 和 font.h 文件中) ...
extern Font_t Font8x16; // 示例 ASCII 8x16 字库
extern Font_t Font16x16_Chinese; // 示例 16x16 中文字库 (点阵字库)

/* LCD 驱动接口函数声明 */
void LCD_Init(void);
void LCD_SendCommand(uint8_t cmd);
void LCD_SendData(uint8_t data);
void LCD_SetWindow(uint16_t xStart, uint16_t yStart, uint16_t xEnd, uint16_t yEnd);
void LCD_Clear(uint16_t color);
void LCD_DrawPixel(uint16_t x, uint16_t y, uint16_t color);
void LCD_DrawLine(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color);
void LCD_DrawChar(uint16_t x, uint16_t y, char ch, Font_t *font, uint16_t textColor, uint16_t bgColor);
void LCD_DrawString(uint16_t x, uint16_t y, const char *str, Font_t *font, uint16_t textColor, uint16_t bgColor);
void LCD_DrawChineseString(uint16_t x, uint16_t y, const char *str, Font_t *font, uint16_t textColor, uint16_t bgColor);

#endif /* LCD_DRIVER_H */

4. wifi_module.cwifi_module.h (Wi-Fi 模块驱动)

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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/* wifi_module.c */
#include "wifi_module.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件
#include "string.h" // for memset, strcpy, strlen
#include "stdio.h" // for sprintf

// ... (Wi-Fi 模块驱动相关宏定义、变量、函数声明,例如 UART 接口、AT 指令、Wi-Fi 状态等,根据实际 Wi-Fi 模块型号和通信协议定义) ...
// 示例 (基于 UART AT 指令的 ESP32 模块驱动):
#define WIFI_UART_INSTANCE USART2 // 示例 UART 实例
#define WIFI_BAUDRATE 115200 // 波特率
#define WIFI_CMD_TIMEOUT_MS 5000 // AT 指令超时时间

static UART_HandleTypeDef huart_wifi; // UART 句柄

/* 发送 AT 指令 */
static HAL_StatusTypeDef WiFi_SendATCommand(const char *cmd, char *response, uint32_t timeout_ms);

/* Wi-Fi 模块初始化 */
void WiFi_Module_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
UART_InitTypeDef UART_InitStruct = {0};

// 使能 UART 时钟
__HAL_RCC_USART2_CLK_ENABLE(); // 示例
__HAL_RCC_GPIOA_CLK_ENABLE(); // 示例 (UART GPIO 端口)

// 配置 UART GPIO 管脚 (示例)
GPIO_InitStruct.Pin = GPIO_PIN_2 | GPIO_PIN_3; // TX, RX
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF7_USART2; // UART2_TX_AF7, UART2_RX_AF7
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// 配置 UART
huart_wifi.Instance = WIFI_UART_INSTANCE; // 示例
huart_wifi.Init.BaudRate = WIFI_BAUDRATE;
huart_wifi.Init.WordLength = UART_WORDLENGTH_8B;
huart_wifi.Init.StopBits = UART_STOPBITS_1;
huart_wifi.Init.Parity = UART_PARITY_NONE;
huart_wifi.Init.Mode = UART_MODE_TX_RX;
huart_wifi.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart_wifi.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart_wifi) != HAL_OK) {
Error_Handler();
}

// 测试 Wi-Fi 模块是否正常工作 (发送 AT 指令)
char response[128] = {0};
if (WiFi_SendATCommand("AT\r\n", response, WIFI_CMD_TIMEOUT_MS) != HAL_OK) {
printf("Wi-Fi 模块初始化失败,AT 指令无响应!\r\n");
Error_Handler();
}
if (strstr(response, "OK") == NULL) {
printf("Wi-Fi 模块初始化失败,AT 指令响应错误: %s\r\n", response);
Error_Handler();
}

printf("Wi-Fi 模块初始化成功!\r\n");
}

/* 发送 AT 指令 */
static HAL_StatusTypeDef WiFi_SendATCommand(const char *cmd, char *response, uint32_t timeout_ms) {
HAL_StatusTypeDef status = HAL_OK;
uint8_t rx_buffer[128] = {0};
uint32_t start_time = HAL_GetTick();

memset(rx_buffer, 0, sizeof(rx_buffer));
memset(response, 0, sizeof(response));

// 清空 UART 接收缓冲区
__HAL_UART_FLUSH_DRREGISTER(&huart_wifi);

// 发送 AT 指令
status = HAL_UART_Transmit(&huart_wifi, (uint8_t *)cmd, strlen(cmd), timeout_ms);
if (status != HAL_OK) {
printf("AT 指令发送失败: %s\r\n", cmd);
return status;
}

// 接收响应数据 (非阻塞接收,超时处理)
uint32_t rx_len = 0;
while (HAL_GetTick() - start_time < timeout_ms) {
if (HAL_UART_Receive(&huart_wifi, &rx_buffer[rx_len], 1, 1) == HAL_OK) {
rx_len++;
if (rx_len >= sizeof(rx_buffer) - 1) break; // 防止缓冲区溢出
if (strstr((char *)rx_buffer, "\r\nOK") != NULL || strstr((char *)rx_buffer, "\r\nERROR") != NULL) break; // 接收到 OK 或 ERROR 结束
}
}

if (rx_len > 0) {
strncpy(response, (char *)rx_buffer, sizeof(response) - 1); // 复制响应数据
} else {
printf("AT 指令接收超时: %s\r\n", cmd);
return HAL_TIMEOUT;
}

return HAL_OK;
}

/* 连接 Wi-Fi 网络 (示例,需要根据实际 Wi-Fi 模块 AT 指令集实现) */
HAL_StatusTypeDef WiFi_ConnectAP(const char *ssid, const char *password) {
char cmd[128] = {0};
char response[128] = {0};
HAL_StatusTypeDef status = HAL_OK;

// 设置 Wi-Fi 模式为 Station 模式 (如果需要)
sprintf(cmd, "AT+CWMODE=1\r\n");
status = WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);
if (status != HAL_OK || strstr(response, "OK") == NULL) {
printf("设置 Wi-Fi 模式失败!\r\n");
return HAL_ERROR;
}

// 连接 AP
sprintf(cmd, "AT+CWJAP=\"%s\",\"%s\"\r\n", ssid, password);
status = WiFi_SendATCommand(cmd, response, 60000); // 连接 AP 超时时间可以设置长一些
if (status != HAL_OK || strstr(response, "WIFI CONNECTED") == NULL || strstr(response, "WIFI GOT IP") == NULL || strstr(response, "OK") == NULL) {
printf("Wi-Fi 连接 AP 失败: %s\r\n", response);
return HAL_ERROR;
}

printf("Wi-Fi 连接 AP 成功!\r\n");
return HAL_OK;
}

/* 断开 Wi-Fi 连接 (示例) */
HAL_StatusTypeDef WiFi_DisconnectAP(void) {
char cmd[32] = "AT+CWQAP\r\n";
char response[128] = {0};
HAL_StatusTypeDef status = WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);
if (status != HAL_OK || strstr(response, "OK") == NULL) {
printf("Wi-Fi 断开连接失败!\r\n");
return HAL_ERROR;
}
printf("Wi-Fi 断开连接成功!\r\n");
return HAL_OK;
}

/* 获取 Wi-Fi IP 地址 (示例) */
HAL_StatusTypeDef WiFi_GetIPAddress(char *ip_addr) {
char cmd[32] = "AT+CIPSTA?\r\n";
char response[128] = {0};
HAL_StatusTypeDef status = WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);
if (status != HAL_OK || strstr(response, "OK") == NULL) {
printf("获取 IP 地址失败!\r\n");
return HAL_ERROR;
}

// 解析 IP 地址 (示例,需要根据实际 AT 指令响应格式解析)
char *start = strstr(response, "+CIPSTA:ip:\"");
if (start != NULL) {
start += strlen("+CIPSTA:ip:\"");
char *end = strchr(start, '"');
if (end != NULL) {
strncpy(ip_addr, start, end - start);
ip_addr[end - start] = '\0';
printf("Wi-Fi IP 地址: %s\r\n", ip_addr);
return HAL_OK;
}
}

printf("解析 IP 地址失败: %s\r\n", response);
return HAL_ERROR;
}

/* 发送 TCP 数据 (示例,需要根据实际 Wi-Fi 模块 AT 指令集实现) */
HAL_StatusTypeDef WiFi_SendTCPData(const char *ip, int port, const char *data) {
char cmd[256] = {0};
char response[128] = {0};
HAL_StatusTypeDef status = HAL_OK;

// 创建 TCP 连接
sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", ip, port);
status = WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);
if (status != HAL_OK || (strstr(response, "CONNECT") == NULL && strstr(response, "ALREADY CONNECTED") == NULL) || strstr(response, "OK") == NULL) {
printf("TCP 连接创建失败: %s\r\n", response);
return HAL_ERROR;
}

HAL_Delay(1000); // 等待连接建立完成 (可能需要根据实际情况调整)

// 发送数据
sprintf(cmd, "AT+CIPSEND=%d\r\n", strlen(data));
status = WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);
if (status != HAL_OK || strstr(response, ">") == NULL || strstr(response, "OK") == NULL) {
printf("发送数据指令失败: %s\r\n", response);
return HAL_ERROR;
}

HAL_Delay(100); // 等待 ">" 提示符出现

status = HAL_UART_Transmit(&huart_wifi, (uint8_t *)data, strlen(data), WIFI_CMD_TIMEOUT_MS); // 直接发送数据
if (status != HAL_OK) {
printf("数据发送失败!\r\n");
return status;
}

// 关闭 TCP 连接 (根据实际需求决定是否需要关闭连接,或者保持连接)
// sprintf(cmd, "AT+CIPCLOSE\r\n");
// WiFi_SendATCommand(cmd, response, WIFI_CMD_TIMEOUT_MS);

printf("TCP 数据发送成功!\r\n");
return HAL_OK;
}

// ... (可以添加更多 Wi-Fi 模块驱动函数,例如 MQTT 连接、数据接收、OTA 固件升级等) ...


/* wifi_module.h */
#ifndef WIFI_MODULE_H
#define WIFI_MODULE_H

#include <stdint.h>
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件

/* Wi-Fi 模块驱动接口函数声明 */
void WiFi_Module_Init(void);
HAL_StatusTypeDef WiFi_ConnectAP(const char *ssid, const char *password);
HAL_StatusTypeDef WiFi_DisconnectAP(void);
HAL_StatusTypeDef WiFi_GetIPAddress(char *ip_addr);
HAL_StatusTypeDef WiFi_SendTCPData(const char *ip, int port, const char *data);

#endif /* WIFI_MODULE_H */

5. buzzer_driver.cbuzzer_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
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
/* buzzer_driver.c */
#include "buzzer_driver.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件

// 定义蜂鸣器控制管脚 (示例,根据实际硬件连接配置)
#define BUZZER_GPIO_PORT GPIOC
#define BUZZER_GPIO_PIN GPIO_PIN_13

/* 蜂鸣器驱动初始化 */
void Buzzer_Driver_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 示例

// 配置蜂鸣器管脚为输出模式
GPIO_InitStruct.Pin = BUZZER_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BUZZER_GPIO_PORT, &GPIO_InitStruct);

Buzzer_Driver_Off(); // 初始状态关闭蜂鸣器
}

/* 打开蜂鸣器 */
void Buzzer_Driver_On(void) {
HAL_GPIO_WritePin(BUZZER_GPIO_PORT, BUZZER_GPIO_PIN, GPIO_PIN_SET); // 高电平触发蜂鸣器 (根据实际硬件电路调整)
}

/* 关闭蜂鸣器 */
void Buzzer_Driver_Off(void) {
HAL_GPIO_WritePin(BUZZER_GPIO_PORT, BUZZER_GPIO_PIN, GPIO_PIN_RESET); // 低电平关闭蜂鸣器 (根据实际硬件电路调整)
}

/* 蜂鸣器鸣响指定时间 (毫秒) */
void Buzzer_Driver_Beep(uint32_t duration_ms) {
Buzzer_Driver_On();
HAL_Delay(duration_ms);
Buzzer_Driver_Off();
}


/* buzzer_driver.h */
#ifndef BUZZER_DRIVER_H
#define BUZZER_DRIVER_H

#include <stdint.h>

/* 蜂鸣器驱动接口函数声明 */
void Buzzer_Driver_Init(void);
void Buzzer_Driver_On(void);
void Buzzer_Driver_Off(void);
void Buzzer_Driver_Beep(uint32_t duration_ms);

#endif /* BUZZER_DRIVER_H */

6. led_driver.cled_driver.h (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
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
/* led_driver.c */
#include "led_driver.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件

// 定义 LED 控制管脚 (示例,根据实际硬件连接配置)
#define LED_POWER_GPIO_PORT GPIOC
#define LED_POWER_GPIO_PIN GPIO_PIN_14
#define LED_WIFI_GPIO_PORT GPIOC
#define LED_WIFI_GPIO_PIN GPIO_PIN_15
#define LED_ALARM_GPIO_PORT GPIOD
#define LED_ALARM_GPIO_PIN GPIO_PIN_2

/* LED 驱动初始化 */
void LED_Driver_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOC_CLK_ENABLE(); // 示例
__HAL_RCC_GPIOD_CLK_ENABLE(); // 示例

// 配置 LED 管脚为输出模式
GPIO_InitStruct.Pin = LED_POWER_GPIO_PIN | LED_WIFI_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

GPIO_InitStruct.Pin = LED_ALARM_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

LED_Driver_PowerOff(); // 初始状态关闭电源 LED
LED_Driver_WifiOff(); // 初始状态关闭 Wi-Fi LED
LED_Driver_AlarmOff(); // 初始状态关闭报警 LED
}

/* 控制电源 LED */
void LED_Driver_PowerOn(void) {
HAL_GPIO_WritePin(LED_POWER_GPIO_PORT, LED_POWER_GPIO_PIN, GPIO_PIN_SET); // 点亮电源 LED
}

void LED_Driver_PowerOff(void) {
HAL_GPIO_WritePin(LED_POWER_GPIO_PORT, LED_POWER_GPIO_PIN, GPIO_PIN_RESET); // 关闭电源 LED
}

void LED_Driver_PowerToggle(void) {
HAL_GPIO_TogglePin(LED_POWER_GPIO_PORT, LED_POWER_GPIO_PIN); // 切换电源 LED 状态
}

/* 控制 Wi-Fi LED */
void LED_Driver_WifiOn(void) {
HAL_GPIO_WritePin(LED_WIFI_GPIO_PORT, LED_WIFI_GPIO_PIN, GPIO_PIN_SET); // 点亮 Wi-Fi LED
}

void LED_Driver_WifiOff(void) {
HAL_GPIO_WritePin(LED_WIFI_GPIO_PORT, LED_WIFI_GPIO_PIN, GPIO_PIN_RESET); // 关闭 Wi-Fi LED
}

void LED_Driver_WifiToggle(void) {
HAL_GPIO_TogglePin(LED_WIFI_GPIO_PORT, LED_WIFI_GPIO_PIN); // 切换 Wi-Fi LED 状态
}

/* 控制报警 LED */
void LED_Driver_AlarmOn(void) {
HAL_GPIO_WritePin(LED_ALARM_GPIO_PORT, LED_ALARM_GPIO_PIN, GPIO_PIN_SET); // 点亮报警 LED
}

void LED_Driver_AlarmOff(void) {
HAL_GPIO_WritePin(LED_ALARM_GPIO_PORT, LED_ALARM_GPIO_PIN, GPIO_PIN_RESET); // 关闭报警 LED
}

void LED_Driver_AlarmToggle(void) {
HAL_GPIO_TogglePin(LED_ALARM_GPIO_PORT, LED_ALARM_GPIO_PIN); // 切换报警 LED 状态
}


/* led_driver.h */
#ifndef LED_DRIVER_H
#define LED_DRIVER_H

#include <stdint.h>

/* LED 驱动接口函数声明 */
void LED_Driver_Init(void);

// 电源 LED 控制
void LED_Driver_PowerOn(void);
void LED_Driver_PowerOff(void);
void LED_Driver_PowerToggle(void);

// Wi-Fi LED 控制
void LED_Driver_WifiOn(void);
void LED_Driver_WifiOff(void);
void LED_Driver_WifiToggle(void);

// 报警 LED 控制
void LED_Driver_AlarmOn(void);
void LED_Driver_AlarmOff(void);
void LED_Driver_AlarmToggle(void);

#endif /* LED_DRIVER_H */

7. button_driver.cbutton_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
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
/* button_driver.c */
#include "button_driver.h"
#include "stm32f4xx_hal.h" // 或其他 MCU HAL 库头文件
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

// 定义按键管脚 (示例,根据实际硬件连接配置)
#define BUTTON_MENU_GPIO_PORT GPIOA
#define BUTTON_MENU_GPIO_PIN GPIO_PIN_1
#define BUTTON_UP_GPIO_PORT GPIOA
#define BUTTON_UP_GPIO_PIN GPIO_PIN_4
#define BUTTON_DOWN_GPIO_PORT GPIOA
#define BUTTON_DOWN_GPIO_PIN GPIO_PIN_5
#define BUTTON_ENTER_GPIO_PORT GPIOA
#define BUTTON_ENTER_GPIO_PIN GPIO_PIN_6

#define BUTTON_DEBOUNCE_TIME_MS 50 // 按键去抖动时间 (毫秒)

/* 按键状态结构体 */
typedef struct {
uint8_t menu_pressed;
uint8_t up_pressed;
uint8_t down_pressed;
uint8_t enter_pressed;
} ButtonState_t;

static ButtonState_t g_button_state = {0}; // 当前按键状态
static QueueHandle_t xButtonEventQueue = NULL; // 按键事件队列句柄

/* 按键扫描任务 */
static void ButtonScanTask(void *pvParameters);

/* 按键驱动初始化 */
void Button_Driver_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};

// 使能 GPIO 时钟
__HAL_RCC_GPIOA_CLK_ENABLE(); // 示例

// 配置按键管脚为输入模式,上拉
GPIO_InitStruct.Pin = BUTTON_MENU_GPIO_PIN | BUTTON_UP_GPIO_PIN | BUTTON_DOWN_GPIO_PIN | BUTTON_ENTER_GPIO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 上拉,按键按下时为低电平
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BUTTON_MENU_GPIO_PORT, &GPIO_InitStruct);

// 创建按键事件队列
xButtonEventQueue = xQueueCreate(10, sizeof(ButtonEvent_t));
if (xButtonEventQueue == NULL) {
Error_Handler();
}

// 创建按键扫描任务
if (xTaskCreate(ButtonScanTask, "ButtonScanTask", 128, NULL, 2, NULL) != pdPASS) {
Error_Handler();
}
}

/* 获取按键事件队列句柄 */
QueueHandle_t Button_Driver_GetEventQueue(void) {
return xButtonEventQueue;
}

/* 按键扫描任务 */
static void ButtonScanTask(void *pvParameters) {
ButtonState_t last_button_state = {0}; // 上一次按键状态
ButtonEvent_t button_event;

while (1) {
// 读取按键状态
g_button_state.menu_pressed = (HAL_GPIO_ReadPin(BUTTON_MENU_GPIO_PORT, BUTTON_MENU_GPIO_PIN) == GPIO_PIN_RESET); // 低电平按下
g_button_state.up_pressed = (HAL_GPIO_ReadPin(BUTTON_UP_GPIO_PORT, BUTTON_UP_GPIO_PIN) == GPIO_PIN_RESET);
g_button_state.down_pressed = (HAL_GPIO_ReadPin(BUTTON_DOWN_GPIO_PORT, BUTTON_DOWN_GPIO_PIN) == GPIO_PIN_RESET);
g_button_state.enter_pressed = (HAL_GPIO_ReadPin(BUTTON_ENTER_GPIO_PORT, BUTTON_ENTER_GPIO_PIN) == GPIO_PIN_RESET);

// 检测按键事件 (按键按下/释放)
if (g_button_state.menu_pressed && !last_button_state.menu_pressed) {
button_event.type = BUTTON_MENU_PRESSED;
xQueueSend(xButtonEventQueue, &button_event, 0); // 发送按键按下事件
}
if (!g_button_state.menu_pressed && last_button_state.menu_pressed) {
button_event.type = BUTTON_MENU_RELEASED;
xQueueSend(xButtonEventQueue, &button_event, 0); // 发送按键释放事件
}
// ... (其他按键事件检测,例如 UP, DOWN, ENTER) ...

last_button_state = g_button_state; // 更新上一次按键状态
vTaskDelay(pdMS_TO_TICKS(BUTTON_DEBOUNCE_TIME_MS)); // 延时去抖动
}
}

/* 获取当前按键状态 */
ButtonState_t Button_Driver_GetState(void) {
return g_button_state;
}

/* 检查指定按键是否按下 */
uint8_t Button_Driver_IsMenuPressed(void) {
return g_button_state.menu_pressed;
}

uint8_t Button_Driver_IsUpPressed(void) {
return g_button_state.up_pressed;
}

uint8_t Button_Driver_IsDownPressed(void) {
return g_button_state.down_pressed;
}

uint8_t Button_Driver_IsEnterPressed(void) {
return g_button_state.enter_pressed;
}


/* button_driver.h */
#ifndef BUTTON_DRIVER_H
#define BUTTON_DRIVER_H

#include <stdint.h>
#include "FreeRTOS.h"
#include "queue.h"

/* 按键事件类型 */
typedef enum {
BUTTON_MENU_PRESSED,
BUTTON_MENU_RELEASED,
BUTTON_UP_PRESSED,
BUTTON_UP_RELEASED,
BUTTON_DOWN_PRESSED,
BUTTON_DOWN_RELEASED,
BUTTON_ENTER_PRESSED,
BUTTON_ENTER_RELEASED,
BUTTON_LONG_PRESSED, // 长按事件 (可选)
} ButtonEventType_t;

/* 按键事件结构体 */
typedef struct {
ButtonEventType_t type;
} ButtonEvent_t;

/* 按键状态结构体 */
typedef struct {
uint8_t menu_pressed;
uint8_t up_pressed;
uint8_t down_pressed;
uint8_t enter_pressed;
} ButtonState_t;

/* 按键驱动接口函数声明 */
void Button_Driver_Init(void);
QueueHandle_t Button_Driver_GetEventQueue(void);
ButtonState_t Button_Driver_GetState(void);
uint8_t Button_Driver_IsMenuPressed(void);
uint8_t Button_Driver_IsUpPressed(void);
uint8_t Button_Driver_IsDownPressed(void);
uint8_t Button_Driver_IsEnterPressed(void);

#endif /* BUTTON_DRIVER_H */

(后续代码文件 - data_process.c, data_process.h, config_manager.c, config_manager.h, display_task.c, display_task.h, geiger_task.c, geiger_task.h, wifi_task.c, wifi_task.h, ui_task.c, ui_task.h, alarm_task.c, alarm_task.h, data_storage.c, data_storage.h, firmware_update.c, firmware_update.h, main.h, FreeRTOSConfig.h, stm32f4xx_it.c 等)

由于代码量限制,以上只提供了部分核心驱动代码示例。为了达到 3000 行代码,需要继续完善以下模块的代码,并添加更多的功能和细节:

  • data_process.cdata_process.h: 实现辐射数据处理逻辑,包括:

    • 计数率到剂量率的转换 (需要校准系数)
    • 累积剂量计算
    • CPM 计算
    • 数据平滑滤波 (可选)
    • 单位转换 (µSv/hr, mR/hr, CPM 等)
    • 定义 RadiationData_t 结构体 (包含 doseRate, cumulativeDose, cpm, status 等字段)
  • config_manager.cconfig_manager.h: 实现配置管理模块,负责:

    • 读取和保存系统配置参数 (Wi-Fi 设置、报警阈值、显示设置等)
    • 使用 Flash 存储配置参数
    • 提供 API 函数用于获取和设置配置参数
  • display_task.cdisplay_task.h: 实现显示任务,负责:

    • 接收数据更新事件 (DataUpdateEvent_t)
    • 从全局辐射数据 g_RadiationData 中读取数据
    • 使用 lcd_driver.c 提供的 LCD 驱动函数,在 LCD 屏幕上显示辐射数据、状态信息、菜单界面等
    • 实现中文界面显示 (需要集成中文字库和编码处理)
    • 处理 UI 事件 (UIEvent_t),例如菜单导航、参数设置等
  • geiger_task.cgeiger_task.h: 实现盖革计数任务,负责:

    • 周期性读取盖革管计数 (Geiger_Driver_GetCount())
    • 调用 data_process.c 中的函数进行数据处理,计算剂量率等参数
    • 更新全局辐射数据 g_RadiationData (需要使用互斥锁 xRadiationDataMutex 保护)
    • 发送数据更新事件 (DataUpdateEvent_t) 给显示任务和 Wi-Fi 通信任务
  • wifi_task.cwifi_task.h: 实现 Wi-Fi 通信任务,负责:

    • Wi-Fi 连接管理 (连接 AP, 断开 AP, 获取 IP 地址等)
    • 数据上传 (将辐射数据通过 Wi-Fi 上传到服务器,例如 MQTT 协议)
    • 接收远程控制指令 (可选)
    • 处理 Wi-Fi 事件 (WifiEvent_t)
    • 实现 OTA 固件升级 (接收固件数据,校验固件,更新固件)
  • ui_task.cui_task.h: 实现用户界面任务,负责:

    • 接收按键事件 (ButtonEvent_t)
    • 实现菜单导航逻辑 (例如主菜单、设置菜单、历史数据菜单等)
    • 处理用户按键操作,例如菜单选择、参数设置等
    • 发送 UI 事件 (UIEvent_t) 给显示任务更新界面
  • alarm_task.calarm_task.h: 实现报警任务,负责:

    • 周期性检测辐射剂量 (从全局辐射数据 g_RadiationData 中读取剂量率)
    • 比较剂量率与报警阈值 (从配置参数中读取)
    • 当超过阈值时,触发声光报警 (控制蜂鸣器 Buzzer_Driver_Beep() 和报警 LED LED_Driver_AlarmOn())
    • 发送报警事件 (AlarmEvent_t) 给显示任务更新报警状态显示
  • data_storage.cdata_storage.h: 实现数据存储任务,负责:

    • 将历史辐射数据 (时间戳 + 剂量率) 存储到 Flash 中 (例如使用文件系统或直接 Flash 扇区读写)
    • 提供 API 函数用于读取历史数据
  • firmware_update.cfirmware_update.h: 实现固件升级任务,负责:

    • 接收 OTA 固件升级请求
    • 通过 Wi-Fi 接收固件数据
    • 校验固件完整性和有效性 (例如 CRC 校验、数字签名校验)
    • 更新固件到 Flash 中
    • 实现本地固件升级 (例如通过 UART 或 USB 接口)
  • main.h: 定义全局宏、类型定义、函数声明,例如任务句柄、事件队列句柄、互斥锁句柄、全局变量 g_RadiationData、事件结构体 (DataUpdateEvent_t, UIEvent_t, WifiEvent_t, AlarmEvent_t, FirmwareUpdateEvent_t) 等。

  • FreeRTOSConfig.h: FreeRTOS 配置文件,根据实际需求配置 FreeRTOS 参数,例如任务堆栈大小、优先级、时间片大小等。

  • stm32f4xx_it.c: STM32 中断服务例程文件,实现中断处理函数,例如 EXTI 中断处理函数 (用于盖革管计数脉冲检测)。

技术和方法实践验证

本项目中采用的各种技术和方法都是经过实践验证的,符合嵌入式系统开发的最佳实践:

  • 分层架构: 提高代码模块化、可维护性和可扩展性。
  • RTOS: 实现系统的实时性、并发性和稳定性,简化任务调度和资源管理。
  • 事件驱动架构: 提高系统响应性和效率,任务之间通过事件通信,解耦任务依赖。
  • C 语言: 嵌入式系统开发的首选语言,效率高,可移植性好。
  • HAL 库: STM32 HAL 库简化了硬件驱动开发,提高了代码可移植性。
  • 模块化设计: 将系统分解为独立的模块,每个模块负责特定功能,方便开发、测试和维护。
  • 代码注释: 代码中添加详细注释,提高代码可读性和可维护性。
  • 版本控制: 使用 Git 等版本控制工具管理代码,方便代码版本管理和协作开发。
  • 单元测试和集成测试: 对每个模块进行单元测试,确保模块功能正确;进行集成测试,验证模块之间的协同工作。
  • 代码审查: 进行代码审查,提高代码质量,发现潜在 bug。
  • 性能分析和优化: 对系统进行性能分析,找出性能瓶颈,进行代码优化,提高系统效率。
  • 低功耗设计: 在硬件和软件层面进行低功耗设计,延长电池续航时间。
  • 固件升级机制: 实现 OTA 固件升级,方便 bug 修复和功能更新。
  • 日志记录: 记录系统运行日志,方便故障排查和问题定位。

总结

中文固件多管型 Wi-Fi 盖革计数器(MWGC-2T)嵌入式系统采用分层架构和基于 RTOS 的事件驱动架构,具有良好的可靠性、高效性和可扩展性。 通过模块化的 C 代码实现,并结合实践验证的技术和方法,可以构建一个功能完善、性能优良的嵌入式产品。 以上提供的代码示例是系统架构和核心模块的框架,实际开发中需要根据具体硬件平台、功能需求和性能指标进行详细设计和实现,并进行充分的测试和验证。

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