编程技术分享

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

0%

简介:功能包括日历、计算器、秒表、NFC、蓝牙无线升级、与手机传信息、抬腕亮屏、心率检测、环境温湿度检测、指南针、海拔测量等功能。已经尽量满足现在智能手表的功能,后期还需要继续添加修改很多东西。

我将为您详细阐述这款DIY智能手表项目最适合的代码设计架构,并提供相应的C代码实现框架。考虑到项目的功能丰富性和未来的可扩展性,我们将采用分层架构模块化设计,并结合事件驱动状态机等编程范式,确保系统的可靠性、高效性和可维护性。
关注微信公众号,提前获取相关推文

1. 代码设计架构:分层架构与模块化设计

我们将采用经典的分层架构,将系统划分为以下几个主要层次:

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 这是最底层,直接与硬件交互。HAL 的目标是屏蔽底层硬件的差异,为上层提供统一的硬件访问接口。这层包含对 MCU 外设(GPIO, SPI, I2C, UART, ADC, Timers, DMA, NFC, Bluetooth, 传感器等)的驱动程序。

  • 板级支持包 (BSP - Board Support Package): 构建在 HAL 之上,BSP 负责具体的硬件初始化、时钟配置、中断管理、内存管理等与特定硬件平台相关的操作。BSP 可以看作是 HAL 的具体实现,针对特定的 MCU 和开发板。

  • 操作系统抽象层 (OSAL - Operating System Abstraction Layer): 如果项目使用了实时操作系统 (RTOS),OSAL 层将对 RTOS API 进行封装,使得上层代码不直接依赖于特定的 RTOS。如果未来需要更换 RTOS,只需修改 OSAL 层即可。即使不使用 RTOS,也可以设计一个简化的 OSAL 层,提供基本的任务调度和同步机制。

  • 中间件层 (Middleware Layer): 这一层提供各种通用的服务和组件,供应用层调用。例如:

    • 通信协议栈: 蓝牙协议栈 (Bluetooth stack),NFC 协议栈 (NFC stack),手机通信协议 (例如自定义协议或标准协议)。
    • 传感器驱动框架: 心率传感器驱动,温湿度传感器驱动,指南针传感器驱动,海拔传感器驱动。
    • 图形用户界面框架 (GUI Framework): 处理屏幕显示、触摸输入、UI 元素管理等。
    • 数据存储与管理: 日历数据存储,用户配置数据存储,运动数据存储等。
    • 电源管理模块: 负责低功耗管理、睡眠模式切换、唤醒源管理等。
    • 升级管理模块: 蓝牙无线升级 (OTA - Over-The-Air) 功能的实现。
    • 日志与调试模块: 提供日志输出、错误处理、调试接口等。
  • 应用层 (Application Layer): 这是最顶层,实现智能手表的具体功能。应用层调用中间件层提供的服务,完成用户可见的功能,例如:

    • 日历应用 (Calendar App): 显示日历、日程管理。
    • 计算器应用 (Calculator App): 提供计算功能。
    • 秒表应用 (Stopwatch App): 计时功能。
    • NFC 应用 (NFC App): NFC 功能交互。
    • 蓝牙通信应用 (Bluetooth Communication App): 与手机通信,消息传输。
    • 抬腕亮屏功能 (Raise-to-Wake Feature): 检测手腕抬起动作并点亮屏幕。
    • 心率监测应用 (Heart Rate Monitoring App): 采集和显示心率数据。
    • 温湿度监测应用 (Temperature & Humidity Monitoring App): 采集和显示环境温湿度数据。
    • 指南针应用 (Compass App): 显示方向。
    • 海拔测量应用 (Altimeter App): 测量和显示海拔高度。
    • 主界面与状态管理 (Main UI & State Management): 管理主界面的显示和各个应用之间的切换。

