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 的1k 个样本

Guru**** 2560390 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/593279/msp430f5529-convert-and-store-1k-samples-from-adc

器件型号:MSP430F5529

我正在尝试使用 ADC 从模拟加速计采集1k 个样本、并将其存储在数组中以供进一步处理、然后通过 MQTT 发送。 我是这方面的初学者、尝试了很多方法、包括使用计时器、但没有成功。 因此、我最终得到了以下代码、这些代码起作用、但我想知道这是否是一种好方法。

代码如下:我已使用 ACLK 时钟设置 ADC

static void setup_ADC(void){
    ADC12CTL0 = ADC12SHT02 + ADC12ON;         // Sampling time, ADC12 on
    ADC12CTL1 = ADC12SHP + ADC12SSEL_1;       // Use sampling timer; Pulse Sample Mode; ADC12SHT02 decides the interval of the sampling timer
                                              // ACLK (32.768 kHz) selected as the clock source
    ADC12IE = 0x01;                           // Enable interrupt
    ADC12CTL0 |= ADC12ENC;
    P6SEL |= 0x01;                            // P6.0 ADC option select
    }

main 中的无限 while 循环具有 MQTT 函数、应在存储1000个样本后运行。

while(1){
        /* Signal processing and communication only to be done after collecting 1K samples */
        if (SamplesDone){
            /* Send the values through mqtt */
            mqtt();
            samples_count = 0;
        }

        ADC12CTL0 |= ADC12SC;                   // Start sampling/conversion
        __bis_SR_register(LPM0_bits + GIE);     // LPM0, ADC12_ISR will force exit
        __no_operation();                       // For debugger
    }

在 ISR 中、我只需将数据移动到可存储多达1000个元素的数组中

#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = ADC12_VECTOR
__interrupt void ADC12_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(ADC12_VECTOR))) ADC12_ISR (void)
#else
#error Compiler not supported!
#endif
{
    switch(__even_in_range(ADC12IV,34))
    {
    case  0: break;                           // Vector  0:  No interrupt
    case  2: break;                           // Vector  2:  ADC overflow
    case  4: break;                           // Vector  4:  ADC timing overflow
    case  6:                                  // Vector  6:  ADC12IFG0
        adc_value = ADC12MEM0 & 0x0FFF; // keep only low 12 bits; Move results, IFG is cleared
        adc_values[samples_count] = adc_value;
        samples_count += 1;
        /* Set the flag when 1000 samples are collected */
        if (samples_count == 999) {
            SamplesDone = 1;
        }
        __bic_SR_register_on_exit(LPM0_bits);   // Exit active CPU
    case  8: break;                           // Vector  8:  ADC12IFG1
    case 10: break;                           // Vector 10:  ADC12IFG2
    case 12: break;                           // Vector 12:  ADC12IFG3
    case 14: break;                           // Vector 14:  ADC12IFG4
    case 16: break;                           // Vector 16:  ADC12IFG5
    case 18: break;                           // Vector 18:  ADC12IFG6
    case 20: break;                           // Vector 20:  ADC12IFG7
    case 22: break;                           // Vector 22:  ADC12IFG8
    case 24: break;                           // Vector 24:  ADC12IFG9
    case 26: break;                           // Vector 26:  ADC12IFG10
    case 28: break;                           // Vector 28:  ADC12IFG11
    case 30: break;                           // Vector 30:  ADC12IFG12
    case 32: break;                           // Vector 32:  ADC12IFG13
    case 34: break;                           // Vector 34:  ADC12IFG14
    default: break;
    }
}

