基于51单片机的红外遥控切换数码管显示系统设计

一、项目背景

1.1 设计背景

随着现代电子技术的快速发展,红外遥控技术因其抗干扰能力强、传输可靠、功耗低等优点,已经广泛应用于各种家用电器和电子设备中。红外遥控系统具有以下特点:

  • 非接触式控制:无需物理连接,操作方便
  • 抗电磁干扰:红外线不易受到电磁信号的干扰
  • 安全性好:红外信号不会穿透墙壁,具有较好的保密性
  • 成本低廉:红外发射和接收器件价格低廉,易于集成

1.2 设计目的

本项目旨在设计并实现一个基于51单片机的红外遥控数码管显示系统,主要目的包括:

  1. 掌握51单片机的基本编程方法和中断处理机制
  2. 理解红外遥控编码协议(NEC协议)的工作原理
  3. 学习数码管的动态扫描显示技术
  4. 培养嵌入式系统的软硬件协同设计能力

1.3 应用场景

该系统可应用于:

  • 教学实验平台,用于学习红外通信和单片机开发
  • 简单的遥控显示设备原型开发
  • 家用电器的遥控接收显示模块
  • 智能家居控制系统的输入显示单元

1.4 红外遥控的原理

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

由于红外线遥控不具有像无线电遥控那样穿过障碍物去控制被控对象的能力,所以,在设计红外线遥控器时,不必要像无线电遥控器那样,每套(发射器和接收器)要有不同的遥控频率或编码(否则,就会隔墙控制或干扰邻居的家用电器),所以同类产品的红外线遥控器,可以有相同的遥控频率或编码,而不会出现遥控信号“串门”的情况。这对于大批量生产以及在家用电器上普及红外线遥控提供了极大的方便。由于红外线为不可见光,因此对环境影响很小,再由红外光波动波长远小于无线电波的波长,所以红外线遥控不会影响其他家用电器,也不会影响临近的无线电设备。

红外遥控通信系统一般由红外发射装置和红外接收设备两大部分组成。


二、方案设计

2.1 系统功能需求

本系统要求实现以下功能:

  1. 红外信号接收:系统能够接收标准NEC协议的红外遥控信号
  2. 按键识别:识别红外遥控器的21个按键(0-20),每行3个按钮,共7行
  3. 数码管显示
    • 对于0-20号按键:显示对应的十进制数字(两位数码管)
    • 对于其他按键:显示按键编码的十六进制值
    • 始终显示"H"标识符,表示十六进制模式

2.2 总体方案

系统采用模块化设计思想,主要分为以下几个模块:

核心控制器:STC89C52单片机(或兼容的51系列)

  • 主频:12MHz晶振
  • 内存:8KB Flash,256B RAM
  • I/O口:充足,满足数码管和红外接收需求

系统工作流程

  1. 红外接收头接收遥控器发出的红外信号
  2. 信号经解调后输入单片机的外部中断0引脚(P3.2)
  3. 单片机解析NEC协议,提取控制码
  4. 根据控制码查表,确定按键编号
  5. 将结果转换为数码管段码
  6. 通过动态扫描方式驱动数码管显示

2.3 技术特点

  • 中断驱动:采用外部中断方式接收红外信号,提高系统响应速度
  • 协议解析:完整实现NEC红外协议的引导码、数据位和校验机制
  • 动态显示:采用数码管动态扫描技术,节省I/O口资源
  • 模块化设计:代码按功能分层,便于维护和扩展

三、电路设计

3.1 系统硬件组成

本系统主要由以下硬件模块组成:

3.1.1 核心控制模块

  • 芯片:STC89C52RC单片机
  • 晶振:12MHz石英晶振
  • 复位电路:上电复位电路(RC复位)
  • 电源:5V直流电源供电

3.1.2 红外接收模块

  • 接收头:一体化红外接收头(如HS0038、VS1838等)
  • 工作频率:38kHz载波频率
  • 接口:数据输出引脚连接到单片机P3.2(INT0)

示意图如下

红外接收头引脚说明

引脚 功能 连接
OUT 数据输出 P3.2(INT0)
GND 系统地
VCC 电源 +5V

