尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

CCS环境下DSP FFT实验完整指南:从环境搭建到频谱分析

CCS环境下DSP FFT实验完整指南:从环境搭建到频谱分析 简介本资源是面向DSP嵌入式开发初学者与高校信号处理课程实践者的FFT算法实操项目聚焦于利用TI C5509A DSP芯片在Code Composer StudioCCS环境下完成时域到频域的实时转换。资源包共32个文件涵盖核心C源码main.c、DSP底层头文件c5509.h、csedu.h、TI官方库文件csl5509x.lib、工程配置文件.ccsproject、.cproject、编译链接脚本makefile、sources.mk、调试配置EVM5509A.ccxml、.launch、映射与输出文件.out、.map及说明文档readme.txt完整复现从信号生成、FFT计算到结果分析的全流程开发环境。压缩包仅140KB结构规范、模块清晰便于理解DSP工程组织逻辑与CCS调试机制。已有1153人学习下载读者可直接导入CCS运行验证掌握FFT库调用、窗口函数应用、频谱幅值解析等关键技能并基于现有框架快速拓展如滤波、谐波检测等进阶实验。1. 实验背景与核心目标拆解1.1 为什么每个学DSP的人都绕不开FFT大学阶段学DSP数字信号处理这门课FFT快速傅里叶变换实验基本是标配中的标配。你可能在《信号与系统》里已经接触过傅里叶变换的理论推导但到了DSP课程里事情就不一样了——你需要在一颗真实的DSP芯片上用CCSCode Composer Studio这个集成开发环境把离散傅里叶变换DFT的算法用C语言写出来再让它在硬件上跑起来最后通过CCS的图形工具看到频谱波形。这个实验难吗说实话单纯把FFT的C代码敲出来并不难网上随便一搜就有大量现成版本。真正劝退学生的是另一堆问题CCS怎么装、工程怎么建、CMD文件里那些SECTION伪指令到底在干什么、为什么编译一堆报错、为什么跑起来结果不对、怎么看波形图……这些才是实验二真正磨人的地方。我当年带实验课的时候见过太多学生卡在环境搭建和工程配置上FFT算法本身反而没花多少时间。所以这篇文章我不打算只是贴一份FFT代码了事而是把整个实验从零到一完整走一遍包括CCS环境、工程结构、CMD文件、FFT代码实现、运行验证和排错帮你把这个实验吃透。先明确一下这个实验的目标理解DFT的计算原理和FFT的加速思路掌握在CCS环境下用C语言实现基2时间抽取DITFFT的方法能通过CCS的图形显示工具观察时域波形和频域幅度谱最终在DSP硬件或软件仿真器上跑通整个流程。1.2 前置知识你需要掌握哪些基础在动手敲代码之前有几个前置概念最好先梳理清楚否则后面会遇到很多“看不懂”的瞬间。第一个是DFT的公式。N点序列x(n)的N点DFT定义为X(k) Σ(n0到N-1) x(n)·W_N^(nk)其中W_N e^(-j2π/N)这里的W_N叫做旋转因子是DFT计算的核心。直接按这个公式算N点DFT需要N²次复数乘法和N(N-1)次复数加法当N1024时就是超过一百万次复数乘法在早期的DSP上简直是灾难。FFT的核心价值就是把复杂度从O(N²)降到O(N·log₂N)1024点从约104万次乘法降到约5120次差距是两百多倍。第二个是FFT的基本思路——分治。最经典的基2时间抽取DIT-FFT把N点序列按奇偶位置分成两个N/2点序列分别做DFT再用旋转因子组合起来。这个过程可以递归下去直到变成2点蝶形运算。8点FFT的蝶形运算层级是log₂83级每级有N/24个蝶形。第三个是位反序。因为按奇偶抽取输入序列的顺序被打乱了FFT处理前需要对输入序列做位反序重排。比如N8时原始序号0到7对应的二进制是000、001、010、011、100、101、110、111位反序后变成000、100、010、110、001、101、011、111对应十进制就是0、4、2、6、1、5、3、7。这个细节最容易出错代码里必须处理对。第四个是CCS的基本使用。你要知道怎么创建工程、怎么添加源文件、怎么编译链接、怎么下载程序、怎么用Graph工具看波形。这部分我会在后面的章节详细展开这里先有个概念就行。2. CCS开发环境搭建与工程结构解析2.1 CCS版本选择与安装避坑指南CCSCode Composer Studio是TI德州仪器官方的DSP集成开发环境。目前主流版本是CCS 6.x、CCS 8.x和CCS 12.x不同版本对应不同的DSP芯片平台。选择版本的核心原则是看你手上的开发板或者学校实验室指定的是哪颗DSP芯片然后选择支持该芯片的CCS版本。C2000系列比如TMS320F28335在老版本CCS 3.3和CCS 6以上都支持C5000和C6000系列在CCS 5.x以上支持比较好。如果你用的是比较老的实验箱比如合众达的SEED-DEC2812那大概率要用CCS 3.3因为老版本才自带这些老芯片的驱动支持。安装CCS时有几个坑特别值得注意第一路径不能有中文和空格。很多同学习惯把软件装在“D:\软件\CCS”这样的路径下结果编译时报一堆莫名其妙的错误。正确做法是装在纯英文路径比如“D:\ti\ccs”。第二安装时选择组件要匹配芯片型号。CCS安装向导会让你选择要支持的芯片家族如果你的板子是C2000系列就勾选C2000如果是C6748就勾选C6000。全勾选也可以但安装包会非常大而且启动速度变慢。第三首次启动会让你设置工作空间Workspace这个路径同样不能有中文。工作空间是存放工程文件的地方建议单独建一个目录比如“D:\DSP_Workspace”和CCS安装目录分开。第四CCS闪退问题。这个在热搜词里出现了确实是高频问题。大部分情况是显卡驱动不兼容或者Java运行环境问题。老版本CCS建议右键用管理员身份运行如果还是闪退可以把CCS安装目录下的“ccs_config”文件里的-jvm参数改成-Xmx512m限制内存占用实测有效。2.2 创建工程与文件结构规划打开CCS后新建工程的路径是File - New - CCS Project。在弹出的对话框中需要配置几个关键参数Target选择你的DSP芯片型号比如TMS320F28335Connection选择仿真器类型比如XDS100v2Project name工程名建议用英文比如“FFT_Experiment”Compiler version选择已安装的编译器版本工程创建完成后CCS会自动生成一个基础目录结构。你需要手动添加或修改的文件主要有三类第一类是源文件.c存放你的C语言代码包括主函数、FFT实现、初始化配置等。我习惯把每个功能模块拆成单独的.c和.h文件比如“fft.c/fft.h”放FFT实现“main.c”放主流程这样代码结构清晰也方便后期维护。第二类是链接命令文件.cmd分为两种一种是芯片自带的存储器资源配置文件定义了芯片内部RAM、Flash等存储器的地址范围另一种是用户自定义的段分配文件用SECTION伪指令把代码段、数据段分配到具体的存储空间。大家常说的“CMD文件”通常指后者。第三类是库文件.lib和头文件.hTI官方提供了很多现成的 DSP 库比如C28x系列有C28x_FPU_Lib做FFT可以直接调用官方库函数头文件则声明了寄存器结构和函数原型。刚开始做实验时我建议你用最简单的结构main.c、fft.c、fft.h、一个CMD文件。别急着搞复杂的工程架构先把流程跑通。2.3 CMD文件中的SECTIONS伪指令必须弄明白CMD文件是DSP开发里最容易被忽略但又最重要的配置文件。很多同学编译报错“cannot find section”或者程序跑飞都是CMD文件配置有问题。CMD文件的核心作用有两个一是描述芯片的存储器资源MEMORY指令二是把代码和数据段分配到具体位置SECTIONS指令。以下是一份典型的C2000系列CMD文件片段MEMORY { PAGE 0: RAML0 : origin 0x008000, length 0x001000 PAGE 1: RAML1 : origin 0x009000, length 0x001000 } SECTIONS { .text : RAML0, PAGE 0 .data : RAML1, PAGE 1 .bss : RAML1, PAGE 1 }这里面有几个常见问题第一PAGE 0和PAGE 1的区别。PAGE 0通常放程序只读PAGE 1放数据可读写。你定义的全局变量会放到.bss段如果.bss段分配的空间不够链接时就会报错。第二段名对应的实际内容。.text是编译后的代码段.cinit是C语言全局变量初始化数据段.stack是系统栈段.sysmem是动态内存分配段。每个段都要在SECTIONS里分配地址区域。第三不同芯片的存储空间不完全相同。你在网上抄的CMD文件很可能和你的芯片不匹配最简单的办法是先用CCS自带的标准CMD模板比如28335_RAM_lnk.cmd在Debug模式下先跑通再从RAM模式改成Flash模式。我见过太多人在CMD文件上卡住的案例。最好的学习路径是先直接用官方模板跑通Hello World级别的程序再逐步修改CMD文件观察不同段分配对程序运行的影响。3. FFT算法原理与代码实现详解3.1 从DFT到FFT的数学推导为了后面代码能真正看懂我把FFT的核心推导快速过一遍。DFT的公式是X(k) Σ(n0到N-1) x(n)·W_N^(nk)k 0, 1, ..., N-1其中旋转因子 W_N e^(-j2π/N)。利用旋转因子的对称性 W_N^(kN/2) -W_N^k 和周期性 W_N^(kN) W_N^k可以把N点DFT拆成两个N/2点DFT。具体做法是把x(n)按n的奇偶分成两组偶数序列 x(2r)r 0, 1, ..., N/2-1奇数序列 x(2r1)r 0, 1, ..., N/2-1则N点DFT可以表示为X(k) X_even(k) W_N^k · X_odd(k)k 0, 1, ..., N/2-1 X(kN/2) X_even(k) - W_N^k · X_odd(k)k 0, 1, ..., N/2-1其中X_even(k)是偶数序列的N/2点DFTX_odd(k)是奇数序列的N/2点DFT。这个公式就是蝶形运算的原型。一个蝶形运算包含一次复数乘法、两次复数加/减法。N2^M点时FFT共有Mlog₂N级运算每级有N/2个蝶形所以总计算量约为(N/2)·log₂N次复数乘法。以8点FFT为例具体的运算过程是第一级2点DFT4个蝶形 第二级4点DFT4个蝶形第三级8点DFT4个蝶形每一级的旋转因子都不一样第一级是W_2^0第二级是W_4^0和W_4^1第三级是W_8^0到W_8^3。3.2 位反序的代码实现位反序的实现是FFT代码里最容易出bug的地方。它的原理是对于N2^M把序号n的M位二进制表示逐位倒序得到新的序号。// 位反序函数 void bit_reverse(int n, float* real, float* imag) { int i, j 0; int m 0; int temp; // 计算log2(n) int k n; while (k 1) { k 1; m; } // 位反序重排 for (i 0; i n - 1; i) { if (i j) { // 交换real[i]和real[j] temp real[i]; real[i] real[j]; real[j] temp; // 交换imag[i]和imag[j] temp imag[i]; imag[i] imag[j]; imag[j] temp; } // 计算下一个反序序号 k n 1; while (j k) { j - k; k 1; } j k; } }这段代码是经典算法很多教科书上都有。它的核心思想是j的二进制表示是i的二进制表示的反序通过一个while循环配合优先高位的特点高效生成下一个反序序号。如果你用的是C语言注意两个数组分别交换不要漏掉虚部数组。我见过有同学只交换了实部数组结果频谱完全是错的。3.3 旋转因子的计算与精度处理FFT里旋转因子的计算有两种方式第一种是实时计算每次用库函数sin和cos计算。优点是代码简单缺点是速度慢。在CCS中调用sin/cos需要包含math.h并且要在编译选项里勾选浮点库。第二种是查表法把预先计算好的旋转因子存在数组里。这种方式速度快是实际工程中最常用的方法。N点FFT只需要N/2个旋转因子因为利用对称性可以复用。// 预先计算旋转因子并存表 void init_rotate_factors(int n, float* cos_table, float* sin_table) { int i; for (i 0; i n / 2; i) { cos_table[i] cos(2 * PI * i / n); sin_table[i] -sin(2 * PI * i / n); } }在DSP实现中计算旋转因子时还要注意数据类型。C2000系列DSP有浮点型号和定点型号如果是定点DSP比如F2812你需要用Q格式来表示小数比如Q15格式把浮点数乘以32768后存成整数。浮点DSP比如F28335就方便很多直接定义float数组即可。做实验时如果用的是浮点DSP直接用float数组就好。如果是定点DSP旋转因子和输入数据都要做Q格式转换否则结果会完全错误。3.4 蝶形运算的核心实现蝶形运算是FFT的主体代码逻辑如下// FFT主函数基于基2时间抽取DIT void fft(int n, float* real, float* imag) { int levels 0; int k n; int i, j, size, step; float cos_table[N], sin_table[N]; // 计算级数 log2(n) while (k 1) { k 1; levels; } // 初始化旋转因子表 init_rotate_factors(n, cos_table, sin_table); // 位反序 bit_reverse(n, real, imag); // 多级蝶形运算 size 1; // 当前蝶形运算的宽度N/2 for (i 0; i levels; i) { step size * 2; for (j 0; j n; j step) { int k2; for (k2 0; k2 size; k2) { int idx j k2; int idx2 idx size; // 旋转因子索引 int w_idx k2 * (n / step); float wr cos_table[w_idx]; float wi sin_table[w_idx]; // 蝶形运算 float tr real[idx2] * wr - imag[idx2] * wi; float ti real[idx2] * wi imag[idx2] * wr; real[idx2] real[idx] - tr; imag[idx2] imag[idx] - ti; real[idx] real[idx] tr; imag[idx] imag[idx] ti; } } size step; } }蝶形运算的核心是tmp X2 * W然后 X1 tmp 和 X1 - tmp。注意这里变量的更新顺序必须先算出tmp再更新X2和X1否则会污染原始数据。旋转因子的索引计算是关键。在第i级i从0开始计数蝶形宽度size2^i每组的步长step2×size旋转因子的步长是n/step。这个索引计算方式是FFT代码的精华也是出错率最高的地方务必对照蝶形运算图逐级验证。3.5 输入信号构造如何生成测试波形实验中最常用的测试信号是正弦波叠加。生成方式是在代码里用sin函数构造。#define N 1024 #define PI 3.14159265358979 float input_real[N]; float input_imag[N]; void generate_signal(void) { int i; float fs 8000.0; // 采样率 8kHz float f1 1000.0; // 信号频率 1kHz float f2 2000.0; // 信号频率 2kHz for (i 0; i N; i) { input_real[i] 0.5 * sin(2 * PI * f1 * i / fs) 0.25 * sin(2 * PI * f2 * i / fs); input_imag[i] 0.0; } }这里构造了一个包含1kHz和2kHz两个频率成分的信号。理论上FFT结果中应该在这两个频率对应的频点出现峰值。1kHz对应的频点是N×f1/fs 1024×1000/8000 1282kHz对应的频点是256。实验时可以先计算理论频点再对照CCS图形工具里的实际波峰位置如果一致说明FFT实现正确。4. 基于CCS的完整实验流程实录4.1 配置仿真器与目标板连接在做FFT实验时有两种运行方式硬件仿真和软件仿真。如果你有真实的DSP开发板需要在CCS里配置仿真器连接。在Project重命名上右键 - Properties - Debug - Target确认Connection选中的是你手头的仿真器型号。如果用的是XDS100v2插上USB后设备管理器里应该能看到对应的端口。连接时最容易出的问题是驱动没装好。XDS100v2的驱动在CCS安装目录下自带路径一般在“C:\ti\ccsv8\ccs_base\emulation\windows\xds100”下。如果连接不上先设备管理器里确认驱动是否正常再检查板卡供电。如果学校实验室没有真实硬件也没关系。CCS支持软件仿真模式在Target配置中选择“TMS320F28335 Simulator”或你对应芯片的Simulator不需要连接任何硬件就可以运行程序、查看波形。软件仿真模式对FFT实验来说完全够用因为FFT是纯数值计算不依赖外设。我个人建议如果是初次做这个实验先跑软件仿真把算法和波形验证通了再上硬件平台折腾。这样能减少调试变量的干扰。4.2 编译、链接与常见错误处理在CCS中点击Build按钮或者快捷键CtrlB即可编译工程。编译过程会在Console窗口输出信息如果一切正常会显示“Build Finished”和“0 errors, 0 warnings”。这里列几个最常见的编译错误及解决方案“Cannot find file: ... .cmd”CMD文件没有添加到工程中。右键工程名 - Add Files选中.cmd文件。“#10234-D: unresolved symbols remain”有未解析的函数一般是某个.c文件没添加到工程或者函数名拼写不一致。“error: could not open source file ... .h”头文件路径没配置。在Project Properties - Build - C2000 Compiler - Include Options里添加头文件所在目录。“warning: null character in file”文件格式不是UTF-8无BOM用Notepad或VS Code另存为UTF-8格式即可。编译通过后点击Debug按钮绿色小虫子图标进入调试模式。CCS会自动下载程序到DSP芯片或仿真器并停在main函数入口处。4.3 用Graph工具观察时域波形与频谱FFT实验的最终目的是观察频谱这一点CCS的Graph工具非常关键。操作方法在调试模式下点击Tools - Graph - Single Time或FFT Magnitude。配置对话框里有几个参数需要注意Acquisition Buffer Size要显示的样本点数填N1024Dsp Data Type数据类型选32-bit floating pointStart Address数据起始地址填数组名比如“input_real”Display Data Size显示点数如果选择FFT Magnitude图形它会在CCS内部对数据做FFT省去你自己写FFT的步骤。但实验要求的是你自己写的FFT所以建议先用Single Time看时域波形再用Graph里的FFT功能验证你的FFT结果是否正确。还有一种方法把FFT的运算结果幅度谱也存到一个数组里然后用Single Time模式数组名观察频域波形。这样你能确认是自己的算法结果而不是CCS内置FFT算出来的。4.4 完整实验流程与结果判读下面是我建议的完整操作顺序第一步创建工程添加fft.c、main.c和CMD文件。 第二步编写代码先构造测试信号在generate_signal函数里生成正弦叠加信号。 第三步调用fft函数对采样数据做变换。 第四步计算幅度谱公式是 amp[k] sqrt(real[k]² imag[k]²)/ (N/2)归一化处理。 第五步用Graph工具观察input_real的时域波形再观察amp数组的幅度谱。判断FFT是否正确的标准时域波形应该是平滑的正弦波叠加幅度谱应该在频点128处出现一个约0.5的峰值对应1kHz分量幅度谱应该在频点256处出现一个约0.25的峰值对应2kHz分量其他频点应该接近零但会有少量泄露如果幅度谱峰值位置不对首先检查采样率和频率设置如果频谱对称出现两个峰说明你画了0到N-1的全部频点需要只画0到N/2-1的一半实信号频谱是共轭对称的。这个问题非常常见但不算代码错误。5. 实验中的疑难问题与实用排查技巧5.1 CCS安装与闪退问题解决实录CCS闪退是热搜词里的重点我单独拎出来说。闪退通常发生在启动时或打开Workspace时原因主要有三个第一个是显卡驱动兼容性。CCS是基于Eclipse的Eclipse对显卡驱动的OpenGL渲染比较敏感。解决方法是关闭硬件加速在CCS安装目录下找到ccs.ini文件添加一行-Dorg.eclipse.swt.internal.gtk.disableHardwareAccelerationtrueLinux系统或在Windows下找到ccs.bat添加-Dorg.eclipse.swt.graphics.Device.VSyncfalse。第二个是Java内存不足。修改CCS安装目录下的ccs_config.ini把-Xmx参数调大比如-Xmx1024m。同时确保系统有足够的物理内存。第三个是工作空间路径问题。如果Workspace路径包含中文、空格或处于网络驱动器上CCS可能启动异常。解决办法是新建一个纯英文路径。还有一个容易被忽视的坑杀毒软件会拦截CCS的运行进程。我遇到过学生电脑上360安全卫士把CCS的tomcat进程拦截导致启动失败的情况建议安装CCS时暂时关闭杀毒软件并在恢复后把CCS安装目录加入白名单。5.2 FFT结果不对的6个典型原因结果不对不要慌。FFT结果是“非黑即白”的——对就是对错就是错很少出现“差不多对”的情况。所以排查起来反而目标明确。根据我的经验FFT结果异常基本逃不出以下6个原因第一位反序没做或做错。如果频谱出现大量噪声或看起来完全不对先检查bit_reverse函数。特别是N不是2的幂次方时位反序的逻辑直接崩溃。一定要确保N2^M。第二旋转因子符号弄反。DFT的旋转因子是e^(-j2π/N)如果你用了正号版本得到的是逆变换IDFT的结果频谱会是原始信号的共轭对称版本。第三旋转因子索引错误。前面提到过第i级的旋转因子索引是k2×(n/step)。这个公式写错会导致部分蝶形用错旋转因子结果频谱出现非预期的峰值或变形。第四输入数组被破坏。如果FFT处理后你又用同一个数组操作real和imag可能导致数据错乱。建议定义两个独立的数组存放实部和虚部不要复用。第五溢出或精度问题。定点DSP上如果不做Q格式转换直接算数据会溢出浮点DSP上如果N特别大比如65536点累加误差会被放大。实验时N取256、512、1024完全足够。第六Graph工具配置错误。波形看起来不对可能是Graph参数设错了比如Start Address填错、类型选成16-bit integer而数据是float。先确认Graph配置再怀疑代码。5.3 实验报告需要包含哪些内容这部分是给要做实验报告的同学的参考。在我看来一份合格的FFT实验报告至少应该包含这几个部分第一实验目的和原理。简要说明FFT的基本原理画出8点DIT-FFT的蝶形运算流图对比DFT和FFT的计算量。第二实验环境和实现方案。写明所用DSP芯片型号、CCS版本、仿真器类型说明你的代码结构。第三测试数据和结果分析。把输入信号和FFT结果的截图放进报告标注峰值频点与理论计算值对比。第四问题与讨论。写出你实验中遇到的问题以及解决方法这是老师最看重的部分也是真正能体现你做了实验并思考过的地方。第五源代码附录。把完整的main.c和fft.c代码附上注意格式排版。5.4 扩展与思考FFT还能做什么实验做完之后如果还有余力我建议你思考几个问题FFT在工程中应用极广。FIR滤波器的频响分析可以用FFT快速计算OFDM通信系统里调制和解调就是IFFT和FFT频谱分析仪的核心模块也是FFT语音识别前端的特征提取MFCC同样依赖FFT。你在DSP课上学的这个实验实际上是工业界最常用的信号处理工具之一。你可以尝试的扩展方向有几个第一个是加窗。构造一个包含两个距离很近的正弦波的信号比如1000Hz和1010Hz直接做FFT可能分不清两个峰加上Hamming窗或Hanning窗后就能明显分辨。这个实验能让你直观理解“频谱泄漏”和“频率分辨率”的概念。第二个是自己实现IFFT。把FFT的旋转因子符号反转再在最后除以N就得到逆变换。用IFFT验证正变换的正确性先做FFT再做IFFT看能否还原原始信号。第三个是去直流分量实验。给信号加一个直流偏置你会发现0频点的幅度非常大看频谱时要注意0频点附近的信号被淹没用“信号减去均值”的方法去掉直流后再做FFT频谱特征会更清晰。第四个是用TI官方DSP库的FFT函数对比性能。TI的DSPLib针对C28x做了大量优化同样做1024点FFT官方库比自己写的快几倍到几十倍。对比一下能让你对“优化”有更直观的体会。6. 课程总结与踩坑心得这个实验做完你不仅掌握了FFT的原理和实现更关键的是完整走了一遍DSP开发流程从环境搭建、工程创建、代码编写、编译调试到结果验证。这条流程以后做任何DSP项目都绕不开实验二相当于提前把路趟平了。最后分享几个我这几年带实验积累的小习惯。第一代码里多注释。FFT这种算法密集型的代码一个月后回来看如果不注释真的看不懂自己当时怎么写的。特别是旋转因子索引那种关键行一定写清楚。第二用版本管理工具。哪怕只是课设级别的代码也建议用Git管理。你改坏了还可以回滚CCS工程文件多版本管理能省不少事。第三调试时用小块数据。先拿N8或N16验证FFT的正确性手算一个简单的序列比如全1序列的FFT确认无误后再上N1024。全1序列的DFT结果是有理论值的——只有在k0处有N其他都为0——非常适合验证。第四不要只抄代码。网上能找到很多FFT的C实现但如果只是复制粘贴遇到问题完全不知道在哪里改。哪怕是自己照着算法思想重写一遍收获也比抄十遍大。做实验的过程本质上就是不断出错、不断修正、最后跑通的过程。你在这个过程里踩过的每一个坑都是最值钱的学习材料。FFT实验做完你已经算半个DSP开发者了。本文还有配套的精品资源点击获取
返回列表