音频使用说明#
概述#
K230 的音频子系统提供三条采集通路和一条播放通路:
通路 |
信号来源 |
涉及引脚 |
所属声卡 |
|---|---|---|---|
内置 codec 采集 |
板载模拟麦克风 |
芯片内部,不占用引脚 |
|
外置 I2S 数字麦采集 |
I2S 从设备(数字麦克风) |
IO32、IO33 + IO34 或 IO36 |
|
PDM 数字麦采集 |
PDM 麦克风,最多 4 根数据线 8 声道 |
IO26 + IO27(或 IO37)、IO28(或 IO35)、IO36、IO34 |
|
播放 |
内置 codec 或外置 I2S DAC |
耳机座 / IO35 |
|
默认镜像只使能内置 codec。外置 I2S 数字麦和 PDM 数字麦都需要修改板级设备树, 分别见下面两节。
查看系统中的声卡:
[root@canaan ~ ]#cat /proc/asound/cards
0 [K230I2SINNO ]: K230_I2S_INNO - K230_I2S_INNO
K230_I2S_INNO
1 [K230PDM ]: K230_PDM - K230_PDM
K230_PDM
声卡编号由驱动注册顺序决定,未配置 PDM 时只有 0 号一张卡。本文中的
hw:0,0、hw:1,0、amixer -c0 按上面这份实测编号书写,实际编号以
cat /proc/asound/cards 为准。
内置 codec#
板级设备树中的 sound 节点(各板卡默认已有,不需要修改):
sound {
status = "okay";
compatible = "canaan,k230-audio-inno";
canaan,model = "K230_I2S_INNO";
canaan,k230-i2s-controller = <&i2s>;
canaan,k230-audio-codec = <&inno_codec>;
};
录音和播放:
# 板载模拟麦录音
arecord -D hw:0,0 -f S16_LE -r 44100 -c 2 -d 10 test.wav
# 播放
aplay -D hw:0,0 test.wav
外置 I2S 数字麦#
外置数字麦克风作为 I2S 从设备接在 K230 上,K230 提供时钟。支持两种接法, 在设备树中选择。
引脚#
引脚 |
function |
功能 |
用途 |
|---|---|---|---|
io32 |
alt2 |
|
位时钟,两种接法都要 |
io33 |
alt2 |
|
帧时钟,两种接法都要 |
io34 |
alt2 |
|
接法 A 的数据输入,对应声道 0/1 |
io36 |
alt2 |
|
接法 B 的数据输入,对应声道 2/3 |
io35 |
alt2 |
|
数据输出,接外置 I2S DAC 时才需要 |
设备树属性#
在 sound 节点里新增,两个属性都是可选的,都不写时行为与默认镜像一致:
属性 |
取值 |
缺省 |
作用 |
|---|---|---|---|
|
|
|
声道 0/1 的来源:内置 codec 或 IO34 |
|
|
|
是否启用 IO36 作为声道 2/3 |
canaan,i2s-route 只是开机默认值,运行时可以用 Codec Route 控件切换;
canaan,rx1-source 决定采集声道数上限(2 或 4),只能开机确定。
接法 A:IO34 外置数字麦,2 声道#
声道 0/1 来自 IO34,内置 codec 不参与采集。
/ {
sound {
status = "okay";
compatible = "canaan,k230-audio-inno";
canaan,model = "K230_I2S_INNO";
canaan,k230-i2s-controller = <&i2s>;
canaan,k230-audio-codec = <&inno_codec>;
canaan,i2s-route = "external-i2s";
};
};
&iomux {
i2s_pins: i2s_pins {
pin_clk_ws {
pins = "io32", "io33"; /* IIS_CLK、IIS_WS */
function = "alt2";
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
};
pin_din0 {
pins = "io34";
function = "alt2"; /* IIS_D_IN0 */
input-enable;
output-disable;
bias-disable;
};
pin_dout0 {
pins = "io35";
function = "alt2"; /* IIS_D_OUT0,只录音可省略 */
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
};
};
};
&i2s {
pinctrl-names = "default";
pinctrl-0 = <&i2s_pins>;
};
采集:
amixer -c0 cset name='Codec Route' 'External I2S' # 切到 IO34
arecord -D hw:0,0 -f S16_LE -r 44100 -c 2 -d 10 test.wav
amixer -c0 cset name='Codec Route' 'Internal Codec' # 切回板载模拟麦
注意
Codec Route 是双向的:切到 External I2S 后,采集来自 IO34,播放数据从
IO35 输出,板载喇叭无声。要听声音需要在 IO35 接外置 I2S DAC,或者先切回
Internal Codec 再 aplay。
录音或播放进行中不能切换,此时 amixer 会返回 Device or resource busy。
要让选择在重启后保留,推荐用设备树属性 canaan,i2s-route 设定开机默认值。
默认镜像开机不执行 alsactl restore,所以只跑 alsactl store 是不生效的,
要用它得自己补一个开机恢复脚本:
alsactl store # 存到 /var/lib/alsa/asound.state
cat > /etc/init.d/S60alsa <<'EOF'
#!/bin/sh
[ "$1" = start ] && alsactl restore
EOF
chmod +x /etc/init.d/S60alsa
接法 B:内置 codec + IO36 外置数字麦,4 声道#
声道 0/1 是板载模拟麦,声道 2/3 是 IO36 上的外置数字麦,两者同时采集。
sound 节点只加 canaan,rx1-source:
/ {
sound {
status = "okay";
compatible = "canaan,k230-audio-inno";
canaan,model = "K230_I2S_INNO";
canaan,k230-i2s-controller = <&i2s>;
canaan,k230-audio-codec = <&inno_codec>;
canaan,rx1-source = "external-i2s";
};
};
&iomux {
i2s_pins: i2s_pins {
pin_clk_ws {
pins = "io32", "io33"; /* IIS_CLK、IIS_WS */
function = "alt2";
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
};
pin_din1 {
pins = "io36";
function = "alt2"; /* IIS_D_IN1 → 声道 2/3 */
input-enable;
output-disable;
bias-disable;
};
};
};
&i2s {
pinctrl-names = "default";
pinctrl-0 = <&i2s_pins>;
};
这种接法下 IO34 不参与采集,不需要配置,Codec Route 保持默认值
Internal Codec 即可,播放从板载喇叭正常出声。
采集并拆分成两个文件:
arecord -D hw:0,0 -f S16_LE -r 44100 -c 4 -d 10 test_4.wav
ffmpeg -i test_4.wav -af "pan=stereo|c0=c0|c1=c1" 内置.wav
ffmpeg -i test_4.wav -af "pan=stereo|c0=c2|c1=c3" 外置.wav
4 声道的交织顺序是 [内置L][内置R][外置L][外置R]。
检查#
# 接法 B 应打印 external-i2s。设备树字符串带结尾 '\0',用 tr 去掉便于脚本比较
cat /proc/device-tree/sound/canaan,rx1-source | tr -d '\0'
arecord -D hw:0,0 --dump-hw-params # 接法 B 应出现 CHANNELS: [2 4]
amixer -c0 cget name='Codec Route' # 查看当前通路
没写这个属性时 /proc/device-tree/sound/canaan,rx1-source 不存在,cat 报
No such file or directory 是正常的,表示走缺省值 none。
PDM 数字麦#
PDM 是一条独立的采集通路,不占用 I2S 控制器,注册为单独的声卡 K230_PDM,
只能采集,不能播放,可以和内置 codec 的录音、播放同时使用。
接线和引脚#
PDM 麦克风只需要时钟、数据、地和供电。一根数据线上可以挂两个麦克风,
两者的 L/R 引脚一个接 GND、一个接 VDD,各自占用时钟的一个边沿:
K230 IO26 ─── CLK ──┬──▶ MIC A L/R 接 GND → 上升沿
└──▶ MIC B L/R 接 VDD → 下降沿
K230 IO27 ◀── DATA ─┬─── MIC A
└─── MIC B 两个麦克风共用这一根数据线
芯片共有 4 根数据线,每根 2 个麦克风,最多 8 声道:
数据线 |
引脚 |
function |
对应声道 |
|---|---|---|---|
PDM_IN0 |
io27(或 io37) |
alt3(io37 为 alt2) |
0、1 |
PDM_IN1 |
io28(或 io35) |
alt3(io35 为 alt2) |
2、3 |
PDM_IN2 |
io36 |
alt2 |
4、5 |
PDM_IN3 |
io34 |
alt2 |
6、7 |
时钟固定用 io26(alt3,PDM_CLK)。
设备树配置#
在板级设备树的根节点下新增 sound_pdm 节点,并配置引脚。下面是
一根数据线、2 声道的最小配置:
/ {
sound_pdm: sound_pdm {
status = "okay";
compatible = "canaan,k230-audio-pdm";
canaan,model = "K230_PDM";
clocks = <&pdm_clk>;
clock-names = "pdm";
dmas = <&pdma 1 0xfff 0 0x21>;
dma-names = "rx";
pinctrl-names = "default";
pinctrl-0 = <&pdm_pins>;
};
};
&iomux {
pdm_pins: pdm_pins {
pin_clk {
pins = "io26";
function = "alt3"; /* PDM_CLK */
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
input-schmitt = <0>;
};
pin_data0 {
pins = "io27";
function = "alt3"; /* PDM_IN0 */
input-enable;
output-disable;
bias-disable;
drive-strength = <4>;
input-schmitt = <0>;
};
};
};
clocks、dmas 这几行是固定写法,照抄即可。
可选属性#
四个属性都可以不写,不写就是下表中的缺省值(一根数据线、双边沿、2 声道):
属性 |
取值 |
缺省 |
作用 |
|---|---|---|---|
|
|
|
接了几根数据线,从 PDM_IN0 数起 |
|
|
|
在哪个时钟边沿采样, |
|
|
|
过采样倍率,与采样率一起决定 PDM 时钟频率 |
|
|
|
前两根数据线接在哪组引脚上 |
声道数 = 数据线数 × (both ? 2 : 1),最多 8。
注意
canaan,pdm-io-group 必须和实际接线一致。前两根数据线接在 io35/io37 上的板卡
要写 canaan,pdm-io-group = "io35-37",配错的表现是全程静音。
选了 io35-37 之后,io35 和 io37 就不能再作 I2S 数据输出,也就是
io35(IIS_D_OUT0)上接不了外置 I2S DAC 播放;同时 io27、io28 无论 pinctrl
怎么配都收不到 PDM 数据了。第 3、4 根数据线(io36、io34)不受这个属性影响,
按 pinctrl 配置即可。
增加数据线#
canaan,pdm-data-lines 和 pdm_pins 里的引脚必须一起改,写几根线就在
pins 列表里列几个引脚。接满 4 根线(8 声道)时:
sound_pdm: sound_pdm {
/* 其余属性同上 */
canaan,pdm-data-lines = <4>; /* 4 根线 × 双边沿 = 8 声道 */
};
&iomux {
pdm_pins: pdm_pins {
pin_clk {
pins = "io26";
function = "alt3"; /* PDM_CLK */
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
input-schmitt = <0>;
};
pin_data01 {
pins = "io27", "io28"; /* PDM_IN0、PDM_IN1 */
function = "alt3";
input-enable;
output-disable;
bias-disable;
drive-strength = <4>;
input-schmitt = <0>;
};
pin_data23 {
pins = "io36", "io34"; /* PDM_IN2、PDM_IN3 */
function = "alt2";
input-enable;
output-disable;
bias-disable;
drive-strength = <4>;
input-schmitt = <0>;
};
};
};
一个子节点里的所有引脚共用同一条 function 和同一份配置,所以数据脚按档位分成
两个子节点:io27、io28 是 alt3,io36、io34 是 alt2,档位不同的引脚不能写在一起。
只要 6 声道就写 canaan,pdm-data-lines = <3>,并把 pin_data23 的 pins
改成只有 "io36";只要 4 声道就写 <2>,整个 pin_data23 都不要。
注意
用到第 4 根线(io34)时,必须把 Codec Route 切到 External I2S,
否则声道 6、7 始终是一个恒定的满量程值:
amixer -c0 cset name='Codec Route' 'External I2S'
切过去之后内置 codec 的采集和播放都不可用(板载模拟麦、板载喇叭)。 只用 3 根线(io27 + io28 + io36,6 声道)没有这个限制。
采集#
# 先确认驱动认到的配置和实际接线一致
dmesg | grep -i pdm
# PDM capture: 1 data line(s) on IO27/IO28, both edge, 64x oversampling, up to 2 channels
# 录音
arecord -D hw:1,0 -f S16_LE -r 48000 -c 2 -d 10 pdm.wav
# 从板载喇叭放出来听
aplay -D hw:0,0 pdm.wav
硬件出的是 24bit,驱动支持三种格式:
|
得到的数据 |
使用场景 |
|---|---|---|
|
24bit 的高 16 位 |
常规录音、语音识别,推荐 |
|
完整 24bit,装在 4 字节容器低位 |
需要满精度 |
|
24bit 左移 8 位 |
后续处理链要求 32bit 对齐 |
多声道采集,声道数只能从 1 到上限连续取,不能跳着指定:
arecord -D hw:1,0 --dump-hw-params 2>&1 | grep -i channels
# 根线时应为 CHANNELS: [1 8]
amixer -c0 cset name='Codec Route' 'External I2S' # 需要声道 6、7 时才执行
arecord -D hw:1,0 -f S16_LE -r 48000 -c 8 -d 5 八声道.wav
# 拆成单声道文件
for i in 0 1 2 3 4 5 6 7; do
ffmpeg -y -i 八声道.wav -af "pan=mono|c0=c$i" ch$i.wav
done
canaan,pdm-edge = "both" 时的声道对应关系:
声道 |
来源 |
声道 |
来源 |
|---|---|---|---|
0 |
io27,L/R 接 GND |
4 |
io36,L/R 接 GND |
1 |
io27,L/R 接 VDD |
5 |
io36,L/R 接 VDD |
2 |
io28,L/R 接 GND |
6 |
io34,L/R 接 GND |
3 |
io28,L/R 接 VDD |
7 |
io34,L/R 接 VDD |
PDM 和内置 codec 可以同时使用:
arecord -D hw:1,0 -f S16_LE -r 48000 -c 2 -d 20 pdm.wav &
arecord -D hw:0,0 -f S16_LE -r 48000 -c 2 -d 20 板载麦.wav &
aplay -D hw:0,0 test.wav
wait
资源冲突#
io34 和 io36 同时是 I2S 数据输入和 PDM 数据输入,同一时刻只能给一条通路使用:
需求 |
PDM 数据线 |
可用的外置 I2S 数字麦 |
|---|---|---|
2 声道 PDM |
io27 |
io34、io36 都可用 |
4 声道 PDM |
io27 + io28 |
io34、io36 都可用 |
6 声道 PDM |
+ io36 |
只剩 io34 |
8 声道 PDM |
+ io36 + io34 |
都不可用,且需要切 |
板载模拟麦(内置 codec 采集)和喇叭播放在上述组合下都不受影响,
但 Codec Route 切到 External I2S 后不可用。
上表是前两根数据线接 io27、io28(缺省的 canaan,pdm-io-group = "io27-28")的情况。
改接 io35、io37(canaan,pdm-io-group = "io35-37")时还会多一条冲突:io35 是
IIS_D_OUT0,被 PDM 占走后外置 I2S DAC 播放不可用,板载喇叭播放不受影响。
