STM32 DMA从定时器计数到内存

STM32 DMA from timer count to memory

我使用的是 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 功能可以更轻松地计算脉冲之间的时间?

免责声明

我必须承认,我在STM32上的长期实践经验,到现在都停留在STM32F0、STM32F3、STM32F4和STM32L4等主流控制器系列上。 因此,我会根据这些控制器在您的情况下为您提供的内容来回答。 STM32H7 系列更强大,更不用说它提供了一些额外的 DMA 技术,如 DMA2D、MDMA 和许多我不确定的其他东西。 但我觉得一个简化的答案可能对你现在也有帮助,所以我敢写。


Can I use the timer's CNT register as a DMA transfer source? Would it be a peripheral-to-memory transfer? Or a memory-to-memory transfer? Are there other flags I need to make this work? Or is it not possible?

我希望这能奏效。 我看不出有什么理由不在 DMA 传输中读取 TIMx_CNT 寄存器。

CNT 寄存器肯定是一个外设地址所以你必须将它配置为外设到内存的传输。 我相信 peripheral/memory 分离是指在每个 STM32 中实现的总线矩阵中,DMA 控制器从中获取数据(或将数据传送到哪个总线)的总线。

Or is there another STM32 feature that would make it easier to count time between pulses?

是的,有: 许多 TIM 外设(并非所有外设都相同)为您提供称为 "Input Capture" 的功能,该功能将 TIM 实例的 通道 (子)外设连接到输入并具有main 部分(相同!)TIM 外围设备执行内部时钟。 这样做的先决条件是,您要测量的引脚具有 TIMx_CHy 备用功能,而不是 "only" 和 TIMx_ETR 功能。

TIM 外围设备提供了丰富的不同配置选项 - 如果您还不习惯的话,它们会很复杂。 作为介绍和很好的概述,我推荐 ST 的两个应用笔记:

查找这两个,我发现了第三个你可能想要检查以获得更好的精度,与 HRTIM 计时器相关:

使用 STM32CubeIDE 配置器可以轻松完成:

  1. 配置定时器,启用输入捕获通道,启用DMA(模式 圆形,存储器外设,数据宽度 word/word)。使能够 中断。
  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)
{
    //...
}