VHDL+DSP语音滤波项目实战:从FIR滤波器设计到CCS调试
2026/8/31 1:28:59 网站建设 项目流程

简介:本资源是一套基于VHDL与TI CCS平台实现的语音数字滤波器完整工程包,面向数字信号处理初学者、FPGA/DSP课程设计学生及嵌入式语音算法实践者,解决语音信号去噪、频带选择与实时滤波硬件化落地的核心问题。压缩包共25个文件(326KB),涵盖7幅关键波形图(如原始/滤波后时域与频域对比)、2个汇编源文件(fir.asm/fir vectors.asm)与链接命令(fir.cmd)、2个数据文件(indata.dat等)、MATLAB仿真脚本(.m)、参数配置文本(.txt)、CCS工程文件(.pjt/.paf)及调试日志(.log/.lkf/.lkv),完整呈现从算法设计、VHDL建模、CCS编译仿真到硬件部署的全流程。已有237人学习下载,资源结构清晰,图文与代码对应紧密,特别适合理解FIR滤波器在DSP上的VHDL实现机制、掌握CCS+VHDL协同开发范式,并快速复现带通/低通等语音滤波效果。 前阵子从同事那儿拿了一个CCS.rar的压缩包,文件名后缀一串VOICE filterVHDLDSP语音滤波的关键词。这种事情在高校实验室和刚入行的工程师群体里太常见了——解压之后资料是有点乱的,有的版本带源码,有的版本只有工程目录和一堆配置文件,能顺利编译起来、跑通仿真的,已经算运气不错。这篇文章就从这个压缩包入手,聊聊一个VHDL写的语音滤波项目到底该怎么拆、怎么改、怎么在CCS里跑起来。

语音滤波本身不是什么新东西,但把VHDL、DSP、CCS这几个词凑在一起,其实意味着两条技术路线在同一套代码里交汇:一条是FPGA方向,用硬件描述语言实现数字滤波器;另一条是DSP处理器方向,用C代码在CCS环境下完成算法验证和实时运行。搞清楚这个包的结构、滤波器参数怎么定、代码怎么写、工程怎么配,四个问题解决了,这个项目基本就吃透了。

1. 项目概述:CCS.rar里到底装了什么

1.1 一个典型的语音滤波项目包长什么样

第一次拿到这种压缩包,不要急着双击什么文件,先看一下目录结构。带VOICE filter字样的VHDL+DSP语音滤波项目,包里通常包含这几类东西:源码目录、CCS工程配置文件、仿真脚本,以及一堆dat或txt格式的采样数据文件。以我经手过的类似项目为例子,你会看到类似这样的目录组织:

CCS.rar/ ├── voice_filter/ │ ├── .project │ ├── .cproject │ ├── src/ │ │ ├── fir_filter.vhd │ │ ├── voice_filter.vhd │ │ └── top_level.vhd │ ├── sim/ │ │ ├── tb_voice_filter.vhd │ │ └── run_sim.do │ ├── data/ │ │ ├── input_voice.dat │ │ └── output_voice.dat │ └── cmd/ │ └── 28335_RAM_lnk.cmd

VHDL文件和.cmd链接器命令文件放在一起,看起来有点混搭,其实说明这个项目走了两条验证路线:一条是把VHDL代码放到FPGA上做硬件验证,另一条是把同样的滤波算法移植到DSP上用CCS跑。两条路的边界在哪里,取决于你的毕设或者课设要求。搞清楚这个包的物理结构,后面的工作才有线索可循。

.cproject.project是CCS的Eclipse工程描述文件,用CCS导入时必须有这两个文件。src目录里的VHDL文件是核心,fir_filter.vhd一般是滤波器实体,voice_filter.vhd是顶层封装,top_level.vhd负责引脚和时钟管理。sim目录下的testbench和.do脚本是ModelSim仿真用的,data目录里的.dat文件是给testbench读的输入语音数据,也是仿真输出的落盘位置。cmd目录里那个28335_RAM_lnk.cmd一看就是给TMS320F28335用的链接脚本,这些都是老熟人了。

1.2 这个项目能解决什么问题

从文件名VOICE filter和关键词语音滤波可以判断,这个项目的核心功能就是对语音信号做数字滤波处理。语音信号里常见的不干净成分包括:直流偏置、50Hz工频干扰、高频白噪声、以及采样过程中引入的宽带噪声。传统的模拟RC滤波电路也能起到一定作用,但动态范围和一致性都有限,尤其在多级滤波或者可重构滤波的场景下,模拟方案做起来非常痛苦。

