五、解读VisualDSP++案例之21489 AD1939 C Sample-based Talkthru 48 or 96 kHz

记录一下,方便以后翻阅~

一开始以为读懂案例21489 AD1939 C Block-Based Talkthru 48 or 96 kHz,这个案例随便看看也就懂了,实际上里面的差异还是蛮大的。

养成个好习惯,先看下该案例的Readme部分内容
This project contains a talkthrough example using the onboard AD1939 to acquire and output an audio stream. The digital audio data is available for processing in the file SPORT1_ISR_processor_samples_.asm. The block size is 256 samples per audio channel.
百度直译就是:该项目使用板载AD1939采集和输出音频流。数字音频数据在SPORT1_ISR_processor_samples.asm文件中进行处理。块大小为每个音频通道256个采样。
参考解读VisualDSP++案例之21489 AD1939 C Block-Based Talkthru 48 or 96 kHz的文章,那个案例的数字音频数据处理在SPORT1_isr.c.文件里完成,这个一个不同点。
那么.asm是什么格式的文件呢?参考网上资料:ASM是汇编语言源程序的扩展名,汇编语言(Assembly Language)是面向机器的程序设计语言。汇编语言是一种功能很强的程序设计语言,是利用计算机所有硬件特性并能直接控制硬件的语言。
好吧,汇编。。。
在这里插入图片描述
继续阅读Readme
The AD1939 can be set up for 48/96/192 Khz Sampling rate. The ADC is connected to SPORT 0A. DAC1 is connected to SPORT 1A, DAC2 to SPORT 1A, DAC3 to SPORT1A, and DAC4 (Headphone output) to SPORT1A. All channels of the codec are accessed in TDM mode. See initSRU.c for the details of which DAI pins are used to access the codec.
The output on OUT2 is delayed and the user can expect to hear an echo effect.
百度直译就是:AD1939可设置为48/96/192 Khz采样率。ADC连接到SPORT 0A。DAC1,DAC2,DAC3和DAC4(耳机输出)均连接到SPORT 1A。编解码器的所有通道以TDM模式访问。参阅initSRU.c文件了解用于访问编解码器的DAI引脚的详细信息。
也就是说,硬件引脚配置与解读VisualDSP++案例之21489 AD1939 C Block-Based Talkthru 48 or 96 kHz文章的案例一样,这部分知识是一样的。
最后一句说的是OUT2上的输出延迟,用户可以期望听到回声效果。

接下来看下该案例所包含的文件
Source Files contained in this directory:
21489 AD1939 C Sampled-Based Talkthru 48 or 96 kHz.dpj-----------VisualDSP project file
ad1939.h-----------Macro Definitions for AD1939 registers
ADDS_21489_EzKit.h-----------Includes and external declarations used for all files
init1939viaSPI.c-----------ADSP-21489 source - AD1939 SPI Control Port Subroutines
initSRU.c-----------Set up the DAI pins and SRU to connect to the AD1939
initPLL_SDRAM.c-----------Configures core for 400 MHz and enables SDRAM memory
main.c-----------Main section to call setup routines
Process_audio.c-----------Process the multiple audio data streams
SPORT01_TDM_init.c-----------Initialize the SPORT DMA to communicate with the AD1939
SPORT1_ISR_process_samples_.asm-----------Process SPORT 1 interrupts

Dependencies contained in VisualDSP++ default include path:
def21489.h-----------Header file with generic definitions for ADSP-21489
SRU.h-----------Header file with SRU definitions and Macros

对比之前的案例(如下图所示),主要的不同点是:1)本案例SPORT1中断函数位于SPORT1_ISR_process_samples_.asm文件里;2)Process_audio.c文件用来处理多个音频数据流,而之前的案例blockProcess_audio.c文件用来处理音频数据块。
在这里插入图片描述
看下这个案例的Project树状图:
在这里插入图片描述
项目树里多了一个Sinetbl.dat文件,这里存放了一段音频数据,以一维数组格式保存,在SPORT1_ISR_process_samples_.asm文件里的前几行,注释了一行代码:

//#define GENERATE_DAC_PURE_TONES_TEST

如果取消注释,那么程序编译完成运行时将发出一段纯音,案例默认是关闭的。

接下来看下main函数里都有些什么,上代码