模块化设计: 在每一层,我们都将采用模块化设计思想,将功能分解为独立的模块。例如,在中间件层,蓝牙协议栈、NFC 协议栈、传感器驱动等都是独立的模块。在应用层,日历、计算器、秒表等也都是独立的模块。模块化设计的好处是:

  • 高内聚,低耦合: 每个模块专注于完成特定的功能,模块之间通过清晰的接口进行交互,降低模块间的依赖性。
  • 易于维护和扩展: 修改或添加新功能时,只需要关注相关的模块,不会影响其他模块。
  • 代码复用性高: 模块可以在不同的项目或系统中复用。
  • 团队协作开发: 不同的开发人员可以并行开发不同的模块。

2. 编程范式:事件驱动与状态机

  • 事件驱动编程: 智能手表是一个典型的事件驱动系统。用户的操作(触摸、按键、抬腕)、传感器的数据更新、蓝牙消息的接收、定时器到期等都是事件。系统需要根据不同的事件做出相应的响应。事件驱动编程能够提高系统的响应速度和效率。我们将使用事件队列来管理事件,并由事件处理循环 (Event Loop) 来处理事件。

  • 状态机: 对于复杂的系统或模块,例如 UI 管理、蓝牙通信、NFC 交互等,可以使用状态机来管理系统的状态和状态之间的转换。状态机能够清晰地描述系统的行为,简化代码逻辑,提高系统的可靠性。每个状态代表系统的一种运行模式,状态之间通过事件触发转换。

3. C 代码实现框架 (核心模块示例,包含详细注释)

为了演示整个系统的架构和实现思路,我将提供核心模块的 C 代码示例,包括 HAL, BSP, OSAL (简化版), Middleware (部分模块), 和 Application (部分应用)。

3.1. HAL (Hardware Abstraction Layer)

hal_gpio.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
/**
* @file hal_gpio.h
* @brief GPIO 硬件抽象层头文件
*/
#ifndef HAL_GPIO_H
#define HAL_GPIO_H

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

/**
* @brief GPIO 端口号枚举
*/
typedef enum {
GPIO_PORT_A,
GPIO_PORT_B,
GPIO_PORT_C,
// ... 可以根据具体 MCU 添加更多端口
GPIO_PORT_MAX
} gpio_port_t;

/**
* @brief GPIO 引脚号枚举
*/
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,
// ... 可以根据具体 MCU 添加更多引脚
GPIO_PIN_MAX
} gpio_pin_t;

/**
* @brief GPIO 方向枚举
*/
typedef enum {
GPIO_DIRECTION_INPUT, // 输入
GPIO_DIRECTION_OUTPUT // 输出
} gpio_direction_t;

/**
* @brief GPIO 输出类型枚举
*/
typedef enum {
GPIO_OUTPUT_TYPE_PUSH_PULL, // 推挽输出
GPIO_OUTPUT_TYPE_OPEN_DRAIN // 开漏输出
} gpio_output_type_t;

/**
* @brief GPIO 上下拉电阻枚举
*/
typedef enum {
GPIO_PULL_NONE, // 无上下拉
GPIO_PULL_UP, // 上拉
GPIO_PULL_DOWN // 下拉
} gpio_pull_t;

/**
* @brief 初始化 GPIO 引脚
* @param port GPIO 端口号
* @param pin GPIO 引脚号
* @param direction GPIO 方向 (输入/输出)
* @param output_type GPIO 输出类型 (推挽/开漏)
* @param pull GPIO 上下拉电阻
* @return true: 初始化成功, false: 初始化失败
*/
bool hal_gpio_init(gpio_port_t port, gpio_pin_t pin, gpio_direction_t direction,
gpio_output_type_t output_type, gpio_pull_t pull);

/**
* @brief 设置 GPIO 引脚输出电平
* @param port GPIO 端口号
* @param pin GPIO 引脚号
* @param value 输出值 (true: 高电平, false: 低电平)
*/
void hal_gpio_set_output(gpio_port_t port, gpio_pin_t pin, bool value);

/**
* @brief 读取 GPIO 引脚输入电平
* @param port GPIO 端口号
* @param pin GPIO 引脚号
* @return true: 高电平, false: 低电平
*/
bool hal_gpio_read_input(gpio_port_t port, gpio_pin_t pin);

#endif // HAL_GPIO_H

hal_gpio.c (假设目标 MCU 是 STM32,这里只是示例,实际代码需要根据具体 MCU 的寄存器定义和库函数进行编写)

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
/**
* @file hal_gpio.c
* @brief GPIO 硬件抽象层实现文件
*/
#include "hal_gpio.h"
// ... 包含 STM32 相关的头文件,例如 #include "stm32fxxx_hal.h"

bool hal_gpio_init(gpio_port_t port, gpio_pin_t pin, gpio_direction_t direction,
gpio_output_type_t output_type, gpio_pull_t pull) {
// ... 根据 port 和 pin 转换为 STM32 的 GPIO 端口和引脚
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
// ... 代码省略,根据 port 和 pin 设置 GPIOx 和 GPIO_Pin

GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_Pin;
if (direction == GPIO_DIRECTION_OUTPUT) {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 默认推挽输出
if (output_type == GPIO_OUTPUT_TYPE_OPEN_DRAIN) {
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出
}
} else {
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
}

if (pull == GPIO_PULL_UP) {
GPIO_InitStruct.Pull = GPIO_PULLUP;
} else if (pull == GPIO_PULL_DOWN) {
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
} else {
GPIO_InitStruct.Pull = GPIO_NOPULL;
}
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 低速
HAL_GPIO_Init(GPIOx, &GPIO_InitStruct);

return true; // 假设初始化成功,实际需要添加错误处理
}

void hal_gpio_set_output(gpio_port_t port, gpio_pin_t pin, bool value) {
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
// ... 代码省略,根据 port 和 pin 设置 GPIOx 和 GPIO_Pin

HAL_GPIO_WritePin(GPIOx, GPIO_Pin, value ? GPIO_PIN_SET : GPIO_PIN_RESET);
}

bool hal_gpio_read_input(gpio_port_t port, gpio_pin_t pin) {
GPIO_TypeDef* GPIOx;
uint16_t GPIO_Pin;
// ... 代码省略,根据 port 和 pin 设置 GPIOx 和 GPIO_Pin

return HAL_GPIO_ReadPin(GPIOx, GPIO_Pin) == GPIO_PIN_SET;
}

3.2. BSP (Board Support Package)

bsp.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
/**
* @file bsp.h
* @brief 板级支持包头文件
*/
#ifndef BSP_H
#define BSP_H

#include "hal_gpio.h"
// ... 可以包含其他 HAL 头文件

/**
* @brief 系统初始化
* @return true: 初始化成功, false: 初始化失败
*/
bool bsp_init(void);

/**
* @brief 获取当前时间戳 (例如毫秒级)
* @return 当前时间戳
*/
uint32_t bsp_get_tick(void);

/**
* @brief 延时函数 (毫秒级)
* @param ms 延时毫秒数
*/
void bsp_delay_ms(uint32_t ms);

// ... 其他 BSP 相关的功能,例如 LED 控制,按键检测,串口初始化等

#endif // BSP_H

bsp.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
/**
* @file bsp.c
* @brief 板级支持包实现文件
*/
#include "bsp.h"
// ... 包含 MCU 相关的头文件,例如 #include "stm32fxxx_hal.h"

bool bsp_init(void) {
HAL_Init(); // 初始化 HAL 库

// ... 时钟配置 (System Clock Configuration)
// ... 例如 SystemClock_Config();

// ... GPIO 初始化 (例如 LED 灯,按键)
hal_gpio_init(GPIO_PORT_A, GPIO_PIN_0, GPIO_DIRECTION_OUTPUT, GPIO_OUTPUT_TYPE_PUSH_PULL, GPIO_PULL_NONE); // LED 灯
hal_gpio_init(GPIO_PORT_A, GPIO_PIN_1, GPIO_DIRECTION_INPUT, GPIO_OUTPUT_TYPE_PUSH_PULL, GPIO_PULL_UP); // 按键

// ... 其他外设初始化 (例如串口,SPI,I2C 等)

return true;
}

