简介:《高通audio入门.docx》是一份面向高通平台音频驱动初学者与Bringup工程师的系统性入门文档,聚焦手机与嵌入式音频软硬件架构的理解及调试准备。内容从硬件框架、音频硬件接口与codec入手,延伸到软件架构和代码位置,并完整梳理Audio Bringup流程,涵盖PIL镜像加载、SLIMbus UP、声卡注册、codec上电时序与复位、软硬件匹配检查、GPIO配置及audio通路配置等关键环节,便于读者建立从硬件接口到kernel层驱动的整体认知。压缩包仅含1个docx文档,大小约2.33MB,轻量易查阅,适合作为随查随用的框架梳理资料。目前已有1193人学习,说明其在音频入门与bringup排错场景中具有一定参考价值。读者可据此快速定位高通Audio系统的模块划分与代码线索,理解bringup各阶段的检查项和常见问题切入点,为后续深入驱动调试与项目实践打下基础。
1. 先弄明白一件事:高通平台 Audio 到底在忙什么
很多人搜「高通 Audio 入门」,心里想的其实是个特别朴素的问题:我不过想让手机扬声器响一声,怎么资料一翻开,全是总线、镜像、混音器、校准数据库这些词?真相是,声音从你按下播放键到从喇叭出来,中间要穿过四五层软件和两三条硬件通道,任何一层没打通,结果都表现为“没声音”三个字。而这三个字背后,可能是十几个完全不同的原因。所以入门头一件事不是背术语,而是先建立一张地图:知道声音路过了谁,卡住时该去问谁。这份手册面向的就是刚接触这个平台、手上有台调试机、想让声音先响起来的人,路径是一层层往下走,不是一上来就啃原理。
2. 把系统认全:声音从 App 到喇叭要过几道门
2.1 硬件那一层:codec 是嗓子,总线是神经
先看硬件,因为它决定了你后面调试时“能问谁”。这个平台上,声音的终点是一颗音频编解码芯片,资料里常叫 codec,常见的型号前缀是 WCD 加数字。这颗芯片干三件事:把数字信号变成模拟波形推给喇叭(DAC)、把麦克风送来的模拟波形变成数字(ADC)、顺便管理耳机插拔检测、麦克风偏置电压这些杂活。你可以把它想成嗓子,嗓子本身不聪明,但它必须通电、必须复位、必须收到正确的时钟。
codec 不会自己工作,它挂在总线上等命令。这里要区分两条完全不同的“线”。一条是控制通道,比如 SLIMbus 和 SoundWire,走的是寄存器读写,你发一条命令告诉 codec“把扬声器放大器打开”,走的是它。另一条是数据通道,比如 MI2S、PCM 这类数字音频接口,走的是真正的 PCM 采样流,声音本身从它流过。入门的第一个认知分水岭就在这:控制通道通了,不代表声音能出来;数据通道配错了,声音同样出不来。很多新手排查半天,就是没分清自己查的是哪条线。
还有个词你一定会碰到:单端和差分。单端输出是以共同的地线为基准,只有一根信号线在动,干扰来了电压差就变,抗干扰差;差分输出有两个输出端,信号是两端之间的电压差,干扰同时作用在两根线上,差值几乎不变,所以抗干扰好。落到实际电路上,扬声器通常走差分(推挽输出,声音更响也更干净),麦克风常见单端输入,耳机则两者都可能有。你不用会算,但看到“差分”两个字要知道它在说“这根线更抗噪”,看到“单端”就知道“这根线对布线更挑剔”。
2.2 软件那一层:四层台阶,一层都不能跳
硬件看完看软件。把整条链路想成一家餐厅,你就能记住每一层在干嘛。
最上面是应用层,也就是播放器、铃声、通话这些 App,它是“顾客”,只负责点菜。往下是框架层,Android 里主要是 AudioFlinger 和 AudioPolicyService,前者管音频流怎么混音、往哪个设备送,后者管“现在该用扬声器还是耳机”这种策略,它相当于前台,负责把顾客的点单翻译成厨房听得懂的话。再往下是 HAL 层,硬件抽象层,代码里通常是 audio_hw.c 这一套,它负责把框架层的请求翻译成具体的寄存器操作和路由配置,同时读一份 XML 描述文件来决定“扬声器这条路要走哪几个开关”,它是传达室。再往下是 kernel 层,声音在这里用 ALSA 的 SoC 框架组织,分成 machine driver(把 codec 和主控粘在一起)、platform driver(管前端后端 DAI 和 DMA 搬运)、codec driver(管 codec 寄存器)三部分,它是厨房调度。最底下是 DSP,也就是 ADSP 里跑的音频固件,AFE 管接口、ADM 管路由、ASM 管流,它才是真正炒菜的灶台。
这一层层之间靠什么说话?kernel 和 DSP 之间靠 APR 这个信使,你会在 log 里看到它;HAL 和 kernel 之间就是标准的 ALSA 接口,tinymix、tinyplay 这些工具就是直接跟 kernel 对话的。记住这个顺序有个实际好处:出问题时你可以从下往上排除。先问 DSP 起来没有,再问声卡注册没有,再问路由配了没有,最后才怀疑 App。反过来从上往下猜,基本等于闭着眼睛修车。
2.3 代码都放在哪:新手先认这五个目录
工程代码量很大,但入门阶段真正需要翻的只有几个地方。下面这张表按“你什么时候会去找它”排列,不用一次记全,遇到问题回来查就行。
| 目录大致位置 | 里面是什么 | 什么时候去翻 |
|---|---|---|
| hardware/qcom/audio/hal 一带 | HAL 层实现,audio_hw.c 及其同类文件 | 改路由、改设备选择、加新通路时 |
| external/tinyalsa | tinymix、tinyplay、tinycap 三个小工具 | 想在命令行直接验证声音通不通时 |
| kernel/sound/soc/msm | machine driver 和平台驱动,前端后端 DAI | 声卡注册失败、DAI 链路对不上时 |
| kernel/sound/soc/codecs | codec 驱动,WCD 系列那几个文件 | codec 上电、复位、寄存器读写异常时 |
| kernel/arch/arm/boot/dts 一带 | 设备树文件,描述硬件怎么连 | 换板子、改 GPIO、改时钟时 |
这五个地方覆盖了入门阶段八成的问题。剩下那些编码解码组件、蓝牙音频、USB 音频、无线投屏相关的目录,等你能把扬声器打通了再去了解,不然很容易一头扎进去出不来。
2.4 为什么建议先认结构再敲命令
我见过太多新手(包括当年的自己)拿到一份命令清单就开始抄:tinymix 设一圈值、tinyplay 放一段音频、没声音、换一个值、再放、还是没声音。折腾一整天,最后发现是 codec 压根没上电。这种翻车不是手笨,是缺了“我现在在哪一层”的判断。
认结构带来的最直接收益是缩小搜索范围。当你确认 DSP 镜像加载成功、声卡已经注册、总线上的设备也枚举出来了,那问题就只可能出在路由配置或者 codec 供电上,范围一下从“整个系统”缩到“两个文件”。反过来,如果你连声卡都没注册成功就去改 XML 里的音量值,那叫隔靴搔痒。
所以第 2 章讲的东西不用背,你只要记住三个判断句:DSP 起来了吗?声卡注册了吗?路由配上了吗?后面所有的动手操作,都是围绕这三句话做验证。
3. 第一个能跑的例子:用三个小工具让声音出来
3.1 动手之前:先确认系统认得这块 codec
别急着放音乐,先做体检。连上设备,敲几条只读命令,看看系统到底认出了什么。这些命令不改变任何状态,随便跑。
# 看系统注册了几张声卡,正常应该能看到一张以平台命名的卡 adb shell cat /proc/asound/cards # 看这张卡下面有哪些 PCM 设备,p 是播放,c 是录音 adb shell cat /proc/asound/pcm # 看总线上的 codec 有没有被枚举出来(以 SLIMbus 为例) adb shell ls /sys/bus/slimbus/devices # 看内核启动阶段音频相关的打印,先粗看有没有明显的报错 adb shell dmesg | grep -i -E "asoc|codec|slimbus|adsp"第一条命令的输出最值得盯。如果里面有一张卡,说明 machine driver 匹配成功、codec 驱动 probe 通过、声卡注册这条线是通的,你可以往下走。如果输出是空的或者只有一行“no soundcards”,那说明还没到调路由的阶段,得回去看第 4 章的 bringup 顺序。第二条命令告诉你有哪些 PCM 设备可用,后面 tinyplay 的-D和-d两个参数就是从这儿读出来的。第三条是看控制通道,设备节点在,说明总线和 codec 握手成功;不在,先查供电和复位。
提示:这几条命令建议每次改完配置都跑一遍,形成习惯。它们的成本是几秒钟,但能帮你省掉大量“改了没反应”的困惑。
3.2 用 tinymix 看懂混音器:控件名就是一个个开关
系统认出 codec 之后,会暴露一堆混音器控件,这些控件就是音频通路上的开关和音量旋钮。查看它们只需要一条命令:
# 列出当前声卡上所有混音器控件及取值 adb shell tinymix # 只看名字里带 RX(播放方向)的控件,输出短一些 adb shell tinymix | grep -i "RX" # 把某个控件设成指定值,注意控件名要加引号,名字必须完全一致 adb shell tinymix "RX1 MIX1 INP1" "RX1" # 单独查一个控件现在是什么值 adb shell tinymix "RX1 Digital Volume"控件名看起来像天书,其实有规律。带 RX 的基本是播放方向(接收),带 TX 的是录音方向(发送),带 MIX 的是混音器输入端,带 Volume 或 Gain 的是音量,带 INP 的是“这个混音器的输入接谁”。你要做的就是把一条通路上的开关从源头到终点依次打开:数字端接通、混音器选对输入、增益给个合理值、最后打开输出驱动。中间任何一个没开,声音就断在那儿。
这里有个新手常犯的错:只看音量不看通路。音量拉到最大但混音器输入端选错了,照样一点声都没有。所以顺序永远是先通路后音量,通路对了再用音量微调。
3.3 用 tinyplay 放第一段声音
通路配好之后,就可以放音了。tinyplay 是最轻量的播放工具,它绕开整个 Android 框架,直接往 kernel 的 PCM 设备写数据。这意味着它出不出声,只跟 kernel 和硬件有关,跟 App 层无关,用来做隔离验证特别合适。
# 先把一段测试音频推到设备上,wav 格式最省事,16bit 48kHz 立体声 adb push test.wav /data/test.wav # 指定第 0 张声卡、第 0 个播放设备,播放这个文件 adb shell tinyplay /data/test.wav -D 0 -d 0 # 如果文件头信息不对,可以显式指定采样率和位深 adb shell tinyplay /data/test.wav -D 0 -d 0 -c 2 -r 48000 -b 16参数含义很直白:-D是声卡编号,-d是这张卡下的设备编号,都从 3.1 那两条命令的输出里读;-c是声道数,-r是采样率,-b是位深,不带的话 tinyplay 会尝试从 wav 文件头里解析。如果文件是裸 PCM 数据没有文件头,就必须手动带上这三个参数,否则出来的要么是噪音要么是快放慢放的怪声。
声音出来的那一刻,你的验证链就完整了:硬件通路没问题、codec 上电正常、kernel 驱动正常、路由配置生效。接下来所有问题都可以拿这个状态当基准点,改了什么东西之后不出声了,回退就好。
3.4 用 tinycap 录一段,做一次回环验证
播放通了只证明了一半,录音是另一半。录音链路和播放链路在硬件上是分开的,麦克风偏置、ADC、TX 通路都有自己的坑。
# 录 5 秒,双声道 48kHz 16bit,写到 /data/rec.wav adb shell tinycap /data/rec.wav -D 0 -d 0 -c 2 -r 48000 -b 16 -T 5 # 拉回本地听一下,或者直接在设备上看文件大小是否合理 adb pull /data/rec.wav .-T是录制时长,单位秒,不给的话会一直录到你自己打断。录完先看文件大小:5 秒双声道 48kHz 16bit 大概是 960KB 左右,如果只有几十字节,说明数据没进来,问题在采集端;如果大小正常但全是静音,那多半是麦克风偏置没开或者 TX 通路没接通。
更进一步可以做回环:一边播放一边录音,录到的应该就是刚才放的内容(扬声器外放时通过空气回授,或者走内部回环通路)。这个动作能把整条链路的时延和稳定性都暴露出来,是这个平台上验证音频是否真正 bringup 完成的经典手法。
3.5 没声音时先看这三条线索
第一条线索是 kernel log。音频大部分问题都发生在内核态,App 层的 log 往往只告诉你“播放失败”,具体原因在内核里。跑一下adb shell dmesg | tail -100,重点看 codec probe、声卡注册、DAI 链路有没有报错。
第二条线索是控件状态。回到 3.2,把通路上每个控件挨个查一遍,确认它们的值和你的预期一致。别凭记忆,凭输出。
第三条线索是硬件供电和复位。前面两条都干净但就是没声,就得怀疑物理层了:codec 的供电轨有没有起来、复位脚电平对不对、扬声器本身是不是好的。这类问题在 log 里通常一片安静,属于最难查的一类,也是新手最容易忽略的一类。
4. Bringup 到底在干嘛:从上电到声卡注册的七步
4.1 第一步:DSP 镜像到底加载了没有
这个平台的音频处理不在主 CPU 上跑,而是在一颗专用 DSP 上。所以 bringup 的第一件事不是配通路,是确认 DSP 的固件被加载进了内存。这一步在资料里通常叫加载 PIL 镜像。
固件加载成功的标志性打印,是内核日志里出现类似“Q6 is up”的字样,看到它说明 DSP 已经跑起来了,APR 通信通道也就通了。如果这条日志没出现,先查两件事:一是内核配置里 DSP 加载相关的选项有没有打开,二是在启动脚本里有没有写那一句触发加载的命令,往/sys/kernel/boot_adsp/boot写 1。
# 确认 DSP 加载状态,正常能读到 1 adb shell cat /sys/kernel/boot_adsp/boot # 过滤内核日志里的 DSP 加载信息 adb shell dmesg | grep -i -E "adsp|q6|apr"这里翻车的典型现象是:日志里只有 DSP 加载器初始化,没有加载完成。原因往往就是上面两件事之一没做,或者固件文件没被放进固件分区对应目录。这一步不过,后面所有音频功能都无从谈起,因为整个音频通路的中枢是空的。
4.2 第二步:控制总线起来了没有
DSP 起来之后,接着确认 codec 挂的那条总线。SLIMbus 和 SoundWire 是两种常见的控制总线,它们各自的设备目录下应该能看到枚举出来的设备节点。
# SLIMbus 上的设备 adb shell ls /sys/bus/slimbus/devices # SoundWire 上的设备 adb shell ls /sys/bus/soundwire/devices设备节点在,说明总线控制器初始化完成、codec 应答正常。不在的话,问题通常在 codec 的供电和复位时序上——芯片没上电或者复位没释放,自然不会应答。这时候改驱动代码是没用的,得回去看硬件和时序。
4.3 第三步:声卡注册,一切的及格线
声卡注册是 bringup 最关键的一个里程碑,因为它意味着 machine driver 匹配成功、codec 驱动 probe 通过、DAI 链路建立完成。判断方法就一条命令:cat /proc/asound/cards,有卡就过,没卡就查。
没卡的时候怎么定位?先看内核日志里 machine driver 的匹配过程,有没有打印匹配失败的兼容字符串;再看 codec 驱动的 probe 有没有报错;最后看设备树里给声卡节点写的那些字段(比如声卡名字、路由表、主时钟频率)是不是和驱动里的预期一致。这三者任何一处对不上,声卡就注册不出来。
常见的注册失败原因,一种是硬件层面的,比如某个耦合元件参数不对导致 codec 应答异常;另一种是配置层面的,比如总线地址写错、设备树里给的总线编号和实际接的对不上。这两种现象很像,但排查方向完全不同:前者要动硬件,后者只要改配置。判断方法是看总线枚举那一步——总线能看到设备而声卡注册不出来,问题在驱动和配置;总线都看不到设备,问题在硬件和时序。
4.4 第四步:codec 的上电时序、复位和 GPIO
这一步是新手最容易跳过、也最容易吃亏的地方。codec 不是一通电就能干活的,它有明确的上电顺序:先给某几路电源、再释放复位、然后给主时钟、最后才允许写寄存器。顺序错了,芯片要么不应答,要么应答了但内部状态是乱的。
这条链路在设备树里描述。你不用会写完整的设备树,但要能看懂关键字段:
/* 声卡节点的简化示意,字段名随平台不同,重点看三类信息 */ sound { compatible = "qcom,msm8xxx-audio"; /* 匹配串,必须和 machine driver 里写的一致 */ qcom,model = "msm8xxx-snd-card"; /* 声卡名,注册成功后出现在 /proc/asound/cards */ qcom,audio-routing = /* 路由表,描述哪个引脚连到哪个输入 */ "RX_BIAS", "MCLK", "SPK_RX_BIAS", "MCLK", "AMIC1", "MIC BIAS1"; qcom,msm-mclk-freq = <9600000>; /* 主时钟频率,给错了声音会变调或者干脆不出声 */ };看这段配置要抓三个点:匹配串对不上,machine driver 根本不会 probe;声卡名是你在/proc/asound/cards里看到的名字,出问题时对着它确认自己改的是不是同一张卡;路由表写的是“哪个输出接到哪个输入”,这一项错了,声卡能注册成功但通路是断的。主时钟频率更是个隐形杀手,频率错了不会报错,只会让声音听起来像慢放或者快进,新手往往会怀疑是文件问题,其实是时钟配错了。
4.5 第五步:路由配好,声音才知道往哪走
声卡注册成功、codec 上电正常,接下来就是告诉系统“播放的时候走哪条路”。这一层由 HAL 读取一份 XML 描述文件完成,文件里为每条通路(扬声器、听筒、耳机、麦克风)列出了一串控件和取值。
<!-- mixer_paths.xml 里的简化片段:扬声器通路的开关顺序 --> <path name="speaker"> <ctl name="RX1 MIX1 INP1" value="RX1" /> <!-- 混音器输入选到 RX1 --> <ctl name="RX1 Digital Volume" value="84" /> <!-- 数字侧音量 --> <ctl name="SPK Driver Gain" value="6" /> <!-- 输出驱动增益 --> </path>改这份文件的时候有个铁律:控件名必须和 tinymix 列出来的完全一致,一个字符都不能差。多一个空格、大小写不一致,这一行就会被静默忽略,不报错,只是没效果。这是新手最高频的翻车点之一。
改完之后不是重启就好,得让 HAL 重新加载这份配置。调试阶段更快的办法是用前面 3.2 讲的 tinymix 直接手动设值,先在命令行验证这套控件组合能不能出声,确认有效了再写进 XML,这样能省掉大量重启时间。
4.6 为什么 bringup 必须按这个顺序走
这七步的顺序不是随便排的,它是一条单向依赖链:DSP 不起来,总线没意义;总线不通,声卡注册不了;声卡没有,路由配了也没人读;路由不对,控件设了也不出声。倒过来做,等于在没通水电的房子里调灯光。
我带新人的时候会强调一个习惯:每次只改一件事,改完立刻用最小成本验证。改完设备树就重编内核看声卡,改完 XML 就用 tinymix 试通路,别一次改五个地方再重启,那样出了问题是黑的,连后悔药都没有。
5. 新手最容易踩的五个坑
5.1 坑一:改了 XML 却毫无反应
现象是明明在 mixer_paths.xml 里加了一行,重启之后声音还是老样子,甚至 tinymix 里查不到这个控件。
原因是这份文件改的地方不对,或者控件名拼错被静默忽略。控件名必须和驱动里注册的完全一致,大小写、空格、下划线都算数;另外有些平台有多份同名文件,改的是编译不进去的那一份也很常见。
解决办法是先别改文件,用 tinymix 手动设一遍。手动设能出声,说明控件名对,问题在文件路径或者加载时机;手动设也没反应,说明控件名或者通路本身就是错的。确认有效之后,再照着 tinymix 里的名字原样抄进 XML,抄完 diff 一遍。
5.2 坑二:声卡没注册就盯着 codec 驱动改
现象是/proc/asound/cards是空的,然后一头扎进 codec 驱动里加打印、改逻辑,改了两天没进展。
原因是声卡注册失败的原因可能根本不在 codec 驱动,而在更上游——DSP 没加载、总线没枚举、设备树匹配串写错,任何一个都会导致同样的现象。只在最下游找,自然找不到。
解决办法是按第 4 章的顺序从下往上过一遍:DSP 加载、总线枚举、机器驱动匹配、codec probe。每一步都有明确的日志可看,哪一步断了就停在哪一步修,别跳步。
5.3 坑三:只看应用层日志,不看内核日志
现象是 App 里播放报错,logcat 里只有一句“创建音频流失败”;或者干脆一点错都不报,就是没声音。
原因是音频链路的绝大部分发生在内核态和 DSP 里,应用层拿到的只是一个结果码,真正的原因在 dmesg 里。只盯 logcat,等于只看体温计不看伤口。
解决办法是把adb shell dmesg | grep -i -E "asoc|codec|adsp|slimbus"当成肌肉记忆。尤其是改完配置之后第一次启动的日志,信息量最大,很多问题在那一段里写得清清楚楚。
5.4 坑四:以为录音没声音就是麦克风坏了
现象是播放正常,录音文件大小也正常,但回放全是静音。
原因是录音链路上有好几个独立环节:麦克风偏置电压有没有给、ADC 有没有开、TX 通路的混音器有没有选对输入、录音增益是不是 0。任何一个环节断掉,结果都是“文件正常但没内容”,看起来就像硬件坏了。
解决办法是分层验证。先查控件状态,把 TX 通路上每个开关确认一遍;再换一个已知能用的麦克风输入源(比如耳机麦克风)交叉验证;最后才怀疑硬件。顺序反了,很容易白换一堆器件。
5.5 坑五:直接照抄寄存器值往芯片里写
现象是网上或者别处看到一段寄存器配置,直接写进去,运气好没事,运气不好 codec 直接不响应,得断电重来。
原因是 codec 的寄存器有读写顺序约束,有些位必须在特定状态下才能改,有些写操作会触发内部状态机复位。不了解约束就乱写,等于在黑匣子上乱按按钮。
解决办法是入门阶段先用官方提供的调试接口读,别急着写。读出来的值可以和默认值对比,确认自己理解的是哪一位。真要写,一次只写一个寄存器,写完立刻读回确认,出问题知道是哪一步。
6. 下一步学什么:从“能出声”到“能定位问题”
打通扬声器和麦克风之后,很多人会卡在一个新阶段:声音能出来了,但一旦出问题就不知道从哪下手。这时候该学的不是更多命令,而是三条并行的线。
第一条线是把通路吃透。找一张纸,把从应用到 DSP 的每一层画出来,标上每层的输入输出和你能用的验证手段。画完之后你会发现,很多看似玄学的问题,其实是某一层的数据格式对不上——比如采样率是 48k 但通路配的是 44.1k,声音就会变调;比如声道数不匹配,右声道可能整个是空的。
第二条线是学会看日志。内核日志、框架层日志、DSP 侧日志,三个来源各有各的视角。入门阶段至少要做到:看到一条报错能判断它属于哪一层,然后知道该去哪个目录找对应代码。这个能力没法速成,只能靠一次次真实问题积累,但每积累一次,下次排查就快一倍。
第三条线是建立自己的验证清单。下面这张表是我自己一直在用的,每次改完配置按顺序过一遍,比凭感觉靠谱得多。
| 检查项 | 命令或位置 | 通过标准 |
|---|---|---|
| DSP 固件 | dmesg里找 DSP 加载完成打印 | 有加载完成日志 |
| 控制总线 | /sys/bus/slimbus/devices等 | 能看到 codec 设备节点 |
| 声卡注册 | cat /proc/asound/cards | 有对应声卡 |
| PCM 设备 | cat /proc/asound/pcm | 播放/录音设备齐全 |
| 通路控件 | tinymix | 关键控件取值符合预期 |
| 播放验证 | tinyplay | 有声音且不失真 |
| 录音验证 | tinycap | 文件大小合理且内容非静音 |
学习顺序上,我建议先把播放打通,再做录音,最后碰通话。通话涉及 modem 侧和 DSP 侧的协同,链路比播放复杂一个量级,在播放和录音都稳定之前去碰它,很容易被一堆新概念淹没。等这三样都稳了,再去了解音频校准这类偏产线的东西,那时候你已经有足够的上下文理解它在解决什么问题。
最后说个我自己的教训。刚接触这块的时候,我特别迷信“找到那个正确的配置”,觉得只要抄对了就能跑通。结果有一次调了整整两天,最后发现是设备树里一个时钟频率写错了,声音能出来但一直不对味。从那以后我每次改完配置,都强制走一遍上面那张清单,一项都不跳,哪怕看起来最不可能出问题的那一项。慢一点,但不会返工。
希望这些能帮你少走点弯路,先把声音放出来,剩下的自然会长出来。
本文还有配套的精品资源,点击获取