机器人团队第四周学习总结

智能小车循迹模块

一. PWM电机调速原理
控制电机的时候,电源并非连续地向电机供电,而是在一个特定的频率下以方波脉冲的形式提供电能。电机实际上是一个大电感,它有阻碍输入电流和电压突变的能力,因此脉冲输入信号被平均分配到作用时间上,这样,改变在始能端EN1 和EN2 上输入方波的占空比就能改变加在电机两端的电压大小,从而改变了转速。
方法:
(1)软件:设置不同的延时时间得到不同的占空比(本实验使用),采用定时器中断来产生PWM波形
(2)硬件:用具有硬件PWM功能的芯片
在这里插入图片描述
注:占空比和电机转速并不是简单的线性关系。(电机调速后的能量,很大一部分会损耗在三极管上发热)

二. 循迹实现原理
平面为黑色:红外光反射量很少,传感器输出高电平1(达不到传感器动作的水平),L1(L2)信号灯不亮
平面为白色:红外光反射量很多,传感器输出低电平0,L1(L2)信号灯亮

各种循迹方法:

  • 红外对管循迹法:利用黑、白色对红外线的吸收
    作用不同;
  • 摄像头循迹法:利用摄像头读取赛道信息,分为
    模拟和数字
  • 激光管循迹法:和红外循迹法原理相似,但是检
    测距离远;
  • 电磁循迹法

三. 循迹硬件调试
W1,W2调节----顺时针灵敏度增加,逆时针灵敏度降低
灵敏度越高越不易检测到黑线,因为灵敏度太高黑色反射的红外光都能被传感器识别

四. 程序

#include<AT89X52.H> //包含51单片机头文件,内部有各种寄存器定义
#include<HJ-4WD_PWM.H> //包含智能小车驱动IO口定义等函数
    
//主函数
void main(void)
{
    
    	
	TMOD=0X01; 
	TH0= 0XFc;
	TL0= 0X18;
	TR0= 1;
	ET0= 1;
	EA = 1;	//1ms定时

	while(1) //无限循环
	{
    
     
		if(Left_1_led==0&&Right_1_led==0)
			run(); //调用前进函数
		else
		{
    
    			  
			if(Left_1_led==1&&Right_1_led==0) //左边检测到黑线
			{
    
    
				leftrun(); //调用小车左转函数
			}	   
			if(Right_1_led==1&&Left_1_led==0) //右边检测到黑线
			{
    
    	  
				rightrun();	//调用小车右转函数
			}
		}	 
	}
}

HJ-4WD_PWM.H文件:

#ifndef _LED_H_
#define _LED_H_

//定义小车驱动模块输入IO口 
sbit IN1=P1^2;
sbit IN2=P1^3;
sbit IN3=P1^6;
sbit IN4=P1^7;
sbit EN1=P1^4;
sbit EN2=P1^5;

#define Left_1_led        P3_3	 //左传感器       
#define Right_1_led       P3_2	 //右传感器    
 
#define Left_moto_pwm	  P1_5	 //PWM信号端
#define Right_moto_pwm	  P1_4	 //PWM信号端


#define Left_moto_go      {P1_2=0,P1_3=1;}  //左电机向前走
#define Left_moto_back    {P1_2=1,P1_3=0;} 	//左边电机向后转
#define Left_moto_Stop    {P1_5=0;}  //左边电机停转                     
#define Right_moto_go     {P1_6=1,P1_7=0;}	//右边电机向前走
#define Right_moto_back   {P1_6=0,P1_7=1;}	//右边电机向后走
#define Right_moto_Stop   {P1_4=0;}	//右边电机停转  

unsigned char pwm_val_left  =0;//变量定义
unsigned char push_val_left =0;// 左电机占空比N/20

unsigned char pwm_val_right =0;
unsigned char push_val_right=0;// 右电机占空比N/20

