红外遥控接收模块+普中51单片机+江科大自化协

1 实验现象

  LCD1602上显示红外解码遥控器地址、键值。通过红外遥控器,按下某个按键时,遥控器通过红外发光二极管发射红外线,红外一体化接受头接受信号,并对其解码,单片机处理相应的信息,将得到的数据显示在LCD1602上。

2 实验原理

 红外遥控是一种无线、非接触控制技术,具有抗干扰性能力强,信息传输可靠,功耗低、成本低、易实现等显著优点,被诸多电子设备特别是家用电器广泛采用,在工业设备中也得到广泛应用。

什么是红外线?红外线是一种肉眼看不见的光。红外线是波长介于微波和可见光之间的电磁波,波长在760nm和1000nm之间,是波形比红光长的非可见光。

如何发出红外光?红外发射二极管就可以发出红外线。最常用的红外发射管是发出940nm的红外光。它的外形类似二极管,但是它发射出来的是红外光,是我们肉眼所看不到的。发光二极管的亮度会随着电路的增大而增加,同样的道理,红外发射管发射红外线的强度也会随着电流的增大而增强。

红外遥控基本原理:红外遥控器常用的载波的方式传送二级制编码,常用的载波频率为38KHz,通常的红外遥控器是将遥控信号(二进制脉冲码)调制在38KHz的载波上,井缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。

3 系统设计

4 硬件设计(略)

5 软件设计

 5.1 主函数

#include <REGX52.H>
#include "lcd1602.h"
#include "InfraredRemote.h"

unsigned char Num;
unsigned char address;
unsigned char command;

void main()
{
    LCD1602_Init();
    LCD1602_ShowString(1,1,"ADDR  CMD  NUM");
    LCD1602_ShowString(2,1,"00    00   000");
    
    IR_init();
    
    while(1)
    {
        if(IR_getdataflag()||IR_getrepeatflag()) //如果收到数据帧或者收到连发帧
        {
            address=IR_getaddress();    //获取遥控器地址码
            command=IR_getcommand();    //获取遥控器命令码
            
            LCD1602_ShowHexNum(2,1,address,2);    //显示遥控器地址码
            LCD1602_ShowHexNum(2,7,command,2);    //显示遥控器命令码
            
            if(command==IR_VOL_MINUS)    //如果遥控器VOL-按键按下
            {
                Num--;                    //Num自减
            }
            
            if(command==IR_VOL_ADD)        //如果遥控器VOL+按键按下
            {
                Num++;                    //Num自增
            }
            LCD1602_ShowNum(2,12,Num,3);//显示Num
        }
    }
}

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 外部中断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.4 定时器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.5 LCD1602显示函数

#include <REGX52.H>
#include "intrins.h"

//引脚配置,硬件原理图,普中A2
sbit LCD1602_RS = P2^6;
sbit LCD1602_RW = P2^5;
sbit LCD1602_E  = P2^7;
#define LCD1602_DATAPORT P0


/**
  * @brief  LCD1602延时函数
  * @param  xms=1, 延时1ms
  * @retval 无
  */
void LCD1602_Delayms(unsigned char xms)        //@11.0592MHz
{
    unsigned char i, j,k;

    for(k=0;k<xms;k++)
    {
        _nop_();
        i = 2;
        j = 199;
        do
        {
            while (--j);
        } while (--i);
    }

}

/**
  * @brief  LCD1602写命令,时序图
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD1602_WriteCommand(unsigned char Command)
{
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DATAPORT  = Command;
    LCD1602_E  = 1;
    LCD1602_Delayms(1);    //延时1ms
    LCD1602_E  = 0;
    LCD1602_Delayms(1);    //延时1ms
}

/**
  * @brief  LCD1602写数据,时序图
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD1602_WriteData(unsigned char Data)
{
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DATAPORT  = Data;
    LCD1602_E  = 1;
    LCD1602_Delayms(1);    //延时1ms
    LCD1602_E  = 0;
    LCD1602_Delayms(1);    //延时1ms
}

/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD1602_Init(void)
{
    LCD1602_WriteCommand(0x38);    //0011_1000 功能设置,设置16x2显示,5x7点阵,8位数据接口
    LCD1602_WriteCommand(0x0C);    //0000_1100 显示开关控制,显示开,光标关,闪烁关    
    LCD1602_WriteCommand(0x06);    //0000_0110 输入方式设置,数据写后,AC自动加一,画面不动
    LCD1602_WriteCommand(0x01);    //0000_0001 清屏,数据指针清零、所有显示清零
}
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD1602_SetCursor(unsigned char Line,unsigned char Column)
{
    if(Line == 1)
    {
        LCD1602_WriteCommand(0x80|(Column-1)); //设置要存入数据的DDROM地址,第一行地址
    }
    else
    {
        LCD1602_WriteCommand(0x80|(Column-1)+0x40); //设置要存入数据的DDROM地,第二行地址
    }
}
    

/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char)
{
    LCD1602_SetCursor(Line,Column);
    LCD1602_WriteData(Char);
}

/**
  * @brief  在LCD1602指定位置上显示所给字符串
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符串
  * @retval 无
  */
void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String)        //指针
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=0;String[i]!='\0';i++)    //字符串结束标志'\0'
    {
        LCD1602_WriteData(String[i]);
    }
    
}

/**
  * @brief  返回值=X的Y次方,指数运算
  */
int LCD_Power(int X,int Y)
{
    unsigned char i;
    int result = 1;
    for(i=0;i<Y;i++)
    {
        result = result * X;
    }
    return result;
}

/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number/LCD_Power(10,i-1)%10); // '0'=0x30,转换为字符显示;
        
        /** 
          * 789, 789/100%10, 取出百位
          * 789, 789/10%10, 取出十位
          * 789, 789/1%10, 取出个位
         */
    }
}

/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column, int Number,unsigned char Length)
{
    unsigned char i;
    unsigned int Number1; 
    LCD1602_SetCursor(Line,Column);
    if(Number >= 0)
    {
        LCD1602_WriteData('+');
        Number1 = Number;
    }
    else
    {
        LCD1602_WriteData('-');
        Number1 = -Number;     //-32768
    }
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number1/LCD_Power(10,i-1)%10); 
    }
}

/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xffff
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    unsigned char SingleNumber;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        SingleNumber = Number/LCD_Power(16,i-1)%16; //从高位到低位,取出每一个十六进制数字
        if(SingleNumber < 10)
            LCD1602_WriteData('0'+SingleNumber); 
        else
            LCD1602_WriteData('A'+SingleNumber-10); //SingleNumber已含有10+x
    }
}

/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111_1111_1111_1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
    unsigned char i;
    LCD1602_SetCursor(Line,Column);
    for(i=Length;i>0;i--)
    {
        LCD1602_WriteData('0'+Number/LCD_Power(2,i-1)%2); //从高位到低位,取出每一个二进制数字
    }
}
#ifndef __LCD1602_H__
#define __LCD1602_H__
    
    void LCD1602_WriteCommand(unsigned char Command);
    void LCD1602_WriteData(unsigned char Data);

    void LCD1602_Init(void);
    void LCD1602_ShowChar(unsigned char Line,unsigned char Column,unsigned char Char);
    void LCD1602_ShowString(unsigned char Line,unsigned char Column,unsigned char *String);
    void LCD1602_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowSignedNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    void LCD1602_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
    
#endif

6 参考资料

(1)单片机应用实践篇——红外遥控的应用之红外遥控简介,协议分析及硬件电路的设计_哔哩哔哩_bilibili

posted @ 2023-01-18 11:00  豌豆茶  阅读(1171)  评论(0编辑  收藏  举报