输入捕获应用

1.1 输入捕获应用

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

1.1.1 测量脉宽或者频率

                            图 011 脉宽/频率测量示意图

1.    测量频率

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

2.    测量脉宽

当捕获通道TIx上出现上升沿时,发生第一次捕获,计数器CNT的值会被锁存到捕获寄存器CCR中,而且还会进入捕获中断,在中断服务程序中记录一次捕获(可以用一个标志变量来记录),并把捕获寄存器中的值读取到value1中。然后把捕获边沿改变为下降沿捕获,目的是捕获后面的下降沿。当下降沿到来的时候,发生第二次捕获,计数器CNT的值会再次被锁存到捕获寄存器CCR中,并再次进入捕获中断,在捕获中断中,把捕获寄存器的值读取到value3中,并清除捕获记录标志。然后把捕获边沿设置为上升沿捕获。

在测量脉宽过程中需要来回的切换捕获边沿的极性,如果测量的脉宽时间比较长,定时器就会发生溢出,溢出的时候会产生更新中断,我们可以在中断里面对溢出进行记录处理。

1.1.2 PWM输入模式

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

                                    图 012 输入通道和捕获通道的关系映射图

当使用PWM输入模式的时候,因为一个输入通道(TIx)会占用两个捕获通道(ICx),所以一个定时器在使用PWM输入的时候最多只能使用两个输入通道(TIx)。

我们以输入通道TI1工作在PWM输入模式为例来讲解下具体的工作原理,其他通道以此类推即可。

PWM信号由输入通道TI1进入,因为是PWM输入模式的缘故,信号会被分为两路,一路是TI1FP1,另外一路是TI2FP2。其中一路是周期,另一路是占空比,具体哪一路信号对应周期还是占空比,得从程序上设置哪一路信号作为触发输入,作为触发输入的哪一路信号对应的就是周期,另一路就是对应占空比。作为触发输入的那一路信号还需要设置极性,是上升沿还是下降沿捕获,一旦设置好触发输入的极性,另外一路硬件就会自动配置为相反的极性捕获,无需软件配置。一句话概括就是:选定输入通道,确定触发信号,然后设置触发信号的极性即可,因为是PWM输入的缘故,另一路信号则由硬件配置,无需软件配置。

当使用PWM输入模式的时候必须将从模式控制器配置为复位模式(配置寄存器SMCR的位SMS[2:0]来实现),即当我们启动触发信号开始进行捕获的时候,同时把计数器CNT复位清零。

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

                                     图 013 PWM输入模式时序

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

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

1.2 PWM输入捕获实验

实验中,我们用定时器的PWM输入模式来测量一个已知的PWM信号的频率和占空比,通过两者的对比即可知道测量是否准确。

1.2.1 硬件设计

实验中用到两个引脚,一个是PWM波形输出,另一个是定时器通道用于输入捕获,实验中直接使用一根杜邦线短接即可。

1.2.2 软件设计

这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。

1.    编程要点

(1)    定时器PWM输入配置

2.    软件分析 

 

 

//SIGNAL_PWM(GPIO)
#define SIGNAL_PWM_ENA_CLK_ENABLE()            __HAL_RCC_GPIOB_CLK_ENABLE() //PB0
#define SIGNAL_PWM_ENA_GPIO_Port               (GPIOB)
#define SIGNAL_PWM_ENA_Pin                     (GPIO_PIN_0)
#define SIGNAL_PWM_DIR_CLK_ENABLE()            __HAL_RCC_GPIOA_CLK_ENABLE() //PA7
#define SIGNAL_PWM_DIR_GPIO_Port               (GPIOA)
#define SIGNAL_PWM_DIR_Pin                     (GPIO_PIN_7)
//SIGNAL_PWM(AFIO & TIM)
#define SIGNAL_PWM_PUL_CLK_ENABLE()            __HAL_RCC_GPIOA_CLK_ENABLE() //PA6
#define SIGNAL_PWM_PUL_GPIO_Port               (GPIOA)
#define SIGNAL_PWM_PUL_Pin                     (GPIO_PIN_6)
#define SIGNAL_PWM_TIM_CLK_ENABLE()            __HAL_RCC_TIM3_CLK_ENABLE() //TIM3
#define SIGNAL_PWM_Get_TIM                     (TIM3)
#define SIGNAL_PWM_Get_HTIM                    (htim3)
#define SIGNAL_PWM_Get_IRQn                    (TIM3_IRQn) //TIM3中断
//GPIO输入
#define SIGNAL_PWM_READ_DIR_IO()               (SIGNAL_PWM_DIR_GPIO_Port -> IDR & SIGNAL_PWM_DIR_Pin)
#define SIGNAL_PWM_READ_ENA_IO()               (SIGNAL_PWM_ENA_GPIO_Port -> IDR & SIGNAL_PWM_ENA_Pin)

 

typedef struct{
    uint8_t count_rising; //PWM上升沿计数器镜像
    uint8_t count_falling; //PWM下降沿计数器镜像
    uint8_t count_update; //PWM更新计数器
    union
    {
        uint16_t DWORD;
        struct
        {
            uint16_t valid_top_width :1; //各种有效标志
            uint16_t valid_bottom_width :1; //各种有效标志
            uint16_t valid_top_location :1; //各种有效标志
            uint16_t valid_bottom_location :1; //各种有效标志
            uint16_t valid_top_speed :1; //各种有效标志
            uint16_t valid_bottom_speed :1; //各种有效标志
            uint16_t valid_top_current :1; //各种有效标志
            uint16_t valid_bottom_current :1; //各种有效标志

            uint16_t whole_h_flag :1; //PWM全高标志
            uint16_t whole_l_flag :1; //PWM全低标志
            uint16_t ready_first :1; //PWM就绪标志(完成一次PWM采集后置位)
            uint16_t ready_second :1; //PWM就绪标志(完成二次PWM采集后置位)
            uint16_t ready_third :1; //PWM就绪标志(完成三次PWM采集后置位)

            uint16_t Default :3;
        }Bits;
    }BoolData;
    //采集数据(PWM)
    uint16_t h_width; //PWM高宽度
    uint16_t period; //PWM周期
    uint16_t valid_width; //PWM有效宽度
    //配置
    uint16_t top_width; //Top_PWM
    uint16_t bottom_width; //Bottom_PWM
    int16_t top_current; //Top_Current
    int16_t bottom_current; //Bottom_Current
    int32_t top_location; //Top_Location
    int32_t bottom_location; //Bottom_Location
    int32_t top_speed; //Top_Speed
    int32_t bottom_speed; //Bottom_Speed
}Signal_PWM_Typedef;

//Signal_PWM接口
Signal_PWM_Typedef sg_pwm = {0};