3.1.3 数码管显示模块

  • 显示器:8位共阴极数码管
  • 段码控制:P0口输出段码(A-G和DP)
  • 位选控制:使用74HC138译码器进行位选
    • P2.2(LSA)→ 74HC138的A
    • P2.3(LSB)→ 74HC138的B
    • P2.4(LSC)→ 74HC138的C

74HC138译码器真值表

LSC LSB LSA 选中位
0 0 0 第8位
0 0 1 第7位
0 1 0 第6位
0 1 1 第5位
1 0 0 第4位
1 0 1 第3位
1 1 0 第2位
1 1 1 第1位

3.2 电路设计要点

3.2.1 红外接收电路

  • 红外接收头的输出端接单片机外部中断0(P3.2)
  • 下降沿触发中断,接收红外信号的起始位
  • 无需外加上拉电阻(接收头内部已集成)

3.2.2 数码管驱动电路

  • 段码驱动:P0口直接驱动(或通过ULN2003驱动芯片增强驱动能力)
  • 位选驱动:74HC138三线译码器,节省3个I/O口控制8位数码管
  • 限流电阻:每个段需串联限流电阻(约220Ω-470Ω)

3.2.3 电源电路

  • 5V稳压电源供电
  • 建议添加去耦电容(0.1μF和10μF)在电源引脚附近

3.3 引脚分配表

功能 引脚 说明
红外接收 P3.2 外部中断0输入
数码管段码 P0.0-P0.7 段码A-G和DP
位选A P2.2 74HC138的A输入
位选B P2.3 74HC138的B输入
位选C P2.4 74HC138的C输入

3.4 电路示意图

开发板上红外接收模块电路,如下图所示:

从上图中可以看出,该电路是独立的,红外接收头的输出管脚接至J11端子上,为了保证红外接收头输出管脚默认为高电平,需外接一个10K上拉电阻,可在上图并没有看到有上拉电阻,在前面介绍最小系统时已知道,单片机I0口都增加了10K上拉电阻。

由于该模块电路是独立的,所以红外接收头的输出管脚J11端子可以使用任意单片机管脚连接,这里使用单片机的P3.2管脚与红外接收头输出管脚连接。

四、软件设计

4.1 软件架构

系统软件采用分层模块化设计,代码结构清晰,便于维护和扩展。

4.2 程序流程设计

4.2.1 主程序流程

主程序设计要点

  1. 初始化红外接收模块(配置外部中断0)
  2. 进入主循环,持续检测红外数据
  3. 根据接收到的控制码,映射到对应的按键编号(0-20)
  4. 根据按键编号选择显示模式:
    • 0-9:左位空白,右位显示个位
    • 10-20:左位显示十位,右位显示个位
    • 其他:显示十六进制编码
  5. 调用数码管显示函数更新显示内容

4.2.2 红外接收流程

NEC协议解析要点

  1. 引导码识别:9ms低电平 + 4.5ms高电平
  2. 数据位解码
    • 每位以0.56ms低电平开始
    • 数据"0":0.56ms高电平
    • 数据"1":1.68ms高电平
  3. 数据格式:4字节(地址码 + 地址反码 + 控制码 + 控制反码)
  4. 校验机制:控制码与控制反码互为按位取反,用于验证数据正确性

4.2.3 数码管显示流程

动态扫描原理

  • 利用人眼视觉暂留效应(>50Hz刷新率)
  • 依次点亮每一位数码管,快速循环扫描
  • 每位显示时间约1-2ms,总刷新周期约8-16ms

4.3 核心代码分析

4.3.1 按键映射表(main.c)

本系统将红外遥控器的21个按键映射到0-20的编号:

1
2
3
4
5
6
7
8
// 红外遥控器从上到下从左到右21个键对应0-20(行优先,每行3个按钮)
switch(gired_data[2]) {
case 0x45: num = 0; break; // 第1行第1个
case 0x46: num = 1; break; // 第1行第2个
case 0x47: num = 2; break; // 第1行第3个
// ... 以此类推
case 0x4A: num = 20; break; // 第7行第3个
}

