定时器&PWM应用编程

一、STM32定时器及PWM原理简介

1、STM32定时器
(1)分类

  在STM32F1 系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。通用定时器 TIM2/3/4/5是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
在这里插入图片描述

(2)通用定时器

  STM32F1 的通用定时器是由一个通过可编程预分频器(PSC)驱动的16位自由装载计数器(CNT)构成,通用TIMx(2、3、4、5)定时器可用于测量输入信号的脉冲长度(输入捕获)或者 产生输出波形(输出比较和PWM)等。

  • 定时器功能
    (1)16位 向上 向下自动装载计数器(TIMx_CNT)
    (2)16位可编程(可实时修改) 预分频(TIMx_PSC)
    (3) 4 个独立通道( TIMx_CH1~4 ),这些通道可以用来作为:PWM ,输入捕获 , 输出比较 , 单脉冲模式输出

  • 产生中断的条件

    • 更新:计数器向上溢出 / 向下溢出,计数器初始化 (通过软件或者内部 / 外部触发)
    • 触发事件 ( 计数器启动、停止、初始化或者由内部 / 外部触发计数 )
    • 输入捕获
    • 输出比较
    • 支持针对定位的增量 ( 正交 ) 编码器和霍尔传感器电路
    • 触发输入作为外部时钟或者按周期的电流管理
  • TIM的时钟源
    定时器的时钟来源有4个:

    • 内部时钟( CK_INT )
    • 外部时钟模式 1 :外部输入脚( TIx )
    • 外部时钟模式 2 :外部触发输入( ETR )
    • 内部触发输入( ITRx ):使用 A 定时器作为 B 定时器的预分频器( A 为 B 提供时钟)
  • 定时器配置

    • 预分频系数:用于设置定时器的计数速度,可以根据需要进行调整。
    • 计数周期:用于设置定时器的计数周期,决定定时器溢出的时间间隔。
    • 工作模式:定时器可以工作在不同的模式下,如定时器模式、计数器模式、PWM模式等。
    • 中断使能:可以选择是否使能定时器的中断功能,用于触发中断任务。
  • 参数计算

    • 计数频率:计数频率 = 主时钟频率 / (预分频系数 * 计数周期)
2、PWM脉冲宽度调制

  脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。

(1)主要参数
  • PWM占空比
      PWM信号保持高电平的时间百分比称为占空比。如果信号始终为高电平,则它处于1
    00%占空比,如果它始终处于低电平,则占空比为0%。如图1所示,T1为占空比,T为一个PWM周期。
      PWM占空比:PWM占空比 = (比较值 / 计数周期) * 100%
    在这里插入图片描述
  • PWM的频率
      PWM信号的频率决定PWM完成一个周期的速度,就是一秒钟PWM有多少个周期。STM32的MDK编译器可以选择5MHZ,10MHZ,20MHZ和50MHZ。
  • PWM的周期
      周期T=1/频率f
      假设周期T为 1s ,那么频率就是 1Hz, 高电平时间0.5s ,低电平时间0.5s ,总的占空比就是 0.5 /1 =50%。
(2)产生方式

通过STM32控制板,有两种方式能产生PWM,第一是利用普通IO口输出PWM,第二种是利用定时器的PWM的IO口或复用IO口

  • PWM端口
      STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出。
  • 普通IO口
      一般能够输出PWM的端口都会在主要功能那一栏出现CHx的标志,而普通定时器没有出现这种标志。如图所示,上面的红框就是普通的定时器,不是专用的PWM端口。
    在这里插入图片描述
  • 区别
      一般而言,尽量选用PWM口进行PWM输出,因为普通IO口模拟PWM的输出频率越高,进入定时器中断的次数就越快,中断间隔的时间越短,如果再有其他类型的中断也要处理时,会因为中断的优先级嵌套等待响应,影响控制精度,PWM输出误差增大,也会影响其他如ADC等中断处理,甚至会较出现单片机逻辑出错,死机或者跑飞的情况。
      普通IO也可以输出PWM,只是产生PWM一般用转用芯片(开关电源上用的较多)或者单片机的PWM内置模块如定时器,很小直接用MCU的IO口线直接输出因为那样太耗MCU资源了。

二、定时器计数方式控制LED

1、编程思路

使能APB1时钟
使用定时器3,打开定时器时钟
初始化时基初始化结构体
开启中断,配置NVIC
使能定时器
编写TIM3中断服务程序

