[AVR_ISP]: add AVR ISP Programmer FAP (#2475)
* [AVR_ISP]: add AVR ISP Programmer FAP * [AVR_ISP]: add auto detect AVR chip * [AVR_ISP]: fix auto detect chip * [AVR_ISP]: fix fast write flash * AVR_ISP: auto set SPI speed * AVR_ISP: add clock 4Mhz on &gpio_ext_pa4 * AVR_ISP: fix "[CRASH][ISR 4] NULL pointer dereference" with no AVR chip connected * AVR_ISP: add AVR ISP Reader * AVR_ISP: add read and check I32HEX file * AVR_ISP: add write eerom, flash, fuse, lock byte * AVR_ISP: add gui Reader, Writer * Github: unshallow on decontamination * AVR_ISP: move to external * API: fix api_symbols * AVR_ISP: add wiring scene * GUI: model mutex FuriMutexTypeNormal -> FuriMutexTypeRecursive * AVR_ISP: add chip_detect view * AVR_ISP: refactoring gui ISP Programmer * AVR_ISP: add gui "Dump AVR" * AVR_ISP: add gui "Flash AVR" * AVR_ISP: fix navigation gui * GUI: model mutex FuriMutexTypeRecursive -> FuriMutexTypeNormal * AVR_ISP: fix conflicts * AVR_ISP: fix build * AVR_ISP: delete images * AVR_ISP: add images * AVR_ISP: fix gui * AVR_ISP: fix stuck in navigation * AVR_ISP: changing the Fuse bit recording logic * AVR_ISP: fix read/write chips with memory greater than 64Kb * AVR_ISP: fix auto set speed SPI * AVR_ISP: fix gui * ISP: switching on +5 volts to an external GPIO Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
							
								
								
									
										17
									
								
								applications/external/avr_isp_programmer/application.fam
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,17 @@ | ||||
| App( | ||||
|     appid="avr_isp", | ||||
|     name="AVR Flasher", | ||||
|     apptype=FlipperAppType.EXTERNAL, | ||||
|     entry_point="avr_isp_app", | ||||
|     requires=["gui"], | ||||
|     stack_size=4 * 1024, | ||||
|     order=20, | ||||
|     fap_icon="avr_app_icon_10x10.png", | ||||
|     fap_category="GPIO", | ||||
|     fap_icon_assets="images", | ||||
|     fap_private_libs=[ | ||||
|         Lib( | ||||
|             name="driver", | ||||
|         ), | ||||
|     ], | ||||
| ) | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/avr_app_icon_10x10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										179
									
								
								applications/external/avr_isp_programmer/avr_isp_app.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,179 @@ | ||||
| #include "avr_isp_app_i.h" | ||||
| 
 | ||||
| static bool avr_isp_app_custom_event_callback(void* context, uint32_t event) { | ||||
|     furi_assert(context); | ||||
|     AvrIspApp* app = context; | ||||
|     return scene_manager_handle_custom_event(app->scene_manager, event); | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_app_back_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspApp* app = context; | ||||
|     return scene_manager_handle_back_event(app->scene_manager); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_app_tick_event_callback(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspApp* app = context; | ||||
|     scene_manager_handle_tick_event(app->scene_manager); | ||||
| } | ||||
| 
 | ||||
| AvrIspApp* avr_isp_app_alloc() { | ||||
|     AvrIspApp* app = malloc(sizeof(AvrIspApp)); | ||||
| 
 | ||||
|     app->file_path = furi_string_alloc(); | ||||
|     furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); | ||||
|     app->error = AvrIspErrorNoError; | ||||
| 
 | ||||
|     // GUI
 | ||||
|     app->gui = furi_record_open(RECORD_GUI); | ||||
| 
 | ||||
|     // View Dispatcher
 | ||||
|     app->view_dispatcher = view_dispatcher_alloc(); | ||||
|     app->scene_manager = scene_manager_alloc(&avr_isp_scene_handlers, app); | ||||
|     view_dispatcher_enable_queue(app->view_dispatcher); | ||||
| 
 | ||||
|     view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||||
|     view_dispatcher_set_custom_event_callback( | ||||
|         app->view_dispatcher, avr_isp_app_custom_event_callback); | ||||
|     view_dispatcher_set_navigation_event_callback( | ||||
|         app->view_dispatcher, avr_isp_app_back_event_callback); | ||||
|     view_dispatcher_set_tick_event_callback( | ||||
|         app->view_dispatcher, avr_isp_app_tick_event_callback, 100); | ||||
| 
 | ||||
|     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||
| 
 | ||||
|     // Open Notification record
 | ||||
|     app->notifications = furi_record_open(RECORD_NOTIFICATION); | ||||
| 
 | ||||
|     // SubMenu
 | ||||
|     app->submenu = submenu_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, AvrIspViewSubmenu, submenu_get_view(app->submenu)); | ||||
| 
 | ||||
|     // Widget
 | ||||
|     app->widget = widget_alloc(); | ||||
|     view_dispatcher_add_view(app->view_dispatcher, AvrIspViewWidget, widget_get_view(app->widget)); | ||||
| 
 | ||||
|     // Text Input
 | ||||
|     app->text_input = text_input_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, AvrIspViewTextInput, text_input_get_view(app->text_input)); | ||||
| 
 | ||||
|     // Popup
 | ||||
|     app->popup = popup_alloc(); | ||||
|     view_dispatcher_add_view(app->view_dispatcher, AvrIspViewPopup, popup_get_view(app->popup)); | ||||
| 
 | ||||
|     //Dialog
 | ||||
|     app->dialogs = furi_record_open(RECORD_DIALOGS); | ||||
| 
 | ||||
|     // Programmer view
 | ||||
|     app->avr_isp_programmer_view = avr_isp_programmer_view_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, | ||||
|         AvrIspViewProgrammer, | ||||
|         avr_isp_programmer_view_get_view(app->avr_isp_programmer_view)); | ||||
| 
 | ||||
|     // Reader view
 | ||||
|     app->avr_isp_reader_view = avr_isp_reader_view_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, | ||||
|         AvrIspViewReader, | ||||
|         avr_isp_reader_view_get_view(app->avr_isp_reader_view)); | ||||
| 
 | ||||
|     // Writer view
 | ||||
|     app->avr_isp_writer_view = avr_isp_writer_view_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, | ||||
|         AvrIspViewWriter, | ||||
|         avr_isp_writer_view_get_view(app->avr_isp_writer_view)); | ||||
| 
 | ||||
|     // Chip detect view
 | ||||
|     app->avr_isp_chip_detect_view = avr_isp_chip_detect_view_alloc(); | ||||
|     view_dispatcher_add_view( | ||||
|         app->view_dispatcher, | ||||
|         AvrIspViewChipDetect, | ||||
|         avr_isp_chip_detect_view_get_view(app->avr_isp_chip_detect_view)); | ||||
| 
 | ||||
|     // Enable 5v power, multiple attempts to avoid issues with power chip protection false triggering
 | ||||
|     uint8_t attempts = 0; | ||||
|     while(!furi_hal_power_is_otg_enabled() && attempts++ < 5) { | ||||
|         furi_hal_power_enable_otg(); | ||||
|         furi_delay_ms(10); | ||||
|     } | ||||
| 
 | ||||
|     scene_manager_next_scene(app->scene_manager, AvrIspSceneStart); | ||||
| 
 | ||||
|     return app; | ||||
| } //-V773
 | ||||
| 
 | ||||
| void avr_isp_app_free(AvrIspApp* app) { | ||||
|     furi_assert(app); | ||||
| 
 | ||||
|     // Disable 5v power
 | ||||
|     if(furi_hal_power_is_otg_enabled()) { | ||||
|         furi_hal_power_disable_otg(); | ||||
|     } | ||||
| 
 | ||||
|     // Submenu
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewSubmenu); | ||||
|     submenu_free(app->submenu); | ||||
| 
 | ||||
|     //  Widget
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWidget); | ||||
|     widget_free(app->widget); | ||||
| 
 | ||||
|     // TextInput
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewTextInput); | ||||
|     text_input_free(app->text_input); | ||||
| 
 | ||||
|     // Popup
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewPopup); | ||||
|     popup_free(app->popup); | ||||
| 
 | ||||
|     //Dialog
 | ||||
|     furi_record_close(RECORD_DIALOGS); | ||||
| 
 | ||||
|     // Programmer view
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewProgrammer); | ||||
|     avr_isp_programmer_view_free(app->avr_isp_programmer_view); | ||||
| 
 | ||||
|     // Reader view
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewReader); | ||||
|     avr_isp_reader_view_free(app->avr_isp_reader_view); | ||||
| 
 | ||||
|     // Writer view
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewWriter); | ||||
|     avr_isp_writer_view_free(app->avr_isp_writer_view); | ||||
| 
 | ||||
|     // Chip detect view
 | ||||
|     view_dispatcher_remove_view(app->view_dispatcher, AvrIspViewChipDetect); | ||||
|     avr_isp_chip_detect_view_free(app->avr_isp_chip_detect_view); | ||||
| 
 | ||||
|     // View dispatcher
 | ||||
|     view_dispatcher_free(app->view_dispatcher); | ||||
|     scene_manager_free(app->scene_manager); | ||||
| 
 | ||||
|     // Notifications
 | ||||
|     furi_record_close(RECORD_NOTIFICATION); | ||||
|     app->notifications = NULL; | ||||
| 
 | ||||
|     // Close records
 | ||||
|     furi_record_close(RECORD_GUI); | ||||
| 
 | ||||
|     // Path strings
 | ||||
|     furi_string_free(app->file_path); | ||||
| 
 | ||||
|     free(app); | ||||
| } | ||||
| 
 | ||||
| int32_t avr_isp_app(void* p) { | ||||
|     UNUSED(p); | ||||
|     AvrIspApp* avr_isp_app = avr_isp_app_alloc(); | ||||
| 
 | ||||
|     view_dispatcher_run(avr_isp_app->view_dispatcher); | ||||
| 
 | ||||
|     avr_isp_app_free(avr_isp_app); | ||||
| 
 | ||||
|     return 0; | ||||
| } | ||||
							
								
								
									
										31
									
								
								applications/external/avr_isp_programmer/avr_isp_app_i.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,31 @@ | ||||
| #include "avr_isp_app_i.h" | ||||
| #include <lib/toolbox/path.h> | ||||
| #include <flipper_format/flipper_format_i.h> | ||||
| 
 | ||||
| #define TAG "AvrIsp" | ||||
| 
 | ||||
| bool avr_isp_load_from_file(AvrIspApp* app) { | ||||
|     furi_assert(app); | ||||
| 
 | ||||
|     FuriString* file_path = furi_string_alloc(); | ||||
|     FuriString* file_name = furi_string_alloc(); | ||||
| 
 | ||||
|     DialogsFileBrowserOptions browser_options; | ||||
|     dialog_file_browser_set_basic_options( | ||||
|         &browser_options, AVR_ISP_APP_EXTENSION, &I_avr_app_icon_10x10); | ||||
|     browser_options.base_path = STORAGE_APP_DATA_PATH_PREFIX; | ||||
| 
 | ||||
|     // Input events and views are managed by file_select
 | ||||
|     bool res = dialog_file_browser_show(app->dialogs, file_path, app->file_path, &browser_options); | ||||
| 
 | ||||
|     if(res) { | ||||
|         path_extract_dirname(furi_string_get_cstr(file_path), app->file_path); | ||||
|         path_extract_filename(file_path, file_name, true); | ||||
|         strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); | ||||
|     } | ||||
| 
 | ||||
|     furi_string_free(file_name); | ||||
|     furi_string_free(file_path); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
							
								
								
									
										44
									
								
								applications/external/avr_isp_programmer/avr_isp_app_i.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "helpers/avr_isp_types.h" | ||||
| #include <avr_isp_icons.h> | ||||
| 
 | ||||
| #include "scenes/avr_isp_scene.h" | ||||
| #include <gui/gui.h> | ||||
| #include <gui/view_dispatcher.h> | ||||
| #include <gui/scene_manager.h> | ||||
| #include <gui/modules/submenu.h> | ||||
| #include <gui/modules/widget.h> | ||||
| #include <notification/notification_messages.h> | ||||
| #include <gui/modules/text_input.h> | ||||
| #include <dialogs/dialogs.h> | ||||
| #include <storage/storage.h> | ||||
| #include <gui/modules/popup.h> | ||||
| 
 | ||||
| #include "views/avr_isp_view_programmer.h" | ||||
| #include "views/avr_isp_view_reader.h" | ||||
| #include "views/avr_isp_view_writer.h" | ||||
| #include "views/avr_isp_view_chip_detect.h" | ||||
| 
 | ||||
| #define AVR_ISP_MAX_LEN_NAME 64 | ||||
| 
 | ||||
| typedef struct { | ||||
|     Gui* gui; | ||||
|     ViewDispatcher* view_dispatcher; | ||||
|     SceneManager* scene_manager; | ||||
|     NotificationApp* notifications; | ||||
|     DialogsApp* dialogs; | ||||
|     Popup* popup; | ||||
|     Submenu* submenu; | ||||
|     Widget* widget; | ||||
|     TextInput* text_input; | ||||
|     FuriString* file_path; | ||||
|     char file_name_tmp[AVR_ISP_MAX_LEN_NAME]; | ||||
|     AvrIspProgrammerView* avr_isp_programmer_view; | ||||
|     AvrIspReaderView* avr_isp_reader_view; | ||||
|     AvrIspWriterView* avr_isp_writer_view; | ||||
|     AvrIspChipDetectView* avr_isp_chip_detect_view; | ||||
|     AvrIspError error; | ||||
| } AvrIspApp; | ||||
| 
 | ||||
| bool avr_isp_load_from_file(AvrIspApp* app); | ||||
							
								
								
									
										490
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,490 @@ | ||||
| #include "avr_isp.h" | ||||
| #include "../lib/driver/avr_isp_prog_cmd.h" | ||||
| #include "../lib/driver/avr_isp_spi_sw.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||
| #define TAG "AvrIsp" | ||||
| 
 | ||||
