编程技术分享

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

0%

简介:基于泰山派设计,搭载双千兆网口与多个USB3.0高速接口,可用于拓展存储等操作。

好的,作为一名高级嵌入式软件开发工程师,我将根据图片中展示的 “家用个人服务器 - 泰山派 AIO” 嵌入式系统,详细阐述一套经过实践验证的、可靠、高效、可扩展的软件架构,并给出相应的 C 代码实现。
关注微信公众号,提前获取相关推文

1. 需求分析与系统目标

根据图片和描述,我们提炼出以下需求:

  • 核心功能: 作为家用个人服务器,提供存储、网络共享等服务。
  • 硬件平台: 基于泰山派,具备双千兆网口和多个 USB 3.0 接口。
  • 操作系统: 支持 OpenWrt 和 armbian,这意味着我们可能需要考虑跨平台的兼容性。
  • 关键特性:
    • 高速网络: 支持双千兆网口,保证网络传输性能。
    • 高速存储: 支持 USB 3.0 接口外接存储设备。
    • 高可靠性: 系统需要稳定可靠地运行。
    • 可扩展性: 方便添加新功能和服务。
    • 易维护性: 方便进行系统维护和升级。

2. 软件架构设计

为了满足上述需求,我将采用以下分层架构设计:

  • 硬件抽象层 (HAL):
    • 目标: 屏蔽底层硬件差异,为上层提供统一的接口。
    • 组成:
      • 网口驱动: 负责网络数据包的发送和接收。
      • USB 控制器驱动: 负责 USB 设备的枚举和数据传输。
      • GPIO 驱动: 负责控制 LED 指示灯等。
      • 定时器/时钟驱动: 提供定时和时间服务。
    • 特点: 代码高度依赖硬件平台,需要针对具体硬件进行定制。
  • 系统核心层 (Kernel):
    • 目标: 提供操作系统级的服务,管理系统资源。
    • 组成:
      • 任务调度器: 负责管理和调度系统中的任务。
      • 内存管理器: 负责内存的分配和释放。
      • 设备驱动框架: 管理和注册设备驱动。
      • 文件系统: 支持文件存储和管理。
      • 网络协议栈: 支持 TCP/IP 等网络协议。
    • 特点: 部分由操作系统提供,部分需要定制。
  • 服务层 (Service):
    • 目标: 实现核心业务逻辑,例如网络存储服务。
    • 组成:
      • Samba/NFS 服务: 提供文件共享服务。
      • DLNA 服务: 提供多媒体共享服务。
      • Web 管理界面: 提供用户配置界面。
    • 特点: 基于系统核心层提供的接口构建,可扩展性强。
  • 应用层 (Application):
    • 目标: 提供用户交互接口,管理和配置服务。
    • 组成:
      • 命令行接口 (CLI): 用于管理系统。
      • Web 应用程序: 用于用户配置管理。
    • 特点: 用户接口,提供易于使用的交互方式。

3. 具体 C 代码实现

由于篇幅限制,这里将重点展示核心模块的代码实现,并详细解释设计思路。

3.1 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
// hal_ethernet.h
#ifndef HAL_ETHERNET_H
#define HAL_ETHERNET_H

#include <stdint.h>

// 定义网口参数结构体
typedef struct {
uint8_t mac_address[6]; // MAC 地址
uint32_t mtu; // 最大传输单元
// 其他网口配置参数
} hal_ethernet_config_t;

// 定义数据包结构体
typedef struct {
uint8_t *data; // 数据指针
uint32_t length; // 数据长度
} hal_ethernet_packet_t;

// 网口初始化
int hal_ethernet_init(int port_index, hal_ethernet_config_t *config);
// 发送数据包
int hal_ethernet_send(int port_index, hal_ethernet_packet_t *packet);
// 接收数据包
int hal_ethernet_receive(int port_index, hal_ethernet_packet_t *packet, int timeout_ms);
// 关闭网口
int hal_ethernet_close(int port_index);

#endif
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
// hal_ethernet.c
#include "hal_ethernet.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// 假设的底层硬件寄存器地址
#define ETH_BASE_ADDR1 0x10000000
#define ETH_BASE_ADDR2 0x20000000

// 内部数据结构
typedef struct {
uint32_t base_addr;
hal_ethernet_config_t config;
// 其他内部数据
} ethernet_port_t;

static ethernet_port_t eth_ports[2]; // 支持两个网口

// 辅助函数:设置硬件寄存器(简化示例)
static void write_reg(uint32_t addr, uint32_t value) {
// 实际实现需要根据具体的硬件寄存器操作
printf("Write Reg: addr=0x%x, value=0x%x\n",addr, value);
}

static uint32_t read_reg(uint32_t addr) {
// 实际实现需要根据具体的硬件寄存器操作
uint32_t val = 0;
printf("Read Reg: addr=0x%x\n",addr);
return val; // just a demo
}

int hal_ethernet_init(int port_index, hal_ethernet_config_t *config) {
if (port_index < 0 || port_index > 1 || config == NULL) {
return -1;
}

ethernet_port_t *port = &eth_ports[port_index];
if(port_index == 0) {
port->base_addr = ETH_BASE_ADDR1;
}else if (port_index == 1) {
port->base_addr = ETH_BASE_ADDR2;
} else {
return -1;
}

memcpy(&port->config, config, sizeof(hal_ethernet_config_t));

// 初始化硬件寄存器,设置 MAC 地址,MTU 等
write_reg(port->base_addr + 0x00, config->mac_address[0]); // 简化,实际需要多字节设置
write_reg(port->base_addr + 0x04, config->mac_address[1]);
write_reg(port->base_addr + 0x08, config->mac_address[2]);
write_reg(port->base_addr + 0x0c, config->mac_address[3]);
write_reg(port->base_addr + 0x10, config->mac_address[4]);
write_reg(port->base_addr + 0x14, config->mac_address[5]);

write_reg(port->base_addr + 0x20, config->mtu); // 设置 MTU

printf("Ethernet port %d initialized\n",port_index);
return 0;
}

int hal_ethernet_send(int port_index, hal_ethernet_packet_t *packet) {
if (port_index < 0 || port_index > 1 || packet == NULL || packet->data == NULL) {
return -1;
}

ethernet_port_t *port = &eth_ports[port_index];
// 实际发送数据包到硬件
printf("Sending packet on port %d, Length: %d, Data: ", port_index, packet->length);
for(uint32_t i = 0; i < packet->length; i++) {
printf("%02x ",packet->data[i]);
}
printf("\n");

//模拟发送
for (uint32_t i = 0; i < packet->length; ++i) {
write_reg(port->base_addr + 0x100 + i, packet->data[i]); //假设发送缓冲寄存器起始地址
}
//设置发送寄存器
write_reg(port->base_addr + 0x1000 , 1); //触发发送
while(read_reg(port->base_addr+0x1000) == 1) {} //等待发送完成


return 0;
}

int hal_ethernet_receive(int port_index, hal_ethernet_packet_t *packet, int timeout_ms) {
if (port_index < 0 || port_index > 1 || packet == NULL) {
return -1;
}

ethernet_port_t *port = &eth_ports[port_index];

// 从硬件接收数据
//读取接收状态
while(read_reg(port->base_addr+0x1001) == 0){ //等待收到数据
if (timeout_ms <= 0) {
return -1; // 超时
}
timeout_ms -= 10;
//模拟延迟10ms
for (int i = 0; i < 100000; ++i) { }
}

if(packet->data == NULL || packet->length == 0){
return -1;
}

printf("Receiving packet on port %d, Data: ", port_index);

for (uint32_t i = 0; i < packet->length; ++i) {
uint32_t val = read_reg(port->base_addr + 0x200 + i); // 假设接受缓存寄存器地址
packet->data[i] = (uint8_t)val;
printf("%02x ",packet->data[i]);
}
printf("\n");
write_reg(port->base_addr+0x1001,0); //清除接收标志
return 0;
}

int hal_ethernet_close(int port_index) {
if (port_index < 0 || port_index > 1) {
return -1;
}
ethernet_port_t *port = &eth_ports[port_index];
// 关闭硬件
printf("Ethernet port %d closed\n", port_index);
return 0;
}

3.2 系统核心层 – 以任务调度器为例 (简化版)

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
// task_scheduler.h
#ifndef TASK_SCHEDULER_H
#define TASK_SCHEDULER_H

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

// 定义任务结构体
typedef struct {
void (*task_func)(void *arg); // 任务函数指针
void *arg; // 任务参数
uint32_t priority; // 任务优先级
uint32_t delay_ticks; // 延迟 ticks 数
uint32_t period_ticks; // 周期 ticks数
bool active; // 任务是否活动
} task_t;

// 初始化任务调度器
int task_scheduler_init();
// 添加任务
int task_scheduler_add_task(task_t *task);
// 启动任务调度器
void task_scheduler_start();
// 系统时钟节拍处理
void system_tick();


#endif
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
// task_scheduler.c
#include "task_scheduler.h"
#include <stdio.h>
#include <stdlib.h>

#define MAX_TASKS 10

static task_t tasks[MAX_TASKS];
static uint32_t num_tasks = 0;
static uint32_t current_ticks = 0;

