单片机外部中断+定时器实现红外遥控NEC协议解码

news2024/12/1 0:38:10

单片机外部中断+定时器实现红外遥控NEC协议解码

  • 概述
  • 解码过程
  • 参考代码

概述

  • 红外(Infrared,IR)遥控,是一种通过调制红外光实现的无线遥控器,常用于家电设备:电视机、机顶盒等等。
  • NEC协议采用PPM(Pulse Position Modulation,脉冲位置调制)的形式进行编码,数据的每一位(Bit)脉冲长度为560us,由38KHz的载波脉冲 (carrier burst) 进行调制。

解码过程

  • 单片机定时器100us定时
  • 单片机外部中断设为下降沿触发
  • IR接收头输出脚作为单片机外部中断信号输入
  • 每操作一次遥控器按键会收到33个中断信号,通过判断定时器计数值范围解析遥控码
    在这里插入图片描述

参考代码

ir_nec.h

#ifndef __IR_NEC_H__
#define __IR_NEC_H__

#include "app.h"

#define IR_USER_CODE_A       (0x00)    // 遥控用户码
#define IR_USER_CODE_B       (0xFF)    // 遥控用户码反码

#define IR_KEYCODE_PWR          (0x45)
#define IR_KEYCODE_MENU         (0x47)
#define IR_KEYCODE_TEST         (0x44)
#define IR_KEYCODE_BACK         (0x43)
#define IR_KEYCODE_UP           (0x40)
#define IR_KEYCODE_DOWN         (0x19)
#define IR_KEYCODE_LEFT         (0x07)
#define IR_KEYCODE_RIGHT        (0x09)
#define IR_KEYCODE_ENTER        (0x15)
#define IR_KEYCODE_CANCEL       (0x0D)
#define IR_KEYCODE_0            (0x16)
#define IR_KEYCODE_1            (0x0C)
#define IR_KEYCODE_2            (0x18)
#define IR_KEYCODE_3            (0x5E)
#define IR_KEYCODE_4            (0x08)
#define IR_KEYCODE_5            (0x1C)
#define IR_KEYCODE_6            (0x5A)
#define IR_KEYCODE_7            (0x42)
#define IR_KEYCODE_8            (0x52)
#define IR_KEYCODE_9            (0x4A)

enum {
    E_IR_KEYCODE_NONE=0,    // 无效遥控码值
    E_IR_KEYCODE_VALUE,     // 有效遥控码值
    E_IR_KEYCODE_REPEAT,    // 遥控重复码值
    E_IR_KEYCODE_MAX
};

struct ir_keycode_t {
    uint8_t isvalid:4;  // 是否有效    
    uint8_t repeat:4;   // 重复码
    uint8_t keycode;    // 按键值
};

void ir_nec_driver_init(void);

struct ir_keycode_t ir_nec_driver_keycode(void);

#endif

ir_nec.c



#include "rjm8l151s_crg.h"
#include "rjm8l151s_gpio.h"
#include "rjm8l151s_vic.h"
#include "rjm8l151s_delay.h"
#include "rjm8l151s_timer01.h"

#include "app_config.h"
#include "ir_nec.h"
#include "debug.h"
#include "gpio.h"
#include "app.h"

/*
T=0.0000015     # 定时器计数周期  16M晶振时间 12/(16000000/2分频)=0.0000015
_t = 0.00001    # 目标定时周期
X=_t/T          # TICK值
print("T:", T)
print("X:", X)
*/


#define TIMER1_TICK         133  //100us
#define TIMERMODE_VALUE     TIMERMODE2  //8位重载模式,最大计数256

// #define TIMER1_TICK 1333  //1ms
// #define TIMERMODE_VALUE     TIMERMODE0  //13位定时模式,最大计数8192

// #define TIMER1_TICK 13330  //10ms
// #define TIMERMODE_VALUE     TIMERMODE1  //16位定时模式,最大计数65536

enum {
    E_IR_STATUS_IDLE=0,     // 等待解码
    E_IR_STATUS_START,      // 开始解码 
    E_IR_STATUS_DONE,       // 解码完成
    E_IR_STATUS_UNKNOW
};

struct ir_nec_t {
    uint8_t status;         // 开始接收
    uint8_t bit_index;      // 位索引
    uint8_t flg_repeat;     // 重复码标记
    uint8_t keycode[4];     // 4字节遥控码:用户码+用户码反码+键值码+键值反码
    uint16_t timer_cnt;     // 定时器记数值
    uint16_t tmrcnt[33];    // 接收到的时间数据
};

