一、實驗環境
1.1 虛擬機環境
a) Vmware版本:Vmware Workstation 12.5.7
b) Ubuntu版本:9.10
c) 內核版本:2.6.31.14
d) toolchain版本:arm-linux-gcc 4.3.2
1.2 開發板
優龍FS2410開發板,UDA1341聲卡
內核版本:3.4.2
二、調試過程記錄
1. 編譯聲卡驅動,修改語法錯誤
2. 配置內核去掉原來的聲卡驅動
-> Device Drivers
-> Sound card support
-> Advanced Linux Sound Architecture
-> ALSA for SoC audio support
< > ASoC support for Samsung // CONFIG_SND_SOC_SAMSUNG 這項去掉勾選后,下面這項會連帶自動消失
< > SoC I2S Audio support UDA134X wired to a S3C24XX // CONFIG_SND_SOC_SAMSUNG_S3C24XX_UDA134X // s3c24xx_uda134x.c
3. 編譯內核,然后通過nfs把新的內核下載到開發板,并啟動
4. 把新的聲卡驅動通過nfs拷貝到根文件系統里,然后安裝
insmod /alsa/driver/myalsa/platform/s3c2440_iis.ko
insmod /alsa/driver/myalsa/platform/s3c2440_dma.ko
insmod /alsa/driver/myalsa/codec/uda1341.ko
insmod /alsa/driver/myalsa/machine/s3c2440_uda1341.ko
mkdir /dev/snd
cd /dev/snd/
ln -s /dev/controlC0
ln -s /dev/pcmC0D0p
ln -s /dev/pcmC0D0c
cd /
為了方便,把以上這些語句放到prepare.sh里:
#!/bin/sh
#insmod alsa/driver/myalsa/platform/s3c2440_iis.ko
#insmod alsa/driver/myalsa/platform/s3c2440_dma.ko #這樣寫的話,執行時會報錯:can’t insert s3c2440_iis.ko,invalid parameter c2440_iis.ko,原因待查
#insmod alsa/driver/myalsa/codec/uda1341.ko #同上
#insmod alsa/driver/myalsa/machine/s3c2440_uda1341.ko #同上
mkdir -p /dev/snd && cd /dev/snd
cd /alsa/driver/myalsa/platform/ #得按照這種套路寫才行?
insmod s3c2440_iis.ko
insmod s3c2440_dma.ko
cd /alsa/driver/myalsa/codec/
insmod uda1341.ko
#insmod wm8976.ko #在JZ2440開發板上,codec驅動用wm8976.ko
cd /alsa/driver/myalsa/machine/
insmod s3c2440_uda1341.ko
cd /dev/snd && ln -s /dev/controlC0 && ln -s /dev/pcmC0D0p && ln -s /dev/pcmC0D0c
cd /
執行prepare.sh,報錯:
soc-audio: coherent DMA mask is unset
asoc: platform pcm constructor failed
asoc: can’t create pcm 100ask_UDA1341:-12
解決辦法:
參考內核自帶的soundsocsamsungdma.c 的dma_new,在我們的s3c2440_dma.c的s3c2440_dma_new()中添加:
if (!card->dev->dma_mask)
card->dev->dma_mask = &dma_mask;
if (!card->dev->coherent_dma_mask)
card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
5. aplay windows.wav后kernel oops了:
LR指向snd_pcm_info,PC指向0
猜測是在snd_pcm_info里調用了某個不存在的子函數,推測是在這里出錯:substream->ops->ioctl(substream, SNDRV_PCM_IOCTL1_INFO, info);
在《第2課第1.1_17節_ALSA聲卡05_ASoC驅動框架》里,曾分析過這個ioctl的賦值是在soc_new_pcm里:
soc_pcm_ops->ioctl = platform->driver->ops->ioctl;
遂查看s3c2440_dma_ops,發現確實沒有ioctl,遂參考內核的soundsocsamsungdma.c,加上 .ioctl = snd_pcm_lib_ioctl,
6. 再次aplay windows.wav,又kernel oops:
pc is at copy_from_user
backtrace:
snd_pcm_lib_write_transfer
copy_from_user
查snd_pcm_lib_write_transfer:
...
else {
char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))
return -EFAULT;
}
而runtime->dma_area的賦值是在:
(sound/soc/samsung/dma.c)
dma_hw_params
snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); //substream->dma_buffer是在dma_new里分配的
struct snd_pcm_runtime *runtime = substream->runtime;
runtime->dma_area = bufp->area;
查我們的s3c2440_dma_new,發現確實沒有提供這個信息給內核,遂加上:
buf->area = playback_dma_info.virt_addr;
buf->bytes = playback_dma_info.buf_max_size;
7. 重新編譯安裝驅動,再次aplay windows.wav,這次沒有oops,但報:aplay: pcm_write:1939:write error: Input/output error
aplay windows.wav & 讓它后臺運行
cat /proc/interrupts
發現中斷一次都沒有發生,難辦了?!
8. 用devmem2來查看相關寄存器有沒有被正確的設置:
注:我在實驗中沒有用二期課程中的寄存器編輯器ker_wr和regeditor,因為在編譯ker_rw的時候,報了一些錯誤,比如:
error: asm/arch/regs-gpio.h: No such file or directory
error: implicit declaration of function 'class_device_create'
原因應該是原代碼是針對2.6的內核編譯的,和3.4.2內核不兼容。修改了代碼之后,雖然編譯通過,而且也能運行,但讀不出寄存器的值,原因待查。
/ # devmem2 0x4B000080
/dev/mem opened.
Memory mapped at address 0xb6f9a000.
Value at address 0x4B000080 (0xb6f9a080): 0x33B00000 //讀DMA2_BASE_ADDR寄存器, 結果是有值的
/ # devmem2 0x55000000
/dev/mem opened.
Memory mapped at address 0xb6f50000.
Value at address 0x55000000 (0xb6f50000): 0x100 //讀IISCON寄存器,結果都是0 (bit8的1是默認值,代表Left/Right channel index (Read only))
原因是:s3c2440的IIS的時鐘沒有使能?(排查思路是怎樣的?是憑經驗么?)
解決辦法:在s3c2440_iis_init()里加入:
clk = clk_get(NULL, 'iis');
clk_enable(clk);
clk_put(clk);
疑問:為什么裸板程序沒有問題?
答案:因為裸板程序開始運行時,板子的時鐘都是默認使能的,而linux啟動后,為了省電,默認都是關閉的!
9. 重新編譯安裝驅動,再次aplay windows.wav,仍然沒有聲音,
cat /proc/interrupts
中斷還是沒有發生
/ # devmem2 0x55000000
/dev/mem opened.
Memory mapped at address 0xb6fd8000.
Value at address 0x55000000 (0xb6fd8000): 0x1A2 //讀IISCON寄存器,bit0(iis_start)=0,表明iis傳輸沒有啟動
解決辦法:
s3c2440_iis.c加入s3c2440_i2s_trigger、s3c2440_iis_start、s3c2440_iis_stop
10. 重新編譯安裝驅動,再次aplay windows.wav,這次終于有聲音了!
最后一個改進:
雖然能播放聲音,但會有周期性雜音。視頻略去排查過程,直接上結論:load_dma_period()里,要把dma_regs->dcon的bit22設為1:noreload
不知道排查思路是怎樣的?
推測是由于period_size比較小,導致音頻數據被分割成了多塊來傳輸,并且由于dcon的bit[22] =0即autoreload模式,所以每次傳完一個period的數據后,dma會自動將src、dst、TC的值加載到CURR_SRC、CURR_DST、CURR_TC,并開始一次新的DMA傳輸(所以如果新的數據還來不及加載到CURR_SRC,則導致相當于重復播放上個period的數據)。而這之后才調用了DMA中斷服務(硬件總是比軟件快),進而load_dma_period()加載下一個period的數據到DMA的CURR_SRC。因此出現了播放時有周期性的雜音。
如果把s3c2440_dma_hardware修改一下(僅用于實驗),使其能在一個period里播完windows.wav,則也能消除周期性的雜音:
// .buffer_bytes_max = 128*1024,
// .period_bytes_min = PAGE_SIZE,
// .period_bytes_max = PAGE_SIZE*2,
.buffer_bytes_max = 1024*1024,
.period_bytes_min = 512*1024,
.period_bytes_max = 512*1024,
另外,如果用內核自帶的驅動,則不會出現上述問題。經初步分析,因為它利用了dcon的autoload功能,并且設計了一個比較復雜的框架,使得當一個period正在傳輸時,下一個period準備就緒。這樣就能平滑的播放出聲音。
三、參考資料
1. 韋東山 嵌入式Linux視頻教程_3期項目實戰之ALSA聲卡:第2課第1.1_17節_ALSA聲卡11_從零編寫之調試
2. 李蘭溪 S3C24XX DMA框架源碼分析
3. rushzengjianmei Alsa period_size/periods/buffer_size計算邏輯
上一篇:ALSA聲卡_從零編寫之數據傳輸(基于優龍FS2410開發板,UDA1341聲卡)
下一篇:ALSA聲卡_從零編寫之添加錄音功能(基于優龍FS2410開發板,UDA1341聲卡)
推薦閱讀最新更新時間:2025-05-23 05:10



設計資源 培訓 開發板 精華推薦
- NCP110AFCT105T2GEVB:NCP110 WLCSP4 評估板
- AD9200SSOP-EVAL,用于評估 AD9200 完整 10 位、20 MSPS A/D 轉換器的評估板
- 基于DA14580_ADXL362原理圖(小米手環方案)
- 用于儀表的 2.7 至 11V 儀表放大器
- 可調臺燈
- 針對交流性能進行優化的 18 位、250 kSPS 數據采集系統
- 200W Natural Interleaving 轉換模式 PFC 反激 LED 驅動器參考設計
- DC2732A,LTC2949 電流、電壓和演示板高壓電池組的充電監控器
- LT1185CT 折返式電流調節器限制的典型應用電路
- AD8615AUJZ-R2單電源緩沖DAC輸出運算放大器典型應用電路