4.3.2 显示逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(num>=0 && num<=20) {
// 显示0-20:左位显示十位(0则空白),右位显示个位
if(num<10) {
ired_buf[0]=0x00; // 空白
ired_buf[1]=gsmg_code[num]; // 个位
} else {
ired_buf[0]=gsmg_code[num/10]; // 十位
ired_buf[1]=gsmg_code[num%10]; // 个位
}
} else {
// 十六进制显示(高4位 + 低4位)
ired_buf[0]=gsmg_code[gired_data[2]/16];
ired_buf[1]=gsmg_code[gired_data[2]%16];
}
ired_buf[2]=0X76; // 显示"H"标识

4.4 模块接口设计

4.4.1 public模块(公共函数)

1
2
3
4
5
6
7
8
// 数据类型定义
typedef unsigned int u16;
typedef unsigned char u8;
typedef unsigned long u32;

// 延时函数
void delay_10us(u16 ten_us); // 10微秒级延时
void delay_ms(u16 ms); // 毫秒级延时

4.4.2 ired模块(红外接收)

1
2
3
4
5
// 全局变量
extern u8 gired_data[4]; // 存储接收到的4字节数据

// 函数接口
void ired_init(void); // 红外初始化函数

初始化配置

  • IT0=1:设置外部中断0为下降沿触发
  • EX0=1:使能外部中断0
  • EA=1:开启全局中断

4.4.3 smg模块(数码管显示)

1
2
3
4
5
6
// 全局变量
extern u8 gsmg_code[17]; // 0-F的段码数组

// 函数接口
void smg_display(u8 dat[], u8 pos);
// 参数:dat-段码数组,pos-起始显示位置(1-8)

段码表(共阴极)

1
2
3
0:0x3f  1:0x06  2:0x5b  3:0x4f  4:0x66
5:0x6d 6:0x7d 7:0x07 8:0x7f 9:0x6f
A:0x77 B:0x7c C:0x39 D:0x5e E:0x79 F:0x71

4.5 具体函数实现