/**
* @brief TIM_SIGNAL_PWM初始化
* @param NULL
* @retval NULL
**/
void REIN_TIM_SIGNAL_PWM_Init(void)
{
    /* GPIO初始化 */
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    /* GPIO Ports Clock Enable*/
    SIGNAL_PWM_PUL_CLK_ENABLE(); //启用SIGNAL_PWM_PUL端口时钟
    /*Configure GPIO pin*/
    GPIO_InitStruct.Pin = SIGNAL_PWM_PUL_Pin;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT; //输入模式
    GPIO_InitStruct.Pull = GPIO_PULLUP; //禁用上下拉
    HAL_GPIO_Init(SIGNAL_PWM_PUL_GPIO_Port, &GPIO_InitStruct);

    /* 定时器初始化 */
    TIM_SlaveConfigTypeDef sSlaveConfig = {0};
    TIM_MasterConfigTypeDef sMasterConfig = {0};
    TIM_IC_InitTypeDef sConfigIC = {0};
    SIGNAL_PWM_TIM_CLK_ENABLE(); //启用TIM时钟
    SIGNAL_PWM_Get_HTIM.Instance = SIGNAL_PWM_Get_TIM;
    SIGNAL_PWM_Get_HTIM.Init.Prescaler = 71; //采样频率 72M/(71+1) = 1M 周期1us
    SIGNAL_PWM_Get_HTIM.Init.CounterMode = TIM_COUNTERMODE_UP; //向上计数模式
    SIGNAL_PWM_Get_HTIM.Init.Period = 65535; //采样宽度 65536 * 1us = 65.536ms 频率15.23Hz (满足20Hz及以上频率PWM信号)
    SIGNAL_PWM_Get_HTIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; //不分频
    SIGNAL_PWM_Get_HTIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; //禁用自动重新加载
    if (HAL_TIM_Base_Init(&SIGNAL_PWM_Get_HTIM) != HAL_OK){
        Error_Handler();
    }
    if (HAL_TIM_IC_Init(&SIGNAL_PWM_Get_HTIM) != HAL_OK){
        Error_Handler();
    }
    sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET; //从模式:复位模式
    sSlaveConfig.InputTrigger = TIM_TS_TI1FP1; //从模式触发信号:TI1FP1
    sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING; //从模式触发极性:上升沿触发
    sSlaveConfig.TriggerFilter = 0; //禁用滤波器
    if (HAL_TIM_SlaveConfigSynchro(&SIGNAL_PWM_Get_HTIM, &sSlaveConfig) != HAL_OK){
        Error_Handler();
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; //主模式:复位
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; //禁用主机模式
    if (HAL_TIMEx_MasterConfigSynchronization(&SIGNAL_PWM_Get_HTIM, &sMasterConfig) != HAL_OK){
        Error_Handler();
    }
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING; //上升沿捕获
    sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; //TI1FP1
    sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; //不分频
    sConfigIC.ICFilter = 0;
    if (HAL_TIM_IC_ConfigChannel(&SIGNAL_PWM_Get_HTIM, &sConfigIC, TIM_CHANNEL_1) != HAL_OK){
        Error_Handler();
    }
    sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING; //下降沿捕获
    sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI; //TI1FP2
    if (HAL_TIM_IC_ConfigChannel(&SIGNAL_PWM_Get_HTIM, &sConfigIC, TIM_CHANNEL_2) != HAL_OK){
        Error_Handler();
    }

    /* interrupt init*/
    HAL_NVIC_EnableIRQ(SIGNAL_PWM_Get_IRQn); //使能定时中断

    //开始TIM工作
    HAL_TIM_Base_Stop(&SIGNAL_PWM_Get_HTIM); //停用TIM
    HAL_TIM_OC_Start_IT(&SIGNAL_PWM_Get_HTIM, TIM_CHANNEL_1); //启动CH1捕获比较中断
    HAL_TIM_OC_Start_IT(&SIGNAL_PWM_Get_HTIM, TIM_CHANNEL_2); //启动CH2捕获比较中断
    HAL_TIM_Base_Start_IT(&SIGNAL_PWM_Get_HTIM); //启动TIM中断
}

/**
* @brief Signal_PWM采集中断回调
* @param NULL
* @retval NULL
**/
void Signal_PWM_TIM_Callback(void)
{//Signal_PWM采集中断回调
    if(__HAL_TIM_GET_FLAG(&SIGNAL_PWM_Get_HTIM, TIM_FLAG_CC1) != RESET)
    {//定时器捕获中断通道1 (上升沿中断) (必须在更新中断前执行)
        if (__HAL_TIM_GET_IT_SOURCE(&SIGNAL_PWM_Get_HTIM, TIM_IT_CC1) != RESET)
        {
            __HAL_TIM_CLEAR_IT(&SIGNAL_PWM_Get_HTIM, TIM_IT_CC1);
            SIGNAL_PWM_Get_HTIM.Channel = HAL_TIM_ACTIVE_CHANNEL_1;

            //采集上升沿数据
            sg_pwm.period = HAL_TIM_ReadCapturedValue(&SIGNAL_PWM_Get_HTIM, TIM_CHANNEL_1) + 1; //获取PWM周期
            sg_pwm.count_rising = sg_pwm.count_update;
        }
    }
    if(__HAL_TIM_GET_FLAG(&SIGNAL_PWM_Get_HTIM, TIM_FLAG_CC2) != RESET)
    {//定时器捕获中断通道2 (下降沿中断)
        if (__HAL_TIM_GET_IT_SOURCE(&SIGNAL_PWM_Get_HTIM, TIM_IT_CC2) != RESET)
        {
            __HAL_TIM_CLEAR_IT(&SIGNAL_PWM_Get_HTIM, TIM_IT_CC2);
            SIGNAL_PWM_Get_HTIM.Channel = HAL_TIM_ACTIVE_CHANNEL_2;

            //采集下降沿数据
            sg_pwm.h_width = HAL_TIM_ReadCapturedValue(&SIGNAL_PWM_Get_HTIM, TIM_CHANNEL_2) + 1; //获取PWM高宽度
            sg_pwm.count_falling = sg_pwm.count_update;
        }
    }
    if(__HAL_TIM_GET_FLAG(&SIGNAL_PWM_Get_HTIM, TIM_FLAG_UPDATE) != RESET)
    {//定时器更新中断 (更新事件中断) (由更新事件触发 | 溢出事件触发)
        if (__HAL_TIM_GET_IT_SOURCE(&SIGNAL_PWM_Get_HTIM, TIM_IT_UPDATE) != RESET)
        {
            __HAL_TIM_CLEAR_IT(&SIGNAL_PWM_Get_HTIM, TIM_IT_UPDATE);

            if( (sg_pwm.count_update != sg_pwm.count_rising) //(上升沿计数器镜像,更新计数器不相等)
             && (sg_pwm.count_update != sg_pwm.count_falling) ) //(下降沿计数器镜像,更新计数器不相等)
            {//单次PWM全高全低检测
                if(SIGNAL_PWM_PUL_GPIO_Port -> IDR & SIGNAL_PWM_PUL_Pin)
                {//读取PWM电平用于判定全高或全低
                    sg_pwm.BoolData.Bits.whole_h_flag = true; //置位100%标志
                    sg_pwm.BoolData.Bits.whole_l_flag = false; //清位0%标志
                }
                else
                {
                    sg_pwm.BoolData.Bits.whole_h_flag = false; //清位100%标志
                    sg_pwm.BoolData.Bits.whole_l_flag = true; //置位0%标志
                }
            }
            else
            {
                sg_pwm.count_update ++;
                sg_pwm.BoolData.Bits.whole_h_flag = false; //清位100%标志
                sg_pwm.BoolData.Bits.whole_l_flag = false; //清位0%标志
            }
            //提取本周期PWM
            if(sg_pwm.BoolData.Bits.whole_h_flag)
                sg_pwm.h_width = 65535;
            if(sg_pwm.BoolData.Bits.whole_l_flag)
                sg_pwm.h_width = 0;
            //单次PWM有效性确认
            if(0){}
            else if((sg_pwm.BoolData.Bits.whole_h_flag))
                sg_pwm.BoolData.Bits.ready_first = false; //100%_PWM无效
            else if((sg_pwm.BoolData.Bits.whole_l_flag))
                sg_pwm.BoolData.Bits.ready_first = false; //0%_PWM无效
            else if((sg_pwm.top_width < (65535 - 100))
                && ((sg_pwm.top_width + 100) < sg_pwm.h_width))
                sg_pwm.BoolData.Bits.ready_first = false; //脉宽超长
            else if((sg_pwm.bottom_width > (0 + 100))
                && ((sg_pwm.bottom_width - 100) > sg_pwm.h_width))
                sg_pwm.BoolData.Bits.ready_first = false; //脉宽超短
            else
                sg_pwm.BoolData.Bits.ready_first = true;

            if(sg_pwm.BoolData.Bits.ready_first)
            {//可靠的PWM有效性确认
                if(sg_pwm.BoolData.Bits.ready_second)
                {
                    sg_pwm.BoolData.Bits.ready_third = true;
                }
                else
                {
                    sg_pwm.BoolData.Bits.ready_second = true;
                    sg_pwm.BoolData.Bits.ready_third = false;
                }
            }
            else
            {
                sg_pwm.BoolData.Bits.ready_second = false;
                sg_pwm.BoolData.Bits.ready_third = false;
            }

            if(sg_pwm.BoolData.Bits.ready_third)
            {//第三次采样成功时开始获取数据
                /************************ 提取有效PWM **********************************/
                if(sg_pwm.h_width > sg_pwm.top_width)//大于最大取最大
                sg_pwm.valid_width = sg_pwm.top_width;
                else if(sg_pwm.h_width < sg_pwm.bottom_width)//小于最小取最小
                sg_pwm.valid_width = sg_pwm.bottom_width;
                else//不大不小就合适
                sg_pwm.valid_width = sg_pwm.h_width;
                /************************ 提取有效目标 *********************************/
                if(signal_moreio.mode == MoreIO_Mode_PWM_Location)
                {//提取目标位置
                    //(当前脉宽-设置最小脉宽)/(设置最大脉宽-设置最小脉宽)*(设置最大位置-设置最小位置)
                    signal_moreio.goal_location = (int32_t) ( (float)( (int32_t)sg_pwm.valid_width - (int32_t)sg_pwm.bottom_width )
                                                            * (float)( (int32_t)sg_pwm.top_location - (int32_t)sg_pwm.bottom_location )
                                                            / (float)( (int32_t)sg_pwm.top_width - (int32_t)sg_pwm.bottom_width ) )
                                                            + (int32_t) (sg_pwm.bottom_location);
                }
                else if(signal_moreio.mode == MoreIO_Mode_PWM_Speed)
                {//提取目标速度
                    signal_moreio.goal_speed = (int32_t) ( (float)( (int32_t)sg_pwm.valid_width - (int32_t)sg_pwm.bottom_width )
                                                         * (float)( (int32_t)sg_pwm.top_speed - (int32_t)sg_pwm.bottom_speed )
                                                         / (float)( (int32_t)sg_pwm.top_width - (int32_t)sg_pwm.bottom_width ) )
                                                         + (int32_t) (sg_pwm.bottom_speed);
                    //DIR引脚低电平--->目标速度取负值
                    if(!SIGNAL_PWM_READ_DIR_IO())
                    {
                        signal_moreio.goal_speed = -signal_moreio.goal_speed;
                    }
                }
                else if(signal_moreio.mode == MoreIO_Mode_PWM_Current)
                {//提取目标电流
                    signal_moreio.goal_current = (int32_t) ( (float)( (int32_t)sg_pwm.valid_width - (int32_t)sg_pwm.bottom_width )
                                                           * (float)( (int32_t)sg_pwm.top_current - (int32_t)sg_pwm.bottom_current )
                                                           / (float)( (int32_t)sg_pwm.top_width - (int32_t)sg_pwm.bottom_width ) )
                                                           + (int32_t) (sg_pwm.bottom_current);
                    if(!SIGNAL_PWM_READ_DIR_IO())
                    {//DIR引脚低电平--->目标电流取负值
                        signal_moreio.goal_current = -signal_moreio.goal_current;
                    }
                }
                //提取特殊目标
                signal_moreio.goal_disable = false;
                signal_moreio.goal_brake = false;
            }
            else
            {//提取特殊目标
                signal_moreio.goal_disable = true;
                signal_moreio.goal_brake = false;
            }

        }
    }
}

/**
* @brief TIM3_IRQHandler
**/
void TIM3_IRQHandler(void)
{

    Signal_PWM_TIM_Callback(); //SIGNAL_PWM采集中断回调

}

 
posted @ 2022-02-18 10:04  我是,一株蒜。  阅读(261)  评论(0编辑  收藏  举报