避障小车—51单片机

一、小车底盘组装

根据视频的安装步骤安装

二、 电机模块开发

2.1 L9110s概述

接通VCC,GND 模块电源指示灯亮, 以下资料来源官方,但是不对,根据下节课实际调试

IA1输入高电平,IA1输入低电平,【OA1 OB1】电机正转; 倒退

IA1输入低电平,IA1输入高电平,【OA1 OB1】电机反转; 前进

IA2输入高电平,IA2输入低电平,【OA2 OB2】电机正转; 倒退

IA2输入低电平,IA2输入高电平,【OA2 OB2】电机反转; 前进

2.1.1 与C51单片机解法
2.2 让小车动起来

代码看网盘资料,因为分文件编写,不好贴近来,只放核心部分代码

#include "reg52.h"
#include "intrins.h"
		
		// 左轮
sbit RightCon1A = P3^2;
sbit RightCon1B = P3^3;
		
		// 右轮
sbit LeftCon1A = P3^4;
sbit LeftCon1B = P3^5;

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}


void goForward()  //前进
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goRight()    //  右转弯
{
	LeftCon1A = 0;
	LeftCon1B = 1;
	
	RightCon1A = 0;
	RightCon1B = 0;
}


void goLeft()     //  左转弯
{
	LeftCon1A = 0;
	LeftCon1B = 0;
	
	RightCon1A = 0;
	RightCon1B = 1;
}

void goBack()  // 倒退
{
	LeftCon1A = 1;
	LeftCon1B = 0;
	
	RightCon1A = 1;
	RightCon1B = 0;
}

void main()
{
	while(1){
		goForward();       	 //	前进
		Delay1000ms();
		Delay1000ms();
		goBack();			//  倒退
		Delay1000ms();
		Delay1000ms();
		goLeft();           //  左转弯
		Delay1000ms();
		Delay1000ms();
		goRight();          //  右转弯
		Delay1000ms();
		Delay1000ms();
	}
}
2.2.1 串口控制小车方向
  1. 串口分文件编程进行代码整合------具体过程看课程,主要考验C语言功底和代码调试能力,通过现象来改代码

  2. 接入蓝牙模块,通过蓝牙控制小车,实现6.6.1的课程需求,蓝牙透传太容易了。

  3. 添加点动控制,如果APP支持按下一直发数据,松开就停止发数据(蓝牙调试助手的自定义按键不能实现),就能实现前进按键按下后小车一直往前走的功能

    #include "reg52.h"
    #include "motor.h"
    #include "string.h"
    #include "delay.h"
    sbit D5 = P3^7;
    #define SIZE 12

    sfr AUXR = 0x8E;
    char buffer[SIZE];

    void UartInit(void) //9600bps@11.0592MHz
    {
    AUXR = 0x01;
    SCON = 0x50; //配置串口工作方式1,REN使能接收
    TMOD &= 0x0F;
    TMOD |= 0x20;//定时器1工作方式位8位自动重装

     TH1 = 0xFD;
     TL1 = 0xFD;//9600波特率的初值
     TR1 = 1;//启动定时器
     
     EA = 1;//开启总中断
     ES = 1;//开启串口中断
    

    }

    //M1qian M2 hou M3 zuo M4 you
    void Uart_Handler() interrupt 4
    {
    static int i = 0;//静态变量,被初始化一次
    char tmp;

     if(RI)//中断处理函数中,对于接收中断的响应
     {
     		RI = 0;//清除接收中断标志位
     		tmp = SBUF;
     		if(tmp == 'M'){
     			i = 0;
     		}
     		buffer[i++] = tmp;
     	
     		//灯控指令
     		if(buffer[0] == 'M'){
     			switch(buffer[1]){
     				case '1':
     					goForward();
     					Delay10ms();
     					break;
     				case '2':
     					goBack();
     					Delay10ms();
     					break;
     				case '3':
     					goLeft();
     					Delay10ms();
     					break;
     				case '4':
     					goRight();
     					Delay10ms();
     					break;
     				default:
     					stop();
     					break;
     			}
     		}
     	
     		if(i == 12) {
     			memset(buffer, '\0', SIZE);
     			i = 0;
     		}
     }
    

    }

2.3 如何进行小车PWM调速

原理: 全速前进是LeftCon1A = 0; LeftCon1B = 1;完全停止是LeftCon1A = 0;LeftCon1B = 0;那么单位时间内,比如20ms, 有15ms是全速前进,5ms是完全停止, 速度就会比5ms全速前进,15ms完全停止获得的功率多,相应的速度更快!