数字滤波器就完全不一样了。只要把模拟语音信号以足够高的采样率(比如8kHz到48kHz)量化成数字序列,就可以用一组乘法累加运算实现高通、低通、带通甚至带阻滤波。这个项目用的是VHDL描述FIR滤波器,FIR最大的优势是线性相位,也就是说语音信号通过滤波器后,各个频率分量的相对延迟一致,波形不会发生相位失真,这对于语音信号的可懂度来说至关重要。

换句话说,这个项目本质上是一个用硬件描述语言实现的数字信号处理IP核,同时配套了DSP工程,便于在真实芯片上验证。适合的受众很明确:正在做EDA课程设计、FPGA课设、数字信号处理实验的在校生,以及刚接触DSP开发、想从例程里快速上手的初级工程师。看懂这个包,VHDL模块化设计、FIR原理、CCS工程配置这几个技能点基本都能串起来了。

2. 语音滤波的核心原理与方案选型

2.1 语音信号的频谱特性和滤波目标

在动手改代码之前,必须先搞清楚语音信号的频谱长什么样。电话语音的带宽一般被限制在300Hz到3400Hz,这是从电话通信时代就定下来的标准,因为人声的可懂度主要集中在这个频段。低于300Hz的部分多为低频气流噪声和直流偏置,高于3400Hz的部分大部分是齿音和摩擦音的细微成分,而实际宽带语音信号经过麦克风采集后,在20Hz以下和20kHz以上往往还有大量的背景噪声。

所以做语音滤波一般有三个方向。第一种是带通滤波,只保留300到3400Hz的频段,砍掉低频和高频噪声;第二种是高通滤波,只去掉直流偏置和工频干扰,保留全频带语音;第三种是更高级的自适应降噪,这个在纯VHDL里实现复杂度非常高,一般不会出现在课程项目里。就这个CCS.rar项目而言,八成是第一种或者第二种,打开代码看滤波器系数就能确认。

滤波器的设计参数主要看采样率、截止频率、阻带衰减和通带纹波。举个例子,如果采样率是8kHz,要设计一个300Hz到3400Hz的带通滤波器,用窗函数法设计FIR,那么滤波器阶数N大致可以用经验公式估算:N ≈ 3.3 × fs / Δf,这里的Δf是过渡带带宽(单位Hz),系数3.3对应汉明窗。如果过渡带宽设为200Hz,那么N ≈ 3.3 × 8000 / 200 ≈ 132,取整到128或者136都行,通常选一个以8为单位对齐的数字。这个数值直接决定了VHDL里移位寄存器的长度和乘法器数量,是写代码前必须先算清楚的东西。

2.2 为什么选FIR而不用IIR,为什么用VHDL

选FIR还是IIR做滤波?很多初学者会纠结。直接说结论:在VHDL里实现语音滤波,几乎所有项目都选FIR。

第一,FIR是有限冲激响应,结构上是纯粹的横向抽头延迟线,每一级只做乘累加,不存在反馈环路。没有反馈就意味着一不来不会存在稳定性问题,二来在硬件上不需要处理递归运算带来的时序收敛麻烦。IIR虽然阶数低、计算量小,但它有反馈回路,在VHDL里实现时要格外小心溢出和时序,而且量化误差可能造成极限环振荡,这在课程设计阶段非常劝退。

第二,FIR能做到严格的线性相位。语音信号对相位敏感度其实不算特别高,但在做科研演示或者后续实验时,线性相位能让滤波前后的信号时域对比更容易看出效果,不会出现波形包络畸变。VHDL实现FIR最简单的结构是转置结构,它把乘累加器以流水线的方式组织,寄存器链和乘法器链并行执行,时钟频率可以跑到很高,即使不用PLL倍频也能满足8kHz到48kHz的音频采样率要求。

第三,用VHDL的原因,往往是因为这个项目是EDA课程、FPGA课程或者硬件设计的作业,要求把滤波算法用硬件描述语言落地。也有一定可能,这个项目是从GitHub上移植的RTL代码,配合TI的DSP工程一起交上去,硬件上先用FPGA验证算法,再在DSP板上跑C代码做对照实验。这种情况下VHDL代码就是算法的参考实现,DSP工程则是实际验证平台,两边必须用相同的量化系数和阶数,才能保证结果一致。

