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:DMA 触发的 UART 操作

Guru**** 2589280 points
Other Parts Discussed in Thread: MSP430F5529

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

https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1055243/msp430f5529-dma-triggered-uart-operation

器件型号:MSP430F5529

大家好、我现在正在做一个简单的项目

  1. 我使用 MSP430F5529内的12位 ADC 来测量温度。
  2. ADC 结果由 DMA0传输到 RAM (由 ADC12IFGx 触发)。 DMA0传输完成后、其 ISR 会根据一些内部校准参数计算温度。
  3. DMA1  将以 ºC (ab.CD↵=> 6字节)为单位的温度传输到 UART (通过设置 DMAREQ 位进行软件触发)。

ADC 每秒生成1个样本、UART 以115,200 b/s 的速度运行。

以下是我的代码:

#include <msp430f5529.h>
#include <stdint.h>
#include <stdio.h>
#include <float.h>

#define ADC_RESULTION_BITS 12
unsigned DMA_DST;                                                       // Global variables
unsigned int CAL_ADC_T30;
unsigned int CAL_ADC_T85;
unsigned int i =0;
char string [6];

float temperature;

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 DMA
{
// Setup DMA0
        DMACTL0 = DMA0TSEL_24;                                           // ADC12IFGx triggered
        DMACTL4 = DMARMWDIS;                                             // Read-modify-write disable
        DMA0CTL &= ~DMAIFG;                                              // Enable DMA interrupt
        DMA0CTL = DMADT_4+DMAIE;                                         // Repeated Single Transfer + Increment source address + Enable DMA Interrupt + DST/SRC both Word
        DMA0SZ = 1;                                                      // Block size
        __data20_write_long((uintptr_t) &DMA0SA,(uintptr_t) &ADC12MEM0); // Source single address
        __data20_write_long((uintptr_t) &DMA0DA,(uintptr_t) &DMA_DST);   // Destination single address
        DMA0CTL |= DMAEN;                                                // Enable DMA0, pending trigger from ADC

// Setup DMA1
        DMACTL0 |= DMA1TSEL_0;                                           // Software Controls DMA1
        DMA1CTL = DMADT_4+DMASRCBYTE+DMADSTBYTE;                         // Repeated Single Transfer + Enable DMA Interrupt + Enable DMA Interrupt + DST/SRC both byte
        DMA1SZ = 1;                                                      // Block size
        __data20_write_long((uintptr_t) &DMA1SA,(uintptr_t) &string[0]); // Initialize Source single address
        __data20_write_long((uintptr_t) &DMA1DA,(uintptr_t) &UCA1TXBUF); // Initialize Destination single address
        DMA1CTL |= DMAEN;                                                // Enable DMA1, pending SW trigger
}

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
                                                                         // sample-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;                                          // 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)
// 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**
        i = 0;

}

int main(void)
{
        WDTCTL = WDTPW + WDTHOLD;                                         // Stop WDT
        IO_config();
        ConfigUCS ();
        ConfigUART();
        DMA_config();
        ADC_config();

        unsigned int* p = (unsigned int *)0x001A22;                        // Address of
        CAL_ADC_T30 = p[0];                                                // Temperature Calibration at 30C
        CAL_ADC_T85 = p[1];                                                // Temperature Calibration at 85C
        __bis_SR_register(GIE);

        while (1)
        {
            ADC12CTL0|= ADC12SC;                                           // Start ADC12 conversion
            // Once ADC12 conversion finishes, following will happen:
            // Its interrupt flag ADC12IFGx will trigger DMA0 to transfer conversion result from ADC12MEM0
            // to variable DMA_DST
            // Once the DMA operation finishes, DMA ISR will calculate temperature and store the result
            // in string[0]..string[5] as ASCII code
        }
}

