关于HAL_TIM_IC_Start_DMA中pData参数含义的技术咨询
问题描述
我正在入门STM32F103C8T6开发,想用TIM2的输入捕获功能,通过测量两个上升沿的时间间隔来获取TIM1的输出频率。但我搞不懂HAL_TIM_IC_Start_DMA函数中pData参数的数值含义:原本以为它是上升沿触发时TIM2的计数器值,但TIM2是16位定时器,自动重载最大值是65535,可实际读到的pData数值却达到2^32级别,完全不知道该怎么计算频率。
我的部分主代码如下:
#include "main.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define numval 100 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ TIM_HandleTypeDef htim1; TIM_HandleTypeDef htim2; DMA_HandleTypeDef hdma_tim2_ch1; DMA_HandleTypeDef hdma_tim2_ch2_ch4; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_TIM1_Init(void); static void MX_TIM2_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ uint32_t riseData[numval]; uint32_t fallData[numval]; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_TIM1_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, SET); TIM1->CCR1 = 500; HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_2, riseData, numval); HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_2, fallData, numval); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_Delay(500); HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_2, riseData, numval); HAL_TIM_IC_Start_DMA(&htim2, TIM_CHANNEL_2, fallData, numval); } /* USER CODE END 3 */ }
TIM2配置说明:已启用DMA及中断功能,配置为输入捕获模式;数值显示情况:读取到的riseData数组中数值为32位大整数(例如类似4294967295、1073741823这类量级的数值)。
解析与解决
1. HAL_TIM_IC_Start_DMA中pData的真实含义
STM32F1系列的定时器输入捕获配合DMA使用时,HAL库会自动把捕获时刻的定时器计数器值和定时器溢出次数拼接成一个32位整数,存入pData指向的数组:
- 低16位:捕获触发时TIM2的
CNT寄存器值(也就是你原本以为的计数器值) - 高16位:从启动捕获到当前触发时刻,TIM2已经发生的溢出次数
这样设计的原因是:TIM2是16位定时器,最大计数周期有限,如果被测信号的周期超过TIM2的溢出周期,单纯的CNT值无法反映完整的时间间隔,HAL库自动整合溢出次数,让你直接通过32位数值计算总时间,不需要手动处理溢出中断。
2. 频率计算方法
假设TIM2的时钟频率为TIM2_CLK(比如APB1预分频为1时,TIM2时钟是72MHz),计算步骤如下:
- 取数组中相邻两个上升沿的捕获值,比如
riseData[i]和riseData[i+1] - 计算总计数差:
uint32_t count_diff = riseData[i+1] - riseData[i]; - 计算信号周期:
float period = (float)count_diff / TIM2_CLK; - 计算频率:
float freq = 1.0f / period;
举个例子:如果TIM2_CLK是72MHz,相邻两个捕获值差为72000,那么周期是72000/72000000 = 0.001秒,频率就是1000Hz。
3. 代码中的问题修正
- 重复启动DMA的问题:你在
while(1)里每隔500ms重复调用HAL_TIM_IC_Start_DMA,而且两次都是同一个通道,这会导致DMA传输被频繁重置,数据混乱。正确的做法是初始化时启动一次DMA,然后在DMA传输完成的回调函数HAL_TIM_IC_CaptureCallback或HAL_DMA_TransferCpltCallback中重新启动传输。 - 同一通道同时捕获升降沿的问题:你现在用同一个TIM2_CH2同时启动两次DMA来捕获上升沿和下降沿,这会造成资源冲突。如果需要同时捕获升降沿,应该在TIM2的配置里把通道设置为双边沿捕获,然后在回调中判断是上升沿还是下降沿,再分别存入对应的数组。
内容的提问来源于stack exchange,提问作者Diablo
相关产品推荐
相关产品推荐

