51单片机--红外遥控

简介: 51单片机--红外遥控


红外遥控的介绍

红外遥控是一种无线、非接触控制技术,通过使用红外线来传送控制信号。它具有抗干扰能力强、信息传输可靠、功耗低、成本低、易实现等显著优点,因此被广泛应用于各种电子设备和家用电器,也越来越多地应用于计算机和手机系统中。

硬件电路

红外遥控系统一般由发射和接收两个部分组成。发射部分包括红外发光二极管,它是一种特殊的发光二极管,能够发射调制后的红外光波。接收部分通常采用红外接收模块,用于接收来自遥控器发射的红外信号。当遥控器处于学习状态时,接收模块会接收外来红外信号,并将其转换成电信号。经过检波、整形和放大等处理后,这些电信号会被传送到中央处理单元(CPU)进行解码和执行相应的控制动作。

发射端

IN是输入信号端口,输入的信号会通过38kHz的频率在三极管中放大,到发光二极管中,二极管发出闪烁着的特制红外光线

在现实生活中有很多种类型的红外线,为了让接收方只接收这一类型的红外线,就在电路中加入了频率,让这个信号能有一定频率的闪烁;

当我们没有输入时,默认为高电平,而当我们输入信号时,就会在低电平发出有频率波动的信号

接收端

这里将接收的发光二极管内置在了电路中,电路也很容易,电源、接地、以及信号输出

在这里我们会让OUT端口接上外部中断,当产生下降沿就进入中断

NEC编码

NEC编码是一种红外遥控协议,常用于遥控器与设备之间的通信。它是一种常用的编码格式;将遥控发送过来的信号进行一定形式的编码,转换为对应的信息

NEC编码的数据格式通常由以下几部分组成:

  1. 引导码(Leader Code):持续低电平(通常为9ms)和持续高电平(通常为4.5ms),用于标识开始传输数据。
  2. 系统码(Customer Code):8位二进制代码,用于识别具体的设备或品牌。
  3. 数据码(Data Code):8位二进制代码,表示具体的指令或按键信息。在传输数据时一般持续低电平(560us)和持续高电平(560us)表示传输‘0’,持续低电平(560us)和持续高电平(1690us)表示传输‘1’;
  4. 取反码(Inverted Data Code):与数据码和系统码相反的二进制代码,用于检验数据的正确性。
  5. 结束码(Stop Bit):持续高电平(通常为0.56ms),表示数据传输结束。在这里,我们没有引用到;
  6. 重复码(Repeat Code):数据传输之后,若持续低电平(9ms)和持续高电平(2.25ms),那么将会重复这个命令信息;

NEC编码使用38kHz的载波频率进行传输,通过在红外光线的调制和解调实现遥控功能。在发射时,数据按照引导码、系统码、数据码和取反码的顺序发送;在接收端,通过解码器将红外信号还原为对应的系统码和数据码,从而实现遥控器与设备的交互。

外部中断

51单片机的外部中断是指通过外部信号触发单片机进行中断处理的一种机制。单片机通过外部引脚接收到特定的信号后,会立即暂停当前的任务,转而执行与该中断相关的程序。

在51单片机中,有两个外部中断源,分别为外部中断0(INT0)和外部中断1(INT1)。这两个中断源分别对应单片机的引脚P3.2和P3.3。

外部中断可以通过两种触发方式来进行中断处理:电平触发和边沿触发。电平触发是指当外部信号维持在某个电平上时触发中断,可以是低电平触发或高电平触发;边沿触发是指当外部信号的跳变沿(上升沿或下降沿)出现时触发中断。

中断号

此次操作相关寄存器以及原理

使用外部中断前,需要进行相应的初始化操作。

初始化

void Interrupt0_Init()
{
  IT0=1;//外部中断0选择边沿触发
  IE0=1;//外部中断0请求标志
  EX0=1;//外部中断0请求允许位
  EA=1;//总中断允许
  PX0=1;//中断优先级控制
}

红外遥控实例代码

接下来,我们要实现的是在屏幕上显示地址,命令码,数字增减,通过遥控的控制,让命令码随之改变,数字达到增减;

我们将通过定时器和外部中断0来进行操控,写出NEC编码;

利用不同的状态表示到达不同的编码阶段;

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init();
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter();
void Timer0_Run(unsigned char Flag);
#endif

Timer0.c

