请大佬帮我看看
单片机源程序如下:
- #include<reg52.h> //包含51单片机系统头文件
- sfr T2MOD = 0xC9;
- #define SPEED_30C 3495 //30摄氏度时的声速,声速V= 331.5 + 0.6*温度;
- #define SPEED_23C 3453 //23摄氏度时的声速,声速V= 331.5 + 0.6*温度;
- #define LCD_Data P0 //定义液晶1602数据接口对应单片机的P0口
- #define Busy 0x80 //液晶为忙时对应的状态字
- //定义智能小车电机驱动芯片L293D输入IO口
- sbit IN1 = P1^4; //电机驱动芯片L298N的IN1管脚
- sbit IN2 = P1^5; //电机驱动芯片L298N的IN2管脚
- sbit IN3 = P1^6; //电机驱动芯片L298N的IN3管脚
- sbit IN4 = P1^7; //电机驱动芯片L298N的IN4管脚
- sbit EN1 = P1^3; //电机驱动芯片L298N的EN1管脚
- sbit EN2 = P3^2; //电机驱动芯片L298N的EN2管脚
- sbit key1 = P3^7; //暂停按键
- sbit key2 = P3^6; //避障按键
- sbit key3 = P3^5; //循迹按键
- sbit key4 = P3^4;
- sbit LCD_RS = P1^0; //液晶的RS管脚
- sbit LCD_RW = P1^1; //液晶的RW管脚
- sbit LCD_E = P1^2; //液晶的E管脚
- sbit ECHO = P2^6; //超声波模块回声接收端口
- sbit TRIG = P2^5; //超声波模块触发端口
- sbit left_led = P2^7; //左循迹引脚
- sbit right_led = P2^0; //右循迹引脚
- sbit zhong_led = P2^2; //中循迹引脚
- unsigned char code table0[] = {"State:Stop "}; //定义字符数组SL-51B用于液晶显示
- unsigned char code table1[] = {" "}; //定义字符数组NO ECHO用于液晶显示
- unsigned char code table2[] ={"State:Avoiding "}; //液晶显示避障标志
- unsigned char code table3[] ={"Distance:xxx.xcm"}; //定义字符数组Distance:xxx.xcm用于显示
- unsigned char code table4[] ={"State:Tracing "}; //液晶显示循迹标志
- unsigned char code table5[] ={"State:Telecont "};
- unsigned char disbuff[4]={0,0,0,0}; //用于分别存放距离的值0.1mm、mm、cm和m的值
- unsigned char pwmval_left = 0; //变量定义pwmval_left 并初始化为0.用于小车的PWM调速
- unsigned char pwmval_right = 0; //变量定义pwmval_right并初始化为0.用于小车的PWM调速
- //小车启动时的初始占空比(左电机)
- unsigned char pwmval_left_init = 8; //左电机占空比调节 ,调节值在0到20之间,调节此值可调节小车速度。
- unsigned char pwmval_right_init = 8; //右电机占空比调节,调节值在0到20之间,调节此值可调节小车速度。
- bit right_pwm = 1; //右电机PWM开关,为1时打开
- bit left_pwm = 1; //左电机PWM开关,为1时打开
- bit bz_flag1 = 1; //超声波避障标志变量
-
- unsigned char lyen = 0; //小车工作模式标识(为0时表示小车暂停,为1时表示工作在避障模式,为2表示工作在循迹模式)
- long int distance = 0; //用于暂存超声波模块测到的距离
- long int distance1 = 0; //用于转存超声波模块测到的距离
- unsigned char count; //count变量用于超声波测距
- unsigned char UART_data;
- void delay(int In,int Out) //定义延时函数
- {
- inti,j;
- for(i = 0;i < In;i++ )
- {
- for( j = 0;j < Out;j++ )
- {;}
- }
- }
- void delayt(unsigned int x) //延时函数
- {
- unsigned char j;
- while(x-- > 0)
- {
- for(j = 0;j < 125;j++)
- {;}
- }
- }
- void Delay5Ms(void) //延时函数
- {
- unsigned int TempCyc = 3552;
- while(TempCyc--);
- }
- /**************************读状态函数***************************/
- unsigned char ReadStatusLCD(void) //读液晶状态函数
- {
- LCD_Data = 0xFF; //给液晶1602的数据口置0xff
- LCD_RS = 0; //控制液晶的RS管脚为低电平
- LCD_RW = 1; //控制液晶的RW管脚为高电平
- LCD_E = 0; //控制液晶的E管脚为低电平
- LCD_E = 0; //控制液晶的E管脚为低电平
- LCD_E = 1;
- while (LCD_Data & Busy); //检测忙信号
- return(LCD_Data);
- }
- /**************************写数据函数***************************/
- void WriteDataLCD(unsigned char WDLCD)
- {
- ReadStatusLCD(); //检测忙
- LCD_Data = WDLCD; //将数据写入液晶的数据口
- LCD_E = 0; //控制液晶的E管脚为低电平(若晶振速度太高可以在这后加小的延时)
- LCD_E = 0; //控制液晶的E管脚为低电平(设置两次,相当于小小的延时)
- LCD_RS = 1; //控制液晶的RS管脚为高电平
- LCD_RW = 0; //控制液晶的RW管脚维低电平
- LCD_E = 1; //控制液晶的E管脚为高电平
- LCD_E = 0; //控制液晶的E管脚为低电平
- }
- /*************************写指令函数****************************/
- void WriteCommandLCD(unsigned charWCLCD,BuysC) //BuysC为0时忽略忙检测
- {
- if(BuysC) ReadStatusLCD(); //根据需要检测忙
- LCD_Data = WCLCD; //给液晶写命令
- LCD_E = 0; //将液晶的管脚E设置成0,若晶振速度太高可以在这后加小的延时
- LCD_E = 0; //延时
- LCD_RS = 0; //将液晶的RS设置成0
- LCD_RW = 0; //将液晶的RW设置成0
- LCD_E = 1; //将液晶的E设置成1
- LCD_E = 0; //将液晶的E设置成0
- }
- /***************************LCD初始化***************************/
- void LCDInit(void) //LCD初始化
- {
- LCD_Data = 0;
- WriteCommandLCD(0x38,0); //三次显示模式设置,不检测忙信号(给液晶写命令0x38)
- Delay5Ms(); //延时 5ms
- WriteCommandLCD(0x38,0); //给液晶写命令0x38
- Delay5Ms(); //延时 5ms
- WriteCommandLCD(0x38,0); //给液晶写命令0x38
- Delay5Ms(); //延时 5ms
- WriteCommandLCD(0x38,1); //显示模式设置, 开始要求每次检测忙信号 (给液晶写命令0x38)
- WriteCommandLCD(0x08,1); //关闭显示,(给液晶写命令0x08)
- WriteCommandLCD(0x01,1); //显示清屏 ,(给液晶写命令0x01)
- WriteCommandLCD(0x06,1); //显示光标移动设置 ,(给液晶写命令0x06)
- WriteCommandLCD(0x0C,1); //显示开及光标设置 , (给液晶写命令0x0C)
- }
- /**********************按指定位置显示一个字符*********************/
- 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; //算出指令码
- WriteCommandLCD(X, 0); //这里不检测忙信号,发送地址码
- WriteDataLCD(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]>=0x20) //若到达字串尾则退出
- {
- if(X <= 0xF) //X坐标应小于0xF
- {
- DisplayOneChar(X, Y, DData[ListLength]); //依次显示单个字符,最终完成一串字符的显示
- ListLength++;
- X++;
- }
- }
- }
- void Init_Parameter(void) //超声波模块初始化
- {
- TRIG = 1; //将超声波模块的trig管脚设置成高电平
- ECHO = 1; //将超声波模块的echo管脚设置成低电平
- count = 0; //将变量count设置成0
- distance = 0; //将变量distance设置成0
- }
- void Trig_SuperSonic(void) //控制超声波模块发出超声波
- {
- TRIG= 1; //设置超声波模块的trig为高,发出超声波
- delayt(1); //延时
- TRIG= 0; //设置超声波模块的trig为低,停止发出超声波
- }
- void Measure_Distance(void) //距离计算函数
- {
- unsigned char l;
- unsigned int h,y;
- TR0= 1; //让定时器0开始计数
- while(ECHO) //等待超声波的回波信号
- {
- ;
- }
- TR0= 0; //让定时器0停止计数
- l =TL0; //读取定时器0计数值的低8位
- h =TH0; //读取定时器0计数值的高8位
- y =(h << 8) + l; //得到定时器总的计数值
- y =y - 0xfc66; //us部分
- distance = y + 1000 * count; //计算总时间
- TL0= 0x66; //重置定时器0的tl0
- TH0= 0xfc; //重置定时器0的th0
- delayt(30); //延时
- distance = SPEED_30C * distance / 20000; //计算距离值
- distance1 = distance; //将距离值放在变量distance1中
- }
- void forward(void) //小车前进控制函数
- {
- IN1= 1; //将电机驱动芯片L298N的控制管脚 IN1设置成高电平
- IN2= 0; //将电机驱动芯片L298N的控制管脚 IN2设置成低电平
- IN3= 1; //将电机驱动芯片L298N的控制管脚 IN3设置成高电平
- IN4= 0; //将电机驱动芯片L298N的控制管脚 IN4设置成低电平
- }
- void back(void) //小车后退控制函数
- {
- IN1= 0; //将电机驱动芯片L298N的控制管脚 IN1设置成低电平
- IN2= 1; //将电机驱动芯片L298N的控制管脚 IN2设置成高电平
- IN3= 0; //将电机驱动芯片L298N的控制管脚 IN3设置成低电平
- IN4= 1; //将电机驱动芯片L298N的控制管脚 IN4设置成高电平
- }
- void stop(void) //小车停止控制函数
- {
- IN1= 0; //将电机驱动芯片L298N的控制管脚 IN1设置成低电平
- IN2= 0; //将电机驱动芯片L298N的控制管脚 IN2设置成低电平
- IN3= 0; //将电机驱动芯片L298N的控制管脚 IN3设置成低电平
- IN4= 0; //将电机驱动芯片L298N的控制管脚 IN4设置成低电平
- }
- void left(void) //小车向左转控制函数
- {
- IN1= 0; //将电机驱动芯片L298N的控制管脚 IN1设置成低电平
- IN2= 0; //将电机驱动芯片L298N的控制管脚 IN2设置成低电平
- IN3= 1; //将电机驱动芯片L298N的控制管脚 IN3设置成高电平
- IN4= 0; //将电机驱动芯片L298N的控制管脚 IN4设置成低电平
- }
- void right(void) //小车向右转控制函数
- {
- IN1= 1; //将电机驱动芯片L298N的控制管脚 IN1设置成高电平
- IN2= 0; //将电机驱动芯片L298N的控制管脚 IN2设置成低电平
- IN3= 0; //将电机驱动芯片L298N的控制管脚 IN3设置成低电平
- IN4= 0; //将电机驱动芯片L298N的控制管脚 IN4设置成低电平
- }
- void circle_right(void) //小车向右打转控制函数
- {
- IN1= 1; //将电机驱动芯片L298N的控制管脚 IN1设置成低电平
- IN2= 0; //将电机驱动芯片L298N的控制管脚 IN2设置成高电平
- IN3= 0; //将电机驱动芯片L298N的控制管脚 IN3设置成高电平
- IN4= 1; //将电机驱动芯片L298N的控制管脚 IN4设置成低电平
- }
- /************************************************************************/
- void left_moto(void) //小车左电机PWM调速控制函数
- {
- if(left_pwm) //如果变量left_pwm为1,执行左电机pwm调速功能(left_pwm相当于一个开关,只有为1时才有pwm调速功能)
- {
- if(pwmval_left <= pwmval_left_init) //当pwmval_left小于等于pwm_left_init时
- {
- EN1 = 1;
- //将电机驱动芯片的EN1管脚设置成高电平
- }
- else //当pwmval_left小不于等于pwm_left_init时
- {
- EN1 = 0;
- //将电机驱动芯片的EN1管脚设置成低电平
- }
- if(pwmval_left >= 20) //如果 pwmval_left大于等于20
- {
- pwmval_left = 0; //将 pwmval_left设为0
- }
- }
- else // 如果变量left_pwm为0,将电机驱动芯片的EN1管脚设置成低电平 (left_pwm相当于一个开关,只有为1时才有pwm调速功能)
- {
- EN1= 0;
-
- }
- }
- /******************************************************************/
- void right_moto(void) //小车右电机PWM调速控制函数
- {
- if(right_pwm) //如果变量right_pwm为1,执行右电机pwm调速功能(right_pwm相当于一个开关,只有为1时才有pwm调速功能)
- {
- if(pwmval_right <= pwmval_right_init) //当pwmval_right小于等于pwm_right_init时
- {
- EN2 = 1; //将电机驱动芯片的EN2管脚设置成高电平
- }
- else if(pwmval_right > pwmval_right_init) //当pwmval_right小不于等于pwm_right_init时
- {
- EN2 = 0; //将电机驱动芯片的EN2管脚设置成低电平
- }
- if(pwmval_right >= 20) //如果 pwmval_right大于等于20
- {
- pwmval_right = 0; //将 pwmval_right设为0
- }
- }
- else //如果变量right_pwm为0,将电机驱动芯片的EN2管脚设置成低电平 (right_pwm相当于一个开关,只有为1时才有pwm调速功能)
- {
- EN2= 0;
- }
- }
- /******************************************************************/
- void timer0_init() //定时器0初始化 ,此定时器用于超声波模块测距
- {
- TMOD|=0x01; //设置定时器的工作模式
- TH0=0xfc; //设置定时器0为1ms定时
- TL0=0x66; //设置定时器0为1ms定时
- TR0=1; //启动定时器0的计数
- ET0=1; //开定时器0中断
- EA =1; //开总中断
- }
- /* 串口配置函数,baud-通信波特率 */
- void ConfigUART(unsigned int baud)
- {
- SCON = 0x50; //配置串口为模式1
- TMOD &= 0x0F; //清零T1的控制位
- TMOD |= 0x20; //配置T1为模式2
- TH1 = 256 - (11059200/12/32)/baud; //计算T1重载值
- TL1 = TH1; //初值等于重载值
- ET1 = 0; //禁止T1中断
- TR1 = 1; //启动T1
- EA=1;
- ES=1;
- }
- void timer2_init() //定时器2初始化 ,此定时器用于pwm调速
- {
- T2CON=0; //设置定时器的T2CON,设置T2为内部定时器,T2EX的跳变对定时器2无效
- T2MOD=0; //定时器2输出使能,定时器递增计数
- /// 以下设置将定时器2的输出频率设置成42HZ///
- RCAP2H = (0xFFFF-0x400)/256; //设置定时器2的RCAP2H为254
- RCAP2L = (0xFFFF-0x400)%256; //设置定时器2的RCAP2L为255
- TH2 =RCAP2H; //将RCAP2H中的值(254)重装到TH2中
- TL2 =RCAP2L; //将RCAP2L中的值(255)重装到TL2中
- ///////////////////////////////////////////
- TR2 =1; //开启定时器2计数
- ET2 =1; //开启定时器2中断
- EA =1; //开总中断
- }
- void PROCESS(void) //蓝牙遥控与工作模式切换处理函数
- {
- if(key1 == 0)
- {
- Delay5Ms();
- if (key1 == 0)
- {
- while(!key1);
- lyen=0;
- DisplayListChar(0,0,table0); //在液晶上显示字体 SL-51B
- DisplayListChar(0,1,table1); //在液晶上显示字体 ECHO
- }
- }
- if(key2 == 0)
- {
- Delay5Ms();
- if (key2 == 0)
- {
- while(!key2);
-
- lyen=1; //如果小车单片机接收到的命令是将小车工作模式切换成避障模式
- DisplayListChar(0,0,table2); //在液晶上显示字体 SL-51B
- DisplayListChar(0,1,table3); //在液晶上显示字体 ECHO
-
- stop(); //控制小车停下 //将小车工作模式切换到避障模式(非蓝牙遥控模式)
- pwmval_left_init = 8;
- pwmval_right_init = 8;
- }
- }
- if(key3 == 0)
- {
- Delay5Ms();
- if (key3 == 0)
- {
- while(!key3);
- lyen=2; //如果小车单片机接收到的命令是将小车工作模式切换成循迹模式
- DisplayListChar(0,0,table4); //在液晶上显示字体
- DisplayListChar(0,1,table1); //在液晶上显示字体
- stop();
- pwmval_left_init = 6;
- pwmval_right_init = 6; //控制小车停下 //将小车工作模式切换到循迹模式(非蓝牙遥控模式)
- }
- }
-
- if(key4 == 0)
- {
- Delay5Ms();
- if (key4 == 0)
- {
- while(!key4);
- lyen=3; //如果小车单片机接收到的命令是将小车工作模式切换成无线遥控模式
- DisplayListChar(0,0,table5); //在液晶上显示字体
- DisplayListChar(0,1,table1); //在液晶上显示字体
- stop(); //将小车工作模式切换到循迹模式(非蓝牙遥控模式)
- }
- }
- }
- void PROCESS1(void)//ok //避障处理函数
- {
- if((distance1>=300)||(distance1 == 0)) //超声波模块测到障碍物在30毫米外
- {
- bz_flag1 = 0; //将标志变量bz_flag1设置成0
- }
- else
- {
- bz_flag1 = 1; //否则将标志变量bz_flag1设置成1
- }
-
- if(bz_flag1 == 0) //如果标志变量bz_flag1和标志变量bz_flag2都为0(表示超声波模块和红外避障模块都没有感应到障碍物)
- {
- forward(); //智能小车前进
- }
- elseif(bz_flag1 == 1) //超声波模块或红外避障模块感应到障碍物
- {
- back(); //小车后退
- delay(20,1000); //延时
- circle_right(); //小车转个角度
- delay(5,500); //延时
- distance1 = 0; //将变量distance1清零
- }
- }
- void display(int number) //显示距离值
- {
- unsigned char b,c,d,e;
- b =(number/1000); //计算出距离的百位
- c =(number/100)%10; //计算出距离的十位
- d =(number/10)%10; //计算出距离的个位
- e =number%10; //计算出距离的小数位
- DisplayOneChar(9,1,(0x30+b)); //显示距离的百位
- DisplayOneChar(10,1,(0x30+c)); //显示距离的十位
- DisplayOneChar(11,1,(0x30+d)); //显示距离的个位
- DisplayOneChar(13,1,(0x30+e)); //显示距离的小数位
- }
- void PROCESS2(void) //ok //超声波模块测距函数
- {
- Trig_SuperSonic(); //触发超声波发射
- while(ECHO == 0) //等待回声
- {
- ;
- }
- Measure_Distance(); //计算脉宽并转换为距离
- DisplayListChar(0,1,table3); //在液晶1602上显示distance字体
- display(distance); //显示距离值
- Init_Parameter(); //参数重新初始化
- delayt(100); //延时,两次发射之间要至少有10ms间隔
- }
- void PROCESS3(void) // 白0 黑1 //循迹处理函数
- {
-
- if((left_led == 1) && (right_led ==1)&& (zhong_led == 1)) //全是黑线
- {
- pwmval_left_init = 5;
- pwmval_right_init = 5;
- forward(); //调用前进函数
- }
- if((left_led == 0 && right_led ==0&& zhong_led == 0)||(left_led == 0 && right_led == 0&&zhong_led == 1)) //全是白线,或中间检测到黑线
- {
- pwmval_left_init = 5;
- pwmval_right_init = 5;
- forward(); //调用前进函数
- }
-
- if((left_led == 1) && (right_led == 0) ) //左边检测到黑线
- {
- pwmval_left_init = 4;
- pwmval_right_init = 4;
- left(); //调用小车左转函数
- while(!zhong_led);
- // right();
- // delayt(150);
- forward();
- // delay(50,50);Delay5Ms() ;
- }
- if((right_led == 1) && (left_led == 0)) //右边检测到黑线
- {
- pwmval_left_init = 4;
- pwmval_right_init = 4;
- right(); //调用小车右转函数
- while(!zhong_led);
- // left();
- // delayt(150);
- forward();
-
- //delay(50,50); Delay5Ms(); (zhong_led == 1)
-
- }
-
- }
- //系统初始化函数
- void sys_init(void)
- {
- timer0_init(); //定时器0初始化
- timer2_init(); //定时器2初始化
- ConfigUART(9600); //配置波特率为9600
- Init_Parameter(); //超声波模块初始化
- EA =1; //打开总中断
- LCDInit(); //液晶1602初始化
- }
- void main(void) //主函数
- {
- sys_init(); //系统初始化函数
- DisplayListChar(0,0,table0); //在液晶上显示字体 SL-51B
- DisplayListChar(0,1,table1); //在液晶上显示字体 ECHO
- while(1) //while(1)循环(死循环)
- {
- PROCESS(); //调用小车工作模式切换处理函数
- if(lyen== 0) //暂停模式
- {
- stop();
- } //调用超声波模块测距函数
- if(lyen == 1) //如果小车工作在避障工作模式
- {
- PROCESS2(); //当前工作模式是避障模式
- PROCESS1(); //调用避障处理函数
- }
- if(lyen == 2) //当前工作模式是循迹模式
- {
- PROCESS3(); //调用循迹处理函数
- }
- }
- }
-
- void timer0()interrupt 1 using 2 //定时器0中断处理函数
- {
- TF0= 0; //清定时器0中断标志
- TL0= 0x66; //重置定时器0的TL0
- TH0= 0xfc; //重置定时器0的TH0
- count++; //变量count加1,count用于超声波测距,每毫秒count加1(定时器0每毫秒中断一次)
- if(count == 18) //超声波回声脉宽最多18ms
- {
- TR0=0; //定时器0停止计数
- TL0= 0x66; //重置定时器0的TL0
- TH0= 0xfc; //重置定时器0的TH0
- count = 0; //变量count置0
- }
- }
- void timer2()interrupt 5 using 1 //定时器2中断处理函数
- {
- TF2= 0; //清定时器2中断标志
- pwmval_left = pwmval_left + 1; //变量pwmval_left加1,用于小车PWM调速
- pwmval_right = pwmval_right + 1; //变量pwmval_right加1,用于小车PWM调速
- left_moto(); //调用小车左电机调速函数
- right_moto(); //调用小车右电机调速函数
- }
- /************串行口1中断处理函数*************/
- void ser() interrupt 4
- {
- if(RI)
- {
- UART_data=SBUF;
- if(lyen== 3)
- {
- switch(UART_data)
- {
- case 'a':forward();break; //前
- case 'b':back(); break; //后
- case 'c':left(); break; //左
- case 'd':right(); break; //右
- case 'e':stop(); break; //停
- }
- }
- }
- RI=0;
- }
复制代码
|