定时器&PWM应用编程

深入了解STM32定时器原理,掌握脉宽调制pwm生成方法。
一. 使用STM32F103的 Tim2~Tim5其一定时器的某一个通道pin(与GPIOx管脚复用,见下图),连接一个LED,用定时器计数方式,控制LED以2s的频率周期性地亮-灭。
二. 接上,采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒,自己调整到一个满意效果。使用Keil虚拟示波器,观察 pwm输出波形。
三. 再接上,采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。

文章目录


一、stm32定时器

1、定时器简介

定时器就是用来定时的机器,是存在于STM32单片机中的一个外设。其本质就是计数器,只不过 计数器 记录的是STM32的外部情况,所接收的也是外部脉冲,而 定时器 则是由STM32自身提供的一个非常稳定的计数器,这个稳定的计数器就是STM32上连接的晶振部件。

2、定时器分类

STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为 基本定时器,通用定时器 和 高级定时器 。

  • 基本定时器:
    TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
  • 通用定时器:
    TIM2 TIM3 TIM4 TIM5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
  • 高级定时器:
    TIM1 和 TIM8 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。

3、通用定时器介绍(TIM2/TIM3/TIM4/TIM5)

STM32的众多定时器中我们使用最多的是高级定时器和通用定时器,而高级定时器一般也是用作通用定时器的功能。

通用定时器的功能和特点:

  • 位于低速的APB1总线上(APB1)

  • 16 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT)

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

  • 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
    ① 输入捕获
    ② 输出比较
    ③ PWM 生成(边缘或中间对齐模式)
    ④ 单脉冲模式输出

  • 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路

  • 如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
    ①更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
    ②触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
    ③输入捕获
    ④输出比较
    ⑤支持针对定位的增量(正交)编码器和霍尔传感器电路
    ⑥触发输入作为外部时钟或者按周期的电流管理

STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

功能框图:
在这里插入图片描述

4、计数器模式

通用定时器可以向上计数、向下计数、向上向下双向计数模式。

  • 向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
  • 向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
  • 中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
    在这里插入图片描述

5、定时器工作原理

定时器大致可以分为四个大部分,分别是:
①时钟产生器部分
②时基单元部分
③输入捕获部分
④输出比较部分

1、时钟产生器部分
在第一部分时钟选择上,STM32定时器有四种时钟源选择,分别是:
①内部时钟(CK_INT)
②外部时钟模式:外部触发输入(ETR)
③内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时 Timer1 而作为另一个定时器Timer2的预分频器。
④外部时钟模式:外部输入脚(TIx)

2、时基单元
时基单元就是定时器框图的第二部分,它包括三个寄存器,分别是:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)和自动装载寄存器(TIMx_ARR) 。

  • 计数器寄存器(TIMx_CNT):
    向上计数、向下计数或者中心对齐计数;

  • 预分频器寄存器(TIMx_PSC):
    可将时钟频率按1到65535之间的任意值进行分频,可在运行时改变其设置值;

  • 自动装载寄存器(TIMx_ARR):
    如果TIMx_CR1寄存器中的ARPE位为0,ARR寄存器的内容将直接写入影子寄存器;如果ARPE为1,ARR寄存器的那日同将在每次的更新时间UEV发生时,传送到影子寄存器;如果TIM1_CR1中的UDIS位为0,当计数器产生溢出条件时,产生更新事件。

3、输入捕获通道
IC1、2和IC3、4可以分别通过软件设置将其映射到TI1、TI2和TI3、TI4;4个16位捕捉比较寄存器可以编程用于存放检测到对应的每一次输入捕捉时计数器的值;当产生一次捕捉,相应的CCxIF标志位被置1;同时如果中断或DMA请求使能,则产生中断或DMA请求。如果当CCxIF标志位已经为1,当又产生一个捕捉,则捕捉溢出标志位CCxOF将被置1。

在这里插入图片描述

4、输出比较通道
比较输出功能:定时器通过对预设的比较值与定时器的值做匹配比较之后,并依据相应的输出模式从而实现各类输出。如 PWM输出、电平翻转、单脉冲模式、强制输出 等。

二、PWM介绍

1.PWM的工作原理

在下图的通用定时器框图中,主要涉及到最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、右下部分(PWM输出)这三个部分。这里主要讲解一下右下部分(PWM输出):
在这里插入图片描述
下面以向上计数为例,简单地讲述一下PWM的工作原理:

  • 在PWM输出模式下,除了CNT(计数器当前值)、ARR(自动重装载值)之外,还多了一个值CCRx(捕获/比较寄存器值);
  • 当CNT小于CCRx时,TIMx_CHx通道输出低电平;
  • 当CNT等于或大于CCRx时,TIMx_CHx通道输出高电平;
  • 这个时候就可以对其下一个准确的定义了:所谓脉冲宽度调制模式(PWM模式),就是可以产生一个由TIMx_ARR寄存器确定频率,由TIMx_CCRx寄存器确定占空比的信号。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术