static struct ir_nec_t ir_nec = {0};

void timer1_init(void)
{
	TIMER01_InitTypeDef  TIMER01_InitStructure;

	RCC_Sccm1_ClockCmd(RCC_SCCM1_TIMER1,ENABLE); //定时器1 时钟使能

	TIMER01_InitStructure.Timer01Mode   =TIMERMODE_VALUE; //16位定时模式,最大计数65536
	TIMER01_InitStructure.Timer01Period =TIMER1_TICK; 
	TIMER01_InitConfig(TIMER1,&TIMER01_InitStructure);

	TIMER01_Cmd(TIMER1,ENABLE);

	/*中断配置*/
	TIMER01_IntCmd(TIMER1,ENABLE);
}

void ir_nec_driver_init(void)
{
    timer1_init();

    RCC_Sccm1_ClockCmd(RCC_SCCM1_GPIO,ENABLE);

    // TOODO: IR红外接收接口
	/*只有P0和P1口可以配置为电平触发,其他端口只能配置为沿触发*/
	IO_FUN_Config( 	GPIO_P4,GPIO_Pin_1,GPIO_FUNCTION_DF0); //配置引脚为GPIO功能
	IO_INPUT_Enable(GPIO_P4,GPIO_Pin_1);            //配置引脚为GPIO输入模式
	IO_INT_Config(  GPIO_P4,GPIO_Pin_1, falling);	//需要外接接下拉电阻	
	IO_INT_Enable(  GPIO_P4,GPIO_Pin_1);
	IRQ_Enable(IT_GPIO4);

    ir_nec.status = E_IR_STATUS_IDLE;
    ir_nec.bit_index = 0;
    ir_nec.flg_repeat = 0;
    ir_nec.timer_cnt = 0;
}


uint8_t ir_nec_driver_decoder(void)
{
    uint8_t i,j=0;
    uint8_t id=0;
    uint8_t _bit=0;
    if (E_IR_STATUS_DONE == ir_nec.status) {
        // log_d("IR bit_index:%d\n", (int)ir_nec.bit_index);
        ir_nec.keycode[0]=0x00;
        ir_nec.keycode[1]=0x00;
        ir_nec.keycode[2]=0x00;
        ir_nec.keycode[3]=0x00;

        #if 0 // for debug
        for (i=0; i<33; i++) {
            log_d("IR [%d]:%d\n", (int)i, (int)ir_nec.tmrcnt[i]);
        }
        #endif

        id=1;   // 第一个索引值是引导码时间,之后是遥控码的脉冲时间
        for (i=0; i<4; i++) {
            // 循环4次解码4字节数据
            for (j=0; j<8; j++) {
                // 解码1个字节数据
                if ((ir_nec.tmrcnt[id]>=8) && (ir_nec.tmrcnt[id]<=15)) { // bit 0
                    _bit = 0;
                } else if ((ir_nec.tmrcnt[id]>=18) && (ir_nec.tmrcnt[id]<=25)) { // bit 1
                    _bit = 1;
                }
                ir_nec.keycode[i] |= (_bit<<j);
                id++;
            }
        }
        // log_d("0x%02X,0x%02X,0x%02X,0x%02X\n", (int)ir_nec.keycode[0], (int)ir_nec.keycode[1], (int)ir_nec.keycode[2], (int)ir_nec.keycode[3]);
        ir_nec.status = E_IR_STATUS_IDLE;
        return E_IR_KEYCODE_VALUE;
    } else if (ir_nec.flg_repeat == 1) {
        // log_d("Repeat 0x%02X,0x%02X,0x%02X,0x%02X\n", (int)ir_nec.keycode[0], (int)ir_nec.keycode[1], (int)ir_nec.keycode[2], (int)ir_nec.keycode[3]);
        ir_nec.flg_repeat = 0;
        return E_IR_KEYCODE_REPEAT;        
    }

    return E_IR_KEYCODE_NONE;
}


struct ir_keycode_t ir_nec_driver_keycode(void)
{
    uint8_t ret=0;
    struct ir_keycode_t keycode={0};
    ret = ir_nec_driver_decoder();

    // TODO: 过滤指定用户码
    if ((ir_nec.keycode[0] != IR_USER_CODE_A)&&(ir_nec.keycode[0] != IR_USER_CODE_B)) {
        ret = E_IR_KEYCODE_NONE;
    }

