S48{]M6DTS_71S]ITRSSW}R.png (75.7 KB, 下載次數: 49)
下載附件
2022-4-19 22:38 上傳
請大佬幫我看看
單片機源程序如下:
- #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;
- }
復制代碼
|