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.

[参考译文] CCS/TM4C123BH6PZ:ADC 捕获和 DMA 问题

Guru**** 2398695 points


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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/643173/ccs-tm4c123bh6pz-adc-capture-and-dma-question

器件型号:TM4C123BH6PZ
主题中讨论的其他器件:TM4C123

工具/软件:Code Composer Studio

我正在使用 CCS 7.0。  本质上、这是我尝试执行的操作的最简单的形式。  我们有一个有点快的38Khz 信号、我要在该信号中对数据进行采样。  中断时间比我想的要长。  我认为 DMA 很适合将结果转储到缓冲区中、在缓冲区已满时生成中断、然后处理数据。 但是,缓冲区中没有任何 ADC 数据。  

此外、ADC 应在下降沿 GPIO 触发、但它似乎在需要时随时运行。  我不知道问题是否相关,因为我可以在实施 DMA 之前读取 ADC 值。

如果您需要、我有一个代码片段文件可供查看。  它只是大量尝试中的最新版本。  其中的大部分内容都来自搜索 TI 论坛和示例代码、同时还尝试了解数据表尝试解释的内容。   

查看以下帖子、其中用户最终使 ADC DMA 正常工作、但这未解决问题:

我已按照此处列出的方式设置所有项目、但缓冲区中仍然没有数据。  根据我在数据表中读取的内容、当缓冲区半满时、DMA 应该会触发 ADC 中断处理程序。 (第585页) 我注册了 ADC 中断处理程序、但它从不会关闭。  它唯一的触发时间是我有 ADCIntEnable( ADC1_base,0);函数调用进入,但这不是我需要的行为,因为它忽略了 DMA。  即使暂停代码,也会显示缓冲区为空,因此它不仅仅是一个中断问题。