bit Right_moto_stop=1;
bit Left_moto_stop =1;
unsigned  int  time=0;
/************************************************************/	
//延时函数	
void delay(unsigned int k)
{
    
        
	unsigned int x,y;
	for(x=0;x<k;x++) 
	for(y=0;y<2000;y++);
}
/************************************************************/
//前进
void  run(void)
{
    
    
    push_val_left=10; //速度调节变量 0-20(0最小,20最大)
	push_val_right=10;
	 
	Left_moto_go ; //左电机往前走
	Right_moto_go ; //右电机往前走
}
//左转
void  leftrun(void)
{
    
    	 
    push_val_left=10;
	push_val_right=10;

	Right_moto_go ; //右电机往前走
    Left_moto_back ; //左电机后走
}
//右转
void  rightrun(void)
{
    
     
	push_val_left=10;
	push_val_right=10;

    Left_moto_go ; //左电机往前走
	Right_moto_back ; //右电机往后走	
}
/************************************************************/
/*PWM调制电机转速,调节push_val_left的值改变电机转速,占空比(设置前10ms工作,后10ms不工作)*/
//左电机调速                                        
void pwm_out_left_moto(void)
{
    
      
   if(Left_moto_stop)
   {
    
    
		if(pwm_val_left<=push_val_left) //计数器的值小于等于10
  		{
    
    
	 		Left_moto_pwm=1; //输出高电平1
		}
		else 
    	{
    
    
     		Left_moto_pwm=0; //输出低电平0
		}
		if(pwm_val_left>=20) //计数超过20
	       pwm_val_left=0; //计数器重置为0
   }
   else    
   {
    
    
		Left_moto_pwm=0;
   }
}
//右电机调速
void pwm_out_right_moto(void)
{
    
     
  if(Right_moto_stop)
  {
    
     
  	if(pwm_val_right<=push_val_right)
    {
    
    
    	Right_moto_pwm=1; 
	}
	else 
    {
    
    
	    Right_moto_pwm=0;
	}
	if(pwm_val_right>=20)
		pwm_val_right=0;
  }
  else    
  {
    
    
   Right_moto_pwm=0;
  }
}
/***************************************************/
/*TIMER0中断服务子函数产生PWM信号*/
void timer0()interrupt 1   using 2
{
    
    
     TH0=0XFc;	  //1Ms定时
	 TL0=0X18;
	 time++;
	 pwm_val_left++;
	 pwm_val_right++;
	 pwm_out_left_moto();
	 pwm_out_right_moto();
 }	
/*********************************************************************/	
#endif

超声波测距模块

一. 有关HC-SR04

  • HC-SR04超声波模块接口定义:
    Vcc、 Trig(控制端)、 Echo(接收端)、 Gnd
  • HC-SR04产品特点:
    1、典型工作用电压:5V。
    2、超小静态工作电流:小于2mA。
    3、感应角度:不大于15 度。
    4、探测距离:2cm-400cm
    5、高精度:可达0.3cm。
    6、盲区(2cm)超近。

二. 超声波测距原理
控制口发一个10us以上的高电平,就可以在接收口等待高电平输出,一有输出就可以开定时器计时,当此口变为低电平时就可以读定时器的值,此时就为此次测距的时间,方可算出距离.如此不断的周期测,就可以达到你移动测量的值了。

测距公式:L=C×T (L :测量距离;C :超声波在空气中的传播速度344m/s (20℃室温);T :发射到接收时间数值的一半)

注:超声波的传播速度受空气的密度所影响,空气的密度越高则超声波的传播速度就越快,而空气的密度又与温度有着密切的关系,近似公式为:C=C0+0.607×T℃ 式中:C0为零度时的声波速度332m/s;T 为实际温度(℃)。
对于超声波测距精度要求达到1mm 时,就必须把超声波传播的环境温度考虑进去。

