当前位置:网站首页>单片机:NEC 协议红外遥控器
单片机:NEC 协议红外遥控器
2022-06-13 03:38:00 【DC-STDIO】
文章目录
NEC 协议红外遥控器
家电遥控器通信距离往往要求不高,而红外的成本比其它无线设备要低的多,所以家电遥控器应用中红外始终占据着一席之地。遥控器的基带通信协议很多,大概有几十种,常用的就有 ITT 协议、NEC 协议、Sharp 协议、Philips RC-5 协议、Sony SIRC 协议等。用的最多的就是 NEC 协议了,因此我们 KST-51 开发板配套的遥控器直接采用 NEC 协议,我们这节课也以 NEC 协议标准来讲解一下。
NEC 协议的数据格式包括了引导码、用户码、用户码(或者用户码反码)、按键键码和键码反码,最后一个停止位。停止位主要起隔离作用,一般不进行判断,编程时我们也不予理会。其中数据编码总共是4个字节32位,如下图所示。第一个字节是用户码,第二个字节可能也是用户码,或者是用户码的反码,具体由生产商决定,第三个字节就是当前按键的键数据码,而第四个字节是键数据码的反码,可用于对数据的纠错。
这个 NEC 协议,表示数据的方式不像我们之前学过的比如 UART 那样直观,而是每一位数据本身也需要进行编码,编码后再进行载波调制。
- 引导码:9 ms 的载波 +4.5 ms 的空闲。
- 比特值“0”:560 us 的载波 +560 us 的空闲。
- 比特值“1”:560 us 的载波 +1.68 ms 的空闲。
结合上图我们就能看明白了,最前面黑乎乎的一段,是引导码的 9 ms 载波,紧接着是引导码的 4.5 ms 的空闲,而后边的数据码,是众多载波和空闲交叉,它们的长短就由其要传递的具体数据来决定。HS0038B 这个红外一体化接收头,当收到有载波的信号的时候,会输出一个低电平,空闲的时候会输出高电平,我们用逻辑分析仪抓出来一个红外按键通过HS0038B 解码后的图形来了解一下,如下图所示。
从图上可以看出,先是 9 ms 载波加 4.5 ms 空闲的起始码,数据码是低位在前,高位在后,数据码第一个字节是8组 560 us 的载波加 560 us 的空闲,也就是 0x00,第二个字节是8组 560 us的载波加 1.68 ms 的空闲,可以看出来是 0xFF,这两个字节就是用户码和用户码的反码。按键的键码二进制是 0x0C,反码就是 0xF3,最后跟了一个 560 us 载波停止位。对于我们的遥控器来说,不同的按键,就是键码和键码反码的区分,用户码是一样的。这样我们就可以通过单片机的程序,把当前的按键的键码给解析出来。
我们前边学习中断的时候,学到51单片机有外部中断0和外部中断1这两个外部中断。我们的红外接收引脚接到了 P3.3 引脚上,这个引脚的第二功能就是外部中断1。在寄存器TCON 中的 bit3 和 bit2 这两位,是和外部中断1相关的两位。其中 IE1 是外部中断标志位,当外部中断发生后,这一位被自动置1,和定时器中断标志位 TF 相似,进入中断后会自动清零,也可以软件清零。bit2 是设置外部中断类型的,如果 bit2 为0,那么只要 P3.3 为低电平就可以触发中断,如果 bit2 为1,那么 P3.3 从高电平到低电平的下降沿发生才可以触发中断。此外,外部中断1使能位是 EX1。那下面我们就把程序写出来,使用数码管把遥控器的用户码和键码显示出来。
Infrared.c 文件主要是用来检测红外通信的,当发生外部中断后,进入外部中断,通过定时器1定时,首先对引导码判断,而后对数据码的每个位逐位获取高低电平的时间,从而得知每一位是0还是1,最终把数据码解出来。虽然最终实现的功能很简单,但因为编码本身的复杂性,使得红外接收的中断程序在逻辑上显得就比较复杂,那么我们首先提供出中断函数的程序流程图,大家可以对照流程图来理解程序代码
/***************************Infrared.c 文件程序源代码*****************************/
#include <reg52.h>
sbit IR_INPUT = P3^3; //红外接收引脚
bit irflag = 0; //红外接收标志,收到一帧正确数据后置 1
unsigned char ircode[4]; //红外代码接收缓冲区
/* 初始化红外接收功能 */
void InitInfrared(){
IR_INPUT = 1; //确保红外接收引脚被释放
TMOD &= 0x0F; //清零 T1 的控制位
TMOD |= 0x10; //配置 T1 为模式 1
TR1 = 0; //停止 T1 计数
ET1 = 0; //禁止 T1 中断
IT1 = 1; //设置 INT1 为负边沿触发
EX1 = 1; //使能 INT1 中断
}
/* 获取当前高电平的持续时间 */
unsigned int GetHighTime(){
TH1 = 0; //清零 T1 计数初值
TL1 = 0;
TR1 = 1; //启动 T1 计数
while (IR_INPUT){
//红外输入引脚为 1 时循环检测等待,变为 0 时则结束本循环
//当 T1 计数值大于 0x4000,即高电平持续时间超过约 18ms 时,
//强制退出循环,是为了避免信号异常时,程序假死在这里。
if (TH1 >= 0x40){
break;
}
}
TR1 = 0; //停止 T1 计数
return (TH1*256 + TL1); //T1 计数值合成为 16bit 整型数,并返回该数
}
/* 获取当前低电平的持续时间 */
unsigned int GetLowTime(){
TH1 = 0; //清零 T1 计数初值
TL1 = 0;
TR1 = 1; //启动 T1 计数
while (!IR_INPUT){
//红外输入引脚为 0 时循环检测等待,变为 1 时则结束本循环
//当 T1 计数值大于 0x4000,即低电平持续时间超过约 18ms 时,
//强制退出循环,是为了避免信号异常时,程序假死在这里。
if (TH1 >= 0x40){
break;
}
}
TR1 = 0; //停止 T1 计数
return (TH1*256 + TL1); //T1 计数值合成为 16bit 整型数,并返回该数
}
/* INT1 中断服务函数,执行红外接收及解码 */
void EXINT1_ISR() interrupt 2{
unsigned char i, j;
unsigned char byt;
unsigned int time;
//接收并判定引导码的 9ms 低电平
time = GetLowTime();
//时间判定范围为 8.5~9.5ms,
//超过此范围则说明为误码,直接退出
if ((time<7833) || (time>8755)){
IE1 = 0; //退出前清零 INT1 中断标志
return;
}
//接收并判定引导码的 4.5ms 高电平
time = GetHighTime();
//时间判定范围为 4.0~5.0ms,
//超过此范围则说明为误码,直接退出
if ((time<3686) || (time>4608)){
IE1 = 0;
return;
}
//接收并判定后续的 4 字节数据
for (i=0; i<4; i++){
//循环接收 4 个字节
for (j=0; j<8; j++){
//循环接收判定每字节的 8 个 bit
//接收判定每 bit 的 560us 低电平
time = GetLowTime();
//时间判定范围为 340~780us,
//超过此范围则说明为误码,直接退出
if ((time<313) || (time>718)){
IE1 = 0;
return;
}
//接收每 bit 高电平时间,判定该 bit 的值
time = GetHighTime();
//时间判定范围为 340~780us,
//在此范围内说明该 bit 值为 0
if ((time>313) && (time<718)){
byt >>= 1; //因低位在先,所以数据右移,高位为 0
//时间判定范围为 1460~1900us,
//在此范围内说明该 bit 值为 1
}else if ((time>1345) && (time<1751)){
byt >>= 1; //因低位在先,所以数据右移,
byt |= 0x80; //高位置 1
}else{
//不在上述范围内则说明为误码,直接退出
IE1 = 0;
return;
}
}
ircode[i] = byt; //接收完一个字节后保存到缓冲区
}
irflag = 1; //接收完毕后设置标志
IE1 = 0; //退出前清零 INT1 中断标志
}
大家在阅读这个程序时,会发现学长在获取高低电平时间的时候做了超时判断 if(TH1 >= 0x40),这个超时判断主要是为了应对输入信号异常(比如意外的干扰等)情况的,如果不做超时判断,当输入信号异常时,程序就有可能会一直等待一个无法到来的跳变沿,而造成程序假死。
另外补充一点,遥控器的单按按键和持续按住按键发出来的信号是不同的。我们先来对比一下两种按键方式的实测信号波形
红外单次按键时序图
红外持续按键时序图
单次按键的结果16-9和我们之前的图16-8是一样的,这个不需要再解释。而持续按键,首先会发出一个和单次按键一样的波形出来,经过大概 40 ms 后,会产生一个 9 ms 载波加 2.25 ms 空闲,再跟一个停止位的波形,这个叫做重复码,而后只要你还在按住按键,那么每隔约 108 ms 就会产生一个重复码。对于这个重复码我们的程序并没有对它单独解析,而是直接忽略掉了,这并不影响对正常按键数据的接收。如果你日后做程序时需要用到这个重复码,那么只需要再把对重复码的解析添加进来就可以了。
边栏推荐
- Doris data import broker load
- Oracle database management
- [test development] automatic test selenium (I)
- Multi thread implementation of selling tickets and producers and consumers
- 【 développement d'essais 】 sélénium d'essais automatisés (Ⅲ) - - analyse du cadre unitest
- [multithreading] what is multithreading in the end -- the elementary level of multithreading (review for self use)
- 【测试开发】博客系统——Loadrunner性能测试(发布博客功能 基准测试)
- Doris creates OLAP, mysql, and broker tables
- [test development] automated test selenium (III) -- unittest framework analysis
- Doris' table creation and data division
猜你喜欢
The latest collation of the number of years of education per capita in the country and provinces -1989-2020- includes the annual original data, calculation process and result summary
Explain usage, field explanations, and optimization instances of MySQL
不卷了!团队又一位成员离职了。。
[200 opencv routines by youcans] 201 Color space conversion of images
Doris data backup and recovery
To resolve project conflicts, first-class project managers do so
Installing MySQL 8.0.20 under Linux and ubuntu20.04 LTS
Detailed explanation of MySQL storage process
Tencent cloud instant messaging IM
Install cnpm and use cnpm command in vscode terminal
随机推荐
MySQL auto sort function deny_ rank() over()、rank() over()、row_ Num() over() usage and differences
LVS四层负载均衡集群(3)集群功能分类 - HPC
Lambda终结操作count
谈谈激光雷达的波长
Several common ways for Flink to extract eventtime and generate watermark
Triggers & built-in packages
Batch image Download & socket dialogue
在JDBC连接数据库时报错:Connection to 139.9.130.37:15400 refused.
【测试开发】自动化测试selenium篇(一)
Use of Oracle PL-SQL
Lambda终结操作查找与匹配allMatch
Advanced API review
【MySQL】索引与事务
Solve the error in CONDA installation PyMOL
Simulink代码生成: 查表模块及其代码
Fundamentals of robot obstacle avoidance system
Wechat payment configuration
Scala sets (array, list, set, map, tuple, option)
机器人避障系统基础
MapReduce internal execution principle