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