《DNESP32P4开发指南_V1.0》第二十八章 INFRARED_RECEPTION实验

[复制链接]
发表于 2026-6-17 07:31:11 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
第二十八章 INFRARED_RECEPTION实验
本章将先容ESP32-P4对红外遥控器的信号解码。ESP32-P4板子上标配的红外吸收头和一个小巧的红外遥控器。我们将使用管脚输入功能,解码开发板标配的红外遥控器的编码信号,并将编码后的键值在LCD屏中表现出来。
本章分为如下几个末节:
28.1 红外遥控先容
28.2 硬件计划
28.3 步伐计划
28.4 下载验证
28.1 红外遥控简介
28.1.1 红外遥控技能先容
红外遥控是一种无线、非打仗控制技能,具有抗干扰本领强,信息传输可靠,功耗低,本钱低,易实现等显着优点,被诸多电子装备特别是家用电器广泛接纳,并越来越多的应用到盘算机体系中。 由于红外线遥控不具有像无线电遥控那样穿过停滞物去控制被控对象的本领,以是,在计划红外线遥控器时,不须要像无线电遥控器那样,每套(发射器和吸收器)要有差别的遥控频率或编码(否则,就会隔墙控制或干扰邻人的家用电器),以是同类产物的红外线遥控器,可以有类似的遥控频率或编码,而不会出现遥控信号“串门”的环境。这对于大批量生产以及在家用电器上遍及红外线遥控提供了极大的方便。由于红外线为不可见光,因此对环境影响很小,再由红外光颠簸波长远小于无线电波的波长,以是红外线遥控不会影响其他家用电器,也不会影响邻近的无线电装备。
28.1.2 红外器件特性
红外遥控的情形中,肯定会有一个红外发射端和红外吸收端。在本实验中,正点原子的红外遥控器作为红外发射端,红外吸收端就是板载的红外吸收器,实物图可以检察28.2.3末节原理图部门。要使两者通讯乐成,收/发红外波长与载波频率需同等,在这里波长就是940nm,载波频率就是38kHz。
红外发射管也是属于二极管类,红外发射电路通常使用三极管控制红外发射器的导通大概制止,在导通的时间,红外发射管会发射出红外光,反之,就不会发射出红外光。固然我们用肉眼看不到红外光,但是我们借助手机摄像头就能看到红外光。但是红外吸收管的特性是当吸收到红外载波信号时,OUT引脚输出低电平;如果没有吸收到红外载波信号时,OUT引脚输出高电平。
红外载波信号着实就是由一个个红外载波周期构成。在频率为38KHz下,红外载波周期约便是26.3us(1s / 38KHz ≈ 26.3us)。在一个红外载波发射周期里,发射红外光时间8.77us和不发射红外光17.53us,发射红外光的占空比一样平常为1/3。相对的,整个周期内不发射红外光,就是载波不发射周期。在红外遥控器内已经把载波和不载波信号处理处罚好,我们须要做的就是辨认遥控器按键发射出的信号,信号也是遵照某种协议的。
28.1.3 红外编解码协议先容
红外遥控的编码方式现在广泛使用的是:PWM(脉冲宽度调制)的NEC协媾和Philips PPM(脉冲位置调制)的RC-5协议的。开发板配套的遥控器使用的是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
红外发射器的位界说如下图所示。

图28.1.3.1 红外发射器位界说图
红外吸收器:吸收到协议数据‘0’ = 560us低电平 + 560us高电平
吸收到协议数据‘1’ = 560us低电平 + 1680us高电平
红外吸收器的位界说如下图所示。

图28.1.3.2 红外吸收器位界说图
NEC遥控指令的数据格式为:同步船埠、所在码、所在反码、控制码、控制反码。同步码由一个9ms的低电平和一个4.5ms的高电平构成,所在码、所在反码、控制码、控制反码均是8位数据格式。按照低位在前,高位在后的序次发送。接纳反码是为了增长传输的可靠性(可用于校验)。
我们遥控器的按键“ALIENTEK”按下时,从红外吸收头端收到的波形如下图所示。

