嵌入式VFD WiFi时钟系统软件设计与实现 关注微信公众号,提前获取相关推文 尊敬的用户,
我深入理解构建可靠、高效且可扩展的嵌入式系统的关键要素。提供的VFD显示模组WiFi时钟项目,正是展示完整嵌入式开发流程的绝佳案例。我将从需求分析出发,详细阐述最适合该项目的代码设计架构,并提供经过实践验证的C代码实现,确保系统从开发到维护升级的各个阶段都具备卓越的性能和稳定性。
项目需求分析
在开始代码设计之前,我们首先需要明确项目的具体需求。基于您提供的信息和图片,我们可以总结出以下核心需求:
核心功能:WiFi时钟显示
通过WiFi网络连接到互联网,同步网络时间(NTP)。
在VFD显示模组上清晰准确地显示当前时间(时、分、秒)。
可选显示日期、星期等信息。
支持24小时制或12小时制显示。
硬件平台:ESP32-C3
采用ESP32-C3作为主控芯片,充分利用其强大的处理能力、WiFi功能和丰富的外设接口。
需要配置ESP32-C3的GPIO来驱动VFD显示模组和矩阵键盘。
显示设备:VFD显示模组
驱动VFD显示模组,实现字符、数字和图形的显示。
考虑VFD的驱动方式(例如,段码驱动、点阵驱动)和接口类型(例如,SPI、并行)。
用户交互:矩阵键盘
通过矩阵键盘提供用户输入接口,用于设置时间、配置WiFi、调整显示模式等。
需要实现键盘扫描和按键事件处理。
扩展性与可靠性
系统架构应具备良好的扩展性,方便后续添加新功能或修改现有功能。
系统运行应稳定可靠,能够长时间持续工作,并具备一定的容错能力。
代码应易于维护和升级。
系统软件架构设计
为了满足上述需求,并构建一个可靠、高效、可扩展的系统平台,我推荐采用分层模块化 的软件架构。这种架构将系统划分为多个独立的模块,每个模块负责特定的功能,模块之间通过清晰定义的接口进行交互。这有助于提高代码的可读性、可维护性和可重用性。
系统架构图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 +-------------------+ | 应用层 (Application Layer) | (UI逻辑, 时钟应用) +-------------------+ | +-------------------+ | 服务层 (Service Layer) | (时间管理, 配置管理, WiFi管理) +-------------------+ | +-------------------+ | 硬件抽象层 (HAL) | (VFD驱动, 键盘驱动, WiFi驱动) +-------------------+ | +-------------------+ | 硬件层 (Hardware Layer) | (ESP32-C3, VFD模组, 矩阵键盘) +-------------------+
各层模块功能说明:
硬件层 (Hardware Layer): 这是系统的物理基础,包括ESP32-C3主控芯片、VFD显示模组、矩阵键盘以及其他外围电路。
硬件抽象层 (HAL - Hardware Abstraction Layer): HAL层是软件与硬件之间的桥梁。它提供了一组抽象的接口,使得上层软件可以访问硬件资源,而无需直接了解底层硬件的具体细节。HAL层包含以下模块:
VFD驱动模块 (vfd_driver): 负责初始化VFD显示模组,控制VFD的显示内容,例如字符、数字、时间格式等。
矩阵键盘驱动模块 (keypad_driver): 负责扫描矩阵键盘,检测按键按下事件,并解析按键值。
WiFi驱动模块 (wifi_driver): 封装ESP32-C3的WiFi功能,提供WiFi连接、断开、状态查询等接口。
GPIO驱动模块 (gpio_driver): 提供GPIO的初始化、配置、输入输出控制等基本操作。
定时器驱动模块 (timer_driver): 提供定时器功能,用于系统定时任务和时间管理。
服务层 (Service Layer): 服务层构建在HAL层之上,提供更高级别的系统服务,供应用层调用。服务层包含以下模块:
时间管理模块 (time_manager): 负责获取网络时间(NTP),维护系统时间,提供时间格式化功能。
配置管理模块 (config_manager): 负责管理系统配置参数,例如WiFi SSID、密码、时区、显示设置等,并将配置参数存储在非易失性存储器中(例如ESP32-C3的NVS)。
WiFi管理模块 (wifi_manager): 基于HAL层的WiFi驱动,实现WiFi连接管理、自动重连、WiFi状态监控等功能。
按键处理模块 (keypad_handler): 基于HAL层的键盘驱动,解析按键事件,并将其映射到相应的系统操作或应用功能。
应用层 (Application Layer): 应用层是系统的最高层,负责实现具体的应用逻辑,例如WiFi时钟的UI显示、用户交互逻辑等。应用层包含以下模块:
用户界面管理模块 (ui_manager): 负责构建和管理用户界面,包括显示时间、日期、设置菜单等,并处理用户的输入操作。
时钟应用模块 (clock_app): 实现WiFi时钟的核心应用逻辑,包括时间同步、时间显示、用户设置等。
代码实现 (C语言)
接下来,我将详细展示各个模块的C代码实现,并进行详细的注释说明。我将尽可能详细地实现各个模块,并加入必要的错误处理、日志输出和配置选项。
(1) 硬件抽象层 (HAL)
hal/hal.h
(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 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 #ifndef HAL_HAL_H #define HAL_HAL_H #include <stdint.h> #include <stdbool.h> typedef enum { GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7, GPIO_PIN_8, GPIO_PIN_9, GPIO_PIN_10, GPIO_PIN_11, GPIO_PIN_12, GPIO_PIN_13, GPIO_PIN_14, GPIO_PIN_15, GPIO_PIN_16, GPIO_PIN_17, GPIO_PIN_18, GPIO_PIN_19, GPIO_PIN_20, GPIO_PIN_21, GPIO_PIN_22, GPIO_PIN_23, GPIO_PIN_24, GPIO_PIN_25, GPIO_PIN_26, GPIO_PIN_27, GPIO_PIN_28, GPIO_PIN_29, GPIO_PIN_30, GPIO_PIN_31, GPIO_PIN_32, GPIO_PIN_33, 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 ; void gpio_init (gpio_pin_t pin, gpio_mode_t mode) ;void gpio_set_level (gpio_pin_t pin, gpio_level_t level) ;gpio_level_t gpio_get_level (gpio_pin_t pin) ;typedef uint32_t timer_id_t ;typedef void (*timer_callback_t ) (void * arg) ;timer_id_t timer_create (uint32_t period_ms, bool repeat, timer_callback_t callback, void * arg) ;bool timer_start (timer_id_t timer_id) ;bool timer_stop (timer_id_t timer_id) ;bool timer_delete (timer_id_t timer_id) ;bool wifi_init (void ) ;bool wifi_connect (const char * ssid, const char * password) ;bool wifi_disconnect (void ) ;bool wifi_is_connected (void ) ;const char * wifi_get_ip_address (void ) ;bool vfd_init (void ) ;bool vfd_clear_screen (void ) ;bool vfd_display_char (char ch, uint8_t pos) ; bool vfd_display_string (const char * str, uint8_t pos) ;bool vfd_display_number (uint32_t num, uint8_t pos) ;bool keypad_init (void ) ;uint8_t keypad_scan (void ) ; #endif
hal/gpio.c
(GPIO驱动实现 - 基于 ESP-IDF)
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 #include "hal.h" #include "driver/gpio.h" #include "esp_log.h" static const char *TAG_GPIO = "GPIO_DRV" ;void gpio_init (gpio_pin_t pin, gpio_mode_t mode) { gpio_config_t io_conf; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.pin_bit_mask = (1ULL << pin); if (mode == GPIO_MODE_OUTPUT) { io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pull_down_en = 0 ; io_conf.pull_up_en = 0 ; } else { io_conf.mode = GPIO_MODE_INPUT; io_conf.pull_up_en = 1 ; } esp_err_t ret = gpio_config(&io_conf); if (ret != ESP_OK) { ESP_LOGE(TAG_GPIO, "GPIO initialization failed for pin %d, error code: %d" , pin, ret); } else { ESP_LOGI(TAG_GPIO, "GPIO pin %d initialized in %s mode" , pin, (mode == GPIO_MODE_OUTPUT) ? "OUTPUT" : "INPUT" ); } } void gpio_set_level (gpio_pin_t pin, gpio_level_t level) { gpio_set_level(pin, (level == GPIO_LEVEL_HIGH) ? 1 : 0 ); } gpio_level_t gpio_get_level (gpio_pin_t pin) { return (gpio_get_level(pin) == 1 ) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; }
hal/timer.c
(定时器驱动实现 - 基于 ESP-IDF 软件定时器)
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 #include "hal.h" #include "esp_timer.h" #include "esp_log.h" static const char *TAG_TIMER = "TIMER_DRV" ;typedef struct { esp_timer_handle_t timer_handle; timer_callback_t callback; void * arg; } timer_context_t ; static void timer_event_handler (void * arg) { timer_context_t * timer_ctx = (timer_context_t *)arg; if (timer_ctx && timer_ctx->callback) { timer_ctx->callback(timer_ctx->arg); } } timer_id_t timer_create (uint32_t period_ms, bool repeat, timer_callback_t callback, void * arg) { timer_context_t * timer_ctx = (timer_context_t *)malloc (sizeof (timer_context_t )); if (timer_ctx == NULL ) { ESP_LOGE(TAG_TIMER, "Failed to allocate timer context" ); return 0 ; } timer_ctx->callback = callback; timer_ctx->arg = arg; esp_timer_create_args_t timer_args = { .callback = timer_event_handler, .arg = timer_ctx, .name = "app_timer" }; esp_err_t ret = esp_timer_create(&timer_args, &(timer_ctx->timer_handle)); if (ret != ESP_OK) { ESP_LOGE(TAG_TIMER, "Failed to create timer, error code: %d" , ret); free (timer_ctx); return 0 ; } return (timer_id_t )timer_ctx; } bool timer_start (timer_id_t timer_id) { timer_context_t * timer_ctx = (timer_context_t *)timer_id; if (timer_ctx == NULL || timer_ctx->timer_handle == NULL ) { ESP_LOGE(TAG_TIMER, "Invalid timer ID" ); return false ; } esp_err_t ret = esp_timer_start_periodic(timer_ctx->timer_handle, period_ms * 1000ULL ); if (ret != ESP_OK) { ESP_LOGE(TAG_TIMER, "Failed to start timer, error code: %d" , ret); return false ; } return true ; } bool timer_stop (timer_id_t timer_id) { timer_context_t * timer_ctx = (timer_context_t *)timer_id; if (timer_ctx == NULL || timer_ctx->timer_handle == NULL ) { ESP_LOGE(TAG_TIMER, "Invalid timer ID" ); return false ; } esp_err_t ret = esp_timer_stop(timer_ctx->timer_handle); if (ret != ESP_OK) { ESP_LOGE(TAG_TIMER, "Failed to stop timer, error code: %d" , ret); return false ; } return true ; } bool timer_delete (timer_id_t timer_id) { timer_context_t * timer_ctx = (timer_context_t *)timer_id; if (timer_ctx == NULL || timer_ctx->timer_handle == NULL ) { ESP_LOGE(TAG_TIMER, "Invalid timer ID" ); return false ; } esp_err_t ret = esp_timer_delete(timer_ctx->timer_handle); if (ret != ESP_OK) { ESP_LOGE(TAG_TIMER, "Failed to delete timer, error code: %d" , ret); return false ; } free (timer_ctx); return true ; }
hal/wifi.c
(WiFi驱动实现 - 基于 ESP-IDF WiFi API)
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 #include "hal.h" #include "esp_wifi.h" #include "esp_log.h" #include "esp_event.h" #include "string.h" static const char *TAG_WIFI = "WIFI_DRV" ;static bool wifi_connected = false ;static char wifi_ip_address[16 ] = {0 }; static void wifi_event_handler (void * arg, esp_event_base_t event_base, int32_t event_id, void * event_data) { if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { esp_wifi_connect(); } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { wifi_connected = false ; ESP_LOGI(TAG_WIFI, "WiFi disconnected, attempting to reconnect..." ); esp_wifi_connect(); } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { ip_event_got_ip_t * event = (ip_event_got_ip_t *) event_data; sprintf (wifi_ip_address, IPSTR, IP2STR(&event->ip_info.ip)); wifi_connected = true ; ESP_LOGI(TAG_WIFI, "WiFi connected, IP address: %s" , wifi_ip_address); } } bool wifi_init (void ) { esp_netif_init(); esp_event_loop_create_default(); esp_netif_create_default_wifi_sta(); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); esp_wifi_init(&cfg); esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL , NULL ); esp_event_handler_instance_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL , NULL ); wifi_config_t wifi_config = { .sta = { .ssid = "YOUR_WIFI_SSID" , .password = "YOUR_WIFI_PASSWORD" , .threshold.authmode = WIFI_AUTH_WPA2PSK, .pmf_cfg = { .capable = true , .required = false }, }, }; esp_wifi_set_mode(WIFI_MODE_STA); esp_wifi_set_config(WIFI_IF_STA, &wifi_config); esp_wifi_start(); ESP_LOGI(TAG_WIFI, "WiFi initialization finished" ); return true ; } bool wifi_connect (const char * ssid, const char * password) { wifi_config_t wifi_config = { .sta = { .threshold.authmode = WIFI_AUTH_WPA2PSK, .pmf_cfg = { .capable = true , .required = false }, }, }; strcpy ((char *)wifi_config.sta.ssid, ssid); strcpy ((char *)wifi_config.sta.password, password); esp_wifi_set_config(WIFI_IF_STA, &wifi_config); esp_err_t ret = esp_wifi_connect(); if (ret != ESP_OK) { ESP_LOGE(TAG_WIFI, "WiFi connect failed, error code: %d" , ret); return false ; } ESP_LOGI(TAG_WIFI, "Connecting to WiFi network: %s..." , ssid); return true ; } bool wifi_disconnect (void ) { esp_err_t ret = esp_wifi_disconnect(); if (ret != ESP_OK) { ESP_LOGE(TAG_WIFI, "WiFi disconnect failed, error code: %d" , ret); return false ; } wifi_connected = false ; ESP_LOGI(TAG_WIFI, "WiFi disconnected" ); return true ; } bool wifi_is_connected (void ) { return wifi_connected; } const char * wifi_get_ip_address (void ) { if (wifi_connected) { return wifi_ip_address; } else { return "0.0.0.0" ; } }
hal/vfd.c
(VFD驱动实现 - 示例代码,需要根据实际VFD模组修改)
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 #include "hal.h" #include "esp_log.h" #include "string.h" #include "stdio.h" static const char *TAG_VFD = "VFD_DRV" ;#define VFD_DATA_PIN GPIO_PIN_21 #define VFD_CLK_PIN GPIO_PIN_22 #define VFD_CS_PIN GPIO_PIN_23 bool vfd_init (void ) { gpio_init(VFD_DATA_PIN, GPIO_MODE_OUTPUT); gpio_init(VFD_CLK_PIN, GPIO_MODE_OUTPUT); gpio_init(VFD_CS_PIN, GPIO_MODE_OUTPUT); gpio_set_level(VFD_CS_PIN, GPIO_LEVEL_HIGH); ESP_LOGI(TAG_VFD, "VFD driver initialized" ); return true ; } bool vfd_clear_screen (void ) { for (int i = 0 ; i < 20 ; i++) { vfd_display_char(' ' , i); } return true ; } static void vfd_send_byte (uint8_t data) { gpio_set_level(VFD_CS_PIN, GPIO_LEVEL_LOW); for (int i = 0 ; i < 8 ; i++) { gpio_set_level(VFD_CLK_PIN, GPIO_LEVEL_LOW); if ((data >> i) & 0x01 ) { gpio_set_level(VFD_DATA_PIN, GPIO_LEVEL_HIGH); } else { gpio_set_level(VFD_DATA_PIN, GPIO_LEVEL_LOW); } gpio_set_level(VFD_CLK_PIN, GPIO_LEVEL_HIGH); } gpio_set_level(VFD_CS_PIN, GPIO_LEVEL_HIGH); } bool vfd_display_char (char ch, uint8_t pos) { uint8_t char_code = (uint8_t )ch; uint8_t pos_cmd = 0x80 + pos; vfd_send_byte(pos_cmd); vfd_send_byte(char_code); return true ; } bool vfd_display_string (const char * str, uint8_t pos) { for (int i = 0 ; str[i] != '\0' ; i++) { vfd_display_char(str[i], pos + i); } return true ; } bool vfd_display_number (uint32_t num, uint8_t pos) { char num_str[11 ]; sprintf (num_str, "%lu" , num); vfd_display_string(num_str, pos); return true ; }
hal/keypad.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 #include "hal.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG_KEYPAD = "KEYPAD_DRV" ;#define KEYPAD_ROW_1 GPIO_PIN_15 #define KEYPAD_ROW_2 GPIO_PIN_16 #define KEYPAD_ROW_3 GPIO_PIN_17 #define KEYPAD_ROW_4 GPIO_PIN_18 #define KEYPAD_COL_1 GPIO_PIN_19 #define KEYPAD_COL_2 GPIO_PIN_20 #define KEYPAD_COL_3 GPIO_PIN_21 #define KEYPAD_COL_4 GPIO_PIN_22 static const gpio_pin_t keypad_rows[] = {KEYPAD_ROW_1, KEYPAD_ROW_2, KEYPAD_ROW_3, KEYPAD_ROW_4};static const gpio_pin_t keypad_cols[] = {KEYPAD_COL_1, KEYPAD_COL_2, KEYPAD_COL_3, KEYPAD_COL_4};bool keypad_init (void ) { for (int i = 0 ; i < sizeof (keypad_rows) / sizeof (keypad_rows[0 ]); i++) { gpio_init(keypad_rows[i], GPIO_MODE_OUTPUT); gpio_set_level(keypad_rows[i], GPIO_LEVEL_HIGH); } for (int i = 0 ; i < sizeof (keypad_cols) / sizeof (keypad_cols[0 ]); i++) { gpio_init(keypad_cols[i], GPIO_MODE_INPUT); } ESP_LOGI(TAG_KEYPAD, "Keypad driver initialized" ); return true ; } uint8_t keypad_scan (void ) { for (int row = 0 ; row < sizeof (keypad_rows) / sizeof (keypad_rows[0 ]); row++) { gpio_set_level(keypad_rows[row], GPIO_LEVEL_LOW); for (int col = 0 ; col < sizeof (keypad_cols) / sizeof (keypad_cols[0 ]); col++) { if (gpio_get_level(keypad_cols[col]) == GPIO_LEVEL_LOW) { vTaskDelay(pdMS_TO_TICKS(20 )); if (gpio_get_level(keypad_cols[col]) == GPIO_LEVEL_LOW) { gpio_set_level(keypad_rows[row], GPIO_LEVEL_HIGH); return (row * 4 + col + 1 ); } } } gpio_set_level(keypad_rows[row], GPIO_LEVEL_HIGH); } return 0 ; }
(2) 服务层 (Service Layer)
service/service.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 #ifndef SERVICE_SERVICE_H #define SERVICE_SERVICE_H #include <stdint.h> #include <stdbool.h> typedef struct { int year; int month; int day; int hour; int minute; int second; } time_t ; bool time_manager_init (void ) ;bool time_manager_sync_ntp (void ) ;time_t time_manager_get_current_time (void ) ;const char * time_manager_format_time (const time_t * time, const char * format) ; bool config_manager_init (void ) ;bool config_manager_load_config (void ) ;bool config_manager_save_config (void ) ;const char * config_manager_get_wifi_ssid (void ) ;const char * config_manager_get_wifi_password (void ) ;void config_manager_set_wifi_ssid (const char * ssid) ;void config_manager_set_wifi_password (const char * password) ;bool wifi_manager_init (void ) ;bool wifi_manager_connect_ap (const char * ssid, const char * password) ;bool wifi_manager_disconnect_ap (void ) ;bool wifi_manager_is_connected (void ) ;const char * wifi_manager_get_ip_address (void ) ;typedef enum { KEY_NONE = 0 , KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_STAR, KEY_0, KEY_HASH, KEY_A, KEY_B, KEY_C, KEY_D, KEY_MAX } key_event_t ; key_event_t keypad_handler_scan_key (void ) ;void keypad_handler_register_callback (void (*callback)(key_event_t key)) ; #endif
service/time_manager.c
(时间管理模块实现 - 基于 sntp)
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 #include "service.h" #include "hal.h" #include "esp_sntp.h" #include "esp_log.h" #include "time.h" #include "string.h" #include "stdio.h" static const char *TAG_TIME = "TIME_MGR" ;static time_t current_time;static bool time_synced = false ;void time_sync_notification_cb (struct timeval *tv) { ESP_LOGI(TAG_TIME, "Time synchronized from NTP server" ); time_synced = true ; } bool time_manager_init (void ) { sntp_setoperatingmode(SNTP_OPMODE_POLL); sntp_setservername(0 , "pool.ntp.org" ); sntp_set_time_sync_notification_cb(time_sync_notification_cb); sntp_init(); setenv("TZ" , "CST-8" , 1 ); tzset(); ESP_LOGI(TAG_TIME, "Time manager initialized" ); return true ; } bool time_manager_sync_ntp (void ) { if (!wifi_manager_is_connected()) { ESP_LOGW(TAG_TIME, "WiFi not connected, cannot sync time from NTP" ); return false ; } if (time_synced) { ESP_LOGI(TAG_TIME, "Time already synchronized" ); return true ; } int retry = 0 ; const int retry_count = 10 ; while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { ESP_LOGI(TAG_TIME, "Waiting for system time to be set... (%d/%d)" , retry, retry_count); vTaskDelay(pdMS_TO_TICKS(2000 )); } if (sntp_get_sync_status() != SNTP_SYNC_STATUS_RESET) { time_synced = true ; ESP_LOGI(TAG_TIME, "NTP synchronization successful" ); return true ; } else { ESP_LOGE(TAG_TIME, "NTP synchronization failed after %d retries" , retry_count); return false ; } } time_t time_manager_get_current_time (void ) { struct tm timeinfo ; ::time(¤t_time); localtime_r(¤t_time, &timeinfo); time_t app_time; app_time.year = timeinfo.tm_year + 1900 ; app_time.month = timeinfo.tm_mon + 1 ; app_time.day = timeinfo.tm_mday; app_time.hour = timeinfo.tm_hour; app_time.minute = timeinfo.tm_min; app_time.second = timeinfo.tm_sec; return app_time; } const char * time_manager_format_time (const time_t * time, const char * format) { static char time_str_buf[64 ]; struct tm timeinfo ; timeinfo.tm_year = time->year - 1900 ; timeinfo.tm_mon = time->month - 1 ; timeinfo.tm_mday = time->day; timeinfo.tm_hour = time->hour; timeinfo.tm_min = time->minute; timeinfo.tm_sec = time->second; timeinfo.tm_isdst = -1 ; mktime(&timeinfo); strftime(time_str_buf, sizeof (time_str_buf), format, &timeinfo); return time_str_buf; }
service/config_manager.c
(配置管理模块实现 - 基于 ESP-IDF NVS)
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 #include "service.h" #include "nvs_flash.h" #include "nvs.h" #include "esp_log.h" #include "string.h" static const char *TAG_CONFIG = "CONFIG_MGR" ;static nvs_handle_t nvs_handle;#define NVS_WIFI_SSID_KEY "wifi_ssid" #define NVS_WIFI_PASSWORD_KEY "wifi_password" static char wifi_ssid_config[32 ] = "YOUR_WIFI_SSID" ; static char wifi_password_config[64 ] = "YOUR_WIFI_PASSWORD" ; bool config_manager_init (void ) { esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); ret = nvs_open("config" , NVS_READWRITE, &nvs_handle); if (ret != ESP_OK) { ESP_LOGE(TAG_CONFIG, "Error opening NVS namespace! error code: %d" , ret); return false ; } ESP_LOGI(TAG_CONFIG, "NVS initialized" ); return true ; } bool config_manager_load_config (void ) { size_t ssid_len = sizeof (wifi_ssid_config); esp_err_t ret = nvs_get_str(nvs_handle, NVS_WIFI_SSID_KEY, wifi_ssid_config, &ssid_len); if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) { ESP_LOGE(TAG_CONFIG, "Error reading WiFi SSID from NVS! error code: %d" , ret); return false ; } if (ret == ESP_ERR_NVS_NOT_FOUND) { ESP_LOGI(TAG_CONFIG, "WiFi SSID not found in NVS, using default value" ); } else { ESP_LOGI(TAG_CONFIG, "WiFi SSID loaded from NVS: %s" , wifi_ssid_config); } size_t password_len = sizeof (wifi_password_config); ret = nvs_get_str(nvs_handle, NVS_WIFI_PASSWORD_KEY, wifi_password_config, &password_len); if (ret != ESP_OK && ret != ESP_ERR_NVS_NOT_FOUND) { ESP_LOGE(TAG_CONFIG, "Error reading WiFi password from NVS! error code: %d" , ret); return false ; } if (ret == ESP_ERR_NVS_NOT_FOUND) { ESP_LOGI(TAG_CONFIG, "WiFi password not found in NVS, using default value" ); } return true ; } bool config_manager_save_config (void ) { esp_err_t ret = nvs_set_str(nvs_handle, NVS_WIFI_SSID_KEY, wifi_ssid_config); if (ret != ESP_OK) { ESP_LOGE(TAG_CONFIG, "Error writing WiFi SSID to NVS! error code: %d" , ret); return false ; } ret = nvs_set_str(nvs_handle, NVS_WIFI_PASSWORD_KEY, wifi_password_config); if (ret != ESP_OK) { ESP_LOGE(TAG_CONFIG, "Error writing WiFi password to NVS! error code: %d" , ret); return false ; } ret = nvs_commit(nvs_handle); if (ret != ESP_OK) { ESP_LOGE(TAG_CONFIG, "Error committing NVS changes! error code: %d" , ret); return false ; } ESP_LOGI(TAG_CONFIG, "Configuration saved to NVS" ); return true ; } const char * config_manager_get_wifi_ssid (void ) { return wifi_ssid_config; } const char * config_manager_get_wifi_password (void ) { return wifi_password_config; } void config_manager_set_wifi_ssid (const char * ssid) { strncpy (wifi_ssid_config, ssid, sizeof (wifi_ssid_config) - 1 ); wifi_ssid_config[sizeof (wifi_ssid_config) - 1 ] = '\0' ; } void config_manager_set_wifi_password (const char * password) { strncpy (wifi_password_config, password, sizeof (wifi_password_config) - 1 ); wifi_password_config[sizeof (wifi_password_config) - 1 ] = '\0' ; }
service/wifi_manager.c
(WiFi管理模块实现)
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 "service.h" #include "hal.h" #include "esp_log.h" #include "string.h" static const char *TAG_WIFI_MGR = "WIFI_MGR" ;bool wifi_manager_init (void ) { if (!wifi_init()) { ESP_LOGE(TAG_WIFI_MGR, "Failed to initialize WiFi driver" ); return false ; } ESP_LOGI(TAG_WIFI_MGR, "WiFi manager initialized" ); return true ; } bool wifi_manager_connect_ap (const char * ssid, const char * password) { if (!wifi_connect(ssid, password)) { ESP_LOGE(TAG_WIFI_MGR, "Failed to connect to WiFi AP: %s" , ssid); return false ; } ESP_LOGI(TAG_WIFI_MGR, "Connecting to WiFi AP: %s..." , ssid); return true ; } bool wifi_manager_disconnect_ap (void ) { if (!wifi_disconnect()) { ESP_LOGE(TAG_WIFI_MGR, "Failed to disconnect from WiFi AP" ); return false ; } ESP_LOGI(TAG_WIFI_MGR, "Disconnected from WiFi AP" ); return true ; } bool wifi_manager_is_connected (void ) { return wifi_is_connected(); } const char * wifi_manager_get_ip_address (void ) { return wifi_get_ip_address(); }
service/keypad_handler.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 #include "service.h" #include "hal.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG_KEYPAD_HANDLER = "KEYPAD_HANDLER" ;static void (*key_event_callback) (key_event_t key) = NULL ; key_event_t keypad_handler_scan_key (void ) { uint8_t keypad_value = keypad_scan(); key_event_t key_event = KEY_NONE; switch (keypad_value) { case 1 : key_event = KEY_1; break ; case 2 : key_event = KEY_2; break ; case 3 : key_event = KEY_3; break ; case 4 : key_event = KEY_4; break ; case 5 : key_event = KEY_5; break ; case 6 : key_event = KEY_6; break ; case 7 : key_event = KEY_7; break ; case 8 : key_event = KEY_8; break ; case 9 : key_event = KEY_9; break ; case 10 : key_event = KEY_STAR; break ; case 11 : key_event = KEY_0; break ; case 12 : key_event = KEY_HASH; break ; case 13 : key_event = KEY_A; break ; case 14 : key_event = KEY_B; break ; case 15 : key_event = KEY_C; break ; case 16 : key_event = KEY_D; break ; default : key_event = KEY_NONE; break ; } return key_event; } void keypad_handler_register_callback (void (*callback)(key_event_t key)) { key_event_callback = callback; } void keypad_handler_task (void *pvParameters) { while (1 ) { key_event_t key = keypad_handler_scan_key(); if (key != KEY_NONE) { ESP_LOGI(TAG_KEYPAD_HANDLER, "Key pressed: %d" , key); if (key_event_callback != NULL ) { key_event_callback(key); } } vTaskDelay(pdMS_TO_TICKS(50 )); } } bool keypad_handler_start (void ) { if (!keypad_init()) { ESP_LOGE(TAG_KEYPAD_HANDLER, "Failed to initialize keypad driver" ); return false ; } BaseType_t task_ret = xTaskCreate(keypad_handler_task, "KeypadHandlerTask" , 2048 , NULL , 10 , NULL ); if (task_ret != pdPASS) { ESP_LOGE(TAG_KEYPAD_HANDLER, "Failed to create keypad handler task" ); return false ; } ESP_LOGI(TAG_KEYPAD_HANDLER, "Keypad handler started" ); return true ; }
(3) 应用层 (Application Layer)
app/app.h
(应用层头文件)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #ifndef APP_APP_H #define APP_APP_H #include <stdint.h> #include <stdbool.h> #include "service.h" bool ui_manager_init (void ) ;void ui_manager_display_time (const time_t * time) ;void ui_manager_display_message (const char * msg) ;void ui_manager_show_menu (void ) ; bool clock_app_init (void ) ;void clock_app_run (void ) ;void clock_app_handle_key_event (key_event_t key) ;#endif
app/ui_manager.c
(UI 管理模块实现)
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 #include "app.h" #include "hal.h" #include "service.h" #include "esp_log.h" #include "string.h" static const char *TAG_UI = "UI_MGR" ;bool ui_manager_init (void ) { if (!vfd_init()) { ESP_LOGE(TAG_UI, "Failed to initialize VFD display driver" ); return false ; } vfd_clear_screen(); ESP_LOGI(TAG_UI, "UI manager initialized" ); return true ; } void ui_manager_display_time (const time_t * time) { char time_str[20 ]; sprintf (time_str, "%02d:%02d:%02d" , time->hour, time->minute, time->second); vfd_display_string(time_str, 0 ); } void ui_manager_display_message (const char * msg) { vfd_clear_screen(); vfd_display_string(msg, 0 ); } void ui_manager_show_menu (void ) { vfd_clear_screen(); vfd_display_string("Menu:" , 0 ); vfd_display_string("1.WiFi Set" , 1 ); vfd_display_string("2.Time Set" , 2 ); vfd_display_string("3.Display" , 3 ); }
app/clock_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 #include "app.h" #include "service.h" #include "hal.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG_CLOCK_APP = "CLOCK_APP" ;static bool in_menu = false ; bool clock_app_init (void ) { if (!ui_manager_init()) { ESP_LOGE(TAG_CLOCK_APP, "Failed to initialize UI manager" ); return false ; } if (!time_manager_init()) { ESP_LOGE(TAG_CLOCK_APP, "Failed to initialize time manager" ); return false ; } if (!config_manager_init()) { ESP_LOGE(TAG_CLOCK_APP, "Failed to initialize config manager" ); return false ; } if (!config_manager_load_config()) { ESP_LOGW(TAG_CLOCK_APP, "Failed to load configuration, using default values" ); } if (!wifi_manager_init()) { ESP_LOGE(TAG_CLOCK_APP, "Failed to initialize WiFi manager" ); return false ; } if (!keypad_handler_start()) { ESP_LOGE(TAG_CLOCK_APP, "Failed to start keypad handler" ); return false ; } keypad_handler_register_callback(clock_app_handle_key_event); ESP_LOGI(TAG_CLOCK_APP, "Clock application initialized" ); return true ; } void clock_app_run (void ) { if (wifi_manager_connect_ap(config_manager_get_wifi_ssid(), config_manager_get_wifi_password())) { time_manager_sync_ntp(); } else { ui_manager_display_message("WiFi Connect Failed" ); vTaskDelay(pdMS_TO_TICKS(3000 )); } while (1 ) { if (!in_menu) { time_t current_time = time_manager_get_current_time(); ui_manager_display_time(¤t_time); } vTaskDelay(pdMS_TO_TICKS(1000 )); } } void clock_app_handle_key_event (key_event_t key) { if (in_menu) { switch (key) { case KEY_1: ui_manager_display_message("WiFi Setting..." ); break ; case KEY_2: ui_manager_display_message("Time Setting..." ); break ; case KEY_3: ui_manager_display_message("Display Set..." ); break ; case KEY_HASH: in_menu = false ; ui_manager_display_message("Exit Menu" ); vTaskDelay(pdMS_TO_TICKS(1000 )); vfd_clear_screen(); break ; default : break ; } } else { switch (key) { case KEY_STAR: in_menu = true ; ui_manager_show_menu(); break ; default : break ; } } }
(4) 主程序 main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <stdio.h> #include "app.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *TAG_MAIN = "MAIN" ;void app_main (void ) { ESP_LOGI(TAG_MAIN, "Starting WiFi VFD Clock Application..." ); if (!clock_app_init()) { ESP_LOGE(TAG_MAIN, "Clock application initialization failed!" ); while (1 ); } clock_app_run(); ESP_LOGI(TAG_MAIN, "Application finished (should not reach here)" ); }
测试验证与维护升级
测试验证:
单元测试: 针对每个模块进行单元测试,例如测试VFD驱动的显示功能、键盘驱动的扫描功能、时间管理模块的时间同步功能等。可以使用模拟器或者开发板进行单元测试。
集成测试: 将各个模块集成起来进行测试,验证模块之间的协同工作是否正常。例如测试WiFi连接、NTP时间同步、VFD时间显示的完整流程。
系统测试: 进行全面的系统测试,模拟实际使用场景,测试系统的稳定性、可靠性、性能和用户体验。包括长时间运行测试、异常情况测试、用户交互测试等。
维护升级:
模块化设计: 分层模块化的架构使得系统的维护和升级更加容易。修改或添加功能时,只需要修改相应的模块,而不会影响其他模块。
代码注释: 清晰的代码注释可以提高代码的可读性,方便后续的维护人员理解代码逻辑。
版本控制: 使用版本控制系统(例如Git)管理代码,方便代码的版本管理、bug修复和功能升级。
OTA升级: 对于嵌入式系统,可以考虑实现OTA (Over-The-Air) 升级功能,方便远程更新固件,修复bug或添加新功能。
总结
以上代码提供了一个基于ESP32-C3、VFD显示模组和矩阵键盘的WiFi时钟系统的完整软件设计框架和C代码实现。 该设计采用了分层模块化的架构,将系统划分为硬件抽象层、服务层和应用层,每个层级包含多个独立的模块,实现了功能的解耦和代码的复用。 代码中包含了GPIO驱动、定时器驱动、WiFi驱动、VFD驱动、键盘驱动等HAL层模块,时间管理、配置管理、WiFi管理、按键处理等服务层模块,以及UI管理和时钟应用等应用层模块。
这套代码框架具备良好的可扩展性和可维护性,可以作为您嵌入式VFD WiFi时钟项目的坚实基础。 在实际应用中,您需要根据具体的硬件连接和VFD模组型号,调整HAL层驱动代码,并根据实际需求完善应用层的功能逻辑,例如添加更多的显示模式、设置选项、用户交互功能等。
希望这份详细的设计方案和代码实现能够帮助您成功开发出可靠、高效、可扩展的嵌入式VFD WiFi时钟系统!