23- ESP32 红外遥控 (RMT)

ESP32 IDF库中的RMT驱动

RMT(Remote Control Module)驱动是ESP-IDF库中的一个重要组成部分,它主要用于处理远程控制编码和解码。

红外遥控器介绍

一、红外遥控技术介绍

红外遥控是一种无线、非接触控制技术,具有抗干扰能力强,信息传输可靠,功耗低,成本低,易实现等显著优点。它被诸多电子设备特别是家用电器广泛采用,并越来越多的应用到计算机和手机系统中。

红外遥控系统由发射和接收两大部分组成,应用编码/解码专用集成电路芯片来进行控制操作。发射部分包括键盘矩阵、编码调制、LED红外发送器;接收部分包括光、电转换放大器、解调、解码电路。

二、红外器件特性

红外遥控器主要由红外发射器和红外接收器两部分组成。

  1. 红外发射器:红外发射器通常是一个红外LED,它可以发射红外光。当电流通过LED时,它会发射出红外光。红外LED的发射波长通常在940nm左右,这是人眼无法看到的红外光。

  2. 红外接收器:红外接收器是一个光电二极管,它可以将接收到的红外光转换为电信号。红外接收器通常有一个滤波器,可以过滤掉大部分的环境光,只接收特定频率的红外光。

红外发射管也是属于二极管类,红外发射电路通常使用三极管控制红外发射器的导通或者截至,在导通的时候,红外发射管会发射出红外光,反之,就不会发射出红外光。虽然我们用肉眼看不到红外光,但是我们借助手机摄像头就能看到红外光。但是红外接收管的特性是当接收到红外载波信号时,OUT 引脚输出低电平;假如没有接收到红外载波信号时,OUT 引脚输出高电平。

红外载波信号其实就是由一个个红外载波周期组成。在频率为 ( 38 K H z ) (38KHz ) (38KHz)下,红外载波周期约等于 26.3 u s ( 1 s / 38 K H z ≈ 26.3 u s ) 26.3us(1s / 38KHz ≈ 26.3us) 26.3us1s/38KHz26.3us。在一个红外载波发射周期里,发射红外光时间 8.77 u s 8.77us 8.77us 和不发射红外光 17.53 u s 17.53us 17.53us,发射红外光的占空比一般为 1 / 3 1/3 1/3。相对的,整个周期内不发射红外光,就是载波不发射周期。在红外遥控器内已经把载波和不载波信号处理好,我们需要做的就是识别遥控器按键发射出的信号,信号也是遵循某种协议的。

这里推荐一个视频可以快速了解红外控制器件:

📻一帧红外遥控信号,竟如此复杂,超乎你的想象!红外遥控的工作原理!

三、红外编解码协议介绍

红外遥控器通常使用特定的编解码协议来发送和接收数据。目前广泛使用的是:PWM(脉冲宽度调制)的NEC 协议和Philips PPM(脉冲位置调制)的 RC-5 协议的。这里以NEC为主讲解ESP32 红外遥控 (RMT)

NEC 协议,其特征如下:

  1. 8 位地址和 8 位指令长度
  2. 地址和命令 2 次传输(确保可靠性)
  3. PWM 脉冲位置调制,以发射红外载波的占比代表“0”和“1”
  4. 载波频率为 38Khz
  5. 位时间为 1.125ms 或 2.25ms

在 NEC 协议中,如何为协议中的数据‘0’或者‘1’这里分开红外接收器和红外发射器
红外发射器:

  • 发送协议数据‘0’ = 发射载波信号560us + 不发射载波信号 560us
  • 发送协议数据‘1’ = 发射载波信号 560us + 不发射载波信号 1680us

红外发射器的位定义如下图所示。
在这里插入图片描述
🚨需要注意的是红外编码发送的时候,并不单单是通过高低电平发送的,是在38khz的载波下进行发送的

红外接收器:

  • 接收到协议数据‘0’ = 560us 低电平 + 560us 高电平
  • 接收到协议数据‘1’ = 560us 低电平 + 1680us 高电平