图28.1.3.3 按键“ALIENTEK”所对应的红外波形
从上图中可以看到,其所在码为0,控制码为21(精确解码后00010101)。可以看到在100ms之后,我们还收到了几个脉冲,这是NEC码规定的连发码(由9ms低电平+2.25ms高电平+0.56ms低电平+97.94ms高电平构成),如果在一帧数据发送完毕之后,按键仍然没有放开,则发射重复码,即连发码可以通过统计连发码的次数来标记按键按下的黑白/次数。
28.1.4 ESP32-P4红外遥控(RMT)外设先容
红外遥控(RMT)是一个红外发送和吸收控制器。各种红外控制协议都可通过RMT外设举行编码/解码。就网络分层而言,RMT硬件包罗物理层和数据链路层。物理层界说通讯介质和比特信号的表现方式,数据链路层界说RMT帧的格式。RMT帧的最小数据单位称为RMT符号。
ESP32-P4的RMT外设有8个通道,编号从0到7。每个通道都可独立运行,通道03(TX通道)专用于传输信号,通道47(RX通道)专用于吸收信号。
ESP32-P4发送通道支持:正常发送模式、乒乓发送模式、连续发送模式、发送脉冲调制、可编程多通道同时传输数据、发送通道3支持GDMA。
ESP32-P4吸收通道支持:正常吸收模式、乒乓吸收模式、吸收过滤器、吸收脉冲解调、吸收通道7支持GDMA。
接下来,看一下RMT外设的结构框图,如下图所示。

图28.1.4.1 RMT外设结构框图
每个发送通道(SEND_CHn)内部各有:一个时钟分频计数器(Div Counter)、一个状态机(FSM)和一个发射器(Transmitter)。每个吸收通道(RECV_CHm)内部也各有:一个时钟分频计数器(Div Counter)、一个状态机(FSM)和一个吸收器(Receiver)。八个通道共享用一块384x32位的RAM。
简单来说,RMT外设可以实现将外设内置RAM中的脉冲编码转换成信号通过发送通道输出,或将外设的输入信号通过吸收通道转换为脉冲编码存入RAM中。
接下来,通过一个简图更加清晰相识RMT发送通道的数据路径和控制路径,如下图所示。

图28.1.4.2 RMT发送通道的数据路径和控制路径
驱动步伐将用户数据编码为RMT数据格式,随后由RMT发射器根据编码天生波形。在将波形发生GPIO管脚前,还可以调制高频载波信号。
同样的,也来相识一下RMT吸收通道的数据路径和控制路径,如下图所示。

图28.1.4.3 RMT吸收通道的数据路径和控制路径
RMT吸收器可以对输入信号采样,将其转换为RMT数据格式,并将数据存储在内存中。还可以向吸收器提供输入信号的根本特性,使其辨认信号制止条件,并过滤信号干扰和噪声。RMT外设还支持从基准信号中解调出高频载波信号。
RAM中脉冲编码结构如下图所示。

图28.1.4.4 RAM中脉冲编码结构
每个脉冲编码为16位,由level和period两部门构成。此中level表现输入或输出信号的逻辑电平值(0或1),period表现该电平信号连续的时钟周期数。period的最小值为0,period为0是一次传输的竣事标记。
28.2 硬件计划
28.2.1 例程功能
在LCD上表现一些实验信息后,进入等候红外触发,如果吸收到精确的红外信号,则解码,并在LCD上表现键值和所代表的意义。LED0闪烁用于提示步伐正在运行。
28.2.2 硬件资源
1)LED灯
LED        0        - IO51
2)RGBLCD / MIPILCD(引脚太多,不摆列出来)
3)红外吸收头
REMOTE_IN        - IO2
4)正点原子红外遥控器
28.2.3 原理图
红外吸收头相干原理图如下图所示。

图28.2.3.1 红外吸收头原理图
开发板配套的红外遥控器表面如图28.2.3.2所示:

图28.2.3.2 红外遥控器
ESP32-P4开发板底板上吸收红外遥控器信号的红外管表面如图28.2.3.3所示。使用时须要遥控器有红外管的一端对准开发板上的红外管才华精确收到信号。

