在消費電子產品中,往往都會用到音頻系統來播放音樂、進行通話等多媒體應用,此外,對于一些需語音提示的產品,音頻部分都是不可或缺的功能。筆者此處就s3c2416的音頻驅動實現作一個簡單的介紹。
1. IIS音頻總線s3c2416支持IIS、PCM、AC97這三種音頻接口,此處只分析IIS音頻接口。IIS接口(Inter-IC Sound)在20世紀80年代首先被飛利浦公司用于消費音頻,為數字音頻設備之間的音頻數據傳輸而制定的一種總線標準。IIS有以下三個主要的信號:
1) 串行時鐘SCLK,也叫做位時鐘(BCLK)。數字音頻的每一位均需對應一個SCLK脈沖,因此位時鐘頻率應大于等于2*采樣頻率*采樣位數。乘以2表示每個采樣會產生左聲道和右聲道的數據。
2) 幀時鐘LRCK,也叫做左右聲道切換時鐘(WS)。LRCK為1時表示傳輸的是右聲道的數據,為0時表示傳輸的是左聲道的數據,因此IIS是非常適合于立體聲系統的。LRCK是一個占空比約50%的方波,這個頻率是需要盡可能與采樣頻率一致的,不然無法體現原來的音頻本質。
3) 串行數據SDATA,用二進制補碼表示音頻數據。在串行時鐘SCLK脈沖下,數據一位一位出現在SDATA線上。對于具體的IIS主機或設備,為支持全雙工(例如通話時需同時支持放音與錄音),串行數據線分串行輸入SDI和串行輸出SDO這兩根。SDI用來傳輸采樣設備數字化后的錄音數據,SDO用來傳輸需播放的音頻數據。
有時為了IIS主控制與IIS設備能夠更好的同步,還需要傳輸一路時鐘信號MCLK,也叫做主時鐘。主要用于IIS設備A/D、D/A采樣時的采樣時鐘,一般是采樣頻率的256倍、384倍、512倍、768倍。在滿足要求的條件下,應盡可能選用較低的主時鐘。
2. WM8960音頻編解碼器WM8960是歐勝微電子推出的一款低功耗、高質量的立體編碼解碼器。該芯片內置有麥克風接口、立體聲耳機驅動器以及D類立體聲揚聲器驅動,24比特模數轉換器(ADC)和數模轉換器(DAC)。
WM8960具有三對左右聲道的模擬輸入,其中INPUT1專用于Mic輸入,支持單端或差分的Mic信號接入。這個輸入具有一個程控放大器(PGA),并且可用自動電平控制(ALC)對Mic信號進行增益放大。其它的INPUT2、3可做為Mic差分接入的同相輸入或線輸入。
WM8960具有一對左右聲道的耳機輸出,16歐負載時,輸出40mW。一對D類左右聲道揚聲器輸出,每聲道8歐負載,在1W輸出功率時,具有87%的效率。一路左右聲道混合輸出。
3. WM8960驅動編寫聲音是模擬信號,cpu是不能處理模擬信號的,并且認為模擬信號也是不具有傳輸性的。因此音頻編解碼器至少具有三個主要功能部分:模數轉換器(ADC)、數模轉換器(DAC)、程控放大器(PGA)。ADC用來采樣外部的模擬聲音信號(如Mic錄音),進行離散化后,轉換成數字音頻,通過音頻總線(如IIS)傳輸給cpu,cpu再對數字音頻進行處理,如調頻、混合、存儲等。DAC用來把從cpu過來的數字音頻信號還原成原來的模擬聲音信號,DAC轉換后的離散化PCM調制信號再通過濾波器真實還原出原來的模擬聲音。PGA可在各個階段對音頻信號進行可編程的增益放大,例如音量的控制(可參考WM8960_HeadphoneVolume()函數),Mic靈敏度的調節(jié)(可參考WM8960_RecorderVolume()函數)等。
WM8960在使用前必須進行初始化,即需配置音頻接口IIS的參數(可參考WM8960_Init()的實現),若進行錄音,需配置錄音路徑的上電、接通,并進行增益的設定(具體見WM8960_RecorderStart()函數的實現)。若進行放音,需配置是耳機、揚聲器等的話音路徑,進行增益設定(可參考WM8960_HeadphoneStart()函數的實現)。IIS是音頻接口,只能傳輸音頻信號,因此WM8960還需另外的IIC接口,通過IIC總線寫寄存器對這些配置進行設定。IIC驅動編寫在前面的章節(jié)有詳細的介紹,此處不再細說,WM8960模塊驅動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位,構成8位數據
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) { // 耳機音量減
if ((0x7f-0x6f)/VolumeLevel ==0) {
HeadphoneVolume--;
} else {
HeadphoneVolume -=(0x7f-0x6f)/VolumeLevel;
}
if (HeadphoneVolume < 0) {
HeadphoneVolume = 0;
}
} else {// 耳機音量加
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靈敏度調低
if ((0xd7-0xaf)/VolumeLevel ==0) {
RecorderVolume--;
} else {
RecorderVolume -=(0xd7-0xaf)/VolumeLevel;
}
if (RecorderVolume < 0) {
RecorderVolume = 0;
}