[FL-3057] Allow use of any suitable pin for 1-Wire devices (#2350)
* Add 1-wire thermometer example app stub * Working 1-wire thermometer app * Refactor app to use threads * Clean up code, add comments * Add CRC checking * Increase update period * Fix error in fbt * Revert the old update period * Use settable pin in onewire_host * Use settable pin for onewire_slave * Clear EXTI flag after callback, make private methods static in onewire_slave * Do not hardcode GPIO pin number * Remove iButton hal from furi_hal_rfid * Remove most of furi_hal_ibutton * Add some of furi_hal_ibutton back * Slightly neater code * Fix formatting * Fix PVS-studio warnings * Update CODEOWNERS * Add furi_hal_gpio_get_ext_pin_number * Create README.md * FuriHal: move furi_hal_gpio_get_ext_pin_number to resources --------- Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									e3d473bf42
								
							
						
					
					
						commit
						7a3a1aaf0d
					
				
							
								
								
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/CODEOWNERS
									
									
									
									
										vendored
									
									
								
							| @ -42,6 +42,8 @@ | ||||
| 
 | ||||
| /applications/debug/unit_tests/ @skotopes @DrZlo13 @hedger @nminaylov @gornekich @Astrrra @gsurkov @Skorpionm | ||||
| 
 | ||||
| /applications/examples/example_thermo/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| 
 | ||||
| # Assets | ||||
| /assets/resources/infrared/ @skotopes @DrZlo13 @hedger @gsurkov | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										44
									
								
								applications/examples/example_thermo/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								applications/examples/example_thermo/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| # 1-Wire Thermometer | ||||
| This example application demonstrates the use of the 1-Wire library with a DS18B20 thermometer.  | ||||
| It also covers basic GUI, input handling, threads and localisation. | ||||
| 
 | ||||
| ## Electrical connections | ||||
| Before launching the application, connect the sensor to Flipper's external GPIO according to the table below: | ||||
| | DS18B20 | Flipper | | ||||
| | :-----: | :-----: | | ||||
| | VDD | 9 | | ||||
| | GND | 18 | | ||||
| | DQ  | 17 | | ||||
| 
 | ||||
| *NOTE 1*: GND is also available on pins 8 and 11. | ||||
| 
 | ||||
| *NOTE 2*: For any other pin than 17, connect an external 4.7k pull-up resistor to pin 9. | ||||
| 
 | ||||
| ## Launching the application | ||||
| In order to launch this demo, follow the steps below: | ||||
| 1. Make sure your Flipper has an SD card installed. | ||||
| 2. Connect your Flipper to the computer via a USB cable. | ||||
| 3. Run `./fbt launch_app APPSRC=example_thermo` in your terminal emulator of choice. | ||||
| 
 | ||||
| ## Changing the data pin | ||||
| It is possible to use other GPIO pin as a 1-Wire data pin. In order to change it, set the `THERMO_GPIO_PIN` macro to any of the options listed below: | ||||
| 
 | ||||
| ```c | ||||
| /* Possible GPIO pin choices: | ||||
|  - gpio_ext_pc0 | ||||
|  - gpio_ext_pc1 | ||||
|  - gpio_ext_pc3 | ||||
|  - gpio_ext_pb2 | ||||
|  - gpio_ext_pb3 | ||||
|  - gpio_ext_pa4 | ||||
|  - gpio_ext_pa6 | ||||
|  - gpio_ext_pa7 | ||||
|  - ibutton_gpio | ||||
| */ | ||||
| 
 | ||||
| #define THERMO_GPIO_PIN (ibutton_gpio) | ||||
| ``` | ||||
| Do not forget about the external pull-up resistor as these pins do not have one built-in. | ||||
| 
 | ||||
| With the changes been made, recompile and launch the application again.  | ||||
| The on-screen text should reflect it by asking to connect the thermometer to another pin. | ||||
							
								
								
									
										10
									
								
								applications/examples/example_thermo/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								applications/examples/example_thermo/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| App( | ||||
|     appid="example_thermo", | ||||
|     name="Example: Thermometer", | ||||
|     apptype=FlipperAppType.EXTERNAL, | ||||
|     entry_point="example_thermo_main", | ||||
|     requires=["gui"], | ||||
|     stack_size=1 * 1024, | ||||
|     fap_icon="example_thermo_10px.png", | ||||
|     fap_category="Examples", | ||||
| ) | ||||
							
								
								
									
										356
									
								
								applications/examples/example_thermo/example_thermo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										356
									
								
								applications/examples/example_thermo/example_thermo.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,356 @@ | ||||
| /*
 | ||||
|  * This file contains an example application that reads and displays | ||||
|  * the temperature from a DS18B20 1-wire thermometer. | ||||
|  * | ||||
|  * It also covers basic GUI, input handling, threads and localisation. | ||||
|  * | ||||
|  * References: | ||||
|  * [1] DS18B20 Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/DS18B20.pdf
 | ||||
|  */ | ||||
| 
 | ||||
| #include <gui/gui.h> | ||||
| #include <gui/view_port.h> | ||||
| 
 | ||||
| #include <core/thread.h> | ||||
| #include <core/kernel.h> | ||||
| 
 | ||||
| #include <locale/locale.h> | ||||
| 
 | ||||
| #include <one_wire/maxim_crc.h> | ||||
| #include <one_wire/one_wire_host.h> | ||||
| 
 | ||||
| #define UPDATE_PERIOD_MS 1000UL | ||||
| #define TEXT_STORE_SIZE 64U | ||||
| 
 | ||||
| #define DS18B20_CMD_CONVERT 0x44U | ||||
| #define DS18B20_CMD_READ_SCRATCHPAD 0xbeU | ||||
| 
 | ||||
| #define DS18B20_CFG_RESOLUTION_POS 5U | ||||
| #define DS18B20_CFG_RESOLUTION_MASK 0x03U | ||||
| #define DS18B20_DECIMAL_PART_MASK 0x0fU | ||||
| 
 | ||||
| #define DS18B20_SIGN_MASK 0xf0U | ||||
| 
 | ||||
| /* Possible GPIO pin choices:
 | ||||
|  - gpio_ext_pc0 | ||||
|  - gpio_ext_pc1 | ||||
|  - gpio_ext_pc3 | ||||
|  - gpio_ext_pb2 | ||||
|  - gpio_ext_pb3 | ||||
|  - gpio_ext_pa4 | ||||
|  - gpio_ext_pa6 | ||||
|  - gpio_ext_pa7 | ||||
|  - ibutton_gpio | ||||
| */ | ||||
| 
 | ||||
| #define THERMO_GPIO_PIN (ibutton_gpio) | ||||
| 
 | ||||
| /* Flags which the reader thread responds to */ | ||||
| typedef enum { | ||||
|     ReaderThreadFlagExit = 1, | ||||
| } ReaderThreadFlag; | ||||
| 
 | ||||
| typedef union { | ||||
|     struct { | ||||
|         uint8_t temp_lsb; /* Least significant byte of the temperature */ | ||||
|         uint8_t temp_msb; /* Most significant byte of the temperature */ | ||||
|         uint8_t user_alarm_high; /* User register 1 (Temp high alarm) */ | ||||
|         uint8_t user_alarm_low; /* User register 2 (Temp low alarm) */ | ||||
|         uint8_t config; /* Configuration register */ | ||||
|         uint8_t reserved[3]; /* Not used */ | ||||
|         uint8_t crc; /* CRC checksum for error detection */ | ||||
|     } fields; | ||||
|     uint8_t bytes[9]; | ||||
| } DS18B20Scratchpad; | ||||
| 
 | ||||
| /* Application context structure */ | ||||
| typedef struct { | ||||
|     Gui* gui; | ||||
|     ViewPort* view_port; | ||||
|     FuriThread* reader_thread; | ||||
|     FuriMessageQueue* event_queue; | ||||
|     OneWireHost* onewire; | ||||
|     float temp_celsius; | ||||
|     bool has_device; | ||||
| } ExampleThermoContext; | ||||
| 
 | ||||
| /*************** 1-Wire Communication and Processing *****************/ | ||||
| 
 | ||||
| /* Commands the thermometer to begin measuring the temperature. */ | ||||
| static void example_thermo_request_temperature(ExampleThermoContext* context) { | ||||
|     OneWireHost* onewire = context->onewire; | ||||
| 
 | ||||
|     /* All 1-wire transactions must happen in a critical section, i.e
 | ||||
|        not interrupted by other threads. */ | ||||
|     FURI_CRITICAL_ENTER(); | ||||
| 
 | ||||
|     bool success = false; | ||||
|     do { | ||||
|         /* Each communication with a 1-wire device starts by a reset.
 | ||||
|            The functon will return true if a device responded with a presence pulse. */ | ||||
|         if(!onewire_host_reset(onewire)) break; | ||||
|         /* After the reset, a ROM operation must follow.
 | ||||
|            If there is only one device connected, the "Skip ROM" command is most appropriate | ||||
|            (it can also be used to address all of the connected devices in some cases).*/ | ||||
|         onewire_host_skip(onewire); | ||||
|         /* After the ROM operation, a device-specific command is issued.
 | ||||
|            In this case, it's a request to start measuring the temperature. */ | ||||
|         onewire_host_write(onewire, DS18B20_CMD_CONVERT); | ||||
| 
 | ||||
|         success = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     context->has_device = success; | ||||
| 
 | ||||
|     FURI_CRITICAL_EXIT(); | ||||
| } | ||||
| 
 | ||||
| /* Reads the measured temperature from the thermometer. */ | ||||
| static void example_thermo_read_temperature(ExampleThermoContext* context) { | ||||
|     /* If there was no device detected, don't try to read the temperature */ | ||||
|     if(!context->has_device) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     OneWireHost* onewire = context->onewire; | ||||
| 
 | ||||
|     /* All 1-wire transactions must happen in a critical section, i.e
 | ||||
|        not interrupted by other threads. */ | ||||
|     FURI_CRITICAL_ENTER(); | ||||
| 
 | ||||
|     bool success = false; | ||||
| 
 | ||||
|     do { | ||||
|         DS18B20Scratchpad buf; | ||||
| 
 | ||||
|         /* Attempt reading the temperature 10 times before giving up */ | ||||
|         size_t attempts_left = 10; | ||||
|         do { | ||||
|             /* Each communication with a 1-wire device starts by a reset.
 | ||||
|             The functon will return true if a device responded with a presence pulse. */ | ||||
|             if(!onewire_host_reset(onewire)) continue; | ||||
| 
 | ||||
|             /* After the reset, a ROM operation must follow.
 | ||||
|             If there is only one device connected, the "Skip ROM" command is most appropriate | ||||
|             (it can also be used to address all of the connected devices in some cases).*/ | ||||
|             onewire_host_skip(onewire); | ||||
| 
 | ||||
|             /* After the ROM operation, a device-specific command is issued.
 | ||||
|             This time, it will be the "Read Scratchpad" command which will | ||||
|             prepare the device's internal buffer memory for reading. */ | ||||
|             onewire_host_write(onewire, DS18B20_CMD_READ_SCRATCHPAD); | ||||
| 
 | ||||
|             /* The actual reading happens here. A total of 9 bytes is read. */ | ||||
|             onewire_host_read_bytes(onewire, buf.bytes, sizeof(buf.bytes)); | ||||
| 
 | ||||
|             /* Calculate the checksum and compare it with one provided by the device. */ | ||||
|             const uint8_t crc = maxim_crc8(buf.bytes, sizeof(buf.bytes) - 1, MAXIM_CRC8_INIT); | ||||
| 
 | ||||
|             /* Checksums match, exit the loop */ | ||||
|             if(crc == buf.fields.crc) break; | ||||
| 
 | ||||
|         } while(--attempts_left); | ||||
| 
 | ||||
|         if(attempts_left == 0) break; | ||||
| 
 | ||||
|         /* Get the measurement resolution from the configuration register. (See [1] page 9) */ | ||||
|         const uint8_t resolution_mode = (buf.fields.config >> DS18B20_CFG_RESOLUTION_POS) & | ||||
|                                         DS18B20_CFG_RESOLUTION_MASK; | ||||
| 
 | ||||
|         /* Generate a mask for undefined bits in the decimal part. (See [1] page 6) */ | ||||
|         const uint8_t decimal_mask = | ||||
|             (DS18B20_DECIMAL_PART_MASK << (DS18B20_CFG_RESOLUTION_MASK - resolution_mode)) & | ||||
|             DS18B20_DECIMAL_PART_MASK; | ||||
| 
 | ||||
|         /* Get the integer and decimal part of the temperature (See [1] page 6) */ | ||||
|         const uint8_t integer_part = (buf.fields.temp_msb << 4U) | (buf.fields.temp_lsb >> 4U); | ||||
|         const uint8_t decimal_part = buf.fields.temp_lsb & decimal_mask; | ||||
| 
 | ||||
|         /* Calculate the sign of the temperature (See [1] page 6) */ | ||||
|         const bool is_negative = (buf.fields.temp_msb & DS18B20_SIGN_MASK) != 0; | ||||
| 
 | ||||
|         /* Combine the integer and decimal part together */ | ||||
|         const float temp_celsius_abs = integer_part + decimal_part / 16.f; | ||||
| 
 | ||||
|         /* Set the appropriate sign */ | ||||
|         context->temp_celsius = is_negative ? -temp_celsius_abs : temp_celsius_abs; | ||||
| 
 | ||||
|         success = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     context->has_device = success; | ||||
| 
 | ||||
|     FURI_CRITICAL_EXIT(); | ||||
| } | ||||
| 
 | ||||
| /* Periodically requests measurements and reads temperature. This function runs in a separare thread. */ | ||||
| static int32_t example_thermo_reader_thread_callback(void* ctx) { | ||||
|     ExampleThermoContext* context = ctx; | ||||
| 
 | ||||
|     for(;;) { | ||||
|         /* Tell the termometer to start measuring the temperature. The process may take up to 750ms. */ | ||||
|         example_thermo_request_temperature(context); | ||||
| 
 | ||||
|         /* Wait for the measurement to finish. At the same time wait for an exit signal. */ | ||||
|         const uint32_t flags = | ||||
|             furi_thread_flags_wait(ReaderThreadFlagExit, FuriFlagWaitAny, UPDATE_PERIOD_MS); | ||||
| 
 | ||||
|         /* If an exit signal was received, return from this thread. */ | ||||
|         if(flags != (unsigned)FuriFlagErrorTimeout) break; | ||||
| 
 | ||||
|         /* The measurement is now ready, read it from the termometer. */ | ||||
|         example_thermo_read_temperature(context); | ||||
|     } | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| /*************** GUI, Input and Main Loop *****************/ | ||||
| 
 | ||||
| /* Draw the GUI of the application. The screen is completely redrawn during each call. */ | ||||
| static void example_thermo_draw_callback(Canvas* canvas, void* ctx) { | ||||
|     ExampleThermoContext* context = ctx; | ||||
|     char text_store[TEXT_STORE_SIZE]; | ||||
|     const size_t middle_x = canvas_width(canvas) / 2U; | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     canvas_draw_str_aligned(canvas, middle_x, 12, AlignCenter, AlignBottom, "Thermometer Demo"); | ||||
|     canvas_draw_line(canvas, 0, 16, 128, 16); | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str_aligned( | ||||
|         canvas, middle_x, 30, AlignCenter, AlignBottom, "Connnect thermometer"); | ||||
| 
 | ||||
|     snprintf( | ||||
|         text_store, | ||||
|         TEXT_STORE_SIZE, | ||||
|         "to GPIO pin %ld", | ||||
|         furi_hal_resources_get_ext_pin_number(&THERMO_GPIO_PIN)); | ||||
|     canvas_draw_str_aligned(canvas, middle_x, 42, AlignCenter, AlignBottom, text_store); | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontKeyboard); | ||||
| 
 | ||||
|     if(context->has_device) { | ||||
|         float temp; | ||||
|         char temp_units; | ||||
| 
 | ||||
|         /* The applicaton is locale-aware.
 | ||||
|            Change Settings->System->Units to check it out. */ | ||||
|         switch(locale_get_measurement_unit()) { | ||||
|         case LocaleMeasurementUnitsMetric: | ||||
|             temp = context->temp_celsius; | ||||
|             temp_units = 'C'; | ||||
|             break; | ||||
|         case LocaleMeasurementUnitsImperial: | ||||
|             temp = locale_celsius_to_fahrenheit(context->temp_celsius); | ||||
|             temp_units = 'F'; | ||||
|             break; | ||||
|         default: | ||||
|             furi_crash("Illegal measurement units"); | ||||
|         } | ||||
|         /* If a reading is available, display it */ | ||||
|         snprintf(text_store, TEXT_STORE_SIZE, "Temperature: %+.1f%c", (double)temp, temp_units); | ||||
|     } else { | ||||
|         /* Or show a message that no data is available */ | ||||
|         strncpy(text_store, "-- No data --", TEXT_STORE_SIZE); | ||||
|     } | ||||
| 
 | ||||
|     canvas_draw_str_aligned(canvas, middle_x, 58, AlignCenter, AlignBottom, text_store); | ||||
| } | ||||
| 
 | ||||
| /* This function is called from the GUI thread. All it does is put the event
 | ||||
|    into the application's queue so it can be processed later. */ | ||||
| static void example_thermo_input_callback(InputEvent* event, void* ctx) { | ||||
|     ExampleThermoContext* context = ctx; | ||||
|     furi_message_queue_put(context->event_queue, event, FuriWaitForever); | ||||
| } | ||||
| 
 | ||||
| /* Starts the reader thread and handles the input */ | ||||
| static void example_thermo_run(ExampleThermoContext* context) { | ||||
|     /* Configure the hardware in host mode */ | ||||
|     onewire_host_start(context->onewire); | ||||
| 
 | ||||
|     /* Start the reader thread. It will talk to the thermometer in the background. */ | ||||
|     furi_thread_start(context->reader_thread); | ||||
| 
 | ||||
|     /* An endless loop which handles the input*/ | ||||
|     for(bool is_running = true; is_running;) { | ||||
|         InputEvent event; | ||||
|         /* Wait for an input event. Input events come from the GUI thread via a callback. */ | ||||
|         const FuriStatus status = | ||||
|             furi_message_queue_get(context->event_queue, &event, FuriWaitForever); | ||||
| 
 | ||||
|         /* This application is only interested in short button presses. */ | ||||
|         if((status != FuriStatusOk) || (event.type != InputTypeShort)) { | ||||
|             continue; | ||||
|         } | ||||
| 
 | ||||
|         /* When the user presses the "Back" button, break the loop and exit the application. */ | ||||
|         if(event.key == InputKeyBack) { | ||||
|             is_running = false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /* Signal the reader thread to cease operation and exit */ | ||||
|     furi_thread_flags_set(furi_thread_get_id(context->reader_thread), ReaderThreadFlagExit); | ||||
| 
 | ||||
|     /* Wait for the reader thread to finish */ | ||||
|     furi_thread_join(context->reader_thread); | ||||
| 
 | ||||
|     /* Reset the hardware */ | ||||
|     onewire_host_stop(context->onewire); | ||||
| } | ||||
| 
 | ||||
| /******************** Initialisation & startup *****************************/ | ||||
| 
 | ||||
| /* Allocate the memory and initialise the variables */ | ||||
| static ExampleThermoContext* example_thermo_context_alloc() { | ||||
|     ExampleThermoContext* context = malloc(sizeof(ExampleThermoContext)); | ||||
| 
 | ||||
|     context->view_port = view_port_alloc(); | ||||
|     view_port_draw_callback_set(context->view_port, example_thermo_draw_callback, context); | ||||
|     view_port_input_callback_set(context->view_port, example_thermo_input_callback, context); | ||||
| 
 | ||||
|     context->event_queue = furi_message_queue_alloc(8, sizeof(InputEvent)); | ||||
| 
 | ||||
|     context->reader_thread = furi_thread_alloc(); | ||||
|     furi_thread_set_stack_size(context->reader_thread, 1024U); | ||||
|     furi_thread_set_context(context->reader_thread, context); | ||||
|     furi_thread_set_callback(context->reader_thread, example_thermo_reader_thread_callback); | ||||
| 
 | ||||
|     context->gui = furi_record_open(RECORD_GUI); | ||||
|     gui_add_view_port(context->gui, context->view_port, GuiLayerFullscreen); | ||||
| 
 | ||||
|     context->onewire = onewire_host_alloc(&THERMO_GPIO_PIN); | ||||
| 
 | ||||
|     return context; | ||||
| } | ||||
| 
 | ||||
| /* Release the unused resources and deallocate memory */ | ||||
| static void example_thermo_context_free(ExampleThermoContext* context) { | ||||
|     view_port_enabled_set(context->view_port, false); | ||||
|     gui_remove_view_port(context->gui, context->view_port); | ||||
| 
 | ||||
|     onewire_host_free(context->onewire); | ||||
|     furi_thread_free(context->reader_thread); | ||||
|     furi_message_queue_free(context->event_queue); | ||||
|     view_port_free(context->view_port); | ||||
| 
 | ||||
|     furi_record_close(RECORD_GUI); | ||||
| } | ||||
| 
 | ||||
| /* The application's entry point. Execution starts from here. */ | ||||
| int32_t example_thermo_main(void* p) { | ||||
|     UNUSED(p); | ||||
| 
 | ||||
|     /* Allocate all of the necessary structures */ | ||||
|     ExampleThermoContext* context = example_thermo_context_alloc(); | ||||
| 
 | ||||
|     /* Start the applicaton's main loop. It won't return until the application was requested to exit. */ | ||||
|     example_thermo_run(context); | ||||
| 
 | ||||
|     /* Release all unneeded resources */ | ||||
|     example_thermo_context_free(context); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/examples/example_thermo/example_thermo_10px.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/examples/example_thermo/example_thermo_10px.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 7.1 KiB | 
| @ -271,7 +271,7 @@ void onewire_cli_print_usage() { | ||||
| 
 | ||||
| static void onewire_cli_search(Cli* cli) { | ||||
|     UNUSED(cli); | ||||
|     OneWireHost* onewire = onewire_host_alloc(); | ||||
|     OneWireHost* onewire = onewire_host_alloc(&ibutton_gpio); | ||||
|     uint8_t address[8]; | ||||
|     bool done = false; | ||||
| 
 | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| entry,status,name,type,params | ||||
| Version,+,12.2,, | ||||
| Version,+,13.0,, | ||||
| Header,+,applications/services/bt/bt_service/bt.h,, | ||||
| Header,+,applications/services/cli/cli.h,, | ||||
| Header,+,applications/services/cli/cli_vcp.h,, | ||||
| @ -910,6 +910,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" | ||||
| Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" | ||||
| Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* | ||||
| Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* | ||||
| Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* | ||||
| Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" | ||||
| Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" | ||||
| Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" | ||||
|  | ||||
| 
 | 
| @ -199,3 +199,27 @@ void furi_hal_resources_init() { | ||||
|     NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); | ||||
|     NVIC_EnableIRQ(EXTI15_10_IRQn); | ||||
| } | ||||
| 
 | ||||
| int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { | ||||
|     // TODO: describe second ROW
 | ||||
|     if(gpio == &gpio_ext_pa7) | ||||
|         return 2; | ||||
|     else if(gpio == &gpio_ext_pa6) | ||||
|         return 3; | ||||
|     else if(gpio == &gpio_ext_pa4) | ||||
|         return 4; | ||||
|     else if(gpio == &gpio_ext_pb3) | ||||
|         return 5; | ||||
|     else if(gpio == &gpio_ext_pb2) | ||||
|         return 6; | ||||
|     else if(gpio == &gpio_ext_pc3) | ||||
|         return 7; | ||||
|     else if(gpio == &gpio_ext_pc1) | ||||
|         return 15; | ||||
|     else if(gpio == &gpio_ext_pc0) | ||||
|         return 16; | ||||
|     else if(gpio == &ibutton_gpio) | ||||
|         return 17; | ||||
|     else | ||||
|         return -1; | ||||
| } | ||||
|  | ||||
| @ -111,6 +111,13 @@ void furi_hal_resources_deinit_early(); | ||||
| 
 | ||||
| void furi_hal_resources_init(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get a corresponding external connector pin number for a gpio | ||||
|  * @param gpio GpioPin | ||||
|  * @return pin number or -1 if gpio is not on the external connector | ||||
|  */ | ||||
| int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -1,5 +1,5 @@ | ||||
| entry,status,name,type,params | ||||
| Version,+,12.2,, | ||||
| Version,+,13.0,, | ||||
| Header,+,applications/services/bt/bt_service/bt.h,, | ||||
| Header,+,applications/services/cli/cli.h,, | ||||
| Header,+,applications/services/cli/cli_vcp.h,, | ||||
| @ -1094,6 +1094,7 @@ Function,-,furi_hal_flash_write_dword,void,"size_t, uint64_t" | ||||
| Function,+,furi_hal_gpio_add_int_callback,void,"const GpioPin*, GpioExtiCallback, void*" | ||||
| Function,+,furi_hal_gpio_disable_int_callback,void,const GpioPin* | ||||
| Function,+,furi_hal_gpio_enable_int_callback,void,const GpioPin* | ||||
| Function,+,furi_hal_resources_get_ext_pin_number,int32_t,const GpioPin* | ||||
| Function,+,furi_hal_gpio_init,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed" | ||||
| Function,+,furi_hal_gpio_init_ex,void,"const GpioPin*, const GpioMode, const GpioPull, const GpioSpeed, const GpioAltFn" | ||||
| Function,+,furi_hal_gpio_init_simple,void,"const GpioPin*, const GpioMode" | ||||
| @ -1129,20 +1130,13 @@ Function,+,furi_hal_i2c_tx,_Bool,"FuriHalI2cBusHandle*, const uint8_t, const uin | ||||
| Function,+,furi_hal_i2c_write_mem,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t*, uint8_t, uint32_t" | ||||
| Function,+,furi_hal_i2c_write_reg_16,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint16_t, uint32_t" | ||||
| Function,+,furi_hal_i2c_write_reg_8,_Bool,"FuriHalI2cBusHandle*, uint8_t, uint8_t, uint8_t, uint32_t" | ||||
| Function,+,furi_hal_ibutton_add_interrupt,void,"GpioExtiCallback, void*" | ||||
| Function,+,furi_hal_ibutton_emulate_set_next,void,uint32_t | ||||
| Function,+,furi_hal_ibutton_emulate_start,void,"uint32_t, FuriHalIbuttonEmulateCallback, void*" | ||||
| Function,+,furi_hal_ibutton_emulate_stop,void, | ||||
| Function,-,furi_hal_ibutton_init,void, | ||||
| Function,+,furi_hal_ibutton_pin_get_level,_Bool, | ||||
| Function,+,furi_hal_ibutton_pin_high,void, | ||||
| Function,+,furi_hal_ibutton_pin_low,void, | ||||
| Function,+,furi_hal_ibutton_remove_interrupt,void, | ||||
| Function,+,furi_hal_ibutton_start_drive,void, | ||||
| Function,+,furi_hal_ibutton_start_drive_in_isr,void, | ||||
| Function,+,furi_hal_ibutton_start_interrupt,void, | ||||
| Function,+,furi_hal_ibutton_start_interrupt_in_isr,void, | ||||
| Function,+,furi_hal_ibutton_stop,void, | ||||
| Function,+,furi_hal_ibutton_pin_configure,void, | ||||
| Function,+,furi_hal_ibutton_pin_reset,void, | ||||
| Function,+,furi_hal_ibutton_pin_write,void,const _Bool | ||||
| Function,+,furi_hal_info_get,void,"PropertyValueCallback, char, void*" | ||||
| Function,+,furi_hal_infrared_async_rx_set_capture_isr_callback,void,"FuriHalInfraredRxCaptureCallback, void*" | ||||
| Function,+,furi_hal_infrared_async_rx_set_timeout,void,uint32_t | ||||
| @ -2040,7 +2034,7 @@ Function,+,onewire_device_detach,void,OneWireDevice* | ||||
| Function,+,onewire_device_free,void,OneWireDevice* | ||||
| Function,+,onewire_device_get_id_p,uint8_t*,OneWireDevice* | ||||
| Function,+,onewire_device_send_id,void,OneWireDevice* | ||||
| Function,+,onewire_host_alloc,OneWireHost*, | ||||
| Function,+,onewire_host_alloc,OneWireHost*,const GpioPin* | ||||
| Function,+,onewire_host_free,void,OneWireHost* | ||||
| Function,+,onewire_host_read,uint8_t,OneWireHost* | ||||
| Function,+,onewire_host_read_bit,_Bool,OneWireHost* | ||||
| @ -2054,7 +2048,7 @@ Function,+,onewire_host_stop,void,OneWireHost* | ||||
| Function,+,onewire_host_target_search,void,"OneWireHost*, uint8_t" | ||||
| Function,+,onewire_host_write,void,"OneWireHost*, uint8_t" | ||||
| Function,+,onewire_host_write_bit,void,"OneWireHost*, _Bool" | ||||
| Function,+,onewire_slave_alloc,OneWireSlave*, | ||||
| Function,+,onewire_slave_alloc,OneWireSlave*,const GpioPin* | ||||
| Function,+,onewire_slave_attach,void,"OneWireSlave*, OneWireDevice*" | ||||
| Function,+,onewire_slave_detach,void,OneWireSlave* | ||||
| Function,+,onewire_slave_free,void,OneWireSlave* | ||||
|  | ||||
| 
 | 
| @ -1,6 +1,7 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal_gpio.h> | ||||
| #include <furi_hal_version.h> | ||||
| #include <furi_hal_resources.h> | ||||
| #include <stm32wbxx_ll_comp.h> | ||||
| 
 | ||||
| #define GET_SYSCFG_EXTI_PORT(gpio)                \ | ||||
| @ -224,85 +225,85 @@ static void furi_hal_gpio_int_call(uint16_t pin_num) { | ||||
| /* Interrupt handlers */ | ||||
| void EXTI0_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); | ||||
|         furi_hal_gpio_int_call(0); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI1_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); | ||||
|         furi_hal_gpio_int_call(1); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI2_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); | ||||
|         furi_hal_gpio_int_call(2); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI3_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); | ||||
|         furi_hal_gpio_int_call(3); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI4_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); | ||||
|         furi_hal_gpio_int_call(4); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI9_5_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); | ||||
|         furi_hal_gpio_int_call(5); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); | ||||
|         furi_hal_gpio_int_call(6); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); | ||||
|         furi_hal_gpio_int_call(7); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); | ||||
|         furi_hal_gpio_int_call(8); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); | ||||
|         furi_hal_gpio_int_call(9); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void EXTI15_10_IRQHandler(void) { | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); | ||||
|         furi_hal_gpio_int_call(10); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); | ||||
|         furi_hal_gpio_int_call(11); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); | ||||
|         furi_hal_gpio_int_call(12); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); | ||||
|         furi_hal_gpio_int_call(13); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); | ||||
|         furi_hal_gpio_int_call(14); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14); | ||||
|     } | ||||
|     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) { | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); | ||||
|         furi_hal_gpio_int_call(15); | ||||
|         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15); | ||||
|     } | ||||
| } | ||||
|  | ||||
| @ -89,47 +89,16 @@ void furi_hal_ibutton_emulate_stop() { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_start_drive() { | ||||
|     furi_hal_ibutton_pin_high(); | ||||
| void furi_hal_ibutton_pin_configure() { | ||||
|     furi_hal_gpio_write(&ibutton_gpio, true); | ||||
|     furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_start_drive_in_isr() { | ||||
|     furi_hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||
|     LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_start_interrupt() { | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_start_interrupt_in_isr() { | ||||
|     furi_hal_gpio_init(&ibutton_gpio, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||
|     LL_EXTI_ClearFlag_0_31(ibutton_gpio.pin); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_stop() { | ||||
|     furi_hal_ibutton_pin_high(); | ||||
| void furi_hal_ibutton_pin_reset() { | ||||
|     furi_hal_gpio_write(&ibutton_gpio, true); | ||||
|     furi_hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context) { | ||||
|     furi_hal_gpio_add_int_callback(&ibutton_gpio, cb, context); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_remove_interrupt() { | ||||
|     furi_hal_gpio_remove_int_callback(&ibutton_gpio); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_pin_low() { | ||||
|     furi_hal_gpio_write(&ibutton_gpio, false); | ||||
| } | ||||
| 
 | ||||
| void furi_hal_ibutton_pin_high() { | ||||
|     furi_hal_gpio_write(&ibutton_gpio, true); | ||||
| } | ||||
| 
 | ||||
| bool furi_hal_ibutton_pin_get_level() { | ||||
|     return furi_hal_gpio_read(&ibutton_gpio); | ||||
| void furi_hal_ibutton_pin_write(const bool state) { | ||||
|     furi_hal_gpio_write(&ibutton_gpio, state); | ||||
| } | ||||
|  | ||||
| @ -7,7 +7,6 @@ | ||||
| 
 | ||||
| #include <stdbool.h> | ||||
| #include <stdint.h> | ||||
| #include <furi_hal_gpio.h> | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| @ -18,70 +17,43 @@ typedef void (*FuriHalIbuttonEmulateCallback)(void* context); | ||||
| /** Initialize */ | ||||
| void furi_hal_ibutton_init(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Start emulation timer | ||||
|  * @param period timer period | ||||
|  * @param callback timer callback | ||||
|  * @param context callback context | ||||
|  */ | ||||
| void furi_hal_ibutton_emulate_start( | ||||
|     uint32_t period, | ||||
|     FuriHalIbuttonEmulateCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Update emulation timer period | ||||
|  * @param period new timer period | ||||
|  */ | ||||
| void furi_hal_ibutton_emulate_set_next(uint32_t period); | ||||
| 
 | ||||
| /**
 | ||||
|  * Stop emulation timer | ||||
|  */ | ||||
| void furi_hal_ibutton_emulate_stop(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to normal mode (open collector), and sets it to float | ||||
|  * Set the pin to normal mode (open collector), and sets it to float | ||||
|  */ | ||||
| void furi_hal_ibutton_start_drive(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to normal mode (open collector), and clears pin EXTI interrupt. | ||||
|  * Used in EXTI interrupt context. | ||||
|  */ | ||||
| void furi_hal_ibutton_start_drive_in_isr(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and sets it to float | ||||
|  */ | ||||
| void furi_hal_ibutton_start_interrupt(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to interrupt mode (EXTI interrupt on rise or fall), and clears pin EXTI interrupt. | ||||
|  * Used in EXTI interrupt context. | ||||
|  */ | ||||
| void furi_hal_ibutton_start_interrupt_in_isr(); | ||||
| void furi_hal_ibutton_pin_configure(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to analog mode, and sets it to float | ||||
|  */ | ||||
| void furi_hal_ibutton_stop(); | ||||
| void furi_hal_ibutton_pin_reset(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Attach interrupt callback to iButton pin | ||||
|  * @param cb callback | ||||
|  * @param context context | ||||
|  * iButton write pin | ||||
|  * @param state true / false | ||||
|  */ | ||||
| void furi_hal_ibutton_add_interrupt(GpioExtiCallback cb, void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Remove interrupt callback from iButton pin | ||||
|  */ | ||||
| void furi_hal_ibutton_remove_interrupt(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to low | ||||
|  */ | ||||
| void furi_hal_ibutton_pin_low(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Sets the pin to high (float in iButton pin modes) | ||||
|  */ | ||||
| void furi_hal_ibutton_pin_high(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get pin level | ||||
|  * @return true if level is high | ||||
|  * @return false if level is low | ||||
|  */ | ||||
| bool furi_hal_ibutton_pin_get_level(); | ||||
| void furi_hal_ibutton_pin_write(const bool state); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -191,3 +191,26 @@ void furi_hal_resources_init() { | ||||
|     NVIC_SetPriority(EXTI15_10_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0)); | ||||
|     NVIC_EnableIRQ(EXTI15_10_IRQn); | ||||
| } | ||||
| 
 | ||||
| int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio) { | ||||
|     if(gpio == &gpio_ext_pa7) | ||||
|         return 2; | ||||
|     else if(gpio == &gpio_ext_pa6) | ||||
|         return 3; | ||||
|     else if(gpio == &gpio_ext_pa4) | ||||
|         return 4; | ||||
|     else if(gpio == &gpio_ext_pb3) | ||||
|         return 5; | ||||
|     else if(gpio == &gpio_ext_pb2) | ||||
|         return 6; | ||||
|     else if(gpio == &gpio_ext_pc3) | ||||
|         return 7; | ||||
|     else if(gpio == &gpio_ext_pc1) | ||||
|         return 15; | ||||
|     else if(gpio == &gpio_ext_pc0) | ||||
|         return 16; | ||||
|     else if(gpio == &ibutton_gpio) | ||||
|         return 17; | ||||
|     else | ||||
|         return -1; | ||||
| } | ||||
|  | ||||
| @ -216,6 +216,13 @@ void furi_hal_resources_deinit_early(); | ||||
| 
 | ||||
| void furi_hal_resources_init(); | ||||
| 
 | ||||
| /**
 | ||||
|  * Get a corresponding external connector pin number for a gpio | ||||
|  * @param gpio GpioPin | ||||
|  * @return pin number or -1 if gpio is not on the external connector | ||||
|  */ | ||||
| int32_t furi_hal_resources_get_ext_pin_number(const GpioPin* gpio); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| @ -77,7 +77,7 @@ void furi_hal_rfid_init() { | ||||
| 
 | ||||
| void furi_hal_rfid_pins_reset() { | ||||
|     // ibutton bus disable
 | ||||
|     furi_hal_ibutton_stop(); | ||||
|     furi_hal_ibutton_pin_reset(); | ||||
| 
 | ||||
|     // pulldown rfid antenna
 | ||||
|     furi_hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
| @ -94,8 +94,8 @@ void furi_hal_rfid_pins_reset() { | ||||
| 
 | ||||
| void furi_hal_rfid_pins_emulate() { | ||||
|     // ibutton low
 | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_ibutton_pin_low(); | ||||
|     furi_hal_ibutton_pin_configure(); | ||||
|     furi_hal_ibutton_pin_write(false); | ||||
| 
 | ||||
|     // pull pin to timer out
 | ||||
|     furi_hal_gpio_init_ex( | ||||
| @ -115,8 +115,8 @@ void furi_hal_rfid_pins_emulate() { | ||||
| 
 | ||||
| void furi_hal_rfid_pins_read() { | ||||
|     // ibutton low
 | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_ibutton_pin_low(); | ||||
|     furi_hal_ibutton_pin_configure(); | ||||
|     furi_hal_ibutton_pin_write(false); | ||||
| 
 | ||||
|     // dont pull rfid antenna
 | ||||
|     furi_hal_gpio_init(&gpio_nfc_irq_rfid_pull, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow); | ||||
|  | ||||
| @ -25,8 +25,8 @@ iButtonWorker* ibutton_worker_alloc() { | ||||
|     iButtonWorker* worker = malloc(sizeof(iButtonWorker)); | ||||
|     worker->key_p = NULL; | ||||
|     worker->key_data = malloc(ibutton_key_get_max_size()); | ||||
|     worker->host = onewire_host_alloc(); | ||||
|     worker->slave = onewire_slave_alloc(); | ||||
|     worker->host = onewire_host_alloc(&ibutton_gpio); | ||||
|     worker->slave = onewire_slave_alloc(&ibutton_gpio); | ||||
|     worker->writer = ibutton_writer_alloc(worker->host); | ||||
|     worker->device = onewire_device_alloc(0, 0, 0, 0, 0, 0, 0, 0); | ||||
|     worker->messages = furi_message_queue_alloc(1, sizeof(iButtonMessage)); | ||||
|  | ||||
| @ -234,16 +234,13 @@ void ibutton_worker_emulate_timer_cb(void* context) { | ||||
|     furi_assert(context); | ||||
|     iButtonWorker* worker = context; | ||||
| 
 | ||||
|     LevelDuration level = | ||||
|     const LevelDuration level_duration = | ||||
|         protocol_dict_encoder_yield(worker->protocols, worker->protocol_to_encode); | ||||
| 
 | ||||
|     furi_hal_ibutton_emulate_set_next(level_duration_get_duration(level)); | ||||
|     const bool level = level_duration_get_level(level_duration); | ||||
| 
 | ||||
|     if(level_duration_get_level(level)) { | ||||
|         furi_hal_ibutton_pin_high(); | ||||
|     } else { | ||||
|         furi_hal_ibutton_pin_low(); | ||||
|     } | ||||
|     furi_hal_ibutton_emulate_set_next(level); | ||||
|     furi_hal_ibutton_pin_write(level); | ||||
| } | ||||
| 
 | ||||
| void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { | ||||
| @ -266,7 +263,7 @@ void ibutton_worker_emulate_timer_start(iButtonWorker* worker) { | ||||
|     protocol_dict_set_data(worker->protocols, worker->protocol_to_encode, key_id, key_size); | ||||
|     protocol_dict_encoder_start(worker->protocols, worker->protocol_to_encode); | ||||
| 
 | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_ibutton_pin_configure(); | ||||
|     furi_hal_ibutton_emulate_start(0, ibutton_worker_emulate_timer_cb, worker); | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -1,18 +1,19 @@ | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #include "one_wire_host.h" | ||||
| #include "one_wire_host_timing.h" | ||||
| 
 | ||||
| struct OneWireHost { | ||||
|     // global search state
 | ||||
|     unsigned char saved_rom[8]; | ||||
|     const GpioPin* gpio_pin; | ||||
|     unsigned char saved_rom[8]; /** < global search state */ | ||||
|     uint8_t last_discrepancy; | ||||
|     uint8_t last_family_discrepancy; | ||||
|     bool last_device_flag; | ||||
| }; | ||||
| 
 | ||||
| OneWireHost* onewire_host_alloc() { | ||||
| OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin) { | ||||
|     OneWireHost* host = malloc(sizeof(OneWireHost)); | ||||
|     host->gpio_pin = gpio_pin; | ||||
|     onewire_host_reset_search(host); | ||||
|     return host; | ||||
| } | ||||
| @ -23,49 +24,47 @@ void onewire_host_free(OneWireHost* host) { | ||||
| } | ||||
| 
 | ||||
| bool onewire_host_reset(OneWireHost* host) { | ||||
|     UNUSED(host); | ||||
|     uint8_t r; | ||||
|     uint8_t retries = 125; | ||||
| 
 | ||||
|     // wait until the gpio is high
 | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, true); | ||||
|     do { | ||||
|         if(--retries == 0) return 0; | ||||
|         furi_delay_us(2); | ||||
|     } while(!furi_hal_ibutton_pin_get_level()); | ||||
|     } while(!furi_hal_gpio_read(host->gpio_pin)); | ||||
| 
 | ||||
|     // pre delay
 | ||||
|     furi_delay_us(OWH_RESET_DELAY_PRE); | ||||
| 
 | ||||
|     // drive low
 | ||||
|     furi_hal_ibutton_pin_low(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, false); | ||||
|     furi_delay_us(OWH_RESET_DRIVE); | ||||
| 
 | ||||
|     // release
 | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, true); | ||||
|     furi_delay_us(OWH_RESET_RELEASE); | ||||
| 
 | ||||
|     // read and post delay
 | ||||
|     r = !furi_hal_ibutton_pin_get_level(); | ||||
|     r = !furi_hal_gpio_read(host->gpio_pin); | ||||
|     furi_delay_us(OWH_RESET_DELAY_POST); | ||||
| 
 | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| bool onewire_host_read_bit(OneWireHost* host) { | ||||
|     UNUSED(host); | ||||
|     bool result; | ||||
| 
 | ||||
|     // drive low
 | ||||
|     furi_hal_ibutton_pin_low(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, false); | ||||
|     furi_delay_us(OWH_READ_DRIVE); | ||||
| 
 | ||||
|     // release
 | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, true); | ||||
|     furi_delay_us(OWH_READ_RELEASE); | ||||
| 
 | ||||
|     // read and post delay
 | ||||
|     result = furi_hal_ibutton_pin_get_level(); | ||||
|     result = furi_hal_gpio_read(host->gpio_pin); | ||||
|     furi_delay_us(OWH_READ_DELAY_POST); | ||||
| 
 | ||||
|     return result; | ||||
| @ -90,22 +89,21 @@ void onewire_host_read_bytes(OneWireHost* host, uint8_t* buffer, uint16_t count) | ||||
| } | ||||
| 
 | ||||
| void onewire_host_write_bit(OneWireHost* host, bool value) { | ||||
|     UNUSED(host); | ||||
|     if(value) { | ||||
|         // drive low
 | ||||
|         furi_hal_ibutton_pin_low(); | ||||
|         furi_hal_gpio_write(host->gpio_pin, false); | ||||
|         furi_delay_us(OWH_WRITE_1_DRIVE); | ||||
| 
 | ||||
|         // release
 | ||||
|         furi_hal_ibutton_pin_high(); | ||||
|         furi_hal_gpio_write(host->gpio_pin, true); | ||||
|         furi_delay_us(OWH_WRITE_1_RELEASE); | ||||
|     } else { | ||||
|         // drive low
 | ||||
|         furi_hal_ibutton_pin_low(); | ||||
|         furi_hal_gpio_write(host->gpio_pin, false); | ||||
|         furi_delay_us(OWH_WRITE_0_DRIVE); | ||||
| 
 | ||||
|         // release
 | ||||
|         furi_hal_ibutton_pin_high(); | ||||
|         furi_hal_gpio_write(host->gpio_pin, true); | ||||
|         furi_delay_us(OWH_WRITE_0_RELEASE); | ||||
|     } | ||||
| } | ||||
| @ -123,13 +121,13 @@ void onewire_host_skip(OneWireHost* host) { | ||||
| } | ||||
| 
 | ||||
| void onewire_host_start(OneWireHost* host) { | ||||
|     UNUSED(host); | ||||
|     furi_hal_ibutton_start_drive(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, true); | ||||
|     furi_hal_gpio_init(host->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void onewire_host_stop(OneWireHost* host) { | ||||
|     UNUSED(host); | ||||
|     furi_hal_ibutton_stop(); | ||||
|     furi_hal_gpio_write(host->gpio_pin, true); | ||||
|     furi_hal_gpio_init(host->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void onewire_host_reset_search(OneWireHost* host) { | ||||
| @ -150,7 +148,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code) { | ||||
|     host->last_device_flag = false; | ||||
| } | ||||
| 
 | ||||
| uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode) { | ||||
| uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode) { | ||||
|     uint8_t id_bit_number; | ||||
|     uint8_t last_zero, rom_byte_number, search_result; | ||||
|     uint8_t id_bit, cmp_id_bit; | ||||
| @ -259,7 +257,7 @@ uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSear | ||||
|         host->last_family_discrepancy = 0; | ||||
|         search_result = false; | ||||
|     } else { | ||||
|         for(int i = 0; i < 8; i++) newAddr[i] = host->saved_rom[i]; | ||||
|         for(int i = 0; i < 8; i++) new_addr[i] = host->saved_rom[i]; | ||||
|     } | ||||
| 
 | ||||
|     return search_result; | ||||
|  | ||||
| @ -22,10 +22,10 @@ typedef struct OneWireHost OneWireHost; | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocate onewire host bus | ||||
|  * @param gpio  | ||||
|  * @param pin | ||||
|  * @return OneWireHost*  | ||||
|  */ | ||||
| OneWireHost* onewire_host_alloc(); | ||||
| OneWireHost* onewire_host_alloc(const GpioPin* gpio_pin); | ||||
| 
 | ||||
| /**
 | ||||
|  * Deallocate onewire host bus | ||||
| @ -114,7 +114,7 @@ void onewire_host_target_search(OneWireHost* host, uint8_t family_code); | ||||
|  * @param mode  | ||||
|  * @return uint8_t  | ||||
|  */ | ||||
| uint8_t onewire_host_search(OneWireHost* host, uint8_t* newAddr, OneWireHostSearchMode mode); | ||||
| uint8_t onewire_host_search(OneWireHost* host, uint8_t* new_addr, OneWireHostSearchMode mode); | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| } | ||||
|  | ||||
| @ -27,6 +27,7 @@ typedef enum { | ||||
| } OneWireSlaveError; | ||||
| 
 | ||||
| struct OneWireSlave { | ||||
|     const GpioPin* gpio_pin; | ||||
|     OneWireSlaveError error; | ||||
|     OneWireDevice* device; | ||||
|     OneWireSlaveResultCallback result_cb; | ||||
| @ -35,15 +36,15 @@ struct OneWireSlave { | ||||
| 
 | ||||
| /*********************** PRIVATE ***********************/ | ||||
| 
 | ||||
| uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { | ||||
|     UNUSED(bus); | ||||
| static uint32_t | ||||
|     onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, const bool pin_value) { | ||||
|     uint32_t start = DWT->CYCCNT; | ||||
|     uint32_t time_ticks = time * furi_hal_cortex_instructions_per_microsecond(); | ||||
|     uint32_t time_captured; | ||||
| 
 | ||||
|     do { //-V1044
 | ||||
|         time_captured = DWT->CYCCNT; | ||||
|         if(furi_hal_ibutton_pin_get_level() != pin_value) { | ||||
|         if(furi_hal_gpio_read(bus->gpio_pin) != pin_value) { | ||||
|             uint32_t remaining_time = time_ticks - (time_captured - start); | ||||
|             remaining_time /= furi_hal_cortex_instructions_per_microsecond(); | ||||
|             return remaining_time; | ||||
| @ -53,14 +54,14 @@ uint32_t onewire_slave_wait_while_gpio_is(OneWireSlave* bus, uint32_t time, cons | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| bool onewire_slave_show_presence(OneWireSlave* bus) { | ||||
| static bool onewire_slave_show_presence(OneWireSlave* bus) { | ||||
|     // wait while master delay presence check
 | ||||
|     onewire_slave_wait_while_gpio_is(bus, OWS_PRESENCE_TIMEOUT, true); | ||||
| 
 | ||||
|     // show presence
 | ||||
|     furi_hal_ibutton_pin_low(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, false); | ||||
|     furi_delay_us(OWS_PRESENCE_MIN); | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
| 
 | ||||
|     // somebody also can show presence
 | ||||
|     const uint32_t wait_low_time = OWS_PRESENCE_MAX - OWS_PRESENCE_MIN; | ||||
| @ -74,7 +75,7 @@ bool onewire_slave_show_presence(OneWireSlave* bus) { | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool onewire_slave_receive_bit(OneWireSlave* bus) { | ||||
| static bool onewire_slave_receive_bit(OneWireSlave* bus) { | ||||
|     // wait while bus is low
 | ||||
|     uint32_t time = OWS_SLOT_MAX; | ||||
|     time = onewire_slave_wait_while_gpio_is(bus, time, false); | ||||
| @ -98,7 +99,7 @@ bool onewire_slave_receive_bit(OneWireSlave* bus) { | ||||
|     return (time > 0); | ||||
| } | ||||
| 
 | ||||
| bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { | ||||
| static bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { | ||||
|     const bool write_zero = !value; | ||||
| 
 | ||||
|     // wait while bus is low
 | ||||
| @ -119,7 +120,7 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { | ||||
| 
 | ||||
|     // choose write time
 | ||||
|     if(write_zero) { | ||||
|         furi_hal_ibutton_pin_low(); | ||||
|         furi_hal_gpio_write(bus->gpio_pin, false); | ||||
|         time = OWS_WRITE_ZERO; | ||||
|     } else { | ||||
|         time = OWS_READ_MAX; | ||||
| @ -127,12 +128,12 @@ bool onewire_slave_send_bit(OneWireSlave* bus, bool value) { | ||||
| 
 | ||||
|     // hold line for ZERO or ONE time
 | ||||
|     furi_delay_us(time); | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void onewire_slave_cmd_search_rom(OneWireSlave* bus) { | ||||
| static void onewire_slave_cmd_search_rom(OneWireSlave* bus) { | ||||
|     const uint8_t key_bytes = 8; | ||||
|     uint8_t* key = onewire_device_get_id_p(bus->device); | ||||
| 
 | ||||
| @ -151,7 +152,7 @@ void onewire_slave_cmd_search_rom(OneWireSlave* bus) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { | ||||
| static bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { | ||||
|     uint8_t cmd; | ||||
|     onewire_slave_receive(bus, &cmd, 1); | ||||
| 
 | ||||
| @ -178,14 +179,14 @@ bool onewire_slave_receive_and_process_cmd(OneWireSlave* bus) { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool onewire_slave_bus_start(OneWireSlave* bus) { | ||||
| static bool onewire_slave_bus_start(OneWireSlave* bus) { | ||||
|     bool result = true; | ||||
| 
 | ||||
|     if(bus->device == NULL) { | ||||
|         result = false; | ||||
|     } else { | ||||
|         FURI_CRITICAL_ENTER(); | ||||
|         furi_hal_ibutton_start_drive_in_isr(); | ||||
|         furi_hal_gpio_init(bus->gpio_pin, GpioModeOutputOpenDrain, GpioPullNo, GpioSpeedLow); | ||||
|         bus->error = NO_ERROR; | ||||
| 
 | ||||
|         if(onewire_slave_show_presence(bus)) { | ||||
| @ -197,7 +198,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { | ||||
|             result = false; | ||||
|         } | ||||
| 
 | ||||
|         furi_hal_ibutton_start_interrupt_in_isr(); | ||||
|         furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||
|         FURI_CRITICAL_EXIT(); | ||||
|     } | ||||
| 
 | ||||
| @ -207,7 +208,7 @@ bool onewire_slave_bus_start(OneWireSlave* bus) { | ||||
| static void exti_cb(void* context) { | ||||
|     OneWireSlave* bus = context; | ||||
| 
 | ||||
|     volatile bool input_state = furi_hal_ibutton_pin_get_level(); | ||||
|     volatile bool input_state = furi_hal_gpio_read(bus->gpio_pin); | ||||
|     static uint32_t pulse_start = 0; | ||||
| 
 | ||||
|     if(input_state) { | ||||
| @ -234,8 +235,9 @@ static void exti_cb(void* context) { | ||||
| 
 | ||||
| /*********************** PUBLIC ***********************/ | ||||
| 
 | ||||
| OneWireSlave* onewire_slave_alloc() { | ||||
| OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin) { | ||||
|     OneWireSlave* bus = malloc(sizeof(OneWireSlave)); | ||||
|     bus->gpio_pin = gpio_pin; | ||||
|     bus->error = NO_ERROR; | ||||
|     bus->device = NULL; | ||||
|     bus->result_cb = NULL; | ||||
| @ -249,14 +251,15 @@ void onewire_slave_free(OneWireSlave* bus) { | ||||
| } | ||||
| 
 | ||||
| void onewire_slave_start(OneWireSlave* bus) { | ||||
|     furi_hal_ibutton_add_interrupt(exti_cb, bus); | ||||
|     furi_hal_ibutton_start_interrupt(); | ||||
|     furi_hal_gpio_add_int_callback(bus->gpio_pin, exti_cb, bus); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
|     furi_hal_gpio_init(bus->gpio_pin, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedLow); | ||||
| } | ||||
| 
 | ||||
| void onewire_slave_stop(OneWireSlave* bus) { | ||||
|     UNUSED(bus); | ||||
|     furi_hal_ibutton_stop(); | ||||
|     furi_hal_ibutton_remove_interrupt(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
|     furi_hal_gpio_init(bus->gpio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_remove_int_callback(bus->gpio_pin); | ||||
| } | ||||
| 
 | ||||
| void onewire_slave_attach(OneWireSlave* bus, OneWireDevice* device) { | ||||
| @ -282,7 +285,7 @@ void onewire_slave_set_result_callback( | ||||
| bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t data_length) { | ||||
|     uint8_t bytes_sent = 0; | ||||
| 
 | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
| 
 | ||||
|     // bytes loop
 | ||||
|     for(; bytes_sent < data_length; ++bytes_sent) { | ||||
| @ -304,7 +307,7 @@ bool onewire_slave_send(OneWireSlave* bus, const uint8_t* address, const uint8_t | ||||
| bool onewire_slave_receive(OneWireSlave* bus, uint8_t* data, const uint8_t data_length) { | ||||
|     uint8_t bytes_received = 0; | ||||
| 
 | ||||
|     furi_hal_ibutton_pin_high(); | ||||
|     furi_hal_gpio_write(bus->gpio_pin, true); | ||||
| 
 | ||||
|     for(; bytes_received < data_length; ++bytes_received) { | ||||
|         uint8_t value = 0; | ||||
|  | ||||
| @ -19,10 +19,10 @@ typedef void (*OneWireSlaveResultCallback)(void* context); | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocate onewire slave | ||||
|  * @param pin  | ||||
|  * @param gpio_pin | ||||
|  * @return OneWireSlave*  | ||||
|  */ | ||||
| OneWireSlave* onewire_slave_alloc(); | ||||
| OneWireSlave* onewire_slave_alloc(const GpioPin* gpio_pin); | ||||
| 
 | ||||
| /**
 | ||||
|  * Free onewire slave | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Georgii Surkov
						Georgii Surkov