引言
我们上一讲说到了有两种思路来初始化我们的超声波测距模块!
1使用从模式 2使用捕获通道
但其实他们的初始化结构基本相似,所以我们只使用第二种方法初始化。
我们使用的是TIM4定时器。
补充
上节课我们遗漏了一个知识点,传进定时器的ETR信号是什么!理解这个十分重要!
我在这里补充一下。
ETR
ETR(External Trigger Input)外部/触发/输入(是一个输入端引脚)
外部时钟源:ETR引脚可以作为定时器的外部时钟源输入,允许定时器根据外部信号的频率进行计数。这对于需要与外部事件同步的定时器应用尤为重要。
触发信号输入:除了作为时钟源外,ETR引脚还可以接收外部触发信号,用于在特定条件下启动或重置定时器的计数。
应用实例
- 脉冲计数:ETR引脚可以用于计数外部输入的脉冲个数,实现频率测量等功能。
- 同步控制:在多个定时器或外部设备需要同步工作的场景中,ETR引脚可以用于接收同步信号,确保各设备按照相同的节奏工作。
- 位置控制:在电机控制等应用中,ETR引脚可以接收编码器的信号,实现精确的位置控制。
捕获模式
我们使用的是TIM4定时器的一通道。
所以引脚对应关系为:trrig——PA0(产生TTL电平信号) echo——PB6与PE0(一个是接收ETR信号,一个是TIM4的一通道)那我们直接开写了:
TRIG引脚初始化
我们需要使PA0初始化完处于低电平状态,当我们需要测距时,产生一个TTL脉冲,脉冲的时间大于10us。根据此思路可以写出一个初始化代码和触发测距代码。
#include "echo.h"
#include "Delay.h"
void TRIG_Init(void)
{
GPIO_InitTypeDef triggpio;
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);
triggpio.GPIO_Mode=GPIO_Mode_Out_PP;
triggpio.GPIO_Pin=GPIO_Pin_0;
triggpio.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &triggpio);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
}
void TRIG(void)
{
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
Delay_us(11);
GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_RESET);
}
ECHO引脚初始化
这里就不多讲了,需要的直接拿走即可。接线方式,PE0与PB6同时接入echo引脚。
void echo_Init(void)
{
GPIO_InitTypeDef echogpio;
TIM_TimeBaseInitTypeDef time4;
TIM_ICInitTypeDef ic1;
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOB, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM4, ENABLE);
echogpio.GPIO_Mode=GPIO_Mode_IN_FLOATING;
echogpio.GPIO_Pin=GPIO_Pin_6;
echogpio.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &echogpio);
TIM_TimeBaseStructInit(&time4);
time4.TIM_CounterMode=TIM_CounterMode_Up;
time4.TIM_Period=71;
time4.TIM_Prescaler=65535;
TIM_TimeBaseInit(TIM4, &time4);
TIM_ICStructInit(&ic1);
ic1.TIM_Channel=TIM_Channel_1;
ic1.TIM_ICPolarity=TIM_ICPolarity_Falling;//从定时器开始到捕获到下降沿结束
ic1.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM4, &ic1);
// DMA/中断操作(计数和捕获)
TIM_ClearFlag(TIM4,TIM_FLAG_CC1|TIM_FLAG_Update);
TIM_ITConfig(TIM4, TIM_IT_CC1,ENABLE);
TIM_CCxNCmd(TIM4, TIM_Channel_1, TIM_CCxN_Enable);
TIM_Cmd(TIM4, DISENABLE);
// 计数涉及到了加减所以需要清零
TIM4->CNT=0x00;
}
那么什么时候才打开定时器呢,这就涉及到了PE0引脚的作用。(我们的捕获指的是从定时器打开到定时器接收到电平变低的脉冲,所以我们需要一个能够控制定时器打开的功能)因此还需要一个函数。
void echo(void)
{
GPIO_InitTypeDef ping;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
ping.GPIO_Mode=GPIO_Mode_IN_FLOATING;
ping.GPIO_Pin=GPIO_Pin_0;
ping.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &ping);
TIM_ETRConfig(TIM4, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted,0x00);
TIM_SelectInputTrigger(TIM4, TIM_TS_ETRF);
TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Trigger);
TIM4->CNT =0x0;
TIM_Cmd(TIM4, DISABLE);
}
模块初始化
void ECHO(void)
{
TRIG_Init();
TRIG();
echo_Init();
echo();
}
echo.h
#ifndef __ECHO_H
#define __ECHO_H
#include "stm32f10x.h"
void TRIG_Init(void);
void TRIG(void);
void echo_Init(void);
void echo(void);
#endif
中断函数计算距离
在初始化完成,我们就实现了当测距开始时打开时钟,拉成高电平,当我们测距完成时拉低电平,进行捕获。那么我们的终极目标是测距,所以必须有一个中断服务函数。
#include "echo.h"
float Distance =0;
void TIM4_IRQChannel(void)
{
__IO uint16_t cnt;
cnt=TIM4->CCR1;//(us计时)
if(TIM_GetITStatus(TIM4,TIM_IT_CC1)==SET)
{
Distance=cnt/58.82;//(当时间单位为微秒距离单位为厘米时:时间*1/2*340m/s就近似等于58.82)
TIM_ClearFlag(TIM4, TIM_FLAG_CC1);
TIM_Cmd(TIM4, DISABLE);
TIM4->CNT = 0;
}
}
主函数使用
优先级配置
有了模块有了中断必不可少的就是配置中断优先级。我们尽量配置的高一些,因为就靠中断来完成我们的功能!!!
#include "stm32f10x.h"
#include "echo.h"
#include "myIT.h"
int main(void)
{
NVIC_InitTypeDef nvic;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
nvic.NVIC_IRQChannel=TIM4_IRQn ;
nvic.NVIC_IRQChannelPreemptionPriority=1;
nvic.NVIC_IRQChannelSubPriority=1;
nvic.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&nvic);
while(1)
{
ECHO(); //触发超声波测距
Delay_ms(10);
}
}
下一节课我们就涉及到了如何完成超声波测距避障!并且如何将模块装载到我们的小车上!