九齐NY8A051D硬件IR模块的配置和使用以及解码处理

news2024/11/17 15:41:59

写在前面

最近在群里有看到有小伙伴在问九齐的硬件IR模块,突然想起来我好像用过,找了半天才把项目找出来,写篇博客记录一下方便下次使用和寻找。

按道理来说九齐所有内置硬件IR模块的MCU都是可以用类似的方法配置和使用的,举一反三哈。

快速导航

  • 写在前面
    • 发码
      • 寄存器介绍
      • 红外管端代码
    • 解码
      • 解码逻辑
      • 接收端代码

发码

寄存器介绍

在这里插入图片描述
其实就是用到这个IRCR寄存器,通过配置IRCSEL可以选择通过PB1的什么电平来输出IR波。

红外管端代码

代码也很简单,全部贴出来自取吧:

/* =========================================================================
 *  Project:       发送端
 *  File:          main.c
 *  Description:   051D
 *  
 * 
 *  Author:        Pd
 *  Version:       V1.0
 *  Date:          2022/4/20	
 =========================================================================*/
#include <ny8.h>
#include "ny8_constant.h"

#define  u16  unsigned int
#define  u8   unsigned char

void delay_us(int);
void delay_ms(int);

/* =======================================================================*/

#define  LED       PORTBbits.PB5            //红外LED


/* =======================================================================*/

u16 t_200ms=0;
u8  f_send=0;
u8  f_start=0;
u8  t_start=0;
u8  send_data=0x89;
u8  i=0;
u8  j=0;

/* =======================================================================*/
void delay_us(int count)  //@16M  2T   2.5us
{
	for(;count>0;count--);

}

void delay_ms(int count) //@16M  2T
{
	int i;for(;count>0;count--){for(i=0;i<=330;i++);}
}

void sys_init(void)
{
	PCON &= ~(1<<3);  //关闭LVR  需要在IC_CONFIG里面设置寄存器配置
}

/* =======================================================================*/

void io_init(void)
{
	IOSTB|=((1<<4)|(1<<3)|(1<<2)|(1<<0));      //B4输入

    IOSTB&=~(1<<1);     //B1输出
    IOSTB&=~(1<<2);     //B2输出
    IOSTB&=~(1<<5);     //B5输出
	BPHCON=0;           //开启B口所有上拉电阻
	PB1=0;LED=0;
}


void timer1_init(void)
{
    T1CR1=0X02;                                     //当下溢发生,定时器 1 初始值从TMR1 寄存器被重新加载并继续下数
    T1CR2=0X03;                                     //分频

    TMR1 =123;
    INTE  |=(1<<3);                                 //开启T1中断
    T1CR1 |=0x01;                                   //开启T1
}

void ir_init(void)
{
    IRCR=0X05;
}

/* =======================================================================*/

void isr(void) __interrupt(0)
{
    if(INTFbits.T1IF)	//定时器1中断  //500us
    {
        INTFbits.T1IF=0;  
             
        t_200ms++;
        if(t_200ms>=1000)
        {
            t_200ms=0;
            f_send=1;
            f_start=1;
        }

        if(f_send==1)
        {
            if(f_start==1)
            {
                t_start++;
                if(t_start<22)
                {
                    PB1=0;
                }
                else
                {
                    PB1=1;
                }
            }
            if(t_start>=30)
            {
                t_start=0;
                f_start=0;
            }
        }
    }
}

void main(void)
{
    io_init();
	delay_ms(50);
	
    timer1_init();
    ir_init();
	INTF = 0;         //清除所有中断标志
    DISI();     	  //关闭总中断
	ENI();	    	  //开启总中断

    while(1)
    {
        if(f_send==1 && f_start==0)
        {
        	j++;
            if(j<=8)
            {
                i=send_data&0x01;
                if(i==0)
                {
                	PB1=1;
                    delay_us(500);
                    PB1=0;
                    delay_us(500);
                    PB1=1;
                }
                else
                {
                	PB1=1;
                    delay_us(500);
                    PB1=0;
                    delay_us(1500);
                    PB1=1;
                }
                send_data=send_data>>1;
            }
            if(j==9) {j=0;send_data=0x89;f_send=0;}
        }
    }
}

