《DNESP32P4开发指南_V1.0》第二十九章 INFRARED_TRANSMISSION实行

[复制链接]
发表于 2026-6-18 05:00:19 | 显示全部楼层 |阅读模式

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

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

×
第二十九章 INFRARED_TRANSMISSION实行
上一章中,我们通过红外吸收头来吸收遥控器发过来的红外信号。在本章,我们将先容怎样通过红外发射头来发射红外信号,同时来模拟我们的红外遥控器。ESP32-P4开发板上板载一个红外发射头,我们可以通过软件编程来直接实现红外信号的发送,同时我们利用上一章红外吸收实行来实现开发板的红外信号自发自收功能。
本章分为如下几个末节:
29.1 红外发射简介
29.2 硬件计划
29.3 步伐计划
29.4 下载验证
29.1 红外发射简介
在上一章中,我们已经教学了红外遥控器的原理以及ESP32-P4红外遥控(RMT)外设,这里请各人参考上一章的内容举行学习。
29.2 硬件计划
29.2.1 例程功能
在LCD上体现一些信息后,然后等候红外吸收触发解码,在死循环中,我们利用红外发射头发送键值(每100ms加1),同时在LCD上体现当前发送键值与吸收到的键值,来观察自发自收环境。
29.2.2 硬件资源
1)LED灯
LED        0        - IO51
2)RGBLCD / MIPILCD(引脚太多,不摆列出来)
3)红外吸收头
REMOTE_IN        - IO2
4)红外发射头
REMOTE_OUT – IO19(必要在P4端口处用跳线帽将AIN与RMT毗连起来)
29.2.3 原理图
红外吸收头的原理图请参考上一章节的内容,在此不再贴出。红外发射头相干原理图如下图所示。
图29.2.3.1 红外

吸收头原理图
必要留意:REMOTE_OUT并没有直接与ESP32-P4芯片的引脚毗连,而是必要在P4端口处利用跳线帽将REMOTE_OUT和ADC_IN毗连起来,P4端口的原理图及实物毗连如下图所示

图29.2.3.2 P4端口原理图及实物毗连图
ESP32-P4开发板上的红外发射头如下图所示。

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

表29.3.1.1 rmt_new_tx_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_tx_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.     size_t trans_queue_depth;   /* 内部传输队列深度 */
  7.     int intr_priority;          /* 设置中断优先级 */
  8.     struct {
  9.         uint32_t invert_out: 1;                /* 在输出信号传递到RMT发射器前对其进行反转 */
  10.         uint32_t with_dma: 1;             /* 为通道启动DMA后端 */
  11.         uint32_t io_loop_back: 1;         /* 用于调试/测试 */
  12.         uint32_t io_od_mode: 1;           /* 配置GPIO模式为开漏输出 */
  13.     } flags;                              /* 接收通道配置标志 */
  14. } rmt_tx_channel_config_t;
复制代码
rmt_tx_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)trans_queue_depth:
设置内部事件队列深度。队列越深,待处置惩罚队列中可准备的的事件越多。
6)intr_priority:
设置停止优先级。假如设置为0,体系将会利用一个中低优先级的停止。
7)invert_out:
在RMT信号发送到GPIO引脚前翻转RMT信号。
8)with_dma:
为通道启用DMA后端。启动DMA后端可以开释CPU上的大部分通道工作负载,显着减轻CPU负担。
9)io_loop_back:
从GPIO输出的信号也将会被反馈到输入路径。
10) io_od_mode:
设置GPIO为开漏模式。
ret_chan为指向RMT通道句柄结构体指针,rmt_channel_handle_t结构体成员比力多,可以不必要相识。
2,载波调制函数rmt_apply_carrier
该函数用于RMT发射器天生载波信号,并将其调制到消息信号上,其函数原型如下:
  1. esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const
  2. rmt_carrier_config_t *config)
复制代码
函数形参:

表29.3.1.2 rmt_apply_carrier函数形参形貌
函数返回值:
ESP_OK体现设置乐成。
ESP_ERR_INVALID_ARG体现参数有误。
ESP_FAIL体现其他错误。       
channel为RMT通道句柄,在rmt_new_tx_channel函数中创建的。
config为指向载波相干设置结构体指针。接下来,先容rmt_carrier_config_t结构体中各个成员,如下代码所示:
  1. typedef struct {
  2.     uint32_t frequency_hz;                                         /* 载波频率,单位为Hz */
  3.     float duty_cycle;                                              /* 设置载波占空比 */
  4.     struct {
  5.         uint32_t polarity_active_low: 1;         /* 设置载波极性,应用载波的电平 */
  6.         uint32_t always_on: 1;                   /* 是否在数据发送完成后仍输出载波 */
  7.     } flags;                                     /* 载波配置标志 */
  8. } rmt_carrier_config_t;
