/***********
提供控制器开发订做、单片机项目开发、工控测控传感自动化系统构建、仪器仪表测试设备定制、软件开发EXE编程等服务。永珂科技工作室 QQ 2531263726
http://shop73510260.taobao.com/
***********/
/*----------------载入头文件--------------------*/
#include <reg52.h>
#include <intrins.h>
/*----------------IO口设置----------------------*/
sbit a=P2^4;//L298信号口,{ a=1 ;b=0;} //启动输出 正转
sbit b=P2^5;// { a=0 ;b=1;} //启动输出 反转
sbit K1 =P1^0 ; //减少键
sbit K2 =P1^1 ; //增加键
sbit K3 =P1^2 ; //正转键
sbit K4 =P1^3 ; //反转键
#define LCD_DATA P0 //LCD接口
sbit LCD_RS = P2^0;
sbit LCD_RW = P2^1;
sbit LCD_EN = P2^2;
/*----------------全局变量设置-------------------*/
unsigned int speed_set=6; //设置的转速,单位rpm
unsigned int speed_m=0; //测量的当前转速
unsigned char zhuanxiang=1; //转向标志,0不转,1正转,2反转.默认正转
unsigned char jishi=0;//计时
unsigned long pusle=0;//脉冲间隔时间
//LCD的变量
unsigned char dispBuff0[16]={'S','e','t',
':',' ',' ',' ',' ','R','P','M',' ',' ',' ',' ',' '};//LCD第一行显示
unsigned char dispBuff1[16]={'N','o','w',
':',' ',' ',' ',' ','R','P','M',' ',' ',' ',' ',' '};//LCD第二行显示
//PID算法的变量
int Now_speed[3]={0,0,0}; //用于存储当前转速、前一次转速、再前一次转速
int KP=20;//10000us,160rpm的比约为70
int KI=2;
int KD=0;
int last_out=0;
#define out_max 10000
#define out_min 0
unsigned int PWMH=0;//PWM波在10000us内高电平时间
bit pid_lock=1;//PID锁存标志位,1表示可以改变定时器1中初值为PWMH
/*----------------子程序预定义--------------------*/
void Delay20ms() ;//延时函数
void LCD_Init(void);//LCD子程序
void LCD_WriteDat(unsigned char lcd_dat);
void LCD_WriteCmd(unsigned char lcd_cmd);
unsigned char LCD_ReadStatus(void);
void LCD_Goto(unsigned char x,unsigned char y);
void LCD_Display(unsigned char row,unsigned char *str);
void key();//按键子程序
void Motor_control(void) ;//PID控制电机
/*----------------主程序----------------------*/
void main()
{
/*计时器2设置,用于测量电机脉冲间隔*/
TCLK=0; //可令T2CON=0;或TCLK=0,RCLK=0;
RCLK=0; //【T2CON中其他位可默认为0,而TCLK和RCLK必须手动置0】
//因RCAP2L和RCAP2H是由软件预设的
/*说明
补充:52单片机定时器2作为16位自动重装计时器。
RCAP2H=0xFF; // 定时器2的重装载值,
RCAP2L=0xFD; //16位自动再装入值:定时器2溢出时(CP/RL2=0时),会把 RCAP2H, RCAP2L 中的数据装入TH2,TL2
*/
RCAP2H=(65536-1000) / 256; // TH2 = (65536-X) / 256;
RCAP2L=(65536-1000) % 256; // TL2 = (65536-X) % 256;
TH2=RCAP2H;
TL2=RCAP2L;
ET2 = 1;
TR2 = 1;
/*计时器0设置,用于设置PWM的周期*/
TMOD=0x11;//定时器0、1工作于方式1
TH0=(65536-10000) / 256;//10000us ,计时器0用于定时产生PWM周期,周期10ms
TL0=(65536-10000) % 256;
ET0=1;
TR0=1;//开始计时
/*计时器1设置,用于设置PWM的高电平时间*/
TH1=0;
TL1=0;
ET1=1;
TR1=0;//停止计时
/*外部中断0设置*/
IT0=1; //外部中断0,0低电平触发,1边沿触发
EX0=1; //打开外部中断0
EA=1;//开全局中断
/*LCD显示*/
LCD_Init();//LCD初始化
LCD_Display(0,dispBuff0);
LCD_Display(1,dispBuff1);
Delay20ms();
while(1)
{key();//按键识别
Delay20ms();
}
}
/*----------------子程序----------------------*/
//LCD子程序
void LCD_Init(void)
{
Delay20ms();
/*************************************************
功能设置: DB5=1;DB6...=0;
DB4=1:8位接口; 0:4位接口;
DB3=1:双行显示;0:单行显示;
DB3=1:5×10点阵;0:5×7点阵;
*************************************************/
LCD_WriteCmd(0x38); //8位机接口、双行显示、5×7字符点阵;
/*************************************************
显示控制:DB3=1;DB4...=0;
DB2=1:开启显示;0:关闭LCD显示;
DB1=1:显示光标;0:光标隐藏;
DB0=1:光标闪烁;0:光标闪烁关闭;
*************************************************/
LCD_WriteCmd(0x0c); //显示开启、光标不显示也不闪烁;
/*************************************************
清屏指令:DB0=1;DB1...=0;
*************************************************/
LCD_WriteCmd(0x01); //清屏;
/****************************************************************
访问模式:DB2=1;DB3...=0;
DB1=1:对内部Ram读写后AC自增,否则自减;
DB0=1:与DB1设置有关,对DD RAM写时同时整屏移动1位;
*****************************************************************/
LCD_WriteCmd(0x06); //光标右移一位、整屏不移动;
LCD_Goto(0,0);
}
void LCD_WriteDat(unsigned char lcd_dat)
{
unsigned char tmp;
tmp = LCD_ReadStatus(); //读状态;
while((tmp & 0x80)) //是否忙 ?
{
tmp = LCD_ReadStatus();
}
LCD_RS = 1;
LCD_RW = 0;
LCD_DATA = lcd_dat;
_nop_();
LCD_EN = 0;
_nop_();
_nop_();
LCD_EN = 1;
}
void LCD_WriteCmd(unsigned char lcd_cmd)
{
unsigned char tmp;
tmp = LCD_ReadStatus();
while((tmp & 0x80))
{
tmp = LCD_ReadStatus();
}
LCD_RS = 0;
LCD_RW = 0;
LCD_DATA = lcd_cmd;
_nop_();
LCD_EN = 0;
_nop_();
_nop_();
LCD_EN = 1;
}
unsigned char LCD_ReadStatus(void)
{
unsigned char tmp;
#if 0
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 1;
tmp = LCD_DATA;
LCD_EN = 0;
#endif
LCD_DATA = 0xff;
LCD_RS = 0;
LCD_RW = 1;
LCD_EN = 0;
_nop_();
_nop_();
LCD_EN = 1;
tmp = LCD_DATA;
return tmp;
}
void LCD_Goto(unsigned char x,unsigned char y)
{
unsigned char tmp;
if(y) //若是第二行;
{
tmp = 0xc0 + x;
LCD_WriteCmd(tmp);
}
else
{
tmp = 0x80 + x;
LCD_WriteCmd(tmp);
}
}
void LCD_Display(unsigned char row,unsigned char *str)
{
if(row)
{
LCD_Goto(0,1);
}
else
{
LCD_Goto(0,0);
}
while(*str != '\0')
{
LCD_WriteDat(*str++);
}
}
//延时函数
void Delay20ms() //粗略延时;
{
unsigned int tmp = 50000;
while(tmp--);
}
//按键识别程序
void key() //正转反转调节 zhuanxiang: 转向标志,0不转,1正转,2反转
{ unsigned char speed_m0;
if(!K1) //减速
{ if (speed_set>6)
{speed_set--;}
}
if(!K2) //加速
{ if (speed_set<150)
{speed_set++;}
}
dispBuff0[4]=(speed_set/1000)%10+'0';dispBuff0[5]=(speed_set/100)%10+'0';
dispBuff0[6]=(speed_set/10)%10+'0';dispBuff0[7]=(speed_set)%10+'0';
LCD_Display(0,dispBuff0);
speed_m0=speed_m;
dispBuff1[4]=(speed_m0/1000)%10+'0';dispBuff1[5]=(speed_m0/100)%10+'0';
dispBuff1[6]=(speed_m0/10)%10+'0';dispBuff1[7]=(speed_m0)%10+'0';
LCD_Display(1,dispBuff1);
if(!K3 && K4) //正转
{
zhuanxiang=1;
}
if(!K4 && K3) //反转
{
zhuanxiang=2;
}
if(!K4 && !K3) //两键按下,停止
{
zhuanxiang=0;
}
}
//外部中断0程序,测转速。根据转一圈需要的时间计算转速。
void int0() interrupt 0 //外部中断0中断处理程序,用于脉冲计数
{
//仿真中的motor encoder,每一圈中间引脚产生一个脉冲,显示的数值单位为RPM
/*******
motor encoder,中间的是编码器转一周,高电平一次。根据这可以测出转速。
左右两边是检测左转还是右转。每周高电平10次(可以在PROTEUS里面设置),哪边先高电平,
就是往哪边转。。
********/
unsigned long temp;
temp=1000UL*60UL/10UL/pusle; //一圈10个脉冲 注意加UL
speed_m=(unsigned int)temp;//计算转速rpm
pusle=0;//脉冲间隔时间清零
}
void timer0 (void) interrupt 1 //定时器0中断,用于定时产生PWM周期,一个PWM周期为10000us (10ms)
{ jishi++;
if (jishi>=20)//200ms计算一次PID,网上的人说5ms 10ms都可以
{
Motor_control() ;
jishi=0;
}
if (pid_lock==1)
{
TH1=((65536-PWMH) & 0xff00)>>8;//需要加锁定
TL1=(65536-PWMH) & 0x00ff;
TR1=1;//打开计时器1,产生PWM波的高电平。定时器1中断程序触发后输出PWM低电平。
pid_lock=0;//不允许改变PWMH
}
TH0=(65536-10000)/256;//10000us
TL0=(65536-10000)%256;
switch (zhuanxiang)
{case 0:
{a=0 ;b=0;break;} //停止
case 1:
{a=1 ;b=0;break;} //启动输出-正转
case 2:
{a=0 ;b=1;break;} //启动输出-反转
default:break;
}
}
void timer1 (void) interrupt 3 //定时器1中断,用于