成 绩:
重庆邮电大学
自动化学院综合实验报告
题 目:51系列单片机直流电机闭环调速实验
学生姓名:蒋运和
班 级:0841004
学 号:2010213316
同组人员:李海涛 陈超
指导教师: 郭鹏
完成时间:2013年12月
一、实验名称:
51系列单片机直流电机闭环调速实验
——基于Protuse仿真实验平台实现
基本情况:
1. 学生姓名:蒋运和
2. 学 号:2010213316
3. 班 级:0841004
4. 同组其他成员:
二、实验内容(实验原理介绍):
1、直流电机调速原理
图1所示电枢电压为V,电枢电流I,电枢回路总电阻为R,电机常数C,励磁磁通量Φ。
那么根据KVL方程:电机转速,其中,对于极对数为P,匝数为N,电枢支路数为a的电机来说:电机常数,意味着电机确定后,该值是不变的。
而在V-IR中,由于R仅为绕组电阻,导致IR非常小,所以V-IR≈V。
由此可见我们改变电枢电压,转速n即可随之改变。
实现直流电机的闭环调速
实现原理如下图所示:
2、测速软件设计
图12 软件测速的方框图
/****T1中断服务程序********单位时间(S)方波的个数*************/
void time1_int(void) interrupt 3
{
count_speed++;
if(count_speed == 20)
{ count_speed = 0;
num_display = num_medium;
num_medium = 0;
}
}
3、PID算法的数字实现
由于DDC(Direct Digital Control)系统是一种时间离散控制系统。因此,为了用微机实现(式3-1-1)必须将其离散化,
用数字形式的差分方程来代替连续系统的微分方程。离散化的PID表达式为:
(式3-1-2)
式中,——采样周期;
——第n次采样时微机输出;
——第n次采样时的偏差值;
——第n-1次采样时的偏差值;
——采样序号,n=0,1,2,…。
通常把(式3-1-2)称为PID的位置控制算式。根据(式3-1-2)可以进一步推导出离散化的位置型PID编程表达式,如(式3-1-3):
第K次采样PID的输出式为:
(式3-1-3)
其中,设
式中,
确定了的值后,实现(式3-1-3)的编程框图如右图所示:由(式3-1-3)还可得离散化的位置型P控制和PI控制的编程表达式。它们各自的编程框图也只需在该图的基础上稍作删减即可。
4、调速设计方案
调速采用PWM(Pulse Width Modulation)脉宽调制,工作原理:通过产生矩形波,改变占空比,以达到调整脉宽的目的。PWM的定义:脉宽调制(PWM)是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。模拟信号的值可以连续变化,其时间和幅度的分辨率都没有限制。9V电池就是一种模拟器件,因为它的输出电压并不精确地等于9V,而是随时间发生变化,并可取任何实数值。与此类似,从电池吸收的电流也不限定在一组可能的取值范围之内。模拟信号与数字信号的区别在于后者的取值通常只能属于预先确定的可能取值集合之内,例如在{0V,5V}这一集合中取值。
模拟电压和电流可直接用来进行控制,如对汽车收音机的音量进行控制。在简单的模拟收音机中,音量旋钮被连接到一个可变电阻。拧动旋钮时,电阻值变大或变小;流经这个电阻的电流也随之增加或减少,从而改变了驱动扬声器的电流值,使音量相应变大或变小。与收音机一样,模拟电路的输出与输入成线性比例。
尽管模拟控制看起来可能直观而简单,但它并不总是非常经济或可行的。其中一点就是,模拟电路容易随时间漂移,因而难以调节。能够解决这个问题的精密模拟电路可能非常庞大、笨重(如老式的家庭立体声设备)和昂贵。模拟电路还有可能严重发热,其功耗相对于工作元件两端电压与电流的乘积成正比。模拟电路还可能对噪声很敏感,任何扰动或噪声都肯定会改变电流值的大小。通过以数字方式控制模拟电路,可以大幅度降低系统的成本和功耗。此外,许多微控制器和DSP已经在芯片上包含了PWM控制器,这使数字控制的实现变得更加容易了。
5、电路原理图
三、实验结果分析(含程序、数据记录及分析和实验总结等,可附页):
1、51系列单片机直流电机闭环调速实验程序
/*--------------------------------------------------------------------------
REG52.H
Header file for generic 80C52 and 80C32 microcontroller.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/
#ifndef __REG52_H__
#define __REG52_H__
/* BYTE Registers */
sfr P0 = 0x80;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr IE = 0xA8;
sfr IP = 0xB8;
sfr SCON = 0x98;
sfr SBUF = 0x99;
/* 8052 Extensions */
sfr T2CON = 0xC8;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;
/* BIT Registers */
/* PSW */
sbit CY = PSW^7;
sbit AC = PSW^6;
sbit F0 = PSW^5;
sbit RS1 = PSW^4;
sbit RS0 = PSW^3;
sbit OV = PSW^2;
sbit P = PSW^0; //8052 only
/* TCON */
sbit TF1 = TCON^7;
sbit TR1 = TCON^6;
sbit TF0 = TCON^5;
sbit TR0 = TCON^4;
sbit IE1 = TCON^3;
sbit IT1 = TCON^2;
sbit IE0 = TCON^1;
sbit IT0 = TCON^0;
/* IE */
sbit EA = IE^7;
sbit ET2 = IE^5; //8052 only
sbit ES = IE^4;
sbit ET1 = IE^3;
sbit EX1 = IE^2;
sbit ET0 = IE^1;
sbit EX0 = IE^0;
/* IP */
sbit PT2 = IP^5;
sbit PS = IP^4;
sbit PT1 = IP^3;
sbit PX1 = IP^2;
sbit PT0 = IP^1;
sbit PX0 = IP^0;
/* P3 */
sbit RD = P3^7;
sbit WR = P3^6;
sbit T1 = P3^5;
sbit T0 = P3^4;
sbit INT1 = P3^3;
sbit INT0 = P3^2;
sbit TXD = P3^1;
sbit RXD = P3^0;
/* SCON */
sbit SM0 = SCON^7;
sbit SM1 = SCON^6;
sbit SM2 = SCON^5;
sbit REN = SCON^4;
sbit TB8 = SCON^3;
sbit RB8 = SCON^2;
sbit TI = SCON^1;
sbit RI = SCON^0;
/* P1 */
sbit T2EX = P1^1; // 8052 only
sbit T2 = P1^0; // 8052 only
/* T2CON */
sbit TF2 = T2CON^7;
sbit EXF2 = T2CON^6;
sbit RCLK = T2CON^5;
sbit TCLK = T2CON^4;
sbit EXEN2 = T2CON^3;
sbit TR2 = T2CON^2;
sbit C_T2 = T2CON^1;
sbit CP_RL2 = T2CON^0;
#endif
#ifndef TEST_H
#define TEST_H
#define u8 unsigned char
#define u16 unsigned int
////// LCD ////
sbit RS = P2^0;
sbit RW = P2^1;
sbit EN = P2^2;
//////L298/////
sbit IN1 = P2^5;
sbit IN2 = P2^6;
sbit ENA = P2^7;
void delay_ms(u16 z);
void LCD_WriteData(u8 Dat);
void LCD_WriteCOM(u8 com);
void Show_Num(u8 x,u8 y,u16 num);
void Show_fNum(u8 x,u8 y,u16 num);
void LCD_Init(void);
void set_Dir(u8 dir);
void key_scan(void);
extern void timer_init();
extern void timer0();
extern u16 count;
extern u8 num;
extern u16 set_value;
extern u8 kp,ki,kd;
#endif
#ifndef SPEED_H
#define SPEED_H
extern u16 count;
extern u8 pulse;
extern void Show_Num(u8 x,u8 y,u16 num);
#endif
#include<reg52.h>
#include"test.h"
u8 Dir=1; //方向
/*************
函数功能:延时
****************/
void delay_ms(u16 z)
{
u16 i;
u8 j;
for(i=z;i>0;i--)
for(j=120;j>0;j--);
}
/*************
函数功能:LCD写数据
****************/
void LCD_WriteData(u8 Dat)
{
RS = 1;
delay_ms(5);
P0 = Dat;
EN = 1;
delay_ms(20);
EN = 0;
// delay_ms(15);
}
/*************
函数功能:LCD命令
****************/
void LCD_WriteCOM(u8 com)
{
RS = 0;
delay_ms(5);
P0 = com;
EN = 1;
delay_ms(20);
EN = 0;
// delay_ms(15);
}
/*************
函数功能:Show_Num初始化
****************/
void Show_Num(u8 x,u8 y,u16 num)
{
u8 a,b,c;
a = num/100;
b = num/10%10;
c = num%10;
if(y%2 == 1)
LCD_WriteCOM(0x80+x);
else
LCD_WriteCOM(0x80+0x40+x);
LCD_WriteData(a+0x30);
LCD_WriteData(b+0x30);
LCD_WriteData(c+0x30);
}
/*************
函数功能:Show_fNum初始化
****************/
void Show_fNum(u8 x,u8 y,u16 num)
{
u8 a,b;
a = num/10%10;
b = num%10;
if(y%2 == 1)
LCD_WriteCOM(0x80+x);
else
LCD_WriteCOM(0x80+0x40+x);
LCD_WriteData(a+0x30);
LCD_WriteData(b+0x30);
}
/*************
函数功能:Show_Num初始化
****************/
void Show_char(u8 x,u8 y,u8 ch)
{
if(y%2 == 1)
LCD_WriteCOM(0x80+x);
else
LCD_WriteCOM(0x80+0x40+x);
LCD_WriteData(ch);
}
/*************
函数功能:LCD 固定显示
****************/
void LCD_Dispaly(void)
{
Show_char(0,1,'P'); //实际测量值
Show_char(1,1,'V');
Show_char(2,1,':');
Show_Num(3,1,0);
Show_char(9,1,'S'); //设定值
Show_char(10,1,'V');
Show_char(11,1,':');
Show_Num(12,1,set_value);
Show_char(0,2,'P'); //P实际测量值
Show_char(1,2,':');
Show_fNum(2,2,kp);
Show_char(6,2,'I'); //I实际测量值
Show_char(7,2,':');
Show_fNum(8,2,ki);
Show_char(12,2,'D'); //D实际测量值
Show_char(13,2,':');
Show_fNum(14,2,kd);
}
/*************
函数功能:LCD初始化
****************/
void LCD_Init(void)
{
RW = 0;
EN = 0;
delay_ms(20);
LCD_WriteCOM(0x38);
LCD_WriteCOM(0x0c);
LCD_WriteCOM(0x06);
LCD_WriteCOM(0x01);
LCD_Dispaly();
}
/*************
函数功能:设置转向
****************/
void set_Dir(u8 dir)
{
if(dir)
{
IN1 = 1;
IN2 = 0;
}
else
{
IN1 = 0;
IN2 = 1;
}
ENA = 1;
}
int main(void)
{
LCD_Init();
set_Dir(Dir);
timer_init();
EA = 1;
while(1)
{
key_scan();
Show_Num(3,1,count);
}
return 0;
}
void key_scan(void)
{
u8 Temp=P1;
static u8 Add_Ver=0,one_ten=0;
static Tri=0,CON=0;
Temp = Temp^0xff;
Tri = Temp&(Temp^CON);
CON = Temp;
if(Tri==0x10)
{Add_Ver++;Add_Ver=Add_Ver%2;}
else if(Tri==0x20)
{one_ten++;one_ten=one_ten%2;}
else if(Tri==0x01)
{
if(set_value+1<=230&&Add_Ver==0&&one_ten==0)
set_value++;
else if(set_value>=1&&Add_Ver==1&&one_ten==0)
set_value--;
else if(set_value+10<=230&&Add_Ver==0&&one_ten==1)
set_value=set_value+10;
else if(set_value>=10&&Add_Ver==1&&one_ten==1)
set_value=set_value-10;
Show_Num(12,1,set_value);
}
else if(Tri==0x02)
{
if(kp+1<=100&&Add_Ver==0&&one_ten==0)
kp++;
else if(kp>=1&&Add_Ver==1&&one_ten==0)
kp--;
else if(kp+10<=100&&Add_Ver==0&&one_ten==1)
kp=kp+10;
else if(kp>=10&&Add_Ver==1&&one_ten==1)
kp=kp-10;
Show_fNum(2,2,kp);
}
else if(Tri==0x04)
{
if(ki+1<=100&&Add_Ver==0&&one_ten==0)
ki++;
else if(ki>=1&&Add_Ver==1&&one_ten==0)
ki--;
else if(ki+10<=100&&Add_Ver==0&&one_ten==1)
ki=ki+10;
else if(ki>=10&&Add_Ver==1&&one_ten==1)
ki=ki-10;
Show_fNum(8,2,ki);
}
else if(Tri==0x08)
{
if(kd+1<=100&&Add_Ver==0&&one_ten==0)
kd++;
else if(kd>=1&&Add_Ver==1&&one_ten==0)
kd--;
else if(kd+10<=100&&Add_Ver==0&&one_ten==1)
kd=kd+10;
else if(kd>=10&&Add_Ver==1&&one_ten==1)
kd=kd-10;
Show_fNum(14,2,kd);
}
}
#include<reg52.h>
#include"timer.h"
#include"test.h"
u16 count=0; //脉冲计数
u8 num = 0;
u8 time=0;
u8 num1=0,pulse=50;
/*************
函数功能:计数 初始化
****************/
void timer0_init()
{
TMOD |= 0X05;
TH0 = 0;
TL0 = 0;
TR0 = 1;
}
/*************
函数功能:定时器1初始化
****************/
void timer1_init()
{
TMOD|=0x10;
TH1=(65535-50000)/256; //20ms
TL1=(65535-50000)%256;
TR1=1;
ET1=1;
}
/*************
函数功能:定时器2初始化
****************/
void timer2_init()
{
RCAP2H =(65536-100)/256; //50us
RCAP2L =(65536-100)%256;
TH2 = RCAP2H;
TL2 = RCAP2L;
ET2 = 1;
TR2 = 1;
}
/*************
函数功能:定时器初始化
****************/
void timer_init()
{
timer0_init();
timer1_init();
timer2_init();
}
void timer0() interrupt 3
{
// EA = 0;
TH1 = (65535-50000)/256; //25ms
TL1 = (65535-50000)%256;
num++;
if(num>=4) //100ms
{
num=0;
count = TL0;
TL0 = 0;
TH0 = 0;
speed();
}
// EA = 1;
}
void timer2() interrupt 5
{
TF2 = 0;
num1++;
if(num1>=pulse)
IN1 = 0;
if(num1>=PULSE)
{IN1=1;num1=0;}
}
#include<reg52.h>
#include"test.h"
#include"speed.h"
u16 set_value=100;
u8 kp=25,ki=10,kd=15;
int uk=0;
/****************
函数功能:设定速度
****************/
void set_speed(u8 value)
{
static int essd=0,essd1=0,essd2=0;
essd = value-count;
uk=kp*essd+ki*essd1+kd*(essd-essd2);
essd2=essd;
essd1+=essd;
//uk =(int)(uk/100)+(int)(100*value/240);
uk =(int)(uk/100);
if(uk<0)
pulse = 0;
else if(uk<100)
pulse=uk;
else
pulse=99;
//Show_Num(0,2,uk);
}
void speed(void)
{
set_speed(set_value);
}
2、实验总结
经过这次的实验,我又一次加深了对LED的认识和加强了对keil以及Protues等软件的使用技能。虽然之前做过很多的实验,但是自己任然存在很大的问题。在本次实验中任然有很多东西不会,所以又重新学了一遍单片机,加大了对单片机学习的兴趣。刚开始做实验时是很盲目的,胡乱的查资料。所以经过这次的实验我明白了做任何事之前都得有充分的准备和必要的预习。遇到困难是不可避免的,所以我们要做好随时挑战困难的准备,和同学团结,虚心请教老师,一起解决问题。做实验是需要耐心和信心的。比如在调试程序的时候要细心的检查错误。从这次的实验中我学会了把自己学的东西学以致用,学到了课本上没有的东西。很感谢学校,老师给我们这次锻炼的机会,在以后的学习中我会学好理论知识,将理论与实践相结合。不断提高自己动手能力。