西电微机系统课程设计——步进电机开环控制系统设计

西电微机系统课程设计——步进电机开环控制系统设计


一、课程设计目的

1.掌握微机系统总线与各芯片管脚连接方法,提高接口扩展硬件电路的连接能力。

2.加深对 A/D 和并行接口芯片的工作方式和编程方法的理解。

3.搞懂步进电机的工作原理及控制方式,掌握开环控制系统的设计思路和实现方法。


二、课程设计的内容

手动调节电位器旋钮(0V~5V),通过 ADC0809 模拟输入水库水位 0 米~50 米,CPU收到水位信号后,根据水位高度控制步进电机(水闸)进行调节。

在这里插入图片描述


三、系统功能与设计要求

基本功能要求

手动调节电位器旋钮,步进电机根据水位实时调节水闸。设水闸全部打开需要逆时针旋转 10 圈 (10 x 360°)度。随着上游进入水库的水流量变化,水库水位不断变化(手动调节电位器旋钮),每到一定高度,步进电机顺时针(关)或逆时针(开)旋转一定的角度调节水闸开启程度,从而控制水库水位在 10~50 米之间。调节精度控制在±5%,调节规律如下:

在这里插入图片描述

步进电机采用四相八步控制,开关顺序如图:

在这里插入图片描述
步进电机不使用时要关闭,不可以停止在一个相位上时间过长,会导致发热;

发挥部分

1.增加速度调节功能。水位在 10~40 米期间,步进电机中速转动,水位低于 10 (水位过低)或高于 40 米(水位过高)时,步进电机高速转动。

2.增加实时水位显示。用数码管 DLED 高两位显示当前水位(00~50 米)。

3.增加水闸开启程度显示。用数码管 DLED 低两位实时显示水闸开启程度(00~10圈)。


四、操作步骤

  1. 首先根据题目要求选择合适的器件并参考示例代码了解各器件的操作方法:
器件名称功能
电位器获取0~5V的模拟量
ADC0809模拟量转换为数字量
步进电机模拟水闸转动
数码管显示必要参数
LED根据水位亮不同的灯
74HC273N锁存器,锁存控制LED的信号
系统总线控制其他器件
8255将系统总线的命令传输到步进电机和数码管
  1. 阅读硬件接口手册,粗略连接各器件,并加部分的流程控制,画出大致的流程控制图:

在这里插入图片描述

  1. 对于一些实验要求的分析:
    • 由于水位可能下降也可能上升,所以步进电机要记录历史状态,根据上一次水位信息来判断正转和反转;
    • 步进电机的速度控制通过延时不同时间来实现,延时函数可以粗略地使用空循环;
    • 数码管水位信息应该随步进电机旋转动态变化,所以应该放入步进电机旋转的函数中不停得对数码管进行刷新以实现;
    • 步进电机的每次旋转停下之后的相位应该被记录,下一次旋转应该在上一次停下的相位进行继续驱动,否则会出现旋转开始时倒转的现象;
    • 步进电机正转就是根据控制图由上到下循环out给步进电机的控制端口,同样反转是由下到上循环out给步进电机的控制端口;由于步进角我们并不清楚,所以采用测验来大致确定步进角,测验结果为步进电机运行40步为旋转一圈,在代码实现中,可以通过acircle这个全局变量来修改旋转一圈的步数;

五、硬件连线图

在这里插入图片描述
在这里插入图片描述


六、代码实现

/*
	步进电机开环控制系统设计:
		电位器功能描述:
			调节电位器旋钮(0~5V)作为水位;
			动态获取模拟量并转换为数字量显示在数码管上;
			4s左右时间不旋转电位器则停止采样,进入控制水闸功能模块;
		8位数码管功能描述:
			数码管低2位显示水位(m);
			数码管高2位显示水闸开启程度,用百分比表示;
			数码管4(中间位置)显示旋转速度,1表示中速,2表示高速;
		步进电机功能描述:
			[0,10)m和[40,50]m水位时,高速旋转,每转一步60ms延时;
			[10,40)m时,中速旋转,每转一步160ms延时;
			可以根据历史水闸开启状态来关闸或开闸(即逆时针旋转和顺时针旋转);
		LED管功能描述:
        	[0,10)m水位时,黄灯闪烁;
			[10,50)m水位时,绿灯常亮;
			50m水位时,红灯闪烁;
*/
#define	u8 unsigned char
#define u16 unsigned int

/* 接口地址定义 */
#define IO8255_PC	0x272			//8255 C口地址;
#define IO8255_Con	0x273			//8255 控制字地址;
#define addr0809	0x250			//0809 地址;
#define IO273       0x230 			//74LS273地址;