开发:借用PWM的舵机控制代码

#include "motor.h"
#include "reg52.h"

char speed;
char cnt = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD = 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void Time0Handler() interrupt 1
{
	cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cnt < speed){
		//前进
		goForward();
	}else{
		//停止
		stop();
	}
	
	if(cnt == 40){//爆表40次,经过了20ms
		cnt = 0;  //当40次表示20ms,重新让cnt从0开始,计算下一次的20ms
	}
		
}

extern char speed;

void main()
{
	Time0Init();
	//UartInit();
	
	while(1){
		speed = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
		Delay1000ms();
		Delay1000ms();
		speed = 20;
		Delay1000ms();
		Delay1000ms();
		speed = 40;
		Delay1000ms();
		Delay1000ms();
	}
}
2.3.1 PWM方式实现小车转向

原理: 左轮定时器0调速,右轮定时器1调速,那么左转就是右轮速度大于左轮!

开发:有手就行

#include "motor.h"
#include "reg52.h"

char speedLeft;
char cntLeft = 0;

char speedRight;
char cntRight = 0;

void Time1Init()
{
	//1. 配置定时器1工作模式位16位计时
	TMOD &= 0x0F;
	TMOD |= 0x1 << 4;
	//2. 给初值,定一个0.5出来
	TL1=0x33;
	TH1=0xFE;
	//3. 开始计时
	TR1 = 1;
	TF1 = 0;
	//4. 打开定时器1中断
	ET1 = 1;
	//5. 打开总中断EA
	EA = 1;
}


void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD = 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void Time1Handler() interrupt 3
{
	cntRight++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL1=0x33;
	TH1=0xFE;
	
	//控制PWM波
	if(cntRight < speedRight){
		//右前进
		goForwardRight();
	}else{
		//停止
		stopRight();
	}
	
	if(cntRight == 40){//爆表40次,经过了20ms
		cntRight = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
	}
		
}

void Time0Handler() interrupt 1
{
	cntLeft++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cntLeft < speedLeft){
		//左前进
		goForwardLeft();
	}else{
		//停止
		stopLeft();
	}
	
	if(cntLeft == 40){//爆表40次,经过了20ms
		cntLeft = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
	}
		
}



extern char speedLeft;
extern char speedRight;

void main()
{
	Time0Init();
	Time1Init();
	//UartInit();
	
	while(1){
		speedLeft = 10;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
		speedRight = 40;
		Delay1000ms();
		Delay1000ms();
	
		speedLeft = 40;
		speedRight = 10;
		Delay1000ms();
		Delay1000ms();
	}
}
2.4 循迹小车
2.4.1 循迹模块使用

TCRT5000传感器的红外发射二极管不断发射红外线

当发射出的红外线没有被反射回来或被反射回来但强度不够大时,

红外接收管一直处于关断状态,此时模块的输出端为高电平 ,指示二极管一直处于熄灭状态

被检测物体出现在检测范围内时,红外线被反射回来且强度足够大,红外接收管饱和,

此时模块的输出端为低电平 ,指示二极管被点亮

总结就是一句话,没反射回来,D0输出高电平,灭灯

  • 接线方式
    • VCC:接电源正极(3-5V)
    • GND:接电源负极
    • DO:TTL开关信号输出0、1
    • AO:模拟信号输出(不同距离输出不同的电压,此脚一般可以不接)

6.3.2 循迹小车原理

由于黑色具有较强的吸收能力,当循迹模块发射的红外线照射到黑线时,红外线将会被黑线吸收,导致

循迹模块上光敏三极管处于关闭状态,此时模块上一个LED熄灭。在没有检测到黑线时,模块上两个LED

常亮

总结就是一句话,有感应到黑线,D0输出高电平,灭灯

循迹模块安装在小车车头两侧下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走

上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转

  • 代码实现

    #include "motor.h"
    #include "delay.h"
    #include "reg52.h"

    sbit leftSensor = P2^7;
    sbit rightSensor = P2^6;

    void main()
    {
    //下方小车两个模块都能反射回来红外,输出低电平,灯亮,直走

      //上方小车左模块遇到黑线,红外被吸收,左模块输出高电平,右模块输出低电平,左转,反之右转
      while(1){
      	if(leftSensor == 0 && rightSensor == 0){
      		goForward();  // q前进
      	}
      	if(leftSensor == 1 && rightSensor == 0){
      		goLeft();          // 左转
      	}
      	
      	if(leftSensor == 0 && rightSensor == 1){
      		goRight(); 			// 右转
      	}
      	
      	if(leftSensor == 1 && rightSensor == 1){
      		//停
      		stop();           
      	}
      }
    

    }

