STM32
直播中

低调de炫耀爱

11年用户 762经验值
私信 关注
[问答]

如何利用STM32F103的输入捕获模式去测量PWM输出波形频率呢

输入捕获模式有何功能?

如何利用STM32F103的输入捕获模式去测量PWM输出波形频率呢?

回帖(1)

陈嘉宁

2021-12-13 11:34:43
实验目的

STM32的通用定时器和高级定时器拥有输入捕获功能,本章我们将利用TIM3的CH3通道产生PWM输出波形连接到TIM5的CH2输入捕捉通道,测量PWM输出波形频率。数据手册请参看第14章中的相关内容
实验简介

输入捕获模式可以用来测量脉冲宽度和频率。所谓的捕获,就是检测定时器的TIMx_CHx输入捕获引脚,检测到信号跳变时,将当前的定时器的计数值(TIMx_CNT)保存到捕获寄存器(TIMx_CCRx),完成一次捕获,当使能捕获中断时,会产生中断。
本实验是利用捕获功能来测量脉冲频率,脉冲由PWM模块产生,那我们是怎么样测量的呢?原理是这样的:连续捕获2次上升沿/下降沿信号跳变,分别记录下2次的定时器计数值,这2个计数值的差值就是脉冲宽度,且计数器的计数频率我们是知道的,这样我们就可以计算出周期和频率。
威廉希尔官方网站 设计

本实验,是将上一章产生的PWM波形,通过跳帽连接到TIM5的CH2(PA1)进行捕捉,USB转串口打印频率信息





有关寄存器

因为使用的是TIM5_CH2通道
APB1 外设时钟使能寄存器(RCC_APB1ENR)





APB2 外设时钟使能寄存器(RCC_APB2ENR)





TIM1 和TIM8 捕获/比较模式寄存器 1(TIMx_CCMR1)





这里配置为10 IC2映射到TI1上















注意: 这里主要靠介绍,我们使用的是2 这里是1
TIM1 和TIM8 捕获/比较使能寄存器(TIMx_CCER)









**TIM1 和TIM8 DMA/中断使能寄存器(TIMx_DIER) **










寄存器代码

test.c

#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "key.h"


extern u8 TIM5CH2_CAPTURE_STA;
//输入捕获状态
extern u8 TIM5CH2_CAPTURE_VAL;
//输入捕获值


int main(void)
{
        u32 temp = 0;
        Stm32_Clock_Init(9);
        uart_init(72,115200);
        delay_init(72);
        LED_Init();
        TIM3_PWM_Init(899,0);
        //不分频 ,PWM频率为72000/(899+1)=80Khz
        TIM5_Cap_Init(0xffff,72-1);
        //以1Mhz的频率计数
        while(1)
        {
                delay_ms(10);
                LED3_PWM_VAL++;
                if(LED3_PWM_VAL ==300)
                        LED3_PWM_VAL=0;
                if(TIM5CH2_CAPTURE_STA & 0x80)
                //成功捕获道了一次高电平
                {
                        temp = TIM5CH2_CAPTURE_STA & 0x3f;
                        //一般情况下定时器不会溢出,前六位做溢出时间
                        temp *= 65536;
                        //1次 65536
                        temp += TIM5CH2_CAPTURE_VAL;
                        //得到高电平时间时间
                        printf("HIGH: %d usrn",temp);
                        //打印高电平
                        TIM5CH2_CAPTURE_STA = 0;
                        //开启下次捕获
                       
                }
               
        }
}


timer.h


#ifndef __TIMER_H
#define __TIMER_H


#include "sys.h"


#define LED3_PWM_VAL  TIM3->CCR3


void TIM3_Int_Init(u16 arr,u16 psc);
void TIM3_PWM_Init(u16 arr,u16 psc);
void TIM5_Cap_Init(u16 arr,u16 psc);


#endif


timer.c


#include "timer.h"
#include "led.h"






//捕捉前的前章有,不再赘述
void TIM3_IRQHandler(void)
{
        if(TIM3->SR & 0x0001)
        {
                LED2 = !LED2;
        }
        TIM3->SR &= ~(1<<0);
}


void TIM3_Int_Init(u16 arr,u16 psc)
{
        RCC->APB1ENR |= 1<<1;
        TIM3->ARR = arr;
        TIM3->PSC = psc;  
        TIM3->DIER |= 1<<0;  
        TIM3->CR1  |= 0x01;   
       
        MY_NVIC_Init(1,3,TIM3_IRQn,2);×é 2
}








void TIM3_PWM_Init(u16 arr,u16 psc)
{
        RCC->APB1ENR |= 1<<1;
        RCC->APB2ENR |= 1<<3;
       
        GPIOB->CRL &= 0xFFFFFFF0;
        GPIOB->CRL |= 0x0000000B;
  
        RCC->APB2ENR |= 1<<0;
       
        AFIO->MAPR &= 0xFFFFF3FF;
  AFIO->MAPR |= 1<<11;  
       
        TIM3->ARR = arr;
        TIM3->PSC = psc;
       
        TIM3->CCMR2 |= 7<<4;
        TIM3->CCMR2 |= 1<<3;
        TIM3->CCER |= 1<<8;
        TIM3->CR1 = 0x0080;
        TIM3->CR1 |= 0x01;
}