2.PWM输出的模式区别

  • PWM模式1:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为有效电平,否则为无效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为无效电平(OC1REF=0),否则为有效电平(OC1REF=1)
  • PWM模式2:在向上计数时,一旦TIMx_CNT<TIMx_CCR1时通道1为无效电平,否则为有效电平;在向下计数时,一旦TIMx_CNT>TIMx_CCR1时通道1为有效电平,否则为无效电平
  • 注意:PWM的模式只是区别什么时候是有效电平,但并没有确定是高电平有效还是低电平有效。这需要结合CCER寄存器的CCxP位的值来确定
  • 例如:若PWM模式1,且CCER寄存器的CCxP位为0,则当TIMx_CNT<TIMx_CCR1时,输出高电平;同样的,若PWM模式1,且CCER寄存器的CCxP位为2,则当TIMx_CNT<TIMx_CCR1时,输出低电平

3.PWM的计数模式

  • 向上计数模式:
    下面是一个PWM模式1的例子。当TIMx_CNT<TIMx_CCRx时PWM信号参考OCxREF为高,否则为低。如果TIMx_CCRx中的比较值大于自动重装载值(TIMx_ARR),则OCxREF保持为’1’。如果比较值为0,则OCxREF保持为’0’:
    在这里插入图片描述

  • 向下计数模式:
    在PWM模式1,当TIMx_CNT>TIMx_CCRx时参考信号OCxREF为低,否则为高。如果TIMx_CCRx中的比较值大于TIMx_ARR中的自动重装载值,则OCxREF保持为’1’。该模式下不能产生0%的PWM波形

  • 中央对齐模式:
    当TIMx_CR1寄存器中的CMS位不为’00’时,为中央对齐模式(所有其他的配置对OCxREF/OCx信号都有相同的作用)。根据不同的CMS位设置,比较标志可以在计数器向上计数时被置’1’、在计数器向下计数时被置’1’、或在计数器向上和向下计数时被置’1’。TIMx_CR1寄存器中的计数方向位(DIR)由硬件更新,不要用软件修改它。

4.PWM的一般步骤

  • 使能定时器和相关IO口时钟。调用函数:RCC_APB1PeriphClockCmd()
  • RCC_APB2PeriphClockCmd();
  • 初始化IO口为复用功能输出。调用函数:GPIO_Init();
  • 这里是要把PB5用作定时器的PWM输出引脚,所以要重映射配置,所以需要开- 启AFIO时钟,同时设置重映射;调用函数:RCC_APB2PeriphClockCmd();GPIO_PinRemapConfig();
  • 初始化定时器。调用函数:ARR,PSC等:TIM_TimeBaseInit();
  • 初始化输出比较参数。调用函数:TIM_OC2Init();
  • 使能预装载寄存器。调用函数:TIM_OC2PreloadConfig();
    使能定时器。调用函数:TIM_Cmd();
  • 不断改变比较值CCRx,达到不同的占空比效果;调用函TIM_SetCompare2()

三、用定时器计数方式,控制LED以2s的频率周期性地亮-灭

1.工程创建

  • 打开外部时钟,点击 System Core,选择RCC,在右侧弹出的菜单栏中选择Crystal/Ceramic Resonator

在这里插入图片描述

  • 选择调试接口,点击 System Core,选择SYS,在右侧弹出的菜单栏中选 Serial Wire
    在这里插入图片描述
  • 配置IO口,选择 PA5 作为 LED 灯的阴极输入,将其设置为 GPIO-Output
    在这里插入图片描述
  • 配置定时器,选择定时器2来实现定时的功能。选中 TIM2,将定时器2的时钟源设置为内部时钟;设置分频系数为71,向上计数模式,计数周期为50000。
    这里将分频系数设置为71,系统处理的时候会自动加1,所以此处进行的是72分频。由于时钟设置为为72MHZ,所以72分频后得到1MHZ的时钟;1MHZ的时钟,计数50000次,得到时间50000/1000000=0.05秒;每隔0.05秒,定时器2产生一次定时中断。这里要设置灯周期性的亮灭,周期为两秒,即亮一秒,灭一秒,则中断产生20次后,改变led的引脚电平。
    在这里插入图片描述
  • 配置中断,允许定时器2的中断
    在这里插入图片描述
    配置时钟,将HCLK修改为 72MHz
    在这里插入图片描述
  • 设置项目名称,生成项目
    在这里插入图片描述
    在这里插入图片描述

