Blackfin DSP(八):1D DMA与音频处理模板
1.DMA产生的背景
在许多需要使用DSP 的场合,一般都需要大量的数据搬移工作,而如果每次数据搬移都由DSP 内核来参与完成,将大大占用DSP 内核的处理时间,从而严重影响其信号处理能力。因此,Blackfin DSP 集成了直接访问(DMA)控制器来完成数据搬移这种简单却耗时的工作。它可以直接进行数据搬移而不需要内核的参与。
说说我对DMA的理解:其实我觉得DMA不算难,反而十分便利,将它想象成城市供水局,为了给城市中不同的小区供水,最原始的办法就是挨家挨户的去送,这就耗费了大量的供水局(DSP内核)的人力物力,而当我们建成了自来水管道(DMA通道),一端连接到自来水厂(数据源),一端连接到需要供水的小区(目的地),当需要供水时阀门一开(DMA_ENABLE),自来水(数据)就源源不断的在管道中流动,再也不需要其它人员的参与了,是不是很方便???下面就看看DSP中的这条管道是如何建立的。
2.BF533的DMA总线结构图如下:
从图中可以看出,DMA可以在各种存储器与外设之间直接进行数据传输。
3. DMA的种类
DMA分为基于寄存器的DMA和基于描述符的DMA:
1)基于寄存器的DMA
允许用户直接对DMA寄存器进行编程,当DMA完成时,由配置寄存器中特定的位来决定接下来的动作:是重装初始值,还是自动停止。
2)基于描述符的DMA
这种DMA要求我们先将所要设置的寄存器值存储在内存单元中,这组参数就叫做描述符,然后配置Current Descriptor Pointer寄存器和NEXT_DESC_PTR寄存器指向这组描述符,当DMA开始时,内核会自动控制将这组描述符按顺序载入到DMA控制寄存器中,从而完成对DMA的初始化。
当然,描述符的排列顺序是有要求的,根据不同的排列顺序就衍生出了以下几种描述符的排列方式:Array mode,small mode和Large model,直接从图就可以看出他们的区别,就不再用语言描述了。
在本音频处理模板中,采用的是直接配置寄存器的方法。
4.如何配置寄存器
对于不同的传输模式,需要配置的寄存器也不同。每种模式至少需要配置以下寄存器:
以下例程将DMA与SPORT相连,接收SPORT传递来的数据,同时也通过SPORT将数据发送出去:
//--------------------------------------------------------------------------//
// Function: Init_DMA //
// //
// Description: Initialize DMA1 in autobuffer mode to receive and DMA2 in //
// autobuffer mode to transmit //
//--------------------------------------------------------------------------//
void Init_DMA(void)
{
// Set up DMA1 to receive
// Map DMA1 to Sport0 RX
*pDMA1_PERIPHERAL_MAP = 0x1000; // Configure DMA1
// 32-bit transfers, Interrupt on completion, Autobuffer mode
*pDMA1_CONFIG = WNR | WDSIZE_32 | DI_EN | 0x1000;
// Start address of data buffer
*pDMA1_START_ADDR = (void *)iRxBuffer1;
// DMA inner loop count
*pDMA1_X_COUNT = ;
// Inner loop address increment
*pDMA1_X_MODIFY = ; // Set up DMA2 to transmit
// Map DMA2 to Sport0 TX
*pDMA2_PERIPHERAL_MAP = 0x2000; // Configure DMA2
// 32-bit transfers, Autobuffer mode
*pDMA2_CONFIG = WDSIZE_32 | 0x1000;
// Start address of data buffer
*pDMA2_START_ADDR = (void *)iTxBuffer1;
// DMA inner loop count
*pDMA2_X_COUNT = ;
// Inner loop address increment
*pDMA2_X_MODIFY = ;
}
这里最需要说明的就是COUNT和MODIFY的值。COUNT指定了每次要读取/发送的element的数量,这个element就是CONFIG寄存器中的WDSIZE_32,也就是说,当COUNT=4时,我们共要传输的数据为4个32位字。因此需要的缓冲的大小为4×32 = 4×sizeof(int),由此可以推断程序中iTxBuffer1的定义为:
int iTxBuffer1[];
而MODIFY的值指出,当每次COUNT值减1时,也就是每次传输完一个elements时,指针移动的字节数,注意,是字节数!此时MODIFY的值至少为4,否则就会发生覆盖现象。示意图如下:
当传输完毕后,由于设置的是Autobuffer 模式,当这4个elements传输结束后,参数寄存器会自动重载当前寄存器的值,0延时的重新开始下一次传输。因此DMA的开启和停止都需要手动来完成:
//--------------------------------------------------------------------------//
// Function: Enable_DMA_Sport //
// //
// Description: Enable DMA1, DMA2, Sport0 TX and Sport0 RX //
//--------------------------------------------------------------------------//
void Enable_DMA_Sport0(void)
{
// enable DMAs
*pDMA2_CONFIG = (*pDMA2_CONFIG | DMAEN);
*pDMA1_CONFIG = (*pDMA1_CONFIG | DMAEN); // enable Sport0 TX and RX
*pSPORT0_TCR1 = (*pSPORT0_TCR1 | TSPEN);
*pSPORT0_RCR1 = (*pSPORT0_RCR1 | RSPEN);
}
以上就是DMA的设置过程,下面趁热打铁,利用上述设置,介绍音频处理模板。
<<<<<<<<<<<<<<<<<<<<<<<<< 分隔符 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
本模板使用ezkit-533,通过SPI接口将AD1836配置为I2S模式,然后利用SPORT0口接收数据的采样值,并通过DMA传输给DSP,DSP处理后,再通过SPORT0口传回DAC,输出波形。配置的过程如下:
1)EBIU初始化,见Blackfin DSP(三):BF533 的EBIU接口之flash和Blackfin DSP(四):BF533 EBIU之SDRAM;
2)SPI初始化,见Blackfin DSP(五):BF533的SPI接口
3)SPORT0初始化,见Blackfin DSP(六):BF533的SPORT接口
4)DMA setup;
5)配置系统中断;
void main(void)
{ Set_PLL(,); //初始化PLL
Init_EBIU(); //初始化EBIU,与flash接口
Init_SDRAM(); //初始化SDRAM
ezConfigureFlashA(); //配置板载flash的IO口方向,主要为了引脚复位AD1836 Init1836();
Init_Sport0();
Init_DMA();
Init_Interrupts();
Enable_DMA_Sport0(); while(); }
对于AD1836的配置如下:
// names for codec registers, used for sCodec1836TxRegs[]
#define DAC_CONTROL_1 0x0000
#define DAC_CONTROL_2 0x1000
#define DAC_VOLUME_0 0x2000
#define DAC_VOLUME_1 0x3000
#define DAC_VOLUME_2 0x4000
#define DAC_VOLUME_3 0x5000
#define DAC_VOLUME_4 0x6000
#define DAC_VOLUME_5 0x7000
#define ADC_0_PEAK_LEVEL 0x8000
#define ADC_1_PEAK_LEVEL 0x9000
#define ADC_2_PEAK_LEVEL 0xA000
#define ADC_3_PEAK_LEVEL 0xB000
#define ADC_CONTROL_1 0xC000
#define ADC_CONTROL_2 0xD000
#define ADC_CONTROL_3 0xE000 // names for slots in ad1836 audio frame
#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 volatile short sCodec1836TxRegs[CODEC_1836_REGS_LENGTH] =
{
DAC_CONTROL_1 | 0x000,
DAC_CONTROL_2 | 0x000,
DAC_VOLUME_0 | 0x3ff,
DAC_VOLUME_1 | 0x3ff,
DAC_VOLUME_2 | 0x3ff,
DAC_VOLUME_3 | 0x3ff,
DAC_VOLUME_4 | 0x000,
DAC_VOLUME_5 | 0x000,
ADC_CONTROL_1 | 0x000,
ADC_CONTROL_2 | 0x000,
ADC_CONTROL_3 | 0x000 }; void Init1836(void)
{
int i;
int j;
static unsigned char ucActive_LED = 0x01; // write to Port A to reset AD1836
*pFlashA_PortA_Out = 0x00; // write to Port A to enable AD1836
*pFlashA_PortA_Out = ucActive_LED; // wait to recover from reset
for (i=; i<0xf0000; i++) asm("nop;"); // Enable PF4
*pSPI_FLG = FLS4;
// Set baud rate SCK = HCLK/(2*SPIBAUD) SCK = 2MHz
*pSPI_BAUD = ;
// configure spi port
// SPI DMA write, 16-bit data, MSB first, SPI Master
*pSPI_CTL = 0x0003 | SIZE | MSTR; // Set up DMA5 to transmit
// Map DMA5 to SPI
*pDMA5_PERIPHERAL_MAP = 0x5000; // Configure DMA5
// 16-bit transfers
*pDMA5_CONFIG = WDSIZE_16;
// Start address of data buffer
*pDMA5_START_ADDR = (void *)sCodec1836TxRegs;
// DMA inner loop count
*pDMA5_X_COUNT = CODEC_1836_REGS_LENGTH;
// Inner loop address increment
*pDMA5_X_MODIFY = ; // enable DMAs
*pDMA5_CONFIG = (*pDMA5_CONFIG | DMAEN);
// enable spi
*pSPI_CTL = (*pSPI_CTL | SPE); // wait until dma transfers for spi are finished
for (j=; j<0xaff0; j++) asm("nop;"); // disable spi
*pSPI_CTL = 0x0000;
}
SPORT0的初始化:
//--------------------------------------------------------------------------//
// Function: Init_Sport0 //
// //
// Description: Configure Sport0 for I2S mode, to transmit/receive data //
// to/from the AD1836. Configure Sport for external clocks and //
// frame syncs. //
//--------------------------------------------------------------------------//
void Init_Sport0(void)
{
// Sport0 receive configuration
// External CLK, External Frame sync, MSB first, Active Low
// 24-bit data, Stereo frame sync enable
*pSPORT0_RCR1 = RFSR | RCKFE;
*pSPORT0_RCR2 = 0x0017 | RXSE | RSFSE; // Sport0 transmit configuration
// External CLK, External Frame sync, MSB first, Active Low
// 24-bit data, Secondary side enable, Stereo frame sync enable
*pSPORT0_TCR1 = TFSR | TCKFE;
*pSPORT0_TCR2 = 0x0017 | TXSE | TSFSE;
}
每一次DMA传输完毕后,都会进入中断函数,调用Process_data()进行数据处理,系统中断配置:
//--------------------------------------------------------------------------//
// Function: Init_Interrupts //
// //
// Description: Initialize Interrupt for Sport0 RX //
//--------------------------------------------------------------------------//
void Init_Interrupts(void)
{
// Set Sport0 RX (DMA1) interrupt priority to 2 = IVG9
*pSIC_IAR0 = 0xffffffff;
*pSIC_IAR1 = 0xffffff2f;
*pSIC_IAR2 = 0xffffffff; // assign ISRs to interrupt vectors
// Sport0 RX ISR -> IVG 9
register_handler(ik_ivg9, Sport0_RX_ISR); // enable Sport0 RX interrupt
*pSIC_IMASK = 0x00000200;
} //--------------------------------------------------------------------------//
// Function: Sport0_RX_ISR //
// //
// Description: This ISR is executed after a complete frame of input data //
// has been received. The new samples are stored in //
// iChannel0LeftIn, iChannel0RightIn, iChannel1LeftIn and //
// iChannel1RightIn respectively. Then the function //
// Process_Data() is called in which user code can be executed.//
// After that the processed values are copied from the //
// variables iChannel0LeftOut, iChannel0RightOut, //
// iChannel1LeftOut and iChannel1RightOut into the dma //
// transmit buffer. //
//--------------------------------------------------------------------------//
EX_INTERRUPT_HANDLER(Sport0_RX_ISR)
{
// confirm interrupt handling
*pDMA1_IRQ_STATUS = 0x0001; // copy input data from dma input buffer into variables
iChannel0LeftIn = iRxBuffer1[INTERNAL_ADC_L0];
iChannel0RightIn = iRxBuffer1[INTERNAL_ADC_R0];
iChannel1LeftIn = iRxBuffer1[INTERNAL_ADC_L1];
iChannel1RightIn = iRxBuffer1[INTERNAL_ADC_R1]; // call function that contains user code
Process_Data(); // copy processed data from variables into dma output buffer
iTxBuffer1[INTERNAL_DAC_L0] = iChannel0LeftOut;
iTxBuffer1[INTERNAL_DAC_R0] = iChannel0RightOut;
iTxBuffer1[INTERNAL_DAC_L1] = iChannel1LeftOut;
iTxBuffer1[INTERNAL_DAC_R1] = iChannel1RightOut;
}
这里的Process_Data()可以按照自己的需求添加信号处理函数,下面代码只是将连续的256个int32_t类型的音频采样点数据保存到数组中,输入和输出都只取了一个通道,即通道0的右声道。
int32_t audio_data[];
uint16_t cnt=; void Process_Data(void)
{
cnt %= ;
if(cnt<)
{
audio_data[cnt] = (iChannel0RightIn<<);
cnt ++ ;
} //iChannel0LeftOut = iChannel0LeftIn;
iChannel0RightOut = iChannel0RightIn; //原样输出
//iChannel1LeftOut = iChannel1LeftIn;
//iChannel1RightOut = iChannel1RightIn;
}
注意这里的 (iChannel0RightIn<<8); 这是因为AD1836的数据是24位的,符号位在第24位上,而我们的数组是int32的,所以需要左移8位将符号位放置到最高位上。
用IDE的plot功能将audio_data[256]显示出来,结果如下:
如果继续相对数据进行其他处理比如FFT,做一个“if(cnt==256) FFT_func(audio_data[256]);” 运算即可;
FFT变换结果:
Blackfin DSP(八):1D DMA与音频处理模板的更多相关文章
- Blackfin DSP(八):BF533的DMA
#include <cdefBF533.h> #include <sys\exception.h> #define POLC 0x00004000 #define PORT_C ...
- Blackfin DSP(二):寄存器操作与GPIO
BlackfinDSP的寄存器是通过指针操作的,与51.ARM等MCU一样,通过“或”操作来置1,通过“与”操作清零. 在DSP上最简单的外设非IO口莫属,但是由于其功能强大,远非一般IO口可比,因此 ...
- Blackfin DSP(五):BF533的SPI接口
533SPI的特性 最高速度可达SCLK/4: 支持主模式和从模式: 可使用8个GPIO口作为从选择线: 1 slave select input pins 7 slave select output ...
- Blackfin DSP(三):BF533 的EBIU接口之flash
上一节谈了GPIO问题,是用BF561 ezkit进行说明的,这是因为561 ezkit上的GPIO是与LED直连的,讲解GPIO时不会涉及到其它问题,降低了复杂性.对于533,也采取同样的操作即可. ...
- Blackfin DSP(四):BF533 EBIU之SDRAM
BF533的SDRAM控制器最大支持128M bytes的SDRAM空间:总线宽度可以配置为4位.8位.16位.处理器与SDRAM的连线包括数据总线D[0:15].地址总线A[1:19].SDRAM刷 ...
- Blackfin DSP(六):BF533的SPORT接口
1.特性 bf533有两个SPORT口(synchronous serial Port),即同步串行接口.完全独立的接收和发送通道,且每个通道都具有缓冲,最高速度可达SCLK/2.最大支持32bit字 ...
- Blackfin DSP(七):用SPORT口模拟SPI
1.问题的提出 当系统从SPI device启动时,若SCLK = 133M,则SPI的最大速度为33M.然而,有一些串行的flash设备能更快的运行,因此,如果我们使用SPORT口,它的最大速度为S ...
- 第八十七天请假 PHP smarty模板配置以及简单的调用方式
smarty模板的配置文件 <?php define("ROOT",str_replace("\\","/",dirname(__FI ...
- Android智能手机上的音频浅析
手机可以说是现在人日常生活中最离不开的电子设备了.它自诞生以来,从模拟的发展到数字的,从1G发展到目前的4G以及不久将来的5G,从最初的只有唯一的功能(打电话)发展到目前的全功能,从功能机(featu ...
随机推荐
- Less函数说明
索引 escape(@string); // 通过 URL-encoding 编码字符串 e(@string); // 对字符串转义 %(@string, values...); // 格式化字符串 ...
- POSTGRES与JDBC对照
POSTGRES与JDBC对照 未经验证,仅供参考.
- MySQL中MyISAM和InnoDB的区别
MyISAM和InnoDB的区别 MySQL默认采用的是MyISAM. MyISAM不支持事务,而InnoDB支持.InnoDB的AUTOCOMMIT默认是打开的,即每条SQL语句会默认被封装成一个事 ...
- 【转载】Ansys中的阻尼
原文地址:http://www.cnblogs.com/ylhome/archive/2009/08/26/1554195.html ANSYS动力学分析中提供了各种的阻尼形式,这些阻尼在分析中是如何 ...
- Vim找不到配色文件的解决方法
Vim新出了8.0,又成功的勾起了我的好奇心. 重新从零开始配置,结果第一步设置配色主题就没过,好丢人-- 提示找不到evening.vim配色文件,于是上网查了一下,有说改环境变量的,又说改这个改那 ...
- 实现DevExpress GridControl 只有鼠标双击后才进行修改数据
1. 实现DevExpress GridControl 只有鼠标双击后才进行修改数据:修改GridView.OptionsBehavior.EditorShowMode属性为Click 2. 实现De ...
- linq语法大全(转集)
Join操作符 适用场景:在我们表关系中有一对一关系,一对多关系,多对多关系等.对各个表之间的关系,就用这些实现对多个表的操作. 说明:在Join操作中,分别为Join(Join查询), Selec ...
- php lock_sh共享锁 与 lock_ex排他锁
参考网站:http://hi.baidu.com/honly1215/item/8d27a66d11689c3aac3e83fe 文件锁有两种:共享锁和排他锁,也就是读锁(LOCK_SH)和写锁(LO ...
- 如何在IDEA 中使用Git
1,下载最新的 git 包 地址: https://git-scm.com/download/win 下载便携版 64,32 根据个人爱好 2,解压后随便放个位置即可,例如图: (不太建议使用它自 ...
- spring retry 使用
1. 场景 系统方法调用时无状态的,同时因为网络原因,或者系统暂时故障,进行的重试 2. maven 依赖 <project xmlns="http://maven.apa ...