| struct AvrIsp { | ||||
|     AvrIspSpiSw* spi; | ||||
|     bool pmode; | ||||
|     AvrIspCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| AvrIsp* avr_isp_alloc(void) { | ||||
|     AvrIsp* instance = malloc(sizeof(AvrIsp)); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_free(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(instance->spi) avr_isp_end_pmode(instance); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_spi_transaction( | ||||
|     AvrIsp* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||
|     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_set_pmode(AvrIsp* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t res = 0; | ||||
|     avr_isp_spi_sw_txrx(instance->spi, a); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, b); | ||||
|     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, d); | ||||
|     return res == 0x53; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_end_pmode(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_spi_sw_res_set(instance->spi, true); | ||||
|         // We're about to take the target out of reset
 | ||||
|         // so configure SPI pins as input
 | ||||
|         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|         instance->spi = NULL; | ||||
|     } | ||||
| 
 | ||||
|     instance->pmode = false; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_start_pmode(AvrIsp* instance, AvrIspSpiSwSpeed spi_speed) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||
| 
 | ||||
|     // SPI.begin() will configure SS as output,
 | ||||
|     // so SPI master mode is selected.
 | ||||
|     // We have defined RESET as pin 10,
 | ||||
|     // which for many arduino's is not the SS pin.
 | ||||
|     // So we have to configure RESET as output here,
 | ||||
|     // (reset_target() first sets the correct level)
 | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||
| 
 | ||||
|     avr_isp_spi_sw_res_set(instance->spi, false); | ||||
|     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||
| 
 | ||||
|     // Pulse RESET after PIN_SCK is low:
 | ||||
|     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||
| 
 | ||||
|     // discharge PIN_SCK, value arbitrally chosen
 | ||||
|     furi_delay_ms(20); | ||||
|     avr_isp_spi_sw_res_set(instance->spi, true); | ||||
| 
 | ||||
|     // Pulse must be minimum 2 target CPU speed cycles
 | ||||
|     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||
|     furi_delay_ms(1); | ||||
| 
 | ||||
|     avr_isp_spi_sw_res_set(instance->spi, false); | ||||
| 
 | ||||
|     // Send the enable programming command:
 | ||||
|     // datasheet: must be > 20 msec
 | ||||
|     furi_delay_ms(50); | ||||
|     if(avr_isp_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||
|         instance->pmode = true; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     AvrIspSpiSwSpeed spi_speed[] = { | ||||
|         AvrIspSpiSwSpeed1Mhz, | ||||
|         AvrIspSpiSwSpeed400Khz, | ||||
|         AvrIspSpiSwSpeed250Khz, | ||||
|         AvrIspSpiSwSpeed125Khz, | ||||
|         AvrIspSpiSwSpeed60Khz, | ||||
|         AvrIspSpiSwSpeed40Khz, | ||||
|         AvrIspSpiSwSpeed20Khz, | ||||
|         AvrIspSpiSwSpeed10Khz, | ||||
|         AvrIspSpiSwSpeed5Khz, | ||||
|         AvrIspSpiSwSpeed1Khz, | ||||
|     }; | ||||
|     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||
|         if(avr_isp_start_pmode(instance, spi_speed[i])) { | ||||
|             AvrIspSignature sig = avr_isp_read_signature(instance); | ||||
|             AvrIspSignature sig_examination = avr_isp_read_signature(instance); //-V656
 | ||||
|             uint8_t y = 0; | ||||
|             while(y < 8) { | ||||
|                 if(memcmp((uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspSignature)) != | ||||
|                    0) | ||||
|                     break; | ||||
|                 sig_examination = avr_isp_read_signature(instance); | ||||
|                 y++; | ||||
|             } | ||||
|             if(y == 8) { | ||||
|                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||
|                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||
|                         avr_isp_end_pmode(instance); | ||||
|                         i++; | ||||
|                         return avr_isp_start_pmode(instance, spi_speed[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_commit(AvrIsp* instance, uint16_t addr, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||
|     /* polling flash */ | ||||
|     if(data == 0xFF) { | ||||
|         furi_delay_ms(5); | ||||
|     } else { | ||||
|         /* polling flash */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint16_t avr_isp_current_page(AvrIsp* instance, uint32_t addr, uint16_t page_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint16_t page = 0; | ||||
|     switch(page_size) { | ||||
|     case 32: | ||||
|         page = addr & 0xFFFFFFF0; | ||||
|         break; | ||||
|     case 64: | ||||
|         page = addr & 0xFFFFFFE0; | ||||
|         break; | ||||
|     case 128: | ||||
|         page = addr & 0xFFFFFFC0; | ||||
|         break; | ||||
|     case 256: | ||||
|         page = addr & 0xFFFFFF80; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         page = addr; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_flash_write_pages( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     size_t x = 0; | ||||
|     uint16_t page = avr_isp_current_page(instance, addr, page_size); | ||||
| 
 | ||||
|     while(x < data_size) { | ||||
|         if(page != avr_isp_current_page(instance, addr, page_size)) { | ||||
|             avr_isp_commit(instance, page, data[x - 1]); | ||||
|             page = avr_isp_current_page(instance, addr, page_size); | ||||
|         } | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_LO(addr, data[x++])); | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FLASH_HI(addr, data[x++])); | ||||
|         addr++; | ||||
|     } | ||||
|     avr_isp_commit(instance, page, data[x - 1]); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_erase_chip(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(!instance->pmode) avr_isp_auto_set_spi_speed_start_pmode(instance); | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_ERASE_CHIP); | ||||
|         furi_delay_ms(100); | ||||
|         avr_isp_end_pmode(instance); | ||||
|         ret = true; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
|     avr_isp_eeprom_write(AvrIsp* instance, uint16_t addr, uint8_t* data, uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     for(uint16_t i = 0; i < data_size; i++) { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, data[i])); | ||||
|         furi_delay_ms(10); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint32_t mem_size, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     switch(mem_type) { | ||||
|     case STK_SET_FLASH_TYPE: | ||||
|         if((addr + data_size / 2) <= mem_size) { | ||||
|             ret = avr_isp_flash_write_pages(instance, addr, page_size, data, data_size); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     case STK_SET_EEPROM_TYPE: | ||||
|         if((addr + data_size) <= mem_size) { | ||||
|             ret = avr_isp_eeprom_write(instance, addr, data, data_size); | ||||
|         } | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         furi_crash(TAG " Incorrect mem type."); | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_flash_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(page_size > data_size) return false; | ||||
|     for(uint16_t i = 0; i < page_size; i += 2) { | ||||
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(addr)); | ||||
|         data[i + 1] = avr_isp_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_eeprom_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     if(page_size > data_size) return false; | ||||
|     for(uint16_t i = 0; i < page_size; i++) { | ||||
|         data[i] = avr_isp_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr)); | ||||
|         addr++; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool res = false; | ||||
|     if(mem_type == STK_SET_FLASH_TYPE) | ||||
|         res = avr_isp_flash_read_page(instance, addr, page_size, data, data_size); | ||||
|     if(mem_type == STK_SET_EEPROM_TYPE) | ||||
|         res = avr_isp_eeprom_read_page(instance, addr, page_size, data, data_size); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| AvrIspSignature avr_isp_read_signature(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     AvrIspSignature signature; | ||||
|     signature.vendor = avr_isp_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||
|     signature.part_family = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||
|     signature.part_number = avr_isp_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||
|     return signature; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_lock_byte(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_lock_byte(instance) == lock) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_LOCK_BYTE(lock)); | ||||
|         /* polling lock byte */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_LOCK_BYTE) == lock) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_low(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_low(instance) == lfuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_LOW(lfuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_LOW) == lfuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_high(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_high(instance) == hfuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_HIGH(hfuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_HIGH) == hfuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_extended(AvrIsp* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint8_t data = 0; | ||||
|     uint32_t starttime = furi_get_tick(); | ||||
|     while((furi_get_tick() - starttime) < 300) { | ||||
|         data = avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED); | ||||
|         if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == data) { | ||||
|             break; | ||||
|         }; | ||||
|         data = 0x00; | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     bool ret = false; | ||||
|     if(avr_isp_read_fuse_extended(instance) == efuse) { | ||||
|         ret = true; | ||||
|     } else { | ||||
|         avr_isp_spi_transaction(instance, AVR_ISP_WRITE_FUSE_EXTENDED(efuse)); | ||||
|         /* polling fuse */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_spi_transaction(instance, AVR_ISP_READ_FUSE_EXTENDED) == efuse) { | ||||
|                 ret = true; | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_transaction(instance, AVR_ISP_EXTENDED_ADDR(extended_addr)); | ||||
|     furi_delay_ms(10); | ||||
| } | ||||
							
								
								
									
										70
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,70 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIsp AvrIsp; | ||||
| typedef void (*AvrIspCallback)(void* context); | ||||
| 
 | ||||
| struct AvrIspSignature { | ||||
|     uint8_t vendor; | ||||
|     uint8_t part_family; | ||||
|     uint8_t part_number; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspSignature AvrIspSignature; | ||||
| 
 | ||||
| AvrIsp* avr_isp_alloc(void); | ||||
| 
 | ||||
| void avr_isp_free(AvrIsp* instance); | ||||
| 
 | ||||
| void avr_isp_set_tx_callback(AvrIsp* instance, AvrIspCallback callback, void* context); | ||||
| 
 | ||||
| bool avr_isp_auto_set_spi_speed_start_pmode(AvrIsp* instance); | ||||
| 
 | ||||
| AvrIspSignature avr_isp_read_signature(AvrIsp* instance); | ||||
| 
 | ||||
| void avr_isp_end_pmode(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_erase_chip(AvrIsp* instance); | ||||
| 
 | ||||
| uint8_t avr_isp_spi_transaction( | ||||
|     AvrIsp* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data); | ||||
| 
 | ||||
| bool avr_isp_read_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t memtype, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| bool avr_isp_write_page( | ||||
|     AvrIsp* instance, | ||||
|     uint32_t mem_type, | ||||
|     uint32_t mem_size, | ||||
|     uint16_t addr, | ||||
|     uint16_t page_size, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| uint8_t avr_isp_read_lock_byte(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_lock_byte(AvrIsp* instance, uint8_t lock); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_low(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_low(AvrIsp* instance, uint8_t lfuse); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_high(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_high(AvrIsp* instance, uint8_t hfuse); | ||||
| 
 | ||||
| uint8_t avr_isp_read_fuse_extended(AvrIsp* instance); | ||||
| 
 | ||||
| bool avr_isp_write_fuse_extended(AvrIsp* instance, uint8_t efuse); | ||||
| 
 | ||||
| void avr_isp_write_extended_addr(AvrIsp* instance, uint8_t extended_addr); | ||||
							
								
								
									
										23
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_event.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,23 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| typedef enum { | ||||
|     //SubmenuIndex
 | ||||
|     SubmenuIndexAvrIspProgrammer = 10, | ||||
|     SubmenuIndexAvrIspReader, | ||||
|     SubmenuIndexAvrIspWriter, | ||||
|     SubmenuIndexAvrIsWiring, | ||||
|     SubmenuIndexAvrIspAbout, | ||||
| 
 | ||||
|     //AvrIspCustomEvent
 | ||||
|     AvrIspCustomEventSceneChipDetectOk = 100, | ||||
|     AvrIspCustomEventSceneReadingOk, | ||||
|     AvrIspCustomEventSceneWritingOk, | ||||
|     AvrIspCustomEventSceneErrorVerification, | ||||
|     AvrIspCustomEventSceneErrorReading, | ||||
|     AvrIspCustomEventSceneErrorWriting, | ||||
|     AvrIspCustomEventSceneErrorWritingFuse, | ||||
|     AvrIspCustomEventSceneInputName, | ||||
|     AvrIspCustomEventSceneSuccess, | ||||
|     AvrIspCustomEventSceneExit, | ||||
|     AvrIspCustomEventSceneExitStartMenu, | ||||
| } AvrIspCustomEvent; | ||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_types.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi.h> | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define AVR_ISP_VERSION_APP "0.1" | ||||
| #define AVR_ISP_DEVELOPED "SkorP" | ||||
| #define AVR_ISP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||
| 
 | ||||
| #define AVR_ISP_APP_FILE_VERSION 1 | ||||
| #define AVR_ISP_APP_FILE_TYPE "Flipper Dump AVR" | ||||
| #define AVR_ISP_APP_EXTENSION ".avr" | ||||
| 
 | ||||
| typedef enum { | ||||
|     //AvrIspViewVariableItemList,
 | ||||
|     AvrIspViewSubmenu, | ||||
|     AvrIspViewProgrammer, | ||||
|     AvrIspViewReader, | ||||
|     AvrIspViewWriter, | ||||
|     AvrIspViewWidget, | ||||
|     AvrIspViewPopup, | ||||
|     AvrIspViewTextInput, | ||||
|     AvrIspViewChipDetect, | ||||
| } AvrIspView; | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspErrorNoError, | ||||
|     AvrIspErrorReading, | ||||
|     AvrIspErrorWriting, | ||||
|     AvrIspErrorVerification, | ||||
|     AvrIspErrorWritingFuse, | ||||
| } AvrIspError; | ||||
							
								
								
									
										266
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,266 @@ | ||||
| #include "avr_isp_worker.h" | ||||
| #include <furi_hal_pwm.h> | ||||
| #include "../lib/driver/avr_isp_prog.h" | ||||
| #include "../lib/driver/avr_isp_prog_cmd.h" | ||||
| #include "../lib/driver/avr_isp_chip_arr.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define TAG "AvrIspWorker" | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWorkerEvtStop = (1 << 0), | ||||
| 
 | ||||
|     AvrIspWorkerEvtRx = (1 << 1), | ||||
|     AvrIspWorkerEvtTxCoplete = (1 << 2), | ||||
|     AvrIspWorkerEvtTx = (1 << 3), | ||||
|     AvrIspWorkerEvtState = (1 << 4), | ||||
| 
 | ||||
|     //AvrIspWorkerEvtCfg = (1 << 5),
 | ||||
| 
 | ||||
| } AvrIspWorkerEvt; | ||||
| 
 | ||||
| struct AvrIspWorker { | ||||
|     FuriThread* thread; | ||||
|     volatile bool worker_running; | ||||
|     uint8_t connect_usb; | ||||
|     AvrIspWorkerCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| #define AVR_ISP_WORKER_PROG_ALL_EVENTS (AvrIspWorkerEvtStop) | ||||
| #define AVR_ISP_WORKER_ALL_EVENTS                                                             \ | ||||
|     (AvrIspWorkerEvtTx | AvrIspWorkerEvtTxCoplete | AvrIspWorkerEvtRx | AvrIspWorkerEvtStop | \ | ||||
|      AvrIspWorkerEvtState) | ||||
| 
 | ||||
| //########################/* VCP CDC */#############################################
 | ||||
| #include "usb_cdc.h" | ||||
| #include <cli/cli_vcp.h> | ||||
| #include <cli/cli.h> | ||||
| #include <furi_hal_usb_cdc.h> | ||||
| 
 | ||||
| #define AVR_ISP_VCP_CDC_CH 1 | ||||
| #define AVR_ISP_VCP_CDC_PKT_LEN CDC_DATA_SZ | ||||
| #define AVR_ISP_VCP_UART_RX_BUF_SIZE (AVR_ISP_VCP_CDC_PKT_LEN * 5) | ||||
| 
 | ||||
| static void vcp_on_cdc_tx_complete(void* context); | ||||
| static void vcp_on_cdc_rx(void* context); | ||||
| static void vcp_state_callback(void* context, uint8_t state); | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state); | ||||
| static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config); | ||||
| 
 | ||||
| static const CdcCallbacks cdc_cb = { | ||||
|     vcp_on_cdc_tx_complete, | ||||
|     vcp_on_cdc_rx, | ||||
|     vcp_state_callback, | ||||
|     vcp_on_cdc_control_line, | ||||
|     vcp_on_line_config, | ||||
| }; | ||||
| 
 | ||||
| /* VCP callbacks */ | ||||
| 
 | ||||
| static void vcp_on_cdc_tx_complete(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTxCoplete); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_cdc_rx(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||
| } | ||||
| 
 | ||||
| static void vcp_state_callback(void* context, uint8_t state) { | ||||
|     UNUSED(context); | ||||
| 
 | ||||
|     AvrIspWorker* instance = context; | ||||
|     instance->connect_usb = state; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtState); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_cdc_control_line(void* context, uint8_t state) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(state); | ||||
| } | ||||
| 
 | ||||
| static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(config); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_vcp_cdc_init(void* context) { | ||||
|     furi_hal_usb_unlock(); | ||||
|     Cli* cli = furi_record_open(RECORD_CLI); | ||||
|     //close cli
 | ||||
|     cli_session_close(cli); | ||||
|     //disable callbacks VCP_CDC=0
 | ||||
|     furi_hal_cdc_set_callbacks(0, NULL, NULL); | ||||
|     //set 2 cdc
 | ||||
|     furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true); | ||||
|     //open cli VCP_CDC=0
 | ||||
|     cli_session_open(cli, &cli_vcp); | ||||
|     furi_record_close(RECORD_CLI); | ||||
| 
 | ||||
|     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, (CdcCallbacks*)&cdc_cb, context); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_vcp_cdc_deinit(void) { | ||||
|     //disable callbacks AVR_ISP_VCP_CDC_CH
 | ||||
|     furi_hal_cdc_set_callbacks(AVR_ISP_VCP_CDC_CH, NULL, NULL); | ||||
| 
 | ||||
|     Cli* cli = furi_record_open(RECORD_CLI); | ||||
|     //close cli
 | ||||
|     cli_session_close(cli); | ||||
|     furi_hal_usb_unlock(); | ||||
|     //set 1 cdc
 | ||||
|     furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true); | ||||
|     //open cli VCP_CDC=0
 | ||||
|     cli_session_open(cli, &cli_vcp); | ||||
|     furi_record_close(RECORD_CLI); | ||||
| } | ||||
| 
 | ||||
| //#################################################################################
 | ||||
| 
 | ||||
| static int32_t avr_isp_worker_prog_thread(void* context) { | ||||
|     AvrIspProg* prog = context; | ||||
|     FURI_LOG_D(TAG, "AvrIspProgWorker Start"); | ||||
|     while(1) { | ||||
|         if(furi_thread_flags_get() & AvrIspWorkerEvtStop) break; | ||||
|         avr_isp_prog_avrisp(prog); | ||||
|     } | ||||
|     FURI_LOG_D(TAG, "AvrIspProgWorker Stop"); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_worker_prog_tx_data(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWorker* instance = context; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtTx); | ||||
| } | ||||
| 
 | ||||
| /** Worker thread
 | ||||
|  *  | ||||
|  * @param context  | ||||
|  * @return exit code  | ||||
|  */ | ||||
| static int32_t avr_isp_worker_thread(void* context) { | ||||
|     AvrIspWorker* instance = context; | ||||
|     avr_isp_worker_vcp_cdc_init(instance); | ||||
| 
 | ||||
|     /* start PWM on &gpio_ext_pa4 */ | ||||
|     furi_hal_pwm_start(FuriHalPwmOutputIdLptim2PA4, 4000000, 50); | ||||
| 
 | ||||
|     AvrIspProg* prog = avr_isp_prog_init(); | ||||
|     avr_isp_prog_set_tx_callback(prog, avr_isp_worker_prog_tx_data, instance); | ||||
| 
 | ||||
|     uint8_t buf[AVR_ISP_VCP_UART_RX_BUF_SIZE]; | ||||
|     size_t len = 0; | ||||
| 
 | ||||
|     FuriThread* prog_thread = | ||||
|         furi_thread_alloc_ex("AvrIspProgWorker", 1024, avr_isp_worker_prog_thread, prog); | ||||
|     furi_thread_start(prog_thread); | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Start"); | ||||
| 
 | ||||
|     while(instance->worker_running) { | ||||
|         uint32_t events = | ||||
|             furi_thread_flags_wait(AVR_ISP_WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever); | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtRx) { | ||||
|             if(avr_isp_prog_spaces_rx(prog) >= AVR_ISP_VCP_CDC_PKT_LEN) { | ||||
|                 len = furi_hal_cdc_receive(AVR_ISP_VCP_CDC_CH, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||
|                 // for(uint8_t i = 0; i < len; i++) {
 | ||||
|                 //     FURI_LOG_I(TAG, "--> %X", buf[i]);
 | ||||
|                 // }
 | ||||
|                 avr_isp_prog_rx(prog, buf, len); | ||||
|             } else { | ||||
|                 furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtRx); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if((events & AvrIspWorkerEvtTxCoplete) || (events & AvrIspWorkerEvtTx)) { | ||||
|             len = avr_isp_prog_tx(prog, buf, AVR_ISP_VCP_CDC_PKT_LEN); | ||||
| 
 | ||||
|             // for(uint8_t i = 0; i < len; i++) {
 | ||||
|             //     FURI_LOG_I(TAG, "<-- %X", buf[i]);
 | ||||
|             // }
 | ||||
| 
 | ||||
|             if(len > 0) furi_hal_cdc_send(AVR_ISP_VCP_CDC_CH, buf, len); | ||||
|         } | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtStop) { | ||||
|             break; | ||||
|         } | ||||
| 
 | ||||
|         if(events & AvrIspWorkerEvtState) { | ||||
|             if(instance->callback) | ||||
|                 instance->callback(instance->context, (bool)instance->connect_usb); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     FURI_LOG_D(TAG, "Stop"); | ||||
| 
 | ||||
|     furi_thread_flags_set(furi_thread_get_id(prog_thread), AvrIspWorkerEvtStop); | ||||
|     avr_isp_prog_exit(prog); | ||||
|     furi_delay_ms(10); | ||||
|     furi_thread_join(prog_thread); | ||||
|     furi_thread_free(prog_thread); | ||||
| 
 | ||||
|     avr_isp_prog_free(prog); | ||||
|     furi_hal_pwm_stop(FuriHalPwmOutputIdLptim2PA4); | ||||
|     avr_isp_worker_vcp_cdc_deinit(); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| AvrIspWorker* avr_isp_worker_alloc(void* context) { | ||||
|     furi_assert(context); | ||||
|     UNUSED(context); | ||||
|     AvrIspWorker* instance = malloc(sizeof(AvrIspWorker)); | ||||
| 
 | ||||
|     instance->thread = furi_thread_alloc_ex("AvrIspWorker", 2048, avr_isp_worker_thread, instance); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_free(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     furi_check(!instance->worker_running); | ||||
|     furi_thread_free(instance->thread); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_set_callback( | ||||
|     AvrIspWorker* instance, | ||||
|     AvrIspWorkerCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_start(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(!instance->worker_running); | ||||
| 
 | ||||
|     instance->worker_running = true; | ||||
| 
 | ||||
|     furi_thread_start(instance->thread); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_worker_stop(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(instance->worker_running); | ||||
| 
 | ||||
|     instance->worker_running = false; | ||||
|     furi_thread_flags_set(furi_thread_get_id(instance->thread), AvrIspWorkerEvtStop); | ||||
| 
 | ||||
|     furi_thread_join(instance->thread); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_worker_is_running(AvrIspWorker* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->worker_running; | ||||
| } | ||||
							
								
								
									
										49
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,49 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspWorker AvrIspWorker; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerCallback)(void* context, bool connect_usb); | ||||
| 
 | ||||
| /** Allocate AvrIspWorker
 | ||||
|  *  | ||||
|  * @param context AvrIsp* context | ||||
|  * @return AvrIspWorker*  | ||||
|  */ | ||||
| AvrIspWorker* avr_isp_worker_alloc(void* context); | ||||
| 
 | ||||
| /** Free AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_free(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Callback AvrIspWorker
 | ||||
|  * | ||||
|  * @param instance AvrIspWorker instance | ||||
|  * @param callback AvrIspWorkerOverrunCallback callback | ||||
|  * @param context | ||||
|  */ | ||||
| void avr_isp_worker_set_callback( | ||||
|     AvrIspWorker* instance, | ||||
|     AvrIspWorkerCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| /** Start AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_start(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Stop AvrIspWorker
 | ||||
|  *  | ||||
|  * @param instance AvrIspWorker instance | ||||
|  */ | ||||
| void avr_isp_worker_stop(AvrIspWorker* instance); | ||||
| 
 | ||||
| /** Check if worker is running
 | ||||
|  * @param instance AvrIspWorker instance | ||||
|  * @return bool - true if running | ||||
|  */ | ||||
| bool avr_isp_worker_is_running(AvrIspWorker* instance); | ||||
							
								
								
									
										1145
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/helpers/avr_isp_worker_rw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspWorkerRW AvrIspWorkerRW; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerRWCallback)( | ||||
|     void* context, | ||||
|     const char* name, | ||||
|     bool detect_chip, | ||||
|     uint32_t flash_size); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWorkerRWStatusILDE = 0, | ||||
|     AvrIspWorkerRWStatusEndReading = 1, | ||||
|     AvrIspWorkerRWStatusEndVerification = 2, | ||||
|     AvrIspWorkerRWStatusEndWriting = 3, | ||||
|     AvrIspWorkerRWStatusEndWritingFuse = 4, | ||||
| 
 | ||||
|     AvrIspWorkerRWStatusErrorReading = (-1), | ||||
|     AvrIspWorkerRWStatusErrorVerification = (-2), | ||||
|     AvrIspWorkerRWStatusErrorWriting = (-3), | ||||
|     AvrIspWorkerRWStatusErrorWritingFuse = (-4), | ||||
| 
 | ||||
|     AvrIspWorkerRWStatusReserved = 0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||
| } AvrIspWorkerRWStatus; | ||||
| 
 | ||||
| typedef void (*AvrIspWorkerRWStatusCallback)(void* context, AvrIspWorkerRWStatus status); | ||||
| 
 | ||||
| AvrIspWorkerRW* avr_isp_worker_rw_alloc(void* context); | ||||
| 
 | ||||
| void avr_isp_worker_rw_free(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_start(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_stop(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_is_running(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| void avr_isp_worker_rw_set_callback( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     AvrIspWorkerRWCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void avr_isp_worker_rw_set_callback_status( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     AvrIspWorkerRWStatusCallback callback_status, | ||||
|     void* context_status); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_detect_chip(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| float avr_isp_worker_rw_get_progress_flash(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| float avr_isp_worker_rw_get_progress_eeprom(AvrIspWorkerRW* instance); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_read_dump( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_read_dump_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_verification( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_verification_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_check_hex( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_write_dump( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_write_dump_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| bool avr_isp_worker_rw_write_fuse( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_worker_rw_write_fuse_start( | ||||
|     AvrIspWorkerRW* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
							
								
								
									
										321
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,321 @@ | ||||
| #include "flipper_i32hex_file.h" | ||||
| #include <string.h> | ||||
| #include <storage/storage.h> | ||||
| #include <toolbox/stream/stream.h> | ||||
| #include <toolbox/stream/file_stream.h> | ||||
| #include <toolbox/hex.h> | ||||
| 
 | ||||
| //https://en.wikipedia.org/wiki/Intel_HEX
 | ||||
| 
 | ||||
| #define TAG "FlipperI32HexFile" | ||||
| 
 | ||||
| #define COUNT_BYTE_PAYLOAD 32 //how much payload will be used
 | ||||
| 
 | ||||
| #define I32HEX_TYPE_DATA 0x00 | ||||
| #define I32HEX_TYPE_END_OF_FILE 0x01 | ||||
| #define I32HEX_TYPE_EXT_LINEAR_ADDR 0x04 | ||||
| #define I32HEX_TYPE_START_LINEAR_ADDR 0x05 | ||||
| 
 | ||||
| struct FlipperI32HexFile { | ||||
|     uint32_t addr; | ||||
|     uint32_t addr_last; | ||||
|     Storage* storage; | ||||
|     Stream* stream; | ||||
|     FuriString* str_data; | ||||
|     FlipperI32HexFileStatus file_open; | ||||
| }; | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr) { | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||
|     instance->addr = start_addr; | ||||
|     instance->addr_last = 0; | ||||
|     instance->storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->stream = file_stream_alloc(instance->storage); | ||||
| 
 | ||||
|     if(file_stream_open(instance->stream, name, FSAM_WRITE, FSOM_CREATE_ALWAYS)) { | ||||
|         instance->file_open = FlipperI32HexFileStatusOpenFileWrite; | ||||
|         FURI_LOG_D(TAG, "Open write file %s", name); | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||
|         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||
|     } | ||||
|     instance->str_data = furi_string_alloc(instance->storage); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name) { | ||||
|     furi_assert(name); | ||||
| 
 | ||||
|     FlipperI32HexFile* instance = malloc(sizeof(FlipperI32HexFile)); | ||||
|     instance->addr = 0; | ||||
|     instance->addr_last = 0; | ||||
|     instance->storage = furi_record_open(RECORD_STORAGE); | ||||
|     instance->stream = file_stream_alloc(instance->storage); | ||||
| 
 | ||||
|     if(file_stream_open(instance->stream, name, FSAM_READ, FSOM_OPEN_EXISTING)) { | ||||
|         instance->file_open = FlipperI32HexFileStatusOpenFileRead; | ||||
|         FURI_LOG_D(TAG, "Open read file %s", name); | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Failed to open file %s", name); | ||||
|         instance->file_open = FlipperI32HexFileStatusErrorNoOpenFile; | ||||
|     } | ||||
|     instance->str_data = furi_string_alloc(instance->storage); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void flipper_i32hex_file_close(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     furi_string_free(instance->str_data); | ||||
|     file_stream_close(instance->stream); | ||||
|     stream_free(instance->stream); | ||||
|     furi_record_close(RECORD_STORAGE); | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||
|     } | ||||
|     uint8_t count_byte = 0; | ||||
|     uint32_t ind = 0; | ||||
|     uint8_t crc = 0; | ||||
| 
 | ||||
|     furi_string_reset(instance->str_data); | ||||
| 
 | ||||
|     if((instance->addr_last & 0xFF0000) < (instance->addr & 0xFF0000)) { | ||||
|         crc = 0x02 + 0x04 + ((instance->addr >> 24) & 0xFF) + ((instance->addr >> 16) & 0xFF); | ||||
|         crc = 0x01 + ~crc; | ||||
|         //I32HEX_TYPE_EXT_LINEAR_ADDR
 | ||||
|         furi_string_cat_printf( | ||||
|             instance->str_data, ":02000004%04lX%02X\r\n", (instance->addr >> 16), crc); | ||||
|         instance->addr_last = instance->addr; | ||||
|     } | ||||
| 
 | ||||
|     while(ind < data_size) { | ||||
|         if((ind + COUNT_BYTE_PAYLOAD) > data_size) { | ||||
|             count_byte = data_size - ind; | ||||
|         } else { | ||||
|             count_byte = COUNT_BYTE_PAYLOAD; | ||||
|         } | ||||
|         //I32HEX_TYPE_DATA
 | ||||
|         furi_string_cat_printf( | ||||
|             instance->str_data, ":%02X%04lX00", count_byte, (instance->addr & 0xFFFF)); | ||||
|         crc = count_byte + ((instance->addr >> 8) & 0xFF) + (instance->addr & 0xFF); | ||||
| 
 | ||||
|         for(uint32_t i = 0; i < count_byte; i++) { | ||||
|             furi_string_cat_printf(instance->str_data, "%02X", *data); | ||||
|             crc += *data++; | ||||
|         } | ||||
|         crc = 0x01 + ~crc; | ||||
|         furi_string_cat_printf(instance->str_data, "%02X\r\n", crc); | ||||
| 
 | ||||
|         ind += count_byte; | ||||
|         instance->addr += count_byte; | ||||
|     } | ||||
|     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileWrite) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileWrite; | ||||
|     } | ||||
|     furi_string_reset(instance->str_data); | ||||
|     //I32HEX_TYPE_END_OF_FILE
 | ||||
|     furi_string_cat_printf(instance->str_data, ":00000001FF\r\n"); | ||||
|     if(instance->file_open) stream_write_string(instance->stream, instance->str_data); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->addr = addr; | ||||
| } | ||||
| 
 | ||||
| const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return furi_string_get_cstr(instance->str_data); | ||||
| } | ||||
| 
 | ||||
| static FlipperI32HexFileRet flipper_i32hex_file_parse_line( | ||||
|     FlipperI32HexFile* instance, | ||||
|     const char* str, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     char* str1; | ||||
|     uint32_t data_wrire_ind = 0; | ||||
|     uint32_t data_len = 0; | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusErrorData, .data_size = 0}; | ||||
| 
 | ||||
|     //Search for start of data I32HEX
 | ||||
|     str1 = strstr(str, ":"); | ||||
|     do { | ||||
|         if(str1 == NULL) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorData; | ||||
|             break; | ||||
|         } | ||||
|         str1++; | ||||
|         if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorData; | ||||
|             break; | ||||
|         } | ||||
|         str1++; | ||||
|         if(++data_wrire_ind > data_size) { | ||||
|             ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||
|             break; | ||||
|         } | ||||
|         data_len = 5 + data[0]; // +5 bytes per header and crc
 | ||||
|         while(data_len > data_wrire_ind) { | ||||
|             str1++; | ||||
|             if(!hex_char_to_uint8(*str1, str1[1], data + data_wrire_ind)) { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorData; | ||||
|                 break; | ||||
|             } | ||||
|             str1++; | ||||
|             if(++data_wrire_ind > data_size) { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorOverflow; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         ret.status = FlipperI32HexFileStatusOK; | ||||
|         ret.data_size = data_wrire_ind; | ||||
| 
 | ||||
|     } while(0); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static bool flipper_i32hex_file_check_data(uint8_t* data, uint32_t data_size) { | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     uint8_t crc = 0; | ||||
|     uint32_t data_read_ind = 0; | ||||
|     if(data[0] > data_size) return false; | ||||
|     while(data_read_ind < data_size - 1) { | ||||
|         crc += data[data_read_ind++]; | ||||
|     } | ||||
|     return data[data_size - 1] == ((1 + ~crc) & 0xFF); | ||||
| } | ||||
| 
 | ||||
| static FlipperI32HexFileRet flipper_i32hex_file_parse( | ||||
|     FlipperI32HexFile* instance, | ||||
|     const char* str, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = flipper_i32hex_file_parse_line(instance, str, data, data_size); | ||||
| 
 | ||||
|     if((ret.status == FlipperI32HexFileStatusOK) && (ret.data_size > 4)) { | ||||
|         switch(data[3]) { | ||||
|         case I32HEX_TYPE_DATA: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 ret.data_size -= 5; | ||||
|                 memcpy(data, data + 4, ret.data_size); | ||||
|                 ret.status = FlipperI32HexFileStatusData; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_END_OF_FILE: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 ret.status = FlipperI32HexFileStatusEofFile; | ||||
|                 ret.data_size = 0; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_EXT_LINEAR_ADDR: | ||||
|             if(flipper_i32hex_file_check_data(data, ret.data_size)) { | ||||
|                 data[0] = data[4]; | ||||
|                 data[1] = data[5]; | ||||
|                 data[3] = 0; | ||||
|                 data[4] = 0; | ||||
|                 ret.status = FlipperI32HexFileStatusUdateAddr; | ||||
|                 ret.data_size = 4; | ||||
|             } else { | ||||
|                 ret.status = FlipperI32HexFileStatusErrorCrc; | ||||
|                 ret.data_size = 0; | ||||
|             } | ||||
|             break; | ||||
|         case I32HEX_TYPE_START_LINEAR_ADDR: | ||||
|             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||
|             ret.data_size = 0; | ||||
|             break; | ||||
|         default: | ||||
|             ret.status = FlipperI32HexFileStatusErrorUnsupportedCommand; | ||||
|             ret.data_size = 0; | ||||
|             break; | ||||
|         } | ||||
|     } else { | ||||
|         ret.status = FlipperI32HexFileStatusErrorData; | ||||
|         ret.data_size = 0; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| bool flipper_i32hex_file_check(FlipperI32HexFile* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     uint32_t data_size = 280; | ||||
|     uint8_t data[280] = {0}; | ||||
|     bool ret = true; | ||||
| 
 | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||
|         FURI_LOG_E(TAG, "File is not open"); | ||||
|         ret = false; | ||||
|     } else { | ||||
|         stream_rewind(instance->stream); | ||||
| 
 | ||||
|         while(stream_read_line(instance->stream, instance->str_data)) { | ||||
|             FlipperI32HexFileRet parse_ret = flipper_i32hex_file_parse( | ||||
|                 instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||
| 
 | ||||
|             if(parse_ret.status < 0) { | ||||
|                 ret = false; | ||||
|             } | ||||
|         } | ||||
|         stream_rewind(instance->stream); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
| 
 | ||||
|     FlipperI32HexFileRet ret = {.status = FlipperI32HexFileStatusOK, .data_size = 0}; | ||||
|     if(instance->file_open != FlipperI32HexFileStatusOpenFileRead) { | ||||
|         ret.status = FlipperI32HexFileStatusErrorFileRead; | ||||
|     } else { | ||||
|         stream_read_line(instance->stream, instance->str_data); | ||||
|         ret = flipper_i32hex_file_parse( | ||||
|             instance, furi_string_get_cstr(instance->str_data), data, data_size); | ||||
|     } | ||||
| 
 | ||||
|     return ret; | ||||
| } | ||||
							
								
								
									
										55
									
								
								applications/external/avr_isp_programmer/helpers/flipper_i32hex_file.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,55 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct FlipperI32HexFile FlipperI32HexFile; | ||||
| 
 | ||||
| typedef enum { | ||||
|     FlipperI32HexFileStatusOK = 0, | ||||
|     FlipperI32HexFileStatusData = 2, | ||||
|     FlipperI32HexFileStatusUdateAddr = 3, | ||||
|     FlipperI32HexFileStatusEofFile = 4, | ||||
|     FlipperI32HexFileStatusOpenFileWrite = 5, | ||||
|     FlipperI32HexFileStatusOpenFileRead = 6, | ||||
| 
 | ||||
|     // Errors
 | ||||
|     FlipperI32HexFileStatusErrorCrc = (-1), | ||||
|     FlipperI32HexFileStatusErrorOverflow = (-2), | ||||
|     FlipperI32HexFileStatusErrorData = (-3), | ||||
|     FlipperI32HexFileStatusErrorUnsupportedCommand = (-4), | ||||
|     FlipperI32HexFileStatusErrorNoOpenFile = (-5), | ||||
|     FlipperI32HexFileStatusErrorFileWrite = (-6), | ||||
|     FlipperI32HexFileStatusErrorFileRead = (-7), | ||||
| 
 | ||||
|     FlipperI32HexFileStatusReserved = | ||||
|         0x7FFFFFFF, ///< Prevents enum down-size compiler optimization.
 | ||||
| } FlipperI32HexFileStatus; | ||||
| 
 | ||||
| typedef struct { | ||||
|     FlipperI32HexFileStatus status; | ||||
|     uint32_t data_size; | ||||
| } FlipperI32HexFileRet; | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_write(const char* name, uint32_t start_addr); | ||||
| 
 | ||||
| FlipperI32HexFile* flipper_i32hex_file_open_read(const char* name); | ||||
| 
 | ||||
| void flipper_i32hex_file_close(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_bin_to_i32hex_set_end_line(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| const char* flipper_i32hex_file_get_string(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| void flipper_i32hex_file_bin_to_i32hex_set_addr(FlipperI32HexFile* instance, uint32_t addr); | ||||
| 
 | ||||
| bool flipper_i32hex_file_check(FlipperI32HexFile* instance); | ||||
| 
 | ||||
| FlipperI32HexFileRet flipper_i32hex_file_i32hex_to_bin_get_data( | ||||
|     FlipperI32HexFile* instance, | ||||
|     uint8_t* data, | ||||
|     uint32_t data_size); | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_app_icon_10x10.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.5 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/avr_wiring.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 4.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chif_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_error_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_long_70x22.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/chip_not_found_83x37.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/dolphin_nice_96x59.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/isp_active_128x53.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/images/link_waiting_77x56.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.8 KiB | 
							
								
								
									
										386
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,386 @@ | ||||
| #include "avr_isp_chip_arr.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| //https://github.com/avrdudes/avrdude/blob/master/src/avrintel.c
 | ||||
| 
 | ||||
| const AvrIspChipArr avr_isp_chip_arr[] = {   // Value of -1 typically means unknown
 | ||||
|   //{mcu_name,       mcuid,  family, {sig,    na, ture}, flstart,  flsize, pgsiz, nb, bootsz, eestart, eesize, ep, rambeg, ramsiz, nf, nl,  ni}, // Source
 | ||||
|   {"ATtiny4",            0, F_AVR8L, {0x1E, 0x8F, 0x0A},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny5",            1, F_AVR8L, {0x1E, 0x8F, 0x09},       0, 0x00200, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny9",            2, F_AVR8L, {0x1E, 0x90, 0x08},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny10",           3, F_AVR8L, {0x1E, 0x90, 0x03},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  11}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny20",           4, F_AVR8L, {0x1E, 0x91, 0x0F},       0, 0x00800, 0x020,  0,      0,       0,      0,  0, 0x0040, 0x0080,  1,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny40",           5, F_AVR8L, {0x1E, 0x92, 0x0E},       0, 0x01000, 0x040,  0,      0,       0,      0,  0, 0x0040, 0x0100,  1,  1,  18}, // atdf, avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATtiny102",          6, F_AVR8L, {0x1E, 0x90, 0x0C},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||
|   {"ATtiny104",          7, F_AVR8L, {0x1E, 0x90, 0x0B},       0, 0x00400, 0x010,  0,      0,       0,      0,  0, 0x0040, 0x0020,  1,  1,  16}, // atdf, avrdude, boot size (manual)
 | ||||
| 
 | ||||
|   {"ATtiny11",           8,  F_AVR8, {0x1E, 0x90, 0x04},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   5}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny12",           9,  F_AVR8, {0x1E, 0x90, 0x05},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny13",          10,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny13A",         11,  F_AVR8, {0x1E, 0x90, 0x07},       0, 0x00400, 0x020,  0,      0,       0, 0x0040,  4, 0x0060, 0x0040,  2,  1,  10}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny15",          12,  F_AVR8, {0x1E, 0x90, 0x06},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  2, 0x0060, 0x0020,  1,  1,   9}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny22",          13,  F_AVR8, {0x1E, 0x91, 0x06},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATtiny24",          14,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny24A",         15,  F_AVR8, {0x1E, 0x91, 0x0B},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny25",          16,  F_AVR8, {0x1E, 0x91, 0x08},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny26",          17,  F_AVR8, {0x1E, 0x91, 0x09},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  2,  1,  12}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny28",          18,  F_AVR8, {0x1E, 0x91, 0x07},       0, 0x00800, 0x002,  0,      0,       0,      0,  0, 0x0060, 0x0020,  1,  1,   6}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny43U",         19,  F_AVR8, {0x1E, 0x92, 0x0C},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0060, 0x0100,  3,  1,  16}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny44",          20,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny44A",         21,  F_AVR8, {0x1E, 0x92, 0x07},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny45",          22,  F_AVR8, {0x1E, 0x92, 0x06},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny48",          23,  F_AVR8, {0x1E, 0x92, 0x09},       0, 0x01000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny84",          24,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny84A",         25,  F_AVR8, {0x1E, 0x93, 0x0C},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny85",          26,  F_AVR8, {0x1E, 0x93, 0x0B},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  15}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny87",          27,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny88",          28,  F_AVR8, {0x1E, 0x93, 0x11},       0, 0x02000, 0x040,  0,      0,       0, 0x0040,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny167",         29,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny261",         30,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny261A",        31,  F_AVR8, {0x1E, 0x91, 0x0C},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny441",         32,  F_AVR8, {0x1E, 0x92, 0x15},       0, 0x01000, 0x010,  0,      0,       0, 0x0100,  4, 0x0100, 0x0100,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny461",         33,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny461A",        34,  F_AVR8, {0x1E, 0x92, 0x08},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny828",         35,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny828R",        36,  F_AVR8, {0x1E, 0x93, 0x14},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // avrdude, from ATtiny828
 | ||||
|   {"ATtiny841",         37,  F_AVR8, {0x1E, 0x93, 0x15},       0, 0x02000, 0x010,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  30}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny861",         38,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny861A",        39,  F_AVR8, {0x1E, 0x93, 0x0D},       0, 0x02000, 0x040,  0,      0,       0, 0x0200,  4, 0x0060, 0x0200,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1634",        40,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1634R",       41,  F_AVR8, {0x1E, 0x94, 0x12},       0, 0x04000, 0x020,  0,      0,       0, 0x0100,  4, 0x0100, 0x0400,  3,  1,  28}, // avrdude, from ATtiny1634
 | ||||
|   {"ATtiny2313",        42,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny2313A",       43,  F_AVR8, {0x1E, 0x91, 0x0A},       0, 0x00800, 0x020,  0,      0,       0, 0x0080,  4, 0x0060, 0x0080,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny4313",        44,  F_AVR8, {0x1E, 0x92, 0x0D},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0060, 0x0100,  3,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8",           45,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8A",          46,  F_AVR8, {0x1E, 0x93, 0x07},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  19}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8HVA",        47,  F_AVR8, {0x1E, 0x93, 0x10},       0, 0x02000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega8U2",         48,  F_AVR8, {0x1E, 0x93, 0x89},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16",          49,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16A",         50,  F_AVR8, {0x1E, 0x94, 0x03},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0400,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16HVA",       51,  F_AVR8, {0x1E, 0x94, 0x0C},       0, 0x04000, 0x080,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  1,  1,  21}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVB",       52,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVBrevB",   53,  F_AVR8, {0x1E, 0x94, 0x0D},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16M1",        54,  F_AVR8, {0x1E, 0x94, 0x84},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega16HVA2",      55,  F_AVR8, {0x1E, 0x94, 0x0E},       0, 0x04000, 0x080, -1,     -1,      -1,     -1, -1, 0x0100, 0x0400,  2,  1,  22}, // avr-gcc 12.2.0
 | ||||
|   {"ATmega16U2",        56,  F_AVR8, {0x1E, 0x94, 0x89},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega16U4",        57,  F_AVR8, {0x1E, 0x94, 0x88},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0500,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32",          58,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32A",         59,  F_AVR8, {0x1E, 0x95, 0x02},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0060, 0x0800,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32HVB",       60,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32HVBrevB",   61,  F_AVR8, {0x1E, 0x95, 0x10},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  2,  1,  29}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32C1",        62,  F_AVR8, {0x1E, 0x95, 0x86},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega32M1",        63,  F_AVR8, {0x1E, 0x95, 0x84},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U2",        64,  F_AVR8, {0x1E, 0x95, 0x8A},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0400,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U4",        65,  F_AVR8, {0x1E, 0x95, 0x87},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0a00,  3,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega32U6",        66,  F_AVR8, {0x1E, 0x95, 0x88},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x0a00,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega48",          67,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48A",         68,  F_AVR8, {0x1E, 0x92, 0x05},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48P",         69,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48PA",        70,  F_AVR8, {0x1E, 0x92, 0x0A},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega48PB",        71,  F_AVR8, {0x1E, 0x92, 0x10},       0, 0x01000, 0x040,  0,      0,       0, 0x0100,  4, 0x0100, 0x0200,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64",          72,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64A",         73,  F_AVR8, {0x1E, 0x96, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64HVE",       74,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,      -1,     -1, -1, 0x0100, 0x1000,  2,  1,  25}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega64C1",        75,  F_AVR8, {0x1E, 0x96, 0x86},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega64M1",        76,  F_AVR8, {0x1E, 0x96, 0x84},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega64HVE2",      77,  F_AVR8, {0x1E, 0x96, 0x10},       0, 0x10000, 0x080,  4, 0x0400,       0, 0x0400,  4, 0x0100, 0x1000,  2,  1,  25}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATmega64RFR2",      78,  F_AVR8, {0x1E, 0xA6, 0x02},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88",          79,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88A",         80,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88P",         81,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88PA",        82,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega88PB",        83,  F_AVR8, {0x1E, 0x93, 0x16},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega103",         84,  F_AVR8, {0x1E, 0x97, 0x01},       0, 0x20000, 0x100,  0,      0,       0, 0x1000,  1, 0x0060, 0x0fa0,  1,  1,  24}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega128",         85,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128A",        86,  F_AVR8, {0x1E, 0x97, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128RFA1",     87,  F_AVR8, {0x1E, 0xA7, 0x01},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  72}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega128RFR2",     88,  F_AVR8, {0x1E, 0xA7, 0x02},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega161",         89,  F_AVR8, {0x1E, 0x94, 0x01},       0, 0x04000, 0x080,  1, 0x0400,       0, 0x0200,  1, 0x0060, 0x0400,  1,  1,  21}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega162",         90,  F_AVR8, {0x1E, 0x94, 0x04},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega163",         91,  F_AVR8, {0x1E, 0x94, 0x02},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  1, 0x0060, 0x0400,  2,  1,  18}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega164A",        92,  F_AVR8, {0x1E, 0x94, 0x0F},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega164P",        93,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega164PA",       94,  F_AVR8, {0x1E, 0x94, 0x0A},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165",         95,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega165A",        96,  F_AVR8, {0x1E, 0x94, 0x10},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165P",        97,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega165PA",       98,  F_AVR8, {0x1E, 0x94, 0x07},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168",         99,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168A",       100,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168P",       101,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168PA",      102,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega168PB",      103,  F_AVR8, {0x1E, 0x94, 0x15},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  27}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATmega169",        104,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"ATmega169A",       105,  F_AVR8, {0x1E, 0x94, 0x11},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega169P",       106,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega169PA",      107,  F_AVR8, {0x1E, 0x94, 0x05},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega256RFR2",    108,  F_AVR8, {0x1E, 0xA8, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega323",        109,  F_AVR8, {0x1E, 0x95, 0x01},       0, 0x08000, 0x080,  4, 0x0200,      -1,     -1, -1, 0x0060, 0x0800,  2,  1,  21}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATmega324A",       110,  F_AVR8, {0x1E, 0x95, 0x15},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324P",       111,  F_AVR8, {0x1E, 0x95, 0x08},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324PA",      112,  F_AVR8, {0x1E, 0x95, 0x11},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega324PB",      113,  F_AVR8, {0x1E, 0x95, 0x17},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  51}, // atdf, avrdude
 | ||||
|   {"ATmega325",        114,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325A",       115,  F_AVR8, {0x1E, 0x95, 0x05},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325P",       116,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega325PA",      117,  F_AVR8, {0x1E, 0x95, 0x0D},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328",        118,  F_AVR8, {0x1E, 0x95, 0x14},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328P",       119,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega328PB",      120,  F_AVR8, {0x1E, 0x95, 0x16},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  45}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATmega329",        121,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329A",       122,  F_AVR8, {0x1E, 0x95, 0x03},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329P",       123,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega329PA",      124,  F_AVR8, {0x1E, 0x95, 0x0B},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega406",        125,  F_AVR8, {0x1E, 0x95, 0x07},       0, 0x0a000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0800,  2,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega640",        126,  F_AVR8, {0x1E, 0x96, 0x08},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644",        127,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  28}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644A",       128,  F_AVR8, {0x1E, 0x96, 0x09},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644P",       129,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644PA",      130,  F_AVR8, {0x1E, 0x96, 0x0A},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega644RFR2",    131,  F_AVR8, {0x1E, 0xA6, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0200, 0x2000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645",        132,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645A",       133,  F_AVR8, {0x1E, 0x96, 0x05},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega645P",       134,  F_AVR8, {0x1E, 0x96, 0x0D},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  22}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649",        135,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649A",       136,  F_AVR8, {0x1E, 0x96, 0x03},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega649P",       137,  F_AVR8, {0x1E, 0x96, 0x0B},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  23}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1280",       138,  F_AVR8, {0x1E, 0x97, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1281",       139,  F_AVR8, {0x1E, 0x97, 0x04},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284",       140,  F_AVR8, {0x1E, 0x97, 0x06},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284P",      141,  F_AVR8, {0x1E, 0x97, 0x05},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x4000,  3,  1,  35}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1284RFR2",   142,  F_AVR8, {0x1E, 0xA7, 0x03},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x4000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2560",       143,  F_AVR8, {0x1E, 0x98, 0x01},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2561",       144,  F_AVR8, {0x1E, 0x98, 0x02},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0200, 0x2000,  3,  1,  57}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega2564RFR2",   145,  F_AVR8, {0x1E, 0xA8, 0x03},       0, 0x40000, 0x100,  4, 0x0400,       0, 0x2000,  8, 0x0200, 0x8000,  3,  1,  77}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250",       146,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250A",      147,  F_AVR8, {0x1E, 0x95, 0x06},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250P",      148,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3250PA",     149,  F_AVR8, {0x1E, 0x95, 0x0E},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290",       150,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290A",      151,  F_AVR8, {0x1E, 0x95, 0x04},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290P",      152,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3290PA",     153,  F_AVR8, {0x1E, 0x95, 0x0C},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450",       154,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450A",      155,  F_AVR8, {0x1E, 0x96, 0x06},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6450P",      156,  F_AVR8, {0x1E, 0x96, 0x0E},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490",       157,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490A",      158,  F_AVR8, {0x1E, 0x96, 0x04},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega6490P",      159,  F_AVR8, {0x1E, 0x96, 0x0C},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  25}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8515",       160,  F_AVR8, {0x1E, 0x93, 0x06},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  17}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega8535",       161,  F_AVR8, {0x1E, 0x93, 0x08},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0060, 0x0200,  2,  1,  21}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT43USB320",       162,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0200, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT43USB355",       163,  F_AVR8, {0xff,   -1,   -1},       0, 0x06000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0400, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT76C711",         164,  F_AVR8, {0xff,   -1,   -1},       0, 0x04000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x07a0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT86RF401",        165,  F_AVR8, {0x1E, 0x91, 0x81},       0, 0x00800,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0080,  0,  1,   3}, // avr-gcc 12.2.0
 | ||||
|   {"AT90PWM1",         166,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90PWM2",         167,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90PWM2B",        168,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM3",         169,  F_AVR8, {0x1E, 0x93, 0x81},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM3B",        170,  F_AVR8, {0x1E, 0x93, 0x83},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90CAN32",        171,  F_AVR8, {0x1E, 0x95, 0x81},       0, 0x08000, 0x100,  4, 0x0400,       0, 0x0400,  8, 0x0100, 0x0800,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90CAN64",        172,  F_AVR8, {0x1E, 0x96, 0x81},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM81",        173,  F_AVR8, {0x1E, 0x93, 0x88},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0100,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90USB82",        174,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90SCR100",       175,  F_AVR8, {0x1E, 0x96, 0xC1},       0, 0x10000, 0x100,  4, 0x0200,      -1,     -1, -1, 0x0100, 0x1000,  3,  1,  38}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"AT90CAN128",       176,  F_AVR8, {0x1E, 0x97, 0x81},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x1000,  3,  1,  37}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM161",       177,  F_AVR8, {0x1E, 0x94, 0x8B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"AT90USB162",       178,  F_AVR8, {0x1E, 0x94, 0x82},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  29}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM216",       179,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90PWM316",       180,  F_AVR8, {0x1E, 0x94, 0x83},       0, 0x04000, 0x080,  4, 0x0200,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  32}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB646",       181,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB647",       182,  F_AVR8, {0x1E, 0x96, 0x82},       0, 0x10000, 0x100,  4, 0x0400,       0, 0x0800,  8, 0x0100, 0x1000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90S1200",        183,  F_AVR8, {0x1E, 0x90, 0x01},       0, 0x00400, 0x001,  0,      0,       0, 0x0040,  1, 0x0060, 0x0020,  1,  1,   4}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90USB1286",      184,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90USB1287",      185,  F_AVR8, {0x1E, 0x97, 0x82},       0, 0x20000, 0x100,  4, 0x0400,       0, 0x1000,  8, 0x0100, 0x2000,  3,  1,  38}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AT90S2313",        186,  F_AVR8, {0x1E, 0x91, 0x01},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,  11}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S2323",        187,  F_AVR8, {0x1E, 0x91, 0x02},       0, 0x00800,    -1,  0,      0,      -1,     -1, -1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"AT90S2333",        188,  F_AVR8, {0x1E, 0x91, 0x05},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080, -1, -1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S2343",        189,  F_AVR8, {0x1E, 0x91, 0x03},       0, 0x00800, 0x001,  0,      0,       0, 0x0080,  1, 0x0060, 0x0080,  1,  1,   3}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4414",        190,  F_AVR8, {0x1E, 0x92, 0x01},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4433",        191,  F_AVR8, {0x1E, 0x92, 0x03},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0080,  1,  1,  14}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S4434",        192,  F_AVR8, {0x1E, 0x92, 0x02},       0, 0x01000, 0x001,  0,      0,       0, 0x0100,  1, 0x0060, 0x0100,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90S8515",        193,  F_AVR8, {0x1E, 0x93, 0x01},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  13}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT90C8534",        194,  F_AVR8, {0xff,   -1,   -1},       0, 0x02000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0100, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"AT90S8535",        195,  F_AVR8, {0x1E, 0x93, 0x03},       0, 0x02000, 0x001,  0,      0,       0, 0x0200,  1, 0x0060, 0x0200,  1,  1,  17}, // avr-gcc 12.2.0, avrdude, boot size (manual)
 | ||||
|   {"AT94K",            196,  F_AVR8, {0xff,   -1,   -1},       0, 0x08000,    -1, -1,     -1,      -1,     -1, -1, 0x0060, 0x0fa0, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"ATA5272",          197,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  37}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5505",          198,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5700M322",      199,  F_AVR8, {0x1E, 0x95, 0x67}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf
 | ||||
|   {"ATA5702M322",      200,  F_AVR8, {0x1E, 0x95, 0x69}, 0x08000, 0x08000, 0x040,  0,      0,       0, 0x0880, 16, 0x0200, 0x0400,  1,  1,  51}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5781",          201,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5782",          202,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5783",          203,  F_AVR8, {0x1E, 0x95, 0x66},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5787",          204,  F_AVR8, {0x1E, 0x94, 0x6C}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||
|   {"ATA5790",          205,  F_AVR8, {0x1E, 0x94, 0x61},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  30}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5790N",         206,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5791",          207,  F_AVR8, {0x1E, 0x94, 0x62},       0, 0x04000, 0x080,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  31}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA5795",          208,  F_AVR8, {0x1E, 0x93, 0x61},       0, 0x02000, 0x040,  1, 0x0800,       0, 0x0800, 16, 0x0100, 0x0200,  1,  1,  23}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5831",          209,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA5832",          210,  F_AVR8, {0x1E, 0x95, 0x62},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5833",          211,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA5835",          212,  F_AVR8, {0x1E, 0x94, 0x6B}, 0x08000, 0x05200, 0x040,  0,      0,       0, 0x0400, 16, 0x0200, 0x0800,  1,  1,  44}, // atdf
 | ||||
|   {"ATA6285",          213,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6286",          214,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0140,  4, 0x0100, 0x0200,  2,  1,  27}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6289",          215,  F_AVR8, {0x1E, 0x93, 0x82},       0, 0x02000, 0x040,  4, 0x0100,      -1,     -1, -1, 0x0100, 0x0200,  2,  1,  27}, // avr-gcc 12.2.0, boot size (manual)
 | ||||
|   {"ATA6612C",         216,  F_AVR8, {0x1E, 0x93, 0x0A},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6613C",         217,  F_AVR8, {0x1E, 0x94, 0x06},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6614Q",         218,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6616C",         219,  F_AVR8, {0x1E, 0x93, 0x87},       0, 0x02000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA6617C",         220,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATA8210",          221,  F_AVR8, {0x1E, 0x95, 0x65}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA8215",          222,  F_AVR8, {0x1E, 0x95, 0x64},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA8510",          223,  F_AVR8, {0x1E, 0x95, 0x61}, 0x08000, 0x05000, 0x040,  1, 0x5000,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf, avr-gcc 7.3.0
 | ||||
|   {"ATA8515",          224,  F_AVR8, {0x1E, 0x95, 0x63},      -1,      -1,    -1,  0,      0,       0, 0x0400, 16, 0x0200, 0x0400,  1,  1,  42}, // atdf
 | ||||
|   {"ATA664251",        225,  F_AVR8, {0x1E, 0x94, 0x87},       0, 0x04000, 0x080,  0,      0,       0, 0x0200,  4, 0x0100, 0x0200,  3,  1,  20}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"M3000",            226,  F_AVR8, {0xff,   -1,   -1},       0, 0x10000,    -1, -1,     -1,      -1,     -1, -1, 0x1000, 0x1000, -1, -1,   0}, // avr-gcc 12.2.0
 | ||||
|   {"LGT8F88P",         227,  F_AVR8, {0x1E, 0x93, 0x0F},       0, 0x02000, 0x040,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega88
 | ||||
|   {"LGT8F168P",        228,  F_AVR8, {0x1E, 0x94, 0x0B},       0, 0x04000, 0x080,  4, 0x0100,       0, 0x0200,  4, 0x0100, 0x0400,  3,  1,  26}, // avrdude, from ATmega168P
 | ||||
|   {"LGT8F328P",        229,  F_AVR8, {0x1E, 0x95, 0x0F},       0, 0x08000, 0x080,  4, 0x0200,       0, 0x0400,  4, 0x0100, 0x0800,  3,  1,  26}, // avrdude, from ATmega328P
 | ||||
| 
 | ||||
|   {"ATxmega8E5",       230, F_XMEGA, {0x1E, 0x93, 0x41},       0, 0x02800, 0x080,  1, 0x0800,       0, 0x0200, 32, 0x2000, 0x0400,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16A4",      231, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16A4U",     232, F_XMEGA, {0x1E, 0x94, 0x41},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16C4",      233, F_XMEGA, {0x1E, 0x94, 0x43},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16D4",      234, F_XMEGA, {0x1E, 0x94, 0x42},       0, 0x05000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x0800,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega16E5",      235, F_XMEGA, {0x1E, 0x94, 0x45},       0, 0x05000, 0x080,  1, 0x1000,       0, 0x0200, 32, 0x2000, 0x0800,  7,  1,  43}, // atdf, avr-gcc 7.3.0, avrdude
 | ||||
|   {"ATxmega32C3",      236, F_XMEGA, {0x1E, 0x95, 0x49},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATxmega32D3",      237, F_XMEGA, {0x1E, 0x95, 0x4A},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0
 | ||||
|   {"ATxmega32A4",      238, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  94}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32A4U",     239, F_XMEGA, {0x1E, 0x95, 0x41},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32C4",      240, F_XMEGA, {0x1E, 0x95, 0x44},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32D4",      241, F_XMEGA, {0x1E, 0x95, 0x42},       0, 0x09000, 0x100,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega32E5",      242, F_XMEGA, {0x1E, 0x95, 0x4C},       0, 0x09000, 0x080,  1, 0x1000,       0, 0x0400, 32, 0x2000, 0x1000,  7,  1,  43}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A1",      243, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A1U",     244, F_XMEGA, {0x1E, 0x96, 0x4E},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64B1",      245, F_XMEGA, {0x1E, 0x96, 0x52},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A3",      246, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A3U",     247, F_XMEGA, {0x1E, 0x96, 0x42},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64B3",      248, F_XMEGA, {0x1E, 0x96, 0x51},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64C3",      249, F_XMEGA, {0x1E, 0x96, 0x49},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64D3",      250, F_XMEGA, {0x1E, 0x96, 0x4A},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64A4",      251, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega64A4U",     252, F_XMEGA, {0x1E, 0x96, 0x46},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega64D4",      253, F_XMEGA, {0x1E, 0x96, 0x47},       0, 0x11000, 0x100,  1, 0x1000,       0, 0x0800, 32, 0x2000, 0x1000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A1",     254, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 125}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A1revD", 255, F_XMEGA, {0x1E, 0x97, 0x41},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega128A1U",    256, F_XMEGA, {0x1E, 0x97, 0x4C},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128B1",     257, F_XMEGA, {0x1E, 0x97, 0x4D},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  81}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A3",     258, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A3U",    259, F_XMEGA, {0x1E, 0x97, 0x42},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128B3",     260, F_XMEGA, {0x1E, 0x97, 0x4B},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  54}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128C3",     261, F_XMEGA, {0x1E, 0x97, 0x52},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128D3",     262, F_XMEGA, {0x1E, 0x97, 0x48},       0, 0x22000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128A4",     263, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega128A4U",    264, F_XMEGA, {0x1E, 0x97, 0x46},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega128D4",     265, F_XMEGA, {0x1E, 0x97, 0x47},       0, 0x22000, 0x100,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x2000,  6,  1,  91}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192A1",     266, F_XMEGA, {0x1E, 0x97, 0x4E},       0, 0x32000, 0x200, -1,     -1,       0, 0x0800, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega192A3",     267, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192A3U",    268, F_XMEGA, {0x1E, 0x97, 0x44},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192C3",     269, F_XMEGA, {0x1E, 0x97, 0x51},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega192D3",     270, F_XMEGA, {0x1E, 0x97, 0x49},       0, 0x32000, 0x200,  1, 0x2000,       0, 0x0800, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A1",     271, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200, -1,     -1,       0, 0x1000, 32,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"ATxmega256A3",     272, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3B",    273, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 122}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3BU",   274, F_XMEGA, {0x1E, 0x98, 0x43},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256A3U",    275, F_XMEGA, {0x1E, 0x98, 0x42},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256C3",     276, F_XMEGA, {0x1E, 0x98, 0x46},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega256D3",     277, F_XMEGA, {0x1E, 0x98, 0x44},       0, 0x42000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x4000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega384C3",     278, F_XMEGA, {0x1E, 0x98, 0x45},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 127}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATxmega384D3",     279, F_XMEGA, {0x1E, 0x98, 0x47},       0, 0x62000, 0x200,  1, 0x2000,       0, 0x1000, 32, 0x2000, 0x8000,  6,  1, 114}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
| 
 | ||||
|   {"ATtiny202",        280, F_AVR8X, {0x1E, 0x91, 0x23},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny204",        281, F_AVR8X, {0x1E, 0x91, 0x22},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny212",        282, F_AVR8X, {0x1E, 0x91, 0x21},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny214",        283, F_AVR8X, {0x1E, 0x91, 0x20},       0, 0x00800, 0x040,  1,      0, 0x01400, 0x0040, 32, 0x3f80, 0x0080, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny402",        284, F_AVR8X, {0x1E, 0x92, 0x27},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny404",        285, F_AVR8X, {0x1E, 0x92, 0x26},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny406",        286, F_AVR8X, {0x1E, 0x92, 0x25},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny412",        287, F_AVR8X, {0x1E, 0x92, 0x23},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny414",        288, F_AVR8X, {0x1E, 0x92, 0x22},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny416",        289, F_AVR8X, {0x1E, 0x92, 0x21},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny416auto",    290, F_AVR8X, {0x1E, 0x92, 0x28},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf
 | ||||
|   {"ATtiny417",        291, F_AVR8X, {0x1E, 0x92, 0x20},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3f00, 0x0100, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny424",        292, F_AVR8X, {0x1E, 0x92, 0x2C},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny426",        293, F_AVR8X, {0x1E, 0x92, 0x2B},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny427",        294, F_AVR8X, {0x1E, 0x92, 0x2A},       0, 0x01000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny804",        295, F_AVR8X, {0x1E, 0x93, 0x25},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny806",        296, F_AVR8X, {0x1E, 0x93, 0x24},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny807",        297, F_AVR8X, {0x1E, 0x93, 0x23},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny814",        298, F_AVR8X, {0x1E, 0x93, 0x22},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny816",        299, F_AVR8X, {0x1E, 0x93, 0x21},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny817",        300, F_AVR8X, {0x1E, 0x93, 0x20},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3e00, 0x0200, 10,  1,  26}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny824",        301, F_AVR8X, {0x1E, 0x93, 0x29},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny826",        302, F_AVR8X, {0x1E, 0x93, 0x28},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny827",        303, F_AVR8X, {0x1E, 0x93, 0x27},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0080, 32, 0x3c00, 0x0400, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1604",       304, F_AVR8X, {0x1E, 0x94, 0x25},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1606",       305, F_AVR8X, {0x1E, 0x94, 0x24},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1607",       306, F_AVR8X, {0x1E, 0x94, 0x23},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1614",       307, F_AVR8X, {0x1E, 0x94, 0x22},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1616",       308, F_AVR8X, {0x1E, 0x94, 0x21},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1617",       309, F_AVR8X, {0x1E, 0x94, 0x20},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny1624",       310, F_AVR8X, {0x1E, 0x94, 0x2A},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1626",       311, F_AVR8X, {0x1E, 0x94, 0x29},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny1627",       312, F_AVR8X, {0x1E, 0x94, 0x28},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3214",       313, F_AVR8X, {0x1E, 0x95, 0x20},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // avr-gcc 12.2.0
 | ||||
|   {"ATtiny3216",       314, F_AVR8X, {0x1E, 0x95, 0x21},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny3217",       315, F_AVR8X, {0x1E, 0x95, 0x22},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3800, 0x0800, 10,  1,  31}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATtiny3224",       316, F_AVR8X, {0x1E, 0x95, 0x28},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3226",       317, F_AVR8X, {0x1E, 0x95, 0x27},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATtiny3227",       318, F_AVR8X, {0x1E, 0x95, 0x26},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3400, 0x0c00, 10,  1,  30}, // atdf, avrdude
 | ||||
|   {"ATmega808",        319, F_AVR8X, {0x1E, 0x93, 0x26},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega809",        320, F_AVR8X, {0x1E, 0x93, 0x2A},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3c00, 0x0400, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1608",       321, F_AVR8X, {0x1E, 0x94, 0x27},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega1609",       322, F_AVR8X, {0x1E, 0x94, 0x26},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0100, 32, 0x3800, 0x0800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3208",       323, F_AVR8X, {0x1E, 0x95, 0x30},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega3209",       324, F_AVR8X, {0x1E, 0x95, 0x31},       0, 0x08000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x3000, 0x1000, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega4808",       325, F_AVR8X, {0x1E, 0x96, 0x50},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  36}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"ATmega4809",       326, F_AVR8X, {0x1E, 0x96, 0x51},       0, 0x0c000, 0x080,  1,      0, 0x01400, 0x0100, 64, 0x2800, 0x1800, 10,  1,  40}, // atdf, avr-gcc 12.2.0, avrdude
 | ||||
|   {"AVR8EA28",         327, F_AVR8X, {0x1E, 0x93, 0x2C},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR8EA32",         328, F_AVR8X, {0x1E, 0x93, 0x2B},       0, 0x02000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16DD14",        329, F_AVR8X, {0x1E, 0x94, 0x34},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16DD20",        330, F_AVR8X, {0x1E, 0x94, 0x33},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16DD28",        331, F_AVR8X, {0x1E, 0x94, 0x32},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16EA28",        332, F_AVR8X, {0x1E, 0x94, 0x37},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16DD32",        333, F_AVR8X, {0x1E, 0x94, 0x31},       0, 0x04000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7800, 0x0800, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR16EA32",        334, F_AVR8X, {0x1E, 0x94, 0x36},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR16EA48",        335, F_AVR8X, {0x1E, 0x94, 0x35},       0, 0x04000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DD14",        336, F_AVR8X, {0x1E, 0x95, 0x3B},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32DD20",        337, F_AVR8X, {0x1E, 0x95, 0x3A},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32DA28",        338, F_AVR8X, {0x1E, 0x95, 0x34},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR32DB28",        339, F_AVR8X, {0x1E, 0x95, 0x37},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR32DD28",        340, F_AVR8X, {0x1E, 0x95, 0x39},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32EA28",        341, F_AVR8X, {0x1E, 0x95, 0x3E},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DA32",        342, F_AVR8X, {0x1E, 0x95, 0x33},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR32DB32",        343, F_AVR8X, {0x1E, 0x95, 0x36},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR32DD32",        344, F_AVR8X, {0x1E, 0x95, 0x38},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x7000, 0x1000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR32EA32",        345, F_AVR8X, {0x1E, 0x95, 0x3D},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR32DA48",        346, F_AVR8X, {0x1E, 0x95, 0x32},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR32DB48",        347, F_AVR8X, {0x1E, 0x95, 0x35},       0, 0x08000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x7000, 0x1000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR32EA48",        348, F_AVR8X, {0x1E, 0x95, 0x3C},       0, 0x08000, 0x040,  1,      0, 0x01400, 0x0200,  8,     -1,     -1, -1, -1,   0}, // avrdude
 | ||||
|   {"AVR64DD14",        349, F_AVR8X, {0x1E, 0x96, 0x1D},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64DD20",        350, F_AVR8X, {0x1E, 0x96, 0x1C},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64DA28",        351, F_AVR8X, {0x1E, 0x96, 0x15},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR64DB28",        352, F_AVR8X, {0x1E, 0x96, 0x19},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR64DD28",        353, F_AVR8X, {0x1E, 0x96, 0x1B},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64EA28",        354, F_AVR8X, {0x1E, 0x96, 0x20},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||
|   {"AVR64DA32",        355, F_AVR8X, {0x1E, 0x96, 0x14},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR64DB32",        356, F_AVR8X, {0x1E, 0x96, 0x18},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR64DD32",        357, F_AVR8X, {0x1E, 0x96, 0x1A},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0100,  1, 0x6000, 0x2000, 16,  4,  36}, // atdf, avrdude
 | ||||
|   {"AVR64EA32",        358, F_AVR8X, {0x1E, 0x96, 0x1F},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  37}, // atdf, avrdude
 | ||||
|   {"AVR64DA48",        359, F_AVR8X, {0x1E, 0x96, 0x13},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR64DB48",        360, F_AVR8X, {0x1E, 0x96, 0x17},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR64EA48",        361, F_AVR8X, {0x1E, 0x96, 0x1E},       0, 0x10000, 0x080,  1,      0, 0x01400, 0x0200,  8, 0x6800, 0x1800, 16,  4,  45}, // atdf, avrdude
 | ||||
|   {"AVR64DA64",        362, F_AVR8X, {0x1E, 0x96, 0x12},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  64}, // atdf, avrdude
 | ||||
|   {"AVR64DB64",        363, F_AVR8X, {0x1E, 0x96, 0x16},       0, 0x10000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x6000, 0x2000, 16,  4,  65}, // atdf, avrdude
 | ||||
|   {"AVR128DA28",       364, F_AVR8X, {0x1E, 0x97, 0x0A},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  41}, // atdf, avrdude
 | ||||
|   {"AVR128DB28",       365, F_AVR8X, {0x1E, 0x97, 0x0E},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  42}, // atdf, avrdude
 | ||||
|   {"AVR128DA32",       366, F_AVR8X, {0x1E, 0x97, 0x09},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR128DB32",       367, F_AVR8X, {0x1E, 0x97, 0x0D},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  44}, // atdf, avrdude
 | ||||
|   {"AVR128DA48",       368, F_AVR8X, {0x1E, 0x97, 0x08},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  58}, // atdf, avrdude
 | ||||
|   {"AVR128DB48",       369, F_AVR8X, {0x1E, 0x97, 0x0C},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  61}, // atdf, avrdude
 | ||||
|   {"AVR128DA64",       370, F_AVR8X, {0x1E, 0x97, 0x07},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  64}, // atdf, avrdude
 | ||||
|   {"AVR128DB64",       371, F_AVR8X, {0x1E, 0x97, 0x0B},       0, 0x20000, 0x200,  1,      0, 0x01400, 0x0200,  1, 0x4000, 0x4000, 16,  4,  65}, // atdf, avrdude
 | ||||
| }; | ||||
| 
 | ||||
| const size_t avr_isp_chip_arr_size = COUNT_OF(avr_isp_chip_arr); | ||||
							
								
								
									
										33
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_chip_arr.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,33 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| #define F_AVR8L 1 // TPI programming, ATtiny(4|5|9|10|20|40|102|104)
 | ||||
| #define F_AVR8 2 // ISP programming with SPI, "classic" AVRs
 | ||||
| #define F_XMEGA 4 // PDI programming, ATxmega family
 | ||||
| #define F_AVR8X 8 // UPDI programming, newer 8-bit MCUs
 | ||||
| 
 | ||||
| struct AvrIspChipArr { // Value of -1 typically means unknown
 | ||||
|     const char* name; // Name of part
 | ||||
|     uint16_t mcuid; // ID of MCU in 0..2039
 | ||||
|     uint8_t avrarch; // F_AVR8L, F_AVR8, F_XMEGA or F_AVR8X
 | ||||
|     uint8_t sigs[3]; // Signature bytes
 | ||||
|     int32_t flashoffset; // Flash offset
 | ||||
|     int32_t flashsize; // Flash size
 | ||||
|     int16_t pagesize; // Flash page size
 | ||||
|     int8_t nboots; // Number of supported boot sectors
 | ||||
|     int16_t bootsize; // Size of (smallest) boot sector
 | ||||
|     int32_t eepromoffset; // EEPROM offset
 | ||||
|     int32_t eepromsize; // EEPROM size
 | ||||
|     int32_t eeprompagesize; // EEPROM page size
 | ||||
|     int32_t sramstart; // SRAM offset
 | ||||
|     int32_t sramsize; // SRAM size
 | ||||
|     int8_t nfuses; // Number of fuse bytes
 | ||||
|     int8_t nlocks; // Number of lock bytes
 | ||||
|     uint8_t ninterrupts; // Number of vectors in interrupt vector table
 | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspChipArr AvrIspChipArr; | ||||
| 
 | ||||
| extern const AvrIspChipArr avr_isp_chip_arr[]; | ||||
| extern const size_t avr_isp_chip_arr_size; | ||||
							
								
								
									
										633
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,633 @@ | ||||
| #include "avr_isp_prog.h" | ||||
| #include "avr_isp_prog_cmd.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_PROG_TX_RX_BUF_SIZE 320 | ||||
| #define TAG "AvrIspProg" | ||||
| 
 | ||||
| struct AvrIspProgSignature { | ||||
|     uint8_t vendor; | ||||
|     uint8_t part_family; | ||||
|     uint8_t part_number; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspProgSignature AvrIspProgSignature; | ||||
| 
 | ||||
| struct AvrIspProgCfgDevice { | ||||
|     uint8_t devicecode; | ||||
|     uint8_t revision; | ||||
|     uint8_t progtype; | ||||
|     uint8_t parmode; | ||||
|     uint8_t polling; | ||||
|     uint8_t selftimed; | ||||
|     uint8_t lockbytes; | ||||
|     uint8_t fusebytes; | ||||
|     uint8_t flashpoll; | ||||
|     uint16_t eeprompoll; | ||||
|     uint16_t pagesize; | ||||
|     uint16_t eepromsize; | ||||
|     uint32_t flashsize; | ||||
| }; | ||||
| 
 | ||||
| typedef struct AvrIspProgCfgDevice AvrIspProgCfgDevice; | ||||
| 
 | ||||
| struct AvrIspProg { | ||||
|     AvrIspSpiSw* spi; | ||||
|     AvrIspProgCfgDevice* cfg; | ||||
|     FuriStreamBuffer* stream_rx; | ||||
|     FuriStreamBuffer* stream_tx; | ||||
| 
 | ||||
|     uint16_t error; | ||||
|     uint16_t addr; | ||||
|     bool pmode; | ||||
|     bool exit; | ||||
|     bool rst_active_high; | ||||
|     uint8_t buff[AVR_ISP_PROG_TX_RX_BUF_SIZE]; | ||||
| 
 | ||||
|     AvrIspProgCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| static void avr_isp_prog_end_pmode(AvrIspProg* instance); | ||||
| 
 | ||||
| AvrIspProg* avr_isp_prog_init(void) { | ||||
|     AvrIspProg* instance = malloc(sizeof(AvrIspProg)); | ||||
|     instance->cfg = malloc(sizeof(AvrIspProgCfgDevice)); | ||||
|     instance->stream_rx = | ||||
|         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||
|     instance->stream_tx = | ||||
|         furi_stream_buffer_alloc(sizeof(int8_t) * AVR_ISP_PROG_TX_RX_BUF_SIZE, sizeof(int8_t)); | ||||
|     instance->rst_active_high = false; | ||||
|     instance->exit = false; | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_free(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->spi) avr_isp_prog_end_pmode(instance); | ||||
|     furi_stream_buffer_free(instance->stream_tx); | ||||
|     furi_stream_buffer_free(instance->stream_rx); | ||||
|     free(instance->cfg); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) { | ||||
|     return furi_stream_buffer_spaces_available(instance->stream_rx); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(data); | ||||
|     furi_assert(len != 0); | ||||
|     size_t ret = furi_stream_buffer_send(instance->stream_rx, data, sizeof(uint8_t) * len, 0); | ||||
|     return ret == sizeof(uint8_t) * len; | ||||
| } | ||||
| 
 | ||||
| size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len) { | ||||
|     furi_assert(instance); | ||||
|     return furi_stream_buffer_receive(instance->stream_tx, data, sizeof(int8_t) * max_len, 0); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_exit(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     instance->exit = true; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(context); | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_tx_ch(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     furi_stream_buffer_send(instance->stream_tx, &data, sizeof(uint8_t), FuriWaitForever); | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_getch(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t data[1] = {0}; | ||||
|     while(furi_stream_buffer_receive(instance->stream_rx, &data, sizeof(int8_t), 30) == 0) { | ||||
|         if(instance->exit) break; | ||||
|     }; | ||||
|     return data[0]; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_fill(AvrIspProg* instance, size_t len) { | ||||
|     furi_assert(instance); | ||||
|     for(size_t x = 0; x < len; x++) { | ||||
|         instance->buff[x] = avr_isp_prog_getch(instance); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_reset_target(AvrIspProg* instance, bool reset) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_spi_sw_res_set(instance->spi, (reset == instance->rst_active_high) ? true : false); | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_spi_transaction( | ||||
|     AvrIspProg* instance, | ||||
|     uint8_t cmd, | ||||
|     uint8_t addr_hi, | ||||
|     uint8_t addr_lo, | ||||
|     uint8_t data) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     avr_isp_spi_sw_txrx(instance->spi, cmd); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_hi); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, addr_lo); | ||||
|     return avr_isp_spi_sw_txrx(instance->spi, data); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_empty_reply(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_breply(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, data); | ||||
|         avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_get_version(AvrIspProg* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     switch(data) { | ||||
|     case STK_HW_VER: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_HWVER); | ||||
|         break; | ||||
|     case STK_SW_MAJOR: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_SWMAJ); | ||||
|         break; | ||||
|     case STK_SW_MINOR: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_SWMIN); | ||||
|         break; | ||||
|     case AVP_ISP_CONNECT_TYPE: | ||||
|         avr_isp_prog_breply(instance, AVP_ISP_SERIAL_CONNECT_TYPE); | ||||
|         break; | ||||
|     default: | ||||
|         avr_isp_prog_breply(instance, AVR_ISP_RESP_0); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_set_cfg(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     // call this after reading cfg packet into buff[]
 | ||||
|     instance->cfg->devicecode = instance->buff[0]; | ||||
|     instance->cfg->revision = instance->buff[1]; | ||||
|     instance->cfg->progtype = instance->buff[2]; | ||||
|     instance->cfg->parmode = instance->buff[3]; | ||||
|     instance->cfg->polling = instance->buff[4]; | ||||
|     instance->cfg->selftimed = instance->buff[5]; | ||||
|     instance->cfg->lockbytes = instance->buff[6]; | ||||
|     instance->cfg->fusebytes = instance->buff[7]; | ||||
|     instance->cfg->flashpoll = instance->buff[8]; | ||||
|     // ignore (instance->buff[9] == instance->buff[8]) //FLASH polling value. Same as “flashpoll”
 | ||||
|     instance->cfg->eeprompoll = instance->buff[10] << 8 | instance->buff[11]; | ||||
|     instance->cfg->pagesize = instance->buff[12] << 8 | instance->buff[13]; | ||||
|     instance->cfg->eepromsize = instance->buff[14] << 8 | instance->buff[15]; | ||||
|     instance->cfg->flashsize = instance->buff[16] << 24 | instance->buff[17] << 16 | | ||||
|                                instance->buff[18] << 8 | instance->buff[19]; | ||||
| 
 | ||||
|     // avr devices have active low reset, at89sx are active high
 | ||||
|     instance->rst_active_high = (instance->cfg->devicecode >= 0xe0); | ||||
| } | ||||
| static bool | ||||
|     avr_isp_prog_set_pmode(AvrIspProg* instance, uint8_t a, uint8_t b, uint8_t c, uint8_t d) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t res = 0; | ||||
|     avr_isp_spi_sw_txrx(instance->spi, a); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, b); | ||||
|     res = avr_isp_spi_sw_txrx(instance->spi, c); | ||||
|     avr_isp_spi_sw_txrx(instance->spi, d); | ||||
|     return res == 0x53; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_end_pmode(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(instance->pmode) { | ||||
|         avr_isp_prog_reset_target(instance, false); | ||||
|         // We're about to take the target out of reset
 | ||||
|         // so configure SPI pins as input
 | ||||
| 
 | ||||
|         if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|         instance->spi = NULL; | ||||
|     } | ||||
| 
 | ||||
|     instance->pmode = false; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_prog_start_pmode(AvrIspProg* instance, AvrIspSpiSwSpeed spi_speed) { | ||||
|     furi_assert(instance); | ||||
|     // Reset target before driving PIN_SCK or PIN_MOSI
 | ||||
| 
 | ||||
|     // SPI.begin() will configure SS as output,
 | ||||
|     // so SPI master mode is selected.
 | ||||
|     // We have defined RESET as pin 10,
 | ||||
|     // which for many arduino's is not the SS pin.
 | ||||
|     // So we have to configure RESET as output here,
 | ||||
|     // (reset_target() first sets the correct level)
 | ||||
|     if(instance->spi) avr_isp_spi_sw_free(instance->spi); | ||||
|     instance->spi = avr_isp_spi_sw_init(spi_speed); | ||||
| 
 | ||||
|     avr_isp_prog_reset_target(instance, true); | ||||
|     // See avr datasheets, chapter "SERIAL_PRG Programming Algorithm":
 | ||||
| 
 | ||||
|     // Pulse RESET after PIN_SCK is low:
 | ||||
|     avr_isp_spi_sw_sck_set(instance->spi, false); | ||||
| 
 | ||||
|     // discharge PIN_SCK, value arbitrally chosen
 | ||||
|     furi_delay_ms(20); | ||||
|     avr_isp_prog_reset_target(instance, false); | ||||
| 
 | ||||
|     // Pulse must be minimum 2 target CPU speed cycles
 | ||||
|     // so 100 usec is ok for CPU speeds above 20KHz
 | ||||
|     furi_delay_ms(1); | ||||
| 
 | ||||
|     avr_isp_prog_reset_target(instance, true); | ||||
| 
 | ||||
|     // Send the enable programming command:
 | ||||
|     // datasheet: must be > 20 msec
 | ||||
|     furi_delay_ms(50); | ||||
|     if(avr_isp_prog_set_pmode(instance, AVR_ISP_SET_PMODE)) { | ||||
|         instance->pmode = true; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static AvrIspProgSignature avr_isp_prog_check_signature(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     AvrIspProgSignature signature; | ||||
|     signature.vendor = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR); | ||||
|     signature.part_family = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY); | ||||
|     signature.part_number = avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER); | ||||
|     return signature; | ||||
| } | ||||
| 
 | ||||
| static bool avr_isp_prog_auto_set_spi_speed_start_pmode(AvrIspProg* instance) { | ||||
|     AvrIspSpiSwSpeed spi_speed[] = { | ||||
|         AvrIspSpiSwSpeed1Mhz, | ||||
|         AvrIspSpiSwSpeed400Khz, | ||||
|         AvrIspSpiSwSpeed250Khz, | ||||
|         AvrIspSpiSwSpeed125Khz, | ||||
|         AvrIspSpiSwSpeed60Khz, | ||||
|         AvrIspSpiSwSpeed40Khz, | ||||
|         AvrIspSpiSwSpeed20Khz, | ||||
|         AvrIspSpiSwSpeed10Khz, | ||||
|         AvrIspSpiSwSpeed5Khz, | ||||
|         AvrIspSpiSwSpeed1Khz, | ||||
|     }; | ||||
|     for(uint8_t i = 0; i < COUNT_OF(spi_speed); i++) { | ||||
|         if(avr_isp_prog_start_pmode(instance, spi_speed[i])) { | ||||
|             AvrIspProgSignature sig = avr_isp_prog_check_signature(instance); | ||||
|             AvrIspProgSignature sig_examination = avr_isp_prog_check_signature(instance); //-V656
 | ||||
|             uint8_t y = 0; | ||||
|             while(y < 8) { | ||||
|                 if(memcmp( | ||||
|                        (uint8_t*)&sig, (uint8_t*)&sig_examination, sizeof(AvrIspProgSignature)) != | ||||
|                    0) | ||||
|                     break; | ||||
|                 sig_examination = avr_isp_prog_check_signature(instance); | ||||
|                 y++; | ||||
|             } | ||||
|             if(y == 8) { | ||||
|                 if(spi_speed[i] > AvrIspSpiSwSpeed1Mhz) { | ||||
|                     if(i < (COUNT_OF(spi_speed) - 1)) { | ||||
|                         avr_isp_prog_end_pmode(instance); | ||||
|                         i++; | ||||
|                         return avr_isp_prog_start_pmode(instance, spi_speed[i]); | ||||
|                     } | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_universal(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t data; | ||||
| 
 | ||||
|     avr_isp_prog_fill(instance, 4); | ||||
|     data = avr_isp_prog_spi_transaction( | ||||
|         instance, instance->buff[0], instance->buff[1], instance->buff[2], instance->buff[3]); | ||||
|     avr_isp_prog_breply(instance, data); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_commit(AvrIspProg* instance, uint16_t addr, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_prog_spi_transaction(instance, AVR_ISP_COMMIT(addr)); | ||||
|     /* polling flash */ | ||||
|     if(data == 0xFF) { | ||||
|         furi_delay_ms(5); | ||||
|     } else { | ||||
|         /* polling flash */ | ||||
|         uint32_t starttime = furi_get_tick(); | ||||
|         while((furi_get_tick() - starttime) < 30) { | ||||
|             if(avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(addr)) != 0xFF) { | ||||
|                 break; | ||||
|             }; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| static uint16_t avr_isp_prog_current_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint16_t page = 0; | ||||
|     switch(instance->cfg->pagesize) { | ||||
|     case 32: | ||||
|         page = instance->addr & 0xFFFFFFF0; | ||||
|         break; | ||||
|     case 64: | ||||
|         page = instance->addr & 0xFFFFFFE0; | ||||
|         break; | ||||
|     case 128: | ||||
|         page = instance->addr & 0xFFFFFFC0; | ||||
|         break; | ||||
|     case 256: | ||||
|         page = instance->addr & 0xFFFFFF80; | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         page = instance->addr; | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     return page; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_write_flash_pages(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     size_t x = 0; | ||||
|     uint16_t page = avr_isp_prog_current_page(instance); | ||||
|     while(x < length) { | ||||
|         if(page != avr_isp_prog_current_page(instance)) { | ||||
|             --x; | ||||
|             avr_isp_prog_commit(instance, page, instance->buff[x++]); | ||||
|             page = avr_isp_prog_current_page(instance); | ||||
|         } | ||||
|         avr_isp_prog_spi_transaction( | ||||
|             instance, AVR_ISP_WRITE_FLASH_LO(instance->addr, instance->buff[x++])); | ||||
| 
 | ||||
|         avr_isp_prog_spi_transaction( | ||||
|             instance, AVR_ISP_WRITE_FLASH_HI(instance->addr, instance->buff[x++])); | ||||
|         instance->addr++; | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_prog_commit(instance, page, instance->buff[--x]); | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_write_flash(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     avr_isp_prog_fill(instance, length); | ||||
|     if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|         avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|         avr_isp_prog_tx_ch(instance, avr_isp_prog_write_flash_pages(instance, length)); | ||||
|     } else { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // write (length) bytes, (start) is a byte address
 | ||||
| static uint8_t | ||||
|     avr_isp_prog_write_eeprom_chunk(AvrIspProg* instance, uint16_t start, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     // this writes byte-by-byte,
 | ||||
|     // page writing may be faster (4 bytes at a time)
 | ||||
|     avr_isp_prog_fill(instance, length); | ||||
|     for(uint16_t x = 0; x < length; x++) { | ||||
|         uint16_t addr = start + x; | ||||
|         avr_isp_prog_spi_transaction(instance, AVR_ISP_WRITE_EEPROM(addr, instance->buff[x])); | ||||
|         furi_delay_ms(10); | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_write_eeprom(AvrIspProg* instance, size_t length) { | ||||
|     furi_assert(instance); | ||||
|     // here is a word address, get the byte address
 | ||||
|     uint16_t start = instance->addr * 2; | ||||
|     uint16_t remaining = length; | ||||
|     if(length > instance->cfg->eepromsize) { | ||||
|         instance->error++; | ||||
|         return STK_FAILED; | ||||
|     } | ||||
|     while(remaining > AVR_ISP_EECHUNK) { | ||||
|         avr_isp_prog_write_eeprom_chunk(instance, start, AVR_ISP_EECHUNK); | ||||
|         start += AVR_ISP_EECHUNK; | ||||
|         remaining -= AVR_ISP_EECHUNK; | ||||
|     } | ||||
|     avr_isp_prog_write_eeprom_chunk(instance, start, remaining); | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_program_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t result = STK_FAILED; | ||||
|     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||
|     uint8_t memtype = avr_isp_prog_getch(instance); | ||||
|     // flash memory @addr, (length) bytes
 | ||||
|     if(memtype == STK_SET_FLASH_TYPE) { | ||||
|         avr_isp_prog_write_flash(instance, length); | ||||
|         return; | ||||
|     } | ||||
|     if(memtype == STK_SET_EEPROM_TYPE) { | ||||
|         result = avr_isp_prog_write_eeprom(instance, length); | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|             avr_isp_prog_tx_ch(instance, result); | ||||
| 
 | ||||
|         } else { | ||||
|             instance->error++; | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_FAILED); | ||||
|     return; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_flash_read_page(AvrIspProg* instance, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     for(uint16_t x = 0; x < length; x += 2) { | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, | ||||
|             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_LO(instance->addr))); | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, | ||||
|             avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_FLASH_HI(instance->addr))); | ||||
|         instance->addr++; | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static uint8_t avr_isp_prog_eeprom_read_page(AvrIspProg* instance, uint16_t length) { | ||||
|     furi_assert(instance); | ||||
|     // here again we have a word address
 | ||||
|     uint16_t start = instance->addr * 2; | ||||
|     for(uint16_t x = 0; x < length; x++) { | ||||
|         uint16_t addr = start + x; | ||||
|         avr_isp_prog_tx_ch( | ||||
|             instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_EEPROM(addr))); | ||||
|     } | ||||
|     return STK_OK; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_read_page(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t result = STK_FAILED; | ||||
|     uint16_t length = avr_isp_prog_getch(instance) << 8 | avr_isp_prog_getch(instance); | ||||
|     uint8_t memtype = avr_isp_prog_getch(instance); | ||||
|     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
|     if(memtype == STK_SET_FLASH_TYPE) result = avr_isp_prog_flash_read_page(instance, length); | ||||
|     if(memtype == STK_SET_EEPROM_TYPE) result = avr_isp_prog_eeprom_read_page(instance, length); | ||||
|     avr_isp_prog_tx_ch(instance, result); | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_prog_read_signature(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     if(avr_isp_prog_getch(instance) != CRC_EOP) { | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         return; | ||||
|     } | ||||
|     avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
| 
 | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_VENDOR)); | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_FAMILY)); | ||||
|     avr_isp_prog_tx_ch(instance, avr_isp_prog_spi_transaction(instance, AVR_ISP_READ_PART_NUMBER)); | ||||
| 
 | ||||
|     avr_isp_prog_tx_ch(instance, STK_OK); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_prog_avrisp(AvrIspProg* instance) { | ||||
|     furi_assert(instance); | ||||
|     uint8_t ch = avr_isp_prog_getch(instance); | ||||
| 
 | ||||
|     switch(ch) { | ||||
|     case STK_GET_SYNC: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_SYNC"); | ||||
|         instance->error = 0; | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_GET_SIGN_ON: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_SIGN_ON"); | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) { | ||||
|             avr_isp_prog_tx_ch(instance, STK_INSYNC); | ||||
| 
 | ||||
|             avr_isp_prog_tx_ch(instance, 'A'); | ||||
|             avr_isp_prog_tx_ch(instance, 'V'); | ||||
|             avr_isp_prog_tx_ch(instance, 'R'); | ||||
|             avr_isp_prog_tx_ch(instance, ' '); | ||||
|             avr_isp_prog_tx_ch(instance, 'I'); | ||||
|             avr_isp_prog_tx_ch(instance, 'S'); | ||||
|             avr_isp_prog_tx_ch(instance, 'P'); | ||||
| 
 | ||||
|             avr_isp_prog_tx_ch(instance, STK_OK); | ||||
|         } else { | ||||
|             instance->error++; | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         } | ||||
|         break; | ||||
|     case STK_GET_PARAMETER: | ||||
|         FURI_LOG_D(TAG, "cmd STK_GET_PARAMETER"); | ||||
|         avr_isp_prog_get_version(instance, avr_isp_prog_getch(instance)); | ||||
|         break; | ||||
|     case STK_SET_DEVICE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE"); | ||||
|         avr_isp_prog_fill(instance, 20); | ||||
|         avr_isp_prog_set_cfg(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_SET_DEVICE_EXT: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_SET_DEVICE_EXT"); | ||||
|         avr_isp_prog_fill(instance, 5); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_ENTER_PROGMODE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_ENTER_PROGMODE"); | ||||
|         if(!instance->pmode) avr_isp_prog_auto_set_spi_speed_start_pmode(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_LOAD_ADDRESS: | ||||
|         FURI_LOG_D(TAG, "cmd STK_LOAD_ADDRESS"); | ||||
|         instance->addr = avr_isp_prog_getch(instance) | avr_isp_prog_getch(instance) << 8; | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_FLASH: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_FLASH"); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_DATA: // ignore for now
 | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_DATA"); | ||||
|         avr_isp_prog_getch(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_PROG_PAGE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_PROG_PAGE"); | ||||
|         avr_isp_prog_program_page(instance); | ||||
|         break; | ||||
|     case STK_READ_PAGE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_READ_PAGE"); | ||||
|         avr_isp_prog_read_page(instance); | ||||
|         break; | ||||
|     case STK_UNIVERSAL: | ||||
|         FURI_LOG_D(TAG, "cmd STK_UNIVERSAL"); | ||||
|         avr_isp_prog_universal(instance); | ||||
|         break; | ||||
|     case STK_LEAVE_PROGMODE: | ||||
|         FURI_LOG_D(TAG, "cmd STK_LEAVE_PROGMODE"); | ||||
|         instance->error = 0; | ||||
|         if(instance->pmode) avr_isp_prog_end_pmode(instance); | ||||
|         avr_isp_prog_empty_reply(instance); | ||||
|         break; | ||||
|     case STK_READ_SIGN: | ||||
|         FURI_LOG_D(TAG, "cmd STK_READ_SIGN"); | ||||
|         avr_isp_prog_read_signature(instance); | ||||
|         break; | ||||
|     // expecting a command, not CRC_EOP
 | ||||
|     // this is how we can get back in sync
 | ||||
|     case CRC_EOP: | ||||
|         FURI_LOG_D(TAG, "cmd CRC_EOP"); | ||||
|         instance->error++; | ||||
|         avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|         break; | ||||
|     // anything else we will return STK_UNKNOWN
 | ||||
|     default: | ||||
|         FURI_LOG_D(TAG, "cmd STK_ERROR_CMD"); | ||||
|         instance->error++; | ||||
|         if(avr_isp_prog_getch(instance) == CRC_EOP) | ||||
|             avr_isp_prog_tx_ch(instance, STK_UNKNOWN); | ||||
|         else | ||||
|             avr_isp_prog_tx_ch(instance, STK_NOSYNC); | ||||
|     } | ||||
| 
 | ||||
|     if(instance->callback) { | ||||
|         instance->callback(instance->context); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										16
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,16 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "avr_isp_spi_sw.h" | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef struct AvrIspProg AvrIspProg; | ||||
| typedef void (*AvrIspProgCallback)(void* context); | ||||
| 
 | ||||
| AvrIspProg* avr_isp_prog_init(void); | ||||
| void avr_isp_prog_free(AvrIspProg* instance); | ||||
| size_t avr_isp_prog_spaces_rx(AvrIspProg* instance) ; | ||||
| bool avr_isp_prog_rx(AvrIspProg* instance, uint8_t* data, size_t len); | ||||
| size_t avr_isp_prog_tx(AvrIspProg* instance, uint8_t* data, size_t max_len); | ||||
| void avr_isp_prog_avrisp(AvrIspProg* instance); | ||||
| void avr_isp_prog_exit(AvrIspProg* instance); | ||||
| void avr_isp_prog_set_tx_callback(AvrIspProg* instance, AvrIspProgCallback callback, void* context); | ||||
							
								
								
									
										97
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_prog_cmd.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,97 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| // http://ww1.microchip.com/downloads/en/appnotes/atmel-0943-in-system-programming_applicationnote_avr910.pdf
 | ||||
| // AVR ISP Definitions
 | ||||
| #define AVR_ISP_HWVER 0X02 | ||||
| #define AVR_ISP_SWMAJ 0X01 | ||||
| #define AVR_ISP_SWMIN 0X12 | ||||
| #define AVP_ISP_SERIAL_CONNECT_TYPE 0X53 | ||||
| #define AVP_ISP_CONNECT_TYPE 0x93 | ||||
| #define AVR_ISP_RESP_0 0X00 | ||||
| 
 | ||||
| #define AVR_ISP_SET_PMODE 0xAC, 0x53, 0x00, 0x00 | ||||
| #define AVR_ISP_READ_VENDOR 0x30, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_READ_PART_FAMILY 0x30, 0x00, 0x01, 0x00 | ||||
| #define AVR_ISP_READ_PART_NUMBER 0x30, 0x00, 0x02, 0x00 | ||||
| #define AVR_ISP_ERASE_CHIP \ | ||||
|     0xAC, 0x80, 0x00, 0x00 //Erase Chip, Wait N ms, Release RESET to end the erase.
 | ||||
| //The only way to end a Chip Erase cycle is by temporarily releasing the Reset line
 | ||||
| 
 | ||||
| #define AVR_ISP_EXTENDED_ADDR(data) 0x4D, 0x00, data, 0x00 | ||||
| #define AVR_ISP_WRITE_FLASH_LO(add, data) 0x40, (add >> 8) & 0xFF, add & 0xFF, data | ||||
| #define AVR_ISP_WRITE_FLASH_HI(add, data) 0x48, (add >> 8) & 0xFF, add & 0xFF, data | ||||
| #define AVR_ISP_READ_FLASH_LO(add) 0x20, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||
| #define AVR_ISP_READ_FLASH_HI(add) 0x28, (add >> 8) & 0xFF, add & 0xFF, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_WRITE_EEPROM(add, data) \ | ||||
|     0xC0, (add >> 8) & 0xFF, add & 0xFF, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_EEPROM(add) 0xA0, (add >> 8) & 0xFF, add & 0xFF, 0xFF | ||||
| 
 | ||||
| #define AVR_ISP_COMMIT(add) \ | ||||
|     0x4C, (add >> 8) & 0xFF, add & 0xFF, 0x00 //Send cmd, polling read last addr page
 | ||||
| 
 | ||||
| #define AVR_ISP_OSCCAL(add) 0x38, 0x00, add, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_WRITE_LOCK_BYTE(data) 0xAC, 0xE0, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_LOCK_BYTE 0x58, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_LOW(data) 0xAC, 0xA0, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_FUSE_LOW 0x50, 0x00, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_HIGH(data) 0xAC, 0xA8, 0x00, data //Send cmd, Wait N ms
 | ||||
| #define AVR_ISP_READ_FUSE_HIGH 0x58, 0x08, 0x00, 0x00 | ||||
| #define AVR_ISP_WRITE_FUSE_EXTENDED(data) 0xAC, 0xA4, 0x00, data //Send cmd, Wait N ms (~write)
 | ||||
| #define AVR_ISP_READ_FUSE_EXTENDED 0x50, 0x08, 0x00, 0x00 | ||||
| 
 | ||||
| #define AVR_ISP_EECHUNK 0x20 | ||||
| 
 | ||||
| // https://www.microchip.com/content/dam/mchp/documents/OTH/ApplicationNotes/ApplicationNotes/doc2525.pdf
 | ||||
| // STK Definitions
 | ||||
| #define STK_OK 0x10 | ||||
| #define STK_FAILED 0x11 | ||||
| #define STK_UNKNOWN 0x12 | ||||
| #define STK_INSYNC 0x14 | ||||
| #define STK_NOSYNC 0x15 | ||||
| #define CRC_EOP 0x20 | ||||
| 
 | ||||
| #define STK_GET_SYNC 0x30 | ||||
| #define STK_GET_SIGN_ON 0x31 | ||||
| #define STK_SET_PARAMETER 0x40 | ||||
| #define STK_GET_PARAMETER 0x41 | ||||
| #define STK_SET_DEVICE 0x42 | ||||
| #define STK_SET_DEVICE_EXT 0x45 | ||||
| #define STK_ENTER_PROGMODE 0x50 | ||||
| #define STK_LEAVE_PROGMODE 0x51 | ||||
| #define STK_CHIP_ERASE 0x52 | ||||
| #define STK_CHECK_AUTOINC 0x53 | ||||
| #define STK_LOAD_ADDRESS 0x55 | ||||
| #define STK_UNIVERSAL 0x56 | ||||
| #define STK_UNIVERSAL_MULTI 0x57 | ||||
| #define STK_PROG_FLASH 0x60 | ||||
| #define STK_PROG_DATA 0x61 | ||||
| #define STK_PROG_FUSE 0x62 | ||||
| #define STK_PROG_FUSE_EXT 0x65 | ||||
| #define STK_PROG_LOCK 0x63 | ||||
| #define STK_PROG_PAGE 0x64 | ||||
| #define STK_READ_FLASH 0x70 | ||||
| #define STK_READ_DATA 0x71 | ||||
| #define STK_READ_FUSE 0x72 | ||||
| #define STK_READ_LOCK 0x73 | ||||
| #define STK_READ_PAGE 0x74 | ||||
| #define STK_READ_SIGN 0x75 | ||||
| #define STK_READ_OSCCAL 0x76 | ||||
| #define STK_READ_FUSE_EXT 0x77 | ||||
| #define STK_READ_OSCCAL_EXT 0x78 | ||||
| #define STK_HW_VER 0x80 | ||||
| #define STK_SW_MAJOR 0x81 | ||||
| #define STK_SW_MINOR 0x82 | ||||
| #define STK_LEDS 0x83 | ||||
| #define STK_VTARGET 0x84 | ||||
| #define STK_VADJUST 0x85 | ||||
| #define STK_OSC_PSCALE 0x86 | ||||
| #define STK_OSC_CMATCH 0x87 | ||||
| #define STK_SCK_DURATION 0x89 | ||||
| #define STK_BUFSIZEL 0x90 | ||||
| #define STK_BUFSIZEH 0x91 | ||||
| #define STK_STK500_TOPCARD_DETECT 0x98 | ||||
| 
 | ||||
| #define STK_SET_EEPROM_TYPE 0X45 | ||||
| #define STK_SET_FLASH_TYPE 0X46 | ||||
							
								
								
									
										73
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,73 @@ | ||||
| #include "avr_isp_spi_sw.h" | ||||
| 
 | ||||
| #include <furi.h> | ||||
| 
 | ||||
| #define AVR_ISP_SPI_SW_MISO &gpio_ext_pa6 | ||||
| #define AVR_ISP_SPI_SW_MOSI &gpio_ext_pa7 | ||||
| #define AVR_ISP_SPI_SW_SCK &gpio_ext_pb3 | ||||
| #define AVR_ISP_RESET &gpio_ext_pb2 | ||||
| 
 | ||||
| struct AvrIspSpiSw { | ||||
|     AvrIspSpiSwSpeed speed_wait_time; | ||||
|     const GpioPin* miso; | ||||
|     const GpioPin* mosi; | ||||
|     const GpioPin* sck; | ||||
|     const GpioPin* res; | ||||
| }; | ||||
| 
 | ||||
| AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed) { | ||||
|     AvrIspSpiSw* instance = malloc(sizeof(AvrIspSpiSw)); | ||||
|     instance->speed_wait_time = speed; | ||||
| 
 | ||||
|     instance->miso = AVR_ISP_SPI_SW_MISO; | ||||
|     instance->mosi = AVR_ISP_SPI_SW_MOSI; | ||||
|     instance->sck = AVR_ISP_SPI_SW_SCK; | ||||
|     instance->res = AVR_ISP_RESET; | ||||
| 
 | ||||
|     furi_hal_gpio_init(instance->miso, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_write(instance->mosi, false); | ||||
|     furi_hal_gpio_init(instance->mosi, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_write(instance->sck, false); | ||||
|     furi_hal_gpio_init(instance->sck, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
|     furi_hal_gpio_init(instance->res, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_free(AvrIspSpiSw* instance) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_init(instance->res, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->miso, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->mosi, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
|     furi_hal_gpio_init(instance->sck, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||
| 
 | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data) { | ||||
|     furi_assert(instance); | ||||
|     for(uint8_t i = 0; i < 8; ++i) { | ||||
|         furi_hal_gpio_write(instance->mosi, (data & 0x80) ? true : false); | ||||
| 
 | ||||
|         furi_hal_gpio_write(instance->sck, true); | ||||
|         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||
|             furi_delay_us(instance->speed_wait_time - 1); | ||||
| 
 | ||||
|         data = (data << 1) | furi_hal_gpio_read(instance->miso); //-V792
 | ||||
| 
 | ||||
|         furi_hal_gpio_write(instance->sck, false); | ||||
|         if(instance->speed_wait_time != AvrIspSpiSwSpeed1Mhz) | ||||
|             furi_delay_us(instance->speed_wait_time - 1); | ||||
|     } | ||||
|     return data; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_write(instance->res, state); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state) { | ||||
|     furi_assert(instance); | ||||
|     furi_hal_gpio_write(instance->sck, state); | ||||
| } | ||||
							
								
								
									
										24
									
								
								applications/external/avr_isp_programmer/lib/driver/avr_isp_spi_sw.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,24 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <furi_hal.h> | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspSpiSwSpeed1Mhz = 0, | ||||
|     AvrIspSpiSwSpeed400Khz = 1, | ||||
|     AvrIspSpiSwSpeed250Khz = 2, | ||||
|     AvrIspSpiSwSpeed125Khz = 4, | ||||
|     AvrIspSpiSwSpeed60Khz = 8, | ||||
|     AvrIspSpiSwSpeed40Khz = 12, | ||||
|     AvrIspSpiSwSpeed20Khz = 24, | ||||
|     AvrIspSpiSwSpeed10Khz = 48, | ||||
|     AvrIspSpiSwSpeed5Khz = 96, | ||||
|     AvrIspSpiSwSpeed1Khz = 480, | ||||
| } AvrIspSpiSwSpeed; | ||||
| 
 | ||||
| typedef struct AvrIspSpiSw AvrIspSpiSw; | ||||
| 
 | ||||
| AvrIspSpiSw* avr_isp_spi_sw_init(AvrIspSpiSwSpeed speed); | ||||
| void avr_isp_spi_sw_free(AvrIspSpiSw* instance); | ||||
| uint8_t avr_isp_spi_sw_txrx(AvrIspSpiSw* instance, uint8_t data); | ||||
| void avr_isp_spi_sw_res_set(AvrIspSpiSw* instance, bool state); | ||||
| void avr_isp_spi_sw_sck_set(AvrIspSpiSw* instance, bool state); | ||||
							
								
								
									
										
											BIN
										
									
								
								applications/external/avr_isp_programmer/lib/driver/clock.png
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										30
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,30 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| // Generate scene on_enter handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||
| void (*const avr_isp_scene_on_enter_handlers[])(void*) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_event handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||
| bool (*const avr_isp_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_exit handlers array
 | ||||
| #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||
| void (*const avr_isp_scene_on_exit_handlers[])(void* context) = { | ||||
| #include "avr_isp_scene_config.h" | ||||
| }; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Initialize scene handlers configuration structure
 | ||||
| const SceneManagerHandlers avr_isp_scene_handlers = { | ||||
|     .on_enter_handlers = avr_isp_scene_on_enter_handlers, | ||||
|     .on_event_handlers = avr_isp_scene_on_event_handlers, | ||||
|     .on_exit_handlers = avr_isp_scene_on_exit_handlers, | ||||
|     .scene_num = AvrIspSceneNum, | ||||
| }; | ||||
							
								
								
									
										29
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,29 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/scene_manager.h> | ||||
| 
 | ||||
| // Generate scene id and total number
 | ||||
| #define ADD_SCENE(prefix, name, id) AvrIspScene##id, | ||||
| typedef enum { | ||||
| #include "avr_isp_scene_config.h" | ||||
|     AvrIspSceneNum, | ||||
| } AvrIspScene; | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| extern const SceneManagerHandlers avr_isp_scene_handlers; | ||||
| 
 | ||||
| // Generate scene on_enter handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_event handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) \ | ||||
|     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
| 
 | ||||
| // Generate scene on_exit handlers declaration
 | ||||
| #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||
| #include "avr_isp_scene_config.h" | ||||
| #undef ADD_SCENE | ||||
							
								
								
									
										99
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_about.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,99 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| 
 | ||||
| void avr_isp_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(type == InputTypeShort) { | ||||
|         view_dispatcher_send_custom_event(app->view_dispatcher, result); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_about_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     FuriString* temp_str = furi_string_alloc(); | ||||
|     furi_string_printf(temp_str, "\e#%s\n", "Information"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "Version: %s\n", AVR_ISP_VERSION_APP); | ||||
|     furi_string_cat_printf(temp_str, "Developed by: %s\n", AVR_ISP_DEVELOPED); | ||||
|     furi_string_cat_printf(temp_str, "Github: %s\n\n", AVR_ISP_GITHUB); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "This application is an AVR in-system programmer based on stk500mk1. It is compatible with AVR-based" | ||||
|         " microcontrollers including Arduino. You can also use it to repair the chip if you accidentally" | ||||
|         " corrupt the bootloader.\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); | ||||
|     furi_string_cat_printf(temp_str, "- Create a dump of your chip on an SD card\n"); | ||||
|     furi_string_cat_printf(temp_str, "- Flash your chip firmware from the SD card\n"); | ||||
|     furi_string_cat_printf(temp_str, "- Act as a wired USB ISP using avrdude software\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Supported chip series:"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "Example command for avrdude flashing: avrdude.exe -p m328p -c stk500v1 -P COMxx -U flash:r:" | ||||
|         "X:\\sketch_sample.hex" | ||||
|         ":i\n"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "Where: " | ||||
|         "-p m328p" | ||||
|         " brand of your chip, " | ||||
|         "-P COMxx" | ||||
|         " com port number in the system when " | ||||
|         "ISP Programmer" | ||||
|         " is enabled\n\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf(temp_str, "\e#%s\n", "Info"); | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, | ||||
|         "ATtinyXXXX\nATmegaXXXX\nAT43Uxxx\nAT76C711\nAT86RF401\nAT90xxxxx\nAT94K\n" | ||||
|         "ATAxxxxx\nATA664251\nM3000\nLGT8F88P\nLGT8F168P\nLGT8F328P\n"); | ||||
| 
 | ||||
|     furi_string_cat_printf( | ||||
|         temp_str, "For a more detailed list of supported chips, see AVRDude help\n"); | ||||
| 
 | ||||
|     widget_add_text_box_element( | ||||
|         app->widget, | ||||
|         0, | ||||
|         0, | ||||
|         128, | ||||
|         14, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         "\e#\e!                                                      \e!\n", | ||||
|         false); | ||||
|     widget_add_text_box_element( | ||||
|         app->widget, | ||||
|         0, | ||||
|         2, | ||||
|         128, | ||||
|         14, | ||||
|         AlignCenter, | ||||
|         AlignBottom, | ||||
|         "\e#\e!        ISP Programmer       \e!\n", | ||||
|         false); | ||||
|     widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); | ||||
|     furi_string_free(temp_str); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_about_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_about_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Clear views
 | ||||
|     widget_reset(app->widget); | ||||
| } | ||||
							
								
								
									
										72
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,72 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     switch(app->error) { | ||||
|     case AvrIspErrorReading: | ||||
|     case AvrIspErrorWriting: | ||||
|     case AvrIspErrorWritingFuse: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorOccured); | ||||
|         break; | ||||
|     case AvrIspErrorVerification: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateErrorVerification); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         avr_isp_chip_detect_set_state( | ||||
|             app->avr_isp_chip_detect_view, AvrIspChipDetectViewStateNoDetect); | ||||
|         break; | ||||
|     } | ||||
|     app->error = AvrIspErrorNoError; | ||||
|     avr_isp_chip_detect_view_set_callback( | ||||
|         app->avr_isp_chip_detect_view, avr_isp_scene_chip_detect_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewChipDetect); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_chip_detect_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneChipDetectOk: | ||||
| 
 | ||||
|             if(scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                AvrIspViewProgrammer) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneProgrammer); | ||||
|             } else if( | ||||
|                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                 AvrIspViewReader) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneInputName); | ||||
|             } else if( | ||||
|                 scene_manager_get_scene_state(app->scene_manager, AvrIspSceneChipDetect) == | ||||
|                 AvrIspViewWriter) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneLoad); | ||||
|             } | ||||
| 
 | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_chip_detect_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										10
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_config.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,10 @@ | ||||
| ADD_SCENE(avr_isp, start, Start) | ||||
| ADD_SCENE(avr_isp, about, About) | ||||
| ADD_SCENE(avr_isp, programmer, Programmer) | ||||
| ADD_SCENE(avr_isp, reader, Reader) | ||||
| ADD_SCENE(avr_isp, input_name, InputName) | ||||
| ADD_SCENE(avr_isp, load, Load) | ||||
| ADD_SCENE(avr_isp, writer, Writer) | ||||
| ADD_SCENE(avr_isp, wiring, Wiring) | ||||
| ADD_SCENE(avr_isp, chip_detect, ChipDetect) | ||||
| ADD_SCENE(avr_isp, success, Success) | ||||
							
								
								
									
										89
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_input_name.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,89 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| #include <gui/modules/validators.h> | ||||
| 
 | ||||
| #define MAX_TEXT_INPUT_LEN 22 | ||||
| 
 | ||||
| void avr_isp_scene_input_name_text_callback(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneInputName); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_get_timefilename(FuriString* name) { | ||||
|     FuriHalRtcDateTime datetime = {0}; | ||||
|     furi_hal_rtc_get_datetime(&datetime); | ||||
|     furi_string_printf( | ||||
|         name, | ||||
|         "AVR_dump-%.4d%.2d%.2d-%.2d%.2d%.2d", | ||||
|         datetime.year, | ||||
|         datetime.month, | ||||
|         datetime.day, | ||||
|         datetime.hour, | ||||
|         datetime.minute, | ||||
|         datetime.second); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Setup view
 | ||||
|     TextInput* text_input = app->text_input; | ||||
|     bool dev_name_empty = false; | ||||
| 
 | ||||
|     FuriString* file_name = furi_string_alloc(); | ||||
| 
 | ||||
|     avr_isp_scene_input_name_get_timefilename(file_name); | ||||
|     furi_string_set(app->file_path, STORAGE_APP_DATA_PATH_PREFIX); | ||||
|     //highlighting the entire filename by default
 | ||||
|     dev_name_empty = true; | ||||
| 
 | ||||
|     strncpy(app->file_name_tmp, furi_string_get_cstr(file_name), AVR_ISP_MAX_LEN_NAME); | ||||
|     text_input_set_header_text(text_input, "Name dump"); | ||||
|     text_input_set_result_callback( | ||||
|         text_input, | ||||
|         avr_isp_scene_input_name_text_callback, | ||||
|         app, | ||||
|         app->file_name_tmp, | ||||
|         MAX_TEXT_INPUT_LEN, // buffer size
 | ||||
|         dev_name_empty); | ||||
| 
 | ||||
|     ValidatorIsFile* validator_is_file = | ||||
|         validator_is_file_alloc_init(STORAGE_APP_DATA_PATH_PREFIX, AVR_ISP_APP_EXTENSION, ""); | ||||
|     text_input_set_validator(text_input, validator_is_file_callback, validator_is_file); | ||||
| 
 | ||||
|     furi_string_free(file_name); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewTextInput); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_input_name_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         scene_manager_previous_scene(app->scene_manager); | ||||
|         return true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == AvrIspCustomEventSceneInputName) { | ||||
|             if(strcmp(app->file_name_tmp, "") != 0) { | ||||
|                 scene_manager_next_scene(app->scene_manager, AvrIspSceneReader); | ||||
|             } else { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_input_name_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     // Clear validator
 | ||||
|     void* validator_context = text_input_get_validator_callback_context(app->text_input); | ||||
|     text_input_set_validator(app->text_input, NULL, NULL); | ||||
|     validator_is_file_free(validator_context); | ||||
|     // Clear view
 | ||||
|     text_input_reset(app->text_input); | ||||
| } | ||||
							
								
								
									
										22
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_load.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,22 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_load_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(avr_isp_load_from_file(app)) { | ||||
|         scene_manager_next_scene(app->scene_manager, AvrIspSceneWriter); | ||||
|     } else { | ||||
|         scene_manager_previous_scene(app->scene_manager); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_load_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_load_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										28
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,28 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_programmer_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_programmer_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_programmer_view_set_callback( | ||||
|         app->avr_isp_programmer_view, avr_isp_scene_programmer_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewProgrammer); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_programmer_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_programmer_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										64
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,64 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_reader_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_reader_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_reader_set_file_path( | ||||
|         app->avr_isp_reader_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||
|     avr_isp_reader_view_set_callback(app->avr_isp_reader_view, avr_isp_scene_reader_callback, app); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewReader); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_reader_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     UNUSED(app); | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         //do not handle exit on "Back"
 | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneReadingOk: | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneSuccess); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneExit: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorVerification: | ||||
|             app->error = AvrIspErrorVerification; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorReading: | ||||
|             app->error = AvrIspErrorReading; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         avr_isp_reader_update_progress(app->avr_isp_reader_view); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_reader_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										75
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_start.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,75 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_start_submenu_callback(void* context, uint32_t index) { | ||||
|     furi_assert(context); | ||||
|     AvrIspApp* app = context; | ||||
| 
 | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, index); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_start_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Submenu* submenu = app->submenu; | ||||
|     submenu_add_item( | ||||
|         submenu, "Dump AVR", SubmenuIndexAvrIspReader, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, "Flash AVR", SubmenuIndexAvrIspWriter, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, | ||||
|         "ISP Programmer", | ||||
|         SubmenuIndexAvrIspProgrammer, | ||||
|         avr_isp_scene_start_submenu_callback, | ||||
|         app); | ||||
|     submenu_add_item( | ||||
|         submenu, "Wiring", SubmenuIndexAvrIsWiring, avr_isp_scene_start_submenu_callback, app); | ||||
|     submenu_add_item( | ||||
|         submenu, "About", SubmenuIndexAvrIspAbout, avr_isp_scene_start_submenu_callback, app); | ||||
| 
 | ||||
|     submenu_set_selected_item( | ||||
|         submenu, scene_manager_get_scene_state(app->scene_manager, AvrIspSceneStart)); | ||||
| 
 | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewSubmenu); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_start_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == SubmenuIndexAvrIspAbout) { | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneAbout); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspProgrammer) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewProgrammer); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspReader) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewReader); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIspWriter) { | ||||
|             scene_manager_set_scene_state( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect, AvrIspViewWriter); | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|         } else if(event.event == SubmenuIndexAvrIsWiring) { | ||||
|             scene_manager_next_scene(app->scene_manager, AvrIspSceneWiring); | ||||
|             consumed = true; | ||||
|         } | ||||
|         scene_manager_set_scene_state(app->scene_manager, AvrIspSceneStart, event.event); | ||||
|     } | ||||
| 
 | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_start_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     submenu_reset(app->submenu); | ||||
| } | ||||
							
								
								
									
										44
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_success.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,44 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_success_popup_callback(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, AvrIspCustomEventSceneSuccess); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_success_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Popup* popup = app->popup; | ||||
|     popup_set_icon(popup, 32, 5, &I_dolphin_nice_96x59); | ||||
|     popup_set_header(popup, "Success!", 8, 22, AlignLeft, AlignBottom); | ||||
|     popup_set_timeout(popup, 1500); | ||||
|     popup_set_context(popup, app); | ||||
|     popup_set_callback(popup, avr_isp_scene_success_popup_callback); | ||||
|     popup_enable_timeout(popup); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewPopup); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_success_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     if(event.type == SceneManagerEventTypeCustom) { | ||||
|         if(event.event == AvrIspCustomEventSceneSuccess) { | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneStart); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_success_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     Popup* popup = app->popup; | ||||
|     popup_reset(popup); | ||||
| } | ||||
							
								
								
									
										21
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_wiring.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,21 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_wiring_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     widget_add_icon_element(app->widget, 0, 0, &I_avr_wiring); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWidget); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_wiring_on_event(void* context, SceneManagerEvent event) { | ||||
|     UNUSED(context); | ||||
|     UNUSED(event); | ||||
|     return false; | ||||
| } | ||||
| void avr_isp_scene_wiring_on_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     widget_reset(app->widget); | ||||
| } | ||||
							
								
								
									
										69
									
								
								applications/external/avr_isp_programmer/scenes/avr_isp_scene_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,69 @@ | ||||
| #include "../avr_isp_app_i.h" | ||||
| 
 | ||||
| void avr_isp_scene_writer_callback(AvrIspCustomEvent event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     view_dispatcher_send_custom_event(app->view_dispatcher, event); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_writer_on_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     avr_isp_writer_set_file_path( | ||||
|         app->avr_isp_writer_view, furi_string_get_cstr(app->file_path), app->file_name_tmp); | ||||
|     avr_isp_writer_view_set_callback(app->avr_isp_writer_view, avr_isp_scene_writer_callback, app); | ||||
|     view_dispatcher_switch_to_view(app->view_dispatcher, AvrIspViewWriter); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_scene_writer_on_event(void* context, SceneManagerEvent event) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspApp* app = context; | ||||
|     bool consumed = false; | ||||
|     if(event.type == SceneManagerEventTypeBack) { | ||||
|         //do not handle exit on "Back"
 | ||||
|         consumed = true; | ||||
|     } else if(event.type == SceneManagerEventTypeCustom) { | ||||
|         switch(event.event) { | ||||
|         case AvrIspCustomEventSceneExitStartMenu: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneStart); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneExit: | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorVerification: | ||||
|             app->error = AvrIspErrorVerification; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorWriting: | ||||
|             app->error = AvrIspErrorWriting; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         case AvrIspCustomEventSceneErrorWritingFuse: | ||||
|             app->error = AvrIspErrorWritingFuse; | ||||
|             scene_manager_search_and_switch_to_previous_scene( | ||||
|                 app->scene_manager, AvrIspSceneChipDetect); | ||||
|             consumed = true; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } else if(event.type == SceneManagerEventTypeTick) { | ||||
|         avr_isp_writer_update_progress(app->avr_isp_writer_view); | ||||
|     } | ||||
|     return consumed; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_scene_writer_on_exit(void* context) { | ||||
|     UNUSED(context); | ||||
| } | ||||
							
								
								
									
										213
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,213 @@ | ||||
| #include "avr_isp_view_chip_detect.h" | ||||
| #include <avr_isp_icons.h> | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| 
 | ||||
| struct AvrIspChipDetectView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     AvrIspChipDetectViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     uint16_t idx; | ||||
|     const char* name_chip; | ||||
|     uint32_t flash_size; | ||||
|     AvrIspChipDetectViewState state; | ||||
| } AvrIspChipDetectViewModel; | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_set_callback( | ||||
|     AvrIspChipDetectView* instance, | ||||
|     AvrIspChipDetectViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, AvrIspChipDetectViewModel * model, { model->state = state; }, true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_draw(Canvas* canvas, AvrIspChipDetectViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
| 
 | ||||
|     char str_buf[64] = {0}; | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     switch(model->state) { | ||||
|     case AvrIspChipDetectViewStateDetected: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip detected!"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_long_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         snprintf(str_buf, sizeof(str_buf), "%ld Kb", model->flash_size / 1024); | ||||
|         canvas_draw_str_aligned(canvas, 64, 25, AlignCenter, AlignCenter, str_buf); | ||||
|         canvas_draw_str_aligned(canvas, 64, 45, AlignCenter, AlignCenter, model->name_chip); | ||||
|         elements_button_right(canvas, "Next"); | ||||
|         break; | ||||
|     case AvrIspChipDetectViewStateErrorOccured: | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 5, AlignCenter, AlignCenter, "Error occured, try again!"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 45, AlignCenter, AlignCenter, "Check the wiring and retry"); | ||||
|         break; | ||||
|     case AvrIspChipDetectViewStateErrorVerification: | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 5, AlignCenter, AlignCenter, "Data verification failed"); | ||||
|         canvas_draw_icon(canvas, 29, 14, &I_chip_error_70x22); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_str_aligned( | ||||
|             canvas, 64, 45, AlignCenter, AlignCenter, "Try to restart the process"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         //AvrIspChipDetectViewStateNoDetect
 | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "AVR chip not found!"); | ||||
|         canvas_draw_icon(canvas, 29, 12, &I_chif_not_found_83x37); | ||||
| 
 | ||||
|         break; | ||||
|     } | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     elements_button_left(canvas, "Retry"); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_chip_detect_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
| 
 | ||||
|     if(event->type == InputTypeShort) { | ||||
|         if(event->key == InputKeyBack) { | ||||
|             return false; | ||||
|         } else if(event->key == InputKeyRight) { | ||||
|             with_view_model( | ||||
|                 instance->view, | ||||
|                 AvrIspChipDetectViewModel * model, | ||||
|                 { | ||||
|                     if(model->state == AvrIspChipDetectViewStateDetected) { | ||||
|                         if(instance->callback) | ||||
|                             instance->callback( | ||||
|                                 AvrIspCustomEventSceneChipDetectOk, instance->context); | ||||
|                     } | ||||
|                 }, | ||||
|                 false); | ||||
| 
 | ||||
|         } else if(event->key == InputKeyLeft) { | ||||
|             bool detect_chip = false; | ||||
|             with_view_model( | ||||
|                 instance->view, | ||||
|                 AvrIspChipDetectViewModel * model, | ||||
|                 { | ||||
|                     if(model->state != AvrIspChipDetectViewStateDetecting) { | ||||
|                         model->state = AvrIspChipDetectViewStateDetecting; | ||||
|                         detect_chip = true; | ||||
|                     } | ||||
|                 }, | ||||
|                 false); | ||||
|             if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||
|         } | ||||
|     } else { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_chip_detect_detect_chip_callback( | ||||
|     void* context, | ||||
|     const char* name, | ||||
|     bool detect_chip, | ||||
|     uint32_t flash_size) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { | ||||
|             model->name_chip = name; | ||||
|             model->flash_size = flash_size; | ||||
|             if(detect_chip) { | ||||
|                 model->state = AvrIspChipDetectViewStateDetected; | ||||
|             } else { | ||||
|                 model->state = AvrIspChipDetectViewStateNoDetect; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| void avr_isp_chip_detect_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
|     bool detect_chip = false; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { | ||||
|             if(model->state == AvrIspChipDetectViewStateNoDetect || | ||||
|                model->state == AvrIspChipDetectViewStateDetected) { | ||||
|                 detect_chip = true; | ||||
|             } | ||||
|         }, | ||||
|         false); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback( | ||||
|         instance->avr_isp_worker_rw, avr_isp_chip_detect_detect_chip_callback, instance); | ||||
| 
 | ||||
|     if(detect_chip) avr_isp_worker_rw_detect_chip(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspChipDetectView* instance = context; | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback(instance->avr_isp_worker_rw, NULL, NULL); | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspChipDetectView* avr_isp_chip_detect_view_alloc() { | ||||
|     AvrIspChipDetectView* instance = malloc(sizeof(AvrIspChipDetectView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspChipDetectViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_chip_detect_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_chip_detect_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_chip_detect_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_chip_detect_view_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspChipDetectViewModel * model, | ||||
|         { model->state = AvrIspChipDetectViewStateNoDetect; }, | ||||
|         false); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										32
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_chip_detect.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,32 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspChipDetectView AvrIspChipDetectView; | ||||
| 
 | ||||
| typedef void (*AvrIspChipDetectViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspChipDetectViewStateNoDetect, | ||||
|     AvrIspChipDetectViewStateDetecting, | ||||
|     AvrIspChipDetectViewStateDetected, | ||||
|     AvrIspChipDetectViewStateErrorOccured, | ||||
|     AvrIspChipDetectViewStateErrorVerification, | ||||
| } AvrIspChipDetectViewState; | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_set_callback( | ||||
|     AvrIspChipDetectView* instance, | ||||
|     AvrIspChipDetectViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| void avr_isp_chip_detect_set_state(AvrIspChipDetectView* instance, AvrIspChipDetectViewState state); | ||||
| 
 | ||||
| AvrIspChipDetectView* avr_isp_chip_detect_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_free(AvrIspChipDetectView* instance); | ||||
| 
 | ||||
| View* avr_isp_chip_detect_view_get_view(AvrIspChipDetectView* instance); | ||||
| 
 | ||||
| void avr_isp_chip_detect_view_exit(void* context); | ||||
							
								
								
									
										134
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,134 @@ | ||||
| #include "avr_isp_view_programmer.h" | ||||
| #include <avr_isp_icons.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| struct AvrIspProgrammerView { | ||||
|     View* view; | ||||
|     AvrIspWorker* worker; | ||||
|     AvrIspProgrammerViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspProgrammerViewStatus status; | ||||
| } AvrIspProgrammerViewModel; | ||||
| 
 | ||||
| void avr_isp_programmer_view_set_callback( | ||||
|     AvrIspProgrammerView* instance, | ||||
|     AvrIspProgrammerViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_draw(Canvas* canvas, AvrIspProgrammerViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
| 
 | ||||
|     if(model->status == AvrIspProgrammerViewStatusUSBConnect) { | ||||
|         canvas_set_font(canvas, FontPrimary); | ||||
|         canvas_draw_icon(canvas, 0, 0, &I_isp_active_128x53); | ||||
|         elements_multiline_text(canvas, 45, 10, "ISP mode active"); | ||||
|     } else { | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         canvas_draw_icon(canvas, 51, 6, &I_link_waiting_77x56); | ||||
|         elements_multiline_text(canvas, 0, 25, "Waiting for\nsoftware\nconnection"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_programmer_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     UNUSED(context); | ||||
| 
 | ||||
|     if(event->key == InputKeyBack || event->type != InputTypeShort) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_programmer_usb_connect_callback(void* context, bool status_connect) { | ||||
|     furi_assert(context); | ||||
|     AvrIspProgrammerView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { | ||||
|             if(status_connect) { | ||||
|                 model->status = AvrIspProgrammerViewStatusUSBConnect; | ||||
|             } else { | ||||
|                 model->status = AvrIspProgrammerViewStatusNoUSBConnect; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspProgrammerView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start worker
 | ||||
|     instance->worker = avr_isp_worker_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_set_callback( | ||||
|         instance->worker, avr_isp_programmer_usb_connect_callback, instance); | ||||
| 
 | ||||
|     avr_isp_worker_start(instance->worker); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspProgrammerView* instance = context; | ||||
|     //Stop worker
 | ||||
|     if(avr_isp_worker_is_running(instance->worker)) { | ||||
|         avr_isp_worker_stop(instance->worker); | ||||
|     } | ||||
|     avr_isp_worker_set_callback(instance->worker, NULL, NULL); | ||||
|     avr_isp_worker_free(instance->worker); | ||||
| } | ||||
| 
 | ||||
| AvrIspProgrammerView* avr_isp_programmer_view_alloc() { | ||||
|     AvrIspProgrammerView* instance = malloc(sizeof(AvrIspProgrammerView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspProgrammerViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_programmer_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_programmer_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_programmer_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_programmer_view_exit); | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspProgrammerViewModel * model, | ||||
|         { model->status = AvrIspProgrammerViewStatusNoUSBConnect; }, | ||||
|         false); | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_programmer_view_free(AvrIspProgrammerView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										27
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_programmer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,27 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspProgrammerView AvrIspProgrammerView; | ||||
| 
 | ||||
| typedef void (*AvrIspProgrammerViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspProgrammerViewStatusNoUSBConnect, | ||||
|     AvrIspProgrammerViewStatusUSBConnect, | ||||
| } AvrIspProgrammerViewStatus; | ||||
| 
 | ||||
| void avr_isp_programmer_view_set_callback( | ||||
|     AvrIspProgrammerView* instance, | ||||
|     AvrIspProgrammerViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspProgrammerView* avr_isp_programmer_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_programmer_view_free(AvrIspProgrammerView* instance); | ||||
| 
 | ||||
| View* avr_isp_programmer_view_get_view(AvrIspProgrammerView* instance); | ||||
| 
 | ||||
| void avr_isp_programmer_view_exit(void* context); | ||||
							
								
								
									
										215
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,215 @@ | ||||
| #include "avr_isp_view_reader.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| 
 | ||||
| struct AvrIspReaderView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     const char* file_path; | ||||
|     const char* file_name; | ||||
|     AvrIspReaderViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspReaderViewStatus status; | ||||
|     float progress_flash; | ||||
|     float progress_eeprom; | ||||
| } AvrIspReaderViewModel; | ||||
| 
 | ||||
| void avr_isp_reader_update_progress(AvrIspReaderView* instance) { | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             model->progress_flash = | ||||
|                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||
|             model->progress_eeprom = | ||||
|                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_set_callback( | ||||
|     AvrIspReaderView* instance, | ||||
|     AvrIspReaderViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_set_file_path( | ||||
|     AvrIspReaderView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->file_path = file_path; | ||||
|     instance->file_name = file_name; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_draw(Canvas* canvas, AvrIspReaderViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
|     char str_buf[64] = {0}; | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
|     switch(model->status) { | ||||
|     case AvrIspReaderViewStatusIDLE: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to dump"); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Start"); | ||||
|         break; | ||||
|     case AvrIspReaderViewStatusReading: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Reading dump"); | ||||
|         break; | ||||
|     case AvrIspReaderViewStatusVerification: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifyng dump"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||
|     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_buf); | ||||
|     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||
|     snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_buf); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_reader_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     bool ret = true; | ||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspReaderViewModel * model, | ||||
|             { | ||||
|                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspReaderViewModel * model, | ||||
|             { | ||||
|                 if(model->status == AvrIspReaderViewStatusIDLE) { | ||||
|                     model->status = AvrIspReaderViewStatusReading; | ||||
|                     avr_isp_worker_rw_read_dump_start( | ||||
|                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_reader_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             switch(status) { | ||||
|             case AvrIspWorkerRWStatusEndReading: | ||||
|                 model->status = AvrIspReaderViewStatusVerification; | ||||
|                 avr_isp_worker_rw_verification_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspReaderViewStatusVerification; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneReadingOk, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 //AvrIspWorkerRWStatusErrorReading;
 | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorReading, instance->context); | ||||
|                 break; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspReaderView* instance = context; | ||||
| 
 | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspReaderViewModel * model, | ||||
|         { | ||||
|             model->status = AvrIspReaderViewStatusIDLE; | ||||
|             model->progress_flash = 0.0f; | ||||
|             model->progress_eeprom = 0.0f; | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback_status( | ||||
|         instance->avr_isp_worker_rw, avr_isp_reader_callback_status, instance); | ||||
| 
 | ||||
|     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspReaderView* instance = context; | ||||
|     //Stop avr_isp_worker_rw
 | ||||
|     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||
|         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspReaderView* avr_isp_reader_view_alloc() { | ||||
|     AvrIspReaderView* instance = malloc(sizeof(AvrIspReaderView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspReaderViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_reader_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_reader_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_reader_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_reader_view_exit); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_reader_view_free(AvrIspReaderView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_reader_view_get_view(AvrIspReaderView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										35
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_reader.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,35 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspReaderView AvrIspReaderView; | ||||
| 
 | ||||
| typedef void (*AvrIspReaderViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspReaderViewStatusIDLE, | ||||
|     AvrIspReaderViewStatusReading, | ||||
|     AvrIspReaderViewStatusVerification, | ||||
| } AvrIspReaderViewStatus; | ||||
| 
 | ||||
| void avr_isp_reader_update_progress(AvrIspReaderView* instance); | ||||
| 
 | ||||
| void avr_isp_reader_set_file_path( | ||||
|     AvrIspReaderView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_reader_view_set_callback( | ||||
|     AvrIspReaderView* instance, | ||||
|     AvrIspReaderViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspReaderView* avr_isp_reader_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_reader_view_free(AvrIspReaderView* instance); | ||||
| 
 | ||||
| View* avr_isp_reader_view_get_view(AvrIspReaderView* instance); | ||||
| 
 | ||||
| void avr_isp_reader_view_exit(void* context); | ||||
							
								
								
									
										268
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.c
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,268 @@ | ||||
| #include "avr_isp_view_writer.h" | ||||
| #include <gui/elements.h> | ||||
| 
 | ||||
| #include "../helpers/avr_isp_worker_rw.h" | ||||
| #include <float_tools.h> | ||||
| 
 | ||||
| struct AvrIspWriterView { | ||||
|     View* view; | ||||
|     AvrIspWorkerRW* avr_isp_worker_rw; | ||||
|     const char* file_path; | ||||
|     const char* file_name; | ||||
|     AvrIspWriterViewCallback callback; | ||||
|     void* context; | ||||
| }; | ||||
| 
 | ||||
| typedef struct { | ||||
|     AvrIspWriterViewStatus status; | ||||
|     float progress_flash; | ||||
|     float progress_eeprom; | ||||
| } AvrIspWriterViewModel; | ||||
| 
 | ||||
| void avr_isp_writer_update_progress(AvrIspWriterView* instance) { | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             model->progress_flash = | ||||
|                 avr_isp_worker_rw_get_progress_flash(instance->avr_isp_worker_rw); | ||||
|             model->progress_eeprom = | ||||
|                 avr_isp_worker_rw_get_progress_eeprom(instance->avr_isp_worker_rw); | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_set_callback( | ||||
|     AvrIspWriterView* instance, | ||||
|     AvrIspWriterViewCallback callback, | ||||
|     void* context) { | ||||
|     furi_assert(instance); | ||||
|     furi_assert(callback); | ||||
| 
 | ||||
|     instance->callback = callback; | ||||
|     instance->context = context; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_set_file_path( | ||||
|     AvrIspWriterView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     instance->file_path = file_path; | ||||
|     instance->file_name = file_name; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_draw(Canvas* canvas, AvrIspWriterViewModel* model) { | ||||
|     canvas_clear(canvas); | ||||
|     char str_flash[32] = {0}; | ||||
|     char str_eeprom[32] = {0}; | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontPrimary); | ||||
| 
 | ||||
|     switch(model->status) { | ||||
|     case AvrIspWriterViewStatusIDLE: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Press start to write"); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Start"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWriting: | ||||
|         if(float_is_equal(model->progress_flash, 0.f)) { | ||||
|             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying firmware"); | ||||
|             snprintf(str_flash, sizeof(str_flash), "***"); | ||||
|             snprintf(str_eeprom, sizeof(str_eeprom), "***"); | ||||
|         } else { | ||||
|             canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing dump"); | ||||
|             snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|             snprintf( | ||||
|                 str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         } | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusVerification: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Verifying dump"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWritingFuse: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Writing fuse"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         break; | ||||
|     case AvrIspWriterViewStatusWritingFuseOk: | ||||
|         canvas_draw_str_aligned(canvas, 64, 5, AlignCenter, AlignCenter, "Done!"); | ||||
|         snprintf(str_flash, sizeof(str_flash), "%d%%", (uint8_t)(model->progress_flash * 100)); | ||||
|         snprintf(str_eeprom, sizeof(str_eeprom), "%d%%", (uint8_t)(model->progress_eeprom * 100)); | ||||
|         canvas_set_font(canvas, FontSecondary); | ||||
|         elements_button_center(canvas, "Reflash"); | ||||
|         elements_button_right(canvas, "Exit"); | ||||
|         break; | ||||
| 
 | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
| 
 | ||||
|     canvas_set_font(canvas, FontSecondary); | ||||
|     canvas_draw_str(canvas, 0, 27, "Flash"); | ||||
|     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_flash * 100));
 | ||||
|     elements_progress_bar_with_text(canvas, 44, 17, 84, model->progress_flash, str_flash); | ||||
|     canvas_draw_str(canvas, 0, 43, "EEPROM"); | ||||
|     // snprintf(str_buf, sizeof(str_buf), "%d%%", (uint8_t)(model->progress_eeprom * 100));
 | ||||
|     elements_progress_bar_with_text(canvas, 44, 34, 84, model->progress_eeprom, str_eeprom); | ||||
| } | ||||
| 
 | ||||
| bool avr_isp_writer_view_input(InputEvent* event, void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWriterView* instance = context; | ||||
| 
 | ||||
|     bool ret = true; | ||||
|     if(event->key == InputKeyBack && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExit, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyOk && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     model->status = AvrIspWriterViewStatusWriting; | ||||
| 
 | ||||
|                     avr_isp_worker_rw_write_dump_start( | ||||
|                         instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } else if(event->key == InputKeyRight && event->type == InputTypeShort) { | ||||
|         with_view_model( | ||||
|             instance->view, | ||||
|             AvrIspWriterViewModel * model, | ||||
|             { | ||||
|                 if((model->status == AvrIspWriterViewStatusIDLE) || | ||||
|                    (model->status == AvrIspWriterViewStatusWritingFuseOk)) { | ||||
|                     if(instance->callback) | ||||
|                         instance->callback(AvrIspCustomEventSceneExitStartMenu, instance->context); | ||||
|                     ret = false; | ||||
|                 } | ||||
|             }, | ||||
|             false); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| static void avr_isp_writer_callback_status(void* context, AvrIspWorkerRWStatus status) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspWriterView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             switch(status) { | ||||
|             case AvrIspWorkerRWStatusEndWriting: | ||||
|                 model->status = AvrIspWriterViewStatusVerification; | ||||
|                 avr_isp_worker_rw_verification_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspWriterViewStatusVerification; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorVerification: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorVerification, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndVerification: | ||||
|                 avr_isp_worker_rw_write_fuse_start( | ||||
|                     instance->avr_isp_worker_rw, instance->file_path, instance->file_name); | ||||
|                 model->status = AvrIspWriterViewStatusWritingFuse; | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusErrorWritingFuse: | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorWritingFuse, instance->context); | ||||
|                 break; | ||||
|             case AvrIspWorkerRWStatusEndWritingFuse: | ||||
|                 model->status = AvrIspWriterViewStatusWritingFuseOk; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 //AvrIspWorkerRWStatusErrorWriting;
 | ||||
|                 if(instance->callback) | ||||
|                     instance->callback(AvrIspCustomEventSceneErrorWriting, instance->context); | ||||
|                 break; | ||||
|             } | ||||
|         }, | ||||
|         true); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_enter(void* context) { | ||||
|     furi_assert(context); | ||||
| 
 | ||||
|     AvrIspWriterView* instance = context; | ||||
|     with_view_model( | ||||
|         instance->view, | ||||
|         AvrIspWriterViewModel * model, | ||||
|         { | ||||
|             model->status = AvrIspWriterViewStatusIDLE; | ||||
|             model->progress_flash = 0.0f; | ||||
|             model->progress_eeprom = 0.0f; | ||||
|         }, | ||||
|         true); | ||||
| 
 | ||||
|     //Start avr_isp_worker_rw
 | ||||
|     instance->avr_isp_worker_rw = avr_isp_worker_rw_alloc(instance->context); | ||||
| 
 | ||||
|     avr_isp_worker_rw_set_callback_status( | ||||
|         instance->avr_isp_worker_rw, avr_isp_writer_callback_status, instance); | ||||
| 
 | ||||
|     avr_isp_worker_rw_start(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_exit(void* context) { | ||||
|     furi_assert(context); | ||||
|     AvrIspWriterView* instance = context; | ||||
| 
 | ||||
|     //Stop avr_isp_worker_rw
 | ||||
|     if(avr_isp_worker_rw_is_running(instance->avr_isp_worker_rw)) { | ||||
|         avr_isp_worker_rw_stop(instance->avr_isp_worker_rw); | ||||
|     } | ||||
| 
 | ||||
|     avr_isp_worker_rw_free(instance->avr_isp_worker_rw); | ||||
| } | ||||
| 
 | ||||
| AvrIspWriterView* avr_isp_writer_view_alloc() { | ||||
|     AvrIspWriterView* instance = malloc(sizeof(AvrIspWriterView)); | ||||
| 
 | ||||
|     // View allocation and configuration
 | ||||
|     instance->view = view_alloc(); | ||||
| 
 | ||||
|     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(AvrIspWriterViewModel)); | ||||
|     view_set_context(instance->view, instance); | ||||
|     view_set_draw_callback(instance->view, (ViewDrawCallback)avr_isp_writer_view_draw); | ||||
|     view_set_input_callback(instance->view, avr_isp_writer_view_input); | ||||
|     view_set_enter_callback(instance->view, avr_isp_writer_view_enter); | ||||
|     view_set_exit_callback(instance->view, avr_isp_writer_view_exit); | ||||
| 
 | ||||
|     return instance; | ||||
| } | ||||
| 
 | ||||
| void avr_isp_writer_view_free(AvrIspWriterView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     view_free(instance->view); | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| View* avr_isp_writer_view_get_view(AvrIspWriterView* instance) { | ||||
|     furi_assert(instance); | ||||
| 
 | ||||
|     return instance->view; | ||||
| } | ||||
							
								
								
									
										37
									
								
								applications/external/avr_isp_programmer/views/avr_isp_view_writer.h
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						| @ -0,0 +1,37 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include <gui/view.h> | ||||
| #include "../helpers/avr_isp_types.h" | ||||
| #include "../helpers/avr_isp_event.h" | ||||
| 
 | ||||
| typedef struct AvrIspWriterView AvrIspWriterView; | ||||
| 
 | ||||
| typedef void (*AvrIspWriterViewCallback)(AvrIspCustomEvent event, void* context); | ||||
| 
 | ||||
| typedef enum { | ||||
|     AvrIspWriterViewStatusIDLE, | ||||
|     AvrIspWriterViewStatusWriting, | ||||
|     AvrIspWriterViewStatusVerification, | ||||
|     AvrIspWriterViewStatusWritingFuse, | ||||
|     AvrIspWriterViewStatusWritingFuseOk, | ||||
| } AvrIspWriterViewStatus; | ||||
| 
 | ||||
| void avr_isp_writer_update_progress(AvrIspWriterView* instance); | ||||
| 
 | ||||
| void avr_isp_writer_set_file_path( | ||||
|     AvrIspWriterView* instance, | ||||
|     const char* file_path, | ||||
|     const char* file_name); | ||||
| 
 | ||||
| void avr_isp_writer_view_set_callback( | ||||
|     AvrIspWriterView* instance, | ||||
|     AvrIspWriterViewCallback callback, | ||||
|     void* context); | ||||
| 
 | ||||
| AvrIspWriterView* avr_isp_writer_view_alloc(); | ||||
| 
 | ||||
| void avr_isp_writer_view_free(AvrIspWriterView* instance); | ||||
| 
 | ||||
| View* avr_isp_writer_view_get_view(AvrIspWriterView* instance); | ||||
| 
 | ||||
| void avr_isp_writer_view_exit(void* context); | ||||
| @ -158,6 +158,7 @@ Header,+,lib/toolbox/args.h,, | ||||
| Header,+,lib/toolbox/crc32_calc.h,, | ||||
| Header,+,lib/toolbox/dir_walk.h,, | ||||
| Header,+,lib/toolbox/float_tools.h,, | ||||
| Header,+,lib/toolbox/hex.h,, | ||||
| Header,+,lib/toolbox/manchester_decoder.h,, | ||||
| Header,+,lib/toolbox/manchester_encoder.h,, | ||||
| Header,+,lib/toolbox/md5.h,, | ||||
| @ -1309,6 +1310,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" | ||||
| Function,+,hal_sd_detect,_Bool, | ||||
| Function,+,hal_sd_detect_init,void, | ||||
| Function,+,hal_sd_detect_set_low,void, | ||||
| Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" | ||||
| Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" | ||||
| Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" | ||||
| Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" | ||||
| Function,+,icon_animation_alloc,IconAnimation*,const Icon* | ||||
| Function,+,icon_animation_free,void,IconAnimation* | ||||
| Function,+,icon_animation_get_height,uint8_t,const IconAnimation* | ||||
| @ -1870,6 +1875,7 @@ Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uE | ||||
| Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" | ||||
| Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" | ||||
| Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" | ||||
| Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" | ||||
| Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" | ||||
| Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" | ||||
| Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, | ||||
|  | ||||
| 
 | 
| @ -190,6 +190,7 @@ Header,+,lib/toolbox/args.h,, | ||||
| Header,+,lib/toolbox/crc32_calc.h,, | ||||
| Header,+,lib/toolbox/dir_walk.h,, | ||||
| Header,+,lib/toolbox/float_tools.h,, | ||||
| Header,+,lib/toolbox/hex.h,, | ||||
| Header,+,lib/toolbox/manchester_decoder.h,, | ||||
| Header,+,lib/toolbox/manchester_encoder.h,, | ||||
| Header,+,lib/toolbox/md5.h,, | ||||
| @ -1597,6 +1598,10 @@ Function,+,gui_view_port_send_to_front,void,"Gui*, ViewPort*" | ||||
| Function,+,hal_sd_detect,_Bool, | ||||
| Function,+,hal_sd_detect_init,void, | ||||
| Function,+,hal_sd_detect_set_low,void, | ||||
| Function,+,hex_char_to_hex_nibble,_Bool,"char, uint8_t*" | ||||
| Function,+,hex_char_to_uint8,_Bool,"char, char, uint8_t*" | ||||
| Function,+,hex_chars_to_uint64,_Bool,"const char*, uint64_t*" | ||||
| Function,+,hex_chars_to_uint8,_Bool,"const char*, uint8_t*" | ||||
| Function,-,hypot,double,"double, double" | ||||
| Function,-,hypotf,float,"float, float" | ||||
| Function,-,hypotl,long double,"long double, long double" | ||||
| @ -2801,6 +2806,7 @@ Function,+,uECC_sign,int,"const uint8_t*, const uint8_t*, unsigned, uint8_t*, uE | ||||
| Function,-,uECC_sign_deterministic,int,"const uint8_t*, const uint8_t*, unsigned, const uECC_HashContext*, uint8_t*, uECC_Curve" | ||||
| Function,-,uECC_valid_public_key,int,"const uint8_t*, uECC_Curve" | ||||
| Function,-,uECC_verify,int,"const uint8_t*, const uint8_t*, unsigned, const uint8_t*, uECC_Curve" | ||||
| Function,+,uint8_to_hex_chars,void,"const uint8_t*, uint8_t*, int" | ||||
| Function,-,ulTaskGenericNotifyTake,uint32_t,"UBaseType_t, BaseType_t, TickType_t" | ||||
| Function,-,ulTaskGenericNotifyValueClear,uint32_t,"TaskHandle_t, UBaseType_t, uint32_t" | ||||
| Function,-,ulTaskGetIdleRunTimeCounter,uint32_t, | ||||
|  | ||||
| 
 | 
| @ -28,6 +28,7 @@ env.Append( | ||||
|         File("stream/buffered_file_stream.h"), | ||||
|         File("protocols/protocol_dict.h"), | ||||
|         File("pretty_format.h"), | ||||
|         File("hex.h"), | ||||
|     ], | ||||
| ) | ||||
| 
 | ||||
|  | ||||
 Skorpionm
						Skorpionm