三. 模块工作原理:
1.采用 IO 触发测距,给至少10us 的高电平信号;
2.模块自动发送8 个40khz 的方波,自动检测是否有信号返回;
3.有信号返回,通过IO 输出一高电平,高电平持续的时间就是超声波从发射到返回的时间
4.测试距离=(高电平时间*声速(340M/S))/2;在这里插入图片描述

四. 程序

#include <AT89x51.H> //器件配置文件
#include <intrins.h>

#define  RX  P2_0 //超声波模块发射端口
#define  TX  P2_1 //超声波模块接收端口

#define LCM_RW  P1_1 //定义LCD引脚
#define LCM_RS  P1_0
#define LCM_E   P2_5

#define LCM_Data  P0
#define Busy    0x80 //用于检测LCM状态字中的Busy标识

sbit DU = P2^6;
sbit WE = P2^7;

void cmg88()//关数码管
{
    
    
DU=1;  
P0=0X00;
DU=0;
}

void LCMInit(void);//LCD初始化函数
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);//LCD显示一个字符函数
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);//LCD显示一个字符串函数
void Delay5Ms(void);//延时5毫秒函数
void Delay400Ms(void);//延时400毫秒函数
void Decode(unsigned char ScanCode);
void WriteDataLCM(unsigned char WDLCM);//LCD1602写数据函数
void WriteCommandLCM(unsigned char WCLCM,BuysC);//LCD写命令函数

//unsigned char ReadDataLCM(void);
unsigned char ReadStatusLCM(void);
unsigned char code Range[] ="==Range Finder==";//LCD1602显示格式
unsigned char code ASCII[13] = "0123456789.-M";
unsigned char code table[]="Distance:000.0cm";
unsigned char code table1[]="!!! Out of range";

//static unsigned char DisNum = 0; //显示用指针				  
unsigned int  time=0;
unsigned long S=0;
bit  flag =0;
unsigned char disbuff[4]={
    
     0,0,0,0,};

//写数据
void WriteDataLCM(unsigned char WDLCM) 
{
    
    
	ReadStatusLCM(); //检测忙
	LCM_Data = WDLCM;
	LCM_RS = 1;
	LCM_RW = 0;
	LCM_E = 0; //若晶振速度太高可以在这后加小的延时
	LCM_E = 0; //延时
	LCM_E = 1;
}

//写指令
void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测
{
    
    
	if (BuysC) ReadStatusLCM(); //根据需要检测忙
	LCM_Data = WCLCM;
	LCM_RS = 0;
	LCM_RW = 0;	
	LCM_E = 0;
	LCM_E = 0;
	LCM_E = 1;	
}

//读数据
/*unsigned char ReadDataLCM(void)
{
	LCM_RS = 1; 
	LCM_RW = 1;
	LCM_E = 0;
	LCM_E = 0;
	LCM_E = 1;
	return(LCM_Data);
}*/

//读状态
unsigned char ReadStatusLCM(void)
{
    
    
	LCM_Data = 0xFF; 
	LCM_RS = 0;
	LCM_RW = 1;
	LCM_E = 0;
	LCM_E = 0;
	LCM_E = 1;
	while (LCM_Data & Busy); //检测忙信号
	return(LCM_Data);
}

void LCMInit(void) //LCM初始化
{
    
    
	LCM_Data = 0;
	WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号
	Delay5Ms(); 
	WriteCommandLCM(0x38,0);
	Delay5Ms(); 
	WriteCommandLCM(0x38,0);
	Delay5Ms(); 

	WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号
	WriteCommandLCM(0x08,1); //关闭显示
	WriteCommandLCM(0x01,1); //显示清屏
	WriteCommandLCM(0x06,1); // 显示光标移动设置
	WriteCommandLCM(0x0c,1); // 显示开及光标设置
}

//按指定位置显示一个字符
void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData)
{
    
    
	Y &= 0x1;
	X &= 0xF; //限制X不能大于15,Y不能大于1
	if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;
	X |= 0x80; //算出指令码
	WriteCommandLCM(X, 1); //发命令字
	WriteDataLCM(DData); //发数据
}