#include <REGX52.H>
/**
  * @brief 定时器0初始化
  * @param 无
  * @reval 无
  */
void Timer0Init(void)   //1毫秒@11.0592MHz
{
  TMOD &= 0xF0;   //设置定时器模式
  TMOD |=0x01;
  TL0 = 0;    //设置定时初值
  TH0 = 0;    //设置定时初值
  TF0 = 0;    //清除TF0标志
  TR0 = 0;    //定时器0不计时
  
}
//定时器0设置计数器值,范围0-65535
void Timer0_SetCounter(unsigned int Value)
{
  TH0=Value/256;
  TL0=Value%256;
}
//定时器0获取计数器值
unsigned int Timer0_GetCounter()
{
  return (TH0<<8)|TL0;
}
//定时器0启动停止控制
void Timer0_Run(unsigned char Flag)
{
  TR0=Flag;
}

将定时器初始化为0,让定时器实现计时器的功能;

Interrupt.h

#ifndef __INTERRUPT_H__
#define __INTERRUPT_H__
void Interrupt0_Init();
#endif

Interrupt.c

#include <REGX52.H>
//外部中断0初始化
void Interrupt0_Init()
{
  IT0=1;
  IE0=1;
  EX0=1;
  EA=1;
  PX0=1;
}

按键对应命令码:

IR.h

#ifndef __IR_H__
#define __IR_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();
unsigned char IR_GetDataFlag();
unsigned char IR_GetRepeatFlag();
unsigned char IR_GetAddress();
unsigned char IR_GetCommand();
#endif

IR.c

#include <REGX52.H>
#include"Timer0.h"
#include"Interrupt.h"
unsigned int IR_Time;
unsigned char IR_State;
unsigned char IR_Data[4];
unsigned char IR_pData;
unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;
//红外遥控初始化
void IR_Init()
{
  Timer0Init();
  Interrupt0_Init();
}
//红外遥控获取收到数据帧标志位
unsigned char IR_GetDataFlag()
{
  if(IR_DataFlag)
  {
    IR_DataFlag=0;
    return 1;
  }
  return 0;
}
//红外遥控获取收到连发标志位
unsigned char IR_GetRepeatFlag()
{
  if(IR_DataFlag)
  {
    IR_RepeatFlag=0;
    return 1;
  }
  return 0;
}
//红外遥控获取收到的地址数据
unsigned char IR_GetAddress()
{
  return IR_Address;
}
//红外遥控获取收到的命令数
unsigned char IR_GetCommand()
{
  return IR_Command;
}
//外部中断0函数
void Int0_Routine() interrupt 0
{
  if(IR_State==0)  //状态0,空闲状态
  {
    Timer0_SetCounter(0); //计数器清0
    Timer0_Run(1); //定时器启动
    IR_State=1; //变为状态1
  }
  else if(IR_State==1) //状态1,等待Start信号或Repeat信号
  {
    IR_Time=Timer0_GetCounter(); //获取开始的时间或重复的时间
    Timer0_SetCounter(0); //计时器清0
    if(IR_Time>12442-500&&IR_Time<12442+500)//表示是开始信号,进入下一阶段
    {
      IR_State=2;
    }
    else if(IR_Time>10368-500&&IR_Time<10368+500)//是重复信号,回到最初的起点
    {
      IR_RepeatFlag=1; //连发帧标志位置1
      Timer0_Run(0); //定时器停止
      IR_State=0;  //状态置0
    }
    else
    {
      IR_State=1;
    }
  }
  else if(IR_State==2) //状态2,接收数据
  {
    IR_Time=Timer0_GetCounter();//获取接收数据的时间段
    Timer0_SetCounter(0);
    if(IR_Time>1032-500&&IR_Time<1032+500)//数据接到为0
    {
      IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));
      IR_pData++;
    }
    else if(IR_Time>2074-500&&IR_Time<2074+500)//数据接收到为1
    {
      IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));//除8是将32位下标分为4个元素,模8将32位下标从0~7有序循环
      IR_pData++;
    }
    else //接收错误
    {
      IR_pData=0;
      IR_State=1;
    }
    if(IR_pData>=32) //如果收到了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); //定时器停止
      IR_State=0; //状态置0
      
    }
  }
}

LCD1602.c