图28.2.3.3 底板上的红外吸收管位置
28.3 步伐计划
28.3.1 RMT的IDF驱动
RMT外设驱动位于ESP-IDF下的components/esp_driver_rmt目次下。使用RMT功能,必须先导入以下头文件:
  1. #include "driver/rmt_rx.h"
复制代码
本例程告急实现的是RMT的吸收功能。接下来,作者将先容一些常用的函数,这些函数的形貌及其作用如下:
1,创建RMT吸收通道函数rmt_new_rx_channel
该函数用于创建RMT吸收通道,其函数原型如下:
  1. esp_err_t rmt_new_rx_channel(const rmt_rx_channel_config_t *config,
  2.                                                           rmt_channel_handle_t *ret_chan);
复制代码
函数形参:

表28.3.1.1 rmt_new_rx_channel函数形参形貌
函数返回值:
ESP_OK表现创建RMT吸收通道乐成。
ESP_ERR_INVALID_ARG表现错误参数。
ESP_ERR_NO_MEM表现内存不敷。
ESP_ERR_NOT_FOUND表现全部吸收通道已经用完。
ESP_ERR_NOT_SUPPORTED表现某些功能不被硬件支持,好比DMA。
ESP_FAIL表现其他错误。
config为指向RMT吸收通道设置结构体指针。接下来,笔者将先容rmt_rx_channel_config_t结构体中各个成员,如下代码所示:
  1. typedef struct {
  2.     gpio_num_t gpio_num;        /* 接收器使用的GPIO编号 */
  3.     rmt_clock_source_t clk_src; /* RMT通道的时钟源 */
  4.     uint32_t resolution_hz;     /* 内部滴答计数器的分辨率 */
  5.     size_t mem_block_symbols;   /* 专用内存块大小(启动DMA时,内部DMA缓冲区大小) */
  6.     int intr_priority;          /* 设置中断优先级 */
  7.     struct {
  8.         uint32_t invert_in: 1;    /* 在输入信号传递到RMT接收器前对其进行反转 */
  9.         uint32_t with_dma: 1;     /* 为通道启动DMA后端 */
  10.         uint32_t io_loop_back: 1; /* 用于调试/测试 */
  11.     } flags;                      /* 接收通道配置标志 */
  12. } rmt_rx_channel_config_t;
复制代码
rmt_rx_channel_config_t结构体用于设置RMT吸收通道参数,以下对各个成员做简单先容。
1)gpio_num:
设置吸收器使用的GPIO编号。
2)clk_src:
选择RMT通道时钟源,可选RMT_CLK_SRC_APB或RMT_CLK_SRC_REF_TICK,也可以选择RMT_CLK_SRC_DEFAULT,默认指的RMT_CLK_SRC_APB。注意:其他通道须要使用同一所选的时钟源。
3)resolution_hz:
设置内部滴答计数器的分辨率。基于此滴答,可盘算出RMT信号的定时参数。
4)mem_block_symbols:
在启动DMA后端和未启用DMA后端,寄义稍有差别。若with_dma启用DMA,这里的寄义是最大化控制内部DMA缓冲区巨细。若未启用DMA,这里的寄义是控制通道专用内存块巨细,至少为48。
5)intr_priority:
设置制止优先级。如果设置为0,体系将会使用一个中低优先级的制止。
6)invert_in:
在输入信号通报到RMT吸收器前对其举行反转。该反转由GPIO交换矩阵完成,而非RMT外设。
7)with_dma:
为通道启用DMA后端。启动DMA后端可以开释CPU上的大部门通道工作负载,显着减轻CPU负担。
8)io_loop_back:
从GPIO输出的信号也将会被反馈到输入路径。
ret_chan为指向RMT通道句柄结构体指针,rmt_channel_handle_t结构体成员比力多,可以不须要相识。
2,设置RMT吸收通道回调函数rmt_rx_register_event_callbacks
该函数用于设置RMT吸收通道的回调函数,其函数原型如下所示:
  1. esp_err_t rmt_rx_register_event_callbacks(rmt_channel_handle_t rx_channel,                                                                                           const rmt_rx_event_callbacks_t *cbs,                                                                                    void *user_data);