/* 库函数定义 */
extern void outportb( unsigned int, char);						//写I/O;
extern char inportb( unsigned int );							//读I/O;

extern void cInitKeyDisplay();  //对键盘、数码管管理器初始化;

//将pBuffer指向的8字节缓冲区内容显示于F5区数码管上;
extern void cDisplay8(u8* pBuffer);

u8 buffer[8];			//用于显示数码管缓冲区,8个字节;
u8 SpeedNo;				//选择哪一级速度;
u8 StepDelay;			//转动一步后,延时常数;
u16 StepCount;			//转动步数;

//传给步进电机的值(四相八步);
u8 StepControl[8]={0x11, 0x99, 0x88, 0xcc, 0x44, 0x66, 0x22, 0x33};		

u8 WaterHight = 0xff;	//水位;
u8 WaterHight1 = 0xff;	//WaterHight1的副本;
u16 adResult = 0;		//电位器读取的值;
u16 lastResult = 0xff;	//电位器上次读取的值;
u8 Steptemp = 0; //步进电机的历史状态(上次旋转之后位于哪步);
u8 hight = 0; // 水闸开启程度为hight%;
u8 acircle = 40; //转1圈要步进电机需要走的总步数;
u8 adtemp;
u8 count = 0;			
u8 i = 0;

/* 刷新数码管,并延时 */
void delayBuffer()
{
	u16 count = 10;
	while (count--)
	{
		cDisplay8(buffer); //刷新数码管1次;
	}
}

/* 延时函数 */
void delay(u16 ms)
{
	u16 i;	
	while(ms--)
	{
		i = 100;
		do
		{;}while(--i);
	}
}

/* 读AD0809数据 */
u8 ad0809()
{
	u8 i = 100;
	outportb(addr0809, 0x0);
	while (i--)
	{;}									//延时,等待AD转换完成;
	return inportb(addr0809);
}

/* 接收到电位器旋转角度变化,更新数码管 */
void display_data(u16 adResult)
{
	buffer[5] = 0x10;
	buffer[1] = adResult / 51;			//255/51 (16进制的1 = 1/51V) ,十位;
	adResult = (adResult % 51) * 10;
	buffer[0] = adResult / 51;			//个位;
	buffer[2] = 0x10;			
	buffer[3] = 0x10;					//0x10用于消隐,即数码管不显示;
}

/* 初始化8255 */
void Init8255()
{
	outportb(IO8255_Con,0x80);		//写8255控制字,无条件的数据传输方式,A、B、C口都单向输出;
	outportb(IO8255_PC, 0xff);		//0FFH->8255 PC,写C口;
}

/* 	刷新LED灯:
		当水位为[10,50)时,绿灯亮;
		当水位为[0,10)时黄灯闪烁;
		当水位为50时,红灯闪烁 
*/
void Testled(){
/* 	此处用WaterHight的副本WaterHight1作为判断而不用WaterHight的原因是:
    WaterHight在电压器不变而水闸已经运动结束的情况下会被置为0xff,
    0xff并不能表示水位信息,即LED无法刷新;
    WaterHight1作为WaterHight被置为0xff之前的副本,可以一直记录当前水位用于LED刷新;
    */
	if(WaterHight1>100){
		WaterHight1 = 0xff; //水位不正常,不显示LED;
	}
	else if(WaterHight1 >= 50){
		outportw(IO273, 0xef); //红灯;
		delay(20);
		outportw(IO273, 0xff); 
	}
	else if(WaterHight1 >= 10){
		outportw(IO273, 0xbf); //绿灯;
	}
	else{
		outportw(IO273, 0xdf); //黄灯;
		delay(20);
		outportw(IO273, 0xff);
	}
}

