ADI Blackfin DSP处理器-BF533的开发详解17:IIS+AUDIO音频codec的应用(含源代码)

硬件准备

ADSP-EDU-BF533:BF533开发板
AD-HP530ICE:ADI DSP仿真器

软件准备

Visual DSP++软件

硬件链接

很早期做的设计,用的是TI的一颗音频codec,如果要放到今天,我肯定是用ADI的SigmaDSP了,ADI所有的SigmaDSP我都用过,非常熟。

硬件设计原理图

ADI Blackfin DSP处理器-BF533的开发详解17:IIS+AUDIO音频codec的应用(含源代码)_第1张图片

硬件实现原理

音频是采用 TI 公司的 TLV320AIC23B 音频 Codec 芯片,TLV320AIC23B 支持 1 路 MICIN,1 路 LINEIN, 1 路OUT,1 路 HPOUT。硬件设计中将 1 路 LINEIN 和 1 路 HPOUT 通过接口引出,板载 1 个 MIC,连接芯片的 MICIN
接口。

BF533 处理器的 SPORT 接口支持 IIS 协和和 TDM 协议,可直接与 TLV320AIC23B 的接口连接。通过 BF53x处理器的 PF0 和 PF1 接口分别模拟 IIC 的 SCL 和 SDA 总线,用来初始化 TLV320AIC23B 芯片。

TLV320AIC23B 支持多种音频采样格式,硬件设计中为其提供的时钟为 12MHz,可参考 TLV320AIC23B 数据手册将其配置为 USB 模式采样。

ADI Blackfin DSP处理器-BF533的开发详解17:IIS+AUDIO音频codec的应用(含源代码)_第2张图片

硬件连接示意图

ADI Blackfin DSP处理器-BF533的开发详解17:IIS+AUDIO音频codec的应用(含源代码)_第3张图片

初始化配置

TLV320AIC23B 的器件地址可通过接口上的 CS 引脚进行选择,如下表:在这里插入图片描述

硬件中,将 CS 管脚接地,所以其使用的器件地址是:写器件:0x34 读器件:0x35

TLV320AIC23B 需要通过 IIC 接口配置初始化,所以需要通过配置板卡上的 CPLD 寄存器,将 PF0 和 PF1 配置为 IIC 总线模式,该配置映射在 CPLD 的 DEVICE_OE 寄存器,其配置功能为:

DEVICE_OE 寄存器(读/写):
DEVICE_OE 寄存器地址:0x20320000
DEVICE_OE 寄存器设置硬件设备上一些控制管脚的电平状态。
DEVICE_OE 寄存器位功能:

在这里插入图片描述

PF0_SET:PF0 模拟 IIC 总线 SCL 接口或 PF0 中断功能使能

1:关闭 I2C_SCL 输入信号, 使能 PF0 中断信号
0:使能 I2C_SCL 输入信号,关闭 PF0 中断信号

使用 IIC 配置 TLV320AIC23B 时,需将 PF0_SET 位设置为 0,关闭中断,待 IIC 配置完成后,再将中断 PF0_SET位设置为 1,将 PF0 中断功能打开。

代码实现功能

  1. bf53x_audio_talkthrough代码:

代码实现了一个音频输入播放的功能,将一个声源通过 LINEIN 接口(蓝色)输入,将一音响或耳机连接在HPOUT 接口(绿色),运行代码后,音响或耳机中能听到输入声源的声音。输入声源的音量不要太大,否则会产生
削波现象,导致听到的输出声音有爆破音。

  1. bf53x_audio_mic

代码实现了 MIC 输入播放的功能,将一音响或耳机连接在 HPOUT 接口(绿色),运行代码后,用手机或其他播放器对准板上 MIC 接口播放音乐,在音响或耳机中能听到输入声源的声音。

代码实现原理

  1. bf53x_audio_talkthrough

代码通过 IIC 初始化完 TLV320AIC23B 后,TLV320AIC23B 开始通过 LINEIN 接口采集模拟音频数据,并将采集到的数据通过 ADSP-BF53x 的 SPORT 口传送给 ADSP-BF53x,ADSP-BF53x 将数据做内存交换后,再通过 SPORT口传送给 TLV320AIC23B,TLV320AIC23B 将数据转为模拟信号后通过 HPOUT 接口输出。

  1. bf53x_audio_mic