    if (E_IR_KEYCODE_NONE == ret) {
        keycode.isvalid = 0;
        keycode.keycode = 0x00;
        keycode.repeat = 0;
    } else if (E_IR_KEYCODE_VALUE == ret) {
        keycode.isvalid = 1;
        keycode.keycode = ir_nec.keycode[2];
        keycode.repeat = 0;
    } else if (E_IR_KEYCODE_REPEAT == ret) {
        keycode.isvalid = 1;
        keycode.keycode = ir_nec.keycode[2];
        keycode.repeat = 1;
    }
	return keycode;
}


void Interrupt_GPIO4 (void) interrupt 10     //GPIO4中断服务程序
{
	P4_INT_REG = 0xff;

    if (E_IR_STATUS_START == ir_nec.status) {
        if ((ir_nec.timer_cnt<120)&&(ir_nec.timer_cnt>100)) {
            // TODO: 接收到重复码
            ir_nec.bit_index=0;
            ir_nec.flg_repeat = 1;
        } else if ((ir_nec.timer_cnt<150)&&(ir_nec.timer_cnt>110)) {
            // TODO: 接收到引导码
            ir_nec.bit_index=0;
        }
        ir_nec.tmrcnt[ir_nec.bit_index] = ir_nec.timer_cnt;
        ir_nec.timer_cnt = 0;
        ir_nec.bit_index++;
        if (33 == ir_nec.bit_index) {
            ir_nec.bit_index = 0;
            ir_nec.timer_cnt = 0;
            ir_nec.status = E_IR_STATUS_DONE;
        }
    } else if (E_IR_STATUS_IDLE == ir_nec.status) {
        // TODO: 第一个下降沿
        ir_nec.status = E_IR_STATUS_START;
        ir_nec.bit_index = 0;
        ir_nec.timer_cnt = 0;
    }

    // IO_TEST_Toggle();

#if 0
    if (P4&Bit1_En) {
        // TODO: 下降沿
        P35=1;

    } else {
        // TODO: 上升沿
        P35=0;
    }
#endif
    
}


/*中断方式*/
void Interrupt_TIMRT1 (void) interrupt 3    //TIMRT1中断服务程序
{	
	TF1 = 1; //清标志
#if TIMERMODE_VALUE != TIMERMODE2
	TIMER01_SetPeriod(TIMER1,TIMERMODE_VALUE,TIMER1_TICK);
#endif
    // IO_TEST_Toggle();
    ir_nec.timer_cnt++;
}

main.c

main()
{
	struct ir_keycode_t ir_keycode;
	
	// TODO: 单片机系统输出,此处省略。。。


	// TODO: NEC红外解码初始化	
	ir_nec_driver_init()
	
	while(1) {
		ir_keycode = ir_nec_driver_keycode();
		if (ir_keycode.isvalid) {
			if (ir_keycode.repeat) {
				log_d("Repeat ");
			}
			switch (ir_keycode.keycode) {
			case IR_KEYCODE_PWR:
				log_d("IR_KEYCODE_PWR\n");
				break;
			case IR_KEYCODE_MENU:
				log_d("IR_KEYCODE_MENU\n");
				break;
			case IR_KEYCODE_TEST:
				log_d("IR_KEYCODE_TEST\n");
				break;
			case IR_KEYCODE_BACK:
				log_d("IR_KEYCODE_BACK\n");
				break;
			case IR_KEYCODE_UP:
				log_d("IR_KEYCODE_UP\n");
				break;
			case IR_KEYCODE_DOWN:
				log_d("IR_KEYCODE_DOWN\n");
				break;
			case IR_KEYCODE_LEFT:
				log_d("IR_KEYCODE_LEFT\n");
				break;
			case IR_KEYCODE_RIGHT:
				log_d("IR_KEYCODE_RIGHT\n");
				break;
			case IR_KEYCODE_ENTER:
				log_d("IR_KEYCODE_ENTER\n");
				break;
			case IR_KEYCODE_CANCEL:
				log_d("IR_KEYCODE_CANCEL\n");
				break;
			case IR_KEYCODE_0:
				log_d("IR_KEYCODE_0\n");
				break;
			case IR_KEYCODE_1:
				log_d("IR_KEYCODE_1\n");
				break;
			case IR_KEYCODE_2:
				log_d("IR_KEYCODE_2\n");
				break;
			case IR_KEYCODE_3:
				log_d("IR_KEYCODE_3\n");
				break;
			case IR_KEYCODE_4:
				log_d("IR_KEYCODE_4\n");
				break;
			case IR_KEYCODE_5:
				log_d("IR_KEYCODE_5\n");
				break;
			case IR_KEYCODE_6:
				log_d("IR_KEYCODE_6\n");
				break;
			case IR_KEYCODE_7:
				log_d("IR_KEYCODE_7\n");
				break;
			case IR_KEYCODE_8:
				log_d("IR_KEYCODE_8\n");
				break;
			case IR_KEYCODE_9:
				log_d("IR_KEYCODE_9\n");
				break;
			default:
				break;
			}
		}
	}
}

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

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

