编程技术分享

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

0%

简介:基于FPGA的便携式多功能仪器(示波器、信号发生器等)

好的,作为一名高级嵌入式软件开发工程师,我将详细阐述基于FPGA的便携式多功能仪器(示波器、信号发生器等)项目的代码设计架构,并提供具体的C代码实现示例,以满足您的要求。
关注微信公众号,提前获取相关推文

项目概述

本项目旨在开发一款便携式、多功能的电子仪器,核心功能包括:

  • 示波器: 采集并显示电信号的波形,提供电压、时间等测量功能。
  • 信号发生器: 生成各种类型的信号,如正弦波、方波、三角波、任意波等,用于电路测试和调试。
  • 其他功能(可扩展): 频谱分析仪、逻辑分析仪、频率计等,预留未来功能扩展的可能性。

硬件平台

本项目基于FPGA(Field-Programmable Gate Array)作为核心硬件平台。FPGA具有高度的灵活性和并行处理能力,非常适合实现复杂的数字信号处理和硬件加速功能。根据图片,我们可以推断硬件平台可能包含以下组件:

  • FPGA芯片: 核心处理单元,实现数据采集、信号处理、控制逻辑等。
  • ADC(模数转换器): 将模拟信号转换为数字信号,用于示波器功能的数据采集。
  • DAC(数模转换器): 将数字信号转换为模拟信号,用于信号发生器功能的信号输出。
  • 时钟源: 为FPGA和外围器件提供时钟信号。
  • 存储器(RAM/Flash): 存储程序代码、配置数据、采集数据等。
  • 通信接口: 如USB、以太网、UART等,用于数据传输、控制和调试。
  • 用户界面(可能): LCD显示屏、按键、旋钮等(图中未明确显示,但便携式仪器通常需要)。
  • 电源管理电路: 为整个系统供电。
  • BNC接口: 用于连接测试信号。

软件架构设计

为了构建一个可靠、高效、可扩展的系统平台,我将采用分层模块化架构,并结合事件驱动实时性的设计思想。

1. 分层架构

分层架构将系统分解为不同的层次,每一层负责特定的功能,并向上层提供服务,同时隐藏下层的实现细节。这种架构具有以下优点:

  • 模块化: 系统被分解为独立的模块,易于开发、测试和维护。
  • 可重用性: 底层模块可以被上层模块复用,减少代码冗余。
  • 可扩展性: 新的功能模块可以很容易地添加到系统中,而不会影响现有模块。
  • 可移植性: 通过抽象硬件接口,可以更容易地将系统移植到不同的硬件平台。

本项目软件架构可以分为以下层次(从下到上):

  • 硬件抽象层 (HAL - Hardware Abstraction Layer): 直接与硬件交互,封装底层硬件操作,向上层提供统一的硬件访问接口。HAL层包含GPIO控制、时钟管理、中断管理、ADC/DAC驱动、存储器访问等模块。
  • 设备驱动层 (Device Driver Layer): 构建在HAL层之上,负责管理和控制特定的硬件设备,例如ADC驱动、DAC驱动、FPGA IP核驱动(如示波器IP核、信号发生器IP核)、通信接口驱动等。设备驱动层提供更高级别的API,供上层应用程序调用。
  • 核心服务层 (Core Service Layer): 提供核心的系统服务和功能模块,例如数据采集服务、信号生成服务、数据处理服务、触发服务、显示服务、通信服务等。这些服务模块负责实现示波器和信号发生器的核心功能。
  • 应用层 (Application Layer): 构建在核心服务层之上,实现用户界面的逻辑和应用程序的流程控制。应用层调用核心服务层提供的API,完成用户的功能请求,例如设置示波器的参数、生成特定类型的信号、显示波形数据等。
  • 测试与维护层 (Test & Maintenance Layer): 包含测试代码、诊断工具、升级程序等,用于系统的测试验证、故障诊断和维护升级。

2. 模块化设计

