第四节:STM32定时器(3.输入捕获:HC 您所在的位置:网站首页 超声波测距精度最高多少 第四节:STM32定时器(3.输入捕获:HC

第四节:STM32定时器(3.输入捕获:HC

2024-07-12 03:40| 来源: 网络整理| 查看: 265

关联:STM32超全笔记【秋招自用】【含PID小车、飞控等项目】 【引言】

首先,同样是在理论前,我们需要对定时器输入捕获和他的应用做一个简单的了解。

输入捕获,在我们前面定时器学习的基础上,就是由我们设定psc和arr两个数,检测定时器通道输入的信号。

这个信号可以是传感器发出的,比如这节的例程就是使用超声波模块测量距离。

【问】超声波模块测量距离的原理是什么?

超声波模块向前发送超声波,遇到障碍然后返回,这样一次来回的时间,就是超声波模块引脚高电平持续的时间。

所以:我们只需要通过定时器的输入捕获,捕获这个引脚的高电平并且计算出持续时间,就可以计算出距离。

公式:距离 = 高电平时间 * 声速 / 2

声速:0.0343 厘米/微秒

其他不同的传感器也是相似的原理,都用到了输入捕获,所以学好这一节是至关重要的。

【输入捕获】

 接下来就是比较枯燥的理论知识了。

来看看在通用定时器框图中的输入捕获原理吧:

就是左下角的部分:

接下来就是代码了:

【例程7】输入捕获:超声波测距实验

超声波模块的原理已经在前面讲过了,我们要做的,就是配置定时器的输入捕获模式。

在中断处理函数中,计算出高电平的持续时间。

这里有一个要注意的点:在捕获过程中,定时器可能会发生溢出,而溢出会导致计数器的值从 0 开始重新计数。因此,捕获的持续时间不仅包括了捕获值之后的计数时间,还要加上捕获值之前溢出的计数时间。

这也会导致编程的难度加倍,这段代码的逻辑可能比较复杂,实现思想和串口那块相似。

【输入捕获结构体初始化】

首先硬件连接: 传感器的Echo引脚 ----- PA1 ---- TIM2_CH2

void TIM2_Cap_Init(u16 arr,u16 psc) { GPIO_InitTypeDef GPIO_InitStructure; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_ICInitTypeDef TIM2_ICInitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 清除之前设置 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 输入 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_ResetBits(GPIOA,GPIO_Pin_1); //PA1 下拉 //初始化定时器2 TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 //初始化TIM5输入捕获参数 TIM2_ICInitStructure.TIM_Channel = TIM_Channel_2; // 选择输入端 IC1映射到TI2上 TIM2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获 TIM2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI2上 TIM2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM2_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波 TIM_ICInit(TIM2, &TIM2_ICInitStructure); //中断分组初始化 NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_ITConfig(TIM2,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允许更新中断 ,允许CC2IE捕获中断 TIM_Cmd(TIM2,ENABLE ); //使能定时器2 }

【问】为什么echo引脚(PA2)设置上拉输入?

超声波模块没有连接到外部设备时,Echo引脚会保持在逻辑高电平状态,这有助于避免电磁干扰或其他因素导致的误操作。

TIM_TimeBaseStructure输入捕获结构体:

1.因为用到TIM2_CH2,所以设置为TIM_Channel_22.上升沿捕获3.CH2对应TI24.不分频、不滤波 【中断服务函数】(难点) u8 TIM2CH1_CAPTURE_STA=0; // 输入捕获状态 u16 TIM2CH1_CAPTURE_VAL; // 输入捕获值 void TIM2_IRQHandler(void) { if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获 { if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH1_CAPTURE_VAL=0XFFFF; }else TIM2CH1_CAPTURE_STA++; } } if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)//捕获1发生捕获事件 { if(TIM2CH1_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM2CH1_CAPTURE_STA|=0X80; //标记成功捕获 TIM2CH1_CAPTURE_VAL=TIM_GetCapture2(TIM2); TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH1_CAPTURE_STA=0; //清空 TIM2CH1_CAPTURE_VAL=0; TIM_SetCounter(TIM2,0); TIM2CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿 TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获 } } } TIM_ClearITPendingBit(TIM2, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位 }

TIM2CH1_CAPTURE_STA

这个8位无符号整型变量,用于跟踪输入捕获状态:

最高位(7):标记捕获是否成功,如果置1,表示已成功捕获到一次输入信号的上升沿或下降沿。

第七位(6):标记是否捕获到高电平(置1)

前六位用于计数(高电平到来前的时间)TIM2CH1_CAPTURE_VAL

首先我们明确一下前提:

每一个周期时间到了,就会进一次这个中断服务函数。捕获到高电平,也会进一次这个中断服务函数。

然后我们的需求:是求出Echo引脚高电平持续的时间。

难点在于:在捕获过程中,定时器可能会发生溢出,而溢出会导致计数器的值从 0 开始重新计数。因此,捕获的持续时间不仅包括了捕获值之后的计数时间,还要加上捕获值之前溢出的计数时间。

所以每次进入中断服务函数之前,

判断一下:有没有捕获成功?(成功了会置1)

尚未成功捕获到信号边沿,继续等待捕获。

继续判断:是定时器更新事件,还是捕获事件?

(定时器的计数器溢出并重新开始计数时,会触发更新事件)(定时器捕获到外部信号的边沿(上升沿或下降沿)时,会触发捕获事件)

  如果是更新事件:表示定时器溢出

      如果成功捕获到高电平:

            如果高电平持续时间和定时周期一样:标记成功捕获了一次,将捕获值设置为最大值

            如果高电平持续时间小于定时周期:那就让捕获值持续计数

如果是捕获事件,   

        如果第一次捕获上升沿

                    类似初始化:

                    1.两个变量置0     

                    2.定时器计数值为0   

                    3.标记捕获到上升沿  

                    4.【问】为什么设置下次捕获为下降沿?

我要测高电平的时间,第一次是上升沿下一次下降沿到来,就是计时结束的时间。

        如果捕获到下降沿:

                   首先标记捕获成功,然后获取捕获值(因为相当于第二次捕获到下降沿),此时CCR寄存器的值就是高电平的时间(我们需要!),读出来即可,赋值给TIM2CH1_CAPTURE_VAL。然后标记下次捕获为上升沿(相当于重新开始计时)。

【距离计算函数】 int SR04_Distance(void) { SR04 = 1; delay_us(13); SR04 = 0; if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿 { time=TIM2CH1_CAPTURE_STA&0X3F; time*=65536;//溢出时间总和 time+=TIM2CH1_CAPTURE_VAL;//得到总的高电平时间 Distance = time*0.033/2; TIM2CH1_CAPTURE_STA=0;//开启下一次捕获 } return Distance; }

如果捕获成功,读取sta此时的值,然后再加上val计算的时间得到总的高电平时间。

然后进行换算得到距离。同时把sta清0。

代码中,当捕获到上升沿时,会记录捕获值并设置下一次捕获为下降沿; 当捕获到下降沿时,会记录捕获值并设置下一次捕获为上升沿。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有