STM32CubeMX系列06——定时器(定时、PWM、输入捕获)

文章目录

  • 1. 所用硬件
  • 2. 生成工程
  • 2.1. 创建工程选择主控
  • 2.2. 系统配置
  • 2.3. 配置工程目录
  • 3. 定时器配置(三选一)
  • 3.1. 定时功能
  • 3.2. PWM 输出
  • 3.3. 输入捕获
  • ====>>> 文章汇总(有代码汇总) <<<====

    1. 所用硬件

    正点原子Mini板,主控 STM32F103RCT6.

    定时器简介
    这里主要讨论通用定时器(系统嘀嗒定时器、看门狗定时器、RTC定时器不考虑在内)

    对于STM32F103RCT6 单片机:

  • 2个基本定时器。分别是TIM6TIM7。只能16位向上计数、没有IO口,没有捕获和比较通道,只有计时功能
  • 4个通用定时器。分别是TIM2TIM3TIM4TIM5。可以16位向上或向下计数。可以定时、输出比较、输入捕获。每个定时器有4个外部IO。
  • 2个高级定时器。分别是TIM1TIM8。可以16位向上或向下计数。可以定时、输出比较、输入捕获、还可以输出三相电机互补信号。每个定时器有8个外部IO。
  • 另外定时器的通道可以用来作为:

  • 输入捕获
  • 输出比较
  • PWM 生成(边缘或中间对齐模式)
  • 单脉冲模式输出
  • 如下事件发生时产生中断/DMA:

  • 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
  • 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
  • 输入捕获
  • 输出比较
  • 2. 生成工程

    2.1. 创建工程选择主控

    2.2. 系统配置

    配置时钟源

    配置debug模式(如果需要ST-Link下载及调试可以勾选)

    配置时钟树(可以直接在HCLK那里输入72,然后敲回车会自动配置)

    2.3. 配置工程目录


    3. 定时器配置(三选一)

    案例:

  • 定时功能:定时翻转 LED 灯
  • PWM功能:驱动电机 / 呼吸灯
  • 输入捕获:PWM信号频率计算 / 信号高电平或低电平持续时间计算
  • 3.1. 定时功能

    第一步:配置外设
    LED:用来看效果

    TIMER2:用来计时

    在时钟树中可以看到,定时器都是挂在APB1和APB2上,时钟频率都一样,都是72MHZ。我们这里设置预分频系数为7200-1,也就是分频完之后的频率为72 000 000 / 7200 = 10 000HZ;计数值为10000 (设置9999,还有0,就是1万)。也就是每1s产生一次溢出中断中断

    配置中断

    第二步:生成代码

    第三步:写代码
    打开工程,编译。
    stm32f1xx_it.c中可以看到定时器中断处理函数TIM2_IRQHandler.

    HAL_TIM_IRQHandler(&htim2);中,根据发生事件不同,调用了不同函数,其中当发生更新事件的时候,调用了函数HAL_TIM_PeriodElapsedCallback(htim);

    这个函数定义如下。可以看到这是个弱函数,在注释中也有写。我们重新定义此函数,然后当中断发生时会执行此函数。此函数被称为回调函数。

    当需要回调时,不应修改此函数,
    HAL_TIM_PeriodElapsedCallback可以在用户文件中实现

    我们把这个函数的重定义写在 tim.c 中。添加到末尾。

    /* USER CODE BEGIN 1 */
    
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
    	// 这个回调函数 是共用的,因此需要先判断,是不是定时器2
        if(htim->Instance == htim2.Instance)
    	{
    		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    	}
    }
    
    /* USER CODE END 1 */
    

    然后在main.c中,只需要在while(1)之前 使能定时器中断即可。

      // 使能定时器中断
      HAL_TIM_Base_Start_IT(&htim2);
    

    第四步:编译、下载、验证

    PA8 连接的 LED 每隔一秒翻转一次,

    3.2. PWM 输出

    第一步:配置定时器,用于输出PWM信号

    在时钟树中可以看到,定时器都是挂在APB1和APB2上,时钟频率都一样,都是72MHZ。我们这里设置预分频系数为72-1,也就是分频完之后的频率为72 000 000 / 72 = 1 000 000HZ;计数值为1000 (设置999,还有0,就是1 000)。也就是每1ms产生一次溢出中断中断

    PWM 生成通道 mode 参数:

    TIMx_CNT,表示定时器 TIMx 的计数器寄存器值;
    TIMx_CCRn,表示捕获比较寄存器的值,其中 n 表示某个通道,取值为1、2、3、4;

    PWM 生成通道 pulse 参数:决定PWM的占空比。
    占空比Pulse = pulse / period

    这个是可以在程序中改的
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, campNum);

    第二步:生成代码

    第三步:main.c

      /* USER CODE BEGIN WHILE */
      
    	// 开启 PWM 输出
      	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
    	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
    	
    	uint16_t pwm_i = 0;			//  PWM output
    	uint16_t pwm_mode = 0;		//  PWM chance mode
      while (1)
      {
    		if(pwm_mode)
    		{
    			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm_i--);
    			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, pwm_i--);
    			if(pwm_i==0)
    				pwm_mode=0;
    		}
    		else
    		{
    			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm_i++);
    			__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2, pwm_i++);
    			if(pwm_i==1000)
    				pwm_mode=1;
    		}
    		HAL_Delay(1);
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
    

    第四步:效果验证
    编译、烧录
    在PA1和GND 或者 PA2和GND之间接个 LED,可以看到LED有呼吸灯的效果。

    3.3. 输入捕获

    第一步:定时器配置
    这里使用定时器3捕获输入

    在时钟树中可以看到,定时器都是挂在APB1和APB2上,时钟频率都一样,都是72MHZ。我们这里设置预分频系数为72-1,也就是分频完之后的频率为72 000 000 / 72 = 1 000 000HZ;计数值为65536(设置65535,还有0,就是65536)。因为这里其实还是需要计时,能长就长,减少更新中断。同时设置上升沿触发,也就是上升沿到来产生捕获中断。

    设置捕获上升沿还是下降沿 也是可以配置的
    __HAL_TIM_SET_CAPTUREPOLARITY();

    使能中断

    使用定时器2 产生固定占空比的PWM波(配置方法和上一小节一样)

    为了方便看效果,也配置一下串口重定向,看往期文章。–>串口重定向配置<–

    第二步:生成代码

    第三步:写代码
    tim.c末尾加上下面函数。前者为输入捕获中断回调函数。
    后者用来计算 输入信号的占空比和频率。

    /* USER CODE BEGIN 1 */
    #include "usart.h"
    
    uint16_t CCR1, CCR2, CCR3;
    uint8_t measure_flag = 0;
    // 定时器3 捕获中断回调函数
    void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
    {
    	static uint8_t measure_cnt = 1;
    	// 初始设置的是捕获上升沿
    	if (htim == &htim3)
    	{
    		// 1. 第一次发生中断肯定是上升沿
    		if (measure_cnt == 1)
    		{
    			// 2. 获取此时定时器计时数据
    			CCR1 = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
    			// 3. 将定时器设置为捕获下降沿
    			__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
    			measure_cnt = 2;
    		}
    		// 4. 捕获到下降延
    		else if (measure_cnt == 2)
    		{
    			// 5. 获取此时定时器计时数据
    			CCR2 = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
    			// 6. 将定时器重新设置为捕获上升沿
    			__HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
    			measure_cnt = 3;
    		}
    		// 7. 再次捕获到上升沿,说明一个周期结束了。
    		else if (measure_cnt == 3)
    		{
    			// 8. 获取此时定时器计时的数据
    			CCR3 = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
    			// 9. 关闭定时器中断。
    			HAL_TIM_IC_Stop_IT(&htim3, TIM_CHANNEL_1);
    			measure_cnt = 1;
    			measure_flag = 1;
    		}
    	}
    }
    
    // 捕获函数
    void capture(void)
    {
    	// diff1:高电平持续时间
    	// diff2:一个周期的时间
    	uint16_t diff1 = 0, diff2 = 0;
    	
    	uint32_t freq;	// 频率
    	uint8_t duty;	// 占空比
    	if (measure_flag)
    	{
    		measure_flag = 0;
    
    		if (CCR1 < CCR2)
    			diff1 = CCR2 - CCR1;
    		else
    			diff1 = 0xffff + 1 + CCR2 - CCR1;	// 设置的最多能数65535,也就是0xffff + 1
    
    		if (CCR1 < CCR3)
    			diff2 = CCR3 - CCR1;
    		else
    			diff2 = 0xffff + 1 + CCR3 - CCR1;
    		// 每秒能数 1000000.一个周期是 diff2。
    		freq = (72000000 / 72) / diff2;
    		// 高电平持续时间/低电平持续时间 不让出现小数,所以*100
    		duty = diff1 * 100 / diff2;
    	}
    	printf("freq: %d HZ,  duty: %d %% \r\n", freq, duty);
    }
    /* USER CODE END 1 */
    

    main.c中调用函数

    	// 开启 PWM 输出
      	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
    	HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);
    	// 开启定时器输入捕获中断
    	HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
      while (1)
      {
    		capture();
    		// 延时1s
    		// 在中断回调函数中关掉了,再次开启定时器3捕获中断 重新计算。
    		HAL_Delay(1000);
    		HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
    

    第四步:效果验证
    编译、烧录
    用串口查看:
    用线把 PA6和PA0 或者 PA6和PA1 连接起来,就可以查看定时器2的两个通道产生的PWM频率和占空比了(下面有几个数不对,因为手动插上的时候有抖动)。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32CubeMX系列06——定时器(定时、PWM、输入捕获)

    发表评论