3. VHDL FIR滤波器设计与实现

3.1 滤波器系数设计与量化

既然确定了用FIR,第一步就是生成滤波器系数。系数一般用MATLAB的fdatool或者Python的scipy库计算,算完之后再做定点量化。纯粹的浮点系数在FPGA里没法直接用,必须在芯片里用DSP48硬核或者常规乘法器处理定点数。

假定采样率fs = 8000Hz,通带300到3400Hz,用firpm或者kaiserord函数算出一组128阶的系数,也就是129个系数。在MATLAB里可以用一行代码生成:

fir_coeff = fir2(128, [0 300/4000 300/4000 3400/4000 3400/4000 1], ... [0 0 1 1 0 0], boxcar(129));

注意这里的频率轴归一化到奈奎斯特频率4000Hz。计算完系数后,需要决定Q格式。对于语音滤波项目,最常用的是Q15格式,也就是整数部分1位、小数部分15位。因为16位的语音采样数据已经是标准格式了,MAC运算时用Q15系数就能避免大量符号扩展的问题。

量化的代码如下:

coeff_q15 = round(fir_coeff * 2^15);

如果系数的绝对值超过1,需要先做归一化,把最大绝对值缩放到0.9999以下再乘以2^15。量化以后一定要观察一下频响曲线是否还满足指标,尤其是通带纹波。用Q15量化128阶FIR,阻带衰减大概能做到60dB左右,通带纹波会稍微变差,但语音滤波完全够用。量化后的系数先存成一个txt文件,后续写VHDL时直接批量生成常量数组。

3.2 关键VHDL代码结构与MAC单元

下面看VHDL代码的正题。一个FIR滤波器的VHDL实现可以拆成三块:输入寄存器链(延迟线)、乘累加阵列、控制逻辑。以转置结构为例,每一级都做一个乘累计,然后逐级向后传递。这是教科书上最常用的方式,时序因为流水线的存在比较好收敛。

来看一段核心代码(示意):

library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; entity fir_filter is generic ( TAPS : integer := 128 ); port ( clk : in std_logic; rst_n : in std_logic; input_data : in signed(15 downto 0); output_data: out signed(15 downto 0) ); end fir_filter; architecture rtl of fir_filter is type coeff_array is array (0 to TAPS-1) of signed(15 downto 0); constant COEFFS : coeff_array := ( to_signed(12, 16), to_signed(-35, 16), to_signed(76, 16), ... ); type delay_array is array (0 to TAPS-1) of signed(15 downto 0); signal delay_line : delay_array; begin process(clk, rst_n) variable acc : signed(31 downto 0); begin if rst_n = '0' then delay_line <= (others => (others => '0')); output_data <= (others => '0'); elsif rising_edge(clk) then delay_line(0) <= input_data; for i in 1 to TAPS-1 loop delay_line(i) <= delay_line(i-1); end loop; acc := (others => '0'); for i in 0 to TAPS-1 loop acc := acc + delay_line(i) * COEFFS(i); end loop; output_data <= acc(30 downto 15); end if; end process; end rtl;

上面的代码里有几个关键点需要注意。第一个是COEFFS常量数组,由第3.1节生成的Q15系数直接粘贴进来,to_signed的参数必须用十进制的整数。第二个是delay_line用了一个二维数组来表示所有延迟寄存器,索引i就是第i个抽头。第三个是累加器acc用了32位宽,这个宽度是仔细算过的:16位的输入与16位的系数相乘得到32位结果,128阶累加理论上需要增加7位才能严格保证不溢出,所以最稳妥是39位。但实际语音信号的能量比较低,动态范围有限,32位已经够用,更重要的是可以省DSP48资源,让设计在低端FPGA上也能跑得动。

实际调试的时候,最坑的地方往往不在代码逻辑,而在符号位的处理。VHDL里signed类型做乘法时,如果操作数长度不一样,需要先明确扩展位数。写成a * b时,两个操作数都是signed(15 downto 0),在VHDL-2008里会自动扩展;如果用的老编译器标准,可能直接报长度不匹配,这个时候用resize函数显式扩展就行了。另外,累加器的截位方式也很关键,acc(30 downto 15)是直接截断,带符号数直接截断会产生约半个LSB的直流偏移,要求高的话可以改成四舍五入:acc(30 downto 15) + (acc(14) ? 1 : 0)

