[FL-2219, FL-2251] System, FuriCore, FuriHal: various bug fixes and improvements (#986)
* Replace irq shenanigans with critical section * Power: halt system on power off instead of crash. * Gui: properly handle input event on NULL current_view * FuriHal: correct gpio configuration sequence * FuriHal: cleanup uart initialization. Makefile: allow to disable thread support. * Loader: improve locking, fix simultaneous app start crash, full command line args support for gui apps, more consistent insomnia * Loader: correct spelling * FuriHal: increase gpio configuration readability * FuriHal: correct gpio configuration error when mode is GpioModeEventRiseFall Co-authored-by: DrZlo13 <who.just.the.doctor@gmail.com>
This commit is contained in:
		
							parent
							
								
									6b78a8ccfe
								
							
						
					
					
						commit
						df2d1ad13f
					
				| @ -31,9 +31,9 @@ int WIEGAND::getWiegandType() { | |||||||
| 
 | 
 | ||||||
| bool WIEGAND::available() { | bool WIEGAND::available() { | ||||||
|     bool ret; |     bool ret; | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     ret = DoWiegandConversion(); |     ret = DoWiegandConversion(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
|     return ret; |     return ret; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -37,7 +37,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|                 data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8); |                 data[i] = wiegand->getCodeHigh() >> ((i - 4) * 8); | ||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             __disable_irq(); |             FURI_CRITICAL_ENTER(); | ||||||
|             if(onewire->reset()) { |             if(onewire->reset()) { | ||||||
|                 type = 255; |                 type = 255; | ||||||
|                 onewire->write(0x33); |                 onewire->write(0x33); | ||||||
| @ -49,7 +49,7 @@ bool AccessorSceneStart::on_event(AccessorApp* app, AccessorEvent* event) { | |||||||
|                     data[i] = data[i + 1]; |                     data[i] = data[i + 1]; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             __enable_irq(); |             FURI_CRITICAL_EXIT(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if(type > 0) { |         if(type > 0) { | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ | |||||||
| #include "../helpers/archive_browser.h" | #include "../helpers/archive_browser.h" | ||||||
| #include "../views/archive_browser_view.h" | #include "../views/archive_browser_view.h" | ||||||
| 
 | 
 | ||||||
|  | #define TAG "ArchiveSceneBrowser" | ||||||
|  | 
 | ||||||
| static const char* flipper_app_name[] = { | static const char* flipper_app_name[] = { | ||||||
|     [ArchiveFileTypeIButton] = "iButton", |     [ArchiveFileTypeIButton] = "iButton", | ||||||
|     [ArchiveFileTypeNFC] = "NFC", |     [ArchiveFileTypeNFC] = "NFC", | ||||||
| @ -25,7 +27,12 @@ static void archive_run_in_app( | |||||||
|     } else { |     } else { | ||||||
|         string_init_set(full_path, selected->name); |         string_init_set(full_path, selected->name); | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     LoaderStatus status = | ||||||
|         loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); |         loader_start(loader, flipper_app_name[selected->type], string_get_cstr(full_path)); | ||||||
|  |     if(status != LoaderStatusOk) { | ||||||
|  |         FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     string_clear(full_path); |     string_clear(full_path); | ||||||
|     furi_record_close("loader"); |     furi_record_close("loader"); | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ | |||||||
| #include "desktop_scene.h" | #include "desktop_scene.h" | ||||||
| #include "desktop_scene_i.h" | #include "desktop_scene_i.h" | ||||||
| 
 | 
 | ||||||
|  | #define TAG "DesktopSrv" | ||||||
|  | 
 | ||||||
| #define MAIN_VIEW_DEFAULT (0UL) | #define MAIN_VIEW_DEFAULT (0UL) | ||||||
| 
 | 
 | ||||||
| static void desktop_scene_main_app_started_callback(const void* message, void* context) { | static void desktop_scene_main_app_started_callback(const void* message, void* context) { | ||||||
| @ -142,10 +144,12 @@ bool desktop_scene_main_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 Loader* loader = furi_record_open("loader"); |                 Loader* loader = furi_record_open("loader"); | ||||||
|                 LoaderStatus status = |                 LoaderStatus status = | ||||||
|                     loader_start(loader, FLIPPER_APPS[desktop->settings.favorite].name, NULL); |                     loader_start(loader, FLIPPER_APPS[desktop->settings.favorite].name, NULL); | ||||||
|                 furi_check(status == LoaderStatusOk); |                 if(status != LoaderStatusOk) { | ||||||
|  |                     FURI_LOG_E(TAG, "loader_start failed: %d", status); | ||||||
|  |                 } | ||||||
|                 furi_record_close("loader"); |                 furi_record_close("loader"); | ||||||
|             } else { |             } else { | ||||||
|                 FURI_LOG_E("DesktopSrv", "Can't find favorite application"); |                 FURI_LOG_E(TAG, "Can't find favorite application"); | ||||||
|             } |             } | ||||||
|             consumed = true; |             consumed = true; | ||||||
|             break; |             break; | ||||||
|  | |||||||
| @ -253,29 +253,28 @@ void view_dispatcher_handle_input(ViewDispatcher* view_dispatcher, InputEvent* e | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Deliver event
 |     // Deliver event
 | ||||||
|     if(view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { |     if(view_dispatcher->current_view && | ||||||
|         bool is_consumed = false; |        view_dispatcher->ongoing_input_view == view_dispatcher->current_view) { | ||||||
|         if(view_dispatcher->current_view) { |         // Dispatch input to current view
 | ||||||
|             is_consumed = view_input(view_dispatcher->current_view, event); |         bool is_consumed = view_input(view_dispatcher->current_view, event); | ||||||
|         } | 
 | ||||||
|         if(!is_consumed && (event->type == InputTypeShort || event->type == InputTypeLong)) { |         // Navigate if input is not consumed
 | ||||||
|             // TODO remove view navigation handlers
 |         if(!is_consumed && (event->key == InputKeyBack) && | ||||||
|             uint32_t view_id = VIEW_IGNORE; |            (event->type == InputTypeShort || event->type == InputTypeLong)) { | ||||||
|             if(event->key == InputKeyBack) { |             // Navigate to previous
 | ||||||
|                 view_id = view_previous(view_dispatcher->current_view); |             uint32_t view_id = view_previous(view_dispatcher->current_view); | ||||||
|                 if((view_id == VIEW_IGNORE) && (view_dispatcher->navigation_event_callback)) { |             if(view_id != VIEW_IGNORE) { | ||||||
|                     is_consumed = |                 // Switch to returned view
 | ||||||
|                         view_dispatcher->navigation_event_callback(view_dispatcher->event_context); |                 view_dispatcher_switch_to_view(view_dispatcher, view_id); | ||||||
|                     if(!is_consumed) { |             } else if(view_dispatcher->navigation_event_callback) { | ||||||
|  |                 // Dispatch navigation event
 | ||||||
|  |                 if(!view_dispatcher->navigation_event_callback(view_dispatcher->event_context)) { | ||||||
|  |                     // TODO: should we allow view_dispatcher to stop without navigation_event_callback?
 | ||||||
|                     view_dispatcher_stop(view_dispatcher); |                     view_dispatcher_stop(view_dispatcher); | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|             if(!is_consumed) { |  | ||||||
|                 view_dispatcher_switch_to_view(view_dispatcher, view_id); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { |     } else if(view_dispatcher->ongoing_input_view && event->type == InputTypeRelease) { | ||||||
|         FURI_LOG_D( |         FURI_LOG_D( | ||||||
|             TAG, |             TAG, | ||||||
|  | |||||||
| @ -59,9 +59,8 @@ KeyReader::~KeyReader() { | |||||||
| bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { | bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_size) { | ||||||
|     bool readed = false; |     bool readed = false; | ||||||
| 
 | 
 | ||||||
|     switch(read_mode) { |     if(read_mode == ReadMode::DALLAS) { | ||||||
|     case ReadMode::DALLAS: |         FURI_CRITICAL_ENTER(); | ||||||
|         __disable_irq(); |  | ||||||
|         if(onewire_master->search(data)) { |         if(onewire_master->search(data)) { | ||||||
|             onewire_master->reset_search(); |             onewire_master->reset_search(); | ||||||
|             readed = true; |             readed = true; | ||||||
| @ -69,9 +68,8 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s | |||||||
|         } else { |         } else { | ||||||
|             onewire_master->reset_search(); |             onewire_master->reset_search(); | ||||||
|         } |         } | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         break; |     } else if(read_mode == ReadMode::CYFRAL_METAKOM) { | ||||||
|     case ReadMode::CYFRAL_METAKOM: |  | ||||||
|         if(cyfral_decoder.read(data, 2)) { |         if(cyfral_decoder.read(data, 2)) { | ||||||
|             readed = true; |             readed = true; | ||||||
|             *key_type = iButtonKeyType::KeyCyfral; |             *key_type = iButtonKeyType::KeyCyfral; | ||||||
| @ -79,7 +77,6 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s | |||||||
|             readed = true; |             readed = true; | ||||||
|             *key_type = iButtonKeyType::KeyMetakom; |             *key_type = iButtonKeyType::KeyMetakom; | ||||||
|         } |         } | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return readed; |     return readed; | ||||||
| @ -88,10 +85,10 @@ bool KeyReader::read_key(iButtonKeyType* key_type, uint8_t* data, uint8_t data_s | |||||||
| bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { | bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, uint8_t data_size) { | ||||||
|     bool result = true; |     bool result = true; | ||||||
| 
 | 
 | ||||||
|     switch(key_type) { |     if(key_type == iButtonKeyType::KeyDallas) { | ||||||
|     case iButtonKeyType::KeyDallas: |  | ||||||
|         switch_to(ReadMode::DALLAS); |         switch_to(ReadMode::DALLAS); | ||||||
|         __disable_irq(); | 
 | ||||||
|  |         FURI_CRITICAL_ENTER(); | ||||||
|         if(onewire_master->reset()) { |         if(onewire_master->reset()) { | ||||||
|             onewire_master->write(DS1990::CMD_READ_ROM); |             onewire_master->write(DS1990::CMD_READ_ROM); | ||||||
|             for(uint8_t i = 0; i < data_size; i++) { |             for(uint8_t i = 0; i < data_size; i++) { | ||||||
| @ -101,14 +98,11 @@ bool KeyReader::verify_key(iButtonKeyType key_type, const uint8_t* const data, u | |||||||
|             } |             } | ||||||
|         } else { |         } else { | ||||||
|             result = false; |             result = false; | ||||||
|             break; |  | ||||||
|         } |         } | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         break; |  | ||||||
| 
 | 
 | ||||||
|     default: |     } else { | ||||||
|         result = false; |         result = false; | ||||||
|         break; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
|         bool presence = onewire_master->reset(); |         bool presence = onewire_master->reset(); | ||||||
| 
 | 
 | ||||||
|         if(presence) { |         if(presence) { | ||||||
| @ -89,7 +89,7 @@ bool KeyWriter::compare_key_ds1990(iButtonKey* key) { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
| @ -99,7 +99,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // unlock
 |         // unlock
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -120,7 +120,7 @@ bool KeyWriter::write_1990_1(iButtonKey* key) { | |||||||
|         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |         onewire_master->write(RW1990_1::CMD_WRITE_RECORD_FLAG); | ||||||
|         onewire_write_one_bit(1); |         onewire_write_one_bit(1); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|         if(compare_key_ds1990(key)) { |         if(compare_key_ds1990(key)) { | ||||||
|             result = true; |             result = true; | ||||||
| @ -134,7 +134,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { | |||||||
|     bool result = false; |     bool result = false; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // unlock
 |         // unlock
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -154,7 +154,7 @@ bool KeyWriter::write_1990_2(iButtonKey* key) { | |||||||
|         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |         onewire_master->write(RW1990_2::CMD_WRITE_RECORD_FLAG); | ||||||
|         onewire_write_one_bit(0); |         onewire_write_one_bit(0); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|         if(compare_key_ds1990(key)) { |         if(compare_key_ds1990(key)) { | ||||||
|             result = true; |             result = true; | ||||||
| @ -169,7 +169,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { | |||||||
|     bool result = true; |     bool result = true; | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyDallas) { |     if(key->get_key_type() == iButtonKeyType::KeyDallas) { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|         // write rom, addr is 0x0000
 |         // write rom, addr is 0x0000
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| @ -204,7 +204,7 @@ bool KeyWriter::write_TM2004(iButtonKey* key) { | |||||||
| 
 | 
 | ||||||
|         onewire_master->reset(); |         onewire_master->reset(); | ||||||
| 
 | 
 | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|     } else { |     } else { | ||||||
|         result = false; |         result = false; | ||||||
|     } |     } | ||||||
| @ -216,7 +216,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|     /*bool result = true;
 |     /*bool result = true;
 | ||||||
| 
 | 
 | ||||||
|     // TODO test and encoding
 |     // TODO test and encoding
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     // unlock
 |     // unlock
 | ||||||
|     onewire_master->reset(); |     onewire_master->reset(); | ||||||
| @ -240,13 +240,13 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); |     onewire_master->write(TM01::CMD_WRITE_RECORD_FLAG); | ||||||
|     onewire_write_one_bit(0, 10000); |     onewire_write_one_bit(0, 10000); | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     if(!compare_key_ds1990(key)) { |     if(!compare_key_ds1990(key)) { | ||||||
|         result = false; |         result = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     if(key->get_key_type() == iButtonKeyType::KeyMetakom || |     if(key->get_key_type() == iButtonKeyType::KeyMetakom || | ||||||
|        key->get_key_type() == iButtonKeyType::KeyCyfral) { |        key->get_key_type() == iButtonKeyType::KeyCyfral) { | ||||||
| @ -258,7 +258,7 @@ bool KeyWriter::write_TM01(iButtonKey* key) { | |||||||
|         onewire_write_one_bit(1); |         onewire_write_one_bit(1); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     return result;*/ |     return result;*/ | ||||||
|     return false; |     return false; | ||||||
|  | |||||||
| @ -121,12 +121,12 @@ void RfidWriter::write_em(const uint8_t em_data[5]) { | |||||||
|     em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t)); |     em_card.encode(em_data, 5, reinterpret_cast<uint8_t*>(&em_encoded_data), sizeof(uint64_t)); | ||||||
|     const uint32_t em_config_block_data = 0b00000000000101001000000001000000; |     const uint32_t em_config_block_data = 0b00000000000101001000000001000000; | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     write_block(0, 0, false, em_config_block_data); |     write_block(0, 0, false, em_config_block_data); | ||||||
|     write_block(0, 1, false, em_encoded_data); |     write_block(0, 1, false, em_encoded_data); | ||||||
|     write_block(0, 2, false, em_encoded_data >> 32); |     write_block(0, 2, false, em_encoded_data >> 32); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidWriter::write_hid(const uint8_t hid_data[3]) { | void RfidWriter::write_hid(const uint8_t hid_data[3]) { | ||||||
| @ -136,13 +136,13 @@ void RfidWriter::write_hid(const uint8_t hid_data[3]) { | |||||||
| 
 | 
 | ||||||
|     const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; |     const uint32_t hid_config_block_data = 0b00000000000100000111000001100000; | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     write_block(0, 0, false, hid_config_block_data); |     write_block(0, 0, false, hid_config_block_data); | ||||||
|     write_block(0, 1, false, card_data[0]); |     write_block(0, 1, false, card_data[0]); | ||||||
|     write_block(0, 2, false, card_data[1]); |     write_block(0, 2, false, card_data[1]); | ||||||
|     write_block(0, 3, false, card_data[2]); |     write_block(0, 3, false, card_data[2]); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RfidWriter::write_indala(const uint8_t indala_data[3]) { | void RfidWriter::write_indala(const uint8_t indala_data[3]) { | ||||||
| @ -153,10 +153,10 @@ void RfidWriter::write_indala(const uint8_t indala_data[3]) { | |||||||
| 
 | 
 | ||||||
|     const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; |     const uint32_t indala_config_block_data = 0b00000000000010000001000001000000; | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     write_block(0, 0, false, indala_config_block_data); |     write_block(0, 0, false, indala_config_block_data); | ||||||
|     write_block(0, 1, false, card_data[0]); |     write_block(0, 1, false, card_data[0]); | ||||||
|     write_block(0, 2, false, card_data[1]); |     write_block(0, 2, false, card_data[1]); | ||||||
|     write_reset(); |     write_reset(); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,28 +9,46 @@ | |||||||
| 
 | 
 | ||||||
| static Loader* loader_instance = NULL; | static Loader* loader_instance = NULL; | ||||||
| 
 | 
 | ||||||
|  | static bool | ||||||
|  |     loader_start_application(const FlipperApplication* application, const char* arguments) { | ||||||
|  |     loader_instance->application = application; | ||||||
|  | 
 | ||||||
|  |     furi_assert(loader_instance->application_arguments == NULL); | ||||||
|  |     if(arguments && strlen(arguments) > 0) { | ||||||
|  |         loader_instance->application_arguments = strdup(arguments); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     FURI_LOG_I(TAG, "Starting: %s", loader_instance->application->name); | ||||||
|  | 
 | ||||||
|  |     furi_thread_set_name(loader_instance->application_thread, loader_instance->application->name); | ||||||
|  |     furi_thread_set_stack_size( | ||||||
|  |         loader_instance->application_thread, loader_instance->application->stack_size); | ||||||
|  |     furi_thread_set_context( | ||||||
|  |         loader_instance->application_thread, loader_instance->application_arguments); | ||||||
|  |     furi_thread_set_callback( | ||||||
|  |         loader_instance->application_thread, loader_instance->application->app); | ||||||
|  | 
 | ||||||
|  |     bool result = furi_thread_start(loader_instance->application_thread); | ||||||
|  | 
 | ||||||
|  |     if(!result) { | ||||||
|  |         loader_instance->application = NULL; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return result; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static void loader_menu_callback(void* _ctx, uint32_t index) { | static void loader_menu_callback(void* _ctx, uint32_t index) { | ||||||
|     const FlipperApplication* flipper_app = _ctx; |     const FlipperApplication* application = _ctx; | ||||||
| 
 | 
 | ||||||
|     furi_assert(flipper_app->app); |     furi_assert(application->app); | ||||||
|     furi_assert(flipper_app->name); |     furi_assert(application->name); | ||||||
| 
 | 
 | ||||||
|     if(!loader_lock(loader_instance)) return; |     if(!loader_lock(loader_instance)) { | ||||||
| 
 |         FURI_LOG_E(TAG, "Loader is locked"); | ||||||
|     if(furi_thread_get_state(loader_instance->thread) != FuriThreadStateStopped) { |  | ||||||
|         FURI_LOG_E(TAG, "Can't start app. %s is running", loader_instance->current_app->name); |  | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
| 
 | 
 | ||||||
|     loader_instance->current_app = flipper_app; |     loader_start_application(application, NULL); | ||||||
| 
 |  | ||||||
|     FURI_LOG_I(TAG, "Starting: %s", loader_instance->current_app->name); |  | ||||||
|     furi_thread_set_name(loader_instance->thread, flipper_app->name); |  | ||||||
|     furi_thread_set_stack_size(loader_instance->thread, flipper_app->stack_size); |  | ||||||
|     furi_thread_set_context(loader_instance->thread, NULL); |  | ||||||
|     furi_thread_set_callback(loader_instance->thread, flipper_app->app); |  | ||||||
|     furi_thread_start(loader_instance->thread); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_submenu_callback(void* context, uint32_t index) { | static void loader_submenu_callback(void* context, uint32_t index) { | ||||||
| @ -73,32 +91,36 @@ const FlipperApplication* loader_find_application_by_name(const char* name) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_cli_open(Cli* cli, string_t args, Loader* instance) { | void loader_cli_open(Cli* cli, string_t args, Loader* instance) { | ||||||
|     string_strim(args); |     string_t application_name; | ||||||
|  |     string_init(application_name); | ||||||
| 
 | 
 | ||||||
|     if(string_size(args) == 0) { |     do { | ||||||
|  |         if(!args_read_probably_quoted_string_and_trim(args, application_name)) { | ||||||
|             printf("No application provided\r\n"); |             printf("No application provided\r\n"); | ||||||
|         return; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     const FlipperApplication* application = loader_find_application_by_name(string_get_cstr(args)); |         const FlipperApplication* application = | ||||||
|  |             loader_find_application_by_name(string_get_cstr(application_name)); | ||||||
|         if(!application) { |         if(!application) { | ||||||
|         printf("%s doesn't exists\r\n", string_get_cstr(args)); |             printf("%s doesn't exists\r\n", string_get_cstr(application_name)); | ||||||
|         return; |             break; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     if(furi_thread_get_state(instance->thread) != FuriThreadStateStopped) { |         string_strim(args); | ||||||
|  |         if(!loader_start_application(application, string_get_cstr(args))) { | ||||||
|             printf("Can't start, furi application is running"); |             printf("Can't start, furi application is running"); | ||||||
|             return; |             return; | ||||||
|  |         } else { | ||||||
|  |             // We must to increment lock counter to keep balance
 | ||||||
|  |             // TODO: rewrite whole thing, it's complex as hell
 | ||||||
|  |             FURI_CRITICAL_ENTER(); | ||||||
|  |             instance->lock_count++; | ||||||
|  |             FURI_CRITICAL_EXIT(); | ||||||
|         } |         } | ||||||
|  |     } while(false); | ||||||
| 
 | 
 | ||||||
|     instance->lock_semaphore++; |     string_clear(application_name); | ||||||
|     furi_hal_power_insomnia_enter(); |  | ||||||
|     instance->current_app = application; |  | ||||||
|     printf("Starting: %s\r\n", instance->current_app->name); |  | ||||||
|     furi_thread_set_name(instance->thread, application->name); |  | ||||||
|     furi_thread_set_stack_size(instance->thread, application->stack_size); |  | ||||||
|     furi_thread_set_callback(instance->thread, application->app); |  | ||||||
|     furi_thread_start(instance->thread); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_cli_list(Cli* cli, string_t args, Loader* instance) { | void loader_cli_list(Cli* cli, string_t args, Loader* instance) { | ||||||
| @ -152,62 +174,44 @@ void loader_cli(Cli* cli, string_t args, void* _ctx) { | |||||||
| LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | LoaderStatus loader_start(Loader* instance, const char* name, const char* args) { | ||||||
|     furi_assert(name); |     furi_assert(name); | ||||||
| 
 | 
 | ||||||
|     const FlipperApplication* flipper_app = loader_find_application_by_name(name); |     const FlipperApplication* application = loader_find_application_by_name(name); | ||||||
| 
 | 
 | ||||||
|     if(!flipper_app) { |     if(!application) { | ||||||
|         FURI_LOG_E(TAG, "Can't find application with name %s", name); |         FURI_LOG_E(TAG, "Can't find application with name %s", name); | ||||||
|         return LoaderStatusErrorUnknownApp; |         return LoaderStatusErrorUnknownApp; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool locked = loader_lock(instance); |     if(!loader_lock(loader_instance)) { | ||||||
| 
 |         FURI_LOG_E(TAG, "Loader is locked"); | ||||||
|     if(!locked || (furi_thread_get_state(instance->thread) != FuriThreadStateStopped)) { |  | ||||||
|         FURI_LOG_E(TAG, "Can't start app. %s is running", instance->current_app->name); |  | ||||||
|         /* no need to call loader_unlock() - it is called as soon as application stops */ |  | ||||||
|         return LoaderStatusErrorAppStarted; |         return LoaderStatusErrorAppStarted; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     instance->current_app = flipper_app; |     if(!loader_start_application(application, args)) { | ||||||
|     void* thread_args = NULL; |         return LoaderStatusErrorInternal; | ||||||
|     if(args) { |  | ||||||
|         string_set_str(instance->args, args); |  | ||||||
|         string_strim(instance->args); |  | ||||||
|         thread_args = (void*)string_get_cstr(instance->args); |  | ||||||
|         FURI_LOG_I(TAG, "Start %s app with args: %s", name, args); |  | ||||||
|     } else { |  | ||||||
|         string_reset(instance->args); |  | ||||||
|         FURI_LOG_I(TAG, "Start %s app with no args", name); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     furi_thread_set_name(instance->thread, flipper_app->name); |     return LoaderStatusOk; | ||||||
|     furi_thread_set_stack_size(instance->thread, flipper_app->stack_size); |  | ||||||
|     furi_thread_set_context(instance->thread, thread_args); |  | ||||||
|     furi_thread_set_callback(instance->thread, flipper_app->app); |  | ||||||
| 
 |  | ||||||
|     bool thread_started = furi_thread_start(instance->thread); |  | ||||||
|     return thread_started ? LoaderStatusOk : LoaderStatusErrorInternal; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_lock(Loader* instance) { | bool loader_lock(Loader* instance) { | ||||||
|     bool ret = false; |     FURI_CRITICAL_ENTER(); | ||||||
|     furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); |     bool result = false; | ||||||
|     if(instance->lock_semaphore == 0) { |     if(instance->lock_count == 0) { | ||||||
|         instance->lock_semaphore++; |         instance->lock_count++; | ||||||
|         ret = true; |         result = true; | ||||||
|     } |     } | ||||||
|     furi_check(osMutexRelease(instance->mutex) == osOK); |     FURI_CRITICAL_EXIT(); | ||||||
|     return ret; |     return result; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void loader_unlock(Loader* instance) { | void loader_unlock(Loader* instance) { | ||||||
|     furi_check(osMutexAcquire(instance->mutex, osWaitForever) == osOK); |     FURI_CRITICAL_ENTER(); | ||||||
|     furi_check(instance->lock_semaphore > 0); |     if(instance->lock_count > 0) instance->lock_count--; | ||||||
|     instance->lock_semaphore--; |     FURI_CRITICAL_EXIT(); | ||||||
|     furi_check(osMutexRelease(instance->mutex) == osOK); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool loader_is_locked(Loader* instance) { | bool loader_is_locked(Loader* instance) { | ||||||
|     return (instance->lock_semaphore > 0); |     return instance->lock_count > 0; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { | static void loader_thread_state_callback(FuriThreadState thread_state, void* context) { | ||||||
| @ -219,6 +223,7 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|     if(thread_state == FuriThreadStateRunning) { |     if(thread_state == FuriThreadStateRunning) { | ||||||
|         event.type = LoaderEventTypeApplicationStarted; |         event.type = LoaderEventTypeApplicationStarted; | ||||||
|         furi_pubsub_publish(loader_instance->pubsub, &event); |         furi_pubsub_publish(loader_instance->pubsub, &event); | ||||||
|  |         furi_hal_power_insomnia_enter(); | ||||||
| 
 | 
 | ||||||
|         // Snapshot current memory usage
 |         // Snapshot current memory usage
 | ||||||
|         instance->free_heap_size = memmgr_get_free_heap(); |         instance->free_heap_size = memmgr_get_free_heap(); | ||||||
| @ -239,7 +244,13 @@ static void loader_thread_state_callback(FuriThreadState thread_state, void* con | |||||||
|             TAG, |             TAG, | ||||||
|             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", |             "Application thread stopped. Heap allocation balance: %d. Thread allocation balance: %d.", | ||||||
|             heap_diff, |             heap_diff, | ||||||
|             furi_thread_get_heap_size(instance->thread)); |             furi_thread_get_heap_size(instance->application_thread)); | ||||||
|  | 
 | ||||||
|  |         if(loader_instance->application_arguments) { | ||||||
|  |             free(loader_instance->application_arguments); | ||||||
|  |             loader_instance->application_arguments = NULL; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         furi_hal_power_insomnia_exit(); |         furi_hal_power_insomnia_exit(); | ||||||
|         loader_unlock(instance); |         loader_unlock(instance); | ||||||
| 
 | 
 | ||||||
| @ -262,15 +273,12 @@ static uint32_t loader_back_to_primary_menu(void* context) { | |||||||
| static Loader* loader_alloc() { | static Loader* loader_alloc() { | ||||||
|     Loader* instance = furi_alloc(sizeof(Loader)); |     Loader* instance = furi_alloc(sizeof(Loader)); | ||||||
| 
 | 
 | ||||||
|     instance->thread = furi_thread_alloc(); |     instance->application_thread = furi_thread_alloc(); | ||||||
|     furi_thread_enable_heap_trace(instance->thread); |     furi_thread_enable_heap_trace(instance->application_thread); | ||||||
|     furi_thread_set_state_context(instance->thread, instance); |     furi_thread_set_state_context(instance->application_thread, instance); | ||||||
|     furi_thread_set_state_callback(instance->thread, loader_thread_state_callback); |     furi_thread_set_state_callback(instance->application_thread, loader_thread_state_callback); | ||||||
| 
 |  | ||||||
|     string_init(instance->args); |  | ||||||
| 
 | 
 | ||||||
|     instance->pubsub = furi_pubsub_alloc(); |     instance->pubsub = furi_pubsub_alloc(); | ||||||
|     instance->mutex = osMutexNew(NULL); |  | ||||||
| 
 | 
 | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     instance->cli = furi_record_open("cli"); |     instance->cli = furi_record_open("cli"); | ||||||
| @ -327,13 +335,9 @@ static void loader_free(Loader* instance) { | |||||||
|         furi_record_close("cli"); |         furi_record_close("cli"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     osMutexDelete(instance->mutex); |  | ||||||
| 
 |  | ||||||
|     furi_pubsub_free(instance->pubsub); |     furi_pubsub_free(instance->pubsub); | ||||||
| 
 | 
 | ||||||
|     string_clear(instance->args); |     furi_thread_free(instance->application_thread); | ||||||
| 
 |  | ||||||
|     furi_thread_free(instance->thread); |  | ||||||
| 
 | 
 | ||||||
|     menu_free(loader_instance->primary_menu); |     menu_free(loader_instance->primary_menu); | ||||||
|     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); |     view_dispatcher_remove_view(loader_instance->view_dispatcher, LoaderMenuViewPrimary); | ||||||
|  | |||||||
| @ -16,9 +16,11 @@ | |||||||
| 
 | 
 | ||||||
| struct Loader { | struct Loader { | ||||||
|     osThreadId_t loader_thread; |     osThreadId_t loader_thread; | ||||||
|     FuriThread* thread; | 
 | ||||||
|     const FlipperApplication* current_app; |     const FlipperApplication* application; | ||||||
|     string_t args; |     FuriThread* application_thread; | ||||||
|  |     char* application_arguments; | ||||||
|  | 
 | ||||||
|     Cli* cli; |     Cli* cli; | ||||||
|     Gui* gui; |     Gui* gui; | ||||||
| 
 | 
 | ||||||
| @ -29,8 +31,7 @@ struct Loader { | |||||||
|     Submenu* settings_menu; |     Submenu* settings_menu; | ||||||
| 
 | 
 | ||||||
|     size_t free_heap_size; |     size_t free_heap_size; | ||||||
|     osMutexId_t mutex; |     volatile uint8_t lock_count; | ||||||
|     volatile uint8_t lock_semaphore; |  | ||||||
| 
 | 
 | ||||||
|     FuriPubSub* pubsub; |     FuriPubSub* pubsub; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -1,28 +1,29 @@ | |||||||
| #include "power_cli.h" | #include "power_cli.h" | ||||||
| 
 | 
 | ||||||
| #include <power/power_service/power.h> |  | ||||||
| #include <cli/cli.h> |  | ||||||
| #include <furi_hal.h> | #include <furi_hal.h> | ||||||
|  | #include <cli/cli.h> | ||||||
|  | #include <lib/toolbox/args.h> | ||||||
|  | #include <power/power_service/power.h> | ||||||
| 
 | 
 | ||||||
| void power_cli_poweroff(Cli* cli, string_t args, void* context) { | void power_cli_off(Cli* cli, string_t args) { | ||||||
|     Power* power = furi_record_open("power"); |     Power* power = furi_record_open("power"); | ||||||
|     printf("It's now safe to disconnect USB from your flipper\r\n"); |     printf("It's now safe to disconnect USB from your flipper\r\n"); | ||||||
|     power_off(power); |     power_off(power); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_reboot(Cli* cli, string_t args, void* context) { | void power_cli_reboot(Cli* cli, string_t args) { | ||||||
|     power_reboot(PowerBootModeNormal); |     power_reboot(PowerBootModeNormal); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_dfu(Cli* cli, string_t args, void* context) { | void power_cli_reboot2dfu(Cli* cli, string_t args) { | ||||||
|     power_reboot(PowerBootModeDfu); |     power_reboot(PowerBootModeDfu); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_info(Cli* cli, string_t args, void* context) { | void power_cli_debug(Cli* cli, string_t args) { | ||||||
|     furi_hal_power_dump_state(); |     furi_hal_power_dump_state(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_otg(Cli* cli, string_t args, void* context) { | void power_cli_5v(Cli* cli, string_t args) { | ||||||
|     if(!string_cmp(args, "0")) { |     if(!string_cmp(args, "0")) { | ||||||
|         furi_hal_power_disable_otg(); |         furi_hal_power_disable_otg(); | ||||||
|     } else if(!string_cmp(args, "1")) { |     } else if(!string_cmp(args, "1")) { | ||||||
| @ -32,7 +33,7 @@ void power_cli_otg(Cli* cli, string_t args, void* context) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_cli_ext(Cli* cli, string_t args, void* context) { | void power_cli_3v3(Cli* cli, string_t args) { | ||||||
|     if(!string_cmp(args, "0")) { |     if(!string_cmp(args, "0")) { | ||||||
|         furi_hal_power_disable_external_3_3v(); |         furi_hal_power_disable_external_3_3v(); | ||||||
|     } else if(!string_cmp(args, "1")) { |     } else if(!string_cmp(args, "1")) { | ||||||
| @ -42,16 +43,70 @@ void power_cli_ext(Cli* cli, string_t args, void* context) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | static void power_cli_command_print_usage() { | ||||||
|  |     printf("Usage:\r\n"); | ||||||
|  |     printf("power <cmd> <args>\r\n"); | ||||||
|  |     printf("Cmd list:\r\n"); | ||||||
|  | 
 | ||||||
|  |     printf("\toff\t - shutdown power\r\n"); | ||||||
|  |     printf("\treboot\t - reboot\r\n"); | ||||||
|  |     printf("\treboot2dfu\t - reboot to dfu bootloader\r\n"); | ||||||
|  |     printf("\tdebug\t - show debug information\r\n"); | ||||||
|  |     printf("\t5v <0 or 1>\t - enable or disable 5v ext\r\n"); | ||||||
|  |     printf("\t3v3 <0 or 1>\t - enable or disable 3v3 ext\r\n"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void power_cli(Cli* cli, string_t args, void* context) { | ||||||
|  |     string_t cmd; | ||||||
|  |     string_init(cmd); | ||||||
|  | 
 | ||||||
|  |     do { | ||||||
|  |         if(!args_read_string_and_trim(args, cmd)) { | ||||||
|  |             power_cli_command_print_usage(); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "off") == 0) { | ||||||
|  |             power_cli_off(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "reboot") == 0) { | ||||||
|  |             power_cli_reboot(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "reboot2dfu") == 0) { | ||||||
|  |             power_cli_reboot2dfu(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "debug") == 0) { | ||||||
|  |             power_cli_debug(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "5v") == 0) { | ||||||
|  |             power_cli_5v(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if(string_cmp_str(cmd, "3v3") == 0) { | ||||||
|  |             power_cli_3v3(cli, args); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         power_cli_command_print_usage(); | ||||||
|  |     } while(false); | ||||||
|  | 
 | ||||||
|  |     string_clear(cmd); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void power_on_system_start() { | void power_on_system_start() { | ||||||
| #ifdef SRV_CLI | #ifdef SRV_CLI | ||||||
|     Cli* cli = furi_record_open("cli"); |     Cli* cli = furi_record_open("cli"); | ||||||
| 
 | 
 | ||||||
|     cli_add_command(cli, "poweroff", CliCommandFlagParallelSafe, power_cli_poweroff, NULL); |     cli_add_command(cli, "power", CliCommandFlagParallelSafe, power_cli, NULL); | ||||||
|     cli_add_command(cli, "reboot", CliCommandFlagParallelSafe, power_cli_reboot, NULL); |  | ||||||
|     cli_add_command(cli, "dfu", CliCommandFlagParallelSafe, power_cli_dfu, NULL); |  | ||||||
|     cli_add_command(cli, "power_info", CliCommandFlagParallelSafe, power_cli_info, NULL); |  | ||||||
|     cli_add_command(cli, "power_otg", CliCommandFlagParallelSafe, power_cli_otg, NULL); |  | ||||||
|     cli_add_command(cli, "power_ext", CliCommandFlagParallelSafe, power_cli_ext, NULL); |  | ||||||
| 
 | 
 | ||||||
|     furi_record_close("cli"); |     furi_record_close("cli"); | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ void power_off(Power* power) { | |||||||
|     view_dispatcher_send_to_front(power->view_dispatcher); |     view_dispatcher_send_to_front(power->view_dispatcher); | ||||||
|     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); |     view_dispatcher_switch_to_view(power->view_dispatcher, PowerViewPopup); | ||||||
|     osDelay(10); |     osDelay(10); | ||||||
|     furi_crash("Disconnect USB for safe shutdown"); |     furi_halt("Disconnect USB for safe shutdown"); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void power_reboot(PowerBootMode mode) { | void power_reboot(PowerBootMode mode) { | ||||||
|  | |||||||
| @ -69,24 +69,36 @@ void hal_gpio_init_ex( | |||||||
| 
 | 
 | ||||||
|     // Configure gpio with interrupts disabled
 |     // Configure gpio with interrupts disabled
 | ||||||
|     __disable_irq(); |     __disable_irq(); | ||||||
|  | 
 | ||||||
|     // Set gpio speed
 |     // Set gpio speed
 | ||||||
|     if(speed == GpioSpeedLow) { |     switch(speed) { | ||||||
|  |     case GpioSpeedLow: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||||
|     } else if(speed == GpioSpeedMedium) { |         break; | ||||||
|  |     case GpioSpeedMedium: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||||
|     } else if(speed == GpioSpeedHigh) { |         break; | ||||||
|  |     case GpioSpeedHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioSpeedVeryHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio pull mode
 |     // Set gpio pull mode
 | ||||||
|     if(pull == GpioPullNo) { |     switch(pull) { | ||||||
|  |     case GpioPullNo: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||||
|     } else if(pull == GpioPullUp) { |         break; | ||||||
|  |     case GpioPullUp: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioPullDown: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio mode
 |     // Set gpio mode
 | ||||||
|     if(mode >= GpioModeInterruptRise) { |     if(mode >= GpioModeInterruptRise) { | ||||||
|         // Set pin in interrupt mode
 |         // Set pin in interrupt mode
 | ||||||
| @ -100,40 +112,25 @@ void hal_gpio_init_ex( | |||||||
|             LL_EXTI_EnableIT_0_31(exti_line); |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         // Disable interrupt if it was set
 |         // Disable interrupts if set
 | ||||||
|         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && |         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||||
|            LL_EXTI_IsEnabledIT_0_31(exti_line)) { |            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||||
|             LL_EXTI_DisableIT_0_31(exti_line); |             LL_EXTI_DisableIT_0_31(exti_line); | ||||||
|             LL_EXTI_DisableRisingTrig_0_31(exti_line); |             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||||
|             LL_EXTI_DisableFallingTrig_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); |  | ||||||
|         } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); |  | ||||||
|         } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); |  | ||||||
|         } else if(mode == GpioModeAnalog) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|  |         // Prepare alternative part if any
 | ||||||
|         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { |         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|         // enable alternate mode
 |  | ||||||
|         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); |  | ||||||
| 
 |  | ||||||
|             // set alternate function
 |             // set alternate function
 | ||||||
|             if(hal_gpio_get_pin_num(gpio) < 8) { |             if(hal_gpio_get_pin_num(gpio) < 8) { | ||||||
|                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); |                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||||
| @ -142,6 +139,35 @@ void hal_gpio_init_ex( | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // Set not interrupt pin modes
 | ||||||
|  |         switch(mode) { | ||||||
|  |         case GpioModeInput: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAnalog: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     __enable_irq(); |     __enable_irq(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -69,24 +69,36 @@ void hal_gpio_init_ex( | |||||||
| 
 | 
 | ||||||
|     // Configure gpio with interrupts disabled
 |     // Configure gpio with interrupts disabled
 | ||||||
|     __disable_irq(); |     __disable_irq(); | ||||||
|  | 
 | ||||||
|     // Set gpio speed
 |     // Set gpio speed
 | ||||||
|     if(speed == GpioSpeedLow) { |     switch(speed) { | ||||||
|  |     case GpioSpeedLow: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||||
|     } else if(speed == GpioSpeedMedium) { |         break; | ||||||
|  |     case GpioSpeedMedium: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||||
|     } else if(speed == GpioSpeedHigh) { |         break; | ||||||
|  |     case GpioSpeedHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioSpeedVeryHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio pull mode
 |     // Set gpio pull mode
 | ||||||
|     if(pull == GpioPullNo) { |     switch(pull) { | ||||||
|  |     case GpioPullNo: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||||
|     } else if(pull == GpioPullUp) { |         break; | ||||||
|  |     case GpioPullUp: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioPullDown: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio mode
 |     // Set gpio mode
 | ||||||
|     if(mode >= GpioModeInterruptRise) { |     if(mode >= GpioModeInterruptRise) { | ||||||
|         // Set pin in interrupt mode
 |         // Set pin in interrupt mode
 | ||||||
| @ -100,40 +112,25 @@ void hal_gpio_init_ex( | |||||||
|             LL_EXTI_EnableIT_0_31(exti_line); |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         // Disable interrupt if it was set
 |         // Disable interrupts if set
 | ||||||
|         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && |         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||||
|            LL_EXTI_IsEnabledIT_0_31(exti_line)) { |            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||||
|             LL_EXTI_DisableIT_0_31(exti_line); |             LL_EXTI_DisableIT_0_31(exti_line); | ||||||
|             LL_EXTI_DisableRisingTrig_0_31(exti_line); |             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||||
|             LL_EXTI_DisableFallingTrig_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); |  | ||||||
|         } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); |  | ||||||
|         } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); |  | ||||||
|         } else if(mode == GpioModeAnalog) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|  |         // Prepare alternative part if any
 | ||||||
|         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { |         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|         // enable alternate mode
 |  | ||||||
|         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); |  | ||||||
| 
 |  | ||||||
|             // set alternate function
 |             // set alternate function
 | ||||||
|             if(hal_gpio_get_pin_num(gpio) < 8) { |             if(hal_gpio_get_pin_num(gpio) < 8) { | ||||||
|                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); |                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||||
| @ -142,6 +139,34 @@ void hal_gpio_init_ex( | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         // Set not interrupt pin modes
 | ||||||
|  |         switch(mode) { | ||||||
|  |         case GpioModeInput: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAnalog: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|     __enable_irq(); |     __enable_irq(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -4,7 +4,7 @@ | |||||||
| #include <furi_hal_rtc.h> | #include <furi_hal_rtc.h> | ||||||
| #include <stdio.h> | #include <stdio.h> | ||||||
| 
 | 
 | ||||||
| __attribute__((always_inline)) inline static void __furi_print_name() { | static void __furi_print_name() { | ||||||
|     if(task_is_isr_context()) { |     if(task_is_isr_context()) { | ||||||
|         furi_hal_console_puts("[ISR] "); |         furi_hal_console_puts("[ISR] "); | ||||||
|     } else { |     } else { | ||||||
| @ -19,9 +19,9 @@ __attribute__((always_inline)) inline static void __furi_print_name() { | |||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| __attribute__((always_inline)) inline static void __furi_halt() { | static void __furi_halt() { | ||||||
|     asm volatile("bkpt 0x00  \n" |     asm volatile("loop:      \n" | ||||||
|                  "loop:      \n" |                  "bkpt 0x00  \n" | ||||||
|                  "wfi        \n" |                  "wfi        \n" | ||||||
|                  "b loop     \n" |                  "b loop     \n" | ||||||
|                  : |                  : | ||||||
| @ -50,3 +50,18 @@ void furi_crash(const char* message) { | |||||||
|     NVIC_SystemReset(); |     NVIC_SystemReset(); | ||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | void furi_halt(const char* message) { | ||||||
|  |     __disable_irq(); | ||||||
|  | 
 | ||||||
|  |     if(message == NULL) { | ||||||
|  |         message = "System halt requested."; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_console_puts("\r\n\033[0;31m[HALT]"); | ||||||
|  |     __furi_print_name(); | ||||||
|  |     furi_hal_console_puts(message); | ||||||
|  |     furi_hal_console_puts("\r\nSystem halted. Bye-bye!\r\n"); | ||||||
|  |     furi_hal_console_puts("\033[0m\r\n"); | ||||||
|  |     __furi_halt(); | ||||||
|  | } | ||||||
|  | |||||||
| @ -17,6 +17,9 @@ extern "C" { | |||||||
| /** Crash system */ | /** Crash system */ | ||||||
| void furi_crash(const char* message); | void furi_crash(const char* message); | ||||||
| 
 | 
 | ||||||
|  | /** Halt system */ | ||||||
|  | void furi_halt(const char* message); | ||||||
|  | 
 | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
| @ -24,10 +24,10 @@ | |||||||
|  ****************************************************************************** |  ****************************************************************************** | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|   .syntax unified
 | .syntax unified
 | ||||||
| 	.cpu cortex-m4 | .cpu cortex-m4 | ||||||
| 	.fpu softvfp
 | .fpu softvfp
 | ||||||
| 	.thumb | .thumb | ||||||
| 
 | 
 | ||||||
| .global g_pfnVectors
 | .global g_pfnVectors
 | ||||||
| .global Default_Handler
 | .global Default_Handler
 | ||||||
| @ -86,13 +86,15 @@ LoopFillZerobss: | |||||||
|     bcc FillZerobss |     bcc FillZerobss | ||||||
|     bx lr |     bx lr | ||||||
| 
 | 
 | ||||||
|   .section .text.Reset_Handler | 
 | ||||||
|   .weak Reset_Handler
 | .section .text.Reset_Handler | ||||||
|   .type Reset_Handler, %function | .weak Reset_Handler
 | ||||||
|  | .type Reset_Handler, %function | ||||||
|  | 
 | ||||||
| Reset_Handler: | Reset_Handler: | ||||||
|     ldr   r0, =_estack |     ldr   r0, =_estack | ||||||
|     mov   sp, r0          /* set stack pointer */ |     mov   sp, r0          /* set stack pointer */ | ||||||
| /* Call the clock system intitialization function.*/ |     /* Call the clock system intitialization function.*/ | ||||||
|     bl  SystemInit |     bl  SystemInit | ||||||
| 
 | 
 | ||||||
| /* Copy the data segment initializers from flash to SRAM */ | /* Copy the data segment initializers from flash to SRAM */ | ||||||
|  | |||||||
| @ -68,25 +68,37 @@ void hal_gpio_init_ex( | |||||||
|     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); |     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); | ||||||
| 
 | 
 | ||||||
|     // Configure gpio with interrupts disabled
 |     // Configure gpio with interrupts disabled
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|     // Set gpio speed
 |     // Set gpio speed
 | ||||||
|     if(speed == GpioSpeedLow) { |     switch(speed) { | ||||||
|  |     case GpioSpeedLow: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||||
|     } else if(speed == GpioSpeedMedium) { |         break; | ||||||
|  |     case GpioSpeedMedium: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||||
|     } else if(speed == GpioSpeedHigh) { |         break; | ||||||
|  |     case GpioSpeedHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioSpeedVeryHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio pull mode
 |     // Set gpio pull mode
 | ||||||
|     if(pull == GpioPullNo) { |     switch(pull) { | ||||||
|  |     case GpioPullNo: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||||
|     } else if(pull == GpioPullUp) { |         break; | ||||||
|  |     case GpioPullUp: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioPullDown: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio mode
 |     // Set gpio mode
 | ||||||
|     if(mode >= GpioModeInterruptRise) { |     if(mode >= GpioModeInterruptRise) { | ||||||
|         // Set pin in interrupt mode
 |         // Set pin in interrupt mode
 | ||||||
| @ -100,40 +112,25 @@ void hal_gpio_init_ex( | |||||||
|             LL_EXTI_EnableIT_0_31(exti_line); |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         // Disable interrupt if it was set
 |         // Disable interrupts if set
 | ||||||
|         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && |         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||||
|            LL_EXTI_IsEnabledIT_0_31(exti_line)) { |            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||||
|             LL_EXTI_DisableIT_0_31(exti_line); |             LL_EXTI_DisableIT_0_31(exti_line); | ||||||
|             LL_EXTI_DisableRisingTrig_0_31(exti_line); |             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||||
|             LL_EXTI_DisableFallingTrig_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); |  | ||||||
|         } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); |  | ||||||
|         } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); |  | ||||||
|         } else if(mode == GpioModeAnalog) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|  |         // Prepare alternative part if any
 | ||||||
|         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { |         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|         // enable alternate mode
 |  | ||||||
|         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); |  | ||||||
| 
 |  | ||||||
|             // set alternate function
 |             // set alternate function
 | ||||||
|             if(hal_gpio_get_pin_num(gpio) < 8) { |             if(hal_gpio_get_pin_num(gpio) < 8) { | ||||||
|                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); |                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||||
| @ -142,51 +139,79 @@ void hal_gpio_init_ex( | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |         // Set not interrupt pin modes
 | ||||||
|  |         switch(mode) { | ||||||
|  |         case GpioModeInput: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAnalog: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
|     furi_assert(cb); |     furi_assert(cb); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     furi_assert(gpio_interrupt[pin_num].callback == NULL); |     furi_assert(gpio_interrupt[pin_num].callback == NULL); | ||||||
|     gpio_interrupt[pin_num].callback = cb; |     gpio_interrupt[pin_num].callback = cb; | ||||||
|     gpio_interrupt[pin_num].context = ctx; |     gpio_interrupt[pin_num].context = ctx; | ||||||
|     gpio_interrupt[pin_num].ready = true; |     gpio_interrupt[pin_num].ready = true; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_enable_int_callback(const GpioPin* gpio) { | void hal_gpio_enable_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     if(gpio_interrupt[pin_num].callback) { |     if(gpio_interrupt[pin_num].callback) { | ||||||
|         gpio_interrupt[pin_num].ready = true; |         gpio_interrupt[pin_num].ready = true; | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_disable_int_callback(const GpioPin* gpio) { | void hal_gpio_disable_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     gpio_interrupt[pin_num].ready = false; |     gpio_interrupt[pin_num].ready = false; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_remove_int_callback(const GpioPin* gpio) { | void hal_gpio_remove_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     gpio_interrupt[pin_num].callback = NULL; |     gpio_interrupt[pin_num].callback = NULL; | ||||||
|     gpio_interrupt[pin_num].context = NULL; |     gpio_interrupt[pin_num].context = NULL; | ||||||
|     gpio_interrupt[pin_num].ready = false; |     gpio_interrupt[pin_num].ready = false; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void hal_gpio_int_call(uint16_t pin_num) { | static void hal_gpio_int_call(uint16_t pin_num) { | ||||||
|  | |||||||
| @ -516,7 +516,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ | |||||||
|     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|     furi_assert(buffer->polarity != NULL); |     furi_assert(buffer->polarity != NULL); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
| @ -526,7 +526,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ | |||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | ||||||
| @ -536,7 +536,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | |||||||
|     furi_assert(buffer->data != NULL); |     furi_assert(buffer->data != NULL); | ||||||
| 
 | 
 | ||||||
|     /* non-circular mode requires disabled channel before setup */ |     /* non-circular mode requires disabled channel before setup */ | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
| @ -546,7 +546,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | |||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_irda_async_tx_free_resources(void) { | static void furi_hal_irda_async_tx_free_resources(void) { | ||||||
| @ -621,10 +621,10 @@ void furi_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) { | |||||||
|     hal_gpio_init_ex( |     hal_gpio_init_ex( | ||||||
|         &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); |         &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ |     LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ | ||||||
|     LL_TIM_EnableCounter(TIM1); |     LL_TIM_EnableCounter(TIM1); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_irda_async_tx_wait_termination(void) { | void furi_hal_irda_async_tx_wait_termination(void) { | ||||||
| @ -642,9 +642,9 @@ void furi_hal_irda_async_tx_stop(void) { | |||||||
|     furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); |     furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); | ||||||
|     furi_assert(furi_hal_irda_state < IrdaStateMAX); |     furi_assert(furi_hal_irda_state < IrdaStateMAX); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; |     if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_irda_async_tx_wait_termination(); |     furi_hal_irda_async_tx_wait_termination(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -115,11 +115,11 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Stop IRQ handling, no one should disturb us till we finish
 |     // Stop IRQ handling, no one should disturb us till we finish
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     // Confirm OS that sleep is still possible
 |     // Confirm OS that sleep is still possible
 | ||||||
|     if(eTaskConfirmSleepModeStatus() == eAbortSleep) { |     if(eTaskConfirmSleepModeStatus() == eAbortSleep) { | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -136,7 +136,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Reenable IRQ
 |     // Reenable IRQ
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { | void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <furi_hal_resources.h> | #include <furi_hal_resources.h> | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
| 
 | 
 | ||||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | ||||||
| static void* irq_ctx[2]; | static void* irq_ctx[2]; | ||||||
| @ -33,13 +34,12 @@ static void furi_hal_usart_init(uint32_t baud) { | |||||||
|     USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; |     USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; | ||||||
|     USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; |     USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; | ||||||
|     LL_USART_Init(USART1, &USART_InitStruct); |     LL_USART_Init(USART1, &USART_InitStruct); | ||||||
|     LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); |  | ||||||
|     LL_USART_EnableFIFO(USART1); |     LL_USART_EnableFIFO(USART1); | ||||||
|     LL_USART_ConfigAsyncMode(USART1); |     LL_USART_ConfigAsyncMode(USART1); | ||||||
| 
 | 
 | ||||||
|     LL_USART_Enable(USART1); |     LL_USART_Enable(USART1); | ||||||
| 
 | 
 | ||||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)) |     while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|     LL_USART_EnableIT_RXNE_RXFNE(USART1); |     LL_USART_EnableIT_RXNE_RXFNE(USART1); | ||||||
| @ -70,13 +70,11 @@ static void furi_hal_lpuart_init(uint32_t baud) { | |||||||
|     LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; |     LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; | ||||||
|     LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; |     LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; | ||||||
|     LL_LPUART_Init(LPUART1, &LPUART_InitStruct); |     LL_LPUART_Init(LPUART1, &LPUART_InitStruct); | ||||||
|     LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); |  | ||||||
|     LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); |  | ||||||
|     LL_LPUART_EnableFIFO(LPUART1); |     LL_LPUART_EnableFIFO(LPUART1); | ||||||
| 
 | 
 | ||||||
|     LL_LPUART_Enable(LPUART1); |     LL_LPUART_Enable(LPUART1); | ||||||
| 
 | 
 | ||||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) |     while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); |     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); | ||||||
|  | |||||||
| @ -14,7 +14,13 @@ FLASH_ADDRESS	= 0x08000000 | |||||||
| CFLAGS			+= -DNO_BOOTLOADER | CFLAGS			+= -DNO_BOOTLOADER | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | DEBUG_RTOS_THREADS ?= 1 | ||||||
|  | ifeq ($(DEBUG_RTOS_THREADS), 1) | ||||||
| OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" | OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" | ||||||
|  | else | ||||||
|  | OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) | BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) | ||||||
| MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -24,10 +24,10 @@ | |||||||
|  ****************************************************************************** |  ****************************************************************************** | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
|   .syntax unified
 | .syntax unified
 | ||||||
| 	.cpu cortex-m4 | .cpu cortex-m4 | ||||||
| 	.fpu softvfp
 | .fpu softvfp
 | ||||||
| 	.thumb | .thumb | ||||||
| 
 | 
 | ||||||
| .global g_pfnVectors
 | .global g_pfnVectors
 | ||||||
| .global Default_Handler
 | .global Default_Handler
 | ||||||
| @ -86,13 +86,15 @@ LoopFillZerobss: | |||||||
|     bcc FillZerobss |     bcc FillZerobss | ||||||
|     bx lr |     bx lr | ||||||
| 
 | 
 | ||||||
|   .section .text.Reset_Handler | 
 | ||||||
|   .weak Reset_Handler
 | .section .text.Reset_Handler | ||||||
|   .type Reset_Handler, %function | .weak Reset_Handler
 | ||||||
|  | .type Reset_Handler, %function | ||||||
|  | 
 | ||||||
| Reset_Handler: | Reset_Handler: | ||||||
|     ldr   r0, =_estack |     ldr   r0, =_estack | ||||||
|     mov   sp, r0          /* set stack pointer */ |     mov   sp, r0          /* set stack pointer */ | ||||||
| /* Call the clock system intitialization function.*/ |     /* Call the clock system intitialization function.*/ | ||||||
|     bl  SystemInit |     bl  SystemInit | ||||||
| 
 | 
 | ||||||
| /* Copy the data segment initializers from flash to SRAM */ | /* Copy the data segment initializers from flash to SRAM */ | ||||||
|  | |||||||
| @ -68,25 +68,37 @@ void hal_gpio_init_ex( | |||||||
|     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); |     uint32_t exti_line = GET_EXTI_LINE(gpio->pin); | ||||||
| 
 | 
 | ||||||
|     // Configure gpio with interrupts disabled
 |     // Configure gpio with interrupts disabled
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|  | 
 | ||||||
|     // Set gpio speed
 |     // Set gpio speed
 | ||||||
|     if(speed == GpioSpeedLow) { |     switch(speed) { | ||||||
|  |     case GpioSpeedLow: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW); | ||||||
|     } else if(speed == GpioSpeedMedium) { |         break; | ||||||
|  |     case GpioSpeedMedium: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM); | ||||||
|     } else if(speed == GpioSpeedHigh) { |         break; | ||||||
|  |     case GpioSpeedHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioSpeedVeryHigh: | ||||||
|         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); |         LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio pull mode
 |     // Set gpio pull mode
 | ||||||
|     if(pull == GpioPullNo) { |     switch(pull) { | ||||||
|  |     case GpioPullNo: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO); | ||||||
|     } else if(pull == GpioPullUp) { |         break; | ||||||
|  |     case GpioPullUp: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP); | ||||||
|     } else { |         break; | ||||||
|  |     case GpioPullDown: | ||||||
|         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); |         LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN); | ||||||
|  |         break; | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|     // Set gpio mode
 |     // Set gpio mode
 | ||||||
|     if(mode >= GpioModeInterruptRise) { |     if(mode >= GpioModeInterruptRise) { | ||||||
|         // Set pin in interrupt mode
 |         // Set pin in interrupt mode
 | ||||||
| @ -100,40 +112,25 @@ void hal_gpio_init_ex( | |||||||
|             LL_EXTI_EnableIT_0_31(exti_line); |             LL_EXTI_EnableIT_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventRise || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableRisingTrig_0_31(exti_line); |             LL_EXTI_EnableRisingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|         if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) { |         if(mode == GpioModeEventFall || mode == GpioModeEventRiseFall) { | ||||||
|             LL_EXTI_EnableEvent_0_31(exti_line); |             LL_EXTI_EnableEvent_0_31(exti_line); | ||||||
|             LL_EXTI_EnableFallingTrig_0_31(exti_line); |             LL_EXTI_EnableFallingTrig_0_31(exti_line); | ||||||
|         } |         } | ||||||
|     } else { |     } else { | ||||||
|         // Disable interrupt if it was set
 |         // Disable interrupts if set
 | ||||||
|         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && |         if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port && | ||||||
|            LL_EXTI_IsEnabledIT_0_31(exti_line)) { |            LL_EXTI_IsEnabledIT_0_31(exti_line)) { | ||||||
|             LL_EXTI_DisableIT_0_31(exti_line); |             LL_EXTI_DisableIT_0_31(exti_line); | ||||||
|             LL_EXTI_DisableRisingTrig_0_31(exti_line); |             LL_EXTI_DisableRisingTrig_0_31(exti_line); | ||||||
|             LL_EXTI_DisableFallingTrig_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); |  | ||||||
|         } else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); |  | ||||||
|         } else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); |  | ||||||
|             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); |  | ||||||
|         } else if(mode == GpioModeAnalog) { |  | ||||||
|             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 | 
 | ||||||
|  |         // Prepare alternative part if any
 | ||||||
|         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { |         if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) { | ||||||
|         // enable alternate mode
 |  | ||||||
|         LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); |  | ||||||
| 
 |  | ||||||
|             // set alternate function
 |             // set alternate function
 | ||||||
|             if(hal_gpio_get_pin_num(gpio) < 8) { |             if(hal_gpio_get_pin_num(gpio) < 8) { | ||||||
|                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); |                 LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn); | ||||||
| @ -142,51 +139,79 @@ void hal_gpio_init_ex( | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|     __enable_irq(); |         // Set not interrupt pin modes
 | ||||||
|  |         switch(mode) { | ||||||
|  |         case GpioModeInput: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionPushPull: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeOutputOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAltFunctionOpenDrain: | ||||||
|  |             LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN); | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE); | ||||||
|  |             break; | ||||||
|  |         case GpioModeAnalog: | ||||||
|  |             LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG); | ||||||
|  |             break; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
|     furi_assert(cb); |     furi_assert(cb); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     furi_assert(gpio_interrupt[pin_num].callback == NULL); |     furi_assert(gpio_interrupt[pin_num].callback == NULL); | ||||||
|     gpio_interrupt[pin_num].callback = cb; |     gpio_interrupt[pin_num].callback = cb; | ||||||
|     gpio_interrupt[pin_num].context = ctx; |     gpio_interrupt[pin_num].context = ctx; | ||||||
|     gpio_interrupt[pin_num].ready = true; |     gpio_interrupt[pin_num].ready = true; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_enable_int_callback(const GpioPin* gpio) { | void hal_gpio_enable_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     if(gpio_interrupt[pin_num].callback) { |     if(gpio_interrupt[pin_num].callback) { | ||||||
|         gpio_interrupt[pin_num].ready = true; |         gpio_interrupt[pin_num].ready = true; | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_disable_int_callback(const GpioPin* gpio) { | void hal_gpio_disable_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     gpio_interrupt[pin_num].ready = false; |     gpio_interrupt[pin_num].ready = false; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void hal_gpio_remove_int_callback(const GpioPin* gpio) { | void hal_gpio_remove_int_callback(const GpioPin* gpio) { | ||||||
|     furi_assert(gpio); |     furi_assert(gpio); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     uint8_t pin_num = hal_gpio_get_pin_num(gpio); |     uint8_t pin_num = hal_gpio_get_pin_num(gpio); | ||||||
|     gpio_interrupt[pin_num].callback = NULL; |     gpio_interrupt[pin_num].callback = NULL; | ||||||
|     gpio_interrupt[pin_num].context = NULL; |     gpio_interrupt[pin_num].context = NULL; | ||||||
|     gpio_interrupt[pin_num].ready = false; |     gpio_interrupt[pin_num].ready = false; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void hal_gpio_int_call(uint16_t pin_num) { | static void hal_gpio_int_call(uint16_t pin_num) { | ||||||
|  | |||||||
| @ -516,7 +516,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ | |||||||
|     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; |     IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num]; | ||||||
|     furi_assert(buffer->polarity != NULL); |     furi_assert(buffer->polarity != NULL); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
| @ -526,7 +526,7 @@ static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_ | |||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1); | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | ||||||
| @ -536,7 +536,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | |||||||
|     furi_assert(buffer->data != NULL); |     furi_assert(buffer->data != NULL); | ||||||
| 
 | 
 | ||||||
|     /* non-circular mode requires disabled channel before setup */ |     /* non-circular mode requires disabled channel before setup */ | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); |     bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
| @ -546,7 +546,7 @@ static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) { | |||||||
|     if(channel_enabled) { |     if(channel_enabled) { | ||||||
|         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); |         LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2); | ||||||
|     } |     } | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void furi_hal_irda_async_tx_free_resources(void) { | static void furi_hal_irda_async_tx_free_resources(void) { | ||||||
| @ -621,10 +621,10 @@ void furi_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) { | |||||||
|     hal_gpio_init_ex( |     hal_gpio_init_ex( | ||||||
|         &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); |         &gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ |     LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */ | ||||||
|     LL_TIM_EnableCounter(TIM1); |     LL_TIM_EnableCounter(TIM1); | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void furi_hal_irda_async_tx_wait_termination(void) { | void furi_hal_irda_async_tx_wait_termination(void) { | ||||||
| @ -642,9 +642,9 @@ void furi_hal_irda_async_tx_stop(void) { | |||||||
|     furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); |     furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx); | ||||||
|     furi_assert(furi_hal_irda_state < IrdaStateMAX); |     furi_assert(furi_hal_irda_state < IrdaStateMAX); | ||||||
| 
 | 
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
|     if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; |     if(furi_hal_irda_state == IrdaStateAsyncTx) furi_hal_irda_state = IrdaStateAsyncTxStopReq; | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| 
 | 
 | ||||||
|     furi_hal_irda_async_tx_wait_termination(); |     furi_hal_irda_async_tx_wait_termination(); | ||||||
| } | } | ||||||
|  | |||||||
| @ -115,11 +115,11 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Stop IRQ handling, no one should disturb us till we finish
 |     // Stop IRQ handling, no one should disturb us till we finish
 | ||||||
|     __disable_irq(); |     FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|     // Confirm OS that sleep is still possible
 |     // Confirm OS that sleep is still possible
 | ||||||
|     if(eTaskConfirmSleepModeStatus() == eAbortSleep) { |     if(eTaskConfirmSleepModeStatus() == eAbortSleep) { | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -136,7 +136,7 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Reenable IRQ
 |     // Reenable IRQ
 | ||||||
|     __enable_irq(); |     FURI_CRITICAL_EXIT(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { | void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) { | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ | |||||||
| #include <furi_hal_resources.h> | #include <furi_hal_resources.h> | ||||||
| 
 | 
 | ||||||
| #include <furi.h> | #include <furi.h> | ||||||
|  | #include <furi_hal_delay.h> | ||||||
| 
 | 
 | ||||||
| static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context); | ||||||
| static void* irq_ctx[2]; | static void* irq_ctx[2]; | ||||||
| @ -33,13 +34,12 @@ static void furi_hal_usart_init(uint32_t baud) { | |||||||
|     USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; |     USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; | ||||||
|     USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; |     USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; | ||||||
|     LL_USART_Init(USART1, &USART_InitStruct); |     LL_USART_Init(USART1, &USART_InitStruct); | ||||||
|     LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2); |  | ||||||
|     LL_USART_EnableFIFO(USART1); |     LL_USART_EnableFIFO(USART1); | ||||||
|     LL_USART_ConfigAsyncMode(USART1); |     LL_USART_ConfigAsyncMode(USART1); | ||||||
| 
 | 
 | ||||||
|     LL_USART_Enable(USART1); |     LL_USART_Enable(USART1); | ||||||
| 
 | 
 | ||||||
|     while(!LL_USART_IsActiveFlag_TEACK(USART1)) |     while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1)) | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|     LL_USART_EnableIT_RXNE_RXFNE(USART1); |     LL_USART_EnableIT_RXNE_RXFNE(USART1); | ||||||
| @ -70,13 +70,11 @@ static void furi_hal_lpuart_init(uint32_t baud) { | |||||||
|     LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; |     LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX; | ||||||
|     LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; |     LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE; | ||||||
|     LL_LPUART_Init(LPUART1, &LPUART_InitStruct); |     LL_LPUART_Init(LPUART1, &LPUART_InitStruct); | ||||||
|     LL_LPUART_SetTXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); |  | ||||||
|     LL_LPUART_SetRXFIFOThreshold(LPUART1, LL_LPUART_FIFOTHRESHOLD_1_8); |  | ||||||
|     LL_LPUART_EnableFIFO(LPUART1); |     LL_LPUART_EnableFIFO(LPUART1); | ||||||
| 
 | 
 | ||||||
|     LL_LPUART_Enable(LPUART1); |     LL_LPUART_Enable(LPUART1); | ||||||
| 
 | 
 | ||||||
|     while((!(LL_LPUART_IsActiveFlag_TEACK(LPUART1))) || (!(LL_LPUART_IsActiveFlag_REACK(LPUART1)))) |     while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1)) | ||||||
|         ; |         ; | ||||||
| 
 | 
 | ||||||
|     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); |     furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud); | ||||||
|  | |||||||
| @ -14,7 +14,13 @@ FLASH_ADDRESS	= 0x08000000 | |||||||
| CFLAGS			+= -DNO_BOOTLOADER | CFLAGS			+= -DNO_BOOTLOADER | ||||||
| endif | endif | ||||||
| 
 | 
 | ||||||
|  | DEBUG_RTOS_THREADS ?= 1 | ||||||
|  | ifeq ($(DEBUG_RTOS_THREADS), 1) | ||||||
| OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" | OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "stm32wbx.cpu configure -rtos auto" -c "init" | ||||||
|  | else | ||||||
|  | OPENOCD_OPTS	= -f interface/stlink.cfg -c "transport select hla_swd" -f ../debug/stm32wbx.cfg -c "init" | ||||||
|  | endif | ||||||
|  | 
 | ||||||
| BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) | BOOT_CFLAGS		= -DBOOT_ADDRESS=$(BOOT_ADDRESS) -DFW_ADDRESS=$(FW_ADDRESS) -DOS_OFFSET=$(OS_OFFSET) | ||||||
| MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | MCU_FLAGS		= -mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=hard | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -22,6 +22,8 @@ | |||||||
| 
 | 
 | ||||||
| #include <string.h> | #include <string.h> | ||||||
| 
 | 
 | ||||||
|  | #include <furi/common_defines.h> | ||||||
|  | 
 | ||||||
| #include "cmsis_os2.h"                  // ::CMSIS:RTOS2 | #include "cmsis_os2.h"                  // ::CMSIS:RTOS2 | ||||||
| #include "cmsis_compiler.h"             // Compiler agnostic definitions | #include "cmsis_compiler.h"             // Compiler agnostic definitions | ||||||
| #include "os_tick.h"                    // OS Tick API | #include "os_tick.h"                    // OS Tick API | ||||||
| @ -455,11 +457,10 @@ uint32_t osKernelGetTickFreq (void) { | |||||||
|   Get the RTOS kernel system timer count. |   Get the RTOS kernel system timer count. | ||||||
| */ | */ | ||||||
| uint32_t osKernelGetSysTimerCount (void) { | uint32_t osKernelGetSysTimerCount (void) { | ||||||
|   uint32_t irqmask = IS_IRQ_MASKED(); |  | ||||||
|   TickType_t ticks; |   TickType_t ticks; | ||||||
|   uint32_t val; |   uint32_t val; | ||||||
| 
 | 
 | ||||||
|   __disable_irq(); |   FURI_CRITICAL_ENTER(); | ||||||
| 
 | 
 | ||||||
|   ticks = xTaskGetTickCount(); |   ticks = xTaskGetTickCount(); | ||||||
|   val   = OS_Tick_GetCount(); |   val   = OS_Tick_GetCount(); | ||||||
| @ -471,9 +472,7 @@ uint32_t osKernelGetSysTimerCount (void) { | |||||||
|   } |   } | ||||||
|   val += ticks * OS_Tick_GetInterval(); |   val += ticks * OS_Tick_GetInterval(); | ||||||
| 
 | 
 | ||||||
|   if (irqmask == 0U) { |   FURI_CRITICAL_EXIT(); | ||||||
|     __enable_irq(); |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   /* Return system timer count */ |   /* Return system timer count */ | ||||||
|   return (val); |   return (val); | ||||||
|  | |||||||
| @ -1,96 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| 
 |  | ||||||
| #include <furi.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| 
 |  | ||||||
| class CyfralTiming { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t ZERO_HIGH = 50; |  | ||||||
|     constexpr static const uint8_t ZERO_LOW = 70; |  | ||||||
|     constexpr static const uint8_t ONE_HIGH = 100; |  | ||||||
|     constexpr static const uint8_t ONE_LOW = 70; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class CyfralEmulator { |  | ||||||
| private: |  | ||||||
|     void send_nibble(uint8_t nibble); |  | ||||||
|     void send_byte(uint8_t data); |  | ||||||
|     inline void send_bit(bool bit); |  | ||||||
|     const GpioPin* emulate_pin_record; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     CyfralEmulator(const GpioPin* emulate_pin); |  | ||||||
|     ~CyfralEmulator(); |  | ||||||
|     void send(uint8_t* data, uint8_t count = 1, uint8_t repeat = 1); |  | ||||||
|     void start(void); |  | ||||||
|     void stop(void); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| // 7 = 0 1 1 1
 |  | ||||||
| // B = 1 0 1 1
 |  | ||||||
| // D = 1 1 0 1
 |  | ||||||
| // E = 1 1 1 0
 |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::send_nibble(uint8_t nibble) { |  | ||||||
|     for(uint8_t i = 0; i < 4; i++) { |  | ||||||
|         bool bit = nibble & (0b1000 >> i); |  | ||||||
|         send_bit(bit); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::send_byte(uint8_t data) { |  | ||||||
|     for(uint8_t i = 0; i < 8; i++) { |  | ||||||
|         bool bit = data & (0b10000000 >> i); |  | ||||||
|         send_bit(bit); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::send_bit(bool bit) { |  | ||||||
|     if(!bit) { |  | ||||||
|         hal_gpio_write(&ibutton_gpio, false); |  | ||||||
|         delay_us(CyfralTiming::ZERO_LOW); |  | ||||||
|         hal_gpio_write(&ibutton_gpio, true); |  | ||||||
|         delay_us(CyfralTiming::ZERO_HIGH); |  | ||||||
|         hal_gpio_write(&ibutton_gpio, false); |  | ||||||
|         delay_us(CyfralTiming::ZERO_LOW); |  | ||||||
|     } else { |  | ||||||
|         hal_gpio_write(&ibutton_gpio, true); |  | ||||||
|         delay_us(CyfralTiming::ONE_HIGH); |  | ||||||
|         hal_gpio_write(&ibutton_gpio, false); |  | ||||||
|         delay_us(CyfralTiming::ONE_LOW); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralEmulator::CyfralEmulator(const GpioPin* emulate_pin) { |  | ||||||
|     emulate_pin_record = emulate_pin; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralEmulator::~CyfralEmulator() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::send(uint8_t* data, uint8_t count, uint8_t repeat) { |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     for(uint8_t i = 0; i < repeat; i++) { |  | ||||||
|         // start sequence
 |  | ||||||
|         send_nibble(0x01); |  | ||||||
| 
 |  | ||||||
|         // send data
 |  | ||||||
|         for(uint8_t i = 0; i < count; i++) { |  | ||||||
|             send_byte(data[i]); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::start(void) { |  | ||||||
|     hal_gpio_init(emulate_pin_record, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); |  | ||||||
|     hal_gpio_write(emulate_pin_record, false); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralEmulator::stop(void) { |  | ||||||
|     hal_gpio_init(emulate_pin_record, GpioModeAnalog, GpioPullNo, GpioSpeedLow); |  | ||||||
| } |  | ||||||
| @ -1,272 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <furi.h> |  | ||||||
| 
 |  | ||||||
| enum class CyfralReaderError : uint8_t { |  | ||||||
|     NO_ERROR = 0, |  | ||||||
|     UNABLE_TO_DETECT = 1, |  | ||||||
|     RAW_DATA_SIZE_ERROR = 2, |  | ||||||
|     UNKNOWN_NIBBLE_VALUE = 3, |  | ||||||
|     NO_START_NIBBLE = 4, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class CyfralReader { |  | ||||||
| private: |  | ||||||
|     ADC_HandleTypeDef adc_config; |  | ||||||
|     ADC_TypeDef* adc_instance; |  | ||||||
|     uint32_t adc_channel; |  | ||||||
| 
 |  | ||||||
|     void get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level); |  | ||||||
|     void capture_data(bool* data, uint16_t capture_size, uint32_t line_min, uint32_t line_max); |  | ||||||
|     bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); |  | ||||||
|     uint32_t search_array_in_array( |  | ||||||
|         const bool* haystack, |  | ||||||
|         const uint32_t haystack_size, |  | ||||||
|         const bool* needle, |  | ||||||
|         const uint32_t needle_size); |  | ||||||
| 
 |  | ||||||
|     // key is 9 nibbles
 |  | ||||||
|     static const uint16_t bits_in_nibble = 4; |  | ||||||
|     static const uint16_t key_length = 9; |  | ||||||
|     static const uint32_t capture_size = key_length * bits_in_nibble * 2; |  | ||||||
|     CyfralReaderError error; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     CyfralReader(ADC_TypeDef* adc, uint32_t Channel); |  | ||||||
|     ~CyfralReader(); |  | ||||||
|     void start(void); |  | ||||||
|     void stop(void); |  | ||||||
|     bool read(uint8_t* data, uint8_t count); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| void CyfralReader::get_line_minmax(uint16_t times, uint32_t* min_level, uint32_t* max_level) { |  | ||||||
|     uint32_t in = 0; |  | ||||||
|     uint32_t min = UINT_MAX; |  | ||||||
|     uint32_t max = 0; |  | ||||||
| 
 |  | ||||||
|     for(uint32_t i = 0; i < 256; i++) { |  | ||||||
|         HAL_ADC_Start(&adc_config); |  | ||||||
|         HAL_ADC_PollForConversion(&adc_config, 100); |  | ||||||
|         in = HAL_ADC_GetValue(&adc_config); |  | ||||||
|         if(in < min) min = in; |  | ||||||
|         if(in > max) max = in; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     *min_level = min; |  | ||||||
|     *max_level = max; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReader::capture_data( |  | ||||||
|     bool* data, |  | ||||||
|     uint16_t capture_size, |  | ||||||
|     uint32_t line_min, |  | ||||||
|     uint32_t line_max) { |  | ||||||
|     uint32_t input_value = 0; |  | ||||||
|     bool last_input_value = 0; |  | ||||||
| 
 |  | ||||||
|     uint32_t diff = line_max - line_min; |  | ||||||
|     uint32_t mid = line_min + diff / 2; |  | ||||||
| 
 |  | ||||||
|     uint32_t low_threshold = mid - (diff / 4); |  | ||||||
|     uint32_t high_threshold = mid - (diff / 4); |  | ||||||
| 
 |  | ||||||
|     uint16_t capture_position = 0; |  | ||||||
|     uint32_t instructions_per_us = (SystemCoreClock / 1000000.0f); |  | ||||||
|     uint32_t time_threshold = 75 * instructions_per_us; |  | ||||||
|     uint32_t capture_max_time = 140 * (capture_size * 2) * instructions_per_us; |  | ||||||
| 
 |  | ||||||
|     uint32_t start = DWT->CYCCNT; |  | ||||||
|     uint32_t end = DWT->CYCCNT; |  | ||||||
| 
 |  | ||||||
|     memset(data, 0, capture_size); |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
| 
 |  | ||||||
|     uint32_t capture_start = DWT->CYCCNT; |  | ||||||
|     while((capture_position < capture_size) && |  | ||||||
|           ((DWT->CYCCNT - capture_start) < capture_max_time)) { |  | ||||||
|         // read adc
 |  | ||||||
|         HAL_ADC_Start(&adc_config); |  | ||||||
|         HAL_ADC_PollForConversion(&adc_config, 100); |  | ||||||
|         input_value = HAL_ADC_GetValue(&adc_config); |  | ||||||
| 
 |  | ||||||
|         // low to high transition
 |  | ||||||
|         if((input_value > high_threshold) && last_input_value == 0) { |  | ||||||
|             last_input_value = 1; |  | ||||||
|             start = DWT->CYCCNT; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // high to low transition
 |  | ||||||
|         if((input_value < low_threshold) && last_input_value == 1) { |  | ||||||
|             last_input_value = 0; |  | ||||||
|             end = DWT->CYCCNT; |  | ||||||
| 
 |  | ||||||
|             // check transition time
 |  | ||||||
|             if(end - start < time_threshold) { |  | ||||||
|                 data[capture_position] = 1; |  | ||||||
|                 capture_position++; |  | ||||||
|             } else { |  | ||||||
|                 data[capture_position] = 0; |  | ||||||
|                 capture_position++; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     osKernelUnlock(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t CyfralReader::search_array_in_array( |  | ||||||
|     const bool* haystack, |  | ||||||
|     const uint32_t haystack_size, |  | ||||||
|     const bool* needle, |  | ||||||
|     const uint32_t needle_size) { |  | ||||||
|     uint32_t haystack_index = 0, needle_index = 0; |  | ||||||
| 
 |  | ||||||
|     while(haystack_index < haystack_size && needle_index < needle_size) { |  | ||||||
|         if(haystack[haystack_index] == needle[needle_index]) { |  | ||||||
|             haystack_index++; |  | ||||||
|             needle_index++; |  | ||||||
|             if(needle_index == needle_size) { |  | ||||||
|                 return (haystack_index - needle_size); |  | ||||||
|             }; |  | ||||||
|         } else { |  | ||||||
|             haystack_index = haystack_index - needle_index + 1; |  | ||||||
|             needle_index = 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return haystack_index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralReader::parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count) { |  | ||||||
|     const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; |  | ||||||
|     uint32_t start_position = |  | ||||||
|         search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); |  | ||||||
|     uint32_t end_position = 0; |  | ||||||
| 
 |  | ||||||
|     memset(data, 0, count); |  | ||||||
| 
 |  | ||||||
|     if(start_position < capture_size) { |  | ||||||
|         start_position = start_position + bits_in_nibble; |  | ||||||
|         end_position = start_position + count * 2 * bits_in_nibble; |  | ||||||
| 
 |  | ||||||
|         if(end_position >= capture_size) { |  | ||||||
|             error = CyfralReaderError::RAW_DATA_SIZE_ERROR; |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool first_nibble = true; |  | ||||||
|         uint8_t data_position = 0; |  | ||||||
|         uint8_t nibble_value = 0; |  | ||||||
| 
 |  | ||||||
|         while(data_position < count) { |  | ||||||
|             nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | |  | ||||||
|                            !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; |  | ||||||
| 
 |  | ||||||
|             switch(nibble_value) { |  | ||||||
|             case(0x7): |  | ||||||
|             case(0xB): |  | ||||||
|             case(0xD): |  | ||||||
|             case(0xE): |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 error = CyfralReaderError::UNKNOWN_NIBBLE_VALUE; |  | ||||||
|                 return false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(first_nibble) { |  | ||||||
|                 data[data_position] |= nibble_value << 4; |  | ||||||
|             } else { |  | ||||||
|                 data[data_position] |= nibble_value; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             first_nibble = !first_nibble; |  | ||||||
| 
 |  | ||||||
|             if(first_nibble) { |  | ||||||
|                 data_position++; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             start_position = start_position + bits_in_nibble; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         error = CyfralReaderError::NO_ERROR; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     error = CyfralReaderError::NO_START_NIBBLE; |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralReader::CyfralReader(ADC_TypeDef* adc, uint32_t channel) { |  | ||||||
|     adc_instance = adc; |  | ||||||
|     adc_channel = channel; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralReader::~CyfralReader() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReader::start(void) { |  | ||||||
|     ADC_ChannelConfTypeDef sConfig = {0}; |  | ||||||
| 
 |  | ||||||
|     // init ADC
 |  | ||||||
|     adc_config.Instance = adc_instance; |  | ||||||
|     adc_config.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1; |  | ||||||
|     adc_config.Init.Resolution = ADC_RESOLUTION_12B; |  | ||||||
|     adc_config.Init.DataAlign = ADC_DATAALIGN_RIGHT; |  | ||||||
|     adc_config.Init.ScanConvMode = ADC_SCAN_DISABLE; |  | ||||||
|     adc_config.Init.EOCSelection = ADC_EOC_SINGLE_CONV; |  | ||||||
|     adc_config.Init.LowPowerAutoWait = DISABLE; |  | ||||||
|     adc_config.Init.ContinuousConvMode = DISABLE; |  | ||||||
|     adc_config.Init.NbrOfConversion = 1; |  | ||||||
|     adc_config.Init.DiscontinuousConvMode = DISABLE; |  | ||||||
|     adc_config.Init.ExternalTrigConv = ADC_SOFTWARE_START; |  | ||||||
|     adc_config.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE; |  | ||||||
|     adc_config.Init.DMAContinuousRequests = DISABLE; |  | ||||||
|     adc_config.Init.Overrun = ADC_OVR_DATA_PRESERVED; |  | ||||||
|     adc_config.Init.OversamplingMode = DISABLE; |  | ||||||
|     if(HAL_ADC_Init(&adc_config) != HAL_OK) { |  | ||||||
|         Error_Handler(); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // init channel
 |  | ||||||
|     sConfig.Channel = adc_channel; |  | ||||||
|     sConfig.Rank = ADC_REGULAR_RANK_1; |  | ||||||
|     sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5; |  | ||||||
|     sConfig.SingleDiff = ADC_SINGLE_ENDED; |  | ||||||
|     sConfig.OffsetNumber = ADC_OFFSET_NONE; |  | ||||||
|     sConfig.Offset = 0; |  | ||||||
|     if(HAL_ADC_ConfigChannel(&adc_config, &sConfig) != HAL_OK) { |  | ||||||
|         Error_Handler(); |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReader::stop(void) { |  | ||||||
|     HAL_ADC_DeInit(&adc_config); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralReader::read(uint8_t* data, uint8_t count) { |  | ||||||
|     uint32_t line_level_min, line_level_max; |  | ||||||
|     bool raw_data[capture_size]; |  | ||||||
|     bool result = false; |  | ||||||
|     error = CyfralReaderError::NO_ERROR; |  | ||||||
| 
 |  | ||||||
|     // calibrate
 |  | ||||||
|     get_line_minmax(256, &line_level_min, &line_level_max); |  | ||||||
| 
 |  | ||||||
|     // TODO think about other detection method
 |  | ||||||
|     // key not on line
 |  | ||||||
|     if(line_level_max > 2000) { |  | ||||||
|         error = CyfralReaderError::UNABLE_TO_DETECT; |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // capturing raw data consisting of bits
 |  | ||||||
|     capture_data(raw_data, capture_size, line_level_min, line_level_max); |  | ||||||
| 
 |  | ||||||
|     // parse captured data
 |  | ||||||
|     if(parse_data(raw_data, capture_size, data, count)) { |  | ||||||
|         result = true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,283 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include <furi.h> |  | ||||||
| #include "callback-connector.h" |  | ||||||
| #include <atomic> |  | ||||||
| 
 |  | ||||||
| enum class CyfralReaderCompError : uint8_t { |  | ||||||
|     NO_ERROR = 0, |  | ||||||
|     UNABLE_TO_DETECT = 1, |  | ||||||
|     RAW_DATA_SIZE_ERROR = 2, |  | ||||||
|     UNKNOWN_NIBBLE_VALUE = 3, |  | ||||||
|     NO_START_NIBBLE = 4, |  | ||||||
|     NOT_ENOUGH_DATA = 5, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| extern COMP_HandleTypeDef hcomp1; |  | ||||||
| 
 |  | ||||||
| typedef struct { |  | ||||||
|     bool value; |  | ||||||
|     uint32_t dwt_value; |  | ||||||
| } CompEvent; |  | ||||||
| 
 |  | ||||||
| class CyfralReaderComp { |  | ||||||
| private: |  | ||||||
|     bool capture_data(bool* data, uint16_t capture_size); |  | ||||||
|     bool parse_data(bool* raw_data, uint16_t capture_size, uint8_t* data, uint8_t count); |  | ||||||
|     uint32_t search_array_in_array( |  | ||||||
|         const bool* haystack, |  | ||||||
|         const uint32_t haystack_size, |  | ||||||
|         const bool* needle, |  | ||||||
|         const uint32_t needle_size); |  | ||||||
| 
 |  | ||||||
|     // key is 9 nibbles
 |  | ||||||
|     static const uint16_t bits_in_nibble = 4; |  | ||||||
|     static const uint16_t key_length = 9; |  | ||||||
|     static const uint32_t capture_size = key_length * bits_in_nibble * 2; |  | ||||||
|     CyfralReaderCompError error; |  | ||||||
|     const GpioPin* pin_record; |  | ||||||
| 
 |  | ||||||
|     std::atomic<bool> ready_to_process; |  | ||||||
|     void comparator_trigger_callback(void* hcomp, void* comp_ctx); |  | ||||||
|     osMessageQueueId_t comp_event_queue; |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     CyfralReaderComp(const GpioPin* emulate_pin); |  | ||||||
|     ~CyfralReaderComp(); |  | ||||||
|     void start(void); |  | ||||||
|     void stop(void); |  | ||||||
|     bool read(uint8_t* data, uint8_t count); |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| bool CyfralReaderComp::capture_data(bool* data, uint16_t capture_size) { |  | ||||||
|     uint32_t prev_timing = 0; |  | ||||||
|     uint16_t data_index = 0; |  | ||||||
|     CompEvent event_0, event_1; |  | ||||||
|     osStatus_t status; |  | ||||||
| 
 |  | ||||||
|     // read first event to get initial timing
 |  | ||||||
|     status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); |  | ||||||
| 
 |  | ||||||
|     if(status != osOK) { |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     prev_timing = event_0.dwt_value; |  | ||||||
| 
 |  | ||||||
|     // read second event until we get 0
 |  | ||||||
|     while(1) { |  | ||||||
|         status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); |  | ||||||
|         if(status != osOK) { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|         prev_timing = event_0.dwt_value; |  | ||||||
|         if(event_0.value == 0) break; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     while(1) { |  | ||||||
|         // if event "zero" correct
 |  | ||||||
|         if(status == osOK && event_0.value == 0) { |  | ||||||
|             // get timing
 |  | ||||||
|             event_0.dwt_value -= prev_timing; |  | ||||||
|             prev_timing += event_0.dwt_value; |  | ||||||
| 
 |  | ||||||
|             // read next event
 |  | ||||||
|             status = osMessageQueueGet(comp_event_queue, &event_1, NULL, 0); |  | ||||||
| 
 |  | ||||||
|             // if event "one" correct
 |  | ||||||
|             if(status == osOK && event_1.value == 1) { |  | ||||||
|                 // get timing
 |  | ||||||
|                 event_1.dwt_value -= prev_timing; |  | ||||||
|                 prev_timing += event_1.dwt_value; |  | ||||||
| 
 |  | ||||||
|                 // calculate percentage of event "one" to full timing
 |  | ||||||
|                 uint32_t full_timing = event_0.dwt_value + event_1.dwt_value; |  | ||||||
|                 uint32_t percentage_1 = 1000000 / full_timing * event_1.dwt_value; |  | ||||||
| 
 |  | ||||||
|                 // write captured data
 |  | ||||||
|                 data[data_index] = percentage_1 > 500000 ? 0 : 1; |  | ||||||
|                 data_index++; |  | ||||||
|                 if(data_index >= capture_size) return true; |  | ||||||
| 
 |  | ||||||
|                 status = osMessageQueueGet(comp_event_queue, &event_0, NULL, 0); |  | ||||||
|             } else { |  | ||||||
|                 return false; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     osMessageQueueReset(comp_event_queue); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| uint32_t CyfralReaderComp::search_array_in_array( |  | ||||||
|     const bool* haystack, |  | ||||||
|     const uint32_t haystack_size, |  | ||||||
|     const bool* needle, |  | ||||||
|     const uint32_t needle_size) { |  | ||||||
|     uint32_t haystack_index = 0, needle_index = 0; |  | ||||||
| 
 |  | ||||||
|     while(haystack_index < haystack_size && needle_index < needle_size) { |  | ||||||
|         if(haystack[haystack_index] == needle[needle_index]) { |  | ||||||
|             haystack_index++; |  | ||||||
|             needle_index++; |  | ||||||
|             if(needle_index == needle_size) { |  | ||||||
|                 return (haystack_index - needle_size); |  | ||||||
|             }; |  | ||||||
|         } else { |  | ||||||
|             haystack_index = haystack_index - needle_index + 1; |  | ||||||
|             needle_index = 0; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return haystack_index; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReaderComp::comparator_trigger_callback(void* hcomp, void* comp_ctx) { |  | ||||||
|     CyfralReaderComp* _this = static_cast<CyfralReaderComp*>(comp_ctx); |  | ||||||
|     COMP_HandleTypeDef* _hcomp = static_cast<COMP_HandleTypeDef*>(hcomp); |  | ||||||
| 
 |  | ||||||
|     // check that hw is comparator 1
 |  | ||||||
|     if(_hcomp != &hcomp1) return; |  | ||||||
| 
 |  | ||||||
|     // if queue if not full
 |  | ||||||
|     if(_this->ready_to_process == false) { |  | ||||||
|         // send event to queue
 |  | ||||||
|         CompEvent event; |  | ||||||
|         // TOOD F4 and F5 differ
 |  | ||||||
|         event.value = (HAL_COMP_GetOutputLevel(_hcomp) == COMP_OUTPUT_LEVEL_LOW); |  | ||||||
|         event.dwt_value = DWT->CYCCNT; |  | ||||||
|         osStatus_t status = osMessageQueuePut(_this->comp_event_queue, &event, 0, 0); |  | ||||||
| 
 |  | ||||||
|         // queue is full, so we need to process data
 |  | ||||||
|         if(status != osOK) { |  | ||||||
|             _this->ready_to_process = true; |  | ||||||
|         }; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralReaderComp::parse_data( |  | ||||||
|     bool* raw_data, |  | ||||||
|     uint16_t capture_size, |  | ||||||
|     uint8_t* data, |  | ||||||
|     uint8_t count) { |  | ||||||
|     const bool start_nibble[bits_in_nibble] = {1, 1, 1, 0}; |  | ||||||
|     uint32_t start_position = |  | ||||||
|         search_array_in_array(raw_data, capture_size, start_nibble, bits_in_nibble); |  | ||||||
|     uint32_t end_position = 0; |  | ||||||
| 
 |  | ||||||
|     memset(data, 0, count); |  | ||||||
| 
 |  | ||||||
|     if(start_position < capture_size) { |  | ||||||
|         start_position = start_position + bits_in_nibble; |  | ||||||
|         end_position = start_position + count * 2 * bits_in_nibble; |  | ||||||
| 
 |  | ||||||
|         if(end_position >= capture_size) { |  | ||||||
|             error = CyfralReaderCompError::RAW_DATA_SIZE_ERROR; |  | ||||||
|             return false; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         bool first_nibble = true; |  | ||||||
|         uint8_t data_position = 0; |  | ||||||
|         uint8_t nibble_value = 0; |  | ||||||
| 
 |  | ||||||
|         while(data_position < count) { |  | ||||||
|             nibble_value = !raw_data[start_position] << 3 | !raw_data[start_position + 1] << 2 | |  | ||||||
|                            !raw_data[start_position + 2] << 1 | !raw_data[start_position + 3]; |  | ||||||
| 
 |  | ||||||
|             switch(nibble_value) { |  | ||||||
|             case(0x7): |  | ||||||
|             case(0xB): |  | ||||||
|             case(0xD): |  | ||||||
|             case(0xE): |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 error = CyfralReaderCompError::UNKNOWN_NIBBLE_VALUE; |  | ||||||
|                 return false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if(first_nibble) { |  | ||||||
|                 data[data_position] |= nibble_value << 4; |  | ||||||
|             } else { |  | ||||||
|                 data[data_position] |= nibble_value; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             first_nibble = !first_nibble; |  | ||||||
| 
 |  | ||||||
|             if(first_nibble) { |  | ||||||
|                 data_position++; |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             start_position = start_position + bits_in_nibble; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         error = CyfralReaderCompError::NO_ERROR; |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     error = CyfralReaderCompError::NO_START_NIBBLE; |  | ||||||
|     return false; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralReaderComp::CyfralReaderComp(const GpioPin* gpio_pin) { |  | ||||||
|     pin_record = gpio_pin; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| CyfralReaderComp::~CyfralReaderComp() { |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReaderComp::start(void) { |  | ||||||
|     // pulldown lf-rfid pins to prevent interference
 |  | ||||||
|     // TODO open record
 |  | ||||||
|     GpioPin rfid_pull_pin = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin}; |  | ||||||
|     hal_gpio_init((GpioPin*)&rfid_pull_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); |  | ||||||
|     hal_gpio_write((GpioPin*)&rfid_pull_pin, false); |  | ||||||
| 
 |  | ||||||
|     // TODO open record
 |  | ||||||
|     GpioPin rfid_out_pin = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin}; |  | ||||||
|     hal_gpio_init((GpioPin*)&rfid_out_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); |  | ||||||
|     hal_gpio_write((GpioPin*)&rfid_out_pin, false); |  | ||||||
| 
 |  | ||||||
|     // connect comparator callback
 |  | ||||||
|     void* comp_ctx = this; |  | ||||||
|     comp_event_queue = osMessageQueueNew(capture_size * 2 + 2, sizeof(CompEvent), NULL); |  | ||||||
|     ready_to_process = false; |  | ||||||
| 
 |  | ||||||
|     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); |  | ||||||
|     api_interrupt_add(cmp_cb, InterruptTypeComparatorTrigger, comp_ctx); |  | ||||||
| 
 |  | ||||||
|     // start comaparator
 |  | ||||||
|     HAL_COMP_Start(&hcomp1); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void CyfralReaderComp::stop(void) { |  | ||||||
|     // stop comaparator
 |  | ||||||
|     HAL_COMP_Stop(&hcomp1); |  | ||||||
| 
 |  | ||||||
|     // disconnect comparator callback
 |  | ||||||
|     auto cmp_cb = cbc::obtain_connector(this, &CyfralReaderComp::comparator_trigger_callback); |  | ||||||
|     api_interrupt_remove(cmp_cb, InterruptTypeComparatorTrigger); |  | ||||||
|     osMessageQueueDelete(comp_event_queue); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool CyfralReaderComp::read(uint8_t* data, uint8_t count) { |  | ||||||
|     bool raw_data[capture_size]; |  | ||||||
|     bool result = false; |  | ||||||
|     error = CyfralReaderCompError::NO_ERROR; |  | ||||||
| 
 |  | ||||||
|     if(ready_to_process == false) { |  | ||||||
|         error = CyfralReaderCompError::NOT_ENOUGH_DATA; |  | ||||||
|     } else { |  | ||||||
|         memset(raw_data, 0, sizeof(bool) * capture_size); |  | ||||||
|         if(capture_data(raw_data, capture_size)) { |  | ||||||
|             if(parse_data(raw_data, capture_size, data, count)) { |  | ||||||
|                 result = true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         ready_to_process = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| @ -1,320 +0,0 @@ | |||||||
| #include "blanks_writer.h" |  | ||||||
| 
 |  | ||||||
| class RW1990_1 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xD1; |  | ||||||
|     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0xB5; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class RW1990_2 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0x1D; |  | ||||||
|     constexpr static const uint8_t CMD_READ_RECORD_FLAG = 0x1E; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xD5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TM2004 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_READ_STATUS = 0xAA; |  | ||||||
|     constexpr static const uint8_t CMD_READ_MEMORY = 0xF0; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0x3C; |  | ||||||
|     constexpr static const uint8_t CMD_FINALIZATION = 0x35; |  | ||||||
| 
 |  | ||||||
|     constexpr static const uint8_t ANSWER_READ_MEMORY = 0xF5; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class TM01 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_RECORD_FLAG = 0xC1; |  | ||||||
|     constexpr static const uint8_t CMD_WRITE_ROM = 0xC5; |  | ||||||
|     constexpr static const uint8_t CMD_SWITCH_TO_CYFRAL = 0xCA; |  | ||||||
|     constexpr static const uint8_t CMD_SWITCH_TO_METAKOM = 0xCB; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| class DS1990 { |  | ||||||
| public: |  | ||||||
|     constexpr static const uint8_t CMD_READ_ROM = 0x33; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| #include <stdio.h> |  | ||||||
| #include <stdarg.h> |  | ||||||
| #include <string.h> |  | ||||||
| #include <furi_hal.h> |  | ||||||
| 
 |  | ||||||
| void BlanksWriter::onewire_release(void) { |  | ||||||
|     hal_gpio_write(gpio, true); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BlanksWriter::onewire_write_one_bit(bool value, uint32_t delay = 10000) { |  | ||||||
|     onewire->write_bit(value); |  | ||||||
|     delay_us(delay); |  | ||||||
|     onewire_release(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| BlanksWriter::BlanksWriter(const GpioPin* one_wire_gpio) { |  | ||||||
|     gpio = one_wire_gpio; |  | ||||||
|     onewire = new OneWireMaster(gpio); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| BlanksWriter::~BlanksWriter() { |  | ||||||
|     free(onewire); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| WriterResult BlanksWriter::write(KeyType type, const uint8_t* key, uint8_t key_length) { |  | ||||||
|     uint8_t write_result = -1; |  | ||||||
|     WriterResult result = WR_ERROR; |  | ||||||
| 
 |  | ||||||
|     bool same_key = false; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     bool presence = onewire->reset(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(presence) { |  | ||||||
|         switch(type) { |  | ||||||
|         case KeyType::KEY_DS1990: |  | ||||||
|             same_key = compare_key_ds1990(key, key_length); |  | ||||||
| 
 |  | ||||||
|             if(!same_key) { |  | ||||||
|                 // currently we can write:
 |  | ||||||
|                 // RW1990, TM08v2, TM08vi-2 by write_1990_1()
 |  | ||||||
|                 // RW2004, RW2004 with EEPROM by write_TM2004();
 |  | ||||||
| 
 |  | ||||||
|                 if(write_result != 1) { |  | ||||||
|                     write_result = write_1990_1(key, key_length); |  | ||||||
|                 } |  | ||||||
|                 if(write_result != 1) { |  | ||||||
|                     write_result = write_1990_2(key, key_length); |  | ||||||
|                 } |  | ||||||
|                 if(write_result != 1) { |  | ||||||
|                     write_result = write_TM2004(key, key_length); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 if(write_result == 1) { |  | ||||||
|                     result = WR_OK; |  | ||||||
|                 } else if(write_result == 0) { |  | ||||||
|                     result = WR_ERROR; |  | ||||||
|                 } |  | ||||||
|             } else { |  | ||||||
|                 write_result = 0; |  | ||||||
|                 result = WR_SAME_KEY; |  | ||||||
|             } |  | ||||||
|             break; |  | ||||||
| 
 |  | ||||||
|         default: |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BlanksWriter::write_TM2004(const uint8_t* key, uint8_t key_length) { |  | ||||||
|     uint8_t answer; |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     // write rom, addr is 0x0000
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(TM2004::CMD_WRITE_ROM); |  | ||||||
|     onewire->write(0x00); |  | ||||||
|     onewire->write(0x00); |  | ||||||
| 
 |  | ||||||
|     // write key
 |  | ||||||
|     for(uint8_t i = 0; i < key_length; i++) { |  | ||||||
|         // write key byte
 |  | ||||||
|         onewire->write(key[i]); |  | ||||||
|         answer = onewire->read(); |  | ||||||
|         // TODO: check answer CRC
 |  | ||||||
| 
 |  | ||||||
|         // pulse indicating that data is correct
 |  | ||||||
|         delay_us(600); |  | ||||||
|         onewire_write_one_bit(1, 50000); |  | ||||||
| 
 |  | ||||||
|         // read writed key byte
 |  | ||||||
|         answer = onewire->read(); |  | ||||||
| 
 |  | ||||||
|         // check that writed and readed are same
 |  | ||||||
|         if(key[i] != answer) { |  | ||||||
|             result = false; |  | ||||||
|             break; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     onewire->reset(); |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BlanksWriter::write_1990_1(const uint8_t* key, uint8_t key_length) { |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     // unlock
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     delay_us(10); |  | ||||||
|     onewire_write_one_bit(0, 5000); |  | ||||||
| 
 |  | ||||||
|     // write key
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(RW1990_1::CMD_WRITE_ROM); |  | ||||||
|     for(uint8_t i = 0; i < key_length; i++) { |  | ||||||
|         // inverted key for RW1990.1
 |  | ||||||
|         write_byte_ds1990(~key[i]); |  | ||||||
|         delay_us(30000); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // lock
 |  | ||||||
|     onewire->write(RW1990_1::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(1); |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(!compare_key_ds1990(key, key_length)) { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BlanksWriter::write_1990_2(const uint8_t* key, uint8_t key_length) { |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     // unlock
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     delay_us(10); |  | ||||||
|     onewire_write_one_bit(1, 5000); |  | ||||||
| 
 |  | ||||||
|     // write key
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(RW1990_2::CMD_WRITE_ROM); |  | ||||||
|     for(uint8_t i = 0; i < key_length; i++) { |  | ||||||
|         write_byte_ds1990(key[i]); |  | ||||||
|         delay_us(30000); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // lock
 |  | ||||||
|     onewire->write(RW1990_2::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(0); |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(!compare_key_ds1990(key, key_length)) { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // TODO: untested
 |  | ||||||
| bool BlanksWriter::write_TM01(KeyType type, const uint8_t* key, uint8_t key_length) { |  | ||||||
|     bool result = true; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     // unlock
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(TM01::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(1, 10000); |  | ||||||
| 
 |  | ||||||
|     // write key
 |  | ||||||
|     onewire->reset(); |  | ||||||
|     onewire->write(TM01::CMD_WRITE_ROM); |  | ||||||
| 
 |  | ||||||
|     // TODO: key types
 |  | ||||||
|     //if(type == KEY_METAKOM || type == KEY_CYFRAL) {
 |  | ||||||
|     //} else {
 |  | ||||||
|     for(uint8_t i = 0; i < key_length; i++) { |  | ||||||
|         write_byte_ds1990(key[i]); |  | ||||||
|         delay_us(10000); |  | ||||||
|     } |  | ||||||
|     //}
 |  | ||||||
| 
 |  | ||||||
|     // lock
 |  | ||||||
|     onewire->write(TM01::CMD_WRITE_RECORD_FLAG); |  | ||||||
|     onewire_write_one_bit(0, 10000); |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(!compare_key_ds1990(key, key_length)) { |  | ||||||
|         result = false; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     __disable_irq(); |  | ||||||
| 
 |  | ||||||
|     if(type == KEY_METAKOM || type == KEY_CYFRAL) { |  | ||||||
|         onewire->reset(); |  | ||||||
|         if(type == KEY_CYFRAL) |  | ||||||
|             onewire->write(TM01::CMD_SWITCH_TO_CYFRAL); |  | ||||||
|         else |  | ||||||
|             onewire->write(TM01::CMD_SWITCH_TO_METAKOM); |  | ||||||
|         onewire_write_one_bit(1); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     __enable_irq(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BlanksWriter::write_byte_ds1990(uint8_t data) { |  | ||||||
|     for(uint8_t n_bit = 0; n_bit < 8; n_bit++) { |  | ||||||
|         onewire->write_bit(data & 1); |  | ||||||
|         onewire_release(); |  | ||||||
|         delay_us(5000); |  | ||||||
|         data = data >> 1; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool BlanksWriter::compare_key_ds1990(const uint8_t* key, uint8_t key_length) { |  | ||||||
|     uint8_t buff[key_length]; |  | ||||||
|     bool result = false; |  | ||||||
| 
 |  | ||||||
|     osKernelLock(); |  | ||||||
|     bool presence = onewire->reset(); |  | ||||||
|     osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|     if(presence) { |  | ||||||
|         osKernelLock(); |  | ||||||
|         __disable_irq(); |  | ||||||
|         onewire->write(DS1990::CMD_READ_ROM); |  | ||||||
|         onewire->read_bytes(buff, key_length); |  | ||||||
|         __enable_irq(); |  | ||||||
|         osKernelUnlock(); |  | ||||||
| 
 |  | ||||||
|         result = true; |  | ||||||
|         for(uint8_t i = 0; i < 8; i++) { |  | ||||||
|             if(key[i] != buff[i]) { |  | ||||||
|                 result = false; |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     return result; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BlanksWriter::start() { |  | ||||||
|     onewire->start(); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| void BlanksWriter::stop() { |  | ||||||
|     onewire->stop(); |  | ||||||
| } |  | ||||||
| @ -1,40 +0,0 @@ | |||||||
| #pragma once |  | ||||||
| #include "one_wire_master.h" |  | ||||||
| #include "maxim_crc.h" |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     KEY_DS1990, /**< DS1990 */ |  | ||||||
|     KEY_CYFRAL, /**< CYFRAL*/ |  | ||||||
|     KEY_METAKOM, /**< METAKOM */ |  | ||||||
| } KeyType; |  | ||||||
| 
 |  | ||||||
| typedef enum { |  | ||||||
|     WR_OK, |  | ||||||
|     WR_SAME_KEY, |  | ||||||
|     WR_ERROR, |  | ||||||
| } WriterResult; |  | ||||||
| 
 |  | ||||||
| class BlanksWriter { |  | ||||||
| private: |  | ||||||
|     const GpioPin* gpio; |  | ||||||
|     OneWireMaster* onewire; |  | ||||||
| 
 |  | ||||||
|     void onewire_release(void); |  | ||||||
|     void onewire_write_one_bit(bool value, uint32_t delay); |  | ||||||
| 
 |  | ||||||
|     bool write_TM2004(const uint8_t* key, uint8_t key_length); |  | ||||||
|     bool write_1990_1(const uint8_t* key, uint8_t key_length); |  | ||||||
|     bool write_1990_2(const uint8_t* key, uint8_t key_length); |  | ||||||
|     bool write_TM01(KeyType type, const uint8_t* key, uint8_t key_length); |  | ||||||
| 
 |  | ||||||
|     void write_byte_ds1990(uint8_t data); |  | ||||||
|     bool compare_key_ds1990(const uint8_t* key, uint8_t key_length); |  | ||||||
| 
 |  | ||||||
| public: |  | ||||||
|     BlanksWriter(const GpioPin* one_wire_gpio); |  | ||||||
|     ~BlanksWriter(); |  | ||||||
| 
 |  | ||||||
|     WriterResult write(KeyType type, const uint8_t* key, uint8_t key_length); |  | ||||||
|     void start(); |  | ||||||
|     void stop(); |  | ||||||
| }; |  | ||||||
| @ -258,7 +258,7 @@ bool OneWireSlave::bus_start(void) { | |||||||
|     if(device == nullptr) { |     if(device == nullptr) { | ||||||
|         result = false; |         result = false; | ||||||
|     } else { |     } else { | ||||||
|         __disable_irq(); |         FURI_CRITICAL_ENTER(); | ||||||
|         pin_init_opendrain_in_isr_ctx(); |         pin_init_opendrain_in_isr_ctx(); | ||||||
|         error = OneWireSlaveError::NO_ERROR; |         error = OneWireSlaveError::NO_ERROR; | ||||||
| 
 | 
 | ||||||
| @ -274,7 +274,7 @@ bool OneWireSlave::bus_start(void) { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         pin_init_interrupt_in_isr_ctx(); |         pin_init_interrupt_in_isr_ctx(); | ||||||
|         __enable_irq(); |         FURI_CRITICAL_EXIT(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return result; |     return result; | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 あく
						あく