我非常感谢对这种做法提出的任何建议/评论。

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

    Deepak、您好!

    我建议您考虑使用 DMA 将 ADC 转换传输到 RAM。 您可以使用 ADC12IFG 来触发一个从 ADC12MEM0到 ADC_Values 数组的 DMA 传输。 您还可以将 DMA 设置为在 CPU 将1000次转换移动到 RAM 后中断 CPU、然后从 LPM 唤醒。 首先阅读 《MSP430x5xx 和 MSP430x6xx 系列用户指南》(修订版 P)的 DMA 部分 、如果您有任何疑问、请告知我。

    这将在运行期间节省功耗和 CPU 周期。

    此致、

    Caleb Overbay

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

    您好 Caleb、

    非常感谢您的回答。 根据您的响应、我尝试实施 DMA、但似乎缺少一些东西。

    下面是在 main 内完成的 DMA 设置:

    /* Configure DMA to transfer A2 and A3 values from the ADC to RAM */
        DMACTL0 = DMA0TSEL_24;                       // ADC12IFGx triggers DMA0
        DMA0SA = ADC12MEM0;                          // Source address
        DMA0DA = &adc_values[0];                     // Destination address
        DMA0SZ = 2;                                  // Transfer 2 16 bit words per trigger
    

    在 ADC12_ISR 中、我有以下行

    DMA0CTL = DMADT_0 | DMADSTINCR_3 | DMASRCINCR_3 | DMAEN | DMAIE; // Single transfer, increment dest addr, enable DMA & interrupt
    

    以下是我所面临的问题,我非常感谢你的建议:

    1. 我遇到的主要问题是、我在无限 while 环路内有一个函数调用、用于发送这些值。 因此、我无法在重复模式下使用 ADC。 ADC 和 DMA 必须在单个模式下工作、从而相互启用 ISR。 如果我理解正确,流程应该是这样的,ADC ISR->ADC_ISR 调用 DMA_ISR->值从 ADC12MEM0复制,从而清除 ADC->DMA_ISR exit->ADC_ISR exit->ADC_ISR exit->function ininfinite 而 main 循环发送值时的 IFG 标志

      但是、在调试期间、看起来根本不会调用 DMA。

    2. "您还可以将 DMA 设置为在 CPU 将1000次转换移动到 RAM 后中断 CPU、然后从 LPM 唤醒"

      如我的代码中所示、我使用计数器 SAFESS_COUNT 来处理收集1000个样本的情况。 您是说我需要将其移至 DMA ISR (我当前不执行任何操作)吗? 我从 ADC 获取一个样本、然后使用 DMA 将其移动到数组。 我是否仍可以在这种情况下实施您的建议?

    3. 以下行显示警告

      DMA0SA = ADC12MEM0;

      警告#515-D:无法将类型为"无符号整型"的值分配给类型为"__SFR_FARPTR"的实体

      DMA0DA 线也是如此。

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

    Deepak、您好!

    首先、看起来您没有正确设置源地址和目标地址。 请查看 MSP430F55xx_DMA_04.c 示例、其中显示了如何使用 DMA 从 ADC12执行重复单次传输。 代码示例链接

    要回答您的问题:

    1. 我遇到的主要问题是、我在无限 while 环路内有一个函数调用、用于发送这些值。 因此、我无法在重复模式下使用 ADC。 ADC 和 DMA 必须在单个模式下工作、从而相互启用 ISR。 如果我理解正确,流程应该是这样的,ADC ISR->ADC_ISR 调用 DMA_ISR->值从 ADC12MEM0复制,从而清除 ADC->DMA_ISR exit->ADC_ISR exit->ADC_ISR exit->function ininfinite 而 main 循环发送值时的 IFG 标志
      1. 只有在有1000个值时才发送这些值、对吧? 在这种情况下、您可以将 ADC 设置为重复模式、将 DMA 设置为重复单通道模式。 然后、还将 DMAxSZ 寄存器设置为1000。 然后、当1000个结果已传输到存储器时、您将获得 DMA 中断。 然后、在 ISR 中、您可以使用 main while (1)中的函数唤醒 CPU 并发送1000个值。

    2. 如 我的代码所示、"您还可以设置 DMA 以在 CPU 将1000次转换移动到 RAM 后中断 CPU "、我正在使用计数器 SAMPS_COUNT 在收集1000个样本时进行处理。 您是说我需要将其移至 DMA ISR (我当前不执行任何操作)吗? 我从 ADC 获取一个样本、然后使用 DMA 将其移动到数组。 我是否仍可以在这种情况下实施您的建议?
      1. DMAxSZ 跟踪已传输的样本数。 将其设置为1000、在传输1000个样本后、将触发 DMA 中断。 在中断内部、我建议禁用 ADC、然后在退出时唤醒 CPU。 当 CPU 处于唤醒状态时、您可以将1000个样本发送到您的 MQTT 函数。  

    3. 以下行显示警告: DMA0SA = ADC12MEM0;
      1. 您正在错误地设置源地址和目标地址。 请参阅我之前链接的示例、了解正确的方法。

    最后、DMA 是一个相当复杂的模块、但在正确使用时功能强大。 在我完全了解如何正确使用之前、我先阅读了用户指南的 DMA 部分。 我建议您也多次查看它。 请随时向我介绍您的最新进展、如果您有任何疑问、请告知我。  

    此致、  

    Caleb Overbay

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

    您好 Caleb、

    非常感谢您的帮助! 我多次参考用户指南、尝试整合您建议的更改。

    以下是采用单通道重复转换模式时 ADC 设置的外观:

    静态空 setup_ADC (void){
    ADC12CTL0 = ADC12SHT02 + ADC12MSC+ ADC12ON;//采样时间、
    ADC12CTL1上的 ADC12 = ADC12SHP + ADC12SSEL_1 + ADC12CONSEQ_2;//使用采样计时器;脉冲采样模式;ADC12T0
    
    = ADC12S02 = ADC12SSEL_1 + ADC12K/ ADC12THR0的重复采样间隔(ADC12T0 = ADC12THRK = ADC12T0 = ADC12THRK)//启用中断
    ADC12CTL0 |= ADC12ENC;
    P6SEL |= 0x01; // P6.0 ADC 选项选择
    } 

    具有重复单次传输模式的 DMA 设置如下所示:

    静态空 setup_DMA (空){
    //配置 DMA 以将值从 ADC 传输到 RAM *
    / DMACTL0 = DMA0TSEL_24; // ADC12IFGx 触发 DMA0
    DMACTL4 = DMARMWDIS; //读取-修改-写入禁用
    DMA0CTL &&~DMAIFG;
    DMA0CTL = DMADT_4 | DMADSTINCR_3 | DMAEN | DMAIE;//重复单次传输、增量 addr、启用 DMA 和中断
    DMA0SZ = 1000; //每个触发器传输2 16位字
    __data16_write_addr ((无符号短整型)&DMA0SA,(无符号长整型)&ADC12MEM0);//源块地址
    __data16_write_addr ((无符号短整型)&DMA0DA、(无符号长整型)&ADC_Values);//目标单地址
    } 

    在主函数内、按以下顺序调用它们:

    Setup_adc ();
    setup_dma ();
    
    while (1){
    //* Start ADC*/
    ADC12CTL0 |= ADC12SC; //开始采样/转换
    
    __bis_SR_register (LPM0_bits + GIE); // LPM0、ADC12_ISR 将强制退出
    __NO_OPERATION (); //对于调试器
    } 

    只有一个 ISR  

    #if defined (__TI_Compiler_version__)|| defined (__IAR_systems_icc_)
    #pragma vector=DMA_vector
    __interrupt void DMA_ISR (void)
    #Elif defined (__GNU__)
    void __attribute__((interrupt (DMA_vector))) dma_ISR (void 编译
    
    器#else!
    #endif
    {
    switch (__even_in_range (DMAIV、16))
    {
    case 0:break;
    case 2: // DMA0IFG = DMA 通道0
    __BIC_SR_REGISTER_ON_EXIT (LPM0_Bits);//退出活动 CPU
    情况4:中断; // DMA1IFG = DMA 通道1
    情况6:中断; // DMA2IFG = DMA 通道2
    情况8:中断; // DMA3IFG = DMA 通道3
    情况10:中断; // DMA4IFG = DMA 通道4
    情况12:中断; // DMA5IFG = DMA 通道5
    情况14:中断; // DMA6IFG = DMA 通道6
    情况16:中断; // DMA7IFG = DMA 通道7
    默认值:中断;
    }
    } 

    我现在有以下问题,希望你能提出同样的建议/指示:

    调试器卡在主函数内的以下行、永远不会进入 ISR。 (代码中只有 DMA_ISR。)

    _bis_SR_register (LPM0_bits + GIE);

     MSP430F55xx_dma_04.c 示例也是如此。

    由于 DMAEAN=1以及 ADC12IFG0被 置位、不应该 调用 DMA_ISR? 我是否还需要启用/设置其他内容?

    3."在中断内部、我建议禁用 ADC "

    因此、如果我理解正确、在 DMA_ISR 内部、我需要执行一些操作、例如清除 ADC12ENC 位。

    对文档的反馈:
    在 slau208p 的第390页中、"11.2.3启动 DMA 传输"一节显示"只有当 DMACTLx DMAEN 位为0时、DMAxTSEL 位才应被修改。" 我认为它应该是 DMAxCTL DMAEN。

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

    首先、在我提供的示例中、似乎有一个错误。 在进入 LPM 之前、绝不会启动 ADC 转换。 因此、不会触发 DMA 传输。 在进入 LPM0之前、您可以通过添加 ADC12CTL0 |= ADC12SC 来修复此错误。 感谢您将此事提请我注意。

    我看到您提到 DMA ISR 是您系统中当前唯一的一个、但您仍然可以通过以下行启用 ADC 中断:ADC12IE = 0x01;
    这会导致跳转至未定义的 ISR、并随后捕获 CPU。 如果您从代码中删除此行、您将看到 DMA 中断触发!

    是的、我建议在 DMA ISR 内部通过清除 ADC12ENC 位来禁用转换。 然后、在 main 中开始新的转换之前、重新启用转换。 除此之外、您的代码看起来很好!

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

    您好 Caleb、

    是的、它能正常工作! 非常感谢您的参与。

    现在、我正在努力实现 MSP430的最佳节能效果和效率。 但是、这些是我目前面临的障碍:

    我能够通过 DMA 读取这些值、但是代码看起来很慢。  我正在共享最近的代码以显示此问题。 输出"inside main"在串行监视器上显示、就像每秒一次一样。

    while (1){
    cli_Write ((_u8 *)" inside main \n\r\n);
    DMA0SZ = 1000; //每个触发器传输2 16位字
    //读取 ADC 值*/
    ADC12CTL0 |= ADC12ENC | ADC12SC;//启用并开始采样/转换
    __bis_SR_register (LPM0_Bits + GIE); // LPM0、DMA_ISR 将强制退出
    __NO_OPERATION (); //对于调试器
    } 

    ADC 和 DMA 设置如下所示

    静态空 setup_DMA (空){
    //配置 DMA 以将值从 ADC 传输到 RAM */
    DMA0CTL &&~DMAEN;//仅当 DMACTLx DMAEN 位为0时才应修改 DMAxTSEL *
    (~DMAEN){
    DMACTL0 = DMA0TSEL_24; // ADC12IFGx 触发 DMA0
    }else{
    cli_Write ((_u8 *)"无法修改 DMAxTSEL。 DMA 已启用\n\r\n);
    }
    DMACTL4 = DMARMWDIS; //读取-修改-写入禁用
    DMA0CTL &&~DMAIFG;
    DMA0CTL = DMADT_4 | DMADSTINCR_3 | DMAEN | DMAIE;//重复单次传输,递增无
    
    符号地址,启用 DMA 和中断__data16_WRITE_addr (&DMA0CMAEN);无符号地址(&DMA0CADDR_SHORT);无符号地址(&DMA0ADDRE);无符号地址(&DMA0ADDR_D16)//目标单地址
    }
    
    静态空 setup_ADC (void){
    ADC12CTL0 = ADC12SHT0_0 | ADC12MSC | ADC12ON;//采样时间、多个采样和转换、
    ADC12CTL1上的 ADC12 = ADC12SHP | ADC12SSEL_1 | ADC12DIV1 | ADC12C12C12C12C12Q脉冲模式;/采样模式;//使用脉冲;// ADC12SHT02决定采样定时器的间隔
    //选择 ACLK (32.768kHz)作为时钟源
    //将时钟除以2。 新时钟在16kHz 左右成为 ACLK/2
    //重复单通道
    P6SEL |= 0x01; // P6.0 ADC 选项选择
    } 

    在 DMA ISR 内部、我只使用以下行启用 ADC 转换

    ADC12CTL0 &=~ADC12ENC;//禁用转换

    2.在 ADC 中、我目前正在使用 ADC12SHT0_0。 我知道当 ADC12SHP 被置位时、即脉冲采样模式、 ADC12SHT0x 决定采样定时器的间隔。 我应该使用哪个 ADC12SHT0x、这样 SAMPCON 的采样间隔就足够了?

    3.在 LPM0中 MCLK 被禁用、但是 DMA 需要2个 MCLK 周期。 因此、很显然、它在不唤醒 CPU 的情况下启用它、然后禁用它。 它是否会影响效率? 我正在寻找优化代码保持低功耗模式的时间。 如果您有相同的指导原则、请告诉我。

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

    Deepak、您好!  

    我不会说您的代码运行缓慢、只需一段时间即可对1000个样本进行采样、转换、然后将其传输到存储器。 尤其是当您按照上述注释的建议以16kHz 的频率运行 ADC 时。 如果您希望代码运行得更快、则需要增加 ADC 时钟源并尽可能缩短采样保持时间。  

    要将采样保持时间降至最低、请查看《MSP430F5529用户指南》的28.2.5.3节。 本简短部分介绍了如何计算应用所需的最小采样时间。  

    最后、在这种类型的应用中、使用 DMA 是针对低功耗进行优化的最佳方式。 当传输数据需要2个 MCLK 周期时、CPU 在传输期间保持在 LPM0中。

    此致、  
    Caleb Overbay

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

    这方面的进展如何? 您是否还有未回答的问题?

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

    您好 Caleb、

    很抱歉、我以为我已经与您共享了更新、但我错了。 一切都很棒!  

    我要再次感谢您为在我的项目中充分利用 MSP430而提供的所有帮助。

    此致、

    Deepak Koranga