【STM32】HAL库实现定时器多通道输入捕获频率

原理说明

捕获信号的频率其实有很多中实现方式,外部中断、输入捕获、使用外部时钟计数器等。对STM32有一定了解的朋友们在测量频率的问题上往往都会想到利用输入捕获,输入捕获的方式在中低频率段(1HZ-200KHZ)的测量还是比较准确的。在高频段还是建议采用外部时钟计数器的方式来实现。

采用输入捕获实现频率测量的实现步骤如下:

  1. 设置输入捕获为上升沿检测
  2. 记录第一次t1发生上升沿时捕获比较寄存器(CCR)的值cap_val1
  3. 记录第二次t2发生上升沿时捕获比较寄存器(CCR)的值cap_val2
  4. 根据不同溢出情况计算前后两次捕获值之和,同时根据TIM的计数频率,我们就可以算出捕获脉冲的频率。
    定时器输入捕获图示


代码实现

代码实现的部分主要是在输入捕获中断中对捕获的不同溢出情况进行了处理,实测效果还是不错的,4通道同时测的误差也就在1%左右。

宏定义&变量定义部分:


/* IC capture edge define*/
#define        IC_RISE_EDGE_1                       0
#define        IC_RISE_EDGE_2                       1

/* Capture TIMER Prescaler and Period define */
#define     TIM1_PRESCALER                  168-1
#define     TIM1_PERIOD                     0xFFFF

/* IC capture Parameters define*/
#define        TIM1_IC_FREQ                         168000000/TIM1_PRESCALER
#define        TIM_ICOF_MAX                         0x32

static      float      g_TIM1IcFreq =  TIM1_IC_FREQ;
static      float      g_TIM1Period =  TIM1_PERIOD;

static      float      g_FbFreqBuff[SPD_FB_CHAN_NUM] = {0.0};               /* 频率反馈值*/
static      float      g_DevFbRotateSpdBuff[SPD_FB_CHAN_NUM] = {0.0};       /* 转速反馈值*/
            uint16_t   g_IcofCntBuff[TIM_IC_MAX] = {0};                     /* 定时器溢出计数值*/

/* 定时器捕获通道枚举定义*/
typedef enum {                     
    TIM1_IC1 = 0,       
    TIM1_IC2 ,          
    TIM1_IC3 ,          
    TIM1_IC4 ,            
    TIM_IC_MAX ,
} TIM_IC_Typedef;

定时器初始化部分:

void MX_TIM1_Init(void)
{
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_IC_InitTypeDef sConfigIC = {0};
    
    TIMER1_Handler.Instance = TIM1;
    TIMER1_Handler.Init.Prescaler = TIM1_PRESCALER;           //168M/168 = 1M计数频率,1us计数一次;
    TIMER1_Handler.Init.CounterMode = TIM_COUNTERMODE_UP;
    TIMER1_Handler.Init.Period = TIM1_PERIOD;                 //溢出时间,65.5ms
    TIMER1_Handler.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    TIMER1_Handler.Init.RepetitionCounter = 0;
    TIMER1_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_IC_Init(&TIMER1_Handler) != HAL_OK) {
        Error_Handler();
    }
    
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&TIMER1_Handler, &sMasterConfig) != HAL_OK) {
        Error_Handler();
    }
    
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
    sConfigIC.ICFilter = 0;

    if (HAL_TIM_IC_ConfigChannel(&TIMER1_Handler, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) {
        Error_Handler();
    }
    if (HAL_TIM_IC_ConfigChannel(&TIMER1_Handler, &sConfigIC, TIM_CHANNEL_2) != HAL_OK) {
        Error_Handler();
    }
    if (HAL_TIM_IC_ConfigChannel(&TIMER1_Handler, &sConfigIC, TIM_CHANNEL_3) != HAL_OK) {
        Error_Handler();
    }
    if (HAL_TIM_IC_ConfigChannel(&TIMER1_Handler, &sConfigIC, TIM_CHANNEL_4) != HAL_OK) {
        Error_Handler();
    }

    /*## Start the Input Capture in interrupt mode ##########################*/
    HAL_TIM_IC_Start_IT(&TIMER1_Handler,TIM_CHANNEL_1); 
    HAL_TIM_IC_Start_IT(&TIMER1_Handler,TIM_CHANNEL_2);
    HAL_TIM_IC_Start_IT(&TIMER1_Handler,TIM_CHANNEL_3);
    HAL_TIM_IC_Start_IT(&TIMER1_Handler,TIM_CHANNEL_4);

    __HAL_TIM_ENABLE_IT(&TIMER1_Handler,TIM_IT_UPDATE);//更新中断用于溢出计数
}

输入捕获回调函数:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM1) {
        if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1) {
            TIM1CaptureChannel1Callback();
        } else if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_2) {           
            TIM1CaptureChannel2Callback();
        } else if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_3) {
            TIM1CaptureChannel3Callback();
        } else {
            TIM1CaptureChannel4Callback();
        }
    }
}

通道1的捕获处理代码:

/**************************************************************************************************
 * @brief  :Set PMP01 Control Speed Percent,voltage Control.
 * @param  :none.
 * @retval :none.
 * @note   :0xFFFF is the Timer1 Period value set in the MX_TIM1_Init().
 *          Input capture frequency range:1HZ~100KHZ(best)
 **/
 
void TIM1CaptureChannel1Callback(void)
{
    static uint8_t ic_edge = IC_RISE_EDGE_1;
    static uint32_t cap_val1 = 0;
    static uint32_t cap_val2 = 0;
    static uint32_t cap_sum = 0;
    
    if(ic_edge == IC_RISE_EDGE_1){
        
        ResetTimerIcofCnt(TIM1_IC1);
        cap_val1=__HAL_TIM_GET_COMPARE(&TIMER1_Handler, TIM_CHANNEL_1);
	    ic_edge = IC_RISE_EDGE_2;        
   }else{     
       
	   cap_val2=__HAL_TIM_GET_COMPARE(&TIMER1_Handler, TIM_CHANNEL_1);
       if(g_IcofCntBuff[TIM1_IC1] == 0){
           cap_sum = cap_val2 - cap_val1;   
       }else if(g_IcofCntBuff[TIM1_IC1] == 1){//溢出一个计数周期
           cap_sum = (g_TIM1Period - cap_val1) + cap_val2;
       }else{//溢出N个计数周期
           cap_sum = (g_TIM1Period - cap_val1) + g_TIM1Period *(g_IcofCntBuff[TIM1_IC1]-1) +cap_val2;
       }       
        ProcessIcValue(TIM1_IC1,cap_sum);
        ic_edge = IC_RISE_EDGE_1;      
   }
}

其它三个通道都大同小异,就不全贴出来了...

定时器溢出计数函数:


void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if(htim->Instance == TIM1) {
        TIM1_IcOverflowCntCallback();
    }
}


/**************************************************************************************************
 * @brief  :TIM1 Input capture overflow count callback function.
 * @param  :none.
 * @retval :none.
 * @note   :0xFF 为避免出现捕获到周期过长的异常输出(只捕获到一个上升沿、输入波形中断等)
 **/  

void TIM1_IcOverflowCntCallback(void)
{
    for(uint8_t i=0;i<4;i++){
        
        if(g_IcofCntBuff[i] < TIM_ICOF_MAX){
            g_IcofCntBuff[i]++;
        }else{
            g_IcofCntBuff[i]=0;
            g_FbFreqBuff[i] = 0;
        }
    }
}

频率&转速计算:


/**************************************************************************************************
 * @brief  :Process the Input capture value function.
 * @param  :none.
 * @retval :none.
 * @note   :
 **/ 

static void ProcessIcValue(TIM_IC_Typedef ic_id,uint32_t _icCnt)
{   
        g_FbFreqBuff[ic_id] = CalcTheFreq(_icCnt);
        g_DevFbRotateSpdBuff[ic_id] = CalcTheRpm(g_FbFreqBuff[ic_id]);  
}

/**************************************************************************************************
 * @brief  :Calc the frequency by the capture count.
 * @param  :none.
 * @retval :none.
 * @note   :频率计算公式:f=1/T,定时器的输入捕获的计数的频率为1MHZ,两次捕获上升沿的差值sum为计数器
            CNT计的次数,所以总的周期即为T=1us*sum,所以频率就fq=1000000/sum HZ.
            电机转速与频率的公式:n=60f/p,
            n--电机的转速(转/分);
            60--每分钟(秒);
            f--电源频率(赫兹);
            p--电机旋转磁场的极对数(电机也分为分2极,4级,6极,8极等);这里按2极来算
 **/

static float CalcTheFreq(uint32_t _icValue)
{    
    return g_TIM1IcFreq / _icValue;
}

static float CalcTheRpm(float _freq)
{    
//    return (60 * _freq / 2); 
    return (30 * _freq);    //由上式简化而来
}

最后再提一下在开篇提到的采用外部时钟计数器方式实现频率信号测量的实现思路,此方式测量高频段信号频率准确率不错,也会大大减少中断次数。

