feature(ir): add ir tx rx driver

This commit is contained in:
xiongyu
2019-08-20 16:34:46 +08:00
parent 287a2fcbd3
commit 428f2f3b1a
18 changed files with 1213 additions and 2 deletions

View File

@ -44,7 +44,9 @@ else()
"driver/i2s.c"
"driver/pwm.c"
"driver/spi.c"
"driver/uart.c")
"driver/uart.c"
"driver/ir_tx.c"
"driver/ir_rx.c")
set(include_dirs "include" "include/driver")

View File

@ -159,7 +159,7 @@ uint32_t hw_timer_get_count_data()
return frc1.count.data;
}
static void hw_timer_isr_cb(void* arg)
static void IRAM_ATTR hw_timer_isr_cb(void* arg)
{
if (!frc1.ctrl.reload) {
frc1.ctrl.en = 0;

View File

@ -0,0 +1,271 @@
// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/ir_rx.h"
static const char *TAG = "ir rx";
#define IR_RX_CHECK(a, str, ret_val) \
if (!(a)) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
typedef struct {
uint32_t io_num;
uint32_t buf_len;
SemaphoreHandle_t recv_mux;
RingbufHandle_t ring_buf; /*!< rx ring buffer handler*/
} ir_rx_obj_t;
ir_rx_obj_t *ir_rx_obj = NULL;
typedef enum {
IR_RX_IDLE,
IR_RX_HEADER,
IR_RX_DATA,
IR_RX_REP,
} ir_rx_state_t;
/**
* @brief ir rx state machine via gpio intr
*/
static void IRAM_ATTR ir_rx_intr_handler(void *arg)
{
static int ir_state = IR_RX_IDLE;
static int ir_repeat = 0;
static ir_rx_nec_data_t ir_data = {0};
static int cnt = 0;
static uint8_t rep_flg = 0;
static uint32_t time_last = 0;
BaseType_t xHigherPriorityTaskWoken;
uint32_t time_escape, time_current;
struct timeval now;
gettimeofday(&now, NULL);
time_current = now.tv_sec * 1000 * 1000 + now.tv_usec;
time_escape = time_current - time_last;
time_last = time_current;
switch (ir_state) {
case IR_RX_IDLE: {
if (time_escape < IR_RX_NEC_HEADER_US + IR_RX_ERROR_US && time_escape > IR_RX_NEC_HEADER_US - IR_RX_ERROR_US) {
ir_state = IR_RX_DATA;
}
}
break;
case IR_RX_DATA: {
if (time_escape < IR_RX_NEC_DATA1_US + IR_RX_ERROR_US && time_escape > IR_RX_NEC_DATA1_US - IR_RX_ERROR_US) {
ir_data.val = (ir_data.val >> 1) | (0x1 << (IR_RX_NEC_BIT_NUM * 4 - 1));
cnt++;
} else if (time_escape < IR_RX_NEC_DATA0_US + IR_RX_ERROR_US && time_escape > IR_RX_NEC_DATA0_US - IR_RX_ERROR_US) {
ir_data.val = (ir_data.val >> 1) | (0x0 << (IR_RX_NEC_BIT_NUM * 4 - 1));
cnt++;
} else {
goto reset_status;
}
if (cnt == IR_RX_NEC_BIT_NUM * 4) {
// push rcv data to ringbuf
xRingbufferSendFromISR(ir_rx_obj->ring_buf, (void *) &ir_data, sizeof(ir_rx_nec_data_t) * 1, &xHigherPriorityTaskWoken);
ir_state = IR_RX_REP;
rep_flg = 0;
}
}
break;
case IR_RX_REP: {
if (rep_flg == 0) {
if (time_escape > IR_RX_NEC_TM1_REP_US && time_escape < IR_RX_NEC_TM1_REP_US * 8) {
rep_flg = 1;
} else {
goto reset_status;
}
} else if (rep_flg == 1) {
if (time_escape < IR_RX_NEC_TM1_REP_US + IR_RX_ERROR_US && IR_RX_NEC_TM2_REP_US - IR_RX_ERROR_US) {
// push rcv data to ringbuf
xRingbufferSendFromISR(ir_rx_obj->ring_buf, (void *) &ir_data, sizeof(ir_rx_nec_data_t) * 1, &xHigherPriorityTaskWoken);
ir_repeat++;
rep_flg = 0;
} else {
goto reset_status;
}
}
}
break;
}
if (xHigherPriorityTaskWoken == pdTRUE) {
taskYIELD();
}
return;
reset_status:
ir_state = IR_RX_IDLE;
cnt = 0;
ir_data.val = 0;
ir_repeat = 0;
rep_flg = 0;
}
esp_err_t ir_rx_disable()
{
IR_RX_CHECK(ir_rx_obj, "ir rx not been initialized yet", ESP_FAIL);
gpio_isr_handler_remove(ir_rx_obj->io_num);
return ESP_OK;
}
esp_err_t ir_rx_enable()
{
IR_RX_CHECK(ir_rx_obj, "ir rx not been initialized yet", ESP_FAIL);
gpio_isr_handler_add(ir_rx_obj->io_num, ir_rx_intr_handler, (void *) ir_rx_obj->io_num);
return ESP_OK;
}
int ir_rx_recv_data(ir_rx_nec_data_t *data, size_t len, uint32_t timeout_ticks)
{
IR_RX_CHECK(ir_rx_obj, "ir rx not been initialized yet", ESP_FAIL);
int ret;
ir_rx_nec_data_t *buf = NULL;
size_t size = 0;
uint32_t ticks_escape = 0, ticks_last = 0;
struct timeval now;
if (timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_last = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS;
}
ret = xSemaphoreTake(ir_rx_obj->recv_mux, timeout_ticks);
if (ret != pdTRUE) {
IR_RX_CHECK(false, "SemaphoreTake error", -1);
}
if (timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_escape = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS - ticks_last;
if (timeout_ticks <= ticks_escape) {
xSemaphoreGive(ir_rx_obj->recv_mux);
IR_RX_CHECK(false, "timeout", -1);
} else {
timeout_ticks -= ticks_escape;
}
}
for (int x = 0; x < len;) {
buf = (ir_rx_nec_data_t *) xRingbufferReceive(ir_rx_obj->ring_buf, &size, timeout_ticks);
if (buf == NULL) {
xSemaphoreGive(ir_rx_obj->recv_mux);
IR_RX_CHECK(false, "RingbufferReceive error", -1);
}
memcpy(&data[x], buf, size);
vRingbufferReturnItem(ir_rx_obj->ring_buf, buf);
x += size;
if (timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_escape = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS - ticks_last;
if (timeout_ticks <= ticks_escape) {
xSemaphoreGive(ir_rx_obj->recv_mux);
IR_RX_CHECK(false, "timeout, return the actual accepted length", x);
} else {
timeout_ticks -= ticks_escape;
}
}
}
xSemaphoreGive(ir_rx_obj->recv_mux);
return len;
}
static esp_err_t ir_rx_gpio_init(uint32_t io_num)
{
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_NEGEDGE;
io_conf.pin_bit_mask = 1ULL << io_num;
io_conf.mode = GPIO_MODE_INPUT;
io_conf.pull_up_en = 1;
gpio_config(&io_conf);
gpio_install_isr_service(0);
return ESP_OK;
}
esp_err_t ir_rx_deinit()
{
IR_RX_CHECK(ir_rx_obj, "ir rx has not been initialized yet.", ESP_FAIL);
ir_rx_disable();
if (ir_rx_obj->ring_buf) {
vRingbufferDelete(ir_rx_obj->ring_buf);
ir_rx_obj->ring_buf = NULL;
}
if (ir_rx_obj->recv_mux) {
vSemaphoreDelete(ir_rx_obj->recv_mux);
ir_rx_obj->recv_mux = NULL;
}
heap_caps_free(ir_rx_obj);
ir_rx_obj = NULL;
return ESP_OK;
}
esp_err_t ir_rx_init(ir_rx_config_t *config)
{
IR_RX_CHECK(config, "config error", ESP_ERR_INVALID_ARG);
IR_RX_CHECK(NULL == ir_rx_obj, "ir rx has been initialized", ESP_FAIL);
ir_rx_obj = heap_caps_malloc(sizeof(ir_rx_obj_t), MALLOC_CAP_8BIT);
IR_RX_CHECK(ir_rx_obj, "ir rx object malloc error", ESP_ERR_NO_MEM);
ir_rx_obj->io_num = config->io_num;
ir_rx_obj->buf_len = config->buf_len;
ir_rx_obj->ring_buf = xRingbufferCreate(sizeof(ir_rx_nec_data_t) * ir_rx_obj->buf_len, RINGBUF_TYPE_NOSPLIT);
ir_rx_obj->recv_mux = xSemaphoreCreateMutex();
if (NULL == ir_rx_obj->ring_buf || NULL == ir_rx_obj->recv_mux) {
ir_rx_deinit();
IR_RX_CHECK(false, "Ringbuffer or Mutex create fail", ESP_ERR_NO_MEM);
}
// gpio configure for IR rx pin
ir_rx_gpio_init(ir_rx_obj->io_num);
ir_rx_enable();
return ESP_OK;
}

View File

@ -0,0 +1,421 @@
// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/ir_tx.h"
#include "driver/hw_timer.h"
#include "driver/i2s.h"
#include "esp8266/gpio_struct.h"
static const char *TAG = "ir tx";
#define IR_TX_CHECK(a, str, ret_val) \
if (!(a)) { \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
typedef enum {
TX_BIT_CARRIER,
TX_BIT_LOW,
} ir_tx_bit_state_t;
typedef enum {
IR_TX_IDLE,
IR_TX_HEADER,
IR_TX_DATA,
IR_TX_REP,
} ir_tx_state_t;
/**
* @brief IR TX transmission parameter structure type definition
*/
typedef struct {
ir_tx_nec_data_t data;
uint8_t repeat;
} ir_tx_trans_t;
typedef struct {
uint32_t io_num;
uint32_t freq;
SemaphoreHandle_t done_sem;
SemaphoreHandle_t send_mux;
ir_tx_trans_t trans;
} ir_tx_obj_t;
ir_tx_obj_t *ir_tx_obj = NULL;
static ir_tx_state_t ir_tx_state = IR_TX_IDLE;
static void inline ir_tx_clear_carrier()
{
switch (ir_tx_obj->io_num) {
case 2: {
GPIO.out_w1tc |= 0x4; // GPIO 2
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
}
break;
case 14: {
GPIO.out_w1tc |= 0x4000; // GPIO 14
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_GPIO14);
}
break;
}
}
static void inline ir_tx_gen_carrier()
{
switch (ir_tx_obj->io_num) {
case 2: {
GPIO.out_w1ts |= 0x4; // GPIO 2
PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_I2SO_WS);
}
break;
case 14: {
GPIO.out_w1ts |= 0x4000; // GPIO 14
PIN_FUNC_SELECT(PERIPHS_IO_MUX_MTMS_U, FUNC_I2SI_WS);
}
break;
}
}
static void inline ir_tx_timer_alarm(uint32_t val)
{
hw_timer_alarm_us(val - IR_TX_ERROR_US, false);
}
void IRAM_ATTR ir_tx_handler()
{
uint32_t t_expire = 0;
static uint32_t rep_expire_us = IR_TX_NEC_REP_CYCLE; //for nec 32bit mode
static uint16_t data_tmp = 0;
static uint8_t ir_tx_bit_num = 0;
static uint8_t ir_bit_state = TX_BIT_CARRIER;
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
switch (ir_tx_state) {
case IR_TX_IDLE: {
ir_tx_gen_carrier();
ir_tx_timer_alarm(IR_TX_NEC_HEADER_HIGH_US);
ir_tx_state = IR_TX_HEADER;
break;
}
case IR_TX_HEADER: {
ir_tx_clear_carrier();
ir_tx_timer_alarm(IR_TX_NEC_HEADER_LOW_US);
ir_tx_state = IR_TX_DATA;
ir_bit_state = TX_BIT_CARRIER;
data_tmp = ir_tx_obj->trans.data.addr1;
rep_expire_us -= (IR_TX_NEC_HEADER_HIGH_US + IR_TX_NEC_HEADER_LOW_US);
break;
}
case IR_TX_DATA: {
if (ir_bit_state == TX_BIT_CARRIER) {
t_expire = IR_TX_NEC_DATA_HIGH_US;
ir_bit_state = TX_BIT_LOW;
ir_tx_gen_carrier();
} else if (ir_bit_state == TX_BIT_LOW) {
ir_tx_clear_carrier();
if ((data_tmp >> (ir_tx_bit_num % IR_TX_NEC_BIT_NUM)) & 0x1) {
t_expire = IR_TX_NEC_DATA_LOW_1_US;
} else {
t_expire = IR_TX_NEC_DATA_LOW_0_US;
}
ir_tx_bit_num++;
if (ir_tx_bit_num == IR_TX_NEC_BIT_NUM) {
data_tmp = ir_tx_obj->trans.data.addr2;
} else if (ir_tx_bit_num == IR_TX_NEC_BIT_NUM * 2) {
data_tmp = ir_tx_obj->trans.data.cmd1;
} else if (ir_tx_bit_num == IR_TX_NEC_BIT_NUM * 3) {
data_tmp = ir_tx_obj->trans.data.cmd2;
} else if ((ir_tx_bit_num == (IR_TX_NEC_BIT_NUM * 4 + 1))) {
//clean up state for next or for repeat
ir_tx_bit_num = 0;
ir_bit_state = TX_BIT_CARRIER;
if (ir_tx_obj->trans.repeat > 0) {
t_expire = (rep_expire_us - 5);
ir_tx_timer_alarm(t_expire);
rep_expire_us = IR_TX_NEC_REP_CYCLE;
ir_tx_state = IR_TX_REP;
} else {
rep_expire_us = IR_TX_NEC_REP_CYCLE;
ir_tx_state = IR_TX_IDLE;
xSemaphoreGiveFromISR(ir_tx_obj->done_sem, &xHigherPriorityTaskWoken);
}
break;
}
ir_bit_state = TX_BIT_CARRIER;
} else {
}
rep_expire_us -= t_expire;
ir_tx_timer_alarm(t_expire);
break;
}
case IR_TX_REP: {
if (ir_tx_obj->trans.repeat > 0) {
if (ir_tx_bit_num == 0) {
ir_tx_gen_carrier();
t_expire = IR_TX_NEC_REP_HIGH_US ;
} else if (ir_tx_bit_num == 1) {
ir_tx_clear_carrier();
t_expire = IR_TX_NEC_REP_LOW_US ;
} else if (ir_tx_bit_num == 2) {
ir_tx_gen_carrier();
t_expire = IR_TX_NEC_REP_STOP_US;
} else if (ir_tx_bit_num == 3) {
ir_tx_clear_carrier();
ir_tx_obj->trans.repeat--;
if (ir_tx_obj->trans.repeat > 0) {
t_expire = rep_expire_us ;
rep_expire_us = IR_TX_NEC_REP_CYCLE;
} else {
ir_tx_bit_num = 0;
rep_expire_us = IR_TX_NEC_REP_CYCLE;
ir_tx_state = IR_TX_IDLE;
ir_bit_state = TX_BIT_CARRIER;
xSemaphoreGiveFromISR(ir_tx_obj->done_sem, &xHigherPriorityTaskWoken);
break;
}
} else {
}
ir_tx_bit_num++;//bit num reuse for repeat wave form
if (ir_tx_bit_num == 4) {
ir_tx_bit_num = 0;
rep_expire_us = IR_TX_NEC_REP_CYCLE;
} else {
rep_expire_us -= t_expire;
}
ir_tx_timer_alarm(t_expire);
}
break;
}
default:
break;
}
if (xHigherPriorityTaskWoken == pdTRUE) {
taskYIELD();
}
}
static esp_err_t ir_tx_trans(ir_tx_nec_data_t data, uint8_t repeat, uint32_t *timeout_ticks)
{
int ret;
uint32_t ticks_escape = 0, ticks_last = 0;
struct timeval now;
if (*timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_last = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS;
}
if (ir_tx_state != IR_TX_IDLE) {
IR_TX_CHECK(false, "When transmission begins, the state must be idle", ESP_FAIL);
}
ir_tx_obj->trans.data = data;
ir_tx_obj->trans.repeat = repeat;
xSemaphoreTake(ir_tx_obj->done_sem, 0); // Clear possible semaphore
ir_tx_handler();
if (ir_tx_state != IR_TX_IDLE) {
ret = xSemaphoreTake(ir_tx_obj->done_sem, *timeout_ticks);
if (ret != pdTRUE) {
IR_TX_CHECK(false, "Waiting for done_sem error", ESP_ERR_TIMEOUT);
}
}
if (*timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_escape = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS - ticks_last;
if (*timeout_ticks <= ticks_escape) {
IR_TX_CHECK(false, "timeout", ESP_ERR_TIMEOUT);
} else {
*timeout_ticks -= ticks_escape;
}
}
return ESP_OK;
}
int ir_tx_send_data(ir_tx_nec_data_t *data, size_t len, uint32_t timeout_ticks)
{
IR_TX_CHECK(ir_tx_obj, "ir tx has not been initialized yet.", ESP_FAIL);
int ret;
int x, y;
uint32_t ticks_escape = 0, ticks_last = 0;
struct timeval now;
if (timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_last = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS;
}
ret = xSemaphoreTake(ir_tx_obj->send_mux, timeout_ticks);
if (ret != pdTRUE) {
IR_TX_CHECK(false, "SemaphoreTake error", -1);
}
if (timeout_ticks != portMAX_DELAY) {
gettimeofday(&now, NULL);
ticks_escape = (now.tv_sec * 1000 + now.tv_usec / 1000) / portTICK_RATE_MS - ticks_last;
if (timeout_ticks <= ticks_escape) {
xSemaphoreGive(ir_tx_obj->send_mux);
IR_TX_CHECK(false, "timeout", -1);
} else {
timeout_ticks -= ticks_escape;
}
}
for (x = 0; x < len;) {
for (y = 1; y < len - x; y++) {
if (data[y + x].val != data[x].val) { // search repeat
break;
}
}
ret = ir_tx_trans(data[x], y - 1, &timeout_ticks);
if (ret != ESP_OK) {
if (ret == ESP_ERR_TIMEOUT) {
x += y;
}
xSemaphoreGive(ir_tx_obj->send_mux);
IR_TX_CHECK(false, "trans data error", x);
}
x += y;
}
xSemaphoreGive(ir_tx_obj->send_mux);
return len;
}
esp_err_t ir_tx_deinit()
{
IR_TX_CHECK(ir_tx_obj, "ir tx has not been initialized yet.", ESP_FAIL);
if (ir_tx_obj->done_sem) {
vSemaphoreDelete(ir_tx_obj->done_sem);
ir_tx_obj->done_sem = NULL;
}
if (ir_tx_obj->send_mux) {
vSemaphoreDelete(ir_tx_obj->send_mux);
ir_tx_obj->send_mux = NULL;
}
hw_timer_deinit();
i2s_driver_uninstall(I2S_NUM_0);
heap_caps_free(ir_tx_obj);
ir_tx_obj = NULL;
return ESP_OK;
}
esp_err_t ir_tx_init(ir_tx_config_t *config)
{
IR_TX_CHECK(config, "config error", ESP_ERR_INVALID_ARG);
IR_TX_CHECK((config->io_num == 2) || (config->io_num == 14), "Only supports io2 and io14 as carrier outputs", ESP_ERR_INVALID_ARG);
IR_TX_CHECK(NULL == ir_tx_obj, "ir tx has been initialized", ESP_FAIL);
ir_tx_obj = heap_caps_malloc(sizeof(ir_tx_obj_t), MALLOC_CAP_8BIT);
IR_TX_CHECK(ir_tx_obj, "ir tx object malloc error", ESP_ERR_NO_MEM);
ir_tx_obj->io_num = config->io_num;
ir_tx_obj->freq = config->freq;
ir_tx_obj->done_sem = xSemaphoreCreateBinary();
ir_tx_obj->send_mux = xSemaphoreCreateMutex();
if (NULL == ir_tx_obj->done_sem || NULL == ir_tx_obj->send_mux) {
ir_tx_deinit();
IR_TX_CHECK(false, "Semaphore create fail", ESP_ERR_NO_MEM);
}
// init default data
ir_tx_obj->trans.data.addr1 = (uint8_t)0xee; //addr code
ir_tx_obj->trans.data.addr2 = (uint8_t)~0xee;
ir_tx_obj->trans.data.cmd1 = (uint8_t)0x5a; //cmd code
ir_tx_obj->trans.data.cmd2 = (uint8_t)~0x5a;
ir_tx_obj->trans.repeat = 5; //repeat number
gpio_config_t io_conf;
io_conf.intr_type = GPIO_INTR_DISABLE;
io_conf.mode = GPIO_MODE_OUTPUT;
io_conf.pin_bit_mask = 1 << ir_tx_obj->io_num;
io_conf.pull_down_en = 0;
io_conf.pull_up_en = 0;
gpio_config(&io_conf);
i2s_config_t i2s_config = {
.mode = I2S_MODE_MASTER, // Only carrier mode
.sample_rate = ir_tx_obj->freq,
.bits_per_sample = 16,
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, // 2-channels
.communication_format = I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 2, // no use
.dma_buf_len = 8 // no use
};
i2s_pin_config_t pin_config = {
.bck_o_en = -1,
.ws_o_en = (ir_tx_obj->io_num == 2) ? 1 : -1,
.bck_i_en = -1,
.ws_i_en = (ir_tx_obj->io_num == 14) ? 1 : -1,
.data_out_en = -1,
.data_in_en = -1
};
i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);
i2s_set_pin(I2S_NUM_0, &pin_config);
hw_timer_init(ir_tx_handler, NULL);
hw_timer_disarm();
ir_tx_clear_carrier();
return ESP_OK;
}

