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.

[参考译文] EK-TM4C1294XL:在 DMA 目标缓冲器中保持 ADC 通道的顺序

Guru**** 2529810 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/921422/ek-tm4c1294xl-keeping-the-order-of-adc-channel-in-the-dma-destination-buffer

器件型号:EK-TM4C1294XL

您好!

I´m 使用 ADC 0和采样序列发生器0 (6个通道)、ADC 1和采样序列发生器2 (4个通道)。 要´m 数据、请使用支持乒乓模式的 DMA。 我在相应的中断处理程序中重新启用传输通道。 ´s、我的问题是、DMA 的目标缓冲区中的 ADC 通道顺序与采样序列发生器中的顺序不同、因为它总是在变化。  

示例:

采样序列发生器   ->      DMA 目标缓冲器

1.通道1.                     通道2.

2.通道2.                     通道3.

3.通道3.                     通道1

在目的缓冲区中获取与采样序列发生器中相同的顺序是否有任何技巧? 因为如果我想处理数据、我必须知道目标缓冲区中的哪个位置是哪个通道。

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

    您好!

     我从未见过这样的问题。 我有一些问题。

     -该问题是否同时发生在 ADC0和 ADC1上,还是只发生在其中一个 ADC 上?

     -您是否看到两个乒乓缓冲器都有同样的问题? 使用您的示例,您应该会在缓冲区 A 和 B 中看到 CH1->CH2->CH3。现在,您说它被重新排序为 CH2->CH3->CH1。 您是否在缓冲器 A 和缓冲器 B 中看到错误的顺序?

     -您如何知道目的缓冲区的顺序与采样序列发生器的顺序相比是错误的? 你能做一个实验吗? 强制 CH1为0V、CH2为1.5V、CH3为3V? 我只是想确保您没有误解订单。 例如、如果所有三个通道都具有非常相似的输入范围、那么如何确定目标缓冲器中哪一个通道。  

     -您能否显示如何使用 ADCSequenceStepConfigure()配置序列发生器的代码?

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

    您好、Charles、

    -是的,这两个 ADC 都出现了问题。

    -是的,两个缓冲区都出现了问题。

    -我将一个通道连接到0、61V (采样序列发生器中的第四个通道)、将其他通道连接到1、65V。您可以看到、它会随机更改目标缓冲器中的位置。  

    现在、这是第二个地方。

    现在、它是第三个位置。

    - ADC 和 DMA 的初始化:

    感谢你的帮助。

    此致、

    康斯坦丁

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

    您好 Constantin、

     我认为问题可能出在您使用 ADC_TRIGGER_ALEAlways 触发采样的方式上。 您能否使用 ADC_TRIGGER_TIMER 进行实验? 尝试使用计时器创建周期性触发器、看看这是否会产生影响。 在我看来、当 UDMA 仍在传输数据时、您会继续触发 ADC 采样。 请参阅最新 TivaWare (2.2.0.295)版本中的 ADC_UDMA_pingpong 示例、其中它使用计时器来触发 ADC 采样。  

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

    您好、Charles、

    谢谢。 我将于n´t 周上班。 我将在尝试后立即分享结果。

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

    您好 Constantin、

     感谢大家的观看。  请告诉我您的结果。  

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

    您好、Charles、

    我创建了一个周期性计时器、每100kHz 触发一次 ADC、但结果仍然与我使用 ADC_TRIGGER_AUSE 时相同。 还有其他我可以尝试的吗?

    此致、

    康斯坦丁

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

    您好 Constantin、

     我从未见过这样的问题、因此我需要您进行一些实验。  

     -我假设您使用 CPU 读取 ADC 数据,它们将按正确的顺序读取。 是这样吗? 我要求的原因是隔离 UDMA 和 ADC 之间的问题。 如果使用 CPU 读取错误的顺序、则问题不在于 UDMA。 但是、我倾向于认为问题更有可能是 uDMA。

     -为了简单起见,是否可以暂时删除 ADCHardareOversampleConfigure()? ADCHardareOversampleConfigure()将降低 ADC 采样率。 现在、让我们保持 ADC 以尽可能快的速度运行。  

     -尝试使用3个 ADC 通道、每个通道分别驱动为0、1.65V 和3.3V、并使用 UDMA 读取它们。 我想知道序列发生器中的通道数量是否对结果有任何影响。  

     -如果可能,您能否尝试仅进行常规 UDMA 传输而不是乒乓模式。 本实验的目的是验证乒乓模式是否对排序有任何影响。 此时、我们不关心 UDMA 吞吐量。  

      

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

    您好、Charles、

    -如果我使用 ADC 时钟频率:16MHz 与 ADC_CLOCK RATE 一半、则通道的读取顺序正确。 但是、如果我增加频率或速率、通道的顺序会在第二个运行中发生变化。

    它不会n´t 任何东西。

    -第一次运行与乒乓模式下的运行效果非常好。 在第二个运行中、通道的顺序发生变化。 在本测试中、我使用了4个通道、其中一个连接到1、65V、两个连接到0、一个连接到3、3V

    第一个照射行程一直到黑色照射野、正如您在第二个照射行程中看到的、它会更改顺序。 这同样适用于三个通道。 如果您想查看代码、请查看以下代码:

    e2e.ti.com/.../usb_5F00_dev_5F00_cdcserial.c

    感谢你的帮助。

    此致、

    康斯坦丁

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

    [引用 USER="Constantin Weies"]-如果我使用 ADC 时钟频率:16MHz 的 ADC_CLOCK_RATE 一半、则通道按正确的顺序读取。 但是、如果我增加频率或速率、则通道的顺序会在第二次运行中发生变化。[/QUERP]

    您好 Constantin、

     这是通过使用 CPU 进行读取来实现的吗?   如果是这种情况、那么我们可以简要地从图片中删除 UDMA、因为使用 CPU 将读取不同的顺序。  

     我会快速浏览您的代码。 到目前为止、我从未意识到您的 ADC/UDMA 代码只是您的 USB CDC 应用的一部分。 我不确定您是否通过 USB 发送 ADC 数据、并发现数据顺序错误。 由于 USB 已进入图片、我不知道订单是否因 USB 而更改。  您是否可以通过创建仅侧重于 ADC 和 UDMA 的测试用例来保持简单?  

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

    您好、Charles、

    是的、这是通过使用 CPU 进行读取来完成的。 我创建了一个没有整个 USB 部件的测试用例、但结果仍然相同。

    此致、

    康斯坦丁  

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

    您好 Constantin、

     如果 CPU 也会读取错误的顺序、那么我们甚至可以不使用 UDMA。 这是我第一次看到报告、ADC 可以将采样通道以错误的顺序存储在 ADC FIFO 中。 我有一些问题。

      下面是您在7月23日发送的早期代码。 将步骤0配置为 CTL_CH3、将步骤1配置为 CTL_CH2等。 这是否有意以相反的顺序完成?

    ADCSequenceStepConfigure (ADC0_BASE、2、0、ADC_CTL_CH3);
    ADCSequenceStepConfigure (ADC0_BASE、2、1、ADC_CTL_CH2);
    ADCSequenceStepConfigure (ADC0_BASE、2、2、ADC_CTL_CH1);
    ADCSequenceStepConfigure (ADC0_BASE、2、3、ADC_CTL_CH0| ADC_CTL_IE | ADC_CTL_END);

    您的 VCO 为240Mhz、如下面的 SYSCLK 配置所示。 该时钟配置正常。 这里没有问题。

    G_ui32SysClock = MAP_SysCtlClockFreqSet ((SYSCTL_XTAL_25MHz |
    SYSCTL_OSC_MAIN |
    SYSCTL_USE_PLL |
    SYSCTL_CFG_VCO_240)、120000000);

     您还为 ADC 时钟配置编写了以下代码。 这是什么使它工作、是什么使它不工作? 我认为下面的 ADC 时钟配置是正确的。 它将 ADC 时钟配置为16MHz、这应该是可以的。  我只是不知道您是否已经尝试过可能导致 ADC 时钟运行过快或过短的实验。  

    ADCClockConfigSet (ADC0_BASE、ADC_CLOCK_SRC_PLL| ADC_CLOCK_RATE_HALT_Half、15);  

    前面讲过、我想知道是否使用 PIOSC 作为时钟源来配置 ADC 时钟。  

    ADCClockConfigSet (ADC0_BASE、ADC_CClock_SRC_PIOSC | ADC_CClock_RATE_FULL、1);//对于1Msps、PIOSC=16MHz

    您能否调用 SysCtlVCOGet 来找出 VCO 频率是多少? 当您调用 ADCClockConfigSet 时、ADC 时钟取自 VCO。  

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

    您好、Charles、

      n´t 的顺序并非有意采用相反的顺序、但如果我更改顺序、它会产生很大的差异。 VCO 频率符合预期。

    它n´t 我是否使用 PIOSC 作为时钟源。

    [引用用户="Charles Tsaaa"]我不知道您是否尝试过可能导致 ADC 时钟运行过快或不这么做的实验

    我使用的最高频率为30MHz、在数据表中、它表示可以将频率提高到32MHz。 您是否有这样的开发板来尝试通道的顺序是否发生变化?

    此致、

    康斯坦丁

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

    您好 Constantin、

     您的频率正常。  

     我有可以测试的 Launchpad、但在家里工作时、我需要有一个简单的测试环境。 您能否向我发送一个 CCS 项目、该项目将 ADC 通道数限制为仅3个、并且仅使用 CPU 读取 ADC FIFO? 我可以设法为这三个通道提供不同的电压(例如0V、1.6V、3.3V)。 如果您的信道太多、那么我无法在家中使用有限的资源创建输入。 同样、请使用三个通道尽可能简单、并且不使用 DMA、如果这是一种设置、您可以重复订购问题。

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

    您好 Constantin、

     我创建了一个小示例。 项目的目的只是为了显示从 FIFO 中读出的数据的顺序是否正确。 我使用三个通道。 我只使用 CPU 读取 ADC FIFO。 我不使用 UDMA、因为您说过、即使是 CPU、读取的顺序也不正确。 因此、这不是 uDMA 问题、我想将 UDMA 留给调试。 我尝试使用 PIOSC 或 PLL 作为时钟源、数据在 ADC FIFO 中的存储顺序没有差异。

     PE3 -> AIN0 (连接至0V)

     PE2 -> AIN1 (连接至~1.6V)

     PE1 -> AIN2 (连接至3.3V)

    我看到它们以正确的顺序读出。 您能否与您的计划进行比较并查看是否有任何差异?

    这是我的代码。

    //
    //
    //// single_ended.c -演示如何为
    //配置 ADC 的示例 单端操作。
    //
    //版权所有(c) 2010-2017 Texas Instruments Incorporated。 保留所有权利。
    //软件许可协议
    //
    以源代码和二进制形式重新分发和使用,无论是否
    进行//修改,只要
    满足以下条件//:
    //
    重新分发源代码必须保留上述版权
    //声明、此条件列表和以下免责声明。
    //
    //二进制形式的再发行必须复制上述版权
    //声明、此条件列表和//
    
    分发随附的//文档和/或其他材料中的以下免责声明。
    ////
    未经
    
    事先书面许可,不能使用德州仪器公司的名称或//其贡献者的名称来认可或推广源自此软件的产品//。
    ////
    本软件由版权所有者和贡献者提供
    //“按原样”,不
    
    承认任何明示或暗示的保证,包括但不限于//适销性和对//特定用途适用性的暗示保证。 在任何情况下、版权
    //所有者或贡献者都不对任何直接、间接、偶然、
    //特殊、模范、 或相应的损害(包括但不
    限于采购替代产品或服务;丧失使用、
    //数据或利润; 或业务中断)、无论
    
    出于何种原因使用
    本软件(即使被告知可能会造成此类损坏)、还是出于任何原因而产生的任何//责任理论(无论是合同、严格责任还是侵权行为)//(包括疏忽或其他)。
    //
    //这是 Tiva 固件开发包的修订版2.1.4.178的一部分。
    ////
    *****************
    
    #include 
    #include 
    #include "inc/hw_memmap.h"
    #include "driverlib/adc.h"
    #include "driverlib/gpio.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/sysctl.h"
    #include "utils/uartdio.h"
    
    
    
    //
    //! \addtogroup ADC_examples_list
    //! 

    单端 ADC (single_ended)

    //! //! 此示例展示了如何将 ADC0设置为单端输入并采用 //! AIN0/PE3上的单个采样。 //! //! 此示例使用以下外设和 I/O 信号。 您必须 //! 查看这些内容并根据您自己的董事会需要进行更改: //! - ADC0外设 //! - GPIO 端口 E 外设(用于 AIN0引脚) //! - AIN0 - PE3 //! //! 以下 UART 信号仅配置为显示控制台 //! 消息。 操作 //! ADC。 //! - UART0外设 //! - GPIO 端口 A 外设(用于 UART0引脚) //! - UART0RX - PA0 //! - UART0TX - PA1 //! //! 此示例使用以下中断处理程序。 要使用此示例 //! 在您自己的应用程序中、您必须将这些中断处理程序添加到 您的//! 矢量表。 //! -无。 //// ***************** #define USER_LED1 GPIO_PIN_2 //********* // //此函数将 UART0设置为用于控制台,以便 在示例运行时显示信息//。 //// ***************** void InitConsole (void) { // //启用用于 UART0引脚的 GPIO 端口 A。 // TODO:将其更改为您正在使用的 GPIO 端口。 // SysCtlPeripheralEnable (SYSCTL_Periph_GPIOA); // //为端口 A0和 A1上的 UART0功能配置引脚复用。 //如果您的器件不支持引脚复用、则无需执行此步骤。 // TODO:更改此选项以选择您正在使用的端口/引脚。 // GPIOPinConfigure (GPIO_PA0_U0RX); GPIOPinConfigure (GPIO_PA1_U0TX); // //启用 UART0以便我们可以配置时钟。 // SysCtlPeripheralEnable (SYSCTL_Periph_UART0); // //使用内部16MHz 振荡器作为 UART 时钟源。 // UARTClockSourceSet (UART0_BASE、UART_CLOCK_PIOSC); // //为这些引脚选择替代(UART)功能。 // TODO:更改此选项以选择您正在使用的端口/引脚。 // GPIOPinTypeUART (GPIO_Porta_base、GPIO_PIN_0 | GPIO_PIN_1); // //初始化控制台 I/O 的 UART // UARTStdioConfig (0、115200、16000000); } //********* // //为单端输入和单个采样配置 ADC0。 一旦//样本就绪、将设置一个中断标志。 使用轮询方法 //数据将被读取,然后通过 UART0显示在控制台上。 //// ***************** int main (void) { #if defined (target_IS_TM4C129_RA0)|| \ 已定义(TARGET_IS_TM4C129_RA1)|| \ 已定义(TARGET_IS_TM4C129_RA2) uint32_t ui32SysClock; #endif // //该数组用于存储从 ADC FIFO 读取的数据。 // uint32_t pui32ADC0Value[3]; // //使用 PLL 将时钟设置为以20MHz (200MHz/10)运行。 时间 //使用 ADC,您必须使用 PLL 或提供16 MHz 时钟 //源。 // TODO:必须更改 SYSCTL_XTAL_VALUE 以匹配的值 板上的//晶体。 // #if defined (target_IS_TM4C129_RA0)|| \ 已定义(TARGET_IS_TM4C129_RA1)|| \ 已定义(TARGET_IS_TM4C129_RA2) ui32SysClock = SysCtlClockFreqSet ((SYSCTL_XTAL_25MHz | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240)、120000000); #else SysCtlClockSet (SYSCTL_SYSDIV_10 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHz); #endif // //设置用于显示消息的串行控制台。 这是 //仅用于此示例程序,ADC 操作不需要。 // InitConsole(); // //在控制台上显示设置。 // UARTprintf ("ADC ->\n"); UARTprintf ("类型:单端\n"); // //必须启用 ADC0外设才能使用。 // SysCtlPeripheralEnable (SYSCTL_Periph_ADC0); SysCtlPeripheralEnable (SYSCTL_Periph_GPIOE); /* * ADC 时钟以30Mhz 运行 * //ADCClockConfigSet (ADC0_BASE、ADC_CLOCK SRC_PLL | ADC_CLOCK RATE_FULL、8); ADCClockConfigSet (ADC0_BASE、ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL、1); /* PE3 - AIN0 * PE2 - AIN1 * PE1 - AIN2 * GPIOPinTypeADC (GPIO_Porte _BASE、GPIO_PIN_3 | GPIO_PIN_2 | GPIO_PIN_1); ADCSequenceConfigure (ADC0_BASE、0、ADC_TRIGGER_PROCESSOR、0); ADCSequenceStepConfigure (ADC0_BASE、0、0、ADC_CTL_CH0); ADCSequenceStepConfigure (ADC0_BASE、0、1、ADC_CTL_CH1); ADCSequenceStepConfigure (ADC0_BASE、0、2、ADC_CTL_CH2 | ADC_CTL_IE | ADC_CTL_END); ADCSequenceEnable (ADC0_BASE、0); ADCIntClear (ADC0_BASE、0); // //永远对 AIN0/AIN1/AIN2采样。 显示控制台上的值。 // SysCtlPeripheralEnable (SYSCTL_Periph_GPION); while (!SysCtlPeripheralReady (SYSCTL_Periph_GPION)) { } // //为 LED 操作配置 GPIO 端口。 // GPIOPinTypeGPIOOutput (GPIO_PORTN_BASE、USER_LED1); // while (1) { GPIOPinWrite (GPIO_PORTN_BASE、USER_LED1、USER_LED1); // //触发 ADC 转换。 // ADCProcessorTrigger (ADC0_BASE、0); // //等待转换完成。 // while (!ADCIntStatus (ADC0_BASE、0、false)) { } GPIOPinWrite (GPIO_PORTN_BASE、USER_LED1、0); // //清除 ADC 中断标志。 // ADCIntClear (ADC0_BASE、0); // //读取 ADC 值。 // ADCSequenceDataGet (ADC0_BASE、0、pui32ADC0Value); // //在控制台上显示 AIN0 (PE3)数字值。 // UARTprintf ("AIN0 =%4D\n"、pui32ADC0Value[0]); UARTprintf ("AIN1 =%4D\n"、pui32ADC0Value[1]); UARTprintf ("AIN2 =%4D\n"、pui32ADC0Value[2]); // //此函数提供了生成恒定长度的方法 //延迟。 函数延迟(以周期为单位)= 3 *参数。 延迟 //任意地250ms。 // #if defined (target_IS_TM4C129_RA0)|| \ 已定义(TARGET_IS_TM4C129_RA1)|| \ 已定义(TARGET_IS_TM4C129_RA2) SysCtlDelay (ui32SysClock/50000); #else SysCtlDelay (SysCtlClockGet ()/12); #endif } }

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

    您好、Charles、

    你是对的。 n´t 我使用 ADC_TRIGGER_PROCESSORT (这是差异)、阶数不会改变。 但是、如果我将 ADC 的触发器更改为 ADC_TRIGGER_Always 或 ADC_TRIGGER_TIMER、则顺序会发生变化。 函数  ADCSequenceDataGet ()给出了多少个采样被复制到缓冲区。 您可以看到、它有时会将四个以上的样本复制到缓冲区、以便在下一次触发发生时、顺序发生变化。 我´s 这是问题所在。  

    此致、

    康斯坦丁

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

    您好 Constantin、

     我看不到您上传的图像。 我想我们正在取得一些进展。

     ADC_TRIGGER_Always 表示它正在持续采样。 如果您不小心、它将与您的程序流不同步。 假设在序列发生器中有5个要转换的通道。 在全部五个通道被转换后、一个中断被触发或者一个 DMA 请求被生成到 UDMA。 但是、ADC 将立即再次重复通道采样。 到 DMA 或 CPU 读取 ADC FIFO 时、可能会有更多的采样被转换。 您可能已经为 CPU 预留了5个缓冲区进行读取、但 FIFO 实际上包含5个以上的转换数据。

     让我们使用一个示例。

     您正在转换通道 AIN0、1、2、3、4。

     2在全部五个通道被转换后、生成一个中断。  

     到 CPU 准备好读取 FIFO 时、实际上有7个通道被转换、它们是 AIN0、1、2、3、4、0、1

     4.您发出 ADCSequenceDataGet,但只有前五个数据被传输到缓冲区。 第6个和第7个采样永远不会被读取和丢失。

     由于 ADC 在后台继续采样、下一通道(AIN2)转换数据将存储在第一个 FIFO 位置。  

     6、到 ADC 产生下一个中断时、FIFO 将包含 AIN2、3、4或 AIN2、3、4、0、1。

     当 CPU 将数据传输到缓冲区时、缓冲区看起来是不按顺序的(AIN2、3、4、0、1、而不是 AIN0、1、2、3、4)

    我不知道如何使用 ADC_TRIGGER_TIMER。 如果使用定时器作为触发器、则需要确保在完成 FIFO 读取之前定时器不会触发。 您必须进行某种类型的同步、以便在完成 FIFO 读取之前不会触发 ADC。 这是关键。

     我建议您使用 ADC_TRIGGER_PROCESSORD、以便您可以在完成 FIFO 读取之前控制何时开始转换的同步。 使用 ADC_TRIGGER_TIMER 是可以的、但在同步时要非常小心。  

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

    Carles、您好!

    我只能同意。 感谢你的帮助。

    此致、

    康斯坦丁