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

红外遥控模块

1.1红外通信介绍

电视,空调等

成本低,控制简单

遥控器:红外发射关发射红外光,3mm和5mm功率不同,通信距离不同。

接收端:红外接收管,把红外光信号转换为电信号。

原理图

当接收端接收到信号时,灯管导通,上拉电阻就不起作用,输出为0

当接收端没有接收到信号时,灯管不导通,由于上拉电阻的作用,输出为1

电源(VCC)通过上拉电阻(图中 “>20K” 的电阻)向输出端供电,使输出端被拉到高电平(1),上拉电阻的作用就是在 “灯管” 不导通时,为输出端提供高电平的逻辑信号。

1.2红外编码

红外通信时的一个协议。对0和1进行编码。编码格式NEC   RC5

NEC:

1)38K的载波频率。光很容易受到干扰。消除其他光源的干扰

2)有引导码,通信之前先发送高9ms,低4.5ms

3)使用16位的客户代码。开,关。。。每个功能都是一个16位的数据

4)使用8位的数据代码和8位取反的数据代码

0x01                                                     0x08

0000 0001  1111 1110                                 0000 1000    1111 0111

先数据码,再取反码

接收端波形图分析:

引导码:

发射端先发送9ms的高,接收端接收9ms的低,发射端再发送4.5ms的低,接收端接收4.5ms的高

逻辑0和1的编码格式:

逻辑0:是0.56ms高(载波)+0.56ms低 ----------接收端:0.56ms高+0.56ms低

逻辑1:是056ms高(载波)+1.68ms低 -----------接收端:1.68ms高+0.56ms低

数据的接收,由于低电平都一样,只看高电平就可以

0000 0000     1111 1111      1010 0010         0101 1101

0x00                     0xFF                     0XA2                    0x5D

8位的数据代码和8位取反的数据代码

问题:为什么发送的38k的载波,收到的不是呢?

发射端:发1,并不是真正的1,而是红外LED 以 38kHz 的频率快速闪烁(人眼无法察觉),形成一个被 38kHz 载波调制的红外脉冲信号。

由于红外接收装置,采用VS1838B,能够实现38khz的过滤

检测逻辑:轮询和外部中断

采用外部中断进行触发检测

使用定时器进行计数,用于对逻辑0和1的检测

程序:

初始化红外接收器和定时器

void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period=0xFFFF;
TIM_TimeBaseInitStruct.TIM_Prescaler=71; //72/(71+1)=1MhzTIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);NVIC_EnableIRQ(TIM2_IRQn);
}

初始化定时器 TIM2。首先使能 TIM2 的时钟,然后配置定时器的基本参数,包括时钟分频、计数模式、周期和预分频器。这里设置预分频器为 71,使得定时器的计数频率为 1MHz,周期为 0xFFFF。最后使能定时器的更新中断,并配置 NVIC使能 TIM2 的中断。