/* 实现步进电机旋转的函数,参数:需要转动的圈数 */
void StepCircle(u8 circleN){
    
	u8 rate = 1; 		
    u8 htemp = 0;
	if(circleN >= 8){rate = 0;}        //需要转10圈,即水位到了40以上,需要快速转动;
	if(circleN == 0){rate = 0;}			//需要转0圈,即水位到了10以下,需要快速转动;
    
    /*	此处要解决步进电机的正转和反转问题;
    	buffer[7]中存储的水闸的开启程度,而circleN代表假设水闸开启程度为0时,需要转多少圈;
    	buffer[7]可以视作水闸已经转了多少圈;
    	circleN减buffer[7]即代表还需要转多少圈;
    	结果为负数代表需要关闸,步进电机逆时针转;
    	结果为正数代表需要开闸,步进电机顺时针转;
    */
	if(circleN <= buffer[7]){
        //关闸,逆时针转;
		circleN = buffer[7] - circleN;
		while(circleN){
			u8 control = 0;
			for(i = acircle; i > 0; i--){
				control = (Steptemp+i)%8; 		//读取历史状态,根据历史状态旋转步进电机;
				delay(rate*100);				//速度控制;
				buffer[4] = 2 - rate;			//速度显示;
				outportb(IO8255_PC, StepControl[control]);  //步机旋转;
				if(i%(acircle/10) == 0){	
                    //转1圈水闸变化10%,一圈要旋转acircle次,acircle/10次水闸开启1%;
					hight--;
					buffer[7]=hight/10;
					buffer[6]=hight%10;
					delayBuffer();	//刷新显示水闸开启程度;
				}
                htemp = i;
				Testled();
				delay(40); //前面最少delay(20),即最小总延时为delay(60);最大总延时为delay(160);
			}
			circleN--;	//需要转的圈数减一;
		}
	}
	else{
        //开闸,顺时针转;
		circleN = circleN - buffer[7];
		while(circleN){
			u8 control = 0;
			for(i = 0; i < acircle; i++){
				control = (Steptemp+i)%8;
				delay(rate*100);
				buffer[4] = 2 - rate;
				outportb(IO8255_PC, StepControl[control]);
				if(i%(acircle/10) == 0){
					hight++;
					buffer[7]=hight/10;
					buffer[6]=hight%10;
					delayBuffer();	
				}
                htemp = i;
			    Testled();
				delay(40);
			}
			circleN--;	
		}
	}
    Steptemp = htemp;
}

void main()
{
	Init8255(); 		//8255初始化;
	cInitKeyDisplay();	//键盘,数码管初始化;

	while (1)
	{
		count = 0;					
		adtemp = 0;		
        buffer[4] = 0; //步进电机停止转动,速度为0,显示出来;
        //采样时长为4s,如果4s内电压值不变化,则采样结束;
		while(1){		
			adResult = ad0809(); 			//获取电压值;
			display_data(adResult);			//刷新数码管;
			if(adResult == adtemp){			//adResult不等于adtemp时,说明电压旋钮在旋转;
				count++	;
			}
			else{
				adtemp = adResult;	
			}
			if(count == 4){
				break;						//四次采样都相等时,认为电压不再改变,退出采样循环;
			}
			delayBuffer();			//电压旋钮旋转时数码管刷新水位,显示到数码管;
			Testled();						
			delay(1000);					//延时,保证采样过程不会太快;
		}
		if (lastResult != adResult)			//与历史版本比较,只有电压变化时才会重新设置水位;
		{
			lastResult = adResult;			//记录新电压值的副本作为历史版本;
			WaterHight = buffer[1]*10 + buffer[0]; //更新水位;
			if(WaterHight1 == WaterHight){
				WaterHight = 0xff;
			}
			else{
				WaterHight1 = WaterHight;	//保存水位的副本作为历史版本;
			}
		}
		delayBuffer();
		Testled();
		
        //根据水位来调整水闸,一旦调整水闸完成,WaterHight将暂时失效(即如果电压值不变,则不能再用于控制水闸);
		if(WaterHight>100){
			WaterHight = 0xff;
		}
		else if(WaterHight>=50){
			StepCircle(10); 				//水位等于50时,水闸需要开启到100%,转10圈;
			WaterHight = 0xff;
		}
		else if(WaterHight>=40){
			StepCircle(8);
			WaterHight = 0xff;
		}
		else if(WaterHight>=30){
			StepCircle(6);
			WaterHight = 0xff;
		}
		else if(WaterHight>=20){
			StepCircle(4);
			WaterHight = 0xff;
		}
		else if(WaterHight>=10){
			StepCircle(2);
			WaterHight = 0xff;
		}
		else{
			StepCircle(0);
			WaterHight = 0xff;
		}
	}
}

七、心得体会

通过本次课设,我了解了CPU如何控制各器件实现一个简单的系统;

在实验中也有一些不足:可能是由于各个分支的延时不同,也可能是对四相八步理解不到位,步进电机旋转的速度并不是恒定的;此外我们没有使用8259中断来使输入和步进电机的旋转互不干扰,而是采用了不停地定时检测来确保在旋转按钮时步进电机不会旋转,所以反应速度比较慢,延时比较长;

对于操作系统学习的OS通过读写文件控制硬件,以及微机原理学习的各种芯片功能使用等,都通过本次实验得到了更深刻的理解;