复制代码
函数形参:

表28.3.1.2 rmt_rx_register_event_callbacks函数形参形貌
函数返回值:
ESP_OK表现设置乐成。
ESP_ERR_INVALID_ARG表现错误参数。
ESP_FAIL表现
rx_channel为RMT通道句柄,在调用rmt_new_rx_channel函数时将返回RMT通道句柄。
cbs为指向RMT吸收通道的回调函数指针,在RMT通道吸收完成时调用。RMT通道吸收回调函数的格式如下:
  1. typedef bool (*rmt_rx_done_callback_t)(rmt_channel_handle_t rx_chan, const rmt_rx_done_event_data_t *edata, void *user_ctx);
  2. 在编写回调函数时需要注意格式一致。这里会涉及到rmt_rx_done_event_data_t结构体类型,定义如下:
  3. typedef struct {
  4.     rmt_symbol_word_t *received_symbols;         /* 指向接收到的RMT符号 */
  5.     size_t num_symbols;                          /* 接收到的RMT符号的数量 */
  6.     struct {
  7.         uint32_t is_last: 1;                                 /* 当前接收到的数据是否是事务的最后一部分 */
  8.     } flags;                                                 /* 额外标志 */
  9. } rmt_rx_done_event_data_t;
复制代码
须要注意两个成员便可,一个是received_symbols,另一个是num_symbols。吸收到的RMT数据将会存放在rmt_rx_done_event_data_t结构体范例的received_symbols成员中,而吸收到的数据长度便在num_symbols成员中体现。received_symbols成员又是一个结构体范例,界说如下:
  1. typedef union {
  2.     struct {
  3.         uint16_t duration0 : 15;         /* 第一部分的持续时间 */
  4.         uint16_t level0 : 1;             /* 第一部分的电平状态 */
  5.         uint16_t duration1 : 15;         /* 第二部分的持续时间 */
  6.         uint16_t level1 : 1;             /* 第二部分的电平状态 */
  7.     };
  8.     uint32_t val;                                         /* 逻辑值 */
  9. } rmt_symbol_word_t;
复制代码
user_data为RMT吸收通道的回调函数通报的参数。
3,使能RMT吸收通道函数rmt_enable
该函数用于使能RMT吸收通道,其函数原型如下:
  1. esp_err_t rmt_enable(rmt_channel_handle_t channel);
复制代码
函数形参:

表28.3.1.3 rmt_enable函数形参形貌
函数返回值:
ESP_OK表现读取数据乐成。
ESP_ERR_INVALID_ARG表现参数有误。
ESP_ERR_INVALID_STATE表现通道已经使能。
ESP_FAIL表现其他错误。
4,启动RMT吸收通道吸收任务函数rmt_receive
该函数用于启动RMT吸收通道的吸收任务,其函数原型如下:
  1. esp_err_t rmt_receive(rmt_channel_handle_t rx_channel,
  2.                                            void *buffer,
  3.                                            size_t buffer_size,
  4.                                            const rmt_receive_config_t *config);
复制代码
函数形参:

表28.3.1.4 rmt_receive函数形参形貌
函数返回值:
ESP_OK表现读取数据乐成。
ESP_ERR_INVALID_ARG表现参数有误。
ESP_ERR_INVALID_STATE表现通道未启用。
ESP_FAIL表现其他错误。       
rx_channel为RMT通道句柄,在调用rmt_new_rx_channel函数时将返回RMT通道句柄。
buffer为用于存储吸收到的RMT符号的缓冲区,须要注意其数据范例,例程中设置其数据范例设置为rmt_symbol_word_t,且长度为64,可存储一个标准NEC帧的数据。
buffer_size为缓冲区巨细。如果该值不敷以容纳全部吸收到的RMT符号,步伐只会生存缓冲区可以大概容纳的最大数量的符号并扬弃或忽略多以的符号。
config为指向RMT吸收特定设置结构体指针,rmt_receive_config_t结构体成员如下所示。
  1. typedef struct {
  2.     uint32_t signal_range_min_ns;         /* 高电平或低电平有效脉冲的最小持续时间 */
  3.     uint32_t signal_range_max_ns;        /* 高电平或低电平有效脉冲的最大持续时间 */
  4.    
  5.     struct extra_flags {
  6.         uint32_t en_partial_rx: 1; /* 开启部分接收功能,缓冲区快满时多次进入回调函数 */
  7.     } flags;                       
  8. } rmt_receive_config_t;
复制代码
仅调用rmt_enable函数,RX通道是无法吸收RMT符号的,还须要通过rmt_receive_config_t去指明传入信号的根本特性。
28.3.2 步伐流程图

图28.3.2.1 红外吸收实验步伐流程图
28.3.3 步伐分析
在19_infrared_reception例程中,作者在19_infrared_reception\components\BSP路径下新建了1个文件夹RMT_RX,而且须要更改CMakeLists.txt内容,以便在其他文件上调用。
1. RMT_RX驱动代码
这里我们只解说焦点代码,详细的源码请各人参考光盘本实验对应源码。RMT_RX驱动源码包罗两个文件:rmt_nec_rx.c和rmt_nec_rx.h。
此中,rmt_nec_rx.h文件负责声明RMT吸收相干的函数以及界说一些宏,rmt_nec_rx.c存放用于实现RMT吸收的驱动代码。
下面先分析rmt_nec_rx.h的步伐,相识一下在该文件中界说了哪些宏。
  1. #define RMT_IN_GPIO_PIN                 GPIO_NUM_23 /* 连接RMT_RX_IN的端口 */
  2. #define RMT_RESOLUTION_HZ               1000000     /* 1MHz 频率, 1 tick = 1us */
  3. /* 判断NEC时序时长的容差值,小于(值+此值),大于(值-此值)为正确 */
  4. #define RMT_NEC_DECODE_MARGIN           200         
  5. /* NEC协议时序时间 */
  6. #define NEC_LEADING_CODE_DURATION_0     9000
  7. #define NEC_LEADING_CODE_DURATION_1     4500
  8. #define NEC_PAYLOAD_ZERO_DURATION_0     560
  9. #define NEC_PAYLOAD_ZERO_DURATION_1     560
  10. #define NEC_PAYLOAD_ONE_DURATION_0      560
  11. #define NEC_PAYLOAD_ONE_DURATION_1      1690
  12. #define NEC_REPEAT_CODE_DURATION_0      9000
  13. #define NEC_REPEAT_CODE_DURATION_1      2250
复制代码
开发板上使用的是IO23作为RMT通道,内部滴答计数器的分辨率设置为1MHz以及NEC时序时长偏差范围。另有NEC协议内容,其协议中的时序单位时间黑白,好比协议头(同步码)为9ms低电平 + 4.5ms高电平,逻辑0为560us低电平 + 560us高电平,逻辑1为560us低电平 + 1690us高电平,重复码为9ms电平+2.25ms高电平。后续在rmt_nec_rx.c中的解码函数中便能发现使用这些宏做判断,解码出NEC协议内容。
接下来,分析rmt_nec_rx.c步伐。起首看RMT吸收初始化函数rmt_nec_rx_init,代码如下。
  1. /**
  2. * @brief             RMT红外接收初始化
  3. * @param                无
  4. * @retval           ESP_OK:初始化成功
  5. */
  6. esp_err_t rmt_nec_rx_init(void)
  7. {   /* 配置接收通道 */
  8.     rmt_rx_channel_config_t rx_channel_cfg = {
  9.         .gpio_num           = RMT_IN_GPIO_PIN,            /* 设置红外接收通道管脚 */
  10.         .clk_src            = RMT_CLK_SRC_DEFAULT,        /* 设置RMT时钟源 */
  11.         .resolution_hz      = RMT_RESOLUTION_HZ,        /* 设置时钟分辨率 */
  12.         .mem_block_symbols  = 64,                        /* 通道一次可以存储的符号数量 */
  13. };
  14. /* 创建接收通道 */
  15.     ESP_ERROR_CHECK(rmt_new_rx_channel(&rx_channel_cfg, &rx_channel));  
  16. /* 创建消息队列,用于接收红外编码 */
  17. receive_queue = xQueueCreate(1, sizeof(rmt_rx_done_event_data_t));  
  18. assert(receive_queue);
  19. /* 配置红外接收完成回调 */
  20.     rmt_rx_event_callbacks_t cbs = {
  21.         .on_recv_done = rmt_nec_rx_done_callback,        /* RMT信号接收完成回调函数 */
  22.     };
  23. ESP_ERROR_CHECK(rmt_rx_register_event_callbacks(rx_channel, &cbs,
  24. receive_queue));                                  /* 配置RMT接收通道回调函数 */
  25. /* NEC协议的时序要求 */
  26. /* NEC信号的最短持续时间为560us,1250ns<560us,有效信号不会被视为噪声 */
  27. receive_config.signal_range_min_ns = 1250;               
  28. /* NEC信号的最长持续时间为9000us,12000000ns>9000us,接收不会提前停止 */
  29.     receive_config.signal_range_max_ns = 12000000;       
  30.     /* 开启RMT通道 */
  31.     ESP_ERROR_CHECK(rmt_enable(rx_channel));                         /* 使能RMT接收通道 */
  32. ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols, sizeof(raw_symbols),
  33. &receive_config));                /* 准备接收 */
  34.     return ESP_OK;
  35. }
复制代码
在RMT吸收初始化函数中,起首对rx_channel_cfg变量的成员举行赋值,设置好红外吸收通道的引脚为IO23,RMT时钟源为默认,分辨率为1Mz以及通道容量巨细为64,然后调用rmt_new_rx_channel函数创建RMT吸收通道。注册红外吸收回调函数rmt_nec_rx_done_callback用于获取吸收到的RMT数据,通过消息队列方式去通报RMT数据。背面对receive_config结构体成员举行赋值,设置NEC协议的时序要求。末了再调用rmt_enable函数使能RMT吸收通道以及调用rmt_receive函数举行数据吸收。当RMT吸收通道吸收数据完成后,便进入红外吸收回调函数中。
这里注意:rmt_nec_rx.c界说了比力多的全局变量,告急目的是方便其他文件调用。
紧接着看一下,红外吸收回调函数rmt_nec_rx_done_callback,代码如下:
  1. /**
  2. * @brief              RMT数据接收完成回调函数
  3. * @param            channel   : 通道
  4. * @param              edata     : 接收的数据
  5. * @param             user_data : 传入的参数
  6. * @retval           返回是否唤醒了任何任务
  7. */
  8. bool rmt_nec_rx_done_callback(rmt_channel_handle_t channel, const rmt_rx_done_event_data_t *edata, void *user_data)
  9. {
  10.     BaseType_t high_task_wakeup = pdFALSE;
  11. QueueHandle_t receive_queue = (QueueHandle_t)user_data;
  12. /* 将收到的RMT数据通过消息队列发送到解析任务 */
  13.     xQueueSendFromISR(receive_queue, edata, &high_task_wakeup);
  14.     return high_task_wakeup == pdTRUE;
  15. }
复制代码
该函数告急实现的功能是将RMT吸收通道吸收到的数据edata通过在rmt_nec_rx_init函数中创建的消息队列发送出去,而在app_main函数中举行获取分析。
末了先容NEC协议分析函数rmt_nec_parse_frame,代码如下:
[code]/** * @brief            将RMT吸收效果解码出NEC所在和下令 * @param            rmt_nec_symbols:RMT数据帧 * @retval            true乐成;false失败 */bool rmt_nec_parse_frame(rmt_symbol_word_t *rmt_nec_symbols){    rmt_symbol_word_t *cur = rmt_nec_symbols;    uint16_t address = 0;    uint16_t command = 0;bool valid_leading_code = rmt_nec_check_range(cur->duration0, NEC_LEADING_CODE_DURATION_0) &&                              rmt_nec_check_range(cur->duration1, NEC_LEADING_CODE_DURATION_1);    if (!valid_leading_code)     {        return false;    }    cur++;    for (int i = 0; i < 16; i++)    {        if (rmt_nec_logic1(cur))         {            address |= 1
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表