#include <reg52.h> |
| |
| #include <string.h> |
| |
| |
| |
| typedef unsigned char uchar; |
| typedef unsigned int uint; |
| |
| sbit m1 = P2^0; //pwm信号1 |
| sbit m2 = P2^1; //pwm信号2 |
| |
| sbit key0 = P3^3; //反接制动 |
| sbit key1 = P3^4; //方向切换及启动 |
| sbit key2 = P3^5; //能耗制动 |
| sbit key3 = P3^6; //加速 |
| sbit key4 = P3^7; //减速 |
| |
| sbit BUTTON0 = P3^2; //外部中断0 |
| //sbit BUTTON1 = P3^3; //外部中断1 |
| |
| sbit RS = P0^2; |
| sbit RW = P0^1; |
| sbit EN = P0^0; |
| sbit relay = P0^3; |
| sbit led = P2^7; |
| |
| unsigned char flag=1,sflag=0; //flag:转向标志,1为正转、0为翻转 |
| //sflag:启动标志,1为转动状态、0为停止状态 |
| unsigned int t1=0; //t1:定时器中断计数 |
| int time=10; //time:占空比(0~10) |
| int freq,num; // num是外部中断计数值,freq保存外部中断的次数 |
| char cnt=0; // 定时器0 1s计数值 |
| uint pp; // 定时器0 1s计数值,根据程序修改,本程序取pp=20时定时满1s |
| |
| void delayms(unsigned int xms); //微秒级延时函数 |
| void key(); //按键功能函数 |
| void Initial(); //初始化函数,包括定时器0(中断),定时器1,外部中断 |
| void Read_Busy(); //读写检查函数 |
| void Write_Cmd(uchar cmd); //写指令函数 |
| void Write_Dat(uchar dat); //写入数据 |
| void LCD1602_Init(); //LCD初始化 |
| void PrintStr(char *str); // LCD输出字符串 |
| void DisplayNum(int m); // LCD显示数值 |
| void delay(unsigned int x); |
| |
| /*****************主函数***********************/ |
| void main() |
| { |
| |
| m1=1; |
| m2=1; |
| |
| Initial(); // 初始化定时器0,1,外部中断 |
| LCD1602_Init(); // 初始化LCD1602 |
| relay = 0; // 正常工作情况下继电器不能得电 |
| led = 0; |
| while(1) |
| { |
| key(); |
| |
| Write_Cmd(0x80 | 0x00); // 显示第1行 |
| PrintStr("Speed:"); |
| |
| DisplayNum(freq); |
| |
| PrintStr(" rpm"); |
| Write_Cmd(0xc0| 0x00); // 显示第二行 |
| PrintStr("Duty:"); |
| DisplayNum(time*10); |
| Write_Dat('%'); |
| |
| } |
| } |
| |
| |
| |
| /************初始化函数**************** |
| * |
| * 功能描述:实现对定时器0,定时器1,外部中断1中断 |
| * |
| ************************************/ |
| void Initial() |
| { |
| TMOD=0X11; //设置定时器0 和 定时器1,工作方式1,16为定时 |
| |
| TH1 = 0XFC; //定时器0初值1ms |
| TL1 = 0X66; |
| IP = 0X00; // 中断优先级控制 |
| |
| //定时器1初值50ms |
| TH0 = 0x4c; //高8位 |
| TL0 = 0X00; //低8位 |
| |
| EX0 = 1; // 开启外部中断0,默认中断优先级为0 |
| EX1 = 1; // 开启外部中断0,默认中断优先级为2 |
| IT0 = 1; // 外部中断触发方式为脉冲触发 |
| TR0 = 1; // 定时器0启动由后面启动 |
| //TR1 = 1; // 定时器1用来参数PWM波,按下启动按钮后才启动 |
| ET0 = 1; // 开定时器0中断 |
| ET1 = 1; // 开定时器1中断 |
| EA=1; // 开总中断 |
| } |
| |
| /******************************************** |
| * |
| * 函数描述:定时器1中断函数,默认中断编号3 |
| * |
| * t1 PWM波 |
| |-----| |-----| |
| | | | | |
| | | | | |
| ----- | |-----------| |--------- |
| |<--------------->| |
| time |
| ********************************************/ |
| |
| void timer1() interrupt 3 |
| { |
| TH1=0XFC; |
| TL1=0X66; |
| t1++; |
| if(t1<=time) |
| { |
| if(flag==1) //正转,P2^0 输出PWM |
| { |
| m1=1; |
| m2=0; |
| } |
| else //反转,P2^1 输出PWM |
| { |
| m1=0; |
| m2=1; |
| } |
| } |
| else |
| { |
| m1=1; |
| m2=1; |
| } |
| if(t1==10) //定时满 1ms |
| { |
| t1=0; |
| } |
| } |
| |
| /** |
| * |
| * 函数描述:定时器0中断函数,默认中断编号1,处理 |
| * 外部中断的数值 |
| * |
| */ |
| |
| void timer0() interrupt 1 |
| { |
| TH0 = 0x4C; //定时器1初值50ms |
| TL0 = 0X00; |
| |
| pp++; |
| |
| if(pp ==20) //计时满 1s |
| { |
| freq = num/6-25; //获取1s内中断计数的值num,并进行数值处理,这个根据个人不同需求修改 |
| if(freq<=0) |
| freq=0; |
| num = 0; |
| //cnt++; |
| //led = ~led; |
| pp = 0; |
| } |
| } |
| |
| /** |
| * @brief 外部中断0,P3^2口每来一次脉冲就计数值加1 |
| 记录特定时间内的脉冲数,可用作频率计 |
| * |
| */ |
| |
| void extern_int0(void) interrupt 0 //外部中断0 |
| { |
| //led = ~led; |
| num ++; // 外部中断计数 |
| } |
| |
| /** |
| * @brief 外部中断1,控制反接制动,反接约2s(软件延时) |
| * |
| */ |
| |
| void extern_int1(void) interrupt 2 //外部中断1 |
| { |
| TR1 = 0; //关闭定时器1,即关闭PWM输出 |
| |
| if(flag) //判断是由正转停止还是由反转状态下停止, |
| { |
| m1 = 0; |
| m2 = 1; //反接制动 |
| delayms(2000); //延时2s |
| m1 = 1; //停止时m1,m2都输出高电平 |
| sflag=0; |
| } |
| else if(!flag) |
| { |
| m1 = 1; |
| m2 = 0; |
| delayms(2000); |
| m2 = 1; |
| sflag=0; |
| } |
| } |
| |
| /* |
| //判断液晶忙,如果忙则等待,因为1602也是一个CPU,要处理原来的指令,如果不判断会导致数据紊乱 |
| void Read_Busy() //读写检查函数 |
| { |
| uchar busy; |
| P1 = 0xFF; //P0口作为数据端 |
| RS = 0; |
| RW = 1; //读状态的操作时序为 RS=L,RW=H,E=H,D0~D7输出状态字 |
| do |
| { |
| EN = 1; |
| busy = P1; |
| EN = 0; |
| }while(busy & 0x80); |
| //状态字为busy(8位2进制数)的最高位, |
| //若为1则禁止读写,为0则允许读写,该状态用busy&0x80的结果表示 |
| } |
| |
| */ |
| |
| |
| /******************************************* |
| * |
| * 函数描述:写指令函数 |
| * |
| *******************************************/ |
| void Write_Cmd(uchar cmd) //写指令函数 |
| { |
| //Read_Busy(); //对控制器每次进行读写操作都要判断是否正忙,即要进行读写检测 |
| P1 = cmd; //写入十六进制形式的指令(command) |
| RS = 0; |
| EN = 0; |
| delay(10); |
| |
| EN = 1; //写指令的操作时序:RS=0,RW=0,EN=高脉冲 |
| delay(10); |
| EN = 0; //获得高脉冲后使能端重新置零 |
| } |
| |
| |
| void Write_Dat(uchar dat) //写入数据 |
| { |
| // Read_Busy(); //写入数据前进行读写检测 |
| P1 = dat; //P0口写入数据 |
| RS = 1; |
| EN = 0; |
| |
| delay(10); |
| |
| EN = 1; //写数据操作时序:RS=0,RW=0,EN=高脉冲 |
| delay(10); |
| EN = 0; //获得高脉冲后使能端重新置零 |
| } |
| |
| void LCD1602_Init() |
| { |
| RW = 0; |
| Write_Cmd(0x38);//设置16*2显示 |
| delay(8); |
| Write_Cmd(0x0f);//开显示 显示光标,光标闪烁 |
| delay(8); |
| Write_Cmd(0x01);//清屏 |
| delay(8); |
| Write_Cmd(0x06);//地址指针移位命令 |
| delay(8); |
| //Write_Cmd(0x80 | 0x00);//显示地址,0x80是第一行的的首地址。0x80|0x06表示数据从第一行第7个字符位置开始显示 |
| |
| } |
| |
| /** |
| * @brief LCD1602打印字符串的函数,使用到了strlen函数,需要引入string.h头文件 |
| * |
| */ |
| |
| void PrintStr(char *str) // LCD输出字符串 |
| { |
| |
| char i,len; |
| len = strlen(str); // 获取字符串长度 |
| for(i=0;i<len;i++) |
| { |
| Write_Dat(*str); |
| str++; |
| } |
| } |
| |
| /** |
| * @brief LCD1602显示整数,显示4位数 |
| * @note |
| * 希望补充显示浮点型数值,未完成功能 |
| */ |
| void DisplayNum(int m) // LCD显示数值 |
| { |
| |
| int disdata[4]; // 显示占空比的数值,存到此数组 |
| disdata[0] = m/1000+0x30; |
| disdata[1] = m%1000/100+0x30; |
| disdata[2] = m%100/10+0x30; |
| disdata[3] = m%10+0x30; |
| |
| if(disdata[0]==0x30) //如果千位为0,让LCD不显示,0x30和0x20可以查询ACSII码表 |
| { |
| disdata[0]=0x20; |
| if( disdata[1]==0x30) //如果百位为0,让LCD不显示,0x30和0x20可以查询ACSII码表 |
| { |
| disdata[1]=0x20; |
| if( disdata[2]==0x30) //如果百位为0,让LCD不显示,0x30和0x20可以查询ACSII码表 |
| { |
| disdata[2]=0x20; |
| } |
| } |
| } |
| |
| Write_Dat(disdata[0]); |
| delay(4); |
| Write_Dat(disdata[1]); |
| delay(4); |
| Write_Dat(disdata[2]); |
| delay(4); |
| Write_Dat(disdata[3]); |
| delay(4); |
| |
| |
| } |
| |
| /** |
| * |
| * @brief 按键功能函数,包括启动/反转,能耗制动,加速,减速 |
| * |
| */ |
| void key() |
| { |
| if(key1==0) //转向及启动 |
| { |
| delayms(5); |
| if(key1==0) |
| { |
| while(key1==0); |
| if(sflag==1) //为了实现停止后再启动保持之前的转向 |
| { |
| flag=!flag; |
| TR1=1; //开启定时器1,即开始PWM波输出 |
| } |
| else |
| { |
| TR1=1; |
| sflag=1; |
| } |
| } |
| } |
| else if(key2==0) //停止 |
| { |
| delayms(5); |
| if(key2==0) |
| { |
| while(key2==0); |
| relay=1; //能耗制动 |
| TR1=0; //关闭定时器1,即关闭PWM输出 |
| m1=1; |
| m2=1; |
| sflag=0; |
| } |
| } |
| else if(key3==0) //加速 |
| { |
| delayms(5); |
| if(key3==0) |
| { |
| while(key3==0); |
| if(time>=10) |
| time=10; |
| else |
| time++; |
| } |
| } |
| else if(key4==0) //减速 |
| { |
| delayms(5); |
| if(key4==0) |
| { |
| while(key4==0); |
| if(time==0) |
| time=0; |
| else |
| time--; |
| } |
| } |
| /* |
| else if(key0==0) // 按下反接制动 |
| { |
| delayms(5); //软件消抖 |
| if(key0==0) //再次检测到按下 |
| { |
| while(key0==0); |
| TR1 = 0; //关闭定时器1,即关闭PWM输出 |
| |
| if(flag) //判断 是由正转停止还是由反转状态下停止 |
| { |
| m1 = 0; |
| m2 = 1; //反接制动 |
| // delayms(10); //延时1.5s |
| m1 = 1; //停止时m1,m2都输出高电平 |
| sflag=0; |
| } |
| else if(!flag) |
| { |
| m1 = 1; |
| m2 = 0; |
| // delayms(150); |
| m2 = 1; |
| } |
| } |
| } |
| */ |
| |
| } |
| |
| |
| /** |
| * @brief 软件延时函数 |
| * |
| */ |
| void delayms(unsigned int xms) |
| { |
| unsigned int i,j; |
| for(i=xms;i>0;i--) |
| for(j=110;j>0;j--); |
| } |
| |
| /** |
| * @brief 软件延时函数 |
| * |
| */ |
| |
| void delay(unsigned int x) |
| { |
| unsigned int a,b; |
| for(a=x;a>0;a--) |
| for(b=10;b>0;b--); |
| } |