#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);
}
}
https://www.cnblogs.com/AChenWeiqiangA/p/12850554.html
如果文章或资源对您有帮助,欢迎打赏作者。一路走来,感谢有您!
txttool.com 说一段 esp56物联 查询128 IP查询