代码是比较简单的,当时写的协议忘记是通用协议还是私有协议了。

无非就是先发头码,然后发数据。

用硬件IR的好处就是不再需要自己通过硬件PWM去配初值了,不过局限性也很明显,这个硬件IR模块也就只支持38K和57K,而且亲测误差是比较大的,38K量出来实际好像到40多K了。如果要求特别精确,还是只能使用硬件PWM去调。

解码

解码逻辑

实在是忘记了当时的协议了,自己读了下代码推测发射端是,数据“1”是由0.5ms的低电平+1.5ms的38K载波,数据“0”是由0.5ms的低电平+0.5ms的38K载波。

看接收端硬件是将接收头的输出脚接了上拉电阻,也就是说接收头的空闲状态,输入到MCU的电平信号是高电平,当有接收到发射端的38K信号之后,输出一个低电平给到MCU。

那么对于接收端MCU的IO来说:
数据“0” = 0.5ms的高电平 + 0.5ms的低电平 (因为没有收到38K载波,IO会被上拉到高电平。收到载波之后会被拉低)
数据“1” = 0.5ms的高电平 + 1.5ms的低电平

两者的区别就是低电平的时间,那么只需要判定低电平的时间就可以判断出发射端发过来的数据是什么。

那么我的处理方式是开启IO中断,在进入中断后判断IO信号,如果IO信号为0,那么开启计数,当下次进中断判断IO如果为高电平,说明载波数据已经结束,这时候就可以判断低电平(载波时间)是多少,就可以判断出发射端是要传输“0”还是“1”了。

接收端代码

/* =========================================================================
 *  Project:       接收端
 *  File:          main.c
 *  Description:   051D
 *  
 * 
 *  Author:        Pd
 *  Version:       V1.0
 *  Date:          2022/4/22	
 =========================================================================*/
#include <ny8.h>
#include "ny8_constant.h"

#define  u16  unsigned int
#define  u8   unsigned char

void delay_us(int);
void delay_ms(int);

/* =======================================================================*/

#define  REC       PORTBbits.PB5            //红外接收
#define  LED       PORTBbits.PB4            //RGB-12V
#define  GLED	   PORTBbits.PB0			//指示灯   收到---0		没收到---1(高电平有效)
/* =======================================================================*/

u8  f_rec=0;
u8  f_head=0;
u8  f_no_rec=0;
u8  f_receive=0;
u8  f_error=0;
u8  f_bit_end=0;
u8  t_error=0;
u16 t_no_rec=0;
u8  t_head1=0;
u8  t_bit_end=0;
u16 t_receive=0;
u8  rec_data=0;
u8  get_data=0;
u8  bit_receive=0;
u8  t_over_time=0;
u16 t_low_time=0;
u8  f_rece_38k=0;
u8  i=0;
u8  j=0;
u8  z=1;

/* =======================================================================*/
void delay_us(int count)  //@16M  2T   2.5us
{
    for(;count>0;count--);

}

void delay_ms(int count) //@16M  2T
{
    int i;for(;count>0;count--){for(i=0;i<=330;i++);}
}

void sys_init(void)
{
    PCON &= ~(1<<3);  //关闭LVR  需要在IC_CONFIG里面设置寄存器配置
}

/* =======================================================================*/

void io_init(void)
{
    IOSTB|=((1<<5)|(1<<3)|(1<<2));      //B4输入

    IOSTB&=~((1<<4)|(1<<0));     //B4输出
    BPHCON=0;           //开启B口所有上拉电阻
    
    REC=1;LED=1;GLED=0;
}


