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.

[参考译文] MSP430F5529:ADC 不触发 DMA

Guru**** 2589280 points


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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1052259/msp430f5529-dma-not-trigger-by-adc

器件型号:MSP430F5529

大家好、我正在尝试执行一个简单的程序、该程序将 ADC 配置为读取温度、并使用 DMA 将结果发送到 UART 进行传输。 看起来我的 ADC 没有触发我的 DMA、因此当我查看寄存器 UCATXBUF 时、它始终为0x00。

我知道我还没有代码的 UART 比例。 我让该部分处理另一个项目。 因此、在这里、我重点关注通过 DMA 将 ADC 读出到 UCATXBUF。


#include <msp430.h>
#include <stdint.h>

#define ADC_RESULTION_BITS 12

void DMA_config (void)
// configure DMA0
{
    // Setup DMA0
    DMACTL0 = DMA0TSEL_24;                    // ADC12IFGx triggered
    DMACTL4 = DMARMWDIS;                      // Read-modify-write disable
    DMA0CTL &= ~DMAIFG;
    DMA0CTL = DMADT_4+DMAEN+DMADSTINCR_0+DMASRCINCR_0+DMAIE; // Rpt single tranfer, unchanged src & dst, Int
    DMA0SZ = 1;                               // Block size
  __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0); // Source block address
  __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF); // Destination single address
}

void ADC_config()
{
    // ADC Core control
       ADC12CTL0  |= 0x00;                                              // Initialize ADC12CTL0 register (Turn ADC12 OFF before configure it)
       ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
       ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
       REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V

       ADC12CTL1  |= 0x00;                                              // Initialize ADC12CTL1 register
       ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
       ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
       ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
       ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
       ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)

       ADC12CTL2  |= 0x00;                                              // Initialize ADC12CTL2 register
       ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
       ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution

       ADC12IE |= ADC12IE0;                                             // Enable ADC12 Interrupt on Memory 0
       ADC12IFG |= ADC12IFG0;

       ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
       ADC12CTL0  |= ADC12ENC | ADC12SC;                                // Enable ADC
}

int main(void)
{
    ADC_config();
    DMA_config();
    __bis_SR_register(LPM0_bits + GIE);       // LPM0 w/ interrupts
    __no_operation();                         // used for debugging
}
//------------------------------------------------------------------------------
// DMA Interrupt Service Routine
//------------------------------------------------------------------------------
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
  switch(__even_in_range(DMAIV,16))
  {
    case 0: break;
    case 2:                                 // DMA0IFG = DMA Channel 0
      P1OUT ^= BIT0;                        // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
      break;
    case 4: break;                          // DMA1IFG = DMA Channel 1
    case 6: break;                          // DMA2IFG = DMA Channel 2
    case 8: break;                          // DMA3IFG = DMA Channel 3
    case 10: break;                         // DMA4IFG = DMA Channel 4
    case 12: break;                         // DMA5IFG = DMA Channel 5
    case 14: break;                         // DMA6IFG = DMA Channel 6
    case 16: break;                         // DMA7IFG = DMA Channel 7
    default: break;
  }
}

ADC 中断标志已置1:

但 DMA 没有将数据传输到 TxBuffer:

我完全知道从12位 ADC 读取的数据是1字宽(16位)、而目的寄存器 UCATXBUF 只有8位。 根据 MSP430用户指南"当字到字节传输时、只传输源字的低字节。" 因此在本示例中、我希望将0xED 写入 UCATXBUF、但它报告的是0x00

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

    我认为 ADC 配置是正确的。 问题可能出在 DMA 设置上。

    您能从我们的示例代码https://dev.ti.com/tirex/explore/node?node=ALkqBIa6R80LFOC7XAng.Q__IOGqZri__LATEST 中找到一些帮助

    您可以使用 RAM 中的 int 值来检查 DMA 是发送该值还是仅发送0x00。 或者您只需降低输入电压。

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

    我对中断和 DMA 如何协同工作有点困惑。 假设我有一个 ADC 生成中断、并且该中断被配置为 DMA 的触发器。 当中断被触发时、ISR 是运行还是 DMA 执行? 毕竟、DMA 也需要几个时钟周期才能运行。

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

    以下代码为半工作代码:

    #include <msp430.h>
    #include <stdint.h>
    
    #define ADC_RESULTION_BITS 12
    
    void IO_config (void)
    // configure IOs
    {
        P1DIR |= 0x01;                  // configure P1.0 as output (RED LED)
        P4DIR |= 0x80;                  // configure P4.7 as output (GREEN LED)
        P1OUT = 0x00;                   // initialize LED to off
        P4OUT = 0x00;                   // initialize LED to off
        P4SEL = BIT4 + BIT5;            // Port Configuration for UART
    }
    
    void DMA_config (void)
    // configure DMA0
    {
        // Setup DMA0
        DMACTL0 = DMA0TSEL_24;                    // ADC12IFGx triggered
        DMACTL4 = DMARMWDIS;                      // Read-modify-write disable
        DMA0CTL &= ~DMAIFG;                       // Enable DMA interrupt
        DMA0CTL = DMAEN+DMAIE;                    //
        DMA0SZ = 1;                               // Block size
      __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0); // Source single address
      __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF); // Destination single address
    }
    
    void ADC_config()
    {
        // ADC Core control
           ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
           ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
           REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V
    
           ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
           ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
           ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
           ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
           ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)
    
           ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
                                                                            // Based on ACLK at 32,768Hz, divider = 8 and pre-divider = 4, therefore ADCLK is 1,024kHz
                                                                            // sanple-hold at 1024 clock circle, so ADC will output 1 reading every second
           ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution
    
           ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
           ADC12CTL0  |= ADC12ENC | ADC12SC;                                // Enable ADC
    }
    
    void ConfigUCS (void)
    //Configure SMCLK to 4MHz
    {
      P5SEL |= BIT2 | BIT3;//Configure IO as XT2 function
      UCSCTL6 &= ~XT2OFF;//Enable XT2
    
      UCSCTL4 |= SELA_2;//first configure ACLK source as REFCLK
      UCSCTL3 |= SELREF_2;//Configure FLLCLK source as REFCLK
    
      while (SFRIFG1 & OFIFG) {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);//Clear the three types of clock flags
                                 //There are three flag bits that need to be cleared because any
                                 //The flag bit will set OFIFG
        SFRIFG1 &= ~OFIFG;//Clear clock error flag
      }
      UCSCTL4 = UCSCTL4 & (~(SELS_7 | SELM_7)) | SELS_5 | SELM_5;//Configure SMCLK and MCLK clock sources as XT2
    }
    
    void ConfigUART (void)
    {
        P4SEL |= BIT5+BIT4;  // Port Configuration
        UCA1CTL1 |= UCSWRST; // Software reset of UCA1 module
                             // Initialize control register UCA1XTL0.
                             // Default is: no Parity / LSB first / 8bit / One stop bit / UART / Asynchrnoous mode
    
        UCA1CTL1 |= UCSSEL_2;// Set SMCLK (SMCLK is at 4MHz) to BRCLK.
    
        UCA1MCTL |= UCOS16;  // Oversampling mode
                             // N = 4,000,000/115,200 = 34.722
    
        UCA1BR0 |= 0x02;     // UCBR0 = INT (N/16) = 2
        UCA1MCTL|= UCBRF1;   // UCBRF1 = 2
        UCA1MCTL|= UCBRS_1 + UCBRF_0;
    
        UCA1CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    //    UCA1IE |= UCRXIE;                         // Enable USCI_A1 RX interrupt
    }
    
    int main(void)
    {
        IO_config();
        ADC_config();
        DMA_config();
        ConfigUCS ();
        ConfigUART();
        __bis_SR_register(LPM0_bits + GIE);
        __no_operation();                         // used for debugging
    }
    //------------------------------------------------------------------------------
    // DMA Interrupt Service Routine
    //------------------------------------------------------------------------------
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      switch(__even_in_range(DMAIV,16))
      {
        case 0: break;
        case 2:                                 // DMA0IFG = DMA Channel 0
          P1OUT ^= BIT0;                        // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
          P4OUT ^= BIT7;
          break;
        case 4: break;                          // DMA1IFG = DMA Channel 1
        case 6: break;                          // DMA2IFG = DMA Channel 2
        case 8: break;                          // DMA3IFG = DMA Channel 3
        case 10: break;                         // DMA4IFG = DMA Channel 4
        case 12: break;                         // DMA5IFG = DMA Channel 5
        case 14: break;                         // DMA6IFG = DMA Channel 6
        case 16: break;                         // DMA7IFG = DMA Channel 7
        default: break;
      }
    }
    
    /*
    #pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
    {
      switch(__even_in_range(UCA1IV,4))
      {
      case 0:break;                             // Vector 0 - no interrupt
      case 2:break;                             // Vector 2 - RXIFG
      case 4:break;                             // Vector 4 - TXIFG
      default: break;
      }
    }
    */
    

    但是、当我探测 UART Tx 线路时、我看到 Tx 线路上出现了骤降。 我认为这与 DMA 和 ADC 无关、而是在某种程度上与 UART 相关。 但是、独立程序中代码的同一 UART 部分可以正常工作、而不会出现这种干扰。 有什么想法吗? 谢谢大家

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

    1.当我再次检查时、您之前发布的代码不会设置 UART。 很抱歉,我想这个。

    2.对于干扰,我不知道。 它似乎不会因数字信号而导致。 我建议您移出另一个 UART 器件、以检查它是否由 MSP430引起。

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

    它看起来像是在某种程度上与看门狗复位相关的干扰。 在尝试解决此看门狗复位引起的问题时、我发现我的问题更深。 当我添加暂停看门狗计时器的语句时、程序在第一个 DMA 之后停止运行、这意味着 DMA 将发生一次、然后不再初始化 DMA。 我已经通过置位  ADC12CTL0中的 ADC12SC 位将 DMA ISR 配置为重新启动 ADC 转换。 我认为这将触发另一个 ADC 转换、一旦 ADC 完成(由 ADC12IFG 发送信号)、结果将通过 DMA 加载到 UA1TXBUF、然后 周期 会无限重复。 但看起来、在第一次之后、ADC 没有触发 DMA 来传输数据。

    #include <msp430.h>
    #include <stdint.h>
    
    #define ADC_RESULTION_BITS 12
    
    void IO_config (void)
    // configure IOs
    {
        P1DIR |= 0x01;                                                     // configure P1.0 as output (RED LED)
        P4DIR |= 0x80;                                                     // configure P4.7 as output (GREEN LED)
        P1OUT = 0x00;                                                      // initialize LED to off
        P4OUT = 0x00;                                                      // initialize LED to off
        P4SEL = BIT4 + BIT5;                                               // Port Configuration for UART
    }
    
    void DMA_config (void)
    // configure DMA0
    {
        // Setup DMA0
        DMACTL0 = DMA0TSEL_24;                                             // ADC12IFGx triggered
        DMACTL4 = DMARMWDIS;                                               // Read-modify-write disable
        DMA0CTL &= ~DMAIFG;                                                // Enable DMA interrupt
        DMA0CTL = DMAEN+DMAIE;                                             //
        DMA0SZ = 1;                                                        // Block size
      __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0);     // Source single address
      __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &UCA1TXBUF);     // Destination single address
    }
    
    void ADC_config()
    {
        // ADC Core control
           ADC12CTL0  |= ADC12SHT00 + ADC12SHT01 + ADC12SHT02 + ADC12SHT03;
           ADC12CTL0  |= ADC12SHT10 + ADC12SHT11 + ADC12SHT12 + ADC12SHT13; // Sample-and-hold time set to 1024 ADC12CLK cycle
           REFCTL0    |= (REFMSTR + REFON + REFVSEL_3);                     // Reference voltage 2.5V
    
           ADC12CTL1  |= ADC12SHP;                                          // S/H signal comes from Sample Timer
           ADC12CTL1  |= ADC12SSEL0;                                        // Set ADC12 clock source to ACLK (32,768kHz)
           ADC12CTL1  |= ADC12DIV0 + ADC12DIV1 + ADC12DIV2;                 // Set ADC12 divider to 8
           ADC12MCTL0 |= 0x0A;                                              // Configure 12-bit ADC0 control register to 0x0A (1010b) for temperature diode
           ADC12MCTL0 |= ADC12SREF0;                                        // Select reference voltage (VREF+ and VR- = AVSS)
    
           ADC12CTL2  |= ADC12PDIV;                                         // Set ADC12 pre-divider to 4
                                                                            // Based on ACLK at 32,768Hz, divider = 8 and pre-divider = 4, therefore ADCLK is 1,024kHz
                                                                            // sanple-hold at 1024 clock circle, so ADC will output 1 reading every second
           ADC12CTL2  |= ADC12RES_2;                                        // 12-bit resolution
    
           ADC12CTL0  |= ADC12ON;                                           // ADC12 Turn ON
           ADC12CTL0  |= ADC12ENC + ADC12SC;                                // Enable ADC / Start Conversion
    //       ADC12IE    |= ADC12IE0;                                          // Enable ADC Interrupt
    }
    
    void ConfigUCS (void)
    //Configure SMCLK to 4MHz
    {
      P5SEL |= BIT2 | BIT3;                                                 //Configure IO as XT2 function
      UCSCTL6 &= ~XT2OFF;                                                   //Enable XT2
    
      UCSCTL4 |= SELA_2;                                                    //first configure ACLK source as REFCLK
      UCSCTL3 |= SELREF_2;                                                  //Configure FLLCLK source as REFCLK
    
      while (SFRIFG1 & OFIFG) {
        UCSCTL7 &= ~(XT2OFFG + XT1LFOFFG + DCOFFG);                         //Clear the three types of clock flags
                                                                            //There are three flag bits that need to be cleared because any
                                                                            //The flag bit will set OFIFG
        SFRIFG1 &= ~OFIFG;                                                  //Clear clock error flag
      }
      UCSCTL4 = UCSCTL4 & (~(SELS_7 | SELM_7)) | SELS_5 | SELM_5;           //Configure SMCLK and MCLK clock sources as XT2
    }
    
    void ConfigUART (void)
    // Configure UART to 115kbps
    {
        P4SEL |= BIT5+BIT4;                                                 // Port Configuration
        UCA1CTL1 |= UCSWRST;                                                // Software reset of UCA1 module
                                                                            // Initialize control register UCA1XTL0.
                                                                            // Default is: no Parity / LSB first / 8bit / One stop bit / UART / Asynchrnoous mode
    
        UCA1CTL1 |= UCSSEL_2;                                               // Set SMCLK (SMCLK is at 4MHz) to BRCLK.
    
        UCA1MCTL |= UCOS16;                                                 // Oversampling mode
                                                                            // N = 4,000,000/115,200 = 34.722
    
        UCA1BR0 |= 0x02;                                                    // UCBR0 = INT (N/16) = 2
        UCA1MCTL|= UCBRF1;                                                  // UCBRF1 = 2
        UCA1MCTL|= UCBRS_1 + UCBRF_0;
    
        UCA1CTL1 &= ~UCSWRST;                                               // **Initialize USCI state machine**
        UCA1IE |= UCRXIE;                                                   // Enable USCI_A1 RX interrupt
    }
    
    int main(void)
    {
        WDTCTL = WDTPW + WDTHOLD;                                           // Stop WDT
        IO_config();
        ADC_config();
        DMA_config();
        ConfigUCS ();
        ConfigUART();
        __bis_SR_register(GIE);
        while (1)
        {
        __no_operation();                                                   // used for debugging
        }
    }
    //------------------------------------------------------------------------------
    // DMA Interrupt Service Routine
    //------------------------------------------------------------------------------
    #pragma vector=DMA_VECTOR
    __interrupt void DMA_ISR(void)
    {
      switch(__even_in_range(DMAIV,16))
      {
        case 0: break;
        case 2:                                                             // DMA0IFG = DMA Channel 0
          P1OUT ^= BIT0;                                                    // Toggle P1.0 - PLACE BREAKPOINT HERE AND CHECK DMA_DST VARIABLE
          P4OUT ^= BIT7;
          ADC12IFG  |= 0x00;
          ADC12CTL0 |= ADC12SC;                                             // Once the DMA transfer is finished, start another ADC conversion
          break;
        case 4: break;                                                      // DMA1IFG = DMA Channel 1
        case 6: break;                                                      // DMA2IFG = DMA Channel 2
        case 8: break;                                                      // DMA3IFG = DMA Channel 3
        case 10: break;                                                     // DMA4IFG = DMA Channel 4
        case 12: break;                                                     // DMA5IFG = DMA Channel 5
        case 14: break;                                                     // DMA6IFG = DMA Channel 6
        case 16: break;                                                     // DMA7IFG = DMA Channel 7
        default: break;
      }
    }
    
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=USCI_A1_VECTOR
    __interrupt void USCI_A1_ISR(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(USCI_A1_VECTOR))) USCI_A1_ISR (void)
    #else
    #error Compiler not supported!
    #endif
    {
      switch(__even_in_range(UCA1IV,4))
      {
      case 0:break;                                                          // Vector 0 - no interrupt
      case 2:break;                                                          // Vector 2 - RXIFG
      case 4:break;                                                          // Vector 4 - TXIFG
      default: break;
      }
    }
    

    例如、ADC12MEM0第一次为0x04ED、低字节被复制到 UCA1TXBUF 中

           

    示波器还捕获了通过 UART 发送的该数据

    如果我继续执行该程序、则不会再发生传输。 如果我手动暂停程序并查看寄存器值、我会看到 ADC12MEM0被载入新数据、ADC12IFG 被更新、但不再发生 DMA

        

    MSP430用户指南指定了"一个[DMA]传输由 ADC12IFG 标志触发、且对应的 ADC12IE 位复位。" 由于我只是想将数据从 ADC 中复制出来、所以我没有 ADC 的 ISR、因此 ADC12IE 不会设置为"1"、而是保持为0。 我尝试将其设置为"1"、但它无法解决问题。 总之、它意味着"相应 的 ADC12IE 位复位"...这对我来说不清楚...  

    我的代码中一定会有一个愚蠢的错误。 是否有人可以向我解释为什么第一次 DMA 传输后不会再次触发?  谢谢大家。。。

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

    我明白为什么 在单次传输中的每次传输后 DMAEN 位都会被清零。 应将其置于 重复单次传输模式、然后一切正常。