复制代码
在这里紧张关注frequency_hz和duty_cycle两个成员,若要实现28.1.2中的红外载波信号,这里frequency_hz设置38KHz,而duty_cycle设置为33%。
3,使能RMT发送通道函数rmt_enable
该函数用于使能RMT发送通道,其函数原型如下:
  1. esp_err_t rmt_enable(rmt_channel_handle_t channel);
复制代码
函数形参:

表29.3.1.3 rmt_enable函数形参形貌
函数返回值:
ESP_OK体现读取数据乐成。
ESP_ERR_INVALID_ARG体现参数有误。
ESP_ERR_INVALID_STATE体现通道已经使能。
ESP_FAIL体现其他错误。
留意:发送或吸收RMT符号前,都应先调用rmt_enable。
4,启动RMT发送通道发送任务函数rmt_transmit
该函数用于启动RMT发送通道的发送任务,其函数原型如下:
  1. esp_err_t rmt_transmit(        rmt_channel_handle_t tx_channel,
  2. rmt_encoder_handle_t encoder,
  3. const void *payload,
  4. size_t payload_bytes,
  5. const rmt_transmit_config_t *config);
复制代码
函数形参:

表29.3.1.4 rmt_transmit函数形参形貌
函数返回值:
ESP_OK体现发送数据乐成。
ESP_ERR_INVALID_ARG体现参数有误。
ESP_ERR_INVALID_STATE体现通道未启用。
ESP_ERR_NOT_SUPPORTED体现硬件不支持。
ESP_FAIL体现其他错误。       
tx_channel为RMT通道句柄,在调用rmt_new_tx_channel函数时将返回RMT通道句柄。
encoder为RMT编码器,在例程中由rmt_new_bytes_encoder函数创建,简朴明白就是逻辑0和逻辑1的体现时序,联合28.1.3末节内容明白即可。
payload为要编码成RMT符号的数据,按照RMT编码器的设置去天生。
payload_bytes为payload的数据长度。
config为指向专用于发送的设置结构体指针,rmt_transmit_config_t结构体成员如下所示。
  1. typedef struct {
  2.     int loop_count;                                                 /* 设置发送的循环次数 */
  3.     struct {
  4.         uint32_t eot_level : 1;                 /* 设置发射器完成工作时的输出电平 */
  5.         uint32_t queue_nonblocking : 1;         /* 设置当传输队列满时该函数是否需要等待 */
  6.     } flags;                                    /* 发送特定配置的标志 */
  7. } rmt_transmit_config_t;
复制代码
loop_count设置发送的循环次数。在发射器完成一轮发送后,假如该值未设置为零,则再次启动类似的发送步伐。由于循环由硬件控制,RMT通道可以在险些不必要CPU干预的环境下,天生许多周期性序列。
若loop_count设置为-1,体现会启用无穷循环发送机制,此时,除非手动调用rmt_disable函数,否则通道不会制止,也不会天生“完成发送”变乱。
若loop_count设置为正数,体现迭代次数有限。此时,“完成发送”变乱在指定的迭代次数完成后发生。
eot_level和queue_nonblocking中可利用默认设置即可。
5,创建字节编码器函数rmt_new_bytes_encoder
该函数用于创建字节编码器,将用户控件的字节活动态转换成RMT符号,其函数原型如下:
  1. esp_err_t rmt_new_bytes_encoder(const rmt_bytes_encoder_config_t *config,
  2. rmt_encoder_handle_t *ret_encoder);
复制代码
函数形参:

表29.3.1.5 rmt_new_bytes_encoder函数形参形貌
函数返回值:
ESP_OK体现创建乐成。
ESP_ERR_INVALID_ARG体现参数有误。
ESP_ERR_NO_MEM体现内存不敷。
ESP_FAIL体现其他错误。       
config为指向字节编码器设置结构体指针,rmt_bytes_encoder_config_t结构体成员如下所示。
  1. typedef struct {
  2.     rmt_symbol_word_t bit0;         /* 如何在RMT符号中表示bit0 */
  3.     rmt_symbol_word_t bit1;         /* 如何在RMT符号中表示bit1 */
  4.     struct {
  5.         uint32_t msb_first: 1; /* MSB 高位先发 */
  6.     } flags;                   /* 编码器配置标志 */
  7. } rmt_bytes_encoder_config_t;