void timer1_init(void)
{
    T1CR1=0X02;                                     //当下溢发生,定时器 1 初始值从TMR1 寄存器被重新加载并继续下数
    T1CR2=0X00;                                     //2分频

    TMR1 =100;
    INTE  |=(1<<3);                                 //开启T1中断
    T1CR1 |=0x01;                                   //开启T1
}


void pb_init(void)
{
    BWUCON|=(1<<5);     //开启PB5电平变化中断
    INTE  |=(1<<1);                                 //开启PB中断
}

/* =======================================================================*/


void isr(void) __interrupt(0)
{
    if(INTFbits.T1IF)	//定时器1中断                //50us
    {
        INTFbits.T1IF=0;

        if(f_rece_38k==1 && REC==0)
        {
            t_low_time++;
        }


        if(t_receive<=25000)						//不停的计时,当超时之后指示灯亮
        {
            t_receive++;							//每次收到一帧正确的数据时候清零超时计数器
        }
        if(t_receive>=10000 && LED==1)
        {
            LED=0;
            GLED=1;									//没受到数据   指示灯亮提醒
            f_head=0;
            t_low_time=0;
            bit_receive=0;
            rec_data=0;
            i=0;j=0;z=0;
        }
    }

    if(INTFbits.PBIF)	    //PB中断
    {
        INTFbits.PBIF=0;

        if(REC==0)
        {
            f_rece_38k=1;
        }
        else
        {
            if(f_head==0)
            {
                if(t_low_time>180 && t_low_time<480)
                {
                    f_head=1;
                    t_low_time=0;
                    bit_receive=0;
                    rec_data=0;
                    i=0;j=0;z=0;
                }
                else
                {
                    f_head=0;
                    t_low_time=0;
                    f_rece_38k=0;
                }
                
                if(t_low_time>1000)
                {
                    t_error++;
                    if(t_error>=10)
                    {
                        f_error=1;
                        LED=0;
                        GLED=1;									//没收到数据   指示灯亮提醒
                    }
                }
            }
            else
            {
            	rec_data=rec_data<<1;
                if(t_low_time>=50 && t_low_time<=200)				
                {
                    rec_data|=0x01;								//收到的数据是1
                    t_low_time=0;
                }
                else if(t_low_time>=20 && t_low_time<50)
                {
                    t_low_time=0;								//收到的数据是0
                }
                else if(t_low_time>200 || t_low_time<20)		//收到的数据电平持续时间不符合规范,推测是出错了
                {
                    f_head=0;
                    t_low_time=0;
                    bit_receive=0;
                    rec_data=0;
                    t_error++;									//错误次数超过10次开始报警
                    if(t_error>=10)
                    {
                        f_error=1;
                        LED=0;
                        GLED=1;									//没收到数据   指示灯亮提醒
                    }
                }
                
                bit_receive++;
                if(bit_receive>=8)
                {
                    f_head=0;
                    bit_receive=0;
                    f_rece_38k=0;
                    t_low_time=0;
                    i=rec_data&0x01;
                    j=rec_data&0x10;
                    j=j>>4;
                    z=rec_data&0x80;
                    z=z>>7;
                    if(i==1 && j==1 && z==1)
                    {
                        f_error=0;
                        t_error=0;
                        t_receive=0;
                        LED=1;
                        GLED=0;									//受到数据   指示灯灭
                    }
                    rec_data=0; 
                }
            }
        }
    }
}

void main(void)
{
    io_init();
    delay_ms(50);
    pb_init();
    timer1_init();
    INTF = 0;         //清除所有中断标志
    DISI();     	  //关闭总中断
    ENI();	    	  //开启总中断
    while(1)
    {
        
    }
}

代码比较垃圾,接受端解码的校验部分不懂当时是怎样设计的,读者自行忽略吧。。

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

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

相关文章

packaged_task创建异步任务