修改BUG

防止小车跑车赛道

#include "motor.h"
#include "delay.h"
#include "uart.h"
#include "time.h"
#include "reg52.h"
extern char speedLeft;
extern char speedRight;


sbit leftSensor = P2^7;
sbit rightSensor = P2^6;

void main()
{
	Time0Init();    //  中断器
	Time1Init();
	//UartInit();
	
	while(1){
		
		if(leftSensor == 0 && rightSensor == 0){
				speedLeft = 32;
				speedRight = 40;
		}
		if(leftSensor == 1 && rightSensor == 0){
				speedLeft = 12;//10份单位时间全速运行,30份停止,所以慢,20ms是40份的500us
				speedRight = 40;
		}
		
		if(leftSensor == 0 && rightSensor == 1){
				speedLeft = 32;
				speedRight = 20;
		}
		
		if(leftSensor == 1 && rightSensor == 1){
			//停
				speedLeft = 0;
				speedRight = 0;
		}
	}
}
2.5 跟随小车/壁障小车
2.5.1 红外壁障模块分析

原理和寻线是一样的,寻线红外观朝下,跟随朝前

6.4.2 跟随小车的原理

左边跟随模块能返回红外,输出低电平,右边不能返回,输出高电平,说明物体在左边,需要左转

右边跟随模块能返回红外,输出低电平,左边不能返回,输出高电平,说明物体在右边,需要右转

6.4.3 跟随小车开发和调试

有手就行,哈哈

  • 代码实现

    #include "motor.h"
    #include "delay.h"
    #include "reg52.h"

    //sbit leftSensor = P2^7;
    //sbit rightSensor = P2^6;

    sbit leftSensor = P2^5;
    sbit rightSensor = P2^4;

    void main()
    {

      while(1){
      	if(leftSensor == 0 && rightSensor == 0){
      		goForward();
      	}
      	if(leftSensor == 1 && rightSensor == 0){
      		goRight();
      	}
      	
      	if(leftSensor == 0 && rightSensor == 1){
      		
      		goLeft();
      	}
      	
      	if(leftSensor == 1 && rightSensor == 1){
      		//停
      		stop();
      	}
      }
    

    }

6.4.4 超声波避障小车
1. 超声波代码模块

超声波测距模块是用来测量距离的一种产品,通过发送和收超声波,利用时间差和声音传播速度, 计算出模块到前方障碍物的距离。

    • 怎么让它发送波

Trig ,给Trig端口至少10us的高电平

    • 怎么知道它开始发了

Echo信号,由低电平跳转到高电平,表示开始发送波

    • 怎么知道接收了返回波

Echo,由高电平跳转回低电平,表示波回来了

    • 怎么算时间

Echo引脚维持高电平的时间!

波发出去的那一下,开始启动定时器

波回来的拿一下,我们开始停止定时器,计算出中间经过多少时间

    • 怎么算距离

距离 = 速度 (340m/s)* 时间/2

  • 超声波的时序图
  • 代码实现

    #include "reg52.h"
    #include "delay.h"

    sbit Trig = P1^3;
    sbit Echo = P1^2;

    void Time1Init()
    {
    TMOD &= 0x0F; //设置定时器模式
    TMOD |= 0x10;
    TH1 = 0;
    TL1 = 0;
    //设置定时器0工作模式1,初始值设定0开始数数,不着急启动定时器
    }

    void startHC()
    {
    Trig = 0;
    Trig = 1;
    Delay10us();
    Trig = 0;
    }

    double get_distance()
    {
    double time;
    //定时器数据清零,以便下一次测距
    TH1 = 0;
    TL1 = 0;
    //1. Trig ,给Trig端口至少10us的高电平
    startHC();
    //2. echo由低电平跳转到高电平,表示开始发送波
    while(Echo == 0);
    //波发出去的那一下,开始启动定时器
    TR1 = 1;
    //3. 由高电平跳转回低电平,表示波回来了
    while(Echo == 1);
    //波回来的那一下,我们开始停止定时器
    TR1 = 0;
    //4. 计算出中间经过多少时间
    time = (TH1 * 256 + TL1)1.085;//us为单位
    //5. 距离 = 速度 (340m/s)
    时间/2
    return (time * 0.017);
    }

