mirror of
https://github.com/espressif/ESP8266_RTOS_SDK.git
synced 2025-05-21 00:56:38 +08:00
feature(ir): add ir tx rx driver
This commit is contained in:
@ -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")
|
||||
|
||||
|
@ -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;
|
||||
|
271
components/esp8266/driver/ir_rx.c
Normal file
271
components/esp8266/driver/ir_rx.c
Normal 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;
|
||||
}
|
||||
|
421
components/esp8266/driver/ir_tx.c
Normal file
421
components/esp8266/driver/ir_tx.c
Normal 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;
|
||||
}
|
108
components/esp8266/include/driver/ir_rx.h
Normal file
108
components/esp8266/include/driver/ir_rx.h
Normal 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
|
97
components/esp8266/include/driver/ir_tx.h
Normal file
97
components/esp8266/include/driver/ir_tx.h
Normal 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
|
6
examples/peripherals/ir_rx/CMakeLists.txt
Normal file
6
examples/peripherals/ir_rx/CMakeLists.txt
Normal 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)
|
9
examples/peripherals/ir_rx/Makefile
Normal file
9
examples/peripherals/ir_rx/Makefile
Normal 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
|
||||
|
83
examples/peripherals/ir_rx/README.md
Normal file
83
examples/peripherals/ir_rx/README.md
Normal 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.
|
3
examples/peripherals/ir_rx/main/CMakeLists.txt
Normal file
3
examples/peripherals/ir_rx/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
set(COMPONENT_SRCS "ir_rx_example_main.c")
|
||||
|
||||
register_component()
|
3
examples/peripherals/ir_rx/main/component.mk
Normal file
3
examples/peripherals/ir_rx/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
70
examples/peripherals/ir_rx/main/ir_rx_example_main.c
Normal file
70
examples/peripherals/ir_rx/main/ir_rx_example_main.c
Normal 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);
|
||||
}
|
6
examples/peripherals/ir_tx/CMakeLists.txt
Normal file
6
examples/peripherals/ir_tx/CMakeLists.txt
Normal 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)
|
9
examples/peripherals/ir_tx/Makefile
Normal file
9
examples/peripherals/ir_tx/Makefile
Normal 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
|
||||
|
59
examples/peripherals/ir_tx/README.md
Normal file
59
examples/peripherals/ir_tx/README.md
Normal 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.
|
3
examples/peripherals/ir_tx/main/CMakeLists.txt
Normal file
3
examples/peripherals/ir_tx/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
set(COMPONENT_SRCS "ir_tx_example_main.c")
|
||||
|
||||
register_component()
|
3
examples/peripherals/ir_tx/main/component.mk
Normal file
3
examples/peripherals/ir_tx/main/component.mk
Normal file
@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
58
examples/peripherals/ir_tx/main/ir_tx_example_main.c
Normal file
58
examples/peripherals/ir_tx/main/ir_tx_example_main.c
Normal 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);
|
||||
}
|
Reference in New Issue
Block a user