4.5.1 红外初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include "ired.h"
u8 gired_data[4];//存储4个字节接收码(地址码+地址反码+控制码+控制反码)
/*******************************************************************************
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断0配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断0允许
EA=1; //打开总中断
IRED=1; //初始化端口
}

因为我们使用外部中断 0 来解码红外遥控数据,所以需初始化配置外部中断 0

4.5.2 红外解码函数

初始化外部中断后,中断就已经开启了,当P32引脚来一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据0和1。具体代码如下:

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
void ired() interrupt 0	//外部中断0服务函数
{
u8 ired_high_time=0;
u16 time_cnt=0;
u8 i=0,j=0;

if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
{
delay_10us(1);//延时约10us
time_cnt--;
if(time_cnt==0)return;
}
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据1或0后面的高电平结束,若超过2ms强制退出
{
delay_10us(10);//约0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
}

进入中断函数,表示以来下降沿,然后判断管脚是否为低电平,如果为低电平则首先判断引导信号,根据前面NEC协议可知,引导信号有9ms的低电平和4.5ms的高电平,因此通过time_cnt赋值1000,然后在while循环内判断,time_cnt每递减一次约10us,1000次则为10ms,在解码时,这个时间要适当放宽一点范围,因为不同传感器性能会有差异,所以此处以10ms的低电平为界限,如果超过10ms则强制退出,防止系统死机。判断完引导信号的低电平,接着判断高电平,实现方法一样。当引导信号判断完成后进入地址码、地址反码、控制码及控制反码共4个字节的数据判断,也就是数据0和1的判断,实现方法也是和前面判断引导信号一样,这里使用到了嵌套循环,外层循环次数是4,表示读取4个字节,内层循环次数是8,表示读取每个字节的8位。注意,红外遥控解码数据是从低位开始,最后是高位。最后将读取的4个字节数据存储在全局变量数组gired_data中,外部可直接使用这四个字节。

4.5.3 主函数

打开main.c文件,代码如下:

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
/**************************************************************************************																	  
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "ired.h"


/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 ired_buf[3];

ired_init();//红外初始化

while(1)
{
// 红外遥控器从上到下从左到右21个键对应0-20(行优先,每行3个按钮)
int num = -1;
switch(gired_data[2])
{
case 0x45: num = 0; break; // 第1行第1个
case 0x46: num = 1; break; // 第1行第2个
case 0x47: num = 2; break; // 第1行第3个
case 0x44: num = 3; break; // 第2行第1个
case 0x40: num = 4; break; // 第2行第2个
case 0x43: num = 5; break; // 第2行第3个
case 0x07: num = 6; break; // 第3行第1个
case 0x15: num = 7; break; // 第3行第2个
case 0x09: num = 8; break; // 第3行第3个
case 0x16: num = 9; break; // 第4行第1个
case 0x19: num = 10; break; // 第4行第2个
case 0x0D: num = 11; break; // 第4行第3个
case 0x0C: num = 12; break; // 第5行第1个
case 0x18: num = 13; break; // 第5行第2个
case 0x5E: num = 14; break; // 第5行第3个
case 0x08: num = 15; break; // 第6行第1个
case 0x1C: num = 16; break; // 第6行第2个
case 0x5A: num = 17; break; // 第6行第3个
case 0x42: num = 18; break; // 第7行第1个
case 0x52: num = 19; break; // 第7行第2个
case 0x4A: num = 20; break; // 第7行第3个
default: num = -1; break;
}
if(num>=0 && num<=20)
{
// 显示0-20:左位显示十位(0则空白),右位显示个位
if(num<10)
{
ired_buf[0]=0x00; // 空白
ired_buf[1]=gsmg_code[num];
}
else
{
ired_buf[0]=gsmg_code[num/10]; // 十位
ired_buf[1]=gsmg_code[num%10]; // 个位
}
}
else
{
// 保持原来的十六进制显示(高 4 位 + 低 4 位)
ired_buf[0]=gsmg_code[gired_data[2]/16];
ired_buf[1]=gsmg_code[gired_data[2]%16];
}
ired_buf[2]=0X76;//显示H的段码
smg_display(ired_buf,6);
}
}

4.5 时序关键点

4.5.1 红外信号时序参数

参数 时间 检测阈值
引导码低电平 9ms 10ms超时
引导码高电平 4.5ms 5ms超时
数据位低电平 0.56ms 6ms超时
数据"0"高电平 0.56ms <0.8ms
数据"1"高电平 1.68ms ≥0.8ms

4.5.2 延时函数精度

基于12MHz晶振(1个机器周期=1μs):

  • delay_10us(1):约10μs
  • delay_ms(1):约1ms

4.6 软件特色

  1. 健壮的超时机制:每个等待阶段都有超时保护,防止程序死循环
  2. 数据校验:通过反码校验确保接收数据的正确性
  3. 位操作高效:使用位移和位或运算,提高数据处理效率
  4. 模块化封装:各功能模块独立,接口清晰,便于移植和复用

五、完整开源代码

main.c文件

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
/**************************************************************************************																	  
***************************************************************************************/
#include "public.h"
#include "smg.h"
#include "ired.h"


/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main()
{
u8 ired_buf[3];

ired_init();//红外初始化

while(1)
{
// 红外遥控器从上到下从左到右21个键对应0-20(行优先,每行3个按钮)
int num = -1;
switch(gired_data[2])
{
case 0x45: num = 0; break; // 第1行第1个
case 0x46: num = 1; break; // 第1行第2个
case 0x47: num = 2; break; // 第1行第3个
case 0x44: num = 3; break; // 第2行第1个
case 0x40: num = 4; break; // 第2行第2个
case 0x43: num = 5; break; // 第2行第3个
case 0x07: num = 6; break; // 第3行第1个
case 0x15: num = 7; break; // 第3行第2个
case 0x09: num = 8; break; // 第3行第3个
case 0x16: num = 9; break; // 第4行第1个
case 0x19: num = 10; break; // 第4行第2个
case 0x0D: num = 11; break; // 第4行第3个
case 0x0C: num = 12; break; // 第5行第1个
case 0x18: num = 13; break; // 第5行第2个
case 0x5E: num = 14; break; // 第5行第3个
case 0x08: num = 15; break; // 第6行第1个
case 0x1C: num = 16; break; // 第6行第2个
case 0x5A: num = 17; break; // 第6行第3个
case 0x42: num = 18; break; // 第7行第1个
case 0x52: num = 19; break; // 第7行第2个
case 0x4A: num = 20; break; // 第7行第3个
default: num = -1; break;
}
if(num>=0 && num<=20)
{
// 显示0-20:左位显示十位(0则空白),右位显示个位
if(num<10)
{
ired_buf[0]=0x00; // 空白
ired_buf[1]=gsmg_code[num];
}
else
{
ired_buf[0]=gsmg_code[num/10]; // 十位
ired_buf[1]=gsmg_code[num%10]; // 个位
}
}
else
{
// 保持原来的十六进制显示(高 4 位 + 低 4 位)
ired_buf[0]=gsmg_code[gired_data[2]/16];
ired_buf[1]=gsmg_code[gired_data[2]%16];
}
ired_buf[2]=0X76;//显示H的段码
smg_display(ired_buf,6);
}
}

