红外遥控
# 红外遥控
# 一、硬件知识
# 1、简介
其实我们每天接触的各种遥控器大多都是红外遥控的,而且前面都有一个LED灯类似的,但是一般不发光或者闪烁几下,那就是用来发射红外信号的。 然后下面那个黑黑的LED灯就是用来接受解码的.
- 红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出
- 通信方式:单工,异步
- 红外LED波长:940nm
- 通信协议标准:NEC标准
# 2、硬件电路
接下来我们来介绍一下红外遥控的硬件电路。
首先是左边的发射电路,当IN给高电平时,电路不导通,红外LED不亮,接收头输出高电平。而当IN给低电平时,电路导通红外LED以38KHz频率闪烁发光,接收头输出低电平。中间那个也是类似的,只不过需要自己去模拟38KHZ的发射信号。 然后是接收电路,将数据传入红外接收器,经过滤波以及各种解码操作,他就会通过OUT口输出,我们对输出的信号进行分析就行。 具体如下图所示:
# 二、NEC编码
接下来就是我们的重点,NEC编码。红外NEC编码与我们之前学的东西有点不一样,首先,他有一个起始信号以及重复信号,而且都是通过低电平切换到高电平来实现的,只是两者的持续时间不一样。0、1信号也是不一样的,也是通过低电平切换到高电平来实现的,同样是时间不同,与我们之前接触到的都是不一样的,具体如图所示。
然后数据格式也是不一样的,一共是32位,前8位是地址码,后8位是地址码的反码,再后八位是命令码,跟在后面的8位也是命令码的反码,用来校验数据。
我们来看一下各个键按下之后的情况吧!拿第一个键举例子,首先启动码,然后地址码00000000,反码11111111;命令码10100010(0x45,第一个键的键码),反码01011101 。
# 三、遥控器键码
就是每个键对应的键码,也是其命令码。
# 四、外部中断
- STC89C52有4个外部中断
- STC89C52的外部中断有两种触发方式:
- 下降沿触发
- 低电平触发
# 1、中断号
我们这采用的是下降沿触发中断,目前使用中断0来进行操作。
- 这是中断对应的引脚,中断0是P32。
# 五、寄存器
相比于时钟系统要简单一点,INT0用来选择中断方式,EX0使能中断,EA使能所有中断,PX是选择优先级。具体的配置我们到代码进行展示。
- IT0=1,下降沿中断;IT0=0,低电平中断;
# 六、红外遥控代码
这是解码代码的基本思路,当空闲时,状态为0,之后准备接收信号状态为1,接收数据或者重复;如果是接收数据的开始型号,我们就置状态为2,如果是重复信号的话,继续回到状态0。
- Timer0.c/h
#include <REGX52.H>
/**
* @brief 定时器0初始化
* @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;
}
-------------------Timer0.h-----------------------
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
- Int0.c/h (外部中断寄存器)
#include <REGX52.H>
/**
* @brief 外部中断0初始化
* @param 无
* @retval 无
*/
void Int0_Init(void)
{
IT0=1; //设置为下降沿中断
IE0=0; //中断标识位
EX0=1; //EX0使能中断
EA=1; //EA使能所有中断
PX0=1; //选择优先级,1为高
}
------------------Int0.h---------------------
#ifndef __INT0_H__
#define __INT0_H__
void Int0_Init(void);
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- IR.c/h
#include <REGX52.H>
#include "Timer0.h"
#include "Int0.h"
unsigned int IR_Time; //记录时间
unsigned char IR_State; //判断状态
unsigned char IR_Data[4]; //记录DATA,NEC编码的data有32位
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)
{
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) //状态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
//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
if(IR_Time>12442-500 && IR_Time<12442+500)
{
IR_State=2; //置状态为2
}
//如果计时为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); //定时器停止
IR_State=0; //置状态为0
}
else //接收出错
{
IR_State=1; //置状态为1
}
}
else if(IR_State==2) //状态2,接收数据
{
IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
Timer0_SetCounter(0); //定时计数器清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)); //数据对应位清0
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)); //数据对应位置1
IR_pData++; //数据位置指针自增
}
else //接收出错
{
IR_pData=0; //数据位置指针清0
IR_State=1; //置状态为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
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#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(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAddress(void);
unsigned char IR_GetCommand(void);
#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- main.c
#include <REGX52.H>
//#include "Delay.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();
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) //如果遥控器VOL-按键按下
{
Num--; //Num自减
}
if(Command==IR_VOL_ADD) //如果遥控器VOL+按键按下
{
Num++; //Num自增
}
LCD_ShowNum(2,12,Num,3); //显示Num
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
编辑 (opens new window)
上次更新: 2023/09/13, 12:29:52