红外遥控电机调速+普中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 参考资料

posted @ 2023-01-18 21:13  豌豆茶  阅读(946)  评论(1编辑  收藏  举报