语音蓝牙遥控避障小车教程
文章目录
- 语音蓝牙遥控避障小车教程
- 主要硬件选购
- 智能小车硬件
- 遥控手柄硬件
- 小车的基础配置
- 小车电机驱动
- 小车舵机驱动
- 小车超声波测距
- 语音遥控
- 语音模块代码
- STM32F103RCT6接收端代码
- 蓝牙遥控手柄遥控
- 蓝牙遥控手柄代码
- STM32F103RCT6接收端代码
- 超声波避障
语音蓝牙遥控避障小车教程
本项目目的:让初学者完成自己的一个项目
所用开发板:智能小车:STM32F103RCT6 遥控手柄:STM32F103C8T6
所用库函数:STM32 标准 库
开发环境— :Keil u5.38
答疑QQ群: 662455936
此文章用于配合我的视频教程,文章提供了硬件选购和关键代码的讲解,内容上不是很详细,毕竟博主时间有限,不过配合视频讲解下来难度不是很大的。此外语音蓝牙遥控避障小车的源码工程和PCB文件我都打包在一个文件里面去了,需要的朋友请关注我的抖音号点赞相关视频私信我获取,创作不易,希望能得到支持。
注意:有一个OPENMV循迹和陀螺仪转向的功能小车,我这里不做讲解,代码和语音智能小车一起打包发给你们。
- 资料打包图片:


- 抖音视频演示链接:语音蓝牙遥控避障小车
主要硬件选购
硬件均在淘宝购买,你们自行搜索,由于淘宝链接图片在CSDN违规,所以详情见视频讲解吧
智能小车硬件
- 小车底盘
- 电机
- 万向轮
- 电池
- STM32F103RCT6智能小车主控板(见PCB文件)
- 舵机
- 超声波
- 语音模块
- HC05蓝牙模块
- TB6612电机驱动
- 0.96寸OLED
- 蜂鸣器
遥控手柄硬件
- PS2摇杆
- 蜂鸣器(同上)
- HC05蓝牙模块(同上)
- 0.96寸OLED(同上)
- STM32F103C8T6手柄主控板(见PCB文件)
- 电池
小车的基础配置
小车电机驱动
驱动文件就是tb6612的代码,小车用到两个轮子,所以用一个tb6612模块驱动,用四个io控制电机的正反转,2路定时器PWM控制电机速度,详细教程可以看CSDN的tb6612教程。
tb6612详细教程
- tb662.c文件
#include "tb6612.h"
/***@brief 驱动电机的PWM定时器初始化*@breif PWMA-PB8 TIM4_CH3通道-左轮 PWMB-PB9 TIM4_CH4通道-右轮 *@param arr:自动重转载值 psc:预分频系数*/
void TIM4_PWM_MOTOR_Init(u16 arr,u16 psc)
{ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); //使能定时器4时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟//设置该引脚为复用输出功能,输出TIM4_CH3的PWM脉冲波形 GPIOB.8-PWMAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //TIM4_CH3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO//设置该引脚为复用输出功能,输出TIM4_CH4的PWM脉冲波形 GPIOB.9-PWMBGPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //TIM4_CH4GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO //初始化TIM3TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC3TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM3在CCR3上的预装载寄存器TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM4 OC4TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); //使能TIM3在CCR4上的预装载寄存器TIM_Cmd(TIM4, ENABLE); //使能TIM3
}
/***@brief 初始化电机方向控制引脚*@brief PC0-AIN1 PC1-AIN2 PC2-BIN1 PC3-BIN2*/
void motor_Init(void)
{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); //使能PC端口时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3; //PC0-AIN1 PC1-AIN2 PC2-BIN1 PC3-BIN2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOC, &GPIO_InitStructure); //根据设定参数初始化GPIOC.0-3GPIO_ResetBits(GPIOC,GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2|GPIO_Pin_3);//GPIO.C0-C3输出低电平
}
/***@brief 控制电机运动PWM占空比*@param speed_L :左轮的占空比 speed_R :右轮的占空比*/
void set_speed(uint16_t speed_L,uint16_t speed_R)
{TIM_SetCompare3(TIM4,speed_L); TIM_SetCompare4(TIM4,speed_R);
}
/***@brief 控制电机运动*@brief up-前进 down-后退 left-左转 right-右转 stop-停止*@param CONTROL :输入up则前进,输入down则后退 ,在头文件可查看宏定义*/
void Motion_control(int CONTROL)
{switch (CONTROL){case 1: //前进{AIN1=0;AIN2=1;BIN1=0;BIN2=1; }break;case 2: //后退{AIN1=1;AIN2=0;BIN1=1;BIN2=0; }break;case 3: //原地左转{AIN1=1;AIN2=0;BIN1=0;BIN2=1; }break;case 4: //原地右转{AIN1=0;AIN2=1;BIN1=1;BIN2=0; }break;case 5: //停止运动{AIN1=0;AIN2=0;BIN1=0;BIN2=0; }break;}
}
//以下函数可不看,没具体作用
void BZ_RIGHT(){set_speed(200,200);Motion_control(right);
}
void BZ_LEFT()
{set_speed(200,200);Motion_control(left);
}
void BZ_UP()
{set_speed(200,200);Motion_control(up);
}
- tb6612.h文件
#ifndef __TB6612_H
#define __TB6612_H
/**** 头文件包含区 ****/
#include "sys.h"
#include "delay.h"
/*********************//**** 全局变量区 ****//*******************//**** 函数声明区 ****/
void TIM4_PWM_MOTOR_Init(u16 arr,u16 psc); //驱动电机的PWM定时器初始化
void Motion_control(int CONTROL); //控制电机运动
void set_speed(uint16_t speed_L,uint16_t speed_R); //控制电机运动PWM占空比
void motor_Init(void); //TB6612引脚初始化
void BZ_RIGHT(void);
void BZ_LEFT(void);
void BZ_UP(void);
/*******************/
#define AIN1 PCout(1)
#define AIN2 PCout(0)#define BIN1 PCout(2)
#define BIN2 PCout(3)
enum control
{up=1,down,left,right,stop
};
#endif
小车舵机驱动
超声波放在舵机上实现雷达扫描的功能,舵机负责角度旋转,这里用定时器一路PWM即可完成舵机的驱动,将定时器得频率配置成50hz即可,然后控制占空比即可控制舵机打角。
- steer.c
#include "steer.h"
#include "delay.h"
#include "ultrasonic.h"
/***@brief 驱动舵机(MG90舵机 0-180 °)*@breif 舵机-PB0 TIM3_CH3通道*@param arr:自动重转载值 psc:预分频系数*/
void TIM3_PWM_STEER_Init(u16 arr,u16 psc)
{ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟/* 舵机通道 *///设置该引脚为复用输出功能,输出TIM3_CH3的PWM脉冲波形 GPIOB.0GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //TIM3_CH3GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); //初始化GPIO//初始化TIM3TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断//中断优先级NVIC设置NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器//初始化TIM3 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC3Init(TIM3, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM3 OC3TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR3上的预装载寄存器TIM_Cmd(TIM3, ENABLE); //使能TIM3
}
/***@brief 定时器3中断服务程序,用于测量超声波距离,超声波测距放在定时器中断服务函数里面*/
volatile int dis_tim3=0; //超声波距离全局变量,在避障的时候使用这个变量来得到距离与障碍物的
u8 flag_srig=0;
void TIM3_IRQHandler(void) //TIM3中断服务函数
{ if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否{TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志flag_srig++; //这里的flag_srig用来给超声波发送脉冲,启动测距if(flag_srig==1){TRIG=0; //拉低超声波TRIG引脚 }if(flag_srig==2){TRIG=1; //拉高超声波TRIG引脚用来产生大于10us的脉冲,这里为20ms脉冲,因为定时器中断20ms触发一次flag_srig=0;}if(TIM5CH2_CAPTURE_STA&0X80)//成功捕获到了一次上升沿{temp=TIM5CH2_CAPTURE_STA&0X3F;temp*=65536;//溢出时间总和temp+=TIM5CH2_CAPTURE_VAL;//得到总的高电平时间TIM5CH2_CAPTURE_STA=0; //开启下一次捕获if(temp*0.017>80){temp=4705;}dis_tim3=temp*0.017; //根据声速获取超声波距离 }}
}
/* 以下函数是控制舵机旋转速度函数,本程序不用 */
void steer_left()
{uint16_t i;for(i=182;i<=250;i++){TIM_SetCompare3(TIM3,i); /* 舵机旋转45度 舵机左转*/delay_ms(5);}
}void steer_left_to_mid()
{uint16_t i;for(i=250;i>=182;i--){TIM_SetCompare3(TIM3,i); /* 舵机旋转45度 舵机左转*/delay_ms(5);}
}void steer_right()
{uint8_t i;for(i=182;i>=114;i--){TIM_SetCompare3(TIM3,i); /* 舵机旋转45度 舵机右转*/delay_ms(5);}
}void steer_right_to_mid()
{uint8_t i;for(i=114;i<=182;i++){TIM_SetCompare3(TIM3,i); /* 舵机旋转45度 舵机右转*/delay_ms(5);}
}/* 下面是另外一个舵机程序,本程序也不用 */
/***@brief 驱动舵机(SG90舵机 0-180 °)*@breif 舵机-PC8 TIM8_CH3通道*@param arr:自动重转载值 psc:预分频系数*/
void TIM8_PWM_STEER_Init(u16 arr,u16 psc)
{ GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_OCInitTypeDef TIM_OCInitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE); //使能定时器3时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE); //使能GPIO外设和AFIO复用功能模块时钟/* 舵机通道 *///设置该引脚为复用输出功能,输出TIM8_CH2的PWM脉冲波形 GPIOC.7GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //TIM8_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIO//初始化TIM3TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM8 Channel2 PWM模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //选择定时器模式:TIM脉冲宽度调制模式1TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高TIM_OC2Init(TIM8, &TIM_OCInitStructure); //根据T指定的参数初始化外设TIM8 OC2TIM_OC2PreloadConfig(TIM8, TIM_OCPreload_Enable); //使能TIM8在CCR2上的预装载寄存器TIM_CtrlPWMOutputs(TIM8,ENABLE);TIM_Cmd(TIM8, ENABLE); //使能TIM8
}
- steer.h
#ifndef __STEER_H
#define __STEER_H
/**** 头文件包含区 ****/
#include "sys.h"
/*********************//**** 全局变量区 ****/
extern volatile int dis_tim3;
/*******************//**** 函数声明区 ****/
void TIM3_PWM_STEER_Init(u16 arr,u16 psc);
void TIM8_PWM_STEER_Init(u16 arr,u16 psc);
void steer_right(void);
void steer_left(void);
void steer_right_to_mid(void);
void steer_left_to_mid(void);
/*******************/
#define LAY TIM_SetCompare2(TIM8,244); /* 装载货物 */
#define PULL TIM_SetCompare2(TIM8,100); /* 倒货物 */
#endif
小车超声波测距
超声波测距用的是STM32的输入捕获功能,直接用正点原子的代码即可,然后将高电平时间除以2乘以声速即可得到距离。详细教程参考其他CSDN文章
- ultrasonic.c
#include "ultrasonic.h"
#include "tb6612.h"
#include "delay.h"
#include "pid.h"
#include "usart.h"
#include "lcd.h"
#include "stdlib.h"
#include "steer.h"
#include "led.h"
#include "oled.h"
/***@brief 超声波输入捕获定时器初始化*@breif ECHO-PA1-TIM5_CH2*@param arr:自动重转载值 psc:预分频系数*/
void TIM5_Cap_Init(u16 arr,u16 psc)
{ TIM_ICInitTypeDef TIM5_ICInitStructure;GPIO_InitTypeDef GPIO_InitStructure;TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); //使能TIM5时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB, ENABLE); //使能GPIA_GPIOB时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TRIG-->PB1 端口配置GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHzGPIO_Init(GPIOB, &GPIO_InitStructure); //根据设定参数初始化GPIOBGPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //PA1 TIM5_CH2GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA1 下拉输入 GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_ResetBits(GPIOA,GPIO_Pin_1); //PA1 输出低电平//初始化定时器5 TIM5TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值 TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频器 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_timTIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位//初始化TIM2输入捕获参数TIM5_ICInitStructure.TIM_Channel = TIM_Channel_2; //CC1S=02 选择输入端 TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波TIM_ICInit(TIM5, &TIM5_ICInitStructure);//中断分组初始化NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn; //TIM2中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级12级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级0级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC2,ENABLE);//允许更新中断 ,允许CC2IE捕获中断 TIM_Cmd(TIM5,ENABLE ); //使能定时器2
}u8 TIM5CH2_CAPTURE_STA=0; //输入捕获状态
u16 TIM5CH2_CAPTURE_VAL; //输入捕获值/***@brief 定时器5中断服务程序,用于捕获高电平持续时间,正点原子官方例程*@brief 用于检测超声波的反射时间*/
void TIM5_IRQHandler(void)
{ if((TIM5CH2_CAPTURE_STA&0X80)==0)//还未成功捕获 { if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET){ if(TIM5CH2_CAPTURE_STA&0X40)//已经捕获到高电平了{if((TIM5CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了{TIM5CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次TIM5CH2_CAPTURE_VAL=0XFFFF;}else TIM5CH2_CAPTURE_STA++;} }if (TIM_GetITStatus(TIM5, TIM_IT_CC2) != RESET)//捕获1发生捕获事件{ if(TIM5CH2_CAPTURE_STA&0X40) //捕获到一个下降沿 { TIM5CH2_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽TIM5CH2_CAPTURE_VAL=TIM_GetCapture2(TIM5);TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获}else //还未开始,第一次捕获上升沿{TIM5CH2_CAPTURE_STA=0; //清空TIM5CH2_CAPTURE_VAL=0;TIM_SetCounter(TIM5,0);TIM5CH2_CAPTURE_STA|=0X40; //标记捕获到了上升沿TIM_OC2PolarityConfig(TIM5,TIM_ICPolarity_Falling); //CC3P=1 设置为下降沿捕获} } }TIM_ClearITPendingBit(TIM5, TIM_IT_CC2|TIM_IT_Update); //清除中断标志位
}
/***@brief 冒泡排序法,用于均值滤波*/
void bubbleSort(uint16_t *arr, int n) {int i, j, temp;for (i = 0; i < n-1; i++) {for (j = 0; j < n-i-1; j++) {if (arr[j] > arr[j+1]) {temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}
/***@brief 超声波10us脉冲触发函数*/
void SR04_Trig()
{TRIG=1;delay_us(15);TRIG=0;
}
/***@brief 超声波测距函数*@notice 本程序没有利用这个函数测距,用的是steer.c那个中断服务函数进行的测距操作*@retvl temp*0.017:就是测量距离*/
u32 temp=5500; //ECHO引脚高电平时间
uint16_t sum_distance,distance[30],m=0;
int SRO4_Distance()
{static uint8_t i=0;SR04_Trig();if(TIM5CH2_CAPTURE_STA&0X80)//成功捕获到了一次上升沿{temp=TIM5CH2_CAPTURE_STA&0X3F;temp*=65536;//溢出时间总和temp+=TIM5CH2_CAPTURE_VAL;//得到总的高电平时间TIM5CH2_CAPTURE_STA=0; //开启下一次捕获if(temp*0.017>80){temp=4705;}/* 下面这段注释是超声波测距均值滤波程序 */
// distance[i]=(temp*0.017); //统计10次数据
// i++;
// if(i==10) //统计完成开始求平均值
// {
// sum_distance=0;
// i=0;
// bubbleSort(distance,10); //进行排序
// for(m=2;m<8;m++)
// {
// sum_distance+=distance[m];
// }
// }}//return sum_distance/6; //均值滤波后的距离return temp*0.017; //超声波距离返回
}
int length_res[10]; /* 避障用于各个方向上的距离存放 */
extern float A_D; /* 距离的偏差 */
/***@brief 超声波避障函数,此避障函数也可以使用(本程序没有使用这个避障程序)*/
uint16_t z=0;
void BZ_to_control()
{TIM_SetCompare3(TIM3,182); //舵机摆正delay_ms(100);length_res[0] =SRO4_Distance(); //测前方距离放在数组里delay_ms(100);if(length_res[0]>=30.00) //如果前方距离大于30cm 前进{while(1){distance_to_hold(0);if(SRO4_Distance()==20){goto con;}}} con: if(length_res[0]<30.00) //如果前方距离小于30厘米 停车测左右距离{Motion_control(stop);delay_ms(200);steer_left();;//左拐45度length_res[1] =SRO4_Distance(); //把测量结果放进数组delay_ms(200); steer_right();;//右拐45度length_res[4] =SRO4_Distance(); //把测量结果放进数组 delay_ms(200); TIM_SetCompare3(TIM3,182);//舵机摆正delay_ms(10);if(length_res[1]>length_res[4]) //如果左边的距离大于右边的距离{do //舵机摆正{TIM_SetCompare3(TIM3,123);//保持舵机角度delay_ms(10);length_res[0] =SRO4_Distance(); //重复测前方的距离同时右转delay_ms(10); BZ_RIGHT();}while(length_res[0]<30.00); //一直转到前方距离大于30cm }beep_o();if(length_res[1]<=length_res[4]) //如果左边的距离小于右边的距离{do{TIM_SetCompare3(TIM3,234); //保持舵机角度delay_ms(10);length_res[0] =SRO4_Distance(); //重复测前方的距离同时右转delay_ms(10); BZ_LEFT();}while(length_res[0]<30.00); //一直转到前方距离大于30cm}}
}/***@brief 将障碍物状态量化成0-7*@param 障碍物状态,有障碍物=1,无障碍物=0*@retval 障碍物状态0-7*/
uint8_t BZ3_8(uint8_t left,uint8_t mid,uint8_t right)
{static uint8_t status=0; //障碍物状态if(left==0&&mid==0&&right==0){status=0; //左前右均没障碍物}if(left==0&&mid==0&&right==1){status=1; //右边有障碍物,左前没有}if(left==0&&mid==1&&right==0){status=2; //前方有障碍物,左右没有}if(left==0&&mid==1&&right==1){status=3; //右前有障碍物,左边没有}if(left==1&&mid==0&&right==0){status=4; //左边有障碍物,右前没有}if(left==1&&mid==0&&right==1){status=5; //左右均有障碍物,前方没有}if(left==1&&mid==1&&right==0){status=6; //左前有障碍物,右边没有}if(left==1&&mid==1&&right==1){status=7; //三方均有障碍物}return status;
}
/***@brief 检测障碍物状态信息函数并实现雷达扫描避障*/
void status_to_measure()
{static uint8_t left=0,mid=0,right=0,status=0;static int l_d=0,m_d=0,r_d=0;set_speed(130,130);TIM_SetCompare3(TIM3,182);//舵机摆正delay_ms(280);m_d=dis_tim3; //记录前方距离 TIM_SetCompare3(TIM3,250); //舵机左拐角度检测左边障碍物情况delay_ms(280);l_d=dis_tim3; //记录左前方距离 TIM_SetCompare3(TIM3,182);//舵机右拐角度检测右边障碍物情况delay_ms(280);TIM_SetCompare3(TIM3,114);delay_ms(280);r_d=dis_tim3; //记录右前方距离 OLED_ShowNum(32,4,l_d,2,16);OLED_ShowNum(32+24,4,m_d,2,16);OLED_ShowNum(32+24+24,4,r_d,2,16); //将三个距离与25cm比较,如果距离大于25就认定前方没有障碍物,就置零//将三个距离与25cm比较,如果距离小于等于25就认定前方有障碍物,就置1if(m_d>25){mid=0;} if(m_d<=25){mid=1;}if(l_d>25){left=0;} if(l_d<=25){left=1;} if(r_d>25){right=0;}if(r_d<=25){right=1;} status=BZ3_8(left,mid,right); //将三个距离状态转化成数字0-7OLED_ShowNum(32+16,6,left,1,16);OLED_ShowNum(32+8+16,6,mid,1,16);OLED_ShowNum(32+16+16,6,right,1,16); OLED_ShowNum(32,6,status,1,16); /******行驶方向与状态的关系表LMR=000 STATUS=0 前进LMR=001 STATUS=1 左拐LMR=010 STATUS=2 后退LMR=011 STATUS=3 左拐LMR=100 STATUS=4 右拐LMR=101 STATUS=5 后退LMR=110 STATUS=6 右拐LMR=111 STATUS=7 后退*******/if(status==0){beep_o();Motion_control(1); //前进}if(status==1){beep_o();Motion_control(3); //左拐 }if(status==2){beep_o();Motion_control(3); //左拐 }if(status==3){beep_o();Motion_control(3); //左拐 }if(status==4){beep_o();Motion_control(4); //右拐 }if(status==5){beep_o();Motion_control(2); //后退 }if(status==6){beep_o();Motion_control(4); //右拐 }if(status==7){beep_o();Motion_control(2); //后退 }
}
- ultrasonic.h
#ifndef __ULTRASONIC_H
#define __ULTRASONIC_H
/**** 头文件包含区 ****/
#include "sys.h"
#include "delay.h"
/*********************//**** 全局变量区 ****/
extern u8 TIM5CH2_CAPTURE_STA; //输入捕获状态
extern u16 TIM5CH2_CAPTURE_VAL; //输入捕获值
extern u32 temp;//高电平时间
/*******************//**** 函数声明区 ****/
void TIM5_Cap_Init(u16 arr,u16 psc); //超声波输入捕获定时器初始化
void SR04_Trig(void); //超声波触发函数
int SRO4_Distance(void); //超声波测距函数
void BZ_to_control(void); //超声波避障函数
void status_to_measure(void);
uint8_t BZ3_8(uint8_t left,uint8_t mid,uint8_t right);
/*******************/
#define TRIG PBout(1) //TRIG - PB1
#endif
语音遥控
语音模块就是图形化编程,特别简单,就是输入语音指令就通过串口0发送字符给STM32即可
语音模块代码

STM32F103RCT6接收端代码
/***@brief:串口3的中断服务函数,用于语音模块遥控控制小车*@notice:PA9-TXPA10-RX*/
void USART1_IRQHandler(void)
{ char res;if(USART_GetITStatus(USART1,USART_IT_RXNE)!=RESET) { LED=!LED; //进入中断闪烁res= USART_ReceiveData(USART1); if(res=='a') flag=1; //前进else if(res=='b') flag=2; //后退else if(res=='c') flag=3; //左拐else if(res=='d') flag=4; //右拐else if(res=='e') flag=5; //停止else if(res=='f') flag=6; //速度低档else if(res=='g') flag=7; //速度快档else if(res=='h') flag=8; //速度中档else if(res=='i') mode=1; //智能巡线送快递模式else if(res=='k') {mode=2;TIM_SetCompare3(TIM3,182);}//智能避障消毒模式else if(res=='l') {mode=3;Motion_control(stop);} //智能远程遥控模式}
}
蓝牙遥控手柄遥控
蓝牙遥控手柄代码
这个读我的这篇文章可以大致明白蓝牙遥控手柄文章
STM32F103RCT6接收端代码
/***@brief 串口3的中断服务函数,用于蓝牙遥控控制小车*@notice:PA2-TXPA3-RX*/
void USART2_IRQHandler(void) { char res;if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET) { LED=!LED; //进入中断闪烁res= USART_ReceiveData(USART2); if(res=='a') flag=1; //前进else if(res=='b') flag=2; //后退else if(res=='c') flag=3; //左拐else if(res=='d') flag=4; //右拐else if(res=='e') flag=5; //停止else if(res=='f') flag=6; //速度低档else if(res=='g') flag=8; //速度快档else if(res=='h') flag=7; //速度中档else if(res=='i') mode=1; //智能巡线送快递模式else if(res=='k') {mode=2;TIM_SetCompare3(TIM3,182);}//智能避障消毒模式else if(res=='l') {mode=3;Motion_control(stop);} //智能远程遥控模式} }
超声波避障
避障算法就是超声波不断测量前方左前右的距离,然后将三个距离跟25cm比较,哪个方向大于25哪个方向就没障碍物,反之就是有障碍物。,接着根据判断就可以得到8种结果,根据8种结果让小车选择合理的方向运动即可完成避障
/***@brief 将障碍物状态量化成0-7*@param 障碍物状态,有障碍物=1,无障碍物=0*@retval 障碍物状态0-7*/
uint8_t BZ3_8(uint8_t left,uint8_t mid,uint8_t right)
{static uint8_t status=0; //障碍物状态if(left==0&&mid==0&&right==0){status=0; //左前右均没障碍物}if(left==0&&mid==0&&right==1){status=1; //右边有障碍物,左前没有}if(left==0&&mid==1&&right==0){status=2; //前方有障碍物,左右没有}if(left==0&&mid==1&&right==1){status=3; //右前有障碍物,左边没有}if(left==1&&mid==0&&right==0){status=4; //左边有障碍物,右前没有}if(left==1&&mid==0&&right==1){status=5; //左右均有障碍物,前方没有}if(left==1&&mid==1&&right==0){status=6; //左前有障碍物,右边没有}if(left==1&&mid==1&&right==1){status=7; //三方均有障碍物}return status;
}
/***@brief 检测障碍物状态信息函数并实现雷达扫描避障*/
void status_to_measure()
{static uint8_t left=0,mid=0,right=0,status=0;static int l_d=0,m_d=0,r_d=0;set_speed(130,130);TIM_SetCompare3(TIM3,182);//舵机摆正delay_ms(280);m_d=dis_tim3; //记录前方距离 TIM_SetCompare3(TIM3,250); //舵机左拐角度检测左边障碍物情况delay_ms(280);l_d=dis_tim3; //记录左前方距离 TIM_SetCompare3(TIM3,182);//舵机右拐角度检测右边障碍物情况delay_ms(280);TIM_SetCompare3(TIM3,114);delay_ms(280);r_d=dis_tim3; //记录右前方距离 OLED_ShowNum(32,4,l_d,2,16);OLED_ShowNum(32+24,4,m_d,2,16);OLED_ShowNum(32+24+24,4,r_d,2,16); //将三个距离与25cm比较,如果距离大于25就认定前方没有障碍物,就置零//将三个距离与25cm比较,如果距离小于等于25就认定前方有障碍物,就置1if(m_d>25){mid=0;} if(m_d<=25){mid=1;}if(l_d>25){left=0;} if(l_d<=25){left=1;} if(r_d>25){right=0;}if(r_d<=25){right=1;} status=BZ3_8(left,mid,right); //将三个距离状态转化成数字0-7OLED_ShowNum(32+16,6,left,1,16);OLED_ShowNum(32+8+16,6,mid,1,16);OLED_ShowNum(32+16+16,6,right,1,16); OLED_ShowNum(32,6,status,1,16); /******行驶方向与状态的关系表LMR=000 STATUS=0 前进LMR=001 STATUS=1 左拐LMR=010 STATUS=2 后退LMR=011 STATUS=3 左拐LMR=100 STATUS=4 右拐LMR=101 STATUS=5 后退LMR=110 STATUS=6 右拐LMR=111 STATUS=7 后退*******/if(status==0){beep_o();Motion_control(1); //前进}if(status==1){beep_o();Motion_control(3); //左拐 }if(status==2){beep_o();Motion_control(3); //左拐 }if(status==3){beep_o();Motion_control(3); //左拐 }if(status==4){beep_o();Motion_control(4); //右拐 }if(status==5){beep_o();Motion_control(2); //后退 }if(status==6){beep_o();Motion_control(4); //右拐 }if(status==7){beep_o();Motion_control(2); //后退 }
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
