【STM32模块化程序】定时器输入捕获实现超声波测距

发布者:快乐舞蹈最新更新时间:2021-09-18 来源: eefocus关键字:STM32  模块化程序  定时器  输入捕获  超声波测距 手机看文章 扫描二维码
随时随地手机看文章

一、输入捕获介绍

输入捕获模式可以用来测量脉冲宽度或者测量频率。


除了基本定时器,通用和高级均有输入捕获功能。


TIMx_CCMR1(捕获/比较模式寄存器) - 通道1和2的控制

TIMx_CCMR2(捕获/比较模式寄存器) - 通道3和4的控制


以下将分别使用通用定时器和高级定时器实现超声波测距。需要说明的是,代码中的TIM×CH×_CAPTURE_STA我们定义其低八位含义如下:

在这里插入图片描述

二、通用/高级定时器实现超声波测距

通用定时器输入捕获以TIM3的CH3为例,对应IO为PB1。PB0实现触发功能


当然还需要配合printf一起使用,<参考这里>


完整工程可见github<传送门>


timer.c


#include "timer.h"

#include "delay.h"

#include "usart.h"



//**************以下TIM1实现超声波测距****************//

u16 TIM1CH1_CAPTURE_STA,TIM1CH1_CAPTURE_VAL;


void TIM1_Cap_Init(u16 arr,u16 psc)

{

GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

TIM_ICInitTypeDef TIM_ICInitStructure;


RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟



GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;     

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //PB1输出 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;     //2M

GPIO_Init(GPIOB, &GPIO_InitStructure);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;  

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; 

GPIO_Init(GPIOA, &GPIO_InitStructure);



TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);//设置缺省值,这一步最好加上

TIM_TimeBaseStructure.TIM_Period = arr; //自动重装载寄存器周期的值,溢出值

TIM_TimeBaseStructure.TIM_Prescaler =psc; //时钟频率预分频值

TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:输入捕获模式用来滤波

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式

TIM_TimeBaseStructure.TIM_RepetitionCounter=0;//设置重复溢出次数,就是多少次溢出后进入中断,一般为0,只有高级定时器才有用

TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); 



TIM_ICStructInit(&TIM_ICInitStructure);//设置缺省值,这一步最好加上

TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获

TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上

TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;   //配置输入分频,不分频 

TIM_ICInitStructure.TIM_ICFilter = 0x00;   //IC1F=0000 配置输入滤波器 0不滤波

TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //IC1映射到TI1上,这四句不能合并

TIM_ICInit(TIM1, &TIM_ICInitStructure);


//中断分组初始化

NVIC_InitStructure.NVIC_IRQChannel = TIM1_CC_IRQn;  

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 

NVIC_Init(&NVIC_InitStructure);   


TIM_ITConfig(TIM1,TIM_IT_Update|TIM_IT_CC1,ENABLE);   //允许更新中断,允许CC1IE,CC2IE,CC3IE,CC4IE捕获中断

TIM_CtrlPWMOutputs(TIM1,ENABLE); //主输出使能

TIM_Cmd(TIM1, ENABLE); //使能定时器1

}


void Read_TIM1Distane(void)

{   

PBout(1)=1;

delay_us(15);  

PBout(1)=0;

if(TIM1CH1_CAPTURE_STA&0X80)

{

Distance=TIM1CH1_CAPTURE_STA&0X3F;

Distance*=65536;         //溢出时间总和

Distance+=TIM1CH1_CAPTURE_VAL; //得到总的高电平时间

Distance = Distance*170/1000;

printf("%d rn",Distance);

TIM1CH1_CAPTURE_STA = 0;

}

}


void TIM1_CC_IRQHandler(void)

