【51单片机】红外遥控

news2024/11/24 9:13:36

学习使用的开发板:STC89C52RC/LE52RC
编程软件:Keil5
烧录软件:stc-isp

开发板实图:
在这里插入图片描述

文章目录

  • 红外遥控
    • 硬件电路
  • NEC协议编码
  • 编程实例
    • LCD1602显示Data
    • 红外遥控控制扇叶转速

红外遥控

红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出

  • 通信方式:单工、异步
  • 红外LED波长:940nm
  • 通信协议标准:NEC标准

NEC(National Electrical Code),中文翻译为国家电气规范。由美国国家安全委员会(NEC)发布的国家电气安全标准,涉及到电气系统的设计、安装、检查、维护和保养等方面的安全规则,这些规则被广泛应用于各种电气系统的设计、安装、检查、维护和操作。
也包括红外信号与数据的转换

红外发送通常应用在电视、空调遥控器
在这里插入图片描述

红外接收即在对应的设备上。单片机使用接收头进行接收

在这里插入图片描述
在这里插入图片描述

硬件电路

发送电路

在这里插入图片描述

  • Q1、Q2:三极管,低电平导通
  • VCC:电源正极;GND:电源负极
  • 38KHz:固定输出一个38KHz的方波
  • IN:输出的数据

因为自然界有很多红外光,为了避免被其影响,将输出的数据和38KHz方波结合
当 IN 输出低电平时,LED1IR 以 38KHz 频率闪着亮;反之则没有输出

在这里插入图片描述

下图也是发送电路,但省略了 38KHz 方波部分,但这就要求 IN 输入也要能发送一定的方波来抗干扰
在这里插入图片描述

接收电路

接收部分会接收到自然光和发送的红外信号,所以首先就需要一个滤波电路

而对于接收的信号,需要对滤波后的红外信号进行增强,还因为 38KHz 只是用于防干扰的,IN 输入才是真正发送的红外信号,也需要对此进行滤波

在这里插入图片描述

而红外接收头集成了滤波电路,OUT 端口的数据就是真正需要的红外信号

在这里插入图片描述

因为红外信号的时序较短,而且有很多的高低电平,不适合使用按键检测的方法。

因为异步,需要实时检测,可以使用外部中断的方式,将 OUT 端口接在外部中断,这样一旦接收到红外信号,程序就会跳转到红外中断处理,处理红外信号

中断可参看【51单片机】中断 & 定时器

中断0 和 中断1 分别接在 P3^2 和 P3^3 端口

在这里插入图片描述

本单片机将红外接收头的 OUT 端接在了中断0——P3^2l


在红外信号的发送和接收中,规定有三种状态:

  • 空闲状态:红外LED不亮,接收头输出高电平
  • 发送低电平:红外LED以 38KHz 频率闪烁发光,接收头输出低电平
  • 发送高电平:红外LED不亮,接收头输出高电平在这里插入图片描述

NEC协议编码

NEC协议规定有三种信号:

  • Start信号:用于标识即将发送 Data 数据
  • Data信号:实际要传输的数据,分为 Address(用户码) 和 Command 还有对应的反码,用于校验。从低位发起在这里插入图片描述
  • Repeat信号:标识重复发送上一次的 Data 数据

不同信号有不同的时序结构
以下时序结构都是基于12MHz系统频率

  • Start信号:由 9ms低电平+4.5ms高电平 组成在这里插入图片描述

  • Data信号:数据0 由 560us低电平+560us高电平组成;数据1 由 560us低电平+1690us高电平组成
    在这里插入图片描述

  • Repeat信号:由 9ms低电平+2.25ms高电平 组成在这里插入图片描述

在这里插入图片描述

示例:

在这里插入图片描述

该时序由 Start信号 和 Data信号组成

其中 Address:0000 0000;Command:0100 0101
数据由低位发起


在这里插入图片描述

该时序由 Repeat信号组成,其时序的高电平持续时间比 Start信号短一些

红外发送没有数据发出时,红外接收为高电平


遥控器键码

首先先介绍 Data数据

Address (8位):该字段用于标识红外设备的地址。它的作用是确保信号只会被目标设备接收和解析,避免其它设备误接收命令。地址通常是由设备制造商分配的,在同一设备组内,每个设备的地址是唯一的。

举个例子:

  • 假设有两个设备:一个电视机(设备A)和一个空调(设备B)。
  • 电视机的红外遥控器发送的红外信号包含地址 0x01(即设备A的地址),并发送了一个命令。
  • 空调的红外接收器只会响应它自己的地址(例如 0x02),而忽略来自地址 0x01 的信号。
  • 如果设备A(电视机)接收到地址 0x01 的信号,它会执行相应的命令;如果设备B(空调)接收到信号地址是 0x01,它则会忽略该命令,因为它的地址不匹配。

Command (8位):命令数据,表示设备执行的操作指令。

遥控器键码如下:

在这里插入图片描述

编程实例

LCD1602显示Data

首先,使用外部中断0 检测红外信号的到来

中断通道如下:

在这里插入图片描述

  • INT0:外部中断0
  • TCON:配置外部中断。
    • IT0配置中断由下降沿触发/低电平触发,置1代表下降沿触发
    • IE0为中断标志位,当中断来临时,该位由硬件置1,检测到该位为1,若该中断开启,则跳转到相应处理函数
  • IE:中断开关。
    • EX0为外部中断0开关
    • EA为中断总开关
  • IP:中断优先级。
    • PX0:置1为高优先级,置0位低优先级

下降沿触发和低电平触发的区别在于

下降沿触发:输入信号由高电平转为低电平的瞬间触发

在这里插入图片描述

低电平触发:当输入信号处于低电平时触发,不一定要求信号发生变化。只要信号持续处于低电平,触发事件就会发生。

在这里插入图片描述

因为红外信号的时序结构通过高低电平的时间决定

在这里插入图片描述
我们通过下降沿触发,获取两次下降沿间隔时间来判断属于哪个信号

如Start信号的两个下降沿之间间隔13.5ms
在这里插入图片描述

为此,我们还需要使用定时器,来获取间隔时间。定时器参看【51单片机】中断 & 定时器

外部中断0模块

Int0.c

#include <REGX52.H>

/**
  * @brief		外部中断0初始化
  * @parm		无
  * @retval		无
  */
void Int0_Init()
{
	IT0 = 1;//控制中断是下降沿/低电平触发,1为下降沿
	IE0 = 0;//中断标志位置零
	EX0 = 1;//外部中断0开关
	EA = 1; //中断总开关
	PX0 = 1;//优先级,1为高优先级
}

//外部中断0处理函数模版 —— 引脚为P3^2
/*
void Int0_Routine() interrupt 0
{
	
}
*/

定时器模块
提供定时器设初值,获取时间,停止方法
Timer.c

#include <REGX52.h>

/**
  * @brief		计数器0初始化
  * @parm		无
  * @retval		无
  */
void Timer0_Init()
{
	TMOD &= 0xF0; 	//高4位保持不变,低4位清零
	TMOD |= 0x01; 	//高4位保持不变,低4位为0101
	//初始化清零
	TH0 = 0;
	TL0 = 0;
	
	TF0 = 0;		//溢出标志位清零
	TR0 = 0;		//停止计时
}

/**
  * @brief		计时器0设置初值
  * @parm		Value:计时器初值,范围:0 ~ 65535
  * @retval		无
  */
void Timer0_SetValue(unsigned int Value)
{
	TH0 = Value / 256;
	TL0 = Value % 256;
}

/**
  * @brief		计时器0设置初值
  * @parm		无
  * @retval		计时器此时的数值,范围:0 ~ 65535 
  */
unsigned int Timer0_GetValue()
{
	return (TH0 << 8) | TL0;
}

/**
  * @brief		计时器0启动/停止
  * @parm		Flag:1代表启动,0代表停止
  * @retval		无
  */
void Timer0_Run(unsigned char Flag)
{
	TR0 = Flag;
}

红外接收头模块

定义三种状态

在这里插入图片描述

  • 空闲状态:启动定时器,切换至检测状态
  • 检测状态:检测Start信号 和 Repeat信号
    • 若为Start信号,切换至接收状态
    • 若为Repeat信号,置连发标志位,切回空闲状态
  • 接收状态:接收 Address、Command 和 其反码,并进行校验

还需对外提供获取 Address 和 Command 的方法

IR.c

#include <REGX52.H>
#include "Int0.h"
#include "Timer.h"

//State=0,空闲状态
//State=1,接收状态,接收Start信号,Repeat信号
//State=2,接收数据
unsigned char IR_State, DataReady, RepeatFlag;
//红外信号的地址和命令
unsigned char Address, Command;
//存储数据的数组和读取的位数
unsigned char DataArr[4], DataBit;


/**
  * @brief		IR初始化
  * @parm		无
  * @retval		无
  */
void IR_Init()
{
	Timer0_Init();	//计时器初始化
	Int0_Init();	//外部中断0初始化
}
/**
  * @brief		获取红外信号中的地址信息
  * @parm		无
  * @retval		地址信息
  */
unsigned char IR_GetAddress()
{
	return Address;
}
/**
  * @brief		获取红外信号中的命令信息
  * @parm		无
  * @retval		命令字:16进制
  */
unsigned char IR_GetCommand()
{
	return Command;
}
/**
  * @brief		获取红外信号中的命令信息
  * @parm		无
  * @retval		命令字:16进制
  */
unsigned char IR_DataReady()
{
	if(DataReady){
		DataReady = 0;//清零
		return 1;
	}
	return 0;
}
/**
  * @brief		获取红外信号中的命令信息
  * @parm		无
  * @retval		命令字:16进制
  */
unsigned char IR_GetRepeatFlag()
{
	if(RepeatFlag){
		RepeatFlag = 0;//清零
		return 1;
	}
	return 0;
}

//外部中断0处理函数,持续检测信号
void Int0_Routine() interrupt 0
{
	unsigned int Time;
	if(IR_State == 0)//空闲状态
	{
		Timer0_SetValue(0);	//计时器清零
		Timer0_Run(1);		//启动计数器
		IR_State = 1;		//切换置监听开始/重复信号
	}
	else if(IR_State == 1)//接收开始信号&重复信号
	{
		Time = Timer0_GetValue();	//获取两次下降沿之间的间隔
		Timer0_SetValue(0);			//计时器清零
		//如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
		if(Time > 12442 - 500 && Time < 12442 + 500)
			IR_State = 2;
		//如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
		else if(Time > 10368 - 500 && Time < 10368 + 500)
		{
			RepeatFlag = 1;	//连发标志位
			Timer0_Run(0);		//停止计时器
			IR_State = 0;		//空闲状态
		}
		else	//接收错误信号
			IR_State = 1;		//重新检测
	}
	else if(IR_State == 2)
	{
		Time = Timer0_GetValue();	//获取两次下降沿之间的间隔
		Timer0_SetValue(0);			//计时器清零
		//如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
		if(Time > 1032 - 500 && Time < 1032 + 500)
		{
			DataArr[DataBit/8] &= ~(0x01 << (DataBit % 8));//数据先发低位
			DataBit++;
		}
		//如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
		else if(Time > 2074 - 500 & Time < 2074 + 500)
		{
			DataArr[DataBit/8] |= (0x01 << (DataBit % 8));//数据先发低位
			DataBit++;
		}
		else
		{
			//数据错误,重新接受
			IR_State = 1;
			DataBit = 0;
		}
		
		if(DataBit >= 32){//接收数据完成
			if((DataArr[0] == ~DataArr[1]) && (DataArr[2] == ~DataArr[3])){
				Address = DataArr[0], Command = DataArr[2];
				DataReady = 1;
			}
			DataBit = 0;		//数据位清零
			IR_State = 0;		//回到空闲状态
			Timer0_Run(0);		//计时器停止
		}
	}
}	

主程序
检测是否有 Data 数据或 Repeat 连发。IR模块会保存接收到的数据,Repeat 连发直接读取上一次的数据即可

按下遥控上的VOL- 和 VOL+ 可对 Num 进行加减

#include <REGX52.H>
#include "LCD1602.h"
#include "IR.h"
#include "Timer.h"
#include "Delay.h"

unsigned char Add, Com, Num;