uint32_t bsp_get_tick(void) {
return HAL_GetTick(); // 使用 HAL 库提供的 tick 获取函数
}

void bsp_delay_ms(uint32_t ms) {
HAL_Delay(ms); // 使用 HAL 库提供的延时函数
}

// ... 其他 BSP 功能的实现,例如 LED 控制,按键检测,串口初始化等

3.3. OSAL (Operating System Abstraction Layer) - 简化版,不使用 RTOS 的情况

osal.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
/**
* @file osal.h
* @brief 操作系统抽象层头文件 (简化版,无 RTOS)
*/
#ifndef OSAL_H
#define OSAL_H

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

/**
* @brief 任务结构体 (简化版)
*/
typedef struct {
void (*task_func)(void* arg); // 任务函数指针
void* task_arg; // 任务参数
uint32_t task_period_ms; // 任务周期 (毫秒)
uint32_t last_exec_tick; // 上次执行时间 tick
} osal_task_t;

/**
* @brief 初始化 OSAL
* @return true: 初始化成功, false: 初始化失败
*/
bool osal_init(void);

/**
* @brief 创建任务 (简化版,静态创建)
* @param task 任务结构体指针
* @param task_func 任务函数
* @param task_arg 任务参数
* @param period_ms 任务周期 (毫秒)
* @return true: 创建成功, false: 创建失败
*/
bool osal_task_create(osal_task_t* task, void (*task_func)(void* arg), void* task_arg, uint32_t period_ms);

/**
* @brief 任务调度 (简化版,合作式调度)
*/
void osal_task_schedule(void);

#endif // OSAL_H

osal.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
/**
* @file osal.c
* @brief 操作系统抽象层实现文件 (简化版,无 RTOS)
*/
#include "osal.h"
#include "bsp.h" // 需要使用 bsp_get_tick()

#define MAX_TASKS 10 // 最大任务数量 (可配置)
static osal_task_t tasks[MAX_TASKS];
static uint8_t task_count = 0;

bool osal_init(void) {
task_count = 0; // 初始化任务计数
return true;
}

bool osal_task_create(osal_task_t* task, void (*task_func)(void* arg), void* task_arg, uint32_t period_ms) {
if (task_count >= MAX_TASKS) {
return false; // 任务数量已达上限
}
task->task_func = task_func;
task->task_arg = task_arg;
task->task_period_ms = period_ms;
task->last_exec_tick = bsp_get_tick(); // 记录首次执行时间
tasks[task_count++] = *task; // 添加到任务数组
return true;
}

void osal_task_schedule(void) {
uint32_t current_tick = bsp_get_tick();
for (uint8_t i = 0; i < task_count; i++) {
if (current_tick - tasks[i].last_exec_tick >= tasks[i].task_period_ms) {
tasks[i].task_func(tasks[i].task_arg); // 执行任务函数
tasks[i].last_exec_tick = current_tick; // 更新上次执行时间
}
}
}

3.4. Middleware Layer (部分模块示例)

3.4.1. UI 框架 (简化文本 UI)

ui.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
/**
* @file ui.h
* @brief 简化文本 UI 框架头文件
*/
#ifndef UI_H
#define UI_H

#include <stdint.h>
#include <stdbool.h>
// ... 可以包含 LCD 驱动相关的头文件

/**
* @brief 初始化 UI 框架
* @return true: 初始化成功, false: 初始化失败
*/
bool ui_init(void);

/**
* @brief 清屏
*/
void ui_clear_screen(void);

/**
* @brief 在指定位置显示字符串
* @param x X 坐标
* @param y Y 坐标
* @param text 要显示的字符串
*/
void ui_draw_text(uint16_t x, uint16_t y, const char* text);

