避障小车—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信我即可。

相关推荐
国科安芯20 小时前
ASP4644芯片低功耗设计思路解析
网络·单片机·嵌入式硬件·安全
充哥单片机设计20 小时前
【STM32项目开源】基于STM32的智能厨房火灾燃气监控
stm32·单片机·嵌入式硬件
CiLerLinux1 天前
第四十九章 ESP32S3 WiFi 路由实验
网络·人工智能·单片机·嵌入式硬件
时光の尘1 天前
【PCB电路设计】常见元器件简介(电阻、电容、电感、二极管、三极管以及场效应管)
单片机·嵌入式硬件·pcb·二极管·电感·三极管·场效应管
Lu Zelin1 天前
单片机为什么不能跑Linux
linux·单片机·嵌入式硬件
宁静致远20211 天前
stm32 freertos下基于hal库的模拟I2C驱动实现
stm32·嵌入式硬件·freertos
Wave8451 天前
STM32--智能小车
stm32·单片机·嵌入式硬件
wdfk_prog1 天前
[Linux]学习笔记系列 -- lib/timerqueue.c Timer Queue Management 高精度定时器的有序数据结构
linux·c语言·数据结构·笔记·单片机·学习·安全
充哥单片机设计2 天前
【STM32项目开源】基于STM32的智能家居环境(空气质量)检测系统
stm32·单片机·嵌入式硬件
夜月yeyue2 天前
ART 加速器、流水线与指令预测的关系详解
linux·服务器·c语言·单片机·嵌入式硬件·性能优化·嵌入式高阶技巧