复制代码
bit0和bit1都是rmt_symbol_word_t范例的变量,也就是体现逻辑0和逻辑1 的时序构成。rmt_symbol_word_t结构体范例已在前面有分析,这里就不摆列了。
ret_encoder为指向编码器句柄结构体指针,rmt_encoder_handle_t结构体成员如下所示。
  1. struct rmt_encoder_t {
  2.         /* 将用户数据编码为RMT符号并写入RMT内存 */
  3.     size_t (*encode)(rmt_encoder_t *encoder, rmt_channel_handle_t tx_channel, const void *primary_data, size_t data_size, rmt_encode_state_t *ret_state);
  4.     esp_err_t (*reset)(rmt_encoder_t *encoder);        /* 重置编码 */
  5.     esp_err_t (*del)(rmt_encoder_t *encoder);                /* 删除编码对象 */
  6. };
复制代码
rmt_encoder_t界说了编码器的接口。encode是编码器的底子函数,编码会话在此处举行。reset是将编码器重置为初始状态。del可以开释编码器分配的资源。
29.3.2 步伐流程图

图28.3.2.1 红外吸收实行步伐流程图
29.3.3 步伐剖析
在20_infrared_transmission例程中,作者在20_infrared_transmission\components\BSP路径下新建了1个文件夹RMT_TX,而且必要更改CMakeLists.txt内容,以便在其他文件上调用。
1. RMT_TX驱动代码
这里我们只教学焦点代码,详细的源码请各人参考光盘本实行对应源码。RMT_TX驱动源码包罗两个文件:rmt_nec_tx.c、rmt_nec_tx.h、ir_nec_encoder.c和ir_nec_encoder.h。
此中,rmt_nec_tx.h文件负责声明RMT发送相干的宏以及函数声明,rmt_nec_tx.c文件存放用于实现RMT发送初始化函数。ir_nec_encoder.h紧张存放RMT编码器的相干结构体范例以及函数声明,而ir_nec_encoder.c存放的是关于RMT编码器相干的函数接口。
下面先剖析rmt_nec_tx.h的步伐,相识一下在该文件中界说了哪些宏。
  1. #define RMT_TX_PIN                  GPIO_NUM_19           /* 连接RMT_TX的IO */
  2. #define RMT_TX_HZ                   1000000              /* 1MHz 频率, 1 tick = 1us */
复制代码
开发板上利用的是IO19作为RMT发送通道,内部滴答计数器的分辨率设置为1MHz。
接下来,剖析rmt_nec_tx.c步伐。该文件只有RMT发送初始化函数rmt_nec_tx_init,代码如下。
  1. /**
  2. * @brief             RMT红外发送初始化
  3. * @param            无
  4. * @retval           ESP_OK:初始化成功
  5. */
  6. esp_err_t rmt_nec_tx_init(void)
  7. {
  8.     /* 配置发送通道 */
  9.     rmt_tx_channel_config_t tx_channel_cfg = {
  10.         .gpio_num           = RMT_TX_PIN,                 /* RMT发送通道引脚 */
  11.         .clk_src            = RMT_CLK_SRC_DEFAULT,        /* RMT发送通道时钟源 */
  12.         .resolution_hz      = RMT_TX_HZ,                /* RMT发送通道时钟分辨率 */
  13.         .mem_block_symbols  = 64,                       /* 通道一次可存储的RMT符号数量 */
  14.         .trans_queue_depth  = 4,                                          /* 允许在后台挂起的事务数 */
  15. };
  16. /* 创建一个RMT发送通道 */
  17.     ESP_ERROR_CHECK(rmt_new_tx_channel(&tx_channel_cfg, &tx_channel));  
  18.     /* 配置载波与占空比 */
  19.     rmt_carrier_config_t carrier_cfg = {
  20.         .frequency_hz = 38000,                        /* 载波频率,0表示禁用载波 */
  21.         .duty_cycle = 0.33,                            /* 载波占空比33% */
  22. };
  23. /* 对发送信道应用调制功能 */
  24.     ESP_ERROR_CHECK(rmt_apply_carrier(tx_channel, &carrier_cfg));
  25.     /* 不会在循环中发送NEC帧 */
  26.     transmit_config.loop_count = 0;        /* 0为不循环,-1为无限循环 */
  27.     /* 配置编码器 */
  28.     ir_nec_encoder_config_t nec_encoder_cfg = {
  29.         .resolution = RMT_TX_HZ,             /* 编码器分辨率 */
  30. };
  31. /* 配置编码器 */
  32.     ESP_ERROR_CHECK(rmt_new_ir_nec_encoder(&nec_encoder_cfg, &nec_encoder));   
  33.     ESP_ERROR_CHECK(rmt_enable(tx_channel));                 /* 使能发送通道 */
  34.     return ESP_OK;
  35. }
