简介:基于单片机程控滤波器设计与实现的完整毕业设计论文,面向电子信息、自动化等专业本科生及嵌入式初学者,讲解如何以AT89S52单片机为核心设计可动态调整参数的程控滤波系统。资源包内共1个文件,为带完整排版的docx文档,大小1.84MB,可直接阅读。文档覆盖滤波器基础理论、模拟与数字滤波器结构、AT89S52单片机性能与存储器、定时器与波特率发生器、可编程时钟输出、DAC0832数模转换器、系统方案论证与理论计算、硬件外围电路搭建,以及基于C语言的滤波算法与程序实现。通过该资料可掌握程控滤波器完整设计流程,理解数字信号处理与单片机工程实践的结合,对课程设计、毕业设计及嵌入式开发有参考价值,也能为相关硬件选型与代码编写提供思路。已有67人学习,适合需要系统梳理单片机滤波设计知识的读者。
1. 为什么固定截止频率的滤波器不够用——程控滤波器的设计动机
在信号采集和通信系统里,抗混叠滤波和平滑滤波几乎是绕不开的两个环节。A/D转换前需要限带,D/A转换后需要平滑,这两步都依赖滤波器把带外噪声压下去。但有个很现实的问题:当输入信号的频率动态范围很宽,比如从几赫兹到几千赫兹,固定截止频率的滤波器就捉襟见肘了。截止频率设高了,低频段的噪声滤不干净;设低了,高频段的有用信号又被削掉。程控滤波器的思路就是让截止频率和增益都能通过单片机动态调整,一套硬件适配多种频段。本文基于AT89S52单片机、DAC0832和X9312数字电位器,拆解一个增益0~60 dB可调、截止频率1~20 kHz可调的程控滤波器系统,包含硬件选型逻辑、参数计算、C语言驱动和实测验证方法。
2. 硬件路线选型:从AT89S52到DAC0832/X9312的信号链设计
2.1 控制核心为什么选51内核而不是STM32
很多人在做类似项目时第一个疑问是:为什么不用STM32?AT89S52虽然是8位单片机,主频最高33 MHz,Flash只有8 KB,但在这个场景里它确实够用。程控滤波器的核心任务有三个:读按键、算参数、写DAC和数字电位器,这些操作都是低速控制类任务,不需要跑浮点运算,也不需要高精度定时器。51内核的I/O口操作是直接位寻址的,写控制时序非常方便。另外,AT89S52的P1.0~P1.7和P3口都支持第二功能,比如P1.0可以作T2外部计数输入,P3.0/P3.1是UART串口,在调试阶段用来打印中间变量很实用。
AT89S52的Flash是8 KB,对这类程序来说绰绰有余——一个完整的滤波控制固件,包含LCD驱动、按键扫描、DAC写入和电位器时序,编译出来一般在3~4 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码。增益0~60 dB对应电压放大倍数1~1000倍,用对数关系算好之后,把码值存在查表里,程序运行时直接查表输出。
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的阻值范围是0~10 kΩ,如果直接用它做R,配合固定电容,截止频率的调节范围只有10倍,做不到1~20 kHz的20倍范围。所以实际设计里要把X9312跟固定电阻串联,比如串一个1 kΩ的固定电阻,总阻值范围变成1~11 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 nF,C2 = 2.2 nF,代入公式得到fc约3.4 kHz,Q值约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.0~P2.3加三个控制脚。4位模式比8位模式少用4根I/O口线,对引脚紧张的51系统更友好。显示内容分为两行,第一行显示当前增益值,第二行显示截止频率,这样在调节时可以直接看到参数变化,不需要额外接示波器。
| 信号线 | 单片机引脚 | 说明 |
|---|---|---|
| RS | P2.0 | 寄存器选择,0写指令1写数据 |
| RW | P2.1 | 读写选择,接地只写 |
| EN | P2.2 | 使能信号,高电平有效 |
| D4 | P2.3 | 数据线第4位 |
| D5 | P2.4 | 数据线第5位 |
| D6 | P2.5 | 数据线第6位 |
| D7 | P2.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 增益步进映射表
增益0~60 dB,步进1 dB,一共61个档位。电压放大倍数和dB的换算关系:
Av(dB) = 20 × log10(Vout / Vin)
所以每档的电压放大倍数A = 10^(dB / 20)。DAC0832的输出电压跟数字码N的关系是:
Vout = -VREF × (N / 256)
其中N是0~255的数字码。设计时预先算好61个N值存入单片机的Flash,运行时根据按键输入直接查表。这里的关键点是:DAC的输出电压作为下级运放的参考电压,跟增益之间是乘法关系还是加法关系,取决于电路结构。如果是把DAC输出电压直接加到反馈网络里,增益与DAC码值成反比,计算时要取倒数。
3.3 低通、高通滤波电路的参数计算
低通滤波用X9312数字电位器作为可调电阻,配合固定电容实现截止频率的调节。前面说过,X9312的32个抽头对应32个阻值档位,每档步进约333 Ω。以1~20 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 nF,X9312串2.2 kΩ固定电阻,32个档位对应R值从2.2 kΩ到12.2 kΩ,频率范围覆盖约2.8~15.4 kHz。
高通部分的设计思路相同,只是R和C在电路中的位置互换。压控电压源二阶高通滤波器中,C1 = C2 = C,R1和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/D=1,向上调节 POT_INC = 0; // 产生下降沿 _nop_(); _nop_(); // 延时等待内部处理 POT_INC = 1; POT_CS = 1; current++; } // 目标值小于当前值,向下调 while (current > level) { POT_CS = 0; POT_UD = 0; // U/D=0,向下调节 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 V,mA741的第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布线时,滤波电容和运放输入端的连线要尽量短,避免引入额外寄生电容——寄生电容会直接改变截止频率,在1~20 kHz范围内,哪怕1 pF的寄生电容都可能造成几十赫兹的偏差。这是个容易被忽略但直接影响测量精度的细节。
本文还有配套的精品资源,点击获取