Audio Usage Instructions#
Overview#
The K230 audio subsystem provides three capture paths and one playback path:
Path |
Signal Source |
Pins Used |
Sound Card |
|---|---|---|---|
Built-in codec capture |
Onboard analog microphone |
Internal to the chip; no pins used |
|
External I2S digital microphone capture |
I2S slave device (digital microphone) |
IO32, IO33 + IO34 or IO36 |
|
PDM digital microphone capture |
PDM microphone, up to 4 data lines and 8 channels |
IO26 + IO27 (or IO37), IO28 (or IO35), IO36, IO34 |
|
Playback |
Built-in codec or external I2S DAC |
Headphone jack / IO35 |
|
The default image enables only the built-in codec. Both the external I2S digital microphone and the PDM digital microphone require board-level device tree modifications, as described in the following two sections.
View the sound cards in the system:
[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
The sound card numbering is determined by driver registration order. When PDM is not configured, only card 0 is present. The
hw:0,0, hw:1,0, and amixer -c0 values in this document use the numbering from the actual test results above. Refer to
cat /proc/asound/cards for the actual numbering.
Built-in codec#
The sound node in the board-level device tree (present by default on each board and requires no modifications):
sound {
status = "okay";
compatible = "canaan,k230-audio-inno";
canaan,model = "K230_I2S_INNO";
canaan,k230-i2s-controller = <&i2s>;
canaan,k230-audio-codec = <&inno_codec>;
};
Recording and playback:
# Onboard analog microphone recording
arecord -D hw:0,0 -f S16_LE -r 44100 -c 2 -d 10 test.wav
# Playback
aplay -D hw:0,0 test.wav
External I2S Digital Microphone#
The external digital microphone connects to the K230 as an I2S slave device, with the K230 providing the clock. Two wiring options are supported, and the option is selected in the device tree.
Pins#
Pin |
function |
Feature |
Purpose |
|---|---|---|---|
io32 |
alt2 |
|
Bit clock, required for both wiring options |
io33 |
alt2 |
|
Frame clock, required for both wiring options |
io34 |
alt2 |
|
Data input for wiring option A, corresponding to channels 0/1 |
io36 |
alt2 |
|
Data input for wiring option B, corresponding to channels 2/3 |
io35 |
alt2 |
|
Data output, required only when connecting an external I2S DAC |
Device Tree Properties#
Add the following to the sound node. Both properties are optional, and when neither is specified, the behavior is the same as that of the default image:
Property |
Value |
Default |
Purpose |
|---|---|---|---|
|
|
|
Source of channels 0/1: built-in codec or IO34 |
|
|
|
Whether to enable IO36 as channels 2/3 |
canaan,i2s-route is only the default value at startup and can be switched at runtime using the Codec Route control.
canaan,rx1-source determines the maximum number of capture channels (2 or 4) and can only be determined at startup.
Wiring Option A: External Digital Microphone on IO34, 2 Channels#
Channels 0/1 come from IO34, and the built-in codec is not involved in capture.
/ {
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, can be omitted for recording only */
output-enable;
input-disable;
bias-disable;
drive-strength = <7>;
};
};
};
&i2s {
pinctrl-names = "default";
pinctrl-0 = <&i2s_pins>;
};
Capture:
amixer -c0 cset name='Codec Route' 'External I2S' # Switch to 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' # Switch back to the onboard analog microphone
Attention
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
Wiring Option B: Built-in Codec + External Digital Microphone on IO36, 4 Channels#
Channels 0/1 are from the onboard analog microphone, and channels 2/3 are from the external digital microphone on IO36. Both are captured simultaneously.
Add only canaan,rx1-source to the sound node:
/ {
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 → channels 2/3 */
input-enable;
output-disable;
bias-disable;
};
};
};
&i2s {
pinctrl-names = "default";
pinctrl-0 = <&i2s_pins>;
};
With this wiring option, IO34 is not involved in capture and requires no configuration. Leave Codec Route at its default value
Internal Codec, and playback will output normally through the onboard speaker.
Capture and split into two files:
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" Built-in.wav
ffmpeg -i test_4.wav -af "pan=stereo|c0=c2|c1=c3" External.wav
The interleaving order of the 4 channels is [Built-in L][Built-in R][External L][External R].
Verification#
# Wiring option B should print external-i2s. The device tree string has a trailing '\0'; remove it with tr for easy script comparison
cat /proc/device-tree/sound/canaan,rx1-source | tr -d '\0'
arecord -D hw:0,0 --dump-hw-params # Wiring option B should display CHANNELS: [2 4]
amixer -c0 cget name='Codec Route' # View the current path
When this property is not specified, /proc/device-tree/sound/canaan,rx1-source does not exist. It is normal for cat to report
No such file or directory; this indicates that the default value none is being used.
PDM Digital Microphone#
PDM is an independent capture path that does not occupy the I2S controller. It is registered as a separate sound card, K230_PDM,
capture only, no playback, and can be used simultaneously with recording and playback on the built-in codec.
Wiring and Pins#
A PDM microphone only requires a clock, data, ground, and power supply. Two microphones can be connected to one data line,
with the L/R pins of the two microphones connected to GND and VDD respectively, each occupying a different clock edge:
K230 IO26 ─── CLK ──┬──▶ MIC A L/R connected to GND → rising edge
└──▶ MIC B L/R connected to VDD → falling edge
K230 IO27 ◀── DATA ─┬─── MIC A
└─── MIC B Both microphones share this data line
The chip has four data lines, with two microphones on each line, supporting up to 8 channels:
Data Line |
Pin |
Function |
Corresponding Channels |
|---|---|---|---|
PDM_IN0 |
io27 (or io37) |
alt3 (io37 is alt2) |
0, 1 |
PDM_IN1 |
io28 (or io35) |
alt3 (io35 is alt2) |
2, 3 |
PDM_IN2 |
io36 |
alt2 |
4, 5 |
PDM_IN3 |
io34 |
alt2 |
6, 7 |
The clock always uses io26 (alt3, PDM_CLK).
Device Tree Configuration#
Add a sound_pdm node under the root node of the board device tree and configure the pins. The following is the
minimum configuration for one data line and 2 channels:
/ {
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>;
};
};
};
The clocks and dmas lines use a fixed format and can be copied as-is.
Optional Properties#
All four properties are optional. If omitted, the default values shown below are used (one data line, both edges, 2 channels):
Property |
Value |
Default |
Purpose |
|---|---|---|---|
|
|
|
Number of connected data lines, counted starting from PDM_IN0 |
|
|
|
Clock edge used for sampling; |
|
|
|
Oversampling ratio, which determines the PDM clock frequency together with the sample rate |
|
|
|
Pin group to which the first two data lines are connected |
Number of channels = number of data lines × (both ? 2 : 1), up to 8.
Attention
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 配置即可。
Adding Data Lines#
canaan,pdm-data-lines and the pins in pdm_pins must be changed together. List as many pins in the pins array as the number of data lines being used. When all 4 lines are connected (8 channels):
sound_pdm: sound_pdm {
/* Other properties are the same as above */
canaan,pdm-data-lines = <4>; /* 4 lines × both edges = 8 channels */
};
&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>;
};
};
};
All pins in a child node share the same function and configuration, so the data pins are divided into two child nodes according to the mux setting: io27 and io28 are alt3, while io36 and io34 are alt2. Pins with different settings cannot be placed together.
For 6 channels only, use canaan,pdm-data-lines = <3> and change the pins of pin_data23 to include only "io36". For 4 channels only, use <2> and omit the entire pin_data23 node.
Attention
When using the 4th line (io34), Codec Route must be switched to External I2S,
otherwise channels 6 and 7 will always contain constant full-scale values:
amixer -c0 cset name='Codec Route' 'External I2S'
After switching, capture and playback on the built-in codec are unavailable (onboard analog microphone and onboard speaker). Using only 3 lines (io27 + io28 + io36, 6 channels) does not have this restriction.
Capture#
# First confirm that the configuration detected by the driver matches the actual wiring
dmesg | grep -i pdm
# PDM capture: 1 data line(s) on IO27/IO28, both edge, 64x oversampling, up to 2 channels
# Record
arecord -D hw:1,0 -f S16_LE -r 48000 -c 2 -d 10 pdm.wav
# Play back through the onboard speaker for listening
aplay -D hw:0,0 pdm.wav
The hardware outputs 24-bit data, and the driver supports three formats:
|
Resulting Data |
Use Case |
|---|---|---|
|
Upper 16 bits of 24-bit data |
General recording and speech recognition; recommended |
|
Full 24-bit data stored in the low bits of a 4-byte container |
When full precision is required |
|
24-bit data shifted left by 8 bits |
When the subsequent processing chain requires 32-bit alignment |
For multichannel capture, the channel count must be selected continuously from 1 to the maximum and cannot be specified with gaps:
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
Channel mapping when canaan,pdm-edge = "both":
Channel |
Source |
Channel |
Source |
|---|---|---|---|
0 |
io27, L/R connected to GND |
4 |
io36, L/R connected to GND |
1 |
io27, L/R connected to VDD |
5 |
io36, L/R connected to VDD |
2 |
io28, L/R connected to GND |
6 |
io34, L/R connected to GND |
3 |
io28, L/R connected to VDD |
7 |
io34, L/R connected to VDD |
PDM and the built-in codec can be used simultaneously:
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 onboard-mic.wav &
aplay -D hw:0,0 test.wav
wait
Resource Conflicts#
io34 and io36 serve as both I2S data inputs and PDM data inputs, and can only be used by one path at a time:
Requirement |
PDM Data Lines |
Available External I2S Digital Mics |
|---|---|---|
2-Channel PDM |
io27 |
Both io34 and io36 are available |
4-Channel PDM |
io27 + io28 |
Both io34 and io36 are available |
6-Channel PDM |
+ io36 |
Only io34 remains available |
8-Channel PDM |
+ io36 + io34 |
Neither is available, and |
The onboard analog microphone (built-in codec capture) and speaker playback are unaffected under the above combinations,
but are unavailable after Codec Route is switched to External I2S.
The table above applies when the first two data lines are connected to io27 and io28 (the default canaan,pdm-io-group = "io27-28").
When they are connected to io35 and io37 instead (canaan,pdm-io-group = "io35-37"), there is an additional conflict: io35 is
IIS_D_OUT0, so once it is occupied by PDM, external I2S DAC playback is unavailable, while onboard speaker playback remains unaffected.
