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

单片机外部中断+定时器实现红外遥控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位定时模式,最大计数65536enum {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位定时模式,最大计数65536TIMER01_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 debugfor (i=0; i<33; i++) {log_d("IR [%d]:%d\n", (int)i, (int)ir_nec.tmrcnt[i]);}#endifid=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 0if (P4&Bit1_En) {// TODO: 下降沿P35=1;} else {// TODO: 上升沿P35=0;}
#endif}/*中断方式*/
void Interrupt_TIMRT1 (void) interrupt 3    //TIMRT1中断服务程序
{	TF1 = 1; //清标志
#if TIMERMODE_VALUE != TIMERMODE2TIMER01_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.mzph.cn/bicheng/52496.shtml

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

相关文章

Vue的计算属性:methods方法、computed计算属性、watch监听属性

1、methods 方法 在创建的 Vue 应用程序实例中&#xff0c;可以通过 methods 选项定义方法。应用程序实例本身会代理 methods 选项中的所有方法&#xff0c;因此可以像访问 data 数据那样来调用方法。 【实例】在 Vue 应用程序中&#xff0c;使用 methods 选项定义获取用户信…

实验16:定时器中断实验

无硬件图&#xff0c;用到D1灯 代码main.c #include<reg52.h>typedef unsigned int u16; typedef unsigned char u8;sbit LED1P2^0;void delay_10us(u16 n) {while(n--); }void delay_ms(u16 ms) {u16 i,j;for(ims;i>0;i--)for(j110;j>0;j--); }void time0_init(v…

2月公开赛Web-ssrfme

考点&#xff1a; redis未授权访问 源码&#xff1a; <?php highlight_file(__file__); function curl($url){ $ch curl_init();curl_setopt($ch, CURLOPT_URL, $url);curl_setopt($ch, CURLOPT_HEADER, 0);echo curl_exec($ch);curl_close($ch); }if(isset($_GET[url…

innodb_buffer_pool_size在线缩小操作

一、背景 测试数据库内存32G&#xff0c;只有MySQL数据库&#xff0c;但是innodb_buffer_pool_size设置了24G&#xff0c;导致经常出现lack of memory问题、lack of swap问题。 因为使用了MySQL5.7.36版本&#xff0c;利用innodb_buffer_pool_size参数值可在线调整的新特性&…

编程之路:在Bug的迷宫中寻找出口

编程是一种艺术&#xff0c;也是一种科学。它要求我们既要有创造性的思维&#xff0c;又要有严谨的逻辑。在这条充满挑战的道路上&#xff0c;每个人都会遇到挫折&#xff0c;这些挫折可能来自于一个难以解决的Bug&#xff0c;一个复杂的算法&#xff0c;或者是在实现某个功能时…

云手机解决了TikTok哪些账号运营难题?

随着社交媒体的蓬勃发展&#xff0c;TikTok作为一款风靡全球的短视频应用&#xff0c;成为许多个人和企业进行品牌推广、内容创作的首选平台。然而&#xff0c;随之而来的是TikTok账号运营的一系列难题。本文将深入探讨云手机是如何解决这些难题的。 1、多账号运营的便捷性&…

tomcat实战演练

一.tomcat介绍 Tomcat 服务器是一个免费的开放源代码的 Web 应用服务器&#xff0c;属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c; Tomcat 具有处理 HTML 页面的功能&#xff0c;它还是一个 Servlet 和 JSP容器。Tomc…

C语言笔试题(指针、数组、整数在内存中的存储、结构体......)

文章目录 1.选择题2.代码题2.1 模拟实现strncat2.2 模拟实现strncpy2.3 编写判断大小端程序2.4 模拟实现atoi2.5 BC38 变种水仙花数2.6 BC98 序列中删除指定数字 今天我们一起来看一些题目 1.选择题 解析如下&#xff1a; 正确选项&#xff1a;B A.参数错误&#xff1b;D.返回…

Prettier+Vscode setting提高前端开发效率

文章目录 前言Prettier第一步&#xff1a;下载依赖&#xff08;团队合作&#xff09;或下载插件&#xff08;独立开发&#xff09;第二步&#xff1a;添加.prettierrc.json文件**以下是我使用的****配置规则** 第三步&#xff1a;添加.prettierignore文件**以下是我常用的****配…

LabVIEW多显示器环境下主显示器识别与管理

该程序使用 LabVIEW 图形化编程语言&#xff0c;涉及多显示器环境中主显示器的识别与信息提取。图像显示了两个不同的方法来获取主显示器的信息。 第一部分&#xff1a;方法一——基于显示器位置的主显示器识别 1. 当前监视器识别&#xff1a; 使用“FP.Monitor”属性节点获取…

plsql表格怎么显示中文 plsql如何导入表格数据

在Oracle数据库开发中&#xff0c;PL/SQL Developer是一款广泛使用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;它提供了丰富的功能来帮助开发人员高效地进行数据库开发和管理。在使用PL/SQL Developer时&#xff0c;许多用户会遇到表格显示中文的问题&#xff0c;以…

ansible:

ansible&#xff1a; 远程自动化运维 ansible是基于python开发的配置管理和应用部署工具。 也是自动化运维的重要工具。 可以批量配置&#xff0c;部署&#xff0c;管理上千台主机。 只需要在一台主机配置ansible就可以完成其他主机的操作。 操纵模式&#xff1a; 1、模…

EmguCV学习笔记 VB.Net 6.4 霍夫变换

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。 教程VB.net版本请访问…

ArcGIS Pro基础:如何将数据和引用地图样式一起打包分享

如上所示&#xff0c;有2个矢量图斑&#xff0c;一个是耕地地块&#xff0c;另一个是范围图斑&#xff0c;如果我们需要把此工程的所有数据以及引用地图一起分享给别人&#xff0c;就可以使用【打包工程】这个工具。 如上所示&#xff0c;在【地理处理】下输入【打包工程】&am…

【C语言】常见文件操作

文件的常见操作 #include<stdio.h>// 由于devc代码编码为ANCI&#xff0c;故读取的文件中若有中文&#xff0c;请设置文件编码为ANCI&#xff0c;否则会乱码 // 读文件 void test1() {char ch;FILE *fp; // 创建文件指针fp fopen("./file.txt", "r"…

数据结构系列-归并排序

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” 归并排序 递归版本 首先&#xff0c;我们来看一下归并的示意图&#xff1a; 这是归并排序当中分解的过程。 然后便是两个两个进行排序&#xff0c;组合的过程。 归并完美的诠释…

系统编程-管道

管道 目录 管道 1、管道的特点 2、无名管道的使用步骤 &#xff08;1&#xff09;在进程中使用 pipe 函数来获取管道的文件描述符 &#xff08;2&#xff09;使用 fork 函数来创建子进程 &#xff08;3&#xff09;通过获取到的文件描述符来进行数据的传输 &#xff08…

[论文阅读] mobile aloha实验部分

DP:[1] CHI C, FENG S, DU Y, et al. Diffusion Policy: Visuomotor Policy Learning via Action Diffusion[J]. 2023. Diffusion Policy: Visuomotor Policy Learning via Action Diffusion精读笔记&#xff08;一&#xff09;-CSDN博客 哥伦比亚大学突破性的方法- Diffusio…

【SpringBoot】11 多数据源(MyBatis:dynamic-datasource)

介绍 多数据源&#xff1a;指的是一个单一应用程序中涉及了两个及以上的数据库&#xff0c;这种配置允许应用程序根据业务需求灵活地管理和操作不同的数据库。 需求 一个应用服务中&#xff0c;连接多个数据库&#xff0c;有本地的也有远程的&#xff0c;有MysQL、Oracle、P…

PDPS软件 那智机器人 (丰田版)离线程序导出处理

在PDPS仿真软件中导出的那智机器人离线程序&#xff0c;一般是无法直接给TFD控制装置-那智机器人&#xff08;丰田式样版&#xff09;导入及识别使用。因此要对导出的程序进行转换编译处理&#xff0c;才能给TFD那智机器人&#xff08;丰田式样版&#xff09;导入离线程序。以下…