代码通过 IIC 初始化完 TLV320AIC23B 后,TLV320AIC23B 开始通过板上 MIC 采集模拟音频数据,并将采集到的数据通过 ADSP-BF53x 的 SPORT 口传送给 ADSP-BF53x,ADSP-BF53x 将数据做内存交换后,再通过 SPORT口传送给 TLV320AIC23B,TLV320AIC23B 将数据转为模拟信号后通过 HPOUT 接口输出。

调试步骤

  1. bf53x_audio_talkthrough

1)将仿真器(ICE)与 ADSP-EDU-BF53x 开发板和计算机连接好。
2)将正在播放的声源通过标准音频连接线接入开发板的 Lin IN 接口(蓝色接口)接入,音箱或耳机连接 HPOUT接口(绿色接口)。
3)先给 ADSP-EDU-BF53x 开发板上电,再为仿真器(ICE)上电。
4)运行 VisualDSP++ 5.0 软件,选择合适的 BF533 的 session 将仿真器与软件连接。
5)加载 VisualDSP++ 5.0 工程文件 BF53x_AUDIO_TALKTHROUGH.dpj,编译并全速运行。

  1. bf53x_audio_mic

1)将仿真器(ICE)与 ADSP-EDU-BF53x 开发板和计算机连接好。
2)将正在播放的声源通过标准音频连接线接入开发板的 Lin IN 接口(蓝色接口)接入,音箱或耳机连接 HPOUT接口(绿色接口)。
3)先给 ADSP-EDU-BF53x 开发板上电,再为仿真器(ICE)上电。
4)运行 VisualDSP++ 5.0 软件,选择合适的 BF533 的 session 将仿真器与软件连接。
5)加载 VisualDSP++ 5.0 工程文件 BF53x_AUDIO_MIC.dpj,编译并全速运行。

调试结果

bf53x_audio_talkthrough

在音响或耳机中可以听到输入声源的声音。

bf53x_audio_mic

用手机或其他播放器播放音乐,对准板上 MIC,可从耳机中听到 MIC 采集到的音乐。

程序源码

  1. bf53x_audio_talkthrough

aic23b_init.c

#include
#include “i2c.h”
#include “aic23b.h”

#define AIC23B_ADDRESS 0x34
static i2c_device mcu_i2c;

void init_aic23b(void);
int aic23b_write(unsigned char addr, unsigned char dat);
int aic23b_read(unsigned char addr, unsigned char * buf);

unsigned short AudioConfig[] = {
PWDC, 0x000FF,
LLVC, 0x1d,
RLVC, 0x1d,
LHVC, 0x70,
RHVC, 0x70,
AAPC, DAC,
DAPC, 0,
PWDC, 0,
DAIF, MS|FOR|LRP,
SARC, 0,
DIAC, ACT|RES };

