当前位置: 首页 > news >正文

单片机外部中断+定时器实现红外遥控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.mrgr.cn/news/8755.html

相关文章:

  • LEAP模型在能源环境发展、碳排放建模预测及分析中实践应用
  • java操作zookeeper
  • 【话题】关于工厂模式和策略模式
  • 机架式服务器通常适用于哪些场景?
  • 2.redis背景知识
  • 34次8.22(docker基础)
  • 哈希-赎金信字母相关
  • Linux(面试篇)
  • FFmpeg的入门实践系列一
  • 打造高效的编程学习“知识宝库”:从笔记记录到知识管理
  • Object.entries 作用
  • SEO古诗网,可做站群,可二开成泛——码山侠
  • 【网络编程】select实现服务器与客户端进行通信
  • RabbitMQ 基础总结
  • 基于imx6ull平台移植ffmpeg3.4.5及ffmpeg验证
  • 从零开始学cv-6:图像的灰度变换
  • 【C++从练气到飞升】14---深入浅出继承
  • 深度学习--复制机制
  • leetcode1005:K次取反后最大化的数组和
  • Could not resolve host: mirrorlist.centos.org; 未知的错误