从零开始制作51单片机控制的智能小车

从零开始制作51单片机控制的智能小车

出于兴趣自学了电路,单片机相关知识,打手动手做一个智能小车。

参考以下文章及视频从零搭建51单片机智能小车,并结合自己的搭建过程补充一些不够详细的地方(先阅读下面的链接文章后有助于理解)

主要参考的文章(设计思路原理和代码)https://blog.csdn.net/qq_44339029/article/details/106312542

参考B站视频(设计思路不一样但可以参考电机接线)https://www.bilibili.com/video/BV16S4y1C7ad/?spm_id_from=333.337.search-card.all.click&vd_source=780715f28ad320f4653b3e1530a4eb72

一、硬件选择

1. 底盘和电机

​ 4WD智能小车底盘,附带4个直流减速电机,电机接线需要自己焊接(我是直接缠到电机上的)

【淘宝】https://m.tb.cn/h.5Oj2FQ0?tk=cDE9Wf07FqZ CZ3457 「智能小车套件51单片机/STM32/UNO开发板寻迹避障遥控机器人底盘」
点击链接直接打开 或者 淘宝搜索直接打开

2. 电机驱动模块

​ L298N电机驱动模块

【淘宝】https://m.tb.cn/h.5lw3OPK?tk=cCgiWf07RfP CZ3457 「【优信电子】L298N电机驱动板模块 步进电机 智能车 机器人」
点击链接直接打开 或者 淘宝搜索直接打开

3. 单片机最小系统

STC89C52R2 (要买芯片,带有单片机STC下载器)

【淘宝】https://m.tb.cn/h.5lwe0L2?tk=SCgzWf07qWj CZ3457 「51单片机最小系统开发板STC核心板学习板40P锁紧座DIY实验板模块」
点击链接直接打开 或者 淘宝搜索直接打开

4. 电源

​ 后面发现9v的电池驱动能力不够,我又自己串联了2个1.5V的南孚电池

【淘宝】https://m.tb.cn/h.5lw40DY?tk=ClP1Wf0iRyx CZ0001 「倍量9v电池可充电usb锂电池万用表话筒方块6f22九伏9号智能大功率(@ ~)」
点击链接直接打开 或者 淘宝搜索直接打开

​ 我这个电池盒子并不适配,大家可以换别的

【淘宝】https://m.tb.cn/h.5mf0uOt?tk=SWSwWf0RaN1 CZ3457 「电池盒五5号7号18650带盖锂电池座子免焊接充电串联2节4/8节9V12V」
点击链接直接打开 或者 淘宝搜索直接打开

5. 杜邦线

【淘宝】https://m.tb.cn/h.5mfbYm4?tk=b0eEWf0RmdN CZ0001 「杜邦线母对母公对母公对公40P彩色排线连接线公母线10/30/20/40CM」
点击链接直接打开 或者 淘宝搜索直接打开

​ 多个杜邦线接在一块,先将杜邦线的接口拔掉,然后用小刀把铜线弄出来,再缠在一起,包上绝缘胶带,将一条线接在单片机的IO上,另外的线就可以接信号线,这样就多条线连到一个IO口上。

6. 蓝牙模块

​ 这个好像涨价了,现在要30多

【淘宝】https://m.tb.cn/h.5lUln0M?tk=8cO6Wf088JB CZ3457 「汇承HC-08蓝牙模块BLE4.0主从一体CC2540低功耗无线串口通信透传」
点击链接直接打开 或者 淘宝搜索直接打开

7. 超声波模块

【淘宝】https://m.tb.cn/h.5lwUdVp?tk=TIPcWf083fM CZ3457 「HC-SR04 US-100 US-015超声波模块 距离测距模块超声波传感器电子」
点击链接直接打开 或者 淘宝搜索直接打开

8. 漫反射传感器

【淘宝】https://m.tb.cn/h.5OjejRZ?tk=ZZ5VWf0jDVU CZ0001 「红外线漫反射式接近光电感应开关E3F-DS30C4三线传感器NPN常开24V」
点击链接直接打开 或者 淘宝搜索直接打开