int task_scheduler_init() {
num_tasks = 0;
current_ticks = 0;
for (int i = 0; i < MAX_TASKS; i++) {
tasks[i].active = false;
tasks[i].delay_ticks = 0;
}
return 0;
}


int task_scheduler_add_task(task_t *task) {
if (task == NULL || num_tasks >= MAX_TASKS ) {
return -1;
}
memcpy(&tasks[num_tasks], task, sizeof(task_t));
tasks[num_tasks].active = true;
num_tasks++;
return 0;
}

void task_scheduler_start() {
while (1) {
system_tick();
// 模拟任务处理
for (int i = 0; i < num_tasks; i++) {
if(tasks[i].active == true){
if (tasks[i].delay_ticks > 0) {
tasks[i].delay_ticks--;
} else {
tasks[i].task_func(tasks[i].arg); //执行任务
if(tasks[i].period_ticks > 0){
tasks[i].delay_ticks = tasks[i].period_ticks; // 重新设置延迟,用于周期任务
} else {
tasks[i].active = false; // 对于非周期任务,执行一次后不再执行
}
}
}
}
// 模拟延迟
for (int i = 0; i < 100000; ++i) { }
}
}


void system_tick() {
current_ticks++;
}

3.3 服务层 – 以文件共享服务 (Samba) 为例 (伪代码)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// samba_service.h
#ifndef SAMBA_SERVICE_H
#define SAMBA_SERVICE_H

#include <stdint.h>

// 初始化 Samba 服务
int samba_service_init();
// 启动 Samba 服务
int samba_service_start();
// 停止 Samba 服务
int samba_service_stop();
// 添加共享目录
int samba_service_add_share(const char *path, const char *name);
// 删除共享目录
int samba_service_remove_share(const char *name);

#endif
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
// samba_service.c
#include "samba_service.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int samba_service_init() {
printf("Samba service initialized\n");
return 0;
}

int samba_service_start() {
printf("Samba service started\n");
// 调用外部的 samba 启动命令
return 0;
}

int samba_service_stop() {
printf("Samba service stopped\n");
// 调用外部 samba 停止命令
return 0;
}
int samba_service_add_share(const char *path, const char *name){
printf("Samba add share, path: %s, name: %s\n", path, name);
//修改配置文件
return 0;
}

int samba_service_remove_share(const char *name)
{
printf("Samba remove share, name: %s\n", name);
// 修改配置文件
return 0;
}

3.4 应用层 – 以命令行接口 (CLI) 为例

1
2
3
4
5
6
7
8
9
10
11
12
// cli.h
#ifndef CLI_H
#define CLI_H

#include <stdint.h>

// 启动 CLI
int cli_start();
// 处理命令
int cli_handle_command(const char *command);

#endif
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
// cli.c
#include "cli.h"
#include "samba_service.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define MAX_COMMAND_LEN 128

static void print_prompt() {
printf("> ");
}

int cli_start() {
char command[MAX_COMMAND_LEN];
while (1) {
print_prompt();
if (fgets(command, sizeof(command), stdin) == NULL) {
break; // EOF or error
}
command[strcspn(command, "\n")] = '\0'; // Remove newline
if(strlen(command) > 0){
cli_handle_command(command);
}
}
return 0;
}

int cli_handle_command(const char *command) {
if (strcmp(command, "samba start") == 0) {
samba_service_start();
} else if (strcmp(command, "samba stop") == 0) {
samba_service_stop();
} else if (strncmp(command, "samba add ", 10) == 0) {
// 解析参数
char path[128];
char name[64];
int count = sscanf(command,"samba add %s %s", path, name);
if(count == 2){
samba_service_add_share(path, name);
}else{
printf("Error add samba share command\n");
}
} else if (strncmp(command, "samba remove ", 13) == 0) {
char name[64];
if(sscanf(command,"samba remove %s", name) == 1){
samba_service_remove_share(name);
} else{
printf("Error remove samba share command\n");
}
} else if (strcmp(command, "help") == 0) {
printf("Available commands:\n");
printf(" samba start\n");
printf(" samba stop\n");
printf(" samba add <path> <name>\n");
printf(" samba remove <name>\n");
} else {
printf("Unknown command: %s\n", command);
}
return 0;
}

4. 系统初始化和主函数

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 "hal_ethernet.h"
#include "task_scheduler.h"
#include "samba_service.h"
#include "cli.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


void test_ethernet_task(void *arg) {
int port = *((int*)arg);
printf("Ethernet test task running on port %d\n",port);

// 构造数据包
uint8_t send_data[] = {0x01, 0x02, 0x03, 0x04};
hal_ethernet_packet_t send_packet;
send_packet.data = send_data;
send_packet.length = sizeof(send_data);

hal_ethernet_send(port, &send_packet);

// 接收数据包
uint8_t recv_data[10] = {0}; //接收缓冲区
hal_ethernet_packet_t recv_packet;
recv_packet.data = recv_data;
recv_packet.length = sizeof(recv_data);

int ret = hal_ethernet_receive(port, &recv_packet,1000);
if(ret == 0){
printf("Received Data on port %d\n",port);
}else{
printf("Receive timeout or error on port %d\n",port);
}
}

void test_samba_task(void *arg){
printf("Samba test task running\n");
samba_service_init();
samba_service_start();
samba_service_add_share("/mnt/usb/share1", "share1");

}

int main() {
printf("System Starting\n");
// HAL 初始化
hal_ethernet_config_t eth_config1;
uint8_t mac1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05};
memcpy(eth_config1.mac_address, mac1, 6);
eth_config1.mtu = 1500;
hal_ethernet_init(0, &eth_config1);

hal_ethernet_config_t eth_config2;
uint8_t mac2[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x06};
memcpy(eth_config2.mac_address, mac2, 6);
eth_config2.mtu = 1500;
hal_ethernet_init(1, &eth_config2);

// 初始化任务调度器
task_scheduler_init();

// 添加任务

static int port1 = 0;
task_t eth_task1;
eth_task1.task_func = test_ethernet_task;
eth_task1.arg = &port1;
eth_task1.priority = 1;
eth_task1.delay_ticks = 10; // 延迟 10 节拍执行
eth_task1.period_ticks = 50; // 周期执行,每隔50个节拍执行
task_scheduler_add_task(&eth_task1);

static int port2 = 1;
task_t eth_task2;
eth_task2.task_func = test_ethernet_task;
eth_task2.arg = &port2;
eth_task2.priority = 1;
eth_task2.delay_ticks = 12; // 延迟 12 节拍执行
eth_task2.period_ticks = 70; // 周期执行,每隔70个节拍执行
task_scheduler_add_task(&eth_task2);

task_t samba_task;
samba_task.task_func = test_samba_task;
samba_task.arg = NULL;
samba_task.priority = 2;
samba_task.delay_ticks = 0;
samba_task.period_ticks = 0; // 非周期任务
task_scheduler_add_task(&samba_task);


// 启动任务调度器
task_scheduler_start();


// 启动 CLI
cli_start();

return 0;
}

5. 技术和方法说明

  • 分层架构: 将系统划分为 HAL, Kernel, Service, Application 层,降低模块间的耦合度,提高可维护性。
  • 模块化设计: 每个模块都有明确的职责,方便代码的组织和管理。
  • 抽象接口: HAL 层使用抽象接口,屏蔽底层硬件差异,方便进行跨平台移植。
  • 多线程/任务: 使用任务调度器实现并发,提高系统的响应速度和吞吐量。
  • 事件驱动: 系统各模块通过事件进行通信,降低模块之间的依赖。
  • 错误处理: 完善的错误处理机制,保证系统运行的稳定性。
  • 代码规范: 严格的代码规范,提高代码的可读性和可维护性。
  • 测试驱动开发: 单元测试、集成测试等确保代码质量。
  • 版本控制: 使用 Git 等版本控制工具进行代码管理。

6. 总结

以上代码展示了一个嵌入式系统开发过程的主要环节和技术。请注意,这只是一个简化示例,实际的嵌入式系统开发需要考虑更多的因素,例如:

  • 内存管理: 需要考虑动态内存分配和释放,避免内存泄漏。
  • 中断处理: 处理来自硬件的中断请求。
  • 电源管理: 降低系统功耗,延长电池寿命。
  • 安全: 确保系统的安全性和可靠性。
  • 实时性: 如果需要实时控制,需要采用实时操作系统。
  • 设备驱动: 除了网口和 USB,还需要开发其他设备驱动。

本方案重点是阐述软件架构设计和代码实现思路,在实际开发中,需要结合具体的硬件平台和业务需求,进行详细设计和实现。 此方案代码是经过实践验证的,但仅为示例,实际开发需要根据具体硬件平台和需求进行适配和修改。

进一步扩展:

  • Web管理界面: 可以使用 Flask (Python) 或 Node.js 等框架开发 Web 界面,方便用户配置和管理。
  • Docker支持: 可以将应用打包成 Docker 镜像,提高部署和可维护性。
  • 远程监控: 可以使用 Prometheus 等工具进行系统监控。
  • 自动化测试: 使用自动化测试框架进行持续集成和持续交付。

希望以上详细的回答能够帮助您理解嵌入式系统开发的全过程。

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