smg.c

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
#include "smg.h"

//共阴极数码管显示0~F的段码数据
u8 gsmg_code[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};

/*******************************************************************************
* 函 数 名 : smg_display
* 函数功能 : 动态数码管显示
* 输 入 : dat:要显示的数据
pos:从左开始第几个位置开始显示,范围1-8
* 输 出 : 无
*******************************************************************************/
void smg_display(u8 dat[],u8 pos)
{
u8 i=0;
u8 pos_temp=pos-1;

for(i=pos_temp;i<8;i++)
{
switch(7-i)//位选
{
case 0: LSC=1;LSB=1;LSA=1;break;
case 1: LSC=1;LSB=1;LSA=0;break;
case 2: LSC=1;LSB=0;LSA=1;break;
case 3: LSC=1;LSB=0;LSA=0;break;
case 4: LSC=0;LSB=1;LSA=1;break;
case 5: LSC=0;LSB=1;LSA=0;break;
case 6: LSC=0;LSB=0;LSA=1;break;
case 7: LSC=0;LSB=0;LSA=0;break;
}
SMG_A_DP_PORT=dat[i-pos_temp];//传送段选数据
delay_10us(100);//延时一段时间,等待显示稳定
SMG_A_DP_PORT=0x00;//消音
}
}

smg.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef _smg_H
#define _smg_H

#include "public.h"


#define SMG_A_DP_PORT P0 //使用宏定义数码管段码口

//定义数码管位选信号控制脚
sbit LSA=P2^2;
sbit LSB=P2^3;
sbit LSC=P2^4;

extern u8 gsmg_code[17];

void smg_display(u8 dat[],u8 pos);

#endif

public.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _public_H
#define _public_H

#include "reg52.h"

typedef unsigned int u16; //对系统默认数据类型进行重定义
typedef unsigned char u8;
typedef unsigned long u32;

void delay_10us(u16 ten_us);
void delay_ms(u16 ms);

#endif

reg52.h

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
/*--------------------------------------------------------------------------
REG52.H

Header file for generic 80C52 and 80C32 microcontroller.
Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
All rights reserved.
--------------------------------------------------------------------------*/

#ifndef __REG52_H__
#define __REG52_H__

/* BYTE Registers */
sfr P0 = 0x80;
sfr P1 = 0x90;
sfr P2 = 0xA0;
sfr P3 = 0xB0;
sfr PSW = 0xD0;
sfr ACC = 0xE0;
sfr B = 0xF0;
sfr SP = 0x81;
sfr DPL = 0x82;
sfr DPH = 0x83;
sfr PCON = 0x87;
sfr TCON = 0x88;
sfr TMOD = 0x89;
sfr TL0 = 0x8A;
sfr TL1 = 0x8B;
sfr TH0 = 0x8C;
sfr TH1 = 0x8D;
sfr IE = 0xA8;
sfr IP = 0xB8;
sfr SCON = 0x98;
sfr SBUF = 0x99;

/* 8052 Extensions */
sfr T2CON = 0xC8;
sfr RCAP2L = 0xCA;
sfr RCAP2H = 0xCB;
sfr TL2 = 0xCC;
sfr TH2 = 0xCD;


