单片机之红外遥控
目录
红外遥控简介
红外发射二极管
红外接收二极管
红外遥控原理
红外发射二极管原理
红外接收二极管原理
基本的发送与接收
NEC编码
基本原理
DATA数据的格式
DATA中的1与0的表示
遥控器的键码值
按键波形案例
仿真案例
前言
keil文件
红外遥控简介
红外发射二极管
注意:红外发射二极管和一般的LED灯很相似,但是红外发射二极管用来发射红外光,红外光是不可见光,人眼几乎看不到,虽然我们看不到现象,但他也在发射红外光。
红外接收二极管
注意:
红外遥控原理
红外发射二极管原理
理解:
红外接收二极管原理
前言:红外发射二极管通常用于红外遥控,若要遥控设备,则还需要对应的红外接收二极管。
原理:红外接收二极管在电路中需要给他反向供电,当红外发射二极管开关断开时,这个二极管是截止状态,此时二极管阻抗无穷大,所以右侧点位电压输出的接近5V;而当开关闭合之后,该红外接收二极管被红光照射时他的阻抗很小,所以此时输出的电压接近0V;把该引脚连接到单片机声,我们就可以通过红外光是否发射来通过单片机控制外部设备了。
注意:
基本的发送与接收
理解:给红外发射二极管输送含有38KHz载波的波形信号(含有38KHz载波的波形地方为低电平,而整个波形里面封装了地址码与命令码)那么红外发射的二极管就会在输入波形为低电平的时刻以38KHz的频率一直闪烁,这样就能表示将要发的数据,当红外接收二极管接收到这个闪烁的红外光时,就会将对应的闪烁信号转化为波(接收到红光时波形显示为波的低电平,不接受红外光时展示波形显示为波的高电平),并把38KHz的载波信号进行解码,高电平的地方还为高电平,将含有38KHz载波的波形转化为低电平,进而得到完整的波形,然后将该波形传输到单片机中作为控制信号。
NEC编码
基本原理
前言:38KHz的载波频率只是针对于底层通信,而NEC编码中并不会出现38KHz的调制(底层做好了基本的发射高低电平,然后把他封装在一个模块中)
理解:上面的波形就是我们的遥控器按键按下之后接收头发射器out引脚输出的波形,首先没有按键按下那么波形就是前面蓝色的高电平代表空闲状态,一旦有按键按下,那么out就会输出一个start信号,这个start信号由9ms的低电平和4.5ms的高电平组成.主要用于告诉单片机这些接收设备有按键过来了进而做出准备;start信号发送完之后就会有一长段的数据区(就表示我们遥控器的地址码和控制码波形部分)数据区发送格式见下,数据发送完之后需要在跟一个下降沿(该下降沿560us后上升,其作用为终止最后一位)升到高电平后一帧数据就发完了,总共110ms;
注意:
DATA数据的格式
注意:
DATA中的1与0的表示
前言:NEC的data并不像我们传统模式高电平表示1,低电平表示0.
遥控器的键码值
理解:按按键后就会使红外发射器按一定的频率闪烁,进而等待红外接收器的接收。
按键波形案例
前言:通过红外发射器按键2后发射红外光,发射后通过红外接收器接收调制后得到波形。
理解:
仿真案例
需求:通过红外遥控的按键控制单片机将自己按键的地址码和命令码显示在LCD1602液晶屏上,用音量+/-键控制Num+/-,并将Num也显示在LCD上(按键长按多次生效)
前言
keil文件
#include "regx52.h"
//LCD1602模块——————————————————————————————————————————————————————————
sbit RS=P2^6;
sbit RW=P2^5;
sbit E=P2^7;
//延时n毫秒
void delay(unsigned int n)
{
while(n--){
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
//写指令
void writecom(unsigned char com){
RS=0;
RW=0;
E=0;
P0=com;
delay(5);
E=1;
E=0;
}
//写数据
void writedat(unsigned char dat){
RS=1;
RW=0;
E=0;
P0=dat;
delay(5);
E=1;
E=0;
}
//初始化液晶屏
void initlcd(){
writecom(0x38);
writecom(0x0c);
writecom(0x06);
writecom(0x01);
}
int LCD_Pow(int X,int Y){
unsigned char i;
int Result=1;
for(i=0;i<Y;i++){
Result*=X;
}
return Result;
}
//展示16进制数字
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){
unsigned char i;
unsigned char SingleNumber;
if(Line==1){
writecom(0x80|(Column-1));
}else{
writecom(0x80|(Column-1)+0x40);
}
for(i=Length;i>0;i--){
SingleNumber=Number/LCD_Pow(16,i-1)%16;
if(SingleNumber<10){
writedat('0'+SingleNumber);
}else{
writedat('A'+SingleNumber-10);
}
}
}
//外部中断与定时器模块—————————————————————————————————————————————————
//外部中断初始化
void Int0_Init(){
IT0=1; //下降沿有效
IE0=0; //中断请求标志位
EX0=1; //外部中断0允许
EA=1; //开启总中断
PX0=1; //中断优先级高
}
//定时器0初始化
void Timer0_Init(){
TMOD &= 0xF0; //将定时器0清零
TMOD |= 0x01; //设置为定时器0,工作方式16位
TL0=0;
TH0=0;
TF0=0; //定时器请求中断标志
TR0=0; //先不开定时器
}
//设置计数值(定时)
void Timer0_SetCounter(unsigned int Value){
TH0=Value/256;
TL0=Value%256;
}
//获得计数值(定时)
unsigned int Timer0_GetCounter(){
return (TH0<<8)|TL0;
}
//开启或关闭定时器
void Timer0_Run(unsigned char Flag){
TR0=Flag;
}
//红外模块—————————————————————————————————————————————————————————————
unsigned int IR_Time; //获得到时序中对应的时间
unsigned char IR_State; //NEC中时序的状态
unsigned char IR_Data[4]; //获取按键的Data数据容器
unsigned char IR_pData; //IR_Data的下标
unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;
unsigned char Address;
unsigned char Command;
unsigned char Num;
void IR_Init(){
Timer0_Init();
Int0_Init();
}
//获得数据标志
unsigned char IR_GetDataFlag(){
if(IR_DataFlag==1){
IR_DataFlag=0;
return 1;
}
return 0;
}
//获得重发标志
unsigned char IR_GetRepeatFlag(){
if(IR_RepeatFlag==1){
IR_RepeatFlag=0;
return 1;
}
return 0;
}
unsigned char IR_GetAddress(){
return IR_Address;
}
unsigned char IR_GetCommand(){
return IR_Command;
}
//main函数模块—————————————————————————————————————————————————————————
void main(){
initlcd();
IR_Init();
while(1){
//若接收到数据标志或重发标志,那么都可以获得之前的数据
if(IR_GetDataFlag() || IR_GetRepeatFlag()){
Address=IR_GetAddress();
Command=IR_GetCommand();
LCD_ShowHexNum(2,1,Address,2); //将地址码展示在LCD屏幕的2行1列
LCD_ShowHexNum(2,5,Command,2); //将命令码展示在LCD屏幕的2行5列
if(Command==0x15){
//若按键为0x15(音量-键),那么Num数字-1
Num--;
}
if(Command==0x09){
//若按键为0x09(音量+键),那么Num数字+1
Num++;
}
LCD_ShowHexNum(2,10,Num,2); //将Num变量展示在LCD屏幕的2行10列
}
}
}
//外部中断触发函数—————————————————————————————————————————————————————
void Int0_Routine() interrupt 0
{
if(IR_State==0){
//首次下降沿开始计数(时间)
Timer0_SetCounter(0);
Timer0_Run(1);
IR_State=1;
}else if(IR_State==1){
IR_Time=Timer0_GetCounter();
Timer0_SetCounter(0);
//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
if(IR_Time>(12442-500) && IR_Time<(12442+500)){
IR_State=2;
}
//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
else if(IR_Time>(10368-500) && IR_Time<(10368+500)){
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);
//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
if(IR_Time>(1032-500) && IR_Time<(1032+500)){
IR_Data[IR_pData/8] &= ~(0x01<<(IR_pData%8));
IR_pData++;
}
//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
else if(IR_Time>(2074-500) && IR_Time<(2074+500)){
IR_Data[IR_pData/8] |= (0x01<<(IR_pData%8));
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;
}
}
}
作者:小白菜00