目录
参考链接
https://blog.csdn.net/m0_37845735/article/details/105395643
一、实现过程
定时器在输入捕获模式下,当检测到ICx信号上相应的边沿后,计数器(CNT)的当前值被锁存到捕获/比较寄存器(TIMx_CCRx)中。当捕获事件发生时,相应的CCxIF标志(TIMx_SR寄存器)被置’1’,如果使能了中断或者DMA操作,则将产生中断或者DMA操作。如果捕获事件发生时CCxIF标志已经为高,那么重复捕获标志CCxOF(TIMx_SR寄存器)被置’1’。写CCxIF=0可清除CCxIF,或读取存储在TIMx_CCRx寄存器中的捕获数据也可清除CCxIF。
当捕获事件发生时,可通过中断的方式将当前捕获/比较寄存器(TIMx_CCRx)中的计数值读取出来假设为 n1,然后更改输入捕获的信号级性(上升沿或下降沿),当再次检测到ICx信号上相应的边沿后,计数器(CNT)的当前值再次被锁存到捕获/比较寄存器(TIMx_CCRx)中假设为 n2;n2 -n1节可算出电平的持续时间
在输入捕获配置中,除了要配置 选择要使用的通用定时器(TIM2~TIM5),选择计时器的时钟源为内部时钟(CK_INT)、根据要定时的时间计算预分频系数(TIMx_PSC)、自动重装载值(TIMx_ARR)、内部时钟的分频系数以外,更重要的还需要配置 捕获/比较模式寄存器1/2(TIMx_CCMR1/2)、捕获/比较使能寄存器(TIMx_CCER)、DMA/中断使能寄存器(TIMx_DIER)
- 捕获/比较模式寄存器1/2(TIMx_CCMR1/2):有2个寄存器,一个寄存器只能设置2个通道;该寄存器用于设置通道的输入(捕获模式)或输出(比较模式)参数,就输入捕获而言,主要设置滤波器、预分频器、输入映射关系
- 捕获/比较使能寄存器(TIMx_CCER):配置捕获触发的信号级性、捕获使能,每个定时器有4个通道,这里的CC1P/CC1E表示的是第一个通道
DMA/中断使能寄存器(TIMx_DIER):使能计数更新中断或是使能捕获/比较中断
二、STM32CubeMX配置示例
在配置时是需要开启输入捕获中断和计数更新中断的,但是在CubeMX配置中我并没有找到相应的中断配置,而是在配置完成后在工程中手动添加的!!!!!!!在 “Input Capture Channel” 配置项中,
- Polarity Selection :设置的是TIMx_CCER->CCxP,配置捕获出发的信号极性(上升沿或下降沿)
- IC Selection:设置的是TIMx_CCMR->CCxS,配置通道的方向(输入或输出),及输入脚的选择;Direct表示CCx通道配置为输入,ICx映射到TI1上
- Prescaler Division Ratio:设置的是TIMx_CCMR->ICxPSC,配置输入捕获的预分频系数
- Input Filter:设置的是TIMx_CCMR->ICxF,配置输入捕获的滤波器
三、C语言示例程序
我自己做过超声波测距,就是用CubeMX配置生成的工程,这里做个示例。测距原理也很简单,就是给模块的IO触发引脚发送一个至少10us的高电平触发信号后,模块自动发送8个40KHZ的方波,并自动检测信号返回,当有信号返回时,通过IO向外输出一个高电平,高电平持续时间就是超声波从发射到返回的时间,通过定时器捕获超声波模块这个高电平的持续时间,然后乘上340 / 2就可以测出超声波到障碍物之间的距离了。
/* TIM1_CH1输入捕获 init function */
void MX_TIM1_Init(void)
{
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
/* Peripheral clock enable */
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOA);
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_GPIOB);
/**TIM1 GPIO Configuration
PA8 ------> TIM1_CH1 超声波输入信号捕获
PB13 ------> 超声波触发信号
*/
GPIO_InitStruct.Pin = LL_GPIO_PIN_8;
GPIO_InitStruct.Mode = LL_GPIO_MODE_FLOATING;
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = LL_GPIO_PIN_13;
GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_13);
TIM_InitStruct.Prescaler = 71;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0xFFFF;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0; //重复计数器
LL_TIM_Init(TIM1, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIM1); //使能自动重装载
LL_TIM_SetClockSource (TIM1, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_SetTriggerOutput (TIM1, LL_TIM_TRGO_UPDATE);//主从模式下,主模式定时器送到从模式定时器的触发信号
LL_TIM_DisableMasterSlaveMode(TIM1);//不使能主从模式
/* TIM1 interrupt Init */
NVIC_SetPriority(TIM1_UP_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),8, 0));//使能向上计数溢出中断
NVIC_EnableIRQ (TIM1_UP_IRQn);
NVIC_SetPriority(TIM1_CC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),9, 0));//使能捕获计较中断
NVIC_EnableIRQ (TIM1_CC_IRQn);
/*捕获/比较模式寄存器1(TIMx_CCMR1)*/
LL_TIM_IC_SetActiveInput(TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);//映射到TI1上,TIMx_CCER->CCxS
LL_TIM_IC_SetPrescaler (TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1); //设置输入分频,不分频,TIMx_CCER->ICxPSC
LL_TIM_IC_SetFilter (TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);//设置输入滤波器,不滤波 TIMx_CCER->ICxF
/*捕获/比较使能寄存器(TIMx_CCER)*/
LL_TIM_IC_SetPolarity (TIM1, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);//设置输入捕获极性,上升沿捕获 TIMx_CCER->CCxP
LL_TIM_CC_EnableChannel (LL_TIM_CHANNEL_CH1);//设置允许捕获计数器的值到捕获寄存器中 TIMx_CCER->CCxE
// TIM1->CCER|=0<<1; //CC1P=0 上升沿捕获
// TIM1->CCER|=1<<0; //CC1E=1 允许捕获计数器的值到捕获寄存器中
/*DMA/中断使能寄存器(TIMx_DIER)*/
LL_TIM_EnableIT_UPDATE(TIM1);//允许更新中断 TIMx_DIER->UIE
LL_TIM_EnableIT_CC1(TIM1); //允许捕获中断 TIMx_DIER->CCxIE
// TIM1->DIER|=1<<1; //允许捕获中断
// TIM1->DIER|=1<<0; //允许更新中断
LL_TIM_SetCounter(TIM1,0);//计数器清空
TIM1->CNT=0; //计数器清空
TIM1->SR=0; //清除中断标志位
}
void EnableTIMCounter()
{
LL_TIM_EnableCounter (TIM1);
}
unsigned char TIM1CH1_CAPTURE_STA = 0; //输入捕获状态
unsigned short TIM1CH1_CAPTURE_VAL = 0; //输入捕获值
/**
* @brief This function handles TIM1 update interrupt.
*/
void TIM1_UP_IRQHandler(void)
{
if (LL_TIM_IsActiveFlag_UPDATE(TIM1))
{
if(TIM1CH1_CAPTURE_STA & 0X40)//已经捕获到高电平了,高电平时间太长
{
TIM1CH1_CAPTURE_STA = 0;//标记成功捕获了一次
TIM1CH1_CAPTURE_VAL = 0XFFFF;
LL_TIM_DisableCounter(TIM1);//成功捕获到了一次高电平,关闭定时器
LL_TIM_SetCounter(TIM1,0); //计数器清空
/* 把测量数据存入全局变量中 */
g_tSystemData.UTRang = (float)TIM1CH1_CAPTURE_VAL / 1000.0 * 34.0 / 2.0;//单位为cm
/* 把测量数据存入保持寄存器中 */
Write_Holding_Reg((void*)&g_tSystemData.UTRang, eholdreg_utrang1);
}
LL_TIM_ClearFlag_UPDATE(TIM1);
}
}
/**
* @brief This function handles TIM1 capture compare interrupt.
*/
void TIM1_CC_IRQHandler(void)
{
if(LL_TIM_IsActiveFlag_CC1(TIM1))
{
if(TIM1CH1_CAPTURE_STA & 0X40) //捕获到一个下降沿
{
TIM1CH1_CAPTURE_STA = 0; //标记成功捕获到一次下降沿
TIM1CH1_CAPTURE_VAL = LL_TIM_IC_GetCaptureCH1(TIM1);
LL_TIM_IC_SetPolarity(TIM1,LL_TIM_CHANNEL_CH1,LL_TIM_IC_POLARITY_RISING); //CC1P=0 设置为上升沿捕获
LL_TIM_DisableCounter(TIM1);//成功捕获到了一次高电平,关闭定时器
/* 把测量数据存入全局变量中 */
g_tSystemData.UTRang = (float)TIM1CH1_CAPTURE_VAL / 1000.0 * 34.0 / 2.0;//单位为cm
/* 把测量数据存入保持寄存器中 */
Write_Holding_Reg((void*)&g_tSystemData.UTRang, eholdreg_utrang1);
// printf("HighTime = %d us ,UGage = %f cm\r\n",TIM1CH1_CAPTURE_VAL,g_tSystemData.UTRang);
}
else //还未开始,第一次捕获上升沿
{
TIM1CH1_CAPTURE_STA = 0; //清空
TIM1CH1_CAPTURE_VAL = 0;
LL_TIM_SetCounter(TIM1,0);//计数器清空
TIM1CH1_CAPTURE_STA |= 0X40; //标记捕获到了上升沿
LL_TIM_IC_SetPolarity(TIM1,LL_TIM_CHANNEL_CH1,LL_TIM_IC_POLARITY_FALLING); //CC1P=1 设置为下降沿捕获
}
LL_TIM_ClearFlag_CC1(TIM1);
}
}
/**********************************************************************************************************
* 函 数 名: UGageTrig
* 功能说明: 超声波测距触发信号。1、使能定时器输入捕获 2、发送高电平触发脉冲
* 形 参: _cFlg
* 返 回 值: NULL
**********************************************************************************************************/
void UGageTrig(void)
{
EnableTIMCounter();
LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_13);
Delay_us(20);
LL_GPIO_ResetOutputPin(GPIOB, LL_GPIO_PIN_13);
}