红外接收器的位定义如下图所示
在这里插入图片描述
NEC协议的数据格式包括以下几个部分:

  1. 同步码:这是一个9ms的低电平+4.5ms的高电平,用于同步。
  2. 地址码和地址反码:这是一个8位数据格式,低位在前,高位在后。
  3. 控制码和控制反码:这部分用于表示具体的控制命令。

如果你长时间按住遥控按钮,使用NEC协议的红外遥控器将会发射一个以110ms为周期的重复码。每一次用户按下遥控器按钮,遥控器在发送一次指令码后,就不会再发送指令码了,而是发送一段重复码。

RMT驱动

简介

ESP32-S3的RMT 是一个红外发送和接收控制器,可通过软件加解密多种红外协议。RMT 模块可以实现模块内置 RAM 中的脉冲编码转换为信号输出,或将模块的输入信号转换为脉冲编码存入 RAM 中。此外,RMT 模块可以选择是否对输出信号进行载波调制,也可以选择是否对输入信号进行滤波和去噪处理。

RMT 共有八个通道,编码为 0 ~ 7,各通道可独立用于发送或接收信号:

  • 0 ~ 3 通道专门用于发送信号;
  • 4 ~ 7 通道专门用于接收信号。

每个发送通道和接收通道分别有一组功能相同的寄存器。另外,发送通道 3 和接收通道 7 对应的 RAM 支持 DMA访问,因此还有 DMA 相关的控制和状态寄存器。

RMT 符号的内存布局

RMT 硬件定义了自己的数据模式,称为 RMT 符号。对于一个 RMT 符号的位字段:每个符号由两对两个值组成,每对中的第一个值是一个 15 位的值,表示信号持续时间,以 RMT 滴答计。每对中的第二个值是一个 1 位的值,表示信号的逻辑电平,即高电平或低电平。

请添加图片描述

RMT RX驱动的接收解码

1️⃣安装 RMT 接收通道

该函数用于安装 RMT 接收通道,其函数原型如下所示:

esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config,  rmt_channel_handle_t *ret_chan); 

该函数的形参描述,如下表所示:

形参描述
config指向配置 RMT 接收通道的指针
ret_chan返回的通用 RMT 通道句柄

该函数的使用示例,如下所示:

#include "driver/gpio.h" void example_fun(void) 
{ // 初始化 RMT 接收通道句柄为 NULLrmt_channel_handle_t rx_chan = NULL; // 配置 RMT 接收通道参数rmt_rx_channel_config_t rx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT,            // 使用默认时钟源.gpio_num = 0,                             // 使用 GPIO0.mem_block_symbols = 64,                   // 内存块大小为 64.resolution_hz = 1 * 1000 * 1000,          // 分辨率为 1MHz.trans_queue_depth = 4,                    // 传输队列深度为 4.flags.invert_out = false,                 // 不反转输出信号.flags.with_dma = false,                   // 不使用 DMA}; // 安装 RMT 接收通道,并检查错误ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_chan_config, &rx_chan)); 
}

2️⃣配置 RMT 接收通道的回调函数

该函数用于配置 RMT 接收通道的回调函数,其函数原型如下所示:

esp_err_t rmt_rx_register_event_callbacks(rmt_channel_handle_t rx_channel, const rmt_rx_event_callbacks_t *cbs, void *user_data); 

该函数的形参描述,如下表所示:

形参描述
rx_channel创建的 RMT 通道句柄
cbsRMT 接收事件回调函数结构体指针
user_data用户数据,将直接传递给回调函数

该函数的使用示例,如下所示:

#include "driver/gpio.h" void example_fun(void) 
{ QueueHandle_t receive_queue; rmt_channel_handle_t rx_chan = NULL; rmt_rx_channel_config_t rx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT,    // 使用默认时钟源.gpio_num = 0,                     // 使用 GPIO0.mem_block_symbols = 64,           // 内存块大小为 64.resolution_hz = 1 * 1000 * 1000,  // 分辨率为 1MHz.trans_queue_depth = 4,            // 传输队列深度为 4.flags.invert_out = false,         // 不反转输出信号.flags.with_dma = false,           // 不使用 DMA}; ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_chan_config, &rx_chan)); // 创建用于存储接收完成事件数据的队列receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t)); assert(receive_queue); 	//检查队列是否创建成功rmt_rx_event_callbacks_t cbs = { .on_recv_done = RMT_Rx_Done_Callback,  // 设置接收完成回调函数}; ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_chan, &cbs,  receive_queue)); 
} 

3️⃣创建一个基于 NEC 协议的 RMT 编码器

该函数用于创建一个基于 NEC 协议的 RMT 编码器,其函数原型如下所示:

esp_err_t rmt_new_ir_nec_encoder(const ir_nec_encoder_config_t *config, rmt_encoder_handle_t *ret_encoder); 

该函数的形参描述,如下表所示:

形参描述
config指向 RMT 编码器配置的指针
ret_encoder返回的 RMT 编码器句柄

该函数的使用示例,如下所示:

#include "driver/gpio.h" void example_fun(void) 
{ // 配置 NEC 编码器参数ir_nec_encoder_config_t nec_encoder_cfg = { .resolution = 1000000,  // 分辨率为 1MHz};// 初始化 NEC 编码器句柄rmt_encoder_handle_t nec_encoder = NULL; // 创建基于 NEC 协议的 RMT 编码器,并检查错误ESP_ERROR_CHECK(rmt_new_ir_nec_encoder(&nec_encoder_cfg, &nec_encoder)); 
}

4️⃣使能 RMT 接收通道

该函数用于使能 RMT 接收通道,其函数原型如下所示:

esp_err_t rmt_enable(rmt_channel_handle_t channel); 

该函数的形参描述,如下表所示:

形参描述
channel创建的 RMT 通道句柄

该函数的使用示例,如下所示:

#include "driver/gpio.h" void example_fun(void) 
{ // 初始化 RMT 接收通道句柄为 NULLrmt_channel_handle_t rx_channel = NULL; //省略中间过程.....// 使能 RMT 接收通道,并检查错误rmt_enable(rx_channel); 
}

5️⃣启动 RMT 接收通道的接收任务

该函数用于启动 RMT 接收通道的接收任务,其函数原型如下所示:

esp_err_t rmt_receive(rmt_channel_handle_t rx_channel,  void *buffer, size_t buffer_size, const rmt_receive_config_t *config); 

该函数的形参描述,如下表所示:

形参描述
rx_channel创建的 RMT 通道句柄
buffer用于存储接收到的 RMT 符号的缓冲区
buffer_size缓冲区大小
config接收特定配置

该函数的使用示例,如下所示:

#include "driver/gpio.h" void example_fun(void) 
{ // 声明用于存储接收到的 RMT 符号的缓冲区rmt_symbol_word_t raw_symbols[64]; // 配置接收任务特定配置rmt_receive_config_t receive_config; // 启动 RMT 接收通道的接收任务,并检查错误rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols), &receive_config); 
}

RMT TX驱动的发送

1️⃣安装 RMT 发送通道

该函数用于安装 RMT 接收通道,其函数原型如下所示:

esp_err_t rmt_new_tx_channel(const rmt_tx_channel_config_t *config,  rmt_channel_handle_t *ret_chan); 

该函数的形参描述,如下表所示:

形参描述
config指向配置 RMT 发送通道的指针
ret_chan返回的通用 RMT 通道句柄

该函数的使用示例,如下所示:

#include "driver/gpio.h" // 定义示例函数
void example_fun(void) 
{ // 声明 RMT 通道句柄rmt_channel_handle_t tx_chan = NULL; // 定义 RMT 发送通道配置rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT,            // 使用默认时钟源.gpio_num = 0,                             // GPIO0 用于发送.mem_block_symbols = 64,                   // 内存块大小为 64.resolution_hz = 1 * 1000 * 1000,          // 分辨率为 1 MHz.trans_queue_depth = 4,                    // 传输队列深度为 4.flags.invert_out = false,                 // 输出不反转.flags.with_dma = false,                   // 不使用 DMA}; // 调用函数安装 RMT 发送通道,并检查返回错误ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_chan)); 
} 

2️⃣使能 RMT 发送通道

该函数用于使能 RMT 发送通道,其函数原型如下所示:

esp_err_t rmt_enable(rmt_channel_handle_t channel); 

该函数的形参描述,如下表所示:

形参描述
channel创建的 RMT 通用通道
#include "driver/gpio.h" // 定义示例函数
void example_fun(void) 
{ rmt_channel_handle_t tx_channel = NULL; // 声明 RMT 通道句柄rmt_enable(tx_channel); // 调用函数使能 RMT 发送通道
} 

3️⃣通过 RMT 发送通道传输数据

该函数用于启动 RMT 接收通道的接收任务,其函数原型如下所示:

esp_err_t rmt_transmit(rmt_channel_handle_t channel, rmt_encoder_t *encoder, const void *payload, size_t payload_bytes, const rmt_transmit_config_t *config) 

该函数的形参描述,如下表所示:

形参描述
channel创建的 RMT 通道
encoder用户自己创建的编码器或者通过其它 API 构建的编码器
payload要编码为 RMT 符号的原始数据
payload_bytes“有效负载”的大小(以字节为单位)
config发送特定配置

发送示例

#include "driver/gpio.h"
#include "driver/rmt.h"// 定义示例函数
void example_fun(void) 
{ // 1. 安装 RMT 发送通道// 声明 RMT 通道句柄rmt_channel_handle_t tx_chan = NULL; // 定义 RMT 发送通道配置rmt_tx_channel_config_t tx_chan_config = { .clk_src = RMT_CLK_SRC_DEFAULT,            // 使用默认时钟源.gpio_num = GPIO_NUM_0,                    // GPIO0 用于发送.mem_block_symbols = 64,                   // 内存块大小为 64.resolution_hz = 1 * 1000 * 1000,          // 分辨率为 1 MHz.trans_queue_depth = 4,                    // 传输队列深度为 4.flags.invert_out = false,                 // 输出不反转.flags.with_dma = false,                   // 不使用 DMA}; // 调用函数安装 RMT 发送通道,并检查返回错误ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_chan_config, &tx_chan)); // 2. 使能 RMT 发送通道rmt_enable(tx_chan); // 调用函数使能 RMT 发送通道// 3. 通过 RMT 发送通道传输数据// 创建数据数组uint8_t data[4] = {0x12, 0x34, 0x56, 0x78};// 创建 RMT 编码器rmt_encoder_t encoder;ESP_ERROR_CHECK(rmt_translator_init(&encoder, sizeof(data), false)); // 数据长度为4字节,不进行反转// 创建 RMT 发送配置rmt_transmit_config_t transmit_config = {.carrier_freq_hz = 38000,   // 载波频率为38kHz.carrier_duty_percent = 50, // 载波占空比为50%.idle_level = RMT_IDLE_LEVEL_LOW, // 高电平空闲.carrier_level = RMT_CARRIER_LEVEL_HIGH, // 高电平为载波.loop_en = false, // 关闭循环发送.loop_count = 0,  // 循环次数为0};// 使用 RMT 发送通道传输数据ESP_ERROR_CHECK(rmt_transmit(tx_chan, &encoder, data, sizeof(data), &transmit_config));
} 

总结

RMT驱动是ESP32的一个强大功能,它可以方便地处理各种远程控制协议,对于开发物联网和嵌入式系统具有很大的帮助。

参考资料
ESP-IDF 红外遥控 (RMT)
正点原子DNESP32S3 开发板教程-IDF 版

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

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

相关文章

ASP.NET网络在线考试系统

