草庐IT

51单片机——红外遥控 C语言入门编程

muuuu_pi 2023-07-15 原文

目录

红外发射装置:

 NEC码:

红外接收设备:

1.红外遥控:数码管上显示红外解码遥控器键值

        红外遥控方法一:外部中断+延时

2.红外遥控:红外遥控控制LCD1602画面上的值

        红外遥控方法二:外部中断+定时器


红外发射装置:

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

        通常红外遥控为了提高抗干扰性能和降低电源消耗,红外遥控器常用载波的方式传送二进制编码,常用的载波频率为38kHz,这是由发射端所使用的455kHz晶振来决定的。在发射端要对晶振进行整数分频,分频系数一般取12,所以455kHz÷12≈37.9kHz≈38kHz。通常的红外遥控器是将遥控信号(二进制脉冲码)调制在38KHz的载波上,经缓冲放大后送至红外发光二极管,转化为红外信号发射出去的。

        二进制脉冲码的形式有多种,其中最为常用的是NEC Protocol 的PWM码 (脉冲宽度调制)和Philips RC-5 Protocol的PPM码(脉冲位置调制码,脉冲串之间的时间间隔来实现信号调制)。我们配套的红外遥控器使用的是NEC协议。

 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高电平组成),如果在一帧数据发送完毕之后,红外遥控器按键仍然没有放开,则发射连发码,可以通过统计连发码的次数来标记按键按下的长短或次数。

红外接收设备:

        红外接收设备是由红外接收电路、红外解码、电源和应用电路组成。红外遥 控接收器的主要作用是将遥控发射器发来的红外光信好转换成电信号,再放大、 限幅、检波、整形,形成遥控指令脉冲,输出至遥控微处理器。成品红外接收头的封装大致有两种:一种采用铁皮屏蔽;一种是塑料封装。均有三只引脚,正对接收头的凸起处看,从左至右,管脚依次是 1:VOUT,2:GND,3:VDD。

        由于红外接收头在没有脉冲的时候为高电平,当收到脉冲的时候为低电平,所以可以通过外部中断的下降沿触发中断,在中断内通过计算高电平时间来判断接收到的数据是0还是1。

1.红外遥控:数码管上显示红外解码遥控器键值

        红外遥控方法一:外部中断+延时

        红外遥控函数

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

sbit VOUT=P3^2;

unsigned char IR_Data[4];//全局变量 红外解码数组 地址码、地址反码、控制码、控制反码

void IR_Init(void)//初始化
{
    Int0_Init();
}

void Int0_Routine() interrupt 0
{
    unsigned char i,j;
    unsigned int IR_Count=0,IR_HighTime=0;
    if(VOUT==0)
    {
        IR_Count=1000;
        while(!VOUT&&IR_Count)//等待引导信号9ms低电平结束,若超过10ms强制退出
        {
            delay10us(1);//1000*10us
            IR_Count--;
            if(IR_Count==0)return;
        }
        if(VOUT)//引导信号9ms低电平已过,进入4.5ms高电平
        {
            IR_Count=500;
            while(VOUT&&IR_Count)//等待引导信号4.5ms高电平结束,若超过5ms强制退出
            {
                delay10us(1);
                IR_Count--;
                if(IR_Count==0)return;
            }
            for(i=0;i<4;i++)//循环4次,读取4个字节数据
            {
                for(j=0;j<8;j++)//循环8次读取每位数据即一个字节
                {
                    IR_Count=600;
                    while(!VOUT&&IR_Count)//等待数据1或0前面的0.56ms结束,若超过6ms强制退出
                    {
                        delay10us(1);
                        IR_Count--;
                        if(IR_Count==0)return;
                    }
                    IR_Count=20;
                    while(VOUT)//等待数据1或0后面的高电平结束,若超过2ms强制退出
                    {
                        delay10us(10);
                        IR_HighTime++;
                        if(IR_HighTime>20)return;//20*100us
                    }
                    IR_Data[i]>>=1;//先读取的为低位,然后是高位
                    if(IR_HighTime>8)//如果高电平时间大于0.8ms,数据则为1,否则为0
                        IR_Data[i]|=0x80;
                    IR_HighTime=0;//重新清零,等待下一次计算时间
                }
            }
        }
    }
    if(IR_Data[2]!=~IR_Data[3])//校验控制码与反码,错误则返回
    {
        for(i=0;i<4;i++)
            IR_Data[i]=0;
        return;	
    }
}

        主函数

#include <REGX52.H>
#include "IR.h"
#include "shumaguan.h"

void main()
{
    IR_Init();
    while(1)
    {
        shumaguan(1,IR_Data[0]/16);
        shumaguan(2,IR_Data[0]%16);
        shumaguan(3,IR_Data[1]/16);
        shumaguan(4,IR_Data[1]%16);
        shumaguan(5,IR_Data[2]/16);
        shumaguan(6,IR_Data[2]%16);
        shumaguan(7,IR_Data[3]/16);
        shumaguan(8,IR_Data[3]%16);
    }
}

2.红外遥控:红外遥控控制LCD1602画面上的值

        红外遥控方法二:外部中断+定时器

        定时器函数

#include <REGX52.H>//定时器函数

void timer0_init(void)		//定时器0初始化 @11.0592MHz
{
    TMOD &= 0xF0;		//设置定时器模式
    TMOD |= 0x01;		//设置定时器模式
    TL0 = 0;		//设置定时初始值
    TH0 = 0;		//设置定时初始值
    TF0 = 0;		//清除TF0标志
    TR0 = 0;		//定时器0不计时
}