void main()
{
	LCD_Init();
	IR_Init();
	LCD_ShowString(1, 1, "Add  Com  Num");
	LCD_ShowString(2, 1, "00   00   000");
    while(1)
    {
		if(IR_DataReady() || IR_GetRepeatFlag())
		{
			Add = IR_GetAddress();
			Com = IR_GetCommand();
			LCD_ShowHexNum(2, 1, Add, 2);
			LCD_ShowHexNum(2, 6, Com, 2);
			
			if(Com == IR_VOL_MINUS)
				Num--;
			else if(Com == IR_VOL_ADD)
				Num++;
			LCD_ShowNum(2, 11, Num, 3);
		}	
    }
}

完整项目链接:接收红外遥控

红外遥控控制扇叶转速

此处暂不讲解,项目链接:红外遥控调速步进电机


以上就是本篇博客的所有内容,感谢你的阅读
如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2246576.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【解决】Unity TMPro字体中文显示错误/不全问题

问题描述&#xff1a;字体变成方块 原因&#xff1a;字体资源所承载的长度有限 1.找一个中文字体放入Assets中 2.选中字体创建为TMPro 字体资源 3.选中创建好的字体资源&#xff08;蓝色的大F&#xff09; 在右边的属性中找到Atlas Width h和 Atlas Heigth,修改的大一点&…

深度学习:GPT-1的MindSpore实践

GPT-1简介 GPT-1&#xff08;Generative Pre-trained Transformer&#xff09;是2018年由Open AI提出的一个结合预训练和微调的用于解决文本理解和文本生成任务的模型。它的基础是Transformer架构&#xff0c;具有如下创新点&#xff1a; NLP领域的迁移学习&#xff1a;通过最…

CKA认证 | Day2 K8s内部监控与日志

第三章 Kubernetes监控与日志 1、查看集群资源状态 在 Kubernetes 集群中&#xff0c;查看集群资源状态和组件状态是非常重要的操作。以下是一些常用的命令和解释&#xff0c;帮助你更好地管理和监控 Kubernetes 集群。 1.1 查看master组件状态 Kubernetes 的 Master 组件包…

概念解读|K8s/容器云/裸金属/云原生...这些都有什么区别?

随着容器技术的日渐成熟&#xff0c;不少企业用户都对应用系统开展了容器化改造。而在容器基础架构层面&#xff0c;很多运维人员都更熟悉虚拟化环境&#xff0c;对“容器圈”的各种概念容易混淆&#xff1a;容器就是 Kubernetes 吗&#xff1f;容器云又是什么&#xff1f;容器…

JDBC编程---Java

目录 一、数据库编程的前置 二、Java的数据库编程----JDBC 1.概念 2.JDBC编程的优点 三.导入MySQL驱动包 四、JDBC编程的实战 1.创造数据源&#xff0c;并设置数据库所在的位置&#xff0c;三条固定写法 2.建立和数据库服务器之间的连接&#xff0c;连接好了后&#xff…

移动充储机器人“小奥”的多场景应用(上)

在当前现代化城市交通体系中&#xff0c;移动充储机器人“小奥”发挥着至关重要的作用。该机器人不仅是一个简单的设备&#xff0c;而是一个集成了高科技的移动充电站&#xff0c;为新能源汽车提供了一种前所未有的便捷充电解决方案。该机器人配备了先进的电池管理系统&#xf…

element dialog会隐藏body scroll 导致tab抖动 解决方案如下

element dialog会隐藏body scroll 导致tab抖动 解决方案如下 在dialog标签添加 :lockScroll"false"搞定

Android 功耗分析(底层篇)

最近在网上发现关于功耗分析系列的文章很少&#xff0c;介绍详细的更少&#xff0c;于是便想记录总结一下功耗分析的相关知识&#xff0c;有不对的地方希望大家多指出&#xff0c;互相学习。本系列分为底层篇和上层篇。 大概从基础知识&#xff0c;测试手法&#xff0c;以及案例…

Bugku CTF_Web——my-first-sqli

Bugku CTF_Web——my-first-sqli 进入靶场 随便输一个看看 点login没有任何回显 方法一&#xff1a; 上bp抓包 放到repeter测试 试试万能密码&#xff08;靶机过期了重新开了个靶机&#xff09; admin or 11--shellmates{SQLi_goeS_BrrRrRR}方法二&#xff1a; 拿包直接梭…

BUUCTF—Reverse—easyre(1)