采用外部时钟计数器的方式实现思路:
思路是配置两个定时器,定时器a设置为外部时钟计数器模式,定时器b设置为定时器(比如50ms溢出一次,也可以用软件定时器),然后定时器b中断函数中统计定时器a在这段时间内的增量,简单计算即可。

最后放一个通道测试的gif:

在这里插入图片描述

完结撒花~~~

  • 15
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
### 回答1: Hal库是一个用于嵌入式系统的硬件访问层库。该库提供了一些函数和接口,用于与硬件设备进行交互。在使用Hal库进行输入捕获测量占空比时,可以按照以下步骤进行操作。 首先,需要选择一个支持输入捕获功能的硬件定时器。然后,需要使用Hal库的相关函数来配置定时器的工作模式输入捕获功能。 接下来,设置输入捕获的触发源。可以选择外部输入引脚或其他事件作为触发源,当触发事件发生时,定时器会记录下当前的计数值。 然后,启动定时器,开始计数。当触发事件发生时,Hal库会自动记录下当前的计数值,并触发一个输入捕获事件。 在输入捕获事件中,可以通过Hal库提供的接口,获取到输入捕获值。输入捕获值表示两个连续触发事件之间的定时器计数差值。 通过计算输入捕获值与定时器周期的比值,可以得到占空比。占空比表示高电平信号在一个周期内的持续时间占整个周期的比例。 最后,可以根据需求进行进一步的处理,比如计算平均占空比或根据占空比值做一些控制操作。 总之,使用Hal库进行输入捕获测量占空比的步骤包括选择定时器、配置工作模式输入捕获功能、设置触发源、启动定时器、获取输入捕获值并计算占空比。这样就可以实现对占空比的测量和处理。 ### 回答2: Hal库是一个用于嵌入式系统的硬件抽象层。它提供了一组API,用于管理和访问硬件资源。在使用Hal库时,我们可以使用输入捕获功能来测量信号的占空比。 输入捕获是一种用于测量和记录信号占空比的技术。在嵌入式系统中,我们通常会遇到需要测量某个信号的占空比的情况,例如PWM信号或时钟信号等。通过使用Hal库提供的输入捕获功能,我们可以方便地进行这些测量。 使用Hal库进行输入捕获测量占空比的步骤如下: 1. 初始化输入捕获功能:首先,我们需要初始化Hal库输入捕获功能,以准备开始测量。通常,我们需要设置输入捕获的参数,如计数模式、采样频率等。 2. 开始捕获信号:一旦输入捕获功能初始化完成,我们就可以开始捕获需要测量的信号了。通过调用Hal库提供的相应API函数,我们可以开始记录信号的时间信息。 3. 停止捕获信号:在信号捕获到一定的时间间隔后,我们可以停止捕获。通过调用Hal库提供的停止捕获的函数,我们在此时停止记录时间信息。 4. 计算占空比:一旦停止捕获信号,我们可以使用记录的时间信息计算信号的占空比。通常,我们可以通过测量信号高电平和低电平的持续时间来计算占空比。 总的来说,Hal库输入捕获功能提供了一种方便的方式来测量信号的占空比。通过使用Hal库,我们可以轻松地进行这些测量,并且可以根据需要调整捕获的参数。这使得我们能够更好地理解和控制我们的嵌入式系统中的信号。 ### 回答3: HAL库是一种有助于开发嵌入式系统的开源库。它提供了丰富的功能和API,能够简化硬件驱动和应用程序开发的过程。 在HAL库中,输入捕获是一种测量占空比的功能。占空比是指周期性信号中高电平所占的比例。输入捕获能够准确地测量信号的上升沿和下降沿的时间间隔,并计算出占空比。 在使用HAL库进行输入捕获测量时,首先需要通过初始化GPIO引脚和定时器等外设来配置输入捕获功能。然后,可以使用HAL库提供的API函数来启动输入捕获测量并获取测量结果。 例如,可以使用hal_input_capture_start()函数来启动输入捕获测量,并通过hal_input_capture_get_duty_cycle()函数获取测量结果。这个函数返回一个浮点数,表示输入信号的占空比。 使用HAL库进行输入捕获测量还可以通过设置回调函数来实时获取测量结果。可以使用hal_input_capture_register_callback()函数注册回调函数,在每次捕获到输入信号边沿时,HAL库会调用这个回调函数,并把测量结果作为参数传递给回调函数。 总之,HAL库提供了输入捕获功能,能够帮助开发人员方便地测量信号的占空比。通过合理配置和调用HAL库的API函数,可以实现准确的输入捕获测量,并获取测量结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

0.零点开发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值