打印
[其他ST产品]

基于stm32cubeMX的的HAL库开发的智能小车------超声波避障小车

[复制链接]
848|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
我的第一辆智能小车
提示:小编也是初学者,本文适用于想完成一个基础智能四轮车的初学者,大佬还请勿喷,欢迎各位指出错误的地方

使用的是STM32F103C8T6最小系统板

暑假在家无聊,刚好也在学习STM32的HAL库,就想着做个小车巩固自己学到的知识,顺便记录自己所遇到的错误

小编也是现学现写,准备做一个蓝牙遥控红外寻迹超声波避障小车

注意:这篇文章需要用到定时器输出比较和输入捕获,没有基础可能看不懂。

这篇博客讲述的就是如何去制作超声波避障小车

注意:没有基础的是无法完成的

1,相关传感器的介绍
舵机sg90模块

舵机SG90模块是一种小型舵机,适用于各种机器人和电子设备。它由一个直流电机、减速装置和位置反馈电路组成。该模块可以通过PWM信号来控制舵机的角度,典型的工作范围为0到180度。它具有快速响应、精准定位和稳定性好的特点,能够提供可靠的转动效果。

需要用到的知识:    定时器输出比较----pwm输出



使用原理:在PWM周期20MS以内控制有效电平的时间即可控制舵机的转动角度


超声波传感器

工作原理: HC-SR04传感器的工作原理与一般超声波传感器相似,包括发射超声波脉冲和接收反射波。它由超声波发射器和接收器组成,通过测量发射和接收之间的时间差来计算距离。

距离测量范围: HC-SR04传感器的有效测量距离通常在2厘米到4.5米之间,取决于环境条件和具体实现。使用接口: HC-SR04传感器通常通过GPIO口与单片机或其他控制器连接。它需要两个引脚进行数据传输,一个引脚用于发射超声波脉冲,另一个引脚用于接收反射波。

需要使用的知识:定时器输入捕获


使用特权

评论回复
沙发
喂什么玩意|  楼主 | 2024-1-29 15:57 | 只看该作者
2.相关引脚的分配
舵机SG90模块

VCC                        5V供电

GND

信号线(橙色)            PA8       对应TIM1_CH1

超声波HC_04(新版)

VCC             3.3v-5v供电

GND

TRIG引脚              选用普通IO引脚即可    选用PA12

ECHO引脚            选用定时器引脚       PB5的TIM3_CH2重映射

使用特权

评论回复
板凳
喂什么玩意|  楼主 | 2024-1-29 15:57 | 只看该作者
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的值与下面设置转动角度的有关,不懂的建议和我设置的一样



使用特权

评论回复
地板
喂什么玩意|  楼主 | 2024-1-29 15:57 | 只看该作者
超声波传感器的配置

开启定时器3通道2的输入捕获模式

将PSC预分频系数的值设置为72-1

ARR的值设置的65535

使用特权

评论回复
5
喂什么玩意|  楼主 | 2024-1-29 15:58 | 只看该作者
开启定时器3的全局中断,这里不开启的话会导致获取到的超声波避障函数的值为0

使用特权

评论回复
6
喂什么玩意|  楼主 | 2024-1-29 15:58 | 只看该作者
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);
}

使用特权

评论回复
7
喂什么玩意|  楼主 | 2024-1-29 16:08 | 只看该作者
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

使用特权

评论回复
8
喂什么玩意|  楼主 | 2024-1-29 16:08 | 只看该作者
超声波部分

将下面代码添加到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);
    }
  }
}

使用特权

评论回复
9
喂什么玩意|  楼主 | 2024-1-29 16:09 | 只看该作者
将下面的代码添加到main.h中
extern uint8_t TIM3CH2_CAPTURE_STA;  // 输入捕获状态
extern uint16_t TIM3CH2_CAPTURE_VAL; //输入捕获值
extern uint8_t unit_change;                 //单位变换

使用特权

评论回复
10
喂什么玩意|  楼主 | 2024-1-29 16:09 | 只看该作者
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;
}

使用特权

评论回复
11
喂什么玩意|  楼主 | 2024-1-29 16:09 | 只看该作者
HCSR04.h
#ifndef __HCSR04_H
#define __HCSR04_H

#include "stdint.h"


void HCSR_04(void);
void Stop_HCSR_04(void);
float getSR04Distance(void);

#endif

使用特权

评论回复
12
喂什么玩意|  楼主 | 2024-1-29 16:09 | 只看该作者
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();
            }
            }
        }
    }
   }

}

使用特权

评论回复
13
喂什么玩意|  楼主 | 2024-1-29 16:09 | 只看该作者
5.注意事项
1.舵机信号线使用的定时器不能和超声波传感器使用的一致,不然会导致超声波函数接收到的数据不准确,导致经常撞墙。

2.在获取超声波测距的数据的时候,可以使用USB转TTL转换器,用串口获取准确的数据,数据一定要稳定。

3.供电问题,一定要保证要稳定供电,供电不足会导致传感器无法正常工作

使用特权

评论回复
14
喂什么玩意|  楼主 | 2024-1-29 16:10 | 只看该作者
6.寄语
这个功能我调试了很久,相比于前2个功能比较复杂,关于代码,不推荐大家照搬,要根据自己的需求来改,大家有什么疑惑可以发评论区里,看到了我会回复

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

27

主题

337

帖子

0

粉丝