复制代码
在RMT发送初始化函数中,起首对tx_channel_cfg变量的成员举行赋值,设置好红外发送通道的引脚为IO19,RMT时钟源为默认,分辨率为1Mz,通道容量巨细为64和传输队列深度为4,然后调用rmt_new_tx_channel函数创建RMT发送通道。接着,对carrier_cfg变量的成员举行赋值,载波频率设置38K,而载波占空比为33%,然后调用rmt_apply_carrier函数对发送通道应用调制功能。接下来,便是设置RMT编码器,用到乐鑫官方的rmt_new_ir_nec_encoder函数。完成以上操纵以后,末了调用rmt_enable函数使能发送通道,后续便可以通过rmt_transmit函数发送红外数据。
接下来,剖析ir_nec_encoder.c和ir_nec_encoder.h,这两个文件是乐鑫公开的关于RMT编码器的代码。
起首看一下ir_nec_encoder.h文件,相识一下在该文件中界说了两个结构体范例。
  1. typedef struct {
  2.     uint16_t address;                        /* 地址码 */
  3.     uint16_t command;                        /* 命令码 */
  4. } ir_nec_scan_code_t;                         /* NEC扫描码的表达 */
  5. typedef struct {
  6.     uint32_t resolution;                 /* 编码器分辨率 */
  7. } ir_nec_encoder_config_t;                /* NEC编码器配置类型 */
复制代码
ir_nec_scan_code_t结构体范例紧张用来存放RMT帧的内容,而ir_nec_encoder_config_t结构体范例紧张用来存放NEC编码器设置范例。
ir_nec_encoder.c文件中,紧张就是编码器的驱动框架代码,在前面RMT的IDF驱动中有分析到紧张调用rmt_new_bytes_encoder函数实现的。这里先看一下,rmt_nec_tx_init函数中涉及的rmt_new_ir_nec_encoder函数,代码如下。
  1. esp_err_t rmt_new_ir_nec_encoder(const ir_nec_encoder_config_t *config,
  2. rmt_encoder_handle_t *ret_encoder)
  3. {
  4.     esp_err_t ret = ESP_OK;
  5.     rmt_ir_nec_encoder_t *nec_encoder = NULL;
  6. ESP_GOTO_ON_FALSE(config && ret_encoder, ESP_ERR_INVALID_ARG, err, TAG,
  7. "invalid argument");
  8.     nec_encoder = rmt_alloc_encoder_mem(sizeof(rmt_ir_nec_encoder_t));
  9. ESP_GOTO_ON_FALSE(nec_encoder, ESP_ERR_NO_MEM, err, TAG,
  10. "no mem for ir nec encoder");
  11.     nec_encoder->base.encode = rmt_encode_ir_nec;
  12.     nec_encoder->base.del = rmt_del_ir_nec_encoder;
  13.     nec_encoder->base.reset = rmt_ir_nec_encoder_reset;
  14.     rmt_copy_encoder_config_t copy_encoder_config = {};
  15.     ESP_GOTO_ON_ERROR(rmt_new_copy_encoder(&copy_encoder_config,
  16.     &nec_encoder->copy_encoder), err, TAG, "create copy encoder failed");
  17.     // construct the leading code and ending code with RMT symbol format
  18.     nec_encoder->nec_leading_symbol = (rmt_symbol_word_t) {
  19.         .level0 = 1,
  20.         .duration0 = 9000ULL * config->resolution / 1000000,
  21.         .level1 = 0,
  22.         .duration1 = 4500ULL * config->resolution / 1000000,
  23.     };
  24.     nec_encoder->nec_ending_symbol = (rmt_symbol_word_t) {
  25.         .level0 = 1,
  26.         .duration0 = 560 * config->resolution / 1000000,
  27.         .level1 = 0,
  28.         .duration1 = 0x7FFF,
  29.     };
  30.     rmt_bytes_encoder_config_t bytes_encoder_config = {
  31.         .bit0 = {
  32.             .level0 = 1,
  33.             .duration0 = 560 * config->resolution / 1000000, // T0H=560us
  34.             .level1 = 0,
  35.             .duration1 = 560 * config->resolution / 1000000, // T0L=560us
  36.         },
  37.         .bit1 = {
  38.             .level0 = 1,
  39.             .duration0 = 560 * config->resolution / 1000000,  // T1H=560us
  40.             .level1 = 0,
  41.             .duration1 = 1690 * config->resolution / 1000000, // T1L=1690us
  42.         },
  43.     };
  44. ESP_GOTO_ON_ERROR(rmt_new_bytes_encoder(&bytes_encoder_config,
  45. &nec_encoder->bytes_encoder), err, TAG, "create bytes encoder failed");
  46.     *ret_encoder = &nec_encoder->base;
  47.     return ESP_OK;
  48. err:
  49.     if (nec_encoder) {
  50.         if (nec_encoder->bytes_encoder) {
  51.             rmt_del_encoder(nec_encoder->bytes_encoder);
  52.         }
  53.         if (nec_encoder->copy_encoder) {
  54.             rmt_del_encoder(nec_encoder->copy_encoder);
  55.         }
  56.         free(nec_encoder);
  57.     }
  58.     return ret;
  59. }