踩坑点:
星研软件编译链接完要点全速运行(一个小感叹号)才能在板子上运行;
步进电机的示例代码有误,中断处理函数TIMEROif(--StartStepDelay)应该改成--StartStepDelay; if(StartStepDelay)(老师说的,没验证),如果不改就会有一个警告,即使没错误只要有警告就无法在板子上下载运行;


一、 课程设计的目的 课程设计是本科教学全过程中的重要环节。《微机应用系统设计与综合实验(实践)》课程设计主要培养我们自动化专业学生,运用所学知识解决计算机应用领域内实际问题能力,进一步提高学生运用计算机编程语言综合编程能力、程序调试技能和微机系统接口综合应用及电路设计能力。 1、学习在PC系统中扩展简单的I/O接口的方法。 2、熟练掌握和运用汇编和C语言编写程序控制8255各口的输入输出,并正确带动数码管及步进电机;能熟练运用汇编和C语言实现8254的定时功能,以确保8255输出的脉冲频率稳定。 3、熟练掌握ISA总线配置方式下硬件实验的调试,并能独立的排除故障,以确保实验的顺利进行。 二、 设计的题目名称及要求 设计题目:小型步进电机控制系统设计设计要求: 1、编程语言为C语言或汇编语言。 2、硬件电路基于80x86微机小键盘和数码显示接口。 3、控制性能要求,实现步进电机启动、方向、速度调节和停止功能选择,并且在计算机屏幕上及数码管上实时显示步进电机当前参数及工作状态。编程语言为汇编语言或C语言。 三、实验设备 PC机一台(装有TDPIT软件)、唐都AEDK8688ET实验箱。 四、设计的思想和实施方案 由于本次课程设计控制的对象是步进电机,首先我得通过查阅相关资料对步进电机有个初步的了解和认识。所谓步进,就是指每给步进电机一个递进脉冲,步进电机各绕组的通电顺序就改变一次,即电机转动一次(一定的角度),本次课程设计控制的是四项八拍步进电机。在了解了步进电机的工作原理后,我的初步想法是用8255给步进电机传送脉冲,用8254控制传送脉冲的频率,以实现对步进电机转速的控制。 8255内部包含3个8位的输入输出端口A、B和C,端口A和端口B都可以用作一个8位的输入口或8位的输出口,端口C既可以作为一个8位的输入口或8位的输出口,又可以作为两个4位的输入输出口(C口上半部分和C口下半部分)使用,还可以配合A口和B口工作,分别用来产生A口和B口的输出控制信号和输入A口和B口的端口状态信号。 本次设计,我对8255的使用是这样的:8255工作于方式0,A口低四位接键盘及数码管显示单元的X1~X4;C口低四位接键盘及数码管显示单元的Y1~Y4; C口高四位接步进电路的驱动电路,使电机转动起来;B口接数码管的A、B、C、D、E、F、G和DP,以使数码管显示电机的转向和转速。8255的A口高四位本次课程设计没有用到。
摘 要 在现代电子产品中,步进电机广泛应用于ATM机、喷绘机、刻字机、写真机、喷涂设备、医疗仪器及设备、计算机外设及海量存储设备、精密仪器、工业控制系统、办公自动化、机器人等领域。所以步进电机的控制是一门很实用的技术。本实验主要是基于唐都——PIT试验箱的步进电机控制的设计。主要使用到了并行接口电路8255、LED七段数码管电路、8086cpu、步进电机等元件。主要是通过按键的不同来设置直流电机的转速、运行状态和方向。软件部分采用了汇编语言编写程序代码和C语言编写的步进电机控制程序,通过判断、跳转、循环、延时等基本技术实现。 此系统可以通过键盘输入相关数据, 并根据需要, 实时对步进电机工作方式进行设置, 具有实时性和交互性的特点。该设计可应用于步进电机控制的大多数场合 目录 摘要…………….…………………………………………...…...3 一. 课程设计目的……………………………………….....…...4 二. 设计题目名称及要求……………………………….....…….4 三. 实验设备…………………………………………...…..…….4 四. 设计的思想和实施方案……………………………..….…….5 五. 硬件原理图…………………………………………………….11 六. 典型程序模块及典型编程技巧…………………….…....…13 七. 课程设计中遇到的问题及解决方法………………...………16 八. 程序流程图………………………………………….…......19 九. 汇编程序清单及程序注释…………………………..…..……..23 十. C语言程序清单及注释………………………….……..…..…30 十一. 收获体会………………………………….………..……..…37 十二. 参考文献………………………………………..….…..……38
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OutlierLi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值