1 // Copyright 2019 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.#include <stdlib.h>
14 #include <sys/cdefs.h>
15 #include "esp_log.h"
16 #include "ir_tools.h"
17 #include "ir_timings.h"
18 #include "driver/rmt.h"
19 
20 static const char *TAG = "nec_builder";
21 #define NEC_CHECK(a, str, goto_tag, ret_value, ...)                               \
22     do                                                                            \
23     {                                                                             \
24         if (!(a))                                                                 \
25         {                                                                         \
26             ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
27             ret = ret_value;                                                      \
28             goto goto_tag;                                                        \
29         }                                                                         \
30     } while (0)
31 
32 typedef struct {
33     ir_builder_t parent;
34     uint32_t buffer_size;
35     uint32_t cursor;
36     uint32_t flags;
37     uint32_t leading_code_high_ticks;
38     uint32_t leading_code_low_ticks;
39     uint32_t repeat_code_high_ticks;
40     uint32_t repeat_code_low_ticks;
41     uint32_t payload_logic0_high_ticks;
42     uint32_t payload_logic0_low_ticks;
43     uint32_t payload_logic1_high_ticks;
44     uint32_t payload_logic1_low_ticks;
45     uint32_t ending_code_high_ticks;
46     uint32_t ending_code_low_ticks;
47     bool inverse;
48     rmt_item32_t buffer[0];
49 } nec_builder_t;
50 
nec_builder_make_head(ir_builder_t * builder)51 static esp_err_t nec_builder_make_head(ir_builder_t *builder)
52 {
53     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
54     nec_builder->cursor = 0;
55     nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
56     nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->leading_code_high_ticks;
57     nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
58     nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->leading_code_low_ticks;
59     nec_builder->cursor += 1;
60     return ESP_OK;
61 }
62 
nec_builder_make_logic0(ir_builder_t * builder)63 static esp_err_t nec_builder_make_logic0(ir_builder_t *builder)
64 {
65     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
66     nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
67     nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic0_high_ticks;
68     nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
69     nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic0_low_ticks;
70     nec_builder->cursor += 1;
71     return ESP_OK;
72 }
73 
nec_builder_make_logic1(ir_builder_t * builder)74 static esp_err_t nec_builder_make_logic1(ir_builder_t *builder)
75 {
76     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
77     nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
78     nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->payload_logic1_high_ticks;
79     nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
80     nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->payload_logic1_low_ticks;
81     nec_builder->cursor += 1;
82     return ESP_OK;
83 }
84 
nec_builder_make_end(ir_builder_t * builder)85 static esp_err_t nec_builder_make_end(ir_builder_t *builder)
86 {
87     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
88     nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
89     nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->ending_code_high_ticks;
90     nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
91     nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->ending_code_low_ticks;
92     nec_builder->cursor += 1;
93     nec_builder->buffer[nec_builder->cursor].val = 0;
94     nec_builder->cursor += 1;
95     return ESP_OK;
96 }
97 
nec_build_frame(ir_builder_t * builder,uint32_t address,uint32_t command)98 static esp_err_t nec_build_frame(ir_builder_t *builder, uint32_t address, uint32_t command)
99 {
100     esp_err_t ret = ESP_OK;
101     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
102     if (!nec_builder->flags & IR_TOOLS_FLAGS_PROTO_EXT) {
103         uint8_t low_byte = address & 0xFF;
104         uint8_t high_byte = (address >> 8) & 0xFF;
105         NEC_CHECK(low_byte == (~high_byte & 0xFF), "address not match standard NEC protocol", err, ESP_ERR_INVALID_ARG);
106         low_byte = command & 0xFF;
107         high_byte = (command >> 8) & 0xFF;
108         NEC_CHECK(low_byte == (~high_byte & 0xFF), "command not match standard NEC protocol", err, ESP_ERR_INVALID_ARG);
109     }
110     builder->make_head(builder);
111     // LSB -> MSB
112     for (int i = 0; i < 16; i++) {
113         if (address & (1 << i)) {
114             builder->make_logic1(builder);
115         } else {
116             builder->make_logic0(builder);
117         }
118     }
119     for (int i = 0; i < 16; i++) {
120         if (command & (1 << i)) {
121             builder->make_logic1(builder);
122         } else {
123             builder->make_logic0(builder);
124         }
125     }
126     builder->make_end(builder);
127     return ESP_OK;
128 err:
129     return ret;
130 }
131 
nec_build_repeat_frame(ir_builder_t * builder)132 static esp_err_t nec_build_repeat_frame(ir_builder_t *builder)
133 {
134     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
135     nec_builder->cursor = 0;
136     nec_builder->buffer[nec_builder->cursor].level0 = !nec_builder->inverse;
137     nec_builder->buffer[nec_builder->cursor].duration0 = nec_builder->repeat_code_high_ticks;
138     nec_builder->buffer[nec_builder->cursor].level1 = nec_builder->inverse;
139     nec_builder->buffer[nec_builder->cursor].duration1 = nec_builder->repeat_code_low_ticks;
140     nec_builder->cursor += 1;
141     nec_builder_make_end(builder);
142     return ESP_OK;
143 }
144 
nec_builder_get_result(ir_builder_t * builder,void * result,size_t * length)145 static esp_err_t nec_builder_get_result(ir_builder_t *builder, void *result, size_t *length)
146 {
147     esp_err_t ret = ESP_OK;
148     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
149     NEC_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
150     *(rmt_item32_t **)result = nec_builder->buffer;
151     *length = nec_builder->cursor;
152     return ESP_OK;
153 err:
154     return ret;
155 }
156 
nec_builder_del(ir_builder_t * builder)157 static esp_err_t nec_builder_del(ir_builder_t *builder)
158 {
159     nec_builder_t *nec_builder = __containerof(builder, nec_builder_t, parent);
160     free(nec_builder);
161     return ESP_OK;
162 }
163 
ir_builder_rmt_new_nec(const ir_builder_config_t * config)164 ir_builder_t *ir_builder_rmt_new_nec(const ir_builder_config_t *config)
165 {
166     ir_builder_t *ret = NULL;
167     NEC_CHECK(config, "nec configuration can't be null", err, NULL);
168     NEC_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
169 
170     uint32_t builder_size = sizeof(nec_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
171     nec_builder_t *nec_builder = calloc(1, builder_size);
172     NEC_CHECK(nec_builder, "request memory for nec_builder failed", err, NULL);
173 
174     nec_builder->buffer_size = config->buffer_size;
175     nec_builder->flags = config->flags;
176     if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
177         nec_builder->inverse = true;
178     }
179 
180     uint32_t counter_clk_hz = 0;
181     NEC_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
182               "get rmt counter clock failed", err, NULL);
183     float ratio = (float)counter_clk_hz / 1e6;
184     nec_builder->leading_code_high_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_HIGH_US);
185     nec_builder->leading_code_low_ticks = (uint32_t)(ratio * NEC_LEADING_CODE_LOW_US);
186     nec_builder->repeat_code_high_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_HIGH_US);
187     nec_builder->repeat_code_low_ticks = (uint32_t)(ratio * NEC_REPEAT_CODE_LOW_US);
188     nec_builder->payload_logic0_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_HIGH_US);
189     nec_builder->payload_logic0_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ZERO_LOW_US);
190     nec_builder->payload_logic1_high_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_HIGH_US);
191     nec_builder->payload_logic1_low_ticks = (uint32_t)(ratio * NEC_PAYLOAD_ONE_LOW_US);
192     nec_builder->ending_code_high_ticks = (uint32_t)(ratio * NEC_ENDING_CODE_HIGH_US);
193     nec_builder->ending_code_low_ticks = 0x7FFF;
194     nec_builder->parent.make_head = nec_builder_make_head;
195     nec_builder->parent.make_logic0 = nec_builder_make_logic0;
196     nec_builder->parent.make_logic1 = nec_builder_make_logic1;
197     nec_builder->parent.make_end = nec_builder_make_end;
198     nec_builder->parent.build_frame = nec_build_frame;
199     nec_builder->parent.build_repeat_frame = nec_build_repeat_frame;
200     nec_builder->parent.get_result = nec_builder_get_result;
201     nec_builder->parent.del = nec_builder_del;
202     nec_builder->parent.repeat_period_ms = 110;
203     return &nec_builder->parent;
204 err:
205     return ret;
206 }
207