音频是最常用到的功能,音频也是linux和安卓的重点应用场合。STM32MP1带有SAI接口,正点原子的STM32MP1开发板通过此接口外接了一个CS42L51音频DAC芯片,本章就来学习一下如何使能CS42L51驱动,并且CS42L51通过芯片来完成音乐播放与录音。
处理器要想“听到”外界的声音必须要把外界的声 音转化为自己能够理解的“语言”,处理器能理解的就是0和1,也就是二进制数据。 所以需要先把外界的声音转换为处理器能理解的0和1,在信号处理领域,外界的声音是模拟信号,处理器能理解的是数字信号,因此这里就涉及到一个模拟信号转换为数字信号的过程,而完成这个功能的就是ADC芯片。
同理,如果处理器要向外界传达自己的“心声”,也就是放音,那么就涉及到将处理器能理解的0和1转化为外界能理解的连续变化的声音,这个过程就是将数字信号转化为模拟信号,而完成这个功能的是DAC芯片。
处理器如果既想“听到”外界的声音,又想向外界传达自己的“心声”,那么就需要同时用到DAC和ADC这两款芯片。那是不是买两颗DAC和ADC芯片就行了呢?答案肯定是可以的,但是音频不单单是能出声、能听到就行。往往需要听到的声音动听、录进去的语音贴近真实、可以调节音效、对声音能够进行一些处理(需要DSP单元)、拥有统一的标准接口,方便开发等等。将这些针对声音的各种要求全部叠加到DAC和ADC芯片上,那么就会得到一个专门用于音频的芯片,也就是音频编解码芯片,英文名字就是Audio CODEC,所以在手机或者电脑的介绍中看到“CODEC”这个词语,一般说的都是音频编解码。
既然音频CODEC的本质是ADC和DAC,那么采样率和采样位数就是衡量一款音频CODEC最重要的指标。比如常见音频采样率有8K、44.1K、48K、192K甚至384K和768K;采样位数常见的有8位、16位、24位、32位。采样率和采样位数越高,那么音频CODEC越能真实的还原声音,也就是所谓的HIFI。因此会看到高端的音频播放器都会有很高的采样率和采样位数,同样的价格也会越高。当然了,实际的效果还与其他部分有关,采样率和采样位数只是其中重要的指标之一。
前面已经分析了为何需要音频编解码芯片,那是因为专用的音频编解码芯片提供了很多针对音频的特性。以正点原子STM32MP157开发板所使用的CS42L51这颗芯片为例,来看一下专用的音频编解码芯片都有哪些特性。
CS42L51是一 颗由Cirrus公司出品的音频编解码芯片,是一颗低功耗、高质量的立体声音频CODEC。CS42L51的最主要特点在于它只需要1.8V的低电压供电,但是却可以输出立体声,可以为16Ω耳机提供46mW的输出能量。在2.5V的时候可以16Ω耳机提供88mW的输出能量,CS42L51主要特性如下所示:
CS42L51整体框架如下图所示:
依次来看一下上图中这四部分接口都是什么功能:
I2S(Inter-IC Sound)总线有时候也写作IIS。I2S是飞利浦公司提出的一种用于数字音频设备之间进行音频数据传输的总线。和I2C、SPI这些常见的通信协议一样,I2S总线用于主控制器和音频CODEC芯片之间传输音频数据。因此,要想使用I2S协议,主控制器和音频CODEC都得支持I2S协议,STM32MP1的SAI外设就支持I2S协议,CS42L51同样也支持I2S,所以本章实验就是使用I2S协议来完成的。I2S接口需要3根信号线(如果需要实现收和发,那么就要4根信号线,收和发分别使用一根信号线):
另外,有时候为了使音频CODEC芯片与主控制器之间能够更好的同步,会引入另外一个叫做MCLK的信号,也叫做主时钟或系统时钟,一般是采样率的256倍或384倍。
下图就是一帧立体声音频时序图:
下面是教程里面用逻辑分析仪抓取到的一帧真实的音频个时序图:
上图中通道0是LRCK时钟,通道1为BCLK,通道2是DACDATA,通道3是MCLK。随着技术的发展,在统一的I2S接口下,出现了不同的数据格式,根据DATA数据相对于LRCK和SCLK位置的不同,出现了Left Justified(左对齐)和Right Justified(右对齐)两种格式,这两种格式的时序图如下图所示:
SAI接口(串行音频接口)灵活性高、配置多样,可支持多种音频协议。该接口适用许多立体声或单声道应用。支持I2S标准、LSB或MSB对齐、PCM/DSP、TDM等等协议。全称为Synchronous Audio Interface。SAI的特性:
SAI框架图如下图所示:
从图中可以看出SAI是由两个各自带有时钟发生器的音频子模块组成。在红色框架里标明了每个子模块都有4个专用引脚控制(SD、SCK、FS、MCLK)。如果将两个子模块声明为同步模块,那么SCK、FS和MCLK就可以共用引脚,不相同的只有数据SD引脚了。
正点原子STM32MP1开发板音频原理图如下图所示:
上图中重点关注有三方面SAI、I2C和HT6872,依次来看一下这个三方面:
最后音频的耳机和MIC录音功能是有JP13来控制的,如果1-2连接就使用耳机进行录音,2-3连接就使用板载MIC录音。
ST官方已经写好了CS42L51驱动,因此直接配置内核使能CS42L51驱动即可,按照如下所示步骤使能CS42L51驱动。
前面分析原理图的时候已经说过了,CS42L51与STM32MP1之间有两个通信接口:I2C和SAI,因此设备树中会涉及到I2C和SAI两个设备节点。其中I2C用于配置CS42L51,SAI接口用于音频数据传输,依次来配置一下这两个接口。
首先配置一下I2C接口,根据原理图知道CS42L51连接到了STM32MP1的I2C4接口上,因此在设备树中的“i2c4”节点下需要添加cs42l51信息。打开Documentation/devicetree/bindings/sound/cs42l51.txt,此文件仅仅用于描述如何在I2C节点下添加cs41l51相关信息,此文档适用于所有的主控,不局限于STM32MP1。
相关属性如下:
绑定文档给出的参考节点内容如下所示:
根据cs42l51.txt这份绑定文档就可以在任意一个主控的I2C节点下添加cs42l51相关信息了,ST官方STM32M157C-DK2开发板使用的也是CS42L51,因此可以直接拿来使用,要注意官方的cs42l51是使用I2C1来配置的,正点原子的开发板是使用I2C4来配置的。打开stm32mp157d-atk.dts文件,先在里面创建I2C4节点,然后将arch/arm/boot/dts/stm32mp15xx-dkx.dtsi文件中ST为官方DK2开发板编写的CS42L51节点复制到I2C4节点下。完成以后的IC4和CS42L51节点内容如下所示:
示例代码 48.3.1.2 添加cs42l51节点后的i2c4节点
1 &i2c4 {
2 pinctrl-names = "default", "sleep";
3 pinctrl-0 = <&i2c4_pins_a>;
4 pinctrl-1 = <&i2c4_pins_sleep_a>;
5 status = "okay";
6 /delete property/dmas;
7 /delete property/dma-names;
8 cs42l51: cs42l51@4a {
9 compatible = "cirrus,cd42l51";
10 reg = <0x4a>;
11 #sound-dai-cells = <0>;
12 VL-supply = <&v3v3>;
13 VD-supply = <&v1v8_audio>;
14 VA-supply = <&v1v8_audio>;
15 VAHP-supply = <&v1v8_audio>;
16 reset-gpios = <&gpioz 7 GPIO_ACTIVE_LOW>;
17 clocks = <&sai2a>;
18 clock-names = "MCLK";
19 status = "okay";
20
21 cs42l51_port: port {
22 #address-cells = <1>;
23 #size-cells = <0>;
24
25 cs42l51_tx_endpoint: endpoint@0 {
26 reg = <0>;
27 remote-endpoint = <&sai2a_endpoint>;
28 frame-master;
29 bitclock-master;
30 };
31
32 cs42l51_rx_endpoint: endpoint@1 {
33 reg = <1>;
34 remote-endpoint = <&sai2b_endpoint>;
35 frame-master;
36 bitclock-master;
37 }:
38 };
39 };
40 };
示例代码48.3.1.2里,新追加了一个I2C4节点和添加了i2c4的一个cs42l51子节点。
第3、4行,设置I2C4的引脚,要根据自己的实际硬件情况设计,正点原子STM32MP157开发板上I2C4_SDA和I2C4_SCL分别为PZ5和PZ4。stm32mp15-pinctrl.dtsi文件中I2C4引脚默认配置的就是PZ5和PZ4,因此无需修改。
第8-39行,就是cs42l51子节点。
第8-19行,就是跟cs42l51.txt文档一样,只是将复位引脚改为了PZ7,这里就不多说了。重点是第13-15行,多了个1.8V的电源管理,所以需要添加一个1.8V的节点,稍后添加。
第21-38行,cs42l51_port子节点用来设置CS42L51的数据输入/输出端口信息,第25-30行cs42l51_tx_endpoint端口用来接收CPU上哪个SAI的数据,第27行就是告诉CS42L51从sai2a_endpoint获取音频数据,此接口是用来播放音频的通道。第32-37行cs42l51_rx_endpoint端口用来将获取到的音频数据发送到CPU的哪个SAI接口上,第34行就是告诉CS42L51从外面获取的音频数据传输到sai2b_endpoint,此接口是用来录音的通道。
最后我们还需要在根节点里添加一个v1v8_audio的电源管理。如下示例代码所示:
示例代码 48.3.1.3 追加的v1v8_audio节点内容
1 v1v8_audio: regulator-v1v8-audio {
2 compatible = "regulator-fixed";
3 regulator-name = "v1v8_audio";
4 regulator-min-microvolt = <1800000>;
5 regulator-max-microvolt = <1800000>;
6 regulator-always-on;
7 regulator-boot-on;
8 };
至此,关于cs42l51的I2C配置接口设备树就已经添加好了。
接下来就是STM32MP1的SAI音频接口设备树相关内容的修改了,同样的,先查阅一下相应的绑定文档:devicetree/bindings/sound/st,stm32-sai.txt。和前面讲过的IIC接口、SPI等接口一样,在stm32mp151.dtsi文件中会有关于SAI相关接口的描述,这部分是ST原厂编写的,不需要做任何修改,SAI2的设备子节点内容如下所示:
在示例代码48.3.1.4中sai2节点有两个子节点,分别为sai2a和sai2b,刚好对应两个子模块。直接搜索“st,stm32h7-sai”兼容值,那么就会找STM32MP1的SAI接口驱动文件,路径为sound/soc/stm/stm32_sai.c,此驱动文件不需要去研究,是已经ST写好的SAI驱动。从第9行可以看出, SAI2默认是关闭的,因此需要将其打开,也就是设置status属性的值为“okay”,这个工作肯定是在具体板子对应的.dts文件中完成的,其实就是向sai2节点以及对应子节点里面追加或者修改一些属性值。
前面说了,ST官方的DK2开发板也使用了CS42L51,因此DK2开发板的设备树文件stm32mp15xx-dkx.dtsi里面肯定有具体的SAI2节点信息。可以直接将其复制到stm32mp157d-atk.dts文件里面,内容如下:
示例代码48.3.1.5 拷贝的SAI2节点内容
&sai2 {
clocks = <&rcc SAI2>, <&rcc PLL3_Q>, <&rcc PLL3_R>;
clock-names = "pclk", "x8k", "x11k";
pinctrl-names = "default", "sleep";
pinctrl-0 = <&sai2a_pins_a>, <&sai2b_pins_b>;
pinctrl-1 = <&sai2a_sleep_pins_a>, <&sai2b_sleep_pins_b>;
status = "okay";
sai2a: audio-controller@4400b004 {
#clock-cells = <0>;
dma-names = "tx";
clocks = <&rcc SAI2_K>;
clock-names = "sai_ck";
status = "okay";
sai2a_port: port {
sai2a_endpoint: endpoint {
remote-endpoint = <&cs42l51_tx_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
};
};
sai2b: audio-controller@4400b024 {
dma-names = "rx";
st,sync = <&sai2a 2>;
clocks = <&rcc SAI2_K>, <&sai2a>;
clock-names = "sai_ck", "MCLK";
status = "okay";
sai2b_port: port {
sai2b_endpoint: endpoint {
remote-endpoint = <&cs42l51_rx_endpoint>;
format = "i2s";
mclk-fs = <256>;
dai-tdm-slot-num = <2>;
dai-tdm-slot-width = <32>;
};
};
};
};
示例代码48.3.1.5中的内容,主要是对sai2节点做了四个方面描述:SAI2接口引脚的pinctrl设置、时钟配置、修改status为“okay”以及指定收发接口。重点来看一下pinctrl的设置,因为关系到SAI2接口的IO配置,从pinctrl-0属性可以看出这里一共有两组IO:sai2a_pins_a和 sai2b_pins_b,打开stm32mp15-pinctrl.dtsi文件,这两组IO内容如下:
sai2a_pins_a和sai2b_pins_b就是描述的是SAI2接口的IO配置,这个要根据自己板子的实际硬件情况修改,正点原子的STM32MP1开发板上SAI2所使用的引脚和ST官方的DK2开发板一样,因此这里不需要做任何修改。
对于正点原子的STM32MP1开发板,SAI部分的设备树信息不需要做任何修改,直接使用ST官方写好的即可。
最后需要在根节点“/”下创建一个名为“sound”的子节点,此节点用作控制音频数据的收发。可以参考文档:Documentation/devicetree/bindings/sound/audio-graph-card.txt。此文档有很多种配置方法,还要根据你的音频选择对应的配置方法。ST官方已经针对DK2开发板编写了 sound节点,直接拷贝过来即可。在stm32mp15xx-dkx.dtsi文件中找到 sound节点,然后将其拷贝到stm32mp157d-atk.dts根节点下,拷贝完成以后的sound节点内容如下所示:
示例代码48.3.1.7 sound节点内容
sound: sound {
compatible = "audio-graph-card";
label = "STM32MP1-DK";
routing =
"Playback" , "MCLK",
"Capture" , "MCLK",
"MICL" , "Mic Bias";
dais = <&sai2a_port &sai2b_port>;
status = "okay";
};
简单看一下sound节点中几个重要的属性:
设备树配置完成以后就可以使能内核自带的CS42L51驱动了,直接通过图形化界面配置即
可,输入“make menuconfig”即可打开图形化界面。ST官方已经默认使能了CS42L51驱动。
配置路径如下:
-> Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> CODEC drivers -> <*> Cirrus Logic CS42L51 CODEC (I2C)(选中) |
结果如下图所示:
接下来使能sound,配置路径如下:
-> Device Drivers -> Sound card support -> Advanced Linux Sound Architecture -> ALSA for SoC audio support -> -> <*> ASoC Audio Graph sound card support(选中) |
结果如下图所示:
最后使能SAI接口:
-> Device Drivers -> Sound card support (SOUND [=y]) -> Advanced Linux Sound Architecture (SND [=y]) -> ALSA for SoC audio support (SND_SOC [=y]) -> STMicroelectronics STM32 SOC audio support -> <*> STM32 SAI interface (Serial Audio Interface) support (选中) -> <*> STM32 I2S interface (SPI/I2S block) support (选中) -> <*> STM32 S/PDIF receiver (SPDIFRX) support (选中) -> <*> SoC Audio support for STM32 DFSDM (选中) |
结果如下图所示:
CS42L51默认驱动的录音为单通道,所以要修改驱动来实现双通道录音。打开sound/soc/codecs/cs42l51.c文件,找到cs42l51_hw_params函数,将如下代码添加到第477行:
intf_ctl |= (1 << 0); |
结果如下图所示:
上图中添加的内容是将CS42L51的0X04寄存器的bit0置1。驱动使能以后重新编译linux内核,编译完成以后使用新的uImage和.dtb文件启动,如果设备树和驱动都使能的话系统启动过程中就会如下图所示的log信息:
系统最终启动以后会打印出ALSA设备列表,现在的音频CODEC驱动基本都是ALSA架构的,本章的CS42L51驱动也是根据ALSA架构编写的。因此在ALSA设备列表中就会找到“STM32MP1-DK”这个声卡,如下图所示:
进入系统以后查看一下/dev/snd目录,看看有没有如下图所示文件:
上图中的这些文件就是ALSA音频驱动框架对应的设备文件,这些文件的作用如下:
音频驱动使能以后还不能直接播放音乐或录音,还需要移植alsa-lib和alsa-utils这两个东西。
alsa-lib这个库在buildroot已经默认编译进去可以不用管,只需要使能alsa-utils就行了,还是在buildroot的源码目录下,通过“make menuconfig”进入图形化界面,按照如下路径进入alsa-utils:
-> Target packages -> Audio and video applications -> [*] alsa-utils (选中 ) -> [*] alsactl(选中) ...... -> [*] speaker-test(选中) |
把alsa-utils下的软件全部选中,结果如下图所示:
保存退出,然后重新编译buildroot。编译完成后,把新的rootfs替换到原先的根文件系统中:
cd output/images/ //进入到 output/images目录 sudo tar -axvf rootfs.tar -C /home/zuozhongkai/linux/nfs/rootfs //解压到 nfsroot目录 |
上述命令将buildroot中output/images/rootfs.tar这个压缩包解压到/home/zuozhongkai/linux/nfs/rootfs这个目录中,这个目录就是正点原子教程中当前nfsroot目录,根据自己的实际情况解压到对应的目录文件中。
声卡相关选型默认都是关闭的,比如耳机和喇叭的左右声道输出等。因此在使用之前一定要先设置好声卡,alsa-utils自带了amixer这个声卡设置工具。输入如下命令即可查看amixer的帮助信息:
amixer --help //查看amixer帮助信息 |
结果如下图所示:
从上图可以看出,amixer软件命令分为两组,scontrols、scontents、sset和sget为一组。controls、contents、cset和cget为另一组。这两组的基本功能都是一样的,只不过“s”开头的是simple组,这一组命令是简化版,本教程最终使用“s”开头的命令设置声卡,因为少输入很多字符。
要先看一下都有哪些设置项,先来看一下scontrols对应的设置项,输入如下命令:
amixer scontrols //查看所有设置项 |
结果如下图所示:
再来看一下controls对应的设置项,输入如下命令:
amixer controls //查看所有设置项 |
这里只关注一些最常用的设置即可,比如设置耳机和喇叭音量、设置左右声道音量、设置输入音量等等。
不同的设置项对应的设置值类型不同,先查看一下scontents对应的设置值,输入如下命令:
amixer scontents //查看设置值 |
结果如下图所示:
从上图可以看出“PCM”项目就是设置耳机音量的,音量范围为0-127,当前音量为103。有些设置项是bool类型,只有on和off两种状态。关于controls对应的设置值自行输入“amixer contents”命令查看即可。
知道了设置项和设置值,那么设置声卡就很简单了,直接使用下面命令即可:
amixer sset 设置项目 设置值 |
或:
amixer cset 设置项目 设置值 |
如果要读取当前声卡某项设置值的话使用如下命令:
amixer sget 设置项目 |
或:
amixer cget 设置项目 |
第一次使用声卡之前一定要先使用amixer设置声卡,打开耳机或者喇叭,并且设置喇叭或者耳机音量,还有就是开启左右声道,输入如下命令:
amixer cset name='PCM Playback Switch' 'on','on' amixer cset name='PCM Playback Volume' '63','63' amixer cset name='Analog Playback Volume' '204','204' amixer cset name='PCM channel mixer' 'L R' |
声卡设置好以后就可以使用aplay软件播放wav格式的音乐测试一下,aplay也是alsa-utils提供的。buildroot编译alsa-utils的时候已经在/usr/share/sounds/alsa/路径下存放了一些音乐了,可以直接拿来测试,测试命令如下:
aplay /usr/share/sounds/alsa/Front_Right.wav //播放歌曲 |
如果一切设置正常的话就会开始播放音乐。
注意:由于CS42L51只有耳机播放的输出接口,没有喇叭接口。正点原子STM32MP1开发板上的喇叭直接接到了耳机右声道上,所以耳机与喇叭不能自动切换,播放音乐的时候会同时有声音。
正点原子的STM32MP1开发板上有一个板载麦克风,如下图所示:
将上图中的JP13跳接到上方,也就是使用板载MIC录音。如果跳接到下方就是使用耳机自带的话筒。
同样的,第一次使用声卡录音之前要先使用amixer设置一下声卡,设置声卡的命令如下所示:
amixer cset name='PGA-ADC Mux Left' '3' amixer cset name='PGA-ADC Mux Right' '3' amixer cset name='Mic Boost Volume' '1','1' |
使用arecord来录制一段10秒中的音频,arecord也是alsa-utils编译出来的,输入如下命令:
arecord -f S16_LE -d 10 -D hw:0,1 record.wav |
-f是用来设置录音的数据以S16_LE格式进行采样。-d是指定录音时间,单位为s,-D是指定硬件声卡,0是卡数,1是在设备数量。这条指令就是录制一段以S16_LE格式10s的wav音频,音频名字为record.wav。
录制完成以后使用aplay播放刚刚录制的音频,看看录制是否成功,命令如下:
aplay record.wav |
最后进行一下phone录音测试,也就是使用耳机上的MIC录音,前提是耳机支持录音。正点原子的STM32MP1开发板上phone接口如图下图所示:
使用一根3.5mm的接口耳机,耳机要有麦。录音的方法跟MIC录音一样即可。
在使用的时候应该应该会发现开发板重启以后声卡的所有设置都会消失,必须重新设置声卡。也就是说对声卡的设置不能保存,本小节就来学习一下如何保存声卡的设置。
声卡设置的保存通过alsactl工具来完成,此工具也是alsa-utils编译出来的。alsactl默认将声卡配置文件保存在/var/lib/alsa目录下。
首先使用amixer设置声卡,然后输入如下命令保存声卡设置:
alsactl -f /var/lib/alsa/asound.state store //保存声卡设置 |
-f指定声卡配置文件,store表示保存。关于alsactl的详细使用方法,输入“alsactl -h”即可。保存成功以后就会生成/var/lib/alsa/asound.state这个文件,asound.state里面就是关于声卡的各种设置信息,可以打开此文件查看一下里面的内容,如下图所示:
如果要使用asound.state中的配置信息来配置声卡,执行如下命令即可:
alsactl -f /var/lib/alsa/asound.state restore |
在之前的根文件系统buildroot测试中,曾经创建了/etc/init.d/Sautorun自启动文件,如果没有就自行创建,或者使用/etc/init.d/reS文件,添加如下内容:
示例代码 4 .6.1 /etc/init.d/S autorun 追加内容
1 if [ f "/var/lib/alsa/asound.state" ]; then
2 echo "ALSA: Restoring mixer setting......"
3 /usr/sbin/alsactl -f /var/lib/alsa/asound.state restore &
4 fi
第1行判断/var/lib/alsa/asound.state这个文件是否存在,存在的话就执行下面的。首先输出一行提示符:“ALSA: Restoring mixer setting…”,表示设置声卡,最后调用/usr/sbin/alsactl来执行声卡设置工作。
设置完成以后重启开发板,开发板开机就会自动设置声卡,输出如下图所示内容:
直接使用aplay播放音乐测试声卡开机自动配置是否正确。
alsamixer是基于图形化的,直接输入“alsamixer”命令即可打开声卡配置界面,如下图所示:
上图最下面一行就是具体的设置项,比如“Bass”、 “PCM”等等,通过键盘上左右键选择设置项。按下上下键来调整大小,比如设置耳机音量大小等。有些项目会显示“MM”,表示静音,按下“M”键修改为“OO”状态打开“M”键用于修改打开或关闭某些项目。
关于alsamixer的介绍就到这里,用起来还是很简单的。
正点原子的STM32MP157开发板,对于音频的驱动要使用CS42L51芯片来完成,这个驱动linux内核自己已经有了,我们只需要在设备树完成配置就可以了。
CS42L51芯片,挂载在I2C4总线下,同时还需要3.3V和1.8V的供电,设备树中需要tx和rx,以及电源的内容;1.8V的电源也要在根节点下重新添加。
之后还需要在设备树中添加SAI2的节点信息,这里可以直接拷贝stm32mp151.dtsi文件中的相关内容,而sai2在pinctrl的复用中,是已经有过设置的,不需要我们自行去编写。
最后在根节点下,添加一个sound节点调用刚才写好的sai2节点和音频连接的设置。这里可以直接在stm32mp15xx-dkx.dtsi中找到sound节点拷贝过来。
之后可以修改为双通道录音,在sound/soc/codecs/cs42l51.c中的477行添加intf_ctl|=(1<<0);就可以了。
之后的音频测试需要再rootfs中,使能alsa-utils的所有配置。然后通过amixer去设置声卡并通过aplay来测试是否可用。