/**
* @brief 绘制水平线
* @param x1 起始 X 坐标
* @param y Y 坐标
* @param x2 结束 X 坐标
*/
void ui_draw_hline(uint16_t x1, uint16_t y, uint16_t x2);

/**
* @brief 绘制垂直线
* @param x X 坐标
* @param y1 起始 Y 坐标
* @param y2 结束 Y 坐标
*/
void ui_draw_vline(uint16_t x, uint16_t y1, uint16_t y2);

// ... 其他 UI 绘制函数,例如绘制矩形,圆形等

#endif // UI_H

ui.c (简化文本 UI,假设使用简单的 LCD 驱动,需要根据实际 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
/**
* @file ui.c
* @brief 简化文本 UI 框架实现文件
*/
#include "ui.h"
#include "bsp.h" // 可能需要延时函数 bsp_delay_ms()
// ... 包含 LCD 驱动相关的头文件和函数,例如 lcd_init(), lcd_clear(), lcd_draw_char()

bool ui_init(void) {
// ... LCD 初始化代码,例如 lcd_init();
// ... 初始化字体等
return true;
}

void ui_clear_screen(void) {
// ... 清屏代码,例如 lcd_clear();
}

void ui_draw_text(uint16_t x, uint16_t y, const char* text) {
// ... 绘制字符串,循环调用 lcd_draw_char() 逐个字符绘制
while (*text) {
// ... 假设有 lcd_draw_char(x, y, *text); 函数
// lcd_draw_char(x, y, *text);
// ... 更新 x 坐标,例如 x += char_width;
text++;
}
}

void ui_draw_hline(uint16_t x1, uint16_t y, uint16_t x2) {
// ... 绘制水平线,例如循环绘制像素点
for (uint16_t x = x1; x <= x2; x++) {
// ... 假设有 lcd_draw_pixel(x, y); 函数
// lcd_draw_pixel(x, y);
}
}

void ui_draw_vline(uint16_t x, uint16_t y1, uint16_t y2) {
// ... 绘制垂直线,例如循环绘制像素点
for (uint16_t y = y1; y <= y2; y++) {
// ... 假设有 lcd_draw_pixel(x, y); 函数
// lcd_draw_pixel(x, y);
}
}

3.4.2. 传感器驱动框架 (心率传感器示例)

sensor_heartrate.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
/**
* @file sensor_heartrate.h
* @brief 心率传感器驱动头文件
*/
#ifndef SENSOR_HEARTRATE_H
#define SENSOR_HEARTRATE_H

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

/**
* @brief 初始化心率传感器
* @return true: 初始化成功, false: 初始化失败
*/
bool sensor_heartrate_init(void);

/**
* @brief 读取心率数据
* @param heart_rate 心率值 (输出参数)
* @return true: 读取成功, false: 读取失败
*/
bool sensor_heartrate_read(uint16_t* heart_rate);

#endif // SENSOR_HEARTRATE_H

sensor_heartrate.c (示例,需要根据具体的心率传感器型号和通信协议进行修改,例如 I2C 通信)

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
/**
* @file sensor_heartrate.c
* @brief 心率传感器驱动实现文件
*/
#include "sensor_heartrate.h"
#include "hal_i2c.h" // 假设使用 I2C 通信
#include "bsp.h" // 需要延时函数

// ... 定义心率传感器 I2C 地址,寄存器地址等
#define HEART_RATE_SENSOR_I2C_ADDR 0xXX
#define HEART_RATE_DATA_REG 0xYY

bool sensor_heartrate_init(void) {
// ... 初始化 I2C 总线 (如果 HAL 层还没有初始化)
// hal_i2c_init(...);

// ... 初始化心率传感器,例如发送配置命令
// ... 具体初始化 sequence 需要参考心率传感器的数据手册

bsp_delay_ms(100); // 延时等待传感器稳定

return true; // 假设初始化成功,实际需要添加错误处理
}

bool sensor_heartrate_read(uint16_t* heart_rate) {
uint8_t data_buffer[2];
// ... 通过 I2C 读取心率传感器数据寄存器 HEART_RATE_DATA_REG,读取 2 字节数据到 data_buffer
// hal_i2c_read_bytes(HEART_RATE_SENSOR_I2C_ADDR, HEART_RATE_DATA_REG, data_buffer, 2);

// ... 解析数据 buffer,得到心率值 (具体解析方式需要参考心率传感器的数据手册)
*heart_rate = (data_buffer[0] << 8) | data_buffer[1]; // 假设心率值是 16 位数据

return true; // 假设读取成功,实际需要添加错误处理
}

3.5. Application Layer (部分应用示例)

3.5.1. 主界面应用 (Main UI Application)

app_mainui.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* @file app_mainui.h
* @brief 主界面应用头文件
*/
#ifndef APP_MAINUI_H
#define APP_MAINUI_H

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

/**
* @brief 初始化主界面应用
* @return true: 初始化成功, false: 初始化失败
*/
bool app_mainui_init(void);

/**
* @brief 主界面应用任务函数
* @param arg 任务参数 (未使用)
*/
void app_mainui_task(void* arg);

#endif // APP_MAINUI_H

app_mainui.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
/**
* @file app_mainui.c
* @brief 主界面应用实现文件
*/
#include "app_mainui.h"
#include "ui.h"
#include "osal.h"
#include "bsp.h"

#include <stdio.h> // sprintf

bool app_mainui_init(void) {
return true;
}

void app_mainui_task(void* arg) {
static uint32_t last_update_tick = 0;
uint32_t current_tick = bsp_get_tick();

if (current_tick - last_update_tick >= 1000) { // 每秒更新一次
last_update_tick = current_tick;

ui_clear_screen(); // 清屏

ui_draw_text(10, 10, "DIY Smartwatch");

// ... 显示时间日期 (需要实现 RTC 驱动和时间管理模块)
char time_str[32];
sprintf(time_str, "Time: %lu", current_tick / 1000); // 简单显示秒数
ui_draw_text(10, 30, time_str);

// ... 显示步数 (需要实现计步器功能)
// ui_draw_text(10, 50, "Steps: ...");

// ... 显示心率 (需要心率传感器驱动)
// uint16_t heart_rate;
// if (sensor_heartrate_read(&heart_rate)) {
// char hr_str[32];
// sprintf(hr_str, "HR: %d bpm", heart_rate);
// ui_draw_text(10, 70, hr_str);
// } else {
// ui_draw_text(10, 70, "HR: --");
// }

// ... 可以添加更多主界面显示内容
}
}

3.5.2. 秒表应用 (Stopwatch Application)

app_stopwatch.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
/**
* @file app_stopwatch.h
* @brief 秒表应用头文件
*/
#ifndef APP_STOPWATCH_H
#define APP_STOPWATCH_H

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

/**
* @brief 初始化秒表应用
* @return true: 初始化成功, false: 初始化失败
*/
bool app_stopwatch_init(void);

/**
* @brief 秒表应用任务函数
* @param arg 任务参数 (未使用)
*/
void app_stopwatch_task(void* arg);

/**
* @brief 启动秒表
*/
void app_stopwatch_start(void);

/**
* @brief 停止秒表
*/
void app_stopwatch_stop(void);

/**
* @brief 复位秒表
*/
void app_stopwatch_reset(void);

#endif // APP_STOPWATCH_H

app_stopwatch.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
/**
* @file app_stopwatch.c
* @brief 秒表应用实现文件
*/
#include "app_stopwatch.h"
#include "ui.h"
#include "osal.h"
#include "bsp.h"

#include <stdio.h> // sprintf

typedef enum {
STOPWATCH_STATE_STOPPED,
STOPWATCH_STATE_RUNNING,
STOPWATCH_STATE_PAUSED
} stopwatch_state_t;

static stopwatch_state_t stopwatch_state = STOPWATCH_STATE_STOPPED;
static uint32_t start_time = 0;
static uint32_t elapsed_time = 0;

bool app_stopwatch_init(void) {
stopwatch_state = STOPWATCH_STATE_STOPPED;
elapsed_time = 0;
return true;
}

void app_stopwatch_task(void* arg) {
static uint32_t last_update_tick = 0;
uint32_t current_tick = bsp_get_tick();

if (current_tick - last_update_tick >= 50) { // 每 50ms 更新一次 (20FPS)
last_update_tick = current_tick;

ui_clear_screen();
ui_draw_text(10, 10, "Stopwatch");

uint32_t display_time_ms = elapsed_time;
if (stopwatch_state == STOPWATCH_STATE_RUNNING) {
display_time_ms = elapsed_time + (current_tick - start_time);
}

uint32_t minutes = (display_time_ms / 60000);
uint32_t seconds = (display_time_ms % 60000) / 1000;
uint32_t milliseconds = (display_time_ms % 1000) / 10; // 显示两位毫秒

char time_str[32];
sprintf(time_str, "%02lu:%02lu:%02lu", minutes, seconds, milliseconds);
ui_draw_text(10, 40, time_str);

if (stopwatch_state == STOPWATCH_STATE_RUNNING) {
ui_draw_text(10, 60, "Running");
} else if (stopwatch_state == STOPWATCH_STATE_PAUSED) {
ui_draw_text(10, 60, "Paused");
} else {
ui_draw_text(10, 60, "Stopped");
}
}
}

void app_stopwatch_start(void) {
if (stopwatch_state != STOPWATCH_STATE_RUNNING) {
if (stopwatch_state == STOPWATCH_STATE_STOPPED) {
elapsed_time = 0; // 从头开始计时
}
start_time = bsp_get_tick();
stopwatch_state = STOPWATCH_STATE_RUNNING;
}
}

void app_stopwatch_stop(void) {
if (stopwatch_state == STOPWATCH_STATE_RUNNING) {
elapsed_time += (bsp_get_tick() - start_time);
stopwatch_state = STOPWATCH_STATE_PAUSED;
}
}

void app_stopwatch_reset(void) {
stopwatch_state = STOPWATCH_STATE_STOPPED;
elapsed_time = 0;
}

4. 系统初始化与主循环

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
/**
* @file main.c
* @brief 主程序入口
*/
#include "bsp.h"
#include "osal.h"
#include "ui.h"
#include "app_mainui.h"
#include "app_stopwatch.h"

int main(void) {
bsp_init(); // 初始化板级支持包
osal_init(); // 初始化操作系统抽象层
ui_init(); // 初始化 UI 框架

app_mainui_init(); // 初始化主界面应用
app_stopwatch_init(); // 初始化秒表应用

// 创建任务 (示例,实际项目中需要根据功能需求创建更多任务)
osal_task_t mainui_task;
osal_task_create(&mainui_task, app_mainui_task, NULL, 100); // 主界面任务,100ms 周期

osal_task_t stopwatch_task;
osal_task_create(&stopwatch_task, app_stopwatch_task, NULL, 50); // 秒表任务,50ms 周期


while (1) {
osal_task_schedule(); // 任务调度
// ... 可以添加其他后台处理,例如事件处理,低功耗管理等
}
}

5. 代码结构组织

建议将项目代码组织成以下目录结构:

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
project/
├── hal/ # 硬件抽象层
│ ├── hal_gpio.h
│ ├── hal_gpio.c
│ ├── ... (其他 HAL 模块,例如 hal_spi.h, hal_i2c.h, hal_uart.h, hal_nfc.h, hal_bluetooth.h, hal_sensor.h)
├── bsp/ # 板级支持包
│ ├── bsp.h
│ ├── bsp.c
├── osal/ # 操作系统抽象层 (简化版)
│ ├── osal.h
│ ├── osal.c
├── middleware/ # 中间件层
│ ├── ui/ # UI 框架
│ │ ├── ui.h
│ │ ├── ui.c
│ ├── sensor/ # 传感器驱动框架
│ │ ├── sensor_heartrate.h
│ │ ├── sensor_heartrate.c
│ │ ├── ... (其他传感器驱动,例如 sensor_temp_humidity.h, sensor_compass.h, sensor_altimeter.h)
│ ├── bluetooth/ # 蓝牙协议栈 (可以集成第三方协议栈或自行实现简化版)
│ │ ├── ...
│ ├── nfc/ # NFC 协议栈 (可以集成第三方协议栈或自行实现简化版)
│ │ ├── ...
│ ├── ... (其他中间件模块,例如 power_manager/, upgrade_manager/, log/)
├── application/ # 应用层
│ ├── app_mainui/ # 主界面应用
│ │ ├── app_mainui.h
│ │ ├── app_mainui.c
│ ├── app_stopwatch/ # 秒表应用
│ │ ├── app_stopwatch.h
│ │ ├── app_stopwatch.c
│ ├── app_calendar/ # 日历应用
│ │ ├── ...
│ ├── app_calculator/ # 计算器应用
│ │ ├── ...
│ ├── ... (其他应用模块,例如 app_nfc/, app_bluetooth_msg/, app_heartrate/, app_compass/, app_altimeter/)
├── include/ # 公共头文件 (可选,如果需要存放跨模块使用的头文件)
├── main.c # 主程序入口
├── Makefile # 编译 Makefile (或其他构建系统配置文件)
└── README.md # 项目说明文件

6. 代码扩展与功能完善

上述代码框架只是一个基础的示例,要实现完整的智能手表功能,还需要继续扩展和完善各个模块的功能,例如:

  • 完善 HAL 和 BSP 层: 根据具体的硬件平台,编写完整的 HAL 驱动程序,并配置 BSP 初始化代码。
  • 实现 RTOS 支持: 如果需要更复杂的功能和更好的实时性,可以考虑使用 RTOS (例如 FreeRTOS, Zephyr, RT-Thread 等),并完善 OSAL 层,对接 RTOS API。
  • 完善 UI 框架: 实现更丰富的 UI 元素 (例如图标,进度条,列表等),支持触摸输入,状态管理,UI 动画等。
  • 实现蓝牙和 NFC 功能: 集成蓝牙协议栈 (例如 Bluedroid, NimBLE) 和 NFC 协议栈,实现蓝牙通信、蓝牙 OTA 升级、NFC 支付等功能。
  • 实现传感器驱动: 编写各种传感器 (心率,温湿度,指南针,海拔等) 的驱动程序,并进行数据处理和算法优化。
  • 实现应用层功能: 完成日历、计算器、消息通信、抬腕亮屏等应用功能的开发。
  • 电源管理: 实现完善的电源管理策略,包括低功耗模式切换、睡眠模式、唤醒机制等,延长电池续航时间。
  • 测试与调试: 进行充分的单元测试、集成测试和系统测试,确保系统的稳定性和可靠性。添加日志输出和调试接口,方便问题定位和调试。
  • 代码优化: 进行代码性能优化,例如减少内存占用,提高运行速度,降低功耗等。

总结

这个代码架构和示例代码提供了一个构建 DIY 智能手表项目的起点。 通过分层架构、模块化设计、事件驱动编程和状态机等方法,可以构建一个可靠、高效、可扩展的嵌入式系统平台。 请根据您的具体硬件平台和功能需求,逐步完善各个模块的代码,最终打造出一款功能强大的 DIY 智能手表。 由于篇幅限制,这里只提供了核心模块的框架和示例代码,实际项目中每个模块的代码量都会远超示例。 按照这个架构和思路,逐步实现各个功能模块。

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