基于51单片机设计的红外遥控器
一、项目介绍
遥控器是现代生活中必不可少的电子产品之一,目前市面上的遥控器种类繁多,应用范围广泛。而 NEC 红外遥控器协议则是目前应用最为广泛的一种协议之一,几乎所有的电视、空调等家用电器都支持该协议。
本项目是基于 51 单片机设计支持 NEC 协议的红外遥控器,实现接收解码和发送功能。用户通过按下相应按键进行信号的发射,红外发射二极管向外发射红外信号,被控制设备通过红外接收头接收到这个信号,然后解码执行相应的操作。
二、硬件设计
本项目所需的硬件器件主要包括:
(1)5STC89C52单片机
(2)红外发射管
(3)红外接收头
(4)OLED显示屏
(5)按键开关
三、软件设计
本项目的程序代码采用 C 语言编写,主要分为三个部分:初始化部分、接收解码部分和发送数据部分。
(1)初始化部分
初始化函数主要完成各个端口的初始化和定时器的配置,以及红外接收头和红外发射管的引脚的配置。
(2)接收解码部分
接收解码函数主要采用计数器方式对红外遥控器发送的信号进行捕获,并将捕获到的信号转换成 NEC 码。然后根据 NEC 码的规定,解码出用户所输入的指令,最终实现控制设备的功能。
(3)发送数据部分
发送数据函数主要将单片机中存放的指令码进行编码,并通过红外发射管发送给被控制的设备。在该函数中,需要通过计时器的方式来调节发送信号的时间和频率,以保证信号能够正确传输。
四、代码实现
4.1 NEC协议解码代码
下面是基于 51 单片机实现 NEC 协议解码的代码:
#define IRIN P1_0 //红外接收管
unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f};
//遥控器按键对应的命令码
unsigned char read_IR() //读取红外信号
{
int k = 0;
unsigned char data = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
k = 0;
while (!IRIN) //等待高电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
k = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
for (int i = 0; i < 8; i++) //解码 8 个 bit
{
k = 0;
while (!IRIN) //等待高电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
delay_us(650);
if (IRIN) //判断 bit 的值
data |= (1 << i);
k = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
}
return data; //返回解码结果
}
unsigned char decode_IR(unsigned char code_val) //NEC 码转换为命令码
{
for (int i = 0; i < 7; i++)
{
if (Remote6[i] == code_val)
return (i + 1);
}
return 0;
}
void main()
{
unsigned char data = 0;
unsigned char code_val = 0;
while (1)
{
data = read_IR(); //读取红外信号
if (data == 0) //判断是否读取成功
continue;
code_val = decode_IR(data); //将 NEC 码转换为命令码
switch (code_val) //根据命令码控制设备
{
case 1: //控制设备执行命令 1
break;
case 2: //控制设备执行命令 2
break;
case 3: //控制设备执行命令 3
break;
case 4: //控制设备执行命令 4
break;
case 5: //控制设备执行命令 5
break;
case 6: //控制设备执行命令 6
break;
default:
break;
}
}
}
以上代码主要实现了读取红外信号和将 NEC 码转换为命令码的功能,并且可以根据不同的命令码控制设备执行不同的指令。
4.2 NEC协议发送代码
以下是基于 51 单片机实现 NEC 协议发送的代码,可以根据需要修改指令码来控制不同的设备:
#define IRLED P1_1 //红外发射管
unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f};
//遥控器按键对应的命令码
void delay_us(unsigned int us) //延时函数,单位为微秒
{
while (us--)
_nop_();
}
void send_IR(unsigned char data) //发送红外信号
{
unsigned char mask = 0x01; //bit 掩码
for (int i = 0; i < 8; i++) //发送 8 个 bit
{
if (data & mask)
{
IRLED = 0;
delay_us(600);
IRLED = 1;
delay_us(1600);
}
else
{
IRLED = 0;
delay_us(600);
IRLED = 1;
delay_us(600);
}
mask <<= 1; //移位更新掩码
}
IRLED = 0; //发送结束,将红外发射管关闭
}
void send_command(unsigned char code_val) //将命令码转换为 NEC 码并发送
{
unsigned char nec_val = 0;
if (code_val > 6) //判断是否超出范围
return;
nec_val = 0x80 | (code_val << 4) | ((~code_val) & 0x0F); //计算 NEC 码
for (int i = 0; i < 2; i++) //发送两遍,以提高成功率
{
send_IR(nec_val);
delay_ms(50);
}
}
void main()
{
while (1)
{
//向电视发送命令码为 1 的指令
send_command(1);
delay_ms(1000); //延时 1s
}
}
以上代码主要实现了将命令码转换为 NEC 码并发送的功能,可以根据需要修改指令码来控制不同的设备。
红外发射管发射的红外信号有一定的传输范围和传输角度限制,需要根据具体情况调整发射管的位置和方向。
4.3 按键检测代码
以下是支持控制发送不同控制码的代码,可以根据需要修改指令码和按键设置:
#define IRLED P1_1 //红外发射管
#define IRIN P1_0 //红外接收管
unsigned char code Remote6[] = {0x06, 0x09, 0x08, 0x0a, 0x0c, 0x0d, 0x0f};
//遥控器按键对应的命令码
unsigned char read_IR() //读取红外信号
{
int k = 0;
unsigned char data = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
k = 0;
while (!IRIN) //等待高电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
k = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
for (int i = 0; i < 8; i++) //解码 8 个 bit
{
k = 0;
while (!IRIN) //等待高电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
delay_us(650);
if (IRIN) //判断 bit 的值
data |= (1 << i);
k = 0;
while (IRIN) //等待低电平出现
{
k++;
if (k > 1000)
return 0; //超时返回
}
}
return data; //返回解码结果
}
void delay_ms(unsigned int ms) //延时函数,单位为毫秒
{
while (ms--)
for (int i = 0; i < 120; i++)
_nop_();
}
void delay_us(unsigned int us) //延时函数,单位为微秒
{
while (us--)
_nop_();
}
void send_IR(unsigned char data) //发送红外信号
{
unsigned char mask = 0x01; //bit 掩码
for (int i = 0; i < 8; i++) //发送 8 个 bit
{
if (data & mask)
{
IRLED = 0;
delay_us(600);
IRLED = 1;
delay_us(1600);
}
else
{
IRLED = 0;
delay_us(600);
IRLED = 1;
delay_us(600);
}
mask <<= 1; //移位更新掩码
}
IRLED = 0; //发送结束,将红外发射管关闭
}
void send_command(unsigned char code_val) //将命令码转换为 NEC 码并发送
{
unsigned char nec_val = 0;
if (code_val > 6) //判断是否超出范围
return;
nec_val = 0x80 | (code_val << 4) | ((~code_val) & 0x0F); //计算 NEC 码
for (int i = 0; i < 2; i++) //发送两遍,以提高成功率
{
send_IR(nec_val);
delay_ms(50);
}
}
void main()
{
unsigned char data = 0;
unsigned char code_val = 0;
while (1)
{
data = read_IR(); //读取红外信号
if (data > 0) //判断是否有按键按下
{
code_val = Remote6[data - 1]; //根据按键编号获取命令码
send_command(code_val); //将命令码转换为 NEC 码并发送
delay_ms(500); //延时一段时间,防止频繁发送
}
}
}
以上代码主要实现了支持控制发送不同控制码的功能,可以根据需要修改指令码和按键设置。当用户按下遥控器上的按键时,程序会根据按键编号获取到对应的命令码,并将其转换为 NEC 码进行发送。