1.什么是红外通信
无线,非接触式数据传输技术,将信息加入红外光波中,利用发射装置发射,接收设备收到红外光后,将信息解码并做出相应操作。
2.NEC
将信息附加到红外光上涉及到红外编码协议
常见的有 Philips,RC-5Protocol,Sharp,NEC
NEC 协议中如何表示 0 或 1 呢
0: 560us 的连续载波 +560us 的低电平,总时长为 1.125ms
1: 560us 的连续载波 +1680us 的高电平,总时常为 2.25ms
传输一贞 NEC 格式的数据包括以下内容:
- 同步码头: 9ms 载波 +4.5ms 高电平
- 用户码: 用户自定义的 8bit 数据
- 用户反码: 8bit 用户码按位取反
- 数据码: 8bit 数据,也称为 8 位按键数据码
- 数据反码: 8bit 数据码按位取反
3.红外接收电路图
OUT 在 P33 上
4.红外解码
红外解码步骤
-
- 初始化外部中断 1 下降沿触发
-
- 外部中断 1 判断引导码的正确性,判断方法为通过定时器的计数器计算高低电平持续时间,并与引导码做对比
-
- 超出了规定时间范围,不是一贞标准的红外数据,直接跳出中断子程序
-
- 验证符合要求后,判断和接收 4 个字节的数据,将接收的数据放到指定的存储变量中,并置位红外标志位
-
- 在主程序中查询标志位是否收到了红外数据,将变量取出,进行相关联的操作
4.1 回顾 中断和定时器
4.1.1 TMOD 定时器
https://rymcu.com/article/173
4.1.2 中断
https://rymcu.com/article/177
4.1.3 程序
好了,直接写程序
首先目录结构
1602 从之前的复制过来就好
infrared.h
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-04 22:56:46
* @LastEditTime: 2021-04-05 14:57:29
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/ir/include/infrared.h
* @LICENSE: NONE
*/
#ifndef __IR_H__
#define __IR_H__
// 初始化TR0,外部中断1
extern void InitIR();
// 按键标志位
extern unsigned char Flag_IR;
// 4个自己的数据 [31:0] 数据反码,数据码,用户反码,用户码
extern unsigned long Data_IR;
// 计算时长
extern unsigned int T_Count(unsigned int flag);
// 等待信号输入
extern void WaitRed(void);
#define HIGH_IR 1
#define LOW_IR 0
#define Min_9ms 8000
#define Max_9ms 10000
#define Min_4_5ms 3500
#define Max_4_5ms 5000
#define Min_560us 300
#define Max_560us 700
#define Min_1680us 1300
#define Max_1680us 1800
#define Time_16ms 16000
#endif
infrared.c
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-04 23:09:09
* @LastEditTime: 2021-04-05 14:56:56
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/ir/include/infrared.c
* @LICENSE: NONE
*/
#include "../../../include/STC89xx.h"
#include "./infrared.h"
// LED
SBIT(LED1, _P1, 1);
// 红外接收引脚
SBIT(IR_out, _P3, 3);
// 红外接收标志位
unsigned char Flag_IR = 0;
// 数据位
unsigned long Data_IR;
// 初始化TR0,外部中断1
void InitIR()
{
// -------IR
// IR初始化
IR_out = 1;
// -------T0
// 由于是头文件,所以不知道TMOD里面是什么情况,但是后面的M1,M0不变,所以初始化先清除后几位
TMOD &= 0xF0;
// timer0 初始化
// M1=0,M0=1模式,TH0,TL0合并位16位定时器
// 这里用 |= 也是为了保证前面的不变
TMOD |= 0x01;
// 结束中断
TR0 = 0;
// 关闭定时器0中断
ET0 = 0;
// 装载计数器目标值
TL0 = 12;
TH0 = 34;
// ------INT1
// 下降沿触发
IT1 = 1;
// IT1中断开
EX1 = 1;
// 总中断开
EA = 1;
}
unsigned int T_Count(unsigned int flag)
{
// 清除TH0,TL0
TH0 = 0;
TL0 = 0;
// 开定时器0中断
TR0 = 1;
// 判断IR引脚是否等同于flag
// 如果等同于flag
while (IR_out == flag)
{
// 比较计时器高8位和16ms高8位
// 如果超过了16ms跳出循环
if (TH0 > (Time_16ms >> 8))
{
break;
}
}
// 关闭定时器0
TR0 = 0;
// 返回结果
// TH0 高8位 * 256 + TL0 低八位
return (TH0 * 256 + TL0);
}
void WaitRed()
{
unsigned int i;
unsigned int T_Low;
unsigned int T_High;
// 用户码 头 9ms载波 + 4.5ms 高电平
// 9ms引导码的低电平
T_Low = T_Count(LOW_IR);
// 4.5ms引导码的高电平
T_High = T_Count(HIGH_IR);
// 判断引导码是否正确
// 比较1.09us的次数,推算出时常
if (T_Low < Min_9ms || T_Low > Max_9ms || T_High < Min_4_5ms || T_High > Max_4_5ms)
{
// I重置INT1 中断
IE1 = 0;
return;
}
// 接收4个字节数据,共计32位
// 第一个字节 用户码
// 第二个字节 用户反码
// 第三个字节 数据码
// 第四个字节 数据反码
for (i = 0; i < 32; i++)
{
// 560us 低电平
T_Low = T_Count(LOW_IR);
// 560us/1680us高电平
T_High = T_Count(HIGH_IR);
// 判断位电平正确性
if (T_Low < Min_560us || T_Low > Max_560us || T_High < Min_560us || T_High > Max_1680us)
{
// 重置IINT1中断
IE1 = 0;
return;
}
// 高位补0
Data_IR >>= 1;
if (T_High > Min_1680us)
{
// 取数据
Data_IR |= 0x80000000;
}
}
// 接收完毕,置位(存一个值)
Flag_IR = 1;
// 重置INT1中断
IE1 = 0;
}
main.c
/*
* @Author: cuihaonan
* @Email: devcui@outlook.com
* @Date: 2021-04-05 00:49:37
* @LastEditTime: 2021-04-05 15:03:23
* @LastEditors: cuihaonan
* @Description: Basic description
* @FilePath: /sdcc-include/src/ir/main.c
* @LICENSE: NONE
*/
#include "./include/1602.h"
#include "./include/infrared.h"
#include "../../include/STC89xx.h"
#include "../../include/mcs51/lint.h"
unsigned char *Key_Str = 0;
void main()
{
P10 = 1;
unsigned char Key;
Init_1602();
InitIR();
Disp_1602_str(1, 2, "Nebula-Pi IR");
Disp_1602_str(2, 1, "KEY: ");
while (1)
{
// 如果接收到32位被验证过的值了
if (Flag_IR)
{
// 先置0
Flag_IR = 0;
// 首先,低位先存,所以数据码和反码在最前面16位
// 32位 右移16,获取到数据码和数据反码
Key = (unsigned char)(Data_IR >> 16);
switch (Key)
{
case 69:
Key_Str = "CH-";
break;
case 70:
Key_Str = "CH";
break;
case 71:
Key_Str = "CH+";
break;
case 68:
Key_Str = "PREV";
break;
case 64:
Key_Str = "NEXT";
break;
case 67:
Key_Str = "PLAY/PAUSE";
break;
case 7:
Key_Str = "-";
break;
case 21:
Key_Str = "+";
break;
case 9:
Key_Str = "EQ";
break;
case 22:
Key_Str = "0";
break;
case 25:
Key_Str = "100+";
break;
case 13:
Key_Str = "200+";
break;
case 12:
Key_Str = "1";
break;
case 24:
Key_Str = "2";
break;
case 94:
Key_Str = "3";
break;
case 8:
Key_Str = "4";
break;
case 28:
Key_Str = "5";
break;
case 90:
Key_Str = "6";
break;
case 66:
Key_Str = "7";
break;
case 82:
Key_Str = "8";
break;
case 74:
Key_Str = "9";
break;
default:
Key_Str = "error!";
}
Disp_1602_str(2,5," ");
Disp_1602_str(2, 5, Key_Str);
}
}
}
void wait2() __interrupt(2)
{
if (P10 == 0)
{
P10 = 1;
}
else
{
P10 = 0;
}
WaitRed();
}
闪回?