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_parser";
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 #define RC5_MAX_FRAME_RMT_WORDS (14) // S1+S2+T+ADDR(5)+CMD(6)
34 
35 typedef struct {
36     ir_parser_t parent;
37     uint32_t flags;
38     uint32_t pulse_duration_ticks;
39     uint32_t margin_ticks;
40     rmt_item32_t *buffer;
41     uint32_t buffer_len;
42     uint32_t last_command;
43     uint32_t last_address;
44     bool last_t_bit;
45 } rc5_parser_t;
46 
rc5_check_in_range(uint32_t raw_ticks,uint32_t target_ticks,uint32_t margin_ticks)47 static inline bool rc5_check_in_range(uint32_t raw_ticks, uint32_t target_ticks, uint32_t margin_ticks)
48 {
49     return (raw_ticks < (target_ticks + margin_ticks)) && (raw_ticks > (target_ticks - margin_ticks));
50 }
51 
rc5_parser_input(ir_parser_t * parser,void * raw_data,uint32_t length)52 static esp_err_t rc5_parser_input(ir_parser_t *parser, void *raw_data, uint32_t length)
53 {
54     esp_err_t ret = ESP_OK;
55     rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
56     rc5_parser->buffer = raw_data;
57     rc5_parser->buffer_len = length;
58     if (length > RC5_MAX_FRAME_RMT_WORDS) {
59         ret = ESP_FAIL;
60     }
61     return ret;
62 }
63 
rc5_duration_one_unit(rc5_parser_t * rc5_parser,uint32_t duration)64 static inline bool rc5_duration_one_unit(rc5_parser_t *rc5_parser, uint32_t duration)
65 {
66     return (duration < (rc5_parser->pulse_duration_ticks + rc5_parser->margin_ticks)) &&
67            (duration > (rc5_parser->pulse_duration_ticks - rc5_parser->margin_ticks));
68 }
69 
rc5_duration_two_unit(rc5_parser_t * rc5_parser,uint32_t duration)70 static inline bool rc5_duration_two_unit(rc5_parser_t *rc5_parser, uint32_t duration)
71 {
72     return (duration < (rc5_parser->pulse_duration_ticks * 2 + rc5_parser->margin_ticks)) &&
73            (duration > (rc5_parser->pulse_duration_ticks * 2 - rc5_parser->margin_ticks));
74 }
75 
rc5_parser_get_scan_code(ir_parser_t * parser,uint32_t * address,uint32_t * command,bool * repeat)76 static esp_err_t rc5_parser_get_scan_code(ir_parser_t *parser, uint32_t *address, uint32_t *command, bool *repeat)
77 {
78     esp_err_t ret = ESP_FAIL;
79     uint32_t parse_result = 0; // 32 bit is enough to hold the parse result of one RC5 frame
80     uint32_t addr = 0;
81     uint32_t cmd = 0;
82     bool s1 = true;
83     bool s2 = true;
84     bool t = false;
85     bool exchange = false;
86     rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
87     RC5_CHECK(address && command && repeat, "address, command and repeat can't be null", out, ESP_ERR_INVALID_ARG);
88     for (int i = 0; i < rc5_parser->buffer_len; i++) {
89         if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
90             parse_result <<= 1;
91             parse_result |= exchange;
92             if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
93                 exchange = !exchange;
94             }
95         } else if (rc5_duration_two_unit(rc5_parser, rc5_parser->buffer[i].duration0)) {
96             parse_result <<= 1;
97             parse_result |= rc5_parser->buffer[i].level0;
98             parse_result <<= 1;
99             parse_result |= !rc5_parser->buffer[i].level0;
100             if (rc5_duration_one_unit(rc5_parser, rc5_parser->buffer[i].duration1)) {
101                 exchange = !exchange;
102             }
103         } else {
104             goto out;
105         }
106     }
107     if (!(rc5_parser->flags & IR_TOOLS_FLAGS_INVERSE)) {
108         parse_result = ~parse_result;
109     }
110     s1 = ((parse_result & 0x2000) >> 13) & 0x01;
111     s2 = ((parse_result & 0x1000) >> 12) & 0x01;
112     t = ((parse_result & 0x800) >> 11) & 0x01;
113     // Check S1, must be 1
114     if (s1) {
115         if (!(rc5_parser->flags & IR_TOOLS_FLAGS_PROTO_EXT) && !s2) {
116             // Not standard RC5 protocol, but S2 is 0
117             goto out;
118         }
119         addr = (parse_result & 0x7C0) >> 6;
120         cmd = (parse_result & 0x3F);
121         if (!s2) {
122             cmd |= 1 << 6;
123         }
124         *repeat = (t == rc5_parser->last_t_bit && addr == rc5_parser->last_address && cmd == rc5_parser->last_command);
125         *address = addr;
126         *command = cmd;
127         rc5_parser->last_address = addr;
128         rc5_parser->last_command = cmd;
129         rc5_parser->last_t_bit = t;
130         ret = ESP_OK;
131     }
132 out:
133     return ret;
134 }
135 
rc5_parser_del(ir_parser_t * parser)136 static esp_err_t rc5_parser_del(ir_parser_t *parser)
137 {
138     rc5_parser_t *rc5_parser = __containerof(parser, rc5_parser_t, parent);
139     free(rc5_parser);
140     return ESP_OK;
141 }
142 
ir_parser_rmt_new_rc5(const ir_parser_config_t * config)143 ir_parser_t *ir_parser_rmt_new_rc5(const ir_parser_config_t *config)
144 {
145     ir_parser_t *ret = NULL;
146     RC5_CHECK(config, "rc5 configuration can't be null", err, NULL);
147 
148     rc5_parser_t *rc5_parser = calloc(1, sizeof(rc5_parser_t));
149     RC5_CHECK(rc5_parser, "request memory for rc5_parser failed", err, NULL);
150 
151     rc5_parser->flags = config->flags;
152 
153     uint32_t counter_clk_hz = 0;
154     RC5_CHECK(rmt_get_counter_clock((rmt_channel_t)config->dev_hdl, &counter_clk_hz) == ESP_OK,
155               "get rmt counter clock failed", err, NULL);
156     float ratio = (float)counter_clk_hz / 1e6;
157     rc5_parser->pulse_duration_ticks = (uint32_t)(ratio * RC5_PULSE_DURATION_US);
158     rc5_parser->margin_ticks = (uint32_t)(ratio * config->margin_us);
159     rc5_parser->parent.input = rc5_parser_input;
160     rc5_parser->parent.get_scan_code = rc5_parser_get_scan_code;
161     rc5_parser->parent.del = rc5_parser_del;
162     return &rc5_parser->parent;
163 err:
164     return ret;
165 }
166