本文将介绍基本的packaged_task的用法&#xff1a; 看完上述代码后一定有疑问&#xff1a; 为什么要有packaged_task&#xff1f; 解答&#xff1a;上文的线程通信代码如下&#xff1a; 功能主要是做了求两数之和&#xff0c;然而一般情况下&#xff0c;我们并不习惯这样编写子…

Hive(15):Table(表)DDL操作

1 Describe table Hive中的DESCRIBE table语句用于显示Hive中表的元数据信息 describe formatted [db_name.]table_name; describe extended [db_name.]table_name; 如果指定了EXTENDED关键字,则它将以Thrift序列化形式显示表的所有元数据。如果指定了FORMATTED关键字,则它…

大数据Doris(五十三):MySQL Dump 导出

文章目录 MySQL dump 导出 一、Dump导出案例 二、注意事项 MySQL Dump 导出 mysqldump是一个常用的 MySQL 数据库备份工具&#xff0c;它可以将 MySQL 数据库中的数据导出为 SQL 格式的文件&#xff0c;从而实现对数据的备份、迁移和恢复等操作。Doris 在0.15 之后的版本已…

Flutter 实现任意控件拖动功能

文章目录 前言一、如何实现&#xff1f;1、使用GestureDetector响应拖动事件2、使用Transform变换控件位置3、计算活动区域 二、完整代码三、使用示例1、基本用法 总结 前言 使用flutter开发是需要控件能拖动&#xff0c;比如画板中的元素&#xff0c;或者工具条&#xff0c;搜…

递归入门-数据结构和算法教程

什么是递归&#xff1f; 函数直接或间接调用自身的过程称为递归&#xff0c;相应的函数称为递归函数。使用递归算法&#xff0c;某些问题可以很容易地解决。这样的问题的示例是河内塔&#xff08;TOH&#xff09;、中序/前序/后序树遍历、图的DFS等。递归函数通过调用自身的副…

网络安全(黑客)自学建议

建议一&#xff1a;黑客七个等级 黑客&#xff0c;对很多人来说充满诱惑力。很多人可以发现这门领域如同任何一门领域&#xff0c;越深入越敬畏&#xff0c;知识如海洋&#xff0c;黑客也存在一些等级&#xff0c;参考知道创宇 CEO ic&#xff08;世界顶级黑客团队 0x557 成员…

基于微信小程序的英语单词记忆系统的设计与实现(论文+源码)_kaic

摘 要 当前时期&#xff0c;国内的经济获得了非常快速的发展&#xff0c;互联网技术在持续的创新和完善&#xff0c;教育教学方面也在不断的进步&#xff0c;教育全面深化改革在发展&#xff0c;并且移动互联网技术在教育领域获得了大量的实践以及应用。语言的全球化慢慢的变…

【算法】01背包和完全背包

