输入捕获应用
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采集中断回调
}