zoukankan      html  css  js  c++  java
  • (转)CortexM3 (NXP LPC1788)之IIS应用UDA1380进行音频数据播放

    LPC1788发送到I2S总线上的音频数据要通过音频解码芯片才能输出模拟音频信号。开发板上使用的是UDA1380,对它的寄存器的配置可以通过L3总线或者I2C总线进行,这里使用I2C总线进行控制,对于I2C总线的操作可以参考之前I2C的介绍。UDA1380的寄存器主要分成3类,系统控制、插值滤波(interpolation filter)、抽取滤波(decimator filter)。插值滤波和DAC转换有关,用于控制控制声音的输出参数。抽取滤波和ADC有关,用于控制对音频的采样。寄存器的地址和功能如图1所示。

    图1:UDA1380寄存器地址和功能

            根据图1的红色标记中的内容,可以知道两个滤波器的正常使用需要一个128fs的clock,这个时钟可以通过SYSCLK引脚或者WSI的信号获得。在硬件连接上,通过将LPC1788的MCLK输出的时钟,连接到UDA1380的SYSCLK引脚。因此,我们需要配置I2S的发送模式控制寄存器I2STXMODE,使能TX_REF在MCLK输出,使UDA1380内部产生一个滤波器需要的时钟。

            程序中我们通过I2S发送一段音频数据,该数据是我从WAV格式的文件中去掉WAV头格式后得到的一个纯音频数据数组。该WAV音频为16位双通道 采样频率为44.1KHZ。LPC1788将该数组发送到I2S总线,UDA1380读取该数据进行声音的输出。程序如下

     

    1. #include "i2c.h" 
    2. #include "audio.h" 
    3.  
    4. #define rI2SDAO         (*(volatile unsigned *)(0x400A8000)) 
    5. #define rI2STXFIFO      (*(volatile unsigned *)(0x400A8008)) 
    6. #define rI2STXRATE      (*(volatile unsigned *)(0x400A8020)) 
    7. #define rI2STXBITRATE   (*(volatile unsigned *)(0x400A8028)) 
    8. #define rI2STXMODE      (*(volatile unsigned *)(0x400A8030)) 
    9.  
    10. #define rI2SDMA1        (*(volatile unsigned *)(0x400A8014)) 
    11. #define rI2SDMA2        (*(volatile unsigned *)(0x400A8018)) 
    12. #define rI2SSTATE       (*(volatile unsigned *)(0x400A8010)) 
    13. #define rI2SIRQ         (*(volatile unsigned *)(0x400A801C)) 
    14.  
    15. #define rI2SDAI         (*(volatile unsigned *)(0x400A8004)) 
    16. #define rI2SRXFIFO      (*(volatile unsigned *)(0x400A800C)) 
    17. #define rI2SRXRATE      (*(volatile unsigned *)(0x400A8024)) 
    18. #define rI2SRXBITRATE   (*(volatile unsigned *)(0x400A802C)) 
    19. #define rI2SRXMODE      (*(volatile unsigned *)(0x400A8034)) 
    20.  
    21. #define rIOCON_P0_07    (*(volatile unsigned *)(0x4002C01C)) 
    22. #define rIOCON_P0_08    (*(volatile unsigned *)(0x4002C020)) 
    23. #define rIOCON_P0_09    (*(volatile unsigned *)(0x4002C024)) 
    24. #define rIOCON_P1_16    (*(volatile unsigned *)(0x4002C0C0)) 
    25.  
    26. #define UDA1380_ADDRESS 0x1A 
    27.  
    28. void Uda1380_WriteData(unsigned char reg, unsigned shortint data) 
    29.     unsigned char config[3]; 
    30.      
    31.     config[0] = reg; 
    32.     config[1] = (data >> 8) & 0xFF;    //MS 
    33.     config[2] = data&0xFF;             //LS 
    34.      
    35.      
    36.     I2C0_MasterTransfer(UDA1380_ADDRESS, config, sizeof(config), 0, 0); 
    37.      
    38.     I2C0_MasterTransfer(UDA1380_ADDRESS, config, 1, &config[1], 2);     //校验写入的数据是否正确 
    39.     if((config[1]<<8|config[2]) != data) 
    40.     { 
    41.         while(1);   //写入和读出的数据不一致 
    42.     } 
    43.  
    44. void Uda1380_config() 
    45.     I2C0_Init(); 
    46.      
    47.     Uda1380_WriteData(0x7F, 0x0);         //restore L3-default values 
    48.     Uda1380_WriteData(0x01, 0x0);         //数据格式为标准的I2S格式 
    49.      
    50.     Uda1380_WriteData(0x13, 0x0);         //配置音频的输出 
    51.     Uda1380_WriteData(0x14, 0x0);            
    52.  
    53.     Uda1380_WriteData(0x00, 0x2|0x1<<8|0x1<<9);     //使能DAC的时钟,选择使用SYSCLK产生128fs的时钟 
    54.     Uda1380_WriteData(0x02,0x1<<15|0x1<<13|0x1<<10|0x1<<8); //使能DAC 电源 
    55.      
    56.  
    57. int main(void
    58. {    
    59.     unsigned int count=0, i; 
    60.     unsigned char flag=1; 
    61.      
    62.     rIOCON_P0_07 = (rIOCON_P0_07&(~0x3))|0x1;   //I2S_TX_SCK 
    63.     rIOCON_P0_08 = (rIOCON_P0_08&(~0x3))|0x1;   //I2S_TX_WS 
    64.     rIOCON_P0_09 = (rIOCON_P0_09&(~0x3))|0x1;   //I2S_TX_SDA 
    65.     rIOCON_P1_16 = (rIOCON_P1_16&(~0x3))|0x2;   //I2SMCLK 
    66.     rPCONP |= 0x1<<27; 
    67.      
    68.     rI2SDAO = (16 - 1)<<6 | 0x1<<4 | 0x1<<3  |0x1;  //16位, 立体音, 禁止发送 
    69.  
    70.     rI2STXMODE |= 0x1<<3;       //使能MCLK输出,使TX_REF输出到UDA1380的SYSCLK引脚 
    71.      
    72.     rI2STXRATE = 0x1<<8|0x1;    //配置分数速率寄存器 得到TX_REF=CCLK/(1/1)/2 
    73.     rI2STXBITRATE = CCLK/2/(44100*2*16) - 1;  //44.1KHZ采样16位 
    74.      
    75.     for(i = 0; i <0x1000000; i++);  //延时 等待UDA1380内部通过SYSCLK产生稳定的128fs提供插值滤波和抽取滤波使用 
    76.      
    77.     Uda1380_config(); 
    78.      
    79.     rI2SDAO &= ~ (1<<4); 
    80.     rI2SDAO &= ~ (1<<3); 
    81.     rI2SDAO &= ~ (1<<15);   //启动I2S数据传输 
    82.      
    83.     while(flag) 
    84.     { 
    85.         if(((rI2SSTATE>>16)&0xFF)<=4)       //如果发送FIFO中的数据小于或等于4个字 
    86.         { 
    87.             for(i=0; i<8-(((rI2SSTATE>>16)&0xFF)); i++)     //将FIFO填充到8个字 
    88.             { 
    89.                 rI2STXFIFO = *(unsigned int *)(audio + count);  //转换成int类型的指针,从指针指向的位置读取32位数据 
    90.                  
    91.                 count+=4;               //读取一个字,相当于读取char类型数组中的4个元素 
    92.                 
    93.                 if(count>=sizeof(audio)) //数组中的数据发送完 
    94.                 { 
    95.                     flag = 0; 
    96.                     break
    97.                 } 
    98.             } 
    99.         } 
    100.     } 
    101.      
    102.     rI2SDAO |= 0x1<<3|0x1<<4;   //停止I2S传输 
    103.      
    104.     return 0; 
    #include "i2c.h"
    #include "audio.h"
    
    #define rI2SDAO         (*(volatile unsigned *)(0x400A8000))
    #define rI2STXFIFO      (*(volatile unsigned *)(0x400A8008))
    #define rI2STXRATE      (*(volatile unsigned *)(0x400A8020))
    #define rI2STXBITRATE   (*(volatile unsigned *)(0x400A8028))
    #define rI2STXMODE      (*(volatile unsigned *)(0x400A8030))
    
    #define rI2SDMA1        (*(volatile unsigned *)(0x400A8014))
    #define rI2SDMA2        (*(volatile unsigned *)(0x400A8018))
    #define rI2SSTATE       (*(volatile unsigned *)(0x400A8010))
    #define rI2SIRQ         (*(volatile unsigned *)(0x400A801C))
    
    #define rI2SDAI         (*(volatile unsigned *)(0x400A8004))
    #define rI2SRXFIFO      (*(volatile unsigned *)(0x400A800C))
    #define rI2SRXRATE      (*(volatile unsigned *)(0x400A8024))
    #define rI2SRXBITRATE   (*(volatile unsigned *)(0x400A802C))
    #define rI2SRXMODE      (*(volatile unsigned *)(0x400A8034))
    
    #define rIOCON_P0_07	(*(volatile unsigned *)(0x4002C01C))
    #define rIOCON_P0_08	(*(volatile unsigned *)(0x4002C020))
    #define rIOCON_P0_09	(*(volatile unsigned *)(0x4002C024))
    #define rIOCON_P1_16	(*(volatile unsigned *)(0x4002C0C0))
    
    #define UDA1380_ADDRESS 0x1A
    
    void Uda1380_WriteData(unsigned char reg, unsigned short int data)
    {
        unsigned char config[3];
        
        config[0] = reg;
        config[1] = (data >> 8) & 0xFF;    //MS
        config[2] = data&0xFF;             //LS
        
        
        I2C0_MasterTransfer(UDA1380_ADDRESS, config, sizeof(config), 0, 0);
        
        I2C0_MasterTransfer(UDA1380_ADDRESS, config, 1, &config[1], 2);     //校验写入的数据是否正确
        if((config[1]<<8|config[2]) != data)
        {
            while(1);   //写入和读出的数据不一致
        }
    }
    
    void Uda1380_config()
    {
        I2C0_Init();
        
        Uda1380_WriteData(0x7F, 0x0);         //restore L3-default values
        Uda1380_WriteData(0x01, 0x0);         //数据格式为标准的I2S格式
        
        Uda1380_WriteData(0x13, 0x0);         //配置音频的输出
        Uda1380_WriteData(0x14, 0x0);           
    
        Uda1380_WriteData(0x00, 0x2|0x1<<8|0x1<<9);     //使能DAC的时钟,选择使用SYSCLK产生128fs的时钟
        Uda1380_WriteData(0x02,0x1<<15|0x1<<13|0x1<<10|0x1<<8); //使能DAC 电源
        
    }
    
    int main(void)
    {   
        unsigned int count=0, i;
        unsigned char flag=1;
        
        rIOCON_P0_07 = (rIOCON_P0_07&(~0x3))|0x1;   //I2S_TX_SCK
        rIOCON_P0_08 = (rIOCON_P0_08&(~0x3))|0x1;   //I2S_TX_WS
        rIOCON_P0_09 = (rIOCON_P0_09&(~0x3))|0x1;   //I2S_TX_SDA
        rIOCON_P1_16 = (rIOCON_P1_16&(~0x3))|0x2;   //I2SMCLK
        rPCONP |= 0x1<<27;
        
        rI2SDAO = (16 - 1)<<6 | 0x1<<4 | 0x1<<3  |0x1;  //16位, 立体音, 禁止发送
    
        rI2STXMODE |= 0x1<<3;       //使能MCLK输出,使TX_REF输出到UDA1380的SYSCLK引脚
        
        rI2STXRATE = 0x1<<8|0x1;    //配置分数速率寄存器 得到TX_REF=CCLK/(1/1)/2
        rI2STXBITRATE = CCLK/2/(44100*2*16) - 1;  //44.1KHZ采样16位
        
        for(i = 0; i <0x1000000; i++);  //延时 等待UDA1380内部通过SYSCLK产生稳定的128fs提供插值滤波和抽取滤波使用
        
        Uda1380_config();
        
        rI2SDAO &= ~ (1<<4);
        rI2SDAO &= ~ (1<<3);
        rI2SDAO &= ~ (1<<15);   //启动I2S数据传输
        
        while(flag)
        {
            if(((rI2SSTATE>>16)&0xFF)<=4)       //如果发送FIFO中的数据小于或等于4个字
            {
                for(i=0; i<8-(((rI2SSTATE>>16)&0xFF)); i++)     //将FIFO填充到8个字
                {
                    rI2STXFIFO = *(unsigned int *)(audio + count);  //转换成int类型的指针,从指针指向的位置读取32位数据
                    
                    count+=4;               //读取一个字,相当于读取char类型数组中的4个元素
                   
                    if(count>=sizeof(audio)) //数组中的数据发送完
                    {
                        flag = 0;
                        break;
                    }
                }
            }
        }
        
        rI2SDAO |= 0x1<<3|0x1<<4;   //停止I2S传输
        
        return 0;
    }
    

    下面对程序需要注意的做下说明:

    1,i2c.h中是上一篇介绍I2C总线中所用的函数,aduio.h中存放的是音频数据的数组,const unsigned char audio[]={0,0,0,0,0,......................

    2,I2C总线每次发送的数据为1个字节,而UDA1380的寄存器为16为,因此我们先发送高字节然后再发送低字节,具体的时序可以参考UDA1380的数据手册。

    3,程序中配置发送控制寄存器I2STXMODE使能了MCLK输出TX_REF的时钟到UDA1380的SYSCLK引脚,而UDA1380中配置成使用该时钟产生内部滤波器需要的128fs的时钟。在图1中标志中说明run at ....因此在程序中配置UDA1380之前,使用了一个for延时,用于等待UDA1380内部产生稳定的128fs时钟。只有这样才能正确的配置0x10之后的滤波器相关寄存器。否则对0x10之后的滤波器相关寄存器操作会失败。这点没有验证,但是在debug调试的时候可以正常的有声音输出,但是下载到板子上运行,则没有效果。如果去掉for循环延时效果也不正常发音。如果不使能MCLK输出,则写0x13寄存器的值不会成功,读取该寄存器的值永远都是其默认值。因此推测和UDA1380的SYSCLK产生内部滤波器使用的128fs时钟有关。

          参考了linux内核里面的uda1380的驱动,其中也提到了配置0x10以后的滤波器相关寄存器要满足条件

    1. 107         /* the interpolator & decimator regs must only be written when the 
    2. 108          * codec DAI is active. 
    3. 109          */ 
    107         /* the interpolator & decimator regs must only be written when the
    108          * codec DAI is active.
    109          */

    谁有这方面的经验,希望多指教!

  • 相关阅读:
    DevOps实施方法论,为什么大企业一定要使用DevOps?
    SpringCloudAlibaba基础入门,基于Nacos构建分布式与配置,Sentinel服务治理
    艾编程Java进阶架构师必看:15次架构演进详解
    实战笔记:来一起探究下Kafka是如何实现万亿级海量数据的高并发写入的?
    520疯狂之后我彻底蒙了,老板让我做技术选型,数据处理选kafka还是RocketMQ?
    如何实现Redis数据持久化以及内存管理之缓存过期机制
    SpringBoot源码深度解析
    分布式缓存Redis高级应用实战:为什么要用缓存机制
    全面上云实战教程:基于阿里云安装配置部署docker详解
    Solr学习笔记(2)—— solr-7.0.0 安装与目录说明
  • 原文地址:https://www.cnblogs.com/tdyizhen1314/p/2704610.html
Copyright © 2011-2022 走看看