人若无名 便可潜心练剑.|

hazy1k

园龄:7个月粉丝:14关注:0

2024-08-23 09:28阅读: 41评论: 0推荐: 0

第22章 红外遥控实验

第二十二章 红外遥控实验

1. 导入

在前面章节, 我们介绍的通信, 如 SPI、 IIC 等通信均为有线, 这一章我们来介绍一种无线通信技术--红外遥控通信。 我们开发板标配了一个一体化红外接收头和红外遥控器, 我们来学习如何使用 51 单片机解码红外遥控器的信号。 本章使用 51 单片机的外部中断功能来解码红外遥控器的编码信号。

2. 红外遥控介绍

红外遥控是一种无线、 非接触控制技术, 具有抗干扰能力强, 信息传输可靠,功耗低, 成本低, 易实现等显著优点, 被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机系统中。

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

屏幕截图 2024 06 18 091920

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

二进制脉冲码的形式有多种, 其中最为常用的是 NEC Protocol 的 PWM 码(脉冲宽度调制)和 Philips RC-5 Protocol 的 PPM 码(脉冲位置调制码, 脉冲串之间的时间间隔来实现信号调制)。 如果要开发红外接收设备, 一定要知道红外遥控器的编码方式和载波频率, 我们才可以选取一体化红外接收头和制定解码方案。

更多内容可以参考:红外遥控_百度百科 (baidu.com)

单片机学习(十)红外遥控与外部中断 - CodeReaper - 博客园 (cnblogs.com)

3. 硬件设计

本实验使用到硬件资源如下:

  • 动态数码管

  • 红外接收头和遥控器

动态数码管电路在前面章节都介绍过, 这里就不再重复。 红外遥控器和接收头是一体的, 内部结构不用去管它。 下面来看下开发板上红外接收模块电路, 如下图所示:

屏幕截图 2024 06 18 092125

从上图中可以看出, 该电路是独立的, 红外接收头的输出管脚接至 J11 端子上, 为了保证红外接收头输出管脚默认为高电平, 需外接一个 10K 上拉电阻, 可在上图并没有看到有上拉电阻, 在前面介绍最小系统时已知道, 单片机 IO 口都增加了 10K 上拉电阻, 所以此处可省略。

4. 软件设计

本章所要实现的功能是: 数码管上显示红外解码遥控器键值。程序框架如下:

  • 编写数码管显示功能

  • 编写红外解码函数

  • 编写主函数

数码管显示功能我们已经封装好了,下面开始写红外解码相关函数:

#ifndef _ired_H
#define _ired_H

#include "public.h"

//管脚定义
sbit IRED = P3^2;

//声明变量
extern unsigned char gired_data[4];

//函数声明
void ired_init(void);

#endif
#include "ired.h"

unsigned char gired_data[4]; // 存储4个字节接收码(地址码+地址反码+控制码+控制反码)

// 红外端口初始化函数,外部中断0配置 
void ired_init(void)
{
    IT0 = 1; // 下降沿触发
    EX0 = 1; // 打开中断0允许
    EA = 1;  // 打开总中断
    IRED = 1;// 初始化端口
}

void ired() interrupt 0    // 外部中断0服务函数
{
    unsigned char ired_high_time = 0;
    unsigned int time_cnt = 0;
    unsigned char i = 0,j = 0;

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

因为我们使用外部中断 0 来解码红外遥控数据, 所以需初始化配置外部中断0。 初始化配置在外部中断实验章节已详细介绍, 此处不再重复。

初始化外部中断后, 中断就已经开启了, 当 P3.2 引脚来一个下降沿, 就会触发一次中断, 在中断内我们可以计算高电平时间, 通过高电平时间判断是否进入引导码及数据 0 和 1。

进入中断函数, 表示以来下降沿, 然后判断管脚是否为低电平, 如果为低电平则首先判断引导信号, 根据前面 NEC 协议可知, 引导信号有 9ms 的低电平和4.5ms 的高电平, 因此通过 time_cnt 赋值 1000, 然后在 while 循环内判断, time_cnt 每递减一次约 10us, 1000 次则为 10ms, 在解码时, 这个时间要适当放宽一点范围, 因为不同传感器性能会有差异, 所以此处以 10ms 的低电平为界限,如果超过 10ms 则强制退出, 防止系统死机。 判断完引导信号的低电平, 接着判断高电平, 实现方法一样。 当引导信号判断完成后进入地址码、 地址反码、 控制码及控制反码共 4 个字节的数据判断, 也就是数据 0 和 1 的判断, 实现方法也是和前面判断引导信号一样, 这里使用到了嵌套循环, 外层循环次数是 4, 表示读取 4 个字节, 内层循环次数是 8, 表示读取每个字节的 8 位。 注意, 红外遥控解码数据是从低位开始, 最后是高位。

最后将读取的 4 个字节数据存储在全局变量数组 gired_data 中, 外部可直接使用这四个字节。

#include "public.h"
#include "smg.h"
#include "ired.h"

void main()
{    
    unsigned char ired_buf[3];

    ired_init();//红外初始化

    while(1)
    {                
        ired_buf[0]=gsmg_code[gired_data[2]/16]; // 将控制码高4位转换为数码管段码
        ired_buf[1]=gsmg_code[gired_data[2]%16]; // 将控制码低4位转换为数码管段码
        ired_buf[2]=0X76; // 显示H的段码
        smg_display(ired_buf, 6);    
    }        
}

主函数代码很简单, 首先调用使用的外设头文件, 然后初始化红外, 进入while 循环将红外解码数据转换为数码管段码数据, 然后在数码管上显示。

5. 小结

照例,我们来详细分析一下重要代码:

屏幕截图 2024 06 20 091556

屏幕截图 2024 06 20 091622


2024.7.22 第一次修订

2024.8.23 第二次修订,后期不再维护

本文作者:hazy1k

本文链接:https://www.cnblogs.com/hazy1k/p/18375249

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hazy1k  阅读(41)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起