红外遥控
介绍
红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出,其通信方式为单工,异步。它的红外LED波长为940nm,通信协议标准:NEC标准
硬件电路
对于左边的图
这里连接了两个三极管开关;R1所在电路输入端为38KHZ的方波;IN口输入为波形;对应的三极管开关(PHP型)为低电平导通(高电平不导通);利用两个三极管开关,组合成IN口传出的波形,波形每段对应38KHZ的波形,从而与控制红外LED的闪烁相对应。但是,加入38KHZ的波形闪烁,使得该红外波形成为特有的类型,从而不会受到外界干扰,比如太阳光,
对于中间的图
该图的原理与左边的图类似,只不过需要用程序自己模拟一遍38KHZ的方波。
对于右边的图
这是接收电路,将数据传入红外接收器,经过滤波以及各种解码操作,他就会通过OUT口输出,我们对输出的信号进行分析就行
在此基础上,它还有好几个状态,处于空闲状态时红外LED不亮,接收头输出高电平,处于发送低电平时,红外LED以38KHz频率闪烁发光,接收头输出低电平,处于发送高电平时红外LED不亮,接收头输出高电平。
NEC编码
对于Data格式部分,地址码反码对地址取反,用于对地址码进行验证;命令反码同理。一共是32位,前8位是地址码,后8位是地址码的反码,再后八位是命令码,跟在后面的8位也是命令码的反码,用来校验数据。
对于蓝色部分,即数据红外接收部分,560us的下降沿与560us的上升沿组成数据0;560us的下降沿与1600us的上升沿组成数据1。一帧数据时间长度为110ms,采集完后接下来进行分析解调。绿色的Repeat部分为按住按键不放时,连续发送数据的意思。
以上图的第一个为例,首先是启动码,然后是00000000是地址码,11111111是反码,10100010是命令码,01011101是反码。
其实51单片机的遥控器的键码就是命令码
外部中断
外部中断寄存器
从上图中,我们可以知道 INT0用来选择中断方式,EX0表示使能中断,EA表示使能所有中断,PX则是选择优先级。
红外遥控代码
主函数代码
#include <REGX52.H>
#include "Delay.h"
#include "LCD1602.h"
#include "Int0.h"
#include "Timer0.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();
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);
}
}
}
部分模块代码
#include <REGX52.H>
/**
*@brief 定时器0初始化,100us@12.000MHz
*@param 无
*@retval 无
*/
void Timer0_Init(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0; //设置定时初值
TH0 = 0; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 0; //定时器0开始计时
}
/**
* @brief 定时器0设置计数器值
* @param Value,要设置的计数器值,范围:0~65535
* @retval 无
*/
void Timer0_SetCounter(unsigned int Value)//计数器部分
{
TH0=Value/256;
TL0=Value%256;
}
/**
* @brief 定时器0获取计数器值
* @param 无
* @retval 计数器值,范围:0~65535
*/
unsigned int Timer0_GetCounter(void)//返回计算值
{
return (TH0<<8)|TL0;
}
/**
* @brief 定时器0启动停止控制
* @param Flag 启动停止标志,1为启动,0为停止
* @retval 无
*/
void Timer0_Run(unsigned char Flag) //控制计数器是否启动
{
TR0=Flag; //给1开始计时,给零停止计时
}
#include <REGX52.H>
/**
* @brief 外部中断0初始化
* @param 无
* @retval 无
*/
void Int0_Init(void)
{
IT0=1; //配置为下降沿触发
IE0=0; //设置为中断标志位清零
EX0=1; //打开后续通路
EA=1;
PX0=1; //设置为优先级为1,即高优先级,从而能够打断其它中断进行中断嵌套
}
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.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; //存储命令码
/**
* @brief 红外遥控初始化
* @param 无
* @retval 无
*/
void IR_Init(void)//初始化
{
Timer0_Init();
Int0_Init();
}
/**
* @brief 红外遥控获取收到数据帧标志位
* @param 无
* @retval 是否收到数据帧,1为收到,0为未收到
*/
unsigned char IR_GetDataFlag(void)
{
if(IR_DataFlag) //判断是否接收到数据
{
IR_DataFlag=0;
return 1;
}
return 0;
}
/**
* @brief 红外遥控获取收到连发帧标志位
* @param 无
* @retval 是否收到连发帧,1为收到,0为未收到
*/
unsigned char IR_GetRepeatFlag(void)
{
if(IR_RepeatFlag) //判断是否接收到重复(Repeat)信号
{
IR_RepeatFlag=0;
return 1;
}
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) //处于空闲状态
{
Timer0_SetCounter(0);//确保启动时计数器为0
Timer0_Run(1); //启动定时器
IR_State=1; //将状态置1,开始处理
}
else if(IR_State==1) //触发读取
{
IR_Time=Timer0_GetCounter();//获取当前时间
Timer0_SetCounter(0); //将计数器清零
if(IR_Time>12442-500&&IR_Time<12442+500) //得到启动信号
{
IR_State=2; //下次中断要解码数据
}
else if(IR_Time>10368-500&&IR_Time<10368+500)//得到重发(Repeat)信号
{
IR_RepeatFlag=1; //表示收到数据
Timer0_Run(0); //停止计数器
IR_State=0; //回到空闲状态
}
else //数据出错时
{
IR_State=1; //搜索起始信号
}
}
else if(IR_State==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)); //将数组单元特定位清零(先1在取反)
IR_pData++;
}
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;
IR_State=1; //搜索下一个信号
}
if(IR_pData>=32) //搜索完成时
{
IR_pData=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; //已获得数据
}
Timer0_Run(0); //停止计数器运行
IR_State=0; //切回空闲状态
}
}
}
红外遥控直流电机代码
主函数代码
#include <REGX52.H>
#include "Key.h"
#include "Delay.h"
#include "Nixie.h"
#include "Timer1.h"
#include "Timer0.h"
#include "IR.h"
#include "Int0.h"
#include "Motor.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;}
Speed++;
Speed%=4;
if(Speed==0){Motor_SetSpeed(0);}
if(Speed==1){Motor_SetSpeed(50);}
if(Speed==2){Motor_SetSpeed(75);}
if(Speed==3){Motor_SetSpeed(100);}
}
Nixie(1,Speed);
}
}
部分模块代码
#include <REGX52.H>
#include "Timer1.h"
sbit Motor=P1^0;
unsigned char Counter,Compare;//定义计数器与比较值
/**
* @brief 电机初始化
* @param 无
* @retval 无
*/
void Motor_Init(void)
{
Timer1_Init();
}
/**
* @brief 电机设置速度
* @param Speed 要设置的速度,范围0~100
* @retval 无
*/
void Motor_SetSpeed(unsigned char Speed)
{
Compare=Speed;
}
void Timer1_Routine() interrupt 3
{
TL1 = 0x9C; //设置定时初值
TH1 = 0xFF; //设置定时初值
Counter++;
Counter%=100;
if(Counter<Compare)
{
Motor=1;
}
else
{
Motor=0;
}
}