This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] MSP430FR5994:使用12位 ADC 实施 DMA

Guru**** 2539500 points
Other Parts Discussed in Thread: MSP430FR5994

请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1071125/msp430fr5994-implementing-dma-with-12-bit-adc

部件号:MSP430FR5994
《线程》中讨论的其他部件:MSPWARE

我在 MSP430FR5994上开发了一款使用12位 ADC 和 FFT LEA 的应用程序。 它正在进行华丽的工作。 下一步是整合 DMA 将 ADC 数据移至 RAM,这是我遇到一些困难的地方。 MSPWare 中没有任何相关的代码示例,家庭用户指南有点不透明。 在搜索论坛后,我能够将一些内容放在一起,但似乎无法正常工作。 修改后的代码将运行,但与无 DMA 应用程序相比,性能会显著下降。

ADC 在定时同时循环中以33 ks/秒的速度收集256个时间域样本。  采集后,LEA 将执行固定刻度的真实 FFT。 我想强调的是,我的问题只是如何正确实施 DMA。 CPU 中介数据传输工作正常。

正确运行的应用程序的相关部分的代码片段(省略了大量离合线)如下:

            // ADC sampling using Timer_A1 sourced to SMCLK at 8 MHz
            TA1CTL = TASSEL__SMCLK | MC__UP; //SMCLK, up mode
            TA1CCR0 = SAMPLING_PERIOD;  // PWM Period = 30 us or 33.333 kHz sampling frequency
            TA1CCTL1 = OUTMOD_3;  // Set/reset
            TA1CCR1 = SAMPLE_TIME; // PWM Duty Cycle

            ADC12CTL0 |= ADC12ON; //ADC12 on
            /* ADC triggered by Timer_A1 at 33.333 kHz; ADC clock is SMCLK at 8 MHz; see 5994 data sheet
               TA1.1 output selected with ADC12SHS_4; SMCLK; single-channel repeat */
            ADC12CTL1 = ADC12SHS_4 | ADC12SSEL_3 | ADC12CONSEQ_2;
            ADC12CTL2 = ADC12RES_2;  //12-bit conversion
            ADC12MCTL0 |= ADC12INCH_4;   //Input on A4 (P1.4)
            ADC12MCTL0 |= ADC12VRSEL_1; //ADC range between Vref and ground
            ADC12IER0 |= ADC12IE0;  // Enable ADC conversion-complete interrupt

           while(1)
           {
		        int16_t ADC_array[SAMPLES],*ADC;

                ADC12CTL0 |= ADC12ENC;
                for(k=0; k<SAMPLES; k++) //Acquire the time-sampled data
                {
                    LPM0; //Wait for the ADC interrupt
                    ADC_array[k] = ADC_Output;
                }
                // Perform real FFT with fixed scaling on ADC data
                ADC = ADC_array; //Point to first element of ADC_array
                for (k=0;k<SAMPLES;k++) input[k] = *ADC++; //Copy time data to input array
                status = MAP_msp_fft_fixed_q15(&fftParams, input);
                msp_checkStatus(status);
                for (k=0;k<SAMPLES;k++) fft_array[k] = input[k]; //Copy the complex FFT data
	
	            *** etc ***
            }

#pragma vector = ADC12_B_VECTOR
__interrupt void ADC12_ISR(void)
{
    switch(__even_in_range(ADC12IV, ADC12IV__ADC12RDYIFG))
    {
        case ADC12IV__ADC12IFG0:            // Vector 12:  ADC12MEM0 Interrupt
        ADC_Output = ADC12MEM0;
        __bic_SR_register_on_exit(LPM0_bits); // Clear CPUOFF bit from LPM0
        break;
	}
}

在尝试使用 DMA 时,我消除了 ADC 中断(上面的第14行)及其中断向量 ISR。 代码的新增内容:

    DMACTL0 = DMA0TSEL_26; // Channel 0 trigger is ADC12 end of conversion
    
    /* Configure Ch 0. Repeated single transfer, increment destination address,
    source address unchanged, enable DMA interrupt to signal that all conversions
    are complete and let DMA ISR escape from LPM0 */
    
    DMA0CTL |= DMADT_4 + DMADSTINCR_3 + DMAIE;
    DMACTL4 = DMARMWDIS; // Read-modify-write disable. Makes no difference
    DMA0SZ = SAMPLES; //Allocate space for 256 ADC samples

    while(1){

    uint16_t ADC_array[SAMPLES],*ADC;
    
    //Assign the DMA source and destination addresses
    __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0);
    __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &ADC_array[0]);

    DMA0CTL |= DMAEN; //Enable DMA for repeated single transfers
    ADC12CTL0 |= ADC12ENC; //Turn on the ADC
    LPM0; //Wait for the DMA interrupt that occurs at the completion of SAMPLES number of transfers

    // Perform real FFT with fixed scaling on ADC data
    ADC = ADC_array; //Point to first element of ADC_array
    for (k=0;k<SAMPLES;k++) input[k] = *ADC++; //Copy time data to input array

        *** etc, etc ***
}

#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
  switch(__even_in_range(DMAIV,16))
  {
        case 0: break;
        case 2:                                 // DMA0IFG = DMA Channel 0
        __bic_SR_register_on_exit(LPM0_bits);
        DMA0CTL &= ~DMAIFG; //Including this line doesn't make an difference
        break;
    }
}