void NEC_Init(void)
{RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_InitTypeDef GPIO_InitStruct;GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;//上拉输入GPIO_InitStruct.GPIO_Pin=GPIO_Pin_1;GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStruct);GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);EXTI_InitTypeDef  EXTI_InitStruct;EXTI_InitStruct.EXTI_Line=EXTI_Line1;EXTI_InitStruct.EXTI_LineCmd=ENABLE;EXTI_InitStruct.EXTI_Mode=EXTI_Mode_Interrupt;//中断模式EXTI_InitStruct.EXTI_Trigger=EXTI_Trigger_Rising_Falling;EXTI_Init(&EXTI_InitStruct);NVIC_InitTypeDef NVIC_InitStruct;NVIC_InitStruct.NVIC_IRQChannel=EXTI1_IRQn;NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;NVIC_Init(&NVIC_InitStruct);NVIC_EnableIRQ(EXTI1_IRQn);}

首先使能 GPIOA 和 AFIO 的时钟,然后配置 GPIOA 的 Pin1 为上拉输入模式,用于接收红外信号。接着配置 GPIO 的外部中断线,将 PA1 与外部中断线 EXTI_Line1 关联。再配置 EXTI 的相关参数,包括中断线、使能、中断模式和触发方式(上升沿和下降沿触发)。最后配置 NVIC,设置 EXTI1 的中断优先级,并使能该中断。

外部中断处理

void EXTI1_IRQHandler(void)
{if (EXTI_GetITStatus(EXTI_Line1) != RESET){//引导switch (nec.trigger){case 0:Start_Count();nec.trigger++;break;case 1:Stop_Count();if (Get_count() > 8000 && Get_count() < 10000){nec.trigger++;Start_Count();}elsenec.trigger = 0;break;case 2:Stop_Count();if (Get_count() > 3800 && Get_count() < 5500){nec.trigger++;nec.cnt = 0;memset(nec.data, 0, sizeof(nec.data));}elsenec.trigger = 0;break;default://接收数据if (Nec_Read_Pin())Start_Count();else{Stop_Count();if (Get_count() > 380 && Get_count() < 750){}else if (Get_count() > 1350 && Get_count() < 2000){nec.data[nec.cnt / 8] |= 1 << (nec.cnt % 8);}elsenec.trigger = 0;nec.cnt++;if (nec.cnt >= 32){nec.receive_complete = 1;nec.trigger = 0;nec.cnt = 0;}}}EXTI_ClearITPendingBit(EXTI_Line1);}
}

触发外部中断时,打开定时器,trigger++,准备进入状态1;

进入状态1,停止定时器,并且进行判断,是否是低电平的9ms,由于定时器计数时存在误差,就把范围设置为8000-10000us。

如果是低电平的9ms,那么trigger++,并且开始定时器,准备进入状态2。否则trigger=0;重新开始。

进入状态2,停止定时器,并且进行判断,是否是高电平的4.5ms,由于定时器计数时存在误差,就把范围设置为3800-5500us。

如果是高电平的4.5ms,那么trigger++,cnt=0,并且data数组清零,准备进入状态3。否则trigger=0;重新开始。

状态3就是接收数据,当引导码(9ms 低 + 4.5ms 高)验证通过后,就进入32 位数据接收阶段(NEC 协议规定数据是 “8 位地址码 + 8 位地址反码 + 8 位数据码 + 8 位数据反码”),核心是通过 “电平跳变 + 定时器计时” 区分二进制的 0 和 1。

跳变到高电平→开定时器计时→跳变到低电平→关定时器→看时长判 0/1→存数据 / 只计数→直到收满 32 位,标记完成。

数据获取:

uint8_t NEC_GetData(NEC *nec, u8 *data)
{if (nec->receive_complete){if ((nec->data[0] == (uint8_t)~nec->data[1]) &&(nec->data[2] == (uint8_t)~nec->data[3])){memcpy(data, nec->data, sizeof(nec->data));nec->receive_complete = 0;return 1;}else{nec->receive_complete = 0;}}return 0;
}

就是确认接收到数据后,在进行对数据校验,验证数据的地址码与反码、数据码与反码是否符合 NEC 协议标准。如果验证通过,则将数据复制到输出缓冲区data,重置接收完成标志,并返回 1 表示数据有效;否则,也重置接收完成标志并返回 0 表示数据无效。

在红外解码代码中,(uint8_t) 是 强制类型转换,用于将取反运算的结果转换为 8 位无符号整数,解决 C 语言中 “整数提升” 导致的校验错误问题

  • 例如:nec->data[1] 是 u8 类型,值为 0x00
    执行 ~nec->data[1] 时,实际过程是:
    1. 0x00 先被提升为 int 类型的 0x00000000
    2. 按位取反后得到 0xFFFFFFFF(32 位,对应十进制 -1
http://www.dtcms.com/a/352620.html

相关文章:

  • 逻辑流图、作业图、执行图、物理图
  • 嵌入式软件移植
  • 【制作100个Unity游戏】从零开始构建类《月圆之夜》《杀戮尖塔》的卡牌游戏(附带项目源码)
  • Windows远程协助安全配置与使用限制
  • STM32G4 SVPWM VF开环强拖电机
  • 2026 届最新大数据专业毕设选题推荐,毕业设计题目汇总
  • 达索 Enovia 许可管理技术白皮书:机制解析与智能优化实践
  • 段式存储、页式存储、段页式存储:三种内存管理策略的演进与权衡
  • PyTorch生成式人工智能——PatchGAN详解与实现
  • Docker实战系列:使用Docker部署YouTrack项目管理系统
  • Linux修改bootflag(启动标签)到指定分区
  • RedHat 5.7升级为PAE内核,并更新yum源
  • 软件产品线过程模型全景解析:双生命周期、SEI 与三生命周期
  • 《数据之心》——鱼小妖全传
  • 数据结构07(Java)-- (堆,大根堆,堆排序)
  • JHipster 一个基于 Yeoman 的全栈应用生成器
  • 常见软件优化功能项
  • More Effective C++ 条款09:使用析构函数防止资源泄漏
  • 用友NCC 如何通过OpenApi 上传附件
  • 【计组】总线与IO
  • 【C++】智能指针底层原理:引用计数与资源管理机制
  • 菜鸡还没有找到工作(DAY41)
  • 永磁同步电机无速度算法--高频脉振正弦波注入到两相静止坐标系
  • 全新机器人遥操作触觉感知解决方案
  • postman使用教程
  • MATLAB 实现子图不规则排列
  • 【软考论文】论自动化测试方法及其应用
  • 这个AI有点懒
  • ZAM5404B:通道速率和信号带宽双提升,工业采集更高效
  • Tokenizer