在每一层内部,进一步采用模块化设计,将功能进一步细分到独立的模块中。例如,在核心服务层,可以有独立的示波器模块、信号发生器模块、数据处理模块、触发模块、显示模块等。模块之间通过定义清晰的接口进行通信,降低模块之间的耦合度。

3. 事件驱动

系统采用事件驱动的设计模式,提高系统的响应速度和效率。例如,数据采集模块可以等待ADC数据就绪事件,信号发生器模块可以等待定时器触发事件,用户界面模块可以等待用户输入事件等。事件驱动机制可以使系统在空闲时进入低功耗状态,并在事件发生时快速响应。

4. 实时性

由于示波器和信号发生器对实时性要求较高,系统需要具备良好的实时性能。这需要在软件架构设计和代码实现中考虑以下方面:

  • RTOS (Real-Time Operating System) (可选但强烈推荐): 可以考虑使用实时操作系统,如FreeRTOS、uC/OS等。RTOS提供任务调度、优先级管理、实时性保障等机制,可以更好地满足系统的实时性需求。
  • 中断处理: 合理设计中断处理程序,保证关键任务的中断响应时间。
  • 数据缓冲: 使用高效的数据缓冲机制,避免数据丢失和延迟。
  • 代码优化: 对关键代码进行性能优化,提高执行效率。

系统模块划分

基于上述架构设计,可以将系统划分为以下主要模块:

  • HAL模块:
    • GPIO驱动模块
    • 时钟管理模块
    • 中断管理模块
    • ADC驱动模块
    • DAC驱动模块
    • 存储器驱动模块 (Flash/RAM)
    • 通信接口驱动模块 (USB/Ethernet/UART)
    • 定时器驱动模块
  • 设备驱动模块:
    • ADC设备驱动模块
    • DAC设备驱动模块
    • 示波器IP核驱动模块
    • 信号发生器IP核驱动模块
    • 用户界面设备驱动模块 (例如 LCD 驱动, 按键驱动)
    • 通信接口设备驱动模块 (USB CDC/以太网协议栈驱动)
  • 核心服务模块:
    • 数据采集服务模块 (示波器功能核心)
    • 信号生成服务模块 (信号发生器功能核心)
    • 数据处理模块 (滤波, FFT, 测量等)
    • 触发服务模块 (示波器触发功能)
    • 显示服务模块 (波形显示, 参数显示)
    • 参数配置管理模块
    • 命令解析与控制模块
    • 文件系统管理模块 (如果需要存储配置或数据)
    • 通信协议栈模块 (TCP/IP, USB协议等)
  • 应用层模块:
    • 示波器应用模块
    • 信号发生器应用模块
    • 用户界面应用模块 (菜单, 参数设置, 功能切换)
    • 系统配置与管理模块
    • 测试与诊断应用模块
  • 测试与维护模块:
    • 单元测试模块
    • 集成测试模块
    • 系统测试模块
    • 诊断工具模块
    • 固件升级模块

C代码实现示例 (简化版,突出架构)

为了展示上述架构,我将提供一些关键模块的C代码实现示例。由于篇幅限制,以下代码仅为简化版本,重点在于展示架构思想和关键代码结构,并非完整的可直接运行的代码。实际项目中,每个模块的代码量会远大于示例代码。

1. HAL层代码示例 (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

typedef enum {
GPIO_PIN_0,
GPIO_PIN_1,
GPIO_PIN_2,
// ... more pins
GPIO_PIN_MAX
} GPIO_Pin;

typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT
} GPIO_Mode;

typedef enum {
GPIO_LEVEL_LOW,
GPIO_LEVEL_HIGH
} GPIO_Level;

// 初始化GPIO引脚
void HAL_GPIO_Init(GPIO_Pin pin, GPIO_Mode mode);

// 设置GPIO引脚输出电平
void HAL_GPIO_WritePin(GPIO_Pin pin, GPIO_Level level);

// 读取GPIO引脚输入电平
GPIO_Level HAL_GPIO_ReadPin(GPIO_Pin pin);

#endif // HAL_GPIO_H