文章目录 背包问题概览01背包二维dp数组写法一维dp数组写法 完全背包关于遍历顺序相关题目[416. 分割等和子集](https://leetcode.cn/problems/partition-equal-subset-sum/)[279. 完全平方数](https://leetcode.cn/problems/perfect-squares/)[518. 零钱兑换 II](https://leet…

【webrtc】vs2017 重新构建m98

配置了一台13900k的主机,需要重新配置webrtc 构建环境代码已经gclient sync 同步好了,打算重新构建:vs2017 的win10 sdk最大17763 vs2017 环境 set vs2017_install=S:\Program Files (x86)\Microsoft Visual Studio\2017\Communitywin10 SD

Python_上下文管理器

目录 上下文管理器类 多上下文管理器 contextmanager实现上下文管理器 上下文管理器(context manager)是 Python 编程中的重要概念&#xff0c;用于规定某个对象的使用范围。一旦进入或者离开该使用范围&#xff0c;会有特殊操作被调用 (比如为对象分配或者释放内存)。它的语…

【Kubernetes资源篇】ConfigMap配置管理中心详解

文章目录 一、ConfigMap配置中心理论知识1、ConfigMap配置中心简介2、ConfigMap局限性 二、创建ConfigMap的四种方式1、第一种&#xff1a;通过命令行创建ConfigMap2、第二种&#xff1a;通过指定文件创建ConfigMap3、第三种&#xff1a;通过指定目录创建ConfigMap4、第四种&am…

C++线程池(1)理论基础及简单实现

写过CURD的程序员肯定都熟悉“数据库连接池”这个东西&#xff0c;数据库连接是个昂贵的东西&#xff0c;因为它的创建过程比较耗费资源。所以为了节约资源、提高数据库操作的性能&#xff0c;“数据库连接池”就应运而生了。 其实线程池跟数据库连接池类似&#xff0c;一个线…

《PyTorch深度学习实践》第七讲 处理多维特征的输入

b站刘二大人《PyTorch深度学习实践》课程第七讲处理多维特征的输入笔记与代码&#xff1a;https://www.bilibili.com/video/BV1Y7411d7Ys?p7&vd_sourceb17f113d28933824d753a0915d5e3a90 Diabetes Dataset 每一行是一个记录每一列是一个特征&#xff0c;每个样本有8个特征…

为什么我们家里的IP都是192.168开头的?

为什么我们家里的IP都是192.168开头的&#xff1f; 本文为掘金社区首发签约文章&#xff0c;14天内禁止转载&#xff0c;14天后未获授权禁止转载&#xff0c;侵权必究&#xff01; 是的&#xff0c;还是我小白&#xff0c;什么技术博主&#xff0c;老情感博主了。 来讲个故事。…

网络安全合规-数据安全分类分级

数据安全是指保护数据免受未经授权的访问、使用、泄露、破坏或篡改的措施。数据安全包括物理安全、网络安全、应用程序安全、数据备份和恢复等方面。 数据分级分类是指根据数据的重要性和敏感程度&#xff0c;将数据划分为不同的级别&#xff0c;并根据不同级别的数据制定不同…

enote笔记法之附录1——“语法词”(即“关联词”)(ver0.23)

enote笔记法之附录1——“语法词”&#xff08;即“关联词”&#xff09;&#xff08;ver0.23&#xff09; 最上面的是截屏的完整版&#xff0c;分割线下面的是纯文字版本&#xff1a; 作者姓名&#xff08;本人的真实姓名&#xff09;&#xff1a;胡佳吉 居住地&#xff1…

前言-----

因要参加电赛&#xff0c;接触到STC89C52RC&#xff08;A51&#xff09;单片机 STC89C52RC引脚功能 1电源: ①VCC - 芯片电源&#xff0c;接5V&#xff1b; ②VSS - 接地端&#xff1b; 2.时钟: XTAL1、XTAL2 - 晶体振荡电路反相输入端和输出端。 3.控制线: 控制线共…

Java 17官方编程手册都针对哪些方面做了更新?

Java 17&#xff0c;官方编程手册&#xff0c; 《International Developer》杂志称为“全世界醉著名的编程书籍创作者之一”的Herbert Schildt倾情解读 《Java官方编程手册》从1996年首次出版以来&#xff0c;已经经历了数次改版&#xff0c;每次改版都反映 了Java不断演化的进…

分享解析,2+1链动模式为何能在市场上经久不衰

​小编介绍&#xff1a;10年专注商业模式设计及软件开发&#xff0c;擅长企业生态商业模式&#xff0c;商业零售会员增长裂变模式策划、商业闭环模式设计及方案落地&#xff1b;扶持10余个电商平台做到营收过千万&#xff0c;数百个平台达到百万会员&#xff0c;欢迎咨询。 随…

服务网格:Istio 架构

什么是服务网格 服务网格(Service Mesh)这个术语通常用于描述构成这些应用程序的微服务网络以及应用之间的交互。随着规模和复杂性的增长&#xff0c;服务网格越来越难以理解和管理。 它的需求包括服务发现、负载均衡、故障恢复、指标收集和监控以及通常更加复杂的运维需求&am…