View File

@ -0,0 +1,108 @@
// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Currently only supports infrared NEC code */
/* NEC time parameter configuration */
#define IR_RX_NEC_BIT_NUM 8
#define IR_RX_NEC_HEADER_US 13500
#define IR_RX_NEC_DATA0_US 1120
#define IR_RX_NEC_DATA1_US 2250
#define IR_RX_NEC_TM1_REP_US 20000
#define IR_RX_NEC_TM2_REP_US 11250
#define IR_RX_ERROR_US 200 // Used to compensate errors
/**
* @brief ir rx initialization parameter structure type definition
*/
typedef struct {
uint32_t io_num;
uint32_t buf_len;
} ir_rx_config_t;
/**
* @brief ir rx nec data union type definition
*/
typedef union {
struct {
uint32_t addr1: 8;
uint32_t addr2: 8;
uint32_t cmd1: 8;
uint32_t cmd2: 8;
};
uint32_t val; /*!< union fill */
} ir_rx_nec_data_t;
/**
* @brief Disable the ir rx
*
* @return
* - ESP_OK Success
* - ESP_FAIL ir rx has not been initialized yet
*/
esp_err_t ir_rx_disable();
/**
* @brief Enable the ir rx
*
* @return
* - ESP_OK Success
* - ESP_FAIL ir rx has not been initialized yet
*/
esp_err_t ir_rx_enable();
/**
* @brief Receive infrared data
*
* @param data Pointer to the rx data buffer
* @param len Length of ir_rx_data, range: 0 < len < (uint16_t)
* @param timeout_ticks freertos timeout ticks
*
* @return
* - -1 error
* - length The actual length of data received
*/
int ir_rx_recv_data(ir_rx_nec_data_t *data, size_t len, uint32_t timeout_ticks);
/**
* @brief Deinit the ir rx
*
* @return
* - ESP_OK Success
* - ESP_FAIL ir rx has not been initialized yet
*/
esp_err_t ir_rx_deinit();
/**
* @brief Initialize the ir rx
*
* @param config Pointer to deliver initialize configuration parameter
*
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM malloc fail
* - ESP_FAIL ir rx has been initialized
*/
esp_err_t ir_rx_init(ir_rx_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,97 @@
// Copyright 2018-2025 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Currently only supports infrared NEC code */
/* NEC time parameter configuration */
#define IR_TX_NEC_BIT_NUM 8
#define IR_TX_NEC_HEADER_HIGH_US 9000
#define IR_TX_NEC_HEADER_LOW_US 4500
#define IR_TX_NEC_DATA_HIGH_US 560
#define IR_TX_NEC_DATA_LOW_1_US 1690
#define IR_TX_NEC_DATA_LOW_0_US 560
#define IR_TX_NEC_REP_HIGH_US 9000
#define IR_TX_NEC_REP_LOW_US 2250
#define IR_TX_NEC_REP_STOP_US 562
#define IR_TX_NEC_REP_CYCLE 108000
#define IR_TX_ERROR_US 40 // Timing in advance to reduce errors
/**
* @brief ir tx initialization parameter structure type definition
*/
typedef struct {
uint32_t io_num; // 2 or 14, 2: I2SO_WS 14: I2SI_WS
uint32_t freq;
} ir_tx_config_t;
/**
* @brief ir tx data union type definition
*/
typedef union {
struct {
uint32_t addr1: 8;
uint32_t addr2: 8;
uint32_t cmd1: 8;
uint32_t cmd2: 8;
};
uint32_t val; /*!< union fill */
} ir_tx_nec_data_t;
/**
* @brief Send ir data
*
* @note If multiple data are identical, repeat signals will be used.
* Infrared data consumes more than 100 ms per transmission, so note the timeout_ticks parameter
*
* @param data Pointer to the tx data buffer
* @param len Length of ir_tx_data, range: 0 < len < (uint16_t)
* @param timeout_ticks freertos timeout ticks
*
* @return
* - -1 error
* - length The actual length of data sent
*/
int ir_tx_send_data(ir_tx_nec_data_t *data, size_t len, uint32_t timeout_ticks);
/**
* @brief Deinit the ir tx
*
* @return
* - ESP_OK Success
* - ESP_FAIL ir tx has not been initialized yet
*/
esp_err_t ir_tx_deinit();
/**
* @brief Initialize the ir tx
*
* @param config Pointer to deliver initialize configuration parameter
*
* @return
* - ESP_OK Success
* - ESP_ERR_NO_MEM malloc fail
* - ESP_FAIL ir tx has been initialized
*/
esp_err_t ir_tx_init(ir_tx_config_t *config);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,6 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ir_rx)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ir_rx
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,83 @@
# IR RX Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
In this example, we use IO5 to recv the nec infrared signal
## How to Use Example
### Hardware Required
* A development board with ESP8266 SoC (e.g., ESP8266-DevKitC, etc.)
* A USB cable for power supply and programming
### Configure the Project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (471) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:2
I (19161) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x0, cmd2: 0xff
I (19171) main: ir rx nec data: 0x0
I (19211) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x0, cmd2: 0xff
I (19221) main: ir rx nec data: 0x0
I (19321) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x0, cmd2: 0xff
I (19331) main: ir rx nec data: 0x0
I (19431) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x0, cmd2: 0xff
I (19431) main: ir rx nec data: 0x0
I (19541) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x0, cmd2: 0xff
I (19541) main: ir rx nec data: 0x0
I (20601) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x1, cmd2: 0xfe
I (20601) main: ir rx nec data: 0x1
I (20651) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x1, cmd2: 0xfe
I (20661) main: ir rx nec data: 0x1
I (20761) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x1, cmd2: 0xfe
I (20761) main: ir rx nec data: 0x1
I (20871) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x1, cmd2: 0xfe
I (20871) main: ir rx nec data: 0x1
I (20981) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x1, cmd2: 0xfe
I (20981) main: ir rx nec data: 0x1
I (22041) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x2, cmd2: 0xfd
I (22041) main: ir rx nec data: 0x2
I (22091) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x2, cmd2: 0xfd
I (22091) main: ir rx nec data: 0x2
I (22201) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x2, cmd2: 0xfd
I (22201) main: ir rx nec data: 0x2
I (22311) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x2, cmd2: 0xfd
I (22311) main: ir rx nec data: 0x2
I (22411) main: addr1: 0x55, addr2: 0xaa, cmd1: 0x2, cmd2: 0xfd
I (22421) main: ir rx nec data: 0x2
```
If you have a logic analyzer, you can use a logic analyzer to grab online data. The following table describes the pins we use by default (Note that you can also use other pins for the same purpose).
| pin name| function | gpio_num |
|:---:|:---:|:---:|
| IR RX|Infrared reception | GPIO_NUM_5 |
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `make monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
For any technical queries, please open an [issue](https://github.com/espressif/ESP8266_RTOS_SDK/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,3 @@
set(COMPONENT_SRCS "ir_rx_example_main.c")
register_component()

View File

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@ -0,0 +1,70 @@
/* IR RX Example
This is an ir rx demo of ir function( NEC CODE ,32 BIT LENGTH)
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/ir_rx.h"
static const char *TAG = "main";
#define IR_RX_IO_NUM 5
#define IR_RX_BUF_LEN 128 // The actual allocated memory size is sizeof(uint32_t)*BUF_LEN. If the allocation is too small, the old infrared data may be overwritten.
/**
* @brief check whether the ir cmd and addr obey the protocol
*
* @param nec_code nec ir code that received
*
* @return
* - ESP_OK success
* - ESP_ERR_INVALID_ARG Parameter error
*/
static esp_err_t ir_rx_nec_code_check(ir_rx_nec_data_t nec_code)
{
if ((nec_code.addr1 != ((~nec_code.addr2) & 0xff))) {
return ESP_FAIL;
}
if ((nec_code.cmd1 != ((~nec_code.cmd2) & 0xff))) {
return ESP_FAIL;
}
return ESP_OK;
}
void ir_rx_task(void *arg)
{
ir_rx_nec_data_t ir_data;
ir_rx_config_t ir_rx_config = {
.io_num = IR_RX_IO_NUM,
.buf_len = IR_RX_BUF_LEN
};
ir_rx_init(&ir_rx_config);
while (1) {
ir_data.val = 0;
ir_rx_recv_data(&ir_data, 1, portMAX_DELAY);
ESP_LOGI(TAG, "addr1: 0x%x, addr2: 0x%x, cmd1: 0x%x, cmd2: 0x%x", ir_data.addr1, ir_data.addr2, ir_data.cmd1, ir_data.cmd2);
if (ESP_OK == ir_rx_nec_code_check(ir_data)) {
ESP_LOGI(TAG, "ir rx nec data: 0x%x", ir_data.cmd1);
} else {
ESP_LOGI(TAG, "Non-standard nec infrared protocol");
}
}
vTaskDelete(NULL);
}
void app_main()
{
xTaskCreate(ir_rx_task, "ir_rx_task", 2048, NULL, 5, NULL);
}

View File

@ -0,0 +1,6 @@
# The following four lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ir_tx)

View File

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ir
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,59 @@
# IR TX Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
In this example, we use IO14 to transmit the nec infrared signal and i2s to generate the 38Khz carrier.
## How to Use Example
### Hardware Required
* A development board with ESP8266 SoC (e.g., ESP8266-DevKitC, etc.)
* A USB cable for power supply and programming
### Configure the Project
```
make menuconfig
```
* Set serial port under Serial Flasher Options.
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
make -j4 flash monitor
```
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (467) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
I (937) main: ir tx nec: addr:55h;cmd:00h;repeat:4
I (2377) main: ir tx nec: addr:55h;cmd:01h;repeat:4
I (3817) main: ir tx nec: addr:55h;cmd:02h;repeat:4
I (5257) main: ir tx nec: addr:55h;cmd:03h;repeat:4
I (6697) main: ir tx nec: addr:55h;cmd:04h;repeat:4
I (8137) main: ir tx nec: addr:55h;cmd:05h;repeat:4
```
If you have a logic analyzer, you can use a logic analyzer to grab online data. The following table describes the pins we use by default (Note that you can also use other pins for the same purpose).
| pin name| function | gpio_num |
|:---:|:---:|:---:|
| IR TX|Infrared emission | GPIO_NUM_14 |
## Troubleshooting
* Program upload failure
* Hardware connection is not correct: run `make monitor`, and reboot your board to see if there are any output logs.
* The baud rate for downloading is too high: lower your baud rate in the `menuconfig` menu, and try again.
For any technical queries, please open an [issue](https://github.com/espressif/ESP8266_RTOS_SDK/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,3 @@
set(COMPONENT_SRCS "ir_tx_example_main.c")
register_component()

View File

@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@ -0,0 +1,58 @@
/* IR TX Example
This is an ir tx demo of ir function( NEC CODE ,32 BIT LENGTH)
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "driver/ir_tx.h"
static const char *TAG = "main";
#define IR_TX_IO_NUM 14
void ir_tx_task(void *arg)
{
ir_tx_config_t ir_tx_config = {
.io_num = IR_TX_IO_NUM,
.freq = 38000
};
ir_tx_init(&ir_tx_config);
ir_tx_nec_data_t ir_data[5];
/*
The standard NEC ir code is:
addr + ~addr + cmd + ~cmd
*/
ir_data[0].addr1 = 0x55;
ir_data[0].addr2 = ~0x55;
ir_data[0].cmd1 = 0x00;
ir_data[0].cmd2 = ~0x00;
while (1) {
for (int x = 1; x < 5; x++) { // repeat 4 times
ir_data[x] = ir_data[0];
}
ir_tx_send_data(ir_data, 5, portMAX_DELAY);
ESP_LOGI(TAG, "ir tx nec: addr:%02xh;cmd:%02xh;repeat:%d", ir_data[0].addr1, ir_data[0].cmd1, 4);
ir_data[0].cmd1++;
ir_data[0].cmd2 = ~ir_data[0].cmd1;
vTaskDelay(1000 / portTICK_RATE_MS);
}
vTaskDelete(NULL);
}
void app_main()
{
xTaskCreate(ir_tx_task, "ir_tx_task", 2048, NULL, 5, NULL);
}