我的第一辆智能小车
提示:小编也是初学者,本文适用于想完成一个基础智能四轮车的初学者,大佬还请勿喷,欢迎各位指出错误的地方
使用的是STM32F103C8T6最小系统板
暑假在家无聊,刚好也在学习STM32的HAL库,就想着做个小车巩固自己学到的知识,顺便记录自己所遇到的错误
小编也是现学现写,准备做一个蓝牙遥控红外寻迹超声波避障小车
注意:这篇文章需要用到定时器输出比较和输入捕获,没有基础可能看不懂。
这篇博客讲述的就是如何去制作超声波避障小车
注意:没有基础的是无法完成的
1,相关传感器的介绍
舵机sg90模块
舵机SG90模块是一种小型舵机,适用于各种机器人和电子设备。它由一个直流电机、减速装置和位置反馈电路组成。该模块可以通过PWM信号来控制舵机的角度,典型的工作范围为0到180度。它具有快速响应、精准定位和稳定性好的特点,能够提供可靠的转动效果。
需要用到的知识: 定时器输出比较----pwm输出
使用原理:在PWM周期20MS以内控制有效电平的时间即可控制舵机的转动角度
超声波传感器
工作原理: HC-SR04传感器的工作原理与一般超声波传感器相似,包括发射超声波脉冲和接收反射波。它由超声波发射器和接收器组成,通过测量发射和接收之间的时间差来计算距离。
距离测量范围: HC-SR04传感器的有效测量距离通常在2厘米到4.5米之间,取决于环境条件和具体实现。使用接口: HC-SR04传感器通常通过GPIO口与单片机或其他控制器连接。它需要两个引脚进行数据传输,一个引脚用于发射超声波脉冲,另一个引脚用于接收反射波。
需要使用的知识:定时器输入捕获
使用原理:描述起来比较麻烦,建议看下面的博客
2.相关引脚的分配
舵机SG90模块
VCC 5V供电
GND
信号线(橙色) PA8 对应TIM1_CH1
超声波HC_04(新版)
VCC 3.3v-5v供电
GND
TRIG引脚 选用普通IO引脚即可 选用PA12
ECHO引脚 选用定时器引脚 PB5的TIM3_CH2重映射
3.stm32cubeMX的配置
这里是在上一篇----蓝牙小车的基础上完成的
相信大家学到这里了,基本的设置都会配置了
舵机的配置
选用的是PA8 TIM1_CH1 高级定时器具有通用定时器的全部功能
选择定时器1的通道一的PWM输出模式
设置PSC预分频系数为72-1 ARR的值为20000-1
PWM频率=72MHZ/(72-1+1)*(20000-1+1)=50hz
PWM周期=1/50hz=20ms
注意: ARR的值与下面设置转动角度的有关,不懂的建议和我设置的一样
超声波传感器的配置
开启定时器3通道2的输入捕获模式
将PSC预分频系数的值设置为72-1
ARR的值设置的65535
开启定时器3的全局中断,这里不开启的话会导致获取到的超声波避障函数的值为0
4.代码展示部分
舵机部分
duoji.c
#include "duoji.h"
void SG90_GetAngle(float value)
{/* 舵机转向函数*/
/*
PSC:20000-1 ARR:72-1
0 500
180 2500
角度 CCR的值
*/
value=value/180*2000+500;
__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,value);
/*value=50相当于一个周期内(20ms)有0.5ms高脉冲*/
}
void SG90_Init()
{
SG90_GetAngle(90);
}
void SG90_TurnL()
{
SG90_GetAngle(135);
}
void SG90_TurnR()
{
SG90_GetAngle(45);
}
duoji.h
#ifndef __DUOJI_H_
#define __DUOJI_H_
#include "main.h"
#include "tim.h"
void SG90_GetAngle(float value);
void SG90_Init(void); //舵机头初始化,摆正
void SG90_TurnL(void); //舵机头左转
void SG90_TurnR(void); //舵机头右转
#endif
超声波部分
这部分代码我引用了这个博主的,大家有不懂的点这里
将下面代码添加到tim.c中,复制粘粘到最下面
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到低电平;1,已经捕获到低电平了.
//[5:0]:捕获低电平后溢出的次数
uint8_t TIM3CH2_CAPTURE_STA; // 输入捕获状态
uint16_t TIM3CH2_CAPTURE_VAL; //输入捕获值
//溢出回调函数和捕获回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if ((TIM3CH2_CAPTURE_STA & 0x80) == 0) // 还未捕获成功
{
if (TIM3CH2_CAPTURE_STA & 0x40) // 捕获到一个下降沿
{
if ((TIM3CH2_CAPTURE_STA & 0x3F) == 0x3F) // 高电平的时间太长
{
TIM3CH2_CAPTURE_STA |= 0X80; // 标记为成功捕获一次
TIM3CH2_CAPTURE_VAL = 0XFFFF;
}
else
TIM3CH2_CAPTURE_STA++; // 否则标记溢出数加1
}
}
}
// 捕获中断发生时执行 上升沿复位开始计时,下降沿获取捕获值计算
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if ((TIM3CH2_CAPTURE_STA & 0X80) == 0) //还未捕获成功 [7]:0,没有成功的捕获;1,成功捕获到一次.
{
if (TIM3CH2_CAPTURE_STA & 0X40) // 成功率捕获到1个下降沿 [6]:0,还没捕获到低电平;1,已经捕获到低电平了.
{
// usart_printf("get down\r\n");
TIM3CH2_CAPTURE_STA |= 0X80; // 标记成功,捕获到1次高电平完成
TIM3CH2_CAPTURE_VAL = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_2); // 捕获当前设置捕获值
TIM_RESET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2); // 清除原来设置
TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2, TIM_ICPOLARITY_RISING); // 捕获到下降沿之后,将捕获到复位为上升沿
}
else // 捕获到一个上升沿
{
// usart_printf("get up\r\n");
TIM3CH2_CAPTURE_STA = 0;
TIM3CH2_CAPTURE_VAL = 0;
TIM3CH2_CAPTURE_STA |= 0X40; //将STA置为0x40 当下一次触发中断时,会进入上面的if语句
__HAL_TIM_DISABLE(&htim3);
__HAL_TIM_SET_COUNTER(&htim3, 0);
TIM_RESET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2);
TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2, TIM_ICPOLARITY_FALLING);
__HAL_TIM_ENABLE(&htim3);
}
}
}
将下面的代码添加到main.h中
extern uint8_t TIM3CH2_CAPTURE_STA; // 输入捕获状态
extern uint16_t TIM3CH2_CAPTURE_VAL; //输入捕获值
extern uint8_t unit_change; //单位变换
HCSR04.C
/**
* @file HCSR04.c
* @author Zhong Zepeng (1935595312@qq.com)
* @brief
* @version 0.1
* @date 2022-11-25
*
* @copyright Copyright (c) 2022
*
*/
#include "HCSR04.h"
#include "gpio.h"
#include "tim.h"
#include "usart.h"
#include "User_Debug.h"
/**
* @brief 激活超声波定时器
*
*/
void HCSR_04()
{
uint32_t i;
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET);
for (i = 0; i < 72 * 40; i++)
__NOP();
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
}
/**
* @brief 关闭超声波定时器
*
*/
void Stop_HCSR_04()
{
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET);
}
/**
* @brief 计算超声波检测的距离
*
* @return float
*/
//uint8_t TIM3CH2_CAPTURE_STA; // 输入捕获状态
//uint16_t TIM3CH2_CAPTURE_VAL; //输入捕获值
uint8_t unit_change;
float getSR04Distance()
{
float len = 0;
uint32_t time = 0;
if (TIM3CH2_CAPTURE_STA & 0X80) // 输入捕获 触发
{
time = TIM3CH2_CAPTURE_STA & 0X3f; // 获得溢出次数
time *= 65535; // 一次溢出为65536 得到溢出的时间
time += TIM3CH2_CAPTURE_VAL; // 溢出的时间+现在定时器的值 得到总的时间
if (unit_change == 0)
{
len = (time * 342.62 * 100 / 2000000); // 计算得到距离 cm
}
// else if (unit_change == 1)
// {
// len = time * 342.62 * 100 / 200000000; // 计算得到距离 m
// usart_printf("m\r\n");
// }
TIM3CH2_CAPTURE_STA = 0; // 清除溢出
}
return len;
}
HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H
#include "stdint.h"
void HCSR_04(void);
void Stop_HCSR_04(void);
float getSR04Distance(void);
#endif
main.c
这里我是用函数封装了起来,大家使用的时候在while(1)里调用这个函数就行
因为超声波数据获取的时候有视野盲区,小编在代码方法添加了2个红外传感器防止撞墙
void ultrasonic_waves()
{
//★舵机指向90° 正中间
SG90_Init();
Change_speed(70);
HAL_Delay(700); //延时,不做延时的话,会超过采样频率
HCSR_04(); //激活超声波模块
distance_res[0] = getSR04Distance();
HAL_Delay(10);
if(LR1==0 || LR2==0)
{
HAL_Delay(10);
if(LR1==0)
{
Backward();
HAL_Delay(300);
Turn_Right();
HAL_Delay(200);
}
if(LR2==0)
{
Backward();
HAL_Delay(300);
Turn_Left();
HAL_Delay(200);
}
}
else if(distance_res[0]>35.00)
{
Forward();
}
else if(LR2==1&&LR2==1)//如果前方距离小于30厘米 停车测左右距离
{
Stopward();
SG90_TurnL();//舵机左转45度测距
HAL_Delay(1200);
HCSR_04(); //激活超声波模块
distance_res[1]=getSR04Distance();
HAL_Delay(10);
SG90_TurnR();//舵机右转45度测距
HAL_Delay(1200);
HCSR_04(); //激活超声波模块
distance_res[4]=getSR04Distance();
HAL_Delay(10);
SG90_Init();
if(distance_res[1]>distance_res[4]) //如果左边的距离大于右边的距离
{
SG90_Init(); //舵机摆正
HAL_Delay(700);
HCSR_04(); //激活超声波模块
distance_res[0] =getSR04Distance(); //重复测前方的距离同时左转
HAL_Delay(100);
Turn_Left();
if(distance_res[0]>30.0000)
{
Forward();
}
if(distance_res[1]<distance_res[4]) //如果右边的距离大于左边的距离
{
SG90_Init();
HAL_Delay(700);
HCSR_04(); //激活超声波模块
distance_res[0] =getSR04Distance(); //重复测前方的距离同时右转
HAL_Delay(10);
Turn_Right();
{
Forward();
HCSR_04(); //激活超声波模块
distance_res[0] = getSR04Distance();
HAL_Delay(10);
Turn_Right();
if(distance_res[0]>30.0000)
{
Forward();
}
}
}
}
}
}
5.注意事项
1.舵机信号线使用的定时器不能和超声波传感器使用的一致,不然会导致超声波函数接收到的数据不准确,导致经常撞墙。
2.在获取超声波测距的数据的时候,可以使用USB转TTL转换器,用串口获取准确的数据,数据一定要稳定。
3.供电问题,一定要保证要稳定供电,供电不足会导致传感器无法正常工作
6.寄语
这个功能我调试了很久,相比于前2个功能比较复杂,关于代码,不推荐大家照搬,要根据自己的需求来改,大家有什么疑惑可以发评论区里,看到了我会回复
关于代码部位我已上传,分别关于舵机部分,超声波部分,避障小车部分
7.视频展示
基于stm32的HAL库开发的超声波避障小车