void TIM5_Cap_Init(u16 arr,u16 psc)
{
        RCC->APB1ENR |= 1<<3;
        //TIM5使能
        RCC->APB2ENR |= 1<<2;
        //GPIOA 也就是PORTA时钟使能
        //因为使用的是PB1
        GPIOA->CRL &= 0xFFFFFF0F;
        GPIOA->CRL |= 0x00000080;
        //PA1 引脚 下拉/上拉输入模式
        GPIOA->ODR |= 0<<1;
        //先让PA1为低电平,然后上升沿捕捉
       
        TIM5->ARR = arr;
        //设定计时器自动重装值
        TIM5->PSC = psc;
        //预分频器
       
        TIM5->CCMR1 |= 1<<9;
        //CC2S = 10
        TIM5->CCMR1 |= 0<<12;
         //IC2F = 0000 ,配置输入滤波器,不滤波
        TIM5->CCMR1 |= 0<<10;
        //配置输入分频,不分频
       
        TIM5->CCER |= 0<<5;  
         //CC2P = 0 上升沿捕捉
        TIM5->CCER |= 1<<4;   
         //允许捕捉计数器值到捕获寄存器中
       
        TIM5->DIER |= 1<<2;
         //允许2捕获中断
  TIM5->DIER |= 1<<0;
  //允许更新中断
        TIM5->CR1 |=0x01;  
         //使能定时器
        MY_NVIC_Init(2,0,TIM5_IRQn,2);
}




u8 TIM5CH2_CAPTURE_STA = 0;
//输入捕获状态
u16 TIM5CH2_CAPTURE_VAL ;
//输入捕获值


void TIM5_IRQHandler(void)
{
        u16 tsr;
        tsr = TIM5->SR;
        //状态寄存器
        if((TIM5CH2_CAPTURE_STA&0x80) == 0)
        //还未捕捉成功
        {
                if(tsr&0x01)
                //溢出
                {
                        if(TIM5CH2_CAPTURE_STA&0x40)
                        //已经捕获到了高电平
                        {
                                if((TIM5CH2_CAPTURE_STA&0x3F) == 0x3F)
                                //高电平太长了
                                {
                                        TIM5CH2_CAPTURE_STA |= 0x80;
                                        //标志成功捕获了一次
                                        TIM5CH2_CAPTURE_VAL = 0xFFFF;
                                }
                                else
                                        TIM5CH2_CAPTURE_STA++;
                        }
                }
                if(tsr&0x04)
                //捕获2发生捕获事件
                {
                        if(TIM5CH2_CAPTURE_STA&0x40)
                        //捕获到一个下降沿
                        {
                                TIM5CH2_CAPTURE_STA|=0x80;
                                //标记成功捕获到一次高电平脉宽
                                TIM5CH2_CAPTURE_VAL = TIM5->CCR1;
                                //读取当前的捕获值
                                TIM5->CCER &= ~(1<<5);
                               
                        }
                        else
                        {
                                TIM5CH2_CAPTURE_STA = 0;
                                //清空
                                TIM5CH2_CAPTURE_VAL = 0;
                                //
                                TIM5CH2_CAPTURE_STA |= 0x40;
                                //标记捕捉上升沿
                                TIM5->CNT = 0;
                                //计数器清零
                                TIM5->CCER |= 1<<5;
                                //CC1p = 1 设置为下降沿捕捉
                 }
                }
         }
        TIM5->SR = 0;
        //清除中断标志位
}


HAL库代码
main.c


#include "MyIncludes.h"
uint32_t uwIC2Value1 = 0;
//第一次捕捉值
uint32_t uwIC2Value2 = 0;
//第二次捕捉值
uint32_t uwDiffCapture = 0;
//两次捕捉差值
uint16_t uhCaptureIndex = 0;
//捕捉索引
uint32_t uwFrequency = 0;
//捕捉到的波形频率


uint8_t Cap_Over = 0;


void Cap_Process(void)
{
   if(TimCapHandle.Channel == HAL_TIM_ACTIVE_CHANNEL_2)
//如果有源通道  为2
// TIM5 的 CH2 输入捕捉通道
   {
     if(uhCaptureIndex == 0)
     //第一次捕捉
     {
       uwIC2Value1 = HAL_TIM_ReadCapturedValue(&TimCapHandle,TIM_CHANNEL_2);
       //第一次捕捉值
       uhCaptureIndex = 1;
     }
     else if(uhCaptureIndex == 1)
     //第二次捕捉
     {
      uwIC2Value2 = HAL_TIM_ReadCapturedValue(&TimCapHandle,TIM_CHANNEL_2);
      //第二次捕捉值
      if(uwIC2Value2 > uwIC2Value1)
      {
       uwDiffCapture = (uwIC2Value2 - uwIC2Value1);
      }
      else if(uwIC2Value2 < uwIC2Value1)
      {
        uwDiffCapture = ((0xffff - uwIC2Value1) + uwIC2Value2) + 1;
        //如果第二次小于第一次说明已经计满一次
      }
      uwFrequency = HAL_RCC_GetPCLK1Freq()/uwDiffCapture;
      uhCaptureIndex = 0;
      //捕捉到的波形频率=定时器PCK频率/2次捕捉的差值
      Cap_Over = 1;
      //捕捉完成
     }
   }
}