void timer0_setcounter(unsigned char value)//设置定时器初值
{
    TH0=value/256;
    TL0=value%256;
}

unsigned int timer0_getcounter(void)//读取定时器的值
{
    return (TH0<<8)|TL0;
}

void timer0_run(unsigned char flag)//设置定时器是否启动 1开始0停止
{
    TR0=flag;
}

        外部中断函数

#include <REGX52.H>

void int0_init ()//外部中断下降沿启动
{
	IT0=1;
	IE0=0;
	EX0=1;
	EA=1;
	PX0=1;
}
/*中断函数模板
void int0_routine (void)  interrupt 0
{
	
}
*/

        红外遥控函数

#include <REGX52.H>
#include "timer0.h"
#include "int0.h"

unsigned int ir_time;//红外读取定时器的时间
unsigned char ir_state;//红外接收阶段的模式

unsigned char ir_data[4];//四组数据
unsigned char ir_pdata;//8位数据为一组

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;//是否收到数据帧,1为收到,0为未收到
    }
    return 0;
}

unsigned char ir_getrepeatflag(void)//红外遥控获取收到连发帧标志位
{
    if(ir_repeatflag)
    {
        ir_repeatflag=0;
        return 1;//是否收到连发帧,1为收到,0为未收到
    }
    return 0;
}

unsigned char ir_getaddress(void)//红外遥控获取收到的地址数据
{
    return ir_address;
}

unsigned char ir_getcommand(void)//红外遥控获取收到的命令数据
{
    return ir_command;
}

void int0_Routine(void) interrupt 0
{
    if(ir_state==0)//当下降沿触发时 模式为0
    {
        timer0_setcounter(0);//定时计数器清0
        timer0_run(1);//定时器启动
        ir_state=1;//将模式置为1
    }
    else if(ir_state==1)//模式1,等待Start信号或Repeat信号
    {
        ir_time=timer0_getcounter();//获取上一次中断到此次中断的时间
        timer0_setcounter(0);//定时计数器清0
        //如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
        if(ir_time>12442-500&&ir_time<12442+500)
        {
            ir_state=2;//将模式置为2
        }
        //如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
        else if(ir_time>10368-500&&ir_time<10368+500)
        {
            ir_repeatflag=1;//置收到连发帧标志位为1
            timer0_run(0);//定时器停止
            ir_state=0;//将模式置为0
        }
        else//接收出错
        {
            ir_state=1;//置模式为1
        }
    }
    else if(ir_state==2)//模式2,接收数据
    {
        ir_time=timer0_getcounter();//获取上一次中断到此次中断的时间
        timer0_setcounter(0);//定时计数器清0
        //如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
        if((ir_time>1032-500) && (ir_time<1032+500))
        {
            ir_data[ir_pdata/8] &= ~(0x01<<(ir_pdata%8));//数据对应位清0
            ir_pdata++;//数据位置指针自增
        }
        //如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
        else if(ir_time>2074-500 && ir_time<2074+500)
        {
            ir_data[ir_pdata/8] |= (0x01<<(ir_pdata%8));//数据对应位置1
            ir_pdata++;//数据位置指针自增
        }
        else//接收出错
        {
            ir_pdata=0;//数据位置指针清0
            ir_state=1;//模式置为1
        }
        if(ir_pdata>=32)//如果接收到了32位数据
        {
            ir_pdata=0;//数据位置指针清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;//置收到连发帧标志位为1				
            }
            timer0_run(0);//定时器停止
            ir_state=0;//置状态为0
        }
    }
}

        主函数

#include <REGX52.H>
#include "LCD1602.h"
#include "ir.h"

unsigned char num;
unsigned char address,command;

void main()
{
    LCD_Init();
    LCD_ShowString(1,1,"ADDR  CMD  NUM");
    LCD_ShowString(2,1," 00   00   000");
    ir_init();
    while(1)
    {
        if(ir_getdataflag()||ir_getrepeatflag())
        {
            address=ir_getaddress();
            command=ir_getcommand();
            LCD_ShowHexNum(2,2,address,2);
            LCD_ShowHexNum(2,7,command,2);
            if(command==0x15)//IR_VOL_MINUS
            {
                num--;
            }
            if(command==0x09)//IR_VOL_ADD
            {
                num++;
            }
            LCD_ShowNum(2,12,num,3);
        }
    }
}

有关51单片机——红外遥控 C语言入门编程的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  3. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

  4. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  5. 网络编程套接字 - 2

    网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识

  6. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. 微信小程序开发入门与实战(Behaviors使用) - 2

    @作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors    1、什么是behaviors    2、behaviors的工作方式    3、创建behavior    4、导入并使用behavior    5、behavior中所有可用的节点    6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors    1、什么是behaviorsbehaviors是小程序中,用于实现

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. ES基础入门 - 2

    ES一、简介1、ElasticStackES技术栈:ElasticSearch:存数据+搜索;QL;Kibana:Web可视化平台,分析。LogStash:日志收集,Log4j:产生日志;log.info(xxx)。。。。使用场景:metrics:指标监控…2、基本概念Index(索引)动词:保存(插入)名词:类似MySQL数据库,给数据Type(类型)已废弃,以前类似MySQL的表现在用索引对数据分类Document(文档)真正要保存的一个JSON数据{name:"tcx"}二、入门实战{"name":"DESKTOP-1TSVGKG","cluster_name":"elasticsear

随机推荐