是否无法让 ADC 触发并通过 DMA 将值加载到缓冲器中?  那么、当 DMA 缓冲区半满时、是否获得一个中断来处理该数据?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Mark、
    我建议您使用 UDMA 的乒乓模式。 当一个缓冲区被填满时、UDMA 生成一个中断并自动开始将数据放入下一个缓冲区。 您必须在第二个缓冲区填满之前处理第一个缓冲区中的数据。

    如果您检查所使用的 ADC 序列的 ADC 采样序列 FIFO 状态寄存器、它是否显示 FIFO 不为空?
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Bob、

    该模式设置为乒乓模式、但仍然无法在缓冲器中获取任何中断事件或数据。  我已经查看过许多过帐、似乎所有用于设置的函数都包含在代码中、但事件并未发生。  下面是 Mark 参考的设置代码。

    #include 
    #include 
    #include 
    #include "inc/tm4c123bh6pz.h"
    #include "inc/hw_types.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_adc.h"
    #include "driverlib/sysctl.h"
    #include "driverlib/dma.hr"
    #include "driverlib/driver.h"#include "driverlib_diptine.h.hd"#include "driverlib_da.h"
    
    
    
    
    uint8;
    typedef 字符 int8;
    typedef unsigned short uint16;
    typedef signed short int16;
    typedef unsigned int uint32;
    typedef signed int int32;
    typedef unsigned long long uint64;
    typedef signed long long int64;
    
    #ifndef Boolean
    typedef enum
    {
    错误、
    True
    }布尔;
    #endif
    
    静态 UINT32 ADC1S0_DMA_Control_Table[256]={0};
    #define NUMBER_OW_STEPS (1) // ADC0序列0中的转换数
    #define NUMBER_ON_SET (128) //存储在缓冲区
    
    中的转换组数//用于存储 ADC 转换的缓冲区数结果
    静态 uint16缓冲区 A[number_for_sets][number_for_Steps];
    静态 uint16缓冲区 B[number_for_sets];
    
    U****32 DMACount;/******Erring***
    
    
    //**
    *@详细信息可为
    *触发的单个序列初始化 ADC 所需的一切功能 外部引脚、然后保存到 DMA 中。
    *
    *@return 函数如果成功将返回1、否则将返回0。
    (二 /
    Int32 HW_ADC1_Initialize_Sequence_GPIO (void)
    {
    //使用虚拟读取启用 ADC0外设以插入几个周期。
    SysCtlPeripheralEnable (sysctl_Periph_ADC1);
    SysCtlDelay (10);
    
    //配置 ADC 时钟速度
    ADCClockConfigSet (ADC1_base、ADC_CClock_SRC_PIOSC | ADC_CClock_RATE_八分之一,1);
    
    //设置参考源
    ADC1_base (ADC1_base、ADC_C12T_OACK_0
    
    
    );//配置 ADC12T_RATE_RATE_RATE_RATE
    
    
    (0) 0);//配置参考源 ADCK_0 (ADCK_0);ADCK_0 (0);ADCK_0 (0);ADCK_OPENDACK_0 (0)
    ADCIntClear (ADC1_base,0);
    
    //禁用序列发生
    
    
    器,以便我们可以配置它 ADCSequenceDisable (ADC1_base,0);//设置外部 GPIO 引脚以触发 ADC。
    GPIOIntTypeSet (GPIO_Porth_BASE、GPIO_PIN_0、GPIO_RISING _EDGE);
    GPIOIntEnable (GPIO_Porth_BASE、GPIO_PIN_0);
    
    //将 ADC 设置为从 GPIO 引脚中断触发。
    GPIOADCTriggerEnable (GPIO_Porth_BASE、GPIO_PIN_0);
    
    //将端口引脚配置为 ADC 备用功能
    GPIOPinTypeADC (GPIO_Porte _BASE、GPIO_PIN_0);
    
    //将序列发生器分配给 PWM 并将其优先级设置为 Seq #
    ADCSequenceConfigure (ADC1_base、GPIO_PIN0);
    
    //读取 ADC1_0的外部 ADC0_0序列。
    ADCSequenceStepConfigure (ADC1_base、0、0、ADC_CTL_CH3 | ADC_CTL_IE | ADC_CTL_END);
    
    //为采样序列发生器启用 DMA。
    ADCSequenceDMAEnable( ADC1_base,0);
    
    //清除中断状态标志。 这样做是为了确保在
    采样前清除中断标志//。
    ADCIntClear (ADC1_BASE,0);
    
    //加电 DMA
    SysCtlPeripheralReset (SYSCTL_Periph_UDMA);
    SysCtlPeripheralEnable (SYSCTL_Periph_UDMA);
    
    //启用 UDMA 控制器以供使用。
    uDMAEnable();
    
    //指定用于 DMA 控制值的存储器位置。
    uDMAControlBaseSet( ADC1S2_DMA_Control_Table );
    
    //为采样序列发生器启用 DMA。
    ADCSequenceEnable( ADC1_base,0);
    
    //将属性置于已知状态。 默认情况下应已禁用这些功能。
    uDMAChannelAttributeDisable( uDMA_CHANGE_ADC1、UDMA_ATTR_ALL );
    
    //设置 USEBURST 属性。 这比默认的
    //允许单次或猝发传输的总线使用效率更高一些。
    uDMAChannelAttributeEnable (uDMA_CHANGE_ADC1、UDMA_ATTR_USEBURST);
    
    //为
    uDMAChannelControlSet (UDMA_CHANGE_ADC1 | UDMA_PRI_SELECT、
    UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_NEW_USEBURST | UDMA_ARB_128 | UDMA_DST_INC_16);
    
    uDMAChannelControlSet (UDMA_CHANNEL_ADC1 | UDMA_ALT_SELECT、
    UDMA_SIZE_16 | UDMA_SRC_INC_NONE | UDMA_NEW_USEBURST | UDMA_ARB_128 | UDMA_DST_INC_16);
    
    //设置主控制结构体的传输参数。 模式设置为
    //乒乓。
    uDMAChannelTransferSet (UDMA_CHANGE_ADC1 | UDMA_PRI_SELECT、UDMA_MODE_PINGONG、
    (void *)(ADC1_base + ADC_O_SSFIFO0)、BufferA、64);
    
    uDMAChannelTransferSet (UDMA_CHANGE_ADC1 | UDMA_ALT_SELECT、UDMA_MODE_PINGONG、
    (void *)(ADC1_base + ADC_O_SSFIFO0)、BufferB、64);
    
    ADCIntRegister (ADC1_base、0、 ADC0S0IntHandler );
    
    //用于测试 ADC 在 DMA 之外工作。 它确实会以适当的时间和速度跳闸。
    // ADCIntEnable( ADC1_base,0 );
    
    //将 DMA 错误处理程序中断注册为可能导致问题的错误。
    uDMAIntRegister (UDMA_INT_ERR、uDMAErrorHandler);
    
    //在处理器上启用 ADC 序列0中断(NVIC)。
    IntEnable( INT_ADC1SS0 );
    
    //启用采样序列中断。
    ADCIntEnableEx (ADC1_base、ADC_INT_DMA_SS0);
    
    //为 ADC
    uDMAChannelEnable (UDMA_CHANGE_ADC1)启用 DMA 通道;
    
    return 1;
    }/*********
    
    //**
    *@详细介绍了 ADC 序列0的中断处理程序。 代码几乎是此论坛帖子的镜像:
    * e2e.ti.com/.../612982
    /
    void ADC0S0IntHandler (void)
    {
    uint16_t I = 0、j = 0;
    uint32状态= 0;
    uint32_t priChMode = 0、altChMode = 0;
    
    //测试引脚、用于验证缓冲区半满时中断是否关闭。 工作后拆下。
    GPIOPinWrite (GPIO_PORTF_BASE、GPIO_PIN_6、GPIO_PIN_6);
    
    //获取中断状态
    = ADCIntStatusEx (ADC1_BASE,0);
    
    //获取主 DMA 通道
    PRIMCHMode = uDMAChannelModeGet (UDMA_CHANGE_ADC1 | UDMA_PRI)
    
    
    
    
    ;//选择 ADCMTS_RATE_RATE_ADAPPLICT_D0 (UDMA_DE_ADAPPLICT_ADAPPLICT_ADDR_ADAPPLOR_ADAPPLOR_ADDR_ADAPPLOR_ADT0);/ ADAPPLICT_ADAPPLOR_ADAPPLICT_ADAPPLICT_ADAP_ADAPPLICT_ADAPPLICT_ADAPPLICT_ADAP
    
    
    //如果主 DMA 缓冲区已满
    (priChMode = 0)
    {,则清除并重新初始化主 DMA 缓冲区
    对于(I = 0;I < number_of _阶跃;I++)
    {
    对于(j = 0;j < number_of _sets;j++)
    {
    BufferA[i][j]= 0;
    }
    }
    uDMAChannelTransferSet (UDMA_CHANGE_ADC1 | UDMA_PRI_SELECT、UDMA_MODE_PINGONG、
    (void *)(ADC1_base + ADC_O_SSFIFO0)、BufferA、NUMBER_OF_SET * NUMBER_OF_STEPS);
    }
    
    //如果备用 DMA 缓冲区已满
    (altChMode = 0)
    则清除并重新初始化该缓冲区。{
    对于(I = 0;I < number_of _阶跃;I++)
    {
    对于(j = 0;j < number_of _sets;j++)
    {
    BufferB[i][j]= 0;
    }
    }
    uDMAChannelTransferSet (UDMA_CHANGE_ADC1 | UDMA_ALT_SELECT、UDMA_MODE_PINGONG、
    (void *)(ADC1_base + ADC_O_SSFIFO0)、BufferB、NUMBER_OF_SET * NUMBER_OF_STEPS);
    }
    
    //测试引脚,以验证缓冲区半满时中断是否关闭。 工作后拆下。
    GPIOPinWrite (GPIO_PORTF_BASE、GPIO_PIN_6、0);
    }
    
    
    //*************
    // UDMA 错误的中断处理程序。 如果
    // uDMA 在尝试执行传输时遇到总线错误,则会发生此中断。 此
    //处理程序仅在发生错误时递增计数器。
    //
    void uDMAErrorHandler (void)
    {
    uint32_t ui32Status;
    
    //检查 uDMA 错误位
    ui32Status = uDMAErrorStatusGet (void);
    
    //如果存在 uDMA 错误,则清除错误并递增
    //错误计数器。
    if (ui32Status)
    {
    uDMAErrorStatusClear ();
    DMA 勘误表++;
    }
    
    

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Mark、您好!
    我正在研究一个示例。 我还没有完成、但在 TM4C123器件上、中断是在 DMA 传输发生后由 ADC 生成的。 DMA 中断仅用于软件启动的传输和错误。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Mark、您好!

    下面是一个 TM4C123示例、该示例展示了一个计时器生成周期性中断以触发 AtoD。 UDMA 读取加载两个缓冲器之间的 AtoD 和乒声。 重要的一点是、UDMA 仲裁数目不应大于 AtoD 序列。 在本例中、我只在序列中进行单次转换、因此我使用的 DMA 仲裁大小为1。

    /cfs-file/__key/communityserver-discussions-components-files/908/ADCwDMA.zip

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

    Bob、

    我查看了您发送的代码、虽然它不是同一触发器、但我根据您的代码进行了一些更改。  但是、所有这些项目都没有解决 ADC 中断在配置为 DMA 时仍然不触发的问题。  它会直接触发。

    • 将 ADC 中断处理程序代码更改为与您的代码相同、但 ADC1而不是 ADC0除外。
    • 删除了以下行: ADCIntEnableEx (ADC1_base、ADC_INT_DMA_SS0);
    • 删除了以下行:ADCHardwareOversampleConfigure( ADC1_base,0);
    • 删除 了 uDMAChannelControlSet 函数中的选项 UDMA_NEW_USEBURST  
    • 将仲裁值更改为 UDMA_ARB_1
    • 将缓冲区指针地址更改为地址的地址(对我来说似乎有点奇怪、但在您的示例中却是这样) :&BufferA
    • 重新排列初始化顺序、将 DMA 置于首位、然后将 ADC 置于高位(与您的顺序匹配)
    • 在  函数 uDMAChannelAttributeDisable ()中将 uDMA_attr_all 选项更改为 uDMA_attr_ALTSELECT | uDMA_attr_high_priority | uDMA_attr_REQMASK

    我可以使用更改重新发布我的代码。  是否可以有触发机制?  您使用了一个具有计时器触发器(已经在论坛中)的示例、但我需要它从外部中断触发。  如果我不使用 DMA、ADC 中断将按预期触发、因此我认为触发器设置正确。

    是否有其他建议可实现此功能?

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    当模数缓冲区到达触发点时、可以让模数转换器生成中断、也可以让模数转换器生成 DMA 猝发请求。 它不会同时执行这两个操作。 如果使用 DMA、一旦 DMA 传输序列完成、您将获得模数转换中断。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    Tim、

    如果您从 ADC0更改为 ADC1、请注意如何映射 UDMA 通道。 ADC1序列0是 UDMA 通道24上的第二个外设。 请注意、宏 UDMA_CHANGE_ADC1定义为通道15、这是 ADC0序列1的主通道。 这不是要使用的正确宏。 您必须调用 uDMAChannelAssign、然后为通道使用值24、或者分配一个描述性更强的宏。

    #define uDMACHANNEL_ADC1_SEQ0 24
    
    void init_dma ()
    {
    uDMAEnable();//启用 UDMA
    uDMAControlBaseSet (ucControlTable);
    uDMAChannelAssign (UDMA_CH24_ADC1_0);
    uDMAChannelAttributeDisable (uDMACHANNEL_ADC1_SEQ0、UDMA_ATTR_ALTSELECT | UDMA_ATTR_HIGH_PRIOR| UDMA_ATTR_REQMASK);
    
    uDMAChannelAttributeEnable (uDMACHANNEL_ADC1_SEQ0、UDMA_ATTR_USEBURST);
    //仅允许突发传输
    
    uDMAChannelControlSet (uDMACHANNEL_ADC1_SEQ0 | UDMA_PRI_SELECT、UDMA_SIZE_16 | UDMA_SRC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
    uDMAChannelControlSet (uDMACHANNEL_ADC1_SEQ0 | UDMA_ALT_SELECT、UDMA_SIZE_16 | UDMA_SRC_NONE | UDMA_DST_INC_16 | UDMA_ARB_1);
    
    uDMAChannelTransferSet (uDMACHANNEL_ADC1_SEQ0 | UDMA_PRI_SELECT、UDMA_MODE_PINGONG、(void *)(ADC1_BASE + ADC_O_SSFIFO0)、&ADC_Out1、ADC_SAMPLE_BUF_SIZE);
    uDMAChannelTransferSet (uDMACHANNEL_ADC1_SEQ0 | UDMA_ALT_SELECT、UDMA_MODE_PINGONG、(void *)(ADC1_BASE + ADC_O_SSFIFO0)、&ADC_Out2、ADC_SAMPLE_BUF_SIZE);
    
    uDMAChannelEnable (uDMACHANNEL_ADC1_SEQ0);//启用 DMA 通道以便它可以执行传输
    
    }
    

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

    这一直是问题所在。  有趣的是、这与编号1的 Tiva 实现断开、与 SSI1或 UART1类似、是指模块1。  感谢您找到问题的根源、因为它现在按预期运行。

    Tim