摘 要 随着计算机技术的发展和互联网时代的到来,人们已经进入了信息时代,也有人称为数字化时代。数在数字化的网络环境下,学生希望得到个性化的满足,根据自己的情况进行学习,同时也希望能够得到科学的评价&#xff0c…

【深度学习】第一门课 神经网络和深度学习 Week 3 浅层神经网络

🚀Write In Front🚀 📝个人主页:令夏二十三 🎁欢迎各位→点赞👍 收藏⭐️ 留言📝 📣系列专栏:深度学习 💬总结:希望你看完之后,能对…

Kelpa-小型服务器开发框架分享

分享我的服务器开发框架--Kelpa: 这是一个由现代C编写的小型、学习性质的服务器框架,包含压缩,序列化,IO调度,Socket封装,文件配置,日志库等多个完整自研模块: 项目目前仍处于开发阶…

QT:输入类控件的使用

LineEdit 录入个人信息 #include "widget.h" #include "ui_widget.h" #include <QDebug> #include <QString>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);// 初始化输入框ui->lineEdit…

文本嵌入的隐私风险:从嵌入向量重建原始文本的探索

随着大型语言模型&#xff08;LLMs&#xff09;的广泛应用&#xff0c;文本嵌入技术在语义相似性编码、搜索、聚类和分类等方面发挥着重要作用。然而&#xff0c;文本嵌入所蕴含的隐私风险尚未得到充分探讨。研究提出了一种控制生成的方法&#xff0c;通过迭代修正和重新嵌入文…

嵌入式硬件中PCB走线与过孔的电流承载能力分析

简介 使用FR4敷铜板PCBA上各个器件之间的电气连接是通过其各层敷着的铜箔走线和过孔来实现的。 由于不同产品、不同模块电流大小不同,为实现各个功能,设计人员需要知道所设计的走线和过孔能否承载相应的电流,以实现产品的功能,防止过流时产品烧毁。 文中介绍设计和测试FR4敷…

【探索】文字游侠AI新时代,每天5分钟自动化创作图文月入1万+,十分适合新手小白,附上渠道和教程(全面)

在这个信息爆炸的时代&#xff0c;内容创作者面临着空前的竞争。为了在今日头条这样的平台上脱颖而出并获取稳定收入&#xff0c;他们需要找到更高效、更创新的方法。而今&#xff0c;一款全新的AI工具正引领着一场革命&#xff0c;彻底改变了内容创作的生态。 自从GPT问世以来…

Quad SPI的DLP优化原理

1 前言 1.1 Quad SPI Flash QSPI的I/O接口如图1所示&#xff0c;其中&#xff1a; ① CS&#xff1a;片选信号&#xff0c;低电平有效&#xff08;FLASH被选中&#xff09;&#xff1b; ② CK&#xff1a;时钟信号&#xff0c;由主设备产生&#xff1b; ③ SI/SO&#xff1a; …

开源的 RAG 和 workflow 技术对比调研

一、先来了解一下开源的技术有哪些&#xff0c;怎么样 我自己就是做RAG工作的&#xff0c;但是还是想关注一下开源的技术做到了什么程度。 所以调研了很长时间&#xff0c;也体验了一下。这里写一篇文章来分享一下结果。 我用五一的假期时间&#xff0c;来做调研&#xff0c;看…

扩展学习|本体研究进展

文献来源&#xff1a; 王向前,张宝隆,李慧宗.本体研究综述[J].情报杂志,2016,35(06):163-170. 一、本体的定义 本体概念被引入人工智能、知识工程等领域后被赋予了新的含义。然而不同的专家学者对本体的理解不同,所给出的定义也有所差异。 人工智能领域的学者Neches(1991)等人对…

常用AI工具分享 + IDEA内使用通义灵码