相关文章

敏感词替换为星号

编写一个函数&#xff0c;接收一个字符串参数&#xff0c;将其中 的敏感词替换为星号&#xff0c;并返回替换后的结果。 def getReplace(s):wordList["阿里巴巴","苹果","亚马逊","京东","字节","脸书"]for word …

月圆之夜梦儿时 贡秋竹唱响游子心声

自今年年初贡秋竹的首支单曲《逐梦》发布以来&#xff0c;其人气和传唱度便一直屡创新高&#xff0c;口碑上佳表现良好&#xff0c;网友们纷纷隔空喊话贡秋竹再发新作。时至今日&#xff0c;久经打磨的贡秋竹全新力作《低头思故乡》在千呼万唤中终于震撼首发&#xff01; 贡秋竹…

500以内开放式耳机哪款好?五款高性价比开放式耳机推荐

现在很多人会利用休闲时间进行锻炼&#xff0c;增强体质&#xff0c;在锻炼之前很多人会先入手一些运动设备&#xff0c;像慢跑鞋&#xff0c;还有臂环&#xff0c;运动手表等~当然运动耳机肯定也不能少&#xff0c;边运动边听音乐真的是一大享受&#xff01;但是哪种耳机比较适…

从零到一,全面掌握Apache DolphinScheduler发版流程,实战派经验分享!

引言 Apache DolphinScheduler的发版流程对于确保软件质量和社区协作至关重要&#xff0c;社区Committer王兴杰为我们详细介绍了Apache DolphinScheduler的发版流程&#xff0c;包括环境准备、流程文档、基础工具准备、依赖包确认等关键步骤&#xff0c;并指出了发版流程中可能…

一机两用的“多面手”既防勒索病毒又能做到数据防泄密!

随着数字化转型的加速&#xff0c;企业对互联网的依赖日益加深&#xff0c;网络安全风险也随之增加。勒索病毒作为网络安全领域的一大威胁&#xff0c;不仅加密重要文件&#xff0c;还可能泄露敏感信息&#xff0c;给企业带来巨大损失。SPN沙盒产品&#xff0c;以其独特的隔离技…

【python报错解决】ImportError: DLL load failed while importing win32gui: 找不到指定的程序

在 Python 中安装 pywin32 库 pip install pywin32安装完成后找到自己的 Python 根目录&#xff0c;在该目录下打开命令行。 在命令行中输入&#xff1a; python.exe Scripts/pywin32_postinstall.py -install执行后显示以下信息&#xff0c;即问题解决。 Parsed argumen…

KP8530X系列KP85302SGA 650V耐压 集成自举二极管的半桥栅极驱动器 专用于驱动功率MOSFET或IGBT

KP8530X系列KP85302SGA是一款 650V 耐压&#xff0c;集成自举二极管的半桥栅极驱动器&#xff0c;具有 0.3A 拉电流和 0.6A 灌电流能力&#xff0c;专用于驱动功率 MOSFETs 或 IGBTs。采用高压器件工艺技术&#xff0c;具有良好的电流输出及出色的抗瞬态干扰能力。在输入逻辑引…

React+Vis.js(05):vis.js的节点的点击事件

