红外遥控电机调速+普中51单片机+江科大自化协
1 实验现象
基于STC89C52单片机设计一个红外遥控直流电机调速系统。一上电,数码管上显示0,此时直流电机不转动。当按下红外遥控器上的“1”键时,数码管显示1,直流电机开始转动。电机转速分为4个挡位,每升高一个挡位,数码管都会显示对应的挡位数字同时提高电机的转速。
2 实验原理
该系统整体采用红外遥控器控制,红外接收模块接收到遥控器键码信号后,向单片机送入中断信号并由单片机译码,单片机开始进行相应挡位的数据处理和挡位的改变。通过模拟PWM模块产生不同的PWM矩形波来控制直流电机调速,最后显示挡位到数码管上。
3 系统设计
4 硬件设计(略)
5 软件设计
5.1 主函数
#include <REGX52.H> #include "Nixie.h" #include "motor.h" #include "InfraredRemote.h" unsigned char Command,Speed; void main() { motor_init(); IR_init(); while(1) { if(IR_getdataflag()) //如果收到数据帧 { Command=IR_getcommand(); //获取遥控器命令码 if(Command==IR_0) {Speed=0;} //根据遥控器命令码设置速度 if(Command==IR_1) {Speed=1;} if(Command==IR_2) {Speed=2;} if(Command==IR_3) {Speed=3;} if(Command==IR_4) {Speed=4;} if(Speed==0) {motor_setspeed(0);} //速度输出 if(Speed==1) {motor_setspeed(25);} if(Speed==2) {motor_setspeed(50);} if(Speed==3) {motor_setspeed(75);} if(Speed==4) {motor_setspeed(100);} } Nixie(1,Speed); } }
5.2 红外遥控函数
#include <REGX52.H> #include "timer0.h" #include "exint0.h" unsigned int IR_time; //记录相邻两个下降沿的时间 unsigned char IR_state; //状态机设计 unsigned char IR_data[4]; //用来接收数据32位,4个字节 unsigned char IR_pdata; //用来记录接收第几位数据,0-31 unsigned char IR_dataflag; //数据接收完毕标识信号 unsigned char IR_repeatflag; //重复发送标识信号 unsigned char IR_address; //接收地址 unsigned char IR_command; //接收命令 /** * @brief 红外遥控初始化 * @param 无 * @retval 无 */ void IR_init(void) { exint0_init(); //外部中断INT0 timer0_init(); //定时器T0 } /** * @brief 红外遥控获取收到数据帧标志位 * @param 无 * @retval 是否收到数据帧,1为收到,0为未收到 */ unsigned char IR_getdataflag(void) { if(IR_dataflag) { IR_dataflag=0; return 1; } else return 0; } /** * @brief 红外遥控获取收到连发帧标志位 * @param 无 * @retval 是否收到连发帧,1为收到,0为未收到 */ unsigned char IR_getrepeatflag(void) { if(IR_repeatflag) { IR_repeatflag=0; return 1; } else return 0; } /** * @brief 红外遥控获取收到的地址数据 * @param 无 * @retval 收到的地址数据 */ unsigned char IR_getaddress(void) { return IR_address; } /** * @brief 红外遥控获取收到的命令数据 * @param 无 * @retval 收到的命令数据 */ unsigned char IR_getcommand(void) { return IR_command; } //外部中断0中断函数,下降沿触发执行,状态机设计 void int0_routine(void) interrupt 0 { if(IR_state==0) //状态0,空闲状态 { timer0_SetCounter(0); //设置计数器起始值,从0开始计数 timer0_Run(1); //启动定时器T0 IR_state=1; } else if(IR_state==1) //状态1,等待Start信号或Repeat信号 { IR_time=timer0_GetCounter(); //获取上一次中断到此次中断的时间 timer0_SetCounter(0); //设置计数器起始值,从0开始计数 //如果计时为9ms+4.5ms=13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442) if(IR_time>12442-500 && IR_time<12442+500) //起始信号(11.0592MHz) { IR_state=2; //状态2,接收数据 } //如果计时为9ms+2.25ms=11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368) else if(IR_time>10368-500 && IR_time<10368+500) //重复信号 { IR_repeatflag=1; //置收到连发帧标志位为1 timer0_Run(0); //关闭定时器T0 IR_state=0; //置状态为0 } else IR_state=1; //接收错误,状态保持 } else if(IR_state==2) //接收数据状态 { IR_time=timer0_GetCounter(); //获取上一次中断到此次中断的时间 timer0_SetCounter(0); //设置计数器起始值,从0开始计数 //如果计时为560+560=1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032) if(IR_time>1032-500 && IR_time<1032+500) //数据0 { IR_data[IR_pdata/8] &= ~(0x01<<(IR_pdata%8)); //数据对应位清0 IR_pdata++; //数据位置指针自增 } //如果计时为560+1690=2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074) else if(IR_time>2074-500 && IR_time<2074+500) //数据1 { IR_data[IR_pdata/8] |= (0x01<<(IR_pdata%8)); //数据对应位置1 IR_pdata++; //数据位置指针自增 } } else //接收出错 { IR_pdata=0; //数据位置指针清0 IR_state=1; //置状态为1 } if(IR_pdata>=32) //接收完所有数据 { IR_pdata=0; //数据位置指针清0 //判断地址码和地址反码,数据码和数据反码是否相反,数据验证 if((IR_data[0]==~IR_data[1])&&(IR_data[2]==~IR_data[3])) { IR_address=IR_data[0]; //转存数据,地址数据 IR_command=IR_data[2]; //命令数据 IR_dataflag=1; //置收到数据帧标志位为1 } timer0_Run(0); //关闭定时器T0 IR_state=0; //空闲状态 } }
#ifndef _InfraredRemote_h_ #define _InfraredRemote_h_ #define IR_POWER 0x45 #define IR_MODE 0x46 #define IR_MUTE 0x47 #define IR_START_STOP 0x44 #define IR_PREVIOUS 0x40 #define IR_NEXT 0x43 #define IR_EQ 0x07 #define IR_VOL_MINUS 0x15 #define IR_VOL_ADD 0x09 #define IR_0 0x16 #define IR_RPT 0x19 #define IR_USD 0x0D #define IR_1 0x0C #define IR_2 0x18 #define IR_3 0x5E #define IR_4 0x08 #define IR_5 0x1C #define IR_6 0x5A #define IR_7 0x42 #define IR_8 0x52 #define IR_9 0x4A void IR_init(void); unsigned char IR_getdataflag(void); unsigned char IR_getrepeatflag(void); unsigned char IR_getaddress(void); unsigned char IR_getcommand(void); #endif
5.3 定时器T0
#include <REGX52.H> /** * @brief 定时器0初始化,1毫秒@11.0592MHz * @param 无 * @retval 无 */ void timer0_init(void) //1毫秒@11.0592MHz { TMOD &= 0xF0; //设置定时器模式,1111_0000,&,高四位保留,低四位清零 TMOD |= 0x01; //设置定时器模式,0000_0001,|,高四位保留,设置模式为T0 TL0 = 0; //设置定时初始值 TH0 = 0; //设置定时初始值 TF0 = 0; //清除TF0标志 TR0 = 0; //定时器0暂时不计时 } void timer0_SetCounter(unsigned int value) { TH0=value/256; TL0=value%256; } unsigned int timer0_GetCounter(void) { return (TH0<<8)|TL0; } void timer0_Run(unsigned char Flag) { TR0=Flag; }
#ifndef _timer0_h_ #define _timer0_h_ void timer0_init(void); void timer0_SetCounter(unsigned int value); unsigned int timer0_GetCounter(void); void timer0_Run(unsigned char Flag); #endif
5.4 外部中断INT0
#include <REGX52.H> void exint0_init(void) { IT0=1; IE0=0; EX0=1; EA=1; PX0=1; } //void int0_routine(void) interrupt 0 //{ // //}
#ifndef _exint0_h_ #define _exint0_h_ void exint0_init(void); #endif
5.5 直流电机驱动函数
#include <REGX52.H> #include "timer1.h" sbit Motor=P1^3; //直流电机,高电平驱动 unsigned char Counter,Compare; //计数值和比较值,用于输出PWM /** * @brief 电机初始化 * @param 无 * @retval 无 */ void motor_init() { timer1_init(); } /** * @brief 电机设置速度 * @param Speed 要设置的速度,范围0~100 * @retval 无 */ void motor_setspeed(unsigned char speed) { Compare=speed; } //定时器1中断函数 void Timer1_Routine() interrupt 3 { TL1 = 0xAE; //设置定时初始值,100us,@11.0592MHz TH1 = 0xFB; //设置定时初始值,100us,@11.0592MHz Counter++; Counter%=100; //计数值计算范围为0-99 if(Counter<Compare) //计数值小于比较值 { Motor=1; //高电平驱动,电机转动 } else { Motor=0; //电机停止 } }
#ifndef _motor_h_ #define _motor_h_ void motor_init(); void motor_setspeed(unsigned char speed); #endif
5.6 定时器T1
#include <REGX52.H> /** * @brief 定时器1初始化,100微秒@11.0592MHz * @param 无 * @retval 无 */ void timer1_init(void) //100微秒@11.0592MHz { TMOD &= 0x0F; //设置定时器模式,0000_1111,&,低四位保留,高四位清零 TMOD |= 0x10; //设置定时器模式,0001_0000,|,低四位保留,设置模式为T1 TL1 = 0xAE; //设置定时初始值,100us,@11.0592MHz TH1 = 0xFB; //设置定时初始值,100us,@11.0592MHz TF1 = 0; //清除TF0标志 TR1 = 1; //定时器0开始计时 ET1=1; //打开定时器T0中断开关 EA=1; //打开中断系统总开关 PT1=0; //设置T0中断优先级,低 } /*定时器中断函数模板 void Timer1_Routine() interrupt 3 { static unsigned int T1Count; TL1 = 0x66; //设置定时初值,1毫秒@11.0592MHz TH1 = 0xFC; //设置定时初值,1毫秒@11.0592MHz T1Count++; if(T1Count>=1000) { T1Count=0; } } */
#ifndef _timer1_h_ #define _timer1_h_ void timer1_init(void); #endif
5.7 数码管显示函数
#include <REGX52.H> #include "delayms.h" sbit HC138_A=P2^2; sbit HC138_B=P2^3; sbit HC138_C=P2^4; //数码管段码表,0-9 unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}; /** * @brief 数码管显示 * @param Location 要显示的位置,范围:1~8 * @param Number 要显示的数字,范围:段码表索引范围 * @retval 无 */ void Nixie(unsigned char Location,Number) { switch(Location) //位码输出 { case 1:HC138_C=1;HC138_B=1;HC138_A=1;break; case 2:HC138_C=1;HC138_B=1;HC138_A=0;break; case 3:HC138_C=1;HC138_B=0;HC138_A=1;break; case 4:HC138_C=1;HC138_B=0;HC138_A=0;break; case 5:HC138_C=0;HC138_B=1;HC138_A=1;break; case 6:HC138_C=0;HC138_B=1;HC138_A=0;break; case 7:HC138_C=0;HC138_B=0;HC138_A=1;break; case 8:HC138_C=0;HC138_B=0;HC138_A=0;break; } P0=NixieTable[Number]; //段码输出 delayms(1); //显示一段时间 P0=0x00; //段码清0,消影 }
#ifndef __NIXIE_H__ #define __NIXIE_H__ void Nixie(unsigned char Location,Number); #endif
5.8 延时函数
#include <intrins.h> void delayms(unsigned int xms) //@11.0592MHz { unsigned char i, j; while(xms--) { _nop_(); i = 2; j = 199; do { while (--j); } while (--i); } }
#ifndef _delayms_h_ #define _delayms_h_ delayms(unsigned int xms); #endif
6 参考资料