3.3 仿真testbench与数据验证

写完RTL代码,下一步是仿真。仿真不能只看波形形状,要把输入语音数据和输出数据导出成文件,和MATLAB里的理想输出做数值对比。这一步是判断RTL实现是否正确的金标准。

testbench的基本结构很简单:例化fir_filter模块,驱动时钟和复位,然后读取输入数据文件,喂给滤波器,把输出写入另一个文件。时钟频率不用太高,语音滤波采样率才8kHz,testbench时钟给10MHz就足够。核心代码如下:

-- testbench 简要结构 entity tb_voice_filter is end tb_voice_filter; architecture sim of tb_voice_filter is signal clk : std_logic := '0'; signal rst_n : std_logic := '0'; signal input : signed(15 downto 0) := (others => '0'); signal output : signed(15 downto 0); begin clk <= not clk after 50 ns; -- 读入文件、驱动输入、捕获输出的过程略 end sim;

看起来简单,但实际验证时有个非常重要的技巧:不要直接拿滤波器的16位输出和MATLAB对比,而是先用浮点模型算出一份参考输出,再对参考输出做Q15量化,允许±1的误差范围。因为RTL和MATLAB的舍入方式可能不同,直接逐位相等会把人搞疯,最后发现只是四舍五入和截断的差别。

如果对照MATLAB输出时发现误差大于几个LSB,那多半是系数粘贴错位或者累加器截位方式不对。有一个经验技巧:不要直接对比最终的16位输出,而是对比全精度累加值在截位之前的数值。这样能快速定位差异是来自算术运算还是来自舍入。仿真数据验证这块,建议用Python写个小脚本,读取两个文件逐点计算误差,超过阈值的点标出来,比用肉眼看波形图高效多了。

4. CCS工程搭建与调试全流程

4.1 CCS版本选择、安装以及工程加载

这一章进入DSP实际开发的部分。CCS,全称Code Composer Studio,是TI家的集成开发环境。做语音滤波项目,一般用的器件是C2000系列(比如TMS320F28335)或者C5000系列(比如TMS320C5505)。F28335在高校里用得非常广,因为它的例程多、外设全、买开发板送教程。C5505则更贴近音频应用,有内置的ADC和McBSP,适合接语音数据。

平台开发环境适用场景学习成本
TMS320F28335CCS(C2000编译器)电机控制、电力电子、实时信号处理中等
TMS320C5505CCS(C55编译器)音频处理、语音识别前端偏低
ADSP-21489VisualDSP++专业音频、车载DSP较高
STM32F4Keil + CMSIS-DSP库嵌入式通用,非DSP芯片

关于CCS安装,不同版本差异很大。老一点的CCS 6.x/7.x只有32位支持,装起来倒是小,但后来在新电脑上经常出现兼容性问题。现在TI主推CCS 12.x,基于Eclipse,安装包有十几个G,安装时建议直接默认全选,省得后续缺组件。安装过程中有几个细节值得留意:第一,安装路径不能带中文和空格,不然debug时可能找不到目标配置文件;第二,首次启动时Workspace目录也尽量用英文路径;第三,杀毒软件最好先关掉,否则Eclipse的插件生成过程会被误拦,导致启动卡在进度条。

安装完以后,打开CCS,最常用的加载工程方法是Project菜单下的Import CCS Projects。选中压缩包里解压出来的工程目录,CCS会自动识别.project文件,然后把它导入工作区。这里有个常见问题:如果从网上下载的工程是用CCS 6建的,而你用的是CCS 10,往往需要右键工程名,选择Properties,把编译器版本改成当前版本的GNU或者TI编译器,再重新编译一遍。看到错误列表时,先看是不是编译器版本不匹配再说。

4.2 .cmd文件与SECTIONS伪指令解析

CCS工程里最让新手摸不着头脑的就是.cmd文件。cmd是linker command file,它的作用是把程序段(section)分配到DSP的具体内存地址。语音滤波项目的.cmd文件一般把可执行代码放在RAM里,把reset向量放在flash的入口地址,最常看见的关键字就是SECTION和MEMORY。

MEMORY指令定义整个芯片上可用的内存块,比如:

MEMORY { PAGE 0 : RAML0 : origin = 0x008000, length = 0x001000 PAGE 1 : RAML1 : origin = 0x009000, length = 0x001000 }

SECTIONS指令则把可执行文件和变量映射到这些块里。原理可以这么理解:整个DSP存储空间是一套房子,MEMORY负责画隔断,SECTIONS负责安排家具放哪个隔间。如果访问到未分配的内存,程序直接跑飞,调试器只能看到PC指针跳到0x3FFFFF这种怪异地址。

在语音滤波工程中,SECTIONS通常包含这样几段:

SECTIONS { .text : >> RAML0 | RAML1, PAGE = 0 .cinit : > RAML0, PAGE = 0 .stack : > RAML1, PAGE = 1 .data : > RAML1, PAGE = 1 .const : > RAML1, PAGE = 1 }

注意.cinit段存放的是C语言全局变量的初始化值,.text是可执行代码,.stack是函数调用栈,.const存放const常量。语音滤波器如果用C实现,采样的数组如果定义成const,会被放进.const段,要确保.cmd文件里为.const分配了空间。我在实际项目里犯过的错是:滤波系数数组太大(128个Q15系数,总共256字节),但RAML1分配的空间不够,链接时直接报错"placement fails for object"。这种问题的排查方法很简单,看build log里的section allocation summary,哪一段空间不足一目了然。

4.3 在CCS中完成DSP端语音滤波C代码移植

VHDL描述的是FPGA里的硬件逻辑,但要在DSP芯片上跑,还需要一份C代码版本。这本质上就是同一个算法的两种实现。C代码会比VHDL直观得多,核心是一个for循环:

#define TAPS 128 #pragma DATA_ALIGN(coeff, 4) const int16_t coeff[TAPS] = { ... }; int16_t fir_process(int16_t input) { static int16_t hist[TAPS] = { 0 }; int32_t acc = 0; int i; for (i = TAPS - 1; i > 0; i--) { hist[i] = hist[i - 1]; } hist[0] = input; for (i = 0; i < TAPS; i++) { acc += (int32_t)hist[i] * coeff[i]; } return (int16_t)(acc >> 15); }

这份代码可以直接放在CCS里编译,挂上.cmd文件后下载到DSP板子上跑。在CCS里配置定时器时,通常把定时器周期设置成采样周期的倒数。比如采样率8kHz,采样周期是125微秒,如果主频150MHz,定时器分频后需要精确计算周期寄存器的值。C2000系列用CPU Timer0比较方便,配置好定时器中断后,在中断服务函数里调用fir_process,读取ADC采样值并把滤波结果送出。

调试时最常见的错误想法是打印hist数组和acc的值,但千万不要直接调用printf函数来打印,CCS的printf实现非常慢,而且挂仿真器的时候printf会阻塞程序运行,导致滤波结果完全不对。我的习惯做法是把中间结果写到一个全局数组里,程序跑完后再在Debug视图的Expressions窗口里查看数组内容。如果要在串口上输出数据,就配置一个SCI外设,用轮询的方式发送数据帧,每帧带上帧头,PC端再用串口助手解析成波形。

5. 常见问题与排查技巧实录

5.1 CCS安装、启动和工程导入的坑

这部分是真实项目里最容易浪费时间的环节,列几个我亲测过的现象和解决方法。

现象一:CCS安装完成后启动闪退。

先检查JDK版本和CCS的匹配情况。CCS 12要求JDK 17或者更高,系统装了多个JDK时,Eclipse启动可能选择了不兼容版本。解决办法:设置系统环境变量JAVA_HOME指向CCS自带的JDK路径,通常是CCS安装目录下的eclipse/jre。另外,Workspace路径不要指向网络驱动器或者同步盘目录,CCS对这类路径处理不好,会产生大量锁文件然后闪退。

现象二:CCS启动后状态栏一直显示Loading,或者双击目标配置文件(Target Configuration)时卡死。

目标配置文件后缀是.ccxml,用来描述仿真器和芯片型号。如果双击.ccxml卡死,先确认XDS100仿真器的驱动装好没有。Win10/11系统有时会自动安装一个错误的驱动,设备管理器里看到的是感叹号,需要手动更新驱动路径到CCS安装目录下的ccs_base/emulation/drivers目录。还有一种情况是工程自带的.ccxml文件版本太旧,可以在CCS里重新New一个Target Configuration,选择对应的芯片和仿真器,然后重新生成。状态栏一直在打转的问题,多半是杀毒软件在后台扫描Eclipse的插件目录,把整个CCS安装目录加入白名单基本能解决。