//------------------------------------------------------------------------------
// DMA Interrupt Service Routine
//------------------------------------------------------------------------------
#pragma vector=DMA_VECTOR
__interrupt void DMA_ISR(void)
{
    switch(__even_in_range(DMAIV,16))
    {
        case 0: break;
        case 2:                                                                   // When DMA finishes transfer the data
            {
                ADC12CTL0|= !ADC12SC;                                             // Stop ADC conversion
                P1OUT ^= BIT0;                                                    // Blink the LEDs
                P4OUT ^= BIT7;
                temperature = (float)((float)DMA_DST - CAL_ADC_T30)*(85-30)/(CAL_ADC_T85 - CAL_ADC_T30)+30; // Calculate temperature reading
                sprintf (string, "%.2f", temperature);
                string[5] = 0x0d;                                                  // Add Carriage Return to the last character to sent
                UCA1IE  |= UCTXIE;                                                 // Enable Interrupt for the UART controller
                UCA1IFG |= 0x02;                                                   // Manually set the TxIFG
                i = 0;
                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:                                                         // Vector 4 - TXIFG is set
            {
                if (i<=5)
                {
                    __data20_write_long((uintptr_t) &DMA1SA,(uintptr_t) &string[i]); // Source single address
                    DMA1CTL |= DMAREQ;
                    if (i==5)
                    {
                        i=0;
                        __data20_write_long((uintptr_t) &DMA1SA,(uintptr_t) &string[i]); // Source single address
                        UCA1IE  = 0x00;
                        break;
                    }
                    else
                    {
                        i++;
                    }
                }                                                                                     // Vector 4 - TXIFG
            }
            default: break;
        }
}

我是 C 编程的新手、因此我完全知道该代码已经完成了许多不推荐的操作(例如在 ISR 中进行浮点计算)。

但是、我的直接问题是、当我单步执行代码时、第一个 ADC 读数的第一个字符 由 DMA1发送两次到 UART。 因此、UART 将写入 、即 AAB.CD↵。

例如、首先执行此代码、I = 0、DMA1源地址配置为0x2400 (其中 x[0]存储)

DMA1触发后、发送第一个字符"2"

继续单步执行代码、I 递增至1、现在 DMA1源地址更新为0x2401 (其中存储了 x[1])

然而、在触发后、UCA1TXBUF 没有更新、所以字符"2"再次被发送

   

继续单步执行代码、I 递增到2、现在 DMA1源地址更新到 0x2402 (其中存储了 x[2])

然而、在触发后、UCA1TXBUF 确实更新了、所以字符"8"被发送

相应的 DMA 操作似乎正常

当我第一次初始化 DMA1时、可能会出现问题?

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

    BTW、本练习的全部内容、旨在学习如何使用 ADC/DMA/UART

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

    您好、Yi、

    调试模式下的代码在自由运行模式下运行时似乎不同?

    您可以使用一个中断尝试 DMA 传输和 UART 发送吗?

    由于 CPU 和 DMA 在采样时无法工作、这可能是原因。

    伊斯天

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

    伊斯迅、您好、感谢您再次与我取得合作。

    自由运行或调试模式、这两种情况都将发生。 第一个读数的第一个字符始终重复。

    我认为 DMA 的空穴点是释放 CPU 时间。 那么、CPU 和 DMA 不能同时工作的点是什么? 也许我误解了一些东西

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

    "ADC12CTL0|=!ADC12SC;"可能无法正常工作。 真标志的倒数是 false、也称为零。 因此、这不会执行任何操作、可能会得到优化。

    混合逻辑和位逻辑、而不是这里看起来很奇怪、因为位是在其他地方使用的。 sprintf 也被弄乱了。 未指定浮点数字字段宽度、因此您不知道字符串[]中最终会有多少个字符。 但您将 CR 置于固定位置。

    使用 DMA 将单个 ADC 转换结果传输到存储器、然后使用 DMA 中断触发处理是 DMA 通道的浪费。 即使作为一项练习也是无用的。 跳过 DMA 并使用 ADC 中断。

    使用 TXIFG 中断为单个字符手动启动另一个 DMA 通道也是无用的。 如果您想为此使用 DMA、请跳过 USCI 中断并对 DMA 进行编程以发送由 TXIFG 触发的 N 个字符。 它可以做到这一点。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    [引用 userid="215629" URL"~/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1055243/msp430f5529-dma-triggered-uart-operation/3905219 #3905219"]"ADC12CTL0|=!ADC12SC;"可能无法正常工作。 真标志的倒数是 false、也称为零。 因此、这不起任何作用、可能会得到优化。[/quot]

    执行 ISR 时、ADC 转换停止、只能在 Main 中再次启动...

    sprintf 函数已将 float 转换为5字节(ab.cd)。 无论我得到的转换结果是什么、我都是通过 UART 进行分离的。 因此、真正的问题是 DMA1为什么第二次不将数据传输到 UCA1TxBUF、而它应该通过 x[1]进行传输。

    正如我说过的、我是 c 语言的新手、因此请使用我的编码不好的方法。  

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

    您好、Yi、

    我将尝试调试您的代码以查看发生了什么。 2-3天后我会回来的。

    伊斯天

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

    谢谢你。 与此同时,我将尝试了解更多信息。 如果我意识到某件事、我会在这里发布。 再次感谢。

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

    您好、Yi、

    抱歉、目前我没有 F5529。 我需要一些时间才能获得新的。

    当我选中"char string [6]"时、该字符串为6字节。 不是5字节。 您能否检查"temperature (温度)"和 "string [6](字符串[6])"的值、以了解第一次从 UART 发送结果时问题是否出在字符串的温度上。

    伊斯天

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

    您好、奕迅、ADC 温度读数正确。  sprintf 函数将浮点数转换为字符串、我将结果限制为2个小数位、因此我希望 AB.CD 的 A=2。 RESULES[6]的最后一个字节是回车。 这允许我的超级终端为每次读取更改为新行。

    问题实际上在于 DMA。

    当 i=0时。 DMA 设置为从 Result [0](addr 0x2400)传输

    在手动触发 DMA 后、第一个数字"2"(ASCII 代码0x32)被传输到 UCA2TXBUF 并在 UART 上发出

    问题是当我递增到1时的下一个迭代。 DMA 源地址更新为 Result [1](addr  0x2401)

    在手动触发 DMA 后、UCA2TXBUF 没有更新为第二位。 由于某种原因、DMA 似乎没有触发

    如果我们继续单步执行代码、I 递增至2、DMA 源地址递增。 这一次、DMA 的确触发并且 第二位数字"5"(ASCII 代码0x35)被传输到 UCA2TXBUF 并在 UART 上被传输出去。

    等等。

      我们也不会对所有后续传输产生此问题

    *此超级终端屏幕截图是一个示例、不是我在上面执行代码时提到的特殊情况...因此您会看到第一读数是226.27

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

    我回来了。  

    我不知道为什么在 F5529上难以调试代码。 断点不能正常工作。

    从 UART 波形中可以看到、最后一个字节(0x0d)似乎不随当前帧一起发送。 希望它能为您提供一些想法。

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

    您好、伊斯天、感谢您再次与我见面。

    我还观察到、回车的最后一个字节(0x0d)实际上是在下一次迭代中发送的...从您的屏幕截图中、第一次执行时、发送了两次"1"、(我假设正确的操作是仅发送一次"1")。 这个额外的"1"基本上将最后一个字符(回车返回0x0d)压入传输的下一个迭代。

    问题始终是在代码的第一次迭代期间、当 I = 1时、DMA 没有将正确的数据从 string[1]传输 到 UCA1TXBUF、而源目的寄存 器 DMA1SA 被更新为正确的地址。

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

    [参考用户指南(SLAU208Q)图11-3、右下角框] DMA1SA 被提取(a)当 DMAEN 被置位时、早于第一个字节、(b)在(1字节)周期完成后立即被提取。 因此、当 UART ISR 更新 DMA1SA 时、由于已(重新)提取旧值、因此已经太晚了。 新值将在下一次传输后生效。 由于 DMADD=4、DMA 永远(实际上)不会结束、因此您的程序永远是一对一的。 您不会看到、因为字符串[]末尾的'\r\n'是一个常量、因此"旧"值与"新"值相同。

    我可以看到、使该模型正常工作的唯一方法是为每个字节使用 DMADD=0并设置(DMAEN|DMAREQ)。 更简单的解决方案(正如 David 所说的)是使用 TXIFG 作为触发器、并通过使用 DMADD=0和设置 DMAEN (这是 DMA 的设计目标)来发送 DMA1SZ=6的每条(完整)线路。

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

    我按照您和 David 的建议使用 TXIFG 作为触发器和批量传输6个字节、现在一切都正常。

    但是、我觉得有点令人困惑的是 MSP430用户指南说:"如果 UCAxTXIE 被置位、UCAxTXIFG会触发一个传输。" 因此 、我们正在使用中断标志、但我们必须禁用中断才能使其正常工作。 我不清楚设置中断标志和执行 ISR 是两个独立的事情

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

    TXIFG-AS 触发器和 TXIFG-AS 中断源(如果同时使用)之间存在定义冲突、设计人员决定选择此选项。

    知道您引用的那个段存在很有用、但最好自己选择(仅)其中一个用途。

    如果需要说明:一般而言,IFG (事件)的发生独立于相应的 IES。 通常会为您不感兴趣的事件设置许多 IFG。