引言 随着人工智能技术的飞速发展&#xff0c;AI工具已经渗透到我们日常生活和工作的各个领域&#xff0c;带来了前所未有的便利。现在我将分享一下常用的AI工具&#xff0c;以及介绍如何在IDEA中使用通义灵码。 常用AI工具 1. 通义灵码 (TONGYI Lingma) - 由阿里云开发的智能…

时间复杂度空间复杂度 力扣:转轮数组,消失的数字

1. 算法效率 如何衡量一个算法的好坏&#xff1f;一般是从时间和空间的维度来讨论复杂度&#xff0c;但是现在由于计算机行业发展迅速&#xff0c;所以现在并不怎么在乎空间复杂度了下面例子中&#xff0c;斐波那契看上去很简洁&#xff0c;但是复杂度未必如此 long long Fib…

Java -- (part20)

一.Map集合 1.概述 双列集合的顶级接口 2.实现类 HashMap 特点: a.key唯一,value可重复->如果key重复了,会发生value覆盖 b.无序 c.无索引 d.线程不安全 e.可以存null键null值 数据结构: 哈希表 方法: LinkedHashMap 特点: a.key唯一,value可重复->如果ke…

PHP医院安全(不良)事件报告系统源码 vue2+element支持11大类不良事件上报、审核处理、分析改进

PHP医院安全&#xff08;不良&#xff09;事件报告系统源码 vue2element支持11大类不良事件上报、审核处理、分析改进 医院安全&#xff08;不良&#xff09;事件管理系统采用无责的、自愿的填报不良事件方式&#xff0c;有效地减轻医护人员的思想压力&#xff0c;实现以事件为…

深度学习500问——Chapter08:目标检测(6)

文章目录 8.3.7 RetinaNet 8.3.7 RetinaNet 研究背景 Two-Stage 检测器&#xff08;如Faster R-CNN、FPN&#xff09;效果好&#xff0c;但速度相对慢。One-Stage 检测器&#xff08;如YOLO、SSD&#xff09;速度快&#xff0c;但效果一般。 作者对one-stage检测器准确率不高…

QT:label标签的使用

文章目录 设置不同格式的文本显示图片文本对齐/自动换行/缩进/边距 设置不同格式的文本 在文本格式中&#xff0c;存在富文本&#xff0c;makedown格式的文本&#xff0c;还有纯文本&#xff0c;下面就依据这三个进行举例 #include "widget.h" #include "ui_w…

缩小COCO数据集

在运行YOLOS模型的过程中&#xff0c;需要使用到COCO2017这个数据集&#xff0c;但从实验运行来看&#xff0c;其所需时间无疑是相当漫长&#xff0c;预计可能需要近几十天才能完成&#xff0c;因此便考虑缩小COCO数据集大小&#xff0c;即尽可能在遵循其分布的情况下&#xff…

导游讲解口才技巧心得体会总结(3篇)

导游讲解口才技巧心得体会总结&#xff08;3篇&#xff09; **篇&#xff1a;提升表达力&#xff0c;传递独特魅力 在导游工作中&#xff0c;口才技巧的重要性不言而喻。通过不断的实践和反思&#xff0c;我深刻体会到提升表达力对于导游工作的重要性。一个清晰、生动、有趣的…

【c++】继承学习(一):继承机制与基类派生类转换

&#x1f525;个人主页&#xff1a;Quitecoder &#x1f525;专栏&#xff1a;c笔记仓 朋友们大家好&#xff0c;本篇文章我们来学习继承部分 目录 1.继承的概念和定义继承的定义继承基类成员的访问方式变化 2.基类和派生类对象赋值转换3.继承中的作用域 1.继承的概念和定义 …

OSPF实验系列---3.综合实验

OSPF的综合实验 实验拓扑及要求如下 实验分析 1.R4为ISP&#xff0c;进行IP配置&#xff0c;区域0为公网区域&#xff0c;配置IP地址 2.做MGRE&#xff0c;R3为中心站点&#xff0c;形成Hub-Spoke 3.子网划分 4.私网互通&#xff0c;NAT转换 5.做特殊区域&#xff0c;修改hel…