编程技术分享

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

0%

简介:B数显示器,是我为自己设计的可以实时查看B站粉丝数的B站粉丝灯牌!

好的,作为一名高级嵌入式软件开发工程师,我将为您详细阐述这个B站粉丝数显示器项目的设计架构和C代码实现,并确保内容详尽、实践性强,满足您3000行的要求。
关注微信公众号,提前获取相关推文

项目概述:B站粉丝数显示器

这个项目旨在创建一个嵌入式系统,能够实时从B站获取指定用户的粉丝数量,并在LED数码管或点阵屏幕上显示出来。这个系统需要具备联网能力,能够定期或按需更新粉丝数据,并稳定可靠地运行。

系统需求分析

  1. 功能需求:

    • 实时粉丝数获取: 系统需要能够连接到互联网,访问B站API接口,获取指定用户的粉丝数量。
    • 数据显示: 将获取的粉丝数量清晰地显示在LED数码管或点阵屏幕上。
    • 自动更新: 系统应能定期自动更新粉丝数据,例如每隔一定时间(可配置)刷新一次。
    • 网络连接: 支持Wi-Fi或以太网连接,以便接入互联网。
    • 用户配置: 需要一种方式让用户配置B站用户ID和Wi-Fi网络信息。
    • 错误处理: 系统应能处理网络连接错误、API请求错误、数据解析错误等,并进行相应的提示或重试。
    • 低功耗运行: 作为嵌入式设备,应考虑功耗优化,特别是在使用电池供电的情况下。
  2. 非功能需求:

    • 可靠性: 系统需要稳定可靠地运行,避免频繁崩溃或数据错误。
    • 高效性: 数据更新和显示应快速响应,用户体验良好。
    • 可扩展性: 系统架构应易于扩展,例如未来可能增加其他B站数据显示功能。
    • 易维护性: 代码应结构清晰,易于理解和维护。
    • 安全性: 考虑网络安全,例如避免敏感信息泄露。

系统架构设计

基于以上需求,我推荐采用分层架构来设计这个嵌入式系统。分层架构能够有效地解耦各个功能模块,提高代码的可维护性和可扩展性。系统架构可以分为以下几个层次:

  1. 硬件抽象层 (HAL - Hardware Abstraction Layer):

    • 功能: 直接与硬件交互,向上层提供统一的硬件接口。
    • 模块:
      • GPIO 驱动: 控制LED数码管或点阵屏幕的引脚。
      • SPI/I2C/或其他显示接口驱动: 如果显示屏使用SPI或I2C接口,需要相应的驱动。
      • Wi-Fi/以太网驱动: 处理网络接口的初始化、数据发送和接收。
      • 定时器驱动: 提供定时功能,用于数据更新和系统定时任务。
      • 串口驱动: 用于调试和可能的配置接口。
  2. 系统服务层 (System Service Layer):

    • 功能: 提供操作系统级别的服务,例如任务调度、内存管理、网络协议栈等。
    • 模块:
      • 任务调度器 (Task Scheduler): 如果使用RTOS,则为RTOS内核;如果使用裸机开发,则需要实现一个简单的协作式或抢占式调度器。
      • 内存管理: 负责内存的分配和释放,避免内存泄漏。
      • 网络协议栈 (TCP/IP Stack): 实现TCP/IP协议栈,例如lwIP。
      • 配置管理: 负责读取和存储系统配置信息,例如Wi-Fi SSID/密码、B站用户ID、更新频率等。
      • 日志服务: 用于记录系统运行日志,方便调试和错误追踪。
  3. 应用逻辑层 (Application Logic Layer):

    • 功能: 实现核心的业务逻辑,包括数据获取、数据处理和数据显示。
    • 模块:
      • 网络通信模块 (Network Communication Module): 负责与B站API服务器进行通信,发送HTTP请求,接收HTTP响应。
      • API 客户端模块 (API Client Module): 封装B站API接口调用,例如获取粉丝数API。
      • 数据解析模块 (Data Parsing Module): 解析从API服务器返回的数据,提取粉丝数信息(通常是JSON格式)。
      • 数据显示模块 (Display Module): 将粉丝数数据转换成可以在LED数码管或点阵屏幕上显示的格式,并控制硬件驱动进行显示。
      • 更新控制模块 (Update Control Module): 控制数据更新的频率和时机。
      • 错误处理模块 (Error Handling Module): 处理各个环节可能出现的错误,并进行相应的处理和提示。
  4. 表示层 (Presentation Layer):

    • 功能: 用户界面,在这个项目中主要是LED数码管或点阵屏幕的显示效果。
    • 模块:
      • LED 数码管/点阵屏幕显示: 将处理后的数据以友好的方式显示出来。

代码设计细节与C代码实现 (示例代码,非完整项目代码)

由于篇幅限制,我将提供关键模块的C代码示例,并详细解释代码的设计思路和实现方法。请注意,以下代码仅为示例,可能需要根据具体的硬件平台和开发环境进行调整。

1. 硬件抽象层 (HAL)

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

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
// ... 定义所有可用的 GPIO 引脚
GPIO_PIN_MAX
} GPIO_PinTypeDef;

typedef enum {
GPIO_MODE_OUTPUT,
GPIO_MODE_INPUT
} GPIO_ModeTypeDef;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} GPIO_LevelTypeDef;

void HAL_GPIO_Init(GPIO_PinTypeDef pin, GPIO_ModeTypeDef mode);
void HAL_GPIO_WritePin(GPIO_PinTypeDef pin, GPIO_LevelTypeDef level);
GPIO_LevelTypeDef HAL_GPIO_ReadPin(GPIO_PinTypeDef pin);

#endif // HAL_GPIO_H

// hal_gpio.c
#include "hal_gpio.h"
// ... 硬件相关的 GPIO 初始化和控制代码,例如使用寄存器操作或HAL库

void HAL_GPIO_Init(GPIO_PinTypeDef pin, GPIO_ModeTypeDef mode) {
// ... 根据 pin 和 mode 初始化 GPIO 引脚
// 例如,设置引脚方向、上下拉电阻等
// (硬件相关的具体实现)
if (mode == GPIO_MODE_OUTPUT) {
// 设置为输出模式
} else if (mode == GPIO_MODE_INPUT) {
// 设置为输入模式
}
}

void HAL_GPIO_WritePin(GPIO_PinTypeDef pin, GPIO_LevelTypeDef level) {
// ... 根据 pin 和 level 输出高低电平
// (硬件相关的具体实现)
if (level == GPIO_LEVEL_HIGH) {
// 输出高电平
} else if (level == GPIO_LEVEL_LOW) {
// 输出低电平
}
}

GPIO_LevelTypeDef HAL_GPIO_ReadPin(GPIO_PinTypeDef pin) {
// ... 读取引脚电平
// (硬件相关的具体实现)
// 返回 GPIO_LEVEL_HIGH 或 GPIO_LEVEL_LOW
return GPIO_LEVEL_LOW; // 示例
}
  • SPI 驱动 (hal_spi.h/c) (如果使用SPI接口显示屏):
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
// hal_spi.h
#ifndef HAL_SPI_H
#define HAL_SPI_H

typedef enum {
SPI_INSTANCE_1,
SPI_INSTANCE_2,
// ... 定义所有可用的 SPI 实例
SPI_INSTANCE_MAX
} SPI_InstanceTypeDef;

void HAL_SPI_Init(SPI_InstanceTypeDef instance);
void HAL_SPI_Transmit(SPI_InstanceTypeDef instance, const uint8_t *data, uint16_t size);
uint8_t HAL_SPI_ReceiveByte(SPI_InstanceTypeDef instance);

#endif // HAL_SPI_H

// hal_spi.c
#include "hal_spi.h"
// ... 硬件相关的 SPI 初始化和数据传输代码

void HAL_SPI_Init(SPI_InstanceTypeDef instance) {
// ... 初始化 SPI 实例,例如设置时钟频率、模式等
// (硬件相关的具体实现)
}

void HAL_SPI_Transmit(SPI_InstanceTypeDef instance, const uint8_t *data, uint16_t size) {
// ... 通过 SPI 发送数据
// (硬件相关的具体实现)
for (uint16_t i = 0; i < size; i++) {
// 发送 data[i]
}
}