2、函数分析
  • 定义函数

    void TIM3_Int_Init(uint16_t arr,uint16_t psc);
    

    arr:自动重装载值
    psc:预分频系数
    定时器周期主要由TIMx_ARRTIMx_PSC两个寄存器值决定。


    时钟计算:
    各时钟频率:
    SYSCLK = 72M
    AHB时钟 = 72M
    APB1时钟 =36M
    则APB1的分频系数=AHB / APB1 =2
    因此CK_INT的时钟频率为 2*36M= 72M
    中断时间计算公式:
    Tout=( (arr+1) * (psc+1) )/Tclk
    其中Tclk(MHz)为TIM3的输入时钟频率,Tout(us)为TIM3溢出时间


  • APB1时钟使能

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    

    在这里插入图片描述

  • 初始化定时器

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    	//定时器TIM3初始化
    TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
    TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
    
  • 使能中断

    TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
    

    在这里插入图片描述

  • 开启中断,配置NVIC

    NVIC_InitTypeDef NVIC_InitStructure;
    //中断优先级NVIC设置
    NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
    

    在这里插入图片描述

  • 使能定时器TIM3

    TIM_Cmd(TIM3, ENABLE);  //使能TIMx	
    

    在这里插入图片描述

  • 编写TIM3中断服务程序

    //定时器3中断服务程序
    void TIM3_IRQHandler(void)   //TIM3中断
    {
      if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
      	{
      	TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
      	GPIOA->ODR ^=GPIO_Pin_5;//对PA5取反
      	}
    }
    
    • 读取中断状态寄存器的值判断中断类型的函数,用于判断定时器 TIMx 的中断类型 TIM_IT 是否发生中断
      ITStatus TIM_GetITStatus(TIM3, TIM_IT_Update)
      
      在这里插入图片描述
    • 清除中断标志位的函数,用于清除定时器 TIMx 的中断 TIM_IT 标志位
      TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志
      
      在这里插入图片描述
3、完整代码

main.c

#include "stm32f10x.h"
#include "led.h"

//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3
void TIM3_Int_Init(uint16_t arr,uint16_t psc)
{
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
 
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
	
	//定时器TIM3初始化
	TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
	TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
 
	TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
 
	//中断优先级NVIC设置
	NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
 
 
	TIM_Cmd(TIM3, ENABLE);  //使能TIMx					 
}
//定时器3中断服务程序
void TIM3_IRQHandler(void)   //TIM3中断
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
		{
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
		GPIOA->ODR ^=GPIO_Pin_5;//对PA5取反
		}
}

 int main(void)
 {		
    	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
 	LED_Config();
	TIM3_Int_Init(19999,7199);//10Khz的计数频率,计数到10000为1s  
   	while(1)
	{}	 
}	 

led.c

#include "led.h"

void LED_Config(void)
{
	 //定义一个GPIO_InitTypeDef类型的结构体
     GPIO_InitTypeDef  GPIO_InitStructure;
 
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	 //使能PA,PB,PC端口时钟
     //选择要控制的GPIO引脚	
     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;				 //LED0-->PA5 端口配置
     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; 		 //推挽输出
     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;		 //IO口速度为50MHz
     //调用库函数初始化GPIO
     GPIO_Init(GPIOA, &GPIO_InitStructure);					 //根据设定参数初始化GPIOA4
     GPIO_SetBits(GPIOA,GPIO_Pin_5);						 //PA4 输出高
}

led.h

    #ifndef __LED_H
#define __LED_H

#include "stm32f10x.h"

void LED_Config(void);
void LED_Toggle(void);

#endif
4、硬件实现

在这里插入图片描述

三、采用定时器PWM模式设置呼吸灯

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

1、函数分析
  • 使能APB1外设时钟

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    

    在这里插入图片描述

  • 使能APB2外设时钟

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    

    在这里插入图片描述

  • 初始化GPOIOA端口

    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
  • 设置TIM2内部时钟

    TIM_InternalClockConfig(TIM2);
    

    在这里插入图片描述

  • 初始化TIM2

      //初始化TIM2
      TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
      //时钟分割
      TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      //TIM向上计数模式
      TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
      //设置在下一个更新事件装入活动的自动重装载寄存器周期的值ARR
      TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		 
      //设置用来作为 TIMx 时钟频率除数的预分频值PSC
      TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;	 	
      TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
      TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    

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

  • 配置PWM模式

      //初始化TIM2 
      TIM_OCInitTypeDef TIM_OCInitStructure;
      
      TIM_OCStructInit(&TIM_OCInitStructure);
      //选择定时器模式为TIM 脉冲宽度调制模式 1 
      TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
      //TIM 输出比较极性高
      TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
      //TIM比较输出使能
      TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
      //设置待装入捕获比较寄存器的脉冲值CCR
      TIM_OCInitStructure.TIM_Pulse = 0;		
      //根据指定的参数初始化外设TIM2 OC2
      TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    

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

  • 设置 TIM2 捕获比较 1 寄存器值

    void PWM_SetCompare1(uint16_t Compare)
    {
      TIM_SetCompare1(TIM2, Compare);
    }
    

    在这里插入图片描述