非常简单的逆向 拿到exe文件先查下信息&#xff0c;是一个64位程序&#xff0c;没有加壳&#xff08;壳是对代码的加密&#xff0c;起混淆保护的作用&#xff0c;一般用来阻止逆向&#xff09;。 然后拖进IDA(64位)进行反汇编 打开以后就可以看到flag flag{this_Is_a_EaSyRe}

全面击破工程级复杂缓存难题

目录 一、走进业务中的缓存 &#xff08;一&#xff09;本地缓存 &#xff08;二&#xff09;分布式缓存 二、缓存更新模式分析 &#xff08;一&#xff09;Cache Aside Pattern&#xff08;旁路缓存模式&#xff09; 读操作流程 写操作流程 流程问题思考 问题1&#…

React基础知识一

写的东西太多了&#xff0c;照成csdn文档编辑器都开始卡顿了&#xff0c;所以分篇写。 1.安装React 需要安装下面三个包。 react:react核心包 react-dom:渲染需要用到的核心包 babel:将jsx语法转换成React代码的工具。&#xff08;没使用jsx可以不装&#xff09;1.1 在html中…

Vue3中使用:deep修改element-plus的样式无效怎么办?

前言&#xff1a;当我们用 vue3 :deep() 处理 elementui 中 el-dialog_body和el-dislog__header 的时候样式一直无法生效&#xff0c;遇到这种情况怎么办&#xff1f; 解决办法&#xff1a; 1.直接在 dialog 上面增加class 我试过&#xff0c;也不起作用&#xff0c;最后用这种…

鸿蒙进阶-状态管理

大家好啊&#xff0c;这里是鸿蒙开天组&#xff0c;今天我们来学习状态管理。 开始组件化开发之后&#xff0c;如何管理组件的状态会变得尤为重要&#xff0c;咱们接下来系统的学习一下这部分的内容 状态管理机制 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&a…

leetcode:129. 求根节点到叶节点数字之和

给你一个二叉树的根节点 root &#xff0c;树中每个节点都存放有一个 0 到 9 之间的数字。 每条从根节点到叶节点的路径都代表一个数字&#xff1a; 例如&#xff0c;从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。 计算从根节点到叶节点生成的 所有数字之和 。…

(南京观海微电子)——GH7006+BOE2.6_GV026WVQ-N81-1QP0_800RGB480_MIPI_LVDS_RGB原理介绍

1. 原理介绍 2. 代码 // Model - GV026WVQ-1QP0 // IC - GH7006 // Width - 800 // Height - 480 // REV: - V01 // DATA - 20240507 // INTERFACE- MIPI //"Vfp" value"16" /> //"…

速度革命:esbuild如何改变前端构建游戏 (1)

什么是 esbuild&#xff1f; esbuild 是一款基于 Go 语言开发的 JavaScript 构建打包工具&#xff0c;以其卓越的性能著称。相比传统的构建工具&#xff08;如 Webpack&#xff09;&#xff0c;esbuild 在打包速度上有着显著的优势&#xff0c;能够将打包速度提升 10 到 100 倍…

Excel的图表使用和导出准备

目的 导出Excel图表是很多软件要求的功能之一&#xff0c;那如何导出Excel图表呢&#xff1f;或者说如何使用Excel图表。 一种方法是软件生成图片&#xff0c;然后把图片写到Excel上&#xff0c;这种方式&#xff0c;因为格式种种原因&#xff0c;导出的图片不漂亮&#xff0c…

自动化运维-Linux通用性日志切割脚本

一、公司提供的参考脚本&#xff1a; #!/bin/bash # 定义需要清理的文件 log_file("/mpjava/ly.mp.dfpv.acc.biz/bin/nohup.out""/mpjava/ly.mp.dfpv.acc.service/bin/nohup.out"# 添加更多微服务的日志目录路径 ) # 获取当天日期 date_now$(date %Y%m%d)…

Let‘s Encrypt SSL证书:acmessl.cn申请免费3个月证书

目录 一、CA机构 二、Lets Encrypt特点 三、申请SSL 一、CA机构 ‌Lets Encrypt‌是一个由非营利组织Internet Security Research Group (ISRG)运营的证书颁发机构&#xff08;CA&#xff09;&#xff0c;旨在通过自动化和开放的方式为全球网站提供免费、可靠的SSL/TLS证书。…