基于51单片机的直流电机PID控制一、试验器件选择1、控制芯片的作用主要是与L289相连接驱动直流电机,以及与八位数码管相连显示。(1)、AT89C51是一种带4K自己FLASH存储器的低压、高性能CMOS8为微处理器。单片机的可擦除只读存储器可以反复擦除1000次。该器件采用ATMEL高密度非易失真存储制造技术制造,与工业标准的MCS-51指令集和输出关键相兼容。由于将多功能8位CPU和闪存组合在单个芯片中,ATMEL的AT89C51是一种高效微控制器。AT89C51单片机为很多嵌入式控制系统提供了一种灵活性奥高且廉价的方案。(2)、AT89C51引脚图如下:2、电机驱动芯片(1)、电机驱动芯片选择L298。其主要功能是作为单片机与直流电机中间的过度链接,单片机输出的信号通过L298加载到直流电机上驱动直流电机运行。其引脚图如下:(2)、主要工作原理:1、15脚分别是两个H桥的电流反馈脚,不用时可以直接接地;2、3为一对输出端口,13、14为一对输出端口;4为驱动电压输入,最小值必须比输入的低电平高2;5、7一对输入端口,10、12一对输入端口,TTL电平兼容6、11使能端,低电平禁止输出;8、9分别为接地和逻辑电源3、直流电机。在protues中选择motor-encoder直流电机,引脚图如下:上方左右的两个引脚在点击运转时输出频率相同的方波,但是在相位上相差90,而且在正转和反转是相反,因此可以根据这两个引脚的输出情况判断点击的转向。上方中间的引脚每当电机转一圈就输出一个正脉冲,可以据此册数点击的转速。左右两个引脚是电机的电压输入端。4、74HC74。当D触发器的D和CLK输入端分别接电机上方的左右两个输出端口时可以根据D触发器的输出情况判断点击的转速。5、八位数码管。用以显示。二、系统硬件设计连接1、系统的器件连接图如下:2、单片机与数码管通过P0口和P2口相连,其中P2口选择点亮哪一个数码管,P0口则控制被点亮的数码管显示的数据。3、单片通过P1^0和P1^1和L298的第一对输入端IN1和IN2相连,然后又L298的第一对输出端OUT1和OUT2与直流电机相连,已达到控制直流电机的目的。4、从P1^4到P1^7分别接一个Button按钮来实现PID控制,P3^6和P3^7分别接Button按钮实现对电机的加速与减速控制。5、双刀双掷开关SW1课实现点击的正转与反转,单刀双掷开关SW2可实现点击的开启与停止。三、系统程序设计及功能实现1、程序流程图:开始输入一个值,点击Enter按钮,通过PID算法,改变PWM波输出让电机运行通过按钮加减改变当前值,Enter键确定判断新的输入值与当前值的差值,将差值加到原值上,通过PID算法改变PWM波输出控制电机运行循环2、各部分实现程序:(1)、普通延时:#includedelay.hvoiddelayms(unsignedcharx){unsignedchari;while(x--)for(i=0;i120;i++);}(2)、数码管显示:#includereg51.h#includedisplay.h#includedelay.hvoiddisplay_lilun(doublenum){charcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,//0~7对应数码0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//8~F对应数码longintn=num;P0=0;P2=0x7f;P0=table[n%1000/100];delayms(2);//百位加小数点P0=0;P2=0xbf;P0=table[n%100/10];delayms(2);//十位P0=0;P2=0xdf;P0=table[n%10];delayms(2);//个位P0=0;//关闭显示器}voiddisplay(doublenum,intdir){charcodetable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,//0~7对应数码0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//8~F对应数码longintn=num*10;P0=0;P2=0xf7;P0=table[n/1000];delayms(2);//百位加小数点P0=0;P2=0xfb;P0=table[n%1000/100];delayms(2);//百位加小数点P0=0;P2=0xfd;P0=table[n%100/10]+128;delayms(2);//十位P0=0;P2=0xfe;P0=table[n%10];delayms(2);//个位P0=0;//关闭显示器if(dir==1){P0=0;P2=0xef;P0=0x40;delayms(2);}}(3)、按键扫描:#includereg51.hsbitjiashi=P1^3;//对各个按钮进行位定义sbitjianshi=P1^4;sbitjia=P1^5;sbitjian=P1^6;sbitenter=P1^7;externintnum_key[5];//定义全局变量voidkeyscan(){if(jiashi==0){while(!jiashi);num_key[0]++;}if(jianshi==0){while(!jianshi);num_key[1]++;}if(jia==0){while(!jia);num_key[2]++;}if(jian==0){while(!jian);num_key[3]++;}if(enter==0){while(!enter);num_key[4]++;}}(4)、PWM波输出:#includereg51.h#includedelay.h#includePWM.hexternintPWM;//赋初值externintstart,sudu_lilun;externdoubledis_count;sbitS2=P3^6;//PWM值减少键sbitS3=P3^7;//PWM值增加键voidPWM_duty(){if(start=1)PWM=sudu_lilun;do{if(PWM!=0xff){PWM++;delayms(10);}}while(S3==0);do{if(PWM=0x02){PWM--;delayms(10);}}while(S2==0);}(5)、中断服务程序:#includereg51.hexternintPWM,count,count1,dir,count20ms_flag;externdoubledis_count;sbitPWM_OUT1=P1^1;sbitPWM_OUT2=P1^0;voidtimer0()interrupt1{longinti;TR1=0;dir=0;TL0=0x00;TH0=0xd8;//10msTL0=0xf0;TH1=PWM;TR1=1;PWM_OUT1=0;//启动输出PWM_OUT2=0;if(i++==492){count20ms_flag=1;i=0;dis_count=(double)(count+count1)/2;count=0;count1=0;}}voidtimer1()interrupt3{TR1=0;if(dir==0){PWM_OUT1=1;//结束输出PWM_OUT2=0;}else{PWM_OUT1=0;PWM_OUT2=1;}}voidEIRQ0(void)interrupt0{EX0=0;count++;EX0=1;}voidEIRQ1(void)interrupt2{EX1=0;count1++;EX1=1;}(6)、PID控制:#includePID.h#includePWM.hintP,I,D;externintsudu_lilun;typedefstructPID//定义结构体{intSetPoint;//目标longSumError;//误差doubleProportion;//比例系数doubleIntegral;//积分系数doubleDerivative;//微分系数intLastError;intPrevError;}PID;staticPIDsPID;staticPID*sptr=&sPID;voidIncPIDInit(){sptr-SumError=0;sptr-LastError=0;//Error[-1]sptr-PrevError=0;//Error[-2]sptr-Proportion=0.5;//比例系数sptr-Integral=0.3;//积分系数sptr-Derivative=0.3;//微分系数sptr-SetPoint=sudu_lilun;}intIncPIDCalc(intNextPoint){registerintiError,iIncpid;//当前的误差值iError=sptr-SetPoint-NextPoint;//计算增加量iIncpid=sptr-Proportion*iError//E[k]项-sptr-Integral*sptr-LastError//E[k-1]项+sptr-Derivative*sptr-PrevError;//E[k-2]项//存储当前误差以便后面计算sptr-PrevError=sptr-LastError;sptr-LastError=iError;//返回增量值return(iIncpid);}(7)、main函数:#includereg51.h#includeintrins.h#includekey.h#includedelay.h#includedisplay.h#includePWM.h#includePID.hsbitP10=P1^2;//确定电机转向intnum_key[5];intPWM,PWM1,count,count1,dir,sudu_lilun,start,count20ms_flag;doubledis_count;intPWM_PID;voidinit();voidmain(){init();while(1){PWM_duty();if(P10==1)dir=0;elsedir=1;keyscan();sudu_lilun=num_key[0]*10-num_key[1]*10+num_key[2]-num_key[3];display_lilun(sudu_lilun);if(num_key[4]==1)start=1;display(dis_count,dir);if(start=1&&count20ms_flag==1){count20ms_flag=0;IncPIDInit();PWM_PID=IncPIDCalc(dis_count);PWM=PWM+PWM_PID*2;}}}voidinit()//定时器、外部中断初始化{TMOD=0x01;TH0=0xd8;TL0=0xf0;IT0=1;EX0=1;IT1=1;EX1=1;TH1=PWM;TL1=0xff;EA=1;ET0=1;ET1=1;TR0=1;}3、程序实现效果: