声卡wm9714l的驱动(基于6410)

2019-04-14 16:06发布

typedef volatile unsigned int u32;
typedef volatile unsigned short u16;

#define GPDCON           *(u32 *)0x7F008060
#define AC_GLBCTRL     *(u32 *)0x7F001000
#define AC_GLBSTAT     *(u32 *)0x7F001004
#define AC_CODEC_CMD *(u32 *)0x7F001008
#define AC_CODEC_STAT *(u32 *)0x7F00100C
#define AC_PCMDATA    *(u32 *)0x7F001018
#define MUSIC_ADDR        0x54000000  //定义音乐编码存放的内存地址
#define LEN                    44100*2*2*20    

void (*printf)(char *,...)=(void *)0x57e11df8;


void delay(int count);
u16  read_mw9714l_cmd(char addr); //读值
void write_mw9714l_cmd(char addr,u16 data);//写值
void wm9714l_init();

void _start(){
    //配置相关引脚寄存器,也就是WM9714L引出来的连在cpu上的
    //先看底板原理图,在看核心板原理图,可以看到对应的是gpd
    GPDCON = 0x44444;//设置为AC97使用

    //配置声卡启动,这个很关键
    //1,cold reset
    //A cold reset is generated when the nRESET pin is asserted through the AC_GLBCTRL. Asserting anddeasserting nRESET activates BITCLK and SDATA_OUT. All AC97 control registers are initialized to their default power on reset values. nRESET is an asynchronous AC97 input
    //查看wm9714l芯片手册的cold reset的时序(大约14页)
    //配置寄存器AC_GLBCTRL的第一位
    AC_GLBCTRL = 1;
    delay(1000);
    AC_GLBCTRL &= ~1;//也许是上升沿有效吧!!
    delay(1000);

    //2.warm reset
    //A Warm AC97 reset reactivates the AC-link without altering the current AC97 register values.
    //A warm reset is generated when BITCLK is absent and SYNC is driven high. In normal audio frames, SYNC is a synchronous
    //     AC97 input. When BITCLK is absent, SYNC is treated as an asynchronous input used to generate a warm reset
    //     to AC97.The AC97 Controller must not activate BITCLK until it samples SYNC low again. This prevents a new audio frame from being falsely detected
    //查看wm9714l芯片手册的warm reset的时序(大约14页)
    //配置寄存器AC_GLBCTRL
    AC_GLBCTRL |= (1 << 2)|(1 << 1);//打开AC-Link on,让BITCLK开启(见14页)
    delay(1000);
    AC_GLBCTRL &= ~(1 << 1);
    delay(1000);

    AC_GLBCTRL |= 1 << 3;//Transfer data enable using AC-link
    //检查Controller main state是否工作(如果不active,后面的操作就没意义了)
    
    //在这里需要读一下,否则后面的操作就会失败,有时人品好没问题就过去了
    //原理见读函数
    AC_CODEC_CMD = (1 << 23)|(0x04 << 16);
    delay(1000);
    //printf("%p ",AC_CODEC_STAT);

    //AC_GLBSTAT的前三位
    if((AC_GLBSTAT & 7) != 3){
        printf("AC-link is not active!! ");
        return;
    }
    //printf("go==%p ",AC_GLBSTAT);

/////////////////////////////////////////////////////////////////////////

    //好吧!做了这么多其实就是打通了ac97总线和wm9714l的联系
    //那下面就可以利用读的命令看看wm9714l的相关情况了,这个就不做了,感兴趣的可以结合wm9714l手册的80页进行查看
    //当然我们的目标是配置wm9714l,让他工作来发声音,所以下面的操作是结合手册来配置wm9714l,
    //总图在18页,配置寄存器在81-110页之间,我们接下来都是在这两点之前配置
    //由于wm9714l的功能很多,但我们需要的只是配置ac97和耳机,其他一概不问
    //封转一个函数吧

    wm9714l_init();

    //配置好了,就需要检验了吧!于是在内存中下载一段音乐,并把他由ac97直接噴给wm9714l,就可以听到音乐了
    //采用查询方式
    int num;
    while(1){
        if(AC_GLBSTAT & (1 << 18)){
            AC_GLBCTRL |= 1 << 26;
            AC_PCMDATA = *(u32 *)(MUSIC_ADDR+num*4);
            num++;    
        }
    }

    return ;
}

//为了方便,我们再次封装一个读wm9714l的寄存器的值的函数
//这里使用两个寄存器AC_CODEC_STAT和AC_CODEC_CMD
//具体使用参见大约1153页的手册上,很简单的
u16 read_mw9714l_cmd(char addr){
    AC_CODEC_CMD = (1 << 23)|(addr << 16);
    delay(1000);
    return AC_CODEC_STAT & 0xffff;
}

//有了读,那就有写的功能了,具体操作同读差不多,都是那两个寄存器
void write_mw9714l_cmd(char addr,u16 data){
    AC_CODEC_CMD = (0 << 23)|(addr << 16)|data;
    delay(1000);
}

//初始化wm9714l(注:这只是个简单的,要考虑音效什么的就需专业的了)
void wm9714l_init(){

    write_mw9714l_cmd(0x04, (1 << 14)|(0xa << 8)|(1 << 6)|0xa); //no mute, max vol
   write_mw9714l_cmd(0x0c, 0); //dac no mute, max vol
    write_mw9714l_cmd(0x1c, (2 << 6)|(2 << 4)); //hpl/hpr source sel
    //0x1e  3d
    //0x20  tone, bass
    //0x24  jack control
    write_mw9714l_cmd(0x26, 0); //power
    write_mw9714l_cmd(0x2a, 1); //variable rate control
    write_mw9714l_cmd(0x2c, 0xac44);  //441000
    write_mw9714l_cmd(0x3c, 0); //power
    write_mw9714l_cmd(0x3e, 0); //power
    //0x40  3D enhance
    //0x5a  jack gpio detect
}

//延时函数
void delay(int count){
    count *= 100;
    while(count--)
         ;
}