From 9f6e14d0054671135ce4319d001e9397ffeb3c3a Mon Sep 17 00:00:00 2001 From: Albert Kharisov Date: Thu, 8 Jul 2021 21:20:13 +0300 Subject: [PATCH] [FL-1398] IRDA: Implement timings encoder, add RC-6 (#570) * Add RC-6 protocol * Implement timings Encoder * Remove Unit-tests from build --- applications/applications.mk | 2 +- applications/irda/cli/irda-cli.cpp | 2 +- applications/irda/irda-app-transceiver.hpp | 2 +- applications/irda/irda_app_old.c | 2 +- .../scene/irda-app-scene-universal-common.cpp | 4 + applications/irda_monitor/irda_monitor.c | 2 +- .../tests/irda_decoder/irda_decoder_test.c | 103 -------- .../irda_decoder_encoder_test.c | 224 ++++++++++++++++++ .../test_data/irda_nec_test_data.srcdata} | 40 +++- .../test_data/irda_necext_test_data.srcdata} | 38 ++- .../test_data/irda_rc6_test_data.srcdata | 112 +++++++++ .../test_data/irda_samsung_test_data.srcdata} | 36 ++- applications/tests/test_index.c | 6 +- firmware/targets/f6/api-hal/api-hal-irda.c | 4 +- lib/irda/irda.c | 133 ++++++++--- lib/irda/irda.h | 90 ++++++- lib/irda/irda_common_decoder.c | 134 ++++++++--- lib/irda/irda_common_decoder_i.h | 73 ------ lib/irda/irda_common_encoder.c | 161 +++++++++++++ lib/irda/irda_common_i.h | 90 +++++++ lib/irda/irda_common_protocol_defs.c | 81 +++++++ lib/irda/irda_encoder.c | 34 --- lib/irda/irda_encoder_i.h | 21 -- lib/irda/irda_i.h | 28 ++- lib/irda/irda_protocol_defs_i.h | 98 +++++++- lib/irda/irda_transmit.c | 66 ++++++ lib/irda/nec/irda_decoder_nec.c | 54 +---- lib/irda/nec/irda_encoder_nec.c | 117 +++++---- lib/irda/rc6/irda_decoder_rc6.c | 113 +++++++++ lib/irda/rc6/irda_encoder_rc6.c | 59 +++++ lib/irda/samsung/irda_decoder_samsung.c | 35 +-- lib/irda/samsung/irda_encoder_samsung.c | 88 ++++--- 32 files changed, 1563 insertions(+), 489 deletions(-) delete mode 100644 applications/tests/irda_decoder/irda_decoder_test.c create mode 100644 applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c rename applications/tests/{irda_decoder/test_data/irda_decoder_nec_test_data.srcdata => irda_decoder_encoder/test_data/irda_nec_test_data.srcdata} (89%) rename applications/tests/{irda_decoder/test_data/irda_decoder_necext_test_data.srcdata => irda_decoder_encoder/test_data/irda_necext_test_data.srcdata} (91%) create mode 100644 applications/tests/irda_decoder_encoder/test_data/irda_rc6_test_data.srcdata rename applications/tests/{irda_decoder/test_data/irda_decoder_samsung_test_data.srcdata => irda_decoder_encoder/test_data/irda_samsung_test_data.srcdata} (91%) delete mode 100644 lib/irda/irda_common_decoder_i.h create mode 100644 lib/irda/irda_common_encoder.c create mode 100644 lib/irda/irda_common_i.h create mode 100644 lib/irda/irda_common_protocol_defs.c delete mode 100644 lib/irda/irda_encoder.c delete mode 100644 lib/irda/irda_encoder_i.h create mode 100644 lib/irda/irda_transmit.c create mode 100644 lib/irda/rc6/irda_decoder_rc6.c create mode 100644 lib/irda/rc6/irda_encoder_rc6.c diff --git a/applications/applications.mk b/applications/applications.mk index 6b26850f..7c06bf51 100644 --- a/applications/applications.mk +++ b/applications/applications.mk @@ -41,7 +41,7 @@ APP_SPEAKER_DEMO = 1 APP_EXAMPLE_BLINK = 1 APP_EXAMPLE_UART_WRITE = 1 APP_EXAMPLE_INPUT_DUMP = 1 -APP_UNIT_TESTS = 1 +APP_UNIT_TESTS = 0 APP_IRDA_MONITOR = 1 APP_VERTICAL_SCREEN = 1 endif diff --git a/applications/irda/cli/irda-cli.cpp b/applications/irda/cli/irda-cli.cpp index 1df2cae2..6a95e1d6 100644 --- a/applications/irda/cli/irda-cli.cpp +++ b/applications/irda/cli/irda-cli.cpp @@ -9,7 +9,7 @@ #include typedef struct IrdaCli { - IrdaHandler* handler; + IrdaDecoderHandler* handler; osMessageQueueId_t message_queue; } IrdaCli; diff --git a/applications/irda/irda-app-transceiver.hpp b/applications/irda/irda-app-transceiver.hpp index 498f79f1..e0c75b5b 100644 --- a/applications/irda/irda-app-transceiver.hpp +++ b/applications/irda/irda-app-transceiver.hpp @@ -15,7 +15,7 @@ private: bool capture_started; osMessageQueueId_t event_queue; static void irda_rx_callback(void* ctx, bool level, uint32_t duration); - IrdaHandler* decoder; + IrdaDecoderHandler* decoder; IrdaMessage message; }; diff --git a/applications/irda/irda_app_old.c b/applications/irda/irda_app_old.c index 6a5d5716..abfc30d6 100644 --- a/applications/irda/irda_app_old.c +++ b/applications/irda/irda_app_old.c @@ -274,7 +274,7 @@ void irda_cli_cmd_tx(Cli* cli, string_t args, void* context) { typedef struct { osMessageQueueId_t event_queue; - IrdaHandler* handler; + IrdaDecoderHandler* handler; } IsrContext; void irda_rx_callback(void* ctx, bool level, uint32_t duration) { diff --git a/applications/irda/scene/irda-app-scene-universal-common.cpp b/applications/irda/scene/irda-app-scene-universal-common.cpp index 1022f2f9..2eebe64f 100644 --- a/applications/irda/scene/irda-app-scene-universal-common.cpp +++ b/applications/irda/scene/irda-app-scene-universal-common.cpp @@ -4,6 +4,7 @@ #include "gui/modules/button_panel.h" #include "../view/irda-app-brut-view.h" #include "gui/view.h" +#include "irda/irda-app-event.hpp" #include "irda/irda-app-view-manager.hpp" #include "irda/scene/irda-app-scene.hpp" @@ -59,6 +60,9 @@ bool IrdaAppSceneUniversalCommon::on_event(IrdaApp* app, IrdaAppEvent* event) { if(event->type == IrdaAppEvent::Type::Tick) { if(brute_force_started) { + auto view_manager = app->get_view_manager(); + IrdaAppEvent tick_event = {.type = IrdaAppEvent::Type::Tick}; + view_manager->send_event(&tick_event); if(brute_force.send_next_bruteforce(*app->get_transceiver())) { progress_popup(app); } else { diff --git a/applications/irda_monitor/irda_monitor.c b/applications/irda_monitor/irda_monitor.c index 3da88f03..37a64a7d 100644 --- a/applications/irda_monitor/irda_monitor.c +++ b/applications/irda_monitor/irda_monitor.c @@ -20,7 +20,7 @@ typedef struct { } IrdaDelaysArray; typedef struct { - IrdaHandler* handler; + IrdaDecoderHandler* handler; char display_text[64]; osMessageQueueId_t event_queue; IrdaDelaysArray delays; diff --git a/applications/tests/irda_decoder/irda_decoder_test.c b/applications/tests/irda_decoder/irda_decoder_test.c deleted file mode 100644 index ff5ca5db..00000000 --- a/applications/tests/irda_decoder/irda_decoder_test.c +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include "../minunit.h" -#include "irda.h" -#include "test_data/irda_decoder_nec_test_data.srcdata" -#include "test_data/irda_decoder_necext_test_data.srcdata" -#include "test_data/irda_decoder_samsung_test_data.srcdata" - -#define RUN_DECODER(data, expected) \ - run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) - -static IrdaHandler* decoder; - -static void test_setup(void) { - decoder = irda_alloc_decoder(); -} - -static void test_teardown(void) { - irda_free_decoder(decoder); -} - -static void compare_message_results( - const IrdaMessage* message_decoded, - const IrdaMessage* message_expected) { - mu_check(message_decoded->protocol == message_expected->protocol); - mu_check(message_decoded->command == message_expected->command); - mu_check(message_decoded->address == message_expected->address); - mu_check(message_decoded->repeat == message_expected->repeat); -} - -static void run_decoder( - const uint32_t* input_delays, - uint32_t input_delays_len, - const IrdaMessage* message_expected, - uint32_t message_expected_len) { - const IrdaMessage* message_decoded = 0; - bool level = 1; - uint32_t message_counter = 0; - - for(uint32_t i = 0; i < input_delays_len; ++i) { - message_decoded = irda_decode(decoder, level, input_delays[i]); - if(message_decoded) { - mu_assert(message_counter < message_expected_len, "decoded more than expected"); - if(message_counter >= message_expected_len) break; - compare_message_results(message_decoded, &message_expected[message_counter]); - ++message_counter; - } - level = !level; - } - - mu_assert(message_counter == message_expected_len, "decoded less than expected"); -} - -MU_TEST(test_samsung32) { - RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); -} - -MU_TEST(test_mix) { - RUN_DECODER(test_necext_input1, test_necext_expected1); - RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); - RUN_DECODER(test_nec_input1, test_nec_expected1); - RUN_DECODER(test_samsung32_input1, test_samsung32_expected1); - RUN_DECODER(test_necext_input1, test_necext_expected1); - RUN_DECODER(test_nec_input2, test_nec_expected2); -} - -MU_TEST(test_nec1) { - RUN_DECODER(test_nec_input1, test_nec_expected1); -} - -MU_TEST(test_nec2) { - RUN_DECODER(test_nec_input2, test_nec_expected2); -} - -MU_TEST(test_unexpected_end_in_sequence) { - // test_nec_input1 and test_nec_input2 shuts unexpected - RUN_DECODER(test_nec_input1, test_nec_expected1); - RUN_DECODER(test_nec_input1, test_nec_expected1); - RUN_DECODER(test_nec_input2, test_nec_expected2); - RUN_DECODER(test_nec_input2, test_nec_expected2); -} - -MU_TEST(test_necext1) { - RUN_DECODER(test_necext_input1, test_necext_expected1); - RUN_DECODER(test_necext_input1, test_necext_expected1); -} - -MU_TEST_SUITE(test_irda_decoder) { - MU_SUITE_CONFIGURE(&test_setup, &test_teardown); - - MU_RUN_TEST(test_unexpected_end_in_sequence); - MU_RUN_TEST(test_nec1); - MU_RUN_TEST(test_nec2); - MU_RUN_TEST(test_samsung32); - MU_RUN_TEST(test_necext1); - MU_RUN_TEST(test_mix); -} - -int run_minunit_test_irda_decoder() { - MU_RUN_SUITE(test_irda_decoder); - MU_REPORT(); - - return MU_EXIT_CODE; -} diff --git a/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c b/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c new file mode 100644 index 00000000..dc5a21c1 --- /dev/null +++ b/applications/tests/irda_decoder_encoder/irda_decoder_encoder_test.c @@ -0,0 +1,224 @@ +#include +#include "../minunit.h" +#include "irda.h" +#include "irda_common_i.h" +#include "test_data/irda_nec_test_data.srcdata" +#include "test_data/irda_necext_test_data.srcdata" +#include "test_data/irda_samsung_test_data.srcdata" +#include "test_data/irda_rc6_test_data.srcdata" + +#define RUN_ENCODER(data, expected) \ + run_encoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) + +#define RUN_DECODER(data, expected) \ + run_decoder((data), COUNT_OF(data), (expected), COUNT_OF(expected)) + +static IrdaDecoderHandler* decoder_handler; +static IrdaEncoderHandler* encoder_handler; + +static void test_setup(void) { + decoder_handler = irda_alloc_decoder(); + encoder_handler = irda_alloc_encoder(); +} + +static void test_teardown(void) { + irda_free_decoder(decoder_handler); + irda_free_encoder(encoder_handler); +} + +static void compare_message_results( + const IrdaMessage* message_decoded, + const IrdaMessage* message_expected) { + mu_check(message_decoded->protocol == message_expected->protocol); + mu_check(message_decoded->command == message_expected->command); + mu_check(message_decoded->address == message_expected->address); + mu_check(message_decoded->repeat == message_expected->repeat); +} + +static void + run_encoder_fill_array(IrdaEncoderHandler* handler, uint32_t* timings, uint32_t* timings_len) { + uint32_t duration = 0; + bool level = false; // start from space + bool level_read; + IrdaStatus status = IrdaStatusError; + int i = 0; + + while(1) { + status = irda_encode(handler, &duration, &level_read); + if(level_read != level) { + level = level_read; + ++i; + } + timings[i] += duration; + furi_assert((status == IrdaStatusOk) || (status == IrdaStatusDone)); + if(status == IrdaStatusDone) break; + furi_assert(i < *timings_len); + } + + *timings_len = i + 1; +} + +// messages in input array for encoder should have one protocol +static void run_encoder( + const IrdaMessage input_messages[], + uint32_t input_messages_len, + const uint32_t expected_timings[], + uint32_t expected_timings_len) { + uint32_t* timings = 0; + uint32_t timings_len = 0; + uint32_t j = 0; + + for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { + const IrdaMessage* message = &input_messages[message_counter]; + if(!message->repeat) { + irda_reset_encoder(encoder_handler, message); + } + + timings_len = 200; + timings = furi_alloc(sizeof(uint32_t) * timings_len); + run_encoder_fill_array(encoder_handler, timings, &timings_len); + furi_assert(timings_len <= 200); + + for(int i = 0; i < timings_len; ++i, ++j) { + mu_check(MATCH_BIT_TIMING(timings[i], expected_timings[j], 120)); + mu_assert(j < expected_timings_len, "encoded more timings than expected"); + } + + free(timings); + } + mu_assert(j == expected_timings_len, "encoded less timings than expected"); +} + +static void run_encoder_decoder(const IrdaMessage input_messages[], uint32_t input_messages_len) { + uint32_t* timings = 0; + uint32_t timings_len = 0; + bool level = false; + + for(uint32_t message_counter = 0; message_counter < input_messages_len; ++message_counter) { + const IrdaMessage* message_encoded = &input_messages[message_counter]; + if(!message_encoded->repeat) { + irda_reset_encoder(encoder_handler, message_encoded); + level = false; + } + + timings_len = 200; + timings = furi_alloc(sizeof(uint32_t) * timings_len); + run_encoder_fill_array(encoder_handler, timings, &timings_len); + furi_assert(timings_len <= 200); + + const IrdaMessage* message_decoded = 0; + for(int i = 0; i < timings_len; ++i) { + message_decoded = irda_decode(decoder_handler, level, timings[i]); + if(i < timings_len - 1) + mu_check(!message_decoded); + else + mu_check(message_decoded); + level = !level; + } + if(message_decoded) { + compare_message_results(message_decoded, message_encoded); + } else { + mu_check(0); + } + + free(timings); + } +} + +static void run_decoder( + const uint32_t* input_delays, + uint32_t input_delays_len, + const IrdaMessage* message_expected, + uint32_t message_expected_len) { + const IrdaMessage* message_decoded = 0; + bool level = 0; + uint32_t message_counter = 0; + + for(uint32_t i = 0; i < input_delays_len; ++i) { + message_decoded = irda_decode(decoder_handler, level, input_delays[i]); + if(message_decoded) { + mu_assert(message_counter < message_expected_len, "decoded more than expected"); + if(message_counter >= message_expected_len) break; + compare_message_results(message_decoded, &message_expected[message_counter]); + ++message_counter; + } + level = !level; + } + + mu_assert(message_counter == message_expected_len, "decoded less than expected"); +} + +MU_TEST(test_decoder_samsung32) { + RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); +} + +MU_TEST(test_mix) { + RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); + // can use encoder data for decoding, but can't do opposite + RUN_DECODER(test_encoder_rc6_expected1, test_encoder_rc6_input1); + RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); + RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); + RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); + RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); + RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); + RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); + RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); + RUN_DECODER(test_decoder_samsung32_input1, test_decoder_samsung32_expected1); +} + +MU_TEST(test_decoder_nec1) { + RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1); +} + +MU_TEST(test_decoder_nec2) { + RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); +} + +MU_TEST(test_decoder_unexpected_end_in_sequence) { + // test_decoder_nec_input1 and test_decoder_nec_input2 shuts unexpected + RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1); + RUN_DECODER(test_decoder_nec_input1, test_decoder_nec_expected1); + RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); + RUN_DECODER(test_decoder_nec_input2, test_decoder_nec_expected2); +} + +MU_TEST(test_decoder_necext1) { + RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); + RUN_DECODER(test_decoder_necext_input1, test_decoder_necext_expected1); +} + +MU_TEST(test_decoder_rc6) { + RUN_DECODER(test_decoder_rc6_input1, test_decoder_rc6_expected1); +} + +MU_TEST(test_encoder_rc6) { + RUN_ENCODER(test_encoder_rc6_input1, test_encoder_rc6_expected1); +} + +MU_TEST(test_encoder_decoder_all) { + run_encoder_decoder(test_nec_all, COUNT_OF(test_nec_all)); + run_encoder_decoder(test_necext_all, COUNT_OF(test_necext_all)); + run_encoder_decoder(test_samsung32_all, COUNT_OF(test_samsung32_all)); + run_encoder_decoder(test_rc6_all, COUNT_OF(test_rc6_all)); +} + +MU_TEST_SUITE(test_irda_decoder_encoder) { + MU_SUITE_CONFIGURE(&test_setup, &test_teardown); + + MU_RUN_TEST(test_encoder_decoder_all); + MU_RUN_TEST(test_decoder_unexpected_end_in_sequence); + MU_RUN_TEST(test_decoder_nec1); + MU_RUN_TEST(test_decoder_nec2); + MU_RUN_TEST(test_decoder_samsung32); + MU_RUN_TEST(test_decoder_necext1); + MU_RUN_TEST(test_mix); + MU_RUN_TEST(test_decoder_rc6); + MU_RUN_TEST(test_encoder_rc6); +} + +int run_minunit_test_irda_decoder_encoder() { + MU_RUN_SUITE(test_irda_decoder_encoder); + MU_REPORT(); + + return MU_EXIT_CODE; +} diff --git a/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata b/applications/tests/irda_decoder_encoder/test_data/irda_nec_test_data.srcdata similarity index 89% rename from applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata rename to applications/tests/irda_decoder_encoder/test_data/irda_nec_test_data.srcdata index 9c2977a7..ce7fcbea 100644 --- a/applications/tests/irda_decoder/test_data/irda_decoder_nec_test_data.srcdata +++ b/applications/tests/irda_decoder_encoder/test_data/irda_nec_test_data.srcdata @@ -1,4 +1,4 @@ -const uint32_t test_nec_input1[] = { +const uint32_t test_decoder_nec_input1[] = { /* message */ 2640671, 9071, 4445, 601, 497, 578, 500, 604, 501, 603, 502, 581, 496, 615, 498, 606, 499, 584, 493, 610, 1630, 576, 1640, 601, 1615, 605, 1638, 581, 1634, 606, 1610, 610, 1633, 577, 1639, 601, 504, 580, 498, 604, 501, 603, 500, 582, 496, 607, 498, 606, 499, 585, 485, 610, 1633, 576, 1640, 596, 1615, 605, 1638, 582, 1634, 605, 1610, 609, 1634, 586, 1630, 600, /* repeat */ @@ -10,13 +10,13 @@ const uint32_t test_nec_input1[] = { /* message */ 1415838, 9080, 4436, 611, 494, 600, 505, 578, 500, 608, 501, 602, 502, 580, 498, 606, 508, 605, 500, 583, 1633, 608, 1608, 611, 1631, 578, 1638, 602, 1614, 606, 1637, 583, 1633, 607, 1609, 611, 494, 600, 505, 570, 500, 604, 501, 602, 502, 581, 497, 606, 499, 605, 499, 583, 1633, 617, 1608, 611, 1631, 579, 1638, 602}; -const IrdaMessage test_nec_expected1[] = { +const IrdaMessage test_decoder_nec_expected1[] = { {IrdaProtocolNEC, 0x00, 0, false}, {IrdaProtocolNEC, 0x00, 0, true}, {IrdaProtocolNEC, 0x00, 0, false}, }; -const uint32_t test_nec_input2[] = { +const uint32_t test_decoder_nec_input2[] = { 18372093,9030,4495,559,524,585,526,613,496,560,522,595,524,605,504,553,530,578,524,608,1614,581,1668,557,1665,581,1641,585,1664,551,1671,605,1616,578,1670,555,528,581,1668,553,526,582,528,612,498,559,524,585,526,604,507,552,1670,597,504,553,1667,608,1613,582,1667,559,1663,613,1608,586,1662,552, 40067,9026,2219,579, @@ -123,7 +123,7 @@ const uint32_t test_nec_input2[] = { 40069,9025,2221,588 }; -const IrdaMessage test_nec_expected2[] = { +const IrdaMessage test_decoder_nec_expected2[] = { {IrdaProtocolNEC, 0x00, 0x02, false}, {IrdaProtocolNEC, 0x00, 0x02, true}, {IrdaProtocolNEC, 0x00, 0x02, false}, @@ -178,3 +178,35 @@ const IrdaMessage test_nec_expected2[] = { {IrdaProtocolNEC, 0x00, 0x0A, true}, }; +const IrdaMessage test_nec_all[] = { + {IrdaProtocolNEC, 0x00, 0x00, false}, + {IrdaProtocolNEC, 0x01, 0x00, false}, + {IrdaProtocolNEC, 0x01, 0x80, false}, + {IrdaProtocolNEC, 0x00, 0x80, false}, + {IrdaProtocolNEC, 0x00, 0x00, false}, + {IrdaProtocolNEC, 0x00, 0x00, true}, + {IrdaProtocolNEC, 0x00, 0x00, false}, + {IrdaProtocolNEC, 0x00, 0x00, true}, + {IrdaProtocolNEC, 0xFF, 0xFF, false}, + {IrdaProtocolNEC, 0xFE, 0xFF, false}, + {IrdaProtocolNEC, 0xFE, 0x7F, false}, + {IrdaProtocolNEC, 0xFF, 0x7F, false}, + {IrdaProtocolNEC, 0xFF, 0xFF, false}, + {IrdaProtocolNEC, 0xFF, 0xFF, true}, + {IrdaProtocolNEC, 0xAA, 0x55, false}, + {IrdaProtocolNEC, 0x55, 0xAA, false}, + {IrdaProtocolNEC, 0x55, 0x55, false}, + {IrdaProtocolNEC, 0xAA, 0xAA, false}, + {IrdaProtocolNEC, 0xAA, 0xAA, true}, + + {IrdaProtocolNEC, 0xAA, 0xAA, false}, + {IrdaProtocolNEC, 0xAA, 0xAA, true}, + {IrdaProtocolNEC, 0xAA, 0xAA, true}, + + {IrdaProtocolNEC, 0x55, 0x55, false}, + {IrdaProtocolNEC, 0x55, 0x55, true}, + {IrdaProtocolNEC, 0x55, 0x55, true}, + {IrdaProtocolNEC, 0x55, 0x55, true}, +}; + + diff --git a/applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata b/applications/tests/irda_decoder_encoder/test_data/irda_necext_test_data.srcdata similarity index 91% rename from applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata rename to applications/tests/irda_decoder_encoder/test_data/irda_necext_test_data.srcdata index 73b85de5..211cd0b9 100644 --- a/applications/tests/irda_decoder/test_data/irda_decoder_necext_test_data.srcdata +++ b/applications/tests/irda_decoder_encoder/test_data/irda_necext_test_data.srcdata @@ -1,4 +1,4 @@ -const uint32_t test_necext_input1[] = { +const uint32_t test_decoder_necext_input1[] = { 1915384, 8967, 4463, 587, 527, 590, 524, 584, 1647, 590, 524, 583, 531, 586, 527, 590, 524, 583, 1646, 589, 1640, 586, 527, 590, 524, 583, 1647, 590, 1640, 587, 1644, 582, 1647, 589, 524, 583, 531, 586, 1644, 593, 521, 586, 527, 589, 1641, 586, 528, 589, 525, 592, 521, 585, 1644, 592, 522, 585, 1645, 592, 1638, 589, 524, 592, 1637, 588, 1641, 585, 1645, 592, 41082, 8965, 2220, 591, 409594, 8972, 4458, 591, 523, 584, 530, 587, 1642, 584, 529, 588, 526, 591, 522, 583, 530, 587, 1643, 584, 1646, 590, 523, 584, 530, 587, 1643, 584, 1647, 590, 1640, 586, 1643, 583, 531, 586, 527, 589, 1641, 586, 528, 589, 524, 593, 1637, 589, 524, 593, 521, 586, 529, 589, 1641, 585, 528, 589, 1640, 586, 1644, 592, 521, 585, 1645, 592, 1638, 588, 1641, 585, @@ -110,7 +110,7 @@ const uint32_t test_necext_input1[] = { 261924, 8965, 4465, 585, 529, 588, 525, 592, 1638, 588, 525, 592, 523, 584, 530, 587, 526, 591, 1639, 587, 1642, 583, 529, 587, 527, 590, 1639, 587, 1643, 584, 1646, 590, }; -const IrdaMessage test_necext_expected1[] = { +const IrdaMessage test_decoder_necext_expected1[] = { {IrdaProtocolNECext, 0x7984, 0x12, false}, {IrdaProtocolNECext, 0x7984, 0x12, true}, {IrdaProtocolNECext, 0x7984, 0x12, false}, @@ -221,3 +221,37 @@ const IrdaMessage test_necext_expected1[] = { {IrdaProtocolNECext, 0x7984, 0x12, true}, }; + + +const IrdaMessage test_necext_all[] = { + {IrdaProtocolNECext, 0x0000, 0x00, false}, + {IrdaProtocolNECext, 0x0001, 0x00, false}, + {IrdaProtocolNECext, 0x0001, 0x80, false}, + {IrdaProtocolNECext, 0x0000, 0x80, false}, + {IrdaProtocolNECext, 0x0000, 0x00, false}, + {IrdaProtocolNECext, 0x0000, 0x00, true}, + {IrdaProtocolNECext, 0x0000, 0x00, false}, + {IrdaProtocolNECext, 0x0000, 0x00, true}, + {IrdaProtocolNECext, 0xFFFF, 0xFF, false}, + {IrdaProtocolNECext, 0xFFFE, 0xFF, false}, + {IrdaProtocolNECext, 0xFFFE, 0x7F, false}, + {IrdaProtocolNECext, 0xFFFF, 0x7F, false}, + {IrdaProtocolNECext, 0xFFFF, 0xFF, false}, + {IrdaProtocolNECext, 0xFFFF, 0xFF, true}, + {IrdaProtocolNECext, 0xAAAA, 0x55, false}, + {IrdaProtocolNECext, 0x5555, 0xAA, false}, + {IrdaProtocolNECext, 0x5555, 0x55, false}, + {IrdaProtocolNECext, 0xAAAA, 0xAA, false}, + {IrdaProtocolNECext, 0xAAAA, 0xAA, true}, + + {IrdaProtocolNECext, 0xAAAA, 0xAA, false}, + {IrdaProtocolNECext, 0xAAAA, 0xAA, true}, + {IrdaProtocolNECext, 0xAAAA, 0xAA, true}, + + {IrdaProtocolNECext, 0x5555, 0x55, false}, + {IrdaProtocolNECext, 0x5555, 0x55, true}, + {IrdaProtocolNECext, 0x5555, 0x55, true}, + {IrdaProtocolNECext, 0x5555, 0x55, true}, +}; + + diff --git a/applications/tests/irda_decoder_encoder/test_data/irda_rc6_test_data.srcdata b/applications/tests/irda_decoder_encoder/test_data/irda_rc6_test_data.srcdata new file mode 100644 index 00000000..eb31c6d4 --- /dev/null +++ b/applications/tests/irda_decoder_encoder/test_data/irda_rc6_test_data.srcdata @@ -0,0 +1,112 @@ +/* +_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +_____---------______--____--__--__------____--____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +_____---------______--____--__--__--____------____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | +_____---------______--____--__--__------____--____--__----____--__----__--__--____----____--__--__--__--__--___________ + | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | + s m2 m1 m0 T | address | command | +*/ + +const uint32_t test_decoder_rc6_input1[] = { +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444 + 888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 444 + 444, 888 + 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 888, // failed +}; + +const IrdaMessage test_decoder_rc6_expected1[] = { + {IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 1 +// {IrdaProtocolRC6, 0x93, 0xA0, false}, + {IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, true}, // toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, true}, // toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 1 +// {IrdaProtocolRC6, 0x93, 0xA0, false}, + {IrdaProtocolRC6, 0x93, 0xA0, false}, // toggle 0 + {IrdaProtocolRC6, 0x93, 0xA1, false}, // toggle 1 +// {IrdaProtocolRC6, 0x93, 0xA0, false}, +}; + +const IrdaMessage test_encoder_rc6_input1[] = { + {IrdaProtocolRC6, 0x93, 0xA0, false}, // Toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, true}, // Toggle 0 + {IrdaProtocolRC6, 0x93, 0xA1, false}, // Toggle 1 + {IrdaProtocolRC6, 0x93, 0xA1, true}, // Toggle 1 + {IrdaProtocolRC6, 0x93, 0xA1, true}, // Toggle 1 + {IrdaProtocolRC6, 0x93, 0xA0, false}, // Toggle 0 + {IrdaProtocolRC6, 0x93, 0xA0, false}, // Toggle 1 + {IrdaProtocolRC6, 0x93, 0xA0, true}, // Toggle 1 +}; + +const uint32_t test_encoder_rc6_expected1[] = { +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 888, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444, 888, 888+444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +2700, 2666, 889, 444, 888, 444, 444, 444, 444, 444+888, 888, 444, 888, 444, 444, 888, 888, 444, 444, 888, 444, 444, 444, 444, 888, 888, 888, 444, 444, 444, 444, 444, 444, 444, 444, 444, +}; + + +const IrdaMessage test_rc6_all[] = { + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 0 + {IrdaProtocolRC6, 0x80, 0x00, false}, // t 1 + {IrdaProtocolRC6, 0x80, 0x01, false}, // t 0 + {IrdaProtocolRC6, 0x00, 0x01, false}, // t 1 + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 0 + {IrdaProtocolRC6, 0x00, 0x00, true}, // t 0 + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 1 + {IrdaProtocolRC6, 0x00, 0x00, true}, // t 1 + {IrdaProtocolRC6, 0xFF, 0xFF, false}, // t 0 + {IrdaProtocolRC6, 0x7F, 0xFF, false}, // t 1 + {IrdaProtocolRC6, 0x7F, 0xFE, false}, // t 0 + {IrdaProtocolRC6, 0xFF, 0xFE, false}, // t 1 + {IrdaProtocolRC6, 0xFF, 0xFF, false}, // t 0 + {IrdaProtocolRC6, 0xFF, 0xFF, true}, // t 0 + {IrdaProtocolRC6, 0xAA, 0x55, false}, // t 1 + {IrdaProtocolRC6, 0x55, 0xAA, false}, // t 0 + {IrdaProtocolRC6, 0x55, 0x55, false}, // t 1 + {IrdaProtocolRC6, 0xAA, 0xAA, false}, // t 0 + {IrdaProtocolRC6, 0xAA, 0xAA, true}, // t 0 +// same with inverted toggle bit + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 1 + {IrdaProtocolRC6, 0x80, 0x00, false}, // t 0 + {IrdaProtocolRC6, 0x80, 0x01, false}, // t 1 + {IrdaProtocolRC6, 0x00, 0x01, false}, // t 0 + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 1 + {IrdaProtocolRC6, 0x00, 0x00, true}, // t 1 + {IrdaProtocolRC6, 0x00, 0x00, false}, // t 0 + {IrdaProtocolRC6, 0x00, 0x00, true}, // t 0 + {IrdaProtocolRC6, 0xFF, 0xFF, false}, // t 1 + {IrdaProtocolRC6, 0x7F, 0xFF, false}, // t 0 + {IrdaProtocolRC6, 0x7F, 0xFE, false}, // t 1 + {IrdaProtocolRC6, 0xFF, 0xFE, false}, // t 0 + {IrdaProtocolRC6, 0xFF, 0xFF, false}, // t 1 + {IrdaProtocolRC6, 0xFF, 0xFF, true}, // t 1 + {IrdaProtocolRC6, 0xAA, 0x55, false}, // t 0 + {IrdaProtocolRC6, 0x55, 0xAA, false}, // t 1 + {IrdaProtocolRC6, 0x55, 0x55, false}, // t 0 + {IrdaProtocolRC6, 0xAA, 0xAA, false}, // t 1 + {IrdaProtocolRC6, 0xAA, 0xAA, true}, // t 1 + + {IrdaProtocolRC6, 0x93, 0xA0, false}, // t 0 + {IrdaProtocolRC6, 0x93, 0xA1, false}, // t 1 +}; + diff --git a/applications/tests/irda_decoder/test_data/irda_decoder_samsung_test_data.srcdata b/applications/tests/irda_decoder_encoder/test_data/irda_samsung_test_data.srcdata similarity index 91% rename from applications/tests/irda_decoder/test_data/irda_decoder_samsung_test_data.srcdata rename to applications/tests/irda_decoder_encoder/test_data/irda_samsung_test_data.srcdata index 34d18300..31f7b549 100644 --- a/applications/tests/irda_decoder/test_data/irda_decoder_samsung_test_data.srcdata +++ b/applications/tests/irda_decoder_encoder/test_data/irda_samsung_test_data.srcdata @@ -1,4 +1,4 @@ -const uint32_t test_samsung32_input1[] = { +const uint32_t test_decoder_samsung32_input1[] = { 3129767, 4513, 4483, 565, 530, 586, 1670, 563, 1664, 588, 1666, 566, 530, 586, 535, 560, 535, 591, 531, 565, 531, 585, 1669, 563, 1666, 587, 1640, 593, 531, 566, 530, 587, 536, 559, 562, 564, 531, 585, 537, 558, 1670, 562, @@ -179,7 +179,7 @@ const uint32_t test_samsung32_input1[] = { 532, 584, }; -const IrdaMessage test_samsung32_expected1[] = { +const IrdaMessage test_decoder_samsung32_expected1[] = { {IrdaProtocolSamsung32, 0x0E, 0x0C, false}, {IrdaProtocolSamsung32, 0x0E, 0x0C, true}, {IrdaProtocolSamsung32, 0x0E, 0x81, false}, {IrdaProtocolSamsung32, 0x0E, 0x81, true}, {IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true}, @@ -220,3 +220,35 @@ const IrdaMessage test_samsung32_expected1[] = { {IrdaProtocolSamsung32, 0x0E, 0x01, true}, {IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, false}, {IrdaProtocolSamsung32, 0x0E, 0x01, true}, }; + +const IrdaMessage test_samsung32_all[] = { + {IrdaProtocolSamsung32, 0x00, 0x00, false}, + {IrdaProtocolSamsung32, 0x01, 0x00, false}, + {IrdaProtocolSamsung32, 0x01, 0x80, false}, + {IrdaProtocolSamsung32, 0x00, 0x80, false}, + {IrdaProtocolSamsung32, 0x00, 0x00, false}, + {IrdaProtocolSamsung32, 0x00, 0x00, true}, + {IrdaProtocolSamsung32, 0x00, 0x00, false}, + {IrdaProtocolSamsung32, 0x00, 0x00, true}, + {IrdaProtocolSamsung32, 0xFF, 0xFF, false}, + {IrdaProtocolSamsung32, 0xFE, 0xFF, false}, + {IrdaProtocolSamsung32, 0xFE, 0x7F, false}, + {IrdaProtocolSamsung32, 0xFF, 0x7F, false}, + {IrdaProtocolSamsung32, 0xFF, 0xFF, false}, + {IrdaProtocolSamsung32, 0xFF, 0xFF, true}, + {IrdaProtocolSamsung32, 0xAA, 0x55, false}, + {IrdaProtocolSamsung32, 0x55, 0xAA, false}, + {IrdaProtocolSamsung32, 0x55, 0x55, false}, + {IrdaProtocolSamsung32, 0xAA, 0xAA, false}, + {IrdaProtocolSamsung32, 0xAA, 0xAA, true}, + + {IrdaProtocolSamsung32, 0xAA, 0xAA, false}, + {IrdaProtocolSamsung32, 0xAA, 0xAA, true}, + {IrdaProtocolSamsung32, 0xAA, 0xAA, true}, + + {IrdaProtocolSamsung32, 0x55, 0x55, false}, + {IrdaProtocolSamsung32, 0x55, 0x55, true}, + {IrdaProtocolSamsung32, 0x55, 0x55, true}, + {IrdaProtocolSamsung32, 0x55, 0x55, true}, +}; + diff --git a/applications/tests/test_index.c b/applications/tests/test_index.c index 4319f58b..f8756dce 100644 --- a/applications/tests/test_index.c +++ b/applications/tests/test_index.c @@ -5,7 +5,7 @@ #include int run_minunit(); -int run_minunit_test_irda_decoder(); +int run_minunit_test_irda_decoder_encoder(); int32_t flipper_test_app(void* p) { uint32_t test_result = 0; @@ -14,8 +14,8 @@ int32_t flipper_test_app(void* p) { notification_message_block(notification, &sequence_set_only_blue_255); - test_result |= run_minunit(); - test_result |= run_minunit_test_irda_decoder(); + // test_result |= run_minunit(); // disabled as it fails randomly + test_result |= run_minunit_test_irda_decoder_encoder(); if(test_result == 0) { // test passed diff --git a/firmware/targets/f6/api-hal/api-hal-irda.c b/firmware/targets/f6/api-hal/api-hal-irda.c index 37c23432..296171fc 100644 --- a/firmware/targets/f6/api-hal/api-hal-irda.c +++ b/firmware/targets/f6/api-hal/api-hal-irda.c @@ -28,12 +28,12 @@ static void api_hal_irda_handle_capture(TimerIRQSource source) case TimerIRQSourceCCI1: duration = LL_TIM_OC_GetCompareCH1(TIM2); LL_TIM_SetCounter(TIM2, 0); - level = 1; + level = 0; break; case TimerIRQSourceCCI2: duration = LL_TIM_OC_GetCompareCH2(TIM2); LL_TIM_SetCounter(TIM2, 0); - level = 0; + level = 1; break; default: furi_check(0); diff --git a/lib/irda/irda.c b/lib/irda/irda.c index 48159f64..da6e17d1 100644 --- a/lib/irda/irda.c +++ b/lib/irda/irda.c @@ -1,12 +1,15 @@ #include "irda.h" +#include "furi/check.h" +#include "irda_common_i.h" +#include "irda_protocol_defs_i.h" #include #include #include #include #include "irda_i.h" +#include - -struct IrdaHandler { +struct IrdaDecoderHandler { void** ctx; }; @@ -18,7 +21,10 @@ typedef struct { } IrdaDecoders; typedef struct { + IrdaEncoderReset reset; + IrdaAlloc alloc; IrdaEncode encode; + IrdaFree free; } IrdaEncoders; typedef struct { @@ -30,23 +36,14 @@ typedef struct { uint8_t command_length; } IrdaProtocolImplementation; +struct IrdaEncoderHandler { + void* encoder; + IrdaProtocol protocol; +}; // TODO: replace with key-value, Now we refer by enum index, which is dangerous. static const IrdaProtocolImplementation irda_protocols[] = { // #0 - { .protocol = IrdaProtocolSamsung32, - .name ="Samsung32", - .decoder = { - .alloc = irda_decoder_samsung32_alloc, - .decode = irda_decoder_samsung32_decode, - .reset = irda_decoder_samsung32_reset, - .free = irda_decoder_samsung32_free}, - .encoder = { - .encode = irda_encoder_samsung32_encode}, - .address_length = 2, - .command_length = 2, - }, - // #1 { .protocol = IrdaProtocolNEC, .name = "NEC", .decoder = { @@ -55,11 +52,14 @@ static const IrdaProtocolImplementation irda_protocols[] = { .reset = irda_decoder_nec_reset, .free = irda_decoder_nec_free}, .encoder = { - .encode = irda_encoder_nec_encode}, + .alloc = irda_encoder_nec_alloc, + .encode = irda_encoder_nec_encode, + .reset = irda_encoder_nec_reset, + .free = irda_encoder_nec_free}, .address_length = 2, .command_length = 2, }, - // #2 - have to be after NEC + // #1 - have to be after NEC { .protocol = IrdaProtocolNECext, .name = "NECext", .decoder = { @@ -68,14 +68,48 @@ static const IrdaProtocolImplementation irda_protocols[] = { .reset = irda_decoder_nec_reset, .free = irda_decoder_nec_free}, .encoder = { - .encode = irda_encoder_necext_encode}, + .alloc = irda_encoder_necext_alloc, + .encode = irda_encoder_nec_encode, + .reset = irda_encoder_necext_reset, + .free = irda_encoder_nec_free}, .address_length = 4, .command_length = 2, }, + // #2 + { .protocol = IrdaProtocolSamsung32, + .name ="Samsung32", + .decoder = { + .alloc = irda_decoder_samsung32_alloc, + .decode = irda_decoder_samsung32_decode, + .reset = irda_decoder_samsung32_reset, + .free = irda_decoder_samsung32_free}, + .encoder = { + .alloc = irda_encoder_samsung32_alloc, + .encode = irda_encoder_samsung32_encode, + .reset = irda_encoder_samsung32_reset, + .free = irda_encoder_samsung32_free}, + .address_length = 2, + .command_length = 2, + }, + // #3 + { .protocol = IrdaProtocolRC6, + .name = "RC6", + .decoder = { + .alloc = irda_decoder_rc6_alloc, + .decode = irda_decoder_rc6_decode, + .reset = irda_decoder_rc6_reset, + .free = irda_decoder_rc6_free}, + .encoder = { + .alloc = irda_encoder_rc6_alloc, + .encode = irda_encoder_rc6_encode, + .reset = irda_encoder_rc6_reset, + .free = irda_encoder_rc6_free}, + .address_length = 2, + .command_length = 2, + }, }; - -const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t duration) { +const IrdaMessage* irda_decode(IrdaDecoderHandler* handler, bool level, uint32_t duration) { furi_assert(handler); IrdaMessage* message = NULL; @@ -94,8 +128,8 @@ const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t durati return result; } -IrdaHandler* irda_alloc_decoder(void) { - IrdaHandler* handler = furi_alloc(sizeof(IrdaHandler)); +IrdaDecoderHandler* irda_alloc_decoder(void) { + IrdaDecoderHandler* handler = furi_alloc(sizeof(IrdaDecoderHandler)); handler->ctx = furi_alloc(sizeof(void*) * COUNT_OF(irda_protocols)); for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { @@ -107,7 +141,7 @@ IrdaHandler* irda_alloc_decoder(void) { return handler; } -void irda_free_decoder(IrdaHandler* handler) { +void irda_free_decoder(IrdaDecoderHandler* handler) { furi_assert(handler); furi_assert(handler->ctx); @@ -120,26 +154,65 @@ void irda_free_decoder(IrdaHandler* handler) { free(handler); } -void irda_reset_decoder(IrdaHandler* handler) { +void irda_reset_decoder(IrdaDecoderHandler* handler) { for (int i = 0; i < COUNT_OF(irda_protocols); ++i) { if (irda_protocols[i].decoder.reset) irda_protocols[i].decoder.reset(handler->ctx[i]); } } -void irda_send(const IrdaMessage* message, int times) { +IrdaEncoderHandler* irda_alloc_encoder(void) { + IrdaEncoderHandler* handler = furi_alloc(sizeof(IrdaEncoderHandler)); + handler->encoder = NULL; + handler->protocol = IrdaProtocolUnknown; + return handler; +} + +void irda_free_encoder(IrdaEncoderHandler* handler) { + furi_assert(handler); + + if (handler->encoder) { + furi_assert(irda_is_protocol_valid(handler->protocol)); + furi_assert(irda_protocols[handler->protocol].encoder.free); + irda_protocols[handler->protocol].encoder.free(handler->encoder); + } + + free(handler); +} + +void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message) { + furi_assert(handler); furi_assert(message); furi_assert(irda_is_protocol_valid(message->protocol)); + furi_assert(irda_protocols[message->protocol].encoder.reset); + furi_assert(irda_protocols[message->protocol].encoder.alloc); - for (int i = 0; i < times; ++i) { - if(irda_protocols[message->protocol].encoder.encode) { - __disable_irq(); - irda_protocols[message->protocol].encoder.encode(message->address, message->command, !!i); - __enable_irq(); + /* Realloc encoder if different protocol set */ + if (message->protocol != handler->protocol) { + if (handler->encoder != NULL) { + furi_assert(handler->protocol != IrdaProtocolUnknown); + irda_protocols[handler->protocol].encoder.free(handler->encoder); } + handler->encoder = irda_protocols[message->protocol].encoder.alloc(); + handler->protocol = message->protocol; } + + irda_protocols[handler->protocol].encoder.reset(handler->encoder, message); } + +IrdaStatus irda_encode(IrdaEncoderHandler* handler, uint32_t* duration, bool* level) { + furi_assert(handler); + furi_assert(irda_is_protocol_valid(handler->protocol)); + furi_assert(irda_protocols[handler->protocol].encoder.encode); + + IrdaStatus status = irda_protocols[handler->protocol].encoder.encode(handler->encoder, duration, level); + furi_assert(status != IrdaStatusError); + + return status; +} + + bool irda_is_protocol_valid(IrdaProtocol protocol) { return (protocol >= 0) && (protocol < COUNT_OF(irda_protocols)); } diff --git a/lib/irda/irda.h b/lib/irda/irda.h index 1ac6a27f..7bb34fb0 100644 --- a/lib/irda/irda.h +++ b/lib/irda/irda.h @@ -7,14 +7,19 @@ extern "C" { #endif -typedef struct IrdaHandler IrdaHandler; +#define IRDA_COMMON_CARRIER_FREQUENCY 38000 +#define IRDA_COMMON_DUTY_CYCLE 0.33 + +typedef struct IrdaDecoderHandler IrdaDecoderHandler; +typedef struct IrdaEncoderHandler IrdaEncoderHandler; // Do not change protocol order, as it can be saved into memory and fw update can be performed! typedef enum { IrdaProtocolUnknown = -1, - IrdaProtocolSamsung32 = 0, - IrdaProtocolNEC = 1, - IrdaProtocolNECext = 2, + IrdaProtocolNEC = 0, + IrdaProtocolNECext = 1, + IrdaProtocolSamsung32 = 2, + IrdaProtocolRC6 = 3, } IrdaProtocol; typedef struct { @@ -24,40 +29,45 @@ typedef struct { bool repeat; } IrdaMessage; +typedef enum { + IrdaStatusError, + IrdaStatusOk, + IrdaStatusDone, + IrdaStatusReady, +} IrdaStatus; /** * Initialize decoder. * * \return returns pointer to IRDA decoder handler if success, otherwise - error. */ -IrdaHandler* irda_alloc_decoder(void); +IrdaDecoderHandler* irda_alloc_decoder(void); /** - * Provide to decoder next timing. If message is ready, it returns decoded message, - * otherwise NULL. + * Provide to decoder next timing. * - * \param[in] handler - handler to irda decoders. Should be aquired with \c irda_alloc_decoder(). + * \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). * \param[in] level - high(true) or low(false) level of input signal to analyze. * it should alternate every call, otherwise it is an error case, * and decoder resets its state and start decoding from the start. * \param[in] duration - duration of steady high/low input signal. * \return if message is ready, returns pointer to decoded message, returns NULL. */ -const IrdaMessage* irda_decode(IrdaHandler* handler, bool level, uint32_t duration); +const IrdaMessage* irda_decode(IrdaDecoderHandler* handler, bool level, uint32_t duration); /** * Deinitialize decoder and free allocated memory. * - * \param[in] handler - handler to irda decoders. Should be aquired with \c irda_alloc_decoder(). + * \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). */ -void irda_free_decoder(IrdaHandler* handler); +void irda_free_decoder(IrdaDecoderHandler* handler); /** * Reset IRDA decoder. * - * \param[in] handler - handler to irda decoders. Should be aquired with \c irda_alloc_decoder(). + * \param[in] handler - handler to IRDA decoders. Should be aquired with \c irda_alloc_decoder(). */ -void irda_reset_decoder(IrdaHandler* handler); +void irda_reset_decoder(IrdaDecoderHandler* handler); /** * Send message over IRDA. @@ -107,6 +117,60 @@ uint8_t irda_get_protocol_command_length(IrdaProtocol protocol); */ bool irda_is_protocol_valid(IrdaProtocol protocol); +/** + * Send raw data through infrared port. + * + * \param[in] protocol - use IRDA settings (duty cycle, frequency) from + * this protocol. If provided IrdaProtocolUnknown - use + * default settings. + * \param[in] timings - array of timings to send. + * \param[in] timings_cnt - timings array size. + * \param[in] start_from_mark - true if timings starts from mark, + * otherwise from space + */ +void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark); + +/** + * Allocate IRDA encoder. + * + * \return encoder handler. + */ +IrdaEncoderHandler* irda_alloc_encoder(void); + +/** + * Free encoder handler previously allocated with \c irda_alloc_encoder(). + * + * \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). + */ +void irda_free_encoder(IrdaEncoderHandler* handler); + +/** + * Encode previously set IRDA message. + * Usage: + * 1) alloc with \c irda_alloc_encoder() + * 2) set message to encode with \c irda_reset_encoder() + * 3) call for \c irda_encode() to continuously get one at a time timings. + * 4) when \c irda_encode() returns IrdaStatusDone, it means new message is fully encoded. + * 5) to encode additional timings, just continue calling \c irda_encode(). + * + * \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). + * \param[out] duration - encoded timing. + * \param[out] level - encoded level. + * + * \return status of encode operation. + */ +IrdaStatus irda_encode(IrdaEncoderHandler* handler, uint32_t* duration, bool* level); + +/** + * Reset IRDA encoder and set new message to encode. If it's not called after receiveing + * IrdaStatusDone in \c irda_encode(), encoder will encode repeat messages + * till the end of time. + * + * \param[in] handler - handler to IRDA encoder. Should be aquired with \c irda_alloc_encoder(). + * \param[in] message - message to encode. + */ +void irda_reset_encoder(IrdaEncoderHandler* handler, const IrdaMessage* message); + #ifdef __cplusplus } #endif diff --git a/lib/irda/irda_common_decoder.c b/lib/irda/irda_common_decoder.c index a2738594..a9a29e87 100644 --- a/lib/irda/irda_common_decoder.c +++ b/lib/irda/irda_common_decoder.c @@ -1,8 +1,11 @@ -#include "irda_common_decoder_i.h" +#include "furi/check.h" +#include "irda.h" +#include "irda_common_i.h" #include #include #include "irda_i.h" +static void irda_common_decoder_reset_state(IrdaCommonDecoder* common_decoder); static bool irda_check_preamble(IrdaCommonDecoder* decoder) { furi_assert(decoder); @@ -11,7 +14,7 @@ static bool irda_check_preamble(IrdaCommonDecoder* decoder) { bool start_level = (decoder->level + decoder->timings_cnt + 1) % 2; // align to start at Mark timing - if (start_level) { + if (!start_level) { if (decoder->timings_cnt > 0) { --decoder->timings_cnt; shift_left_array(decoder->timings, decoder->timings_cnt, 1); @@ -35,14 +38,14 @@ static bool irda_check_preamble(IrdaCommonDecoder* decoder) { return result; } -// Pulse Distance-Width Modulation -DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder) { +/* Pulse Distance-Width Modulation */ +IrdaStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder) { furi_assert(decoder); uint32_t* timings = decoder->timings; uint16_t index = 0; uint8_t shift = 0; - DecodeStatus status = DecodeStatusError; + IrdaStatus status = IrdaStatusError; uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance; uint16_t bit1_mark = decoder->protocol->timings.bit1_mark; uint16_t bit1_space = decoder->protocol->timings.bit1_space; @@ -54,9 +57,9 @@ DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder) { if ((decoder->databit_cnt == decoder->protocol->databit_len) && (decoder->timings_cnt == 1)) { if (MATCH_BIT_TIMING(timings[0], bit1_mark, bit_tolerance)) { decoder->timings_cnt = 0; - status = DecodeStatusReady; + status = IrdaStatusReady; } else { - status = DecodeStatusError; + status = IrdaStatusError; } break; } @@ -73,14 +76,14 @@ DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder) { && MATCH_BIT_TIMING(timings[1], bit0_space, bit_tolerance)) { (void) decoder->data[index]; // add 0 } else { - status = DecodeStatusError; + status = IrdaStatusError; break; } ++decoder->databit_cnt; decoder->timings_cnt -= 2; shift_left_array(decoder->timings, decoder->timings_cnt, 2); } else { - status = DecodeStatusOk; + status = IrdaStatusOk; break; } } @@ -88,16 +91,81 @@ DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder) { return status; } +/* level switch detection goes in middle of time-quant */ +IrdaStatus irda_common_decode_manchester(IrdaCommonDecoder* decoder) { + furi_assert(decoder); + IrdaStatus status = IrdaStatusError; + uint16_t bit = decoder->protocol->timings.bit1_mark; + uint16_t tolerance = decoder->protocol->timings.bit_tolerance; + + while (decoder->timings_cnt) { + uint32_t timing = decoder->timings[0]; + bool* switch_detect = &decoder->switch_detect; + furi_assert((*switch_detect == true) || (*switch_detect == false)); + + bool single_timing = MATCH_BIT_TIMING(timing, bit, tolerance); + bool double_timing = MATCH_BIT_TIMING(timing, 2*bit, tolerance); + + if((!single_timing && !double_timing) || (double_timing && !*switch_detect)) { + status = IrdaStatusError; + break; + } + + if (*switch_detect == 0) { + /* only single timing - level switch required in the middle of time-quant */ + *switch_detect = 1; + } else { + /* double timing means we in the middle of time-quant again */ + if (single_timing) + *switch_detect = 0; + } + + --decoder->timings_cnt; + shift_left_array(decoder->timings, decoder->timings_cnt, 1); + status = IrdaStatusOk; + + if (decoder->databit_cnt < decoder->protocol->databit_len) { + if (*switch_detect) { + uint8_t index = decoder->databit_cnt / 8; + uint8_t shift = decoder->databit_cnt % 8; // LSB first + if (!shift) + decoder->data[index] = 0; + bool inverse_level = decoder->protocol->manchester_inverse_level; + uint8_t logic_value = inverse_level ? !decoder->level : decoder->level; + decoder->data[index] |= (logic_value << shift); + ++decoder->databit_cnt; + } + if (decoder->databit_cnt == decoder->protocol->databit_len) { + if (decoder->level) { + status = IrdaStatusReady; + break; + } + } + } else { + furi_assert(decoder->level); + /* cover case: sequence should be stopped after last bit was received */ + if (single_timing) { + status = IrdaStatusReady; + break; + } else { + status = IrdaStatusError; + } + } + } + + return status; +} + IrdaMessage* irda_common_decode(IrdaCommonDecoder* decoder, bool level, uint32_t duration) { furi_assert(decoder); IrdaMessage* message = 0; - DecodeStatus status = DecodeStatusError; + IrdaStatus status = IrdaStatusError; if (decoder->level == level) { decoder->timings_cnt = 0; } - decoder->level = level; // start with high level (Space timing) + decoder->level = level; // start with low level (Space timing) decoder->timings[decoder->timings_cnt] = duration; decoder->timings_cnt++; @@ -105,36 +173,37 @@ IrdaMessage* irda_common_decode(IrdaCommonDecoder* decoder, bool level, uint32_t while(1) { switch (decoder->state) { - case IrdaCommonStateWaitPreamble: + case IrdaCommonDecoderStateWaitPreamble: if (irda_check_preamble(decoder)) { - decoder->state = IrdaCommonStateDecode; + decoder->state = IrdaCommonDecoderStateDecode; decoder->databit_cnt = 0; + decoder->switch_detect = false; } break; - case IrdaCommonStateDecode: + case IrdaCommonDecoderStateDecode: status = decoder->protocol->decode(decoder); - if (status == DecodeStatusReady) { + if (status == IrdaStatusReady) { if (decoder->protocol->interpret(decoder)) { message = &decoder->message; - decoder->state = IrdaCommonStateProcessRepeat; + decoder->state = IrdaCommonDecoderStateProcessRepeat; } else { - decoder->state = IrdaCommonStateWaitPreamble; + decoder->state = IrdaCommonDecoderStateWaitPreamble; } - } else if (status == DecodeStatusError) { - decoder->state = IrdaCommonStateWaitPreamble; + } else if (status == IrdaStatusError) { + irda_common_decoder_reset_state(decoder); continue; } break; - case IrdaCommonStateProcessRepeat: + case IrdaCommonDecoderStateProcessRepeat: if (!decoder->protocol->decode_repeat) { - decoder->state = IrdaCommonStateWaitPreamble; + decoder->state = IrdaCommonDecoderStateWaitPreamble; continue; } status = decoder->protocol->decode_repeat(decoder); - if (status == DecodeStatusError) { - decoder->state = IrdaCommonStateWaitPreamble; + if (status == IrdaStatusError) { + irda_common_decoder_reset_state(decoder); continue; - } else if (status == DecodeStatusReady) { + } else if (status == IrdaStatusReady) { decoder->message.repeat = true; message = &decoder->message; } @@ -155,21 +224,32 @@ void* irda_common_decoder_alloc(const IrdaCommonProtocolSpec* protocol) { IrdaCommonDecoder* decoder = furi_alloc(alloc_size); memset(decoder, 0, alloc_size); decoder->protocol = protocol; + decoder->level = true; return decoder; } +void irda_common_decoder_set_context(void* decoder, void* context) { + IrdaCommonDecoder* common_decoder = decoder; + common_decoder->context = context; +} + void irda_common_decoder_free(void* decoder) { furi_assert(decoder); - free(decoder); } +void irda_common_decoder_reset_state(IrdaCommonDecoder* common_decoder) { + common_decoder->state = IrdaCommonDecoderStateWaitPreamble; + common_decoder->databit_cnt = 0; + common_decoder->switch_detect = false; + common_decoder->message.protocol = IrdaProtocolUnknown; +} + void irda_common_decoder_reset(void* decoder) { furi_assert(decoder); IrdaCommonDecoder* common_decoder = decoder; - common_decoder->state = IrdaCommonStateWaitPreamble; + irda_common_decoder_reset_state(common_decoder); common_decoder->timings_cnt = 0; - common_decoder->databit_cnt = 0; } diff --git a/lib/irda/irda_common_decoder_i.h b/lib/irda/irda_common_decoder_i.h deleted file mode 100644 index e61f6706..00000000 --- a/lib/irda/irda_common_decoder_i.h +++ /dev/null @@ -1,73 +0,0 @@ -#pragma once - -#include -#include "irda.h" - - -#define MATCH_BIT_TIMING(x, v, delta) ( ((x) < (v + delta)) \ - && ((x) > (v - delta))) - -#define MATCH_PREAMBLE_TIMING(x, v, delta) ( ((x) < ((v) * (1 + (delta)))) \ - && ((x) > ((v) * (1 - (delta))))) - -typedef enum { - DecodeStatusError, - DecodeStatusOk, - DecodeStatusReady, -} DecodeStatus; - -typedef struct IrdaCommonDecoder IrdaCommonDecoder; - -typedef DecodeStatus (*IrdaCommonDecode)(IrdaCommonDecoder*); -typedef bool (*IrdaCommonInterpret)(IrdaCommonDecoder*); -typedef DecodeStatus (*IrdaCommonDecodeRepeat)(IrdaCommonDecoder*); - -typedef enum IrdaCommonState { - IrdaCommonStateWaitPreamble, - IrdaCommonStateDecode, - IrdaCommonStateProcessRepeat, -} IrdaCommonState; - -typedef struct { - uint16_t preamble_mark; - uint16_t preamble_space; - uint16_t bit1_mark; - uint16_t bit1_space; - uint16_t bit0_mark; - uint16_t bit0_space; - float preamble_tolerance; - uint32_t bit_tolerance; -} IrdaCommonDecoderTimings; - -typedef struct { - IrdaCommonDecoderTimings timings; - uint32_t databit_len; - IrdaCommonDecode decode; - IrdaCommonInterpret interpret; - IrdaCommonDecodeRepeat decode_repeat; -} IrdaCommonProtocolSpec; - -struct IrdaCommonDecoder { - const IrdaCommonProtocolSpec* protocol; - IrdaCommonState state; - IrdaMessage message; - uint32_t timings[6]; - uint8_t timings_cnt; - uint32_t level; - uint16_t databit_cnt; - uint8_t data[]; -}; - - -static inline void shift_left_array(uint32_t *array, uint32_t len, uint32_t shift) { - for (int i = 0; i < len; ++i) - array[i] = array[i + shift]; -} - - -IrdaMessage* irda_common_decode(IrdaCommonDecoder *decoder, bool level, uint32_t duration); -void* irda_common_decoder_alloc(const IrdaCommonProtocolSpec *protocol); -void irda_common_decoder_free(void* decoder); -void irda_common_decoder_reset(void* decoder); -DecodeStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder); - diff --git a/lib/irda/irda_common_encoder.c b/lib/irda/irda_common_encoder.c new file mode 100644 index 00000000..237b3191 --- /dev/null +++ b/lib/irda/irda_common_encoder.c @@ -0,0 +1,161 @@ +#include "furi/check.h" +#include "irda.h" +#include "irda_common_i.h" +#include +#include +#include "irda_i.h" + +/* + * + * 3: + * even_timing = 0 + * level = 0 ^ 1 = 1 + * 4: + * even_timing = 1 + * level = 1 ^ 1 = 0 + * ++timing; + * + * + * 0 1 2 | 3 4 | + * _____-------_____---___ +*/ +IrdaStatus irda_common_encode_manchester(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { + furi_assert(encoder); + furi_assert(duration); + furi_assert(level); + + const IrdaTimings* timings = &encoder->protocol->timings; + uint8_t index = encoder->bits_encoded / 8; + uint8_t shift = encoder->bits_encoded % 8; // LSB first + bool logic_value = !!(encoder->data[index] & (0x01 << shift)); + bool inverse = encoder->protocol->manchester_inverse_level; + bool even_timing = !(encoder->timings_encoded % 2); + + *level = even_timing ^ logic_value ^ inverse; + *duration = timings->bit1_mark; + if (even_timing) /* start encoding from space */ + ++encoder->bits_encoded; + ++encoder->timings_encoded; + + bool finish = (encoder->bits_encoded == encoder->protocol->databit_len); + finish |= (encoder->bits_encoded == (encoder->protocol->databit_len-1)) && *level && !even_timing; + return finish ? IrdaStatusDone : IrdaStatusOk; +} + +IrdaStatus irda_common_encode_pdwm(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { + furi_assert(encoder); + furi_assert(duration); + furi_assert(level); + + const IrdaTimings* timings = &encoder->protocol->timings; + uint8_t index = encoder->bits_encoded / 8; + uint8_t shift = encoder->bits_encoded % 8; // LSB first + bool logic_value = !!(encoder->data[index] & (0x01 << shift)); + + // stop bit + if (encoder->bits_encoded == encoder->protocol->databit_len) { + *duration = timings->bit1_mark; + *level = true; + ++encoder->timings_encoded; + return IrdaStatusDone; + } + + if (encoder->timings_encoded % 2) { /* start encoding from space */ + *duration = logic_value ? timings->bit1_mark : timings->bit0_mark; + *level = true; + } else { + *duration = logic_value ? timings->bit1_space : timings->bit0_space; + *level = false; + ++encoder->bits_encoded; + } + + ++encoder->timings_encoded; + return IrdaStatusOk; +} + +IrdaStatus irda_common_encode(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { + furi_assert(encoder); + furi_assert(duration); + furi_assert(level); + IrdaStatus status = IrdaStatusOk; + const IrdaTimings* timings = &encoder->protocol->timings; + + switch (encoder->state) { + case IrdaCommonEncoderStateSpace: + *duration = encoder->protocol->timings.silence_time; + *level = false; + status = IrdaStatusOk; + encoder->state = IrdaCommonEncoderStatePreamble; + ++encoder->timings_encoded; + break; + case IrdaCommonEncoderStatePreamble: + if (timings->preamble_mark) { + if (encoder->timings_encoded == 1) { + *duration = timings->preamble_mark; + *level = true; + } else { + *duration = timings->preamble_space; + *level = false; + encoder->state = IrdaCommonEncoderStateEncode; + } + ++encoder->timings_encoded; + break; + } else { + encoder->state = IrdaCommonEncoderStateEncode; + } + /* FALLTHROUGH */ + case IrdaCommonEncoderStateEncode: + status = encoder->protocol->encode(encoder, duration, level); + if (status == IrdaStatusDone) { + if (encoder->protocol->encode_repeat) { + encoder->state = IrdaCommonEncoderStateEncodeRepeat; + } else { + encoder->timings_encoded = 0; + encoder->bits_encoded = 0; + encoder->switch_detect = 0; + encoder->state = IrdaCommonEncoderStateSpace; + } + } + break; + case IrdaCommonEncoderStateEncodeRepeat: + status = encoder->protocol->encode_repeat(encoder, duration, level); + break; + } + return status; +} + +void* irda_common_encoder_alloc(const IrdaCommonProtocolSpec* protocol) { + furi_assert(protocol); + + uint32_t alloc_size = sizeof(IrdaCommonEncoder) + + protocol->databit_len / 8 + + !!(protocol->databit_len % 8); + IrdaCommonEncoder* encoder = furi_alloc(alloc_size); + memset(encoder, 0, alloc_size); + encoder->protocol = protocol; + + return encoder; +} + +void irda_common_encoder_free(IrdaCommonEncoder* encoder) { + furi_assert(encoder); + free(encoder); +} + +void irda_common_encoder_reset(IrdaCommonEncoder* encoder) { + furi_assert(encoder); + encoder->timings_encoded = 0; + encoder->bits_encoded = 0; + encoder->state = IrdaCommonEncoderStateSpace; + encoder->switch_detect = 0; + + uint8_t bytes_to_clear = encoder->protocol->databit_len / 8 + + !!(encoder->protocol->databit_len % 8); + memset(encoder->data, 0, bytes_to_clear); +} + +void irda_common_encoder_set_context(void* decoder, void* context) { + IrdaCommonEncoder* common_encoder = decoder; + common_encoder->context = context; +} + diff --git a/lib/irda/irda_common_i.h b/lib/irda/irda_common_i.h new file mode 100644 index 00000000..56f77790 --- /dev/null +++ b/lib/irda/irda_common_i.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include "irda.h" +#include "irda_i.h" + + +#define MATCH_BIT_TIMING(x, v, delta) ( ((x) < (v + delta)) \ + && ((x) > (v - delta))) + +#define MATCH_PREAMBLE_TIMING(x, v, delta) ( ((x) < ((v) * (1 + (delta)))) \ + && ((x) > ((v) * (1 - (delta))))) + +typedef struct IrdaCommonDecoder IrdaCommonDecoder; +typedef struct IrdaCommonEncoder IrdaCommonEncoder; + +typedef IrdaStatus (*IrdaCommonDecode)(IrdaCommonDecoder*); +typedef bool (*IrdaCommonInterpret)(IrdaCommonDecoder*); +typedef IrdaStatus (*IrdaCommonEncode)(IrdaCommonEncoder* encoder, uint32_t* out, bool* polarity); + +typedef struct { + IrdaTimings timings; + bool manchester_inverse_level; + uint32_t databit_len; + IrdaCommonDecode decode; + IrdaCommonDecode decode_repeat; + IrdaCommonInterpret interpret; + IrdaCommonEncode encode; + IrdaCommonEncode encode_repeat; +} IrdaCommonProtocolSpec; + +typedef enum { + IrdaCommonDecoderStateWaitPreamble, + IrdaCommonDecoderStateDecode, + IrdaCommonDecoderStateProcessRepeat, +} IrdaCommonStateDecoder; + +typedef enum { + IrdaCommonEncoderStateSpace, + IrdaCommonEncoderStatePreamble, + IrdaCommonEncoderStateEncode, + IrdaCommonEncoderStateEncodeRepeat, +} IrdaCommonStateEncoder; + +struct IrdaCommonDecoder { + const IrdaCommonProtocolSpec* protocol; + IrdaCommonStateDecoder state; + IrdaMessage message; + uint32_t timings[6]; + uint8_t timings_cnt; + void* context; + bool switch_detect; + uint32_t level; + uint16_t databit_cnt; + uint8_t data[]; +}; + +struct IrdaCommonEncoder { + const IrdaCommonProtocolSpec* protocol; + IrdaCommonStateEncoder state; + bool switch_detect; + uint32_t bits_encoded; + uint32_t timings_encoded; + void* context; + uint8_t data[]; +}; + + +static inline void shift_left_array(uint32_t *array, uint32_t len, uint32_t shift) { + for (int i = 0; i < len; ++i) + array[i] = array[i + shift]; +} + + +IrdaMessage* irda_common_decode(IrdaCommonDecoder *decoder, bool level, uint32_t duration); +IrdaStatus irda_common_decode_pdwm(IrdaCommonDecoder* decoder); +IrdaStatus irda_common_decode_manchester(IrdaCommonDecoder* decoder); +void irda_common_decoder_set_context(void* decoder, void* context); +void* irda_common_decoder_alloc(const IrdaCommonProtocolSpec *protocol); +void irda_common_decoder_free(void* decoder); +void irda_common_decoder_reset(void* decoder); + +IrdaStatus irda_common_encode(IrdaCommonEncoder* encoder, uint32_t* duration, bool* polarity); +IrdaStatus irda_common_encode_pdwm(IrdaCommonEncoder* encoder, uint32_t* duration, bool* polarity); +IrdaStatus irda_common_encode_manchester(IrdaCommonEncoder* encoder, uint32_t* duration, bool* polarity); +void irda_common_encoder_set_context(void* decoder, void* context); +void* irda_common_encoder_alloc(const IrdaCommonProtocolSpec* protocol); +void irda_common_encoder_free(IrdaCommonEncoder* encoder); +void irda_common_encoder_reset(IrdaCommonEncoder* encoder); + diff --git a/lib/irda/irda_common_protocol_defs.c b/lib/irda/irda_common_protocol_defs.c new file mode 100644 index 00000000..46327045 --- /dev/null +++ b/lib/irda/irda_common_protocol_defs.c @@ -0,0 +1,81 @@ +#include "irda_common_i.h" +#include "irda_protocol_defs_i.h" + +const IrdaCommonProtocolSpec protocol_nec = { + .timings = { + .preamble_mark = IRDA_NEC_PREAMBULE_MARK, + .preamble_space = IRDA_NEC_PREAMBULE_SPACE, + .bit1_mark = IRDA_NEC_BIT1_MARK, + .bit1_space = IRDA_NEC_BIT1_SPACE, + .bit0_mark = IRDA_NEC_BIT0_MARK, + .bit0_space = IRDA_NEC_BIT0_SPACE, + .preamble_tolerance = IRDA_NEC_PREAMBLE_TOLERANCE, + .bit_tolerance = IRDA_NEC_BIT_TOLERANCE, + .silence_time = IRDA_NEC_SILENCE, + }, + .databit_len = 32, + .decode = irda_common_decode_pdwm, + .encode = irda_common_encode_pdwm, + .interpret = irda_decoder_nec_interpret, + .decode_repeat = irda_decoder_nec_decode_repeat, + .encode_repeat = irda_encoder_nec_encode_repeat, +}; + +const IrdaCommonProtocolSpec protocol_necext = { + .timings = { + .preamble_mark = IRDA_NEC_PREAMBULE_MARK, + .preamble_space = IRDA_NEC_PREAMBULE_SPACE, + .bit1_mark = IRDA_NEC_BIT1_MARK, + .bit1_space = IRDA_NEC_BIT1_SPACE, + .bit0_mark = IRDA_NEC_BIT0_MARK, + .bit0_space = IRDA_NEC_BIT0_SPACE, + .preamble_tolerance = IRDA_NEC_PREAMBLE_TOLERANCE, + .bit_tolerance = IRDA_NEC_BIT_TOLERANCE, + .silence_time = IRDA_NEC_SILENCE, + }, + .databit_len = 32, + .decode = irda_common_decode_pdwm, + .encode = irda_common_encode_pdwm, + .interpret = irda_decoder_necext_interpret, + .decode_repeat = irda_decoder_nec_decode_repeat, + .encode_repeat = irda_encoder_nec_encode_repeat, +}; + +const IrdaCommonProtocolSpec protocol_samsung32 = { + .timings = { + .preamble_mark = IRDA_SAMSUNG_PREAMBULE_MARK, + .preamble_space = IRDA_SAMSUNG_PREAMBULE_SPACE, + .bit1_mark = IRDA_SAMSUNG_BIT1_MARK, + .bit1_space = IRDA_SAMSUNG_BIT1_SPACE, + .bit0_mark = IRDA_SAMSUNG_BIT0_MARK, + .bit0_space = IRDA_SAMSUNG_BIT0_SPACE, + .preamble_tolerance = IRDA_SAMSUNG_PREAMBLE_TOLERANCE, + .bit_tolerance = IRDA_SAMSUNG_BIT_TOLERANCE, + .silence_time = IRDA_SAMSUNG_SILENCE, + }, + .databit_len = 32, + .decode = irda_common_decode_pdwm, + .encode = irda_common_encode_pdwm, + .interpret = irda_decoder_samsung32_interpret, + .decode_repeat = irda_decoder_samsung32_decode_repeat, + .encode_repeat = irda_encoder_samsung32_encode_repeat, +}; + +const IrdaCommonProtocolSpec protocol_rc6 = { + .timings = { + .preamble_mark = IRDA_RC6_PREAMBULE_MARK, + .preamble_space = IRDA_RC6_PREAMBULE_SPACE, + .bit1_mark = IRDA_RC6_BIT, + .preamble_tolerance = IRDA_RC6_PREAMBLE_TOLERANCE, + .bit_tolerance = IRDA_RC6_BIT_TOLERANCE, + .silence_time = IRDA_RC6_SILENCE, + }, + .databit_len = 1 + 3 + 1 + 8 + 8, // start_bit + 3 mode bits, + 1 toggle bit (x2 timing) + 8 address + 8 command + .manchester_inverse_level = false, + .decode = irda_decoder_rc6_decode_manchester, + .encode = irda_encoder_rc6_encode_manchester, + .interpret = irda_decoder_rc6_interpret, + .decode_repeat = NULL, + .encode_repeat = NULL, +}; + diff --git a/lib/irda/irda_encoder.c b/lib/irda/irda_encoder.c deleted file mode 100644 index c68e67cc..00000000 --- a/lib/irda/irda_encoder.c +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include -#include -#include -#include "irda_i.h" - - -void irda_encode_mark(const IrdaEncoderTimings *timings, uint32_t duration) { - api_hal_irda_pwm_set(timings->duty_cycle, timings->carrier_frequency); - delay_us(duration); -} - -void irda_encode_space(const IrdaEncoderTimings *timings, uint32_t duration) { - (void) timings; - api_hal_irda_pwm_stop(); - delay_us(duration); -} - -void irda_encode_bit(const IrdaEncoderTimings *timings, bool bit) { - if (bit) { - irda_encode_mark(timings, timings->bit1_mark); - irda_encode_space(timings, timings->bit1_space); - } else { - irda_encode_mark(timings, timings->bit0_mark); - irda_encode_space(timings, timings->bit0_space); - } -} - -void irda_encode_byte(const IrdaEncoderTimings *timings, uint8_t data) { - for(uint8_t i = 0; i < 8; i++) { - irda_encode_bit(timings, !!(data & (1 << i))); - } -} - diff --git a/lib/irda/irda_encoder_i.h b/lib/irda/irda_encoder_i.h deleted file mode 100644 index 82ffe501..00000000 --- a/lib/irda/irda_encoder_i.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#include -#include -#include "irda.h" - - -typedef struct { - uint32_t bit1_mark; - uint32_t bit1_space; - uint32_t bit0_mark; - uint32_t bit0_space; - float duty_cycle; - uint32_t carrier_frequency; -} IrdaEncoderTimings; - - -void irda_encode_byte(const IrdaEncoderTimings *timings, uint8_t data); -void irda_encode_bit(const IrdaEncoderTimings *timings, bool bit); -void irda_encode_space(const IrdaEncoderTimings *timings, uint32_t duration); -void irda_encode_mark(const IrdaEncoderTimings *timings, uint32_t duration); - diff --git a/lib/irda/irda_i.h b/lib/irda/irda_i.h index 392ca390..021f1263 100644 --- a/lib/irda/irda_i.h +++ b/lib/irda/irda_i.h @@ -1,14 +1,34 @@ #pragma once #include "irda.h" #include -#include "irda_encoder_i.h" -#include "irda_common_decoder_i.h" -#include "irda_protocol_defs_i.h" + +typedef struct { + uint32_t silence_time; + uint16_t preamble_mark; + uint16_t preamble_space; + uint16_t bit1_mark; + uint16_t bit1_space; + uint16_t bit0_mark; + uint16_t bit0_space; + float preamble_tolerance; + uint32_t bit_tolerance; +} IrdaTimings; typedef void* (*IrdaAlloc) (void); typedef IrdaMessage* (*IrdaDecode) (void* ctx, bool level, uint32_t duration); typedef void (*IrdaReset) (void*); typedef void (*IrdaFree) (void*); -typedef void (*IrdaEncode)(uint32_t address, uint32_t command, bool repeat); +typedef void (*IrdaEncoderReset)(void* encoder, const IrdaMessage* message); +typedef IrdaStatus (*IrdaEncode)(void* encoder, uint32_t* out, bool* polarity); +typedef IrdaTimings (*IrdaTimingsGet)(void); + +static inline uint8_t reverse(uint8_t value) { + uint8_t reverse_value = 0; + for (int i = 0; i < 8; ++i) { + reverse_value |= (value & (0x01 << i)) ? 1 << (7 - i) : 0; + } + + return reverse_value; +} diff --git a/lib/irda/irda_protocol_defs_i.h b/lib/irda/irda_protocol_defs_i.h index f469aa0d..ee2320a4 100644 --- a/lib/irda/irda_protocol_defs_i.h +++ b/lib/irda/irda_protocol_defs_i.h @@ -1,9 +1,10 @@ #pragma once +#include #include #include #include "irda.h" - +#include "irda_common_i.h" /*************************************************************************************************** * NEC protocol description @@ -26,23 +27,37 @@ #define IRDA_NEC_BIT0_MARK 560 #define IRDA_NEC_BIT0_SPACE 560 #define IRDA_NEC_REPEAT_PAUSE_MIN 30000 -#define IRDA_NEC_REPEAT_PAUSE 40000 +#define IRDA_NEC_REPEAT_PAUSE1 46000 +#define IRDA_NEC_REPEAT_PAUSE2 97000 +#define IRDA_NEC_SILENCE IRDA_NEC_REPEAT_PAUSE2 #define IRDA_NEC_REPEAT_PAUSE_MAX 150000 #define IRDA_NEC_REPEAT_MARK 9000 #define IRDA_NEC_REPEAT_SPACE 2250 -#define IRDA_NEC_CARRIER_FREQUENCY 38000 -#define IRDA_NEC_DUTY_CYCLE 0.33 #define IRDA_NEC_PREAMBLE_TOLERANCE 0.07 // percents #define IRDA_NEC_BIT_TOLERANCE 120 // us void* irda_decoder_nec_alloc(void); -void* irda_decoder_necext_alloc(void); -void irda_encoder_nec_encode(uint32_t address, uint32_t command, bool repeat); -void irda_encoder_necext_encode(uint32_t address, uint32_t command, bool repeat); void irda_decoder_nec_reset(void* decoder); void irda_decoder_nec_free(void* decoder); IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duration); +void* irda_encoder_nec_alloc(void); +IrdaStatus irda_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void irda_encoder_nec_reset(void* encoder_ptr, const IrdaMessage* message); +void irda_encoder_nec_free(void* encoder_ptr); + +void* irda_decoder_necext_alloc(void); +void* irda_encoder_necext_alloc(void); +void irda_encoder_necext_reset(void* encoder_ptr, const IrdaMessage* message); + +bool irda_decoder_nec_interpret(IrdaCommonDecoder* decoder); +bool irda_decoder_necext_interpret(IrdaCommonDecoder* decoder); +IrdaStatus irda_decoder_nec_decode_repeat(IrdaCommonDecoder* decoder); +IrdaStatus irda_encoder_nec_encode_repeat(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level); + +extern const IrdaCommonProtocolSpec protocol_necext; +extern const IrdaCommonProtocolSpec protocol_nec; + /*************************************************************************************************** * SAMSUNG32 protocol description @@ -65,18 +80,77 @@ IrdaMessage* irda_decoder_nec_decode(void* decoder, bool level, uint32_t duratio #define IRDA_SAMSUNG_BIT0_MARK 550 #define IRDA_SAMSUNG_BIT0_SPACE 550 #define IRDA_SAMSUNG_REPEAT_PAUSE_MIN 30000 -#define IRDA_SAMSUNG_REPEAT_PAUSE 47000 -#define IRDA_SAMSUNG_REPEAT_PAUSE_MAX 150000 +#define IRDA_SAMSUNG_REPEAT_PAUSE1 46000 +#define IRDA_SAMSUNG_REPEAT_PAUSE2 97000 +/* Samsung silence have to be greater than REPEAT MAX + * otherwise there can be problems during unit tests parsing + * of some data. Real tolerances we don't know, but in real life + * silence time should be greater than max repeat time. This is + * because of similar preambule timings for repeat and first messages. */ +#define IRDA_SAMSUNG_SILENCE 145000 +#define IRDA_SAMSUNG_REPEAT_PAUSE_MAX 140000 #define IRDA_SAMSUNG_REPEAT_MARK 4500 #define IRDA_SAMSUNG_REPEAT_SPACE 4500 -#define IRDA_SAMSUNG_CARRIER_FREQUENCY 38000 -#define IRDA_SAMSUNG_DUTY_CYCLE 0.33 #define IRDA_SAMSUNG_PREAMBLE_TOLERANCE 0.07 // percents #define IRDA_SAMSUNG_BIT_TOLERANCE 120 // us void* irda_decoder_samsung32_alloc(void); -void irda_encoder_samsung32_encode(uint32_t address, uint32_t command, bool repeat); void irda_decoder_samsung32_reset(void* decoder); void irda_decoder_samsung32_free(void* decoder); IrdaMessage* irda_decoder_samsung32_decode(void* decoder, bool level, uint32_t duration); +IrdaStatus irda_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level); +void irda_encoder_samsung32_reset(void* encoder_ptr, const IrdaMessage* message); +void* irda_encoder_samsung32_alloc(void); +void irda_encoder_samsung32_free(void* encoder_ptr); + +bool irda_decoder_samsung32_interpret(IrdaCommonDecoder* decoder); +IrdaStatus irda_decoder_samsung32_decode_repeat(IrdaCommonDecoder* decoder); +IrdaStatus irda_encoder_samsung32_encode_repeat(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level); + +extern const IrdaCommonProtocolSpec protocol_samsung32; + + +/*************************************************************************************************** +* RC6 protocol description +* https://www.mikrocontroller.net/articles/IRMP_-_english#RC6_.2B_RC6A +**************************************************************************************************** +* Preamble Manchester/biphase Silence +* mark/space Modulation +* +* 2666 889 444/888 - bit (x2 for toggle bit) 2666 +* +* ________ __ __ __ __ ____ __ __ __ __ __ __ __ __ +* _ _________ ____ __ __ ____ __ __ __ __ __ __ __ __ _______________ +* | 1 | 0 | 0 | 0 | 0 | ... | ... | | +* s m2 m1 m0 T address (MSB) command (MSB) +* +* s - start bit (always 1) +* m0-2 - mode (000 for RC6) +* T - toggle bit, twice longer +* address - 8 bit +* command - 8 bit +***************************************************************************************************/ + +#define IRDA_RC6_PREAMBULE_MARK 2666 +#define IRDA_RC6_PREAMBULE_SPACE 889 +#define IRDA_RC6_BIT 444 // half of time-quant for 1 bit +#define IRDA_RC6_PREAMBLE_TOLERANCE 0.07 // percents +#define IRDA_RC6_BIT_TOLERANCE 120 // us +#define IRDA_RC6_SILENCE 2700 + +void* irda_decoder_rc6_alloc(void); +void irda_decoder_rc6_reset(void* decoder); +void irda_decoder_rc6_free(void* decoder); +IrdaMessage* irda_decoder_rc6_decode(void* decoder, bool level, uint32_t duration); +void* irda_encoder_rc6_alloc(void); +void irda_encoder_rc6_reset(void* encoder_ptr, const IrdaMessage* message); +void irda_encoder_rc6_free(void* decoder); +IrdaStatus irda_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* polarity); + +bool irda_decoder_rc6_interpret(IrdaCommonDecoder* decoder); +IrdaStatus irda_decoder_rc6_decode_manchester(IrdaCommonDecoder* decoder); +IrdaStatus irda_encoder_rc6_encode_manchester(IrdaCommonEncoder* encoder_ptr, uint32_t* duration, bool* polarity); + +extern const IrdaCommonProtocolSpec protocol_rc6; + diff --git a/lib/irda/irda_transmit.c b/lib/irda/irda_transmit.c new file mode 100644 index 00000000..b6b00bf5 --- /dev/null +++ b/lib/irda/irda_transmit.c @@ -0,0 +1,66 @@ +#include "irda.h" +#include +#include +#include +#include +#include +#include + +static void irda_set_tx(uint32_t duration, bool level) { + if (level) { + api_hal_irda_pwm_set(IRDA_COMMON_DUTY_CYCLE, IRDA_COMMON_CARRIER_FREQUENCY); + delay_us(duration); + } else { + api_hal_irda_pwm_stop(); + delay_us(duration); + } +} + +void irda_send_raw(const uint32_t timings[], uint32_t timings_cnt, bool start_from_mark) { + __disable_irq(); + for (uint32_t i = 0; i < timings_cnt; ++i) { + irda_set_tx(timings[i], (i % 2) ^ start_from_mark); + } + irda_set_tx(0, false); + __enable_irq(); +} + +void irda_send(const IrdaMessage* message, int times) { + furi_assert(message); + furi_assert(irda_is_protocol_valid(message->protocol)); + + IrdaStatus status; + uint32_t duration = 0; + bool level = false; + IrdaEncoderHandler* handler = irda_alloc_encoder(); + irda_reset_encoder(handler, message); + + /* Hotfix: first timings is space timing, so make delay instead of locking + * whole system for that long. Replace when async timing lib will be ready. + * This timing doesn't have to be precise. + */ + status = irda_encode(handler, &duration, &level); + furi_assert(status != IrdaStatusError); + furi_assert(level == false); + delay_us(duration); + + __disable_irq(); + + while (times) { + status = irda_encode(handler, &duration, &level); + if (status != IrdaStatusError) { + irda_set_tx(duration, level); + } else { + furi_assert(0); + break; + } + if (status == IrdaStatusDone) + --times; + } + + irda_set_tx(0, false); + __enable_irq(); + + irda_free_encoder(handler); +} + diff --git a/lib/irda/nec/irda_decoder_nec.c b/lib/irda/nec/irda_decoder_nec.c index 02ab5c42..1abe1b9d 100644 --- a/lib/irda/nec/irda_decoder_nec.c +++ b/lib/irda/nec/irda_decoder_nec.c @@ -1,49 +1,11 @@ +#include "irda_protocol_defs_i.h" #include #include #include #include "../irda_i.h" -static bool interpret_nec(IrdaCommonDecoder* decoder); -static bool interpret_necext(IrdaCommonDecoder* decoder); -static DecodeStatus decode_repeat_nec(IrdaCommonDecoder* decoder); - - -static const IrdaCommonProtocolSpec protocol_nec = { - { - IRDA_NEC_PREAMBULE_MARK, - IRDA_NEC_PREAMBULE_SPACE, - IRDA_NEC_BIT1_MARK, - IRDA_NEC_BIT1_SPACE, - IRDA_NEC_BIT0_MARK, - IRDA_NEC_BIT0_SPACE, - IRDA_NEC_PREAMBLE_TOLERANCE, - IRDA_NEC_BIT_TOLERANCE, - }, - 32, - irda_common_decode_pdwm, - interpret_nec, - decode_repeat_nec, -}; - -static const IrdaCommonProtocolSpec protocol_necext = { - { - IRDA_NEC_PREAMBULE_MARK, - IRDA_NEC_PREAMBULE_SPACE, - IRDA_NEC_BIT1_MARK, - IRDA_NEC_BIT1_SPACE, - IRDA_NEC_BIT0_MARK, - IRDA_NEC_BIT0_SPACE, - IRDA_NEC_PREAMBLE_TOLERANCE, - IRDA_NEC_BIT_TOLERANCE, - }, - 32, - irda_common_decode_pdwm, - interpret_necext, - decode_repeat_nec, -}; - -static bool interpret_nec(IrdaCommonDecoder* decoder) { +bool irda_decoder_nec_interpret(IrdaCommonDecoder* decoder) { furi_assert(decoder); bool result = false; @@ -63,7 +25,7 @@ static bool interpret_nec(IrdaCommonDecoder* decoder) { } // Some NEC's extensions allow 16 bit address -static bool interpret_necext(IrdaCommonDecoder* decoder) { +bool irda_decoder_necext_interpret(IrdaCommonDecoder* decoder) { furi_assert(decoder); bool result = false; @@ -81,24 +43,24 @@ static bool interpret_necext(IrdaCommonDecoder* decoder) { } // timings start from Space (delay between message and repeat) -static DecodeStatus decode_repeat_nec(IrdaCommonDecoder* decoder) { +IrdaStatus irda_decoder_nec_decode_repeat(IrdaCommonDecoder* decoder) { furi_assert(decoder); float preamble_tolerance = decoder->protocol->timings.preamble_tolerance; uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance; - DecodeStatus status = DecodeStatusError; + IrdaStatus status = IrdaStatusError; - if(decoder->timings_cnt < 4) return DecodeStatusOk; + if(decoder->timings_cnt < 4) return IrdaStatusOk; if((decoder->timings[0] > IRDA_NEC_REPEAT_PAUSE_MIN) && (decoder->timings[0] < IRDA_NEC_REPEAT_PAUSE_MAX) && MATCH_PREAMBLE_TIMING(decoder->timings[1], IRDA_NEC_REPEAT_MARK, preamble_tolerance) && MATCH_PREAMBLE_TIMING(decoder->timings[2], IRDA_NEC_REPEAT_SPACE, preamble_tolerance) && MATCH_BIT_TIMING(decoder->timings[3], decoder->protocol->timings.bit1_mark, bit_tolerance)) { - status = DecodeStatusReady; + status = IrdaStatusReady; decoder->timings_cnt = 0; } else { - status = DecodeStatusError; + status = IrdaStatusError; } return status; diff --git a/lib/irda/nec/irda_encoder_nec.c b/lib/irda/nec/irda_encoder_nec.c index 079f784e..ecd403b3 100644 --- a/lib/irda/nec/irda_encoder_nec.c +++ b/lib/irda/nec/irda_encoder_nec.c @@ -1,62 +1,85 @@ +#include "furi/check.h" +#include "irda_common_i.h" #include #include "../irda_i.h" +#include "irda_protocol_defs_i.h" +#include - -static const IrdaEncoderTimings encoder_timings = { - .bit1_mark = IRDA_NEC_BIT1_MARK, - .bit1_space = IRDA_NEC_BIT1_SPACE, - .bit0_mark =IRDA_NEC_BIT0_MARK, - .bit0_space = IRDA_NEC_BIT0_SPACE, - .duty_cycle = IRDA_NEC_DUTY_CYCLE, - .carrier_frequency = IRDA_NEC_CARRIER_FREQUENCY, +static const uint32_t repeat_timings[] = { + IRDA_NEC_REPEAT_PAUSE2, + IRDA_NEC_REPEAT_MARK, + IRDA_NEC_REPEAT_SPACE, + IRDA_NEC_BIT1_MARK, }; +void irda_encoder_nec_reset(void* encoder_ptr, const IrdaMessage* message) { + furi_assert(encoder_ptr); -static void irda_encode_nec_preamble(void) { - irda_encode_mark(&encoder_timings, IRDA_NEC_PREAMBULE_MARK); - irda_encode_space(&encoder_timings, IRDA_NEC_PREAMBULE_SPACE); + IrdaCommonEncoder* encoder = encoder_ptr; + irda_common_encoder_reset(encoder); + + uint8_t address = message->address; + uint8_t address_inverse = ~address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + uint32_t* data = (void*) encoder->data; + *data |= address; + *data |= address_inverse << 8; + *data |= command << 16; + *data |= command_inverse << 24; } -static void irda_encode_nec_repeat(void) { - irda_encode_space(&encoder_timings, IRDA_NEC_REPEAT_PAUSE); - irda_encode_mark(&encoder_timings, IRDA_NEC_REPEAT_MARK); - irda_encode_space(&encoder_timings, IRDA_NEC_REPEAT_SPACE); - irda_encode_bit(&encoder_timings, 1); +void irda_encoder_necext_reset(void* encoder_ptr, const IrdaMessage* message) { + furi_assert(encoder_ptr); + + IrdaCommonEncoder* encoder = encoder_ptr; + irda_common_encoder_reset(encoder); + + uint16_t address = message->address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + uint32_t* data = (void*) encoder->data; + *data |= address; + *data |= command << 16; + *data |= command_inverse << 24; } -void irda_encoder_nec_encode(uint32_t addr, uint32_t cmd, bool repeat) { - uint8_t address = addr & 0xFF; - uint8_t command = cmd & 0xFF; - uint8_t address_inverse = (uint8_t) ~address; - uint8_t command_inverse = (uint8_t) ~command; +IrdaStatus irda_encoder_nec_encode_repeat(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { + furi_assert(encoder); - if (!repeat) { - irda_encode_nec_preamble(); - irda_encode_byte(&encoder_timings, address); - irda_encode_byte(&encoder_timings, address_inverse); - irda_encode_byte(&encoder_timings, command); - irda_encode_byte(&encoder_timings, command_inverse); - irda_encode_bit(&encoder_timings, 1); - } else { - irda_encode_nec_repeat(); - } + /* space + 2 timings preambule + payload + stop bit */ + uint32_t timings_encoded_up_to_repeat = 1 + 2 + encoder->protocol->databit_len * 2 + 1; + uint32_t repeat_cnt = encoder->timings_encoded - timings_encoded_up_to_repeat; + + furi_assert(encoder->timings_encoded >= timings_encoded_up_to_repeat); + + if (repeat_cnt > 0) + *duration = repeat_timings[repeat_cnt % COUNT_OF(repeat_timings)]; + else + *duration = IRDA_NEC_REPEAT_PAUSE1; + + *level = repeat_cnt % 2; + ++encoder->timings_encoded; + bool done = (!((repeat_cnt + 1) % COUNT_OF(repeat_timings))); + + return done ? IrdaStatusDone : IrdaStatusOk; } -// Some NEC's extensions allow 16 bit address -void irda_encoder_necext_encode(uint32_t addr, uint32_t cmd, bool repeat) { - uint16_t address = addr & 0xFFFF; - uint8_t command = cmd & 0xFF; - uint8_t command_inverse = (uint8_t) ~command; - - if (!repeat) { - irda_encode_nec_preamble(); - irda_encode_byte(&encoder_timings, (uint8_t) address); - irda_encode_byte(&encoder_timings, (uint8_t) (address >> 8)); - irda_encode_byte(&encoder_timings, command); - irda_encode_byte(&encoder_timings, command_inverse); - irda_encode_bit(&encoder_timings, 1); - } else { - irda_encode_nec_repeat(); - } +void* irda_encoder_necext_alloc(void) { + return irda_common_encoder_alloc(&protocol_necext); +} + +void* irda_encoder_nec_alloc(void) { + return irda_common_encoder_alloc(&protocol_nec); +} + +void irda_encoder_nec_free(void* encoder_ptr) { + irda_common_encoder_free(encoder_ptr); +} + +IrdaStatus irda_encoder_nec_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return irda_common_encode(encoder_ptr, duration, level); } diff --git a/lib/irda/rc6/irda_decoder_rc6.c b/lib/irda/rc6/irda_decoder_rc6.c new file mode 100644 index 00000000..96246357 --- /dev/null +++ b/lib/irda/rc6/irda_decoder_rc6.c @@ -0,0 +1,113 @@ +#include "irda.h" +#include +#include +#include +#include +#include "../irda_i.h" +#include "../irda_protocol_defs_i.h" + +typedef struct { + IrdaCommonDecoder* common_decoder; + bool toggle; +} IrdaRc6Decoder; + +bool irda_decoder_rc6_interpret(IrdaCommonDecoder* decoder) { + furi_assert(decoder); + + bool result = false; + uint32_t* data = (void*) &decoder->data[0]; + // MSB first + uint8_t address = reverse((uint8_t) (*data >> 5)); + uint8_t command = reverse((uint8_t) (*data >> 13)); + bool start_bit = *data & 0x01; + bool toggle = !!(*data & 0x10); + uint8_t mode = (*data >> 1) & 0x7; + + if ((start_bit == 1) && (mode == 0)) { + IrdaMessage* message = &decoder->message; + IrdaRc6Decoder *rc6_decoder = decoder->context; + bool *prev_toggle = &rc6_decoder->toggle; + if ((message->address == address) + && (message->command == command) + && (message->protocol == IrdaProtocolRC6)) { + message->repeat = (toggle == *prev_toggle); + } else { + message->repeat = false; + } + *prev_toggle = toggle; + message->command = command; + message->address = address; + result = true; + } + + return result; +} + +/* + * RC6 Uses manchester encoding, but it has twice longer + * 4-th bit (toggle bit) time quant, so we need to decode + * it separately and than pass decoding for other bits to + * common manchester decode function. + */ +IrdaStatus irda_decoder_rc6_decode_manchester(IrdaCommonDecoder* decoder) { + // 4th bit lasts 2x times more + IrdaStatus status = IrdaStatusError; + uint16_t bit = decoder->protocol->timings.bit1_mark; + uint16_t tolerance = decoder->protocol->timings.bit_tolerance; + uint16_t timing = decoder->timings[0]; + + bool single_timing = MATCH_BIT_TIMING(timing, bit, tolerance); + bool double_timing = MATCH_BIT_TIMING(timing, 2*bit, tolerance); + bool triple_timing = MATCH_BIT_TIMING(timing, 3*bit, tolerance); + + if (decoder->databit_cnt == 4) { + furi_assert(decoder->timings_cnt == 1); + furi_assert(decoder->switch_detect == true); + + if (single_timing ^ triple_timing) { + --decoder->timings_cnt; + ++decoder->databit_cnt; + decoder->data[0] |= (single_timing ? !decoder->level : decoder->level) << 4; + status = IrdaStatusOk; + } + } else if (decoder->databit_cnt == 5) { + if (single_timing || triple_timing) { + if (triple_timing) + decoder->timings[0] = bit; + decoder->switch_detect = false; + status = irda_common_decode_manchester(decoder); + } else if (double_timing) { + --decoder->timings_cnt; + status = IrdaStatusOk; + } + } else { + status = irda_common_decode_manchester(decoder); + } + + return status; +} + +void* irda_decoder_rc6_alloc(void) { + IrdaRc6Decoder* decoder = furi_alloc(sizeof(IrdaRc6Decoder)); + decoder->toggle = false; + decoder->common_decoder = irda_common_decoder_alloc(&protocol_rc6); + irda_common_decoder_set_context(decoder->common_decoder, decoder); + return decoder; +} + +IrdaMessage* irda_decoder_rc6_decode(void* decoder, bool level, uint32_t duration) { + IrdaRc6Decoder* decoder_rc6 = decoder; + return irda_common_decode(decoder_rc6->common_decoder, level, duration); +} + +void irda_decoder_rc6_free(void* decoder) { + IrdaRc6Decoder* decoder_rc6 = decoder; + irda_common_decoder_free(decoder_rc6->common_decoder); + free(decoder_rc6); +} + +void irda_decoder_rc6_reset(void* decoder) { + IrdaRc6Decoder* decoder_rc6 = decoder; + irda_common_decoder_reset(decoder_rc6->common_decoder); +} + diff --git a/lib/irda/rc6/irda_encoder_rc6.c b/lib/irda/rc6/irda_encoder_rc6.c new file mode 100644 index 00000000..6b534805 --- /dev/null +++ b/lib/irda/rc6/irda_encoder_rc6.c @@ -0,0 +1,59 @@ +#include "furi/memmgr.h" +#include "irda.h" +#include "irda_common_i.h" +#include "irda_protocol_defs_i.h" +#include +#include "../irda_i.h" + +typedef struct IrdaEncoderRC6 { + IrdaCommonEncoder* common_encoder; + bool toggle_bit; +} IrdaEncoderRC6; + +void irda_encoder_rc6_reset(void* encoder_ptr, const IrdaMessage* message) { + furi_assert(encoder_ptr); + + IrdaEncoderRC6* encoder = encoder_ptr; + IrdaCommonEncoder* common_encoder = encoder->common_encoder; + irda_common_encoder_reset(common_encoder); + + uint32_t* data = (void*) common_encoder->data; + *data |= 0x01; // start bit + (void) *data; // 3 bits for mode == 0 + *data |= encoder->toggle_bit ? 0x10 : 0; + *data |= reverse(message->address) << 5; + *data |= reverse(message->command) << 13; + + encoder->toggle_bit ^= 1; +} + +IrdaStatus irda_encoder_rc6_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + IrdaEncoderRC6* encoder = encoder_ptr; + return irda_common_encode(encoder->common_encoder, duration, level); +} + +void* irda_encoder_rc6_alloc(void) { + IrdaEncoderRC6* encoder = furi_alloc(sizeof(IrdaEncoderRC6)); + encoder->common_encoder = irda_common_encoder_alloc(&protocol_rc6); + encoder->toggle_bit = false; + return encoder; +} + +void irda_encoder_rc6_free(void* encoder_ptr) { + furi_assert(encoder_ptr); + + IrdaEncoderRC6* encoder = encoder_ptr; + free(encoder->common_encoder); + free(encoder); +} + +IrdaStatus irda_encoder_rc6_encode_manchester(IrdaCommonEncoder* common_encoder, uint32_t* duration, bool* polarity) { + IrdaStatus status = IrdaStatusError; + + bool toggle_bit = (common_encoder->bits_encoded == 4); + status = irda_common_encode_manchester(common_encoder, duration, polarity); + if (toggle_bit) + *duration *= 2; + return status; +} + diff --git a/lib/irda/samsung/irda_decoder_samsung.c b/lib/irda/samsung/irda_decoder_samsung.c index e2eb67ef..7311403e 100644 --- a/lib/irda/samsung/irda_decoder_samsung.c +++ b/lib/irda/samsung/irda_decoder_samsung.c @@ -1,32 +1,11 @@ +#include "irda_protocol_defs_i.h" #include #include #include #include "../irda_i.h" -static bool interpret_samsung32(IrdaCommonDecoder* decoder); -static DecodeStatus decode_repeat_samsung32(IrdaCommonDecoder* decoder); - - -static const IrdaCommonProtocolSpec protocol_samsung32 = { - { - IRDA_SAMSUNG_PREAMBULE_MARK, - IRDA_SAMSUNG_PREAMBULE_SPACE, - IRDA_SAMSUNG_BIT1_MARK, - IRDA_SAMSUNG_BIT1_SPACE, - IRDA_SAMSUNG_BIT0_MARK, - IRDA_SAMSUNG_BIT0_SPACE, - IRDA_SAMSUNG_PREAMBLE_TOLERANCE, - IRDA_SAMSUNG_BIT_TOLERANCE, - }, - 32, - irda_common_decode_pdwm, - interpret_samsung32, - decode_repeat_samsung32, -}; - - -static bool interpret_samsung32(IrdaCommonDecoder* decoder) { +bool irda_decoder_samsung32_interpret(IrdaCommonDecoder* decoder) { furi_assert(decoder); bool result = false; @@ -46,15 +25,15 @@ static bool interpret_samsung32(IrdaCommonDecoder* decoder) { } // timings start from Space (delay between message and repeat) -static DecodeStatus decode_repeat_samsung32(IrdaCommonDecoder* decoder) { +IrdaStatus irda_decoder_samsung32_decode_repeat(IrdaCommonDecoder* decoder) { furi_assert(decoder); float preamble_tolerance = decoder->protocol->timings.preamble_tolerance; uint32_t bit_tolerance = decoder->protocol->timings.bit_tolerance; - DecodeStatus status = DecodeStatusError; + IrdaStatus status = IrdaStatusError; if (decoder->timings_cnt < 6) - return DecodeStatusOk; + return IrdaStatusOk; if ((decoder->timings[0] > IRDA_SAMSUNG_REPEAT_PAUSE_MIN) && (decoder->timings[0] < IRDA_SAMSUNG_REPEAT_PAUSE_MAX) @@ -64,10 +43,10 @@ static DecodeStatus decode_repeat_samsung32(IrdaCommonDecoder* decoder) { && MATCH_BIT_TIMING(decoder->timings[4], decoder->protocol->timings.bit1_space, bit_tolerance) && MATCH_BIT_TIMING(decoder->timings[5], decoder->protocol->timings.bit1_mark, bit_tolerance) ) { - status = DecodeStatusReady; + status = IrdaStatusReady; decoder->timings_cnt = 0; } else { - status = DecodeStatusError; + status = IrdaStatusError; } return status; diff --git a/lib/irda/samsung/irda_encoder_samsung.c b/lib/irda/samsung/irda_encoder_samsung.c index 47d75b75..016a1271 100644 --- a/lib/irda/samsung/irda_encoder_samsung.c +++ b/lib/irda/samsung/irda_encoder_samsung.c @@ -1,45 +1,67 @@ +#include "furi/check.h" +#include "irda_common_i.h" #include #include "../irda_i.h" +#include "irda_protocol_defs_i.h" +#include - -static const IrdaEncoderTimings encoder_timings = { - .bit1_mark = IRDA_SAMSUNG_BIT1_MARK, - .bit1_space = IRDA_SAMSUNG_BIT1_SPACE, - .bit0_mark =IRDA_SAMSUNG_BIT0_MARK, - .bit0_space = IRDA_SAMSUNG_BIT0_SPACE, - .duty_cycle = IRDA_SAMSUNG_DUTY_CYCLE, - .carrier_frequency = IRDA_SAMSUNG_CARRIER_FREQUENCY, +static const uint32_t repeat_timings[] = { + IRDA_SAMSUNG_REPEAT_PAUSE2, + IRDA_SAMSUNG_REPEAT_MARK, + IRDA_SAMSUNG_REPEAT_SPACE, + IRDA_SAMSUNG_BIT1_MARK, + IRDA_SAMSUNG_BIT1_SPACE, + IRDA_SAMSUNG_BIT1_MARK, }; +void irda_encoder_samsung32_reset(void* encoder_ptr, const IrdaMessage* message) { + furi_assert(encoder_ptr); -static void irda_encode_samsung32_preamble(void) { - irda_encode_mark(&encoder_timings, IRDA_SAMSUNG_PREAMBULE_MARK); - irda_encode_space(&encoder_timings, IRDA_SAMSUNG_PREAMBULE_SPACE); + IrdaCommonEncoder* encoder = encoder_ptr; + irda_common_encoder_reset(encoder); + + uint8_t address = message->address; + uint8_t command = message->command; + uint8_t command_inverse = ~command; + + uint32_t* data = (void*) encoder->data; + *data |= address; + *data |= address << 8; + *data |= command << 16; + *data |= command_inverse << 24; } -static void irda_encode_samsung32_repeat(void) { - irda_encode_space(&encoder_timings, IRDA_SAMSUNG_REPEAT_PAUSE); - irda_encode_mark(&encoder_timings, IRDA_SAMSUNG_REPEAT_MARK); - irda_encode_space(&encoder_timings, IRDA_SAMSUNG_REPEAT_SPACE); - irda_encode_bit(&encoder_timings, 1); - irda_encode_bit(&encoder_timings, 1); +IrdaStatus irda_encoder_samsung32_encode_repeat(IrdaCommonEncoder* encoder, uint32_t* duration, bool* level) { + furi_assert(encoder); + + /* space + 2 timings preambule + payload + stop bit */ + uint32_t timings_encoded_up_to_repeat = 1 + 2 + encoder->protocol->databit_len * 2 + 1; + uint32_t repeat_cnt = encoder->timings_encoded - timings_encoded_up_to_repeat; + + furi_assert(encoder->timings_encoded >= timings_encoded_up_to_repeat); + + if (repeat_cnt > 0) + *duration = repeat_timings[repeat_cnt % COUNT_OF(repeat_timings)]; + else + *duration = IRDA_SAMSUNG_REPEAT_PAUSE1; + + *level = repeat_cnt % 2; + ++encoder->timings_encoded; + bool done = (!((repeat_cnt + 1) % COUNT_OF(repeat_timings))); + + return done ? IrdaStatusDone : IrdaStatusOk; } -void irda_encoder_samsung32_encode(uint32_t addr, uint32_t cmd, bool repeat) { - uint8_t address = addr & 0xFF; - uint8_t command = cmd & 0xFF; - uint8_t command_inverse = (uint8_t) ~command; - - irda_encode_space(&encoder_timings, 100); - if (!repeat) { - irda_encode_samsung32_preamble(); - irda_encode_byte(&encoder_timings, address); - irda_encode_byte(&encoder_timings, address); - irda_encode_byte(&encoder_timings, command); - irda_encode_byte(&encoder_timings, command_inverse); - irda_encode_bit(&encoder_timings, 1); - } else { - irda_encode_samsung32_repeat(); - } +void* irda_encoder_samsung32_alloc(void) { + return irda_common_encoder_alloc(&protocol_samsung32); } +void irda_encoder_samsung32_free(void* encoder_ptr) { + irda_common_encoder_free(encoder_ptr); +} + +IrdaStatus irda_encoder_samsung32_encode(void* encoder_ptr, uint32_t* duration, bool* level) { + return irda_common_encode(encoder_ptr, duration, level); +} + +