• STM32F407输入捕获应用--PWM 输入模式测量脉冲频率与宽度



    输入捕获一般应用在两个方面,一个方面是脉冲跳变沿时间测量,另一方面
    是 PWM 输入测量。

    一、测量脉宽或者频率

    在这里插入图片描述
    1.测量频率

    当捕获通道 TIx 上出现上升沿时,发生第一次捕获,计数器 CNT 的值会被锁存到捕获寄存器 CCR 中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器中的值读取到 value1 中。当出现第二次上升沿时,发生第二次捕获,计数器 CNT 的值会再次被锁存到捕获寄存器 CCR 中,并再次进入捕获中断,在捕获中断中,把捕获寄存器的值读取到 value3 中,并清除捕获记录标志。利用 value3 和 value1 的差值我们就可以算出信号的周期(频率)。

    2.测量脉宽

    当捕获通道 TIx 上出现上升沿时,发生第一次捕获,计数器 CNT 的值会被锁存到捕获寄存器 CCR 中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器中的值读取到 value1 中。然后把捕获边沿改变为下降沿捕获,目的是捕获后面的下降沿。当下降沿到来的时候,发生第二次捕获,计数器 CNT 的值会再次被锁存到捕获寄存器 CCR 中,并再次进入捕获中断,在捕获中断中,把捕获寄存器的值读取到 value3 中,并清除捕获记录标志。然后把捕获边沿设置为上升沿捕获。在测量脉宽过程中需要来回的切换捕获边沿的极性,如果测量的脉宽时间比较长,定时器就会发生溢出,溢出的时候会产生更新中断,我们可以在中断里面对溢出进行记录处理。

    二、PWM 输入模式

    测量脉宽和频率还有一个更简便的方法就是使用 PWM 输入模式。与上面那种只使用一个捕获寄存器测量脉宽和频率的方法相比,PWM 输入模式需要占用两个捕获寄存器。

    在这里插入图片描述
    当使用 PWM 输入模式的时候,因为一个输入通道(TIx)会占用两个捕获通道(ICx),所以一个定时器在使用 PWM 输入的时候最多只能使用两个输入通道(TIx)。我们以输入通道 TI1 工作在 PWM 输入模式为例来讲解下具体的工作原理,其他通道以此类推即可。

    PWM 信号由输入通道 TI1 进入,因为是 PWM 输入模式的缘故,信号会被分为两路,一路是 TI1FP1,另外一路是 TI2FP2。其中一路是周期,另一路是占空比,具体哪一路信号对应周期还是占空比,得从程序上设置哪一路信号作为触发输入,作为触发输入的哪一路信号对应的就是周期,另一路就是对应占空比。作为触发输入的那一路信号还需要设置极性,是上升沿还是下降沿捕获,一旦设置好触发输入的极性,另外一路硬件就会自动配置为相反的极性捕获,无需软件配置。一句话概括就是:选定输入通道,确定触发信号,然后设置触发信号的极性即可,因为是 PWM 输入的缘故,另一路信号则由硬件配置,无需软件配置。当使用 PWM 输入模式的时候必须将从模式控制器配置为复位模式(配置寄存器 SMCR 的位 SMS[2:0]来实现),即当我们启动触发信号开始进行捕获的时候,同时把计数器 CNT 复位清零。

    下面我们以一个更加具体的时序图来分析下 PWM 输入模式。

    在这里插入图片描述

    PWM 信号由输入通道 TI1 进入,配置 TI1FP1 为触发信号,上升沿捕获。当上升沿的时候 IC1 和 IC2 同时捕获,计数器 CNT 清零,到了下降沿的时候,IC2捕获,此时计数器 CNT 的值被锁存到捕获寄存器 CCR2 中,到了下一个上升沿的时候,IC1 捕获,计数器 CNT 的值被锁存到捕获寄存器 CCR1 中。其中 CCR2 测量的是脉宽,CCR1 测量的是周期。

    从软件上来说,用 PWM 输入模式测量脉宽和周期更容易,付出的代价是需要占用两个捕获寄存器

    三、软件实现

    3.1、硬件准备

    1、粤嵌开发板一套
    在这里插入图片描述

    2、迷你示波器一个

    在这里插入图片描述

    3.2代码

    初始化代码

    /*****************************************
    引脚说明:
    PB6
    
    TIM4_CH1(TIM4 -- APB1 16位  84MHZ)
    
    *****************************************/
    
    
    void Pwm_PB6_InputInit(void)
    {
    	GPIO_InitTypeDef 			GPIO_InitStructure;
    	TIM_TimeBaseInitTypeDef  	TIM_TimeBaseStructure;
    	NVIC_InitTypeDef 			NVIC_InitStructure;
    	TIM_ICInitTypeDef			TIM4_ICInitStructure;
    	
    	
    	
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//时钟使能
    	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);//使能GPIOB时钟
    	
    	
    	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_6; 			//GPIOB6
    	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;			//复用功能
    	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_100MHz;	//速度100MHz
    	GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP; 		//推挽复用输出
    	GPIO_InitStructure.GPIO_PuPd 	= GPIO_PuPd_NOPULL; 		//下拉
    	GPIO_Init(GPIOB,&GPIO_InitStructure); 					//初始化
    	
    	// 定时器复用引脚
    	GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_TIM4);
    
    	TIM_TimeBaseStructure.TIM_Prescaler		= 83;  					//定时器分频
    	TIM_TimeBaseStructure.TIM_CounterMode	= TIM_CounterMode_Up; 	//向上计数模式
    	TIM_TimeBaseStructure.TIM_Period		= 49999;   				//自动重装载值
    	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; 		//分频因子 配置死区时会用到
    	
    	TIM_TimeBaseInit(TIM4,&TIM_TimeBaseStructure);
    
    
    	//初始化TIM2输入捕获参数
    	TIM4_ICInitStructure.TIM_Channel 	= TIM_Channel_1;   			//通道1
    	TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
    	TIM4_ICInitStructure.TIM_ICSelection= TIM_ICSelection_DirectTI; //映射到TI1上
    	TIM4_ICInitStructure.TIM_ICPrescaler= TIM_ICPSC_DIV1;	 		//配置输入分频,不分频 
    	TIM4_ICInitStructure.TIM_ICFilter   = 0x05;						//IC3F=0000 配置输入滤波器 不滤波
    	// 初始化 PWM 输入模式
    	TIM_PWMIConfig(TIM4, &TIM4_ICInitStructure);
    		
    	
    
    	// 当工作做 PWM 输入模式时,只需要设置触发信号的那一路即可(用于测量周期)
    	// 另外一路(用于测量占空比)会由硬件自带设置,不需要再配置
    	
    	// 选择输入捕获的触发信号
    	TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1);
    
    	// 选择从模式: 复位模式
    	// PWM 输入模式时,从模式必须工作在复位模式,当捕获开始时,计数器 CNT 会被复位
    	TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset);
    	TIM_SelectMasterSlaveMode(TIM4,TIM_MasterSlaveMode_Enable);
    
    	// 使能捕获中断,这个中断针对的是主捕获通道(测量周期那个)
    	TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE);
    	TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
    	
    	NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级2
    	NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;		//子优先级0
    	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
    	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、
    	
    	//TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC2,ENABLE);				//允许更新中断 ,允许CC2IE捕获中断	
    	
    	TIM_Cmd(TIM4,ENABLE ); 	//使能定时器4
    	
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79

    主函数代码

    u32 IC1Value, IC2Value;
    float DutyCycle,Frequency;
    
    
    //定时器4中断服务程序	 
    void TIM4_IRQHandler(void)
    { 		    
    
    	/* 清除定时器捕获/比较 1 中断 */
    	TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);
    
    	/* 获取输入捕获值 */
    	IC1Value = TIM_GetCapture1(TIM4);
    	IC2Value = TIM_GetCapture2(TIM4);
    	//printf("IC1Value = %d IC2Value = %d ",IC1Value,IC2Value);
    	// 注意:捕获寄存器 CCR1 和 CCR2 的值在计算占空比和频率的时候必须加 1
    	if (IC1Value != 0) 
    	{
    		flag = 1;
    	} 
    
    }
    
    
    int main(void)
    {
    	
    	unsigned int count = 0;
    	
    	
    	//设置NVIC分组 第二分组 抢占优先级范围:0~3  响应优先级范围:0~3
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	
    	Delay_Init();
    	//对LED初始化
    	Led_Init();
    
    	Usart1_Init(115200);
    	Pwm_PB6_InputInit();
    	
    	while(1)
    	{
    
    		//每隔500ms计算一次占空比与频率。
    		if(flag == 1)
    		{
    			
    			/* 占空比计算 */
    			DutyCycle = (float)((IC2Value+1) * 100) / (IC1Value+1);
    
    			/* 频率计算 */
    			Frequency = 84000000/(83+1)/(float)(IC1Value+1);			
    			
    			
    			printf("占空比:%0.2f%% 频率:%0.2fHz\r\n",DutyCycle,Frequency);
    			
    			DutyCycle = 0;
    			Frequency = 0;
    			flag = 0;
    		}
    			
    		delay_ms(500);
    			
    	}
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    3.4 验证

    在这里插入图片描述

    最后发现有一定的误差,未知是不是这几百块的示波器产生的问题,还是软件的问题。还待验证,到时再找台高精度的脉冲发生器看看了。有了解的码农可以互相讨论下。

    程序链接:https://download.csdn.net/download/wwwqqq2014/87148727

  • 相关阅读:
    git push origin HEAD:refs/for/master
    使用DISM 删除系统的保留空间
    gin支持prometheus
    微软正式发布:.NET Aspire 云原生开发框架
    SpringBoot2.x 整合AOP切面编程
    python金融分析小知识(38)——Jupyter Notebook更改文件路径
    Base64与MD5(数据加密)与ValidateCode(验证码)
    指定牛导|肿瘤专业医生芝加哥大学博士后实现夙愿
    秋招每日一题T10——峰会
    计算机网络408考研 2014
  • 原文地址:https://blog.csdn.net/wwwqqq2014/article/details/127960582