简介:本项目为Aleksa在 https://hackaday.io/project/180090-thunderscope 上开源的项目「开源四通道示波器 ThunderScope」的复刻。
我很乐意为您详细阐述ThunderScope开源四通道示波器的嵌入式系统代码设计架构,并提供相应的C代码实现框架。我们将从需求分析出发,逐步深入到系统架构设计、模块划分、关键技术实现、测试验证以及维护升级等方面,力求构建一个可靠、高效、可扩展的嵌入式示波器平台。
关注微信公众号,提前获取相关推文
项目简介回顾:
ThunderScope是一个开源的四通道示波器项目,目标是提供一个经济实惠、功能全面的替代传统示波器的方案。其核心在于利用现代微控制器和高速ADC芯片,结合软件算法实现信号采集、处理和显示。开源的特性意味着我们可以深入了解其设计思想,并在此基础上进行复刻和改进。
一、需求分析
在任何嵌入式系统开发之初,需求分析都是至关重要的环节。对于ThunderScope这样的示波器项目,我们需要明确以下几个关键需求:
- 通道数量: 四个模拟输入通道,能够同时采集四个信号。
- 采样率和带宽: 需要确定示波器的最大采样率和带宽,这直接决定了其能够捕获的信号频率范围。例如,我们可能需要达到至少几十MHz的采样率和带宽,以满足通用示波器的需求。
- 垂直分辨率: ADC的位数决定了垂直分辨率,例如8位、12位甚至更高,这将影响信号的精度。
- 触发功能: 示波器必须具备多种触发模式,如边沿触发、脉宽触发、视频触发等,以便稳定捕获感兴趣的信号。
- 时基和垂直灵敏度调节: 用户需要能够调节时基(水平方向的时间刻度)和垂直灵敏度(垂直方向的电压刻度),以便观察不同幅度和频率的信号。
- 显示功能: 需要将采集到的波形数据以图形化的方式显示出来,通常是通过连接外部显示屏或使用嵌入式系统的LCD屏幕。
- 测量功能: 示波器应具备基本的测量功能,如峰峰值、均方根值、频率、周期、占空比等。
- 数据存储和导出(可选): 根据需求,可以考虑是否需要将采集到的波形数据存储到本地存储器或导出到上位机进行进一步分析。
- 用户界面: 需要设计用户友好的界面,方便用户操作和配置示波器。这可以通过按键、旋钮、触摸屏等方式实现。
- 可靠性和稳定性: 嵌入式系统必须保证长时间稳定运行,避免程序崩溃或数据采集错误。
- 可扩展性: 系统架构应具备良好的可扩展性,方便未来添加新的功能或改进性能。
- 低功耗(可选): 如果示波器需要便携式应用,则需要考虑功耗问题。
二、系统架构设计
基于以上需求分析,我们可以设计一个分层式的嵌入式系统架构,这种架构具有良好的模块化和可维护性。主要分为以下几个层次:
-
硬件层 (Hardware Layer):
- 微控制器 (MCU): 作为系统的核心处理器,负责控制整个示波器的运行,包括数据采集、处理、显示和用户交互。我们需要选择一款性能足够强大,外设资源丰富的MCU,例如基于ARM Cortex-M系列的高性能MCU。
- 模数转换器 (ADC): 负责将模拟输入信号转换为数字信号。我们需要选择高速、高分辨率、多通道的ADC芯片,例如四通道同步采样ADC。
- 模拟前端 (AFE): 负责对输入信号进行调理,例如放大、衰减、滤波、阻抗匹配等,以满足ADC的输入范围和性能要求。
- 显示屏接口 (Display Interface): 用于连接显示屏,例如SPI、Parallel RGB、LVDS等接口。
- 输入设备接口 (Input Interface): 用于连接用户输入设备,例如按键、旋钮、触摸屏等,通常通过GPIO、SPI、I2C等接口实现。
- 存储器 (Memory): 包括Flash存储器(用于存储程序代码和配置数据)和RAM存储器(用于程序运行和数据缓存)。
- 电源管理 (Power Management): 负责系统的电源供应和管理,包括稳压、滤波、保护等。
- 时钟源 (Clock Source): 提供系统时钟,确保各个模块的同步运行。
-
硬件抽象层 (HAL - Hardware Abstraction Layer):
- HAL层位于硬件层之上,为上层软件提供统一的硬件访问接口。它将底层的硬件细节抽象出来,使得上层软件可以独立于具体的硬件平台进行开发和移植。
- HAL层主要包括对MCU外设的驱动,例如GPIO驱动、ADC驱动、定时器驱动、SPI驱动、I2C驱动、UART驱动、显示屏驱动等。
- HAL层的设计应遵循模块化和可配置化的原则,方便更换硬件平台或修改硬件配置。
-
驱动层 (Driver Layer):
- 驱动层构建在HAL层之上,提供更高级别的硬件控制接口。例如,在ADC驱动之上,可以构建示波器专用的ADC采集驱动,封装ADC的初始化、启动、停止、数据读取等操作,并提供更友好的API。
- 驱动层可以包括:
- ADC采集驱动 (ADC Acquisition Driver): 负责配置ADC参数,启动ADC采集,读取ADC数据,并进行初步的数据处理(例如校准、滤波等)。
- 触发驱动 (Trigger Driver): 实现各种触发模式,例如边沿触发、脉宽触发等,并控制ADC的采集启动。
- 显示驱动 (Display Driver): 负责将波形数据转换为图形数据,并驱动显示屏进行显示。可以包括图形库的封装,例如GUI库或简单的图形绘制函数。
- 输入设备驱动 (Input Device Driver): 处理用户输入事件,例如按键按下、旋钮旋转、触摸屏触摸等,并将事件传递给上层应用逻辑。
- 时基控制驱动 (Timebase Control Driver): 控制示波器的时基,调整采样率和水平方向的显示范围。
- 垂直灵敏度控制驱动 (Vertical Sensitivity Control Driver): 控制示波器的垂直灵敏度,调整信号的放大或衰减倍数。
-
核心逻辑层 (Core Logic Layer):
- 核心逻辑层是示波器软件的核心部分,负责实现示波器的主要功能,例如数据采集、信号处理、触发控制、测量计算、数据管理等。
- 核心逻辑层主要包括:
- 数据采集模块 (Data Acquisition Module): 调用ADC采集驱动,负责从ADC获取原始数据,并存储到数据缓冲区中。
- 触发模块 (Trigger Module): 根据用户设置的触发条件,检测触发事件,并控制数据采集模块的启动和停止。
- 信号处理模块 (Signal Processing Module): 对采集到的数据进行处理,例如滤波、平均、插值、FFT等,以改善信号质量或提取信号特征。
- 波形显示模块 (Waveform Display Module): 将数据缓冲区中的波形数据转换为屏幕坐标,并调用显示驱动进行绘制。
- 测量模块 (Measurement Module): 根据用户选择的测量类型,对波形数据进行计算,得到测量结果,例如峰峰值、均方根值、频率、周期等。
- 参数配置模块 (Parameter Configuration Module): 管理示波器的各种参数配置,例如采样率、时基、垂直灵敏度、触发模式等,并提供用户配置接口。
- 数据存储模块 (Data Storage Module, 可选): 负责将采集到的波形数据存储到本地存储器或导出到外部设备。
-
应用层 (Application Layer):
- 应用层是用户直接交互的界面,负责处理用户输入,调用核心逻辑层的功能,并将结果显示给用户。
- 应用层主要包括:
- 用户界面 (User Interface): 提供图形化的用户界面或命令行界面,方便用户操作和配置示波器。
- 命令解析器 (Command Parser): 解析用户输入的命令,并调用相应的处理函数。
- 状态管理 (State Management): 管理示波器的各种状态,例如运行状态、配置状态、测量状态等。
三、代码设计架构详解
我们将采用模块化、事件驱动、分层设计的架构来实现ThunderScope的代码。
1. 模块化设计:
将系统划分为独立的模块,每个模块负责特定的功能,模块之间通过定义清晰的接口进行通信。这提高了代码的可读性、可维护性和可重用性。
模块划分示例:
hal_adc.c/h
: HAL层ADC驱动模块hal_gpio.c/h
: HAL层GPIO驱动模块drv_adc_acq.c/h
: ADC采集驱动模块drv_trigger.c/h
: 触发驱动模块drv_display.c/h
: 显示驱动模块ui_input.c/h
: 用户输入处理模块core_acq.c/h
: 核心数据采集模块core_trigger.c/h
: 核心触发逻辑模块core_process.c/h
: 核心信号处理模块core_display.c/h
: 核心波形显示模块core_measure.c/h
: 核心测量模块app_main.c
: 主应用程序模块config.h
: 系统配置头文件
2. 事件驱动:
采用事件驱动的机制来处理用户输入、定时器事件、ADC数据就绪等事件。当事件发生时,系统会调用相应的事件处理函数进行处理。这使得系统能够高效地响应外部事件,并保持实时性。
事件类型示例:
EVENT_BUTTON_PRESSED
: 按键按下事件EVENT_ENCODER_ROTATED
: 旋钮旋转事件EVENT_ADC_DATA_READY
: ADC数据采集完成事件EVENT_TIMER_TICK
: 定时器tick事件EVENT_TRIGGER_DETECTED
: 触发事件检测到
事件处理流程示例:
// 事件处理函数指针类型
typedef void (*EventHandler)(Event event);
// 事件结构体
typedef struct {
EventType type;
// ... 事件数据 ...
} Event;
// 事件处理函数注册表
EventHandler eventHandlers[NUM_EVENTS];
// 添加事件处理函数
void registerEventHandler(EventType type, EventHandler handler) {
eventHandlers[type] = handler;
}
// 事件分发函数
void dispatchEvent(Event event) {
if (eventHandlers[event.type] != NULL) {
eventHandlers[event.type](event);
}
}
// 示例事件处理函数 - 按键按下
void handleButtonPress(Event event) {
// ... 处理按键按下事件 ...
}
// 初始化事件系统
void eventSystemInit() {
// 初始化事件处理函数注册表
for (int i = 0; i < NUM_EVENTS; i++) {
eventHandlers[i] = NULL;
}
// 注册事件处理函数
registerEventHandler(EVENT_BUTTON_PRESSED, handleButtonPress);
// ... 注册其他事件处理函数 ...
}
// 主循环中处理事件
int main(