S3C2416裸機(jī)開發(fā)系列十八_音頻驅(qū)動實(shí)現(xiàn)(1)
在消費(fèi)電子產(chǎn)品中,往往都會用到音頻系統(tǒng)來播放音樂、進(jìn)行通話等多媒體應(yīng)用,此外,對于一些需語音提示的產(chǎn)品,音頻部分都是不可或缺的功能。筆者此處就s3c2416的音頻驅(qū)動實(shí)現(xiàn)作一個簡單的介紹。
1. IIS音頻總線s3c2416支持IIS、PCM、AC97這三種音頻接口,此處只分析IIS音頻接口。IIS接口(Inter-IC Sound)在20世紀(jì)80年代首先被飛利浦公司用于消費(fèi)音頻,為數(shù)字音頻設(shè)備之間的音頻數(shù)據(jù)傳輸而制定的一種總線標(biāo)準(zhǔn)。IIS有以下三個主要的信號:
1) 串行時鐘SCLK,也叫做位時鐘(BCLK)。數(shù)字音頻的每一位均需對應(yīng)一個SCLK脈沖,因此位時鐘頻率應(yīng)大于等于2*采樣頻率*采樣位數(shù)。乘以2表示每個采樣會產(chǎn)生左聲道和右聲道的數(shù)據(jù)。
2) 幀時鐘LRCK,也叫做左右聲道切換時鐘(WS)。LRCK為1時表示傳輸?shù)氖怯衣暤赖臄?shù)據(jù),為0時表示傳輸?shù)氖亲舐暤赖臄?shù)據(jù),因此IIS是非常適合于立體聲系統(tǒng)的。LRCK是一個占空比約50%的方波,這個頻率是需要盡可能與采樣頻率一致的,不然無法體現(xiàn)原來的音頻本質(zhì)。
3) 串行數(shù)據(jù)SDATA,用二進(jìn)制補(bǔ)碼表示音頻數(shù)據(jù)。在串行時鐘SCLK脈沖下,數(shù)據(jù)一位一位出現(xiàn)在SDATA線上。對于具體的IIS主機(jī)或設(shè)備,為支持全雙工(例如通話時需同時支持放音與錄音),串行數(shù)據(jù)線分串行輸入SDI和串行輸出SDO這兩根。SDI用來傳輸采樣設(shè)備數(shù)字化后的錄音數(shù)據(jù),SDO用來傳輸需播放的音頻數(shù)據(jù)。
有時為了IIS主控制與IIS設(shè)備能夠更好的同步,還需要傳輸一路時鐘信號MCLK,也叫做主時鐘。主要用于IIS設(shè)備A/D、D/A采樣時的采樣時鐘,一般是采樣頻率的256倍、384倍、512倍、768倍。在滿足要求的條件下,應(yīng)盡可能選用較低的主時鐘。
2. WM8960音頻編解碼器WM8960是歐勝微電子推出的一款低功耗、高質(zhì)量的立體編碼解碼器。該芯片內(nèi)置有麥克風(fēng)接口、立體聲耳機(jī)驅(qū)動器以及D類立體聲揚(yáng)聲器驅(qū)動,24比特模數(shù)轉(zhuǎn)換器(ADC)和數(shù)模轉(zhuǎn)換器(DAC)。
WM8960具有三對左右聲道的模擬輸入,其中INPUT1專用于Mic輸入,支持單端或差分的Mic信號接入。這個輸入具有一個程控放大器(PGA),并且可用自動電平控制(ALC)對Mic信號進(jìn)行增益放大。其它的INPUT2、3可做為Mic差分接入的同相輸入或線輸入。
WM8960具有一對左右聲道的耳機(jī)輸出,16歐負(fù)載時,輸出40mW。一對D類左右聲道揚(yáng)聲器輸出,每聲道8歐負(fù)載,在1W輸出功率時,具有87%的效率。一路左右聲道混合輸出。
3. WM8960驅(qū)動編寫聲音是模擬信號,cpu是不能處理模擬信號的,并且認(rèn)為模擬信號也是不具有傳輸性的。因此音頻編解碼器至少具有三個主要功能部分:模數(shù)轉(zhuǎn)換器(ADC)、數(shù)模轉(zhuǎn)換器(DAC)、程控放大器(PGA)。ADC用來采樣外部的模擬聲音信號(如Mic錄音),進(jìn)行離散化后,轉(zhuǎn)換成數(shù)字音頻,通過音頻總線(如IIS)傳輸給cpu,cpu再對數(shù)字音頻進(jìn)行處理,如調(diào)頻、混合、存儲等。DAC用來把從cpu過來的數(shù)字音頻信號還原成原來的模擬聲音信號,DAC轉(zhuǎn)換后的離散化PCM調(diào)制信號再通過濾波器真實(shí)還原出原來的模擬聲音。PGA可在各個階段對音頻信號進(jìn)行可編程的增益放大,例如音量的控制(可參考WM8960_HeadphoneVolume()函數(shù)),Mic靈敏度的調(diào)節(jié)(可參考WM8960_RecorderVolume()函數(shù))等。
WM8960在使用前必須進(jìn)行初始化,即需配置音頻接口IIS的參數(shù)(可參考WM8960_Init()的實(shí)現(xiàn)),若進(jìn)行錄音,需配置錄音路徑的上電、接通,并進(jìn)行增益的設(shè)定(具體見WM8960_RecorderStart()函數(shù)的實(shí)現(xiàn))。若進(jìn)行放音,需配置是耳機(jī)、揚(yáng)聲器等的話音路徑,進(jìn)行增益設(shè)定(可參考WM8960_HeadphoneStart()函數(shù)的實(shí)現(xiàn))。IIS是音頻接口,只能傳輸音頻信號,因此WM8960還需另外的IIC接口,通過IIC總線寫寄存器對這些配置進(jìn)行設(shè)定。IIC驅(qū)動編寫在前面的章節(jié)有詳細(xì)的介紹,此處不再細(xì)說,WM8960模塊驅(qū)動WM8960.c如下:
#include "IIC.h"
#include "WM8960.h"
#define VolumeLevel 7
static int RecorderVolume;
static int HeadphoneVolume;
// WM8960寄存器不能通過IIC讀,開辟緩存記錄寄存器的變化
static unsigned short WM8960_Reg[56] = {
0x0097, 0x0097, 0x0000, 0x0000,
0x0000, 0x0008, 0x0000, 0x000a,
0x01c0, 0x0000, 0x00ff, 0x00ff,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x007b, 0x0100, 0x0032,
0x0000, 0x00c3, 0x00c3, 0x01c0,
0x0000, 0x0000, 0x0000, 0x0000,
0x0000, 0x0000, 0x0000, 0x0000,
0x0100, 0x0100, 0x0050, 0x0050,
0x0050, 0x0050, 0x0000, 0x0000,
0x0000, 0x0000, 0x0040, 0x0000,
0x0000, 0x0050, 0x0050, 0x0000,
0x0002, 0x0037, 0x004d, 0x0080,
0x0008, 0x0031, 0x0026, 0x00e9,
};
static void WM8960_WriteReg(unsigned char RegAddr, unsigned short Value)
{
unsigned char Data;
unsigned char Addr;
// WM8960只有7位的寄存器地址,外加寄存器值第8位,構(gòu)成8位數(shù)據(jù)
Addr = (RegAddr<<1) |((Value>>8) & 0x1);
// WM8960有9位的寄存器值,最高位與寄存器地址一齊發(fā)送
Data = (unsigned char)Value; // 低8位寄存器值
IIC_WriteBytes(WM8960_SlaveAddr,Addr, &Data, 1);
WM8960_Reg[RegAddr] = Value; // 寫成功后更新寄存器的值
}
static unsigned short WM8960_ReadReg(unsigned char RegAddr)
{
return WM8960_Reg[RegAddr]; // 返換緩存的WM8960寄存器的值(9位)
}
unsigned char WM8960_HeadphoneVolume(unsigned char Control)
{
// -10db ~ 6db (0x6f ~ 0x7f)
unsigned char Level;
if (Control == VolumeDown) { // 耳機(jī)音量減
if ((0x7f-0x6f)/VolumeLevel ==0) {
HeadphoneVolume--;
} else {
HeadphoneVolume -=(0x7f-0x6f)/VolumeLevel;
}
if (HeadphoneVolume < 0) {
HeadphoneVolume = 0;
}
} else {// 耳機(jī)音量加
if ((0x7f-0x6f)/VolumeLevel ==0) {
HeadphoneVolume++;
} else {
HeadphoneVolume +=(0x7f-0x6f)/VolumeLevel;
}
if (HeadphoneVolume >VolumeLevel) {
HeadphoneVolume =VolumeLevel;
}
}
if (HeadphoneVolume == 0) {
Level = 0; // 靜音
} else {
Level =((0x7f-0x6f)*HeadphoneVolume)/VolumeLevel + 0x6f;
}
// Headphone Volume Updata
WM8960_WriteReg(0x02,(1<<8)|Level);
WM8960_WriteReg(0x03,(1<<8)|Level);
return ((HeadphoneVolume*100)/VolumeLevel);// 返回音量百分比
}
void WM8960_HeadphoneStop()
{
unsigned short RegValue;
RegValue = WM8960_ReadReg(0x1a);
RegValue =~((1<<8)|(1<<7)|(1<<6)|(1<<5));
WM8960_WriteReg(0x1a, RegValue);
}
void WM8960_HeadphoneStart()
{
unsigned short RegValue;
// DAC Left/Right,LOUT1/ROUT1Output Buffer Power up
WM8960_WriteReg(0x1a, 0x01e0);
// Left DAC Digital Volume -28db
WM8960_WriteReg(0x0a, 0x01c5);
// Right DAC Digital Volume -28db
WM8960_WriteReg(0x0b, 0x01c5);
// DAC Digital No mute
WM8960_WriteReg(0x05, 0x0000);
// Left DAC to Left Output Mixer
WM8960_WriteReg(0x22, 0x0100);
// Right DAC to Left Output Mixer
WM8960_WriteReg(0x25, 0x0100);
// Left/Right Output Mixer Enable
RegValue = WM8960_ReadReg(0x2f);
RegValue |= (1<<2) |(1<<3);
WM8960_WriteReg(0x2f, RegValue);
}
unsigned char WM8960_RecorderVolume(unsigned char Control)
{
// -10db ~ 10db (0xaf ~ 0xd7)
unsigned char Level;
if (Control == VolumeDown) { //Mic靈敏度調(diào)低
if ((0xd7-0xaf)/VolumeLevel ==0) {
RecorderVolume--;
} else {
RecorderVolume -=(0xd7-0xaf)/VolumeLevel;
}
if (RecorderVolume < 0) {
RecorderVolume = 0;
}