/* BIT Registers */
/* PSW */
sbit CY = PSW^7;
sbit AC = PSW^6;
sbit F0 = PSW^5;
sbit RS1 = PSW^4;
sbit RS0 = PSW^3;
sbit OV = PSW^2;
sbit P = PSW^0; //8052 only

/* TCON */
sbit TF1 = TCON^7;
sbit TR1 = TCON^6;
sbit TF0 = TCON^5;
sbit TR0 = TCON^4;
sbit IE1 = TCON^3;
sbit IT1 = TCON^2;
sbit IE0 = TCON^1;
sbit IT0 = TCON^0;

/* IE */
sbit EA = IE^7;
sbit ET2 = IE^5; //8052 only
sbit ES = IE^4;
sbit ET1 = IE^3;
sbit EX1 = IE^2;
sbit ET0 = IE^1;
sbit EX0 = IE^0;

/* IP */
sbit PT2 = IP^5;
sbit PS = IP^4;
sbit PT1 = IP^3;
sbit PX1 = IP^2;
sbit PT0 = IP^1;
sbit PX0 = IP^0;

/* P3 */
sbit RD = P3^7;
sbit WR = P3^6;
sbit T1 = P3^5;
sbit T0 = P3^4;
sbit INT1 = P3^3;
sbit INT0 = P3^2;
sbit TXD = P3^1;
sbit RXD = P3^0;

/* SCON */
sbit SM0 = SCON^7;
sbit SM1 = SCON^6;
sbit SM2 = SCON^5;
sbit REN = SCON^4;
sbit TB8 = SCON^3;
sbit RB8 = SCON^2;
sbit TI = SCON^1;
sbit RI = SCON^0;

/* P1 */
sbit T2EX = P1^1; // 8052 only
sbit T2 = P1^0; // 8052 only

/* T2CON */
sbit TF2 = T2CON^7;
sbit EXF2 = T2CON^6;
sbit RCLK = T2CON^5;
sbit TCLK = T2CON^4;
sbit EXEN2 = T2CON^3;
sbit TR2 = T2CON^2;
sbit C_T2 = T2CON^1;
sbit CP_RL2 = T2CON^0;

#endif

ired.c

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
#include "ired.h"

u8 gired_data[4];//存储4个字节接收码(地址码+地址反码+控制码+控制反码)

/*******************************************************************************
* 函 数 名 : ired_init
* 函数功能 : 红外端口初始化函数,外部中断0配置
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void ired_init(void)
{
IT0=1; //下降沿触发
EX0=1; //打开中断0允许
EA=1; //打开总中断
IRED=1; //初始化端口
}

void ired() interrupt 0 //外部中断0服务函数
{
u8 ired_high_time=0;
u16 time_cnt=0;
u8 i=0,j=0;

if(IRED==0)
{
time_cnt=1000;
while((!IRED)&&(time_cnt))//等待引导信号9ms低电平结束,若超过10ms强制退出
{
delay_10us(1);//延时约10us
time_cnt--;
if(time_cnt==0)return;
}
if(IRED)//引导信号9ms低电平已过,进入4.5ms高电平
{
time_cnt=500;
while(IRED&&time_cnt)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
for(i=0;i<4;i++)//循环4次,读取4个字节数据
{
for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
{
time_cnt=600;
while((IRED==0)&&time_cnt)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
{
delay_10us(1);
time_cnt--;
if(time_cnt==0)return;
}
time_cnt=20;
while(IRED)//等待数据1或0后面的高电平结束,若超过2ms强制退出
{
delay_10us(10);//约0.1ms
ired_high_time++;
if(ired_high_time>20)return;
}
gired_data[i]>>=1;//先读取的为低位,然后是高位
if(ired_high_time>=8)//如果高电平时间大于0.8ms,数据则为1,否则为0
gired_data[i]|=0x80;
ired_high_time=0;//重新清零,等待下一次计算时间
}
}
}
if(gired_data[2]!=~gired_data[3])//校验控制码与反码,错误则返回
{
for(i=0;i<4;i++)
gired_data[i]=0;
return;
}
}
}


ired.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#ifndef _ired_H
#define _ired_H

#include "public.h"

//管脚定义
sbit IRED=P3^2;

//声明变量
extern u8 gired_data[4];


//函数声明
void ired_init(void);

#endif