PYNQ開發闆上使用USB聲霸卡+OSS相容層播放音頻需要經過聯網裝庫,編譯驅動子產品,運作三個步驟。
聯網裝庫
首先需要将PYNQ開發闆連上網,才能安裝所需的庫。方法是在電腦上設定共享網絡,但要注意大部分作業系統在共享網絡的時候無法自己指定本機IP,是以需要修改PYNQ開發闆的IP來适應電腦自動設定的IP。設定共享網絡的方法網上教程有很多,這裡不再贅述。
設定完共享網絡後,注意不要關閉開發闆,因為電腦的IP已經改了,如果重新開機開發闆網關不對SSH就連不上了。這時需要檢視電腦的ip(注意是有線區域網路下的ip),Windows下使用
ipconfig
,Linux下使用
ifconfig
。如我的Ubuntu在有線區域網路下的ip是10.42.0.1,由于網關是255.255.255.0,是以PYNQ開發闆的IP必須設定為10.42.0.*。在我的環境下,設定IP的方法是編輯
/etc/network/interfaces.d/eth0
,然後将其中的address這一行的IP位址修改為10.42.0.2。
然後禁用再啟用一下電腦的有線網功能并重新開機開發闆,嘗試能否連進去,連進去以後再嘗試ping外網,都可以才算成功。接下來就可以裝庫了,執行:
sudo apt update
sudo apt install alsa-base alsa-utils alsa-oss bc
編譯驅動子產品
下載下傳核心源代碼,我PYNQ上的Linux核心版本為5.4.0,可以在xilinx的linux核心倉庫下載下傳,其他版本的核心源碼也可以在該倉庫的其他分支下載下傳。
下載下傳并傳到開發闆上後,執行:
unzip linux-xlnx-xlnx_rebase_v5.4_2020.1.zip
解壓結束後進入該檔案夾,然後需要将目前系統的核心配置檔案複制過來,執行:
sudo cp /lib/modules/$(uname -r)/build/.config .
配置需要編譯的驅動子產品,執行:
make menuconfig
進入
Device Drivers
->
Sound card support
Advanced Linux Sound Architecture
USB sound devices
,光标移至
USB Audio/MIDI driver
,按M鍵選擇編譯此子產品,儲存然後一直按Esc鍵退出。執行以下指令:
make prepare
make -C . M=sound
編譯驅動子產品,生成擴充名為ko的子產品檔案,将它們複制到核心檔案夾中:
sudo mkdir -p /lib/modules/$(uname -r)/kernel/sound/core
sudo mkdir -p /lib/modules/$(uname -r)/kernel/sound/usb
sudo cp sound/core/*.ko /lib/modules/$(uname -r)/kernel/sound/core
sudo cp sound/usb/*.ko /lib/modules/$(uname -r)/kernel/sound/usb
然後安裝這些子產品:
sudo depmod
之後插上USB聲霸卡,驅動子產品應該會自動加載,可以用指令
lsmod
檢視子產品是否被成功加載,在我的系統上被加載的有以下4個子產品:
Module Size Used by
snd_usb_audio 167936 0
snd_hwdep 16384 1 snd_usb_audio
snd_usbmidi_lib 24576 1 snd_usb_audio
snd_rawmidi 24576 1 snd_usbmidi_lib
如果沒有自動加載,可以手動加載:
sudo modprobe snd-usb-audio
此時可以檢視聲霸卡是否已被識别:
sudo aplay -l
如果輸出中出現card 1,則需要配置一下聲霸卡編号,因為alsa音頻庫預設使用的聲霸卡是0号聲霸卡,而USB聲霸卡被配置設定的編号是1,是以需要修改alsa的配置檔案,路徑為
/usr/share/alsa/alsa.conf
,在裡面找到
defaults.pcm.card 0
這一行,把0改成1。
這時候應該可以播放音樂了:
sudo aplay test.wav
運作
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/soundcard.h>
int main() {
// 打開裝置檔案
int fd = open("/dev/dsp", O_RDWR);
if (fd < 0) {
perror("open error\n"); return 1;
}
// test.wav為16位,雙聲道,采樣率為20000的波形檔案
int bit = 16, rate = 20000, channel = 2;
ioctl(fd, SOUND_PCM_WRITE_BITS, &bit);
ioctl(fd, SOUND_PCM_WRITE_RATE, &rate);
ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &channel);
/*打開音樂檔案*/
int fp = open("test.wav", O_RDONLY);
if(fp < 0){
perror("open fp error!\n"); return 1;
}
/*求檔案的大小*/
//這裡直接給出了,要計算可以用fstat函數或fseek+ftell函數
int len = 800180;
char *buf = (char *)malloc(len);
memset(buf, 0, len);
// 讀檔案到buf中
int rd = read(fp, buf, len);
if(rd < 0){
perror("read wav error!\n"); return 1;
}
close(fp);
// 把buf寫到裝置檔案中
int wr = write(fd, buf, len);
if(wr < 0){
perror("write dsp error!\n"); return 1;
}
free(buf); buf = NULL;
return 0;
}
sudo LD_PRELOAD=libaoss.so ./audio