Recording WAVE 'test.wav' : [ 137.530635] =========nau8822_set_dai_sysclk clk_id:0,freq:12288000 Signed 16 bit Little Endian, Rat[ 137.539005] @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@nau8822_hw_params val_len:0x0,val_rate:0x0 e 48000 Hz, Stereo [ 137.550268] =======nau8822_set_bias_level:2 [ 137.557970] =======nau8822_set_bias_level:3 [ 137.562349] ==============nau8822_mute:0 [ 137.567276] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 137.700319] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 137.708499] ==========default_read_copy [ 137.713440] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 137.833330] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 137.841502] ==========default_read_copy [ 137.846292] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 137.966340] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 137.974564] ==========default_read_copy [ 137.979420] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.099356] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.107542] ==========default_read_copy [ 138.112395] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.232371] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.240536] ==========default_read_copy [ 138.245348] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.365401] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.373584] ==========default_read_copy [ 138.378445] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.498411] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.506545] ==========default_read_copy [ 138.511380] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.631423] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.639600] ==========default_read_copy [ 138.644365] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.764434] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.772596] ==========default_read_copy [ 138.777439] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 138.897447] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 138.905579] ==========default_read_copy [ 138.910384] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.030457] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.038683] ==========default_read_copy [ 139.043492] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.163475] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.171665] ==========default_read_copy [ 139.176485] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.296492] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.304701] ==========default_read_copy [ 139.309542] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.429518] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.437688] ==========default_read_copy [ 139.442532] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.562528] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.570700] ==========default_read_copy [ 139.575457] =======__snd_pcm_lib_xfer size:6000,data:0x31940,interleaved:1,in_kernel:0 [ 139.695540] k510_peridma_interrupt: process Transfer done irq of phy ch0, int_status(1) [ 139.703705] ==========default_read_copy [ 139.708673] ==============nau8822_mute:1 [ 139.713264] =======nau8822_set_bias_level:2 [ 139.719924] =======nau8822_set_bias_level:1
audio codec 每个寄存器占用两个字节。
#define NAU8822_REG_INPUT_CONTROL 0x2C static const struct snd_kcontrol_new nau8822_right_input_mixer[] = { SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0), SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0), SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0), };
#define AUDIO_BASE_ADDR (0x96060000U) volatile audio_in_reg_s *audio_in_reg = (volatile audio_in_reg_ *)(long)(AUDIO_BASE_ADDR+0x400);
#define AUDIO_BASE_ADDR (0x96060000U)
audio_i2s_in_init
0x96060400+0x21c = 0x9606061c
devmem 0x9606061c w -> 0x00000001
i2s_init
ier
devmem 0x96060000 w -> 0x00000001
irer
devmem 0x96060004 w ->0x00000001
iter
devmem 0x96060008 w ->0x00000001
ccr
0x96060000 + 0x10
devmem 0x96060010 w -> 0x00000B30 ? 0x00000730
imr [0-3 channel]
0x96060000+0x3c+0x40(*0-3)
devmem 0x9606003c w -> 0x00000033
devmem 0x9606007c w -> 0x00000033
devmem 0x960600bc w -> 0x00000033
devmem 0x960600fc w -> 0x00000033
ter [0-3 channel]
0x96060000+0x2c+0x40(*0-3)
devmem 0x9606002c w -> 0x00000001
devmem 0x9606006c w ->0x00000000
devmem 0x960600ac w -> 0x00000000
devmem 0x960600ec w -> 0x00000000
rer [0-3 channel]
0x96060000+0x28+0x40(*0-3)
devmem 0x96060028 w -> 0x00000001
devmem 0x96060068 w ->0x00000000
devmem 0x960600a8 w -> 0x00000000
devmem 0x960600e8 w -> 0x00000000
i2s_rx_channel_config
i2s_set_rx_word_length
rcr [0-3 channel]
0x96060000+0x30+0x40(*0-3)
devmem 0x96060030 w -> 0x00000005 ?0x00000002
devmem 0x96060070 w -> 0x00000005
devmem 0x960600b0 w -> 0x00000005
devmem 0x960600f0 w -> 0x00000005
i2s_set_tx_word_length
tcr [0-3 channel]
0x96060000+0x34+0x40(*0-3)
devmem 0x96060034 w -> 0x00000005 ?0x00000002
devmem 0x96060074 w -> 0x00000005
devmem 0x960600b4 w -> 0x00000005
devmem 0x960600f4 w -> 0x00000005
RFCRx[0-3 channel]
0x96060000+0x48+0x40(*0-3)
devmem 0x96060048 w ->0x00000003
devmem 0x96060088 w ->0x00000005
devmem 0x960600c8 w ->0x00000005
devmem 0x96060108 w ->0x00000005
TFCRx[0-3 channel]
0x96060000+0x4c+0x40(*0-3)
devmem 0x9606004c w ->0x00000003
devmem 0x9606008c w ->0x00000003
devmem 0x960600cc w ->0x00000003
devmem 0x9606010c w ->0x00000003
audio_i2s_out_init
devmem 0x96060000 w ->0x00000001
x
dma将内存buffer数据拷贝到i2s 的transfer fifo中时, 内存buffer的结构:如果是16位数据,则通过设置i2s_set_dma_divide_16后,可将左右声道各16位拼接到一个32位里面,格式与wav/pcm音频格式相同。 i2s拷贝后完成后将32位数据分离成左右声道各16位(split 32bit data to two 16 bit data and filled in left and right channel),再按照i2s的协议将 数据放到左右声道中。
一).整体流程总结:
1).引脚设置
查看原理图,设置指定引脚为以下功能:WCLK(采样时钟),BCKL(位时钟),SDIN(数据输入),SDOUT(数据输出)
2).设置codec参数(nau8822 音频codec)
设置采样率,word length(每个声道传输多少位),i2s传输模式(左对齐,右对齐,标准),其他音频链路的控制(adc input,adc mix,dac out,dacmix )。
以此可以保证:i2s in channel fifo上已经有实时的adc转换后的数据,可以随时通多dma拷贝到内存中使用。如果codec没初始化ok,该fifo上的数据拷贝到内存中始终为0.
i2s out channel fifo上等待输入数据,如果有数据,则触发dac转换,通过audio codec播放。
3).I2S数据时钟设置(实际就是BCLK)
时钟配置,通过配置时钟(SYSCTL_CLK_AUDIO_OUT_SERIAL:I2S out channel,SYSCTL_CLK_AUDIO_IN_SERIAL:i2s in channel),
可以精确控制i2s每秒采样位数,对应于pin:BCK.比如48k,双声道,每个声道16位/32cycle,则时钟为:48000*2(2个声道)*32(每个声道32位,其中16位有效)
4).设置soc中i2s参数(与codec设置要一致)
设置word length(每个声道采样位数),word cycles(每个声道采样时钟),传输模式(左对齐,右对齐,标准)等。
5).启动dma
1)数据采集: 1)手动配置dma,数据流向:I2S->buffer,启动dma
2)触发中断完成内存拷贝后,再次进入步骤1,如此无限循环。
2) 数据播放: 1)手动配置dma,数据流向:buffer->I2S,启动dma
2)触发中断完成内存拷贝后,再次进入步骤1,如此无限循环。
二).注意事项总结:
1.数据采集和数据播放可以单独运行也可以同时运行。同时运行时,使用2个不同的dma通道,使用ping pongbuffer会导致延迟极低,不容易听到自己说话声。可以通过 ring buffer来调整延迟。
2.i2s参数的修改,大部分参数修改2个位置:audio codec 和 soc中i2s模块,且两者修改后要保持一致。比如修改采样率,两边都要修改。
3.内存分配:
1)每个声道16位/32cycle的配置,i2s 输入通道默认配置:左声道(低16位有效,高16位为静音数据,值为0或0xffff两者不断切换),右声道同左声道。
2)每个声道32位/32cycle的配置,i2s 输入通道默认配置:左声道(32位,32位有效),右声道同左声道。
3)fpga以前的裸机程序每个声道32位/32cycle的,而实际驱动程序使用中都是16位/32cycle的。
在linux系统下,通过aplay 播放一个wav文件,实际文件为16位/16cycle的格式,如何变成16/32cycle同时避免cpu拷贝内存,可使用i2s函数:i2s_set_dma_divide_16.
(split 32bit data to two 16 bit data and filled in left and right channel).该函数仅仅适合音频播放(数据从buffer到i2s,指的是buffer中的数据).音频采集无效,采集到的数据仍为(16位/32cycle)。
4.音频codec:包括音频的控制和数据的传输。数据的传输通过I2S协议。音频的控制(复位,音量,音频参数设置等)通过I2C协议。