我自己写的电动车跷跷板程序,做了一个1602菜单(只用了3个键哟!),使用步进电机,采用折半查找法寻找平衡点,最小步进达到1mm,还有很多其他功能,自己用51单片机试试看,这个是源程序,文库里还有个报告,加上一句我用的是倾角传感器,用AD采样算倾角。
#include<reg52.h>#define uchar unsigned char#define uint unsigned int#define Xiaodou 50 //消抖时间#define OneCircle 512//OneCircle次循环为360度#define LCircle 23//轮子转一圈为LCircle cm#define FirstJour 256//找重心第一步#define SAOMIAO 600//延迟600ms测一次角度#define QueDing 700//平衡确定延时#define UpAngle 0x82//上限角度,大于上限角表示向前翘起,2.56V#define DownAngle 0x7e//下限角度,小于下限角表示向后翘起,2.45Vsbit KeyEnter=P3^2;//调整确定键sbit KeyUp=P3^3;//上方向键sbit KeyRight=P3^4;//右方向键sbit LCDRs=P3^0;//液晶的命令数据端sbit LCDEn=P3^1;//液晶的使能端sbit ADST=P3^5;//AD0809的转换启动信号端STsbit ADEOC=P3^6;//AD0809的转换结束信号,高电平表示转换结束sbit ADCLK=P3^7;//AD0809的时钟为500KHzsbit FLLight=P1^4;//前左灯,低电平发光sbit FRLight=P1^5;//前右灯,低电平发光sbit BLLight=P1^6;//后左灯,低电平发光sbit BRLight=P1^7;//后右灯,低电平发光uchar timecount=0;uint count[4]={0,0,0,0};//控制时间,4个过程的时间uint journey[4]={60,65,55,115};//设置行程uchar speed=3;//速度参量uchar step=0;//步骤游标uchar protype[4]={0,2,0,1};//每个步骤指令uchar CharDisp[3];//用于显示字符uchar code F_Rotation[]={0xc9,0xc1,0xc3,0xc2,0xc6,0xc4,0xcc,0xc8};//正转表格,前灯亮uchar code B_Rotation[]={0x38,0x3c,0x34,0x36,0x32,0x33,0x31,0x39};//反转表格,后灯亮void Delay(uint x,uint y)//x=1,y=110每执行一次大概为1ms{for(;x>0;x--)for(y=110;y>0;y--);}void WriteCommond(uchar com)//给液晶写入指令,com为16进制{LCDRs=0;P0=com;//液晶的数据端口Delay(5,110);LCDEn=1;Delay(5,110);LCDEn=0;}void WriteData(uchar date)//给液晶写入字符,com为16进制{LCDRs=1;P0=date;//液晶的数据端口Delay(5,110);LCDEn=1;Delay(5,110);LCDEn=0;}void WriteString(uchar * string)//写入字符串{for(;*string;string++)WriteData(*string);}void DispTimeS(uint times,uchar address)//显示时间或者距离{uchar i=0;CharDisp[0]=times/100;CharDisp[1]=times%100/10;CharDisp[2]=times%10;WriteCommond(address);for(;i<=2;i++){WriteData(CharDisp[i]+'0');}}void RunXCircle(uchar drec,uint num)//轮子跑num圈,drec为0向前,为1向后{uchar i;uint j;while(num--){j=OneCircle;while(j--){for(i=0;i<8;i++) //4相{if(!drec)P1=F_Rotation[i]; //输出对应的相,可以自行换成反转表格elseP1=B_Rotation[i];Delay(speed,110); //改变这个参数可以调整电机转速}}}P1=0xf0;//停止行走,灯熄灭}void RunNCircle(uchar drec,uint num)//轮子转num个最小单位,drec为1向前,为0向后{uchar i;//num*=OneCircle/LCircle;while(num--){for(i=0;i<8;i++) //4相{if(!drec)P1=F_Rotation[i]; //输出对应的相,可以自行换成反转表格elseP1=B_Rotation[i];Delay(speed,110); //改变这个参数可以调整电机转速}}P1=0xf0;//停止行走,灯熄灭}uchar DeltFind()//返回指为2表示平衡了,为0需要向前走,为1需要向后走{uchar angle,result=2;ADST=0;ADST=1;ADST=0;//启动AD0809转换while(!ADEOC);//等待AD0809转换结束angle=P2;//读取转化后的数据if(angle>UpAngle) result=0;//小车向前翘起,需要向前走else if(angle<DownAngle) result=1;//小车向后翘起,需要向后走else result=2;//小车平衡return result;}void RunFwodBak(uchar drec,uchar jour)//drec为方向,0向前,1向后,jour为路程,单位是cm {RunXCircle(drec,jour/LCircle);//先跑完整圈RunNCircle(drec,(jour%LCircle)*OneCircle/LCircle);//再跑半圈}void RunFind(){uchar delt=0,flag=0;uint jour=FirstJour;TR1=1;//开时钟让AD工作for(delt=DeltFind();;delt=DeltFind()){if(delt==2){Delay(QueDing,110);delt=DeltFind();if(delt==2){WriteCommond(0x86);WriteString("balanced!!");Delay(5000,110);break;}}if(delt!=flag) jour/=2;if(!jour){WriteCommond(0x86);WriteString("Failed");Delay(3000,110);break;}RunNCircle(delt,jour);flag=delt;Delay(SAOMIAO,110);}TR1=0;//关时钟}void WindowMain()//输出主界面{WriteCommond(0x81);WriteString("SET");WriteCommond(0x8c);WriteString("VIEW");WriteCommond(0xc1);WriteString("RUN");}void WindowSet()//输出设置界面{WriteCommond(0x81);WriteString("P1");WriteCommond(0x87);WriteString("P2");WriteCommond(0x8c);WriteString("P3");WriteCommond(0xc1);WriteString("P4");WriteCommond(0xc5);WriteString("SPEED");WriteCommond(0xcc);WriteString("EXIT");}void WindowFunction()//输出功能界面{WriteCommond(0x81);WriteString("FOWORD");WriteCommond(0x8c);WriteString("BACK");WriteCommond(0xc1);WriteString("FIND");}void WindowParamet()//输出参数设置界面{WriteCommond(0x80);WriteString("S:");WriteCommond(0x85);WriteString("CM");}void WindowRun()//输出运行时界面{WriteCommond(0x80);WriteData('P');WriteCommond(0xc0);WriteString("AL:");WriteCommond(0xc6);WriteData('s');WriteCommond(0xc8);WriteString("SIG:");WriteCommond(0xcf);WriteData('s');}void WindowView()//输出查看界面{WriteCommond(0x80);WriteString("P1:");WriteCommond(0x89);WriteString("P2:");WriteCommond(0xc0);WriteString("P3:");WriteCommond(0xc9);WriteString("P4:");}void WindowSpeed(){WriteCommond(0x80);WriteString("SPEED STAGE:");}void MenuSpeed(){WriteCommond(0x0e);//显示光标WindowSpeed();WriteCommond(0xc0);WriteData(speed-2+'0');WriteCommond(0xc0);while(1){if(!KeyUp){Delay(Xiaodou,110);if(!KeyUp){while(!KeyUp){}speed++;if(speed>11) speed=3;WriteCommond(0xc0);WriteData(speed-2+'0');WriteCommond(0xc0);}}if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}break;}}}}MenuParamet(uchar process){uchar cursor=0;WriteCommond(0x0e);//显示光标WindowParamet();DispTimeS(journey[process],0x82);while(1){if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}break;}}if(!KeyRight){Delay(Xiaodou,110);if(!KeyRight){while(!KeyRight){}cursor++;cursor%=3;}}if(!KeyUp){Delay(Xiaodou,110);if(!KeyUp){while(!KeyUp){}CharDisp[cursor]++;CharDisp[cursor]%=10;journey[process]=CharDisp[0]*100+CharDisp[1]*10+CharDisp[2];DispTimeS(journey[process],0x82);}}WriteCommond(0x82+cursor);}}void MenuFunction(uchar process)//每步功能设置{uchar cursor=0,address=0x80;WriteCommond(0x0c);//不显示光标也不闪烁while(1){if(!KeyUp){Delay(Xiaodou,110);if(!KeyUp){while(!KeyUp){}cursor+=2;cursor%=4;WriteCommond(0x01);//清除显示}}if(!KeyRight){Delay(Xiaodou,110);if(!KeyRight){while(!KeyRight){}if(cursor==3) cursor=2;cursor+=1;cursor%=3;WriteCommond(0x01);//清除显示}}if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}WriteCommond(0x01);//清除显示if(cursor==2||cursor==3) protype[process]=2;else{protype[process]=cursor;MenuParamet(process);}break;}WriteCommond(0x01);//清除显示WriteCommond(0x0c);//不显示光标也不闪烁}switch(cursor){case 0:address=0x80;break;case 1:address=0x8b;break;case 2:case 3:default:address=0xc0;}WindowFunction();//显示设置界面WriteCommond(address);WriteData(0x7e);}}void MenuSet(){uchar cursor=0,address=0x80;WriteCommond(0x0c);//不显示光标也不闪烁while(1){if(!KeyUp){Delay(Xiaodou,110);if(!KeyUp){while(!KeyUp){}cursor+=3;cursor%=6;WriteCommond(0x01);//清除显示}}if(!KeyRight){Delay(Xiaodou,110);if(!KeyRight){while(!KeyRight){}cursor+=1;cursor%=6;WriteCommond(0x01);//清除显示}}if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}WriteCommond(0x01);//清除显示if(cursor==5) break;else if(cursor==4) MenuSpeed();elseMenuFunction(cursor);}WriteCommond(0x01);//清除显示WriteCommond(0x0c);//不显示光标也不闪烁}switch(cursor){case 0:address=0x80;break;case 1:address=0x86;break;case 2:address=0x8b;break;case 3:address=0xc0;break;case 4:address=0xc4;break;case 5:default:address=0xcb;}WindowSet();//显示设置界面WriteCommond(address);WriteData(0x7e);}}void MenuView()//View功能{WriteCommond(0x0c);//不显示光标也不闪烁WindowView();DispTimeS(count[0],0x83);WriteData('s');DispTimeS(count[1],0x8c);WriteData('s');DispTimeS(count[2],0xc3);WriteData('s');DispTimeS(count[3],0xcc);WriteData('s');while(1){if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}WriteCommond(0x01);//清除显示WriteCommond(0x80);WriteString("OVER ALL TIME:");DispTimeS(count[0]+count[1]+count[2]+count[3],0xc0);WriteData('s');while(1){if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}break;}}}break;}}}}void MenuRun(){WriteCommond(0x0c);//不显示光标也不闪烁WindowRun();count[0]=0;count[1]=0;count[2]=0;count[3]=0;TR0=1;//开定时器WriteCommond(0xc3);WriteString("000");//DispTimeS(count[0]+count[1]+count[2]+count[3],0xc3);for(;step<4;step++){timecount=0;//必须加的WriteCommond(0x81);WriteData(step+1+'0');WriteCommond(0x86);WriteString("Running...");WriteCommond(0xcc);WriteString("000s");//DispTimeS(count[step],0xcc);//必须加的switch(protype[step]){case 0:RunFwodBak(0,journey[step]);break;case 1:RunFwodBak(1,journey[step]);break;case 2:RunFind();}WriteCommond(0x86);WriteString("Completed!");if(step==2) Delay(3000,110);Delay(2000,110);}TR0=0;//关定时器step=0;}void MenuMain()//调整菜单{uchar cursor=0,address=0x80;WriteCommond(0x01);//清除显示WriteCommond(0x0c);//不显示光标也不闪烁while(1){if(!KeyUp){Delay(Xiaodou,110);if(!KeyUp){while(!KeyUp){}cursor+=2;cursor%=4;WriteCommond(0x01);//清除显示}}if(!KeyRight){Delay(Xiaodou,110);if(!KeyRight){while(!KeyRight){}if(cursor==3) cursor=2;cursor+=1;cursor%=3;WriteCommond(0x01);//清除显示}}if(!KeyEnter){Delay(Xiaodou,110);if(!KeyEnter){while(!KeyEnter){}WriteCommond(0x01);//清除显示switch(cursor){case 0:MenuSet();break;case 1:MenuView();break;case 2:case 3:default:MenuRun();}WriteCommond(0x01);//清除显示WriteCommond(0x0c);//不显示光标也不闪烁}}switch(cursor){case 0:address=0x80;break;case 1:address=0x8b;break;case 2:case 3:default:address=0xc0;}WindowMain();//显示主界面WriteCommond(address);WriteData(0x7e);}}void MCUInitial()//初始化MCU{EA=1;TMOD=0x01;//16位定时器TH0=(65536-10000)/256;TL0=(65536-10000)%256;TH1=(65536-235)/256;TL1=(65536-235)%256;ET0=1;//开定时器0中断ET1=1;//开定时器1中断}void LCDInitial()//初始化LCD{WriteCommond(0x38);//设置为16X2,5X7点阵WriteCommond(0x06);//写一个字符地址自加1}void Timer0_Int() interrupt 1{TH0=(65536-10000)/256;TL0=(65536-10000)%256;timecount++;if(timecount>=100){timecount=0;count[step]++;DispTimeS(count[0]+count[1]+count[2]+count[3],0xc3);DispTimeS(count[step],0xcc);}}void Timer1_Int() interrupt 3{TH1=(65536-235)/256;TL1=(65536-235)%256;ADCLK=!ADCLK;}void main(){MCUInitial();LCDInitial();MenuMain();}。