// hal_gpio.c
#include "hal_gpio.h"
#include "platform_hardware.h" // 假设的底层硬件定义头文件

void HAL_GPIO_Init(GPIO_Pin pin, GPIO_Mode mode) {
// 根据pin和mode配置底层硬件寄存器
if (mode == GPIO_MODE_INPUT) {
// 配置为输入模式 (platform_hardware.h 中定义了底层硬件操作)
PLATFORM_GPIO_SetModeInput(pin);
} else if (mode == GPIO_MODE_OUTPUT) {
// 配置为输出模式
PLATFORM_GPIO_SetModeOutput(pin);
}
// ... 其他初始化操作
}

void HAL_GPIO_WritePin(GPIO_Pin pin, GPIO_Level level) {
if (level == GPIO_LEVEL_HIGH) {
PLATFORM_GPIO_SetPinHigh(pin);
} else if (level == GPIO_LEVEL_LOW) {
PLATFORM_GPIO_SetPinLow(pin);
}
}

GPIO_Level HAL_GPIO_ReadPin(GPIO_Pin pin) {
if (PLATFORM_GPIO_GetPinLevel(pin)) {
return GPIO_LEVEL_HIGH;
} else {
return GPIO_LEVEL_LOW;
}
}

2. 设备驱动层代码示例 (driver_adc.h, driver_adc.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
// driver_adc.h
#ifndef DRIVER_ADC_H
#define DRIVER_ADC_H

#include "hal_adc.h" // 假设的 HAL ADC 驱动头文件

typedef struct {
uint32_t sampling_rate; // 采样率
uint32_t resolution; // 分辨率
// ... 其他 ADC 配置参数
} ADC_Config;

// 初始化 ADC 设备
int DRIVER_ADC_Init(ADC_Config *config);

// 启动 ADC 转换
int DRIVER_ADC_Start();

// 停止 ADC 转换
int DRIVER_ADC_Stop();

// 获取 ADC 采样数据
int DRIVER_ADC_GetData(uint16_t *data_buffer, uint32_t buffer_size);

// 注册 ADC 数据就绪回调函数
void DRIVER_ADC_RegisterDataReadyCallback(void (*callback)(void));

#endif // DRIVER_ADC_H

// driver_adc.c
#include "driver_adc.h"
#include "hal_dma.h" // 假设的 HAL DMA 驱动头文件
#include "rtos_task.h" // 假设的 RTOS 任务管理头文件

static ADC_Config current_adc_config;
static uint16_t adc_data_buffer[1024]; // 示例数据缓冲区
static void (*adc_data_ready_callback)(void) = NULL;

// ADC 数据就绪中断处理函数 (假设在 HAL 层中调用)
void HAL_ADC_IRQHandler() {
// ... 从 HAL ADC 获取数据到 adc_data_buffer (可以使用 DMA)
HAL_ADC_GetDataDMA(adc_data_buffer, sizeof(adc_data_buffer));

if (adc_data_ready_callback != NULL) {
adc_data_ready_callback(); // 调用注册的回调函数
}
}

int DRIVER_ADC_Init(ADC_Config *config) {
// 复制配置参数
current_adc_config = *config;

// 初始化 HAL ADC
HAL_ADC_Init(config->sampling_rate, config->resolution);

// 配置 DMA (可选,如果使用 DMA 传输数据)
HAL_DMA_InitForADC();

// 注册 ADC 中断处理函数 (在 HAL 层注册)
HAL_ADC_RegisterIRQHandler(HAL_ADC_IRQHandler);

return 0;
}

int DRIVER_ADC_Start() {
HAL_ADC_StartConversion();
return 0;
}

int DRIVER_ADC_Stop() {
HAL_ADC_StopConversion();
return 0;
}

int DRIVER_ADC_GetData(uint16_t *data_buffer, uint32_t buffer_size) {
// 将内部数据缓冲区的数据复制到用户提供的缓冲区
uint32_t copy_size = (buffer_size < sizeof(adc_data_buffer)) ? buffer_size : sizeof(adc_data_buffer);
memcpy(data_buffer, adc_data_buffer, copy_size);
return copy_size;
}

void DRIVER_ADC_RegisterDataReadyCallback(void (*callback)(void)) {
adc_data_ready_callback = callback;
}

3. 核心服务层代码示例 (service_oscilloscope.h, service_oscilloscope.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
// service_oscilloscope.h
#ifndef SERVICE_OSCILLOSCOPE_H
#define SERVICE_OSCILLOSCOPE_H

#include "driver_adc.h" // 引入 ADC 驱动
#include "data_buffer.h" // 假设的数据缓冲区模块

typedef struct {
uint32_t timebase; // 时基 (时间刻度)
uint32_t voltage_range; // 电压量程
uint32_t trigger_level; // 触发电平
// ... 其他示波器配置参数
} Oscilloscope_Config;

// 初始化示波器服务
int SERVICE_Oscilloscope_Init();

// 配置示波器参数
int SERVICE_Oscilloscope_SetConfig(Oscilloscope_Config *config);

// 启动示波器采集
int SERVICE_Oscilloscope_StartAcquisition();

// 停止示波器采集
int SERVICE_Oscilloscope_StopAcquisition();

// 获取示波器波形数据
int SERVICE_Oscilloscope_GetWaveformData(uint16_t *data_buffer, uint32_t buffer_size);

#endif // SERVICE_OSCILLOSCOPE_H

// service_oscilloscope.c
#include "service_oscilloscope.h"
#include "rtos_task.h" // 假设的 RTOS 任务管理头文件
#include "event_manager.h" // 假设的事件管理模块

static Oscilloscope_Config current_oscilloscope_config;
static DataBuffer waveform_data_buffer; // 使用数据缓冲区模块管理波形数据
static bool is_acquiring = false;

// ADC 数据就绪事件处理函数
static void Oscilloscope_ADCDataReadyHandler() {
if (is_acquiring) {
uint16_t raw_data[1024]; // 临时缓冲区
uint32_t data_count = DRIVER_ADC_GetData(raw_data, sizeof(raw_data));
// ... 数据预处理 (例如校准, 滤波)
DataBuffer_Write(&waveform_data_buffer, raw_data, data_count);
// ... 触发检测逻辑 (根据 trigger_level 判断是否触发)
if (/* 触发条件满足 */) {
// 发送触发事件 (可以使用事件管理模块)
EventManager_SendEvent(EVENT_OSCILLOSCOPE_TRIGGERED);
}
}
}

// 示波器数据采集任务 (如果使用 RTOS)
void Oscilloscope_AcquisitionTask(void *param) {
while (1) {
if (is_acquiring) {
// 等待 ADC 数据就绪事件 (可以使用事件管理模块或信号量)
EventManager_WaitForEvent(EVENT_ADC_DATA_READY);
Oscilloscope_ADCDataReadyHandler();
} else {
// 如果未采集,则任务休眠或等待启动命令
RTOS_TaskSleep(10); // 示例休眠
}
}
}

int SERVICE_Oscilloscope_Init() {
DataBuffer_Init(&waveform_data_buffer, 4096); // 初始化波形数据缓冲区
DRIVER_ADC_RegisterDataReadyCallback(Oscilloscope_ADCDataReadyHandler); // 注册 ADC 回调函数
// ... 其他初始化操作

// 创建示波器采集任务 (如果使用 RTOS)
RTOS_TaskCreate(Oscilloscope_AcquisitionTask, "OscAcqTask", ...);

return 0;
}

int SERVICE_Oscilloscope_SetConfig(Oscilloscope_Config *config) {
current_oscilloscope_config = *config;
// ... 根据 config 设置 ADC 采样率, 量程等参数 (通过调用 ADC 驱动)
// ... 配置触发参数
return 0;
}

int SERVICE_Oscilloscope_StartAcquisition() {
is_acquiring = true;
DataBuffer_Clear(&waveform_data_buffer); // 清空数据缓冲区
DRIVER_ADC_Start(); // 启动 ADC 采集
return 0;
}

int SERVICE_Oscilloscope_StopAcquisition() {
is_acquiring = false;
DRIVER_ADC_Stop(); // 停止 ADC 采集
return 0;
}

int SERVICE_Oscilloscope_GetWaveformData(uint16_t *data_buffer, uint32_t buffer_size) {
return DataBuffer_Read(&waveform_data_buffer, data_buffer, buffer_size);
}

4. 应用层代码示例 (app_oscilloscope.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
// app_oscilloscope.c
#include "service_oscilloscope.h"
#include "ui_display.h" // 假设的用户界面显示模块
#include "ui_input.h" // 假设的用户界面输入模块

void APP_Oscilloscope_Run() {
Oscilloscope_Config config;
SERVICE_Oscilloscope_Init();

while (1) {
// 获取用户输入 (例如按键, 旋钮)
UserInputEvent event = UI_Input_GetEvent();

switch (event.type) {
case INPUT_EVENT_TYPE_BUTTON_TRIGGER:
if (event.button_id == BUTTON_ID_START_STOP) {
if (!is_oscilloscope_running) {
SERVICE_Oscilloscope_StartAcquisition();
is_oscilloscope_running = true;
} else {
SERVICE_Oscilloscope_StopAcquisition();
is_oscilloscope_running = false;
}
}
break;
case INPUT_EVENT_TYPE_KNOB_ADJUST:
if (event.knob_id == KNOB_ID_TIMEBASE) {
config.timebase = UI_Input_GetKnobValue(KNOB_ID_TIMEBASE);
SERVICE_Oscilloscope_SetConfig(&config);
}
// ... 其他旋钮调整处理
break;
// ... 其他事件处理
}

if (is_oscilloscope_running) {
uint16_t waveform_data[2048]; // 示例波形数据缓冲区
uint32_t data_count = SERVICE_Oscilloscope_GetWaveformData(waveform_data, sizeof(waveform_data));
// ... 数据后处理 (例如缩放, 坐标转换)
UI_Display_DrawWaveform(waveform_data, data_count); // 调用 UI 显示模块绘制波形
UI_Display_UpdateParameters(config.timebase, config.voltage_range); // 更新参数显示
} else {
// 显示静态界面或菜单
UI_Display_ShowMenu();
}

RTOS_TaskSleep(20); // 控制界面刷新频率
}
}

项目中采用的关键技术和方法 (实践验证)

  1. FPGA硬件加速: 利用FPGA的并行处理能力,将一些计算密集型任务 (如数字信号处理算法、FFT、触发逻辑等) 在FPGA硬件中实现,提高系统的性能和实时性。可以使用VHDL或Verilog等硬件描述语言开发FPGA IP核。
  2. DMA数据传输: 使用DMA (Direct Memory Access) 技术,在ADC/DAC和存储器之间进行高速数据传输,减少CPU的负担,提高数据传输效率,尤其对于示波器的高速数据采集至关重要。
  3. 环形缓冲区 (Circular Buffer): 在数据采集和信号生成等模块中,使用环形缓冲区管理数据流,可以有效地处理数据生产和消费速度不匹配的问题,避免数据丢失。
  4. 中断驱动: 采用中断驱动的方式处理硬件事件 (如ADC数据就绪、定时器触发、外部按键等),提高系统的响应速度和实时性。
  5. 状态机: 对于复杂的控制逻辑,如示波器的触发状态管理、信号发生器的波形生成状态管理等,可以使用状态机模型进行设计,使代码结构清晰、易于理解和维护。
  6. 软件滤波算法: 在数据处理模块中,实现数字滤波器 (如FIR滤波器、IIR滤波器) 对采集到的信号进行滤波,去除噪声,提高信号质量。
  7. FFT算法: 实现快速傅里叶变换 (FFT) 算法,用于频谱分析仪功能,将时域信号转换为频域信号,分析信号的频谱特性。
  8. 多种信号发生器波形实现: 实现多种信号发生器波形 (正弦波、方波、三角波、锯齿波、任意波等) 的生成算法,可以通过查表法、直接数字合成 (DDS) 等方法实现。
  9. 参数化配置: 将系统的配置参数 (如采样率、时基、量程、触发电平等) 参数化,方便用户配置和修改,提高系统的灵活性和易用性。
  10. 模块化单元测试: 针对每个模块 (HAL层、驱动层、服务层等) 编写单元测试代码,验证模块的功能和接口的正确性,确保代码质量。
  11. 集成测试和系统测试: 进行模块之间的集成测试和整个系统的系统测试,验证系统的整体功能和性能是否满足需求。
  12. JTAG/SWD调试: 使用JTAG或SWD调试接口,进行硬件调试和软件调试,快速定位和解决问题。
  13. 版本控制 (Git): 使用Git等版本控制工具,管理代码版本,跟踪代码修改历史,方便团队协作和代码维护。
  14. 代码审查: 进行代码审查,提高代码质量,发现潜在的bug和代码风格问题。
  15. 持续集成/持续交付 (CI/CD) (可选): 如果项目规模较大,可以考虑引入CI/CD流程,自动化构建、测试和部署过程,提高开发效率和软件质量。
  16. 固件升级机制: 设计固件升级机制,方便用户在产品发布后进行固件升级,修复bug、增加新功能。可以使用OTA (Over-The-Air) 升级或通过USB/SD卡等方式进行升级。

代码量预估 (3000行以上)

上述示例代码仅仅是冰山一角,一个完整的基于FPGA的便携式多功能仪器项目,代码量肯定会超过3000行。以下是更详细的代码量预估 (仅供参考,实际代码量会因功能复杂度和实现方式而异):

  • HAL层: GPIO、时钟、中断、ADC、DAC、存储器、通信接口等驱动,每个驱动可能几百行代码,HAL层总计约 1000-2000行
  • 设备驱动层: ADC设备驱动、DAC设备驱动、FPGA IP核驱动、用户界面驱动、通信接口驱动等,设备驱动层总计约 1000-2000行
  • 核心服务层: 数据采集服务、信号生成服务、数据处理模块 (滤波、FFT等)、触发模块、显示服务、参数配置管理、命令解析等,核心服务层总计约 2000-4000行
  • 应用层: 示波器应用、信号发生器应用、用户界面应用、系统配置管理、测试诊断应用等,应用层总计约 1000-3000行
  • 测试与维护层: 单元测试、集成测试、系统测试、诊断工具、固件升级等,测试与维护层总计约 500-1000行
  • FPGA硬件描述语言 (VHDL/Verilog): FPGA IP核的硬件描述代码 (ADC/DAC接口、信号处理、触发逻辑等),FPGA硬件描述代码可能达到 数千行甚至上万行 (不计入3000行C代码的预估)。

总计 C 代码量预估: 5500 - 12000 行 (甚至更多)

因此,要实现一个功能完善的基于FPGA的便携式多功能仪器,代码量超过3000行是很正常的。上述代码示例和架构设计旨在提供一个清晰的框架和指导,帮助您理解嵌入式系统开发的复杂性和模块化设计的重要性。

总结

本项目基于FPGA的便携式多功能仪器,采用了分层模块化架构,结合事件驱动和实时性设计思想,构建了一个可靠、高效、可扩展的系统平台。通过HAL层、设备驱动层、核心服务层、应用层和测试维护层的划分,实现了代码的模块化和解耦,提高了代码的可维护性和可重用性。项目中采用了多种经过实践验证的技术和方法,包括FPGA硬件加速、DMA数据传输、环形缓冲区、中断驱动、状态机、软件滤波、FFT、多种信号波形生成、参数化配置、单元测试、集成测试、系统测试、JTAG/SWD调试、版本控制等。这些技术和方法的应用,保证了系统的性能、实时性、可靠性和可扩展性。

希望以上详细的架构设计、代码示例以及关键技术方法的说明能够帮助您理解本项目,并为您未来的嵌入式系统开发提供参考。

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