2、完整代码

main.c

#include "stm32f10x.h"                  
#include "delay.h"

#include "PWM.h"
 
uint8_t i;
 
int main(void)
{
	PWM_Init();
	
	while (1)
	{
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(i);
			Delay_ms(10);
		}
		for (i = 0; i <= 100; i++)
		{
			PWM_SetCompare1(100 - i);
			Delay_ms(10);
		}
	}
}

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	//初始化TIM2
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	//时钟分割
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	//TIM向上计数模式
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	//设置在下一个更新事件装入活动的自动重装载寄存器周期的值ARR
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		 
	//设置用来作为 TIMx 时钟频率除数的预分频值PSC
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;	 
	
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	//初始化TIM2 
	TIM_OCInitTypeDef TIM_OCInitStructure;
	
	TIM_OCStructInit(&TIM_OCInitStructure);
	//选择定时器模式为TIM 脉冲宽度调制模式 1 
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	//TIM 输出比较极性高
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	//TIM比较输出使能
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	//设置待装入捕获比较寄存器的脉冲值CCR
	TIM_OCInitStructure.TIM_Pulse = 0;		
	//根据指定的参数初始化外设TIM2 OC2
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}

PWM.h

#ifndef __PWM_H
#define __PWM_H
 
void PWM_Init(void);
void PWM_SetCompare1(uint16_t Compare);
 
#endif
3、硬件实现

在这里插入图片描述

4、观测PWM输出波形

  

四、 采集PWM输出信号重定向显示

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

