硬件准备
ADSP-EDU-BF533:BF533开发板
AD-HP530ICE:ADI DSP仿真器
软件准备
Visual DSP++软件
硬件链接
很早期做的设计,用的是TI的一颗音频codec,如果要放到今天,我肯定是用ADI的SigmaDSP了,ADI所有的SigmaDSP我都用过,非常熟。
硬件实现原理
音频是采用 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 模式采样。
硬件连接示意图
初始化配置
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 中断功能打开。
代码实现功能
- bf53x_audio_talkthrough代码:
代码实现了一个音频输入播放的功能,将一个声源通过 LINEIN 接口(蓝色)输入,将一音响或耳机连接在HPOUT 接口(绿色),运行代码后,音响或耳机中能听到输入声源的声音。输入声源的音量不要太大,否则会产生
削波现象,导致听到的输出声音有爆破音。
- bf53x_audio_mic
代码实现了 MIC 输入播放的功能,将一音响或耳机连接在 HPOUT 接口(绿色),运行代码后,用手机或其他播放器对准板上 MIC 接口播放音乐,在音响或耳机中能听到输入声源的声音。
代码实现原理
- bf53x_audio_talkthrough
代码通过 IIC 初始化完 TLV320AIC23B 后,TLV320AIC23B 开始通过 LINEIN 接口采集模拟音频数据,并将采集到的数据通过 ADSP-BF53x 的 SPORT 口传送给 ADSP-BF53x,ADSP-BF53x 将数据做内存交换后,再通过 SPORT口传送给 TLV320AIC23B,TLV320AIC23B 将数据转为模拟信号后通过 HPOUT 接口输出。
- bf53x_audio_mic
代码通过 IIC 初始化完 TLV320AIC23B 后,TLV320AIC23B 开始通过板上 MIC 采集模拟音频数据,并将采集到的数据通过 ADSP-BF53x 的 SPORT 口传送给 ADSP-BF53x,ADSP-BF53x 将数据做内存交换后,再通过 SPORT口传送给 TLV320AIC23B,TLV320AIC23B 将数据转为模拟信号后通过 HPOUT 接口输出。
调试步骤
- 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,编译并全速运行。
- 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 采集到的音乐。
程序源码
- bf53x_audio_talkthrough
aic23b_init.c
#include <cdefBF533.h>
#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 <cdefBF533.h>
/**********************************************************************************
- 名称 :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<tem;i++)
asm(“nop;”);
}
I2C.c
#include <cdefBF533.h>
#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<<index) & 0x80){
SET_PF(dev->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 <cdefBF533.h>
#include <sys\exception.h>
#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;
}