{

if((TIM1CH1_CAPTURE_STA&0X80) == 0)

{

if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)

{

  //通道1

if(TIM1CH1_CAPTURE_STA&0X40)//已经捕获到高电平了

{

if((TIM1CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了

{

TIM1CH1_CAPTURE_STA|=0X80;

TIM1CH1_CAPTURE_VAL = 0xFFFF;

}

else

{

TIM1CH1_CAPTURE_STA++;

}

}

}


//通道1

if (TIM_GetITStatus(TIM1, TIM_IT_CC1) != RESET) //捕获1发生捕获事件

{

if (TIM1CH1_CAPTURE_STA & 0X40) //捕获到一个下降沿

{

TIM1CH1_CAPTURE_STA|=0X80;

TIM1CH1_CAPTURE_VAL = TIM_GetCapture1(TIM1);//记录下此时的定时器计数值

TIM_OC1PolarityConfig(TIM1, TIM_ICPolarity_Rising); //设置为上升沿捕获

}

else //发生捕获时间但不是下降沿,第一次捕获到上升沿,记录此时的定时器计数值

{

TIM1CH1_CAPTURE_STA = 0;

TIM1CH1_CAPTURE_VAL = 0;

TIM1CH1_CAPTURE_STA |= 0X40; //标记已捕获到上升沿

TIM_SetCounter(TIM1, 0); //清空计数器

TIM_OC1PolarityConfig(TIM1, TIM_ICPolarity_Falling);//设置为下降沿捕获

}

}

}

    TIM_ClearITPendingBit(TIM1, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位

}


//**************以下TIM3实现超声波测距****************//

u16 TIM3CH3_CAPTURE_STA,TIM3CH3_CAPTURE_VAL;


void TIM3_Cap_Init(u16 arr,u16 psc)

{  

GPIO_InitTypeDef GPIO_InitStructure;

TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

NVIC_InitTypeDef NVIC_InitStructure;

TIM_ICInitTypeDef  TIM3_ICInitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能TIM3时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能GPIOB时钟


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0; 

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PB0 输入  

GPIO_Init(GPIOB, &GPIO_InitStructure);


GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_1;     

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     //PB1输出 

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;     //2M

GPIO_Init(GPIOB, &GPIO_InitStructure);


//初始化定时器3 TIM3  

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(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位


//初始化TIM3输入捕获参数

TIM3_ICInitStructure.TIM_Channel = TIM_Channel_3; //CC1S=03 选择输入端 IC3映射到TI1上

TIM3_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获

TIM3_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;

TIM3_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频 

TIM3_ICInitStructure.TIM_ICFilter = 0x00;//配置输入滤波器 不滤波

TIM_ICInit(TIM3, &TIM3_ICInitStructure);


//中断分组初始化

NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;  //从优先级0级

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能

NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

TIM_ITConfig(TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断

TIM_Cmd(TIM3,ENABLE ); //使能定时器3

}


void Read_TIM3Distane(void)

{   

PBout(1)=1;

delay_us(15);  

PBout(1)=0;

if(TIM3CH3_CAPTURE_STA&0X80)//成功捕获到了一次高电平

{

Distance=TIM3CH3_CAPTURE_STA&0X3F;

Distance*=65536;         //溢出时间总和

Distance+=TIM3CH3_CAPTURE_VAL; //得到总的高电平时间

Distance=Distance*170/1000;

printf("%d rn",Distance);

TIM3CH3_CAPTURE_STA=0; //开启下一次捕获

}

}


void TIM3_IRQHandler(void)

{           

if((TIM3CH3_CAPTURE_STA&0X80)==0)//还未成功捕获

{

if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)//溢出

{     

if(TIM3CH3_CAPTURE_STA&0X40)//已经捕获到高电平了

{

if((TIM3CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了

{

TIM3CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次

TIM3CH3_CAPTURE_VAL=0XFFFF;

}

else 

TIM3CH3_CAPTURE_STA++;

}  

}

if (TIM_GetITStatus(TIM3, TIM_IT_CC3) != RESET)//捕获3发生捕获事件

{

if(TIM3CH3_CAPTURE_STA&0X40) //捕获到一个下降沿

{  

TIM3CH3_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽

TIM3CH3_CAPTURE_VAL=TIM_GetCapture3(TIM3); //获取当前的捕获值.

TIM_OC3PolarityConfig(TIM3, TIM_ICPolarity_Rising); //设置为上升沿捕获

}

else  //还未开始,第一次捕获上升沿

{

TIM3CH3_CAPTURE_STA=0; //清空

TIM3CH3_CAPTURE_VAL=0;

TIM3CH3_CAPTURE_STA|=0X40; //标记捕获到了上升沿

TIM_SetCounter(TIM3, 0); //清空计数器

TIM_OC3PolarityConfig(TIM3, TIM_ICPolarity_Falling);//设置为下降沿捕获

}     

}              

}

TIM_ClearITPendingBit(TIM3, TIM_IT_Update|TIM_IT_CC3); //清除中断标志位

}


timer.h


#ifndef __TIMERs_H

#define __TIMERs_H

#include "sys.h"

#include "stm32f10x.h"


extern u32 Distance;


//******TIM1相关***********//


void TIM1_Cap_Init(u16 arr,u16 psc);

void Read_TIM1Distane(void);


//******TIM3相关***********//

void TIM3_Cap_Init(u16 arr,u16 psc);

void Read_TIM3Distane(void);


#endif


main.c


#include "led.h"

#include "delay.h"

#include "sys.h"

#include "usart.h"

#include "timer.h"

 

#define jishu_pinlv_psc 71


/************************************************

 printf使用串口2、波特率115200

 USART2_TX GPIOA.2,USART2_RX GPIOA.3

 

 超声波测距【输入捕获】

 TIM1_CH1(PA8) 

 TIM3_CH3(PB0)

************************************************/


u32 Distance;

int main(void)

{

delay_init();     //延时函数初始化   

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级

uart_init(115200); //串口初始化为115200

LED_Init();

TIM3_Cap_Init(0xFFFF,71);

printf("okrn");

LED = 0;

while(1)

{

Read_TIM3Distane();

delay_ms(200);

}

}

关键字:STM32  模块化程序  定时器  输入捕获  超声波测距 引用地址:【STM32模块化程序】定时器输入捕获实现超声波测距

上一篇:原子的串口ISP一键下载研究
下一篇:STM32中断源位置

推荐阅读最新更新时间:2024-11-09 12:15

msp430时钟/定时器收集的例程汇总
MSP430的时钟模块由DCOCTL, BCSCTL1, BCSCTL2, IE1, IFG1这五个寄存器来确定,具体的功能如下所示: (1)DCOCTL: DCO控制寄存器,地址为56H,初始值为60H // 7 6 5 4 3 2 1 0 // DCO2 DCO1 DCO0 MOD4 MOD3 MOD2 MOD1 MOD0 // // DCO0~DCO2: DCO Select Bit,定义了8种频率之一,而频率由注入直流发生器的电流定义 // MOD0~MOD4: Modulation Bit,频
[单片机]
UART和USART有区别
  UART   UART是一种通用串行数据总线,用于异步通信。该总线双向通信,可以实现全双工传输和接收。在嵌入式设计中,UART用于主机与辅助设备通信,如汽车音响与外接AP之间的通信,与PC机通信包括与监控调试器和其它器件,如EEPROM通信。   UART的功能计算机内部采用并行数据,不能直接把数据发到Modem,必须经过UART整理才能进行异步传输,其过程为:CPU先把准备写入串行设备的数据放到UART的寄存器(临时内存块)中,再通过FIFO(First Input First Output,先入先出队列)传送到串行设备,若是没有FIFO,信息将变得杂乱无章,不可能传送到Modem。它是用于控制计算机与串行设备的芯片。有
[单片机]
这样学习STM32单片机,从菜鸟到牛人很简单!
我想说,为了学习单片机而去学习单片机的思路不对。 你问,如何系统地入门学习stm32呢?应该如何零基础学习电子产品设计呢? 这本身就是一个错误的问题。假如你会使用8051 , 会写C语言,那么STM32本身并不需要刻意的学习。 你要考虑的是, 我可以用STM32实现什么?创客学院带你走进stm32世界 为什么使用STM32而不是8051?是因为51的频率太低,无法满足计算需求?是51的管脚太少,无法满足众多外设的IO?是51的功耗太大,电池挺不住?是51的功能太弱,而你要使用SPI、I2C、ADC、DMA?是51的内存太小而你要存储的东西太多? 当你需要使用STM32某些功能,而51实现不了的时候, 那STM3
[单片机]
STM32字符转整型处理
/* USER CODE BEGIN Header */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include main.h #include usart.h #include gpio.h /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include stdl
[单片机]
<font color='red'>STM32</font>字符转整型处理
基于STM32对DS1302的驱动
// 程序名: STM32驱动DS1302 //头文件 #include “stm32f10x.h” #include “usart.h” #define uchar unsigned char #define uint unsigned int ////DS1302引脚定义,可根据实际情况自行修改端口定义 #define RST PAout(5) #define IO PAout(6) #define SCK PAout(7) //DS1302地址定义 #define ds1302_sec_add 0x80 //秒数据地址 #define ds1302_min_add 0x82 //分数据地址 #define ds1302_
[单片机]
STM32按键实验--连续闪烁的问题
下面两段代码是STM32按键实验的按键扫描代码(两段代码标红的地方不同),但是却有两种不同的现象 1、u8 KEY_Scan(u8 mode) { static u8 key_up=1;//按键按下松开标志 if(mode)key_up=1; //支持连续按下 if(key_up&&(KEY0==0||KEY1==0||WK_UP==1)) { delay_ms(10);//消抖 key_up=0; if(KEY0==0)return KEY0_PRES; else if(KEY1==0)return KEY1_PRES; else if(WK_UP==1)return WKUP_PRES; }else if(KEY0==1
[单片机]
STM32单片机中使用SPI通信的方法
  在本教程中,我们将使用 STM32F103C8 的 Blue Pill 板替换一个 Arduino 板,并将使用 SPI 总线与 Arduino 板进行通信。在这个STM32 SPI 示例中,我们将使用Arduino UNO作为 Slave,STM32F103C8 作为 Master,两个16X2 LCD 显示器分别连接在一起。两个电位器还与STM32(PA0)和Arduino(A0)相连,通过改变电位器来确定主机到从机和从机到主机的发送值(0到255)。   STM32F103C8中的SPI   比较 Arduino 和 STM32F103C8 Blue Pill 板中的 SPI 总线,STM32 有2 条 SPI 总线
[单片机]
在<font color='red'>STM32</font>单片机中使用SPI通信的方法
基于STM32+RFID设计的宿舍检修管理系统
1. 需求 需求: 一个寝室一个标签,设备端扫描标签,通过显示屏键入维修成功与否之类的的信息提交到平台 系统构架: 一个设备端 + 一个上位机 硬件选型: (1)STM32F103RCT6作为设备端的主控MCU (2)RC522作为设备端的射频刷卡设备,读写IC卡 (3)多张IC卡,模拟代表每个宿舍 实现思路: 设计一个上位机,用来管理查看维修检修信息,当维修寝室的设备或者检修完成时,通过STM32上的RC522刷一下这个寝室的IC卡,识别这是哪个寝室,识别成功后在软件上弹出一个对话框,填写本次维修或者检修的事件,填写完毕点击提交即可。 软件分为两个功能: (1)注册功能:每个寝室都有一张IC卡,这张卡第一次使用需
[单片机]
基于STM32+RFID设计的宿舍检修管理系统
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved