仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
- 设计直流电机驱动。
- 设计液晶屏(LCD1602)显示电路和独立键盘输入电路。
- 能准确显示电机状态及转速。
- 能通过按键控制电机状态及转速。
2.1 设计方案方案一:单片机的四组并行口(P0、P1、P2、P3)在复位时是高电平,那么在设计按键接口时可以设计为低电平有效。当低电平时判断按键被按下去,执行相关的操作。 按键按下去会存在抖动现象。如果对按键进行计数,会发现虽然只按下去一次,所计的数却不一致。这样会导致错误的操作。因此需要对按键进行消抖,按键接口只是执行单纯的被检测功能,需要特殊的功能寄存器,所以使用P1口。 显示模块采用LCD1602液晶屏,使用单片机的P0口。P0比较特殊,需要外接上拉电阻才能正常进行输出。考虑到简化仿真图的设计,故使用P0口作为LCD1602液晶屏是数据端口。 由于需要对电机进行测速和状态测,P3存在一些特殊寄存器可以使用,故保留P3口。其它接口均接在除P3口以外的接口。 直流电机的转速与正向电流持续时间成正比,使用PWM方式产生脉冲,电机转速进行调节。具体如何产生则使用定时器T0产生中断。 测速则使用T1定时器、外中断INT1组合测量。通过外中断INT1的负跳变来开启T1计时器,通过测量两次负脉冲的时间间隔即可得到转速(电机转动一圈发出一定时长的高电平)。 状态的测量则在再次使用外中断0、1来完成。状态测量采用这样的测量方式只需要测量一次即可,占用时间很短,并且准确。 方案二:对方案一的部分进行修改。按键部分为了响应准确,采用外接高电平的方法。 电机测速模块:仍然不变。 状态测量改成通过内部编程完成测量,不需要外中断0、1的参与。 方案三:仍然对方案一进行修改。按键部分为确保准确性,按键外部接上高电平。 PWM波形产生模块使用数据选择器对电机的正反转进行控制,降低编程难度。 2.2 方案选择方案一过于追求准确性,编程复杂,过多的开启中断实现功能与其它方案是一样的,但是大量标志位的开启需要特别注意,同时要小心时序问题。对编程能力、协调有比较高的要求。 方案二准确的提高了按键的准确性部分,通过仔细考虑,与开外中断测量比较,编程变简单。全部实现比较容易,但为保证准确性、需要通过编程进行调整。 方案三经过仔细考虑,使用数据选择器虽然降低了变成难度,在选择芯片、了解芯片可能需要多花时间。 最终认为方案二比较可行。 2.3 系统框图整个系统以单片机为核心,处理信息。 - 键盘:有7个键盘,每个键盘有不同的功能。键盘的输入信号流入单片机需要进行消抖,避免产生预期外的结果。
- STC89C51:负责整个系统的信息处理。包括键盘的输入信号处理、根据键盘的信号发出电机驱动信号、对电机的速度和转向进行测量、测量完成将信息送给显示。
- 电机驱动芯片:电机所需要的驱动电流相对于STC89C51直接输出的电流要打的多,对电机的控制不理想。反过来同样会影响到单片机的正常工作。所以需要通过驱动芯片来驱动直流电机。
- 电机:电机为MOTOR-ENCODER,有五条线。其中的两条用来输入电流驱动电机,剩余的三条为电机的编码输出。中间的线输出特征为电机转动一圈输出一个正脉冲。边上两个根线输出的脉冲可调,脉冲的相位相差90度,反应了电机的正反转。
- 显示:显示所用器件为LCD1602显示器,显示电机转速,正反转状态。
3.1 总体硬件设计总体硬件分为:晶体振荡器电路、复位电路、按键输入模块、显示电路、电机驱动电路、电机模块。如下图 3.1?1所示 图 3.1?1 3.2 晶体振荡器电路晶振X1采用11.0592MHZ,电容C1、C2采用典型值22pF。如图 3.2?1。该电路的功能是为单片机提供11.0529MHZ的时钟。 图 3.2?1 3.3 复位电路电路图 3.3?1如所示。复位电路电源为+5V、电容(极性电容)C3采用典型值10uF、电阻R7为820欧姆、按键。该电路为单片机复位端口提供一定时间的高电平,当单片机发生异常时可以通过按键使单片机复位。然后正常运行。其中EA/引脚在此电路中不需要使用,只是接上去了而已。 图 3.3?1 3.4 按键输入电路电路图如图 3.4?1所示。按键共有6个,分别有不同的功能。按键的功能在图上已经标出。图中的六个电阻阻值为1K欧姆,为限流电阻、还有降压的作用。电压没有标出统一为+5V。只有按下按键1才能启动其他按键。 图 3.4?1 3.5 显示电路电路图如图 3.5?1所示。液晶显示模块数据口与单片机的P0口连接,其它端口通过标号与P2.0、P2.1连接。P0口与液晶显示模块交换信息、P2.0、P2.1口对液晶进行读写、使能控制。 图 3.5?1 3.6 电机驱动电路电路图如图 3.6?1所示。驱动芯片型号为L298其驱动能力很强。VCC为芯片的电源+5V、VS为电机驱动电压,最高为45V。IN1、IN2接TTL电平、与单片机标号为ON、OFF的端口(电机控制流的输出端口)连接,ON、OFF端口如图 3.4?1所示。ENA、ENB为芯片的使能端,高电平时芯片工作。ENA控制端口IN1、IN2、OUT1、OUT2。低电平时端口无法工作,无法控制电机。OUT1、OUT2接直流电机输入端。图中接地的端口在图中用不到。 图 3.6?1 3.7 电机模块电路图如图 3.7?1所示。电机的输入端口为1、2。1为高电平时,电机正转2为高电平时,电机反转。1、2同为高或者低电平则不转,ENA低电平也不转。电机的A、B与单片机的A、B连接,用于电机状态的测量。IN1与单片机的IN1相连,用于测速。具体连接与标号在图中已标注。 图 3.7?1 4.1 图形界面设计4.1.1 按键界面设计如图 4.1?1所示
按键扫描模块中有6个按键,每个按键会调动相应的模块。并对数据处理。 4.1.2 LCD1602模块界面界面设计如图 4.1?2所示: 显示模块会显示电机的相关信息(正转、反转、速度、可以加上电机是否加速的、减速的测试、以及显示),显示前将依据按键输入的信号进行运算,处理后送入液晶中。 5.1 程序流程图主程序流程图如图 5.1?1所示。
6.1 硬件调试在确定方案后,便开始搭建硬件。从难易程度来看,该课题是比较简单的所以硬件部分搭建容易,搭建完成即可。开始方案是选择方案一,需要使用多个中断,需要修改的仅仅是电路的连接。后面发现方案一过于复杂,改用方案二,对需要修改的连接电路增加标号即可。 6.2 程序调试由于是自己写程序,经验不足、速度慢,容易出现问题。为了保证每一个环节到位,采用边写程序、边调试的方法。 最先开始的模块是显示模块。显示模块使用器件为LCD1602,在单片机的课程中学过做过关于这个器件的实验,对器件了解有一些,但不是很熟悉。对原来的程序没有进行封装,最开始出现数据送入位置错误,造成预期之外的结果。而后通过仔细查看课本上的资料,通过位置对照,修改后,液晶显示模块初步没有问题。 电机控制模块:对电机控制需要使用中断、在中断里面产生方波信号,并且方波的占空比可调,电机控制流输出通道选择也在这个模块。在这个模块完成前需要完成一部分键盘扫描函数的设计。当以上程序完成并运行后,通道选择和占空比都出现了问题。最开始是无论按什么键电机都不转,于是乎调整程序,首先发现单片机四组端口复位后输出为高电平,即表示电机停转,需要对电机输出端口初始化,都为低电平。然后进行控制。脉冲波形的占空比的调节,设立多个全局变量,设置上下限,这样波形占空比调节没有问题。电机输出信号通道选择,由于写程序经验不足,逻辑上出现了错误,导致想要外接数据选择器来调节信号输出,进而控制电机正反转。最后经过实验发现这个方法实现起来比较困难,思路没有。还是从中断里面进行调节好一些。最终实现了电机的调节。 电机转向测量模块:此模块完成后在多次的调试过程中发现使用while语句莫名奇妙对电极控制模块产生了影响。为了保证电机控制模块没有问题,对转向模块重新进行了设计,理论分析。但是由于时间问题,没能完成。 测速模块的调节:测速模块需要定时器1和外中断1完成。测速模块完成后需要与显示模块共同完成。由于此前在选择方案的时候,过分依赖中断和定时器,进行了较长时间的论证,电机控制模块的调试由于是个人编程思想和能力问题导致花费了大量的时间。测速模块虽然完成,但是没有时间调试。 6.3 心得体会在一周的实训中,发现了很多的问题,也收获了很多。最开始是方案的确定很困难,方案的确定需要对单片机的各个资源非常了解。这从侧面反映了个人对单片机不熟悉,以及特点的不了解,最终方案的敲定花费了大量的时间,导致后面编程、其它问题的解决时间不足。 从定时器T0的编程中发现自身的写程序能力非常薄弱,程序的结构很乱,在一定程度上来说完全是想到什么就写什么,毫无章法。更没有将程序封装起来、留做下次便以稍加修改即可使用。 总之,在这次实训中让我看到了自己各个方面的不足,这些不足通过平时的锻炼是可以补起来的,反映出学习方法存在问题,需要加以改进。通过实训,对单片机的部分硬件有了进一步的了解、熟悉。单片机实训是一个比较综合的课程,结合了C语言、单片机常识、模拟电路、数字电路、硬件设计、软件的使用、软件设计。对培养独立解决问题的能力、自主学习能力、创新思维能力有较大影响。
单片机源程序如下:
- #include <reg52.h>
- #include<string.h>
- #include <intrins.h>
- #define uchar unsigned char
- #define uint unsigned int
-
- sbit lcdrs=P2^0; //寄存器选择,0:数据寄存器。1:为状态/命令寄存器
- sbit lcden=P2^1; //--LCD控制引脚
- sbit speed=P3^2; //测速端口
- sbit ENA=P2^2; //电机使能控制端
- sbit key1=P1^0; //启动按键
- sbit key2=P1^1; //正转按键
- sbit key3=P1^2; //反转按键
- sbit key4=P1^3; //加速按键
- sbit key5=P1^4; //减速按键
- sbit key6=P1^5; //停止按键
- sbit Zheng=P1^6; //正转输出端口
- sbit Fan=P1^7; //反转输出端口
- uchar Timer0_flag=4;
- uchar Timer0_BiaoZhi=0;//初始化占空比为1:3(高:低)
- uchar gewei;
- uchar shiwei;
- uchar baiwei;
- /******标志位设置*****/
- uchar KEY1=0;//优先级标志
- uchar KEY2=0;//加速标志
- uchar INT1_FLAG=0;
- uchar COUNTER_high=0;
- uchar COUNTER_low=0;
- uchar SUM=0;
- uint TIMER1_COUNTER=0;
- uint INT1_tingzhi=0; //在要测速时置0
- /****面板显示****/
- uchar code lcd_desktop11[]="stop";//停止
- uchar code lcd_desktop12[]="ford";//正转
- uchar code lcd_desktop13[]="fanz";//反转
- uchar code lcd_desktop21[]="speedup";//加速
- uchar code lcd_desktop22[]="speeddn";//减速
- uchar code num[]={'0','1','2','3','4','5','6','7','8','9'};//要显示的转速数字。
- //-----lcd液晶屏头文件-----//
- /**延时函数**/
- void delayms(uchar xms)
- {
- uchar i,j;
- for(i=xms;i>0;i--)
- for(j=110;j>0;j--);
- }
- /****LCD写命令******/
- void write_command(uchar com)//com写入命令
- {
- lcdrs=0;
- P0=com;
- delayms(2);
- lcden=1;
- delayms(2);
- lcden=0;
- }
- /**************LCD写数据*********/
- void write_data(uchar date)//数据为DATA
- {
- lcdrs=1;
- P0=date;
- delayms(2);
- lcden=1;
- delayms(2);
- lcden=0;
- }
- /********LCD初始化****************/
- void lcd_init()//显示模式设置
- {
- lcden=0;
- write_command(0x38);
- write_command(0x0c);
- write_command(0x06);
- write_command(0x01);
- }
- /**电机初始化函数**/
- void motor_init(void)
- {
- ENA=0; // 停止
- }
- void ZhongDuanKaiQi(void)
- {
- EA=1;
- ET0=1; //计时器0允许中断。产生脉冲。
- ET1=1; //计时器1允许中断。对低电平进行计时。
- //EX1=1; //INT0允许中断。测速。由外部来启动。
- IT1=1; //低电平触发。
- PX1=0; //低级中断。
- PT0=1;
- PT1=0;
- TMOD=0x12;//定时器工作方式控制
- }
- /***定时器T1初始化!!!等待外部脉冲的到来***/
- void Timer1_init(void)
- {
- TH1=0;
- TL1=0; //等待外部脉的到来。INT1
- }
- /*****定时T0初始化*****/
- void Timer0_init(void)
- {
- TH0=0;
- TL0=0;
- TR0=1;
- }
- /********测速模块,显示速度*******/
- void speed_test(void)
- {
- // TIMER1_COUNTER;
- COUNTER_high=TH1;
- COUNTER_low=TL1;
- SUM=COUNTER_high*256+COUNTER_low+TIMER1_COUNTER*65536;//得到的是多少us。
- SUM=SUM/1000; //化为毫秒。
- SUM=1000/SUM; //圈每秒。
- SUM=SUM*60; //圈每分钟;三位数。
- gewei=SUM%10;
- shiwei=SUM/10%10;
- baiwei=SUM/100;
- write_command(0xc0);
- write_data(baiwei);
- write_command(0xc1);
- write_data(shiwei);
- write_command(0xc2);
- write_data(gewei);
- SUM=0;
- TIMER1_COUNTER=0;
- }
- /********外中断1函数**********/
- void INT1_QIDONG() interrupt 2
- {
- INT1_tingzhi++;
- if(INT1_tingzhi==5)
- {
- INT1_tingzhi=5;EX1=0;//关闭中断。
- }
- if(INT1_tingzhi<=4)
- {
- INT1_FLAG++;
- if(INT1_FLAG==2)
- {
- INT1_FLAG=0;TR1=0;speed_test();
- }
- else
- {
- Timer1_init();
- TR1=1;
- }
- }
- }
- /******定时器T1计数*******/
- void Timer1_QIDONG() interrupt 3
- {
- TIMER1_COUNTER++;
- }
- /**相位测量并输出!!!!!出现问题???**/
- void xiangwei_test(void)
- {
- uchar i=0,j=0;
- //while((P2^3==0)&&(P2^4==0));
- //if((P2^3==0)&&(P2^4==0))
- {
- //delayms(5);
- if(P2^3==1)
- {
- write_command(0x01);
- write_command(0x80);for(i=0;i<=3;i++) write_data(lcd_desktop13[i]);//反转。
- j++;//反转
- }
-
- if(P2^4==1)
- {
- write_command(0x01);
- write_command(0x80);for(i=0;i<=3;i++) write_data(lcd_desktop12[i]);//正转。
- j++;
- }
- }
- }
- /****T0中断发出信号并进行通道选择****/
- void Timer0() interrupt 1
- {
- Timer0_flag--;
- if((Timer0_BiaoZhi)>=(Timer0_flag))
- {
- if(KEY2==0)
- {
- Zheng=1;Fan=0;
- }
- else
- {
- Zheng=0;Fan=1;
- }
- }
- else
- {
- Zheng=0;Fan=0;
- }
- if(Timer0_flag==0) Timer0_flag=5;
- }
- void Guan(void)
- {
- ET0=0;
- EX1=0;
- ET1=0;
- }
- void keyscan() //按键扫描函数
- {
- uint i=0;
- if(key1==0) //按键K1启动按钮。只有该位启动时,后面的几位才有效。启动T0计数开始发出脉冲。
- {
- delayms(10);
- if(key1==0)
- {
- while(!key1);
- KEY1=5;
- Fan=0;
- Zheng=0;
- ENA=1;
- KEY2=0;//初始化为正转。
- Timer0_BiaoZhi=0;
- Timer0_flag=4;
-
- ZhongDuanKaiQi();//开启中断
- Timer0_init();// 初始化并开始发出脉场外中断已经开启。
- EX1=1; //外中断开启;测速,显示速度。五次后结束关闭外中断1。出现问题!!!
-
- write_command(0x80);
- for(i=0;i<strlen(lcd_desktop11);i++)//显示正转。
- write_data(lcd_desktop12[i]);
- }
- }
-
- if((key2==0)&&(KEY1=5)) //按键K2按下,(正转)只改变转向(标志位改变输出端口)
- {
- delayms(10);
- if(key2==0)
- {
- while(!key2);
- KEY2=0; //正转标志,电机正向旋转。
- //KEY2=(~1);
- xiangwei_test();
- KEY1=5;
- }
- }
-
- if((key3==0)&&(KEY1=5)) //按键K3按下,(反转)只改变转向
- {
- delayms(10);
- if(key3==0)
- {
- while(!key3);
- KEY2=~0; //反转标志。
- //KEY2=(~0);
- xiangwei_test();
- KEY1=5;
- }
- }
- if((key4==0)&&(KEY1==5)) //按键K4按下,(加速)改变装入的数值?循环次数?执行if里面的语句,通过size++来改变幅值的频率
- {
- delayms(10);
- if(key4==0)
- {
- while(!key4);
- if(Timer0_BiaoZhi<4)
- {
- Timer0_BiaoZhi++;
- }
- else
- {
- Timer0_BiaoZhi=4;
- }
- EX1=1;
- KEY1=5;
- }
-
- }
-
- if((key5==0)&&(KEY1==5)) //按键K5按下,(减速)对其它标志位没有影响。
- {
- delayms(5);
- if(key5==0)
- {
- while(!key5);
- if(Timer0_BiaoZhi==0)
- {
- Timer0_BiaoZhi=0;
- }
- else
- {
- Timer0_BiaoZhi--;
- }
- EX1=1;
- KEY1=5;
- }
- }
- if((key6==0)&&(KEY1==5))//按键K6按下,电机停止转动使能端和计数启动关闭,计数器工作否?设立标志位。此按键按下后除K1外无法启动
- {
- delayms(5);
- if(key6==0)
- {
- while(!key6);
- //置位,保证后续操作正常。
-
-
-
- EA=0; //总中断关
- motor_init(); //电机关
- Guan();
-
- }
- }
- }
-
- /*主函数*/
- void main()
- {
- ZhongDuanKaiQi();//初始化
- motor_init(); //电机停止
- lcd_init();
- while(1)
- {
- keyscan();
-
- }
- }
复制代码
所有资料51hei提供下载:
直流电机驱动.7z
(2.63 MB, 下载次数: 162)
|