一、定时器与PWM

(一)定时器

1.STM32F103定时器分类及区别

STM32F103一共有8个定时器TIM1~TIM8。STM32的定时器分为基本定时器、通用定时器和高等定时器。

①TIM6、TIM7(基本定时器):基本定时器是只能向上计数的16位定时器,基本定时器只能有定时的功能,没有外部IO口,所以没有捕获和比较通道。

②TIM2、TIM3、TIM4、TIM5(通用定时器):通用定时器是可以向上计数,也可以向下计数的16位定时器。通用定时器可以定时、输出比较、输入捕捉,每个通用定时器具有4个外部IO口。

③TIM1、TIM8(高级定时器):高级定时器是是可以向上计数,也可以向下计数的16位定时器。高等定时器可以定时、输出比较、输入捕捉、还可以输出三相电机互补信号,每个高等定时器有8个外部IO口。

此次主要使用通用定时器,故后续了解以通用定时器为主

1.通用定时器主要功能

通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:

● 16位向上、向下、向上/向下自动装载计数器

● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值

● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出

● 使用外部信号控制定时器和定时器互连的同步电路

● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较

● 支持针对定位的增量(正交)编码器和霍尔传感器电路

通用定时器计数模式:①向上计数 ②向下计数 ③中心对齐

3.通用定时器工作过程

在这里插入图片描述
从时钟源产生框可以看到

定时器时钟有四种来源:
● 内部时钟(CK_INT)
● 外部时钟模式1:外部输入脚(TIx) (输入捕获的引脚)
● 外部时钟模式2:外部触发输入(ETR)
● 内部触发输入(ITRx):(定时器级联)使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

产生CK_PSC时钟,然后在从模式控制器内设置好计数模式(向上向下),再经过预分频器产生CK_CNT,若为CNT计数器向下计数,则当其计数到0时,自动重装载寄存器会重新为CNT计数器装载新值重新递减计数,并产生一个更新事件。

时基单元:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)
输入捕获:信号通过捕获通道进入,经滤波,经分频,到捕获比较寄存器。(经输入捕获与输出比较的四个通道其实是一个)

(二)PWM

1.PWM

脉冲宽度调制Pulse width modulationPWM)是一种对模拟信号电平进行数字编码的方法。

STM32的每个通用定时器都有独立的4个通道可以用来作为:输入捕获、输出比较、PWM输出、单脉冲模式输出等。

STM32的定时器除了TIM6和TIM7(基本定时器)之外,其他的定时器都可以产生PWM输出。其中,高级定时器TIM1、TIM8可以同时产生7路PWM输出

2.PWM原理

PWM的一个周期

定时器从0开始向上计数
当0-t1段,定时器计数器TIMx_CNT值小于CCRx值,输出低电平
t1-t2段,定时器计数器TIMx_CNT值大于CCRx值,输出高电平
当TIMx_CNT值达到ARR时,定时器溢出,重新向上计数...循环此过程
至此一个PWM周期完成

每个定时器有四个通道,每一个通道都有一个捕获比较寄存器,

将寄存器值和计数器值比较,通过比较结果输出高低电平,便可以实现脉冲宽度调制模式(PWM信号)

向上计数:
在这里插入图片描述
假定定时器工作在向上计数 PWM模式:

当 CNT<CCRx 时,输出 0
当 CNT>=CCRx 时输出 1
当 CNT 达到 ARR 值的时候,重新归零,然后重新向上计数,依次循环。

改变 CCRx 的值,就可以改变 PWM 输出的占空比,改变 ARR 的值,就可以改变 PWM 输出的频率。

**具体过程:**若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx_ARR配置为N,即TIMx_CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_CNT的数值X大于N时,会重置TIMx_CNT数值为0重新计数。
而在TIMxCNT计数的同时,TIMxCNT的计数值X会与比较寄存器TIMx_CCR预先存储了的数值A进行比较,当脉冲计数器TIMx_CNT的数值X小于比较寄存器TIMx_CCR的值A时,输出高电平(或低电平),相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。
如此循环,得到的输出脉冲周期就为重载寄存器TIMx_ARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。

二、控制LED灯

(一)定时器控制LED灯

1.STM32CubeMX新建文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

定时器溢出时间:
在这里插入图片描述
这里我们 arr=4999 psc=6399 Tclk=16Mhz Tout = (5000*6400)/16 us = 2s
在这里插入图片描述

在这里插入图片描述

2.Keil编写程序

  HAL_TIM_Base_Start_IT(&htim2);

在这里插入图片描述

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    static unsigned char ledState = 0;
    if (htim == (&htim2))
    {
        if (ledState == 0)
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
        else
            HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
        ledState = !ledState;
    }
}

在这里插入图片描述

3.运行结果

tim2

(二)定时器PWM模式控制呼吸灯

1.STM32CubeMX新建文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在 Parameter Settings 页配置预分频系数为 71,计数周期(自动加载值)为 499,定时器溢出频率,即PWM的周期,就是 72MHz/(71+1)/(499+1) = 2kHz

PWM频率:

Fpwm =Tclk / ((arr+1)*(psc+1))(单位:Hz)

arr 是计数器值
psc 是预分频值
占空比:

duty circle = TIM3->CCR1 / arr(单位:%)
TIM3->CCR1 用户设定值
比如 定时器频率Tclk = 72Mhz arr=499 psc=71 那么PWM频率就是720000/500/72= 2000Hz,即2KHz

arr=499,TIM3->CCR1=250 则pwm的占空比为50%

改CCR1可以修改占空比,修改arr可以修改频率

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.Keil编写程序

  /* USER CODE BEGIN 1 */
	
	uint16_t pwmVal=0;   //PWM占空比  
    uint8_t dir=1;  

  /* USER CODE END 1 */
  /* USER CODE BEGIN 2 */

  HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);

  /* USER CODE END 2 */
while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  while (pwmVal< 500)
	  {
		  pwmVal++;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR1 = pwmVal;    与上方相同
		  HAL_Delay(1);
	  }
	  while (pwmVal)
	  {
		  pwmVal--;
		  __HAL_TIM_SetCompare(&htim3, TIM_CHANNEL_1, pwmVal);    //修改比较值,修改占空比
//		  TIM3->CCR1 = pwmVal;     与上方相同
		  HAL_Delay(1);
	  }
	  HAL_Delay(200);

  }
  /* USER CODE END 3 */

3.运行结果

pwm

(三)定时器采集PWM信号

1.STM32CubeMX新建文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.Keil编写程序

在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 */
while (1)
  {
    /* USER CODE END WHILE */

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

3.运行结果

在这里插入图片描述

三、总结

通过使用定时器PWM模式对输出的控制可以使LED实现呼吸效果,并控制其时间及亮度。

四、引用

【Z小旋】HAL库 STM32CubeMX教程六----定时器中断
【Z小旋】【STM32】HAL库 STM32CubeMX教程七—PWM输出(呼吸灯)
【Z小旋】【STM32】HAL库 STM32CubeMX教程八—定时器输入捕获
【32Haozi】STM32CubeMX系列06——定时器(定时、PWM、输入捕获)

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