复制代码
想要明白这个代码逻辑,照旧得联合NEC遥控指令去明白。NEC遥控指令的数据格式:同步船埠、所在码、所在反码、控制码、控制反码、竣事码。也就是编码器必要按照这个NEC遥控指令数据格式发送波形数据。函数中,紧张就是对字节编码、引导码编码和竣事码相干做了设置,反面在函数中调用rmt_new_bytes_encoder函数创建好字节编码器,发送逻辑0和1就会出现符合NEC时序的信号。该函数中会涉及到rmt_encode_ir_nec,rmt_del_ir_nec_encoder和rmt_ir_nec_encoder_reset函数,这些函数都是在ir_nec_encoder.c中实现。
rmt_encode_ir_nec函数用于编码NEC红外信号,此中会根据状态机编码发送引导码、所在码、下令码和竣事码。
rmt_del_ir_nec_encoder函数用于删除或开释一个NEC红外信号编码器的资源。
rmt_ir_nec_encoder_reset函数用于重置NEC红外信号编码器的状态。
以上三个函数,可自行查察,这里就不摆列出来了。红外发送实行代码教学到这里。
2. CMakeLists.txt文件
本例程的功能实现紧张依靠RMT_TX驱动。要在main函数中,乐成调用RMT_RX文件中的内容,就得必要修改BSP文件夹下的CMakeLists.txt文件,修改如下:
  1. set(src_dirs
  2.                    LED
  3.                         LCD
  4.                         RMT_RX
  5.                         RMT_TX)
  6. set(include_dirs
  7.                   LED
  8.                         LCD
  9.                         RMT_RX
  10.                         RMT_TX)
  11. set(requires
  12.                    driver
  13.                    esp_lcd
  14.                         esp_common)
  15. idf_component_register(        SRC_DIRS ${src_dirs} INCLUDE_DIRS ${include_dirs} REQUIRES ${requires})
  16. component_compile_options(-ffast-math -O3 -Wno-error=format=-Wno-format)
