#include <reg51.h> #include< intrins.h> #define uchar unsigned char #define uint unsigned int //#define ulong unsigned long #define KeyPort P1//自定义按键端 #define AD_IN0 0x40////自定义PCF8591的AD转换通道1 #define AD_IN1 0x41//自定义PCF8591的AD转换通道2 #define AD_IN2 0x42//自定义PCF8591的AD转换通道3 #define AD_IN3 0x43//自定义PCF8591的AD转换通道4 #define CYCLE 100 //限定的最大转速 sbit pwm=P2^0;//位定义脉冲输入端口 sbit IN1=P2^1;//位定义L298输入端1 sbit IN2=P2^2;//位定义L298输入端2 sbit SDA=P2^3;//位定义PCF8591的I2C总线数据端 sbit SCL=P2^4;//位定义PCF8591的I2C总线时钟端 sbit RS = P2^5;//LCD1602数据命令选择端口 sbit RW = P2^6;//LCD1602读写选择端口 sbit EN = P2^7;//LCD1602使能端口 sbit zhongduan=P3^2;//位定义中断次数变量 int out;//声明PID调节后输出偏差值变量 uint value;//声明经PID函数PID调节后输出偏差值返回值变量 uint expc_speed; //设定速度 uint real_speed; //实际速度 uint Inlpuse;//脉冲计数变量 uint time,count;//定时器0中断次数变量、定时器1中断次数变量 uint Proportion=8;//比例常数 Proportional Const uint Integral=3;//积分常数 Integral Const uint Derivative=1;//微分常数 Derivative Const uint Error;//Error[0]为声明当前偏差值变量 uint LastError;//Error[-1]为声明之后偏差值变量 uint PrevError;//Error[-2]为声明再后偏差值变量 uchar code table[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码 void delay(void) { uchar i; for(i=1;i>0;i--); } void delay_ms(uchar x) { uint i,j; for(i=x;i>0;i--) for(j=50;j>0;j--); } uchar KeyScan(void)//按键扫描函数 { uchar keyvalue; if(KeyPort!=0xff) { delay_ms(10); if(KeyPort!=0xff) { keyvalue=KeyPort; while(KeyPort!=0xff); switch(keyvalue) { case 0xfe: return 1; break; case 0xfd: return 2; break; case 0xfb: return 3; break; case 0xf7: return 4; break; case 0xef: return 5; break; case 0xdf: return 6; break; case 0xbf: return 7; break; case 0x7f: return 8; break; default: return 0; break; } } } return 0; } void write_com(uchar com)//lcd1602写命令函数 { RS=0; RW=0; P0=com; delay_ms(5); EN=1; delay_ms(5); EN=0; } void write_data(uchar date)//lcd1602写一个字符函数 { RS=1; RW=0; P0=date; delay_ms(5); EN=1; delay_ms(5); EN=0; } void lcd_display(uchar position,uint speed)//lcd1602显示函数 { write_com(0x80+position);//LCD1602字符型液晶显示器第一行第显示 write_data(table[speed]);//显示字符串 write_com(0x80+0x40+position);//LCD1602字符型液晶显示器第二行显示 write_data(table[speed]);//显示字符串 } void lcd_init()//lcd1602初始化函数 { write_com(0x38); write_com(0x0c); write_com(0x06); write_com(0x01); } int PIDControl()//PID偏差计算函数 { Error=expc_speed-real_speed;//计算偏差 //进行增量式PID计算 out=out+Proportion*(Error-LastError)+Integral*Error+Derivative*(Error+PrevError-2*LastError); //进行误差更新 PrevError=LastError; LastError=Error; //上下限幅处理 if(out<0) out=0;//驱动电机的电压不可能小于零,故当输出小于零时,输出应该值为零。 if(out>255) out=255;//DA转换为8位,最大数值为255,故当大于255时,限制为255。 return out; } void iic_init()//iic初始化函数 { SDA=1; delay(); SCL=1; delay(); } void start()//iic开始信号函数 { SDA=1; delay(); SCL=1; delay(); SDA=0; delay(); } void stop()//iic停止信号函数 { SDA=0; delay(); SCL=1; delay(); SDA=1; delay(); } void respons()//iic应答函数 ,相当于一个智能的延时函数。 { uchar i; SCL=1; delay(); while((SDA==1)&&(i<250)) i++; SCL=0; delay(); } // uchar read_byte()//iic读一个字节数据函数 //{ // uchar i,k; // SCL=0; // delay(); // SDA=1; // delay(); // for(i=0;i<8;i++) // { // SCL=1; // delay(); // k=(k<<1)|SDA;//先左移一位,再在最低位接受当前位。 // SCL=0; // delay(); // } // return k; // } void write_byte(uchar date)//iic写一字节数据函数 { uchar i,temp; temp=date; for(i=0;i<8;i++) { temp=temp<<1; //左移一位,移出的一位在CY中。 SCL=0;//只有在scl=0时sda能变化值 delay(); SDA=CY; delay(); SCL=1; delay(); } SCL=0; delay(); SDA=1; delay(); } void write_add(uchar control,uchar date)//PCF8591的DA转换函数 { start(); write_byte(0x91); respons(); write_byte(control); respons(); write_byte(date); respons(); stop(); } // uchar read_add(uchar control)//PCF8591的AD转换函数 //{ // uchar date; // start(); // write_byte(0x91); // respons(); // write_byte(control); // respons(); // start(); // write_byte(0x91+1);//把最后一位变成1,读数据。 // respons(); // date=read_byte(); // stop(); // return date; // } void SystemInit()// { TMOD=0x21;//设定时器0为工作方式1,定时器1为工作方式2(自动重装初值)。 TH0=0x3c;//设定50ms一次中断 TL0=0xb0; TH1=0x9c;//设定100us一次中断 TL1=0x9c; EA=1;//开总中断 EX0=1;//开外部中断0 IT0=1;//启动下降沿触发有效 ET0=1;//开定时器0中断 ET1=1;//开定时器1中断 TR0=1;//启动定时器0 TR1=1;//启动定时器1 } void exter0() interrupt 0//外部中断0函数 { Inlpuse++;//M法测速度(外部中断0和定时器0用在M法测速上) } void timer0() interrupt 1//定时器0中断函数 { TH0=0x3c;//重装初值 TL0=0xb0; time++; if(time>=20)//1s钟读取一次转速 { EX0=0; TR0=0; real_speed=Inlpuse;//计算转速 Inlpuse=0; value=PIDControl(); write_add(0x40,value);//进行DA转换,将数字量转换为模拟量。 EX0=1; TR0=1; } } void time1() interrupt 3//定时器1中断函数 { count++; if(count>=100) count=0; if(count<out) pwm=1; else pwm=0; } void main() { unsigned char num; SystemInit(); lcd_init(); iic_init(); while (1)//主循环 { num=KeyScan();//循环调用按键扫描 switch (num) { case 1: { if(expc_speed<CYCLE) expc_speed++; } break; case 2: { if(expc_speed>0) expc_speed--; } } lcd_display(4,expc_speed); lcd_display(5,real_speed); } }