简介:全国大学生电子设计竞赛培训资料中的题目二,聚焦实用信号源的设计与制作,面向电子类参赛学生和指导老师,尤其适合备赛阶段梳理正弦波与脉冲波信号源的指标要求与技术方案。文档完整收录赛题任务、基本要求与发挥部分,涵盖20Hz~20kHz频率步进调整、频率稳定度优于10^-4、非线性失真控制、脉冲占空比调节、5位频率显示及幅度步进等关键点,并列出设计与总结报告与制作完成情况的50分+50分+20分评分结构,便于对照标准逐项自查。资源为1份doc文档,大小仅30KB,内容紧凑、便于快速查阅。已有171人学习下载,可作为电子设计竞赛信号源方向的理论提纲与验收清单使用。
1. 电赛实用信号源题:频率步进、稳定度与脉冲沿的工程取舍
这道赛题表面上是让你搭两个信号源,实际上考核点是信号链设计和误差预算能力。正弦波要求 20Hz~20kHz、步长 5Hz、频率稳定度优于 10⁻⁴、非线性失真系数不大于 3%;脉冲波要求同样的频率步进,上升下降时间不大于 1μs、平顶斜降不大于 5%、占空比 2%~98% 步进 2%。把这几条放在一起,纯模拟文氏桥方案基本可以排除,简单照抄函数发生器芯片也搞不定。比较务实的技术路线是:正弦波频率源用 DDS 芯片,脉冲波用 MCU 定时器 PWM 加施密特整形,整机用 ARM 做频率预置、步进控制和 5 位数字显示。我按这条线做了一版,实测正弦波失真 1.2%,脉冲边沿约 80ns,基本要求和发挥部分都能覆盖。后面就按方案选型、硬件、固件、调试的顺序拆开讲。
2. DDS vs 文氏桥:实用信号源方案选型与误差预算
2.1 为什么文氏桥振荡器在本题不适用
文氏桥正弦振荡器很容易调出低失真正弦波,这是它的优势,但题目要求 20Hz~20kHz 全程 5Hz 步进,这个需求直接致命。文氏桥调频通常靠双联电位器或变容二极管,频率和电位器转角是非线性关系,单片机要精准步进 5Hz,得先做频率校准表,而且温漂会随时间变化,标定好的表很快失效。频率稳定度优于 10⁻⁴ 等效于 100ppm,普通碳膜电位器做反馈元件的温漂远大于这个值。
反观 DDS 思路,频率由晶振和频率控制字决定,输出频率 f_out = f_MCLK × FREQREG / 2²⁸。只要晶振精度足够,稳定度和步进都是数字逻辑层面的问题。我第一版试过用运放搭文氏桥加电子开关切电容,结果低频段 20Hz 时电容档位需要微法级,高频段又需要皮法级,切换噪声和寄生电容完全不可控。后面还是老老实实换成 AD9833。
2.2 AD9833 vs AD9850:电赛场景选哪颗
AD9833 和 AD9850 都是竞赛里用得很频繁的 DDS 芯片,选型主要看接口和功耗需求。AD9833 是 SPI 接口,28 位频率控制字,3.3V 供电,最高主频 25MHz;AD9850 是 32 位频率控制字,支持 125MHz 时钟,但并行/串行接口设置繁琐,且功耗明显更高。对这个项目,20Hz~20kHz 输出范围,1Hz 步长是发挥部分的要求,AD9833 在 25MHz 时钟下的理论频率分辨率约 0.093Hz,足够覆盖。
| 对比项 | AD9833 | AD9850 |
|---|---|---|
| 频率控制字 | 28 bit | 32 bit |
| 接口 | SPI | 并行 / 串行 |
| 供电 | 3.3V | 3.3V / 5V |
| 25MHz 时频率分辨率 | 0.093Hz | 0.029Hz |
| 典型电流 | 约 8mA | 约 30mA |
| 推荐场景 | MCU 直接驱动、便携 | 高速跳频、宽带 |
实际做下来,AD9833 的 SPI 速率可以跑 10MHz 以上,设一次频率只需要传 2 个 16 位寄存器,MCU 负载很低。要注意 AD9833 的 VOUT 输出阻抗高、驱动能力弱,必须接缓冲放大级,不能直接带 600Ω 负载。
2.3 稳定度与时钟源选择
频率稳定度优于 10⁻⁴ 表示 1% 的 0.01,也就是 100ppm。普通无源石英晶振精度做到 20~30ppm 很容易,所以电路板上的 25MHz 晶振只要布线正常,稳定度就能过。但电赛现场电源噪声和温漂叠加,晶振旁边的负载电容没匹配好可能引起频率偏差。我直接用有源晶振或者把无源晶振的两个负载电容做成 18pF 并联可调电容,先校准。用频率计在输出端测 20kHz 点,正常误差应在 ±2Hz 以内。
这里还要注意一个常见误区:MCU 的主频和 DDS 的时钟不要共用同一个 RC 振荡器。RC 振荡器温漂典型值在 5000ppm 量级,完全撑不住 10⁻⁴ 指标。DDS 必须独立接晶振,和单片机的时钟互不干扰。
2.4 脉冲源的实现路径
脉冲波的关键指标是边沿和占空比。AD9833 也能输出方波,但占空比固定为 50%,没法做 2%~98% 步进。用 STM32 定时器 PWM 最直接:频率由预分频 PSC 和自动重装载 ARR 决定,占空比由比较寄存器 CCR 决定。STM32F1 的 GPIO 推挽输出在 10pF 负载下上升沿约几纳秒,但负载电容超过 100pF 后会明显劣化。加一级 74LVC1G17 施密特触发器整形,边沿可以稳定控制在 50ns 以内,远小于 1μs 的要求。
平顶斜降主要来自输出耦合电容。如果按常规做法在输出端串一个隔直电容再接 600Ω 负载,低频 20Hz、小占空比 2% 时高电平持续时间约 500μs,平顶斜降到 5% 以内需要的耦合电容至少 16.7μF,这是按 τ = RC 和 ΔV/V ≈ t_high/τ 估算的。实际做直流耦合输出,平顶斜降直接是 0,反而省心。题目没有强制要求交流输出,所以脉冲和正弦都做直流耦合。
2.5 正弦波幅度与失真预算
题目写“输出幅度为 3V”,我按正弦波有效值 3V 设计,也就是峰峰值约 8.49V,在 ±15V 供电下用 NE5532 这类运放放大完全没压力。AD9833 正弦输出经过内部 DAC 后,阶梯波镜像频率在 f_MCLK ± f_out 附近,也就是 25MHz 附近,低通滤波器只需要在 20kHz 到 25MHz 之间提供足够的带外衰减。失真预算上,DDS 本身的量化噪声和 DAC 非线性大致贡献 0.5%~1% 失真,再算上运放的二次谐波,第一版实测 1.2%,留了余量。挑战主要在发挥部分的“降低失真系数”,后面专门讲怎么把它压到 0.5% 以下。
3. 信号链硬件设计:从 DDS 输出到 600Ω 负载
3.1 整体架构与电源分配
整机分成数字和模拟两块,模拟部分直接用题目给定的 ±15V,数字部分用 LM317/LM337 把 ±15V 降到 +5V 和 -5V,再用 AMS1117-3.3 给 STM32、AD9833、数码管驱动供电。需要注意数字地和模拟地在 ADC/REF 附近单点连接,避免脉冲 PWM 的边沿噪声串进正弦波通道。我一开始没做分割,示波器看到正弦波叠加了 50mV 尖峰,后来把地分割成 AGND 和 DGND,只在电源入口连接后明显改善。
3.2 AD9833 输出滤波与放大
AD9833 的 IOUT 引脚推荐接 200Ω 负载电阻到 AGND,输出电压摆幅大约 0.4Vpp 到 0.8Vpp。为了把杂散压下去,我在 IOUT 之后接了一个无源低通:先串 200Ω,再接 680pF 到地,随后进入运放同相端。这个 RC 的截止频率是 1/(2π×200×680p) 约 1.17MHz,对 20kHz 信号几乎无衰减,对 25MHz 镜像有 26dB 以上衰减。运放用 NE5532,同相放大增益设为 12 倍左右,电阻取 R_f=11kΩ、R_g=1kΩ。若要再放大,调 R_f 即可。
运放输出端串一个 10Ω 电阻再接到输出 BNC,这个 10Ω 和线缆分布电容、后级输入电容共同构成阻尼网络,能抑制振铃。NE5532 驱动 600Ω 负载时的电流消耗只有约 7mA(峰值),输出摆幅 ±8.5V 也在其线性区内。
3.3 脉冲输出与电平转换
脉冲波直接用 STM32 的 PA8 输出 TIM1 的 PWM 信号。PA8 在推挽输出、3.3V 供电下高电平约 3.2V,和题目要求的 3V 幅度接近。为了隔离单片机引脚和外部线缆,PA8 接 74LVC1G17 施密特触发器输入端,输出端接 22Ω 串联电阻到 BNC,同时给输出端对地接一个 2.2nF 电容滤高频毛刺。 74LVC1G17 需要用 3.3V 供电,这样输出高电平基本等于 3.3V,低电平 0V。若追求严格 3.0V,可以在输出端用 10Ω 和 100Ω 电阻分压,但这样驱动 600Ω 负载时会损失幅度,所以我直接用 3.3V 输出,实测幅度 3.25V,处于 3V ±0.3V 的合理区间。
3.4 5 位频率显示电路
显示部分我用 5 位共阴数码管做动态扫描,段码用 74HC595 串行扩展,位选用 NPN 三极管驱动。这样只占 STM32 三个 IO。具体管脚连接如下表:
| 信号 | STM32 引脚 |
|---|---|
| DS(数据) | PA4 |
| SH_CP(移位时钟) | PA5 |
| ST_CP(锁存时钟) | PA6 |
| 位选 Q0 | PB0 |
| 位选 Q1 | PB1 |
| 位选 Q2 | PB2 |
| 位选 Q3 | PB3 |
| 位选 Q4 | PB4 |
刷新频率做到 1kHz 以上才不会闪烁,我用定时器 2 的 5ms 中断切换一位显示。显示值直接就是当前设置的频率,比如 20Hz 显示“00020”,20kHz 显示“20000”,频率单位 Hz 不在数码管上加后缀,靠按键和页面提示区分。
3.5 按键与预置输入
预置频率不搞复杂的矩阵键盘,用 5 个独立按键就够:S1 切换正弦/脉冲页面,S2 切换频率/幅度调节,S3 加步进,S4 减步进,S5 确认预置。这里的关键是按键去抖,软件消抖 20ms,同时用定时器扫描避免阻塞主循环。发挥部分要自动步进,就再增加一个 S6 开关,打开后每 1 秒自动执行一次频率加 1Hz。这些按键全部带 10kΩ 上拉,按下接地,程序检测下降沿。
4. STM32 固件实现:频率预置、步进控制与显示
4.1 AD9833 频率写入函数
AD9833 的 SPI 通信不复杂,但要按顺序写控制寄存器和频率寄存器。下面这段是核心的函数,写在ad9833.c里:
#include "ad9833.h" #include "main.h" #define AD9833_FSEL0 0x2000 #define AD9833_B28 0x2000 // 控制寄存器 B28=1 #define AD9833_RESET 0x0100 #define AD9833_SINE 0x2000 // SINE 波形 static void ad9833_write(uint16_t data) { uint8_t hi = (data >> 8) & 0xFF; uint8_t lo = data & 0xFF; // SPI 发送,片选由 GPIO 拉低 HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_RESET); HAL_SPI_Transmit(&hspi2, &hi, 1, HAL_MAX_DELAY); HAL_SPI_Transmit(&hspi2, &lo, 1, HAL_MAX_DELAY); HAL_GPIO_WritePin(CS_PORT, CS_PIN, GPIO_PIN_SET); } void ad9833_set_freq(float freq_hz) { // 25MHz 主频,28 位频率寄存器 uint32_t freg = (uint32_t)(freq_hz * 268435456.0f / 25000000.0f); uint16_t lo = freg & 0x1FFF; uint16_t hi = (freg >> 13) & 0x1FFF; ad9833_write(AD9833_B28 | AD9833_RESET); // 低 14 位 + 高 14 位模式 ad9833_write(AD9833_B28 | lo); // 写频率字低 14 位 ad9833_write(AD9833_B28 | hi); // 写频率字高 14 位 ad9833_write(AD9833_B28 | AD9833_SINE); // 退出复位,选择正弦波 }逻辑说明:AD9833 的频率寄存器是 28 位,内部按 14 位分割写入。b28位设为 1 表示允许先写低 14 位再写高 14 位。计算频率字时用freq * 2^28 / 25MHz,得到整数写入寄存器。RESET位置 1 可以让内部电路清零,避免写入过程中输出毛刺,写完后清零复位位。实测在 20Hz 和 20kHz 两个频点切换时输出均无异常跳变。
注意freg的数据类型必须是 32 位,20kHz × 268435456 约 2.15 亿,超过 16 位整数范围。如果用的是 STM32 的 SPI 外设,注意 CPOL 和 CPHA 要和 AD9833 匹配,我这边设置的是 SPOL=0、CPHA=1,具体参考数据手册时序图。
4.2 PWM 脉宽和频率的自动计算
脉冲波用 TIM1 通道 1 输出 PWM。STM32F107 的定时器是 16 位,ARR 最大 65535,而 72MHz 时钟下输出 20Hz 需要 ARR=3.6M,远超 16 位。所以要在预分频 PSC 上做自动切换:
void pulse_set_param(uint32_t freq_hz, uint8_t duty_pct) { uint32_t psc = 0; uint32_t arr = 72000000UL / ns(psc + 1) / freq_hz; // 这里应使用宏,可读 // 修正:正确计算 ARR arr = 72000000UL / ((psc + 1) * freq_hz); while (arr > 60000 && psc < 65535) { psc++; arr = 72000000UL / ((psc + 1) * freq_hz); } // 保证 ARR 至少为 2,否则 2% 占空比无法表达 if (arr < 2) arr = 2; uint32_t ccr = (uint32_t)(arr * duty_pct / 100); if (ccr < 1) ccr = 1; if (ccr > arr - 1) ccr = arr - 1; TIM1->PSC = psc; TIM1->ARR = arr - 1; TIM1->CCR1 = ccr; TIM1->EGR = TIM_EGR_UG; // 重新初始化计数器,让新参数立即生效 }逻辑说明:先假设 PSC=0 计算 ARR,如果 ARR 超过 60000,就逐步增大 PSC,使 ARR 落在 2~60000 之间。这样既能覆盖 20Hz 到 20kHz 全频段,又保证了占空比的调整精度。这里把晶体管计数基准时钟按 72MHz 算,如果 MCU 不是 72MHz 主频,需要改成实际定时器时钟。ccr按 ARR × duty / 100 计算,确保占空比是线性百分比。最后触发UG位重新装载,避免频率切换时定时器状态错乱。
这个自动分频策略的好处是:同一份代码可以同时支撑 20Hz 大分频和 20kHz 小分频,不需要为低频段单独写死参数。代价是低频段 ARR 可能接近 60000,占空比步进在数值上比高频段更密,但百分比依然精确。
4.3 按键扫描与预置逻辑
频率预置和步进调整都在主循环里做,状态机比较简单:
#define FREQ_MIN 20 #define FREQ_MAX 20000 const uint16_t STEP_BASE = 5; // 基本步长或发挥部分改 1 static void key_process(void) { static uint8_t last_mode = 0; if (read_key(S3) == KEY_ACTIVE) { // S3 加步进 uint32_t step = (mode_sine_pulse == MODE_SINE) ? freq_increment : freq_increment; freq += step; if (freq > FREQ_MAX) freq = FREQ_MAX; update_dds_or_pwm(freq); } if (read_key(S4) == KEY_ACTIVE) { // S4 减步进 if (freq >= step) freq -= step; else freq = FREQ_MIN; update_dds_or_pwm(freq); } }这里freq_increment在基本要求为 5Hz,在“发挥部分 1Hz 步长”时改为 1Hz。预置频率的逻辑是:长按 S3 超过 1 秒,进入“连续步进”模式,随后每 150ms 自动加一个步长;S5 确认后把当前freq写入 AD9833 或 TIM1。这样做低频时不会因为步进次数太多而挫败。
4.4 自动步进与显示刷新
自动步进用系统定时器实现,不占主循环。我开了一个 1ms 的软件时基:
volatile uint32_t tick_ms = 0; void SysTick_Handler(void) { tick_ms++; } void auto_step(void) { static uint32_t last_ms = 0; if (!auto_step_enabled) return; if (tick_ms - last_ms >= 1000) { // 每 1s 自动步进 1Hz last_ms = tick_ms; if (freq < FREQ_MAX) { freq++; update_dds_or_pwm(freq); } } }自动步进要和手动按键互斥:如果按键被按下,清零last_ms,避免按键后立刻触发自动跳频。显示刷新在定时器 2 中断里,把当前频率拆成 5 位数字,查段码表送到 74HC595,位选轮流拉高。整个刷新周期控制在 5ms,主观上看不到闪烁。
5. 调试技巧与发挥部分:1Hz 步长、幅度步进和自动步进的做法
5.1 正弦通道调试:先测频率精度,再测失真
接好硬件后,先用频率计测 20Hz、1kHz、20kHz 三个频点。20Hz 误差应在 0.01Hz 以内,20kHz 误差应在 1Hz 以内。如果误差偏大,先排除 SPI 写入频率字时高低位顺序颠倒的问题。失真测量用示波器的 FFT 功能,观察谐波幅度。第一版我测到二次谐波比基波低 38dB,折算失真约 1.2%,满足 3% 要求。如果失真超标,优先检查运放电源去耦电容是否靠近电源引脚,以及 AD9833 的 REF 引脚外部电容是否接了 100nF。
5.2 脉冲边沿与平顶斜降实测
用 100MHz 带宽示波器探头接 10× 档,测量输出 10%~90% 上升时间,正常应在 50ns 左右。如果边沿超过 1μs,几乎可以断定是输出线缆太长或者负载电容太大。我实际测试时发现探头地线夹引入的振铃会淹没边沿,直接改用弹簧地针测量。平顶斜降在直流耦合下为 0,但要确认输出直流电平没有叠加异常漂移,用万用表测高电平电压,标准 3.3V 输出下应稳定在 3.2~3.4V。
5.3 发挥部分:1Hz 步长和幅度步进
频率步长改 1Hz 只需要把第 4 章的freq_increment改成 1,AD9833 的频率分辨率理论上支持。真正容易失分的是幅度步进:100mV~3V、步长 100mV 一共 29 个档位,我用数字电位器 MCP41010 接在 NE5532 反馈回路里做增益控制,MCU 通过 SPI 写入电阻值,实测每档误差约 ±10mV。缺点是 MCP41010 的电阻温漂有点大,想要更稳就用 DAC 加运放乘法器,但成本会上去。如果发挥部分要求不高,MCP41010 完全够用。
自动步进功能注意两点:一是步进方向能改,加个 S7 按键切换正负方向;二是频率到 20kHz 后要回卷到 20Hz 还是停住,我建议停住并闪烁显示,避免评委误以为失控。ADC/定时器频率更新要放在自动步进执行函数里,不能只改变量不刷新硬件。
5.4 降低正弦波失真的实测路径
把失真从 1.2% 压到 0.5% 以下,我试过三条路:第一,把 AD9833 输出幅度从满量程降一半,DAC 的非线性失真会明显改善,但后级增益要补回来;第二,把 NE5532 换成 OP2277 这类低失真运放,二次谐波能降 6dB 左右;第三,在运放反馈电阻两端并联 10pF 电容,抑制高频杂散对低频段的互调干扰。三条路叠加后实测失真 0.4% 左右。当然,测试时输出端要接纯电阻负载,不能直接接示波器的 1MΩ 输入端,否则波形幅值会虚高。
调试到最后有一个特别值得注意的细节:正弦波和脉冲波共用同一个 MCU 地平面时,脉冲边沿会通过地阻抗耦合到正弦波输出。我的做法是把正弦输出 BNC 的地接到模拟地,脉冲输出 BNC 的地接到数字地,两块地在电源入口用磁珠连接,最终示波器看到的正弦波谐波毛刺下降了 80%。这个物理层面的隔离比任何软件滤波都有效,也是你能在最后半小时拿到加分项的关键。
本文还有配套的精品资源,点击获取