MCU-51:单片机之红外遥控(外部中断)

news2025/1/16 14:53:22

目录

  • 一、红外遥控简介
  • 二、硬件电路
  • 三、基本发送与接收
  • 四、NEC码
  • 五、代码演示
    • 5.1 红外遥控
    • 5.2 红外遥控电机调速
  • 注意:一定要看

一、红外遥控简介

红外发射装置 也就是通常我们说的红外遥控器是由键盘电路、红外编码电路、电源电路和红外发射电路组成。红外发射电路的主要元件为红外发光二极管。 它实际上是一只特殊的发光二极管;由于其内部材料不同于普通发光二极管,因 而在其两端施加一定电压时,它便发出的是红外线而不是可见光。目前大量的使用的红外发光二极管发出的红外线波长为940nm左右,外形与普通发光二极管相同。红外发光二极管有透明的,还有不透明的,在我们的红外遥控器上可以看到这个红外发光二极管。

红外遥控是利用红外光进行通信的设备,由红外LED将调制后的信号发出,由专用的红外接收头进行解调输出
通信方式:单工,异步
红外LED波长:940nm
通信协议标准:NEC标准
在这里插入图片描述

二、硬件电路

在这里插入图片描述
IN引脚是我们要发送波形信息的引脚,38KHZ是调制的信号加载到IN的波形上的。采用两个PNP三极管的串联,使当两个三级管的基极同时为低电平时LED才亮。
亮灭变化:当IN为高电平时,LED不发光,当IN为低电平时,LED按照38KHZ的频率,当为低电平时发光,高电平时不发光。
加上38KHZ的调制波形是为了与自然光区别,当红外光接收器接收信号时会通过滤波电路把38KHZ的波形滤掉,保留信息发送引脚发送的波形信号。
在这里插入图片描述
使用一个IN引脚模拟上面所讲的时序也行

在这里插入图片描述
接收装置:为了保证红外光接收装置接收信息的准确性,不使用IF语句,使用响应更为快送的外部中断处理。

三、基本发送与接收

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

四、NEC码

NEC码的位定义:一个脉冲对应560us的连续载波,一个逻辑1传输需要2.25ms(560us脉冲+1680us低电平),一个逻辑0的传输需要1.125ms(560us脉冲+560us低电平)。而红外接收头在收到脉冲的时候为低电平,在没有脉冲的时候为高电平,这样,我们在接收头端收到的信号为:逻辑1应该是560us低 +1680us高,逻辑0应该是560us低+560us高。所以可以通过计算高电平时间判断接收到的数据是0还是1。
在这里插入图片描述
NEC遥控指令的数据格式为:引导码、地址码、地址反码、控制码、控制反码。引导码由一个9ms的低电平和一个4.5ms的高电平组成,地址码、地址反 码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的顺序发送。采用反码是为了增加传输的可靠性(可用于校验)。
在这里插入图片描述
NEC码还规定了连发码(由9ms低电平+2.5m高电平+0.56ms低电平+97.94ms高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。

五、代码演示

5.1 红外遥控

main.c

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


unsigned char Num;
unsigned char Address;
unsigned char Command;

void main()
{
	
	LCD_Init();
	LCD_ShowString(1,1,"A");
	
	IR_Init();

	while(1)
	{
		if(IR_GetDataFlag() || IR_GetRepeatFlag())
		{
			Address=IR_GetAddress();
			Command=IR_GetCommand();
			
			LCD_ShowHexNum(2,1,Address,2);
			LCD_ShowHexNum(2,5,Command,2);
			
			if(Command==0x15)
			{
				Num--;
			}
			
			if(Command==0x09)
			{
				Num++;
			}
			LCD_ShowNum(2,10,Num,3);
		}
	}
}

Timer0.c

#include <REGX52.H>

/**
  * @brief  定时器0初始化,1毫秒@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer0_Init(void)
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;		//设置定时器模式
	TL0 = 0x18;		//设置定时初值
	TH0 = 0xFC;		//设置定时初值
	TF0 = 0;		//清除TF0标志
	TR0 = 0;		//定时器0不计时
}

void Timer0_SetCounter(unsigned int Value)
{
	TH0=Value/256;
	TL0=Value%256;
}

unsigned int Timer0_GetCounter(void)
{
	return(TH0<<8)|TL0;
}

void Timer0_Run(unsigned char Flag)
{
	TR0=Flag;
}

IR.c

#include "Timer0.h"
#include "Int0.h"

unsigned int IR_Time;
unsigned char IR_State;

unsigned char IR_Data[4];
unsigned char IR_pData;

unsigned char IR_DataFlag;
unsigned char IR_RepeatFlag;
unsigned char IR_Address;
unsigned char IR_Command;

void IR_Init(void)
{
	Timer0_Init();
	Int0_Init();
}

unsigned char IR_GetDataFlag(void)
{
	if(IR_DataFlag)
	{
		IR_DataFlag=0;
		return 1;
	}
	return 0;
}

unsigned char IR_GetRepeatFlag(void)
{
	if(IR_RepeatFlag)
	{
		IR_RepeatFlag=0;
		return 1;
	}
	return 0;
}

unsigned char IR_GetAddress(void)
{
	return IR_GetAddress;
}

unsigned char IR_GetCommand(void)
{
	return IR_GetCommand;
}

void Int0_Routine(void) interrupt 0
{
	if(IR_State==0)
	{
		Timer0_SetCounter(0);
		Timer0_Run(1);
		IR_State=1;
	}
	else if(IR_State==1)
	{
		IR_Time=Timer0_GetCounter();
		Timer0_SetCounter(0);
		if(IR_Time>13500-500&&IR_Time<13500+500)
		{
			IR_State=2;
		}
		else if(IR_Time>11250-500&&IR_Time<11250+500)
		{
			IR_RepeatFlag=1;
			Timer0_Run(0);
			IR_State=0;
		}
		else
		{
			IR_State=1;
		}
	}
	else if(IR_State==2)
	{
		IR_Time=Timer0_GetCounter();
		Timer0_SetCounter(0);
		if(IR_Time>1120-500&&IR_Time<1120+500)
		{
			IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8));
			IR_pData++;
		}
		else if(IR_Time>2250-500&&IR_Time<2250+500)
		{
			IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8));
			IR_pData++;
		}
		else
		{
			IR_pData=0;
			IR_State=1;
		}
		if(IR_pData>=32)
		{
			IR_pData=0;
			if((IR_Data[0]==~IR_Data[1])&&(IR_Data[2]==~IR_Data[3]))
			{
				IR_Address=IR_Data[0];
				IR_Command=IR_Data[2];
				IR_DataFlag=1;
			}
			Timer0_Run(0);
			IR_State=0;
		}
	}	
}

Int0.c

#include <REGX52.H>

void Int0_Init(void)
{
	IT0=1;
	IE0=0;
	EX0=1;
	EA=1;
	PX0=1;
}

//void Int0_Routine(void) interrupt 0
//{
//	
//}

Int0.h

#ifndef __INT0_H__
#define __INT0_H__

void Int0_Init(void);

#endif

Timer0.h

#ifndef __TIMER0_H__
#define __TIMER0_H__

void Timer0_Init(void);
void Timer0_SetCounter(unsigned int Value);
unsigned int Timer0_GetCounter(void);
void Timer0_Run(unsigned char Flag);

#endif

IR.h

#ifndef __IR_H__
#define __IR_H__

#define IR_POWER		0x45
#define IR_MODE			0x46
#define IR_MUTE			0x47
#define IR_START_STOP	0x44
#define IR_PREVIOUS		0x40
#define IR_NEXT			0x43
#define IR_EQ			0x07
#define IR_VOL_MINUS	0x15
#define IR_VOL_ADD		0x09
#define IR_0			0x16
#define IR_RPT			0x19
#define IR_USD			0x0D
#define IR_1			0x0C
#define IR_2			0x18
#define IR_3			0x5E
#define IR_4			0x08
#define IR_5			0x1C
#define IR_6			0x5A
#define IR_7			0x42
#define IR_8			0x52
#define IR_9			0x4A

void IR_Init(void);
unsigned char IR_GetDataFlag(void);
unsigned char IR_GetRepeatFlag(void);
unsigned char IR_GetAdress(void);
unsigned char IR_GetCommand(void);


#endif

5.2 红外遥控电机调速

程序在上一篇的MCU-51:单片机直流电机驱动(PWM)基础上进行修改
main.c

#include "Delay.h"
#include "Key.h"
#include "Nixie.h"
#include "Motor.h"
#include "IR.h"

unsigned char Command,Speed;

void main()
{
	Motor_Init();
	IR_Init();
	while(1)
	{
		if(IR_GetDataFlag())	//如果收到数据帧
		{
			Command=IR_GetCommand();		//获取遥控器命令码
			
			if(Command==IR_0){Speed=0;}		//根据遥控器命令码设置速度
			if(Command==IR_1){Speed=1;}
			if(Command==IR_2){Speed=2;}
			if(Command==IR_3){Speed=3;}
			
			if(Speed==0){Motor_SetSpeed(0);}	//速度输出
			if(Speed==1){Motor_SetSpeed(50);}
			if(Speed==2){Motor_SetSpeed(75);}
			if(Speed==3){Motor_SetSpeed(100);}
		}
		Nixie(1,Speed);						//数码管显示速度
	}
}

Timer1.c

#include <REGX52.H>

/**
  * @brief  定时器1初始化,100us@12.000MHz
  * @param  无
  * @retval 无
  */
void Timer1_Init(void)
{
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;		//设置定时器模式
	TL1 = 0x9C;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	TF1 = 0;		//清除TF1标志
	TR1 = 1;		//定时器1开始计时
	ET1=1;
	EA=1;
	PT1=0;
}

/*定时器中断函数模板
void Timer1_Routine() interrupt 3
{
	static unsigned int T1Count;
	TL1 = 0x9C;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	T1Count++;
	if(T1Count>=1000)
	{
		T1Count=0;
		
	}
}
*/

Motor.c

#include <REGX52.H>
#include "Timer1.h"

//引脚定义
sbit Motor=P1^0;

unsigned char Counter,Compare;

/**
  * @brief  电机初始化
  * @param  无
  * @retval 无
  */
void Motor_Init(void)
{
	Timer1_Init();
}

/**
  * @brief  电机设置速度
  * @param  Speed 要设置的速度,范围0~100
  * @retval 无
  */
void Motor_SetSpeed(unsigned char Speed)
{
	Compare=Speed;
}

//定时器1中断函数
void Timer1_Routine() interrupt 3
{
	TL1 = 0x9C;		//设置定时初值
	TH1 = 0xFF;		//设置定时初值
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

Timer1.h

#ifndef __TIMER1_H__
#define __TIMER1_H__

void Timer1_Init(void);

#endif

Motor.h

#ifndef __MOTOR_H__
#define __MOTOR_H__

void Motor_Init(void);
void Motor_SetSpeed(unsigned char Speed);

#endif

注意:一定要看

千万不要直接复制代码,因为是模块化编写,模块的代码没有往上复制(模块的代码在以往的文章中可以找到),大家一定要自己动手,跟着视频学!!!!!

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

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

相关文章

爬虫逆向之字体反爬(一)、镀金的天空-字体反爬-1

题目地址&#xff1a;http://www.glidedsky.com/level/crawler-font-puzzle-1 写一下之前处理过的几个字体反爬实战&#xff0c;也是很常见的一种反爬类型&#xff0c;这是第一篇 先来看一下题目 源码拿到的数字&#xff0c;和实际显示在网页的数字&#xff0c;明显不一样的 …

相见恨晚的 IDEA 使用技巧,能让你的代码飞起来

Live Templates 是什么&#xff0c;听上去感觉挺玄乎的。有的同学用过之后觉得简直太好用了&#xff0c;不能说大大提高了开发效率吧&#xff0c;至少也是小小的提高一下&#xff0c;节省了很多敲重复代码的时间。有的同学用过之后说&#xff1a;没什么用&#xff0c;奇技淫巧罢…

C语言_程序环境和预处理

目录 1. 程序的翻译环境 2. 程序的执行环境 3. C语言程序的翻译链接 4. 预编译过程详解 4.1 预定义符号介绍 4.1.1 __FILE__ //进行编译的源文件 4.1.2 __LINE__//文件当前的行号 4.1.3 __DATE__//文件被编译的日期 4.1.4 __TIME__//文件被编译的时间 4.1.5 __STDC__…

基于python的学生信息管理系统

《学生信息管理系统》程序使用说明在IDLE中运行《学生信息管理系统》即可进入如图1所示的系统主界面。在该界面中可以选择要使用功能对应的菜单进行不同的操作。在选择功能菜单时&#xff0c;有两种方法&#xff0c;一种是输入1&#xff0c;另一种是按下键盘上的↑或↓方向键进…

【Javascript】高阶函数,JSON,forEach,map,filter,reduce函数

❤️ Author&#xff1a; 老九 ☕️ 个人博客&#xff1a;老九的CSDN博客 &#x1f64f; 个人名言&#xff1a;不可控之事 乐观面对 &#x1f60d; 系列专栏&#xff1a; 文章目录高阶函数箭头函数apply函数JSONfilter函数map函数总结reduce函数find/findIndex函数every/some函…

线缆行业单绞机控制算法(详细图解)

在了解单绞机之前需要大家对收放卷以及排线控制有一定的了解,不清楚的可以参看下面几篇博客,这里不再赘述,受水平和能力所限,文中难免出现错误和不足之处,诚恳的欢迎大家批评和指正。 收放卷行业开环闭环控制算法 PLC张力控制(开环闭环算法分析)_RXXW_Dor的博客-CSDN博…

知识难懂到什么程度

爱因斯坦相对论&#xff0c;诺贝尔评委会都看不懂&#xff0c;到底说的是什么1905年&#xff0c;爱因斯坦发布狭义相对论&#xff1b;1916年&#xff0c;发布广义相对论&#xff1b;2年后&#xff0c;英国的汤姆逊教授通过实验验证了广义相对论的正确&#xff0c;同时也把爱因期…

容器适配器中stack queue priority_queue的介绍及模拟实现

文章目录容器适配器的概念deque的介绍及底层结构stack的介绍 stack的模拟实现 queue的介绍 queue的模拟实现 priority_queue的介绍 priority_queue的模拟实现 容器适配器的概念 适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验…

清华大佬超全超详细讲解——C++STL看这份教程就够了

2022 年年度编程语言揭榜啦&#xff01;在上个月预想的 C、C、Python 三种候选语言中&#xff0c;C 脱颖而出&#xff0c;成为 TIOBE 2022 年度编程语言的最终获得者&#xff01;新的一波学习热潮要来了。TIOBE 编程语言社区排行榜是编程语言流行趋势的一个指标&#xff0c;每月…

数据分析思维(三)|测试/对比思维

测试/对比思维 1、概念 测试/对比思维可以说在数据分析的工作中随处可见。当我们通过各种手段得到一些结果数据后&#xff0c;如何评价结果的好坏呢&#xff1f;这个时候你可能会想到和标准结果进行比较、和之前的数据进行对照等等方法&#xff0c;这些方法归根结底就是一种测…

ubuntu 安装 Gitkraken 8.1.1 Pro 版本

GitKraken 是一个非常好用的 Git 图形界面客户端, 自 6.5.1 版本以后, GitKraken 对私有仓库不再免费开放使用 本文介绍一个 ubuntu 安装 GitKraken 8.1.1 Pro 版本的方法 环境准备 安装 yarn 测试过 node 18.12.1 版本, 没能升级到 Pro 版, 可能是因为 GitCracken 仓库太久…

springboot+sa-token-quick-login实现快速登录

当你的项目需要一个登录认证功能&#xff0c;这个登录界面可以不华丽、可以烂&#xff0c;但是一定要有&#xff0c;同时你又不想花费太多的时间浪费在登录页面上&#xff0c; 那么你便可以尝试一下Sa-Token-Quick-Login。 1、Sa-Token-Quick-Login Sa-Token-Quick-Login 可以…

5.4、TCP 流量控制(滑动窗口机制)

一般来说&#xff0c;我们总是希望数据传输得更快一些。 但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#xff0c;这就会造成数据的丢失。 所谓流量控制(flow control)就是让发送方的发送速率不要太快&#xff0c;要让接收方来得及接收\color{red}让发送…

基于微信小程序的个人健康数据管理系统小程序

文末联系获取源码 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏览器…

第一章.机器学习的前期准备—jupyter 更换文件路径的方法,jupyter使用方法,训练/验证/测试数据集的概念,学习方式,常见应用

第一章.机器学习的前期准备 1.1 第一章.机器学习的前期准备 1.jupyter软件的安装 说明:可以使用Anaconda软件中的jupyter软件 1).jupyter 更换文件路径的方法&#xff1a; ①.查找电脑中是否存在 jupyter_notebook_config.py 文件&#xff0c;若不存在&#xff0c;通过命令提…

前端最基础面试题:说说JavaScript中如何判断数据类型?

1. 基本数据类型的判定&#xff1a;typeof [变量名] typeof 1 // number typeof string呀 // string typeof true // boolean typeof Symbol(abc) // symbol控制台验证&#xff1a; 2. 引用类型 object 的判断&#xff1a; ① constructor ② instanceof ③ Object.prototy…

Redux与前端表格施展“组合拳”,实现大屏展示应用的交互增强

Redux 是 JavaScript 状态容器&#xff0c;提供可预测化的状态管理。它可以用在 react、angular、vue 等项目中, 但与 react 配合使用更加方便一些。 Redux 原理图如下&#xff0c;可以看到store仓库是Redux的核心&#xff0c;通过维护一个store仓库管理 state。state 是只读的…

JS严格模式(use strict)

javascript语法不够严谨&#xff0c;例如我们在项目中不用关键字去定义了一个变量a&#xff0c;控制台一切正常。b123;console.log(b)但是&#xff0c;如果开启了严格模式呢&#xff1f;"use strict" b123; console.log(b)此时将会报错Uncaught ReferenceError: b is…

开关电源中功率电感均方根电流是如何推导的?来自《开关电源宝典》

3.2.8 功率电感的有效电流参考“1.7.3 功率电感”章节内容&#xff0c;我们知道&#xff0c;功率电感具有温升电流、RMS电流、饱和电流、额定电流等电流参数。在后续“第5章 降压电路的应用方法”应用实例中进行功率电感选型时&#xff0c;需要保证所选电感的额定电流参数大于实…

自定义类型:结构体,枚举,联合(1)

tips 1. 2. 结构基础知识复习 1. 结构是一些值的集合&#xff0c;这些值被称为成员变量&#xff0c;结构的每个成员可以是不同类型的变量。 2. 结构体类型&#xff0c;结构体成员&#xff0c;结构体变量&#xff0c;结构体指针的创建方式 3. 初始化结构体变量的时候&…