本文续上篇文章,是微机实验课的第三次实验
一、任务要求
主要功能和技术指标
1. 电机转速控制:通过遥控器或按键输入,控制电机的转速。理解步进电机转速的控制原理,影响转速的因素和控制方法,可以实现电机按照指定的转速旋转,转速范围600-1800RPM。
2. 档位显示:实时显示当前电机的转速档位,至少实现三个档位的风速调节。
3. 信号处理:对遥控器和按键输入信号进行解码和处理。
4. 创意功能:实现如自然风模拟等个性化功能。
提高与创意
- 自然风模拟:通过程序控制电机转速的周期性变化,模拟自然风的无规律性。
- 多模式控制:设计不同的控制模式,如恒速模式、风速递增模式,定时模式等。
- 节能设计:实现电机的节能控制策略,如在无操作一定时间后自动降低转速或停止。
二、设计思路
1.本次设计实现了用开发板上的三个独立按键来控制电风扇,分别控制风扇的启动关闭、档位转换和模式转换,并将电风扇当前转速显示在动态数码管上,将电风扇当前档位显示在静态数码管上。按下开关按键,电风扇启动,再按下开关按键,电风扇关闭。电风扇启动时默认一档,按下档位按键,档位加一;当处于三档时,再按下模式按键,档位回到一档。电风扇启动时默认处于节能模式,启动后五秒内没操作电风扇转速将进一步降低,可通过按下模式按键关闭该模式,进入恒速模式(即五秒内没任何操作也不会降低转速)。
2.程序采用C语言编写,将电机单步驱动、按键状态判断、数码管显示、延时等功能单独封装为函数,便于主函数中调用。主函数主要循环进行档位按键和模式按键的扫描处理、数码管显示和标志位复位等。开关按键标按下用外部中断进行判断。
3.在main.c文件中定义了5个全局变量:第一个是Switch_on,用于表征电风扇的开关状态,全局可见性方便在中断函数中修改其值并用于主函数中的循环判断;第二个是Gear,用于表征电风扇的档位;第三个是motor_step,用于表示电机转动的步相,之所以设成全局是因为电机的单步驱动是通过定时中断实现的,因此需要在中断函数里修改motor_step的值并保存该值直至下一次中断;第四个是Switch_Key_Flag,是开关按键(开关按键)的标志位,由于开关按键是通过外部中断检测的,所以需要一个全局可见的标志位方便在主函数中当按键复位时置标志位为1。
4.使用定时器T0进行电机单步驱动的定时控制,从而实现对步进电机总转速的精密控制。定时器T1则身兼两职,作用之一是进行开关按键的消抖(其实直接用延时函数进行消抖也能完成任务,但是延时函数占用CPU时间,为了体现定时器延时消抖的可行性,使用了该定时器进行延时消抖),作用之二是进行5s的定时(节能模式下无操作5s后自动降低转速),由于这两项任务不会同时发生,所以具有可实现性。使用外部中断0进行开关按键下降沿的判断,当检测到下降沿后,启动定时器T1进行10ms延时消抖。
三、资源分配
1.需要用到单片机内部资源的外部中断0、定时器中断T1和定时器T2,以及数据RAM区的寄存器等。
2.需要用到开发板上的步进电机、动态数码管、静态数码管、独立按键、LED灯以及其他控制芯片等。
四、流程图
五、源代码 (含文件头说明、语句行注释)
1. //main.c
//主函数文件,也存放了中断函数
#include "reg51.h" //51单片机寄存器文件
#include "step_motor.h" //电机单步驱动头文件
#include "delay.h" //延时函数头文件
#include "smg.h" //数码管显示头文件
#include "key.h" //按键头文件
sbit Switch_Key = P3^2; //定义开关按键引脚K1
sbit Gear_Key = P3^3; //定义档位按键引脚K2
sbit Mode_Key = P3^4; //定义模式按键引脚K3
sbit LED1=P3^5; //定义LED灯引脚
u8 Switch_On = 0; //定义风扇开启状态变量,为1时,电机旋转,为0时,电机停转
u8 Gear = 1; //定义风扇档位变量
u8 motor_step = 0; //电机的步拍变量
u8 Switch_Key_Flag = 1; //定义开关按键的状态位变量
u16 T1_Int_times = 0; //定义定时器T1溢满次数变量,用于节能模式的定时
void exti0_init(void) //初始化外部中断0,用于开关按键的消抖
{
IT0=1; //跳变沿触发方式(下降沿)
EX0=1; //打开外部中断0允许
EA=1; //打开总中断
}
void exti0(void) interrupt 0
{
TR1=1; //当中断1引脚检测下降沿,启动定时器T1用于消抖
TH1=0XD8; //设置定时时长为10ms
TL1=0XF0;
}
void time0_init(void) //使用定时器T0进行电风扇的转速控制
{
TMOD|=0X01; //选择为定时器0模式,工作方式1
TH0=0XD8; //给定时器赋初值,定时10ms
TL0=0XF0;
ET0=1; //打开定时器0中断允许
EA=1; //打开总中断
TR0=0; //先不打开定时器,等开关按键被按下进入外部中断0的函数才打开
}
void time0() interrupt 1 //定时器0中断函数
{
step_motor_send_pulse(motor_step); //电机驱动函数
motor_step++;
if(motor_step==8) motor_step=0;
switch(Gear) //根据档位的不同,选择定时不同的时间,从而控制转速不同
{
case 1:TH0=0XD8;TL0=0XF0;break;//单步定时10ms,转速为750PRM
case 2:TH0=0XE8;TL0=0X90;break;//单步定时6ms,转速为1250PRM
case 3:TH0=0XEF;TL0=0XB9;break;//单步定时4.167ms,转速约为1800PRM
case 0:TH0=0XCF;TL0=0X2C;break;//单步定时12.5ms,转速约为600PRM
}
}
void time1_init(void) //使用定时器T1进行开关按键的消抖以及无操作时间的定时
{
TMOD|=0X10; //选择为定时器1模式,工作方式1
TH1=0X3C; //给定时器赋初值,定时50ms,默认为无操作时间的定时
TL1=0XB0;
ET1=1; //打开定时器1中断允许
EA=1; //打开总中断
TR1=0; //先不打开定时器T1
}
void time1() interrupt 3 //定时器1中断函数
{
if(Switch_Key_Flag==1 && Switch_Key==KEY_PRESS)
//如果标志位为1,且经过10ms延时后仍为低电平,则说明开关按键按下
{
Switch_On = 1-(Switch_On); //转换开启状态变量
TR0 = Switch_On; //启动定时器T0用于风扇转速控制
TR1 = Switch_On;//这里主要是为了后面再次按下按键时,可同时关闭定时器T1
Gear = GEAR1; //开启关闭时均将档位Gear重置为档位1
Switch_Key_Flag = 0; //置标志位为0
}
TH1=0X3C; //给定时器T1赋初值,定时50ms,用于节能模式
TL1=0XB0;
T1_Int_times++;
if(T1_Int_times==100) //此时5秒无操作就把转速档位降下来,进入节能模式
{
T1_Int_times=0;
Gear = GEAR0; //节能模式为档位0
}
}
void main()
{
time0_init();//中断和定时器初始化部分
time1_init();
exti0_init();
SMG_PRM_PORT=0;//开始时使P0端口输出全为零,数码管无显示
while(1)
{
while(Switch_On)
{
u8 i =keys_scan(Gear_Key, Mode_Key); //扫描档位按键和模式按键
if(Switch_Key!=KEY_PRESS) Switch_Key_Flag = 1;
//重置开关按键的标志位
if(i==1) //i==1,说明档位按键被按下,见该函数定义
{
Gear++;
if(Gear==4) Gear=1;
T1_Int_times=0;//进入定时器T1的次数清零
}
if(i==2) TR1=~TR1; //i==2,说明模式按键被按下,转换模式
LED1=TR1; //LED1亮:恒速模式;LED1灭:节能模式
smg_static(Gear); //静态数码管显示档位
smg_prm_display(Gear); //动态数码管显示转速
}
if(!Switch_On) {IN_A=0;IN_AN=0;IN_B=0;IN_BN=0;smg_static(NA);LED1=1;}//电机关闭,此时数码管不显示任何数字
if(Switch_Key!=KEY_PRESS) Switch_Key_Flag = 1;
//重置开关按键的标志位
}
}
//step_motor.c
//电机单步驱动头文件
#include "step_motor.h"
#include "delay.h"
void step_motor_send_pulse(u8 step)//定义步进电机的步进值对应什么角度
{
switch(step)
{
case 0:IN_A=1;IN_AN=0;IN_B=0;IN_BN=0;break;
case 1:IN_A=1;IN_AN=0;IN_B=1;IN_BN=0;break;
case 2:IN_A=0;IN_AN=0;IN_B=1;IN_BN=0;break;
case 3:IN_A=0;IN_AN=1;IN_B=1;IN_BN=0;break;
case 4:IN_A=0;IN_AN=1;IN_B=0;IN_BN=0;break;
case 5:IN_A=0;IN_AN=1;IN_B=0;IN_BN=1;break;
case 6:IN_A=0;IN_AN=0;IN_B=0;IN_BN=1;break;
case 7:IN_A=1;IN_AN=0;IN_B=0;IN_BN=1;break;
}
}
//key.c
//按键头文件
#include "key.h"
static u8 flag=1;
u8 keys_scan(u8 key1, u8 key2) //按键状态函数
{
static u8 flag=1;
if(flag==1&&(key1==KEY_PRESS || key2==KEY_PRESS))
{
delay_ms(10); //按键消抖
flag=0;
if(key1==KEY_PRESS) return 1;
else if(key2==KEY_PRESS) return 2;
}
else if(key1!=KEY_PRESS && key2!=KEY_PRESS)
{
flag=1;
}
return 0;
}
//smg.c
//数码管显示头文件
#include "smg.h"
#include "delay.h"
#include "step_motor.h"
//定义速度档位
typedef enum {GEAR0=0,GEAR1=1,GEAR2=2,GEAR3=3} MotorGear_TypeDef;
u8 smg_gear_code[17]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,
0x82, 0xF8, 0x80, 0x90, 0x88, 0x83,
0xC6, 0xA1, 0x86, 0x8E, 0xFF,};
u8 smg_prm_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
u8 geartoprm[4][4]={{NA,6,0,0},{NA,7,5,0},{1,2,5,0},{1,8,0,0}};
//NA即不显示任何数字
void smg_static(u8 number) //静态数码管显示函数
{
EA=0;
SMG_GEAR_PORT = smg_gear_code[number];
EA=1;
}
void smg_dinamic(u8 array[4]) //动态数码管显示函数
{
u8 i=0;
for(i=0;i<4;i++)
{
switch(i)//位选
{
case 0: LSB=0;LSA=0;break;
case 1: LSB=0;LSA=1;break;
case 2: LSB=1;LSA=0;break;
case 3: LSB=1;LSA=1;break;
}
SMG_PRM_PORT=smg_prm_code[array[i]];//传送段选数据
delay_10us(50); //延时一段时间,等待显示稳定
SMG_PRM_PORT=0x00; //消影
}
}
void smg_prm_display(MotorGear_TypeDef GEAR)//转速显示函数
{
EA=0;
smg_dinamic(geartoprm[GEAR]);
EA=1;
}
//delay.c
//延时函数头文件
#include "reg51.h"
#include "delay.h"
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void delay_ms(u8 ms)
{
u16 i, j;
for(i=ms;i>0;i--)
for(j=110;j>0;j--);
}
六、程序测试方法与结果
1.分模块测试: 分别测试电机驱动模块、数码管模块和按键模块等;
2.将程序综合起来烧录进板子,测试实验现象,符合预期要求,程序编写成功。
七、实验问题分析与对策
实验问题分析:在本次实验中,由于使用了C语言和涉及中断,所以变量的可见性需要格外注意。按键状态的判断也是一个难点,由于抖动的存在,使得按键部分的设计需要更加细心和全面。之前所学的课程都是理论课程,由于这次是实践课程,涉及到板子的接线,所以需要对硬件部分有一定的了解和研究。
对策:在keil的debug中观察重要变量的值,从而检验自己函数部分编写有无对错;对硬件不懂的部分,多上网查询和请教同学。