1、代码实现
  • 新建串口通信模块
    serial.c

    #include "stm32f10x.h"                  // Device header
    #include <stdio.h>
    #include <stdarg.h>
    
    void Serial_Init(void)
    {
    //开启时钟,打开GPIO和USART时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
    //初始化GPIO引脚
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;       
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    //初始化USART
    USART_InitTypeDef USART_InitStruture;
    USART_InitStruture.USART_BaudRate=9600;                                             //波特率
    USART_InitStruture.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制
    USART_InitStruture.USART_Mode=USART_Mode_Tx;                                        //串口模式
    USART_InitStruture.USART_Parity=USART_Parity_No;                                        //校验位
    USART_InitStruture.USART_StopBits=USART_StopBits_1;                                 //停止位
    USART_InitStruture.USART_WordLength=USART_WordLength_8b;                        //字长
    USART_Init(USART1,&USART_InitStruture);
    
    //开启USART
    USART_Cmd(USART1,ENABLE);
    }
    
    void Serial_SendByte(uint8_t Byte)
    {
    USART_SendData(USART1,Byte);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
    }
    
    void Serial_SendArray(uint8_t *Array,uint16_t Length)
    {
    uint16_t i;
    for(i=0;i<Length;i++)
    {
       Serial_SendByte(Array[i]);
    }
    }
    
    void Serial_SendString(char *String)
    {
    uint8_t i;
    for(i=0;String[i]!='\0';i++)
    {
       Serial_SendByte(String[i]);
    }
    }
    
    uint32_t Serial_Pow(uint32_t X,uint32_t Y)
    {
    uint32_t Result=1;
    while(Y--)
    {
       Result*=X;
    }
    return Result;
    }
    
    void Serial_SendNum(uint32_t Number,uint8_t Length)
    {
    uint8_t i;
    for(i=0;i<Length;i++)
    {
       Serial_SendByte(Number/Serial_Pow(10,Length-i-1)%10+'0');
    }
    }
    
    int fputc(int ch,FILE *f)
    {
    Serial_SendByte(ch);
    return ch;
    }
    
    void Serial_Printf(char *format,...)
    {
    char String[100];
    va_list arg;
    va_start(arg,format);
    vsprintf(String,format,arg);
    va_end(arg);
    Serial_SendString(String);
    }
    

    serial.h

        #ifndef __SERIAL_H
    #define __SERILA_H
    
    #include <stdio.h>
    void Serial_Init(void);
    void Serial_SendByte(uint8_t Byte);
    void Serial_SendArray(uint8_t *Array,uint16_t Length);
    void Serial_SendString(char *String);
    void Serial_SendNum(uint32_t Number,uint8_t Length);
    void Serial_Printf(char *format,...);
    
    #endif
    
  • 利用TIMx输入捕获功能获取PWM的频率和占空比,新建IC.c和IC.h输入捕获模块
    IC.c

    #include "stm32f10x.h"                  // Device header
    
    //输入捕获模块初始化
    void IC_Init(void)
    {
      //RCC开启时钟,打开GPIO和TIM时钟
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
      
      //GPIO初始化,把GPIO配置成输入模式
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉输入
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;       
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      //配置时基单元,让CNT计数器在内部时钟驱动下自增运行
      TIM_InternalClockConfig(TIM3);
      TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
      TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
      TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
      TIM_TimeBaseInitStructure.TIM_Period =65536 - 1;        //ARR
      TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;       //PSC
      TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
      TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
      
      //配置输入捕获单元
      TIM_ICInitTypeDef TIM_ICInitStructer;
      TIM_ICInitStructer.TIM_Channel=TIM_Channel_1;                   //选择通道
      TIM_ICInitStructer.TIM_ICFilter=0x01;                               //滤波器
      TIM_ICInitStructer.TIM_ICPolarity=TIM_ICPolarity_Rising;            //级性
      TIM_ICInitStructer.TIM_ICPrescaler=TIM_ICPSC_DIV1;          //分频器,DIV1不分频
      TIM_ICInitStructer.TIM_ICSelection=TIM_ICSelection_DirectTI;    //选择触发信号从哪个引脚输入
      TIM_PWMIConfig(TIM3,&TIM_ICInitStructer);
      
      //选择从模式的触发源
      TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);
      
      //选择触发后执行的操作
      TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//CNT自动清零
      
      //开启定时器
      TIM_Cmd(TIM3,ENABLE);
    }
    
    //测量频率
    uint32_t IC_GetFreq(void)
    {
      return 1000000/(TIM_GetCapture1(TIM2)+1);
    }
    
    //测量占空比
    uint32_t IC_GetDuty(void)
    {
      return (TIM_GetCapture2(TIM2)+1)*100/TIM_GetCapture1(TIM2);
    }
    

    IC.h

    #ifndef __IC_H
    #define __IC_H
    
    void IC_Init(void);
    uint32_t IC_GetFreq(void);
    uint32_t IC_GetDuty(void);
    
    #endif
    
  • 主函数main.c

    #include "stm32f10x.h"                  
    #include "delay.h"
    #include "serial.h"
    #include "IC.h"
    #include "PWM.h"
    
    uint8_t i;
    float Pulse_Width=1;//脉宽
    float Cycle=1;//周期
    float Duty=1;//占空比
    
    int main(void)
    {
     
     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
     uart_init(115200);	 //串口初始化为115200
     PWM_Init();
     IC_Init();
     Serial_Init();
    
     while (1)
     {
     	//占空比逐渐变大,LED逐渐变亮
         for (i = 0; i <= 100; i++)
         {
             PWM_SetCompare1(i);
             Serial_Printf("$频率:");
             Serial_SendNum(IC_GetFreq(),5);
             Serial_Printf("Hz\r\t占空比:");
             Serial_SendNum(IC_GetDuty(),2);
             Serial_SendByte(0x25);
             Serial_Printf("\r\t脉宽:");
             Cycle=1000/IC_GetFreq();//ms
             Duty=IC_GetDuty()*0.01;
             Serial_Printf("%f",Cycle*Duty);
             Serial_Printf("ms\r\n");
             
             
             Delay_ms(50);
         }
         //占空比逐渐变小,LED逐渐变暗
         for (i = 0; i <= 100; i++)
         {
             PWM_SetCompare1(100 - i);
             Serial_Printf("$频率:");
             Serial_SendNum(IC_GetFreq(),5);
             Serial_Printf("Hz\r\t占空比:");
             Serial_SendNum(IC_GetDuty(),2);
             Serial_SendByte(0x25);
             Serial_Printf("\r\t脉宽:");
             Cycle=1000/IC_GetFreq();//ms
             Duty=IC_GetDuty()*0.01;
             Serial_Printf("%f",Cycle*Duty);
             Serial_Printf("ms\r\n");
             
             
             Delay_ms(50);
         }
    
     }
    }```
    
    
    
2、硬件实现

  在这里插入图片描述

五、参考资料

(1) STM32——PWM知识详解
(2)PWM原理 PWM频率与占空比详解
(3)STM32F103(七)——通用定时器的说明与功能
(4)STM32学习笔记——通用定时器TIMx介绍及配置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值