/****************************************************************************

  • 名称 :aic23b_write
  • 功能 : 写aic23b寄存器函数
  • 入口参数 :addr:寄存器偏移地址
    dat:寄存器配置值
  • 出口参数 :返回0
    ****************************************************************************/
    int aic23b_write(unsigned char addr, unsigned char dat)
    {
    int ret = -1;
    i2c_start(&mcu_i2c);
    if(i2c_write(&mcu_i2c, AIC23B_ADDRESS, 1)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    if(i2c_write(&mcu_i2c, addr, 1)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    i2c_write(&mcu_i2c, dat, 1);
    i2c_stop(&mcu_i2c);
    return 0;
    }

/****************************************************************************

  • 名称 :aic23b_read
  • 功能 : 读aic23b寄存器函数
  • 入口参数 :addr:寄存器偏移地址
    buf:寄存器读取数据缓存
  • 出口参数 :返回0
    ****************************************************************************/
    int aic23b_read(unsigned char addr, unsigned char * buf)
    {
    unsigned char *p = buf;
    int ret = -1;
    i2c_start(&mcu_i2c);
    //send slave address
    if(i2c_write(&mcu_i2c, AIC23B_ADDRESS, 1)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    //send sub-address of slave
    if(i2c_write(&mcu_i2c, addr, 1)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    i2c_stop(&mcu_i2c);
    i2c_start(&mcu_i2c);
    // send slave address (+1 read mode)
    if(i2c_write(&mcu_i2c, AIC23B_ADDRESS+1, 1)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    if(i2c_wait_slave(&mcu_i2c, 1000)){
    i2c_stop(&mcu_i2c);
    return ret;
    }
    i2c_read(&mcu_i2c, p++, 1); // send ack
    i2c_stop(&mcu_i2c);
    return 0;
    }

/****************************************************************************

  • 名称 :Init_aic23b
  • 功能 : 音频模块的内部初始化
  • 入口参数 :无
  • 出口参数 :无
    ****************************************************************************/

void init_aic23b(void)
{
unsigned char i;

mcu_i2c.sclk = PF0;         //时钟PF脚
mcu_i2c.sdata = PF1;        //数据PF脚
mcu_i2c.low_ns = 7000;      //低电平延时 ns
mcu_i2c.high_ns = 6000;     //高电平延时 ns	   
       
i2c_init(&mcu_i2c);	
	aic23b_write(PWDC,0x000FF);   
	aic23b_write(LLVC,0x1d);    
aic23b_write(RLVC,0x1d);    
aic23b_write(LHVC,0xff);   
aic23b_write(RHVC,0xff);   
	aic23b_write(AAPC,DAC);
	aic23b_write(DAPC,0);    
aic23b_write(PWDC,0);
	aic23b_write(DAIF,MS|FOR|LRP);   
	aic23b_write(SARC,0);        
	aic23b_write(DIAC,ACT|RES);

}

cpu.c

#include
/**********************************************************************************

  • 名称 :Set_PLL
  • 功能 :初始化内核时钟和系统时钟
  • 入口参数 :pmsel pssel 设置参数
  • 出口参数 :无
    ***********/
    void Set_PLL(int pmsel,int pssel)
    {
    int new_PLL_CTL;
    pPLL_DIV = pssel;
    asm(“ssync;”);
    new_PLL_CTL = (pmsel & 0x3f) << 9;
    pSIC_IWR |= 0xffffffff;
    if (new_PLL_CTL != pPLL_CTL)
    {
    pPLL_CTL = new_PLL_CTL;
    asm(“ssync;”);
    asm(“idle;”);
    }
    }
    /
  • 名称 :Init_EBIU
  • 功能 :初始化并允许异步BANK存储器工作
  • 入口参数 :无
  • 出口参数 :无
    ****************************************************************************/

void Init_EBIU(void)
{
*pEBIU_AMBCTL0 = 0x7bb07bb0;
*pEBIU_AMBCTL1 = 0xffc07bb0;
*pEBIU_AMGCTL = 0x000f;
}

/****************************************************************************

  • 名称 :Init_SDRAM
  • 功能 :初始化SDRAM
  • 入口参数 :无
  • 出口参数 :无
    ****************************************************************************/
    void Init_SDRAM(void)
    {
    *pEBIU_SDRRC = 0x00000817;
    *pEBIU_SDBCTL = 0x00000013;
    *pEBIU_SDGCTL = 0x0091998d;
    ssync();
    }

/****************************************************************************

  • 名称 : delay
  • 功能 : 延时函数
  • 入口参数 :无
  • 返回值 :无
    ****************************************************************************/
    void delay(unsigned int tem)
    {
    int i;
    for(i=0;i asm(“nop;”);
    }

I2C.c

#include
#include “i2c.h”

#define CORE_CLK_IN 24 * 1000 * 1000

#define SET_PF(pf)
do{
*pFIO_FLAG_S = (pf);
ssync();
}while(0)

#define CLR_PF(pf)
do{
*pFIO_FLAG_C = (pf);
ssync();
}while(0)

#define SET_PF_OUTPUT(pf)
do{
*pFIO_INEN &= ~(pf);
*pFIO_DIR |= (pf);
ssync();
}while(0)

#define SET_PF_INPUT(pf)
do{
*pFIO_DIR &= ~(pf);
*pFIO_INEN |= (pf);
ssync();
}while(0)

int get_core_clk(void)
{
int tempPLLCTL;
int _DF;
int VCO;
int MSEL1;

tempPLLCTL = *pPLL_CTL;


MSEL1 = ((tempPLLCTL & 0x7E00) >> 9);
_DF   =  tempPLLCTL & 0x0001;

VCO  = MSEL1 * __CORE_CLK_IN__;
if(_DF == 1)
	VCO /= 2;

return  VCO;

}
void delay_ns(unsigned int core_clock, unsigned long long count)
{
count *= core_clock;
count /= 1000000000;
while(count–);

}

int _get_sdata(i2c_device * dev)
{
return ((*pFIO_FLAG_D & dev->sdata) ? 1 : 0);
}

void i2c_init(i2c_device * dev)
{
dev->core_clock = get_core_clk();
dev->delay_ns = delay_ns;
*pFIO_DIR |= dev->sclk | dev->sdata;
ssync();
}

void i2c_deinit(i2c_device * dev)
{
dev->sclk = 0;
dev->sdata = 0;

*pFIO_DIR &=  ~(dev->sclk | dev->sdata);
ssync();

}

void i2c_start(i2c_device * dev)
{
SET_PF_OUTPUT(dev->sdata);
SET_PF_OUTPUT(dev->sclk);

SET_PF(dev->sdata);
SET_PF(dev->sclk);
delay_ns(dev->core_clock, dev->high_ns);

CLR_PF(dev->sdata);
delay_ns(dev->core_clock, dev->low_ns);

CLR_PF(dev->sclk);
delay_ns(dev->core_clock, dev->low_ns);

}

void i2c_stop(i2c_device * dev)
{
CLR_PF(dev->sclk);
delay_ns(dev->core_clock, dev->low_ns);

SET_PF_OUTPUT(dev->sdata);
CLR_PF(dev->sdata);
delay_ns(dev->core_clock, dev->low_ns);

SET_PF_INPUT(dev->sclk);
delay_ns(dev->core_clock, dev->high_ns);

SET_PF_INPUT(dev->sdata);
delay_ns(dev->core_clock, dev->high_ns);

}

int i2c_read_ack(i2c_device * dev)
{
int ret = 0;

SET_PF_INPUT(dev->sdata);
delay_ns(dev->core_clock, dev->high_ns/3);

SET_PF(dev->sclk);
delay_ns(dev->core_clock, dev->high_ns/3);

ret = _get_sdata(dev);

delay_ns(dev->core_clock, dev->high_ns/3);
CLR_PF(dev->sclk);

delay_ns(dev->core_clock, dev->low_ns);

SET_PF_OUTPUT(dev->sdata);
return ret;

}

int i2c_wait_slave(i2c_device * dev, unsigned int time_out)
{
int ret;
int count = time_out * 2 / dev->high_ns;

SET_PF_INPUT(dev->sclk);
delay_ns(dev->core_clock, dev->high_ns/2);

do{
	ret = *pFIO_FLAG_D & dev->sclk;
	if(ret)
	   break;
	delay_ns(dev->core_clock, dev->high_ns/2);
}while(count--);

SET_PF_OUTPUT(dev->sclk);
return !ret;

}

void i2c_write_ack(i2c_device * dev)
{
SET_PF_OUTPUT(dev->sdata);
CLR_PF(dev->sdata);
delay_ns(dev->core_clock, dev->high_ns/2);
SET_PF(dev->sclk);
delay_ns(dev->core_clock, dev->high_ns);

CLR_PF(dev->sclk);
delay_ns(dev->core_clock, dev->low_ns);

}

int i2c_write(i2c_device * dev, unsigned char value, int need_ack)
{
int ret = -1;
unsigned char index;

SET_PF_OUTPUT(dev->sdata);

//send 8 bits to slave
for(index = 0; index < 8; index++){
	//send one bit to the i2c bus
	if((value<sdata);
	} else {
		CLR_PF(dev->sdata);
	}
	
	delay_ns(dev->core_clock, dev->low_ns/2);
	
	SET_PF(dev->sclk);
	delay_ns(dev->core_clock, dev->high_ns);
	
	CLR_PF(dev->sclk);
	delay_ns(dev->core_clock, dev->low_ns/2);
}

if(need_ack){
	ret = i2c_read_ack(dev);
}
return ret;

}

int i2c_read(i2c_device * dev, unsigned char * value, int send_ack)
{
unsigned char index;
*value = 0x00;

SET_PF_INPUT(dev->sdata);
delay_ns(dev->core_clock, dev->high_ns/2);

//get 8 bits from the device
for(index = 0; index < 8; index++){
	SET_PF(dev->sclk);
	delay_ns(dev->core_clock, dev->high_ns/2);
	
	*value <<= 1;
	*value |= _get_sdata(dev);
	
	delay_ns(dev->core_clock, dev->high_ns/2);
	
	CLR_PF(dev->sclk);
	delay_ns(dev->core_clock, dev->low_ns);
}

// send ack to slave
if(send_ack){
	i2c_write_ack(dev);
}
return *value;

}

main.c

void main()
{
Set_PLL(16,4);
Init_EBIU();
Init_SDRAM();
IIC_Enable();

init_aic23b();
Init_Interrupts();
Init_Sport0();
Init_DMA();
Enable_DMA_Sport0();

while(1); 

}

talkthrough.c

#include
#include

#define TIMOD_DMA_TX 0x0003 // SPI transfer mode
#define SLEN_32 0x001f // SPORT0 word length
#define FLOW_1 0x1000 // DMA flow mode

#define INTERNAL_ADC_L0 0
#define INTERNAL_ADC_R0 2
#define INTERNAL_DAC_L0 0
#define INTERNAL_DAC_R0 2
#define INTERNAL_ADC_L1 1
#define INTERNAL_ADC_R1 3
#define INTERNAL_DAC_L1 1
#define INTERNAL_DAC_R1 3

EX_INTERRUPT_HANDLER(Sport0_RX_ISR);

int iChannel0LeftIn, iChannel1LeftIn;
int iChannel0RightIn, iChannel1RightIn;
int iChannel0LeftOut, iChannel1LeftOut;
int iChannel0RightOut, iChannel1RightOut;

volatile int iTxBuffer1[4];
volatile int iRxBuffer1[4];
/****************************************************************************

  • 名称 :Init_Timers
  • 功能 :初始化TIMER0 为PWM模式。
  • 入口参数 :无
  • 出口参数 :无
    ****************************************************************************/
    void Init_Timers(void)
    {
    *pTIMER0_CONFIG = 0x0019;
    *pTIMER0_PERIOD = 0x00800000;
    *pTIMER0_WIDTH = 0x00400000;
    *pTIMER_ENABLE = 0x0001;
    }

void Init_Sport0(void)
{
*pSPORT0_RCR1 = RFSR | RCKFE;
*pSPORT0_RCR2 = RSFSE |SLEN_32;

*pSPORT0_TCR1 = TFSR| LATFS | TCKFE;
*pSPORT0_TCR2 = TSFSE | SLEN_32;	

}

void Init_DMA(void)
{
*pDMA1_PERIPHERAL_MAP = 0x1000;
*pDMA1_CONFIG = WNR | WDSIZE_32 | DI_EN | FLOW_1;
*pDMA1_START_ADDR = (void *)iRxBuffer1;
*pDMA1_X_COUNT = 4;
*pDMA1_X_MODIFY = 4;

*pDMA2_PERIPHERAL_MAP = 0x2000;
*pDMA2_CONFIG = WDSIZE_32 | FLOW_1;
*pDMA2_START_ADDR = (void *)iTxBuffer1;
*pDMA2_X_COUNT = 4;
*pDMA2_X_MODIFY = 4;

}

void Enable_DMA_Sport0(void)
{
*pDMA2_CONFIG = (*pDMA2_CONFIG | DMAEN);
*pDMA1_CONFIG = (*pDMA1_CONFIG | DMAEN);

*pSPORT0_TCR1 	= (*pSPORT0_TCR1 | TSPEN);
*pSPORT0_RCR1 	= (*pSPORT0_RCR1 | RSPEN);

}

void Init_Interrupts(void)
{
*pSIC_IAR0 = 0xffffffff;
*pSIC_IAR1 = 0xffffff2f;
*pSIC_IAR2 = 0xffffffff;

register_handler(ik_ivg9, Sport0_RX_ISR);

*pSIC_IMASK = 0x00000200;

}

void Process_Data(void)
{
iChannel0LeftOut = iChannel0LeftIn;
iChannel0RightOut = iChannel0RightIn;
iChannel1LeftOut = iChannel1LeftIn;
iChannel1RightOut = iChannel1RightIn;
}

EX_INTERRUPT_HANDLER(Sport0_RX_ISR)
{
*pDMA1_IRQ_STATUS = 0x0001;

iChannel0LeftIn = iRxBuffer1[INTERNAL_ADC_L0];
iChannel0RightIn = iRxBuffer1[INTERNAL_ADC_R0];
iChannel1LeftIn = iRxBuffer1[INTERNAL_ADC_L1];
iChannel1RightIn = iRxBuffer1[INTERNAL_ADC_R1];

Process_Data();	
			
iTxBuffer1[INTERNAL_DAC_L0] = iChannel0LeftOut;
iTxBuffer1[INTERNAL_DAC_R0] = iChannel0RightOut;
iTxBuffer1[INTERNAL_DAC_L1] = iChannel1LeftOut;
iTxBuffer1[INTERNAL_DAC_R1] = iChannel1RightOut;

}

你可能感兴趣的:(ADI,DSP资料下载,ADI,DSP技术中心,Blackfin专题,音视频,ADI,DSP,ADI,DSP中文资料)