STM32定时器计数器到内存的DMA传输

5
我在使用STM32H743。有一个外部时钟信号输入到一个GPIO引脚上,我想要非常精确地测量外部时钟信号每个上升(或下降)沿之间的经过时间。因此,我设置了TIM4由外部时钟触发,并将TIM5由内部振荡器触发。
我编写了一个IRQ,使得每次TIM4触发时,都会运行一个中断来捕获TIM5的值。它似乎可以正常工作,但我想知道是否可以通过DMA来避免所有上下文切换并释放CPU。基本上,我想设置一个DMA,以便每个TIM4事件都会启动一个DMA传输,将TIM5计数器值复制到某个循环缓冲区中。
我已经在论坛和DMA文档中进行了搜索,但我对定时器寄存器是否可以作为有效的DMA源存疑。我在考虑是否可以像这样做:
hDma->PAR = (uint32_t) &htim5.Instance->CNT;
hDma->M0AR = (uint32_t) myBufferPtr;
hDma->NDTR = myBufferSize;
hDma->CR |= (uint32_t)DMA_SxCR_EN;

但我不确定这是否可行。

简而言之:我能否使用定时器的CNT寄存器作为DMA传输源?这将是外设到内存传输还是内存到内存传输?我需要设置其他标志才能使其正常工作吗?或者这不可能吗?或者有没有其他STM32功能可以更轻松地计算脉冲之间的时间?

2个回答

7

声明

我必须承认,我的长期STM32实践经验仅限于主流控制器系列,例如STM32F0、STM32F3、STM32F4和STM32L4。 因此,我基于这些控制器在您的情况下提供的内容来回答问题。 STM32H7系列要强大得多,更不用说它还提供了几种额外的DMA技术,如DMA2D、MDMA和许多其他我不确定的东西。 但是我认为,现在也可以为您提供简化的答案,所以我决定写下来。


我能将定时器的CNT寄存器用作DMA传输源吗?这将是一个外设到内存的传输吗?还是内存到内存的传输?是否需要其他标志来使其正常工作?或者这是不可能的?

我认为这应该可行。 我没有理由不在DMA传输中读取TIMx_CNT寄存器。

CNT寄存器绝对是一个外设地址,因此必须将其配置为外设到内存的传输。 我相信,外设/内存分离是指DMA控制器从哪个总线获取数据(或将它们传递到哪个总线)在每个STM32实现的总线矩阵中。

还有其他STM32特性可以更轻松地计算脉冲之间的时间吗?

是的,有: 许多TIM外设(并非全部相同)为您提供了称为“输入捕获”的功能,将TIM实例的通道(子)外设连接到输入,并使(相同!)TIM外设的主要部分进行内部时钟。 这需要的前提条件是,您想要测量的引脚具有TIMx_CHy替代功能,而不是“仅仅”TIMx_ETR一个。

TIM外设提供了丰富的不同配置选项-除非您已经习惯了它,否则就会变成一堆复杂的混乱。 作为介绍和很好的概述,我推荐ST的两个应用笔记:

查找这两个应用笔记时,我发现了第三个笔记,您可能想查看它以获得更好的精度,与HRTIM计时器相关:


再次感谢,我想在接受之前尝试一下 - 它有效!我在TIM5上进行了捕获,似乎比我的原始中断代码给出了更可靠的测量结果,并且使用DMA将这些捕获测量值存储起来非常简单。接下来我会尝试HRTIM,看看它是否能进一步提高精度。 - Kevin Holt
@KevinHolt - 非常棒!祝你在设置方面玩得开心并取得成功! - HelpingHand

1

使用STM32CubeIDE配置器很容易完成:

  1. 配置定时器,启用输入捕获通道,启用DMA(循环模式,外设到内存,数据宽度为字/字)。启用中断。
  2. 准备用于存储捕获计数值的缓冲区。
  3. 在主循环之前以DMA模式启动IC。
  4. 对于高速操作,您可以在这些回调函数中将数据从timerCaptureBuffer复制到timerCaptureBufferSafe。例如,DMA内存到内存传输,以最小化在HAL_TIM_IC_CaptureHalfCpltCallback和HAL_TIM_IC_CaptureCallback中间所花费的时间。在DMA内存到内存回调信号数据就绪后,处理存储在timerCaptureBufferSafe中的相邻捕获值。您可以使用信令标志,以便不会覆盖timerCaptureBufferSafe。

以下是一个示例:

#define TIM_BUFFER_SIZE 128
uint32_t timerCaptureBuffer[TIM_BUFFER_SIZE];
uint32_t timerCaptureBufferSafe[TIM_BUFFER_SIZE];
// ...

HAL_DMA_RegisterCallback(&hdma_memtomem_dma2_stream2,
HAL_DMA_XFER_CPLT_CB_ID,
myDMA_Callback22);
// ...

HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_1, uint32_t*)timerCaptureBuffer,TIM_BUFFER_SIZE);
// ...

void HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef *htim)
{
    HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream2,
    (uint32_t)&timerCaptureBuffer[0],
    (uint32_t)&timerCaptureBufferSafe[0],
    sizeof(timerCaptureBuffer)/2/4);
    // ...
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    HAL_DMA_Start_IT(&hdma_memtomem_dma2_stream2,
    (uint32_t)&timerCaptureBuffer[TIM_BUFFER_SIZE/2],
    (uint32_t)&timerCaptureBufferSafe[TIM_BUFFER_SIZE/2],
    sizeof(timerCaptureBuffer)/2/4);
    // ...
}
void myDMA_Callback22(DMA_HandleTypeDef *_hdma)
{
    //...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接