char buff[50];
int main(void)
{
u8 dir = 0;
u16 duty = 50;
u32 Set_Freq = 10000;
//设置频率
  
System_Init();
SysTick_Init(NULL);
USART1_Init(115200,NULL,NULL);
PWM_Init(Set_Freq,duty);
// 默认10kHz  
//PWM初始化(频率,占空比)
Tim5_CapInit(FREQ_COUNTER,Cap_Process);
//Tim5输入捕捉初始化(计数器频率,函数)

while(1)
{
   if(Cap_Over == 1)
   //显示捕获结果
   {
     Cap_Over = 0;
     sprintf(buff,"Cap Freq: %dHzzrn",uwFrequency);
     printf(buff);
     //打印信息
     if(dir == 0)
     //递增
     {
       if(Set_Freq < 20000)
       Set_Freq+=100;
       else dir = 1;
     }
     else if(dir == 1)
     {
       if(Set_Freq > 10000)
       Set_Freq -= 100;
       else dir = 0;
     }
     PWM_Init(Set_Freq,duty);
     delay_ms(500);
   }
}
}


InCap.h


#ifndef __INCAP_H_
#define __INCAP_H_


#include "stm32f1xx.h"
#include "stm32_types.h"
#include "stm32f1xx_hal.h"


#define FREQ_COUNTER 1000000
//定义计数频率,要和初始化中一致


typedef struct
{
  void (*T5_CapISR)(void);
  //定时器5中断中运行的函数
}_TIMERCAP_ISR;


extern TIM_HandleTypeDef TimCapHandle;


void Tim5_CapInit(u32 Cnt_Freq,void(*ISR)(void));
//TImER5输入捕捉初始化


#endif


InCap.c


#include "InCap.h"


TIM_HandleTypeDef TimCapHandle;
//TIM基本句柄结构变量声明
TIM_IC_InitTypeDef sICConfig;
//TIM输入捕获配置结构变量声明


_TIMERCAP_ISR TIM5_CAP_ISR;


void HAL_TIM_IC_MspInit(TIM_HandleTypeDef  *htim)
//HAL_TIM_IC_Init中调用
{
  GPIO_InitTypeDef GPIO_InitStruct;
  //GPIO基本配置结构
  __HAL_RCC_TIM5_CLK_ENABLE();
  //使能TIM5时钟
  __GPIOA_CLK_ENABLE();
  //TIM5_CH2管脚PA1
  //使能GPIOA时钟
  
  //配置输入捕捉管脚
  GPIO_InitStruct.Pin = GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_INPUT;
  //交替功能输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
  HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);
  //配置NVIC中断
  HAL_NVIC_SetPriority(TIM5_IRQn,0,1);
  //设置中断
  HAL_NVIC_EnableIRQ(TIM5_IRQn);
  //设置中断源
}


void Tim5_CapInit(u32 Cnt_Freq,void (*ISR)(void))
//TIMER5输入捕捉初始化
{
  TIM5_CAP_ISR.T5_CapISR = ISR;
   //回调函数
  TimCapHandle.Instance = TIM5;
  //设置定时器
  TimCapHandle.Init.Period = 0xffffffff;
  //自动重装值,这里设为最大
  TimCapHandle.Init.Prescaler = 0;
  //设置1us的计数频率
  TimCapHandle.Init.ClockDivision = 0;
  //时钟不分频,来自RCC
  TimCapHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
// 递增计数
  TimCapHandle.Init.RepetitionCounter = 0;
  //指定重复计数器值。
  HAL_TIM_IC_Init(&TimCapHandle);
   //定时器输入捕获初始化
  sICConfig.ICPolarity = TIM_ICPOLARITY_RISING;
  //上升沿捕获
  sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI;
  //管脚为捕获输入,映射到TI2
  sICConfig.ICPrescaler = TIM_ICPSC_DIV1;
  //输入捕获预分频,为 0 禁止预分频
  sICConfig.ICFilter =0x00;
//输入捕获滤波器,这是一个递减计数器,这里不滤波
HAL_TIM_IC_ConfigChannel(&TimCapHandle,&sICConfig,TIM_CHANNEL_2);
//定时器捕获配置通道

HAL_TIM_IC_Start_IT(&TimCapHandle,TIM_CHANNEL_2);
//启动输入捕捉
}


void TIM5_IRQHandler(void)
//TIMER5中断服务函数
{
  HAL_TIM_IRQHandler(&TimCapHandle);
}


void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
//用户回调函数,在HAL_TIM_IRQHandler中调用
{
  if(TIM5_CAP_ISR.T5_CapISR != NULL)
  TIM5_CAP_ISR.T5_CapISR();
}


实验现象

注意:要将P8 跳帽连接。


举报

更多回帖

发帖
×
20
完善资料,
赚取积分