基于AT89S52的程控滤波器设计:增益与截止频率可调的实现
简介基于单片机程控滤波器设计与实现的完整毕业设计论文面向电子信息、自动化等专业本科生及嵌入式初学者讲解如何以AT89S52单片机为核心设计可动态调整参数的程控滤波系统。资源包内共1个文件为带完整排版的docx文档大小1.84MB可直接阅读。文档覆盖滤波器基础理论、模拟与数字滤波器结构、AT89S52单片机性能与存储器、定时器与波特率发生器、可编程时钟输出、DAC0832数模转换器、系统方案论证与理论计算、硬件外围电路搭建以及基于C语言的滤波算法与程序实现。通过该资料可掌握程控滤波器完整设计流程理解数字信号处理与单片机工程实践的结合对课程设计、毕业设计及嵌入式开发有参考价值也能为相关硬件选型与代码编写提供思路。已有67人学习适合需要系统梳理单片机滤波设计知识的读者。1. 为什么固定截止频率的滤波器不够用——程控滤波器的设计动机在信号采集和通信系统里抗混叠滤波和平滑滤波几乎是绕不开的两个环节。A/D转换前需要限带D/A转换后需要平滑这两步都依赖滤波器把带外噪声压下去。但有个很现实的问题当输入信号的频率动态范围很宽比如从几赫兹到几千赫兹固定截止频率的滤波器就捉襟见肘了。截止频率设高了低频段的噪声滤不干净设低了高频段的有用信号又被削掉。程控滤波器的思路就是让截止频率和增益都能通过单片机动态调整一套硬件适配多种频段。本文基于AT89S52单片机、DAC0832和X9312数字电位器拆解一个增益060 dB可调、截止频率120 kHz可调的程控滤波器系统包含硬件选型逻辑、参数计算、C语言驱动和实测验证方法。2. 硬件路线选型从AT89S52到DAC0832/X9312的信号链设计2.1 控制核心为什么选51内核而不是STM32很多人在做类似项目时第一个疑问是为什么不用STM32AT89S52虽然是8位单片机主频最高33 MHzFlash只有8 KB但在这个场景里它确实够用。程控滤波器的核心任务有三个读按键、算参数、写DAC和数字电位器这些操作都是低速控制类任务不需要跑浮点运算也不需要高精度定时器。51内核的I/O口操作是直接位寻址的写控制时序非常方便。另外AT89S52的P1.0P1.7和P3口都支持第二功能比如P1.0可以作T2外部计数输入P3.0/P3.1是UART串口在调试阶段用来打印中间变量很实用。AT89S52的Flash是8 KB对这类程序来说绰绰有余——一个完整的滤波控制固件包含LCD驱动、按键扫描、DAC写入和电位器时序编译出来一般在34 KB以内。如果选STM32反而要处理时钟树配置、复用功能映射这些额外复杂度对课程设计或工程验证来说属于过度设计。真正值得关注的是I/O口的驱动能力P0口是开漏输出驱动DAC0832的数据线时需要接上拉电阻否则高电平拉不上去。这一点在原理图设计时就要考虑进去。2.2 增益控制链路DAC0832与运放怎么搭DAC0832是8位电流输出型DAC跟常见的电压输出型DAC不一样它输出的是电流需要外接运放转成电压。在程控增益电路里DAC0832扮演的是“数字控制的可变参考源”角色而不是直接产生信号。典型接法是把DAC0832的Iout1接到运放反相输入端Iout2接地运放反馈电阻用内部Rfb或者外接电阻输出Vout -Iout × Rfb。这里有个容易忽略的参数DAC0832的数字输入是8位分辨率是1/256。如果直接用DAC输出控制运放增益增益步进会是线性的但人耳或示波器上观察到的幅度变化是dB刻度的线性步进在低增益段变化不明显高增益段又变化太快。所以设计时要做一次映射把期望的dB值反算成DAC码。增益060 dB对应电压放大倍数11000倍用对数关系算好之后把码值存在查表里程序运行时直接查表输出。2.3 截止频率控制X9312数字电位器的接入方式X9312是Intersil现在叫Renesas生产的数字电位器端到端阻值10 kΩ有32个抽头位置通过三个控制引脚CS、U/D和INC来调节。本质上它就是一个可以用脉冲控制的电位器调CS和INC的时序就能让内部计数器加一或减一改变输出端VW和VL之间的阻值比例。在滤波电路里截止频率fc的公式是fc 1 / (2π × R × C)其中R和C共同决定截止频率。X9312的阻值范围是010 kΩ如果直接用它做R配合固定电容截止频率的调节范围只有10倍做不到120 kHz的20倍范围。所以实际设计里要把X9312跟固定电阻串联比如串一个1 kΩ的固定电阻总阻值范围变成111 kΩ再配合多档电容切换才能覆盖整个频段。电容切换可以用模拟开关CD4052来实现单片机通过P2口控制通道选择。2.4 滤波拓扑选择贝塞尔低通与压控高通低通部分选了二阶贝塞尔滤波器高通部分选了压控电压源二阶高通VCVS也叫Sallen-Key结构。为什么低通不用巴特沃斯或切比雪夫因为这个项目的主要场景是信号提取对不同频段信号的“保真度”有要求。贝塞尔滤波器的特点是群延迟在通带内最平坦也就是说不同频率成分经过滤波器后到达输出端的时间差最小波形失真最小。代价是幅频特性的过渡带比巴特沃斯宽但从实际效果看在截止频率附近的衰减斜率足够用。元件参数推导是整个设计里最容易被跳过但最关键的环节。以二阶低通为例Sallen-Key结构下截止频率和Q值的关系为fc 1 / (2π × √(R1 × R2 × C1 × C2))Q √(R1 × R2 × C1 × C2) / (R1 × C1 R2 × C1 R1 × C2 × (1 - K))K是同相放大器的增益贝塞尔滤波器的Q值固定为0.577。设计时先定Q值再反推R和C的取值。让我给一组可以直接抄的参考值R1 R2 10 kΩC1 4.7 nFC2 2.2 nF代入公式得到fc约3.4 kHzQ值约0.57接近贝塞尔特性。X9312串在R1或R2的位置调整阻值时fc随之变化。3. 电路实现从原理图到可调试的硬件3.1 单片机最小系统与LCD接口AT89S52的最小系统包含三部分复位电路、晶振电路和电源去耦。复位电路用10 μF电解电容加10 kΩ电阻接到RST引脚上电时电容充电产生高电平复位脉冲。晶振选择11.0592 MHz比较合适因为这个频率可以精确产生9600波特率的串口时钟虽然本项目不一定用串口但调试时方便。XTAL1和XTAL2之间接晶振各接一个30 pF负载电容到地。LCD用的是OCM2×16字符点阵模块即1602液晶工作模式选4位数据总线只占用P2口的P2.0P2.3加三个控制脚。4位模式比8位模式少用4根I/O口线对引脚紧张的51系统更友好。显示内容分为两行第一行显示当前增益值第二行显示截止频率这样在调节时可以直接看到参数变化不需要额外接示波器。信号线单片机引脚说明RSP2.0寄存器选择0写指令1写数据RWP2.1读写选择接地只写ENP2.2使能信号高电平有效D4P2.3数据线第4位D5P2.4数据线第5位D6P2.5数据线第6位D7P2.6数据线第7位3.2 程控放大电路的具体接法放大电路由三级运放组成前两级用OP07做同相放大第三级用mA741做反相放大。OP07是低失调电压运放输入失调电压典型值只有75 μV适合对微弱信号做前置放大。DAC0832输出的模拟电压接到第三级运放的参考输入端通过改变参考电压来改变整个链路的增益。DAC0832工作在直通模式即CS和WR都接地ILE接高电平数据从P0口直接写入即刻生效。P0口作为数据总线需要外接10 kΩ上拉排阻。注意DAC0832是电流输出Iout1接运放反相输入端Iout2接地Rfb接到输出端VREF接-5 V基准电压。因为AT89S52的P0口是开漏结构写数据时如果没上拉高电平状态不稳定会导致DAC输出毛刺。3.2.1 增益步进映射表增益060 dB步进1 dB一共61个档位。电压放大倍数和dB的换算关系Av(dB) 20 × log10(Vout / Vin)所以每档的电压放大倍数A 10^(dB / 20)。DAC0832的输出电压跟数字码N的关系是Vout -VREF × (N / 256)其中N是0255的数字码。设计时预先算好61个N值存入单片机的Flash运行时根据按键输入直接查表。这里的关键点是DAC的输出电压作为下级运放的参考电压跟增益之间是乘法关系还是加法关系取决于电路结构。如果是把DAC输出电压直接加到反馈网络里增益与DAC码值成反比计算时要取倒数。3.3 低通、高通滤波电路的参数计算低通滤波用X9312数字电位器作为可调电阻配合固定电容实现截止频率的调节。前面说过X9312的32个抽头对应32个阻值档位每档步进约333 Ω。以120 kHz为目标范围电容选择C 10 nF那么截止频率对应的R值为R_min 1 / (2π × 20000 × 10nF) ≈ 796 Ω R_max 1 / (2π × 1000 × 10nF) ≈ 15.9 kΩ所以X9312需要串联一个约1 kΩ的固定电阻并且阻值上限要能到16 kΩ。如果单颗X9312只有10 kΩ不够就串两颗或者降低电容值来抬高频率范围。用C 4.7 nF时R_max约33.9 kΩ完全覆盖所有频率需求。实际焊接时用C 4.7 nFX9312串2.2 kΩ固定电阻32个档位对应R值从2.2 kΩ到12.2 kΩ频率范围覆盖约2.815.4 kHz。高通部分的设计思路相同只是R和C在电路中的位置互换。压控电压源二阶高通滤波器中C1 C2 CR1和R2用X9312调整截止频率公式与低通相同但Q值由放大倍数和电阻比值共同决定。设计时取Q 0.707巴特沃斯响应虽然群延迟特性不如贝塞尔平坦但高通滤波主要用于隔直和去除低频干扰对波形保真度要求不如低通那么苛刻。4. 软件设计增益步进与截止频率程控的实现4.1 按键扫描与状态机的实现软件架构用状态机来处理按键和显示避免主循环里出现阻塞式的延时。三个按键分别控制增益加、增益减和滤波模式切换每个按键经过消抖后触发一次事件。状态机分为空闲态、增益调节态和频率调节态当前状态决定了按键事件的行为。#define KEY_NONE 0 #define KEY_UP 1 #define KEY_DOWN 2 #define KEY_MODE 3 unsigned char current_state STATE_GAIN; unsigned char gain_db 20; // 当前增益单位dB unsigned char freq_level 8; // 当前截止频率档位0~31 void key_scan(void) { unsigned char key read_key(); // 从P3口读取按键状态 if (key KEY_UP) { if (current_state STATE_GAIN) { if (gain_db 60) gain_db; } else if (current_state STATE_FREQ) { if (freq_level 31) freq_level; } } else if (key KEY_DOWN) { if (current_state STATE_GAIN) { if (gain_db 0) gain_db--; } else if (current_state STATE_FREQ) { if (freq_level 0) freq_level--; } } else if (key KEY_MODE) { // 切换状态机同时刷新LCD显示 current_state (current_state STATE_GAIN) ? STATE_FREQ : STATE_GAIN; } }read_key()函数负责读P3口并做软件消抖一般用10 ms延时或者定时器扫描方式实现。状态机的核心价值在于增益调节和频率调节互不干扰每种状态下按键只影响对应参数。代码里的gain_db和freq_level是全局变量在状态机里被修改后主循环会调用dac_update()和pot_update()把新值写入硬件。4.2 DAC0832的查表写入DAC0832在直通模式下只需要把8位数据写到P0口就能改变输出电压。但要注意一个细节P0口是开漏输出写1时引脚是高阻态需要上拉电阻才能拉高到VCC。所以在硬件上接了10 kΩ排阻后软件直接操作P0寄存器即可。#include reg52.h // dB转DAC码查找表共61项每项对应0~60dB的DAC数字量 unsigned char code dB_to_dac[61] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x06, 0x08, 0x0A, 0x0D, 0x10, // ... 中间数据省略 ... 0xF0, 0xF5, 0xFA, 0xFF }; void dac_set_gain(unsigned char db) { unsigned char dac_code; if (db 60) db 60; // 增益限幅 dac_code dB_to_dac[db]; // 查表得到DAC码 P0 dac_code; // 写入P0口DAC0832立即输出对应电流 }查表法的好处是避免了运行时计算对数或浮点运算51单片机做浮点运算会消耗大量CPU周期而查表只需要一条MOVC指令就能从代码区取数据。dB_to_dac表是经过仿真正反校准过的不是简单的线性映射而是考虑到了DAC0832参考电压VREF的误差和运放反馈电阻的精度。如果实际硬件有偏差可以单独校准第一档和最后一档中间档位用线性插值修正。4.3 X9312数字电位器的时序驱动X9312的控制时序比DAC0832复杂一些需要三个信号配合CS片选、U/D方向和INC增量。每次调整步进的操作流程是CS拉低U/D设为目标方向INC产生一个下降沿内部计数器加一或减一然后CS拉高完成一次写入。为了让电位器动作可靠INC的脉冲宽度至少需要1 μs这里用空循环延时即可。sbit POT_CS P1^0; // X9312片选 sbit POT_UD P1^1; // X9312方向控制 sbit POT_INC P1^2; // X9312增量脉冲 void pot_set(unsigned char level) { unsigned char current pot_current_level; // 当前电位器位置 // 目标值大于当前值向上调 while (current level) { POT_CS 0; POT_UD 1; // U/D1向上调节 POT_INC 0; // 产生下降沿 _nop_(); _nop_(); // 延时等待内部处理 POT_INC 1; POT_CS 1; current; } // 目标值小于当前值向下调 while (current level) { POT_CS 0; POT_UD 0; // U/D0向下调节 POT_INC 0; _nop_(); _nop_(); POT_INC 1; POT_CS 1; current--; } pot_current_level current; }这段代码是典型的软件模拟时序没有用定时器中断因为X9312的32个档位最多只需要31个脉冲即使每个脉冲加上延时也只有几十微秒对实时性没有影响。跟硬件SPI不同X9312的时序非常简单甚至可以把它理解成一个“用脉冲数来定位”的步进电机。实际调试中容易踩的坑是INC信号拉低后必须等足够时间再拉高否则内部计数器可能不会触发表现为档位跳变或没反应。4.3.1 档位与频率的换算电位器位置和截止频率之间的关系式fc 1 / (2π × (R_fix (level / 31) × 10kΩ) × C)。为了在LCD上显示准确的频率值而不是档位号程序里预先计算好32个频率值存入查表unsigned int code freq_table[32] { 15400, 14800, 14200, 13700, 13200, 12800, 12400, 12000, 11600, 11300, 10900, 10600, 10300, 10000, 9700, 9400, 9200, 8900, 8700, 8400, 8200, 8000, 7800, 7600, 7400, 7200, 7000, 6800, 6600, 6400, 6300, 6200 };这些数值是按C 4.7 nF算出来的理论值真实频率受元件误差影响会略有偏移但LCD显示用理论值即可测试时手动微调。实际测试中发现X9312内部阻值在全温范围内有±20%的漂移所以这个表只做显示用途精确的截止频率还是要靠信号发生器和示波器来标定。5. 测试方法与一个实用技巧用增益校准改善截止频率精度5.1 放大电路的静态与动态测试测试分两步。静态测试是断电状态测各运放供电电压是否正常重点检查OP07的4脚和7脚电压是否为±5 VmA741的第7脚是否有正电源。动态测试用信号发生器给100 mVpp、1 kHz正弦波示波器接输出端从0 dB开始逐档增加增益观察波形是否削顶、是否有自激振荡。增益加到40 dB以上时输出噪声会明显变大这是正常现象但要注意排除电源纹波干扰。5.2 滤波电路的截止频率测试方法用扫频法固定信号发生器幅度为1 Vpp从低到高改变频率记录输出幅度下降到-3 dB时的频率点这个点就是实测截止频率。把实测值和LCD显示的理论值对比如果偏差超过10%说明电容容值或者电位器阻值有偏差。这时候不要急着换元件用数字万用表实测一下R和C的准确值代入公式重新计算比盲目调参数更有效。5.3 一个实用技巧用端点校准修正数字电位器的非线性X9312虽然是线性电位器标称每档步进333 Ω实际在低端和高端会有较大的非线性误差。一种快速校准方法是把电位器调到最低档用万用表测一下VW和VL之间的电阻R_min调到最高档测R_max中间档位用线性插值修正。修正后的阻值代入频率公式误差可以控制在±3%以内。此外还有两个工程细节值得注意。一是X9312的电源去耦电容要靠近VCC引脚放置用100 nF陶瓷电容否则电位器在切换瞬间会产生毛刺耦合到滤波电路里形成可闻的咔哒声。二是PCB布线时滤波电容和运放输入端的连线要尽量短避免引入额外寄生电容——寄生电容会直接改变截止频率在120 kHz范围内哪怕1 pF的寄生电容都可能造成几十赫兹的偏差。这是个容易被忽略但直接影响测量精度的细节。本文还有配套的精品资源点击获取