#include <REGX52.H>
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
  unsigned char i, j;
  i = 2;
  j = 239;
  do
  {
    while (--j);
  } while (--i);
}
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
  LCD_RS=0;
  LCD_RW=0;
  LCD_DataPort=Command;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
  LCD_RS=1;
  LCD_RW=0;
  LCD_DataPort=Data;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
  if(Line==1)
  {
    LCD_WriteCommand(0x80|(Column-1));
  }
  else if(Line==2)
  {
    LCD_WriteCommand(0x80|(Column-1+0x40));
  }
}
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
  LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
  LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
  LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
  LCD_WriteCommand(0x01);//光标复位,清屏
}
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
  LCD_SetCursor(Line,Column);
  LCD_WriteData(Char);
}
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=0;String[i]!='\0';i++)
  {
    LCD_WriteData(String[i]);
  }
}
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
  unsigned char i;
  int Result=1;
  for(i=0;i<Y;i++)
  {
    Result*=X;
  }
  return Result;
}
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
  }
}
/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
  unsigned char i;
  unsigned int Number1;
  LCD_SetCursor(Line,Column);
  if(Number>=0)
  {
    LCD_WriteData('+');
    Number1=Number;
  }
  else
  {
    LCD_WriteData('-');
    Number1=-Number;
  }
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
  }
}
/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i,SingleNumber;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    SingleNumber=Number/LCD_Pow(16,i-1)%16;
    if(SingleNumber<10)
    {
      LCD_WriteData(SingleNumber+'0');
    }
    else
    {
      LCD_WriteData(SingleNumber-10+'A');
    }
  }
}
/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
  }
}

LCD1602.h

#ifndef __LCD1602_H__
#define __LCD1602_H__
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
#endif

main.c

#include <REGX52.H>
#include"LCD1602.h"
#include"IR.h"
unsigned char Num;
unsigned char Address;
unsigned char Command;
void main()
{
  LCD_Init();
  LCD_ShowString(1,1,"ADDR  CMD  NUM");
  LCD_ShowString(2,1,"00    00   000");
  IR_Init(); //NEC编码初始化
  while(1)
  {
    if(IR_GetDataFlag()||IR_GetRepeatFlag()) //数据帧或者重复帧
    {
      Address=IR_GetAddress(); //获取遥控器地址吗
      Command=IR_GetCommand(); //获取遥控器命令吗
      
      LCD_ShowHexNum(2,1,Address,2);
      LCD_ShowHexNum(2,7,Command,2);
      
      if(Command==IR_VOL_MINUS)
      {
        Num--;
      }
      if(Command==IR_VOL_ADD)
      {
        Num++;
      }
      LCD_ShowNum(2,12,Num,3);
      
    }
  }
}

通过判断标志位是否为1来表示是否有遥控信息传输

相关文章
|
3月前
|
存储 数据安全/隐私保护 芯片
毕业设计 基于51单片机的指纹红外密码电子锁
毕业设计 基于51单片机的指纹红外密码电子锁
|
3月前
|
芯片
毕业设计|基于stm32单片机的app视频遥控抽水灭火小车设计
毕业设计|基于stm32单片机的app视频遥控抽水灭火小车设计
|
9月前
|
C语言 芯片 数据格式
C语言课设项目-51单片机-红外通信
C语言课设项目-51单片机-红外通信
|
9月前
|
传感器 算法 安全
基于单片机的红外光控灯系统(二)
基于单片机的红外光控灯系统(二)
138 0
|
9月前
|
传感器 存储 缓存
基于单片机的红外光控灯系统(一)
基于单片机的红外光控灯系统(一)
101 0
|
安全 芯片
基于的单片机的无线遥控器设计
基于的单片机的无线遥控器设计
146 0
基于的单片机的无线遥控器设计
|
存储 编解码 芯片
基于单片机设计的遥控数字音量控制D类功率放大器设计
基于单片机设计的遥控数字音量控制D类功率放大器设计
233 1
基于单片机设计的遥控数字音量控制D类功率放大器设计
|
芯片
基于单片机的红外遥控LED电子钟
基于单片机的红外遥控LED电子钟
基于单片机的红外遥控LED电子钟
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
177 0
单片机:红外遥控实验(内含红外遥控介绍+硬件原理+软件编程+配置环境)
|
4月前
|
物联网
STC51单片机-实验开发装置仿真-物联网应用系统设计
STC51单片机-实验开发装置仿真-物联网应用系统设计
53 0