2. sg90舵机代码模块
  • 怎么控制舵机

向黄色信号线"灌入"PWM信号。

PWM波的频率不能太高,大约50HZ,即周期=1/频率=1/50=0.02s,20ms左右

数据:

0.5ms-------------0度; 2.5% 对应函数中占空比为250

1.0ms------------45度; 5.0% 对应函数中占空比为500

1.5ms------------90度; 7.5% 对应函数中占空比为750

2.0ms-----------135度; 10.0% 对应函数中占空比为1000

2.5ms-----------180度; 12.5% 对应函数中占空比为1250

定时器需要定时20ms, 关心的单位0.5ms, 40个的0.5ms,初值0.5m cnt++

1s = 10ms * 100

20ms = 0.5ms * 40

  • 编程实现
#include "reg52.h"
#include "delay.h"

sbit sg90_con = P1^1;

int jd;
int cnt = 0;

void Time0Init()
{
	//1. 配置定时器0工作模式位16位计时
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;
	//2. 给初值,定一个0.5出来
	TL0=0x33;
	TH0=0xFE;
	//3. 开始计时
	TR0 = 1;
	TF0 = 0;
	//4. 打开定时器0中断
	ET0 = 1;
	//5. 打开总中断EA
	EA = 1;
}

void sgMiddle()
{
	//中间位置
	jd = 3; //90度 1.5ms高电平
	cnt = 0;
}

void sgLeft()
{
	//左边位置
	jd = 5; //135度 1.5ms高电平
	cnt = 0;
}

void sgRight()
{
	//右边位置
	jd = 1; //0度
	cnt = 0;
}


void Time0Handler() interrupt 1
{
	cnt++;  //统计爆表的次数. cnt=1的时候,报表了1
	//重新给初值
	TL0=0x33;
	TH0=0xFE;
	
	//控制PWM波
	if(cnt < jd){
		sg90_con = 1;
	}else{
		sg90_con = 0;
	}
	
	if(cnt == 40){//爆表40次,经过了20ms
		cnt = 0;  //当100次表示1s,重新让cnt从0开始,计算下一次的1s
		sg90_con = 1;
	}
		
}
3. mian函数组合调用
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"

void main()
{
	Time0Init();
	Time1Init();
	//舵机的初始位置

	sgMiddle();
	Delay300ms();
	Delay300ms();
	
	while(1){
	
		sgLeft();    //  左摇头
		Delay440ms();
		
		sgMiddle();   // 回正
		Delay430ms();
		
		sgRight();   //  右摇头
		Delay420ms();
		
		sgMiddle();  // 回正
		Delay450ms();
		
	}
}
4. 测距摇头实现
#include "reg52.h"
#include "hc04.h"
#include "delay.h"
#include "sg90.h"

#define MIDDLE 0
#define LEFT 1
#define RIGHT 2

void main()
{
	char dir;   // 方向变量
	
	double disMiddle;  // 中间距离
	double disLeft;		// 左间距离
	double disRight;	// 右间距离
	
	Time0Init();
	Time1Init();
	//舵机的初始位置

	sgMiddle();
	Delay300ms();
	Delay300ms();
	dir = MIDDLE;
	
	while(1){
		
		if(dir != MIDDLE){    // 保持测完左右间距之后,回到中间值
			sgMiddle();
			dir = MIDDLE;
			Delay300ms();
		}
		disMiddle = get_distance();     // 超声波测距
		
		if(disMiddle > 35){  // 如果中间的距离大于了35cm;就前进
			//前进
		}
		else								// 如果前方的距离小于了35cm;就停止
		{ 		
			//停止
			//测左边距离
			sgLeft();
			
			Delay300ms();
			disLeft = get_distance();
			
			sgMiddle();    //  测中间距离
			Delay300ms();
			
			sgRight();   //  测右间距离
			dir = RIGHT;
			Delay300ms();
			disRight = get_distance();
			
		}
		
	}
}