/
// (c) Copyright 2009 - Analog Devices, Inc.                                               //
// NAME:     main.c (sample-based Talkthrough)                                             //
// DATE:     02/06/10                                                                      //
// PURPOSE:  Function main() for AD1939/ADSP-21489 Talkthrough framework.                  //
// USAGE:    This file contains the main routine calls functions to set up the talkthrough //
//           routine.                                                                      //
// Author:   VS., Analog Devices, Inc                                                      //
/
#include "ADDS_21489_EzKit.h"
float *DelayLine;
int Index  = 0;
int Sine_Index = 0;
int SPORT1_isr_count = 0;
int SPORT0_isr_count = 0;
void SPORT1_isr_counter(void);
void SPORT0_isr_counter(void);
void main()
{
    
     
    initPLL_SDRAM();   // Initialize the PLL and SDRAM controller,时钟配置,位于init_PLL_SDRAM.c文件里
    InitDAI();         // Initialize DAI because the SPORT and SPI signals need to be routed,DAI配置,位于initSRU.c文件里
    Init1939viaSPI();  // This function will configure the AD1939 codec on the 21489 EZ-KIT,SPI配置,比较好理解
    enable_SPORT01_MCM_mode();   // Turn on SPORT0 TX and SPORT1 RX for Multichannel Operation,SPORT配置,位于SPORT01_TDM_init.c文件里  
    interrupts(SIG_SP1, process_AD1939_samples);  // Unmask SPORT1 RX ISR Interrupt,当SIG_SP1中断发生,运行process_AD1939_samples中断函数,比较好理解
    // Enable multichannel operation (SPORT mode and DMA in standby and ready)
    *pSPMCTL0 |= MCEA;       
    *pSPMCTL1 |= MCEA;
    SinTableInit();      // 只有//#define GENERATE_DAC_PURE_TONES_TEST取消注释才有用,这里不用关注
    DelayLine = (float *) 0x000C4000; // Set up small delay buffer
    for (;;)
    {
    
    
    	  asm("idle;");   
    }
}

InitDAI()函数可参考之前的案例讲解,这次重点看enable_SPORT01_MCM_mode()函数和process_AD1939_samples中断服务函数。
先看enable_SPORT01_MCM_mode()函数

void  enable_SPORT01_MCM_mode()
{
    
    
 // Clear out SPORT 0/1 registers
 // SPMCTLx(SPORT Multichannel Control Registers),SPCTLx(Serial Control Registers),这两个寄存器所有位至0
 *pSPMCTL0 = 0; *pSPMCTL1 = 0; *pSPCTL0 = 0; *pSPCTL1 = 0;
 // External clock and frame syncs generated by AD1939
 // DIVx(SPORT Divisor Registers),高16位用于frame syncs的除数,低16位用于clock的除数
 *pDIV0 = 0x00000000;  // Transmitter (SPORT0)
 *pDIV1 = 0x00000000;  // Receiver (SPORT1) at 12.288 MHz SCLK and 48 kHz sample rate
 // SPORT0 and SPORT1 are being operated in "multichannel" mode. This is synonymous with TDM mode which is the operating mode for the AD1939
 // SP01MCTL = 0x000000E2,  Hold off on MCM enable, and number of TDM slots to 8 active channels
 // Multichannel Frame Delay=1, Number of Channels = 8, LB disabled
 // NCH = Actual number of channel slots – 1,MFD(Multichannel Frame Delay):
 *pSPMCTL0 = NCH7 | MFD1;   // NCH7即[5:7]=111,MFD1即[1]=1,1个串行时钟周期,即0x000000E2;
 *pSPMCTL1 = NCH7 | MFD1;   // 参考上面的解释
 // Enable chain pointer registers before enabling DMA
 xmit0a_tcb[4] = *pCPSP0A = ((int) xmit0a_tcb + 7) & 0x7FFFF | (1<<19);
 rcv1a_tcb[4]  = *pCPSP1A = ((int) rcv1a_tcb  + 7) & 0x7FFFF | (1<<19);
 // sport1 control register set up as a receiver in MCM
 // externally generated SCLK1 and RFS1
 // SDEN_A,Enable Channel A Serial Port DMA,0:Disable,1:Enable
 // SCHEN_A,Enable Channel A Serial Port DMA Chaining,0:Disable,1:Enable
 // SLEN,Serial Word Length Select. 
 // sport 1 control register SPCTL1 = 0x000C01F0
 *pSPCTL1 =  SCHEN_A | SDEN_A | SLEN32; // SCHEN_A即[19]=1, SDEN_A即[18]=1,SLEN32即[4:8]=11111
 // sport0 control register set up as a transmitter in MCM
 // SPTRAN,Data Transfer Direction,0 = Receive on both channels A and B,1 = Transmit on both channels A and B. 
 *pSPCTL0 =  SCHEN_A | SDEN_A | SPTRAN | SLEN32;
 // sport1 receive & sport0 transmit multichannel word enable registers
 // enable receive channels 0-7
 // enable transmit channels 0-7
 *pMR1CS0 = *pMT0CS0 =0x000000FF;  
 // sport1 & sport0 receive & transmit multichannel companding enable registers
 // no companding for our 8 active timeslots*/
 // no companding on SPORT1 receive */
 // no companding on SPORT0 transmit */
 *pMR1CCS0 = *pMT0CCS0 = 0;
}

本案例的enable_SPORT01_MCM_mode()函数与前一案例的void InitSPORT()函数重合率80%以上,要看懂这个函数,需要借助《ADSP21489_硬件参考手册》,手册里包含了所涉及的寄存器各个位的含义,该函数主要是对21489芯片的寄存器进行配置。本人借鉴案例的解释及参考手册,对这段代码进行了进一步的解释,在代码中可查看。
另外,本案例的main.c函数里有如下代码:

// Enable multichannel operation (SPORT mode and DMA in standby and ready)
*pSPMCTL0 |= MCEA;       
*pSPMCTL1 |= MCEA;

这段代码也是前一个案例里void InitSPORT()函数的代码,不清楚本案例为啥要单独放在main.c里。

接下来看下process_AD1939_samples函数

void  process_AD1939_samples( int sig_int)
{
    
    
 /* Perform AD1939 Audio Processing Here Using 32-bit Floating-point Math.(Note: The ADC/DAC variables below are used to "double-buffer" the audio samples with temporary variables, allowing active DMA completion of rx1a_buf[] and tx0a_buf[]) */
 Receive_ADC_Samples();                    // 从ADC采集信号
 /* Loop back AD1939 ADC1 data to DAC1 */
 Left_Channel_Out1 = Left_Channel_In2;    // 直接将输入信号传至输出端口
 Right_Channel_Out1 = Right_Channel_In2; 
 /* create a simple stereo digital delay on internal AD1939 stereo DAC2 channels */
 /* Delay-line length must not exceed usable seg_dmda memory addresses! */
 Left_Channel_Out2 = Left_Channel_In2;    // 左输入信号不经过处理直接传至左输出端口
 Right_Channel_Out2 = DelayLine[Index] + Right_Channel_In2; // 右输入信号与上一时刻的右输入信号叠加,一起传至左输出端口
 DelayLine[Index++] = Right_Channel_In2;  // 将此次的右输入信号保存到缓存中
 if (Index == 16000) Index = 0;
 /* loop back ADC1 audio data to other DACs 3 & 4 */
 Left_Channel_Out3 = Left_Channel_In1;
 Right_Channel_Out3 = Right_Channel_In1;
 Left_Channel_Out4 = Left_Channel_In1;
 Right_Channel_Out4 = Right_Channel_In1;
 Transmit_DAC_Samples();                  // 将处理好的信号发送至DAC
}

process_AD1939_samples()函数包含了两个由汇编写的函数 Receive_ADC_Samples()和 Transmit_DAC_Samples(),其他代码不难理解。这两个汇编的函数位于SPORT1_ISR_process_samples_.asm文件里,如下所示:

_Receive_ADC_Samples:
.global _Receive_ADC_Samples;
 /* get AD1939 left channel input samples, save to data holders for processing */
 r1 = -31;
 r0 = dm(_rx1a_buf + Internal_ADC_L1); f0 = float r0 by r1; dm(_Left_Channel_In1) = r0; 
 r0 = dm(_rx1a_buf + Internal_ADC_L2); f0 = float r0 by r1; dm(_Left_Channel_In2) = r0;  
 /* get AD1939 right channel input samples, save to data holders for processing */
 r0 = dm(_rx1a_buf + Internal_ADC_R1); f0 = float r0 by r1; dm(_Right_Channel_In1) = r0; 
 r0 = dm(_rx1a_buf + Internal_ADC_R2); f0 = float r0 by r1; dm(_Right_Channel_In2) = r0;   
 r0 = DM(AD1939_audio_frame_timer);
 r0 = r0 + 1;
 DM(AD1939_audio_frame_timer) = r0; 
 leaf_exit;
_Receive_ADC_Samples.end:

_Transmit_DAC_Samples:
.global _Transmit_DAC_Samples;
 r1 = 31;
 /* output processed left ch audio samples to AD1939 */
 r0 = dm(_Left_Channel_Out1); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_L1) = r0;
 r0 = dm(_Left_Channel_Out2); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_L2) = r0;
 r0 = dm(_Left_Channel_Out3); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_L3) = r0;
 r0 = dm(_Left_Channel_Out4); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_L4) = r0;
 /* output processed right ch audio samples to AD1939 */
 r0 = dm(_Right_Channel_Out1); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_R1) = r0;
 r0 = dm(_Right_Channel_Out2); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_R2) = r0;
 r0 = dm(_Right_Channel_Out3); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_R3) = r0;
 r0 = dm(_Right_Channel_Out4); r0 = trunc f0 by r1; dm(_tx0a_buf + Internal_DAC_R4) = r0;
#ifdef GENERATE_DAC_PURE_TONES_TEST
 Call make_DAC_Pure_Tones;
#endif
 leaf_exit;
_Transmit_DAC_Samples.end:

汇编没学过,只知道 Receive_ADC_Samples()可以同时采集8个通道的输入信号, Transmit_DAC_Samples()函数可以同时向8个通道输出不同的信号。这个技能是SPORT的专有技能,可以参考相关手册好好学习。

好了,今天就写这么多吧。

备注:
软件开发相关技术交流可留言或私信(LabVIEW,Matlab,STM32,ADSP均可)

猜你喜欢

转载自blog.csdn.net/Leisure_ksj/article/details/113825297
96
48
今日推荐