复制代码
3. main.c驱动代码
在main.c内里编写如下代码。
  1. void rmt_rx_scan(rmt_symbol_word_t *rmt_nec_symbols, size_t symbol_num)
  2. {
  3.     switch (symbol_num)                  /* 解码RMT接收数据 */
  4.     {
  5.         case 34:                    /* 正常NEC数据帧 */
  6.         {
  7.             if (rmt_nec_parse_frame(rmt_nec_symbols) )
  8.             {
  9.                 lcd_fill(110, 130, 200, 150, WHITE);
  10.                 sprintf((char *)tbuf, "%d", s_nec_code_command);
  11.                 ESP_LOGI(TAG,"RX KEYCNT = %d", s_nec_code_command);
  12.                 lcd_show_string(110, 130, 200, 16, 16, (char *)tbuf, BLUE);
  13.             }
  14.             break;
  15.         }
  16.         case 2:                         /* 重复NEC数据帧 */
  17.         {
  18.             if (rmt_nec_parse_frame_repeat(rmt_nec_symbols))
  19.             {
  20.                 ESP_LOGI(TAG,"RX KEYCNT = %d, repeat", s_nec_code_command);
  21.             }
  22.             break;
  23.         }
  24.         default:                        /* 未知NEC数据帧 */
  25.         {
  26.             ESP_LOGI(TAG,"Unknown NEC frame");
  27.             break;
  28.         }
  29.     }
  30. }
  31. void app_main(void)
  32. {
  33.     esp_err_t ret;
  34.     uint8_t t = 0;
  35.     rmt_rx_done_event_data_t rx_data;
  36.     ret = nvs_flash_init();           /* 初始化NVS */
  37.     if(ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
  38.     {
  39.         ESP_ERROR_CHECK(nvs_flash_erase());
  40.         ESP_ERROR_CHECK(nvs_flash_init());
  41.     }
  42.     led_init();                     /* LED初始化 */
  43.     lcd_init();                     /* LCD屏初始化 */
  44.     rmt_nec_rx_init();              /* 红外接收器件初始化 */
  45.     rmt_nec_tx_init();              /* 红外发送器件初始化 */
  46.     lcd_show_string(30,  50, 200, 16, 16, "ESP32-P4", RED);
  47.     lcd_show_string(30,  70, 200, 16, 16, "REMOTE TEST", RED);
  48.     lcd_show_string(30,  90, 200, 16, 16, "ATOM@ALIENTEK", RED);
  49.     lcd_show_string(30, 110, 200, 16, 16, "TX KEYVAL:", RED);
  50.     lcd_show_string(30, 130, 200, 16, 16, "RX KEYCNT:", RED);
  51.     while (1)
  52.     {
  53.         if (xQueueReceive(receive_queue,&rx_data,pdMS_TO_TICKS(1000)) == pdPASS)
  54.         {
  55.                         /* 解析接收符号并打印结果 */
  56.             rmt_rx_scan(rx_data.received_symbols, rx_data.num_symbols);                                    
  57.             ESP_ERROR_CHECK(rmt_receive(rx_channel, raw_symbols,
  58.             sizeof(raw_symbols), &receive_config));    /* 重新开始接收 */
  59.         }
  60.         else        /* 超时,没有消息到消息队列,传输预定义的IR NEC数据包 */                                                                                                            
  61.         {
  62.             t++;
  63.             if (t == 0)
  64.             {
  65.                 t = 1;
  66.             }
  67.             const ir_nec_scan_code_t scan_code = {
  68.                 .command = t,
  69.             };
  70.             lcd_fill(110, 110, 200, 150, WHITE);
  71.             sprintf((char *)tbuf, "%d", scan_code.command);
  72.             ESP_LOGI(TAG, "TX KEYVAL = %d", scan_code.command);
  73.             lcd_show_string(110, 110, 200, 16, 16, (char *)tbuf, BLUE);
  74.             ESP_ERROR_CHECK(rmt_transmit(tx_channel, nec_encoder, &scan_code,
  75.             sizeof(scan_code), &transmit_config));        /* 通过RMT发送信道传输数据 */
  76.         }
  77.    
  78.         vTaskDelay(pdMS_TO_TICKS(10));
  79.     }
  80. }
复制代码
rmt_rx_scan函数用来扫描解码效果的,相当于我们的按键扫描函数。相对红外发送实行,该函数缺少了数据剖析功能,由于这时间用的并不是红外遥控器发送红外数据。
在app_main函数,调用rmt_nec_rx_init和rmt_nec_tx_init函数初始化RMT发送和吸收之后,在while循环中消息队列有数据,便可调用rmt_tx_scan对数据举行剖析并在LCD上体现。剖析完数据后,还必要继承调用rmt_receive函数重新开始吸收数据。若消息队列没有数据时,便通过rmt_transmit函数发送红外数据,数据范围为1~255,LCD也会体现发送的数据。
29.4 下载验证
在代码编译乐成后,下载代码到开发板上,可以看到LCD体现如下图所示。

图29.4.1 红外发送实行测试图
我们可以看到开发板发送的红外信号全部被红外吸收头吸收到,分析我们已经实现了开发板自发自收红外信号的功能。温馨提示:由于开发板的红外吸收头和红外发射头没有正对,有大概会造成吸收不到数据环境,这种环境下我们必要一个物体提供反射面,最简朴的做法就是将手放在传感器正火线约莫10cm左右的位置。
回复

使用道具 举报

登录后关闭弹窗

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