文章目录 需求实现思路抽屉实现完整代码需求 双击节点,弹出右侧的“抽屉”,显示节点的详细信息 实现思路 vis.network提供了一个doubleClick事件,代码如下: network.on(doubleClick, function (properties) {// console.log(nodes);let id = properties

el-date-picker根据某个时间动态规定可选的的时间范围

el-date-picker组件根据某一个时间段来动态规定当前时间选择的日期时间范围 例如&#xff1a;开始时间为2024-8-19&#xff0c;规定可循范围为30天的话&#xff0c;可选范围是2024-8-19至2024-9-19号之间 html <el-date-picker class"date" type"date"…

【GIS开发学员故事】地信本科前后跨过六个行业,勇气是人生的第七件装备

“出过外业、送过外卖、搞过环境设计......” 今天&#xff0c;我们就来看看X同学的就业故事&#xff1a; 自我介绍 我毕业于21年&#xff0c;大学是地理信息科学专业&#xff0c;考过一次研&#xff0c;但是没有考上。去年来的新中地学习GIS开发&#xff0c;目前是在广东的…

人机环境系统智能中有三种神经网络相互作用

在人机环境生态系统智能中&#xff0c;人、机器和环境之间的相互作用确实涉及到三种神经网络的协作&#xff0c;分别是人的神经网络、机器的神经网络和环境的神经网络。 1. 人的神经网络 人的神经网络指的是人类大脑及其神经系统的复杂结构&#xff0c;通过神经元之间的连接来处…

SpringBoot MySQL BinLog 监听数据变化(多库多表)

开始 1&#xff1a;引入mysql-binlog-connector-java.jar <!-- binlog --><dependency><groupId>com.zendesk</groupId><artifactId>mysql-binlog-connector-java</artifactId><version>0.27.1</version></dependency>…

亦菲喊你来学习之机器学习(6)--逻辑回归算法

逻辑回归 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广泛使用的统计方法&#xff0c;用于解决分类问题&#xff0c;尤其是二分类问题。尽管名字中有“回归”二字&#xff0c;但它实际上是一种分类算法&#xff0c;因为它试图通过线性回归的方式去预测一个事件…

【计算机组成原理】二、数据的表示和运算:3.算术逻辑单元ALU(逻辑运算、加法器)

4.运算器ALU 文章目录 4.运算器ALU4.1逻辑运算非&#xff08;NOT&#xff09;与&#xff08;AND&#xff09;或&#xff08;OR&#xff09;异或&#xff08;XOR&#xff09;同或&#xff08;XNOR&#xff09; 4.2加法器4.2.1一位全加器4.2.2串行加法器4.2.3并行加法器 4.3ALU功…

金九银十简历石沉大海?别投了,软件测试岗位饱和了....

各大互联网公司的接连裁员&#xff0c;政策限制的行业接连消失&#xff0c;让今年的求职雪上加霜&#xff0c;想躺平却没有资本&#xff0c;还有人说软件测试岗位饱和了&#xff0c;对此很多求职者深信不疑&#xff0c;因为投出去的简历回复的越来越少了。 另一面企业招人真的…

IDEA翻译插件-Translation

简介 Translation是一个为IntelliJ IDEA和其他基于JetBrains的IDE&#xff08;如 PyCharm、WebStorm 等&#xff09;设计的插件。这个插件的主要功能是帮助开发者在编写代码或文档时快速翻译文本。它集成了谷歌翻译、微软翻译、DeepL 翻译、OpenAI 翻译、有道翻译等众多翻译引…

CISAW安全运维认证考试重点内容介绍

CISAW安全运维认证是信息、运维方面非常重要的证书&#xff0c;从事与信息安全以及运维方向的人员都会考这个证书&#xff0c;其持有证书在工作上带来极大的帮助。 那么&#xff0c;CISAW安全运维认证考试重点内容是什么&#xff1f;就目前的问题给大家一些列讲解&#xff0c;…

vue-element-admin解决三级目录的KeepAlive缓存问题(详情版)

vue-element-admin解决三级目录的KeepAlive缓存问题&#xff08;详情版&#xff09; 本文章将从问题出现的角度看看KeepAlive的缓存问题&#xff0c;然后提出两种解决方法。本文章比较详细&#xff0c;如果只是看怎么解决&#xff0c;代码怎么改&#xff0c;请前往配置版。 一…

2007-2022年上市公司资源节约数据

2007-2022年上市公司资源节约数据 1、时间&#xff1a;2007-2022年 2、来源&#xff1a;上市公司年报、社会责任报告、上市公司网站信息 3、指标&#xff1a;水资源节约、电力节约、原煤节约、天然气节约、汽油节约、柴油节约、集中供热节约、折算成统一标准煤共计节约 4、…

stl容器适配器 stack与queue,priority_queue

目录 一.stack 1.stack的使用 2.适配器 3.stack相关的题目 最小栈. - 力扣&#xff08;LeetCode&#xff09; ​编辑 栈的弹出压入序列栈的压入、弹出序列_牛客题霸_牛客网 用两个栈实现队列. - 力扣&#xff08;LeetCode&#xff09; 4.stack的模拟实现 二.queue队列…