现象三:CCS导入工程后编译一堆错误,而且错误行号指向的代码看着没问题。

这种场景十有八九是编译器版本不匹配。老的CCS工程用的是C2000编译器V16.9之类的版本,而新装的CCS自带的编译器版本更新,API或者宏定义有细微差异。在工程属性里把编译器版本改成可用的旧版本,或者干脆把工程先Build一遍,让CCS自动生成新的编译配置,再逐个修正提示的warning。凡是看到unresolved symbol错误,优先检查工程的链接库路径,尤其是用了DSP库或者数学库(比如rts2800_fpu32.lib)的工程。

现象四:使用CCS Debug下载程序时提示Error connecting to the target。

如果仿真器连接没有问题,芯片电源正常,那重点检查.cmd文件里是否把程序下载地址写进了受到保护的区域。比如F28335的OTP区域(0x780000)是只读的,如果.text段被错误映射到这里,仿真器就无法写入。还有一个容易被忽视的坑:目标板上有多个DSP或者JTAG菊花链连接时,CCS默认扫描的是链上第一个芯片,实际要调试的却是第二个,此时需要在.ccxml里修改扫描链配置。

现象常见原因解决思路
启动闪退JDK版本不匹配设置JAVA_HOME指向CCS自带JDK
状态栏卡死杀毒软件扫描插件目录将CCS安装目录加入白名单
打不开.ccxml仿真器驱动不对手动更新驱动到ccs_base目录
编译报一大堆错编译器版本不匹配工程属性里切换编译器版本
Debug连接失败cmd内存映射进OTP区域去掉只读区域映射

5.2 VHDL仿真和DSP联调中的典型问题

问题:VHDL仿真时输出全是0,波形完全没变化。

先查复位逻辑。语音滤波器的VHDL代码一般有时钟和复位端口,如果复位是高有效或者低有效搞反,整个寄存器链都处于复位状态,输入数据根本没进到移位寄存器里。其次查输入数据位宽匹配:testbench里读的数据文件如果头几行是字符,比如"%"注释,ModelSim读取时不会自动跳过,会把字符串转换成了0x0000,导致前N个采样全是0。解决方法是在testbench里添加一个skip文件头的循环,或者把采样数据文件提前处理好。

问题:C代码在DSP上跑出来的滤波结果和MATLAB仿真对不上,时域波形低频部分偏差很大。

这个现象大多数是系数量化精度不够,但也可能是DSP端的乘累加溢出。Q15系数和16位输入相乘的结果是31位,累加128个这样的乘积需要足够宽的累加器。如果acc只声明了32位,在极端输入下就会溢出。如果你用的是C2000系列DSP,可以开启饱和标志让溢出自动饱和,也可以把acc类型换成64位,代价是速度变慢。实际项目中我觉得把累加器改成64位更省心,毕竟滤波器是实时的,一个采样只有125微秒的处理时间,C28x在150MHz主频下完全有余量。

问题:波形有真实滤波效果,但高频部分出现周期性噪声。

这基本是采样数据本身的问题。如果你直接用MATLAB生成一个带噪语音文件,再导入DSP做滤波,注意文件里的数据格式。如果原始音频是16位PCM,但你在C代码里用了int8_t来读取,那么每两个采样被拼成了一个,输出频率就完全乱了。正确的做法是先在PC端把音频文件转成纯ASCII格式的十六进制或者十进制文本,每行一个采样,然后DSP端用fscanf按行读取,或者在testbench里直接按行读取。这种格式转换类工具,用MATLAB的fprintf一行代码就能搞定。

5.3 快速定位问题的三个实用技巧

第一个技巧,做对比测试时不要用一整段语音,先构造一个单频正弦波作为输入。如果滤波器的中心频率在1kHz,你就给一个1kHz正弦和一个5kHz正弦的叠加信号,滤波后看5kHz分量是否明显衰减。用单频信号能迅速判断滤波器的频率响应是不是设计的目标,同时也能暴露系数错位、截止频率偏移之类的问题。

第二个技巧,学会看CCS的Graph功能。在CCS的Debug视图里

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询