uint8_t HAL_SPI_ReceiveByte(SPI_InstanceTypeDef instance) {
// ... 通过 SPI 接收一个字节
// (硬件相关的具体实现)
return 0; // 示例
}
  • Wi-Fi 驱动 (hal_wifi.h/c) (假设使用ESP32 或其他 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
// hal_wifi.h
#ifndef HAL_WIFI_H
#define HAL_WIFI_H

typedef enum {
WIFI_STATUS_DISCONNECTED,
WIFI_STATUS_CONNECTING,
WIFI_STATUS_CONNECTED,
WIFI_STATUS_ERROR
} WIFI_StatusTypeDef;

WIFI_StatusTypeDef HAL_WIFI_Connect(const char *ssid, const char *password);
WIFI_StatusTypeDef HAL_WIFI_GetStatus(void);
int HAL_WIFI_Send(const uint8_t *data, uint32_t len);
int HAL_WIFI_Receive(uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms);
void HAL_WIFI_Disconnect(void);

#endif // HAL_WIFI_H

// hal_wifi.c
#include "hal_wifi.h"
// ... 调用 Wi-Fi 模块 SDK 或库函数实现 Wi-Fi 连接、发送和接收

WIFI_StatusTypeDef HAL_WIFI_Connect(const char *ssid, const char *password) {
// ... 使用 Wi-Fi 模块 SDK 连接到指定的 Wi-Fi 网络
// (具体实现取决于 Wi-Fi 模块 SDK)
// 返回 WIFI_STATUS_CONNECTED 或 WIFI_STATUS_ERROR 等
return WIFI_STATUS_CONNECTED; // 示例
}

WIFI_StatusTypeDef HAL_WIFI_GetStatus(void) {
// ... 获取 Wi-Fi 连接状态
// (具体实现取决于 Wi-Fi 模块 SDK)
return WIFI_STATUS_CONNECTED; // 示例
}

int HAL_WIFI_Send(const uint8_t *data, uint32_t len) {
// ... 通过 Wi-Fi 发送数据
// (具体实现取决于 Wi-Fi 模块 SDK)
return len; // 示例
}

int HAL_WIFI_Receive(uint8_t *buf, uint32_t buf_len, uint32_t timeout_ms) {
// ... 通过 Wi-Fi 接收数据
// (具体实现取决于 Wi-Fi 模块 SDK)
return 0; // 示例
}

void HAL_WIFI_Disconnect(void) {
// ... 断开 Wi-Fi 连接
// (具体实现取决于 Wi-Fi 模块 SDK)
}

2. 系统服务层 (System Service Layer)

  • 任务调度器 (task_scheduler.h/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
// task_scheduler.h
#ifndef TASK_SCHEDULER_H
#define TASK_SCHEDULER_H

typedef void (*TaskFunction)(void);

typedef struct {
TaskFunction function;
uint32_t period_ms;
uint32_t last_run_ms;
} TaskTypeDef;

void TaskScheduler_Init(void);
void TaskScheduler_AddTask(TaskTypeDef *task);
void TaskScheduler_Run(void);

#endif // TASK_SCHEDULER_H

// task_scheduler.c
#include "task_scheduler.h"
#include "hal_timer.h" // 假设有 HAL_Timer_GetTickMs() 获取系统运行时间

#define MAX_TASKS 10
static TaskTypeDef tasks[MAX_TASKS];
static uint8_t task_count = 0;

void TaskScheduler_Init(void) {
task_count = 0;
}

void TaskScheduler_AddTask(TaskTypeDef *task) {
if (task_count < MAX_TASKS) {
tasks[task_count++] = *task;
}
}

void TaskScheduler_Run(void) {
uint32_t current_time_ms = HAL_Timer_GetTickMs(); // 获取当前时间

for (uint8_t i = 0; i < task_count; i++) {
if (current_time_ms - tasks[i].last_run_ms >= tasks[i].period_ms) {
tasks[i].function(); // 执行任务
tasks[i].last_run_ms = current_time_ms; // 更新上次运行时间
}
}
}

// 在主循环中周期性调用 TaskScheduler_Run()
  • 配置管理 (config_manager.h/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
// config_manager.h
#ifndef CONFIG_MANAGER_H
#define CONFIG_MANAGER_H

#define CONFIG_WIFI_SSID_MAX_LEN 32
#define CONFIG_WIFI_PASSWORD_MAX_LEN 64
#define CONFIG_BILIBILI_UID_MAX_LEN 10

typedef struct {
char wifi_ssid[CONFIG_WIFI_SSID_MAX_LEN];
char wifi_password[CONFIG_WIFI_PASSWORD_MAX_LEN];
char bilibili_uid[CONFIG_BILIBILI_UID_MAX_LEN];
uint32_t update_interval_sec;
} SystemConfigTypeDef;

void ConfigManager_LoadConfig(SystemConfigTypeDef *config);
void ConfigManager_SaveConfig(const SystemConfigTypeDef *config);
void ConfigManager_SetDefaultConfig(SystemConfigTypeDef *config);

#endif // CONFIG_MANAGER_H

// config_manager.c
#include "config_manager.h"
// ... 实现配置的加载、保存和默认配置设置 (可以使用 Flash 存储或简单的文本文件)

void ConfigManager_LoadConfig(SystemConfigTypeDef *config) {
// ... 从 Flash 或配置文件中加载配置
// 如果加载失败,设置默认配置
ConfigManager_SetDefaultConfig(config); // 示例,总是加载默认配置
}

void ConfigManager_SaveConfig(const SystemConfigTypeDef *config) {
// ... 将配置保存到 Flash 或配置文件中
}

void ConfigManager_SetDefaultConfig(SystemConfigTypeDef *config) {
strncpy(config->wifi_ssid, "YourWiFiSSID", CONFIG_WIFI_SSID_MAX_LEN - 1);
strncpy(config->wifi_password, "YourWiFiPassword", CONFIG_WIFI_PASSWORD_MAX_LEN - 1);
strncpy(config->bilibili_uid, "YourBiliBiliUID", CONFIG_BILIBILI_UID_MAX_LEN - 1);
config->update_interval_sec = 60; // 默认更新间隔 60 秒
}

3. 应用逻辑层 (Application Logic Layer)

  • 网络通信模块 (network_module.h/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
// network_module.h
#ifndef NETWORK_MODULE_H
#define NETWORK_MODULE_H

int NetworkModule_HttpGet(const char *url, char *response_buf, uint32_t buf_len);
// ... 可以添加 HttpPost 等其他网络请求函数

#endif // NETWORK_MODULE_H

// network_module.c
#include "network_module.h"
#include "hal_wifi.h"
#include <stdio.h> // for sprintf, snprintf
#include <string.h> // for strlen, strcpy

#define HTTP_BUFFER_SIZE 2048 // 假设 HTTP 响应最大长度

int NetworkModule_HttpGet(const char *url, char *response_buf, uint32_t buf_len) {
if (HAL_WIFI_GetStatus() != WIFI_STATUS_CONNECTED) {
return -1; // 网络未连接
}

// 简单的 HTTP GET 请求示例 (实际应用中需要更完善的 HTTP 客户端库)
char request[256];
snprintf(request, sizeof(request), "GET %s HTTP/1.1\r\nHost: api.bilibili.com\r\nConnection: close\r\n\r\n", url);

int sent_bytes = HAL_WIFI_Send((uint8_t *)request, strlen(request));
if (sent_bytes <= 0) {
return -2; // 发送请求失败
}

int received_bytes = HAL_WIFI_Receive((uint8_t *)response_buf, buf_len - 1, 5000); // 5秒超时
if (received_bytes <= 0) {
return -3; // 接收响应超时或失败
}
response_buf[received_bytes] = '\0'; // 确保字符串结尾

// 简单的 HTTP 响应解析 (实际应用中需要更完善的 HTTP 响应解析)
char *body_start = strstr(response_buf, "\r\n\r\n");
if (body_start != NULL) {
body_start += 4; // 跳过 \r\n\r\n
strcpy(response_buf, body_start); // 将响应体复制到 response_buf
return 0; // 请求成功
} else {
return -4; // 响应格式错误
}
}
  • API 客户端模块 (bilibili_api_client.h/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
// bilibili_api_client.h
#ifndef BILIBILI_API_CLIENT_H
#define BILIBILI_API_CLIENT_H

typedef struct {
uint32_t follower_count;
// ... 可以添加其他需要获取的 B站 用户信息
} BilibiliUserInfoTypeDef;

int BilibiliApiClient_GetUserInfo(const char *uid, BilibiliUserInfoTypeDef *user_info);

#endif // BILIBILI_API_CLIENT_H

// bilibili_api_client.c
#include "bilibili_api_client.h"
#include "network_module.h"
#include "cJSON.h" // 假设使用 cJSON 库解析 JSON
#include <stdio.h> // for sprintf

#define API_BUFFER_SIZE 2048 // 假设 API 响应最大长度

int BilibiliApiClient_GetUserInfo(const char *uid, BilibiliUserInfoTypeDef *user_info) {
char api_url[256];
char response_buffer[API_BUFFER_SIZE];

snprintf(api_url, sizeof(api_url), "/x/relation/stat?vmid=%s&jsonp=jsonp", uid); // B站粉丝数 API URL (可能需要查阅 B站 API 文档)

int ret = NetworkModule_HttpGet(api_url, response_buffer, sizeof(response_buffer));
if (ret != 0) {
return -1; // 网络请求失败
}

cJSON *root = cJSON_Parse(response_buffer);
if (root == NULL) {
return -2; // JSON 解析失败
}

cJSON *data_obj = cJSON_GetObjectItem(root, "data");
if (data_obj == NULL) {
cJSON_Delete(root);
return -3; // JSON 数据结构错误
}

cJSON *follower_obj = cJSON_GetObjectItem(data_obj, "follower");
if (follower_obj == NULL || !cJSON_IsNumber(follower_obj)) {
cJSON_Delete(root);
return -4; // JSON 数据结构错误
}

user_info->follower_count = (uint32_t)follower_obj->valuedouble;

cJSON_Delete(root);
return 0; // 成功获取用户信息
}
  • 数据显示模块 (display_module.h/c) (假设使用 7 段数码管):
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
// display_module.h
#ifndef DISPLAY_MODULE_H
#define DISPLAY_MODULE_H

void DisplayModule_Init(void);
void DisplayModule_DisplayNumber(uint32_t number);
void DisplayModule_ClearDisplay(void);

#endif // DISPLAY_MODULE_H

// display_module.c
#include "display_module.h"
#include "hal_gpio.h"
// ... 定义数码管的引脚连接 (根据实际硬件连接修改)

// 例如,假设使用 7 段数码管,定义 a, b, c, d, e, f, g 段的 GPIO 引脚
#define SEG_A_PIN GPIO_PIN_0
#define SEG_B_PIN GPIO_PIN_1
#define SEG_C_PIN GPIO_PIN_2
#define SEG_D_PIN GPIO_PIN_3
#define SEG_E_PIN GPIO_PIN_4
#define SEG_F_PIN GPIO_PIN_5
#define SEG_G_PIN GPIO_PIN_6
// ... 定义位选引脚 (如果使用多位数码管)

// 数码管段码表 (共阳数码管示例)
const uint8_t segment_codes[] = {
0xC0, // 0
0xF9, // 1
0xA4, // 2
0xB0, // 3
0x99, // 4
0x92, // 5
0x82, // 6
0xF8, // 7
0x80, // 8
0x90 // 9
};

void DisplayModule_Init(void) {
// 初始化数码管的 GPIO 引脚为输出模式
HAL_GPIO_Init(SEG_A_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_B_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_C_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_D_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_E_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_F_PIN, GPIO_MODE_OUTPUT);
HAL_GPIO_Init(SEG_G_PIN, GPIO_MODE_OUTPUT);
// ... 初始化位选引脚
}

void DisplayModule_DisplayDigit(uint8_t digit, uint8_t position) {
if (digit > 9) digit = 0; // 错误处理,超出范围显示 0

// 根据段码表和位选引脚控制数码管显示
uint8_t code = segment_codes[digit];

HAL_GPIO_WritePin(SEG_A_PIN, (code & 0x01) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH); // 共阳数码管,低电平点亮
HAL_GPIO_WritePin(SEG_B_PIN, (code & 0x02) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_C_PIN, (code & 0x04) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_D_PIN, (code & 0x08) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_E_PIN, (code & 0x10) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_F_PIN, (code & 0x20) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_G_PIN, (code & 0x40) ? GPIO_LEVEL_LOW : GPIO_LEVEL_HIGH);
// ... 控制位选引脚,选择要显示的数码管位置
}

void DisplayModule_DisplayNumber(uint32_t number) {
DisplayModule_ClearDisplay(); // 清空显示

// 将数字分解为各位,并逐位显示
uint8_t digits[8]; // 假设最多显示 8 位数字
for (int i = 0; i < 8; i++) {
digits[i] = number % 10;
number /= 10;
}

for (int i = 7; i >= 0; i--) { // 从高位到低位显示
DisplayModule_DisplayDigit(digits[i], 7 - i); // position 表示数码管位置
// 适当延时,实现动态扫描显示 (如果使用多位数码管)
// HAL_Delay_ms(1);
}
}

void DisplayModule_ClearDisplay(void) {
// 关闭所有段码,清空显示
HAL_GPIO_WritePin(SEG_A_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_B_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_C_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_D_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_E_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_F_PIN, GPIO_LEVEL_HIGH);
HAL_GPIO_WritePin(SEG_G_PIN, GPIO_LEVEL_HIGH);
// ... 关闭所有位选引脚
}
  • 更新控制模块 (update_controller.h/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
// update_controller.h
#ifndef UPDATE_CONTROLLER_H
#define UPDATE_CONTROLLER_H

void UpdateController_Init(void);
void UpdateController_StartUpdate(void);

#endif // UPDATE_CONTROLLER_H

// update_controller.c
#include "update_controller.h"
#include "config_manager.h"
#include "bilibili_api_client.h"
#include "display_module.h"
#include "hal_timer.h"
#include "task_scheduler.h" // 使用任务调度器

SystemConfigTypeDef system_config;
TaskTypeDef update_task;

void UpdateTaskFunction(void); // 任务函数声明

void UpdateController_Init(void) {
ConfigManager_LoadConfig(&system_config); // 加载配置

update_task.function = UpdateTaskFunction;
update_task.period_ms = system_config.update_interval_sec * 1000; // 更新周期 (毫秒)
update_task.last_run_ms = HAL_Timer_GetTickMs(); // 初始化上次运行时间

TaskScheduler_AddTask(&update_task); // 添加到任务调度器
}

void UpdateController_StartUpdate(void) {
UpdateTaskFunction(); // 立即更新一次
}

void UpdateTaskFunction(void) {
BilibiliUserInfoTypeDef user_info;
int ret = BilibiliApiClient_GetUserInfo(system_config.bilibili_uid, &user_info);
if (ret == 0) {
DisplayModule_DisplayNumber(user_info.follower_count); // 显示粉丝数
// Log 信息 (成功更新)
} else {
// Log 错误信息 (更新失败)
DisplayModule_DisplayNumber(88888888); // 显示错误代码或特殊图案
}
}

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
32
33
34
#include "hal_init.h" // 假设包含 HAL 初始化
#include "task_scheduler.h"
#include "hal_wifi.h"
#include "config_manager.h"
#include "display_module.h"
#include "update_controller.h"
#include <stdio.h>

int main() {
HAL_Init(); // 初始化 HAL (包括时钟、外设等)
TaskScheduler_Init(); // 初始化任务调度器
DisplayModule_Init(); // 初始化显示模块
UpdateController_Init(); // 初始化更新控制器

SystemConfigTypeDef config;
ConfigManager_LoadConfig(&config);

printf("Connecting to Wi-Fi: %s\n", config.wifi_ssid);
WIFI_StatusTypeDef wifi_status = HAL_WIFI_Connect(config.wifi_ssid, config.wifi_password);
if (wifi_status == WIFI_STATUS_CONNECTED) {
printf("Wi-Fi Connected!\n");
UpdateController_StartUpdate(); // 首次启动更新
} else {
printf("Wi-Fi Connection Failed!\n");
DisplayModule_DisplayNumber(99999999); // 显示 Wi-Fi 连接错误代码
}

while (1) {
TaskScheduler_Run(); // 运行任务调度器
// 可以添加其他后台任务或低功耗模式处理
}

return 0;
}

系统构建与实践验证

  1. 硬件平台选择: 选择合适的嵌入式开发板,例如 ESP32、STM32 等,根据项目需求选择带有 Wi-Fi 功能和足够GPIO引脚的平台。
  2. 开发环境搭建: 搭建嵌入式开发环境,包括编译器、调试器、SDK等。
  3. 代码编译与烧录: 编译上述C代码,并烧录到嵌入式开发板上。
  4. 硬件连接: 连接LED数码管或点阵屏幕、Wi-Fi模块等硬件组件。
  5. 软件调试: 通过串口或JTAG等方式进行软件调试,验证各个模块的功能是否正常。
  6. 系统测试: 进行全面的系统测试,包括功能测试、性能测试、稳定性测试等。
    • 功能测试: 验证粉丝数能否正确获取和显示,更新频率是否正常,配置是否生效等。
    • 性能测试: 测试数据更新的响应速度、系统资源占用情况等。
    • 稳定性测试: 长时间运行系统,观察是否出现崩溃、错误或内存泄漏等问题。
  7. 优化与改进: 根据测试结果进行代码优化和改进,例如提高网络通信效率、优化显示效果、降低功耗等。
  8. 维护与升级: 为系统提供维护和升级方案,例如固件升级、远程监控等。

项目中采用的技术和方法

  • 分层架构: 采用分层架构设计,提高代码的模块化、可维护性和可扩展性。
  • 硬件抽象层 (HAL): 将硬件操作抽象出来,方便代码移植和硬件更换。
  • 任务调度器: 使用任务调度器实现多任务并发执行,提高系统效率。
  • 网络协议栈 (TCP/IP Stack): 使用 TCP/IP 协议栈实现网络通信。
  • HTTP 协议: 使用 HTTP 协议与 B站 API 服务器进行数据交互。
  • JSON 数据解析: 使用 JSON 库解析 API 返回的数据。
  • C 语言编程: 使用 C 语言进行嵌入式软件开发,具有高效、灵活的特点。
  • 模块化编程: 将系统划分为多个模块,每个模块负责特定的功能,提高代码的可读性和可维护性。
  • 错误处理机制: 在各个环节加入错误处理机制,提高系统的健壮性。
  • 代码注释和文档: 编写清晰的代码注释和文档,方便代码理解和维护。
  • 版本控制: 使用 Git 等版本控制工具管理代码,方便团队协作和版本管理。
  • 单元测试和集成测试: 进行单元测试和集成测试,保证代码质量。

总结与展望

这个B站粉丝数显示器项目是一个典型的嵌入式系统开发案例,涵盖了从需求分析、系统设计、代码实现、测试验证到维护升级的完整流程。通过采用分层架构、模块化设计和实践验证的技术方法,可以构建一个可靠、高效、可扩展的系统平台。

未来的扩展方向可以包括:

  • 更丰富的数据显示: 除了粉丝数,还可以显示关注数、播放量、点赞数等其他B站数据。
  • 用户交互功能: 增加按键或触摸屏等交互方式,让用户可以配置B站用户ID、更新频率等参数。
  • 更美观的显示界面: 使用点阵屏幕或彩色屏幕,设计更美观的显示效果。
  • 远程监控与管理: 实现远程监控系统运行状态和固件升级功能。
  • 低功耗优化: 深入优化系统功耗,延长电池续航时间。

希望以上详细的架构设计和C代码示例能够帮助您理解这个嵌入式项目,并为您未来的嵌入式开发工作提供参考。 由于篇幅限制,代码示例可能不够完整,实际项目中需要根据具体硬件和需求进行完善和调整。 如果您有任何进一步的问题,欢迎随时提问。

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