仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- #include <intrins.h> //引用程序目录的相对路径中的头文件
- #include <reg52.h> //引用编译器的类库路径里面的头文件
- #define LCD P2 //LCD2004数据接口
- sbit RS=P1^2; //设置RS引脚接口,RS=0,指令寄存器;RS=1,数据寄存器
- sbit RW=P1^1; //设置R/W引脚接口,R/W=0,写;R/W=1,读
- sbit E=P1^0; //设置E引脚接口,E允许信号
- unsigned char RPM_R[]="000"; //右电机转速,H整型转换为字符型,定义数组长度。
- unsigned char RPM_L[]="000"; //左电机转速,H整型转换为字符型,定义数组长度。
- unsigned char V_R[]="000"; //右电机时速,H整型转换为字符型,定义数组长度。
- unsigned char V_L[]="000"; //左电机时速,H整型转换为字符型,定义数组长度。
- unsigned int rpm_r=0; //初始化右电机转速
- unsigned int rpm_l=0; //初始化左电机转速
- unsigned int v_r=0; //初始化右电机时速
- unsigned int v_l=0; //初始化左电机时速
- unsigned int D=770; //直径
- unsigned char cnt=0; //中断次数倍数,50ms为单位,若20倍,组成1秒
- unsigned char flag=0; //时钟标志位
- /******************************
- LCD忙检测函数子程序
- ******************************/
- void CheckBusy(void)
- {
- unsigned int nTimeOut=0;
- RS=0;
- RW=1;
- E=0;
- E=1;
- while((LCD&0x80)&&(++nTimeOut !=0));
- E=0;
- RS=0;
- RW=1;
- }
-
- /******************************
- LCD发送命令或数据子程序
- ******************************/
- void SendCmdorData(unsigned char byCmdorData,bit DI)
- {
- CheckBusy();
- RS=DI; //为寄存器选择脚,高电平时选择数据寄存器、低电平时选择指令寄存器
- RW=0; //为读、写信号线,低电平时进行写操作
- E=0; //E端为使能端,当E端由高电平跳变为低电平时,液晶模块执行命令
- LCD=byCmdorData; //送数据
- _nop_(); //空操作,给硬件反应时间
- _nop_(); //空操作,给硬件反应时间
- E=1;
- _nop_(); //空操作,给硬件反应时间
- _nop_(); //空操作,给硬件反应时间
- E=0; //E引脚要有一个下降沿,才能把数据线上数据,命令送入1602
- RW=1; //为读、写信号线,高电平时进行读操作
- RS=0; //为寄存器选择脚,低电平时选择指令寄存器
- }
- /******************************
- 延时1毫秒
- ******************************/
- void Delay(unsigned int nDelay)
- {
- unsigned int i;
- for(;nDelay>0;nDelay--)
- {
- for(i=124;i>0;i--);
- }
- }
- /******************************
- LCD初始化子程序
- ******************************/
- void Init(void)
- {
- SendCmdorData(0x38,0); //写命令,设置16*2显示,5*7点阵,8位数据接口
- Delay(50); //50ms延时,给硬件反应时间
- SendCmdorData(0x01,0); //写命令,显示清屏:1、数据指针清零 2、所有显示清零
- Delay(50); //50ms延时,给硬件反应时间
- SendCmdorData(0x06,0); //写命令,当读或写一个字符后地址指针加一,且光标加一
- Delay(50); //50ms延时,给硬件反应时间
- SendCmdorData(0x0c,0); //写命令,开显示、不显示光标、光标不显示
- Delay(50); //50ms延时,给硬件反应时间
- }
- /******************************
- 地址转换子程序
- x为行号 y为列号
- ******************************/
- void SetAddress(unsigned char x,y)
- {
- unsigned char byAddress;
- switch(x)
- {
- case 1:byAddress=0x80+y;break; //第一行首地址
- case 2:byAddress=0xC0+y;break; //第二行首地址
- case 3:byAddress=0x94+y;break; //第三行首地址
- case 4:byAddress=0xD4+y;break; //第四行首地址
- default:break; //default就是如果没有符合的case就执行它,default并不是必须的.
- }
- SendCmdorData(byAddress,0); //执行发送指令子程序
- }
- /******************************
- 发送字符串到LCD子程序
- ******************************/
- void PutString(unsigned char x,y,unsigned char *pData)
- {
- SetAddress(x,y); //执行地址转换子程序
- while(*pData !='\0') //表示显示内容不为空时,执行循环体的代码段,
- {
- SendCmdorData(*pData++,1); //发送命令子程序,*就是声明他的类型,是一个指向整型数据的指针
- }
- }
- /******************************
- 4位数的H整型转换为字符型, 加48为转ASCII。
- *就是声明他的类型,是一个指向整型数据的指针
- ******************************/
- void Inttochar4(unsigned int a,unsigned char *chx)
- {
- chx[0]=a/1000+48; //高电平千位
- chx[1]=(a/100)%10+48; //高电平百位
- chx[2]=(a/10)%10+48; //高电平十位
- chx[3]=a%10+48; //高电平个位
- }
- /******************************
- 3位数的H整型转换为字符型,加48为转ASCII。
- *就是声明他的类型,是一个指向整型数据的指针
- ******************************/
- void Inttochar3(unsigned int a,unsigned char *chx)
- {
- chx[0]=a/100+48; //百分数百位
- chx[1]=(a/10)%10+48; //百分数十位
- chx[2]=a%10+48; //百分数个位
- }
- /******************************
- LCD数据显示子程序
- ******************************/
- void Display(void)
- {
- Inttochar3(rpm_r,(unsigned char *)RPM_R); //右电机转速,H整型转换为字符型。*就是声明他的类型,是一个指向整型数据的指针
- Inttochar3(rpm_l,(unsigned char *)RPM_L); //左电机转速,H整型转换为字符型。*就是声明他的类型,是一个指向整型数据的指针
- Inttochar3(v_r,(unsigned char *)V_R); //右电机速度,H整型转换为字符型。*就是声明他的类型,是一个指向整型数据的指针
- Inttochar3(v_l,(unsigned char *)V_L); //左电机速度,H整型转换为字符型。*就是声明他的类型,是一个指向整型数据的指针
- PutString(1,4,"R:"); //字体,2字节
- PutString(1,7,RPM_R); //RPM_R为右电机转速的ASCII数组,3字节
- PutString(1,11,"r/min"); //字体,5字节
- PutString(2,4,"L:"); //字体,2字节
- PutString(2,7,RPM_L); //RPM_L为左电机转速的ASCII数组,3字节
- PutString(2,11,"r/min"); //字体,5字节
- PutString(3,4,"R:"); //字体,2字节
- PutString(3,7,V_R); //V_R为右电机速度的ASCII数组,3字节
- PutString(3,11,"km/h"); //字体,4字节
- PutString(4,4,"L:"); //字体,2字节
- PutString(4,7,V_L); //V_L为左电机速度的ASCII数组,3字节
- PutString(4,11,"km/h"); //字体,4字节
- }
- /******************************
- 单片机初始化
- ******************************/
- void Initial()
- {
- TMOD=0x55; //定时器T0、T1工作于计数器模式1;解释:C/T=1时为计数功能:加1计数器对来自输入引脚T0(P3.4)和T1(P3.5)的外信号脉冲进行计数;M1=0、M0=1,方式1:16位定时器/计数器
- RCAP2H=(65536-50000)/256; //定时/计数器2陷阱寄存器高字节,50000次溢出一次产生中断
- RCAP2L=(65536-50000)%256; //定时/计数器2陷阱寄存器低字节,50ms中断
- ET2=1; //打开定时器中断
- TR2=1; //打开定时器开关
- EA=1; //打开总中断
- TR0=1; //打开定时器开关
- TR1=1; //打开定时器开关
- }
- /******************************
- 主程序
- ******************************/
- void main()
- {
- Init(); //LCD初始化子程序
- Initial(); //单片机初始化
- while(1)
- {
- TH0=0; //定时器T0高八位的初值
- TL0=0; //定时器T0低八位的初值
- TH1=0; //定时器T1高八位的初值
- TL1=0; //定时器T1低八位的初值
- flag=0; //时钟标志位
- while(flag==0); // 时间未满等待
- rpm_r=(TH0*256+TL0)*60/20; //计算转速,乘以60秒,除以每周产生20个脉冲,单位为r/min
- v_r=D*3.14*(TH0*256+TL0)/20*3.6/1000; //v=2π*R*n,线速度为v,单位为m/s,转速为n,单位为r/s,1m/s=3.6km/h
- rpm_l=(TH1*256+TL1)*60/20; //计算转速,除以每周产生20个脉冲,单位为r/s
- v_l=D*3.14*(TH1*256+TL1)/20*3.6/1000; //v=2π*R*n,线速度为v,单位为m/s,转速为n,单位为r/s,1m/s=3.6km/h
- Display();
- }
- }
- /******************************
- 中断0(P3.2),C1接收个数累加
- ******************************/
- void time2() interrupt 5//定时器T2中断服务程序
- {
- TF2=0; //定时器2溢出标志位,注意!定时器2必须由软件对溢出标志位清零,硬件不能清零,这里与定时器0和定时器1不同。
- cnt++; //中断次数倍数,50ms为单位
- if(cnt==20) //中断20次,每次50ms秒,累加1s。
- {
- flag=1; //时钟标志位
- cnt=0; //中断次数倍数
- }
- }
复制代码
Keil代码下载:
OPG测速1.rar
(84.35 KB, 下载次数: 21)
|