Configurare driver I2S: Diferență între versiuni
| (Nu s-au afișat 11 versiuni intermediare efectuate de același utilizator) | |||
| Linia 1: | Linia 1: | ||
==Introducere== | ==Introducere== | ||
Pentru documentația completă despre scrierea unui driver audio, vezi [https://kernel.org/doc/html/latest/sound/kernel-api/writing-an-alsa-driver.html Writing an ALSA Driver] de Takashi Iwai. | |||
Dispozitivele audio implicite [[ALSA]] suportate de Raspberry Pi 3 sunt [[PWM]] (jack-ul de ieșire audio) sau [[HDMI]]. Pentru a folosi [[I2S]], acest protocol trebuie activat. Rezultatul este o interfață [[I2S]] simplă care nu depinde de un DAC sau un CODEC ce ar trebui configurate prin [[I2C]] sau [[SPI]]. | Dispozitivele audio implicite [[ALSA]] suportate de Raspberry Pi 3 sunt [[PWM]] (jack-ul de ieșire audio) sau [[HDMI]]. Pentru a folosi [[I2S]], acest protocol trebuie activat. Rezultatul este o interfață [[I2S]] simplă care nu depinde de un DAC sau un CODEC ce ar trebui configurate prin [[I2C]] sau [[SPI]]. | ||
Pentru evitarea recompilării kernel-ului trebuie creat un modul (extensie de kernel) separat. Astfel, driver-ul pentru [[I2S]] va fi încărcat dinamic, la pornirea sistemul. Detalii pot fi citite la http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=91237. Exemplul folosește o copie a distribuției de Debian ''Jessie''. | Pentru evitarea recompilării kernel-ului trebuie creat un modul (extensie de kernel) separat. Astfel, driver-ul pentru [[I2S]] va fi încărcat dinamic, la pornirea sistemul. Detalii pot fi citite la http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=91237. Exemplul folosește o copie a distribuției de Debian ''Jessie''. | ||
== | Acest lucru este mai simplu decât pare la prima vedere, deoarece sursele kernel-ului sunt disponibile și se poate crea relativ ușor un modul nou folosind șabloanele (templates) existente. | ||
==Actualizarea sistemului de operare== | |||
În primul rând, trebuie actualizat sistemul de operare. | În primul rând, trebuie actualizat sistemul de operare. | ||
<syntaxhighlight lang="Console"> | <syntaxhighlight lang="Console"> | ||
| Linia 33: | Linia 37: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== | ==Instalare aplicații helper== | ||
Descărcarea surselor kernel-ului depinde de două aplicații ''helper'' care trebuie instalate: | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
tom@rpi-i2s:~ $ sudo apt-get install bc | tom@rpi-i2s:~ $ sudo apt-get install bc | ||
tom@rpi-i2s:~ $ sudo apt-get install libncurses5-dev | tom@rpi-i2s:~ $ sudo apt-get install libncurses5-dev | ||
</syntaxhighlight> | |||
==Descărcare surse kernel== | |||
Apoi se descarcă sursele kernel-ului. | |||
<syntaxhighlight lang="console"> | |||
tom@rpi-i2s:~ $ git clone http://github.com/notro/rpi-source | tom@rpi-i2s:~ $ git clone http://github.com/notro/rpi-source | ||
tom@rpi-i2s:~ $ cd rpi-source | tom@rpi-i2s:~ $ cd rpi-source | ||
| Linia 44: | Linia 52: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Odatp sursele descărcate, se poate modifica codul-sursă al driver-ului audio. Sunt necesare două module: | |||
* unul numit ''asoc_simple_card'', care este driver-ul propriu-zis; | |||
* unul numit ''loader'', care crează o instanță a modulului plăcii de sunet. | |||
==Creare driver ''asoc_simple_card''== | |||
Următorul pas este crearea driver-ului audio ''asoc_simple_driver''. Șablonul poate fi luat de la https://github.com/torvalds/linux/tree/master/sound/soc/generic. | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
tom@rpi-i2s:~ $ mkdir snd_driver | tom@rpi-i2s:~ $ mkdir snd_driver | ||
| Linia 55: | Linia 64: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Atenție: denumirea folder-ului ''linux'' va avea adăugat un hash. Trebuie utilizat acest folder în comanda copy de mai sus, adică: | |||
<syntaxhighlight lang="console"> | |||
tom@rpi-i2s:~ $ cp /linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa/sound/soc/generic/simple-card.c ./asoc-simple-card.c</code> | |||
</syntaxhighlight> | |||
Acest fișier trebuie modificat pentru a înlătura un ''bug'' din cod. La finalul funcției <code>asoc_simple_card_dai_init</code> (aproximativ la linia 213) trebuie adăugat: | |||
<syntaxhighlight lang="c"> | |||
ret = snd_soc_dai_set_bclk_ratio(cpu, 64); | |||
pr_alert("BCLK ratio set to 64!\n"); | |||
</syntaxhighlight> | |||
Acest lucru este necesar pentru a inițializa raportul BCLK/LRCLK care în cod este 100 față de 64 cât e în realitate nevoie. | |||
'''Notă:''' Dacă raportul BCLK/LRCLK este 64, MCLK poate fi conectat direct la BCLK. Altfel, MCLK trebuie ''driven'' extern. Rapoartele MCLK/LRCLK acceptate sunt 64, 128, 256 și 512 atâta timp cât MCLK este maxim 25MHz. | |||
Dacă raportul BCLK/LRCLK este 32, e necesar un MCLK extern care satisface raporturile MCLK/LRCLK. | |||
===Compilare modul=== | |||
Următoarea secvență de cod trebuie copiată în ''Makefile''. Ultima linie trebuie indentată cu un caracter ''tab'' și nu cu spații: | |||
<syntaxhighlight lang="makefile"> | |||
obj-m := asoc_simple_card.o | |||
KDIR := /lib/modules/$(shell uname -r)/build | |||
PWD := $(shell pwd) | |||
default: | |||
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules | |||
</syntaxhighlight> | |||
Compilează modulul și încarcă-l în kernel: | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
| Linia 80: | Linia 102: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Consola va afișa: | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
| Linia 94: | Linia 116: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==Crearea modulului ''loader''== | |||
Apoi trebuie scris și compilat ''loader-ul'', programul care ''prezintă'' dispozitivul sistemului de operare: | |||
<syntaxhighlight lang="console"> | <syntaxhighlight lang="console"> | ||
tom@rpi-i2s:~/snd_driver $ mkdir loader | |||
tom@rpi-i2s:~/snd_driver $ cd loader | |||
</syntaxhighlight> | </syntaxhighlight> | ||
Copiază codul de mai jos într-un fișier nou, <code>loader.c</code>: | |||
<syntaxhighlight lang="console"> | |||
tom@rpi-i2s:~/snd_driver $ sudo nano loader.c | |||
</syntaxhighlight> | |||
<syntaxhighlight lang="C"> | <syntaxhighlight lang="C"> | ||
#include <linux/module.h> | #include <linux/module.h> | ||
#include <linux/kernel.h> | |||
#include <linux/kmod.h> | |||
#include <linux/platform_device.h> | |||
#include <sound/simple_card.h> | |||
#include <linux/delay.h> | |||
/* | /* | ||
modified for linux 4.1.5 | modified for linux 4.1.5 | ||
Versiunea curentă din 11 iulie 2025 17:53
Introducere
Pentru documentația completă despre scrierea unui driver audio, vezi Writing an ALSA Driver de Takashi Iwai.
Dispozitivele audio implicite ALSA suportate de Raspberry Pi 3 sunt PWM (jack-ul de ieșire audio) sau HDMI. Pentru a folosi I2S, acest protocol trebuie activat. Rezultatul este o interfață I2S simplă care nu depinde de un DAC sau un CODEC ce ar trebui configurate prin I2C sau SPI.
Pentru evitarea recompilării kernel-ului trebuie creat un modul (extensie de kernel) separat. Astfel, driver-ul pentru I2S va fi încărcat dinamic, la pornirea sistemul. Detalii pot fi citite la http://www.raspberrypi.org/forums/viewtopic.php?f=44&t=91237. Exemplul folosește o copie a distribuției de Debian Jessie.
Acest lucru este mai simplu decât pare la prima vedere, deoarece sursele kernel-ului sunt disponibile și se poate crea relativ ușor un modul nou folosind șabloanele (templates) existente.
Actualizarea sistemului de operare
În primul rând, trebuie actualizat sistemul de operare.
tom@rpi-i2s:~ $ sudo apt-get update
tom@rpi-i2s:~ $ sudo apt-get dist-upgrade
Reboot
Pasul următor este repornirea Raspberry Pi pentru a fi sigur că rulează versiunea de Raspbian actualizată:
tom@rpi-i2s:~ $ sudo reboot
Activare I2S și DMA
Apoi trebuie activat I2S în structura dispozitivelor. Pentru asta, trebuie editat fișierul /boot/config.txt și înlăturat comentariul liniei:
dtparam=i2s=on
Driverul plăcii de sunet depinde de I2S și de modulul controller-ului DMA (Direct Memory Access). În acest sens, în /etc/modules:
tom@rpi-i2s:~ $ sudo nano /etc/modules
trebuie adăugate liniile următoare:
snd_soc_bcm2708
snd_soc_bcm2708_i2s
bcm2708_dmaengine
Instalare aplicații helper
Descărcarea surselor kernel-ului depinde de două aplicații helper care trebuie instalate:
tom@rpi-i2s:~ $ sudo apt-get install bc
tom@rpi-i2s:~ $ sudo apt-get install libncurses5-dev
Descărcare surse kernel
Apoi se descarcă sursele kernel-ului.
tom@rpi-i2s:~ $ git clone http://github.com/notro/rpi-source
tom@rpi-i2s:~ $ cd rpi-source
tom@rpi-i2s:~ $ python rpi-source
tom@rpi-i2s:~ $ cd ..
Odatp sursele descărcate, se poate modifica codul-sursă al driver-ului audio. Sunt necesare două module:
- unul numit asoc_simple_card, care este driver-ul propriu-zis;
- unul numit loader, care crează o instanță a modulului plăcii de sunet.
Creare driver asoc_simple_card
Următorul pas este crearea driver-ului audio asoc_simple_driver. Șablonul poate fi luat de la https://github.com/torvalds/linux/tree/master/sound/soc/generic.
tom@rpi-i2s:~ $ mkdir snd_driver
tom@rpi-i2s:~ $ cd snd_driver
tom@rpi-i2s:~ $ cp linux/sound/soc/generic/simple-card.c ./asoc-simple-card.c
Atenție: denumirea folder-ului linux va avea adăugat un hash. Trebuie utilizat acest folder în comanda copy de mai sus, adică:
tom@rpi-i2s:~ $ cp /linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa/sound/soc/generic/simple-card.c ./asoc-simple-card.c</code>
Acest fișier trebuie modificat pentru a înlătura un bug din cod. La finalul funcției asoc_simple_card_dai_init (aproximativ la linia 213) trebuie adăugat:
ret = snd_soc_dai_set_bclk_ratio(cpu, 64);
pr_alert("BCLK ratio set to 64!\n");
Acest lucru este necesar pentru a inițializa raportul BCLK/LRCLK care în cod este 100 față de 64 cât e în realitate nevoie.
Notă: Dacă raportul BCLK/LRCLK este 64, MCLK poate fi conectat direct la BCLK. Altfel, MCLK trebuie driven extern. Rapoartele MCLK/LRCLK acceptate sunt 64, 128, 256 și 512 atâta timp cât MCLK este maxim 25MHz.
Dacă raportul BCLK/LRCLK este 32, e necesar un MCLK extern care satisface raporturile MCLK/LRCLK.
Compilare modul
Următoarea secvență de cod trebuie copiată în Makefile. Ultima linie trebuie indentată cu un caracter tab și nu cu spații:
obj-m := asoc_simple_card.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
Compilează modulul și încarcă-l în kernel:
tom@rpi-i2s:~ $ make
tom@rpi-i2s:~ $ sudo insmod asoc_simple_card.ko
tom@rpi-i2s:~ $ cd ..
Consola va afișa:
pi@raspberrypi:~/snd_driver $ make
make -C /lib/modules/4.4.13-v7+/build SUBDIRS=/home/pi/snd_driver modules
make[1]: Entering directory '/home/pi/linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa'
CC [M] /home/pi/snd_driver/asoc_simple_card.o
Building modules, stage 2.
MODPOST 1 modules
CC /home/pi/snd_driver/asoc_simple_card.mod.o
LD [M] /home/pi/snd_driver/asoc_simple_card.ko
make[1]: Leaving directory '/home/pi/linux-e88b69aa792dbe2b289ff528c0d07303068aa0aa'
Crearea modulului loader
Apoi trebuie scris și compilat loader-ul, programul care prezintă dispozitivul sistemului de operare:
tom@rpi-i2s:~/snd_driver $ mkdir loader
tom@rpi-i2s:~/snd_driver $ cd loader
Copiază codul de mai jos într-un fișier nou, loader.c:
tom@rpi-i2s:~/snd_driver $ sudo nano loader.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/platform_device.h>
#include <sound/simple_card.h>
#include <linux/delay.h>
/*
modified for linux 4.1.5
inspired by https://github.com/msperl/spi-config
with thanks for https://github.com/notro/rpi-source/wiki
as well as Florian Meier for the rpi i2s and dma drivers
to use a differant (simple-card compatible) codec
change the codec name string in two places and the
codec_dai name string. (see codec's source file)
fmt flags are set for vanilla i2s with rpi as clock slave
N.B. playback vs capture is determined by the codec choice
*/
void device_release_callback(struct device *dev) { /* do nothing */ };
static struct asoc_simple_card_info snd_rpi_simple_card_info = {
.card = "snd_rpi_simple_card", // -> snd_soc_card.name
.name = "simple-card_codec_link", // -> snd_soc_dai_link.name
.codec = "snd-soc-dummy", // -> snd_soc_dai_link.codec_name
.platform = "3f203000.i2s",
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFM,
.cpu_dai = {
.name = "3f203000.i2s", // -> snd_soc_dai_link.cpu_dai_name
.sysclk = 0
},
.codec_dai = {
.name = "snd-soc-dummy-dai", // -> snd_soc_dai_link.codec_dai_name
.sysclk = 0
},
};
static struct platform_device snd_rpi_simple_card_device = {
.name = "asoc-simple-card", //module alias
.id = 0,
.num_resources = 0,
.dev = {
.release = &device_release_callback,
.platform_data = &snd_rpi_simple_card_info, // *HACK ALERT*
},
};
int hello_init(void)
{
const char *dmaengine = "bcm2708-dmaengine"; //module name
int ret;
ret = request_module(dmaengine);
pr_alert("request module load '%s': %d\n",dmaengine, ret);
ret = platform_device_register(&snd_rpi_simple_card_device);
pr_alert("register platform device '%s': %d\n",snd_rpi_simple_card_device.name, ret);
pr_alert("Hello World :)\n");
return 0;
}
void hello_exit(void)
{// you'll have to sudo modprobe -r the card & codec drivers manually (first?)
platform_device_unregister(&snd_rpi_simple_card_device);
pr_alert("Goodbye World!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_DESCRIPTION("ASoC simple-card I2S setup");
MODULE_AUTHOR("Plugh Plover");
MODULE_LICENSE("GPL v2");
Make sure that line 21 is changes as shown below to ensure the Raspberry Pi 3 is the I2S master and the Microphone is the slave::
.daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBS_CFS,
.. [#] https://www.raspberrypi.org/forums/viewtopic.php?p=914713#p914713
Copy the below contents into ``Makefile`` again ensuring that the indent is a tab character rather than spaces::
obj-m := loader.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
And finally build the module and insert it into the kernel::
make sudo insmod loader.ko cd ..
To verify that the I2S audio device driver module has been loaded and is accessible, you run the following command::
arecord -l
This should generate a list of available ALSA captures devices something like this::
**** List of CAPTURE Hardware Devices ****
card 1: sndrpisimplecar [snd_rpi_simple_card], device 0: simple-card_codec_link snd-soc-dummy-dai-0 []
Subdevices: 1/1
Subdevice #0: subdevice #0
The physical sound card device presents a stereo I2S device to Linux. However, some Voice Assistant software such as Amazon's Alexa, requires access to a mono capture device. To present the stereo sound card as a mono device it is necessary to create a virtual mono capture device connected to the physical stereo capture device in ALSA. The microphone audio sent from the Microphone is a mono signal repeated on both stereo channels and so either channel will provide the required signal. The virtual mono capture device then needs to be set as the default device so that the client software picks it first when searching for input devices.
To setup the virtual capture device, modify the file ``.asoundrc`` found in the home directory as follows::
pcm.monocard {
type plug
slave.pcm "plughw:1,0"
slave.channels 2
slave.rate 16000
}
ctl.monocard {
type hw
card 1
device 0
}
pcm.!default {
type asym
playback.pcm {
type hw
card 0
}
capture.pcm {
type plug
slave.pcm "monocard"
}
}
ctl.!default {
type asym
playback.pcm {
type hw
card 0
}
capture.pcm {
type plug
slave.pcm "monocard"
}
}
The above configuration keeps the default playback device as the 3.5mm jack on the Raspberry Pi however this can be changed to the I2S bus by changing the contents of for both ``pcm.!default {`` and ``ctl.!default {`` so that the ``playback.pcm {`` entries match ``capture.pcm``.
To apply the changes made in ``.asoundrc`` and enable the virtual capture device either reboot the Raspberry Pi or use the following command::
sudo /etc/init.d/alsa-utils restart
Following the instructions given so far will enable the I2S soundcard in the current session. To make it available after reach reboot, it is necessary to load the modules at boot time. You can do this by adding a boot-time cron job to run the following script. Save this script as ``load_i2s_driver.sh`` in your home directory::
cd ~ sudo insmod snd_driver/asoc_simple_card.ko sudo insmod loader/loader.ko
The script can be auto-run at boot time by adding the following line in the editor window that appears after typing ``crontab -e``::
@reboot /home/pi/load_i2s_driver.sh