二、硬件连接

这节主要给出我自己的接线,主要原理可以参考上面csdn那篇文章,已经写的很详细了

注意点:

​ 接线共地:单片机的地(GND)、L298N 的GND、直流电源(GND)以及其他模块的GND是同一个,即最后要连在一起。(不然模块可能不起作用)

1. 电脑与单片机的串口通信(仅下载程序到单片机时连接)

2. L298N、电机、电源、单片机 连接

3. 超声波模块、漫反射传感器模块 、单片机 连接

​ 注意:超声波模块的电压是5v

4. 蓝牙模块与单片机 连接

5. 搭建完成

三、代码和相关软件

  • 源代码(基于那篇文章的代码进行了优化,去掉了一些没用的变量,以及蓝牙模块app的bug已经修复了,不再需要一次发送20位)
    • car.h
      #ifndef __car_H
      #define __car_H
      
      #include <reg52.h>
      #include <intrins.h>
      
      sbit Left_moto_pwm=P1^6 ;
      sbit Right_moto_pwm=P1^7;
      sbit p34=P3^4;
      sbit p35=P3^5; 
      sbit p36=P3^6;
      sbit p37=P3^7;
      sbit Trig= P1^4; //产生脉冲引脚
      sbit Echo= P1^5; //回波引脚
      sbit L_sensor=P2^0;
      sbit R_sensor=P2^1;
      void Left_moto_go() ;
      void Left_moto_back() ;
      void Left_moto_stp() ;
      void Right_moto_go();
      void Right_moto_back(); 
      void Right_moto_stp(); 
      void delay(unsigned int k) ;
      void delay1s(void) ;
      void delay1ms(void);
      void pwm_out_left_moto(void) ;
      void pwm_out_right_moto(void);
      void run(void);
      void back(void);
      void left(void);
      void right(void);
      void stop(void);
      void  StartModule() ;
      void Timer1Init();
      void Timer0Init();
      void Conut(void);
      void Timer1Init2();
      
      #endif
      
    • HC_SR04.c
      #include <car.h>
      
      float  S=0;
      extern bit  flag;
      unsigned int  measure_time;
      char M_sensor=1; 
      
      void delay12us()		//@11.0592MHz
      {
      	unsigned char i;
      
      	i = 3;
      	while (--i);
      }
      
       void  StartModule() 		         //启动超声波模块
        {
      	  Trig=1;			                
      	  delay12us();
      	  Trig=0;
        }
      	
       void Conut(void)
      	{
      	 measure_time=TH1*256+TL1;
      	 S=(measure_time*1.87)/100;     //算出来是CM
      	 if(flag==1||S>40)		    //超出测量
      	 {
      		 flag=0;
      		 M_sensor=1;
      	 }
         else
      	 {
      		 M_sensor=0;
      	 }
      	}	
      
    • motor_control.c
      #include <car.h>
      
      unsigned char pwm_val_left =0;
      unsigned char pwm_val_right =0;
      unsigned char Left_Speed_Ratio;
      unsigned char Right_Speed_Ratio;
      
      bit Left_moto_stop =1;
      bit Right_moto_stop =1;
      
      void Left_moto_go()  //左电机正转
      {p34=0;p35=1;} 
      void Left_moto_back() //左电机反转
      {p34=1;p35=0;} 
      void Left_moto_stp()  //左电机停转
       {p34=1;p35=1;} 
      void Right_moto_go()  //右电机正转
      {p36=0;p37=1;} 
      void Right_moto_back() //右电机反转
      {p36=1;p37=0;}  
      void Right_moto_stp()  //右电机停转
      {p36=1;p37=1;} 
      
      
      void pwm_out_left_moto(void)    //左电机PWM
      { 
      if(Left_moto_stop) 
      { 
      if(pwm_val_left<=Left_Speed_Ratio) 
      Left_moto_pwm=1; 
      else 
      Left_moto_pwm=0; 
      if(pwm_val_left>=10) 
      pwm_val_left=0; 
      } 
      else 
      Left_moto_pwm=0; 
      } 
      
      void pwm_out_right_moto(void)    //右电机PWM
      { 
      if(Right_moto_stop) 
      { 
      if(pwm_val_right<=Right_Speed_Ratio) 
      Right_moto_pwm=1; 
      else 
      Right_moto_pwm=0; 
      if(pwm_val_right>=10) 
      pwm_val_right=0; 
      } 
      else 
      Right_moto_pwm=0; 
      } 
      
      void run(void)     //小车前行
      {  
      Left_moto_go(); 
      Right_moto_go(); 
       } 
      
      void back(void)   //小车后退
      { 
      Left_moto_back();
      Right_moto_back();
       } 
      
      void left(void)   //小车左转
      { 
      Right_moto_go(); 
      Left_moto_back();
      } 
      
       void right(void) //小车右转
      { 
      Right_moto_back();
      Left_moto_go();
      } 
      
      void stop(void)  //小车停止
      {  
      Left_moto_stp();
      Right_moto_stp();
       } 
      
    • main.c
      #include <car.h>
      
      extern unsigned char Left_Speed_Ratio;
      extern unsigned char Right_Speed_Ratio;
      unsigned int HC_SR04_time=0;
      extern unsigned char pwm_val_left;
      extern unsigned char pwm_val_right;
       bit   flag =0;
      extern char M_sensor;  
      char Work_Mode=0;   //工作模式的选择  为0时,为手机或者电脑等上位机对小车进行蓝牙遥控 ,为1时小车自动避障模式
      char init_flag=0;
      unsigned char receive_data=0;
       
      void delay1s(void)   
      {
      	unsigned char i, j;
      
      	i = 108;
      	j = 145;
      	do
      	{
      		while (--j);
      	} while (--i);
      }
      
      void delay1ms()		//@11.0592MHz
      {
      	unsigned char i, j;
      
      	_nop_();
      	i = 2;
      	j = 199;
      	do
      	{
      		while (--j);
      	} while (--i);
      }
      
      void Timer0Init()
      {
      	TMOD|=0X01;//选择为定时器0模式,工作方式1,仅用TR0打开启动。
      	TH0=0XFC;	//给定时器赋初值,定时1ms
      	TL0=0X18;	
      	ET0=1;//打开定时器0中断允许
      	EA=1;//打开总中断
      	TR0=1;//打开定时器			
      }
      
      void Timer1Init()
      {
      	TMOD=0X11;//选择为定时器1模式,工作方式1,仅用TR1打开启动。选择为定时器0模式,工作方式1,仅用TR1打开启动
      	TH1=0;	
      	TL1=0;	
      	ET1=1;//打开定时器1中断允许
      	EA=1;//打开总中断
      	TR1=1;//打开定时器			
      }
      
      void Timer1Init2()
      {
      		SCON=0X50;			//设置为工作方式1,8位数据,可变波特率
      	  TMOD |=0X20;			//设置计数器工作方式2
      	  PCON=0X00;			//波特率不加倍
      	  TH1=0XFd;		    //计数器初始值设置,9600  @11.0592MHz
      	  TL1=0XFd;
      	  TR1=1;					//打开计数器
      	  ES = 1;         //开串口中断
          EA = 1;         //开总中断
      }
      
      
      void timer0()interrupt 1 using 2 
      { 
      	TH0=0XFC;	//给定时器赋初值,定时1ms
      	TL0=0X18;
      	pwm_val_left++; 
      	pwm_val_right++; 
      	pwm_out_left_moto(); 
      	pwm_out_right_moto(); 
      	
      	if(Work_Mode==1)
      	{
      		HC_SR04_time++;
      		if(HC_SR04_time>=500)   //500ms 启动一次超声波测距
      		{
      			HC_SR04_time=0;
      			StartModule();
      		}
      	}
      } 
      
      void Timer1() interrupt 3
      {
      	flag=1;    //若定时器1溢出则flag置1
      }
      
      //发送一个字节
      //数字界面接收16进制的
      //文本界面接收字符
      void UART_SendByte(unsigned char Byte)
      {
        SBUF=Byte;
      	while(TI==0);
      	TI=0;
       
      }
      
      void  Speed_add() //加速函数
      {
      		if(Left_Speed_Ratio<10)
      			{
      				Left_Speed_Ratio+=2;
      				Right_Speed_Ratio+=2;	
      			}
      			UART_SendByte(Left_Speed_Ratio);
      }
      
      void  Speed_reduce() //减速函数
      {
      		if(Left_Speed_Ratio>2)
      		{
      			Left_Speed_Ratio-=2;
      			Right_Speed_Ratio-=2;	
      		}
      		UART_SendByte(Left_Speed_Ratio);
      }
      
      
      void Com_Int(void) interrupt 4
      {
         EA = 0;	
        if(RI == 1) //当硬件接收到一个数据时,RI会置位
      	{ 
      		RI = 0;
      		receive_data = SBUF;//接收到的数据
      		switch(receive_data)             //新增的switch函数
      		{ 
      			case '5': Speed_add(); break; 
      			case '6': Speed_reduce(); break;	
      		}
      		
      	}
          EA = 1;	
      }
      
      void main()
      {
      	Left_Speed_Ratio=8;   
      	Right_Speed_Ratio=8;
      	Timer0Init();
      	Timer1Init2();	
      	while(1)
      	{
      		if(Work_Mode==1)
      		{
      			 ES=0;   //关闭串口中断
      			 if(init_flag==0)
      			 {
      					Timer1Init();
      					init_flag=1; 
      			 }
      			 if(Echo==1)
      			 {
      					TH1=0;	
      					TL1=0;
      					TR1=1;			    //开启计数	
      					while(Echo);			//当RX为1计数并等待	
      					TR1=0;				//关闭计数	
      					Conut();			//计算		
      			 }
      			if(M_sensor==1)
      				{  run(); }
      			else
      				{
      					if(L_sensor==1)
      						 {     left();       }
      					else if(R_sensor==1)
      						 {    right() ;      }
      					else
      						 {    back();          }
      			
      			 }	
      		}
      		else	
      		{	
      			switch(receive_data) 
      			{ 
      				case '1': run(); break; 
      				case '2': left(); break; 
      				case '3': right(); break; 
      				case '4': back(); break; 
      				case '7': stop(); break; 
      				case '8': Work_Mode=1; break; 
      			}
      	 }	
      	}
      }
      
  • HC-08蓝牙连接软件设置

    汇承官网: https://www.hc01.com/home

    1. 蓝牙测试架 调试软件

      对应csdn文章中的这部分(文章有误,不是HID 串口助手,而是HC-T测试架串口助手)

      对应下面的下载位置

    2. 手机连接蓝牙软件下载(Step2 蓝牙串口助手)

      下载好后,可以在界面自定义对应按钮发送的字符

    3. 手机蓝牙软件设置

      可以方向按钮这一栏设置对应要发送的内容

四、缺陷

​ 上面csdn那篇文章有说明存在的2个缺陷和解决方案。由于时间问题,现在我并未去实现

1. 超声波模块缺陷

​ 这里超声波反射无法检测,使得小车会撞上去。

​ 解决方案:可以在超声波模块的两侧分别再加一个朝前的检测前方障碍物的漫反射光电传感器

2. 小车无法走直线

​ 电机很难找到两个完全一样的,即使给相同的电压,两轮转速也不一样,不会走完全的直线,一般都是斜着走。

​ 解决方案:

​ 1. 简单的一点就是调节一下给定的电压(pwm值),前行时往那边斜,就稍微增大一下那一边的电压

​ 2. 加上几个循迹传感器,来循线