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.
14 #include <stdlib.h>
15 #include <sys/cdefs.h>
16 #include "esp_log.h"
17 #include "ir_tools.h"
18 #include "ir_timings.h"
19 #include "driver/rmt.h"
20
21 static const char *TAG = "rc5_builder";
22 #define RC5_CHECK(a, str, goto_tag, ret_value, ...) \
23 do \
24 { \
25 if (!(a)) \
26 { \
27 ESP_LOGE(TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
28 ret = ret_value; \
29 goto goto_tag; \
30 } \
31 } while (0)
32
33 typedef struct {
34 ir_builder_t parent;
35 uint32_t buffer_size;
36 uint32_t cursor;
37 uint32_t pulse_duration_ticks;
38 uint32_t flags;
39 bool toggle;
40 bool s2_bit;
41 bool inverse;
42 rmt_item32_t buffer[0];
43 } rc5_builder_t;
44
rc5_builder_make_head(ir_builder_t * builder)45 static esp_err_t rc5_builder_make_head(ir_builder_t *builder)
46 {
47 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
48 rc5_builder->cursor = 0;
49 rc5_builder->toggle = !rc5_builder->toggle;
50 // S1 default (not inverse) is 0
51 rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
52 rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
53 rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
54 rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
55 rc5_builder->cursor += 1;
56 // S2 default (not inverse) is depend on whether use extended protocol
57 rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->s2_bit ^ rc5_builder->inverse;
58 rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
59 rc5_builder->buffer[rc5_builder->cursor].level1 = !(rc5_builder->s2_bit ^ rc5_builder->inverse);
60 rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
61 rc5_builder->cursor += 1;
62 // T
63 rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->toggle;
64 rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
65 rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->toggle;
66 rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
67 rc5_builder->cursor += 1;
68 return ESP_OK;
69 }
70
rc5_builder_make_logic0(ir_builder_t * builder)71 static esp_err_t rc5_builder_make_logic0(ir_builder_t *builder)
72 {
73 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
74 rc5_builder->buffer[rc5_builder->cursor].level0 = !rc5_builder->inverse;
75 rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
76 rc5_builder->buffer[rc5_builder->cursor].level1 = rc5_builder->inverse;
77 rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
78 rc5_builder->cursor += 1;
79 return ESP_OK;
80 }
81
rc5_builder_make_logic1(ir_builder_t * builder)82 static esp_err_t rc5_builder_make_logic1(ir_builder_t *builder)
83 {
84 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
85 rc5_builder->buffer[rc5_builder->cursor].level0 = rc5_builder->inverse;
86 rc5_builder->buffer[rc5_builder->cursor].duration0 = rc5_builder->pulse_duration_ticks;
87 rc5_builder->buffer[rc5_builder->cursor].level1 = !rc5_builder->inverse;
88 rc5_builder->buffer[rc5_builder->cursor].duration1 = rc5_builder->pulse_duration_ticks;
89 rc5_builder->cursor += 1;
90 return ESP_OK;
91 }
92
rc5_builder_make_end(ir_builder_t * builder)93 static esp_err_t rc5_builder_make_end(ir_builder_t *builder)
94 {
95 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
96 rc5_builder->buffer[rc5_builder->cursor].val = 0;
97 rc5_builder->cursor += 1;
98 return ESP_OK;
99 }
100
rc5_build_frame(ir_builder_t * builder,uint32_t address,uint32_t command)101 static esp_err_t rc5_build_frame(ir_builder_t *builder, uint32_t address, uint32_t command)
102 {
103 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
104 if (rc5_builder->flags & IR_TOOLS_FLAGS_PROTO_EXT) {
105 // RC5-extended protocol uses S2 bit as a 7th command bit (MSB of a command)
106 if (command > 63) {
107 rc5_builder->s2_bit = true;
108 } else {
109 rc5_builder->s2_bit = false;
110 }
111 }
112 builder->make_head(builder);
113 // MSB -> LSB
114 for (int i = 4; i >= 0; i--) {
115 if (address & (1 << i)) {
116 builder->make_logic1(builder);
117 } else {
118 builder->make_logic0(builder);
119 }
120 }
121 for (int i = 5; i >= 0; i--) {
122 if (command & (1 << i)) {
123 builder->make_logic1(builder);
124 } else {
125 builder->make_logic0(builder);
126 }
127 }
128 builder->make_end(builder);
129 return ESP_OK;
130 }
131
rc5_build_repeat_frame(ir_builder_t * builder)132 static esp_err_t rc5_build_repeat_frame(ir_builder_t *builder)
133 {
134 // repeat frame is just the latest build frame, so do nothing here
135 return ESP_OK;
136 }
137
rc5_builder_get_result(ir_builder_t * builder,void * result,size_t * length)138 static esp_err_t rc5_builder_get_result(ir_builder_t *builder, void *result, size_t *length)
139 {
140 esp_err_t ret = ESP_OK;
141 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
142 RC5_CHECK(result && length, "result and length can't be null", err, ESP_ERR_INVALID_ARG);
143 *(rmt_item32_t **)result = rc5_builder->buffer;
144 *length = rc5_builder->cursor;
145 return ESP_OK;
146 err:
147 return ret;
148 }
149
rc5_builder_del(ir_builder_t * builder)150 static esp_err_t rc5_builder_del(ir_builder_t *builder)
151 {
152 rc5_builder_t *rc5_builder = __containerof(builder, rc5_builder_t, parent);
153 free(rc5_builder);
154 return ESP_OK;
155 }
156
ir_builder_rmt_new_rc5(const ir_builder_config_t * config)157 ir_builder_t *ir_builder_rmt_new_rc5(const ir_builder_config_t *config)
158 {
159 ir_builder_t *ret = NULL;
160 RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
161 RC5_CHECK(config->buffer_size, "buffer size can't be zero", err, NULL);
162
163 uint32_t builder_size = sizeof(rc5_builder_t) + config->buffer_size * sizeof(rmt_item32_t);
164 rc5_builder_t *rc5_builder = calloc(1, builder_size);
165 RC5_CHECK(rc5_builder, "request memory for rc5_builder failed", err, NULL);
166
167 rc5_builder->buffer_size = config->buffer_size;
168 rc5_builder->flags = config->flags;
169 if (config->flags & IR_TOOLS_FLAGS_INVERSE) {
170 rc5_builder->inverse = true;
171 }
172
173 uint32_t counter_clk_hz = 0;
174 RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
175 "get rmt counter clock failed", err, NULL);
176 float ratio = (float)counter_clk_hz / 1e6;
177 rc5_builder->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
178 rc5_builder->parent.make_head = rc5_builder_make_head;
179 rc5_builder->parent.make_logic0 = rc5_builder_make_logic0;
180 rc5_builder->parent.make_logic1 = rc5_builder_make_logic1;
181 rc5_builder->parent.make_end = rc5_builder_make_end;
182 rc5_builder->parent.build_frame = rc5_build_frame;
183 rc5_builder->parent.build_repeat_frame = rc5_build_repeat_frame;
184 rc5_builder->parent.get_result = rc5_builder_get_result;
185 rc5_builder->parent.del = rc5_builder_del;
186 rc5_builder->parent.repeat_period_ms = 114;
187 return &rc5_builder->parent;
188 err:
189 return ret;
190 }
191