我希望有人能在拉出示波器之前发现错误。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我没有发现任何明显的错误,但写入 DME0SA 和 DME0DA 的操作效率低下。 由于特殊函数寄存器和数据阵列内存不足,因此不需要20位寻址。 随机播放数据需要大量工作,以便为编写这篇文章做好准备。 更简单的方法是:DMA0SAL =&ADC12MM0。 在文字上清除源地址的上位,这样所有的都很好。

    哦,我可能会使用 DMADD_0,以停止传输。 在重复的单次传输模式下,DMASZ 变为零后,DMAC 将重新加载,并再次执行所有操作。 有了 DMADD_0,它将等待您再次与 DMAEN 合作。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    感谢您的反馈。 当我设置 DMA0SAL =&ADC12MEM0和 DMA0DAL =&ADC_array[0]时,CCS 会警告我数据类型不匹配。 应用程序运行,但不传输来自 ADC 的任何数据。

    我确认,使用 DMADT_0替换 DMADD_4并在每个循环中重置 DMAEN 都有效。 从数据表上的系统流程图中无法清楚地设置这种方法。 我仍然需要使用20位寻址来使应用程序正常工作,但似乎我正在 DMA 管道中的某个位置丢失/丢失/截断数据。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    可以确认使用 DMA 时,来自 ADC 的256个样本未正确路由到主程序中。 这些数据不是看到256个行为良好的数据点,而是作为一组较小的,非常大的错误数字(数组索引0-97)来传递。 其余数组点为零。 在下一次迭代中,索引97-127中有大量的数字,其余为零。 此行为在交替迭代时重复。 显然,使用 DMA 配置数据传输时出现问题。 我尝试了 __data20_write_long 和__data20_write_short 的变体,但没有成功。 我是否需要配置 CCS 来处理此问题?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我不会与大卫不同,但我会提到(使用 CCS)我只成功地使用了以下形式:

    >__data20_write_long  ((uintpttr_t)&DMA0SA,(uintpttr_t)&ADC12MEM0);

    海合会可能更好地处理这些寄存器。

    您是否仍在使用 DMADD_0? (我也推荐这种方法。) 请记住,即使在 DMA 停止运行后,ADC 仍在运行。 在设置 DMAEN 后清除 ADC12IFGR0=0可能是谨慎的做法,以避免潜在的停机。

    堆栈中有一个512字节的数组(相当大)。 您是否设置了足够大的堆栈大小来适应这种情况? 作为一项快速实验,请尝试将其移出主(全局),并查看症状是否发生变化。 据我所知,您可以直接将 DMA 转换为 LEA RAM。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    当您需要20位时,GCC 根本不能很好地处理这20位寄存器。 (如果不能修复该问题,编译器至少应该发出警告。) 对于0x10000以下的地址(RAM,LEARAM,SFR),向 DMA0SAL 写入数据对我来说很合适。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    设置 DMA0SAL =&ADC12MEM0和 DMA0DAL =&ADC_ARRAG[0],这将发出我忽略的515-D 警告。 DMA0SZ =256,uint16_t ADC_array[256]作为全局放置。 通过代码,DME0DA 的地址与 ADC_ARRAY[0]匹配,并且有512字节的内存分配。 在第一次迭代中,合理的外观样本值被写入数组元素0-195。 196--255为0,DMA0SZ =60。 在下一个迭代中,用零填充整个数组,DMA 将看似有效的 ADC 值写入索引196--255。 DMA0SZ 重置为256,此交替过程重复。 使用 DMADD_0或 DMADD_4时的行为相同。 每次迭代都清除 ADC12IFGR0,DMAEN 设置在 ADC12ENC 之前。

    我的猜测是,我的 DMA 内存不足,但如果是,我无法确定如何为其配置更多内存。 虽然数据块不正确,但良好的数据正在将其纳入主程序,这是朝着正确方向迈出的一步。

    我感谢大家的关注和帮助。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    没有办法完全消除这些警告。 将指针转换为不同大小的整数时,我收到警告。  (如果使用-mlarge。) 这也是由一个“长铸”来实现的。

    以下几点:

    1)作为一种风格和剪切偏执狂,我不喜欢您处理 DMA0CTL 的方式。 当然,重置后它应该是所有的零,但不需要使用|=来设置其中的位。

    2)你在 DMA 上的等待方式让我感到很困扰。 输入 LPM0并假设在退出时 DMA 已完成。 情况可能是这样,但情况可能不是这样。 在一个更复杂的系统中肯定不会发生这种情况。 使用 DMADD_0 DMAE 时,DMA 完成后应清除。 因此,检查一下这一点,如果不清楚,请等待更多。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    建议2)骗了大卫。 它是通过与 DMA0IFG 无关的中断从 LPM0中跳出来的。 DMA0CTL 位正常。 如果其他人碰巧找到了此线程,下面是 DMA 与 ADC12和 LPM0配合使用的代码编辑:

    DMA0SAL = &ADC12MEM0; //Ignore 515-D warning
    DMA0DAL = &ADC_array[0]; //Ignore 515-D warning
    
    ADC12IFGR0 = 0;
    DMA0CTL |= DMAEN; //Enable DMA for repeated single transfers
    ADC12CTL0 |= ADC12ENC; //Turn on the ADC
    LPM0; //Wait in LPM0
    while (DMA0CTL & DMAEN); //Interrupt occurred. Make sure DMA transfer complete before disabling ADC
    ADC12CTL0 &= ~ADC12ENC; //Disable ADC
    
    // *** proceed with FFT ***
    
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      	switch(__even_in_range(DMAIV,16))
      	{
        		case 0: break;
        		case 2:   // DMA0IFG = DMA Channel 0
            	__bic_SR_register_on_exit(LPM0_bits);
          		break;
    	}
    }

    我希望这条线程保持开放几天,以确保所有线程都能正常工作。 然后我会将问题标记为已解决,如果可以,您可以将其锁定。 非常感谢。