关于模块的一些内容可以在淘宝的购买界面能找到,因为模块比较简单,主要用到的内容是IO口输出方波,外部中断和定时器计时
我买的超声波就4个引脚VCC Tring Echo GND。
主要的原理是Tring端输出一个10us的高电平,Echo接收返回的信号,其中发射与接收中间会空出一段时间,这段时间就是我们求距离得关键。
首先是IO口的初始化,我这里选择了板子的PB0(Tring)和PB1(Echo)。
void SR04_Cfg(void)
{
GPIO_InitTypeDef GPIO_InitSture;
EXTI_InitTypeDef EXTI_InitSture;
//如果外部中断的话则一定使能AFIO复用功能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO|RCC_APB2Periph_GPIOB,ENABLE);
//配置IO端口
GPIO_InitSture.GPIO_Mode=GPIO_Mode_Out_PP; //推挽输出模式
GPIO_InitSture.GPIO_Pin=GPIO_Pin_0; //将PE4于Trig相连
GPIO_InitSture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitSture);
GPIO_InitSture.GPIO_Mode=GPIO_Mode_IPD; //拉输入模式
GPIO_InitSture.GPIO_Pin=GPIO_Pin_1; //将PE6于Echo相连
GPIO_InitSture.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitSture);
//中断和6端口映射一起
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource1);
//外部中断配置
EXTI_InitSture.EXTI_Line=EXTI_Line1;
EXTI_InitSture.EXTI_LineCmd=ENABLE;
EXTI_InitSture.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitSture.EXTI_Trigger=EXTI_Trigger_Rising;
EXTI_Init(&EXTI_InitSture);
NVIC_Config(EXTI1_IRQn,1,1);
}
其中NVIC优先级的配置为:
void NVIC_Config(uint16_t IRQChannel, uint16_t MainPriority, uint16_t SubPriority)
{
NVIC_InitTypeDef NVIC_InitStructure;
//设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = IRQChannel;
//设置主优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = MainPriority;
//设置抢占式优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = SubPriority;
//使能中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
IO口的初始化完成后,来看看是怎么用Tring端发送10us的高电平的:
GPIO_SetBits(GPIOB,GPIO_Pin_0);
SysTick_Delay_Us(20);
GPIO_ResetBits(GPIOB,GPIO_Pin_0);
my_delay_ms(60);
这段代码放在了主函数,虽然说是10us的高电平,但是实现时经常性卡住,数值不更新,将高电平的时间延长到20us就好了,然后建议整个的周期不要低于60us。
最后也是最重要的一块是Echo的外部中断了:
void EXTI1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line1) != RESET )
{
EXTI_ClearITPendingBit(EXTI_Line1);
TIM_SetCounter(TIM3,0);
TIM_Cmd(TIM3,ENABLE);
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)); //等待低电平
TIM_Cmd(TIM3,DISABLE);
Distance=TIM_GetCounter(TIM3)*340/200.0;
}
}
采用外部中断的形式判断是否接受到高电平,当接受到低电平时,开启定时器3进行计时,得到低电平时的时间。
void TIM3_CounterMode(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitSture;
/* 配置时基结构体 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//使能TIM3
//初始化定时器
TIM_TimeBaseInitSture.TIM_Period=9999;
TIM_TimeBaseInitSture.TIM_Prescaler=7199;
TIM_TimeBaseInitSture.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitSture.TIM_ClockDivision=0;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitSture);
//允许更新中断,触发方式中断
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);
TIM_ITConfig(TIM3,TIM_IT_Trigger,ENABLE);
NVIC_Config(TIM3_IRQn, 0, 1);
/*配置中断优先级*/
// TIM_Cmd(TIM3, ENABLE);
//使能定时器
}
到这里就已经完成了模块的使用了,可以在调试界面看数值的变换,或者用串口显示,LCD都行。
但要注意,超声波的晶振被挡住或后面被死死黏住时,超声波模块会将程序给卡住。
Hal库是近期学的,CubeMX经常配错东西 ,所以大家看个大概就行,不过和基本库的代码一样,都是能实现的。首先代码的思路是和基本库一样的,这里就简单的过一下。
GPIO_InitStruct.Pin = Tring_Pin|LEDR_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : Echo_Pin */
GPIO_InitStruct.Pin = Echo_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(Echo_GPIO_Port, &GPIO_InitStruct);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
HAL_NVIC_SetPriority(EXTI1_IRQn, 0, 2);
/* 超声波发送一定频率的高电平 */
void SR04Tring_Go(void)
{
HAL_GPIO_WritePin(GPIOB, Tring_Pin, GPIO_PIN_SET);
HAL_Delay(40);
HAL_GPIO_WritePin(GPIOB, Tring_Pin, GPIO_PIN_RESET);
HAL_Delay(50);
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_1)
{
htim6.Instance->CNT = 0;
HAL_TIM_Base_Start_IT(&htim6);
while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1));
HAL_TIM_Base_Stop_IT(&htim6);
Distance = htim6.Instance->CNT*340/200.0;
}
}
void MX_TIM6_Init(void)
{
/* USER CODE BEGIN TIM6_Init 0 */
/* USER CODE END TIM6_Init 0 */
// TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM6_Init 1 */
/* USER CODE END TIM6_Init 1 */
htim6.Instance = TIM6;
htim6.Init.Prescaler = 9999;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 7199;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim6);
HAL_NVIC_SetPriority(TIM6_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM6_IRQn);
// sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
// sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
/* USER CODE BEGIN TIM6_Init 2 */
/* USER CODE END TIM6_Init 2 */
}
因为是几块钱的小模块,所以在远的距离时会产生较大的误差,但用来做避障还是足够的。
Hal库就很方便,可以看着我的代码自己重新配一个模板,稍微改下,很快就能实现功能,