2.代码撰写

(1)再main主函数立面添加定时器启动代码

HAL_TIM_Base_Start_IT(&htim2);   //打开定时器TIM2

(2)在main主函数后面添加定时器回调中断函数

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//这里灯亮一秒,灭一秒,则中断产生20次改变一次电平
	static uint32_t time_cnt =0;   //记录中断次数
	if(htim->Instance == TIM2)   
	{
		if(++time_cnt >= 20)   //判断是否已经达到一秒
		{
			time_cnt =0;       //点灯用的中断次数归零
			HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5);    //改变LED所接引脚的电平
		}
	}
	
}

3.编译

  • 点击魔术棒->debug->ST-Link旁的setting
    在这里插入图片描述
    编译:
    在这里插入图片描述

4.运行

硬件连接

将二极管阴极接PA5口,阳极接正极,再将芯片3.3v接阳极
在这里插入图片描述

烧录运行

在这里插入图片描述

四、采用定时器pwm模式,让 LED 以呼吸灯方式渐亮渐灭,周期为1~2秒

1.工程创建

(1)配置RCC与SYS

点System Cor,选择RCC,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator:
在这里插入图片描述
选择调试接口,点System Cor,选择SYS。,在右侧弹出的菜单栏中选Serial Wire:
在这里插入图片描述

(2)配置TIM2

  • clock source选择internal clock
  • 将 Channel2 设置为 PWM Generation CH2(PWM输出通道2)
  • Prtscaler (定时器分频系数) 设置为71,即72分频——1MHz
  • Counter Mode(计数模式)设置为Up(向上计数模式)
  • Counter Period(自动重装载值) 设置为500,计数器从0向上计数(递增)到自动装载值,然后再次回到0开始计数,并产生一个计数溢出事件.
  • CKD(时钟分频因子) 设置为No Division (不分频 )
    在这里插入图片描述

(3)配置中断

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

(4)配置USART

在这里插入图片描述

(5)配置时钟,将 HCLK 设置为 72MHz

在这里插入图片描述

(6)设置项目的名称、位置和编译环境,生成项目

在这里插入图片描述

在这里插入图片描述

2.代码撰写

主要利用下面两个函数来控制定时器实现PWM输出

//开启 TIMx 的通道x,输出PWM
void HAL_TIM_PWM_Start(&htimx, TIM_CHANNEL_x);
/*
 * @Descript	PWM占空比更改
 * @param		htimx			相关定时器指针	
 * @param		TIM_CHANNEL_x	相关PWM通道
 * @param		Pulse			Duty_cycle = Pulse / Counter Period 
 * @return		void
*/

//修改TIMx的通道x的PWM波形的占空比
void __HAL_TIM_SET_COMPARE(&htimx, TIM_CHANNEL_x, Pulse);
/*
 * @Descript	PWM频率更改
 * @param		htimx			相关定时器指针	
 * @param		Counter_Period	PWM_fre = TIM_frq / ( Prescaler + 1 ) / ( Counter Period + 1 )
 * @return		void
*/

(1)在 main.c 文件中定义一个变量来记录 pwm 波形的占空比

uint16_t pwm=10;   //占空比

在这里插入图片描述

(2)开启TIM2的PWM的通道2

在main主函数中添加如下代码

HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_2);

在这里插入图片描述

(3)在主函数 while 循环里加入如下代码

要实现一秒由亮到暗,一秒由暗到亮,因初始设置分频系数为71,向上计数模式,计数周期为500,系统处理的分频系数会自动加1,所以此处进行的是72分频。由于时钟设置为为72MHZ,所以72分频后得到1MHZ的时钟;1MHZ的时钟,计数500次,得到时间500/1000000=0.0005秒;每隔0.5毫秒,定时器2产生一次定时中断。如下面代码所示每隔20毫秒pwm加10,到500要加50次,刚好对应一秒的变化。

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

   /* USER CODE BEGIN 3 */
	  while(pwm<500)
	  {
	   pwm = pwm + 10;
	   __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pwm);
	   HAL_Delay(20);//延时20毫秒
	  }
	  while(pwm>0)
	  {
	  pwm = pwm - 10;
	  __HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,pwm);
	  HAL_Delay(20);
	  }
}

3.编译运行

  • 点击魔术棒->debug->ST-Link旁的setting
    在这里插入图片描述

硬件连接

LED灯长脚——+极
LED灯短脚——A1
3.3v—— +极
在这里插入图片描述

编译:

在这里插入图片描述

烧录运行

在这里插入图片描述

4.观察PWM输出波形

1.环境搭建

·魔法棒中Target界面设置:
在这里插入图片描述
·Debug页面设置:
在这里插入图片描述

2.调试,并选择逻辑分析仪

在这里插入图片描述

3.添加引脚,输入

在这里插入图片描述

4.运行程序、观察波形

运行程序:
由于时钟设置为72MHZ,所以72分频后得到1MHZ的时钟;1MHZ的时钟,计数500次,得到时间500/1000000=0.5毫秒;每隔0.5毫秒,定时器产生一次定时中断,如下图可知运行正确。
在这里插入图片描述
我们在while(1)中设置的为20毫秒延时,观察下图,可知运行正确
在这里插入图片描述

五、再接上,采用定时器的另外一个通道,编程采集上面的pwm输出信号,获得其周期和脉宽,并重定向输出到串口显示。

1.工程配置

(1)配置RCC与SYS

点System Cor,选择RCC,在右侧弹出的菜单栏中选Crystal/Ceramic Resonator:
在这里插入图片描述
选择调试接口,点System Cor,选择SYS。,在右侧弹出的菜单栏中选Serial Wire:
在这里插入图片描述

(2)配置TIM1、2、3

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

(3)配置USART

在这里插入图片描述

(4)设置项目的名称、位置和编译环境,生成项目

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

2.代码撰写

1.在usart.c和main.c中添加头文件

#include "string.h"
#include "stdio.h"

main.c:
在这里插入图片描述
usart.c:
在这里插入图片描述

2.重定义printf函数和相关配置

  • 在主函数下面定义:
int fputc(int ch, FILE *f)
 
{
 
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
 
  return ch;
 
}

在这里插入图片描述

3.定义全局变量

uint8_t i = 0;
float Duty = 0;
float Frequency = 0;
uint16_t Cap_val1 = 0;
uint16_t Cap_val2 = 0;

4.mian主函数编写

在while(1)前添加

printf("串口通信测试\r\n");
    HAL_TIM_Base_Start_IT(&htim2); // 使能定时器及其更新中断
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 使能定时器及其PWM输出
    HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_1);       // 使能定时器及其输入捕获
    HAL_TIM_IC_Start_IT(&htim1, TIM_CHANNEL_2);       // 使能定时器及其输入捕获
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 10); // 设置一个PWM波形进行测量

在while(1)循环中添加

// 串口发送 频率 占空比
      	  
      printf("Cap_val1 is :%d ,  Cap_val2 is : %d \r\n", Cap_val1, Cap_val2);
        printf("Duty is :%0.2f%% Frequency is : %0.2f ms\r\n", Duty, Frequency);
	  HAL_Delay(1000);

5.添加捕获回调函数

// 定时输入捕获回调函数 计算占空比和频率
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1)
    {
        if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
        {
            Cap_val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
        }
        if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
        {
            Cap_val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
            Duty = 100 - (float)Cap_val2 / (float)Cap_val1 * 100;
            Frequency = 0.001 * Cap_val1;
        }
    }
	
}

3.编译运行

将PA8与PA6相连,其他保持不变。
在这里插入图片描述
结果:
在这里插入图片描述


总结

 在STM32的定时器及PWM实验中,我获得了许多宝贵的经验和体会。以下是我对这个实验的一些总结:

  • 在开始实验之前,我深入学习了STM32的定时器和PWM的工作原理。理解定时器的基本组成和工作方式,以及PWM信号的产生和运用,对于我在实验中的操作和结果有着决定性的影响。

  • 在实验过程中,我了解到如何正确地将STM32的定时器和PWM引脚与外部设备连接。正确的连接方式可以保证信号的稳定传输,避免信号干扰和其他可能出现的问题。我了解到如何使用中断处理程序来捕捉PWM信号的变化。通过在中断处理程序中读取捕获值,我可以实时地获取PWM信号的状态,这为我在后续的处理提供了便利。

  • 使用STM32CubeMX软件工具可以帮助我轻松地配置定时器和PWM。通过这个工具,我可以直观地设置各种参数,如预分频值、比较值等。这大大提高了我配置定时器的效率,并减少了出错的可能性。

  • 在进行实验的过程中,我遇到了许多问题。例如,定时器的初始化不正确、PWM信号的频率不准确,串口无数据输出。对于这些问题,我通过阅读文档、搜索解决方案和反复调试,最终找到了解决方法。这个过程让我更加深入地理解了STM32定时器和PWM的工作原理,也让我更加熟悉了STM32的开发环境。

 总的来说,这次STM32定时器及PWM实验让我深入了解了这些功能的工作原理和实现方式。通过动手实践和解决问题,我不仅提高了自己的编程技能,还对嵌入式系统开发有了更深入的认识。我相信这次实验的经验将对我的未来学习和工作产生积极的影响。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值