基于立创GD32E230C8T6的简易数字示波器:从硬件设计到软件实现的完整开源项目
最近有不少朋友问我,想自己动手做一个实用的电子测量工具,既能学习嵌入式开发,又能锻炼硬件设计能力。今天咱们就来聊聊一个非常经典且综合的项目——用国产GD32E230C8T6单片机,从零开始打造一个简易的数字示波器。
这个项目麻雀虽小,五脏俱全。它涵盖了模拟前端信号调理、PCB设计、焊接调试,再到单片机固件开发(ADC采样、TFT显示、PWM信号发生、输入捕获测频)的全过程。无论你是电子专业的学生,还是希望提升综合项目能力的嵌入式爱好者,跟着这个项目走一遍,收获绝对不小。
项目所有的硬件设计文件、PCB工程、物料清单和完整的源代码都已开源,你可以完全复现,也可以在此基础上进行二次开发。下面,我就手把手带你走一遍这个项目的完整流程。
1. 项目核心:硬件电路设计
一个示波器,最核心、也最考验功力的部分就是它的模拟前端电路。这部分电路负责把外部千变万化的信号,调理成单片机ADC能安全、准确读取的“标准信号”。
1.1 模拟前端处理电路详解
很多初学者可能会问:信号直接接到单片机的ADC引脚不行吗?答案是:绝大多数情况下不行。原因有三:
- 电压范围不匹配:GD32E230的ADC输入电压范围是0~3.3V,而外部信号可能是±几十伏甚至更高。
- 信号类型复杂:信号里可能混有直流分量,影响我们对交流信号的观察。
- 阻抗匹配与保护:直接连接可能损坏精贵的单片机引脚。
因此,我们需要一个“信号调理电路”来当“翻译官”和“保镖”。
交直流耦合切换电路
信号可以分为直流信号(DC)和交流信号(AC)。现实中很多信号是“混合”的,比如一个1V的直流电平上叠加了一个1V峰峰值的正弦波。
- 直流耦合:信号直接通过,你会看到一个在1V基准线上波动的正弦波。
- 交流耦合:串联一个电容,利用电容“通交流、隔直流”的特性,滤掉直流分量,你会看到一个在0V基准线上波动的“纯净”正弦波。
项目中通过一个拨动开关SW2来切换这两种模式。开关打到1-2脚是直流耦合,打到2-3脚是交流耦合。
注意:耦合电容C2的选型有讲究。不是随便拿个电容就行。电容对不同频率信号的阻抗不同,大电容通低频,小电容通高频。这个项目选用的是100nF(0.1uF)的瓷片电容,其自谐振频率在4MHz左右,对于入门级示波器的频率范围是足够的。如果你的目标频率更高,就需要选用更小容值的电容。
输入信号衰减电路
为了保护后级电路,我们需要一个“量程切换”功能。当输入信号幅度很大时,先把它“缩小”再送进去测量。
电路中通过另一个拨动开关SW3和电阻分压网络(R7, R11, R14)来实现。当SW3的2-3脚接通时,信号直通(低压档位);当1-2脚接通时,信号被衰减到原来的1/50(高压档位)。
根据原文计算,最终得到两个测量档位:
- 低压档位:测量范围约为-1.6V ~ +5V
- 高压档位:测量范围约为-80V ~ +250V
实际使用时,如果不确定信号大小,务必先使用高压档位进行测量,确认安全后再切换到低压档位以获得更精确的读数。
信号调理与放大电路
经过耦合和衰减后的信号,还不能直接给ADC。因为ADC的输入范围是0~3.3V,而我们的信号可能有正有负(比如-1.6V)。所以需要一个电路,把信号“平移”并“放大”到0~3.3V的范围内。
这个任务由运放TL072(U5)完成。电路设计巧妙地将一个同相放大电路和一个反相放大电路组合在一起。咱们不用深究复杂的公式,只需要知道它的作用:将输入电压Vin,线性地转换到输出电压Vo,且Vo的范围被限制在0~3.3V内,正好给ADC读取。
具体转换公式为:Vo = 1.5 * Vin + 0.8根据这个公式和ADC的0~3.3V范围,我们就能反推出前面提到的测量电压范围了。
比较器测频电路
除了看波形,测频率也是示波器的基本功能。这里用了一个LM393比较器芯片(U4)搭建了一个滞回比较器。
它的工作原理很简单:输入信号(来自运放调理后的信号)与一个固定的参考电压(约2.2V)进行比较。当输入电压高于某个阈值(如2.214V)时,输出跳变为低电平;当输入电压低于另一个阈值(如2.172V)时,输出跳变为高电平。这样,一个模拟的正弦波或三角波,就被“整形成”一个数字方波。
单片机通过输入捕获功能,捕捉这个方波的上升沿或下降沿,计算两个边沿之间的时间间隔,就能轻松算出信号的频率了。滞回比较器的好处是抗干扰能力强,避免了信号在阈值附近抖动造成的误触发。
1.2 其他关键电路
- 电源电路:使用Type-C接口输入5V电源。核心板自带5V转3.3V的LDO给单片机供电。扩展板上使用XD7660电荷泵芯片,将+5V转换成约-4.3V的负电压,为运放提供负电源,使其能够处理负电压信号。
- 单片机电路:核心是立创的GD32E230C8T6最小系统板。它板载了CH340 USB转串口芯片,用一根数据线就能完成程序下载和调试,非常方便。其引脚排列与常见的STM32最小系统板兼容。
- 人机交互电路:
- 1.8寸 TFT屏幕:通过SPI接口与单片机通信,用于显示波形和参数。
- EC11旋转编码器:用于调节波形幅度、时间轴等参数。通过检测A、B两相脉冲的相位差来判断是正转还是反转。
- 独立按键:用于功能切换、暂停/运行等。
- LED指示灯:用于状态指示。
- PWM信号输出:引出了一个PA2引脚,可以输出可调频率和占空比的方波,相当于一个简易的信号发生器,可以用来测试自己的示波器。
2. 动手实践:PCB设计与打样
理论懂了,接下来就是“画板子”。这个项目使用嘉立创EDA专业版进行设计,对国内用户非常友好。
2.1 原理图设计
- 创建工程:打开嘉立创EDA专业版,新建一个工程,命名为“【仪器仪表】基于GD32的简易示波器设计”。
- 放置元件:根据原文提供的物料清单(BOM),在软件的库中搜索并放置所有元器件。清单非常详细,包含了位号、参数和嘉立创的物料编号,直接搜索就能找到。
- 连接与整理:参照原理图,用导线和网络标签连接各个元器件。完成后,可以用矩形框和文字标注出各个功能模块(如电源、模拟前端、MCU等),让图纸更清晰。最后一定要使用DRC(设计规则检查)功能查错。
2.2 PCB布局与布线
- 导入与模块化布局:将原理图导入PCB后,元器件是杂乱堆在一起的。这时可以利用EDA的“布局传递”功能,在原理图中框选一个模块(比如电源部分),然后在PCB里把它们单独拖出来摆放。按功能模块分区布局是做好PCB的第一步。
- 设置板框:嘉立创每月有免费打样券,尺寸限制在10cm x 10cm内。我们这个板子设为70mm x 80mm,完全够用。记得给板框设置2mm的圆角,更美观也不划手。
- 布局原则:
- 先放置有固定位置的接口(如Type-C、BNC接口、按键、屏幕接口)。
- 模拟信号部分(运放周围)要尽量紧凑,远离数字部分(单片机、屏幕)以减少干扰。
- 电源芯片、滤波电容要靠近用电单元。
- 布线技巧:
- 线宽:电源线(VCC, GND)建议用20mil(约0.5mm),信号线用15mil(约0.38mm)。
- 走线:优先在顶层走线,走不通了再用过孔换到底层。尽量避免直角走线,多用135度角或圆弧。
- 覆铜:对地网络(GND)进行覆铜,能提供良好的屏蔽和回流路径。注意模拟地(AGND)和数字地(GND)之间用0欧姆电阻或磁珠单点连接,本项目在原理图中已通过0欧姆电阻R16分隔。
- 泪滴:走线完成后,给焊盘添加泪滴,可以加强连接,防止焊接时铜皮脱落。
- 最终检查:布线完成后,再次运行DRC检查,确保没有开路、短路、间距错误等问题。
2.3 免费PCB打样
板子画好了,就可以在嘉立创下单打样了,每个月有两次免费机会。
- 领取优惠券:在嘉立创下单平台领取“PCB+SMT喷锡免费券”。
- 导出生产文件:在EDA中,点击“文件”->“导出”->“PCB制板文件(Gerber)”。系统会提示检查DRC,确认无误后生成Gerber文件压缩包。
- 下单设置:上传Gerber文件后,关键参数如下:
- 板子层数:2层
- 板子厚度:1.6mm(默认)
- 阻焊颜色:绿色发货最快(48小时),其他颜色需要72小时。
- 数量:免费打样是5片。
- 其他选项保持默认即可。
- 支付与发货:在结算页面使用免费券,填写收货地址,选择免费快递,提交订单等待审核。审核通过后,几天内你就能收到崭新的PCB板了!
3. 焊接与调试:让板子“活”起来
拿到PCB板后,下一步就是焊接。对于新手,建议遵循“从矮到高”的焊接顺序:
- 贴片/小器件优先:电阻、二极管、瓷片电容。
- 芯片插座:焊接TL072、LM393、XD7660的DIP8插座。
- 较高器件:电解电容、按键、拨码开关、BNC接口。
- 接插件最后:焊接排母,最后将GD32核心板和TFT屏幕插上去。
焊接小贴士:嘉立创EDA提供了一个非常棒的焊接辅助工具。在PCB设计页面点击“工具”->“焊接辅助工具”,或者在浏览器中打开离线HTML文件。它可以高亮显示当前要焊接的元件,并支持“仅显示已焊接”选项,能极大降低焊错、漏焊的概率。
焊接完成后,先别急着上电!用万用表蜂鸣档仔细检查:
- 电源短路:测量5V(或3.3V)与GND之间是否短路。
- 关键电压:上电后,测量XD7660的输出,应该是-4.3V左右;测量运放和比较器的供电引脚,应该是+5V和-4.3V。
- 信号通路:可以用一个1.5V的电池(或信号发生器)输入一个小的正弦波,用万用表或另一个示波器,沿着信号通路(BNC->耦合开关->衰减开关->运放输出)逐级测量,看信号是否正常传递和放大。
4. 软件驱动开发:赋予板子“灵魂”
硬件准备就绪,接下来就是重头戏——编写单片机程序。我们使用Keil MDK社区版进行开发,它对学生和爱好者免费。
4.1 开发环境与程序下载
- 安装Keil和驱动:从ARM官网下载Keil MDK社区版并安装。为GD32核心板安装CH340串口驱动和GD32的DFU下载驱动。
- 两种下载方式:
- 串口下载(推荐):用跳线帽将核心板上的BOOT0与1短接,按复位键进入下载模式。使用GD32 AllInOneProgrammer软件,选择对应的COM口,加载编译好的
.hex文件进行下载。下载完成后,将BOOT0跳线帽换到0的位置,复位即可运行。 - DAP-Link下载:如果你有DAP-Link调试器,连接SWD接口(SWDIO, SWCLK, GND),在Keil中配置好即可下载和在线调试。
- 串口下载(推荐):用跳线帽将核心板上的BOOT0与1短接,按复位键进入下载模式。使用GD32 AllInOneProgrammer软件,选择对应的COM口,加载编译好的
4.2 基础外设驱动
在开始复杂的示波器逻辑前,我们需要先点亮所有“技能点”。原文提供了非常详尽的基础实验代码,这里我挑几个核心的讲讲思路:
GPIO控制LED与按键
这是嵌入式开发的“Hello World”。要点是:
- LED:配置对应引脚(如PC14, PC15)为推挽输出模式。输出低电平时点亮(因为LED正极接3.3V,阴极接单片机引脚)。
- 按键:配置对应引脚(如PB13, PB14, PB15)为上拉输入模式。因为按键一端接地,按下时引脚被拉低,所以检测低电平即为按键按下。一定要加软件消抖,延时10ms左右再判断一次。
// 按键检测示例(消抖处理) if(gpio_input_bit_get(GPIOB, GPIO_PIN_13) == RESET) // 检测到低电平 { delay_1ms(10); // 延时消抖 if(gpio_input_bit_get(GPIOB, GPIO_PIN_13) == RESET) // 再次确认 { while(gpio_input_bit_get(GPIOB, GPIO_PIN_13) == RESET); // 等待松手 // 执行按键动作... gpio_bit_reset(GPIOC, GPIO_PIN_14); // 点亮LED } }ADC采样
这是示波器的“眼睛”,负责读取模拟前端调理后的电压值。
// ADC初始化关键步骤 rcu_periph_clock_enable(RCU_ADC); // 使能ADC时钟 rcu_adc_clock_config(RCU_ADCCK_AHB_DIV3); // ADC时钟选择AHB 3分频 (72/3=24MHz) gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_3); // PA3设为模拟输入 adc_resolution_config(ADC_RESOLUTION_12B); // 12位分辨率 adc_regular_channel_config(0, ADC_CHANNEL_3, ADC_SAMPLETIME_55POINT5); // 配置通道3 adc_enable(); // 使能ADC adc_calibration_enable(); // 开始校准采样策略:为了实时显示波形,我们需要连续快速地采样。可以使用ADC的扫描模式配合DMA,或者用定时器触发ADC采样。在循环中不断采样,并将采样值存入一个数组(波形缓冲区)。
定时器与PWM输出
定时器是单片机的“心脏”,节奏全靠它。
- 定时器中断:用于产生固定的时间基准,比如每隔1ms执行一次任务,可以用来刷新屏幕、扫描按键。
- PWM输出:用于产生测试用的方波信号。通过修改定时器的自动重装载值(ARR)可以改变频率,修改比较值(CCR)可以改变占空比。
// 配置PWM输出(以TIMER14_CH0, PA2为例) timer_parameter_struct timer_initpara; timer_oc_parameter_struct timer_ocinitpara; // 定时器基础配置 timer_initpara.prescaler = 71; // 预分频72-1 timer_initpara.period = 999; // 自动重装载值1000-1 // 计算:PWM频率 = 72MHz / (71+1) / (999+1) = 1kHz timer_init(TIMER14, &timer_initpara); // PWM通道配置 timer_ocinitpara.outputstate = TIMER_CCX_ENABLE; timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH; timer_channel_output_config(TIMER14, TIMER_CH_0, &timer_ocinitpara); // 设置占空比:比较值500, 周期1000, 占空比50% timer_channel_output_pulse_value_config(TIMER14, TIMER_CH_0, 500); timer_channel_output_mode_config(TIMER14, TIMER_CH_0, TIMER_OC_MODE_PWM1); timer_enable(TIMER14); // 使能定时器输入捕获测频
这是测量信号频率的关键。利用定时器的输入捕获功能,捕捉比较器输出的方波边沿。
// 配置输入捕获(以TIMER2_CH0, PA6为例,捕获上升沿) timer_ic_parameter_struct timer_icinitpara; timer_icinitpara.icpolarity = TIMER_IC_POLARITY_RISING; // 上升沿捕获 timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI; // 直接输入 timer_input_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara); timer_interrupt_enable(TIMER2, TIMER_INT_CH0); // 使能通道捕获中断 // 在中断服务函数中计算频率 void TIMER2_IRQHandler(void) { if(捕获标志置位){ static uint16_t last_capture = 0; uint16_t this_capture = timer_channel_capture_value_register_read(TIMER2, TIMER_CH_0); uint32_t period = this_capture - last_capture; // 计算周期计数 float freq = (float)SystemCoreClock / (timer_prescaler+1) / period; // 计算频率 last_capture = this_capture; // 更新频率显示... } }TFT屏幕显示
1.8寸TFT屏通常使用SPI接口驱动。驱动流程是:初始化GPIO和SPI外设 -> 按照屏幕驱动芯片(如ST7735S)的时序发送初始化命令 -> 设置显示区域 -> 写入像素数据。 显示波形的核心是将ADC采样值数组映射到屏幕的Y坐标,然后以时间为X轴,将相邻的点用线连接起来。
// 简单的画线函数思路(假设屏幕Y轴0在最上方) void draw_waveform_point(uint16_t x, uint16_t adc_value) { // 将ADC值(0-4095)映射到屏幕Y轴的有效范围内(例如 30-130像素) uint16_t y = SCREEN_HEIGHT - ((adc_value * (130-30)) / 4095 + 30); TFT_DrawPoint(x, y, COLOR_WAVE); // 画点 // 如果需要,可以与上一个点连线 TFT_DrawLine(...) }4.3 综合程序框架
将以上所有模块组合起来,就构成了示波器的主程序框架:
- 系统初始化:时钟、GPIO、ADC、定时器、SPI、屏幕等。
- 主循环:
- 状态机:根据当前模式(运行、暂停、设置)执行不同任务。
- 波形采集:在定时器中断或循环中触发ADC采样,填充波形缓冲区。
- 波形处理:对采样数据进行必要的滤波、幅值/时间轴换算。
- 显示刷新:将处理后的波形数据、网格、参数(电压档位、频率、触发状态等)绘制到屏幕上。
- 人机交互:扫描编码器和按键,根据输入调整参数(如垂直灵敏度V/div、水平时基S/div、触发模式等)。
- 频率测量:在输入捕获中断中计算频率,并更新显示。
- 信号输出:根据设置,从PWM引脚输出特定频率和占空比的测试信号。
5. 项目总结与进阶思考
跟着这个项目做下来,你已经完成了一个功能完整的简易数字示波器。它具备了信号采集、显示、测量和简单信号发生的能力。但这只是一个起点,你可以在此基础上进行很多有趣的升级:
- 提高性能:尝试优化ADC的采样率,使用DMA传输减少CPU开销,以提高带宽。
- 增加功能:实现自动触发(边沿触发、电平触发)、FFT频谱分析、波形存储与回放、测量游标等。
- 改善交互:设计更友好的UI菜单系统,用编码器进行多级参数调节。
- 外壳与结构:使用3D打印或亚克力板为你的示波器制作一个漂亮的外壳。
这个项目最大的价值在于,它不是一个简单的模块拼接,而是让你真正理解了从模拟信号输入,到数字处理,再到图形化显示的完整链条。过程中遇到的每一个问题,无论是硬件上的干扰、电源噪声,还是软件上的时序冲突、数据同步,都是宝贵的经验。
希望这个教程能帮你打开嵌入式系统与仪器设计的大门。项目的所有源码和设计文件都在开源仓库里,遇到问题可以随时查阅。动手去做吧,在实践中学习,才是嵌入式开发最快的成长路径。