main函数先调用两个定时器(超声波与sg90舵机定时器);22行至25行代码。将超声波模块保持正前方向之后,进入while循环语句,先进入第一个if语句判断超声波方向,如果超声波方向不在正中间,将其回正之后,将超声波测量的距离,赋值给第二个if语句,进去判断,如何距离大于35cm,小车前进,如果距离不大于35,超声波通过sg90舵机,进行左右测距,测出35cm内无障碍之后,进行前进。

  • 下面是加上电机驱动的代码,让小车跑起来

    #include "reg52.h" // 包含寄存器定义头文件
    #include "hc04.h" // 包含超声波传感器(HC-SR04)头文件
    #include "delay.h" // 包含延时函数头文件
    #include "sg90.h" // 包含舵机(SG90)头文件
    #include "motor.h" // 包含电机控制头文件

    #define MIDDLE 0 // 定义舵机的中间位置为 0
    #define LEFT 1 // 定义舵机的左边位置为 1
    #define RIGHT 2 // 定义舵机的右边位置为 2

    void main()
    {
    char dir; // 定义舵机的当前方向变量

      double disMiddle;  // 定义前方的距离变量
      double disLeft;    // 定义左方的距离变量
      double disRight;   // 定义右方的距离变量
      
      Time0Init();  // 初始化定时器0
      Time1Init();  // 初始化定时器1
      
      // 舵机的初始位置设为中间位置
      sgMiddle();
      Delay300ms();  // 延时300毫秒
      Delay300ms();  // 再延时300毫秒
      dir = MIDDLE;  // 将舵机方向变量设置为中间位置
      
      while(1){  // 无限循环,控制主逻辑
          
          if(dir != MIDDLE){  // 如果当前方向不是中间
              sgMiddle();  // 将舵机调整到中间位置
              dir = MIDDLE;  // 更新方向变量为中间
              Delay300ms();  // 延时300毫秒
          }
          disMiddle = get_distance();  // 获取前方距离
          
          if(disMiddle > 35){  // 如果前方距离大于35
              goForward();  // 向前移动
          }else if(disMiddle < 10){  // 如果前方距离小于10
              goBack();  // 向后移动
              Delay150ms();  // 延时150毫秒
              stop();  // 停止移动
          }else
          {
              // 停止移动
              stop();
              // 测量左边的距离
              sgLeft();
              Delay300ms();  // 延时300毫秒
              disLeft = get_distance();  // 获取左边距离
              
              sgMiddle();  // 将舵机回到中间位置
              Delay300ms();  // 延时300毫秒
              
              sgRight();  // 将舵机转向右边
              dir = RIGHT;  // 更新方向变量为右边
              Delay300ms();  // 延时300毫秒
              disRight = get_distance();  // 获取右边距离
              
              if(disLeft < disRight){  // 如果左边距离小于右边距离
                  goRight();  // 向右移动
                  Delay150ms();  // 延时150毫秒
                  stop();  // 停止移动
              }
              if(disRight < disLeft){  // 如果右边距离小于左边距离
                  goLeft();  // 向左移动
                  Delay150ms();  // 延时150毫秒
                  stop();  // 停止移动
              }
          }
          
      }
    

    }

大家在实际操作中,可根据现场实际情况进行对代码测距的修改。

关于相关代码,si信我即可。

相关推荐
努力看懂帖子7 分钟前
关于STM32项目面试题02:ADC与DAC篇(输入部分NTC、AV:0-5V、AI:4-20mA和DAC的两个引脚)
嵌入式硬件
嵌入式大圣1 小时前
STM32 单片机最小系统全解析
stm32·单片机·嵌入式硬件
LN花开富贵6 小时前
stm32g431rbt6芯片中VREF+是什么?在电路中怎么设计?
笔记·stm32·单片机·嵌入式硬件·学习
qq21084629536 小时前
【stm32笔记】使用rtt-studio与stm32CubeMx联合创建项目
笔记·stm32·嵌入式硬件
CV金科6 小时前
蓝桥杯—STM32G431RBT6按键的多方式使用(包含软件消抖方法精讲)从原理层面到实际应用(一)
stm32·单片机·嵌入式硬件·蓝桥杯
2021.096 小时前
五、CAN总线
嵌入式硬件
luckyluckypolar6 小时前
STM32——输入捕获
stm32·单片机·嵌入式硬件·物联网
hong1616886 小时前
嵌入式硬件基础知识
嵌入式硬件
hai405876 小时前
单片机(Microcontroller)原理及应用
单片机·嵌入式硬件
jun7788957 小时前
嵌入式硬件基础知识
嵌入式硬件