编程技术分享

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

0%

简介:基于W600(WiFi模块)+RT-Thread(RTOS)+SU-03T(离线语音模块)的双IN12辉光管番茄钟。

好的,作为一名高级嵌入式软件开发工程师,我将为您详细解析并实现这个基于W600+RT-Thread+SU-03T的双IN12辉光管番茄钟项目。这个项目充分展示了从需求分析到最终产品落地的完整嵌入式系统开发流程,体现了可靠性、高效性和可扩展性的设计理念。
关注微信公众号,提前获取相关推文

项目概述

本项目旨在设计并实现一个智能化的辉光管番茄钟。它结合了经典的辉光管显示技术和现代的嵌入式系统技术,提供了一种独特的用户体验。核心功能包括:

  • 番茄钟功能: 提供标准的番茄工作法计时功能,帮助用户高效工作和休息。
  • 辉光管显示: 使用IN12辉光管作为时间显示,复古而美观。
  • 离线语音控制: 通过SU-03T离线语音模块,用户可以通过语音指令控制番茄钟,无需手动操作。
  • WiFi联网功能: 利用W600 WiFi模块,可以实现时间同步、固件升级等功能,并为未来的扩展功能预留空间。
  • RT-Thread RTOS: 采用RT-Thread实时操作系统,保证系统的实时性和稳定性,并简化多任务管理和资源调度。

系统架构设计

为了构建一个可靠、高效、可扩展的系统平台,我将采用分层架构模块化设计的思想。这种架构将系统划分为不同的层次和模块,每个层次和模块负责特定的功能,层次之间通过清晰定义的接口进行通信。

1. 硬件架构

系统的硬件组成主要包括以下几个核心模块:

  • 主控MCU: 选择一款合适的MCU作为主控芯片,负责运行RT-Thread操作系统,协调各个硬件模块的工作,并执行核心的番茄钟逻辑。考虑到W600模块通常也集成了MCU,我们可以考虑使用W600模块内置的MCU作为主控,或者选择外部MCU与W600协同工作。为了简化设计,假设我们使用W600模块内置的MCU (例如 ESP8266 或类似的架构)。
  • W600 WiFi模块: 提供WiFi联网功能,用于NTP时间同步、OTA固件升级以及可能的远程控制功能。
  • SU-03T 离线语音模块: 负责语音识别,解析用户语音指令,并将指令传递给主控MCU。
  • IN12 辉光管驱动电路: 包括高压电源模块和辉光管驱动芯片(例如HV5122或分立元件驱动电路),用于驱动IN12辉光管显示数字。
  • 电源模块: 为整个系统提供稳定的电源。
  • 用户交互接口: 可能包括按键(作为语音控制的补充或备用方案),以及辉光管显示本身。

2. 软件架构

软件架构将采用分层设计,主要分为以下几个层次:

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 封装底层硬件操作,提供统一的接口给上层使用,屏蔽硬件差异,提高代码的可移植性。HAL层包括GPIO驱动、UART驱动、SPI驱动、I2C驱动、定时器驱动等。
  • RT-Thread 内核层 (Kernel Layer): RT-Thread实时操作系统内核,负责任务调度、内存管理、线程同步、中断管理等核心功能,为上层应用提供实时、稳定的运行环境。
  • 中间件层 (Middleware Layer): 提供常用的中间件组件,例如:
    • 网络协议栈: TCP/IP协议栈,支持WiFi联网功能。
    • NTP客户端: 用于从网络时间服务器同步时间。
    • 语音识别驱动: 封装SU-03T模块的通信协议,提供语音指令解析接口。
    • 辉光管显示驱动: 控制辉光管显示的逻辑。
    • 番茄钟逻辑模块: 实现番茄钟的核心计时和控制逻辑。
    • 配置管理模块: 负责系统配置参数的存储和管理。
  • 应用层 (Application Layer): 基于中间件层提供的接口,实现具体的应用功能,例如:
    • 主应用程序: 负责初始化系统,创建任务,协调各个模块的工作。
    • 语音控制任务: 负责接收和处理语音指令。
    • 时间显示任务: 负责更新辉光管显示的时间。
    • 网络管理任务: 负责WiFi连接管理和NTP时间同步。

模块化设计

在每个层次内部,我们也将采用模块化设计,将功能划分为独立的模块,模块之间通过接口进行交互,提高代码的可维护性和可复用性。例如:

  • 辉光管显示模块: 可以进一步细分为数字显示模块、位选控制模块、高压控制模块等。
  • 番茄钟逻辑模块: 可以细分为计时模块、状态管理模块、提示音模块等。
  • 语音识别驱动模块: 可以细分为串口通信模块、指令解析模块、错误处理模块等。

代码实现 (C语言)

下面我将逐步给出关键模块的C代码实现,并进行详细的解释。由于篇幅限制,这里只提供核心代码框架和关键功能的实现,完整的代码实现会更加详细和完善。

1. 硬件抽象层 (HAL)

  • GPIO 驱动 (hal_gpio.h / hal_gpio.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
// hal_gpio.h
#ifndef __HAL_GPIO_H__
#define __HAL_GPIO_H__

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

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} gpio_mode_t;

typedef enum {
GPIO_PULL_NONE,
GPIO_PULL_UP,
GPIO_PULL_DOWN
} gpio_pull_t;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} gpio_level_t;

typedef struct {
uint32_t pin; // GPIO引脚号
gpio_mode_t mode; // GPIO模式
gpio_pull_t pull; // 上拉/下拉配置
} gpio_config_t;

// 初始化GPIO
int hal_gpio_init(const gpio_config_t *config);

// 设置GPIO输出电平
int hal_gpio_set_level(uint32_t pin, gpio_level_t level);

// 读取GPIO输入电平
gpio_level_t hal_gpio_get_level(uint32_t pin);

#endif // __HAL_GPIO_H__

// hal_gpio.c (示例 - 针对ESP8266或其他类似MCU,具体实现需参考硬件平台SDK)
#include "hal_gpio.h"
#include "stdio.h" // For printf (for debugging)

int hal_gpio_init(const gpio_config_t *config) {
printf("HAL GPIO Init: Pin %d, Mode %d, Pull %d\n", config->pin, config->mode, config->pull);
// *** 实际硬件初始化代码 (例如 ESP-IDF 的 gpio_config 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
return 0;
}

int hal_gpio_set_level(uint32_t pin, gpio_level_t level) {
printf("HAL GPIO Set Level: Pin %d, Level %d\n", pin, level);
// *** 实际设置GPIO电平代码 (例如 ESP-IDF 的 gpio_set_level 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
return 0;
}

gpio_level_t hal_gpio_get_level(uint32_t pin) {
printf("HAL GPIO Get Level: Pin %d\n", pin);
// *** 实际读取GPIO电平代码 (例如 ESP-IDF 的 gpio_get_level 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
return GPIO_LEVEL_LOW; // 默认返回LOW,实际需根据硬件平台SDK实现
}
  • UART 驱动 (hal_uart.h / hal_uart.c) - 用于SU-03T通信
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
// hal_uart.h
#ifndef __HAL_UART_H__
#define __HAL_UART_H__

#include <stdint.h>

typedef struct {
uint32_t baudrate; // 波特率
uint32_t data_bits; // 数据位 (例如 8)
uint32_t stop_bits; // 停止位 (例如 1)
// ... 其他 UART 配置参数 ...
} uart_config_t;

// 初始化 UART
int hal_uart_init(uint32_t uart_id, const uart_config_t *config);

// 发送数据
int hal_uart_send_data(uint32_t uart_id, const uint8_t *data, uint32_t len);

// 接收数据 (非阻塞)
int hal_uart_receive_data(uint32_t uart_id, uint8_t *buffer, uint32_t buf_len);

// 注册 UART 接收回调函数 (中断方式接收)
typedef void (*uart_rx_callback_t)(uint8_t data);
int hal_uart_register_rx_callback(uint32_t uart_id, uart_rx_callback_t callback);

#endif // __HAL_UART_H__

// hal_uart.c (示例 - 针对ESP8266或其他类似MCU,具体实现需参考硬件平台SDK)
#include "hal_uart.h"
#include "stdio.h"

int hal_uart_init(uint32_t uart_id, const uart_config_t *config) {
printf("HAL UART Init: UART %d, Baudrate %d\n", uart_id, config->baudrate);
// *** 实际 UART 初始化代码 (例如 ESP-IDF 的 uart_param_config, uart_driver_install 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
return 0;
}

int hal_uart_send_data(uint32_t uart_id, const uint8_t *data, uint32_t len) {
printf("HAL UART Send Data: UART %d, Len %d\n", uart_id, len);
// *** 实际 UART 发送数据代码 (例如 ESP-IDF 的 uart_write_bytes 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
for(uint32_t i=0; i<len; ++i) {
printf("%c", data[i]); // 打印发送的数据
}
printf("\n");
return 0;
}

int hal_uart_receive_data(uint32_t uart_id, uint8_t *buffer, uint32_t buf_len) {
// *** 实际 UART 接收数据代码 (例如 ESP-IDF 的 uart_read_bytes 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
// 假设没有数据可接收,返回0
return 0;
}

int hal_uart_register_rx_callback(uint32_t uart_id, uart_rx_callback_t callback) {
printf("HAL UART Register RX Callback: UART %d\n", uart_id);
// *** 实际 UART 注册接收回调函数代码 (例如 ESP-IDF 的 uart_isr_register 函数) ***
// 这里仅为示例,实际需要根据硬件平台SDK实现
return 0;
}
  • 定时器驱动 (hal_timer.h / hal_timer.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// hal_timer.h
#ifndef __HAL_TIMER_H__
#define __HAL_TIMER_H__

#include <stdint.h>

typedef void (*timer_callback_t)(void *arg);

// 初始化定时器
int hal_timer_init(uint32_t timer_id, uint32_t period_ms, bool auto_reload, timer_callback_t callback, void *arg);

// 启动定时器
int hal_timer_start(uint32_t timer_id);

// 停止定时器
int hal_timer_stop(uint32_t timer_id);

#endif // __HAL_TIMER_H__

// hal_timer.c (示例 - 针对ESP8266或其他类似MCU,具体实现需参考硬件平台SDK)
#include "hal_timer.h"
#include "stdio.h"
#include "rtthread.h" // 假设使用 RT-Thread 的定时器

static void timer_wrapper_callback(void *parameter) {
struct rt_timer *timer = (struct rt_timer *)parameter;
timer_callback_t callback = (timer_callback_t)timer->user_data;
if (callback) {
callback(timer->arg); // 调用用户注册的回调函数
}
}

int hal_timer_init(uint32_t timer_id, uint32_t period_ms, bool auto_reload, timer_callback_t callback, void *arg) {
printf("HAL Timer Init: Timer %d, Period %dms, Auto Reload %d\n", timer_id, period_ms, auto_reload);
struct rt_timer *timer = rt_timer_create("hal_timer", timer_wrapper_callback,
rt_timer_user_data(callback), // 存储回调函数到 user_data
period_ms,
RT_TIMER_FLAG_ONE_SHOT | (auto_reload ? RT_TIMER_FLAG_PERIODIC : 0));
if (timer == RT_NULL) {
printf("HAL Timer Init Failed: Timer %d\n", timer_id);
return -1;
}
timer->arg = arg; // 存储用户参数
return 0;
}

int hal_timer_start(uint32_t timer_id) {
printf("HAL Timer Start: Timer %d\n", timer_id);
struct rt_timer *timer = rt_timer_find("hal_timer"); // 假设定时器名称固定为 "hal_timer"
if (timer) {
rt_timer_start(timer);
return 0;
} else {
printf("HAL Timer Start Failed: Timer %d not found\n", timer_id);
return -1;
}
}

int hal_timer_stop(uint32_t timer_id) {
printf("HAL Timer Stop: Timer %d\n", timer_id);
struct rt_timer *timer = rt_timer_find("hal_timer"); // 假设定时器名称固定为 "hal_timer"
if (timer) {
rt_timer_stop(timer);
return 0;
} else {
printf("HAL Timer Stop Failed: Timer %d not found\n", timer_id);
return -1;
}
}

2. 中间件层

  • 辉光管显示驱动 (nixie_display.h / nixie_display.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
// nixie_display.h
#ifndef __NIXIE_DISPLAY_H__
#define __NIXIE_DISPLAY_H__

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

// 初始化辉光管显示
int nixie_display_init(void);

// 显示数字 (两位数)
int nixie_display_show_number(uint8_t number);

// 清空显示
int nixie_display_clear(void);

#endif // __NIXIE_DISPLAY_H__

// nixie_display.c
#include "nixie_display.h"
#include "hal_gpio.h"
#include "rtthread.h"

// 假设使用 GPIO 模拟 74141 驱动,简化实现,实际应用中建议使用专用驱动芯片如 HV5122 或 74141
// 定义 IN12 辉光管的引脚 (需要根据实际硬件连接修改)
#define NIXIE_DIGIT1_PIN_A 12
#define NIXIE_DIGIT1_PIN_B 13
#define NIXIE_DIGIT1_PIN_C 14
#define NIXIE_DIGIT1_PIN_D 15
#define NIXIE_DIGIT2_PIN_A 16
#define NIXIE_DIGIT2_PIN_B 17
#define NIXIE_DIGIT2_PIN_C 18
#define NIXIE_DIGIT2_PIN_D 19

// 共阴极数码管段码 (IN12 辉光管的驱动逻辑与数码管类似,但需要高压驱动)
const uint8_t nixie_digit_codes[10] = {
0b0000, // 0
0b0001, // 1
0b0010, // 2
0b0011, // 3
0b0100, // 4
0b0101, // 5
0b0110, // 6
0b0111, // 7
0b1000, // 8
0b1001 // 9
};

int nixie_display_init(void) {
gpio_config_t config;

// 初始化 Digit 1 引脚
config.mode = GPIO_MODE_OUTPUT;
config.pull = GPIO_PULL_NONE;
config.pin = NIXIE_DIGIT1_PIN_A; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT1_PIN_B; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT1_PIN_C; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT1_PIN_D; hal_gpio_init(&config);

// 初始化 Digit 2 引脚
config.pin = NIXIE_DIGIT2_PIN_A; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT2_PIN_B; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT2_PIN_C; hal_gpio_init(&config);
config.pin = NIXIE_DIGIT2_PIN_D; hal_gpio_init(&config);

nixie_display_clear(); // 初始化时清空显示
return 0;
}

static void set_digit_pins(uint32_t base_pin, uint8_t digit_code) {
hal_gpio_set_level(base_pin + 0, (digit_code & 0x01) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // A
hal_gpio_set_level(base_pin + 1, (digit_code & 0x02) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // B
hal_gpio_set_level(base_pin + 2, (digit_code & 0x04) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // C
hal_gpio_set_level(base_pin + 3, (digit_code & 0x08) ? GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW); // D
// *** 注意: 实际辉光管驱动需要高压控制和位选通逻辑,这里简化为直接控制,实际应用中需要更复杂的驱动电路和逻辑 ***
}

int nixie_display_show_number(uint8_t number) {
if (number > 99) number = 99; // 限制显示范围

uint8_t digit1 = number / 10;
uint8_t digit2 = number % 10;

set_digit_pins(NIXIE_DIGIT1_PIN_A, nixie_digit_codes[digit1]);
set_digit_pins(NIXIE_DIGIT2_PIN_A, nixie_digit_codes[digit2]);

return 0;
}

int nixie_display_clear(void) {
nixie_display_show_number(0); // 显示 00 或 关闭所有段码
return 0;
}
  • SU-03T 语音识别驱动 (su03t_voice.h / su03t_voice.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
// su03t_voice.h
#ifndef __SU03T_VOICE_H__
#define __SU03T_VOICE_H__

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

typedef enum {
VOICE_CMD_UNKNOWN,
VOICE_CMD_START_TIMER,
VOICE_CMD_STOP_TIMER,
VOICE_CMD_PAUSE_TIMER,
VOICE_CMD_RESET_TIMER,
VOICE_CMD_SET_TIME, // 例如 "设置时间 25 分钟"
// ... 其他语音指令 ...
} voice_command_t;

// 初始化 SU-03T 语音模块
int su03t_voice_init(void);

// 注册语音指令回调函数
typedef void (*voice_command_callback_t)(voice_command_t command, uint8_t value);
int su03t_voice_register_command_callback(voice_command_callback_t callback);

#endif // __SU03T_VOICE_H__

// su03t_voice.c
#include "su03t_voice.h"
#include "hal_uart.h"
#include "rtthread.h"
#include <string.h>
#include <stdio.h>

#define SU03T_UART_ID 0 // 假设 SU-03T 使用 UART0

static voice_command_callback_t voice_cmd_callback = NULL;

// SU-03T 指令格式 (需要参考 SU-03T 模块的通信协议文档)
// 假设 SU-03T 发送 JSON 格式的指令,例如: {"cmd":"start"}, {"cmd":"stop"}, {"cmd":"set_time", "value":25}

static void su03t_uart_rx_callback(uint8_t data) {
static uint8_t rx_buffer[128]; // 接收缓冲区
static uint32_t rx_index = 0;

rx_buffer[rx_index++] = data;
if (rx_index >= sizeof(rx_buffer)) rx_index = sizeof(rx_buffer) - 1; // 防止溢出

if (data == '\n' || data == '\r') { // 假设以换行符或回车符作为指令结束
rx_buffer[rx_index] = '\0'; // 字符串结束符
printf("SU-03T RX: %s\n", rx_buffer); // 打印接收到的数据

// *** 解析 JSON 指令 (需要根据实际 SU-03T 协议实现) ***
// 这里只是一个简单的示例,实际解析需要更完善的 JSON 解析库或手动解析
if (strstr((char *)rx_buffer, "\"cmd\":\"start\"")) {
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_START_TIMER, 0);
} else if (strstr((char *)rx_buffer, "\"cmd\":\"stop\"")) {
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_STOP_TIMER, 0);
} else if (strstr((char *)rx_buffer, "\"cmd\":\"pause\"")) {
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_PAUSE_TIMER, 0);
} else if (strstr((char *)rx_buffer, "\"cmd\":\"reset\"")) {
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_RESET_TIMER, 0);
} else if (strstr((char *)rx_buffer, "\"cmd\":\"set_time\"")) {
// 提取时间值 (需要更完善的 JSON 解析)
char *value_str = strstr((char *)rx_buffer, "\"value\":");
if (value_str) {
value_str += strlen("\"value\":");
uint8_t time_value = atoi(value_str); // 简单 atoi 转换,实际需要更健壮的转换
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_SET_TIME, time_value);
}
} else {
if (voice_cmd_callback) voice_cmd_callback(VOICE_CMD_UNKNOWN, 0);
}

rx_index = 0; // 重置接收缓冲区
}
}

int su03t_voice_init(void) {
uart_config_t uart_config;
uart_config.baudrate = 9600; // 假设 SU-03T 默认波特率为 9600
// ... 其他 UART 配置 ...

if (hal_uart_init(SU03T_UART_ID, &uart_config) != 0) {
printf("SU-03T UART Init Failed\n");
return -1;
}

hal_uart_register_rx_callback(SU03T_UART_ID, su03t_uart_rx_callback); // 注册 UART 接收回调

printf("SU-03T Voice Module Init OK\n");
return 0;
}

int su03t_voice_register_command_callback(voice_command_callback_t callback) {
voice_cmd_callback = callback;
return 0;
}
  • 番茄钟逻辑模块 (tomato_timer.h / tomato_timer.c)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
// tomato_timer.h
#ifndef __TOMATO_TIMER_H__
#define __TOMATO_TIMER_H__

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

typedef enum {
TIMER_STATE_IDLE,
TIMER_STATE_RUNNING,
TIMER_STATE_PAUSED,
TIMER_STATE_FINISHED
} timer_state_t;

// 初始化番茄钟
int tomato_timer_init(void);

// 设置番茄钟时间 (分钟)
int tomato_timer_set_duration(uint8_t minutes);

// 启动番茄钟
int tomato_timer_start(void);

// 停止番茄钟
int tomato_timer_stop(void);

// 暂停番茄钟
int tomato_timer_pause(void);

// 重置番茄钟
int tomato_timer_reset(void);

// 获取当前番茄钟状态
timer_state_t tomato_timer_get_state(void);

// 获取剩余时间 (秒)
uint32_t tomato_timer_get_remaining_seconds(void);

#endif // __TOMATO_TIMER_H__

// tomato_timer.c
#include "tomato_timer.h"
#include "nixie_display.h"
#include "hal_timer.h"
#include "rtthread.h"
#include <stdio.h>

#define TIMER_ID 0 // 假设使用 Timer 0

static uint8_t tomato_duration_minutes = 25; // 默认番茄时间 25 分钟
static uint32_t remaining_seconds = 0;
static timer_state_t current_state = TIMER_STATE_IDLE;
static struct rt_timer *timer_handle = RT_NULL;

static void timer_tick_callback(void *arg) {
if (current_state == TIMER_STATE_RUNNING) {
if (remaining_seconds > 0) {
remaining_seconds--;
nixie_display_show_number(remaining_seconds / 60); // 显示分钟
} else {
tomato_timer_stop();
current_state = TIMER_STATE_FINISHED;
printf("Tomato Timer Finished!\n");
// *** 可以添加完成提示音或其他操作 ***
}
}
}

int tomato_timer_init(void) {
nixie_display_init();
nixie_display_show_number(tomato_duration_minutes); // 初始化显示默认时间

timer_handle = rt_timer_create("tomato_timer", timer_tick_callback,
RT_NULL, 1000, // 1秒周期
RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER); // 软件定时器

if (timer_handle == RT_NULL) {
printf("Tomato Timer Init Failed: Timer create\n");
return -1;
}

printf("Tomato Timer Init OK\n");
return 0;
}

int tomato_timer_set_duration(uint8_t minutes) {
if (minutes > 99) minutes = 99; // 限制最大时间
tomato_duration_minutes = minutes;
remaining_seconds = tomato_duration_minutes * 60;
nixie_display_show_number(tomato_duration_minutes);
return 0;
}

int tomato_timer_start(void) {
if (current_state == TIMER_STATE_IDLE || current_state == TIMER_STATE_PAUSED) {
current_state = TIMER_STATE_RUNNING;
if (rt_timer_start(timer_handle) != RT_EOK) {
printf("Tomato Timer Start Failed: rt_timer_start\n");
current_state = TIMER_STATE_IDLE; // 回退状态
return -1;
}
printf("Tomato Timer Started\n");
return 0;
} else {
printf("Tomato Timer Already Running or Finished\n");
return -1; // 或返回其他错误码
}
}

int tomato_timer_stop(void) {
if (current_state == TIMER_STATE_RUNNING || current_state == TIMER_STATE_PAUSED) {
current_state = TIMER_STATE_IDLE;
rt_timer_stop(timer_handle);
nixie_display_show_number(tomato_duration_minutes); // 停止后显示初始时间
printf("Tomato Timer Stopped\n");
return 0;
} else {
printf("Tomato Timer Not Running\n");
return -1;
}
}

int tomato_timer_pause(void) {
if (current_state == TIMER_STATE_RUNNING) {
current_state = TIMER_STATE_PAUSED;
rt_timer_stop(timer_handle);
printf("Tomato Timer Paused\n");
return 0;
} else {
printf("Tomato Timer Not Running\n");
return -1;
}
}

int tomato_timer_reset(void) {
tomato_timer_stop(); // 先停止
remaining_seconds = tomato_duration_minutes * 60;
nixie_display_show_number(tomato_duration_minutes);
current_state = TIMER_STATE_IDLE;
printf("Tomato Timer Reset\n");
return 0;
}

timer_state_t tomato_timer_get_state(void) {
return current_state;
}

uint32_t tomato_timer_get_remaining_seconds(void) {
return remaining_seconds;
}
  • WiFi 网络管理模块 (wifi_manager.h / wifi_manager.c) - 简化的 NTP 同步示例
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
// wifi_manager.h
#ifndef __WIFI_MANAGER_H__
#define __WIFI_MANAGER_H__

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

// 初始化 WiFi
int wifi_manager_init(const char *ssid, const char *password);

// 连接 WiFi
int wifi_manager_connect(void);

// 断开 WiFi
int wifi_manager_disconnect(void);

// 获取 WiFi 连接状态
bool wifi_manager_is_connected(void);

// 同步 NTP 时间
int wifi_manager_sync_ntp_time(void);

#endif // __WIFI_MANAGER_H__

// wifi_manager.c (示例 - 针对W600 或 ESP8266 等 WiFi 模块,具体实现需参考硬件平台SDK)
#include "wifi_manager.h"
#include "rtthread.h"
#include <stdio.h>
#include <string.h>
// *** 需要包含 W600 或 ESP-IDF 等 WiFi SDK 的头文件 ***
// 这里为了示例,假设使用一些通用的函数名,实际需要替换为具体SDK的函数

static char wifi_ssid[32] = "";
static char wifi_password[32] = "";
static bool wifi_connected = false;

int wifi_manager_init(const char *ssid, const char *password) {
strncpy(wifi_ssid, ssid, sizeof(wifi_ssid) - 1);
strncpy(wifi_password, password, sizeof(wifi_password) - 1);
wifi_ssid[sizeof(wifi_ssid) - 1] = '\0';
wifi_password[sizeof(wifi_password) - 1] = '\0';

// *** 初始化 WiFi 模块 (例如 ESP-IDF 的 esp_wifi_init 函数) ***
// *** 设置 WiFi 模式 (STA 模式) ***
// *** 设置 WiFi 事件处理回调 ***
printf("WiFi Manager Init OK\n");
return 0;
}

int wifi_manager_connect(void) {
// *** 配置 WiFi STA 参数 (SSID, Password) ***
// *** 启动 WiFi 连接 (例如 ESP-IDF 的 esp_wifi_start, esp_wifi_connect 函数) ***
printf("WiFi Connecting to SSID: %s\n", wifi_ssid);
// *** 在 WiFi 事件回调中处理连接结果,更新 wifi_connected 标志 ***
wifi_connected = true; // 假设连接成功 (实际需要在事件回调中判断)
return 0;
}

int wifi_manager_disconnect(void) {
// *** 断开 WiFi 连接 (例如 ESP-IDF 的 esp_wifi_disconnect, esp_wifi_stop 函数) ***
wifi_connected = false;
printf("WiFi Disconnected\n");
return 0;
}

bool wifi_manager_is_connected(void) {
return wifi_connected;
}

int wifi_manager_sync_ntp_time(void) {
if (!wifi_manager_is_connected()) {
printf("WiFi Not Connected, Cannot Sync NTP Time\n");
return -1;
}
// *** 初始化 NTP 客户端 (例如 sntp_init 函数) ***
// *** 设置 NTP 服务器地址 (例如 pool.ntp.org) ***
// *** 开始 NTP 同步 (例如 sntp_sync 函数) ***
// *** 获取同步后的时间 (例如 time, localtime 函数) ***
printf("NTP Time Syncing...\n");
// *** 实际 NTP 同步代码,并处理同步结果 ***
printf("NTP Time Sync OK (Placeholder)\n"); // 占位符,实际需要获取并设置系统时间
return 0;
}

3. 应用层

  • 主应用程序 (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
#include <rtthread.h>
#include "tomato_timer.h"
#include "su03t_voice.h"
#include "wifi_manager.h"
#include <stdio.h>

// 语音指令处理回调函数
static void app_voice_command_handler(voice_command_t command, uint8_t value) {
switch (command) {
case VOICE_CMD_START_TIMER:
tomato_timer_start();
break;
case VOICE_CMD_STOP_TIMER:
tomato_timer_stop();
break;
case VOICE_CMD_PAUSE_TIMER:
tomato_timer_pause();
break;
case VOICE_CMD_RESET_TIMER:
tomato_timer_reset();
break;
case VOICE_CMD_SET_TIME:
tomato_timer_set_duration(value);
break;
case VOICE_CMD_UNKNOWN:
printf("Unknown Voice Command\n");
break;
default:
break;
}
}

int main(void) {
rt_kprintf("Nixie Tube Tomato Timer - RT-Thread\n");

// 初始化各个模块
tomato_timer_init();
su03t_voice_init();
su03t_voice_register_command_callback(app_voice_command_handler);

// 初始化 WiFi 并连接 (需要替换为实际的 SSID 和 Password)
wifi_manager_init("YOUR_WIFI_SSID", "YOUR_WIFI_PASSWORD");
wifi_manager_connect();

// 启动 NTP 时间同步 (可选)
if (wifi_manager_is_connected()) {
wifi_manager_sync_ntp_time();
}

// 主循环 (可以添加其他应用逻辑,例如按键控制等)
while (1) {
rt_thread_mdelay(1000); // 1秒延时
// 可以添加状态指示,例如打印当前番茄钟状态
// rt_kprintf("Timer State: %d, Remaining: %d seconds\n", tomato_timer_get_state(), tomato_timer_get_remaining_seconds());
}

return RT_EOK;
}

编译和运行

  1. 搭建开发环境: 安装 RT-Thread 开发环境 (例如 RT-Thread Studio 或 Env 工具),并配置 W600 模块的 SDK。
  2. 创建 RT-Thread 工程: 根据 W600 模块和 RT-Thread 的开发文档,创建基于 RT-Thread 的工程。
  3. 添加代码文件: 将上述 HAL 层、中间件层和应用层的代码文件添加到工程中。
  4. 配置 RT-Thread 组件: 根据项目需求,在 RT-Thread 的配置工具中启用必要的组件,例如 TCP/IP 协议栈、WiFi 驱动等。
  5. 修改硬件配置: 根据实际硬件连接,修改代码中的 GPIO 引脚定义、UART 端口号等硬件相关的配置。
  6. 编译代码: 使用 RT-Thread 的编译工具编译工程代码。
  7. 烧录程序: 将编译生成的固件烧录到 W600 模块中。
  8. 连接硬件: 将辉光管驱动电路、SU-03T 模块等硬件连接到 W600 模块。
  9. 上电测试: 给系统上电,观察辉光管显示是否正常,测试语音控制功能和 WiFi 连接功能。

测试验证和维护升级

1. 测试验证

  • 单元测试: 针对各个模块进行单元测试,例如测试 GPIO 驱动的输出、UART 驱动的收发、辉光管显示驱动的显示效果、番茄钟逻辑模块的计时准确性等。可以使用 RT-Thread 提供的单元测试框架。
  • 集成测试: 将各个模块集成起来进行测试,例如测试语音控制是否能正确控制番茄钟的启动、停止、暂停、重置等功能,测试 WiFi 连接是否稳定,NTP 时间同步是否准确。
  • 系统测试: 进行全面的系统测试,模拟用户实际使用场景,测试系统的稳定性、可靠性、性能等。
  • 用户验收测试: 邀请用户进行测试,收集用户反馈,进一步完善系统。

2. 维护升级

  • 固件升级 (OTA - Over-The-Air): 利用 W600 的 WiFi 功能,实现固件的在线升级。可以设计 OTA 升级模块,从服务器下载新的固件版本,并更新到设备中。RT-Thread 也提供了 OTA 升级的组件支持。
  • 错误日志和诊断: 添加错误日志记录功能,方便在系统运行过程中记录错误信息,用于问题诊断和维护。
  • 远程监控和管理: 如果需要,可以扩展 WiFi 功能,实现远程监控和管理功能,例如远程查看番茄钟状态、远程配置参数等。

可靠性、高效性和可扩展性

  • 可靠性:
    • 采用 RT-Thread RTOS,保证系统的实时性和稳定性。
    • 硬件抽象层 (HAL) 提高了代码的可移植性,降低了硬件故障带来的影响。
    • 完善的错误处理机制,例如语音指令解析错误、WiFi 连接失败等,保证系统在异常情况下也能正常运行。
    • 充分的测试验证,确保系统的稳定性和可靠性。
  • 高效性:
    • RT-Thread RTOS 的高效内核,保证系统的快速响应和低延迟。
    • 模块化设计,代码结构清晰,易于维护和优化。
    • 代码实现注重效率,避免不必要的资源消耗。
  • 可扩展性:
    • 分层架构和模块化设计,方便添加新的功能模块,例如扩展更多的语音指令、添加远程控制功能、增加其他显示方式等。
    • 预留了 WiFi 功能,为未来的扩展功能提供了基础。
    • RT-Thread RTOS 的可扩展性,可以方便地添加新的组件和功能。

总结

这个基于 W600+RT-Thread+SU-03T 的双 IN12 辉光管番茄钟项目,从需求分析、系统架构设计、代码实现到测试维护,完整地展示了一个嵌入式系统的开发流程。通过分层架构、模块化设计、HAL 抽象层、RT-Thread RTOS 以及各种实践验证的技术和方法,构建了一个可靠、高效、可扩展的系统平台。代码实现部分提供了关键模块的 C 代码示例,实际开发中需要根据具体的硬件平台和需求进行完善和优化。

希望这个详细的说明和代码示例能够帮助您理解和实现这个项目。嵌入式系统开发是一个复杂而富有挑战性的领域,需要不断学习和实践才能掌握更多技能。

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