//按指定位置显示一串字符
void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData)
{
    
    
	unsigned char ListLength;
    ListLength = 0;
	Y &= 0x1;
	X &= 0xF; //限制X不能大于15,Y不能大于1
	while (DData[ListLength]>0x19) //若到达字串尾则退出
	{
    
    
		if (X <= 0xF) //X坐标应小于0xF
		{
    
    
			DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符
			ListLength++;
			X++;
		}
	}
}

//5ms延时
void Delay5Ms(void)
{
    
    
	unsigned int TempCyc = 5552;
	while(TempCyc--);
}

//400ms延时
void Delay400Ms(void)
{
    
    
	unsigned char TempCycA = 5;
	unsigned int TempCycB;
	while(TempCycA--)
	{
    
    
		TempCycB=7269;
		while(TempCycB--);
	};
}
/********************************************************/
void Conut(void)			//超声波距离计算函数
{
    
    
	time=TH0*256+TL0;
	TH0=0;
	TL0=0;
	
	S=(time*1.7)/10+10;     //算出来是mm
	if((S>=7000)||flag==1) //超出测量范围
	{
    
    	 
	  flag=0;
      DisplayListChar(0, 1, table1);
	}
	else
	{
    
    
        disbuff[0]=S%10;
		disbuff[1]=S/10%10;
		disbuff[2]=S/100%10;
		disbuff[3]=S/1000;
		DisplayListChar(0, 1, table);
		DisplayOneChar(9, 1, ASCII[disbuff[3]]);
		DisplayOneChar(10, 1, ASCII[disbuff[2]]);	
		DisplayOneChar(11, 1, ASCII[disbuff[1]]);
		DisplayOneChar(12, 1, ASCII[10]);
		DisplayOneChar(13, 1, ASCII[disbuff[0]]);
	 }
}
/********************************************************/
void zd0() interrupt 1 		 //T0中断用来计数器溢出,超过测距范围
{
    
    
    flag=1;			 //中断溢出标志
	RX=0;
}
/********************************************************/
void  StartModule() 		         //启动模块
{
    
    
	TX=1;			                     //启动一次模块
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_(); 
    _nop_(); 
	_nop_();
	_nop_(); 
	_nop_(); 
	_nop_(); 
	_nop_();
	TX=0;
}
/********************************************************/ 
/*void delayms(unsigned int ms)
{
	unsigned char i=100,j;
	for(;ms;ms--)
	{
		while(--i)
		{
			j=10;
			while(--j);
		}
	}
}*/
void Timer_Count(void)			   //超声波高电平脉冲宽度计算函数
{
    
    
	TR0=1;			    //开启计数
	while(RX);			//当RX为1计数并等待
	TR0=0;				//关闭计数
    Conut();			//计算
}
/*********************************************************/
void main(void)
{
    
    
    unsigned int valA;
	Delay400Ms(); //启动等待,等LCM讲入工作状态
	cmg88();//关数码管
	LCMInit(); //LCM初始化
	Delay5Ms(); //延时片刻
	DisplayListChar(0, 0, Range);
	DisplayListChar(0, 1, table);
	//ReadDataLCM();//测试用句无意义
    TMOD=0x01;//设T0为方式1,GATE=1;
    EA=1;		    
    TH0=0;
    TL0=0;          
    ET0=1;             
				  		
 	while(1)
	{
    
    
		RX=1;
	    StartModule();		           //启动模块
        for(valA=7510;valA>0;valA--)   //60ms
	    {
    
    
	       if(RX==1)
		   {
    
    
           	Timer_Count();		 //超声波高电平脉冲宽度计算函数
		   }
    	}
	}
}

猜你喜欢

转载自blog.csdn.net/qq_51461824/article/details/112674800