diff --git a/applications/accessor/helpers/wiegand.cpp b/applications/accessor/helpers/wiegand.cpp index 37ebf8dd..c6ca9fe0 100644 --- a/applications/accessor/helpers/wiegand.cpp +++ b/applications/accessor/helpers/wiegand.cpp @@ -40,11 +40,11 @@ void input_isr(void* _pin, void* _ctx) { uint32_t pin = (uint32_t)_pin; WIEGAND* _this = static_cast(_ctx); - if(pin == ext_pa6_gpio.pin) { + if(pin == gpio_ext_pa6.pin) { _this->ReadD0(); } - if(pin == ext_pa7_gpio.pin) { + if(pin == gpio_ext_pa7.pin) { _this->ReadD1(); } } @@ -57,8 +57,8 @@ void WIEGAND::begin() { _wiegandType = 0; _bitCount = 0; - const GpioPin* pinD0 = &ext_pa6_gpio; - const GpioPin* pinD1 = &ext_pa7_gpio; + const GpioPin* pinD0 = &gpio_ext_pa6; + const GpioPin* pinD1 = &gpio_ext_pa7; hal_gpio_init(pinD0, GpioModeInterruptFall, GpioPullNo, GpioSpeedLow); // Set D0 pin as input hal_gpio_init(pinD1, GpioModeInterruptFall, GpioPullNo, GpioSpeedLow); // Set D1 pin as input diff --git a/applications/applications.c b/applications/applications.c index e5d3d8cd..f5920aa2 100644 --- a/applications/applications.c +++ b/applications/applications.c @@ -13,7 +13,7 @@ int32_t gui_task(void* p); int32_t backlight_control(void* p); int32_t irda(void* p); int32_t app_loader(void* p); -int32_t lf_rfid_workaround(void* p); +int32_t app_lfrfid(void* p); int32_t nfc_task(void* p); int32_t dolphin_task(void* p); int32_t power_task(void* p); @@ -82,10 +82,7 @@ const FlipperApplication FLIPPER_SERVICES[] = { #endif #ifdef SRV_LF_RFID - {.app = lf_rfid_workaround, - .name = "lf rfid workaround", - .stack_size = 1024, - .icon = A_Plugins_14}, + {.app = app_lfrfid, .name = "125 kHz RFID", .stack_size = 1024, .icon = A_Plugins_14}, #endif #ifdef SRV_IRDA @@ -158,7 +155,7 @@ const FlipperApplication FLIPPER_APPS[] = { #endif #ifdef APP_LF_RFID - {.app = lf_rfid_workaround, .name = "125 kHz RFID", .stack_size = 1024, .icon = A_125khz_14}, + {.app = app_lfrfid, .name = "125 kHz RFID", .stack_size = 1024, .icon = A_125khz_14}, #endif #ifdef APP_IRDA diff --git a/applications/gui/gui.c b/applications/gui/gui.c index 40ae8078..05e4a51d 100644 --- a/applications/gui/gui.c +++ b/applications/gui/gui.c @@ -218,6 +218,7 @@ void gui_cli_screen_stream(string_t args, void* context) { Gui* gui = context; gui_set_framebuffer_callback_context(gui, gui); gui_set_framebuffer_callback(gui, gui_cli_screen_stream_callback); + gui_redraw(gui); cli_getc(gui->cli); gui_set_framebuffer_callback(gui, NULL); gui_set_framebuffer_callback_context(gui, NULL); diff --git a/applications/ibutton/helpers/key-reader.cpp b/applications/ibutton/helpers/key-reader.cpp index a22cbb5a..8955c647 100644 --- a/applications/ibutton/helpers/key-reader.cpp +++ b/applications/ibutton/helpers/key-reader.cpp @@ -140,11 +140,10 @@ void KeyReader::comparator_trigger_callback(void* hcomp, void* comp_ctx) { KeyReader* _this = static_cast(comp_ctx); if(hcomp == &hcomp1) { - _this->cyfral_decoder.process_front( - hal_gpio_get_rfid_in_level(), DWT->CYCCNT - last_dwt_value); + uint32_t current_dwt_value = DWT->CYCCNT; - _this->metakom_decoder.process_front( - hal_gpio_get_rfid_in_level(), DWT->CYCCNT - last_dwt_value); + _this->cyfral_decoder.process_front(hal_gpio_get_rfid_in_level(), current_dwt_value); + _this->metakom_decoder.process_front(hal_gpio_get_rfid_in_level(), current_dwt_value); last_dwt_value = DWT->CYCCNT; } diff --git a/applications/ibutton/ibutton-app.cpp b/applications/ibutton/ibutton-app.cpp index 50a52197..ab90c168 100644 --- a/applications/ibutton/ibutton-app.cpp +++ b/applications/ibutton/ibutton-app.cpp @@ -197,6 +197,12 @@ iButtonApp::~iButtonApp() { cli_delete_command(cli, "tm"); furi_record_close("cli"); osMessageQueueDelete(cli_event_result); + + for(std::map::iterator it = scenes.begin(); it != scenes.end(); ++it) { + delete it->second; + scenes.erase(it); + } + api_hal_power_insomnia_exit(); } diff --git a/applications/ibutton/scene/ibutton-scene-generic.h b/applications/ibutton/scene/ibutton-scene-generic.h index 07a23ff2..1c321142 100644 --- a/applications/ibutton/scene/ibutton-scene-generic.h +++ b/applications/ibutton/scene/ibutton-scene-generic.h @@ -8,6 +8,7 @@ public: virtual void on_enter(iButtonApp* app) = 0; virtual bool on_event(iButtonApp* app, iButtonEvent* event) = 0; virtual void on_exit(iButtonApp* app) = 0; + virtual ~iButtonScene(){}; private: }; diff --git a/applications/lf-rfid/em4100.c b/applications/lf-rfid-old/em4100.c similarity index 100% rename from applications/lf-rfid/em4100.c rename to applications/lf-rfid-old/em4100.c diff --git a/applications/lf-rfid/helpers/decoder-analyzer.cpp b/applications/lf-rfid/helpers/decoder-analyzer.cpp new file mode 100644 index 00000000..d3ee90ba --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-analyzer.cpp @@ -0,0 +1,48 @@ +#include "decoder-analyzer.h" +#include +#include + +bool DecoderAnalyzer::read(uint8_t* _data, uint8_t _data_size) { + bool result = false; + + if(ready) { + result = true; + + for(size_t i = 0; i < data_size; i++) { + printf("%lu ", data[i]); + if((i + 1) % 8 == 0) printf("\r\n"); + } + printf("\r\n--------\r\n"); + + ready = false; + } + + return result; +} + +void DecoderAnalyzer::process_front(bool polarity, uint32_t time) { + if(ready) return; + + data[data_index] = time; + + if(data_index < data_size) { + data_index++; + } else { + data_index = 0; + ready = true; + } +} + +DecoderAnalyzer::DecoderAnalyzer() { + data = reinterpret_cast(calloc(data_size, sizeof(uint32_t))); + furi_check(data); + data_index = 0; + ready = false; +} + +DecoderAnalyzer::~DecoderAnalyzer() { + free(data); +} + +void DecoderAnalyzer::reset_state() { +} diff --git a/applications/lf-rfid/helpers/decoder-analyzer.h b/applications/lf-rfid/helpers/decoder-analyzer.h new file mode 100644 index 00000000..eecea6ed --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-analyzer.h @@ -0,0 +1,21 @@ +#pragma once +#include +#include + +class DecoderAnalyzer { +public: + bool read(uint8_t* data, uint8_t data_size); + void process_front(bool polarity, uint32_t time); + + DecoderAnalyzer(); + ~DecoderAnalyzer(); + +private: + void reset_state(); + + std::atomic ready; + + static const uint32_t data_size = 2048; + uint32_t data_index = 0; + uint32_t* data; +}; diff --git a/applications/lf-rfid/helpers/decoder-emmarine.cpp b/applications/lf-rfid/helpers/decoder-emmarine.cpp new file mode 100644 index 00000000..aa00f9d5 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-emmarine.cpp @@ -0,0 +1,172 @@ +#include "emmarine.h" +#include "decoder-emmarine.h" +#include +#include + +constexpr uint32_t clocks_in_us = 64; +constexpr uint32_t short_time = 255 * clocks_in_us; +constexpr uint32_t long_time = 510 * clocks_in_us; +constexpr uint32_t jitter_time = 100 * clocks_in_us; + +constexpr uint32_t short_time_low = short_time - jitter_time; +constexpr uint32_t short_time_high = short_time + jitter_time; +constexpr uint32_t long_time_low = long_time - jitter_time; +constexpr uint32_t long_time_high = long_time + jitter_time; + +void DecoderEMMarine::reset_state() { + ready = false; + readed_data = 0; + manchester_advance( + manchester_saved_state, ManchesterEventReset, &manchester_saved_state, nullptr); +} + +void printEM_raw(uint64_t data) { + // header + for(uint8_t i = 0; i < 9; i++) { + printf("%u ", data & (1LLU << 63) ? 1 : 0); + data = data << 1; + } + printf("\r\n"); + + // nibbles + for(uint8_t r = 0; r < 11; r++) { + printf(" "); + uint8_t value = 0; + for(uint8_t i = 0; i < 5; i++) { + printf("%u ", data & (1LLU << 63) ? 1 : 0); + if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0); + data = data << 1; + } + printf("0x%X", value); + printf("\r\n"); + } +} + +void printEM_data(uint64_t data) { + printf("EM "); + + // header + for(uint8_t i = 0; i < 9; i++) { + data = data << 1; + } + + // nibbles + for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { + uint8_t value = 0; + for(uint8_t i = 0; i < 5; i++) { + if(i < 4) value = (value << 1) | (data & (1LLU << 63) ? 1 : 0); + data = data << 1; + } + printf("%X", value); + if(r % 2) printf(" "); + } + printf("\r\n"); +} + +void copyEM_data(uint64_t data, uint8_t* result, uint8_t result_size) { + furi_assert(result_size >= 5); + uint8_t result_index = 0; + + // clean result + memset(result, 0, result_size); + + // header + for(uint8_t i = 0; i < 9; i++) { + data = data << 1; + } + + // nibbles + uint8_t value = 0; + for(uint8_t r = 0; r < EM_ROW_COUNT; r++) { + uint8_t nibble = 0; + for(uint8_t i = 0; i < 5; i++) { + if(i < 4) nibble = (nibble << 1) | (data & (1LLU << 63) ? 1 : 0); + data = data << 1; + } + value = (value << 4) | nibble; + if(r % 2) { + result[result_index] |= value; + result_index++; + value = 0; + } + } +} + +bool DecoderEMMarine::read(uint8_t* data, uint8_t data_size) { + bool result = false; + + if(ready) { + result = true; + copyEM_data(readed_data, data, data_size); + ready = false; + } + + return result; +} + +void DecoderEMMarine::process_front(bool polarity, uint32_t time) { + if(ready) return; + if(time < short_time_low) return; + + ManchesterEvent event = ManchesterEventReset; + + if(time > short_time_low && time < short_time_high) { + if(polarity) { + event = ManchesterEventShortHigh; + } else { + event = ManchesterEventShortLow; + } + } else if(time > long_time_low && time < long_time_high) { + if(polarity) { + event = ManchesterEventLongHigh; + } else { + event = ManchesterEventLongLow; + } + } + + if(event != ManchesterEventReset) { + bool data; + bool data_ok = + manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data); + + if(data_ok) { + readed_data = (readed_data << 1) | data; + + // header and stop bit + if((readed_data & EM_HEADER_AND_STOP_MASK) != EM_HEADER_AND_STOP_DATA) return; + + // row parity + for(uint8_t i = 0; i < EM_ROW_COUNT; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < 5; j++) { + parity_sum += (readed_data >> (EM_FIRST_ROW_POS - i * 5 + j)) & 1; + } + + if((parity_sum % 2)) { + return; + } + } + + // columns parity + for(uint8_t i = 0; i < 4; i++) { + uint8_t parity_sum = 0; + + for(uint8_t j = 0; j < EM_ROW_COUNT + 1; j++) { + parity_sum += (readed_data >> (EM_COLUMN_POS - i + j * 5)) & 1; + } + + if((parity_sum % 2)) { + return; + } + } + + // checks ok + ready = true; + } + } +} + +DecoderEMMarine::DecoderEMMarine() { + reset_state(); +} diff --git a/applications/lf-rfid/helpers/decoder-emmarine.h b/applications/lf-rfid/helpers/decoder-emmarine.h new file mode 100644 index 00000000..e3356d15 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-emmarine.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include "manchester-decoder.h" + +class DecoderEMMarine { +public: + bool read(uint8_t* data, uint8_t data_size); + void process_front(bool polarity, uint32_t time); + + DecoderEMMarine(); + +private: + void reset_state(); + + uint64_t readed_data = 0; + std::atomic ready; + + ManchesterState manchester_saved_state; +}; diff --git a/applications/lf-rfid/helpers/decoder-hid26.cpp b/applications/lf-rfid/helpers/decoder-hid26.cpp new file mode 100644 index 00000000..534eebb7 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-hid26.cpp @@ -0,0 +1,185 @@ +#include "decoder-hid26.h" +#include + +constexpr uint32_t clocks_in_us = 64; + +constexpr uint32_t jitter_time_us = 20; +constexpr uint32_t min_time_us = 64; +constexpr uint32_t max_time_us = 80; + +constexpr uint32_t min_time = (min_time_us - jitter_time_us) * clocks_in_us; +constexpr uint32_t mid_time = ((max_time_us - min_time_us) / 2 + min_time_us) * clocks_in_us; +constexpr uint32_t max_time = (max_time_us + jitter_time_us) * clocks_in_us; + +bool DecoderHID26::read(uint8_t* data, uint8_t data_size) { + bool result = false; + furi_assert(data_size >= 3); + + if(ready) { + result = true; + data[0] = facility; + data[1] = (uint8_t)(number >> 8); + data[2] = (uint8_t)number; + + //printf("HID %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number); + ready = false; + } + + return result; +} + +void DecoderHID26::process_front(bool polarity, uint32_t time) { + if(ready) return; + + if(polarity == true) { + last_pulse_time = time; + } else { + last_pulse_time += time; + + if(last_pulse_time > min_time && last_pulse_time < max_time) { + bool pulse; + + if(last_pulse_time < mid_time) { + // 6 pulses + pulse = false; + } else { + // 5 pulses + pulse = true; + } + + if(last_pulse == pulse) { + pulse_count++; + + if(pulse) { + if(pulse_count > 4) { + pulse_count = 0; + store_data(1); + } + } else { + if(pulse_count > 5) { + pulse_count = 0; + store_data(0); + } + } + } else { + if(last_pulse) { + if(pulse_count > 2) { + store_data(1); + } + } else { + if(pulse_count > 3) { + store_data(0); + } + } + + pulse_count = 0; + last_pulse = pulse; + } + } + } +} + +DecoderHID26::DecoderHID26() { + reset_state(); +} + +void DecoderHID26::store_data(bool data) { + stored_data[0] = (stored_data[0] << 1) | ((stored_data[1] >> 31) & 1); + stored_data[1] = (stored_data[1] << 1) | ((stored_data[2] >> 31) & 1); + stored_data[2] = (stored_data[2] << 1) | data; + validate_stored_data(); +} + +void DecoderHID26::validate_stored_data() { + // packet preamble + // raw data + if(*(reinterpret_cast(stored_data) + 3) != 0x1D) { + return; + } + + // encoded company/oem + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 + if((*stored_data >> 10 & 0x3FFF) != 0x1556) { + return; + } + + // encoded format/length + // coded with 01 = 0, 10 = 1 transitions + // stored in word 0 and word 1 + if((((*stored_data & 0x3FF) << 12) | ((*(stored_data + 1) >> 20) & 0xFFF)) != 0x155556) { + return; + } + + // data decoding + uint32_t result = 0; + + // decode from word 1 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 9; i >= 0; i--) { + switch((*(stored_data + 1) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return; + break; + } + } + + // decode from word 2 + // coded with 01 = 0, 10 = 1 transitions + for(int8_t i = 15; i >= 0; i--) { + switch((*(stored_data + 2) >> (2 * i)) & 0b11) { + case 0b01: + result = (result << 1) | 0; + break; + case 0b10: + result = (result << 1) | 1; + break; + default: + return; + break; + } + } + + // store decoded data + facility = result >> 17; + number = result >> 1; + + // trailing parity (odd) test + uint8_t parity_sum = 0; + for(int8_t i = 0; i < 13; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) != 1) { + return; + } + + // leading parity (even) test + parity_sum = 0; + for(int8_t i = 13; i < 26; i++) { + if(((result >> i) & 1) == 1) { + parity_sum++; + } + } + + if((parity_sum % 2) == 1) { + return; + } + + ready = true; +} + +void DecoderHID26::reset_state() { + last_pulse = false; + pulse_count = 0; + ready = false; + last_pulse_time = 0; +} diff --git a/applications/lf-rfid/helpers/decoder-hid26.h b/applications/lf-rfid/helpers/decoder-hid26.h new file mode 100644 index 00000000..cd4859e5 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-hid26.h @@ -0,0 +1,26 @@ +#pragma once +#include +#include + +class DecoderHID26 { +public: + bool read(uint8_t* data, uint8_t data_size); + void process_front(bool polarity, uint32_t time); + DecoderHID26(); + +private: + uint32_t last_pulse_time = 0; + bool last_pulse; + uint8_t pulse_count; + + uint32_t stored_data[3] = {0, 0, 0}; + void store_data(bool data); + void validate_stored_data(); + + uint8_t facility = 0; + uint16_t number = 0; + + std::atomic ready; + + void reset_state(); +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/decoder-indala.cpp b/applications/lf-rfid/helpers/decoder-indala.cpp new file mode 100644 index 00000000..9b82a176 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-indala.cpp @@ -0,0 +1,170 @@ +#include "decoder-indala.h" +#include + +constexpr uint32_t clocks_in_us = 64; + +constexpr uint32_t min_time_us = 25 * clocks_in_us; +constexpr uint32_t mid_time_us = 45 * clocks_in_us; +constexpr uint32_t max_time_us = 90 * clocks_in_us; + +bool DecoderIndala::read(uint8_t* data, uint8_t data_size) { + bool result = false; + + if(ready) { + result = true; + printf("IND %02X %02X %02X\r\n", facility, (uint8_t)(number >> 8), (uint8_t)number); + ready = false; + } + + return result; +} + +void DecoderIndala::process_front(bool polarity, uint32_t time) { + if(ready) return; + + if(polarity == false) { + last_pulse_time = time; + } else { + last_pulse_time += time; + pulse_count++; + + if(last_pulse_time > min_time_us && last_pulse_time < max_time_us) { + if(last_pulse_time > mid_time_us) { + bool last_data = !(readed_data & 1); + pulse_count = 0; + readed_data = (readed_data << 1) | last_data; + verify(); + } else if((pulse_count % 16) == 0) { + bool last_data = readed_data & 1; + pulse_count = 0; + readed_data = (readed_data << 1) | last_data; + verify(); + } + } + } +} + +DecoderIndala::DecoderIndala() { +} + +void DecoderIndala::reset_state() { +} + +void DecoderIndala::verify() { + // verify inverse + readed_data = ~readed_data; + verify_inner(); + + // verify normal + readed_data = ~readed_data; + verify_inner(); +} + +typedef union { + uint64_t raw; + struct __attribute__((packed)) { + uint8_t static0 : 3; + uint8_t checksum : 2; + uint8_t static1 : 2; + uint8_t y14 : 1; + + uint8_t x8 : 1; + uint8_t x1 : 1; + uint8_t y13 : 1; + uint8_t static2 : 1; + uint8_t y12 : 1; + uint8_t x6 : 1; + uint8_t y5 : 1; + uint8_t y8 : 1; + + uint8_t y15 : 1; + uint8_t x2 : 1; + uint8_t x5 : 1; + uint8_t x4 : 1; + uint8_t y9 : 1; + uint8_t y2 : 1; + uint8_t x3 : 1; + uint8_t y3 : 1; + + uint8_t y1 : 1; + uint8_t y16 : 1; + uint8_t y4 : 1; + uint8_t x7 : 1; + uint8_t p2 : 1; + uint8_t y11 : 1; + uint8_t y6 : 1; + uint8_t y7 : 1; + + uint8_t p1 : 1; + uint8_t y10 : 1; + uint32_t preamble : 30; + }; +} IndalaFormat; + +void DecoderIndala::verify_inner() { + IndalaFormat id; + id.raw = readed_data; + + // preamble + //if((data >> 34) != 0b000000000000000000000000000001) return; + if(id.preamble != 1) return; + + // static data bits + //if((data & 0b100001100111) != 0b101) return; + if(id.static2 != 0 && id.static1 != 0 && id.static0 != 0b101) return; + + // Indala checksum + uint8_t sum_to_check = id.y2 + id.y4 + id.y7 + id.y8 + id.y10 + id.y11 + id.y14 + id.y16; + + if(sum_to_check % 2 == 0) { + if(id.checksum != 0b10) return; + } else { + if(id.checksum != 0b01) return; + } + + // read facility number + facility = (id.x1 << 7) + (id.x2 << 6) + (id.x3 << 5) + (id.x4 << 4) + (id.x5 << 3) + + (id.x6 << 2) + (id.x7 << 1) + (id.x8 << 0); + + // read serial number + number = (id.y1 << 15) + (id.y2 << 14) + (id.y3 << 13) + (id.y4 << 12) + (id.y5 << 11) + + (id.y6 << 10) + (id.y7 << 9) + (id.y8 << 8) + (id.y9 << 7) + (id.y10 << 6) + + (id.y11 << 5) + (id.y12 << 4) + (id.y13 << 3) + (id.y14 << 2) + (id.y15 << 1) + + (id.y16 << 0); + + // Wiegand checksum left + sum_to_check = 0; + for(int8_t i = 0; i < 8; i--) { + if((facility >> i) & 1) { + sum_to_check += 1; + } + } + + for(int8_t i = 0; i < 4; i--) { + if((number >> i) & 1) { + sum_to_check += 1; + } + } + + if(id.p1) { + sum_to_check += 1; + } + + if((sum_to_check % 2) == 1) return; + + // Wiegand checksum right + sum_to_check = 0; + for(int8_t i = 0; i < 12; i--) { + if((number >> (i + 4)) & 1) { + sum_to_check += 1; + } + } + + if(id.p2) { + sum_to_check += 1; + } + + if((sum_to_check % 2) != 1) return; + + ready = true; +} diff --git a/applications/lf-rfid/helpers/decoder-indala.h b/applications/lf-rfid/helpers/decoder-indala.h new file mode 100644 index 00000000..fd3043d5 --- /dev/null +++ b/applications/lf-rfid/helpers/decoder-indala.h @@ -0,0 +1,28 @@ +#pragma once +#include +#include +#include + +class DecoderIndala { +public: + bool read(uint8_t* data, uint8_t data_size); + void process_front(bool polarity, uint32_t time); + + DecoderIndala(); + +private: + void reset_state(); + + void verify(); + void verify_inner(); + + uint32_t last_pulse_time = 0; + uint32_t pulse_count = 0; + uint32_t overall_pulse_count = 0; + + uint64_t readed_data = 0; + + std::atomic ready; + uint8_t facility = 0; + uint16_t number = 0; +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/emmarine.h b/applications/lf-rfid/helpers/emmarine.h new file mode 100644 index 00000000..5d2e2439 --- /dev/null +++ b/applications/lf-rfid/helpers/emmarine.h @@ -0,0 +1,15 @@ +#pragma once +#include + +#define EM_HEADER_POS 55 +#define EM_HEADER_MASK (0x1FFLLU << EM_HEADER_POS) + +#define EM_FIRST_ROW_POS 50 +#define EM_ROW_COUNT 10 + +#define EM_COLUMN_POS 4 +#define EM_STOP_POS 0 +#define EM_STOP_MASK (0x1LLU << EM_STOP_POS) + +#define EM_HEADER_AND_STOP_MASK (EM_HEADER_MASK | EM_STOP_MASK) +#define EM_HEADER_AND_STOP_DATA (EM_HEADER_MASK) \ No newline at end of file diff --git a/applications/lf-rfid/helpers/encoder-emmarine.cpp b/applications/lf-rfid/helpers/encoder-emmarine.cpp new file mode 100644 index 00000000..204144c4 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-emmarine.cpp @@ -0,0 +1,58 @@ +#include "encoder-emmarine.h" +#include + +void EncoderEM::init(const uint8_t* data, const uint8_t data_size) { + furi_check(data_size == 5); + + // header + card_data = 0b111111111; + + // data + for(uint8_t i = 0; i < 5; i++) { + write_nibble(false, data[i]); + write_nibble(true, data[i]); + } + + // column parity and stop bit + uint8_t parity_sum; + + for(uint8_t c = 0; c < 4; c++) { + parity_sum = 0; + for(uint8_t i = 1; i <= 10; i++) { + uint8_t parity_bit = (card_data >> (i * 5 - 1)) & 1; + parity_sum += parity_bit; + } + card_data = (card_data << 1) | ((parity_sum % 2) & 1); + } + + // stop bit + card_data = (card_data << 1) | 0; + card_data_index = 0; +} + +void EncoderEM::write_nibble(bool low_nibble, uint8_t data) { + uint8_t parity_sum = 0; + uint8_t start = 0; + if(!low_nibble) start = 4; + + for(int8_t i = (start + 3); i >= start; i--) { + parity_sum += (data >> i) & 1; + card_data = (card_data << 1) | ((data >> i) & 1); + } + + card_data = (card_data << 1) | ((parity_sum % 2) & 1); +} + +// data transmitted as manchester encoding +// 0 - high2low +// 1 - low2high +void EncoderEM::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { + *period = clocks_per_bit; + *pulse = clocks_per_bit / 2; + *polarity = (card_data >> (63 - card_data_index)) & 1; + + card_data_index++; + if(card_data_index >= 64) { + card_data_index = 0; + } +} diff --git a/applications/lf-rfid/helpers/encoder-emmarine.h b/applications/lf-rfid/helpers/encoder-emmarine.h new file mode 100644 index 00000000..49605fd9 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-emmarine.h @@ -0,0 +1,23 @@ +#pragma once +#include "encoder-generic.h" + +class EncoderEM : public EncoderGeneric { +public: + /** + * @brief init data to emulate + * + * @param data 1 byte FC, next 4 byte SN + * @param data_size must be 5 + */ + void init(const uint8_t* data, const uint8_t data_size) final; + + void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; + +private: + // clock pulses per bit + static const uint8_t clocks_per_bit = 64; + + uint64_t card_data; + uint8_t card_data_index; + void write_nibble(bool low_nibble, uint8_t data); +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/encoder-generic.h b/applications/lf-rfid/helpers/encoder-generic.h new file mode 100644 index 00000000..dc6ae850 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-generic.h @@ -0,0 +1,27 @@ +#pragma once +#include +#include + +class EncoderGeneric { +public: + /** + * @brief init encoder + * + * @param data data array + * @param data_size data array size + */ + virtual void init(const uint8_t* data, const uint8_t data_size) = 0; + + /** + * @brief Get the next timer pulse + * + * @param polarity pulse polarity true = high2low, false = low2high + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + */ + virtual void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) = 0; + + virtual ~EncoderGeneric(){}; + +private: +}; diff --git a/applications/lf-rfid/helpers/encoder-hid.cpp b/applications/lf-rfid/helpers/encoder-hid.cpp new file mode 100644 index 00000000..b0a6a34a --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-hid.cpp @@ -0,0 +1,13 @@ +#include "encoder-hid.h" +#include + +void EncoderHID::init(const uint8_t* data, const uint8_t data_size) { + card_data = 0b1010000000000000000000000000000010011101111110011001001001010010; + card_data_index = 0; +} + +void EncoderHID::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { + *period = 100; + *pulse = 50; + *polarity = true; +} diff --git a/applications/lf-rfid/helpers/encoder-hid.h b/applications/lf-rfid/helpers/encoder-hid.h new file mode 100644 index 00000000..3d8d3776 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-hid.h @@ -0,0 +1,19 @@ +#pragma once +#include "encoder-generic.h" + +class EncoderHID : public EncoderGeneric { +public: + /** + * @brief init data to emulate + * + * @param data 1 byte FC, next 2 byte SN + * @param data_size must be 3 + */ + void init(const uint8_t* data, const uint8_t data_size) final; + + void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; + +private: + uint64_t card_data; + uint8_t card_data_index; +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/encoder-indala.cpp b/applications/lf-rfid/helpers/encoder-indala.cpp new file mode 100644 index 00000000..67225939 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-indala.cpp @@ -0,0 +1,27 @@ +#include "encoder-indala.h" +#include + +void EncoderIndala::init(const uint8_t* data, const uint8_t data_size) { + card_data = 0b1010000000000000000000000000000010011101111110011001001001010010; + last_polarity = card_data & 1; + card_data_index = 0; +} + +void EncoderIndala::get_next(bool* polarity, uint16_t* period, uint16_t* pulse) { + bool new_bit = (card_data >> (63 - card_data_index)) & 1; + + *period = 2; + *pulse = 1; + *polarity = (new_bit != last_polarity); + + bit_clock_index++; + if(bit_clock_index >= clock_per_bit) { + bit_clock_index = 0; + last_polarity = *polarity; + + card_data_index++; + if(card_data_index >= 64) { + card_data_index = 0; + } + } +} diff --git a/applications/lf-rfid/helpers/encoder-indala.h b/applications/lf-rfid/helpers/encoder-indala.h new file mode 100644 index 00000000..afa3f131 --- /dev/null +++ b/applications/lf-rfid/helpers/encoder-indala.h @@ -0,0 +1,22 @@ +#pragma once +#include "encoder-generic.h" + +class EncoderIndala : public EncoderGeneric { +public: + /** + * @brief init data to emulate + * + * @param data indala raw data + * @param data_size must be 5 + */ + void init(const uint8_t* data, const uint8_t data_size) final; + + void get_next(bool* polarity, uint16_t* period, uint16_t* pulse) final; + +private: + uint64_t card_data; + uint8_t card_data_index; + uint8_t bit_clock_index; + bool last_polarity; + static const uint8_t clock_per_bit = 16; +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/key-info.h b/applications/lf-rfid/helpers/key-info.h new file mode 100644 index 00000000..c8e69caf --- /dev/null +++ b/applications/lf-rfid/helpers/key-info.h @@ -0,0 +1,9 @@ +#pragma once +#include + +static const uint8_t LFRFID_KEY_SIZE = 8; + +enum class LfrfidKeyType : uint8_t { + KeyEmarine, + KeyHID, +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/manchester-decoder.c b/applications/lf-rfid/helpers/manchester-decoder.c new file mode 100644 index 00000000..cbeab44a --- /dev/null +++ b/applications/lf-rfid/helpers/manchester-decoder.c @@ -0,0 +1,34 @@ +#include "manchester-decoder.h" +#include + +static const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011}; +static const ManchesterState manchester_reset_state = ManchesterStateMid1; + +bool manchester_advance( + ManchesterState state, + ManchesterEvent event, + ManchesterState* next_state, + bool* data) { + bool result = false; + ManchesterState new_state; + + if(event == ManchesterEventReset) { + new_state = manchester_reset_state; + } else { + new_state = transitions[state] >> event & 0x3; + if(new_state == state) { + new_state = manchester_reset_state; + } else { + if(new_state == ManchesterStateMid0) { + *data = false; + result = true; + } else if(new_state == ManchesterStateMid1) { + *data = true; + result = true; + } + } + } + + *next_state = new_state; + return result; +} diff --git a/applications/lf-rfid/helpers/manchester-decoder.h b/applications/lf-rfid/helpers/manchester-decoder.h new file mode 100644 index 00000000..580d88e2 --- /dev/null +++ b/applications/lf-rfid/helpers/manchester-decoder.h @@ -0,0 +1,31 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + ManchesterEventShortLow = 0, + ManchesterEventShortHigh = 2, + ManchesterEventLongLow = 4, + ManchesterEventLongHigh = 6, + ManchesterEventReset = 8 +} ManchesterEvent; + +typedef enum { + ManchesterStateStart1 = 0, + ManchesterStateMid1 = 1, + ManchesterStateMid0 = 2, + ManchesterStateStart0 = 3 +} ManchesterState; + +bool manchester_advance( + ManchesterState state, + ManchesterEvent event, + ManchesterState* next_state, + bool* data); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/applications/lf-rfid/helpers/pulse-joiner.cpp b/applications/lf-rfid/helpers/pulse-joiner.cpp new file mode 100644 index 00000000..b6c19cd0 --- /dev/null +++ b/applications/lf-rfid/helpers/pulse-joiner.cpp @@ -0,0 +1,95 @@ +#include "pulse-joiner.h" +#include + +bool PulseJoiner::push_pulse(bool polarity, uint16_t period, uint16_t pulse) { + bool result = false; + furi_check((pulse_index + 1) < pulse_max); + + if(polarity == false && pulse_index == 0) { + // first negative pulse is ommited + + } else { + pulses[pulse_index].polarity = polarity; + pulses[pulse_index].time = pulse; + pulse_index++; + } + + if(period > pulse) { + pulses[pulse_index].polarity = !polarity; + pulses[pulse_index].time = period - pulse; + pulse_index++; + } + + if(pulse_index >= 4) { + // we know that first pulse is always high + // so we wait 2 edges, hi2low and next low2hi + + uint8_t edges_count = 0; + bool last_polarity = pulses[0].polarity; + + for(uint8_t i = 1; i < pulse_index; i++) { + if(pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulses[i].polarity; + } + } + + if(edges_count >= 2) { + result = true; + } + } + + return result; +} + +void PulseJoiner::pop_pulse(uint16_t* period, uint16_t* pulse) { + furi_check(pulse_index <= (pulse_max + 1)); + + uint16_t tmp_period = 0; + uint16_t tmp_pulse = 0; + uint8_t edges_count = 0; + bool last_polarity = pulses[0].polarity; + uint8_t next_fist_pulse = 0; + + for(uint8_t i = 0; i < pulse_max; i++) { + // count edges + if(pulses[i].polarity != last_polarity) { + edges_count++; + last_polarity = pulses[i].polarity; + } + + // wait for 2 edges + if(edges_count == 2) { + next_fist_pulse = i; + break; + } + + // sum pulse time + if(pulses[i].polarity) { + tmp_period += pulses[i].time; + tmp_pulse += pulses[i].time; + } else { + tmp_period += pulses[i].time; + } + pulse_index--; + } + + *period = tmp_period; + *pulse = tmp_pulse; + + // remove counted periods and shift data + for(uint8_t i = 0; i < pulse_max; i++) { + if((next_fist_pulse + i) < pulse_max) { + pulses[i].polarity = pulses[next_fist_pulse + i].polarity; + pulses[i].time = pulses[next_fist_pulse + i].time; + } else { + break; + } + } +} + +PulseJoiner::PulseJoiner() { + for(uint8_t i = 0; i < pulse_max; i++) { + pulses[i] = {false, 0}; + } +} diff --git a/applications/lf-rfid/helpers/pulse-joiner.h b/applications/lf-rfid/helpers/pulse-joiner.h new file mode 100644 index 00000000..96d4482e --- /dev/null +++ b/applications/lf-rfid/helpers/pulse-joiner.h @@ -0,0 +1,36 @@ +#pragma once +#include "stdint.h" + +class PulseJoiner { +public: + /** + * @brief Push timer pulse. First negative pulse is ommited. + * + * @param polarity pulse polarity: true = high2low, false = low2high + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + * + * @return true - next pulse can and must be popped immediatly + */ + bool push_pulse(bool polarity, uint16_t period, uint16_t pulse); + + /** + * @brief Get the next timer pulse. Call only if push_pulse returns true. + * + * @param period overall period time in timer clicks + * @param pulse pulse time in timer clicks + */ + void pop_pulse(uint16_t* period, uint16_t* pulse); + + PulseJoiner(); + +private: + struct Pulse { + bool polarity; + uint16_t time; + }; + + uint8_t pulse_index = 0; + static const uint8_t pulse_max = 6; + Pulse pulses[pulse_max]; +}; \ No newline at end of file diff --git a/applications/lf-rfid/helpers/rfid-reader.cpp b/applications/lf-rfid/helpers/rfid-reader.cpp new file mode 100644 index 00000000..73df7892 --- /dev/null +++ b/applications/lf-rfid/helpers/rfid-reader.cpp @@ -0,0 +1,134 @@ +#include "rfid-reader.h" +#include +#include +#include +#include + +extern COMP_HandleTypeDef hcomp1; + +/** + * @brief private violation assistant for RfidReader + */ +struct RfidReaderAccessor { + static void decode(RfidReader& rfid_reader, bool polarity) { + rfid_reader.decode(polarity); + } +}; + +void RfidReader::decode(bool polarity) { + uint32_t current_dwt_value = DWT->CYCCNT; + + switch(type) { + case Type::Normal: + decoder_em.process_front(polarity, current_dwt_value - last_dwt_value); + decoder_hid26.process_front(polarity, current_dwt_value - last_dwt_value); + //decoder_indala.process_front(polarity, current_dwt_value - last_dwt_value); + //decoder_analyzer.process_front(polarity, current_dwt_value - last_dwt_value); + + last_dwt_value = current_dwt_value; + break; + case Type::Indala: + break; + } +} + +static void comparator_trigger_callback(void* hcomp, void* comp_ctx) { + COMP_HandleTypeDef* _hcomp = static_cast(hcomp); + RfidReader* _this = static_cast(comp_ctx); + + if(hcomp == &hcomp1) { + RfidReaderAccessor::decode( + *_this, (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_HIGH)); + } +} + +RfidReader::RfidReader() { +} + +void RfidReader::start(Type _type) { + type = _type; + + start_gpio(); + switch(type) { + case Type::Normal: + start_timer(); + break; + case Type::Indala: + start_timer_indala(); + break; + } + + start_comparator(); +} + +void RfidReader::stop() { + stop_gpio(); + stop_timer(); + stop_comparator(); +} + +bool RfidReader::read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size) { + bool result = false; + + if(decoder_em.read(data, data_size)) { + *type = LfrfidKeyType::KeyEmarine; + result = true; + } + + if(decoder_hid26.read(data, data_size)) { + *type = LfrfidKeyType::KeyHID; + result = true; + } + + //decoder_indala.read(NULL, 0); + //decoder_analyzer.read(NULL, 0); + + return result; +} + +void RfidReader::start_comparator(void) { + api_interrupt_add(comparator_trigger_callback, InterruptTypeComparatorTrigger, this); + last_dwt_value = DWT->CYCCNT; + + hcomp1.Init.InputMinus = COMP_INPUT_MINUS_1_2VREFINT; + hcomp1.Init.InputPlus = COMP_INPUT_PLUS_IO1; + hcomp1.Init.OutputPol = COMP_OUTPUTPOL_NONINVERTED; + hcomp1.Init.Hysteresis = COMP_HYSTERESIS_LOW; + hcomp1.Init.BlankingSrce = COMP_BLANKINGSRC_NONE; + hcomp1.Init.Mode = COMP_POWERMODE_MEDIUMSPEED; + hcomp1.Init.WindowMode = COMP_WINDOWMODE_DISABLE; + hcomp1.Init.TriggerMode = COMP_TRIGGERMODE_IT_RISING_FALLING; + if(HAL_COMP_Init(&hcomp1) != HAL_OK) { + Error_Handler(); + } + + HAL_COMP_Start(&hcomp1); +} + +void RfidReader::start_timer(void) { + api_hal_rfid_tim_read(125000, 0.5); + api_hal_rfid_tim_read_start(); +} + +void RfidReader::start_timer_indala(void) { + api_hal_rfid_tim_read(62500, 0.25); + api_hal_rfid_tim_read_start(); +} + +void RfidReader::start_gpio(void) { + api_hal_rfid_pins_read(); +} + +void RfidReader::stop_comparator(void) { + HAL_COMP_Stop(&hcomp1); + api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); +} + +void RfidReader::stop_timer(void) { + api_hal_rfid_tim_read_stop(); + api_hal_rfid_tim_reset(); +} + +void RfidReader::stop_gpio(void) { + api_hal_rfid_pins_reset(); +} \ No newline at end of file diff --git a/applications/lf-rfid/helpers/rfid-reader.h b/applications/lf-rfid/helpers/rfid-reader.h new file mode 100644 index 00000000..6fc4ffd9 --- /dev/null +++ b/applications/lf-rfid/helpers/rfid-reader.h @@ -0,0 +1,41 @@ +#pragma once +#include "decoder-analyzer.h" +#include "decoder-emmarine.h" +#include "decoder-hid26.h" +#include "decoder-indala.h" +#include "key-info.h" + +class RfidReader { +public: + enum class Type : uint8_t { + Normal, + Indala, + }; + + RfidReader(); + void start(Type type); + void stop(); + bool read(LfrfidKeyType* type, uint8_t* data, uint8_t data_size); + +private: + friend struct RfidReaderAccessor; + + //DecoderAnalyzer decoder_analyzer; + DecoderEMMarine decoder_em; + DecoderHID26 decoder_hid26; + DecoderIndala decoder_indala; + + uint32_t last_dwt_value; + + void start_comparator(void); + void start_timer(void); + void start_timer_indala(void); + void start_gpio(void); + void stop_comparator(void); + void stop_timer(void); + void stop_gpio(void); + + void decode(bool polarity); + + Type type = Type::Normal; +}; diff --git a/applications/lf-rfid/helpers/rfid-timer-emulator.cpp b/applications/lf-rfid/helpers/rfid-timer-emulator.cpp new file mode 100644 index 00000000..0d9aa789 --- /dev/null +++ b/applications/lf-rfid/helpers/rfid-timer-emulator.cpp @@ -0,0 +1,404 @@ +#include "rfid-timer-emulator.h" + +extern TIM_HandleTypeDef htim1; +/* +static uint16_t times_index = 0; + +constexpr uint16_t hid_237_34672_count = 528; +constexpr uint8_t hid_237_34672[hid_237_34672_count] = { + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, + 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, + 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, + 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, + 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, + 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, + 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, + 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, + 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, + 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, + 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, + 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, + 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, + 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, + 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, + 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, + 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, + 10, 10, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 8, 8, 8, 8, 8, 8, +}; + +static void callback_hid(void* _hw, void* ctx) { + //RfidTimerEmulator* _this = static_cast(ctx); + TIM_HandleTypeDef* hw = static_cast(_hw); + + if(hw == &htim1) { + hw->Instance->ARR = hid_237_34672[times_index] - 1; + hw->Instance->CCR1 = hid_237_34672[times_index] / 2; // - 1 + + times_index++; + if(times_index >= hid_237_34672_count) { + times_index = 0; + } + } +} + +typedef struct { + uint8_t arr; + uint8_t ccr; +} TimerTick; + +constexpr TimerTick indala_data[] = { + {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 3, .ccr = 2}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 2}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 3, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, {.arr = 2, .ccr = 1}, + {.arr = 2, .ccr = 1}, +}; + +constexpr uint16_t indala_size = sizeof(indala_data) / sizeof(TimerTick); + +static void callback_indala(void* _hw, void* ctx) { + //RfidTimerEmulator* _this = static_cast(ctx); + TIM_HandleTypeDef* hw = static_cast(_hw); + + if(hw == &htim1) { + hw->Instance->ARR = indala_data[times_index].arr - 1; + hw->Instance->CCR1 = indala_data[times_index].ccr; + + times_index++; + if(times_index >= indala_size) { + times_index = 0; + } + } +} +*/ + +RfidTimerEmulator::RfidTimerEmulator() { +} + +RfidTimerEmulator::~RfidTimerEmulator() { + std::map::iterator it; + + for(it = encoders.begin(); it != encoders.end(); ++it) { + delete it->second; + encoders.erase(it); + } +} + +void RfidTimerEmulator::start(Type type) { + if(encoders.count(type)) { + current_encoder = encoders.find(type)->second; + uint8_t em_data[5] = {0x53, 0x00, 0x5F, 0xB3, 0xC2}; + + switch(type) { + case Type::EM: + current_encoder->init(em_data, 5); + break; + case Type::HID: + current_encoder->init(nullptr, 3); + break; + case Type::Indala: + current_encoder->init(nullptr, 5); + break; + } + + api_hal_rfid_tim_emulate(125000); + api_hal_rfid_pins_emulate(); + + api_interrupt_add(timer_update_callback, InterruptTypeTimerUpdate, this); + + for(size_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) { + HAL_NVIC_SetPriority(static_cast(i), 15, 0); + } + + HAL_NVIC_SetPriority(TIM1_UP_TIM16_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn); + + api_hal_rfid_tim_emulate_start(); + } else { + // not found + } +} + +void RfidTimerEmulator::stop() { + api_hal_rfid_tim_emulate_stop(); + + api_interrupt_remove(timer_update_callback, InterruptTypeTimerUpdate); +} + +void RfidTimerEmulator::emulate() { +} + +void RfidTimerEmulator::timer_update_callback(void* _hw, void* ctx) { + RfidTimerEmulator* _this = static_cast(ctx); + TIM_HandleTypeDef* hw = static_cast(_hw); + + if(hw == &LFRFID_TIM) { + bool result; + bool polarity; + uint16_t period; + uint16_t pulse; + + do { + _this->current_encoder->get_next(&polarity, &period, &pulse); + result = _this->pulse_joiner.push_pulse(polarity, period, pulse); + } while(result == false); + + _this->pulse_joiner.pop_pulse(&period, &pulse); + + hw->Instance->ARR = period - 1; + hw->Instance->CCR1 = pulse; + } +} diff --git a/applications/lf-rfid/helpers/rfid-timer-emulator.h b/applications/lf-rfid/helpers/rfid-timer-emulator.h new file mode 100644 index 00000000..88bb891b --- /dev/null +++ b/applications/lf-rfid/helpers/rfid-timer-emulator.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include "key-info.h" +#include "encoder-generic.h" +#include "encoder-emmarine.h" +#include "encoder-hid.h" +#include "encoder-indala.h" +#include "pulse-joiner.h" +#include + +class RfidTimerEmulator { +public: + enum class Type : uint8_t { + EM, + HID, + Indala, + }; + + RfidTimerEmulator(); + ~RfidTimerEmulator(); + void start(Type type); + void stop(); + void emulate(); + +private: + EncoderGeneric* current_encoder = nullptr; + + std::map encoders = { + {Type::EM, new EncoderEM()}, + {Type::HID, new EncoderHID()}, + {Type::Indala, new EncoderIndala()}, + }; + + PulseJoiner pulse_joiner; + static void timer_update_callback(void* _hw, void* ctx); +}; \ No newline at end of file diff --git a/applications/lf-rfid/lf-rfid-app.cpp b/applications/lf-rfid/lf-rfid-app.cpp new file mode 100644 index 00000000..8e994c55 --- /dev/null +++ b/applications/lf-rfid/lf-rfid-app.cpp @@ -0,0 +1,146 @@ +#include "lf-rfid-app.h" +#include +#include +#include + +void LfrfidApp::run(void) { + LfrfidEvent event; + bool consumed; + bool exit = false; + + scenes[current_scene]->on_enter(this); + + while(!exit) { + view.receive_event(&event); + + consumed = scenes[current_scene]->on_event(this, &event); + + if(!consumed) { + if(event.type == LfrfidEvent::Type::Back) { + exit = switch_to_previous_scene(); + } + } + }; + + scenes[current_scene]->on_exit(this); +} + +LfrfidApp::LfrfidApp() { + api_hal_power_insomnia_enter(); +} + +LfrfidApp::~LfrfidApp() { + for(std::map::iterator it = scenes.begin(); it != scenes.end(); ++it) { + delete it->second; + scenes.erase(it); + } + + api_hal_power_insomnia_exit(); +} + +LfrfidAppViewManager* LfrfidApp::get_view_manager() { + return &view; +} + +void LfrfidApp::switch_to_next_scene(Scene next_scene) { + previous_scenes_list.push_front(current_scene); + + if(next_scene != Scene::Exit) { + scenes[current_scene]->on_exit(this); + current_scene = next_scene; + scenes[current_scene]->on_enter(this); + } +} + +void LfrfidApp::search_and_switch_to_previous_scene(std::initializer_list scenes_list) { + Scene previous_scene = Scene::Start; + bool scene_found = false; + + while(!scene_found) { + previous_scene = get_previous_scene(); + for(Scene element : scenes_list) { + if(previous_scene == element || previous_scene == Scene::Start) { + scene_found = true; + break; + } + } + } + + scenes[current_scene]->on_exit(this); + current_scene = previous_scene; + scenes[current_scene]->on_enter(this); +} + +bool LfrfidApp::switch_to_previous_scene(uint8_t count) { + Scene previous_scene = Scene::Start; + + for(uint8_t i = 0; i < count; i++) { + previous_scene = get_previous_scene(); + if(previous_scene == Scene::Exit) break; + } + + if(previous_scene == Scene::Exit) { + return true; + } else { + scenes[current_scene]->on_exit(this); + current_scene = previous_scene; + scenes[current_scene]->on_enter(this); + return false; + } +} + +LfrfidApp::Scene LfrfidApp::get_previous_scene() { + Scene scene = previous_scenes_list.front(); + previous_scenes_list.pop_front(); + return scene; +} + +/***************************** NOTIFY *******************************/ + +void LfrfidApp::notify_init() { + // TODO open record + const GpioPin* vibro_record = &vibro_gpio; + hal_gpio_init(vibro_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); + hal_gpio_write(vibro_record, false); +} + +void LfrfidApp::notify_green_blink() { + api_hal_light_set(LightGreen, 0xFF); + delay(10); + api_hal_light_set(LightGreen, 0x00); +} + +void LfrfidApp::notify_green_on() { + api_hal_light_set(LightGreen, 0xFF); +} + +void LfrfidApp::notify_green_off() { + api_hal_light_set(LightGreen, 0x00); +} + +/*************************** TEXT STORE *****************************/ + +char* LfrfidApp::get_text_store() { + return text_store; +} + +uint8_t LfrfidApp::get_text_store_size() { + return text_store_size; +} + +void LfrfidApp::set_text_store(const char* text...) { + va_list args; + va_start(args, text); + + vsnprintf(text_store, text_store_size, text, args); + + va_end(args); +} + +RfidReader* LfrfidApp::get_reader() { + return &reader; +} + +RfidTimerEmulator* LfrfidApp::get_emulator() { + return &emulator; +} \ No newline at end of file diff --git a/applications/lf-rfid/lf-rfid-app.h b/applications/lf-rfid/lf-rfid-app.h new file mode 100644 index 00000000..8bafb05a --- /dev/null +++ b/applications/lf-rfid/lf-rfid-app.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include +#include "lf-rfid-view-manager.h" + +#include "scene/lf-rfid-scene-start.h" +#include "scene/lf-rfid-scene-emulate-indala.h" +#include "scene/lf-rfid-scene-emulate-hid.h" +#include "scene/lf-rfid-scene-emulate-emmarine.h" +#include "scene/lf-rfid-scene-read-normal.h" +#include "scene/lf-rfid-scene-read-indala.h" +#include "scene/lf-rfid-scene-tune.h" + +#include "helpers/rfid-reader.h" +#include "helpers/rfid-timer-emulator.h" + +class LfrfidApp { +public: + void run(void); + + LfrfidApp(); + ~LfrfidApp(); + + enum class Scene : uint8_t { + Exit, + Start, + ReadNormal, + ReadIndala, + EmulateIndala, + EmulateHID, + EmulateEM, + Tune, + }; + + LfrfidAppViewManager* get_view_manager(); + void switch_to_next_scene(Scene index); + void search_and_switch_to_previous_scene(std::initializer_list scenes_list); + bool switch_to_previous_scene(uint8_t count = 1); + Scene get_previous_scene(); + + void notify_init(); + void notify_green_blink(); + void notify_green_on(); + void notify_green_off(); + + char* get_text_store(); + uint8_t get_text_store_size(); + void set_text_store(const char* text...); + + RfidReader* get_reader(); + RfidTimerEmulator* get_emulator(); + +private: + std::list previous_scenes_list = {Scene::Exit}; + Scene current_scene = Scene::Start; + LfrfidAppViewManager view; + + std::map scenes = { + {Scene::Start, new LfrfidSceneStart()}, + {Scene::ReadNormal, new LfrfidSceneReadNormal()}, + {Scene::ReadIndala, new LfrfidSceneReadIndala()}, + {Scene::EmulateIndala, new LfrfidSceneEmulateIndala()}, + {Scene::EmulateHID, new LfrfidSceneEmulateHID()}, + {Scene::EmulateEM, new LfrfidSceneEmulateEMMarine()}, + {Scene::Tune, new LfrfidSceneTune()}, + }; + + static const uint8_t text_store_size = 128; + char text_store[text_store_size + 1]; + + RfidReader reader; + RfidTimerEmulator emulator; +}; \ No newline at end of file diff --git a/applications/lf-rfid/lf-rfid-event.h b/applications/lf-rfid/lf-rfid-event.h new file mode 100644 index 00000000..8b3487ed --- /dev/null +++ b/applications/lf-rfid/lf-rfid-event.h @@ -0,0 +1,21 @@ +#pragma once +#include + +class LfrfidEvent { +public: + // events enum + enum class Type : uint8_t { + Tick, + Back, + MenuSelected, + NextScene, + }; + + // payload + union { + uint32_t menu_index; + } payload; + + // event type + Type type; +}; diff --git a/applications/lf-rfid/lf-rfid-view-manager.cpp b/applications/lf-rfid/lf-rfid-view-manager.cpp new file mode 100644 index 00000000..3b062b6f --- /dev/null +++ b/applications/lf-rfid/lf-rfid-view-manager.cpp @@ -0,0 +1,90 @@ +#include "lf-rfid-view-manager.h" +#include "lf-rfid-event.h" +#include + +LfrfidAppViewManager::LfrfidAppViewManager() { + event_queue = osMessageQueueNew(10, sizeof(LfrfidEvent), NULL); + + view_dispatcher = view_dispatcher_alloc(); + auto callback = cbc::obtain_connector(this, &LfrfidAppViewManager::previous_view_callback); + + // allocate views + submenu = submenu_alloc(); + add_view(ViewType::Submenu, submenu_get_view(submenu)); + + popup = popup_alloc(); + add_view(ViewType::Popup, popup_get_view(popup)); + + tune = new LfRfidViewTune(); + add_view(ViewType::Tune, tune->get_view()); + + gui = static_cast(furi_record_open("gui")); + view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen); + + // set previous view callback for all views + view_set_previous_callback(submenu_get_view(submenu), callback); + view_set_previous_callback(popup_get_view(popup), callback); + view_set_previous_callback(tune->get_view(), callback); +} + +LfrfidAppViewManager::~LfrfidAppViewManager() { + // remove views + view_dispatcher_remove_view( + view_dispatcher, static_cast(LfrfidAppViewManager::ViewType::Submenu)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(LfrfidAppViewManager::ViewType::Popup)); + view_dispatcher_remove_view( + view_dispatcher, static_cast(LfrfidAppViewManager::ViewType::Tune)); + + // free view modules + submenu_free(submenu); + popup_free(popup); + delete tune; + + // free dispatcher + view_dispatcher_free(view_dispatcher); + + // free event queue + osMessageQueueDelete(event_queue); +} + +void LfrfidAppViewManager::switch_to(ViewType type) { + view_dispatcher_switch_to_view(view_dispatcher, static_cast(type)); +} + +Submenu* LfrfidAppViewManager::get_submenu() { + return submenu; +} + +Popup* LfrfidAppViewManager::get_popup() { + return popup; +} + +LfRfidViewTune* LfrfidAppViewManager::get_tune() { + return tune; +} + +void LfrfidAppViewManager::receive_event(LfrfidEvent* event) { + if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) { + event->type = LfrfidEvent::Type::Tick; + } +} + +void LfrfidAppViewManager::send_event(LfrfidEvent* event) { + osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0); + furi_check(result == osOK); +} + +uint32_t LfrfidAppViewManager::previous_view_callback(void* context) { + if(event_queue != NULL) { + LfrfidEvent event; + event.type = LfrfidEvent::Type::Back; + send_event(&event); + } + + return VIEW_IGNORE; +} + +void LfrfidAppViewManager::add_view(ViewType view_type, View* view) { + view_dispatcher_add_view(view_dispatcher, static_cast(view_type), view); +} \ No newline at end of file diff --git a/applications/lf-rfid/lf-rfid-view-manager.h b/applications/lf-rfid/lf-rfid-view-manager.h new file mode 100644 index 00000000..64c24335 --- /dev/null +++ b/applications/lf-rfid/lf-rfid-view-manager.h @@ -0,0 +1,42 @@ +#pragma once +#include +#include +#include +#include +#include "lf-rfid-event.h" +#include "view/lf-rfid-view-tune.h" + +class LfrfidAppViewManager { +public: + enum class ViewType : uint8_t { + Submenu, + Popup, + Tune, + }; + + osMessageQueueId_t event_queue; + + LfrfidAppViewManager(); + ~LfrfidAppViewManager(); + + void switch_to(ViewType type); + + void receive_event(LfrfidEvent* event); + void send_event(LfrfidEvent* event); + + Submenu* get_submenu(); + Popup* get_popup(); + LfRfidViewTune* get_tune(); + +private: + ViewDispatcher* view_dispatcher; + Gui* gui; + + uint32_t previous_view_callback(void* context); + void add_view(ViewType view_type, View* view); + + // view elements + Submenu* submenu; + Popup* popup; + LfRfidViewTune* tune; +}; \ No newline at end of file diff --git a/applications/lf-rfid/lf-rfid.c b/applications/lf-rfid/lf-rfid.c deleted file mode 100644 index a823ecca..00000000 --- a/applications/lf-rfid/lf-rfid.c +++ /dev/null @@ -1,421 +0,0 @@ -#include -#include -#include -#include - -typedef enum { EventTypeTick, EventTypeKey, EventTypeRx } EventType; - -typedef struct { - uint8_t dummy; -} RxEvent; - -typedef struct { - union { - InputEvent input; - RxEvent rx; - } value; - EventType type; -} AppEvent; - -typedef struct { - uint32_t freq_khz; - bool on; - uint8_t customer_id; - uint32_t em_data; - bool dirty; - bool dirty_freq; -} State; - -static void render_callback(Canvas* canvas, void* ctx) { - State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25); - - canvas_clear(canvas); - - canvas_set_color(canvas, ColorBlack); - canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 2, 12, "LF RFID"); - - canvas_draw_str(canvas, 2, 24, state->on ? "Reading" : "Emulating"); - - char buf[30]; - - snprintf(buf, sizeof(buf), "%d kHz", (int)state->freq_khz); - canvas_draw_str(canvas, 2, 36, buf); - - snprintf(buf, sizeof(buf), "%02d:%010ld", state->customer_id, state->em_data); - canvas_draw_str(canvas, 2, 45, buf); - - release_mutex((ValueMutex*)ctx, state); -} - -static void input_callback(InputEvent* input_event, void* ctx) { - osMessageQueueId_t event_queue = ctx; - - if(input_event->type != InputTypeShort) return; - - AppEvent event; - event.type = EventTypeKey; - event.value.input = *input_event; - osMessageQueuePut(event_queue, &event, 1, osWaitForever); -} - -extern TIM_HandleTypeDef TIM_C; -void em4100_emulation(uint8_t* data, GpioPin* pin); -void prepare_data(uint32_t ID, uint32_t VENDOR, uint8_t* data); - -GpioPin debug_0 = {.pin = GPIO_PIN_2, .port = GPIOB}; -GpioPin debug_1 = {.pin = GPIO_PIN_3, .port = GPIOC}; - -extern COMP_HandleTypeDef hcomp1; - -typedef struct { - osMessageQueueId_t event_queue; - uint32_t prev_dwt; - int8_t symbol; - bool center; - size_t symbol_cnt; - StreamBufferHandle_t stream_buffer; - uint8_t* int_buffer; -} ComparatorCtx; - -void init_comp_ctx(ComparatorCtx* ctx) { - ctx->prev_dwt = 0; - ctx->symbol = -1; // init state - ctx->center = false; - ctx->symbol_cnt = 0; - xStreamBufferReset(ctx->stream_buffer); - - for(size_t i = 0; i < 64; i++) { - ctx->int_buffer[i] = 0; - } -} - -void comparator_trigger_callback(void* hcomp, void* comp_ctx) { - ComparatorCtx* ctx = (ComparatorCtx*)comp_ctx; - - uint32_t dt = (DWT->CYCCNT - ctx->prev_dwt) / (SystemCoreClock / 1000000.0f); - ctx->prev_dwt = DWT->CYCCNT; - - if(dt < 150) return; // supress noise - - // wait message will be consumed - if(xStreamBufferBytesAvailable(ctx->stream_buffer) == 64) return; - - hal_gpio_write(&debug_0, true); - - // TOOD F4 and F5 differ - bool rx_value = hal_gpio_get_rfid_in_level(); - - if(dt > 384) { - // change symbol 0->1 or 1->0 - ctx->symbol = rx_value; - ctx->center = true; - } else { - // same symbol as prev or center - ctx->center = !ctx->center; - } - - /* - hal_gpio_write(&debug_1, true); - delay_us(center ? 10 : 30); - hal_gpio_write(&debug_1, false); - */ - - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - - if(ctx->center && ctx->symbol != -1) { - /* - hal_gpio_write(&debug_0, true); - delay_us(symbol ? 10 : 30); - hal_gpio_write(&debug_0, false); - */ - - ctx->int_buffer[ctx->symbol_cnt] = ctx->symbol; - ctx->symbol_cnt++; - } - - // check preamble - if(ctx->symbol_cnt <= 9 && ctx->symbol == 0) { - ctx->symbol_cnt = 0; - ctx->symbol = -1; - } - - // check stop bit - if(ctx->symbol_cnt == 64 && ctx->symbol == 1) { - ctx->symbol_cnt = 0; - ctx->symbol = -1; - } - - // TODO - // write only 9..64 symbols directly to streambuffer - - if(ctx->symbol_cnt == 64) { - if(xStreamBufferSendFromISR( - ctx->stream_buffer, ctx->int_buffer, 64, &xHigherPriorityTaskWoken) == 64) { - AppEvent event; - event.type = EventTypeRx; - osMessageQueuePut(ctx->event_queue, &event, 0, 0); - } - - ctx->symbol_cnt = 0; - } - - hal_gpio_write(&debug_0, false); - - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); -} - -const uint8_t ROW_SIZE = 4; -const uint8_t LINE_SIZE = 10; - -static bool even_check(uint8_t* buf) { - uint8_t col_parity_sum[ROW_SIZE]; - for(uint8_t col = 0; col < ROW_SIZE; col++) { - col_parity_sum[col] = 0; - } - - // line parity - for(uint8_t line = 0; line < LINE_SIZE; line++) { - printf("%d: ", line); - uint8_t parity_sum = 0; - for(uint8_t col = 0; col < ROW_SIZE; col++) { - parity_sum += buf[line * (ROW_SIZE + 1) + col]; - col_parity_sum[col] += buf[line * (ROW_SIZE + 1) + col]; - printf("%d ", buf[line * (ROW_SIZE + 1) + col]); - } - if((1 & parity_sum) != buf[line * (ROW_SIZE + 1) + ROW_SIZE]) { - printf( - "line parity fail at %d (%d : %d)\n", - line, - parity_sum, - buf[line * (ROW_SIZE + 1) + ROW_SIZE]); - return false; - } - printf("\r\n"); - } - - for(uint8_t col = 0; col < ROW_SIZE; col++) { - if((1 & col_parity_sum[col]) != buf[LINE_SIZE * (ROW_SIZE + 1) + col]) { - printf( - "col parity fail at %d (%d : %d)\n", - col, - col_parity_sum[col], - buf[LINE_SIZE * (ROW_SIZE + 1) + col]); - return false; - } - } - - return true; -} - -static void extract_data(uint8_t* buf, uint8_t* customer, uint32_t* em_data) { - uint32_t data = 0; - uint8_t offset = 0; - - printf("customer: "); - for(uint8_t line = 0; line < 2; line++) { - for(uint8_t col = 0; col < ROW_SIZE; col++) { - uint32_t bit = buf[line * (ROW_SIZE + 1) + col]; - - data |= bit << (7 - offset); - printf("%ld ", bit); - - offset++; - } - } - printf("\r\n"); - - *customer = data; - - data = 0; - offset = 0; - printf("data: "); - for(uint8_t line = 2; line < LINE_SIZE; line++) { - for(uint8_t col = 0; col < ROW_SIZE; col++) { - uint32_t bit = buf[line * (ROW_SIZE + 1) + col]; - - data |= bit << (31 - offset); - printf("%ld ", bit); - - offset++; - } - } - printf("\r\n"); - - *em_data = data; -} - -int32_t lf_rfid_workaround(void* p) { - osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL); - - // create pin - GpioPin pull_pin = {.pin = RFID_PULL_Pin, .port = RFID_PULL_GPIO_Port}; - // TODO open record - GpioPin* pull_pin_record = &pull_pin; - - hal_gpio_init(pull_pin_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - hal_gpio_init(&debug_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - hal_gpio_init(&debug_1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); - - // pulldown iBtn pin to prevent interference from ibutton - hal_gpio_init((GpioPin*)&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); - hal_gpio_write((GpioPin*)&ibutton_gpio, false); - - // init ctx - ComparatorCtx comp_ctx; - - // internal buffer - uint8_t int_bufer[64]; - - comp_ctx.stream_buffer = xStreamBufferCreate(64, 64); - comp_ctx.int_buffer = int_bufer; - comp_ctx.event_queue = event_queue; - init_comp_ctx(&comp_ctx); - - if(comp_ctx.stream_buffer == NULL) { - printf("cannot create stream buffer\r\n"); - return 255; - } - - // start comp - HAL_COMP_Start(&hcomp1); - - uint8_t raw_data[64]; - for(size_t i = 0; i < 64; i++) { - raw_data[i] = 0; - } - - State _state; - _state.freq_khz = 125; - _state.on = false; - _state.customer_id = 00; - _state.em_data = 4378151; - _state.dirty = true; - _state.dirty_freq = true; - - ValueMutex state_mutex; - if(!init_mutex(&state_mutex, &_state, sizeof(State))) { - printf("cannot create mutex\r\n"); - return 255; - } - - ViewPort* view_port = view_port_alloc(); - - view_port_draw_callback_set(view_port, render_callback, &state_mutex); - view_port_input_callback_set(view_port, input_callback, event_queue); - - // Open GUI and register view_port - Gui* gui = furi_record_open("gui"); - gui_add_view_port(gui, view_port, GuiLayerFullscreen); - - AppEvent event; - - while(1) { - osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 1024 / 8); - - if(event.type == EventTypeRx && event_status == osOK) { - size_t received = xStreamBufferReceive(comp_ctx.stream_buffer, raw_data, 64, 0); - printf("received: %d\r\n", received); - if(received == 64) { - if(even_check(&raw_data[9])) { - State* state = (State*)acquire_mutex_block(&state_mutex); - extract_data(&raw_data[9], &state->customer_id, &state->em_data); - - printf("customer: %02d, data: %010lu\n", state->customer_id, state->em_data); - - release_mutex(&state_mutex, state); - - view_port_update(view_port); - - api_hal_light_set(LightGreen, 0xFF); - osDelay(50); - api_hal_light_set(LightGreen, 0x00); - } - } - } else { - State* state = (State*)acquire_mutex_block(&state_mutex); - - if(event_status == osOK) { - if(event.type == EventTypeKey) { - // press events - if(event.value.input.key == InputKeyBack) { - break; - } - - if(event.value.input.key == InputKeyUp) { - state->dirty_freq = true; - state->freq_khz += 10; - } - - if(event.value.input.key == InputKeyDown) { - state->dirty_freq = true; - state->freq_khz -= 10; - } - - if(event.value.input.key == InputKeyLeft) { - } - - if(event.value.input.key == InputKeyRight) { - } - - if(event.value.input.key == InputKeyOk) { - state->dirty = true; - state->on = !state->on; - } - } - } else { - // event timeout - } - - if(state->dirty) { - if(state->on) { - hal_gpio_write(pull_pin_record, false); - init_comp_ctx(&comp_ctx); - api_interrupt_add( - comparator_trigger_callback, InterruptTypeComparatorTrigger, &comp_ctx); - } else { - prepare_data(state->em_data, state->customer_id, raw_data); - api_interrupt_remove( - comparator_trigger_callback, InterruptTypeComparatorTrigger); - } - - state->dirty_freq = true; // config new PWM next - - state->dirty = false; - } - - if(state->dirty_freq) { - hal_pwmn_set( - state->on ? 0.5 : 0.0, (float)(state->freq_khz * 1000), &LFRFID_TIM, LFRFID_CH); - - state->dirty_freq = false; - } - - if(!state->on) { - em4100_emulation(raw_data, pull_pin_record); - } - release_mutex(&state_mutex, state); - view_port_update(view_port); - } - } - - hal_pwmn_stop(&TIM_C, TIM_CHANNEL_1); // TODO: move to furiac_onexit - api_interrupt_remove(comparator_trigger_callback, InterruptTypeComparatorTrigger); - - hal_gpio_init(pull_pin_record, GpioModeInput, GpioPullNo, GpioSpeedLow); - hal_gpio_init((GpioPin*)&ibutton_gpio, GpioModeInput, GpioPullNo, GpioSpeedLow); - - // TODO remove all view_ports create by app - view_port_enabled_set(view_port, false); - gui_remove_view_port(gui, view_port); - view_port_free(view_port); - - HAL_COMP_Stop(&hcomp1); - - vStreamBufferDelete(comp_ctx.stream_buffer); - - osMessageQueueDelete(event_queue); - - return 0; -} diff --git a/applications/lf-rfid/lf-rfid.cpp b/applications/lf-rfid/lf-rfid.cpp new file mode 100644 index 00000000..11a44a35 --- /dev/null +++ b/applications/lf-rfid/lf-rfid.cpp @@ -0,0 +1,10 @@ +#include "lf-rfid-app.h" + +// app enter function +extern "C" int32_t app_lfrfid(void* p) { + LfrfidApp* app = new LfrfidApp(); + app->run(); + delete app; + + return 255; +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.cpp b/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.cpp new file mode 100644 index 00000000..50d96e7f --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.cpp @@ -0,0 +1,34 @@ +#include "lf-rfid-scene-emulate-emmarine.h" + +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include "../helpers/key-info.h" + +void LfrfidSceneEmulateEMMarine::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, "LF-RFID", 64, 16, AlignCenter, AlignBottom); + app->set_text_store("EM emulation"); + popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup); + app->get_emulator()->start(RfidTimerEmulator::Type::EM); +} + +bool LfrfidSceneEmulateEMMarine::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + return consumed; +} + +void LfrfidSceneEmulateEMMarine::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + + app->get_emulator()->stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.h b/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.h new file mode 100644 index 00000000..f4c0c4a7 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-emmarine.h @@ -0,0 +1,12 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/key-info.h" + +class LfrfidSceneEmulateEMMarine : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.cpp b/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.cpp new file mode 100644 index 00000000..95ef4ca8 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.cpp @@ -0,0 +1,34 @@ +#include "lf-rfid-scene-emulate-hid.h" + +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include "../helpers/key-info.h" + +void LfrfidSceneEmulateHID::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, "LF-RFID", 64, 16, AlignCenter, AlignBottom); + app->set_text_store("HID emulation"); + popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup); + app->get_emulator()->start(RfidTimerEmulator::Type::HID); +} + +bool LfrfidSceneEmulateHID::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + return consumed; +} + +void LfrfidSceneEmulateHID::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + + app->get_emulator()->stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.h b/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.h new file mode 100644 index 00000000..08c9da60 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-hid.h @@ -0,0 +1,12 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/key-info.h" + +class LfrfidSceneEmulateHID : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.cpp b/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.cpp new file mode 100644 index 00000000..68fe5bd4 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.cpp @@ -0,0 +1,34 @@ +#include "lf-rfid-scene-emulate-indala.h" + +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include "../helpers/key-info.h" + +void LfrfidSceneEmulateIndala::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, "LF-RFID", 64, 16, AlignCenter, AlignBottom); + app->set_text_store("Indala emulation"); + popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup); + app->get_emulator()->start(RfidTimerEmulator::Type::Indala); +} + +bool LfrfidSceneEmulateIndala::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + return consumed; +} + +void LfrfidSceneEmulateIndala::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + + app->get_emulator()->stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.h b/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.h new file mode 100644 index 00000000..7506c017 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-emulate-indala.h @@ -0,0 +1,12 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/key-info.h" + +class LfrfidSceneEmulateIndala : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-generic.h b/applications/lf-rfid/scene/lf-rfid-scene-generic.h new file mode 100644 index 00000000..4260b219 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-generic.h @@ -0,0 +1,14 @@ +#pragma once +#include "../lf-rfid-event.h" + +class LfrfidApp; + +class LfrfidScene { +public: + virtual void on_enter(LfrfidApp* app) = 0; + virtual bool on_event(LfrfidApp* app, LfrfidEvent* event) = 0; + virtual void on_exit(LfrfidApp* app) = 0; + virtual ~LfrfidScene(){}; + +private: +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-read-indala.cpp b/applications/lf-rfid/scene/lf-rfid-scene-read-indala.cpp new file mode 100644 index 00000000..ed227273 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-read-indala.cpp @@ -0,0 +1,34 @@ +#include "lf-rfid-scene-read-indala.h" + +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include "../helpers/key-info.h" + +void LfrfidSceneReadIndala::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, "LF-RFID read Indala", 64, 16, AlignCenter, AlignBottom); + app->set_text_store("[decoder not implemented]"); + popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup); + app->get_reader()->start(RfidReader::Type::Indala); +} + +bool LfrfidSceneReadIndala::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + return consumed; +} + +void LfrfidSceneReadIndala::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + + app->get_reader()->stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-read-indala.h b/applications/lf-rfid/scene/lf-rfid-scene-read-indala.h new file mode 100644 index 00000000..a5ab7ad7 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-read-indala.h @@ -0,0 +1,15 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/key-info.h" + +class LfrfidSceneReadIndala : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: + uint32_t success_reads = 0; + static const uint8_t data_size = LFRFID_KEY_SIZE; + uint8_t last_data[data_size] = {0}; +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-read-normal.cpp b/applications/lf-rfid/scene/lf-rfid-scene-read-normal.cpp new file mode 100644 index 00000000..10e6bdef --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-read-normal.cpp @@ -0,0 +1,80 @@ +#include "lf-rfid-scene-read-normal.h" + +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include "../helpers/key-info.h" + +void LfrfidSceneReadNormal::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, "LF-RFID read EM & HID", 64, 16, AlignCenter, AlignBottom); + app->set_text_store("waiting..."); + popup_set_text(popup, app->get_text_store(), 64, 22, AlignCenter, AlignTop); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Popup); + app->get_reader()->start(RfidReader::Type::Normal); +} + +bool LfrfidSceneReadNormal::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + if(event->type == LfrfidEvent::Type::Tick) { + uint8_t data[data_size]; + LfrfidKeyType type; + + if(app->get_reader()->read(&type, data, data_size)) { + app->notify_green_blink(); + + if(memcmp(last_data, data, data_size) == 0) { + success_reads++; + } else { + success_reads = 1; + memcpy(last_data, data, data_size); + } + + switch(type) { + case LfrfidKeyType::KeyEmarine: + app->set_text_store( + "[EM] %02X %02X %02X %02X %02X\n" + "count: %u", + data[0], + data[1], + data[2], + data[3], + data[4], + success_reads); + break; + case LfrfidKeyType::KeyHID: + app->set_text_store( + "[HID26] %02X %02X %02X\n" + "count: %u", + data[0], + data[1], + data[2], + success_reads); + break; + } + popup_set_text( + app->get_view_manager()->get_popup(), + app->get_text_store(), + 64, + 22, + AlignCenter, + AlignTop); + } + } + + return consumed; +} + +void LfrfidSceneReadNormal::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + + Popup* popup = view_manager->get_popup(); + popup_set_header(popup, NULL, 0, 0, AlignCenter, AlignBottom); + popup_set_text(popup, NULL, 0, 0, AlignCenter, AlignTop); + + app->get_reader()->stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-read-normal.h b/applications/lf-rfid/scene/lf-rfid-scene-read-normal.h new file mode 100644 index 00000000..538a9ebd --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-read-normal.h @@ -0,0 +1,15 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/key-info.h" + +class LfrfidSceneReadNormal : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: + uint32_t success_reads = 0; + static const uint8_t data_size = LFRFID_KEY_SIZE; + uint8_t last_data[data_size] = {0}; +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-start.cpp b/applications/lf-rfid/scene/lf-rfid-scene-start.cpp new file mode 100644 index 00000000..d5e5a30b --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-start.cpp @@ -0,0 +1,77 @@ +#include "lf-rfid-scene-start.h" +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include + +typedef enum { + SubmenuIndexReadNormal, + SubmenuIndexReadIndala, + SubmenuIndexEmulateEM, + SubmenuIndexEmulateHID, + SubmenuIndexEmulateIndala, + SubmenuIndexTune +} SubmenuIndex; + +void LfrfidSceneStart::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + auto callback = cbc::obtain_connector(this, &LfrfidSceneStart::submenu_callback); + + submenu_add_item(submenu, "Read Normal", SubmenuIndexReadNormal, callback, app); + submenu_add_item(submenu, "Read Indala", SubmenuIndexReadIndala, callback, app); + submenu_add_item(submenu, "Emulate EM", SubmenuIndexEmulateEM, callback, app); + submenu_add_item(submenu, "Emulate HID", SubmenuIndexEmulateHID, callback, app); + submenu_add_item(submenu, "Emulate Indala", SubmenuIndexEmulateIndala, callback, app); + submenu_add_item(submenu, "Tune", SubmenuIndexTune, callback, app); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Submenu); +} + +bool LfrfidSceneStart::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + if(event->type == LfrfidEvent::Type::MenuSelected) { + switch(event->payload.menu_index) { + case SubmenuIndexReadNormal: + app->switch_to_next_scene(LfrfidApp::Scene::ReadNormal); + break; + case SubmenuIndexReadIndala: + app->switch_to_next_scene(LfrfidApp::Scene::ReadIndala); + break; + case SubmenuIndexEmulateEM: + app->switch_to_next_scene(LfrfidApp::Scene::EmulateEM); + break; + break; + case SubmenuIndexEmulateHID: + app->switch_to_next_scene(LfrfidApp::Scene::EmulateHID); + break; + case SubmenuIndexEmulateIndala: + app->switch_to_next_scene(LfrfidApp::Scene::EmulateIndala); + break; + case SubmenuIndexTune: + app->switch_to_next_scene(LfrfidApp::Scene::Tune); + break; + } + consumed = true; + } + + return consumed; +} + +void LfrfidSceneStart::on_exit(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + Submenu* submenu = view_manager->get_submenu(); + + submenu_clean(submenu); +} + +void LfrfidSceneStart::submenu_callback(void* context, uint32_t index) { + LfrfidApp* app = static_cast(context); + LfrfidEvent event; + + event.type = LfrfidEvent::Type::MenuSelected; + event.payload.menu_index = index; + + app->get_view_manager()->send_event(&event); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-start.h b/applications/lf-rfid/scene/lf-rfid-scene-start.h new file mode 100644 index 00000000..33a577b3 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-start.h @@ -0,0 +1,13 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/rfid-timer-emulator.h" + +class LfrfidSceneStart : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: + void submenu_callback(void* context, uint32_t index); +}; \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-tune.cpp b/applications/lf-rfid/scene/lf-rfid-scene-tune.cpp new file mode 100644 index 00000000..98d99499 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-tune.cpp @@ -0,0 +1,35 @@ +#include "lf-rfid-scene-tune.h" +#include "../lf-rfid-app.h" +#include "../lf-rfid-view-manager.h" +#include "../lf-rfid-event.h" +#include + +void LfrfidSceneTune::on_enter(LfrfidApp* app) { + LfrfidAppViewManager* view_manager = app->get_view_manager(); + //LfRfidViewTune* tune = view_manager->get_tune(); + + view_manager->switch_to(LfrfidAppViewManager::ViewType::Tune); + + reader.start(RfidReader::Type::Indala); +} + +bool LfrfidSceneTune::on_event(LfrfidApp* app, LfrfidEvent* event) { + bool consumed = false; + + if(event->type == LfrfidEvent::Type::Tick) { + LfRfidViewTune* tune = app->get_view_manager()->get_tune(); + + if(tune->is_dirty()) { + LFRFID_TIM.Instance->ARR = tune->get_ARR(); + LFRFID_TIM.Instance->CCR1 = tune->get_CCR(); + } + } + + return consumed; +} + +void LfrfidSceneTune::on_exit(LfrfidApp* app) { + //LfRfidViewTune* tune = app->get_view_manager()->get_tune(); + + reader.stop(); +} \ No newline at end of file diff --git a/applications/lf-rfid/scene/lf-rfid-scene-tune.h b/applications/lf-rfid/scene/lf-rfid-scene-tune.h new file mode 100644 index 00000000..17e49b76 --- /dev/null +++ b/applications/lf-rfid/scene/lf-rfid-scene-tune.h @@ -0,0 +1,13 @@ +#pragma once +#include "lf-rfid-scene-generic.h" +#include "../helpers/rfid-reader.h" + +class LfrfidSceneTune : public LfrfidScene { +public: + void on_enter(LfrfidApp* app) final; + bool on_event(LfrfidApp* app, LfrfidEvent* event) final; + void on_exit(LfrfidApp* app) final; + +private: + RfidReader reader; +}; \ No newline at end of file diff --git a/applications/lf-rfid/view/lf-rfid-view-tune.cpp b/applications/lf-rfid/view/lf-rfid-view-tune.cpp new file mode 100644 index 00000000..3c83822c --- /dev/null +++ b/applications/lf-rfid/view/lf-rfid-view-tune.cpp @@ -0,0 +1,202 @@ +#include "lf-rfid-view-tune.h" +#include +#include +#include +#include + +struct LfRfidViewTuneModel { + bool dirty; + bool fine; + uint32_t ARR; + uint32_t CCR; + int pos; +}; + +void LfRfidViewTune::view_draw_callback(Canvas* canvas, void* _model) { + LfRfidViewTuneModel* model = reinterpret_cast(_model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + if(model->fine) { + canvas_draw_box( + canvas, + 128 - canvas_string_width(canvas, "Fine") - 4, + 0, + canvas_string_width(canvas, "Fine") + 4, + canvas_current_font_height(canvas) + 1); + canvas_set_color(canvas, ColorWhite); + } + canvas_draw_str_aligned(canvas, 128 - 2, 2, AlignRight, AlignTop, "Fine"); + canvas_set_color(canvas, ColorBlack); + + constexpr uint8_t buffer_size = 128; + char buffer[buffer_size + 1]; + snprintf( + buffer, + buffer_size, + "%sARR: %lu\n" + "freq = %.4f\n" + "%sCCR: %lu\n" + "duty = %.4f", + model->pos == 0 ? ">" : "", + model->ARR, + (float)SystemCoreClock / ((float)model->ARR + 1), + model->pos == 1 ? ">" : "", + model->CCR, + ((float)model->CCR + 1) / ((float)model->ARR + 1) * 100.0f); + elements_multiline_text_aligned(canvas, 2, 2, AlignLeft, AlignTop, buffer); +} + +bool LfRfidViewTune::view_input_callback(InputEvent* event, void* context) { + LfRfidViewTune* _this = reinterpret_cast(context); + bool consumed = false; + + // Process key presses only + if(event->type == InputTypeShort || event->type == InputTypeRepeat) { + consumed = true; + + switch(event->key) { + case InputKeyLeft: + _this->button_left(); + break; + case InputKeyRight: + _this->button_right(); + break; + case InputKeyUp: + _this->button_up(); + break; + case InputKeyDown: + _this->button_down(); + break; + case InputKeyOk: + _this->button_ok(); + break; + default: + consumed = false; + break; + } + } + + return consumed; +} + +void LfRfidViewTune::button_up() { + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + if(model->pos > 0) model->pos--; + return true; + }); +} + +void LfRfidViewTune::button_down() { + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + if(model->pos < 1) model->pos++; + return true; + }); +} + +void LfRfidViewTune::button_left() { + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + if(model->pos == 0) { + if(model->fine) { + model->ARR -= 1; + } else { + model->ARR -= 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR -= 1; + } else { + model->CCR -= 10; + } + } + + model->dirty = true; + return true; + }); +} + +void LfRfidViewTune::button_right() { + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + if(model->pos == 0) { + if(model->fine) { + model->ARR += 1; + } else { + model->ARR += 10; + } + } else if(model->pos == 1) { + if(model->fine) { + model->CCR += 1; + } else { + model->CCR += 10; + } + } + + model->dirty = true; + return true; + }); +} + +void LfRfidViewTune::button_ok() { + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + model->fine = !model->fine; + return true; + }); +} + +LfRfidViewTune::LfRfidViewTune() { + view = view_alloc(); + view_set_context(view, this); + view_allocate_model(view, ViewModelTypeLocking, sizeof(LfRfidViewTuneModel)); + + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + model->dirty = true; + model->fine = false; + model->ARR = 511; + model->CCR = 255; + model->pos = 0; + return true; + }); + + view_set_draw_callback(view, cbc::obtain_connector(this, &LfRfidViewTune::view_draw_callback)); + view_set_input_callback( + view, cbc::obtain_connector(this, &LfRfidViewTune::view_input_callback)); +} + +LfRfidViewTune::~LfRfidViewTune() { + view_free(view); +} + +View* LfRfidViewTune::get_view() { + return view; +} + +bool LfRfidViewTune::is_dirty() { + bool result; + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + result = model->dirty; + model->dirty = false; + return false; + }); + + return result; +} + +uint32_t LfRfidViewTune::get_ARR() { + uint32_t result; + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + result = model->ARR; + return false; + }); + + return result; +} + +uint32_t LfRfidViewTune::get_CCR() { + uint32_t result; + with_view_model_cpp(view, LfRfidViewTuneModel, model, { + result = model->CCR; + return false; + }); + + return result; +} diff --git a/applications/lf-rfid/view/lf-rfid-view-tune.h b/applications/lf-rfid/view/lf-rfid-view-tune.h new file mode 100644 index 00000000..cfac98e2 --- /dev/null +++ b/applications/lf-rfid/view/lf-rfid-view-tune.h @@ -0,0 +1,25 @@ +#pragma once +#include + +class LfRfidViewTune { +public: + LfRfidViewTune(); + ~LfRfidViewTune(); + + View* get_view(); + + bool is_dirty(); + uint32_t get_ARR(); + uint32_t get_CCR(); + +private: + View* view; + void view_draw_callback(Canvas* canvas, void* _model); + bool view_input_callback(InputEvent* event, void* context); + + void button_up(); + void button_down(); + void button_left(); + void button_right(); + void button_ok(); +}; diff --git a/firmware/targets/api-hal-include/api-hal-ibutton.h b/firmware/targets/api-hal-include/api-hal-ibutton.h new file mode 100644 index 00000000..cc19e7e9 --- /dev/null +++ b/firmware/targets/api-hal-include/api-hal-ibutton.h @@ -0,0 +1,20 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void api_hal_ibutton_start(); + +void api_hal_ibutton_stop(); + +void api_hal_ibutton_pin_low(); + +void api_hal_ibutton_pin_high(); + +bool api_hal_ibutton_pin_get_level(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/api-hal-include/api-hal-rfid.h b/firmware/targets/api-hal-include/api-hal-rfid.h new file mode 100644 index 00000000..16de695b --- /dev/null +++ b/firmware/targets/api-hal-include/api-hal-rfid.h @@ -0,0 +1,73 @@ +#pragma once +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief config rfid pins to reset state + * + */ +void api_hal_rfid_pins_reset(); + +/** + * @brief config rfid pins to emulate state + * + */ +void api_hal_rfid_pins_emulate(); + +/** + * @brief config rfid pins to read state + * + */ +void api_hal_rfid_pins_read(); + +/** + * @brief config rfid timer to read state + * + * @param freq timer frequency + * @param duty_cycle timer duty cycle, 0.0-1.0 + */ +void api_hal_rfid_tim_read(float freq, float duty_cycle); + +/** + * @brief start read timer + * + */ +void api_hal_rfid_tim_read_start(); + +/** + * @brief stop read timer + * + */ +void api_hal_rfid_tim_read_stop(); + +/** + * @brief config rfid timer to emulate state + * + * @param freq timer frequency + */ +void api_hal_rfid_tim_emulate(float freq); + +/** + * @brief start emulation timer + * + */ +void api_hal_rfid_tim_emulate_start(); + +/** + * @brief stop emulation timer + * + */ +void api_hal_rfid_tim_emulate_stop(); + +/** + * @brief config rfid timers to reset state + * + */ +void api_hal_rfid_tim_reset(); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/firmware/targets/api-hal-include/api-hal.h b/firmware/targets/api-hal-include/api-hal.h index f78bd7ec..a1af6147 100644 --- a/firmware/targets/api-hal-include/api-hal.h +++ b/firmware/targets/api-hal-include/api-hal.h @@ -23,6 +23,8 @@ template struct STOP_EXTERNING_ME {}; #include "api-hal-flash.h" #include "api-hal-subghz.h" #include "api-hal-vibro.h" +#include "api-hal-ibutton.h" +#include "api-hal-rfid.h" /** Init api-hal */ void api_hal_init(); diff --git a/firmware/targets/f5/api-hal/api-hal-gpio.c b/firmware/targets/f5/api-hal/api-hal-gpio.c index 0b300417..0e1df299 100644 --- a/firmware/targets/f5/api-hal/api-hal-gpio.c +++ b/firmware/targets/f5/api-hal/api-hal-gpio.c @@ -2,30 +2,34 @@ #include #include -#define GET_SYSCFG_EXTI_PORT(gpio) (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA :\ - ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB :\ - ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC :\ - ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD :\ - ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : LL_SYSCFG_EXTI_PORTH) +#define GET_SYSCFG_EXTI_PORT(gpio) \ + (((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \ + ((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \ + ((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \ + ((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \ + ((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \ + LL_SYSCFG_EXTI_PORTH) -#define GPIO_PIN_MAP(pin, prefix) (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 :\ - ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 :\ - ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 :\ - ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 :\ - ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 :\ - ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 :\ - ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 :\ - ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 :\ - ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 :\ - ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 :\ - ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 :\ - ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 :\ - ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 :\ - ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 :\ - ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : prefix##15) +#define GPIO_PIN_MAP(pin, prefix) \ + (((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \ + ((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \ + ((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \ + ((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \ + ((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \ + ((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \ + ((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \ + ((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \ + ((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \ + ((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \ + ((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \ + ((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \ + ((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \ + ((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \ + ((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \ + prefix##15) -#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) -#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) +#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE) +#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_) static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER]; @@ -34,7 +38,6 @@ void hal_gpio_init( const GpioMode mode, const GpioPull pull, const GpioSpeed speed) { - uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port); uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin); uint32_t exti_line = GET_EXTI_LINE(gpio->pin); @@ -87,7 +90,7 @@ void hal_gpio_init( LL_EXTI_DisableIT_0_31(exti_line); LL_EXTI_DisableRisingTrig_0_31(exti_line); LL_EXTI_DisableFallingTrig_0_31(exti_line); - } + } // Set not interrupt pin modes if(mode == GpioModeInput) { LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); @@ -104,11 +107,31 @@ void hal_gpio_init( __enable_irq(); } +void hal_gpio_init_alt( + const GpioPin* gpio, + const GpioMode mode, + const GpioPull pull, + const GpioSpeed speed, + const GpioAltFn alt_fn) { + hal_gpio_init(gpio, mode, pull, speed); + + __disable_irq(); + // enable alternate mode + LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); + + // set alternate function + if(gpio->pin < 8) { + LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); + } else { + LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn); + } + __enable_irq(); +} + static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) { uint8_t pin_num = 0; for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) { - if(gpio->pin & (1 << pin_num)) - break; + if(gpio->pin & (1 << pin_num)) break; } return pin_num; } @@ -252,7 +275,7 @@ extern COMP_HandleTypeDef hcomp1; bool hal_gpio_get_rfid_in_level() { bool value = false; - if (api_hal_version_get_hw_version() > 7) { + if(api_hal_version_get_hw_version() > 7) { value = (HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUT_LEVEL_LOW); } else { value = (HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUT_LEVEL_HIGH); diff --git a/firmware/targets/f5/api-hal/api-hal-gpio.h b/firmware/targets/f5/api-hal/api-hal-gpio.h index 46141088..7bed0572 100644 --- a/firmware/targets/f5/api-hal/api-hal-gpio.h +++ b/firmware/targets/f5/api-hal/api-hal-gpio.h @@ -24,7 +24,7 @@ typedef void (*GpioExtiCallback)(void* ctx); */ typedef struct { GpioExtiCallback callback; - void *context; + void* context; volatile bool ready; } GpioInterrupt; @@ -65,6 +65,96 @@ typedef enum { GpioSpeedVeryHigh, } GpioSpeed; +/** + * Gpio alternate functions + */ +typedef enum { + GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */ + GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */ + GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */ + GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */ + GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */ + GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */ + GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */ + GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */ + GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */ + GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */ + GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */ + GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */ + GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */ + GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */ + GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */ + GpioAltFn0SYS = 0, /*!< System Function mapping */ + + GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */ + GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */ + GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */ + + GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */ + GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */ + + GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */ + GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */ + GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */ + + GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */ + GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */ + + GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */ + GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */ + + GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */ + GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */ + GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */ + GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */ + GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */ + GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */ + GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */ + GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */ + GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */ + GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */ + GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */ + GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */ + GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */ + GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */ + GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */ + GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */ + GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */ + GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */ + GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */ + GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */ + GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */ + GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */ + GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */ + GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */ + GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */ + + GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */ + + GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */ + GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */ + + GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */ + + GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */ + GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */ + + GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */ + + GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */ + GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */ + GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */ + + GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */ + + GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */ + GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */ + GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */ + GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */ + + GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */ +} GpioAltFn; + /** * Gpio structure */ @@ -86,6 +176,21 @@ void hal_gpio_init( const GpioPull pull, const GpioSpeed speed); +/** + * GPIO initialization with alternative function + * @param gpio GpioPin + * @param mode GpioMode + * @param pull GpioPull + * @param speed GpioSpeed + * @param alt_fn GpioAltFn + */ +void hal_gpio_init_alt( + const GpioPin* gpio, + const GpioMode mode, + const GpioPull pull, + const GpioSpeed speed, + const GpioAltFn alt_fn); + /** * Add and enable interrupt * @param gpio GpioPin diff --git a/firmware/targets/f5/api-hal/api-hal-ibutton.c b/firmware/targets/f5/api-hal/api-hal-ibutton.c new file mode 100644 index 00000000..87addbbc --- /dev/null +++ b/firmware/targets/f5/api-hal/api-hal-ibutton.c @@ -0,0 +1,24 @@ +#include +#include + +void api_hal_ibutton_start() { + api_hal_ibutton_pin_high(); + hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioSpeedLow, GpioPullNo); +} + +void api_hal_ibutton_stop() { + api_hal_ibutton_pin_high(); + hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioSpeedLow, GpioPullNo); +} + +void api_hal_ibutton_pin_low() { + hal_gpio_write(&ibutton_gpio, false); +} + +void api_hal_ibutton_pin_high() { + hal_gpio_write(&ibutton_gpio, true); +} + +bool api_hal_ibutton_pin_get_level() { + return hal_gpio_read(&ibutton_gpio); +} diff --git a/firmware/targets/f5/api-hal/api-hal-pwm.c b/firmware/targets/f5/api-hal/api-hal-pwm.c index 76bf21a6..c63f9fc6 100644 --- a/firmware/targets/f5/api-hal/api-hal-pwm.c +++ b/firmware/targets/f5/api-hal/api-hal-pwm.c @@ -2,7 +2,7 @@ void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel) { tim->Init.CounterMode = TIM_COUNTERMODE_UP; - tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq); + tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq) - 1; tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_PWM_Init(tim); @@ -22,7 +22,7 @@ void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t chann void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel) { tim->Init.CounterMode = TIM_COUNTERMODE_UP; - tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq - 1); + tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq) - 1; tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_PWM_Init(tim); @@ -48,10 +48,10 @@ void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel) { HAL_TIMEx_PWMN_Stop(tim, channel); } -void irda_pwm_set(float value, float freq){ +void irda_pwm_set(float value, float freq) { hal_pwmn_set(value, freq, &IRDA_TX_TIM, IRDA_TX_CH); } -void irda_pwm_stop(){ +void irda_pwm_stop() { hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH); } \ No newline at end of file diff --git a/firmware/targets/f5/api-hal/api-hal-resources.c b/firmware/targets/f5/api-hal/api-hal-resources.c index 2d0dcf93..a113b021 100644 --- a/firmware/targets/f5/api-hal/api-hal-resources.c +++ b/firmware/targets/f5/api-hal/api-hal-resources.c @@ -4,51 +4,45 @@ const InputPin input_pins[] = { {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true}, - {.port = BUTTON_DOWN_GPIO_Port, - .pin = BUTTON_DOWN_Pin, - .key = InputKeyDown, - .inverted = true}, + {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true}, {.port = BUTTON_RIGHT_GPIO_Port, .pin = BUTTON_RIGHT_Pin, .key = InputKeyRight, .inverted = true}, - {.port = BUTTON_LEFT_GPIO_Port, - .pin = BUTTON_LEFT_Pin, - .key = InputKeyLeft, - .inverted = true}, + {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true}, {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false}, - {.port = BUTTON_BACK_GPIO_Port, - .pin = BUTTON_BACK_Pin, - .key = InputKeyBack, - .inverted = true}, + {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true}, }; const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin); -const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin}; -const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin}; -const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin}; +const GpioPin vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin}; +const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin}; +const GpioPin cc1101_g0_gpio = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin}; -const GpioPin gpio_subghz_cs = { .port=CC1101_CS_GPIO_Port, .pin=CC1101_CS_Pin }; -const GpioPin gpio_display_cs = { .port=DISPLAY_CS_GPIO_Port, .pin=DISPLAY_CS_Pin }; -const GpioPin gpio_display_rst = { .port=DISPLAY_RST_GPIO_Port, .pin=DISPLAY_RST_Pin }; -const GpioPin gpio_display_di = { .port=DISPLAY_DI_GPIO_Port, .pin=DISPLAY_DI_Pin }; -const GpioPin gpio_sdcard_cs = { .port=SD_CS_GPIO_Port, .pin=SD_CS_Pin }; -const GpioPin gpio_nfc_cs = { .port=NFC_CS_GPIO_Port, .pin=NFC_CS_Pin }; +const GpioPin gpio_subghz_cs = {.port = CC1101_CS_GPIO_Port, .pin = CC1101_CS_Pin}; +const GpioPin gpio_display_cs = {.port = DISPLAY_CS_GPIO_Port, .pin = DISPLAY_CS_Pin}; +const GpioPin gpio_display_rst = {.port = DISPLAY_RST_GPIO_Port, .pin = DISPLAY_RST_Pin}; +const GpioPin gpio_display_di = {.port = DISPLAY_DI_GPIO_Port, .pin = DISPLAY_DI_Pin}; +const GpioPin gpio_sdcard_cs = {.port = SD_CS_GPIO_Port, .pin = SD_CS_Pin}; +const GpioPin gpio_nfc_cs = {.port = NFC_CS_GPIO_Port, .pin = NFC_CS_Pin}; -const GpioPin gpio_spi_d_miso = { .port=SPI_D_MISO_GPIO_Port, .pin=SPI_D_MISO_Pin }; -const GpioPin gpio_spi_d_mosi = { .port=SPI_D_MOSI_GPIO_Port, .pin=SPI_D_MOSI_Pin }; -const GpioPin gpio_spi_d_sck = { .port=SPI_D_SCK_GPIO_Port, .pin=SPI_D_SCK_Pin }; -const GpioPin gpio_spi_r_miso = { .port=SPI_R_MISO_GPIO_Port, .pin=SPI_R_MISO_Pin }; -const GpioPin gpio_spi_r_mosi = { .port=SPI_R_MOSI_GPIO_Port, .pin=SPI_R_MOSI_Pin }; -const GpioPin gpio_spi_r_sck = { .port=SPI_R_SCK_GPIO_Port, .pin=SPI_R_SCK_Pin }; +const GpioPin gpio_spi_d_miso = {.port = SPI_D_MISO_GPIO_Port, .pin = SPI_D_MISO_Pin}; +const GpioPin gpio_spi_d_mosi = {.port = SPI_D_MOSI_GPIO_Port, .pin = SPI_D_MOSI_Pin}; +const GpioPin gpio_spi_d_sck = {.port = SPI_D_SCK_GPIO_Port, .pin = SPI_D_SCK_Pin}; +const GpioPin gpio_spi_r_miso = {.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}; +const GpioPin gpio_spi_r_mosi = {.port = SPI_R_MOSI_GPIO_Port, .pin = SPI_R_MOSI_Pin}; +const GpioPin gpio_spi_r_sck = {.port = SPI_R_SCK_GPIO_Port, .pin = SPI_R_SCK_Pin}; -// external gpio's -const GpioPin ext_pc0_gpio = {.port = GPIOC, .pin = GPIO_PIN_0}; -const GpioPin ext_pc1_gpio = {.port = GPIOC, .pin = GPIO_PIN_1}; -const GpioPin ext_pc3_gpio = {.port = GPIOC, .pin = GPIO_PIN_3}; -const GpioPin ext_pb2_gpio = {.port = GPIOB, .pin = GPIO_PIN_2}; -const GpioPin ext_pb3_gpio = {.port = GPIOB, .pin = GPIO_PIN_3}; -const GpioPin ext_pa4_gpio = {.port = GPIOA, .pin = GPIO_PIN_4}; -const GpioPin ext_pa6_gpio = {.port = GPIOA, .pin = GPIO_PIN_6}; -const GpioPin ext_pa7_gpio = {.port = GPIOA, .pin = GPIO_PIN_7}; \ No newline at end of file +const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = GPIO_PIN_0}; +const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = GPIO_PIN_1}; +const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = GPIO_PIN_3}; +const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = GPIO_PIN_2}; +const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = GPIO_PIN_3}; +const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = GPIO_PIN_4}; +const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = GPIO_PIN_6}; +const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = GPIO_PIN_7}; + +const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; +const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; +const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin}; \ No newline at end of file diff --git a/firmware/targets/f5/api-hal/api-hal-resources.h b/firmware/targets/f5/api-hal/api-hal-resources.h index 92f01b96..4c9e1146 100644 --- a/firmware/targets/f5/api-hal/api-hal-resources.h +++ b/firmware/targets/f5/api-hal/api-hal-resources.h @@ -57,9 +57,6 @@ extern const GpioPin vibro_gpio; extern const GpioPin ibutton_gpio; extern const GpioPin cc1101_g0_gpio; -extern const GpioPin gpio_subghz_cs; -extern const GpioPin gpio_display_cs; - extern const GpioPin gpio_subghz_cs; extern const GpioPin gpio_display_cs; extern const GpioPin gpio_display_rst; @@ -74,14 +71,18 @@ extern const GpioPin gpio_spi_r_miso; extern const GpioPin gpio_spi_r_mosi; extern const GpioPin gpio_spi_r_sck; -extern const GpioPin ext_pc0_gpio; -extern const GpioPin ext_pc1_gpio; -extern const GpioPin ext_pc3_gpio; -extern const GpioPin ext_pb2_gpio; -extern const GpioPin ext_pb3_gpio; -extern const GpioPin ext_pa4_gpio; -extern const GpioPin ext_pa6_gpio; -extern const GpioPin ext_pa7_gpio; +extern const GpioPin gpio_ext_pc0; +extern const GpioPin gpio_ext_pc1; +extern const GpioPin gpio_ext_pc3; +extern const GpioPin gpio_ext_pb2; +extern const GpioPin gpio_ext_pb3; +extern const GpioPin gpio_ext_pa4; +extern const GpioPin gpio_ext_pa6; +extern const GpioPin gpio_ext_pa7; + +extern const GpioPin gpio_rfid_pull; +extern const GpioPin gpio_rfid_carrier_out; +extern const GpioPin gpio_rfid_data_in; #ifdef __cplusplus } diff --git a/firmware/targets/f5/api-hal/api-hal-rfid.c b/firmware/targets/f5/api-hal/api-hal-rfid.c new file mode 100644 index 00000000..95e2fdc9 --- /dev/null +++ b/firmware/targets/f5/api-hal/api-hal-rfid.c @@ -0,0 +1,202 @@ +#include +#include +#include + +void api_hal_rfid_pins_reset() { + // ibutton bus disable + api_hal_ibutton_stop(); + + // pulldown rfid antenna + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_write(&gpio_rfid_carrier_out, true); + + // from both sides + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_write(&gpio_rfid_pull, true); +} + +void api_hal_rfid_pins_emulate() { + // ibutton low + api_hal_ibutton_start(); + api_hal_ibutton_pin_low(); + + // pull pin to timer out + hal_gpio_init_alt( + &gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM1); + + // pull rfid antenna from carrier side + hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_write(&gpio_rfid_carrier_out, true); +} + +void api_hal_rfid_pins_read() { + // ibutton low + api_hal_ibutton_start(); + api_hal_ibutton_pin_low(); + + // dont pull rfid antenna + hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo); + hal_gpio_write(&gpio_rfid_pull, false); + + // carrier pin to timer out + hal_gpio_init_alt( + &gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM1); + + // comparator in + hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioSpeedLow, GpioPullNo); +} + +void api_hal_rfid_tim_read(float freq, float duty_cycle) { + // TODO LL init + uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1; + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + // basic PWM setup with needed freq and internal clock + LFRFID_TIM.Instance = TIM1; + LFRFID_TIM.Init.Prescaler = 0; + LFRFID_TIM.Init.CounterMode = TIM_COUNTERMODE_UP; + LFRFID_TIM.Init.Period = period; + LFRFID_TIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + LFRFID_TIM.Init.RepetitionCounter = 0; + LFRFID_TIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; + if(HAL_TIM_Base_Init(&LFRFID_TIM) != HAL_OK) { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if(HAL_TIM_ConfigClockSource(&LFRFID_TIM, &sClockSourceConfig) != HAL_OK) { + Error_Handler(); + } + if(HAL_TIM_PWM_Init(&LFRFID_TIM) != HAL_OK) { + Error_Handler(); + } + + // no master-slave mode + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if(HAL_TIMEx_MasterConfigSynchronization(&LFRFID_TIM, &sMasterConfig) != HAL_OK) { + Error_Handler(); + } + + // pwm config + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = (uint32_t)(LFRFID_TIM.Init.Period * duty_cycle); + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; + sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; + if(HAL_TIM_OC_ConfigChannel(&LFRFID_TIM, &sConfigOC, LFRFID_CH) != HAL_OK) { + Error_Handler(); + } + + // no deadtime + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; + sBreakDeadTimeConfig.DeadTime = 0; + sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; + sBreakDeadTimeConfig.BreakFilter = 0; + sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT; + sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; + sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; + sBreakDeadTimeConfig.Break2Filter = 0; + sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; + if(HAL_TIMEx_ConfigBreakDeadTime(&LFRFID_TIM, &sBreakDeadTimeConfig) != HAL_OK) { + Error_Handler(); + } +} + +void api_hal_rfid_tim_read_start() { + HAL_TIMEx_PWMN_Start(&LFRFID_TIM, LFRFID_CH); +} + +void api_hal_rfid_tim_read_stop() { + HAL_TIMEx_PWMN_Stop(&LFRFID_TIM, LFRFID_CH); +} + +void api_hal_rfid_tim_emulate(float freq) { + // TODO LL init + uint32_t prescaler = (uint32_t)((SystemCoreClock) / freq) - 1; + + TIM_ClockConfigTypeDef sClockSourceConfig = {0}; + TIM_MasterConfigTypeDef sMasterConfig = {0}; + TIM_OC_InitTypeDef sConfigOC = {0}; + TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0}; + + // basic PWM setup with needed freq and internal clock + LFRFID_TIM.Instance = TIM1; + LFRFID_TIM.Init.Prescaler = prescaler; + LFRFID_TIM.Init.CounterMode = TIM_COUNTERMODE_UP; + LFRFID_TIM.Init.Period = 1; + LFRFID_TIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; + LFRFID_TIM.Init.RepetitionCounter = 0; + LFRFID_TIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; + if(HAL_TIM_Base_Init(&LFRFID_TIM) != HAL_OK) { + Error_Handler(); + } + sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; + if(HAL_TIM_ConfigClockSource(&LFRFID_TIM, &sClockSourceConfig) != HAL_OK) { + Error_Handler(); + } + if(HAL_TIM_PWM_Init(&LFRFID_TIM) != HAL_OK) { + Error_Handler(); + } + + // no master-slave mode + sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; + sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET; + sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; + if(HAL_TIMEx_MasterConfigSynchronization(&LFRFID_TIM, &sMasterConfig) != HAL_OK) { + Error_Handler(); + } + + // pwm config + sConfigOC.OCMode = TIM_OCMODE_PWM1; + sConfigOC.Pulse = 1; + sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; + sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH; + sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; + sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET; + sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET; + if(HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, LFRFID_CH) != HAL_OK) { + Error_Handler(); + } + + // no deadtime + sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE; + sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE; + sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF; + sBreakDeadTimeConfig.DeadTime = 0; + sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE; + sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH; + sBreakDeadTimeConfig.BreakFilter = 0; + sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT; + sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE; + sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH; + sBreakDeadTimeConfig.Break2Filter = 0; + sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT; + sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE; + if(HAL_TIMEx_ConfigBreakDeadTime(&LFRFID_TIM, &sBreakDeadTimeConfig) != HAL_OK) { + Error_Handler(); + } +} + +void api_hal_rfid_tim_emulate_start() { + HAL_TIM_PWM_Start_IT(&LFRFID_TIM, LFRFID_CH); + HAL_TIM_Base_Start_IT(&LFRFID_TIM); +} + +void api_hal_rfid_tim_emulate_stop() { + HAL_TIM_Base_Stop(&LFRFID_TIM); + HAL_TIM_PWM_Stop(&LFRFID_TIM, LFRFID_CH); +} + +void api_hal_rfid_tim_reset() { +} \ No newline at end of file