From b99555e3d56d7964c069d42bbdbe9db7e6b955be Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 13 Jul 2024 14:24:07 +0400 Subject: [PATCH 01/19] FlipTDI: Initial --- flip_tdi/.catalog/README.md | 7 + flip_tdi/.catalog/changelog.md | 2 + flip_tdi/application.fam | 17 + flip_tdi/flip_tdi_app.c | 137 +++++++ flip_tdi/flip_tdi_app_i.c | 17 + flip_tdi/flip_tdi_app_i.h | 34 ++ flip_tdi/flip_tdi_icon_10px.png | Bin 0 -> 181 bytes flip_tdi/helpers/flip_tdi_event.h | 12 + flip_tdi/helpers/flip_tdi_types.h | 14 + flip_tdi/helpers/ftdi.c | 260 ++++++++++++ flip_tdi/helpers/ftdi.h | 29 ++ flip_tdi/helpers/ftdi_uart.c | 144 +++++++ flip_tdi/helpers/ftdi_uart.h | 9 + flip_tdi/helpers/ftdi_usb.c | 517 ++++++++++++++++++++++++ flip_tdi/helpers/ftdi_usb.h | 6 + flip_tdi/helpers/ftdi_usb_define.h | 236 +++++++++++ flip_tdi/images/flip_tdi_wiring.png | Bin 0 -> 4884 bytes flip_tdi/scenes/flip_tdi_scene.c | 31 ++ flip_tdi/scenes/flip_tdi_scene.h | 29 ++ flip_tdi/scenes/flip_tdi_scene_about.c | 68 ++++ flip_tdi/scenes/flip_tdi_scene_config.h | 4 + flip_tdi/scenes/flip_tdi_scene_main.c | 67 +++ flip_tdi/scenes/flip_tdi_scene_wiring.c | 21 + flip_tdi/scenes/subghz_scene_menu.c | 50 +++ flip_tdi/views/flip_tdi_view_main.c | 103 +++++ flip_tdi/views/flip_tdi_view_main.h | 20 + 26 files changed, 1834 insertions(+) create mode 100644 flip_tdi/.catalog/README.md create mode 100644 flip_tdi/.catalog/changelog.md create mode 100644 flip_tdi/application.fam create mode 100644 flip_tdi/flip_tdi_app.c create mode 100644 flip_tdi/flip_tdi_app_i.c create mode 100644 flip_tdi/flip_tdi_app_i.h create mode 100644 flip_tdi/flip_tdi_icon_10px.png create mode 100644 flip_tdi/helpers/flip_tdi_event.h create mode 100644 flip_tdi/helpers/flip_tdi_types.h create mode 100644 flip_tdi/helpers/ftdi.c create mode 100644 flip_tdi/helpers/ftdi.h create mode 100644 flip_tdi/helpers/ftdi_uart.c create mode 100644 flip_tdi/helpers/ftdi_uart.h create mode 100644 flip_tdi/helpers/ftdi_usb.c create mode 100644 flip_tdi/helpers/ftdi_usb.h create mode 100644 flip_tdi/helpers/ftdi_usb_define.h create mode 100644 flip_tdi/images/flip_tdi_wiring.png create mode 100644 flip_tdi/scenes/flip_tdi_scene.c create mode 100644 flip_tdi/scenes/flip_tdi_scene.h create mode 100644 flip_tdi/scenes/flip_tdi_scene_about.c create mode 100644 flip_tdi/scenes/flip_tdi_scene_config.h create mode 100644 flip_tdi/scenes/flip_tdi_scene_main.c create mode 100644 flip_tdi/scenes/flip_tdi_scene_wiring.c create mode 100644 flip_tdi/scenes/subghz_scene_menu.c create mode 100644 flip_tdi/views/flip_tdi_view_main.c create mode 100644 flip_tdi/views/flip_tdi_view_main.h diff --git a/flip_tdi/.catalog/README.md b/flip_tdi/.catalog/README.md new file mode 100644 index 0000000..efec847 --- /dev/null +++ b/flip_tdi/.catalog/README.md @@ -0,0 +1,7 @@ +# FlipTDI + +This is a FT232H device emulation application + +## Usage + +Сonnect Flipper to your computer and open the application, a new FT232H compatible device will appear in the system diff --git a/flip_tdi/.catalog/changelog.md b/flip_tdi/.catalog/changelog.md new file mode 100644 index 0000000..89c049f --- /dev/null +++ b/flip_tdi/.catalog/changelog.md @@ -0,0 +1,2 @@ +## 0.1 + - Initial release diff --git a/flip_tdi/application.fam b/flip_tdi/application.fam new file mode 100644 index 0000000..36d9e57 --- /dev/null +++ b/flip_tdi/application.fam @@ -0,0 +1,17 @@ +App( + appid="flip_tdi", + name="FlipTDI", + apptype=FlipperAppType.EXTERNAL, + entry_point="flip_tdi_app", + requires=[ + "gui", + "dialogs", + ], + stack_size=2 * 1024, + fap_description="Flipper FTDI232H emulator.", + fap_version="0.1", + fap_icon="flip_tdi_icon_10px.png", + fap_category="USB", + fap_icon_assets="images", + fap_author="SkorP", +) diff --git a/flip_tdi/flip_tdi_app.c b/flip_tdi/flip_tdi_app.c new file mode 100644 index 0000000..a161b0e --- /dev/null +++ b/flip_tdi/flip_tdi_app.c @@ -0,0 +1,137 @@ +#include +#include "flip_tdi_app_i.h" +//#include +//#include "helpers/ftdi_usb.h" + +// int32_t flip_tdi_app(void* p) { +// UNUSED(p); + +// FtdiUsb* ftdi = ftdi_usb_start(); + +// if(ftdi) { +// DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); +// DialogMessage* message = dialog_message_alloc(); + +// dialog_message_set_header(message, "Hello, world!", 64, 0, AlignCenter, AlignTop); +// dialog_message_set_text(message, "", 0, 63, AlignLeft, AlignBottom); +// dialog_message_show(dialogs, message); + +// dialog_message_free(message); + +// ftdi_usb_stop(ftdi); +// } + +// return 0; +// } + + + +static bool flip_tdi_app_custom_event_callback(void* context, uint32_t event) { + furi_assert(context); + FlipTDIApp* app = context; + return scene_manager_handle_custom_event(app->scene_manager, event); +} + +static bool flip_tdi_app_back_event_callback(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + return scene_manager_handle_back_event(app->scene_manager); +} + +static void flip_tdi_app_tick_event_callback(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + scene_manager_handle_tick_event(app->scene_manager); +} + +FlipTDIApp* flip_tdi_app_alloc() { + FlipTDIApp* app = malloc(sizeof(FlipTDIApp)); + + // GUI + app->gui = furi_record_open(RECORD_GUI); + + // View Dispatcher + app->view_dispatcher = view_dispatcher_alloc(); + app->scene_manager = scene_manager_alloc(&flip_tdi_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, flip_tdi_app_custom_event_callback); + view_dispatcher_set_navigation_event_callback( + app->view_dispatcher, flip_tdi_app_back_event_callback); + view_dispatcher_set_tick_event_callback( + app->view_dispatcher, flip_tdi_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, FlipTDIViewSubmenu, submenu_get_view(app->submenu)); + + // Widget + app->widget = widget_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, FlipTDIViewWidget, widget_get_view(app->widget)); + + // Field Presence + app->flip_tdi_view_main_instance = flip_tdi_view_main_alloc(); + view_dispatcher_add_view( + app->view_dispatcher, + FlipTDIViewMain, + flip_tdi_view_main_get_view(app->flip_tdi_view_main_instance)); + + // FTDI emulation Start + flip_tdi_start(app); + + scene_manager_next_scene(app->scene_manager, FlipTDISceneMain); + + return app; +} + +void flip_tdi_app_free(FlipTDIApp* app) { + furi_assert(app); + + // FTDI emulation Stop + flip_tdi_stop(app); + + // Submenu + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewSubmenu); + submenu_free(app->submenu); + + // Widget + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewWidget); + widget_free(app->widget); + + // FlipTDIViewMain + view_dispatcher_remove_view(app->view_dispatcher, FlipTDIViewMain); + flip_tdi_view_main_free(app->flip_tdi_view_main_instance); + + // 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); + + free(app); +} + +int32_t flip_tdi_app(void* p) { + UNUSED(p); + FlipTDIApp* flip_tdi_app = flip_tdi_app_alloc(); + + view_dispatcher_run(flip_tdi_app->view_dispatcher); + + flip_tdi_app_free(flip_tdi_app); + + return 0; +} \ No newline at end of file diff --git a/flip_tdi/flip_tdi_app_i.c b/flip_tdi/flip_tdi_app_i.c new file mode 100644 index 0000000..4a37fa2 --- /dev/null +++ b/flip_tdi/flip_tdi_app_i.c @@ -0,0 +1,17 @@ +#include "flip_tdi_app_i.h" + +#include + +#define TAG "FlipTDI" + +void flip_tdi_start(FlipTDIApp* app) { + furi_assert(app); + + app->ftdi_usb = ftdi_usb_start(); +} + +void flip_tdi_stop(FlipTDIApp* app) { + furi_assert(app); + + ftdi_usb_stop(app->ftdi_usb); +} diff --git a/flip_tdi/flip_tdi_app_i.h b/flip_tdi/flip_tdi_app_i.h new file mode 100644 index 0000000..907cfc3 --- /dev/null +++ b/flip_tdi/flip_tdi_app_i.h @@ -0,0 +1,34 @@ +#pragma once + +#include "helpers/flip_tdi_types.h" +#include "helpers/flip_tdi_event.h" + +#include "scenes/flip_tdi_scene.h" +#include +#include +#include +#include +#include +#include +#include "views/flip_tdi_view_main.h" +#include + +#include "helpers/ftdi_usb.h" + + +typedef struct FlipTDIApp FlipTDIApp; + +struct FlipTDIApp { + Gui* gui; + ViewDispatcher* view_dispatcher; + SceneManager* scene_manager; + NotificationApp* notifications; + Submenu* submenu; + Widget* widget; + FlipTDIViewMainType* flip_tdi_view_main_instance; + + FtdiUsb* ftdi_usb; +}; + +void flip_tdi_start(FlipTDIApp* app); +void flip_tdi_stop(FlipTDIApp* app); \ No newline at end of file diff --git a/flip_tdi/flip_tdi_icon_10px.png b/flip_tdi/flip_tdi_icon_10px.png new file mode 100644 index 0000000000000000000000000000000000000000..3ea227b28995a2725c5f1d11802d583dc87a4600 GIT binary patch literal 181 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4F%}28J29*~C-V}>VM%xNb!1@J z*w6hZkrl}2EbxddW?&Fg1z|?dAe9e5K`&1i#}JL+wY`B{3<@01TYul*c}pX3@~@YU zKG)`4QdbfQo?v^_;J$Eb)WqjnrH}t!@JOyri^^N+ux#zkhDhE2_Oep>5sR;1er?21 XClh=u)%V +#include + +#define FLIP_TDI_DEVELOPED "SkorP" +#define FLIP_TDI_GITHUB "https://github.com/flipperdevices/flipperzero-good-faps" + +typedef enum { + FlipTDIViewVariableItemList, + FlipTDIViewSubmenu, + FlipTDIViewMain, + FlipTDIViewWidget, +} FlipTDIView; diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c new file mode 100644 index 0000000..54bea16 --- /dev/null +++ b/flip_tdi/helpers/ftdi.c @@ -0,0 +1,260 @@ +#include "ftdi.h" +#include "furi.h" +#include "ftdi_uart.h" + +#define TAG "FTDI" + +#define FTDI_TX_RX_BUF_SIZE (4096UL) +#define FTDI_INTERFACE_A (0x01UL) +#define FTDI_DRIVER_INTERFACE_A (0x00UL) +#define FTDI_UART_MAX_TX_SIZE (64UL) + +struct Ftdi { + FtdiModemStatus status; + FuriStreamBuffer* stream_rx; + FuriStreamBuffer* stream_tx; + uint32_t baudrate; + FtdiDataConfig data_config; + FtdiBitMode bit_mode; + uint8_t latency_timer; + + FtdiUart* ftdi_uart; +}; + +static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { + UNUSED(ftdi); + uint8_t interface = index & 0xff; + return ((interface) == FTDI_INTERFACE_A) || ((interface) == FTDI_DRIVER_INTERFACE_A); +} + +Ftdi* ftdi_alloc(void) { + Ftdi* ftdi = malloc(sizeof(Ftdi)); + ftdi->stream_rx = + furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); + ftdi->stream_tx = + furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); + ftdi->baudrate = 115200; + + + + ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); + + + return ftdi; +} + +void ftdi_free(Ftdi* ftdi) { + ftdi_uart_free(ftdi->ftdi_uart); + furi_stream_buffer_free(ftdi->stream_tx); + furi_stream_buffer_free(ftdi->stream_rx); + free(ftdi); +} + +void ftdi_reset_purge_rx(Ftdi* ftdi) { + furi_stream_buffer_reset(ftdi->stream_rx); +} + +void ftdi_reset_purge_tx(Ftdi* ftdi) { + furi_stream_buffer_reset(ftdi->stream_tx); +} + +void ftdi_reset_sio(Ftdi* ftdi) { + UNUSED(ftdi); +} + +uint32_t ftdi_set_tx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size) { + uint32_t len = furi_stream_buffer_spaces_available(ftdi->stream_tx); + + if(len < size) { + size = len; + //ToDo: set error + //ftdi->status.DR = 1; + FURI_LOG_E(TAG, "FTDI TX buffer overflow"); + } + + return furi_stream_buffer_send(ftdi->stream_tx, data, size, 0); +} + +uint32_t ftdi_get_tx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size) { + return furi_stream_buffer_receive(ftdi->stream_tx, data, size, 0); +} + +uint32_t ftdi_available_tx_buf(Ftdi* ftdi) { + return furi_stream_buffer_bytes_available(ftdi->stream_tx); +} + +uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size) { + uint32_t len = furi_stream_buffer_spaces_available(ftdi->stream_rx); + + if(len < size) { + size = len; + //Todo: set error + FURI_LOG_E(TAG, "FTDI RX buffer overflow"); + } + return furi_stream_buffer_send(ftdi->stream_rx, data, size, 0); +} + +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size) { + return furi_stream_buffer_receive(ftdi->stream_rx, data, size, 0); +} + +uint32_t ftdi_available_rx_buf(Ftdi* ftdi) { + return furi_stream_buffer_bytes_available(ftdi->stream_rx); +} + +void ftdi_loopback(Ftdi* ftdi) { + //todo fix size + uint8_t data[128]; + uint32_t len = ftdi_get_rx_buf(ftdi, data, 128); + ftdi_set_tx_buf(ftdi, data, len); +} + +/* +https://www.ftdichip.com/Documents/AppNotes/AN_120_Aliasing_VCP_Baud_Rates.pdf + +Note: Divisor = 1 and Divisor = 0 are special cases. A divisor of 0 will give 12MBaud, and a +divisor of 1 will give 8MBaud. Sub-integer divisors are not allowed if the main divisor (n) is +either 0 or 1. + Divisor = 0 -> clk/1 + Divisor = 1 -> clk/1.5 + Divisor = 2 -> clk/2 + +To alias baud rates between 3MBaud and 12MBaud it is necessary to use driver version 2.4.20 or later +and the most significant bit (MSB) of the divisor must be a 1. This will ensure the divisor is dividing a +12MHz clock and not a 3MHz clock. +Example: +Each field consists of 4 bytes, ordered as follows: Byte0, Byte1, Byte2, Byte3. Bits 13 +through 0 denote the integer divisor while bits 16, 15 and 14 denote the sub-integer divisor, as +follows +16,15,14 = 000 - sub-integer divisor = 0 +16,15,14 = 001 - sub-integer divisor = 0.5 +16,15,14 = 010 - sub-integer divisor = 0.25 +16,15,14 = 011 - sub-integer divisor = 0.125 +16,15,14 = 100 - sub-integer divisor = 0.375 +16,15,14 = 101 - sub-integer divisor = 0.625 +16,15,14 = 110 - sub-integer divisor = 0.75 +16,15,14 = 111 - sub-integer divisor = 0.875 + +Step 1 - re-order the bytes: 35,40,01,00 => 00014035 Hex +Step 2 - extract the sub-integer divisor; 16 = 1, 15 = 0, 14 = 1 => sub-integer = 0.625 +Step 3 - extract the integer divisor: 13:0 = 0035 Hex = 53 Dec +Step 4 - combine the integer and sub-integer divisors: 53.625 Dec +Step 5 - divide 3000000 by the divisor => 3000000/53.625 = 55944 baud + +*/ +void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + + uint32_t encoded_divisor = ((index & 0xff00) << 8) | value; + bool is_12mhz = (encoded_divisor & 0x20000) == 0x20000; + uint32_t clk = is_12mhz ? 12000000 : 3000000; + + uint32_t baudrate = 0; + float divisor[8] = {0, 0.5, 0.25, 0.125, 0.375, 0.625, 0.75, 0.875}; + uint16_t integer_divisor = encoded_divisor & 0x3FFF; + float sub_integer_divisor = divisor[(encoded_divisor >> 14) & 0x7]; + + if((sub_integer_divisor == 0) && (integer_divisor < 3)) { + if(integer_divisor == 0) { + baudrate = clk; + } else if(integer_divisor == 1) { + baudrate = (uint32_t)round((float)clk / 1.5f); + } else if(integer_divisor == 2) { + baudrate = clk / 2; + } + } else { + baudrate = + round((float)clk / ((float)integer_divisor + divisor[(encoded_divisor >> 14) & 0x7])); + } + + furi_log_puts("ftdi_set_baudrate="); + char tmp_str2[] = "4294967295"; + itoa(baudrate, tmp_str2, 10); + furi_log_puts(tmp_str2); + furi_log_puts("\r\n"); + + ftdi->baudrate = baudrate; + + ftdi_uart_set_baudrate(ftdi->ftdi_uart, baudrate); +} + +void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + ftdi->data_config = *((FtdiDataConfig*)&value); +} + +void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + uint16_t flow_ctrl = index & 0xFF00; + if(flow_ctrl != FtdiFlowControlDisable) { + //ToDo: implement FtdiFlowControl + } +} + +void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + //todo no use mask value&0xFF + uint8_t bit_mode = value >> 8; + ftdi->bit_mode = *((FtdiBitMode*)&(bit_mode)); +} + +void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index) { + if(!ftdi_check_interface(ftdi, index)) { + return; + } + ftdi->latency_timer = value; +} + +uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { + return ftdi->latency_timer; +} + +// FTDI modem status + +// Layout of the first byte: +// - B0..B3 - must be 0 +// - B4 Clear to send (CTS) +// 0 = inactive +// 1 = active +// - B5 Data set ready (DTS) +// 0 = inactive +// 1 = active +// - B6 Ring indicator (RI) +// 0 = inactive +// 1 = active +// - B7 Receive line signal detect (RLSD) +// 0 = inactive +// 1 = active + +// Layout of the second byte: +// - B0 Data ready (DR) +// - B1 Overrun error (OE) +// - B2 Parity error (PE) +// - B3 Framing error (FE) +// - B4 Break interrupt (BI) +// - B5 Transmitter holding register (THRE) +// - B6 Transmitter empty (TEMT) +// - B7 Error in RCVR FIFO +void ftdi_get_modem_status(uint16_t* status) { + FtdiModemStatus modem_status = {0}; + modem_status.RESERVED1 = 1; + modem_status.TEMT = 1; + modem_status.THRE = 1; + + *status = *((uint16_t*)&modem_status); +} + + + + +void ftdi_start_uart_tx(Ftdi* ftdi) { + ftdi_uart_tx(ftdi->ftdi_uart); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h new file mode 100644 index 0000000..235cee6 --- /dev/null +++ b/flip_tdi/helpers/ftdi.h @@ -0,0 +1,29 @@ +#pragma once +#include "ftdi_usb_define.h" + +typedef struct Ftdi Ftdi; + +Ftdi* ftdi_alloc(void); +void ftdi_free(Ftdi* ftdi); +void ftdi_reset_purge_rx(Ftdi* ftdi); +void ftdi_reset_purge_tx(Ftdi* ftdi); +void ftdi_reset_sio(Ftdi* ftdi); +uint32_t ftdi_set_tx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); +uint32_t ftdi_get_tx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size); +uint32_t ftdi_available_tx_buf(Ftdi* ftdi); +uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size); +uint32_t ftdi_available_rx_buf(Ftdi* ftdi); +void ftdi_loopback(Ftdi* ftdi); +void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index); +void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index); +void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index); +uint8_t ftdi_get_latency_timer(Ftdi* ftdi); + +void ftdi_get_modem_status(uint16_t *status); + + +void ftdi_start_uart_tx(Ftdi* ftdi); + diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c new file mode 100644 index 0000000..47fdce7 --- /dev/null +++ b/flip_tdi/helpers/ftdi_uart.c @@ -0,0 +1,144 @@ +#include "ftdi_uart.h" +#include "furi.h" +#include + +#define TAG "FTDI_UART" +#define FTDI_UART_MAX_TXRX_SIZE (64UL) + +struct FtdiUart { + FuriThread* worker_thread; + FuriHalSerialHandle* serial_handle; + uint32_t baudrate; + Ftdi* ftdi; +}; + +typedef enum { + WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event + WorkerEventStop = (1 << 1), + WorkerEventRxData = (1 << 2), + WorkerEventRxIdle = (1 << 3), + WorkerEventRxOverrunError = (1 << 4), + WorkerEventRxFramingError = (1 << 5), + WorkerEventRxNoiseError = (1 << 6), + WorkerEventTXData = (1 << 7), +} WorkerEvent; + +#define WORKER_EVENTS_MASK \ + (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ + WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventTXData) + +static void ftdi_uart_irq_cb( + FuriHalSerialHandle* handle, + FuriHalSerialRxEvent ev, + size_t size, + void* context) { + FtdiUart* ftdi_uart = context; + + if(ev & (FuriHalSerialRxEventData | FuriHalSerialRxEventIdle)) { + uint8_t data[FURI_HAL_SERIAL_DMA_BUFFER_SIZE] = {0}; + while(size) { + size_t ret = furi_hal_serial_dma_rx( + handle, + data, + (size > FURI_HAL_SERIAL_DMA_BUFFER_SIZE) ? FURI_HAL_SERIAL_DMA_BUFFER_SIZE : size); + ftdi_set_tx_buf(ftdi_uart->ftdi, data, ret); + size -= ret; + }; + //furi_thread_flags_set(furi_thread_get_id(usb_uart->worker_thread), WorkerEventRxData); + } +} + +static int32_t uart_echo_worker(void* context) { + furi_assert(context); + FtdiUart* ftdi_uart = context; + + FURI_LOG_I(TAG, "Worker started"); + + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) break; + + if(events & WorkerEventTXData) { + size_t length = 0; + uint8_t data[FTDI_UART_MAX_TXRX_SIZE]; + do { + length = ftdi_get_rx_buf(ftdi_uart->ftdi, data, FTDI_UART_MAX_TXRX_SIZE); + if(length > 0) { + furi_hal_serial_tx(ftdi_uart->serial_handle, data, length); + } + } while(length > 0); + } + + // if(events & WorkerEventRxData) { + // size_t length = 0; + // do { + // uint8_t data[FTDI_UART_MAX_TXRX_SIZE]; + // //length = ftdi_get_rx_buf(ftdi_uart->ftdi, data, FTDI_UART_MAX_TXRX_SIZE); + + // if(length > 0) { + // ftdi_set_tx_buf(ftdi_uart->ftdi, data, length); + // } + // } while(length > 0); + // } + + if(events & WorkerEventRxIdle) { + //furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); + } + + // if(events & + // (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { + // if(events & WorkerEventRxOverrunError) { + // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14); + // } + // if(events & WorkerEventRxFramingError) { + // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13); + // } + // if(events & WorkerEventRxNoiseError) { + // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13); + // } + // } + } + FURI_LOG_I(TAG, "Worker stopped"); + return 0; +} + +FtdiUart* ftdi_uart_alloc(Ftdi* ftdi) { + FtdiUart* ftdi_uart = malloc(sizeof(FtdiUart)); + ftdi_uart->baudrate = 115200; + + ftdi_uart->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdLpuart); + furi_check(ftdi_uart->serial_handle); + furi_hal_serial_init(ftdi_uart->serial_handle, ftdi_uart->baudrate); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + furi_hal_serial_dma_rx_start(ftdi_uart->serial_handle, ftdi_uart_irq_cb, ftdi_uart, false); + ftdi_uart->ftdi = ftdi; + + ftdi_uart->worker_thread = furi_thread_alloc_ex(TAG, 1024, uart_echo_worker, ftdi_uart); + furi_thread_start(ftdi_uart->worker_thread); + + return ftdi_uart; +} + +void ftdi_uart_free(FtdiUart* ftdi_uart) { + furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventStop); + furi_thread_join(ftdi_uart->worker_thread); + furi_thread_free(ftdi_uart->worker_thread); + + furi_hal_serial_deinit(ftdi_uart->serial_handle); + furi_hal_serial_control_release(ftdi_uart->serial_handle); + ftdi_uart->serial_handle = NULL; + free(ftdi_uart); +} + +void ftdi_uart_tx(FtdiUart* ftdi_uart) { + furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXData); +} + +void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate) { + ftdi_uart->baudrate = baudrate; + furi_hal_serial_set_br(ftdi_uart->serial_handle, baudrate); +} diff --git a/flip_tdi/helpers/ftdi_uart.h b/flip_tdi/helpers/ftdi_uart.h new file mode 100644 index 0000000..3d499ef --- /dev/null +++ b/flip_tdi/helpers/ftdi_uart.h @@ -0,0 +1,9 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiUart FtdiUart; + +FtdiUart* ftdi_uart_alloc(Ftdi* ftdi); +void ftdi_uart_free(FtdiUart* ftdi_uart); +void ftdi_uart_tx(FtdiUart* ftdi_uart); +void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c new file mode 100644 index 0000000..cc04125 --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb.c @@ -0,0 +1,517 @@ +#include "ftdi_usb.h" +#include +#include "ftdi.h" + +#define TAG "FTDI USB" + +#define FTDI_USB_VID (0x0403) +#define FTDI_USB_PID (0x6014) + +#define FTDI_USB_EP_IN (0x81) +#define FTDI_USB_EP_OUT (0x02) + +#define FTDI_USB_EP_IN_SIZE (64UL) +#define FTDI_USB_EP_OUT_SIZE (64UL) + +#define FTDI_USB_RX_MAX_SIZE (FTDI_USB_EP_OUT_SIZE) +#define FTDI_USB_MODEM_STATUS_SIZE (sizeof(uint16_t)) +#define FTDI_USB_TX_MAX_SIZE (FTDI_USB_EP_IN_SIZE - FTDI_USB_MODEM_STATUS_SIZE) + +typedef struct { + uint16_t status; + uint8_t data[FTDI_USB_TX_MAX_SIZE]; +} FtdiTxData; + +static usbd_respond ftdi_usb_ep_config(usbd_device* dev, uint8_t cfg); +static usbd_respond + ftdi_usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); +static void ftdi_usb_send(usbd_device* dev, uint8_t* buf, uint16_t len); +static int32_t ftdi_usb_receive(usbd_device* dev, uint8_t* buf, uint16_t max_len); + +typedef enum { + EventExit = (1 << 0), + EventReset = (1 << 1), + EventRx = (1 << 2), + EventTx = (1 << 3), + EventTxComplete = (1 << 4), + EventResetSio = (1 << 5), + EventResetPurgeRx = (1 << 6), + EventResetPurgeTx = (1 << 7), + EventSetBitmode = (1 << 8), + EventSetLatencyTimer = (1 << 9), + EventSetEventChar = (1 << 10), + EventSetErrorChar = (1 << 11), + EventSetBaudrate = (1 << 12), + EventSetData = (1 << 13), + EventSetFlowCtrl = (1 << 14), + + EventAll = EventExit | EventReset | EventRx | EventTx | EventTxComplete | EventResetSio | + EventResetPurgeRx | EventResetPurgeTx | EventSetBitmode | EventSetLatencyTimer | + EventSetEventChar | EventSetErrorChar | EventSetBaudrate | EventSetData | + EventSetFlowCtrl, +} FtdiEvent; + +struct FtdiUsb { + FuriHalUsbInterface usb; + FuriHalUsbInterface* usb_prev; + + FuriThread* thread; + usbd_device* dev; + Ftdi* ftdi; + uint8_t data_recvest[8]; + uint16_t data_recvest_len; +}; + +static int32_t ftdi_thread_worker(void* context) { + FtdiUsb* ftdi_usb = context; + usbd_device* dev = ftdi_usb->dev; + UNUSED(dev); + + uint32_t len_data = 0; + FtdiTxData tx_data = {0}; + uint16_t status = 0; + ftdi_get_modem_status(&status); + tx_data.status = status; + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + + while(true) { + uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); + + furi_hal_gpio_write(&gpio_ext_pa7, 1); + + if(flags & EventRx) { //fast flag + uint8_t buf[FTDI_USB_RX_MAX_SIZE]; + len_data = ftdi_usb_receive(dev, buf, FTDI_USB_RX_MAX_SIZE); + // if(len_data > 0) { + // for(uint32_t i = 0; i < len_data; i++) { + // FURI_LOG_RAW_I("%c", (char)buf[i]); + // } + // FURI_LOG_RAW_I("\r\n"); + // } + if(len_data > 0) { + ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); + //ftdi_loopback(ftdi_usb->ftdi); + ftdi_start_uart_tx(ftdi_usb->ftdi); + } + flags &= ~EventRx; // clear flag + } + + if(flags) { + if(flags & EventResetSio) { + furi_log_puts("EventResetSio\r\n"); + ftdi_reset_sio(ftdi_usb->ftdi); + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + } + if(flags & EventTxComplete) { + flags |= EventTx; + } + if(flags & EventTx) { + tx_data.status = status; + len_data = ftdi_available_tx_buf(ftdi_usb->ftdi); + + if(len_data > 0) { + if(len_data > FTDI_USB_TX_MAX_SIZE) { + len_data = FTDI_USB_TX_MAX_SIZE; + } + len_data = ftdi_get_tx_buf(ftdi_usb->ftdi, tx_data.data, len_data); + ftdi_usb_send(dev, (uint8_t*)&tx_data, len_data + FTDI_USB_MODEM_STATUS_SIZE); + } else { + ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); + } + } + + // if(flags & EventResetPurgeRx) { + // ftdi_reset_purge_rx(ftdi_usb->ftdi); + // } + // if(flags & EventResetPurgeTx) { + // ftdi_reset_purge_tx(ftdi_usb->ftdi); + // } + + if(flags & EventExit) { + FURI_LOG_I(TAG, "exit"); + break; + } + } + + furi_hal_gpio_write(&gpio_ext_pa7, 0); + } + + return 0; +} + +// needed in ftdi_usb_deinit, ftdi_usb_suspend, usb_rxtx_ep_callback, ftdi_usb_control, +// where if_ctx isn't passed +static FtdiUsb* ftdi_cur = NULL; + +static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { + UNUSED(intf); + FtdiUsb* ftdi_usb = ctx; + ftdi_cur = ftdi_usb; + ftdi_usb->dev = dev; + + usbd_reg_config(dev, ftdi_usb_ep_config); + usbd_reg_control(dev, ftdi_usb_control); + usbd_connect(dev, true); + + ftdi_usb->thread = furi_thread_alloc(); + furi_thread_set_name(ftdi_usb->thread, "FtdiUsb"); + furi_thread_set_stack_size(ftdi_usb->thread, 1024); + furi_thread_set_context(ftdi_usb->thread, ctx); + furi_thread_set_callback(ftdi_usb->thread, ftdi_thread_worker); + + ftdi_usb->ftdi = ftdi_alloc(); + + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); + + furi_thread_start(ftdi_usb->thread); +} + +static void ftdi_usb_deinit(usbd_device* dev) { + usbd_reg_config(dev, NULL); + usbd_reg_control(dev, NULL); + + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) { + return; + } + ftdi_cur = NULL; + + furi_assert(ftdi_usb->thread); + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventExit); + furi_thread_join(ftdi_usb->thread); + furi_thread_free(ftdi_usb->thread); + ftdi_usb->thread = NULL; + + ftdi_free(ftdi_usb->ftdi); + + furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + + free(ftdi_usb->usb.str_prod_descr); + ftdi_usb->usb.str_prod_descr = NULL; + free(ftdi_usb->usb.str_serial_descr); + ftdi_usb->usb.str_serial_descr = NULL; + free(ftdi_usb); +} + +static void ftdi_usb_send(usbd_device* dev, uint8_t* buf, uint16_t len) { + usbd_ep_write(dev, FTDI_USB_EP_IN, buf, len); +} + +static int32_t ftdi_usb_receive(usbd_device* dev, uint8_t* buf, uint16_t max_len) { + int32_t len = usbd_ep_read(dev, FTDI_USB_EP_OUT, buf, max_len); + return ((len < 0) ? 0 : len); +} + +static void ftdi_usb_wakeup(usbd_device* dev) { + UNUSED(dev); +} + +static void ftdi_usb_suspend(usbd_device* dev) { + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) return; + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventReset); +} + +// static void ftdi_usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { +// UNUSED(ep); +// UNUSED(event); +// FtdiUsb* ftdi_usb = ftdi_cur; +// if(!ftdi_usb || ftdi_usb->dev != dev) return; +// // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRxTx); +// } + +static void ftdi_usb_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + FtdiUsb* ftdi_usb = ftdi_cur; + furi_hal_gpio_write(&gpio_ext_pa6, 1); + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRx); + + // uint8_t buf[FTDI_USB_RX_MAX_SIZE]; + // uint32_t len_data = ftdi_usb_receive(dev, buf, FTDI_USB_RX_MAX_SIZE); + // if(len_data > 0) { + // ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); + // // ftdi_loopback(ftdi_usb->ftdi); + // } + furi_hal_gpio_write(&gpio_ext_pa6, 0); +} + +static void ftdi_usb_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { + UNUSED(dev); + UNUSED(event); + UNUSED(ep); + FtdiUsb* ftdi_usb = ftdi_cur; + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventTxComplete); +} + +static usbd_respond ftdi_usb_ep_config(usbd_device* dev, uint8_t cfg) { + switch(cfg) { + case 0: // deconfig + usbd_ep_deconfig(dev, FTDI_USB_EP_OUT); + usbd_ep_deconfig(dev, FTDI_USB_EP_IN); + usbd_reg_endpoint(dev, FTDI_USB_EP_OUT, NULL); + usbd_reg_endpoint(dev, FTDI_USB_EP_IN, NULL); + return usbd_ack; + case 1: // config + usbd_ep_config( + dev, FTDI_USB_EP_IN, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, FTDI_USB_EP_IN_SIZE); + usbd_ep_config( + dev, FTDI_USB_EP_OUT, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, FTDI_USB_EP_OUT_SIZE); + usbd_reg_endpoint(dev, FTDI_USB_EP_IN, ftdi_usb_tx_ep_callback); + usbd_reg_endpoint(dev, FTDI_USB_EP_OUT, ftdi_usb_rx_ep_callback); + return usbd_ack; + } + return usbd_fail; +} + +static usbd_respond + ftdi_usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { + UNUSED(callback); + UNUSED(dev); + + if(!(req->bmRequestType & (FtdiControlTypeVendor | FtdiControlRecipientDevice))) { + return usbd_fail; + } + + FtdiUsb* ftdi_usb = ftdi_cur; + if(!ftdi_usb || ftdi_usb->dev != dev) { + return usbd_fail; + } + + furi_log_puts("-----------\r\n"); + + char tmp_str[] = "0xFFFFFFFF"; + itoa(req->bmRequestType, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->bRequest, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wValue, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wIndex, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" "); + + itoa(req->wLength, tmp_str, 16); + furi_log_puts(tmp_str); + furi_log_puts(" \r\n"); + + // if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != + // (USB_REQ_INTERFACE | USB_REQ_CLASS)) { + // return usbd_fail; + // } + + switch(req->bmRequestType) { + case FtdiControlRequestsOut: + + switch(req->bRequest) { + case FtdiRequestsSiOReqReset: + furi_log_puts("ftdi_usb_control OUT\r\n"); + if(req->wValue == FtdiResetSio) { + furi_log_puts("FtdiResetSio\r\n"); + //ftdi_reset_sio(ftdi_usb->ftdi); + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetSio); + } + if(req->wValue == FtdiResetPurgeRx) { + furi_log_puts("FtdiResetPurgeRx\r\n"); + ftdi_reset_purge_rx(ftdi_usb->ftdi); + + //furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetPurgeRx); + } + if(req->wValue == FtdiResetPurgeTx) { + furi_log_puts("FtdiResetPurgeTx\r\n"); + ftdi_reset_purge_tx(ftdi_usb->ftdi); + //furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetPurgeTx); + } + return usbd_ack; + break; + case FtdiRequestsSiOReqSetBitmode: + furi_log_puts("FtdiRequestsSiOReqSetBitmode\r\n"); + ftdi_set_bitmode(ftdi_usb->ftdi, req->wValue, req->wIndex); + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetBitmode); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetLatencyTimer: + furi_log_puts("FtdiRequestsSiOReqSetLatencyTimer\r\n"); + ftdi_set_latency_timer(ftdi_usb->ftdi, req->wValue, req->wIndex); + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetLatencyTimer); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetEventChar: + furi_log_puts("FtdiRequestsSiOReqSetEventChar\r\n"); + //value?????? bool enable: value |= 1 << 8 + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetEventChar); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetErrorChar: + furi_log_puts("FtdiRequestsSiOReqSetErrorChar\r\n"); + //value?????? bool enable: value |= 1 << 8 + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetErrorChar); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetBaudrate: + furi_log_puts("FtdiRequestsSiOReqSetBaudrate\r\n"); + ftdi_set_baudrate(ftdi_usb->ftdi, req->wValue, req->wIndex); + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetBaudrate); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetData: + furi_log_puts("FtdiRequestsSiOReqSetData\r\n"); + ftdi_set_data_config(ftdi_usb->ftdi, req->wValue, req->wIndex); + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetData); + return usbd_ack; + break; + case FtdiRequestsSiOReqSetFlowCtrl: + furi_log_puts("FtdiRequestsSiOReqSetFlowCtrl\r\n"); + ftdi_set_flow_ctrl(ftdi_usb->ftdi, req->wIndex); + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetFlowCtrl); + return usbd_ack; + break; + default: + break; + } + + break; + case FtdiControlRequestsIn: + furi_log_puts("ftdi_usb_control IN\r\n"); + switch(req->bRequest) { + case FtdiRequestsSiOReqGetLatencyTimer: + furi_log_puts("FtdiRequestsSiOReqGetLatencyTimer\r\n"); + ftdi_usb->data_recvest[0] = ftdi_get_latency_timer(ftdi_usb->ftdi); + ftdi_usb->data_recvest_len = 1; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetLatencyTimer); + return usbd_ack; + break; + default: + break; + } + break; + default: + break; + } + + // switch(req->bRequest) { + // case USB_MSC_BOT_GET_MAX_LUN: { + // static uint8_t max_lun = 0; + // dev->status.data_ptr = &max_lun; + // dev->status.data_count = 1; + // return usbd_ack; + // }; break; + // case USB_MSC_BOT_RESET: { + // FtdiUsb* ftdi_usb = ftdi_cur; + // if(!ftdi_usb || ftdi_usb->dev != dev) return usbd_fail; + // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventReset); + // return usbd_ack; + // }; break; + // } + return usbd_fail; +} + +static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("FTDI"); +static const struct usb_string_descriptor dev_product_desc = USB_STRING_DESC("FlipTDI"); + +struct FtdiUsbDescriptor { + struct usb_config_descriptor config; + struct usb_interface_descriptor intf; + struct usb_endpoint_descriptor ep_in; + struct usb_endpoint_descriptor ep_out; +} __attribute__((packed)); + +static const struct usb_device_descriptor usb_ftdi_dev_descr = { + .bLength = sizeof(struct usb_device_descriptor), + .bDescriptorType = USB_DTYPE_DEVICE, + .bcdUSB = VERSION_BCD(2, 0, 0), + .bDeviceClass = USB_CLASS_PER_INTERFACE, + .bDeviceSubClass = USB_SUBCLASS_NONE, + .bDeviceProtocol = USB_PROTO_NONE, + .bMaxPacketSize0 = 8, // USB_EP0_SIZE + .idVendor = FTDI_USB_VID, + .idProduct = FTDI_USB_PID, + .bcdDevice = VERSION_BCD(9, 0, 0), + .iManufacturer = 1, // UsbDevManuf + .iProduct = 2, // UsbDevProduct + .iSerialNumber = NO_DESCRIPTOR, + .bNumConfigurations = 1, +}; + +static const struct FtdiUsbDescriptor usb_ftdi_cfg_descr = { + .config = + { + .bLength = sizeof(struct usb_config_descriptor), + .bDescriptorType = USB_DTYPE_CONFIGURATION, + .wTotalLength = sizeof(struct FtdiUsbDescriptor), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = NO_DESCRIPTOR, + .bmAttributes = USB_CFG_ATTR_RESERVED, + .bMaxPower = USB_CFG_POWER_MA(500), + }, + .intf = + { + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DTYPE_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR, + .bInterfaceSubClass = USB_SUBCLASS_VENDOR, + .bInterfaceProtocol = USB_PROTO_VENDOR, + .iInterface = 2, + }, + .ep_in = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = FTDI_USB_EP_IN, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = FTDI_USB_EP_IN_SIZE, + .bInterval = 0, + }, + .ep_out = + { + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DTYPE_ENDPOINT, + .bEndpointAddress = FTDI_USB_EP_OUT, + .bmAttributes = USB_EPTYPE_BULK, + .wMaxPacketSize = FTDI_USB_EP_OUT_SIZE, + .bInterval = 0, + }, +}; + +FtdiUsb* ftdi_usb_start(void) { + FtdiUsb* ftdi_usb = malloc(sizeof(FtdiUsb)); + + ftdi_usb->usb_prev = furi_hal_usb_get_config(); + ftdi_usb->usb.init = ftdi_usb_init; + ftdi_usb->usb.deinit = ftdi_usb_deinit; + ftdi_usb->usb.wakeup = ftdi_usb_wakeup; + ftdi_usb->usb.suspend = ftdi_usb_suspend; + ftdi_usb->usb.dev_descr = (struct usb_device_descriptor*)&usb_ftdi_dev_descr; + ftdi_usb->usb.str_manuf_descr = (void*)&dev_manuf_desc; + ftdi_usb->usb.str_prod_descr = (void*)&dev_product_desc; + ftdi_usb->usb.str_serial_descr = NULL; + ftdi_usb->usb.cfg_descr = (void*)&usb_ftdi_cfg_descr; + + if(!furi_hal_usb_set_config(&ftdi_usb->usb, ftdi_usb)) { + FURI_LOG_E(TAG, "USB locked, cannot start Mass Storage"); + free(ftdi_usb->usb.str_prod_descr); + free(ftdi_usb->usb.str_serial_descr); + free(ftdi_usb); + return NULL; + } + return ftdi_usb; +} + +void ftdi_usb_stop(FtdiUsb* ftdi_usb) { + furi_hal_usb_set_config(ftdi_usb->usb_prev, NULL); +} diff --git a/flip_tdi/helpers/ftdi_usb.h b/flip_tdi/helpers/ftdi_usb.h new file mode 100644 index 0000000..dd75e71 --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb.h @@ -0,0 +1,6 @@ +#pragma once + +typedef struct FtdiUsb FtdiUsb; + +FtdiUsb* ftdi_usb_start(void); +void ftdi_usb_stop(FtdiUsb* ftdi); diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h new file mode 100644 index 0000000..08f2f27 --- /dev/null +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -0,0 +1,236 @@ +#pragma once +#include +#include + +// # Clocks and baudrates +#define FTDIBUS_CLOCK_BASE 6000000UL +#define FTDIBUS_CLOCK_HIGH 30000000UL +#define FTDI_BITBANG_BAUDRATE_RATIO_BASE 16 +#define FTDI_BITBANG_BAUDRATE_RATIO_HIGH 5 +#define FTDI_BAUDRATE_REF_BASE 3000000UL +#define FTDI_BAUDRATE_REF_HIGH 12000000UL + +/*descriptor type*/ +typedef enum { + FtdiDescriptorTypeDevice = 0x01, + FtdiDescriptorTypeConfig = 0x02, + FtdiDescriptorTypeString = 0x03, + FtdiDescriptorTypeInterface = 0x04, + FtdiDescriptorTypeEndpoint = 0x05, +} FtdiDescriptorType; + +/*endpoint direction*/ +typedef enum { + FtdiEndpointIn = 0x80, + FtdiEndpointOut = 0x00, +} FtdiEndpointDirection; + +/*endpoint type*/ +typedef enum { + FtdiEndpointTypeCtrl = 0x00, + FtdiEndpointTypeIso = 0x01, + FtdiEndpointTypeBulk = 0x02, + FtdiEndpointTypeIntr = 0x03, +} FtdiEndpointType; + +/*control request type*/ +typedef enum { + FtdiControlTypeStandard = (0 << 5), + FtdiControlTypeClass = (1 << 5), + FtdiControlTypeVendor = (2 << 5), + FtdiControlTypeReserved = (3 << 5), +} FtdiControlType; + +/*control request recipient*/ +typedef enum { + FtdiControlRecipientDevice = 0, + FtdiControlRecipientInterface = 1, + FtdiControlRecipientEndpoint = 2, + FtdiControlRecipientOther = 3, +} FtdiControlRecipient; + +/*control request direction*/ +typedef enum { + FtdiControlOut = 0x00, + FtdiControlIn = 0x80, +} FtdiControlDirection; + +/*endpoint address mask*/ +typedef enum { + FtdiEndpointAddrMask = 0x0f, + FtdiEndpointDirMask = 0x80, + FtdiEndpointTransferTypeMask = 0x03, + FtdiCtrlDirMask = 0x80, +} FtdiEndpointMask; + +// typedef enum { +// FtdiBitModeReset = 0x00, /**< switch off bitbang mode, back to regular serial/FIFO */ +// FtdiBitModeBitbang = +// 0x01, /**< classical asynchronous bitbang mode, introduced with B-type chips */ +// FtdiBitModeMpsse = 0x02, /**< MPSSE mode, available on 2232x chips */ +// FtdiBitModeSyncbb = 0x04, /**< synchronous bitbang mode, available on 2232x and R-type chips */ +// FtdiBitModeMcu = 0x08, /**< MCU Host Bus Emulation mode, available on 2232x chips */ +// /* CPU-style fifo mode gets set via EEPROM */ +// FtdiBitModeOpto = +// 0x10, /**< Fast Opto-Isolated Serial Interface Mode, available on 2232x chips */ +// FtdiBitModeCbus = +// 0x20, /**< Bitbang on CBUS pins of R-type chips, configure in EEPROM before */ +// FtdiBitModeSyncff = +// 0x40, /**< Single Channel Synchronous FIFO mode, available on 2232H chips */ +// FtdiBitModeFt1284 = 0x80, /**< FT1284 mode, available on 232H chips */ +// } FtdiBitMode; + + +//if(FtdiBitMode&0xff00==0) FtdiBitModeReset switch off bitbang mode, back to regular serial/FIFO +typedef struct { + //uint8_t MASK : 8; /*Mask*/ + uint8_t BITBANG : 1; /*classical asynchronous bitbang mode, introduced with B-type chips*/ + uint8_t MPSSE : 1; /*MPSSE mode, available on 2232x chips*/ + uint8_t SYNCBB : 1; /*synchronous bitbang mode, available on 2232x and R-type chips*/ + uint8_t MCU : 1; /*MCU Host Bus Emulation mode, available on 2232x chips*/ + uint8_t OPTO : 1; /*Fast Opto-Isolated Serial Interface Mode, available on 2232x chips*/ + uint8_t CBUS : 1; /*Bitbang on CBUS pins of R-type chips, configure in EEPROM before*/ + uint8_t SYNCFF : 1; /*Single Channel Synchronous FIFO mode, available on 2232H chips*/ + uint8_t FT1284 : 1; /*FT1284 mode, available on 232H chips*/ +} FtdiBitMode; + +/* FTDI MPSSE commands */ +typedef enum { + + FtdiMpsseCommandsSetBitsLow = 0x80, /**< Change LSB GPIO output */ + /*BYTE DATA*/ + /*BYTE Direction*/ + FtdiMpsseCommandsSetBitsHigh = 0x82, /**< Change MSB GPIO output */ + /*BYTE DATA*/ + /*BYTE Direction*/ + FtdiMpsseCommandsGetBitsLow = 0x81, /**< Get LSB GPIO output */ + FtdiMpsseCommandsGetBitsHigh = 0x83, /**< Get MSB GPIO output */ + FtdiMpsseCommandsLoopbackStart = 0x84, /**< Enable loopback */ + FtdiMpsseCommandsLoopbackEnd = 0x85, /**< Disable loopback */ + FtdiMpsseCommandsSetTckDivisor = 0x86, /**< Set clock */ + /* H Type specific commands */ + FtdiMpsseCommandsDisDiv5 = 0x8a, /**< Disable divide by 5 */ + FtdiMpsseCommandsEnDiv5 = 0x8b, /**< Enable divide by 5 */ + FtdiMpsseCommandsEnableClk3Phase = 0x8c, /**< Enable 3-phase data clocking (I2C) */ + FtdiMpsseCommandsDisableClk3Phase = 0x8d, /**< Disable 3-phase data clocking */ + FtdiMpsseCommandsClkBitsNoData = 0x8e, /**< Allows JTAG clock to be output w/o data */ + FtdiMpsseCommandsClkBytesNoData = 0x8f, /**< Allows JTAG clock to be output w/o data */ + FtdiMpsseCommandsClkWaitOnHigh = 0x94, /**< Clock until GPIOL1 is high */ + FtdiMpsseCommandsClkWaitOnLow = 0x95, /**< Clock until GPIOL1 is low */ + FtdiMpsseCommandsEnableClkAdaptive = 0x96, /**< Enable JTAG adaptive clock for ARM */ + FtdiMpsseCommandsDisableClkAdaptive = 0x97, /**< Disable JTAG adaptive clock */ + FtdiMpsseCommandsClkCountWaitOnHigh = 0x9c, /**< Clock byte cycles until GPIOL1 is high */ + FtdiMpsseCommandsClkCountWaitOnLow = 0x9d, /**< Clock byte cycles until GPIOL1 is low */ + //FT232H only + FtdiMpsseCommandsDriveZero = 0x9e, /**< Drive-zero mode */ +} FtdiMpsseCommands; + +/* USB control requests */ +typedef enum { + FtdiControlRequestsOut = (FtdiControlTypeVendor | FtdiControlRecipientDevice | FtdiControlOut), + FtdiControlRequestsIn = (FtdiControlTypeVendor | FtdiControlRecipientDevice | FtdiControlIn), +} FtdiControlRequests; + +/* Requests */ +typedef enum { + FtdiRequestsSiOReqReset = 0x0, /**< Reset the port */ + FtdiRequestsSiOReqSetModemCtrl = 0x1, /**< Set the modem control register */ + FtdiRequestsSiOReqSetFlowCtrl = 0x2, /**< Set flow control register */ + FtdiRequestsSiOReqSetBaudrate = 0x3, /**< Set baud rate */ + FtdiRequestsSiOReqSetData = 0x4, /**< Set the data characteristics of the port */ + FtdiRequestsSiOReqPollModemStatus = 0x5, /**< Get line status */ + FtdiRequestsSiOReqSetEventChar = 0x6, /**< Change event character */ + FtdiRequestsSiOReqSetErrorChar = 0x7, /**< Change error character */ + FtdiRequestsSiOReqSetLatencyTimer = 0x9, /**< Change latency timer */ + FtdiRequestsSiOReqGetLatencyTimer = 0xa, /**< Get latency timer */ + FtdiRequestsSiOReqSetBitmode = 0xb, /**< Change bit mode */ + FtdiRequestsSiOReqReadPins = 0xc, /**< Read GPIO pin value (or "get bitmode") */ + FtdiRequestsSiOReqReadEeprom = 0x90, /**< Read EEPROM */ + FtdiRequestsSiOReqWriteEeprom = 0x91, /**< Write EEPROM */ + FtdiRequestsSiOReqEraseEeprom = 0x92, /**< Erase EEPROM */ +} FtdiRequests; + +/*Eeprom requests*/ +typedef enum { + FtdiEepromRequestsEeprom = 0x90, + FtdiEepromRequestsReadEeprom = FtdiEepromRequestsEeprom + 0, /**< Read EEPROM content */ + FtdiEepromRequestsWriteEeprom = FtdiEepromRequestsEeprom + 1, /**< Write EEPROM content */ + FtdiEepromRequestsEraseEeprom = FtdiEepromRequestsEeprom + 2, /**< Erase EEPROM content */ +} FtdiEepromRequests; + +/*Reset arguments*/ +typedef enum { + FtdiResetSio = 0, /**< Reset device */ + FtdiResetPurgeRx = 1, /**< Drain USB RX buffer (host-to-ftdi) */ + FtdiResetPurgeTx = 2, /**< Drain USB TX buffer (ftdi-to-host) */ +} FtdiReset; + +/*Flow control arguments*/ +typedef enum { + FtdiFlowControlDisable = 0x0, + FtdiFlowControlRtsCtsHs = 0x1 << 8, + FtdiFlowControlDtrDsrHs = 0x2 << 8, + FtdiFlowControlXonXoffHs = 0x4 << 8, + FtdiFlowControlSetDtrMask = 0x1, + FtdiFlowControlSetDtrHigh = FtdiFlowControlSetDtrMask | (FtdiFlowControlSetDtrMask << 8), + FtdiFlowControlSetDtrLow = 0x0 | (FtdiFlowControlSetDtrMask << 8), + FtdiFlowControlSetRtsMask = 0x2, + FtdiFlowControlSetRtsHigh = FtdiFlowControlSetRtsMask | (FtdiFlowControlSetRtsMask << 8), + FtdiFlowControlSetRtsLow = 0x0 | (FtdiFlowControlSetRtsMask << 8), +} FtdiFlowControl; + +/*Parity bits */ +typedef enum { + FtdiParityNone = 0, + FtdiParityOdd = 1, + FtdiParityEven = 2, + FtdiParityMark = 3, + FtdiParitySpace = 4, +} FtdiParity; + +/*Number of stop bits*/ +typedef enum { + FtdiStopBits1 = 0, + FtdiStopBits15 = 1, + FtdiStopBits2 = 2, +} FtdiStopBits; + +/*Number of bits*/ +typedef enum { + FtdiBits7 = 7, + FtdiBits8 = 8, +} FtdiBits; + +/*Break type*/ +typedef enum { + FtdiBreakOff = 0, + FtdiBreakOn = 1, +} FtdiBreak; + +typedef struct { + uint8_t BITS : 4; /*Cound data bits*/ + uint8_t RESERVED : 4; /*Reserved0*/ + uint8_t PARITY : 2; /*Parity*/ + uint8_t STOP_BITS : 2; /*Number of stop bits*/ + uint8_t RESERVED1 : 1; /*Reserved1*/ + uint8_t BREAK: 1; /*Break type*/ +} FtdiDataConfig; + +typedef struct { + uint8_t RESERVED0 : 1; /*Reserved0*/ + uint8_t RESERVED1 : 1; /*Reserved1*/ + uint8_t RESERVED2 : 1; /*Reserved2*/ + uint8_t RESERVED3 : 1; /*Reserved3*/ + uint8_t CTS : 1; /*Clear to send (CTS)*/ + uint8_t DTS : 1; /*Data set ready (DTS)*/ + uint8_t RI : 1; /*Ring indicator (RI)*/ + uint8_t RLSD : 1; /*Receive line signal detect (RLSD)*/ + uint8_t DR : 1; /*Data ready (DR)*/ + uint8_t OE : 1; /*Overrun error (OE)*/ + uint8_t PE : 1; /*Parity error (PE)*/ + uint8_t FE : 1; /*Framing error (FE)*/ + uint8_t BI : 1; /*Break interrupt (BI)*/ + uint8_t THRE : 1; /*Transmitter holding register (THRE)*/ + uint8_t TEMT : 1; /*Transmitter empty (TEMT)*/ + uint8_t RCVR_FIFO : 1; /*Error in RCVR FIFO*/ +} FtdiModemStatus; diff --git a/flip_tdi/images/flip_tdi_wiring.png b/flip_tdi/images/flip_tdi_wiring.png new file mode 100644 index 0000000000000000000000000000000000000000..eec71af20a8ce7ad997a8a461a9e6f51a8cce3b7 GIT binary patch literal 4884 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 x54D|pfr~EHMxzbU2Q3gNgP6{ZbWN>&HXjv + +// Generate scene id and total number +#define ADD_SCENE(prefix, name, id) FlipTDIScene##id, +typedef enum { +#include "flip_tdi_scene_config.h" + FlipTDISceneNum, +} FlipTDIScene; +#undef ADD_SCENE + +extern const SceneManagerHandlers flip_tdi_scene_handlers; + +// Generate scene on_enter handlers declaration +#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); +#include "flip_tdi_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 "flip_tdi_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 "flip_tdi_scene_config.h" +#undef ADD_SCENE diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c new file mode 100644 index 0000000..96d49ed --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -0,0 +1,68 @@ +#include "../flip_tdi_app_i.h" +#include "../helpers/flip_tdi_types.h" + +void flip_tdi_scene_about_widget_callback(GuiButtonType result, InputType type, void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + if(type == InputTypeShort) { + view_dispatcher_send_custom_event(app->view_dispatcher, result); + } +} + +void flip_tdi_scene_about_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* 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", FAP_VERSION); + furi_string_cat_printf(temp_str, "Developed by: %s\n", FLIP_TDI_DEVELOPED); + furi_string_cat_printf(temp_str, "Github: %s\n\n", FLIP_TDI_GITHUB); + + furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); + furi_string_cat_printf(temp_str, "Emulator FT232H.\n\n"); + + furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); + furi_string_cat_printf(temp_str, "- Emulate FT232H VCP mode\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! FlipTDI \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, FlipTDIViewWidget); +} + +bool flip_tdi_scene_about_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} + +void flip_tdi_scene_about_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + // Clear views + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_config.h b/flip_tdi/scenes/flip_tdi_scene_config.h new file mode 100644 index 0000000..12c10ed --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_config.h @@ -0,0 +1,4 @@ +ADD_SCENE(flip_tdi, main, Main) +ADD_SCENE(flip_tdi, menu, Menu) +ADD_SCENE(flip_tdi, wiring, Wiring) +ADD_SCENE(flip_tdi, about, About) diff --git a/flip_tdi/scenes/flip_tdi_scene_main.c b/flip_tdi/scenes/flip_tdi_scene_main.c new file mode 100644 index 0000000..1e77b01 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_main.c @@ -0,0 +1,67 @@ +#include "../flip_tdi_app_i.h" +#include "../views/flip_tdi_view_main.h" + +void flip_tdi_scene_main_callback(FlipTDICustomEvent event, void* context) { + furi_assert(context); + FlipTDIApp* app = context; + view_dispatcher_send_custom_event(app->view_dispatcher, event); +} + +// static const NotificationSequence notification_app_display_on = { + +// &message_display_backlight_on, +// NULL, +// }; + +static void flip_tdi_scene_main_update(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + UNUSED(app); + + // uint32_t frequency = 0; + // bool nfc_field = flip_tdi_app_field_presence_is_nfc(app); + // bool rfid_field = flip_tdi_app_field_presence_is_rfid(app, &frequency); + + // if(nfc_field || rfid_field) + // notification_message(app->notifications, ¬ification_app_display_on); + + // flip_tdi_view_field_presence_update( + // app->flip_tdi_field_presence, nfc_field, rfid_field, frequency); +} + +void flip_tdi_scene_main_on_enter(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + + flip_tdi_view_main_set_callback( + app->flip_tdi_view_main_instance, flip_tdi_scene_main_callback, app); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewMain); +} + +bool flip_tdi_scene_main_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + FlipTDIApp* app = context; + bool consumed = false; + + if(event.type == SceneManagerEventTypeCustom) { + switch(event.event) { + case FlipTDICustomEventMainMore: + scene_manager_next_scene(app->scene_manager, FlipTDISceneMenu); + break; + default: + break; + } + + } else if(event.type == SceneManagerEventTypeTick) { + flip_tdi_scene_main_update(app); + } + + return consumed; +} + +void flip_tdi_scene_main_on_exit(void* context) { + furi_assert(context); + FlipTDIApp* app = context; + UNUSED(app); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring.c b/flip_tdi/scenes/flip_tdi_scene_wiring.c new file mode 100644 index 0000000..f4014ab --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/subghz_scene_menu.c b/flip_tdi/scenes/subghz_scene_menu.c new file mode 100644 index 0000000..204bbae --- /dev/null +++ b/flip_tdi/scenes/subghz_scene_menu.c @@ -0,0 +1,50 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_menu_submenu_callback(void* context, uint32_t index) { + furi_assert(context); + FlipTDIApp* app = context; + + view_dispatcher_send_custom_event(app->view_dispatcher, index); +} + +void flip_tdi_scene_menu_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + Submenu* submenu = app->submenu; + submenu_add_item( + submenu, "Wiring", SubmenuIndexWiring, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "About", SubmenuIndexAbout, flip_tdi_scene_menu_submenu_callback, app); + + submenu_set_selected_item( + submenu, scene_manager_get_scene_state(app->scene_manager, FlipTDISceneMenu)); + + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewSubmenu); +} + +bool flip_tdi_scene_menu_on_event(void* context, SceneManagerEvent event) { + furi_assert(context); + + FlipTDIApp* app = context; + bool consumed = false; + if(event.type == SceneManagerEventTypeCustom) { + if(event.event == SubmenuIndexAbout) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneAbout); + consumed = true; + } else if(event.event == SubmenuIndexWiring) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiring); + consumed = true; + } + scene_manager_set_scene_state(app->scene_manager, FlipTDIViewSubmenu, event.event); + } + + return consumed; +} + +void flip_tdi_scene_menu_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + submenu_reset(app->submenu); +} diff --git a/flip_tdi/views/flip_tdi_view_main.c b/flip_tdi/views/flip_tdi_view_main.c new file mode 100644 index 0000000..b001e03 --- /dev/null +++ b/flip_tdi/views/flip_tdi_view_main.c @@ -0,0 +1,103 @@ +#include "flip_tdi_view_main.h" +#include "../flip_tdi_app_i.h" +//#include + +#include +#include + +// #define FIELD_FOUND_WEIGHT 5 + +// typedef enum { +// NfcRfidDetectorTypeFieldPresenceNfc, +// NfcRfidDetectorTypeFieldPresenceRfid, +// } NfcRfidDetectorTypeFieldPresence; + +// static const Icon* FlipTDIViewMainTypeIcons[] = { +// [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, +// [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, +// }; + +struct FlipTDIViewMainType { + View* view; + FlipTDIViewMainTypeCallback callback; + void* context; +}; + +typedef struct { + uint32_t test; +} FlipTDIViewMainTypeModel; + +void flip_tdi_view_main_set_callback( + FlipTDIViewMainType* instance, + FlipTDIViewMainTypeCallback callback, + void* context) { + furi_assert(instance); + furi_assert(callback); + instance->callback = callback; + instance->context = context; +} + +void flip_tdi_view_main_draw(Canvas* canvas, FlipTDIViewMainTypeModel* model) { + UNUSED(model); + canvas_clear(canvas); + canvas_set_color(canvas, ColorBlack); + + canvas_set_font(canvas, FontPrimary); + canvas_draw_str(canvas, 34, 10, "FlipTDI"); + + elements_button_right(canvas, "More"); +} + +bool flip_tdi_view_main_input(InputEvent* event, void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + UNUSED(instance); + + if(event->key == InputKeyBack) { + return false; + } else if(event->key == InputKeyRight && event->type == InputTypeShort) { + instance->callback(FlipTDICustomEventMainMore, instance->context); + } + return true; +} + +void flip_tdi_view_main_enter(void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + with_view_model(instance->view, FlipTDIViewMainTypeModel * model, { model->test = 0; }, true); +} + +void flip_tdi_view_main_exit(void* context) { + furi_assert(context); + FlipTDIViewMainType* instance = context; + UNUSED(instance); +} + +FlipTDIViewMainType* flip_tdi_view_main_alloc() { + FlipTDIViewMainType* instance = malloc(sizeof(FlipTDIViewMainType)); + + // View allocation and configuration + instance->view = view_alloc(); + + view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(FlipTDIViewMainTypeModel)); + view_set_context(instance->view, instance); + view_set_draw_callback(instance->view, (ViewDrawCallback)flip_tdi_view_main_draw); + view_set_input_callback(instance->view, flip_tdi_view_main_input); + view_set_enter_callback(instance->view, flip_tdi_view_main_enter); + view_set_exit_callback(instance->view, flip_tdi_view_main_exit); + + with_view_model(instance->view, FlipTDIViewMainTypeModel * model, { model->test = 0; }, true); + return instance; +} + +void flip_tdi_view_main_free(FlipTDIViewMainType* instance) { + furi_assert(instance); + + view_free(instance->view); + free(instance); +} + +View* flip_tdi_view_main_get_view(FlipTDIViewMainType* instance) { + furi_assert(instance); + return instance->view; +} diff --git a/flip_tdi/views/flip_tdi_view_main.h b/flip_tdi/views/flip_tdi_view_main.h new file mode 100644 index 0000000..bfb6e4b --- /dev/null +++ b/flip_tdi/views/flip_tdi_view_main.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include "../helpers/flip_tdi_types.h" +#include "../helpers/flip_tdi_event.h" + +typedef struct FlipTDIViewMainType FlipTDIViewMainType; + +typedef void (*FlipTDIViewMainTypeCallback)(FlipTDICustomEvent event, void* context); + +void flip_tdi_view_main_set_callback( + FlipTDIViewMainType* instance, + FlipTDIViewMainTypeCallback callback, + void* context); + +FlipTDIViewMainType* flip_tdi_view_main_alloc(); + +void flip_tdi_view_main_free(FlipTDIViewMainType* instance); + +View* flip_tdi_view_main_get_view(FlipTDIViewMainType* instance); From 11f46933ab695c4783410e871eac99d87af0d161 Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 13 Jul 2024 17:56:52 +0400 Subject: [PATCH 02/19] FlipTDI: add set UART data config --- flip_tdi/flip_tdi_app.c | 25 ------------ flip_tdi/helpers/ftdi.c | 7 +--- flip_tdi/helpers/ftdi_uart.c | 61 ++++++++++++++++++++++++++++++ flip_tdi/helpers/ftdi_uart.h | 3 +- flip_tdi/helpers/ftdi_usb_define.h | 3 +- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/flip_tdi/flip_tdi_app.c b/flip_tdi/flip_tdi_app.c index a161b0e..df9f16e 100644 --- a/flip_tdi/flip_tdi_app.c +++ b/flip_tdi/flip_tdi_app.c @@ -1,30 +1,5 @@ #include #include "flip_tdi_app_i.h" -//#include -//#include "helpers/ftdi_usb.h" - -// int32_t flip_tdi_app(void* p) { -// UNUSED(p); - -// FtdiUsb* ftdi = ftdi_usb_start(); - -// if(ftdi) { -// DialogsApp* dialogs = furi_record_open(RECORD_DIALOGS); -// DialogMessage* message = dialog_message_alloc(); - -// dialog_message_set_header(message, "Hello, world!", 64, 0, AlignCenter, AlignTop); -// dialog_message_set_text(message, "", 0, 63, AlignLeft, AlignBottom); -// dialog_message_show(dialogs, message); - -// dialog_message_free(message); - -// ftdi_usb_stop(ftdi); -// } - -// return 0; -// } - - static bool flip_tdi_app_custom_event_callback(void* context, uint32_t event) { furi_assert(context); diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 54bea16..8ec60d3 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -35,11 +35,8 @@ Ftdi* ftdi_alloc(void) { furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); ftdi->baudrate = 115200; - - ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); - return ftdi; } @@ -185,6 +182,7 @@ void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { return; } ftdi->data_config = *((FtdiDataConfig*)&value); + ftdi_uart_set_data_config(ftdi->ftdi_uart, &ftdi->data_config); } void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { @@ -252,9 +250,6 @@ void ftdi_get_modem_status(uint16_t* status) { *status = *((uint16_t*)&modem_status); } - - - void ftdi_start_uart_tx(Ftdi* ftdi) { ftdi_uart_tx(ftdi->ftdi_uart); } \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c index 47fdce7..e07ec0e 100644 --- a/flip_tdi/helpers/ftdi_uart.c +++ b/flip_tdi/helpers/ftdi_uart.c @@ -2,6 +2,8 @@ #include "furi.h" #include +#include + #define TAG "FTDI_UART" #define FTDI_UART_MAX_TXRX_SIZE (64UL) @@ -142,3 +144,62 @@ void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate) { ftdi_uart->baudrate = baudrate; furi_hal_serial_set_br(ftdi_uart->serial_handle, baudrate); } + +void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config) { + furi_assert(data_config); + UNUSED(ftdi_uart); + + bool is_uart_enabled = LL_LPUART_IsEnabled(LPUART1); + + if(is_uart_enabled) { + LL_LPUART_Disable(LPUART1); + } + + uint32_t data_width = LL_LPUART_DATAWIDTH_8B; + uint32_t parity_mode = LL_LPUART_PARITY_NONE; + uint32_t stop_bits_mode = LL_LPUART_STOPBITS_2; + + switch(data_config->BITS) { + case FtdiBits7: + data_width = LL_LPUART_DATAWIDTH_7B; + break; + case FtdiBits8: + data_width = LL_LPUART_DATAWIDTH_8B; + break; + default: + break; + } + + switch(data_config->PARITY) { + case FtdiParityNone: + parity_mode = LL_LPUART_PARITY_NONE; + break; + case FtdiParityOdd: + parity_mode = LL_LPUART_PARITY_ODD; + break; + case FtdiParityEven: + case FtdiParityMark: + case FtdiParitySpace: + parity_mode = LL_LPUART_PARITY_EVEN; + break; + default: + break; + } + + switch(data_config->STOP_BITS) { + case FtdiStopBits1: + stop_bits_mode = LL_LPUART_STOPBITS_1; + break; + case FtdiStopBits15: + case FtdiStopBits2: + stop_bits_mode = LL_LPUART_STOPBITS_2; + break; + default: + break; + } + + LL_LPUART_ConfigCharacter(LPUART1, data_width, parity_mode, stop_bits_mode); + if(is_uart_enabled) { + LL_LPUART_Enable(LPUART1); + } +} diff --git a/flip_tdi/helpers/ftdi_uart.h b/flip_tdi/helpers/ftdi_uart.h index 3d499ef..ba25605 100644 --- a/flip_tdi/helpers/ftdi_uart.h +++ b/flip_tdi/helpers/ftdi_uart.h @@ -6,4 +6,5 @@ typedef struct FtdiUart FtdiUart; FtdiUart* ftdi_uart_alloc(Ftdi* ftdi); void ftdi_uart_free(FtdiUart* ftdi_uart); void ftdi_uart_tx(FtdiUart* ftdi_uart); -void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate); \ No newline at end of file +void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate); +void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h index 08f2f27..91cb58e 100644 --- a/flip_tdi/helpers/ftdi_usb_define.h +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -210,9 +210,8 @@ typedef enum { typedef struct { uint8_t BITS : 4; /*Cound data bits*/ uint8_t RESERVED : 4; /*Reserved0*/ - uint8_t PARITY : 2; /*Parity*/ + uint8_t PARITY : 3; /*Parity*/ uint8_t STOP_BITS : 2; /*Number of stop bits*/ - uint8_t RESERVED1 : 1; /*Reserved1*/ uint8_t BREAK: 1; /*Break type*/ } FtdiDataConfig; From f761fc9ee406961161694f1a7039637ebabb034e Mon Sep 17 00:00:00 2001 From: SkorP Date: Sat, 13 Jul 2024 20:11:29 +0400 Subject: [PATCH 03/19] FlipTDI: add UART tx dma --- flip_tdi/helpers/ftdi.c | 31 +++++++-- flip_tdi/helpers/ftdi.h | 6 +- flip_tdi/helpers/ftdi_uart.c | 125 ++++++++++++++++++++++++++++++++--- flip_tdi/helpers/ftdi_usb.c | 10 +-- 4 files changed, 151 insertions(+), 21 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 8ec60d3..c5f2a46 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -35,6 +35,12 @@ Ftdi* ftdi_alloc(void) { furi_stream_buffer_alloc(sizeof(uint8_t) * FTDI_TX_RX_BUF_SIZE, sizeof(uint8_t)); ftdi->baudrate = 115200; + FtdiModemStatus status = {0}; + ftdi->status = status; + ftdi->status.RESERVED1 = 1; + ftdi->status.TEMT = 1; + ftdi->status.THRE = 1; + ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); return ftdi; @@ -241,13 +247,26 @@ uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { // - B5 Transmitter holding register (THRE) // - B6 Transmitter empty (TEMT) // - B7 Error in RCVR FIFO -void ftdi_get_modem_status(uint16_t* status) { - FtdiModemStatus modem_status = {0}; - modem_status.RESERVED1 = 1; - modem_status.TEMT = 1; - modem_status.THRE = 1; - *status = *((uint16_t*)&modem_status); +// void ftdi_get_modem_status(uint16_t* status) { +// FtdiModemStatus modem_status = {0}; +// modem_status.RESERVED1 = 1; +// modem_status.TEMT = 1; +// modem_status.THRE = 1; + +// *status = *((uint16_t*)&modem_status); +// } + +uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi) { + return (uint16_t*)&ftdi->status; +} + +FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi) { + return ftdi->status; +} + +void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status) { + ftdi->status = status; } void ftdi_start_uart_tx(Ftdi* ftdi) { diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index 235cee6..bfbc30f 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -22,8 +22,10 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index); void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index); uint8_t ftdi_get_latency_timer(Ftdi* ftdi); -void ftdi_get_modem_status(uint16_t *status); +//void ftdi_get_modem_status(uint16_t *status); +uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi); +FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi); +void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status); void ftdi_start_uart_tx(Ftdi* ftdi); - diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c index e07ec0e..104a108 100644 --- a/flip_tdi/helpers/ftdi_uart.c +++ b/flip_tdi/helpers/ftdi_uart.c @@ -3,15 +3,20 @@ #include #include +#include #define TAG "FTDI_UART" -#define FTDI_UART_MAX_TXRX_SIZE (64UL) +#define FTDI_UART_MAX_TXRX_SIZE (256UL) + +#define FTDI_UART_LPUART_DMA_INSTANCE (DMA2) +#define FTDI_UART_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_1) struct FtdiUart { FuriThread* worker_thread; FuriHalSerialHandle* serial_handle; uint32_t baudrate; Ftdi* ftdi; + uint8_t* buffer_tx_ptr; }; typedef enum { @@ -23,11 +28,17 @@ typedef enum { WorkerEventRxFramingError = (1 << 5), WorkerEventRxNoiseError = (1 << 6), WorkerEventTXData = (1 << 7), + WorkerEventTXDataDmaEnd = (1 << 8), } WorkerEvent; #define WORKER_EVENTS_MASK \ (WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \ - WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventTXData) + WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventTXData | \ + WorkerEventTXDataDmaEnd) + +static void ftdi_uart_tx_dma_init(FtdiUart* ftdi_uart); +static void ftdi_uart_tx_dma_deinit(FtdiUart* ftdi_uart); +static void ftdi_uart_tx_dma(FtdiUart* ftdi_uart, uint8_t* data, size_t size); static void ftdi_uart_irq_cb( FuriHalSerialHandle* handle, @@ -56,6 +67,9 @@ static int32_t uart_echo_worker(void* context) { FURI_LOG_I(TAG, "Worker started"); + ftdi_uart_tx_dma_init(ftdi_uart); + bool is_dma_tx = false; + while(1) { uint32_t events = furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); @@ -64,14 +78,35 @@ static int32_t uart_echo_worker(void* context) { if(events & WorkerEventStop) break; if(events & WorkerEventTXData) { + if(!is_dma_tx) { + // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); + // status.TEMT = 0; + // ftdi_set_modem_status(ftdi_uart->ftdi, status); + is_dma_tx = true; + events |= WorkerEventTXDataDmaEnd; + } + } + + if(events & WorkerEventTXDataDmaEnd) { size_t length = 0; - uint8_t data[FTDI_UART_MAX_TXRX_SIZE]; - do { - length = ftdi_get_rx_buf(ftdi_uart->ftdi, data, FTDI_UART_MAX_TXRX_SIZE); - if(length > 0) { - furi_hal_serial_tx(ftdi_uart->serial_handle, data, length); - } - } while(length > 0); + + // if(ftdi_available_rx_buf(ftdi_uart->ftdi) > 2048) { + // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); + // status.THRE = 0; + // ftdi_set_modem_status(ftdi_uart->ftdi, status); + // } + + length = ftdi_get_rx_buf( + ftdi_uart->ftdi, ftdi_uart->buffer_tx_ptr, FTDI_UART_MAX_TXRX_SIZE); + if(length > 0) { + //furi_hal_serial_tx(ftdi_uart->serial_handle, data, length); + ftdi_uart_tx_dma(ftdi_uart, ftdi_uart->buffer_tx_ptr, length); + } else { + is_dma_tx = false; + // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); + // status.TEMT = 1; + // ftdi_set_modem_status(ftdi_uart->ftdi, status); + } } // if(events & WorkerEventRxData) { @@ -103,6 +138,7 @@ static int32_t uart_echo_worker(void* context) { // } // } } + ftdi_uart_tx_dma_deinit(ftdi_uart); FURI_LOG_I(TAG, "Worker stopped"); return 0; } @@ -119,6 +155,7 @@ FtdiUart* ftdi_uart_alloc(Ftdi* ftdi) { furi_hal_serial_dma_rx_start(ftdi_uart->serial_handle, ftdi_uart_irq_cb, ftdi_uart, false); ftdi_uart->ftdi = ftdi; + //do not change LPUART, functions that directly work with peripherals are used ftdi_uart->worker_thread = furi_thread_alloc_ex(TAG, 1024, uart_echo_worker, ftdi_uart); furi_thread_start(ftdi_uart->worker_thread); @@ -203,3 +240,73 @@ void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config) LL_LPUART_Enable(LPUART1); } } + +static void ftdi_uart_dma_tx_isr(void* context) { +#if FTDI_UART_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_1 + FtdiUart* ftdi_uart = context; + + if(LL_DMA_IsActiveFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE)) { + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + LL_DMA_DisableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + //Todo It's a bad idea to wait for the end of the transfer in an interrupt... + while(!LL_LPUART_IsActiveFlag_TC(LPUART1)) + ; + furi_thread_flags_set( + furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXDataDmaEnd); + } + +#else +#error Update this code. Would you kindly? +#endif +} + +static void ftdi_uart_tx_dma_init(FtdiUart* ftdi_uart) { + ftdi_uart->buffer_tx_ptr = malloc(FTDI_UART_MAX_TXRX_SIZE); + + LL_DMA_SetPeriphAddress( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, (uint32_t) & (LPUART1->TDR)); + + LL_DMA_ConfigTransfer( + FTDI_UART_LPUART_DMA_INSTANCE, + FTDI_UART_LPUART_DMA_CHANNEL, + LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_NORMAL | LL_DMA_PERIPH_NOINCREMENT | + LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE | + LL_DMA_PRIORITY_HIGH); + LL_DMA_SetPeriphRequest( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, LL_DMAMUX_REQ_LPUART1_TX); + + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma2Ch1, ftdi_uart_dma_tx_isr, ftdi_uart); + +#if FTDI_UART_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_1 + if(LL_DMA_IsActiveFlag_HT1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_HT1(FTDI_UART_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + if(LL_DMA_IsActiveFlag_TE1(FTDI_UART_LPUART_DMA_INSTANCE)) + LL_DMA_ClearFlag_TE1(FTDI_UART_LPUART_DMA_INSTANCE); +#else +#error Update this code. Would you kindly? +#endif + + LL_DMA_EnableIT_TC(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + LL_LPUART_EnableDMAReq_TX(LPUART1); +} + +static void ftdi_uart_tx_dma_deinit(FtdiUart* ftdi_uart) { + LL_DMA_DisableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_LPUART_DisableDMAReq_TX(LPUART1); + LL_DMA_DisableIT_TC(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); + LL_DMA_ClearFlag_TC1(FTDI_UART_LPUART_DMA_INSTANCE); + furi_hal_interrupt_set_isr(FuriHalInterruptIdDma2Ch1, NULL, NULL); + free(ftdi_uart->buffer_tx_ptr); +} + +static void ftdi_uart_tx_dma(FtdiUart* ftdi_uart, uint8_t* data, size_t size) { + UNUSED(ftdi_uart); + LL_DMA_SetMemoryAddress( + FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, (uint32_t)data); + LL_DMA_SetDataLength(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL, size); + + LL_DMA_EnableChannel(FTDI_UART_LPUART_DMA_INSTANCE, FTDI_UART_LPUART_DMA_CHANNEL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index cc04125..0818cfe 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -69,9 +69,11 @@ static int32_t ftdi_thread_worker(void* context) { uint32_t len_data = 0; FtdiTxData tx_data = {0}; - uint16_t status = 0; - ftdi_get_modem_status(&status); - tx_data.status = status; + // uint16_t *status = 0; + // ftdi_get_modem_status(&status); + uint16_t *status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); + + tx_data.status = status[0]; ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); while(true) { @@ -106,7 +108,7 @@ static int32_t ftdi_thread_worker(void* context) { flags |= EventTx; } if(flags & EventTx) { - tx_data.status = status; + tx_data.status = status[0]; len_data = ftdi_available_tx_buf(ftdi_usb->ftdi); if(len_data > 0) { From 70737661d32c9e79d65f3e9d60c49fab7c0a3fde Mon Sep 17 00:00:00 2001 From: SkorP Date: Mon, 15 Jul 2024 17:28:04 +0400 Subject: [PATCH 04/19] FlipTDI: add async/sync bitband mode --- flip_tdi/helpers/ftdi.c | 250 ++++++++++++++++++- flip_tdi/helpers/ftdi.h | 1 + flip_tdi/helpers/ftdi_bitbang.c | 318 +++++++++++++++++++++++++ flip_tdi/helpers/ftdi_bitbang.h | 12 + flip_tdi/helpers/ftdi_uart.c | 59 +++-- flip_tdi/helpers/ftdi_uart.h | 3 +- flip_tdi/helpers/ftdi_usb.c | 36 ++- flip_tdi/helpers/ftdi_usb_define.h | 2 +- flip_tdi/scenes/flip_tdi_scene_about.c | 2 + 9 files changed, 634 insertions(+), 49 deletions(-) create mode 100644 flip_tdi/helpers/ftdi_bitbang.c create mode 100644 flip_tdi/helpers/ftdi_bitbang.h diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index c5f2a46..cb414f3 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -1,6 +1,7 @@ #include "ftdi.h" #include "furi.h" #include "ftdi_uart.h" +#include "ftdi_bitbang.h" #define TAG "FTDI" @@ -14,11 +15,14 @@ struct Ftdi { FuriStreamBuffer* stream_rx; FuriStreamBuffer* stream_tx; uint32_t baudrate; + uint32_t bitband_speed; FtdiDataConfig data_config; FtdiBitMode bit_mode; + uint8_t bit_mode_mask; uint8_t latency_timer; FtdiUart* ftdi_uart; + FtdiBitbang* ftdi_bitbang; }; static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { @@ -42,12 +46,14 @@ Ftdi* ftdi_alloc(void) { ftdi->status.THRE = 1; ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); + ftdi->ftdi_bitbang = ftdi_bitbang_alloc(ftdi); return ftdi; } void ftdi_free(Ftdi* ftdi) { ftdi_uart_free(ftdi->ftdi_uart); + ftdi_bitbang_free(ftdi->ftdi_bitbang); furi_stream_buffer_free(ftdi->stream_tx); furi_stream_buffer_free(ftdi->stream_rx); free(ftdi); @@ -172,15 +178,24 @@ void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { round((float)clk / ((float)integer_divisor + divisor[(encoded_divisor >> 14) & 0x7])); } + ftdi->baudrate = baudrate; + if(is_12mhz) { + ftdi->bitband_speed = ftdi->baudrate * FTDI_BITBANG_BAUDRATE_RATIO_HIGH; + } else { + ftdi->bitband_speed = ftdi->baudrate * FTDI_BITBANG_BAUDRATE_RATIO_BASE; + } + + ftdi_uart_set_baudrate(ftdi->ftdi_uart, baudrate); + ftdi_bitbang_set_speed(ftdi->ftdi_bitbang, ftdi->bitband_speed); + furi_log_puts("ftdi_set_baudrate="); char tmp_str2[] = "4294967295"; itoa(baudrate, tmp_str2, 10); furi_log_puts(tmp_str2); + furi_log_puts(" bb_speed="); + itoa(ftdi->bitband_speed, tmp_str2, 10); + furi_log_puts(tmp_str2); furi_log_puts("\r\n"); - - ftdi->baudrate = baudrate; - - ftdi_uart_set_baudrate(ftdi->ftdi_uart, baudrate); } void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { @@ -201,13 +216,115 @@ void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { } } +/* + def control_set_bitmode(self, wValue: int, wIndex: int, + data: array) -> None: + direction = wValue & 0xff + bitmode = (wValue >> 8) & 0x7F + mode = self.BitMode(bitmode).name + self.log.info('> ftdi bitmode %s: %s', mode, f'{direction:08b}') + self._bitmode = bitmode + with self._cmd_q.lock: + self._cmd_q.q.append((self.Command.SET_BITMODE, self._bitmode)) + self._cmd_q.event.set() + # be sure to wait for the command to be popped out before resuming + loop = 10 # is something goes wrong, do not loop forever + while loop: + if not self._cmd_q.q: + # command queue has been fully processed + break + if not self._resume: + # if the worker threads are ending or have ended, abort + self.log.warning('Premature end of worker') + return + loop -= 1 + # kepp some time for the commands to be processed + sleep(0.05) + else: + raise RuntimeError(f'Command {self._cmd_q.q[-1][0].name} ' + f'not handled') + if bitmode == self.BitMode.CBUS: + self._cbus_dir = direction >> 4 + mask = (1 << self._parent.properties.cbuswidth) - 1 + self._cbus_dir &= mask + # clear output pins + self._cbus &= ~self._cbus_dir & 0xF + # update output pins + output = direction & 0xF & self._cbus_dir + self._cbus |= output + self.log.info('> ftdi cbus dir %s, io %s, mask %s', + f'{self._cbus_dir:04b}', + f'{self._cbus:04b}', + f'{mask:04b}') + elif bitmode == self.BitMode.RESET: + self._direction = ((1 << VirtFtdiPort.UART_PINS.TXD) | + (1 << VirtFtdiPort.UART_PINS.RTS) | + (1 << VirtFtdiPort.UART_PINS.DTR)) + self._pins[0].set_function(VirtualFtdiPin.Function.STREAM) + self._pins[1].set_function(VirtualFtdiPin.Function.STREAM) + for pin in self._pins[2:]: + pin.set_function(VirtualFtdiPin.Function.GPIO) + else: + self._direction = direction + for pin in self._pins: + pin.set_function(VirtualFtdiPin.Function.GPIO) + if bitmode == self.BitMode.MPSSE: + for pin in self._pins: + pin.set_function(VirtualFtdiPin.Function.GPIO) + if not self._mpsse: + self._mpsse = VirtMpsseTracer(self, self._parent.version) +*/ + +//buf0 = 0x02 # magic constant, no idea for now +// if self._bitmode == self.BitMode.RESET: +// cts = 0x01 if self._gpio & 0x08 else 0 +// dtr = 0x02 if self._gpio & 0x20 else 0 +// ri = 0x04 if self._gpio & 0x80 else 0 +// dcd = 0x08 if self._gpio & 0x40 else 0 +// buf0 |= cts | dsr | ri | dcd +// else: +// # another magic constant +// buf0 |= 0x30 +// buf1 = 0 +// rx_fifo = self._fifos.rx +// with rx_fifo.lock: +// if not rx_fifo.q: +// # TX empty -> flag THRE & TEMT ("TX empty") +// buf1 |= 0x40 | 0x20 +// return buf0, buf1 + void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { if(!ftdi_check_interface(ftdi, index)) { return; } //todo no use mask value&0xFF + ftdi->bit_mode_mask = value & 0xFF; uint8_t bit_mode = value >> 8; ftdi->bit_mode = *((FtdiBitMode*)&(bit_mode)); + + // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); + + if(bit_mode == 0x00) { // Reset + ftdi_uart_enable(ftdi->ftdi_uart, true); // UART mode + ftdi->status.RESERVED1 = 1; + ftdi->status.CTS = 0; + ftdi->status.DTR = 0; + //todo: cts, dtr, ri, dcd is not implemented + } else { + ftdi_uart_enable(ftdi->ftdi_uart, false); + ftdi->status.CTS = 1; + ftdi->status.DTR = 1; + } + + if(ftdi->bit_mode.BITBANG) { + ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, ftdi->bit_mode_mask); + ftdi_bitbang_enable(ftdi->ftdi_bitbang, true, true); + } else if(ftdi->bit_mode.SYNCBB) { + ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, ftdi->bit_mode_mask); + ftdi_bitbang_enable(ftdi->ftdi_bitbang, true, false); + } else { + ftdi_bitbang_enable(ftdi->ftdi_bitbang, false, true); + } } void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index) { @@ -228,7 +345,7 @@ uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { // - B4 Clear to send (CTS) // 0 = inactive // 1 = active -// - B5 Data set ready (DTS) +// - B5 Data set ready (DTR) // 0 = inactive // 1 = active // - B6 Ring indicator (RI) @@ -257,6 +374,45 @@ uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { // *status = *((uint16_t*)&modem_status); // } +// @property +// def modem_status(self) -> Tuple[int, int]: +// # For some reason, B0 high nibble matches the LPC214x UART:UxMSR +// # B0.0 ? +// # B0.1 ? +// # B0.2 ? +// # B0.3 ? +// # B0.4 Clear to send (CTS) +// # B0.5 Data set ready (DTS) +// # B0.6 Ring indicator (RI) +// # B0.7 Receive line signal / Data carrier detect (RLSD/DCD) + +// # For some reason, B1 exactly matches the LPC214x UART:UxLSR +// # B1.0 Data ready (DR) +// # B1.1 Overrun error (OE) +// # B1.2 Parity error (PE) +// # B1.3 Framing error (FE) +// # B1.4 Break interrupt (BI) +// # B1.5 Transmitter holding register (THRE) +// # B1.6 Transmitter empty (TEMT) +// # B1.7 Error in RCVR FIFO +// buf0 = 0x02 # magic constant, no idea for now +// if self._bitmode == self.BitMode.RESET: +// cts = 0x01 if self._gpio & 0x08 else 0 +// dsr = 0x02 if self._gpio & 0x20 else 0 +// ri = 0x04 if self._gpio & 0x80 else 0 +// dcd = 0x08 if self._gpio & 0x40 else 0 +// buf0 |= cts | dsr | ri | dcd +// else: +// # another magic constant +// buf0 |= 0x30 +// buf1 = 0 +// rx_fifo = self._fifos.rx +// with rx_fifo.lock: +// if not rx_fifo.q: +// # TX empty -> flag THRE & TEMT ("TX empty") +// buf1 |= 0x40 | 0x20 +// return buf0, buf1 + uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi) { return (uint16_t*)&ftdi->status; } @@ -271,4 +427,86 @@ void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status) { void ftdi_start_uart_tx(Ftdi* ftdi) { ftdi_uart_tx(ftdi->ftdi_uart); -} \ No newline at end of file +} + +uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi) { + ftdi_reset_purge_tx(ftdi); + return ftdi_bitbang_gpio_get(ftdi->ftdi_bitbang); +} + +/* + def control_set_event_char(self, wValue: int, wIndex: int, + data: array) -> None: + char = wValue & 0xFF + enable = bool(wValue >> 8) + self.log.info('> ftdi %sable event char: 0x%02x', + 'en' if enable else 'dis', char) + + def control_set_error_char(self, wValue: int, wIndex: int, + data: array) -> None: + char = wValue & 0xFF + enable = bool(wValue >> 8) + self.log.info('> ftdi %sable error char: 0x%02x', + 'en' if enable else 'dis', char) + + +elif self._bitmode == self.BitMode.BITBANG: + for byte in data: + # only 8 LSBs are addressable through this command + gpi = self._gpio & ~self._direction & 0xFF + gpo = byte & self._direction & 0xFF + msb = self._gpio & ~0xFF + gpio = gpi | gpo | msb + self._update_gpio(False, gpio) + self.log.debug('. bbw %02x: %s', + self._gpio, f'{self._gpio:08b}') + elif self._bitmode == self.BitMode.SYNCBB: + tx_fifo = self._fifos.tx + lost = 0 + for byte in data: + with tx_fifo.lock: + free_count = tx_fifo.size - len(tx_fifo.q) + if free_count > 0: + tx_fifo.q.append(self._gpio & 0xFF) + else: + lost += 1 + # only 8 LSBs are addressable through this command + gpi = self._gpio & ~self._direction & 0xFF + gpo = byte & self._direction & 0xFF + msb = self._gpio & ~0xFF + gpio = gpi | gpo | msb + self._update_gpio(False, gpio) + self.log.debug('. bbw %02x: %s', + self._gpio, f'{self._gpio:08b}') + if lost: + self.log.debug('%d samples lost, TX full', lost) + else: + try: + mode = self.BitMode(self._bitmode).name + except ValueError: + mode = 'unknown' + self.log.warning('Write buffer discarded, mode %s', mode) + self.log.warning('. (%d) %s', + len(data), hexlify(data).decode()) + + def _tx_worker_generate(self, bitmode) -> None: + tx_fifo = self._fifos.tx + if bitmode == self.BitMode.BITBANG: + ts = now() + # how much time has elapsed since last background action + elapsed = ts-self._last_txw_ts + self._last_txw_ts = ts + # how many bytes should have been captured since last + # action + byte_count = round(self._baudrate*elapsed) + # fill the TX FIFO with as many bytes, stop if full + if byte_count: + with tx_fifo.lock: + free_count = tx_fifo.size - len(tx_fifo.q) + push_count = min(free_count, byte_count) + tx_fifo.q.extend([self._gpio] * push_count) + self.log.debug('in %.3fms -> %d', + elapsed*1000, push_count) + + +*/ \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index bfbc30f..b6d55bf 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -29,3 +29,4 @@ FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi); void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status); void ftdi_start_uart_tx(Ftdi* ftdi); +uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi); diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c new file mode 100644 index 0000000..a501b44 --- /dev/null +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -0,0 +1,318 @@ +#include "ftdi_bitbang.h" +#include "furi.h" +#include +#include +#include + +#define TAG "FTDI_BITBANG" + +#define gpio_b0 (gpio_ext_pa7) +#define gpio_b1 (gpio_ext_pa6) +#define gpio_b2 (gpio_ext_pa4) +#define gpio_b3 (gpio_ext_pb3) +#define gpio_b4 (gpio_ext_pb2) +#define gpio_b5 (gpio_ext_pc3) +#define gpio_b6 (gpio_ext_pc1) +#define gpio_b7 (gpio_ext_pc0) + +struct FtdiBitbang { + FuriThread* worker_thread; + Ftdi* ftdi; + uint32_t speed; + bool enable; + uint8_t gpio_mask; + bool async; +}; + +typedef enum { + WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event + WorkerEventStop = (1 << 1), + WorkerEventTimerUpdate = (1 << 2), +} WorkerEvent; + +#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventTimerUpdate) + +static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang); +static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang); +static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang); +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang); + +static int32_t ftdi_bitbang_worker(void* context) { + furi_assert(context); + FtdiBitbang* ftdi_bitbang = context; + + uint8_t buffer[64]; + + FURI_LOG_I(TAG, "Worker started"); + ftdi_bitbang_tim_init(ftdi_bitbang); + while(1) { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); + + if(events & WorkerEventStop) break; + if(events & WorkerEventTimerUpdate) { + size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + if(length > 0) { + ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + if(!ftdi_bitbang->async) { + buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, buffer, 1); + } + } + if(ftdi_bitbang->enable && ftdi_bitbang->async) { + buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, buffer, 1); + } + } + } + ftdi_bitbang_tim_deinit(ftdi_bitbang); + ftdi_bitbang_gpio_deinit(ftdi_bitbang); + FURI_LOG_I(TAG, "Worker stopped"); + return 0; +} + +#pragma GCC push_options +#pragma GCC optimize("Ofast,unroll-loops") +// #pragma GCC optimize("O3") +// #pragma GCC optimize("-funroll-all-loops") + +static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { + if(ftdi_bitbang->gpio_mask & 0b00000001) { + furi_hal_gpio_init(&gpio_b0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b00000010) { + furi_hal_gpio_init(&gpio_b1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b00000100) { + furi_hal_gpio_init(&gpio_b2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b00001000) { + furi_hal_gpio_init(&gpio_b3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b00010000) { + furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b00100000) { + furi_hal_gpio_init(&gpio_b5, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b01000000) { + furi_hal_gpio_init(&gpio_b6, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } + if(ftdi_bitbang->gpio_mask & 0b10000000) { + furi_hal_gpio_init(&gpio_b7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + } else { + furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + } +} + +static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang) { + UNUSED(ftdi_bitbang); + furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} + +static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data) { + if(ftdi_bitbang->gpio_mask & 0b00000001) { + if(gpio_data & 0b00000001) { + furi_hal_gpio_write(&gpio_b0, 1); + } else { + furi_hal_gpio_write(&gpio_b0, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b00000010) { + if(gpio_data & 0b00000010) { + furi_hal_gpio_write(&gpio_b1, 1); + } else { + furi_hal_gpio_write(&gpio_b1, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b00000100) { + if(gpio_data & 0b00000100) { + furi_hal_gpio_write(&gpio_b2, 1); + } else { + furi_hal_gpio_write(&gpio_b2, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b00001000) { + if(gpio_data & 0b00001000) { + furi_hal_gpio_write(&gpio_b3, 1); + } else { + furi_hal_gpio_write(&gpio_b3, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b00010000) { + if(gpio_data & 0b00010000) { + furi_hal_gpio_write(&gpio_b4, 1); + } else { + furi_hal_gpio_write(&gpio_b4, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b00100000) { + if(gpio_data & 0b00100000) { + furi_hal_gpio_write(&gpio_b5, 1); + } else { + furi_hal_gpio_write(&gpio_b5, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b01000000) { + if(gpio_data & 0b01000000) { + furi_hal_gpio_write(&gpio_b6, 1); + } else { + furi_hal_gpio_write(&gpio_b6, 0); + } + } + + if(ftdi_bitbang->gpio_mask & 0b10000000) { + if(gpio_data & 0b10000000) { + furi_hal_gpio_write(&gpio_b7, 1); + } else { + furi_hal_gpio_write(&gpio_b7, 0); + } + } +} + +uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang) { + UNUSED(ftdi_bitbang); + uint8_t gpio_data = 0; + if(furi_hal_gpio_read(&gpio_b0)) { + gpio_data |= 0b00000001; + } + if(furi_hal_gpio_read(&gpio_b1)) { + gpio_data |= 0b00000010; + } + if(furi_hal_gpio_read(&gpio_b2)) { + gpio_data |= 0b00000100; + } + if(furi_hal_gpio_read(&gpio_b3)) { + gpio_data |= 0b00001000; + } + if(furi_hal_gpio_read(&gpio_b4)) { + gpio_data |= 0b00010000; + } + if(furi_hal_gpio_read(&gpio_b5)) { + gpio_data |= 0b00100000; + } + if(furi_hal_gpio_read(&gpio_b6)) { + gpio_data |= 0b01000000; + } + if(furi_hal_gpio_read(&gpio_b7)) { + gpio_data |= 0b10000000; + } + + return gpio_data; +} + +#pragma GCC pop_options + +FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { + FtdiBitbang* ftdi_bitbang = malloc(sizeof(FtdiBitbang)); + ftdi_bitbang->ftdi = ftdi; + ftdi_bitbang->enable = false; + ftdi_bitbang->gpio_mask = 0; + ftdi_bitbang_gpio_init(ftdi_bitbang); + ftdi_bitbang->worker_thread = + furi_thread_alloc_ex(TAG, 1024, ftdi_bitbang_worker, ftdi_bitbang); + + furi_thread_start(ftdi_bitbang->worker_thread); + return ftdi_bitbang; +} + +void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { + if(!ftdi_bitbang) return; + ftdi_bitbang->enable = false; + furi_thread_flags_set(ftdi_bitbang->worker_thread, WorkerEventStop); + furi_thread_join(ftdi_bitbang->worker_thread); + furi_thread_free(ftdi_bitbang->worker_thread); + + free(ftdi_bitbang); + ftdi_bitbang = NULL; +} + +void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { + ftdi_bitbang->gpio_mask = gpio_mask; + ftdi_bitbang_gpio_init(ftdi_bitbang); +} + +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async) { + ftdi_bitbang->enable = enable; + ftdi_bitbang->async = async; + + if(enable) { + LL_TIM_SetCounter(TIM17, 0); + LL_TIM_EnableCounter(TIM17); + } else { + LL_TIM_DisableCounter(TIM17); + } +} + +void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed) { + UNUSED(ftdi_bitbang); + UNUSED(speed); + + uint32_t freq_div = 64000000LU / speed; + uint32_t prescaler = freq_div / 0x10000LU; + uint32_t period = freq_div / (prescaler + 1); + LL_TIM_SetPrescaler(TIM17, prescaler); + LL_TIM_SetAutoReload(TIM17, period - 1); +} + +static void ftdi_bitbang_tim_isr(void* context) { + FtdiBitbang* ftdi_bitbang = context; + UNUSED(ftdi_bitbang); + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + LL_TIM_ClearFlag_UPDATE(TIM17); + furi_thread_flags_set( + furi_thread_get_id(ftdi_bitbang->worker_thread), WorkerEventTimerUpdate); + } +} + +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang) { + furi_hal_bus_enable(FuriHalBusTIM17); + + LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM17, 6400-1); + LL_TIM_SetPrescaler(TIM17, 0); //10kHz + LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM17); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1TrgComTim17, ftdi_bitbang_tim_isr, ftdi_bitbang); + + LL_TIM_EnableIT_UPDATE(TIM17); +} + +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang) { + UNUSED(ftdi_bitbang); + LL_TIM_DisableCounter(TIM17); + furi_hal_bus_disable(FuriHalBusTIM17); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h new file mode 100644 index 0000000..a9e364e --- /dev/null +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -0,0 +1,12 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiBitbang FtdiBitbang; + + +FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi); +void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); +void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async); +void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); +uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c index 104a108..bd85bb7 100644 --- a/flip_tdi/helpers/ftdi_uart.c +++ b/flip_tdi/helpers/ftdi_uart.c @@ -17,6 +17,7 @@ struct FtdiUart { uint32_t baudrate; Ftdi* ftdi; uint8_t* buffer_tx_ptr; + bool enable; }; typedef enum { @@ -57,11 +58,10 @@ static void ftdi_uart_irq_cb( ftdi_set_tx_buf(ftdi_uart->ftdi, data, ret); size -= ret; }; - //furi_thread_flags_set(furi_thread_get_id(usb_uart->worker_thread), WorkerEventRxData); } } -static int32_t uart_echo_worker(void* context) { +static int32_t ftdi_uart_echo_worker(void* context) { furi_assert(context); FtdiUart* ftdi_uart = context; @@ -79,9 +79,6 @@ static int32_t uart_echo_worker(void* context) { if(events & WorkerEventTXData) { if(!is_dma_tx) { - // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); - // status.TEMT = 0; - // ftdi_set_modem_status(ftdi_uart->ftdi, status); is_dma_tx = true; events |= WorkerEventTXDataDmaEnd; } @@ -89,42 +86,19 @@ static int32_t uart_echo_worker(void* context) { if(events & WorkerEventTXDataDmaEnd) { size_t length = 0; - - // if(ftdi_available_rx_buf(ftdi_uart->ftdi) > 2048) { - // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); - // status.THRE = 0; - // ftdi_set_modem_status(ftdi_uart->ftdi, status); - // } - length = ftdi_get_rx_buf( ftdi_uart->ftdi, ftdi_uart->buffer_tx_ptr, FTDI_UART_MAX_TXRX_SIZE); if(length > 0) { - //furi_hal_serial_tx(ftdi_uart->serial_handle, data, length); ftdi_uart_tx_dma(ftdi_uart, ftdi_uart->buffer_tx_ptr, length); } else { is_dma_tx = false; - // FtdiModemStatus status = ftdi_get_modem_status(ftdi_uart->ftdi); - // status.TEMT = 1; - // ftdi_set_modem_status(ftdi_uart->ftdi, status); } } - // if(events & WorkerEventRxData) { - // size_t length = 0; - // do { - // uint8_t data[FTDI_UART_MAX_TXRX_SIZE]; - // //length = ftdi_get_rx_buf(ftdi_uart->ftdi, data, FTDI_UART_MAX_TXRX_SIZE); - - // if(length > 0) { - // ftdi_set_tx_buf(ftdi_uart->ftdi, data, length); - // } - // } while(length > 0); + // if(events & WorkerEventRxIdle) { + // //furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); // } - if(events & WorkerEventRxIdle) { - //furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); - } - // if(events & // (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { // if(events & WorkerEventRxOverrunError) { @@ -156,13 +130,16 @@ FtdiUart* ftdi_uart_alloc(Ftdi* ftdi) { ftdi_uart->ftdi = ftdi; //do not change LPUART, functions that directly work with peripherals are used - ftdi_uart->worker_thread = furi_thread_alloc_ex(TAG, 1024, uart_echo_worker, ftdi_uart); + ftdi_uart->worker_thread = furi_thread_alloc_ex(TAG, 1024, ftdi_uart_echo_worker, ftdi_uart); + ftdi_uart->enable = true; + furi_thread_start(ftdi_uart->worker_thread); return ftdi_uart; } void ftdi_uart_free(FtdiUart* ftdi_uart) { + ftdi_uart->enable = false; furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventStop); furi_thread_join(ftdi_uart->worker_thread); furi_thread_free(ftdi_uart->worker_thread); @@ -174,7 +151,9 @@ void ftdi_uart_free(FtdiUart* ftdi_uart) { } void ftdi_uart_tx(FtdiUart* ftdi_uart) { - furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXData); + if(ftdi_uart->enable) { + furi_thread_flags_set(furi_thread_get_id(ftdi_uart->worker_thread), WorkerEventTXData); + } } void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate) { @@ -182,6 +161,22 @@ void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate) { furi_hal_serial_set_br(ftdi_uart->serial_handle, baudrate); } +void ftdi_uart_enable(FtdiUart* ftdi_uart, bool enable) { + if(enable) { + ftdi_uart->enable = true; + furi_hal_serial_resume(ftdi_uart->serial_handle); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_enable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + ftdi_reset_purge_rx(ftdi_uart->ftdi); + ftdi_reset_purge_tx(ftdi_uart->ftdi); + } else { + ftdi_uart->enable = false; + furi_hal_serial_suspend(ftdi_uart->serial_handle); + furi_hal_serial_disable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionRx); + furi_hal_serial_disable_direction(ftdi_uart->serial_handle, FuriHalSerialDirectionTx); + } +} + void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config) { furi_assert(data_config); UNUSED(ftdi_uart); diff --git a/flip_tdi/helpers/ftdi_uart.h b/flip_tdi/helpers/ftdi_uart.h index ba25605..5a49b7a 100644 --- a/flip_tdi/helpers/ftdi_uart.h +++ b/flip_tdi/helpers/ftdi_uart.h @@ -7,4 +7,5 @@ FtdiUart* ftdi_uart_alloc(Ftdi* ftdi); void ftdi_uart_free(FtdiUart* ftdi_uart); void ftdi_uart_tx(FtdiUart* ftdi_uart); void ftdi_uart_set_baudrate(FtdiUart* ftdi_uart, uint32_t baudrate); -void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config); \ No newline at end of file +void ftdi_uart_set_data_config(FtdiUart* ftdi_uart, FtdiDataConfig* data_config); +void ftdi_uart_enable(FtdiUart* ftdi_uart, bool enable); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index 0818cfe..28cb983 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -71,7 +71,7 @@ static int32_t ftdi_thread_worker(void* context) { FtdiTxData tx_data = {0}; // uint16_t *status = 0; // ftdi_get_modem_status(&status); - uint16_t *status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); + uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); tx_data.status = status[0]; ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); @@ -79,7 +79,7 @@ static int32_t ftdi_thread_worker(void* context) { while(true) { uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); - furi_hal_gpio_write(&gpio_ext_pa7, 1); + //furi_hal_gpio_write(&gpio_ext_pa7, 1); if(flags & EventRx) { //fast flag uint8_t buf[FTDI_USB_RX_MAX_SIZE]; @@ -135,7 +135,7 @@ static int32_t ftdi_thread_worker(void* context) { } } - furi_hal_gpio_write(&gpio_ext_pa7, 0); + // furi_hal_gpio_write(&gpio_ext_pa7, 0); } return 0; @@ -163,8 +163,8 @@ static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx ftdi_usb->ftdi = ftdi_alloc(); - furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); + // furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); + // furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); furi_thread_start(ftdi_usb->thread); } @@ -187,8 +187,8 @@ static void ftdi_usb_deinit(usbd_device* dev) { ftdi_free(ftdi_usb->ftdi); - furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + //furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); free(ftdi_usb->usb.str_prod_descr); ftdi_usb->usb.str_prod_descr = NULL; @@ -229,7 +229,7 @@ static void ftdi_usb_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) UNUSED(event); UNUSED(ep); FtdiUsb* ftdi_usb = ftdi_cur; - furi_hal_gpio_write(&gpio_ext_pa6, 1); + //furi_hal_gpio_write(&gpio_ext_pa6, 1); furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRx); // uint8_t buf[FTDI_USB_RX_MAX_SIZE]; @@ -238,7 +238,7 @@ static void ftdi_usb_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) // ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); // // ftdi_loopback(ftdi_usb->ftdi); // } - furi_hal_gpio_write(&gpio_ext_pa6, 0); + //furi_hal_gpio_write(&gpio_ext_pa6, 0); } static void ftdi_usb_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { @@ -394,6 +394,24 @@ static usbd_respond // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetLatencyTimer); return usbd_ack; break; + + case FtdiRequestsSiOReqPollModemStatus: + furi_log_puts("FtdiRequestsSiOReqPollModemStatus\r\n"); + uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); + memcpy(ftdi_usb->data_recvest, status, sizeof(uint16_t)); + ftdi_usb->data_recvest_len = req->wLength; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + return usbd_ack; + break; + case FtdiRequestsSiOReqReadPins: + furi_log_puts("FtdiRequestsSiOReqReadPins\r\n"); + ftdi_usb->data_recvest[0] = ftdi_get_bitbang_gpio(ftdi_usb->ftdi); + ftdi_usb->data_recvest_len = 1; + ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; + ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; + return usbd_ack; + break; default: break; } diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h index 91cb58e..2e82c6a 100644 --- a/flip_tdi/helpers/ftdi_usb_define.h +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -221,7 +221,7 @@ typedef struct { uint8_t RESERVED2 : 1; /*Reserved2*/ uint8_t RESERVED3 : 1; /*Reserved3*/ uint8_t CTS : 1; /*Clear to send (CTS)*/ - uint8_t DTS : 1; /*Data set ready (DTS)*/ + uint8_t DTR : 1; /*Data set ready (DTR)*/ uint8_t RI : 1; /*Ring indicator (RI)*/ uint8_t RLSD : 1; /*Receive line signal detect (RLSD)*/ uint8_t DR : 1; /*Data ready (DR)*/ diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c index 96d49ed..075e92f 100644 --- a/flip_tdi/scenes/flip_tdi_scene_about.c +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -26,6 +26,8 @@ void flip_tdi_scene_about_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); furi_string_cat_printf(temp_str, "- Emulate FT232H VCP mode\n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitband mode, max freq 100kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitband mode, max freq 100kHz \n"); widget_add_text_box_element( app->widget, From 8d4c6c6eb6a0d175e9f1c6db1e9ec666d25a82ac Mon Sep 17 00:00:00 2001 From: SkorP Date: Mon, 15 Jul 2024 17:29:25 +0400 Subject: [PATCH 05/19] FlipTDI: fix syntax --- flip_tdi/scenes/flip_tdi_scene_about.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c index 075e92f..1b3387d 100644 --- a/flip_tdi/scenes/flip_tdi_scene_about.c +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -26,8 +26,8 @@ void flip_tdi_scene_about_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); furi_string_cat_printf(temp_str, "- Emulate FT232H VCP mode\n"); - furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitband mode, max freq 100kHz \n"); - furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitband mode, max freq 100kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitbang mode, max freq 100kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitbang mode, max freq 100kHz \n"); widget_add_text_box_element( app->widget, From 1277e894632f740550d1a2969e2bdb747e80f500 Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 16 Jul 2024 12:47:05 +0400 Subject: [PATCH 06/19] FlipTDI: add latency timer --- flip_tdi/helpers/ftdi.c | 32 ++++++-- flip_tdi/helpers/ftdi.h | 4 + flip_tdi/helpers/ftdi_latency_timer.c | 110 ++++++++++++++++++++++++++ flip_tdi/helpers/ftdi_latency_timer.h | 16 ++++ flip_tdi/helpers/ftdi_usb.c | 31 +++++++- flip_tdi/helpers/ftdi_usb_define.h | 7 +- 6 files changed, 188 insertions(+), 12 deletions(-) create mode 100644 flip_tdi/helpers/ftdi_latency_timer.c create mode 100644 flip_tdi/helpers/ftdi_latency_timer.h diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index cb414f3..c526cfb 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -2,6 +2,7 @@ #include "furi.h" #include "ftdi_uart.h" #include "ftdi_bitbang.h" +#include "ftdi_latency_timer.h" #define TAG "FTDI" @@ -19,10 +20,13 @@ struct Ftdi { FtdiDataConfig data_config; FtdiBitMode bit_mode; uint8_t bit_mode_mask; - uint8_t latency_timer; FtdiUart* ftdi_uart; FtdiBitbang* ftdi_bitbang; + FtdiLatencyTimer* ftdi_latency_timer; + + FtdiCallbackLatencyTimer callback_latency_timer; + void* context_latency_timer; }; static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { @@ -31,6 +35,13 @@ static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { return ((interface) == FTDI_INTERFACE_A) || ((interface) == FTDI_DRIVER_INTERFACE_A); } +static void ftdi_callback_latency_timer(void* context) { + Ftdi* ftdi = context; + if(ftdi->callback_latency_timer) { + ftdi->callback_latency_timer(ftdi->context_latency_timer); + } +} + Ftdi* ftdi_alloc(void) { Ftdi* ftdi = malloc(sizeof(Ftdi)); ftdi->stream_rx = @@ -47,18 +58,25 @@ Ftdi* ftdi_alloc(void) { ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); ftdi->ftdi_bitbang = ftdi_bitbang_alloc(ftdi); - + ftdi->ftdi_latency_timer = ftdi_latency_timer_alloc(); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_latency_timer, ftdi); return ftdi; } void ftdi_free(Ftdi* ftdi) { ftdi_uart_free(ftdi->ftdi_uart); ftdi_bitbang_free(ftdi->ftdi_bitbang); + ftdi_latency_timer_free(ftdi->ftdi_latency_timer); furi_stream_buffer_free(ftdi->stream_tx); furi_stream_buffer_free(ftdi->stream_rx); free(ftdi); } +void ftdi_set_callback_latency_timer(Ftdi* ftdi, FtdiCallbackLatencyTimer callback, void* context) { + ftdi->callback_latency_timer = callback; + ftdi->context_latency_timer = context; +} + void ftdi_reset_purge_rx(Ftdi* ftdi) { furi_stream_buffer_reset(ftdi->stream_rx); } @@ -302,7 +320,7 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { uint8_t bit_mode = value >> 8; ftdi->bit_mode = *((FtdiBitMode*)&(bit_mode)); - // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); + // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); if(bit_mode == 0x00) { // Reset ftdi_uart_enable(ftdi->ftdi_uart, true); // UART mode @@ -331,11 +349,15 @@ void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index) { if(!ftdi_check_interface(ftdi, index)) { return; } - ftdi->latency_timer = value; + ftdi_latency_timer_set_speed(ftdi->ftdi_latency_timer, value); } uint8_t ftdi_get_latency_timer(Ftdi* ftdi) { - return ftdi->latency_timer; + return ftdi_latency_timer_get_speed(ftdi->ftdi_latency_timer); +} + +void ftdi_reset_latency_timer(Ftdi* ftdi) { + ftdi_latency_timer_reset(ftdi->ftdi_latency_timer); } // FTDI modem status diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index b6d55bf..487c5ef 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -3,8 +3,11 @@ typedef struct Ftdi Ftdi; +typedef void (*FtdiCallbackLatencyTimer)(void* context); + Ftdi* ftdi_alloc(void); void ftdi_free(Ftdi* ftdi); +void ftdi_set_callback_latency_timer(Ftdi* ftdi, FtdiCallbackLatencyTimer callback, void* context); void ftdi_reset_purge_rx(Ftdi* ftdi); void ftdi_reset_purge_tx(Ftdi* ftdi); void ftdi_reset_sio(Ftdi* ftdi); @@ -21,6 +24,7 @@ void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index); void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index); void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index); uint8_t ftdi_get_latency_timer(Ftdi* ftdi); +void ftdi_reset_latency_timer(Ftdi* ftdi); //void ftdi_get_modem_status(uint16_t *status); diff --git a/flip_tdi/helpers/ftdi_latency_timer.c b/flip_tdi/helpers/ftdi_latency_timer.c new file mode 100644 index 0000000..a47e0c5 --- /dev/null +++ b/flip_tdi/helpers/ftdi_latency_timer.c @@ -0,0 +1,110 @@ +#include "ftdi_latency_timer.h" +#include "furi.h" +#include +#include + +#define TAG "FTDI_LATENCY_TIMER" + +#define FTDI_LATENCY_TIMER_SPEED_DEFALUT 16 // ms defalut + +struct FtdiLatencyTimer { + uint8_t speed; + bool enable; + FtdiLatencyTimerCallbackUp callback; + void* context; +}; + +static void ftdi_latency_timer_tim_isr(void* context); +static void ftdi_latency_timer_tim_init(FtdiLatencyTimer* ftdi_latency_timer); +static void ftdi_latency_timer_tim_deinit(FtdiLatencyTimer* ftdi_latency_timer); + +FtdiLatencyTimer* ftdi_latency_timer_alloc(void) { + FtdiLatencyTimer* ftdi_latency_timer = malloc(sizeof(FtdiLatencyTimer)); + ftdi_latency_timer->speed = FTDI_LATENCY_TIMER_SPEED_DEFALUT; + ftdi_latency_timer->enable = false; + + ftdi_latency_timer_tim_init(ftdi_latency_timer); + return ftdi_latency_timer; +} + +void ftdi_latency_timer_free(FtdiLatencyTimer* ftdi_latency_timer) { + if(!ftdi_latency_timer) return; + ftdi_latency_timer->enable = false; + ftdi_latency_timer_tim_deinit(ftdi_latency_timer); + free(ftdi_latency_timer); + ftdi_latency_timer = NULL; +} + +void ftdi_latency_timer_set_callback( + FtdiLatencyTimer* ftdi_latency_timer, + FtdiLatencyTimerCallbackUp callback, + void* context) { + ftdi_latency_timer->callback = callback; + ftdi_latency_timer->context = context; +} + +void ftdi_latency_timer_enable(FtdiLatencyTimer* ftdi_latency_timer, bool enable) { + ftdi_latency_timer->enable = enable; + + if(enable) { + LL_TIM_SetCounter(TIM1, 0); + LL_TIM_EnableCounter(TIM1); + } else { + LL_TIM_DisableCounter(TIM1); + } +} + +void ftdi_latency_timer_set_speed(FtdiLatencyTimer* ftdi_latency_timer, uint8_t speed) { + UNUSED(ftdi_latency_timer); + if(speed == 0) { + speed = FTDI_LATENCY_TIMER_SPEED_DEFALUT; + ftdi_latency_timer_enable(ftdi_latency_timer, false); + } else { + ftdi_latency_timer_enable(ftdi_latency_timer, true); + } + + LL_TIM_SetPrescaler(TIM1, speed - 1); +} + +uint8_t ftdi_latency_timer_get_speed(FtdiLatencyTimer* ftdi_latency_timer) { + return ftdi_latency_timer->speed; +} + +void ftdi_latency_timer_reset(FtdiLatencyTimer* ftdi_latency_timer) { + UNUSED(ftdi_latency_timer); + LL_TIM_SetCounter(TIM1, 0); +} + +static void ftdi_latency_timer_tim_isr(void* context) { + FtdiLatencyTimer* ftdi_latency_timer = context; + UNUSED(ftdi_latency_timer); + if(LL_TIM_IsActiveFlag_UPDATE(TIM1)) { + LL_TIM_ClearFlag_UPDATE(TIM1); + if(ftdi_latency_timer->callback) { + ftdi_latency_timer->callback(ftdi_latency_timer->context); + } + } +} + +static void ftdi_latency_timer_tim_init(FtdiLatencyTimer* ftdi_latency_timer) { + furi_hal_bus_enable(FuriHalBusTIM1); + + LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP); + LL_TIM_SetClockDivision(TIM1, LL_TIM_CLOCKDIVISION_DIV1); + LL_TIM_SetAutoReload(TIM1, 64000 - 1); + LL_TIM_SetPrescaler(TIM1, FTDI_LATENCY_TIMER_SPEED_DEFALUT - 1); + LL_TIM_SetClockSource(TIM1, LL_TIM_CLOCKSOURCE_INTERNAL); + LL_TIM_DisableARRPreload(TIM1); + + furi_hal_interrupt_set_isr( + FuriHalInterruptIdTim1UpTim16, ftdi_latency_timer_tim_isr, ftdi_latency_timer); + + LL_TIM_EnableIT_UPDATE(TIM1); +} + +static void ftdi_latency_timer_tim_deinit(FtdiLatencyTimer* ftdi_latency_timer) { + UNUSED(ftdi_latency_timer); + LL_TIM_DisableCounter(TIM1); + furi_hal_bus_disable(FuriHalBusTIM1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1UpTim16, NULL, NULL); +} \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_latency_timer.h b/flip_tdi/helpers/ftdi_latency_timer.h new file mode 100644 index 0000000..a179e5e --- /dev/null +++ b/flip_tdi/helpers/ftdi_latency_timer.h @@ -0,0 +1,16 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiLatencyTimer FtdiLatencyTimer; + +typedef void (*FtdiLatencyTimerCallbackUp)(void* context); + +FtdiLatencyTimer* ftdi_latency_timer_alloc(void); +void ftdi_latency_timer_free(FtdiLatencyTimer* ftdi_latency_timer); +void ftdi_latency_timer_set_callback( + FtdiLatencyTimer* ftdi_latency_timer, + FtdiLatencyTimerCallbackUp callback, + void* context); +void ftdi_latency_timer_reset(FtdiLatencyTimer* ftdi_latency_timer); +void ftdi_latency_timer_set_speed(FtdiLatencyTimer* ftdi_latency_timer, uint8_t speed); +uint8_t ftdi_latency_timer_get_speed(FtdiLatencyTimer* ftdi_latency_timer); diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index 28cb983..3d34e0c 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -44,11 +44,12 @@ typedef enum { EventSetBaudrate = (1 << 12), EventSetData = (1 << 13), EventSetFlowCtrl = (1 << 14), + EventLatencyTimerUp = (1 << 15), EventAll = EventExit | EventReset | EventRx | EventTx | EventTxComplete | EventResetSio | EventResetPurgeRx | EventResetPurgeTx | EventSetBitmode | EventSetLatencyTimer | EventSetEventChar | EventSetErrorChar | EventSetBaudrate | EventSetData | - EventSetFlowCtrl, + EventSetFlowCtrl | EventLatencyTimerUp, } FtdiEvent; struct FtdiUsb { @@ -60,6 +61,9 @@ struct FtdiUsb { Ftdi* ftdi; uint8_t data_recvest[8]; uint16_t data_recvest_len; + + bool tx_complete; + bool latency_timer_up; }; static int32_t ftdi_thread_worker(void* context) { @@ -105,9 +109,25 @@ static int32_t ftdi_thread_worker(void* context) { ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); } if(flags & EventTxComplete) { - flags |= EventTx; + ftdi_usb->tx_complete = true; + if((ftdi_usb->latency_timer_up) || + (ftdi_available_tx_buf(ftdi_usb->ftdi) >= FTDI_USB_TX_MAX_SIZE)) { + ftdi_reset_latency_timer(ftdi_usb->ftdi); + flags |= EventTx; + } + } + + if(flags & EventLatencyTimerUp) { + ftdi_usb->latency_timer_up = true; + if(ftdi_usb->tx_complete) { + flags |= EventTx; + } } + if(flags & EventTx) { + ftdi_usb->tx_complete = false; + ftdi_usb->latency_timer_up = false; + tx_data.status = status[0]; len_data = ftdi_available_tx_buf(ftdi_usb->ftdi); @@ -145,6 +165,11 @@ static int32_t ftdi_thread_worker(void* context) { // where if_ctx isn't passed static FtdiUsb* ftdi_cur = NULL; +static void ftdi_usb_callback_latency_timer(void* context) { + FtdiUsb* ftdi_usb = context; + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventLatencyTimerUp); +} + static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { UNUSED(intf); FtdiUsb* ftdi_usb = ctx; @@ -162,7 +187,7 @@ static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx furi_thread_set_callback(ftdi_usb->thread, ftdi_thread_worker); ftdi_usb->ftdi = ftdi_alloc(); - + ftdi_set_callback_latency_timer(ftdi_usb->ftdi, ftdi_usb_callback_latency_timer, ftdi_usb); // furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); // furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h index 2e82c6a..28385d0 100644 --- a/flip_tdi/helpers/ftdi_usb_define.h +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -80,7 +80,6 @@ typedef enum { // FtdiBitModeFt1284 = 0x80, /**< FT1284 mode, available on 232H chips */ // } FtdiBitMode; - //if(FtdiBitMode&0xff00==0) FtdiBitModeReset switch off bitbang mode, back to regular serial/FIFO typedef struct { //uint8_t MASK : 8; /*Mask*/ @@ -92,7 +91,7 @@ typedef struct { uint8_t CBUS : 1; /*Bitbang on CBUS pins of R-type chips, configure in EEPROM before*/ uint8_t SYNCFF : 1; /*Single Channel Synchronous FIFO mode, available on 2232H chips*/ uint8_t FT1284 : 1; /*FT1284 mode, available on 232H chips*/ -} FtdiBitMode; +} FtdiBitMode; /* FTDI MPSSE commands */ typedef enum { @@ -212,8 +211,8 @@ typedef struct { uint8_t RESERVED : 4; /*Reserved0*/ uint8_t PARITY : 3; /*Parity*/ uint8_t STOP_BITS : 2; /*Number of stop bits*/ - uint8_t BREAK: 1; /*Break type*/ -} FtdiDataConfig; + uint8_t BREAK : 1; /*Break type*/ +} FtdiDataConfig; typedef struct { uint8_t RESERVED0 : 1; /*Reserved0*/ From 82dede0b432efc24dc3865f8424538db4019d8e6 Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 16 Jul 2024 13:14:37 +0400 Subject: [PATCH 07/19] FlipTDI: fix struct copy --- flip_tdi/helpers/ftdi.c | 4 ++-- flip_tdi/helpers/ftdi_usb_define.h | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index c526cfb..a443bbe 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -220,7 +220,7 @@ void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { if(!ftdi_check_interface(ftdi, index)) { return; } - ftdi->data_config = *((FtdiDataConfig*)&value); + memcpy(&ftdi->data_config, &value, sizeof(ftdi->data_config)); ftdi_uart_set_data_config(ftdi->ftdi_uart, &ftdi->data_config); } @@ -318,7 +318,7 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { //todo no use mask value&0xFF ftdi->bit_mode_mask = value & 0xFF; uint8_t bit_mode = value >> 8; - ftdi->bit_mode = *((FtdiBitMode*)&(bit_mode)); + memcpy(&ftdi->bit_mode, &bit_mode, sizeof(ftdi->bit_mode)); // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h index 28385d0..4c93d15 100644 --- a/flip_tdi/helpers/ftdi_usb_define.h +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include // # Clocks and baudrates #define FTDIBUS_CLOCK_BASE 6000000UL @@ -92,7 +93,7 @@ typedef struct { uint8_t SYNCFF : 1; /*Single Channel Synchronous FIFO mode, available on 2232H chips*/ uint8_t FT1284 : 1; /*FT1284 mode, available on 232H chips*/ } FtdiBitMode; - +static_assert(sizeof(FtdiBitMode) == sizeof(uint8_t), "Wrong FtdiBitMode"); /* FTDI MPSSE commands */ typedef enum { @@ -213,6 +214,7 @@ typedef struct { uint8_t STOP_BITS : 2; /*Number of stop bits*/ uint8_t BREAK : 1; /*Break type*/ } FtdiDataConfig; +static_assert(sizeof(FtdiDataConfig) == sizeof(uint16_t), "Wrong FtdiDataConfig"); typedef struct { uint8_t RESERVED0 : 1; /*Reserved0*/ @@ -232,3 +234,4 @@ typedef struct { uint8_t TEMT : 1; /*Transmitter empty (TEMT)*/ uint8_t RCVR_FIFO : 1; /*Error in RCVR FIFO*/ } FtdiModemStatus; +static_assert(sizeof(FtdiModemStatus) == sizeof(uint16_t), "Wrong FtdiModemStatus"); From 2fa0a5d23d446b4502fb90262a705762d7eca07a Mon Sep 17 00:00:00 2001 From: SkorP Date: Tue, 16 Jul 2024 13:36:57 +0400 Subject: [PATCH 08/19] FlipTDI: fix app version --- flip_tdi/.catalog/changelog.md | 2 +- flip_tdi/application.fam | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flip_tdi/.catalog/changelog.md b/flip_tdi/.catalog/changelog.md index 89c049f..b6c1b56 100644 --- a/flip_tdi/.catalog/changelog.md +++ b/flip_tdi/.catalog/changelog.md @@ -1,2 +1,2 @@ -## 0.1 +## 1.0 - Initial release diff --git a/flip_tdi/application.fam b/flip_tdi/application.fam index 36d9e57..6ef58ab 100644 --- a/flip_tdi/application.fam +++ b/flip_tdi/application.fam @@ -9,7 +9,7 @@ App( ], stack_size=2 * 1024, fap_description="Flipper FTDI232H emulator.", - fap_version="0.1", + fap_version="1.0", fap_icon="flip_tdi_icon_10px.png", fap_category="USB", fap_icon_assets="images", From af61f326b51b22ccb2bf6c4d3f477d972260ee60 Mon Sep 17 00:00:00 2001 From: SkorP Date: Wed, 17 Jul 2024 18:16:13 +0400 Subject: [PATCH 09/19] FlipTDI: refactoring --- flip_tdi/helpers/ftdi.c | 2 +- flip_tdi/helpers/ftdi_bitbang.c | 538 ++++++++++++++++++----------- flip_tdi/helpers/ftdi_bitbang.h | 3 +- flip_tdi/helpers/ftdi_gpio.c | 131 +++++++ flip_tdi/helpers/ftdi_gpio.h | 126 +++++++ flip_tdi/helpers/ftdi_mpsse.c | 336 ++++++++++++++++++ flip_tdi/helpers/ftdi_mpsse.h | 6 + flip_tdi/helpers/ftdi_usb_define.h | 52 +++ 8 files changed, 989 insertions(+), 205 deletions(-) create mode 100644 flip_tdi/helpers/ftdi_gpio.c create mode 100644 flip_tdi/helpers/ftdi_gpio.h create mode 100644 flip_tdi/helpers/ftdi_mpsse.c create mode 100644 flip_tdi/helpers/ftdi_mpsse.h diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index a443bbe..2467e17 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -230,7 +230,7 @@ void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { } uint16_t flow_ctrl = index & 0xFF00; if(flow_ctrl != FtdiFlowControlDisable) { - //ToDo: implement FtdiFlowControl + //ToDo: no implement FtdiFlowControl } } diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c index a501b44..3f67618 100644 --- a/flip_tdi/helpers/ftdi_bitbang.c +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -3,28 +3,33 @@ #include #include #include +#include "ftdi_gpio.h" #define TAG "FTDI_BITBANG" -#define gpio_b0 (gpio_ext_pa7) -#define gpio_b1 (gpio_ext_pa6) -#define gpio_b2 (gpio_ext_pa4) -#define gpio_b3 (gpio_ext_pb3) -#define gpio_b4 (gpio_ext_pb2) -#define gpio_b5 (gpio_ext_pc3) -#define gpio_b6 (gpio_ext_pc1) -#define gpio_b7 (gpio_ext_pc0) - -struct FtdiBitbang { - FuriThread* worker_thread; - Ftdi* ftdi; +// #define gpio_b0 (gpio_ext_pa7) +// #define gpio_b1 (gpio_ext_pa6) +// #define gpio_b2 (gpio_ext_pa4) +// #define gpio_b3 (gpio_ext_pb3) +// #define gpio_b4 (gpio_ext_pb2) +// #define gpio_b5 (gpio_ext_pc3) +// #define gpio_b6 (gpio_ext_pc1) +// #define gpio_b7 (gpio_ext_pc0) +typedef uint8_t (*FtdiBitbangGpioIO)(uint8_t state); +struct FtdiBitbang +{ + FuriThread *worker_thread; + Ftdi *ftdi; uint32_t speed; bool enable; uint8_t gpio_mask; bool async; + FtdiBitbangGpioIO gpio_io[8]; + uint8_t gpio_data; }; -typedef enum { +typedef enum +{ WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event WorkerEventStop = (1 << 1), WorkerEventTimerUpdate = (1 << 2), @@ -32,208 +37,320 @@ typedef enum { #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventTimerUpdate) -static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang); -static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang); -static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); -static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang); -static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang); - -static int32_t ftdi_bitbang_worker(void* context) { - furi_assert(context); - FtdiBitbang* ftdi_bitbang = context; - - uint8_t buffer[64]; - - FURI_LOG_I(TAG, "Worker started"); - ftdi_bitbang_tim_init(ftdi_bitbang); - while(1) { - uint32_t events = - furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); - furi_check((events & FuriFlagError) == 0); - - if(events & WorkerEventStop) break; - if(events & WorkerEventTimerUpdate) { - size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); - if(length > 0) { - ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); - if(!ftdi_bitbang->async) { - buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); - ftdi_set_tx_buf(ftdi_bitbang->ftdi, buffer, 1); - } - } - if(ftdi_bitbang->enable && ftdi_bitbang->async) { - buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); - ftdi_set_tx_buf(ftdi_bitbang->ftdi, buffer, 1); - } - } +// static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang); +// static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang); +// static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); +static void ftdi_bitbang_tim_init(FtdiBitbang *ftdi_bitbang); +static void ftdi_bitbang_tim_deinit(FtdiBitbang *ftdi_bitbang); + +void ftdi_bitbang_gpio_set_direction(FtdiBitbang *ftdi_bitbang) +{ + ftdi_gpio_set_direction(ftdi_bitbang->gpio_mask); + if (ftdi_bitbang->gpio_mask & 0b00000001) + { + ftdi_bitbang->gpio_io[0] = ftdi_gpio_set_b0; + } + else + { + ftdi_bitbang->gpio_io[0] = ftdi_gpio_get_b0; } - ftdi_bitbang_tim_deinit(ftdi_bitbang); - ftdi_bitbang_gpio_deinit(ftdi_bitbang); - FURI_LOG_I(TAG, "Worker stopped"); - return 0; -} - -#pragma GCC push_options -#pragma GCC optimize("Ofast,unroll-loops") -// #pragma GCC optimize("O3") -// #pragma GCC optimize("-funroll-all-loops") -static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { - if(ftdi_bitbang->gpio_mask & 0b00000001) { - furi_hal_gpio_init(&gpio_b0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + if (ftdi_bitbang->gpio_mask & 0b00000010) + { + ftdi_bitbang->gpio_io[1] = ftdi_gpio_set_b1; } - if(ftdi_bitbang->gpio_mask & 0b00000010) { - furi_hal_gpio_init(&gpio_b1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + else + { + ftdi_bitbang->gpio_io[1] = ftdi_gpio_get_b1; } - if(ftdi_bitbang->gpio_mask & 0b00000100) { - furi_hal_gpio_init(&gpio_b2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + if (ftdi_bitbang->gpio_mask & 0b00000100) + { + ftdi_bitbang->gpio_io[2] = ftdi_gpio_set_b2; } - if(ftdi_bitbang->gpio_mask & 0b00001000) { - furi_hal_gpio_init(&gpio_b3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + else + { + ftdi_bitbang->gpio_io[2] = ftdi_gpio_get_b2; } - if(ftdi_bitbang->gpio_mask & 0b00010000) { - furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + if (ftdi_bitbang->gpio_mask & 0b00001000) + { + ftdi_bitbang->gpio_io[3] = ftdi_gpio_set_b3; } - if(ftdi_bitbang->gpio_mask & 0b00100000) { - furi_hal_gpio_init(&gpio_b5, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + else + { + ftdi_bitbang->gpio_io[3] = ftdi_gpio_get_b3; } - if(ftdi_bitbang->gpio_mask & 0b01000000) { - furi_hal_gpio_init(&gpio_b6, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + if (ftdi_bitbang->gpio_mask & 0b00010000) + { + ftdi_bitbang->gpio_io[4] = ftdi_gpio_set_b4; } - if(ftdi_bitbang->gpio_mask & 0b10000000) { - furi_hal_gpio_init(&gpio_b7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); - } else { - furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + else + { + ftdi_bitbang->gpio_io[4] = ftdi_gpio_get_b4; } -} - -static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang) { - UNUSED(ftdi_bitbang); - furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -} -static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data) { - if(ftdi_bitbang->gpio_mask & 0b00000001) { - if(gpio_data & 0b00000001) { - furi_hal_gpio_write(&gpio_b0, 1); - } else { - furi_hal_gpio_write(&gpio_b0, 0); - } + if (ftdi_bitbang->gpio_mask & 0b00100000) + { + ftdi_bitbang->gpio_io[5] = ftdi_gpio_set_b5; } - - if(ftdi_bitbang->gpio_mask & 0b00000010) { - if(gpio_data & 0b00000010) { - furi_hal_gpio_write(&gpio_b1, 1); - } else { - furi_hal_gpio_write(&gpio_b1, 0); - } + else + { + ftdi_bitbang->gpio_io[5] = ftdi_gpio_get_b5; } - if(ftdi_bitbang->gpio_mask & 0b00000100) { - if(gpio_data & 0b00000100) { - furi_hal_gpio_write(&gpio_b2, 1); - } else { - furi_hal_gpio_write(&gpio_b2, 0); - } + if (ftdi_bitbang->gpio_mask & 0b01000000) + { + ftdi_bitbang->gpio_io[6] = ftdi_gpio_set_b6; } - - if(ftdi_bitbang->gpio_mask & 0b00001000) { - if(gpio_data & 0b00001000) { - furi_hal_gpio_write(&gpio_b3, 1); - } else { - furi_hal_gpio_write(&gpio_b3, 0); - } + else + { + ftdi_bitbang->gpio_io[6] = ftdi_gpio_get_b6; } - if(ftdi_bitbang->gpio_mask & 0b00010000) { - if(gpio_data & 0b00010000) { - furi_hal_gpio_write(&gpio_b4, 1); - } else { - furi_hal_gpio_write(&gpio_b4, 0); - } + if (ftdi_bitbang->gpio_mask & 0b10000000) + { + ftdi_bitbang->gpio_io[7] = ftdi_gpio_set_b7; } - - if(ftdi_bitbang->gpio_mask & 0b00100000) { - if(gpio_data & 0b00100000) { - furi_hal_gpio_write(&gpio_b5, 1); - } else { - furi_hal_gpio_write(&gpio_b5, 0); - } + else + { + ftdi_bitbang->gpio_io[7] = ftdi_gpio_get_b7; } +} - if(ftdi_bitbang->gpio_mask & 0b01000000) { - if(gpio_data & 0b01000000) { - furi_hal_gpio_write(&gpio_b6, 1); - } else { - furi_hal_gpio_write(&gpio_b6, 0); - } - } +void ftdi_bitbang_gpio_init(FtdiBitbang *ftdi_bitbang) +{ + ftdi_gpio_init(ftdi_bitbang->gpio_mask); + ftdi_bitbang_gpio_set_direction(ftdi_bitbang); +} - if(ftdi_bitbang->gpio_mask & 0b10000000) { - if(gpio_data & 0b10000000) { - furi_hal_gpio_write(&gpio_b7, 1); - } else { - furi_hal_gpio_write(&gpio_b7, 0); - } - } +uint8_t ftdi_bitbang_gpio_io(FtdiBitbang *ftdi_bitbang, uint8_t gpio_data_out) +{ + uint8_t gpio_data_in = 0; + gpio_data_in |= ftdi_bitbang->gpio_io[0](gpio_data_out & 0b00000001); + gpio_data_in |= ftdi_bitbang->gpio_io[1](gpio_data_out & 0b00000010); + gpio_data_in |= ftdi_bitbang->gpio_io[2](gpio_data_out & 0b00000100); + gpio_data_in |= ftdi_bitbang->gpio_io[3](gpio_data_out & 0b00001000); + gpio_data_in |= ftdi_bitbang->gpio_io[4](gpio_data_out & 0b00010000); + gpio_data_in |= ftdi_bitbang->gpio_io[5](gpio_data_out & 0b00100000); + gpio_data_in |= ftdi_bitbang->gpio_io[6](gpio_data_out & 0b01000000); + gpio_data_in |= ftdi_bitbang->gpio_io[7](gpio_data_out & 0b10000000); + return gpio_data_in; } -uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang) { - UNUSED(ftdi_bitbang); - uint8_t gpio_data = 0; - if(furi_hal_gpio_read(&gpio_b0)) { - gpio_data |= 0b00000001; - } - if(furi_hal_gpio_read(&gpio_b1)) { - gpio_data |= 0b00000010; - } - if(furi_hal_gpio_read(&gpio_b2)) { - gpio_data |= 0b00000100; - } - if(furi_hal_gpio_read(&gpio_b3)) { - gpio_data |= 0b00001000; - } - if(furi_hal_gpio_read(&gpio_b4)) { - gpio_data |= 0b00010000; - } - if(furi_hal_gpio_read(&gpio_b5)) { - gpio_data |= 0b00100000; - } - if(furi_hal_gpio_read(&gpio_b6)) { - gpio_data |= 0b01000000; - } - if(furi_hal_gpio_read(&gpio_b7)) { - gpio_data |= 0b10000000; - } +static int32_t ftdi_bitbang_worker(void *context) +{ + furi_assert(context); + FtdiBitbang *ftdi_bitbang = context; + + uint8_t buffer[64]; + + FURI_LOG_I(TAG, "Worker started"); + ftdi_bitbang_tim_init(ftdi_bitbang); + // uint8_t gpio_data[1] = {0}; + while (1) + { + uint32_t events = + furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); + furi_check((events & FuriFlagError) == 0); - return gpio_data; + if (events & WorkerEventStop) + break; + if (events & WorkerEventTimerUpdate) + { + size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + if (length > 0) + { + // ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_io(ftdi_bitbang, buffer[0]); + if (!ftdi_bitbang->async) + { + // buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + } + } + if (ftdi_bitbang->enable && ftdi_bitbang->async) + { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_io(ftdi_bitbang, ftdi_bitbang->gpio_data); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + } + } + } + ftdi_bitbang_tim_deinit(ftdi_bitbang); + // ftdi_bitbang_gpio_deinit(ftdi_bitbang); + // ftdi_gpio_deinit(); + FURI_LOG_I(TAG, "Worker stopped"); + return 0; } -#pragma GCC pop_options +// #pragma GCC push_options +// #pragma GCC optimize("Ofast,unroll-loops") +// // #pragma GCC optimize("O3") +// // #pragma GCC optimize("-funroll-all-loops") + +// static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { +// if(ftdi_bitbang->gpio_mask & 0b00000001) { +// furi_hal_gpio_init(&gpio_b0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b00000010) { +// furi_hal_gpio_init(&gpio_b1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b00000100) { +// furi_hal_gpio_init(&gpio_b2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b00001000) { +// furi_hal_gpio_init(&gpio_b3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b00010000) { +// furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b00100000) { +// furi_hal_gpio_init(&gpio_b5, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b01000000) { +// furi_hal_gpio_init(&gpio_b6, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// if(ftdi_bitbang->gpio_mask & 0b10000000) { +// furi_hal_gpio_init(&gpio_b7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); +// } else { +// furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); +// } +// } + +// static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang) { +// UNUSED(ftdi_bitbang); +// furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +// } + +// static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data) { +// if(ftdi_bitbang->gpio_mask & 0b00000001) { +// if(gpio_data & 0b00000001) { +// furi_hal_gpio_write(&gpio_b0, 1); +// } else { +// furi_hal_gpio_write(&gpio_b0, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b00000010) { +// if(gpio_data & 0b00000010) { +// furi_hal_gpio_write(&gpio_b1, 1); +// } else { +// furi_hal_gpio_write(&gpio_b1, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b00000100) { +// if(gpio_data & 0b00000100) { +// furi_hal_gpio_write(&gpio_b2, 1); +// } else { +// furi_hal_gpio_write(&gpio_b2, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b00001000) { +// if(gpio_data & 0b00001000) { +// furi_hal_gpio_write(&gpio_b3, 1); +// } else { +// furi_hal_gpio_write(&gpio_b3, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b00010000) { +// if(gpio_data & 0b00010000) { +// furi_hal_gpio_write(&gpio_b4, 1); +// } else { +// furi_hal_gpio_write(&gpio_b4, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b00100000) { +// if(gpio_data & 0b00100000) { +// furi_hal_gpio_write(&gpio_b5, 1); +// } else { +// furi_hal_gpio_write(&gpio_b5, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b01000000) { +// if(gpio_data & 0b01000000) { +// furi_hal_gpio_write(&gpio_b6, 1); +// } else { +// furi_hal_gpio_write(&gpio_b6, 0); +// } +// } + +// if(ftdi_bitbang->gpio_mask & 0b10000000) { +// if(gpio_data & 0b10000000) { +// furi_hal_gpio_write(&gpio_b7, 1); +// } else { +// furi_hal_gpio_write(&gpio_b7, 0); +// } +// } +// } + +// uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang) { +// UNUSED(ftdi_bitbang); +// uint8_t gpio_data = 0; +// if(furi_hal_gpio_read(&gpio_b0)) { +// gpio_data |= 0b00000001; +// } +// if(furi_hal_gpio_read(&gpio_b1)) { +// gpio_data |= 0b00000010; +// } +// if(furi_hal_gpio_read(&gpio_b2)) { +// gpio_data |= 0b00000100; +// } +// if(furi_hal_gpio_read(&gpio_b3)) { +// gpio_data |= 0b00001000; +// } +// if(furi_hal_gpio_read(&gpio_b4)) { +// gpio_data |= 0b00010000; +// } +// if(furi_hal_gpio_read(&gpio_b5)) { +// gpio_data |= 0b00100000; +// } +// if(furi_hal_gpio_read(&gpio_b6)) { +// gpio_data |= 0b01000000; +// } +// if(furi_hal_gpio_read(&gpio_b7)) { +// gpio_data |= 0b10000000; +// } + +// return gpio_data; +// } + +// #pragma GCC pop_options + +uint8_t ftdi_bitbang_gpio_get(FtdiBitbang *ftdi_bitbang) +{ + return ftdi_bitbang_gpio_io(ftdi_bitbang, ftdi_bitbang->gpio_data); +} -FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { - FtdiBitbang* ftdi_bitbang = malloc(sizeof(FtdiBitbang)); +FtdiBitbang *ftdi_bitbang_alloc(Ftdi *ftdi) +{ + FtdiBitbang *ftdi_bitbang = malloc(sizeof(FtdiBitbang)); ftdi_bitbang->ftdi = ftdi; ftdi_bitbang->enable = false; ftdi_bitbang->gpio_mask = 0; @@ -245,35 +362,46 @@ FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { return ftdi_bitbang; } -void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { - if(!ftdi_bitbang) return; +void ftdi_bitbang_free(FtdiBitbang *ftdi_bitbang) +{ + if (!ftdi_bitbang) + return; + ftdi_bitbang->enable = false; furi_thread_flags_set(ftdi_bitbang->worker_thread, WorkerEventStop); furi_thread_join(ftdi_bitbang->worker_thread); furi_thread_free(ftdi_bitbang->worker_thread); + ftdi_gpio_deinit(); + free(ftdi_bitbang); ftdi_bitbang = NULL; } -void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { +void ftdi_bitbang_set_gpio(FtdiBitbang *ftdi_bitbang, uint8_t gpio_mask) +{ ftdi_bitbang->gpio_mask = gpio_mask; - ftdi_bitbang_gpio_init(ftdi_bitbang); + ftdi_bitbang_gpio_set_direction(ftdi_bitbang); } -void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async) { +void ftdi_bitbang_enable(FtdiBitbang *ftdi_bitbang, bool enable, bool async) +{ ftdi_bitbang->enable = enable; ftdi_bitbang->async = async; - if(enable) { + if (enable) + { LL_TIM_SetCounter(TIM17, 0); LL_TIM_EnableCounter(TIM17); - } else { + } + else + { LL_TIM_DisableCounter(TIM17); } } -void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed) { +void ftdi_bitbang_set_speed(FtdiBitbang *ftdi_bitbang, uint32_t speed) +{ UNUSED(ftdi_bitbang); UNUSED(speed); @@ -284,23 +412,26 @@ void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed) { LL_TIM_SetAutoReload(TIM17, period - 1); } -static void ftdi_bitbang_tim_isr(void* context) { - FtdiBitbang* ftdi_bitbang = context; +static void ftdi_bitbang_tim_isr(void *context) +{ + FtdiBitbang *ftdi_bitbang = context; UNUSED(ftdi_bitbang); - if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { + if (LL_TIM_IsActiveFlag_UPDATE(TIM17)) + { LL_TIM_ClearFlag_UPDATE(TIM17); furi_thread_flags_set( furi_thread_get_id(ftdi_bitbang->worker_thread), WorkerEventTimerUpdate); } } -static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang) { +static void ftdi_bitbang_tim_init(FtdiBitbang *ftdi_bitbang) +{ furi_hal_bus_enable(FuriHalBusTIM17); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); LL_TIM_SetClockDivision(TIM17, LL_TIM_CLOCKDIVISION_DIV1); - LL_TIM_SetAutoReload(TIM17, 6400-1); - LL_TIM_SetPrescaler(TIM17, 0); //10kHz + LL_TIM_SetAutoReload(TIM17, 6400 - 1); + LL_TIM_SetPrescaler(TIM17, 0); // 10kHz LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL); LL_TIM_DisableARRPreload(TIM17); @@ -310,7 +441,8 @@ static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang) { LL_TIM_EnableIT_UPDATE(TIM17); } -static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang) { +static void ftdi_bitbang_tim_deinit(FtdiBitbang *ftdi_bitbang) +{ UNUSED(ftdi_bitbang); LL_TIM_DisableCounter(TIM17); furi_hal_bus_disable(FuriHalBusTIM17); diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h index a9e364e..067e81d 100644 --- a/flip_tdi/helpers/ftdi_bitbang.h +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -9,4 +9,5 @@ void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async); void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); -uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang); \ No newline at end of file +uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang); +uint8_t ftdi_bitbang_gpio_io(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_gpio.c b/flip_tdi/helpers/ftdi_gpio.c new file mode 100644 index 0000000..7712a4e --- /dev/null +++ b/flip_tdi/helpers/ftdi_gpio.c @@ -0,0 +1,131 @@ +#include "ftdi_latency_timer.h" +#include "furi.h" +#include + +#include + +#define TAG "FTDI_GPIO" + +#define gpio_b0 (gpio_ext_pa7) +#define gpio_b1 (gpio_ext_pa6) +#define gpio_b2 (gpio_ext_pa4) +#define gpio_b3 (gpio_ext_pb3) +#define gpio_b4 (gpio_ext_pb2) +#define gpio_b5 (gpio_ext_pc3) +#define gpio_b6 (gpio_ext_pc1) +#define gpio_b7 (gpio_ext_pc0) + +void ftdi_gpio_set_direction(uint8_t gpio_mask) { + // gpio_ext_pa7 + if(gpio_mask & 0b00000001) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_7, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pa6 + if(gpio_mask & 0b00000010) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_6, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pa4 + if(gpio_mask & 0b00000100) { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_4, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pb3 + if(gpio_mask & 0b00001000) { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_3, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pb2 + if(gpio_mask & 0b00010000) { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_2, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_2, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc3 + if(gpio_mask & 0b00100000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_3, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_3, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc1 + if(gpio_mask & 0b01000000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_1, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_1, LL_GPIO_MODE_INPUT); + } + // gpio_ext_pc0 + if(gpio_mask & 0b10000000) { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT); + } else { + LL_GPIO_SetPinMode(GPIOC, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT); + } +} + +void ftdi_gpio_init(uint8_t gpio_mask) { + // // gpio_ext_pa7 + // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_7, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_7, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_7, LL_GPIO_PULL_NO); + + // // gpio_ext_pa6 + // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_6, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_6, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_6, LL_GPIO_PULL_NO); + + // // gpio_ext_pa4 + // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_4, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_4, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_4, LL_GPIO_PULL_NO); + + // // gpio_ext_pb3 + // LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_3, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_3, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_3, LL_GPIO_PULL_NO); + + // // gpio_ext_pb2 + // LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_2, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_2, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_2, LL_GPIO_PULL_NO); + + // // gpio_ext_pc3 + // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_3, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_3, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_3, LL_GPIO_PULL_NO); + + // // gpio_ext_pc1 + // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_1, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_1, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_1, LL_GPIO_PULL_NO); + + // // gpio_ext_pc0 + // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_0, LL_GPIO_SPEED_FREQ_VERY_HIGH); + // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_0, LL_GPIO_OUTPUT_PUSHPULL); + // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_0, LL_GPIO_PULL_NO); + furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + //furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + + ftdi_gpio_set_direction(gpio_mask); +} + +void ftdi_gpio_deinit() { + furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); + furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); +} diff --git a/flip_tdi/helpers/ftdi_gpio.h b/flip_tdi/helpers/ftdi_gpio.h new file mode 100644 index 0000000..1982c90 --- /dev/null +++ b/flip_tdi/helpers/ftdi_gpio.h @@ -0,0 +1,126 @@ +#pragma once +#include "ftdi.h" + +void ftdi_gpio_set_direction(uint8_t gpio_mask); +void ftdi_gpio_init(uint8_t gpio_mask); +void ftdi_gpio_deinit(); + +static inline uint8_t ftdi_gpio_get_b0(uint8_t state) { // gpio_ext_pa7 + UNUSED(state); + return (GPIOA->IDR & LL_GPIO_PIN_7) ? 0b00000001 : 0; +} + +static inline uint8_t ftdi_gpio_get_b1(uint8_t state) { // gpio_ext_pa6 + UNUSED(state); + return (GPIOA->IDR & LL_GPIO_PIN_6) ? 0b00000010 : 0; +} + +static inline uint8_t ftdi_gpio_get_b2(uint8_t state) { // gpio_ext_pa4 + UNUSED(state); + return (GPIOA->IDR & LL_GPIO_PIN_4) ? 0b00000100 : 0; +} + +static inline uint8_t ftdi_gpio_get_b3(uint8_t state) { // gpio_ext_pb3 + UNUSED(state); + return (GPIOB->IDR & LL_GPIO_PIN_3) ? 0b00001000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b4(uint8_t state) { // gpio_ext_pb2 + UNUSED(state); + return (GPIOB->IDR & LL_GPIO_PIN_2) ? 0b00010000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b5(uint8_t state) { // gpio_ext_pc3 + UNUSED(state); + return (GPIOC->IDR & LL_GPIO_PIN_3) ? 0b00100000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b6(uint8_t state) { // gpio_ext_pc1 + UNUSED(state); + return (GPIOC->IDR & LL_GPIO_PIN_1) ? 0b01000000 : 0; +} + +static inline uint8_t ftdi_gpio_get_b7(uint8_t state) { // gpio_ext_pc0 + UNUSED(state); + return (GPIOC->IDR & LL_GPIO_PIN_0) ? 0b10000000 : 0; +} + +static inline uint8_t ftdi_gpio_set_b0(uint8_t state) { // gpio_ext_pa7 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_7; + return 0b00000001; + } else { + GPIOA->BRR = LL_GPIO_PIN_7; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b1(uint8_t state) { // gpio_ext_pa6 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_6; + return 0b00000010; + } else { + GPIOA->BRR = LL_GPIO_PIN_6; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b2(uint8_t state) { // gpio_ext_pa4 + if(state) { + GPIOA->BSRR = LL_GPIO_PIN_4; + return 0b00000100; + } else { + GPIOA->BRR = LL_GPIO_PIN_4; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b3(uint8_t state) { // gpio_ext_pb3 + if(state) { + GPIOB->BSRR = LL_GPIO_PIN_3; + return 0b00001000; + } else { + GPIOB->BRR = LL_GPIO_PIN_3; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b4(uint8_t state) { // gpio_ext_pb2 + if(state) { + GPIOB->BSRR = LL_GPIO_PIN_2; + return 0b00010000; + } else { + GPIOB->BRR = LL_GPIO_PIN_2; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b5(uint8_t state) { // gpio_ext_pc3 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_3; + return 0b00100000; + } else { + GPIOC->BRR = LL_GPIO_PIN_3; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b6(uint8_t state) { // gpio_ext_pc1 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_1; + return 0b01000000; + } else { + GPIOC->BRR = LL_GPIO_PIN_1; + return 0; + } +} + +static inline uint8_t ftdi_gpio_set_b7(uint8_t state) { // gpio_ext_pc0 + if(state) { + GPIOC->BSRR = LL_GPIO_PIN_0; + return 0b10000000; + } else { + GPIOC->BRR = LL_GPIO_PIN_0; + return 0; + } +} diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c new file mode 100644 index 0000000..55eb4bb --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -0,0 +1,336 @@ +#include "ftdi_mpsse.h" +#include "furi.h" +#include + +#define TAG "FTDI_MPSSE" + +struct FtdiMpsse { + Ftdi* ftdi; + uint8_t gpio_state; + uint8_t gpio_mask; + uint16_t data_size; + bool is_loopback; + bool is_div5; + bool is_clk3phase; + bool is_adaptive; +}; + +FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { + FtdiMpsse* ftdi_mpsse = malloc(sizeof(FtdiMpsse)); + ftdi_mpsse->ftdi = ftdi; + ftdi_mpsse->gpio_state = 0; + ftdi_mpsse->gpio_mask = 0; + ftdi_mpsse->data_size = 0; + ftdi_mpsse->is_loopback = false; + ftdi_mpsse->is_div5 = false; + ftdi_mpsse->is_clk3phase = false; + ftdi_mpsse->is_adaptive = false; + return ftdi_mpsse; +} + +void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { + if(!ftdi_mpsse) return; + free(ftdi_mpsse); + ftdi_mpsse = NULL; +} + +uint8_t ftdi_mpsse_get_data_stream(FtdiMpsse* ftdi_mpsse) { + uint8_t data = 0; + //Todo add timeout + ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1); + return data; +} + +void ftdi_mpssse_set_data_stream(FtdiMpsse* ftdi_mpsse, uint8_t* data, uint16_t size) { + ftdi_set_tx_buf(ftdi_mpsse->ftdi, data, size); +} + +void ftdi_mpsse_get_data(FtdiMpsse* ftdi_mpsse) { + //todo add support for tx buffer, data_size_max = 0xFF00 + ftdi_mpsse->data_size++; + while(ftdi_mpsse->data_size--) { + ftdi_mpsse_get_data_stream(ftdi_mpsse); + } +} + +static uint16_t ftdi_mpsse_get_data_size(FtdiMpsse* ftdi_mpsse) { + return (uint16_t)ftdi_mpsse_get_data_stream(ftdi_mpsse) << 8 | + ftdi_mpsse_get_data_stream(ftdi_mpsse); +} + +static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->data_size++; + while(ftdi_mpsse->data_size--) { + ftdi_mpsse_get_data_stream(ftdi_mpsse); + } +} + +void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { + uint8_t data = ftdi_mpsse_get_data_stream(ftdi_mpsse); + switch(data) { + case FtdiMpsseCommandsSetBitsLow: // 0x80 Change LSB GPIO output */ + ftdi_mpsse->gpio_state = ftdi_mpsse_get_data_stream(ftdi_mpsse); + ftdi_mpsse->gpio_state = ftdi_mpsse_get_data_stream(ftdi_mpsse); + //Write to GPIO + break; + case FtdiMpsseCommandsSetBitsHigh: // 0x82 Change MSB GPIO output */ + //Todo not supported + ftdi_mpsse_get_data_stream(ftdi_mpsse); + ftdi_mpsse_get_data_stream(ftdi_mpsse); + break; + case FtdiMpsseCommandsGetBitsLow: // 0x81 Get LSB GPIO output */ + //Read GPIO + //add to buffer + //read Gpio + ftdi_mpssse_set_data_stream(ftdi_mpsse, &ftdi_mpsse->gpio_state, 1); + break; + case FtdiMpsseCommandsGetBitsHigh: // 0x83 Get MSB GPIO output */ + //Todo not supported + //add to buffer FF + uint8_t gpio = 0xFF; + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio, 1); + break; + case FtdiMpsseCommandsSendImmediate: // 0x87 Send immediate */ + //tx data to host add callback + break; + case FtdiMpsseCommandsWriteBytesPveMsb: // 0x10 Write bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBytesNveMsb: // 0x11 Write bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsPveMsb: // 0x12 Write bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsNveMsb: // 0x13 Write bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBytesPveLsb: // 0x18 Write bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBytesNveLsb: // 0x19 Write bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_get_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsPveLsb: // 0x1a Write bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsWriteBitsNveLsb: // 0x1b Write bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + ftdi_mpsse_skeep_data(ftdi_mpsse); + break; + case FtdiMpsseCommandsReadBytesPveMsb: // 0x20 Read bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBytesNveMsb: // 0x24 Read bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsPveMsb: // 0x22 Read bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsNveMsb: // 0x26 Read bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBytesPveLsb: // 0x28 Read bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBytesNveLsb: // 0x2c Read bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsPveLsb: // 0x2a Read bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsReadBitsNveLsb: // 0x2e Read bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //write data + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesPveNveMsb: // 0x31 Read/Write bytes with positive edge clock, MSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesNvePveMsb: // 0x34 Read/Write bytes with negative edge clock, MSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsPveNveMsb: // 0x33 Read/Write bits with positive edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsNvePveMsb: // 0x36 Read/Write bits with negative edge clock, MSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesPveNveLsb: // 0x39 Read/Write bytes with positive edge clock, LSB first */ + //spi mode 1,3 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBytesNvePveLsb: // 0x3c Read/Write bytes with negative edge clock, LSB first */ + //spi mode 0,2 + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_get_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsPveNveLsb: // 0x3b Read/Write bits with positive edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsRwBitsNvePveLsb: // 0x3e Read/Write bits with negative edge clock, LSB first */ + //not supported + ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + //read data + //ftdi_mpsse_skeep_data(ftdi_mpsse); + //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + break; + case FtdiMpsseCommandsWriteBitsTmsPve: // 0x4a Write bits with TMS, positive edge clock */ + //not supported + break; + case FtdiMpsseCommandsWriteBitsTmsNve: // 0x4b Write bits with TMS, negative edge clock */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsPvePve: // 0x6a Read/Write bits with TMS, positive edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsPveNve: // 0x6b Read/Write bits with TMS, positive edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsNvePve: // 0x6e Read/Write bits with TMS, negative edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsRwBitsTmsNveNve: // 0x6f Read/Write bits with TMS, negative edge clock, MSB first */ + //not supported + break; + case FtdiMpsseCommandsLoopbackStart: // 0x84 Enable loopback */ + ftdi_mpsse->is_loopback = true; + break; + case FtdiMpsseCommandsLoopbackEnd: // 0x85 Disable loopback */ + ftdi_mpsse->is_loopback = false; + break; + case FtdiMpsseCommandsSetTckDivisor: // 0x86 Set clock */ + + break; + case FtdiMpsseCommandsDisDiv5: // 0x8a Disable divide by 5 */ + ftdi_mpsse->is_div5 = false; + break; + case FtdiMpsseCommandsEnDiv5: // 0x8b Enable divide by 5 */ + ftdi_mpsse->is_div5 = true; + break; + case FtdiMpsseCommandsEnableClk3Phase: // 0x8c Enable 3-phase data clocking (I2C) */ + ftdi_mpsse->is_clk3phase = true; + break; + case FtdiMpsseCommandsDisableClk3Phase: // 0x8d Disable 3-phase data clocking */ + ftdi_mpsse->is_clk3phase = false; + break; + case FtdiMpsseCommandsClkBitsNoData: // 0x8e Allows JTAG clock to be output w/o data */ + //not supported + break; + case FtdiMpsseCommandsClkBytesNoData: // 0x8f Allows JTAG clock to be output w/o data */ + //not supported + break; + case FtdiMpsseCommandsClkWaitOnHigh: // 0x94 Clock until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsClkWaitOnLow: // 0x95 Clock until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsEnableClkAdaptive: // 0x96 Enable JTAG adaptive clock for ARM */ + ftdi_mpsse->is_adaptive = true; + break; + case FtdiMpsseCommandsDisableClkAdaptive: // 0x97 Disable JTAG adaptive clock */ + ftdi_mpsse->is_adaptive = false; + break; + case FtdiMpsseCommandsClkCountWaitOnHigh: // 0x9c Clock byte cycles until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsClkCountWaitOnLow: // 0x9d Clock byte cycles until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsDriveZero: // 0x9e Drive-zero mode */ + //not supported + break; + case FtdiMpsseCommandsWaitOnHigh: // 0x88 Wait until GPIOL1 is high */ + //not supported + break; + case FtdiMpsseCommandsWaitOnLow: // 0x89 Wait until GPIOL1 is low */ + //not supported + break; + case FtdiMpsseCommandsReadShort: // 0x90 Read short */ + //not supported + break; + case FtdiMpsseCommandsReadExtended: // 0x91 Read extended */ + //not supported + break; + case FtdiMpsseCommandsWriteShort: // 0x92 Write short */ + //not supported + break; + case FtdiMpsseCommandsWriteExtended: // 0x93 Write extended */ + //not supported + break; + + default: + break; + } +} diff --git a/flip_tdi/helpers/ftdi_mpsse.h b/flip_tdi/helpers/ftdi_mpsse.h new file mode 100644 index 0000000..094e702 --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse.h @@ -0,0 +1,6 @@ +#pragma once +#include "ftdi.h" + +typedef struct FtdiMpsse FtdiMpsse; + + diff --git a/flip_tdi/helpers/ftdi_usb_define.h b/flip_tdi/helpers/ftdi_usb_define.h index 4c93d15..fe2d959 100644 --- a/flip_tdi/helpers/ftdi_usb_define.h +++ b/flip_tdi/helpers/ftdi_usb_define.h @@ -97,6 +97,48 @@ static_assert(sizeof(FtdiBitMode) == sizeof(uint8_t), "Wrong FtdiBitMode"); /* FTDI MPSSE commands */ typedef enum { + // /* Shifting commands IN MPSSE Mode*/ + // #define MPSSE_WRITE_NEG 0x01 /* Write TDI/DO on negative TCK/SK edge*/ + // #define MPSSE_BITMODE 0x02 /* Write bits, not bytes */ + // #define MPSSE_READ_NEG 0x04 /* Sample TDO/DI on negative TCK/SK edge */ + // #define MPSSE_LSB 0x08 /* LSB first */ + // #define MPSSE_DO_WRITE 0x10 /* Write TDI/DO */ + // #define MPSSE_DO_READ 0x20 /* Read TDO/DI */ + // #define MPSSE_WRITE_TMS 0x40 /* Write TMS/CS */ + + /*MPSSE Commands*/ + FtdiMpsseCommandsWriteBytesPveMsb = 0x10, /**< Write bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsWriteBytesNveMsb = 0x11, /**< Write bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsWriteBitsPveMsb = 0x12, /**< Write bits with positive edge clock, MSB first */ + FtdiMpsseCommandsWriteBitsNveMsb = 0x13, /**< Write bits with negative edge clock, MSB first */ + FtdiMpsseCommandsWriteBytesPveLsb = 0x18, /**< Write bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsWriteBytesNveLsb = 0x19, /**< Write bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsPveLsb = 0x1a, /**< Write bits with positive edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsNveLsb = 0x1b, /**< Write bits with negative edge clock, LSB first */ + FtdiMpsseCommandsReadBytesPveMsb = 0x20, /**< Read bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsReadBytesNveMsb = 0x24, /**< Read bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsReadBitsPveMsb = 0x22, /**< Read bits with positive edge clock, MSB first */ + FtdiMpsseCommandsReadBitsNveMsb = 0x26, /**< Read bits with negative edge clock, MSB first */ + FtdiMpsseCommandsReadBytesPveLsb = 0x28, /**< Read bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsReadBytesNveLsb = 0x2c, /**< Read bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsReadBitsPveLsb = 0x2a, /**< Read bits with positive edge clock, LSB first */ + FtdiMpsseCommandsReadBitsNveLsb = 0x2e, /**< Read bits with negative edge clock, LSB first */ + FtdiMpsseCommandsRwBytesPveNveMsb = 0x31, /**< Read/Write bytes with positive edge clock, MSB first */ + FtdiMpsseCommandsRwBytesNvePveMsb = 0x34, /**< Read/Write bytes with negative edge clock, MSB first */ + FtdiMpsseCommandsRwBitsPveNveMsb = 0x33, /**< Read/Write bits with positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsNvePveMsb = 0x36, /**< Read/Write bits with negative edge clock, MSB first */ + FtdiMpsseCommandsRwBytesPveNveLsb = 0x39, /**< Read/Write bytes with positive edge clock, LSB first */ + FtdiMpsseCommandsRwBytesNvePveLsb = 0x3c, /**< Read/Write bytes with negative edge clock, LSB first */ + FtdiMpsseCommandsRwBitsPveNveLsb = 0x3b, /**< Read/Write bits with positive edge clock, LSB first */ + FtdiMpsseCommandsRwBitsNvePveLsb = 0x3e, /**< Read/Write bits with negative edge clock, LSB first */ + FtdiMpsseCommandsWriteBitsTmsPve = 0x4a, /**< Write bits with TMS, positive edge clock */ + FtdiMpsseCommandsWriteBitsTmsNve = 0x4b, /**< Write bits with TMS, negative edge clock */ + FtdiMpsseCommandsRwBitsTmsPvePve = 0x6a, /**< Read/Write bits with TMS, positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsPveNve = 0x6b, /**< Read/Write bits with TMS, positive edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsNvePve = 0x6e, /**< Read/Write bits with TMS, negative edge clock, MSB first */ + FtdiMpsseCommandsRwBitsTmsNveNve = 0x6f, /**< Read/Write bits with TMS, negative edge clock, MSB first */ + + FtdiMpsseCommandsSetBitsLow = 0x80, /**< Change LSB GPIO output */ /*BYTE DATA*/ /*BYTE Direction*/ @@ -123,6 +165,16 @@ typedef enum { FtdiMpsseCommandsClkCountWaitOnLow = 0x9d, /**< Clock byte cycles until GPIOL1 is low */ //FT232H only FtdiMpsseCommandsDriveZero = 0x9e, /**< Drive-zero mode */ + /* Commands in MPSSE and Host Emulation Mode */ + FtdiMpsseCommandsSendImmediate = 0x87, /**< Send immediate */ + FtdiMpsseCommandsWaitOnHigh = 0x88, /**< Wait until GPIOL1 is high */ + FtdiMpsseCommandsWaitOnLow = 0x89, /**< Wait until GPIOL1 is low */ + /* Commands in Host Emulation Mode */ + FtdiMpsseCommandsReadShort = 0x90, /**< Read short */ + FtdiMpsseCommandsReadExtended = 0x91, /**< Read extended */ + FtdiMpsseCommandsWriteShort = 0x92, /**< Write short */ + FtdiMpsseCommandsWriteExtended = 0x93, /**< Write extended */ + } FtdiMpsseCommands; /* USB control requests */ From b290dd95ef497b1f3452e78cfca6afcd9f1002ed Mon Sep 17 00:00:00 2001 From: SkorP Date: Wed, 17 Jul 2024 18:59:53 +0400 Subject: [PATCH 10/19] FlipTDI: gpio speed optimization --- flip_tdi/helpers/ftdi.c | 2 +- flip_tdi/helpers/ftdi_bitbang.c | 423 +++++++++----------------------- flip_tdi/helpers/ftdi_bitbang.h | 3 +- flip_tdi/helpers/ftdi_gpio.c | 42 +--- flip_tdi/helpers/ftdi_gpio.h | 60 ++--- 5 files changed, 141 insertions(+), 389 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 2467e17..898b214 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -453,7 +453,7 @@ void ftdi_start_uart_tx(Ftdi* ftdi) { uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi) { ftdi_reset_purge_tx(ftdi); - return ftdi_bitbang_gpio_get(ftdi->ftdi_bitbang); + return ftdi_bitbang_get_gpio(ftdi->ftdi_bitbang); } /* diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c index 3f67618..e395a24 100644 --- a/flip_tdi/helpers/ftdi_bitbang.c +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -1,3 +1,6 @@ +#pragma GCC optimize("O3") +#pragma GCC optimize("-funroll-all-loops") + #include "ftdi_bitbang.h" #include "furi.h" #include @@ -7,350 +10,152 @@ #define TAG "FTDI_BITBANG" -// #define gpio_b0 (gpio_ext_pa7) -// #define gpio_b1 (gpio_ext_pa6) -// #define gpio_b2 (gpio_ext_pa4) -// #define gpio_b3 (gpio_ext_pb3) -// #define gpio_b4 (gpio_ext_pb2) -// #define gpio_b5 (gpio_ext_pc3) -// #define gpio_b6 (gpio_ext_pc1) -// #define gpio_b7 (gpio_ext_pc0) -typedef uint8_t (*FtdiBitbangGpioIO)(uint8_t state); -struct FtdiBitbang -{ - FuriThread *worker_thread; - Ftdi *ftdi; +typedef void (*FtdiBitbangGpioIO)(uint8_t state); +struct FtdiBitbang { + FuriThread* worker_thread; + Ftdi* ftdi; uint32_t speed; bool enable; uint8_t gpio_mask; bool async; - FtdiBitbangGpioIO gpio_io[8]; + FtdiBitbangGpioIO gpio_o[8]; uint8_t gpio_data; }; -typedef enum -{ - WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event +typedef enum { + WorkerEventReserved = (1 << 0), WorkerEventStop = (1 << 1), WorkerEventTimerUpdate = (1 << 2), } WorkerEvent; #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventTimerUpdate) -// static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang); -// static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang); -// static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); -static void ftdi_bitbang_tim_init(FtdiBitbang *ftdi_bitbang); -static void ftdi_bitbang_tim_deinit(FtdiBitbang *ftdi_bitbang); +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang); +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang); -void ftdi_bitbang_gpio_set_direction(FtdiBitbang *ftdi_bitbang) -{ +void ftdi_bitbang_gpio_set_direction(FtdiBitbang* ftdi_bitbang) { ftdi_gpio_set_direction(ftdi_bitbang->gpio_mask); - if (ftdi_bitbang->gpio_mask & 0b00000001) - { - ftdi_bitbang->gpio_io[0] = ftdi_gpio_set_b0; - } - else - { - ftdi_bitbang->gpio_io[0] = ftdi_gpio_get_b0; + if(ftdi_bitbang->gpio_mask & 0b00000001) { + ftdi_bitbang->gpio_o[0] = ftdi_gpio_set_b0; + } else { + ftdi_bitbang->gpio_o[0] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b00000010) - { - ftdi_bitbang->gpio_io[1] = ftdi_gpio_set_b1; - } - else - { - ftdi_bitbang->gpio_io[1] = ftdi_gpio_get_b1; + if(ftdi_bitbang->gpio_mask & 0b00000010) { + ftdi_bitbang->gpio_o[1] = ftdi_gpio_set_b1; + } else { + ftdi_bitbang->gpio_o[1] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b00000100) - { - ftdi_bitbang->gpio_io[2] = ftdi_gpio_set_b2; - } - else - { - ftdi_bitbang->gpio_io[2] = ftdi_gpio_get_b2; + if(ftdi_bitbang->gpio_mask & 0b00000100) { + ftdi_bitbang->gpio_o[2] = ftdi_gpio_set_b2; + } else { + ftdi_bitbang->gpio_o[2] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b00001000) - { - ftdi_bitbang->gpio_io[3] = ftdi_gpio_set_b3; - } - else - { - ftdi_bitbang->gpio_io[3] = ftdi_gpio_get_b3; + if(ftdi_bitbang->gpio_mask & 0b00001000) { + ftdi_bitbang->gpio_o[3] = ftdi_gpio_set_b3; + } else { + ftdi_bitbang->gpio_o[3] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b00010000) - { - ftdi_bitbang->gpio_io[4] = ftdi_gpio_set_b4; - } - else - { - ftdi_bitbang->gpio_io[4] = ftdi_gpio_get_b4; + if(ftdi_bitbang->gpio_mask & 0b00010000) { + ftdi_bitbang->gpio_o[4] = ftdi_gpio_set_b4; + } else { + ftdi_bitbang->gpio_o[4] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b00100000) - { - ftdi_bitbang->gpio_io[5] = ftdi_gpio_set_b5; - } - else - { - ftdi_bitbang->gpio_io[5] = ftdi_gpio_get_b5; + if(ftdi_bitbang->gpio_mask & 0b00100000) { + ftdi_bitbang->gpio_o[5] = ftdi_gpio_set_b5; + } else { + ftdi_bitbang->gpio_o[5] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b01000000) - { - ftdi_bitbang->gpio_io[6] = ftdi_gpio_set_b6; - } - else - { - ftdi_bitbang->gpio_io[6] = ftdi_gpio_get_b6; + if(ftdi_bitbang->gpio_mask & 0b01000000) { + ftdi_bitbang->gpio_o[6] = ftdi_gpio_set_b6; + } else { + ftdi_bitbang->gpio_o[6] = ftdi_gpio_set_noop; } - if (ftdi_bitbang->gpio_mask & 0b10000000) - { - ftdi_bitbang->gpio_io[7] = ftdi_gpio_set_b7; - } - else - { - ftdi_bitbang->gpio_io[7] = ftdi_gpio_get_b7; + if(ftdi_bitbang->gpio_mask & 0b10000000) { + ftdi_bitbang->gpio_o[7] = ftdi_gpio_set_b7; + } else { + ftdi_bitbang->gpio_o[7] = ftdi_gpio_set_noop; } } -void ftdi_bitbang_gpio_init(FtdiBitbang *ftdi_bitbang) -{ +void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { ftdi_gpio_init(ftdi_bitbang->gpio_mask); ftdi_bitbang_gpio_set_direction(ftdi_bitbang); } -uint8_t ftdi_bitbang_gpio_io(FtdiBitbang *ftdi_bitbang, uint8_t gpio_data_out) -{ - uint8_t gpio_data_in = 0; - gpio_data_in |= ftdi_bitbang->gpio_io[0](gpio_data_out & 0b00000001); - gpio_data_in |= ftdi_bitbang->gpio_io[1](gpio_data_out & 0b00000010); - gpio_data_in |= ftdi_bitbang->gpio_io[2](gpio_data_out & 0b00000100); - gpio_data_in |= ftdi_bitbang->gpio_io[3](gpio_data_out & 0b00001000); - gpio_data_in |= ftdi_bitbang->gpio_io[4](gpio_data_out & 0b00010000); - gpio_data_in |= ftdi_bitbang->gpio_io[5](gpio_data_out & 0b00100000); - gpio_data_in |= ftdi_bitbang->gpio_io[6](gpio_data_out & 0b01000000); - gpio_data_in |= ftdi_bitbang->gpio_io[7](gpio_data_out & 0b10000000); - return gpio_data_in; +static inline void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data_out) { + ftdi_bitbang->gpio_o[0](gpio_data_out & 0b00000001); + ftdi_bitbang->gpio_o[1](gpio_data_out & 0b00000010); + ftdi_bitbang->gpio_o[2](gpio_data_out & 0b00000100); + ftdi_bitbang->gpio_o[3](gpio_data_out & 0b00001000); + ftdi_bitbang->gpio_o[4](gpio_data_out & 0b00010000); + ftdi_bitbang->gpio_o[5](gpio_data_out & 0b00100000); + ftdi_bitbang->gpio_o[6](gpio_data_out & 0b01000000); + ftdi_bitbang->gpio_o[7](gpio_data_out & 0b10000000); +} + +static inline uint8_t ftdi_bitbang_gpio_get(void) { + uint8_t gpio_data = 0; + gpio_data |= ftdi_gpio_get_b0(); + gpio_data |= ftdi_gpio_get_b1(); + gpio_data |= ftdi_gpio_get_b2(); + gpio_data |= ftdi_gpio_get_b3(); + gpio_data |= ftdi_gpio_get_b4(); + gpio_data |= ftdi_gpio_get_b5(); + gpio_data |= ftdi_gpio_get_b6(); + gpio_data |= ftdi_gpio_get_b7(); + return gpio_data; } -static int32_t ftdi_bitbang_worker(void *context) -{ +static int32_t ftdi_bitbang_worker(void* context) { furi_assert(context); - FtdiBitbang *ftdi_bitbang = context; + FtdiBitbang* ftdi_bitbang = context; uint8_t buffer[64]; FURI_LOG_I(TAG, "Worker started"); ftdi_bitbang_tim_init(ftdi_bitbang); - // uint8_t gpio_data[1] = {0}; - while (1) - { + while(1) { uint32_t events = furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); furi_check((events & FuriFlagError) == 0); - if (events & WorkerEventStop) - break; - if (events & WorkerEventTimerUpdate) - { + if(events & WorkerEventTimerUpdate) { size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); - if (length > 0) - { - // ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); - ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_io(ftdi_bitbang, buffer[0]); - if (!ftdi_bitbang->async) - { - // buffer[0] = ftdi_bitbang_gpio_get(ftdi_bitbang); + if(length > 0) { + ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + if(!ftdi_bitbang->async) { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); } } - if (ftdi_bitbang->enable && ftdi_bitbang->async) - { - ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_io(ftdi_bitbang, ftdi_bitbang->gpio_data); + if(ftdi_bitbang->enable && ftdi_bitbang->async) { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); } + continue; } + + if(events & WorkerEventStop) break; } ftdi_bitbang_tim_deinit(ftdi_bitbang); - // ftdi_bitbang_gpio_deinit(ftdi_bitbang); - // ftdi_gpio_deinit(); + FURI_LOG_I(TAG, "Worker stopped"); return 0; } -// #pragma GCC push_options -// #pragma GCC optimize("Ofast,unroll-loops") -// // #pragma GCC optimize("O3") -// // #pragma GCC optimize("-funroll-all-loops") - -// static void ftdi_bitbang_gpio_init(FtdiBitbang* ftdi_bitbang) { -// if(ftdi_bitbang->gpio_mask & 0b00000001) { -// furi_hal_gpio_init(&gpio_b0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b00000010) { -// furi_hal_gpio_init(&gpio_b1, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b00000100) { -// furi_hal_gpio_init(&gpio_b2, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b00001000) { -// furi_hal_gpio_init(&gpio_b3, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b00010000) { -// furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b00100000) { -// furi_hal_gpio_init(&gpio_b5, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b01000000) { -// furi_hal_gpio_init(&gpio_b6, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// if(ftdi_bitbang->gpio_mask & 0b10000000) { -// furi_hal_gpio_init(&gpio_b7, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); -// } else { -// furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); -// } -// } - -// static void ftdi_bitbang_gpio_deinit(FtdiBitbang* ftdi_bitbang) { -// UNUSED(ftdi_bitbang); -// furi_hal_gpio_init(&gpio_b0, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b1, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b2, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b3, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b4, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b5, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// furi_hal_gpio_init(&gpio_b7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); -// } - -// static void ftdi_bitbang_gpio_set(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data) { -// if(ftdi_bitbang->gpio_mask & 0b00000001) { -// if(gpio_data & 0b00000001) { -// furi_hal_gpio_write(&gpio_b0, 1); -// } else { -// furi_hal_gpio_write(&gpio_b0, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b00000010) { -// if(gpio_data & 0b00000010) { -// furi_hal_gpio_write(&gpio_b1, 1); -// } else { -// furi_hal_gpio_write(&gpio_b1, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b00000100) { -// if(gpio_data & 0b00000100) { -// furi_hal_gpio_write(&gpio_b2, 1); -// } else { -// furi_hal_gpio_write(&gpio_b2, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b00001000) { -// if(gpio_data & 0b00001000) { -// furi_hal_gpio_write(&gpio_b3, 1); -// } else { -// furi_hal_gpio_write(&gpio_b3, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b00010000) { -// if(gpio_data & 0b00010000) { -// furi_hal_gpio_write(&gpio_b4, 1); -// } else { -// furi_hal_gpio_write(&gpio_b4, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b00100000) { -// if(gpio_data & 0b00100000) { -// furi_hal_gpio_write(&gpio_b5, 1); -// } else { -// furi_hal_gpio_write(&gpio_b5, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b01000000) { -// if(gpio_data & 0b01000000) { -// furi_hal_gpio_write(&gpio_b6, 1); -// } else { -// furi_hal_gpio_write(&gpio_b6, 0); -// } -// } - -// if(ftdi_bitbang->gpio_mask & 0b10000000) { -// if(gpio_data & 0b10000000) { -// furi_hal_gpio_write(&gpio_b7, 1); -// } else { -// furi_hal_gpio_write(&gpio_b7, 0); -// } -// } -// } - -// uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang) { -// UNUSED(ftdi_bitbang); -// uint8_t gpio_data = 0; -// if(furi_hal_gpio_read(&gpio_b0)) { -// gpio_data |= 0b00000001; -// } -// if(furi_hal_gpio_read(&gpio_b1)) { -// gpio_data |= 0b00000010; -// } -// if(furi_hal_gpio_read(&gpio_b2)) { -// gpio_data |= 0b00000100; -// } -// if(furi_hal_gpio_read(&gpio_b3)) { -// gpio_data |= 0b00001000; -// } -// if(furi_hal_gpio_read(&gpio_b4)) { -// gpio_data |= 0b00010000; -// } -// if(furi_hal_gpio_read(&gpio_b5)) { -// gpio_data |= 0b00100000; -// } -// if(furi_hal_gpio_read(&gpio_b6)) { -// gpio_data |= 0b01000000; -// } -// if(furi_hal_gpio_read(&gpio_b7)) { -// gpio_data |= 0b10000000; -// } - -// return gpio_data; -// } - -// #pragma GCC pop_options - -uint8_t ftdi_bitbang_gpio_get(FtdiBitbang *ftdi_bitbang) -{ - return ftdi_bitbang_gpio_io(ftdi_bitbang, ftdi_bitbang->gpio_data); +uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang) { + return ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); } -FtdiBitbang *ftdi_bitbang_alloc(Ftdi *ftdi) -{ - FtdiBitbang *ftdi_bitbang = malloc(sizeof(FtdiBitbang)); +FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { + FtdiBitbang* ftdi_bitbang = malloc(sizeof(FtdiBitbang)); ftdi_bitbang->ftdi = ftdi; ftdi_bitbang->enable = false; ftdi_bitbang->gpio_mask = 0; @@ -362,10 +167,8 @@ FtdiBitbang *ftdi_bitbang_alloc(Ftdi *ftdi) return ftdi_bitbang; } -void ftdi_bitbang_free(FtdiBitbang *ftdi_bitbang) -{ - if (!ftdi_bitbang) - return; +void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { + if(!ftdi_bitbang) return; ftdi_bitbang->enable = false; furi_thread_flags_set(ftdi_bitbang->worker_thread, WorkerEventStop); @@ -378,33 +181,32 @@ void ftdi_bitbang_free(FtdiBitbang *ftdi_bitbang) ftdi_bitbang = NULL; } -void ftdi_bitbang_set_gpio(FtdiBitbang *ftdi_bitbang, uint8_t gpio_mask) -{ +void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { ftdi_bitbang->gpio_mask = gpio_mask; ftdi_bitbang_gpio_set_direction(ftdi_bitbang); } -void ftdi_bitbang_enable(FtdiBitbang *ftdi_bitbang, bool enable, bool async) -{ +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async) { ftdi_bitbang->enable = enable; ftdi_bitbang->async = async; - if (enable) - { + if(enable) { LL_TIM_SetCounter(TIM17, 0); LL_TIM_EnableCounter(TIM17); - } - else - { + } else { LL_TIM_DisableCounter(TIM17); } } -void ftdi_bitbang_set_speed(FtdiBitbang *ftdi_bitbang, uint32_t speed) -{ +void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed) { UNUSED(ftdi_bitbang); UNUSED(speed); + //Todo limit speed + if(speed > 40000) { + speed = 40000; + } + uint32_t freq_div = 64000000LU / speed; uint32_t prescaler = freq_div / 0x10000LU; uint32_t period = freq_div / (prescaler + 1); @@ -412,20 +214,32 @@ void ftdi_bitbang_set_speed(FtdiBitbang *ftdi_bitbang, uint32_t speed) LL_TIM_SetAutoReload(TIM17, period - 1); } -static void ftdi_bitbang_tim_isr(void *context) -{ - FtdiBitbang *ftdi_bitbang = context; +static void ftdi_bitbang_tim_isr(void* context) { + FtdiBitbang* ftdi_bitbang = context; UNUSED(ftdi_bitbang); - if (LL_TIM_IsActiveFlag_UPDATE(TIM17)) - { + if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) { LL_TIM_ClearFlag_UPDATE(TIM17); + //Speed max 40kHz furi_thread_flags_set( furi_thread_get_id(ftdi_bitbang->worker_thread), WorkerEventTimerUpdate); + //Todo Speed Max 70kHz + // uint8_t buffer[1]; + // size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + // if(length > 0) { + // ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + // if(!ftdi_bitbang->async) { + // ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + // ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + // } + // } + // if(ftdi_bitbang->enable && ftdi_bitbang->async) { + // ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + // ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + // } } } -static void ftdi_bitbang_tim_init(FtdiBitbang *ftdi_bitbang) -{ +static void ftdi_bitbang_tim_init(FtdiBitbang* ftdi_bitbang) { furi_hal_bus_enable(FuriHalBusTIM17); LL_TIM_SetCounterMode(TIM17, LL_TIM_COUNTERMODE_UP); @@ -441,8 +255,7 @@ static void ftdi_bitbang_tim_init(FtdiBitbang *ftdi_bitbang) LL_TIM_EnableIT_UPDATE(TIM17); } -static void ftdi_bitbang_tim_deinit(FtdiBitbang *ftdi_bitbang) -{ +static void ftdi_bitbang_tim_deinit(FtdiBitbang* ftdi_bitbang) { UNUSED(ftdi_bitbang); LL_TIM_DisableCounter(TIM17); furi_hal_bus_disable(FuriHalBusTIM17); diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h index 067e81d..b5d2bcf 100644 --- a/flip_tdi/helpers/ftdi_bitbang.h +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -9,5 +9,4 @@ void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async); void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); -uint8_t ftdi_bitbang_gpio_get(FtdiBitbang* ftdi_bitbang); -uint8_t ftdi_bitbang_gpio_io(FtdiBitbang* ftdi_bitbang, uint8_t gpio_data); \ No newline at end of file +uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_gpio.c b/flip_tdi/helpers/ftdi_gpio.c index 7712a4e..b9b51d1 100644 --- a/flip_tdi/helpers/ftdi_gpio.c +++ b/flip_tdi/helpers/ftdi_gpio.c @@ -67,51 +67,11 @@ void ftdi_gpio_set_direction(uint8_t gpio_mask) { } void ftdi_gpio_init(uint8_t gpio_mask) { - // // gpio_ext_pa7 - // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_7, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_7, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_7, LL_GPIO_PULL_NO); - - // // gpio_ext_pa6 - // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_6, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_6, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_6, LL_GPIO_PULL_NO); - - // // gpio_ext_pa4 - // LL_GPIO_SetPinSpeed(GPIOA, LL_GPIO_PIN_4, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOA, LL_GPIO_PIN_4, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_4, LL_GPIO_PULL_NO); - - // // gpio_ext_pb3 - // LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_3, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_3, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_3, LL_GPIO_PULL_NO); - - // // gpio_ext_pb2 - // LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_2, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOB, LL_GPIO_PIN_2, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOB, LL_GPIO_PIN_2, LL_GPIO_PULL_NO); - - // // gpio_ext_pc3 - // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_3, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_3, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_3, LL_GPIO_PULL_NO); - - // // gpio_ext_pc1 - // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_1, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_1, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_1, LL_GPIO_PULL_NO); - - // // gpio_ext_pc0 - // LL_GPIO_SetPinSpeed(GPIOC, LL_GPIO_PIN_0, LL_GPIO_SPEED_FREQ_VERY_HIGH); - // LL_GPIO_SetPinOutputType(GPIOC, LL_GPIO_PIN_0, LL_GPIO_OUTPUT_PUSHPULL); - // LL_GPIO_SetPinPull(GPIOC, LL_GPIO_PIN_0, LL_GPIO_PULL_NO); furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - //furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b4, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); diff --git a/flip_tdi/helpers/ftdi_gpio.h b/flip_tdi/helpers/ftdi_gpio.h index 1982c90..08242ba 100644 --- a/flip_tdi/helpers/ftdi_gpio.h +++ b/flip_tdi/helpers/ftdi_gpio.h @@ -5,122 +5,102 @@ void ftdi_gpio_set_direction(uint8_t gpio_mask); void ftdi_gpio_init(uint8_t gpio_mask); void ftdi_gpio_deinit(); -static inline uint8_t ftdi_gpio_get_b0(uint8_t state) { // gpio_ext_pa7 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b0(void) { // gpio_ext_pa7 return (GPIOA->IDR & LL_GPIO_PIN_7) ? 0b00000001 : 0; } -static inline uint8_t ftdi_gpio_get_b1(uint8_t state) { // gpio_ext_pa6 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b1(void) { // gpio_ext_pa6 return (GPIOA->IDR & LL_GPIO_PIN_6) ? 0b00000010 : 0; } -static inline uint8_t ftdi_gpio_get_b2(uint8_t state) { // gpio_ext_pa4 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b2(void) { // gpio_ext_pa4 return (GPIOA->IDR & LL_GPIO_PIN_4) ? 0b00000100 : 0; } -static inline uint8_t ftdi_gpio_get_b3(uint8_t state) { // gpio_ext_pb3 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b3(void) { // gpio_ext_pb3 return (GPIOB->IDR & LL_GPIO_PIN_3) ? 0b00001000 : 0; } -static inline uint8_t ftdi_gpio_get_b4(uint8_t state) { // gpio_ext_pb2 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b4(void) { // gpio_ext_pb2 return (GPIOB->IDR & LL_GPIO_PIN_2) ? 0b00010000 : 0; } -static inline uint8_t ftdi_gpio_get_b5(uint8_t state) { // gpio_ext_pc3 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b5(void) { // gpio_ext_pc3 return (GPIOC->IDR & LL_GPIO_PIN_3) ? 0b00100000 : 0; } -static inline uint8_t ftdi_gpio_get_b6(uint8_t state) { // gpio_ext_pc1 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b6(void) { // gpio_ext_pc1 return (GPIOC->IDR & LL_GPIO_PIN_1) ? 0b01000000 : 0; } -static inline uint8_t ftdi_gpio_get_b7(uint8_t state) { // gpio_ext_pc0 - UNUSED(state); +static inline uint8_t ftdi_gpio_get_b7(void) { // gpio_ext_pc0 return (GPIOC->IDR & LL_GPIO_PIN_0) ? 0b10000000 : 0; } -static inline uint8_t ftdi_gpio_set_b0(uint8_t state) { // gpio_ext_pa7 +static inline void ftdi_gpio_set_noop(uint8_t state) { // gpio_ext_pa7 + UNUSED(state); +} + +static inline void ftdi_gpio_set_b0(uint8_t state) { // gpio_ext_pa7 if(state) { GPIOA->BSRR = LL_GPIO_PIN_7; - return 0b00000001; } else { GPIOA->BRR = LL_GPIO_PIN_7; - return 0; } } -static inline uint8_t ftdi_gpio_set_b1(uint8_t state) { // gpio_ext_pa6 +static inline void ftdi_gpio_set_b1(uint8_t state) { // gpio_ext_pa6 if(state) { GPIOA->BSRR = LL_GPIO_PIN_6; - return 0b00000010; } else { GPIOA->BRR = LL_GPIO_PIN_6; - return 0; } } -static inline uint8_t ftdi_gpio_set_b2(uint8_t state) { // gpio_ext_pa4 +static inline void ftdi_gpio_set_b2(uint8_t state) { // gpio_ext_pa4 if(state) { GPIOA->BSRR = LL_GPIO_PIN_4; - return 0b00000100; } else { GPIOA->BRR = LL_GPIO_PIN_4; - return 0; } } -static inline uint8_t ftdi_gpio_set_b3(uint8_t state) { // gpio_ext_pb3 +static inline void ftdi_gpio_set_b3(uint8_t state) { // gpio_ext_pb3 if(state) { GPIOB->BSRR = LL_GPIO_PIN_3; - return 0b00001000; } else { GPIOB->BRR = LL_GPIO_PIN_3; - return 0; } } -static inline uint8_t ftdi_gpio_set_b4(uint8_t state) { // gpio_ext_pb2 +static inline void ftdi_gpio_set_b4(uint8_t state) { // gpio_ext_pb2 if(state) { GPIOB->BSRR = LL_GPIO_PIN_2; - return 0b00010000; } else { GPIOB->BRR = LL_GPIO_PIN_2; - return 0; } } -static inline uint8_t ftdi_gpio_set_b5(uint8_t state) { // gpio_ext_pc3 +static inline void ftdi_gpio_set_b5(uint8_t state) { // gpio_ext_pc3 if(state) { GPIOC->BSRR = LL_GPIO_PIN_3; - return 0b00100000; } else { GPIOC->BRR = LL_GPIO_PIN_3; - return 0; } } -static inline uint8_t ftdi_gpio_set_b6(uint8_t state) { // gpio_ext_pc1 +static inline void ftdi_gpio_set_b6(uint8_t state) { // gpio_ext_pc1 if(state) { GPIOC->BSRR = LL_GPIO_PIN_1; - return 0b01000000; } else { GPIOC->BRR = LL_GPIO_PIN_1; - return 0; } } -static inline uint8_t ftdi_gpio_set_b7(uint8_t state) { // gpio_ext_pc0 +static inline void ftdi_gpio_set_b7(uint8_t state) { // gpio_ext_pc0 if(state) { GPIOC->BSRR = LL_GPIO_PIN_0; - return 0b10000000; } else { GPIOC->BRR = LL_GPIO_PIN_0; - return 0; } } From 03804b006f49a2bbf3091a7f3d86c0b2e9016a92 Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 18 Jul 2024 12:50:17 +0400 Subject: [PATCH 11/19] FlipTDI: MPSSE Mode gpio set/get func --- flip_tdi/helpers/ftdi.c | 11 ++- flip_tdi/helpers/ftdi_bitbang.c | 56 ++++++++++----- flip_tdi/helpers/ftdi_bitbang.h | 3 +- flip_tdi/helpers/ftdi_mpsse.c | 120 ++++++++++++++++++++++++++++++-- flip_tdi/helpers/ftdi_mpsse.h | 5 ++ 5 files changed, 167 insertions(+), 28 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 898b214..0a7ac73 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -59,6 +59,7 @@ Ftdi* ftdi_alloc(void) { ftdi->ftdi_uart = ftdi_uart_alloc(ftdi); ftdi->ftdi_bitbang = ftdi_bitbang_alloc(ftdi); ftdi->ftdi_latency_timer = ftdi_latency_timer_alloc(); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_latency_timer, ftdi); return ftdi; } @@ -321,6 +322,7 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { memcpy(&ftdi->bit_mode, &bit_mode, sizeof(ftdi->bit_mode)); // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_latency_timer, ftdi); if(bit_mode == 0x00) { // Reset ftdi_uart_enable(ftdi->ftdi_uart, true); // UART mode @@ -334,14 +336,11 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { ftdi->status.DTR = 1; } - if(ftdi->bit_mode.BITBANG) { - ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, ftdi->bit_mode_mask); - ftdi_bitbang_enable(ftdi->ftdi_bitbang, true, true); - } else if(ftdi->bit_mode.SYNCBB) { + if(ftdi->bit_mode.BITBANG || ftdi->bit_mode.SYNCBB || ftdi->bit_mode.MPSSE) { ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, ftdi->bit_mode_mask); - ftdi_bitbang_enable(ftdi->ftdi_bitbang, true, false); + ftdi_bitbang_enable(ftdi->ftdi_bitbang, ftdi->bit_mode); } else { - ftdi_bitbang_enable(ftdi->ftdi_bitbang, false, true); + ftdi_bitbang_enable(ftdi->ftdi_bitbang, ftdi->bit_mode); } } diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c index e395a24..f39dd32 100644 --- a/flip_tdi/helpers/ftdi_bitbang.c +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -7,17 +7,27 @@ #include #include #include "ftdi_gpio.h" +#include "ftdi_mpsse.h" #define TAG "FTDI_BITBANG" +typedef enum { + FtdiBitbangModeOff = (0UL), + FtdiBitbangModeBitbang = (1UL), + FtdiBitbangModeSyncbb = (2UL), + FtdiBitbangModeMpsse = (3UL), + FtdiBitbangModeReserved = (4UL), +} FtdiBitbangMode; + typedef void (*FtdiBitbangGpioIO)(uint8_t state); struct FtdiBitbang { FuriThread* worker_thread; Ftdi* ftdi; + FtdiMpsse* ftdi_mpsse; uint32_t speed; - bool enable; + FtdiBitbangMode mode; + uint8_t gpio_mask; - bool async; FtdiBitbangGpioIO gpio_o[8]; uint8_t gpio_data; }; @@ -127,18 +137,22 @@ static int32_t ftdi_bitbang_worker(void* context) { furi_check((events & FuriFlagError) == 0); if(events & WorkerEventTimerUpdate) { - size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); - if(length > 0) { - ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); - if(!ftdi_bitbang->async) { + if(ftdi_bitbang->mode == FtdiBitbangModeMpsse) { + ftdi_mpsse_state_machine(ftdi_bitbang->ftdi_mpsse); + } else { + size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + if(length > 0) { + ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); + if(ftdi_bitbang->mode == FtdiBitbangModeSyncbb) { + ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); + ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); + } + } + if(ftdi_bitbang->mode == FtdiBitbangModeBitbang) { ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); } } - if(ftdi_bitbang->enable && ftdi_bitbang->async) { - ftdi_bitbang->gpio_data = ftdi_bitbang_gpio_get(); - ftdi_set_tx_buf(ftdi_bitbang->ftdi, &ftdi_bitbang->gpio_data, 1); - } continue; } @@ -157,7 +171,9 @@ uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang) { FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { FtdiBitbang* ftdi_bitbang = malloc(sizeof(FtdiBitbang)); ftdi_bitbang->ftdi = ftdi; - ftdi_bitbang->enable = false; + ftdi_bitbang->ftdi_mpsse = ftdi_mpsse_alloc(ftdi); + ftdi_bitbang->mode = FtdiBitbangModeOff; + ftdi_bitbang->speed = 10000; ftdi_bitbang->gpio_mask = 0; ftdi_bitbang_gpio_init(ftdi_bitbang); ftdi_bitbang->worker_thread = @@ -170,7 +186,8 @@ FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi) { void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { if(!ftdi_bitbang) return; - ftdi_bitbang->enable = false; + ftdi_bitbang->mode = FtdiBitbangModeOff; + ftdi_mpsse_free(ftdi_bitbang->ftdi_mpsse); furi_thread_flags_set(ftdi_bitbang->worker_thread, WorkerEventStop); furi_thread_join(ftdi_bitbang->worker_thread); furi_thread_free(ftdi_bitbang->worker_thread); @@ -186,11 +203,18 @@ void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { ftdi_bitbang_gpio_set_direction(ftdi_bitbang); } -void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async) { - ftdi_bitbang->enable = enable; - ftdi_bitbang->async = async; +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, FtdiBitMode mode) { + if(mode.BITBANG) { + ftdi_bitbang->mode = FtdiBitbangModeBitbang; + } else if(mode.SYNCBB) { + ftdi_bitbang->mode = FtdiBitbangModeSyncbb; + } else if(mode.MPSSE) { + ftdi_bitbang->mode = FtdiBitbangModeMpsse; + } else { + ftdi_bitbang->mode = FtdiBitbangModeOff; + } - if(enable) { + if(ftdi_bitbang->mode != FtdiBitbangModeOff) { LL_TIM_SetCounter(TIM17, 0); LL_TIM_EnableCounter(TIM17); } else { diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h index b5d2bcf..7b4b17c 100644 --- a/flip_tdi/helpers/ftdi_bitbang.h +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -3,10 +3,9 @@ typedef struct FtdiBitbang FtdiBitbang; - FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi); void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); -void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, bool enable, bool async); +void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, FtdiBitMode mode); void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); uint8_t ftdi_bitbang_get_gpio(FtdiBitbang* ftdi_bitbang); \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index 55eb4bb..f23f9da 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -1,9 +1,13 @@ #include "ftdi_mpsse.h" #include "furi.h" #include +#include "ftdi_gpio.h" +#include "ftdi_latency_timer.h" #define TAG "FTDI_MPSSE" +typedef void (*FtdiMpsseGpioO)(uint8_t state); + struct FtdiMpsse { Ftdi* ftdi; uint8_t gpio_state; @@ -13,8 +17,102 @@ struct FtdiMpsse { bool is_div5; bool is_clk3phase; bool is_adaptive; + + FtdiMpsseGpioO gpio_o[8]; + uint8_t gpio_mask_old; + + FtdiCallbackLatencyTimer callback_latency_timer; + void* context_latency_timer; }; +void ftdi_mpsse_gpio_set_callback( + FtdiMpsse* ftdi_mpsse, + FtdiCallbackLatencyTimer callback, + void* context) { + ftdi_mpsse->callback_latency_timer = callback; + ftdi_mpsse->context_latency_timer = context; +} + +void ftdi_mpsse_gpio_set_direction(FtdiMpsse* ftdi_mpsse) { + ftdi_gpio_set_direction(ftdi_mpsse->gpio_mask); + if(ftdi_mpsse->gpio_mask & 0b00000001) { + ftdi_mpsse->gpio_o[0] = ftdi_gpio_set_b0; + } else { + ftdi_mpsse->gpio_o[0] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00000010) { + ftdi_mpsse->gpio_o[1] = ftdi_gpio_set_b1; + } else { + ftdi_mpsse->gpio_o[1] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00000100) { + ftdi_mpsse->gpio_o[2] = ftdi_gpio_set_b2; + } else { + ftdi_mpsse->gpio_o[2] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00001000) { + ftdi_mpsse->gpio_o[3] = ftdi_gpio_set_b3; + } else { + ftdi_mpsse->gpio_o[3] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00010000) { + ftdi_mpsse->gpio_o[4] = ftdi_gpio_set_b4; + } else { + ftdi_mpsse->gpio_o[4] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b00100000) { + ftdi_mpsse->gpio_o[5] = ftdi_gpio_set_b5; + } else { + ftdi_mpsse->gpio_o[5] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b01000000) { + ftdi_mpsse->gpio_o[6] = ftdi_gpio_set_b6; + } else { + ftdi_mpsse->gpio_o[6] = ftdi_gpio_set_noop; + } + + if(ftdi_mpsse->gpio_mask & 0b10000000) { + ftdi_mpsse->gpio_o[7] = ftdi_gpio_set_b7; + } else { + ftdi_mpsse->gpio_o[7] = ftdi_gpio_set_noop; + } +} + +void ftdi_mpsse_gpio_init(FtdiMpsse* ftdi_mpsse) { + ftdi_gpio_init(ftdi_mpsse->gpio_mask); + ftdi_mpsse_gpio_set_direction(ftdi_mpsse); +} + +static inline void ftdi_mpsse_gpio_set(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->gpio_o[0](ftdi_mpsse->gpio_state & 0b00000001); + ftdi_mpsse->gpio_o[1](ftdi_mpsse->gpio_state & 0b00000010); + ftdi_mpsse->gpio_o[2](ftdi_mpsse->gpio_state & 0b00000100); + ftdi_mpsse->gpio_o[3](ftdi_mpsse->gpio_state & 0b00001000); + ftdi_mpsse->gpio_o[4](ftdi_mpsse->gpio_state & 0b00010000); + ftdi_mpsse->gpio_o[5](ftdi_mpsse->gpio_state & 0b00100000); + ftdi_mpsse->gpio_o[6](ftdi_mpsse->gpio_state & 0b01000000); + ftdi_mpsse->gpio_o[7](ftdi_mpsse->gpio_state & 0b10000000); +} + +static inline uint8_t ftdi_mpsse_gpio_get(void) { + uint8_t gpio_data = 0; + gpio_data |= ftdi_gpio_get_b0(); + gpio_data |= ftdi_gpio_get_b1(); + gpio_data |= ftdi_gpio_get_b2(); + gpio_data |= ftdi_gpio_get_b3(); + gpio_data |= ftdi_gpio_get_b4(); + gpio_data |= ftdi_gpio_get_b5(); + gpio_data |= ftdi_gpio_get_b6(); + gpio_data |= ftdi_gpio_get_b7(); + return gpio_data; +} + FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { FtdiMpsse* ftdi_mpsse = malloc(sizeof(FtdiMpsse)); ftdi_mpsse->ftdi = ftdi; @@ -25,11 +123,15 @@ FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { ftdi_mpsse->is_div5 = false; ftdi_mpsse->is_clk3phase = false; ftdi_mpsse->is_adaptive = false; + + ftdi_mpsse_gpio_init(ftdi_mpsse); + return ftdi_mpsse; } void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { if(!ftdi_mpsse) return; + ftdi_gpio_deinit(); free(ftdi_mpsse); ftdi_mpsse = NULL; } @@ -67,10 +169,16 @@ static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { uint8_t data = ftdi_mpsse_get_data_stream(ftdi_mpsse); + uint8_t gpio_state_io = 0xFF; switch(data) { case FtdiMpsseCommandsSetBitsLow: // 0x80 Change LSB GPIO output */ ftdi_mpsse->gpio_state = ftdi_mpsse_get_data_stream(ftdi_mpsse); - ftdi_mpsse->gpio_state = ftdi_mpsse_get_data_stream(ftdi_mpsse); + ftdi_mpsse->gpio_mask = ftdi_mpsse_get_data_stream(ftdi_mpsse); + if(ftdi_mpsse->gpio_mask_old != ftdi_mpsse->gpio_mask) { + ftdi_mpsse_gpio_set_direction(ftdi_mpsse); + ftdi_mpsse->gpio_mask_old = ftdi_mpsse->gpio_mask; + } + ftdi_mpsse_gpio_set(ftdi_mpsse); //Write to GPIO break; case FtdiMpsseCommandsSetBitsHigh: // 0x82 Change MSB GPIO output */ @@ -82,16 +190,20 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { //Read GPIO //add to buffer //read Gpio - ftdi_mpssse_set_data_stream(ftdi_mpsse, &ftdi_mpsse->gpio_state, 1); + gpio_state_io = ftdi_mpsse_gpio_get(); + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); break; case FtdiMpsseCommandsGetBitsHigh: // 0x83 Get MSB GPIO output */ //Todo not supported //add to buffer FF - uint8_t gpio = 0xFF; - ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio, 1); + gpio_state_io = 0xFF; + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); break; case FtdiMpsseCommandsSendImmediate: // 0x87 Send immediate */ //tx data to host add callback + if(ftdi_mpsse->callback_latency_timer) { + ftdi_mpsse->callback_latency_timer(ftdi_mpsse->context_latency_timer); + } break; case FtdiMpsseCommandsWriteBytesPveMsb: // 0x10 Write bytes with positive edge clock, MSB first */ //spi mode 1,3 diff --git a/flip_tdi/helpers/ftdi_mpsse.h b/flip_tdi/helpers/ftdi_mpsse.h index 094e702..ba844ae 100644 --- a/flip_tdi/helpers/ftdi_mpsse.h +++ b/flip_tdi/helpers/ftdi_mpsse.h @@ -3,4 +3,9 @@ typedef struct FtdiMpsse FtdiMpsse; +typedef void (*FtdiCallbackLatencyTimer)(void* context); +void ftdi_mpsse_gpio_set_callback(FtdiMpsse* ftdi_mpsse, FtdiCallbackLatencyTimer callback, void* context); +FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi); +void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse); +void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse); From 19768b284763cb4b53257795a833c07d9ca4e2a4 Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 18 Jul 2024 13:55:22 +0400 Subject: [PATCH 12/19] FlipTDI: MPSSE mode add FtdiMpsseCommandsSendImmediate --- flip_tdi/helpers/ftdi.c | 36 +++++++++++++++++++-------- flip_tdi/helpers/ftdi.h | 4 +-- flip_tdi/helpers/ftdi_bitbang.c | 5 +++- flip_tdi/helpers/ftdi_bitbang.h | 2 ++ flip_tdi/helpers/ftdi_latency_timer.h | 1 + flip_tdi/helpers/ftdi_mpsse.c | 19 ++++++-------- flip_tdi/helpers/ftdi_mpsse.h | 4 +-- flip_tdi/helpers/ftdi_usb.c | 20 +++++++-------- 8 files changed, 55 insertions(+), 36 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 0a7ac73..29f891e 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -25,8 +25,8 @@ struct Ftdi { FtdiBitbang* ftdi_bitbang; FtdiLatencyTimer* ftdi_latency_timer; - FtdiCallbackLatencyTimer callback_latency_timer; - void* context_latency_timer; + FtdiCallbackTxImmediate callback_tx_immediate; + void* context_tx_immediate; }; static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { @@ -35,10 +35,10 @@ static bool ftdi_check_interface(Ftdi* ftdi, uint16_t index) { return ((interface) == FTDI_INTERFACE_A) || ((interface) == FTDI_DRIVER_INTERFACE_A); } -static void ftdi_callback_latency_timer(void* context) { +static void ftdi_callback_tx_immediate(void* context) { Ftdi* ftdi = context; - if(ftdi->callback_latency_timer) { - ftdi->callback_latency_timer(ftdi->context_latency_timer); + if(ftdi->callback_tx_immediate) { + ftdi->callback_tx_immediate(ftdi->context_tx_immediate); } } @@ -60,7 +60,7 @@ Ftdi* ftdi_alloc(void) { ftdi->ftdi_bitbang = ftdi_bitbang_alloc(ftdi); ftdi->ftdi_latency_timer = ftdi_latency_timer_alloc(); - ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_latency_timer, ftdi); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); return ftdi; } @@ -73,9 +73,23 @@ void ftdi_free(Ftdi* ftdi) { free(ftdi); } -void ftdi_set_callback_latency_timer(Ftdi* ftdi, FtdiCallbackLatencyTimer callback, void* context) { - ftdi->callback_latency_timer = callback; - ftdi->context_latency_timer = context; +void ftdi_switch_callback_tx_immediate(Ftdi* ftdi) { + FtdiMpsse* mpsse_handle = ftdi_bitbang_get_mpsse_handle(ftdi->ftdi_bitbang); + if(ftdi->bit_mode.MPSSE) { + ftdi_mpsse_gpio_set_callback(mpsse_handle, ftdi_callback_tx_immediate, ftdi); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, NULL, NULL); + ftdi_latency_timer_enable(ftdi->ftdi_latency_timer, false); + } else { + ftdi_mpsse_gpio_set_callback(mpsse_handle, NULL, NULL); + ftdi_latency_timer_enable(ftdi->ftdi_latency_timer, true); + ftdi_latency_timer_set_callback( + ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); + } +} + +void ftdi_set_callback_tx_immediate(Ftdi* ftdi, FtdiCallbackTxImmediate callback, void* context) { + ftdi->callback_tx_immediate = callback; + ftdi->context_tx_immediate = context; } void ftdi_reset_purge_rx(Ftdi* ftdi) { @@ -322,7 +336,7 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { memcpy(&ftdi->bit_mode, &bit_mode, sizeof(ftdi->bit_mode)); // ftdi_bitbang_set_gpio(ftdi->ftdi_bitbang, 0); - ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_latency_timer, ftdi); + ftdi_latency_timer_set_callback(ftdi->ftdi_latency_timer, ftdi_callback_tx_immediate, ftdi); if(bit_mode == 0x00) { // Reset ftdi_uart_enable(ftdi->ftdi_uart, true); // UART mode @@ -342,6 +356,8 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { } else { ftdi_bitbang_enable(ftdi->ftdi_bitbang, ftdi->bit_mode); } + + ftdi_switch_callback_tx_immediate(ftdi); } void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index) { diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index 487c5ef..0bf4240 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -3,11 +3,11 @@ typedef struct Ftdi Ftdi; -typedef void (*FtdiCallbackLatencyTimer)(void* context); +typedef void (*FtdiCallbackTxImmediate)(void* context); Ftdi* ftdi_alloc(void); void ftdi_free(Ftdi* ftdi); -void ftdi_set_callback_latency_timer(Ftdi* ftdi, FtdiCallbackLatencyTimer callback, void* context); +void ftdi_set_callback_tx_immediate(Ftdi* ftdi, FtdiCallbackTxImmediate callback, void* context); void ftdi_reset_purge_rx(Ftdi* ftdi); void ftdi_reset_purge_tx(Ftdi* ftdi); void ftdi_reset_sio(Ftdi* ftdi); diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c index f39dd32..b56bdb3 100644 --- a/flip_tdi/helpers/ftdi_bitbang.c +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -7,7 +7,6 @@ #include #include #include "ftdi_gpio.h" -#include "ftdi_mpsse.h" #define TAG "FTDI_BITBANG" @@ -198,6 +197,10 @@ void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang) { ftdi_bitbang = NULL; } +FtdiMpsse* ftdi_bitbang_get_mpsse_handle(FtdiBitbang* ftdi_bitbang) { + return ftdi_bitbang->ftdi_mpsse; +} + void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask) { ftdi_bitbang->gpio_mask = gpio_mask; ftdi_bitbang_gpio_set_direction(ftdi_bitbang); diff --git a/flip_tdi/helpers/ftdi_bitbang.h b/flip_tdi/helpers/ftdi_bitbang.h index 7b4b17c..5996c17 100644 --- a/flip_tdi/helpers/ftdi_bitbang.h +++ b/flip_tdi/helpers/ftdi_bitbang.h @@ -1,10 +1,12 @@ #pragma once #include "ftdi.h" +#include "ftdi_mpsse.h" typedef struct FtdiBitbang FtdiBitbang; FtdiBitbang* ftdi_bitbang_alloc(Ftdi* ftdi); void ftdi_bitbang_free(FtdiBitbang* ftdi_bitbang); +FtdiMpsse* ftdi_bitbang_get_mpsse_handle(FtdiBitbang* ftdi_bitbang); void ftdi_bitbang_set_gpio(FtdiBitbang* ftdi_bitbang, uint8_t gpio_mask); void ftdi_bitbang_enable(FtdiBitbang* ftdi_bitbang, FtdiBitMode mode); void ftdi_bitbang_set_speed(FtdiBitbang* ftdi_bitbang, uint32_t speed); diff --git a/flip_tdi/helpers/ftdi_latency_timer.h b/flip_tdi/helpers/ftdi_latency_timer.h index a179e5e..6daba3c 100644 --- a/flip_tdi/helpers/ftdi_latency_timer.h +++ b/flip_tdi/helpers/ftdi_latency_timer.h @@ -11,6 +11,7 @@ void ftdi_latency_timer_set_callback( FtdiLatencyTimer* ftdi_latency_timer, FtdiLatencyTimerCallbackUp callback, void* context); +void ftdi_latency_timer_enable(FtdiLatencyTimer* ftdi_latency_timer, bool enable); void ftdi_latency_timer_reset(FtdiLatencyTimer* ftdi_latency_timer); void ftdi_latency_timer_set_speed(FtdiLatencyTimer* ftdi_latency_timer, uint8_t speed); uint8_t ftdi_latency_timer_get_speed(FtdiLatencyTimer* ftdi_latency_timer); diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index f23f9da..9a6e98a 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -21,16 +21,16 @@ struct FtdiMpsse { FtdiMpsseGpioO gpio_o[8]; uint8_t gpio_mask_old; - FtdiCallbackLatencyTimer callback_latency_timer; - void* context_latency_timer; + FtdiMpsseCallbackImmediate callback_immediate; + void* context_immediate; }; void ftdi_mpsse_gpio_set_callback( FtdiMpsse* ftdi_mpsse, - FtdiCallbackLatencyTimer callback, + FtdiMpsseCallbackImmediate callback, void* context) { - ftdi_mpsse->callback_latency_timer = callback; - ftdi_mpsse->context_latency_timer = context; + ftdi_mpsse->callback_immediate = callback; + ftdi_mpsse->context_immediate = context; } void ftdi_mpsse_gpio_set_direction(FtdiMpsse* ftdi_mpsse) { @@ -188,21 +188,18 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { break; case FtdiMpsseCommandsGetBitsLow: // 0x81 Get LSB GPIO output */ //Read GPIO - //add to buffer - //read Gpio gpio_state_io = ftdi_mpsse_gpio_get(); ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); break; case FtdiMpsseCommandsGetBitsHigh: // 0x83 Get MSB GPIO output */ //Todo not supported - //add to buffer FF gpio_state_io = 0xFF; ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); break; case FtdiMpsseCommandsSendImmediate: // 0x87 Send immediate */ - //tx data to host add callback - if(ftdi_mpsse->callback_latency_timer) { - ftdi_mpsse->callback_latency_timer(ftdi_mpsse->context_latency_timer); + //tx data to host callback + if(ftdi_mpsse->callback_immediate) { + ftdi_mpsse->callback_immediate(ftdi_mpsse->context_immediate); } break; case FtdiMpsseCommandsWriteBytesPveMsb: // 0x10 Write bytes with positive edge clock, MSB first */ diff --git a/flip_tdi/helpers/ftdi_mpsse.h b/flip_tdi/helpers/ftdi_mpsse.h index ba844ae..4dc4a35 100644 --- a/flip_tdi/helpers/ftdi_mpsse.h +++ b/flip_tdi/helpers/ftdi_mpsse.h @@ -3,8 +3,8 @@ typedef struct FtdiMpsse FtdiMpsse; -typedef void (*FtdiCallbackLatencyTimer)(void* context); -void ftdi_mpsse_gpio_set_callback(FtdiMpsse* ftdi_mpsse, FtdiCallbackLatencyTimer callback, void* context); +typedef void (*FtdiMpsseCallbackImmediate)(void* context); +void ftdi_mpsse_gpio_set_callback(FtdiMpsse* ftdi_mpsse, FtdiMpsseCallbackImmediate callback, void* context); FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi); void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse); diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index 3d34e0c..f9c6bf3 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -44,12 +44,12 @@ typedef enum { EventSetBaudrate = (1 << 12), EventSetData = (1 << 13), EventSetFlowCtrl = (1 << 14), - EventLatencyTimerUp = (1 << 15), + EventTxImmediate = (1 << 15), EventAll = EventExit | EventReset | EventRx | EventTx | EventTxComplete | EventResetSio | EventResetPurgeRx | EventResetPurgeTx | EventSetBitmode | EventSetLatencyTimer | EventSetEventChar | EventSetErrorChar | EventSetBaudrate | EventSetData | - EventSetFlowCtrl | EventLatencyTimerUp, + EventSetFlowCtrl | EventTxImmediate, } FtdiEvent; struct FtdiUsb { @@ -63,7 +63,7 @@ struct FtdiUsb { uint16_t data_recvest_len; bool tx_complete; - bool latency_timer_up; + bool tx_immediate; }; static int32_t ftdi_thread_worker(void* context) { @@ -110,15 +110,15 @@ static int32_t ftdi_thread_worker(void* context) { } if(flags & EventTxComplete) { ftdi_usb->tx_complete = true; - if((ftdi_usb->latency_timer_up) || + if((ftdi_usb->tx_immediate) || (ftdi_available_tx_buf(ftdi_usb->ftdi) >= FTDI_USB_TX_MAX_SIZE)) { ftdi_reset_latency_timer(ftdi_usb->ftdi); flags |= EventTx; } } - if(flags & EventLatencyTimerUp) { - ftdi_usb->latency_timer_up = true; + if(flags & EventTxImmediate) { + ftdi_usb->tx_immediate= true; if(ftdi_usb->tx_complete) { flags |= EventTx; } @@ -126,7 +126,7 @@ static int32_t ftdi_thread_worker(void* context) { if(flags & EventTx) { ftdi_usb->tx_complete = false; - ftdi_usb->latency_timer_up = false; + ftdi_usb->tx_immediate = false; tx_data.status = status[0]; len_data = ftdi_available_tx_buf(ftdi_usb->ftdi); @@ -165,9 +165,9 @@ static int32_t ftdi_thread_worker(void* context) { // where if_ctx isn't passed static FtdiUsb* ftdi_cur = NULL; -static void ftdi_usb_callback_latency_timer(void* context) { +static void ftdi_usb_callback_tx_immediate(void* context) { FtdiUsb* ftdi_usb = context; - furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventLatencyTimerUp); + furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventTxImmediate); } static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { @@ -187,7 +187,7 @@ static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx furi_thread_set_callback(ftdi_usb->thread, ftdi_thread_worker); ftdi_usb->ftdi = ftdi_alloc(); - ftdi_set_callback_latency_timer(ftdi_usb->ftdi, ftdi_usb_callback_latency_timer, ftdi_usb); + ftdi_set_callback_tx_immediate(ftdi_usb->ftdi, ftdi_usb_callback_tx_immediate, ftdi_usb); // furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); // furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); From 013a4de67dc977085e5ca9056b144b6cd0e3c94b Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 18 Jul 2024 16:05:46 +0400 Subject: [PATCH 13/19] FlipTDI: MPSSE add timeout --- flip_tdi/helpers/ftdi.c | 6 +++--- flip_tdi/helpers/ftdi.h | 2 +- flip_tdi/helpers/ftdi_bitbang.c | 3 +-- flip_tdi/helpers/ftdi_mpsse.c | 25 +++++++++++++++++++------ flip_tdi/helpers/ftdi_uart.c | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index 29f891e..f44a0c7 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -136,8 +136,8 @@ uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size) { return furi_stream_buffer_send(ftdi->stream_rx, data, size, 0); } -uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size) { - return furi_stream_buffer_receive(ftdi->stream_rx, data, size, 0); +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size, uint32_t timeout) { + return furi_stream_buffer_receive(ftdi->stream_rx, data, size, timeout); } uint32_t ftdi_available_rx_buf(Ftdi* ftdi) { @@ -147,7 +147,7 @@ uint32_t ftdi_available_rx_buf(Ftdi* ftdi) { void ftdi_loopback(Ftdi* ftdi) { //todo fix size uint8_t data[128]; - uint32_t len = ftdi_get_rx_buf(ftdi, data, 128); + uint32_t len = ftdi_get_rx_buf(ftdi, data, 128, 0); ftdi_set_tx_buf(ftdi, data, len); } diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index 0bf4240..188b4de 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -15,7 +15,7 @@ uint32_t ftdi_set_tx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); uint32_t ftdi_get_tx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size); uint32_t ftdi_available_tx_buf(Ftdi* ftdi); uint32_t ftdi_set_rx_buf(Ftdi* ftdi, const uint8_t* data, uint32_t size); -uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size); +uint32_t ftdi_get_rx_buf(Ftdi* ftdi, uint8_t* data, uint32_t size, uint32_t timeout); uint32_t ftdi_available_rx_buf(Ftdi* ftdi); void ftdi_loopback(Ftdi* ftdi); void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index); diff --git a/flip_tdi/helpers/ftdi_bitbang.c b/flip_tdi/helpers/ftdi_bitbang.c index b56bdb3..99e0dc8 100644 --- a/flip_tdi/helpers/ftdi_bitbang.c +++ b/flip_tdi/helpers/ftdi_bitbang.c @@ -134,12 +134,11 @@ static int32_t ftdi_bitbang_worker(void* context) { uint32_t events = furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever); furi_check((events & FuriFlagError) == 0); - if(events & WorkerEventTimerUpdate) { if(ftdi_bitbang->mode == FtdiBitbangModeMpsse) { ftdi_mpsse_state_machine(ftdi_bitbang->ftdi_mpsse); } else { - size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1); + size_t length = ftdi_get_rx_buf(ftdi_bitbang->ftdi, buffer, 1, 0); if(length > 0) { ftdi_bitbang_gpio_set(ftdi_bitbang, buffer[0]); if(ftdi_bitbang->mode == FtdiBitbangModeSyncbb) { diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index 9a6e98a..30606d4 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -8,6 +8,8 @@ typedef void (*FtdiMpsseGpioO)(uint8_t state); +#define FTDI_MPSSE_TIMEOUT 5000 + struct FtdiMpsse { Ftdi* ftdi; uint8_t gpio_state; @@ -138,8 +140,8 @@ void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { uint8_t ftdi_mpsse_get_data_stream(FtdiMpsse* ftdi_mpsse) { uint8_t data = 0; - //Todo add timeout - ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1); + ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1, FTDI_MPSSE_TIMEOUT); + //FURI_LOG_RAW_I("0x%02X ", data); return data; } @@ -167,6 +169,11 @@ static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { } } +static inline void ftdi_mpsse_immediate(FtdiMpsse* ftdi_mpsse) { + if(ftdi_mpsse->callback_immediate) { + ftdi_mpsse->callback_immediate(ftdi_mpsse->context_immediate); + } +} void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { uint8_t data = ftdi_mpsse_get_data_stream(ftdi_mpsse); uint8_t gpio_state_io = 0xFF; @@ -198,9 +205,7 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { break; case FtdiMpsseCommandsSendImmediate: // 0x87 Send immediate */ //tx data to host callback - if(ftdi_mpsse->callback_immediate) { - ftdi_mpsse->callback_immediate(ftdi_mpsse->context_immediate); - } + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsWriteBytesPveMsb: // 0x10 Write bytes with positive edge clock, MSB first */ //spi mode 1,3 @@ -374,24 +379,30 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { break; case FtdiMpsseCommandsLoopbackStart: // 0x84 Enable loopback */ ftdi_mpsse->is_loopback = true; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsLoopbackEnd: // 0x85 Disable loopback */ ftdi_mpsse->is_loopback = false; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsSetTckDivisor: // 0x86 Set clock */ - + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsDisDiv5: // 0x8a Disable divide by 5 */ ftdi_mpsse->is_div5 = false; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsEnDiv5: // 0x8b Enable divide by 5 */ ftdi_mpsse->is_div5 = true; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsEnableClk3Phase: // 0x8c Enable 3-phase data clocking (I2C) */ ftdi_mpsse->is_clk3phase = true; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsDisableClk3Phase: // 0x8d Disable 3-phase data clocking */ ftdi_mpsse->is_clk3phase = false; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsClkBitsNoData: // 0x8e Allows JTAG clock to be output w/o data */ //not supported @@ -407,9 +418,11 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { break; case FtdiMpsseCommandsEnableClkAdaptive: // 0x96 Enable JTAG adaptive clock for ARM */ ftdi_mpsse->is_adaptive = true; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsDisableClkAdaptive: // 0x97 Disable JTAG adaptive clock */ ftdi_mpsse->is_adaptive = false; + ftdi_mpsse_immediate(ftdi_mpsse); break; case FtdiMpsseCommandsClkCountWaitOnHigh: // 0x9c Clock byte cycles until GPIOL1 is high */ //not supported diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c index bd85bb7..80d3df8 100644 --- a/flip_tdi/helpers/ftdi_uart.c +++ b/flip_tdi/helpers/ftdi_uart.c @@ -87,7 +87,7 @@ static int32_t ftdi_uart_echo_worker(void* context) { if(events & WorkerEventTXDataDmaEnd) { size_t length = 0; length = ftdi_get_rx_buf( - ftdi_uart->ftdi, ftdi_uart->buffer_tx_ptr, FTDI_UART_MAX_TXRX_SIZE); + ftdi_uart->ftdi, ftdi_uart->buffer_tx_ptr, FTDI_UART_MAX_TXRX_SIZE, 0); if(length > 0) { ftdi_uart_tx_dma(ftdi_uart, ftdi_uart->buffer_tx_ptr, length); } else { From 07bb813b4bc18431a0d802d7de9be24f39ac8070 Mon Sep 17 00:00:00 2001 From: SkorP Date: Thu, 18 Jul 2024 18:21:00 +0400 Subject: [PATCH 14/19] FlipTDI: MPSSE add support SPI mode 0 --- flip_tdi/helpers/ftdi_gpio.c | 16 +++---- flip_tdi/helpers/ftdi_mpsse.c | 73 ++++++++++++++++++++++++------ flip_tdi/helpers/ftdi_mpsse_data.h | 35 ++++++++++++++ 3 files changed, 103 insertions(+), 21 deletions(-) create mode 100644 flip_tdi/helpers/ftdi_mpsse_data.h diff --git a/flip_tdi/helpers/ftdi_gpio.c b/flip_tdi/helpers/ftdi_gpio.c index b9b51d1..c78ae3e 100644 --- a/flip_tdi/helpers/ftdi_gpio.c +++ b/flip_tdi/helpers/ftdi_gpio.c @@ -67,14 +67,14 @@ void ftdi_gpio_set_direction(uint8_t gpio_mask) { } void ftdi_gpio_init(uint8_t gpio_mask) { - furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); - furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b0, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b1, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b2, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b3, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b4, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b5, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b6, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); + furi_hal_gpio_init(&gpio_b7, GpioModeInput, GpioPullDown, GpioSpeedVeryHigh); ftdi_gpio_set_direction(gpio_mask); } diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index 30606d4..82067ec 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -3,12 +3,20 @@ #include #include "ftdi_gpio.h" #include "ftdi_latency_timer.h" +#include "ftdi_mpsse_data.h" #define TAG "FTDI_MPSSE" typedef void (*FtdiMpsseGpioO)(uint8_t state); -#define FTDI_MPSSE_TIMEOUT 5000 +#define FTDI_MPSSE_TIMEOUT 5000 +#define FTDI_MPSSE_TX_RX_SIZE (4096UL) + +typedef enum { + FtdiMpsseErrorNone = 0, + FtdiMpsseErrorTimeout, + FtdiMpsseErrorTxOverflow, +} FtdiMpsseError; struct FtdiMpsse { Ftdi* ftdi; @@ -19,12 +27,16 @@ struct FtdiMpsse { bool is_div5; bool is_clk3phase; bool is_adaptive; + FtdiMpsseError error; FtdiMpsseGpioO gpio_o[8]; uint8_t gpio_mask_old; FtdiMpsseCallbackImmediate callback_immediate; void* context_immediate; + + uint8_t* data_buf; + uint16_t data_buf_count_byte; }; void ftdi_mpsse_gpio_set_callback( @@ -126,6 +138,10 @@ FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { ftdi_mpsse->is_clk3phase = false; ftdi_mpsse->is_adaptive = false; + ftdi_mpsse->error = FtdiMpsseErrorNone; + ftdi_mpsse->data_buf = malloc(FTDI_MPSSE_TX_RX_SIZE * sizeof(uint8_t)); + ftdi_mpsse->data_buf_count_byte = 0; + ftdi_mpsse_gpio_init(ftdi_mpsse); return ftdi_mpsse; @@ -133,6 +149,7 @@ FtdiMpsse* ftdi_mpsse_alloc(Ftdi* ftdi) { void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { if(!ftdi_mpsse) return; + free(ftdi_mpsse->data_buf); ftdi_gpio_deinit(); free(ftdi_mpsse); ftdi_mpsse = NULL; @@ -140,8 +157,11 @@ void ftdi_mpsse_free(FtdiMpsse* ftdi_mpsse) { uint8_t ftdi_mpsse_get_data_stream(FtdiMpsse* ftdi_mpsse) { uint8_t data = 0; - ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1, FTDI_MPSSE_TIMEOUT); - //FURI_LOG_RAW_I("0x%02X ", data); + if(ftdi_get_rx_buf(ftdi_mpsse->ftdi, &data, 1, FTDI_MPSSE_TIMEOUT) != 1) { + FURI_LOG_E(TAG, "Timeout"); + ftdi_mpsse->error = FtdiMpsseErrorTimeout; + } + FURI_LOG_RAW_I("0x%02X ", data); return data; } @@ -149,24 +169,36 @@ void ftdi_mpssse_set_data_stream(FtdiMpsse* ftdi_mpsse, uint8_t* data, uint16_t ftdi_set_tx_buf(ftdi_mpsse->ftdi, data, size); } -void ftdi_mpsse_get_data(FtdiMpsse* ftdi_mpsse) { - //todo add support for tx buffer, data_size_max = 0xFF00 +static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { ftdi_mpsse->data_size++; while(ftdi_mpsse->data_size--) { + if(ftdi_mpsse->error != FtdiMpsseErrorNone) return; ftdi_mpsse_get_data_stream(ftdi_mpsse); } } -static uint16_t ftdi_mpsse_get_data_size(FtdiMpsse* ftdi_mpsse) { - return (uint16_t)ftdi_mpsse_get_data_stream(ftdi_mpsse) << 8 | - ftdi_mpsse_get_data_stream(ftdi_mpsse); -} - -static inline void ftdi_mpsse_skeep_data(FtdiMpsse* ftdi_mpsse) { +void ftdi_mpsse_get_data(FtdiMpsse* ftdi_mpsse) { + //todo add support for tx buffer, data_size_max = 0xFF00 ftdi_mpsse->data_size++; - while(ftdi_mpsse->data_size--) { - ftdi_mpsse_get_data_stream(ftdi_mpsse); + if(ftdi_mpsse->data_size > FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + ftdi_mpsse_skeep_data(ftdi_mpsse); + ftdi_mpsse->data_size = 0; + } + + if(ftdi_get_rx_buf( + ftdi_mpsse->ftdi, ftdi_mpsse->data_buf, ftdi_mpsse->data_size, FTDI_MPSSE_TIMEOUT) != + ftdi_mpsse->data_size) { + FURI_LOG_E(TAG, "Timeout"); + ftdi_mpsse->error = FtdiMpsseErrorTimeout; } + ftdi_mpsse->data_buf_count_byte = ftdi_mpsse->data_size; +} + +static uint16_t ftdi_mpsse_get_data_size(FtdiMpsse* ftdi_mpsse) { + return ftdi_mpsse_get_data_stream(ftdi_mpsse) | + (uint16_t)ftdi_mpsse_get_data_stream(ftdi_mpsse) << 8; } static inline void ftdi_mpsse_immediate(FtdiMpsse* ftdi_mpsse) { @@ -175,6 +207,7 @@ static inline void ftdi_mpsse_immediate(FtdiMpsse* ftdi_mpsse) { } } void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { + ftdi_mpsse->error = FtdiMpsseErrorNone; uint8_t data = ftdi_mpsse_get_data_stream(ftdi_mpsse); uint8_t gpio_state_io = 0xFF; switch(data) { @@ -218,6 +251,7 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); //read data ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); break; case FtdiMpsseCommandsWriteBitsPveMsb: // 0x12 Write bits with positive edge clock, MSB first */ //not supported @@ -264,6 +298,19 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { case FtdiMpsseCommandsReadBytesNveMsb: // 0x24 Read bytes with negative edge clock, MSB first */ //spi mode 0,2 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); + if(ftdi_mpsse->data_size >=FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + gpio_state_io = 0xFF; + do{ + ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + } while (ftdi_mpsse->data_size--); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + } + //write data //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); break; diff --git a/flip_tdi/helpers/ftdi_mpsse_data.h b/flip_tdi/helpers/ftdi_mpsse_data.h new file mode 100644 index 0000000..d0afa2d --- /dev/null +++ b/flip_tdi/helpers/ftdi_mpsse_data.h @@ -0,0 +1,35 @@ +// #pragma GCC optimize("Ofast") +// #pragma GCC optimize("-funroll-all-loops") +#pragma once +#include "ftdi_gpio.h" + +//ftdi_gpio_set_b0 - CLK +//ftdi_gpio_set_b1 - MOSI +//ftdi_gpio_set_b2 - MISO +//ftdi_gpio_set_b3 - CS + +static inline void ftdi_mpsse_data_write_bytes_nve_msb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x80; + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(1); + data[i] <<= 1; + ftdi_gpio_set_b0(0); + } + } + ftdi_gpio_set_b0(0); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_nve_msb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x80; + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask >>= 1; + } + } +} \ No newline at end of file From 00856562699ad405cac8ff185e168eda3c6d4471 Mon Sep 17 00:00:00 2001 From: SkorP Date: Sun, 21 Jul 2024 14:23:10 +0400 Subject: [PATCH 15/19] FlipTDI: SPI mode --- flip_tdi/helpers/ftdi_mpsse.c | 55 ++++++++++++---- flip_tdi/helpers/ftdi_mpsse_data.h | 88 ++++++++++++++++++++++++++ flip_tdi/helpers/ftdi_usb.c | 2 +- flip_tdi/scenes/flip_tdi_scene_about.c | 6 +- 4 files changed, 137 insertions(+), 14 deletions(-) diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index 82067ec..4983894 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -245,6 +245,7 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); //read data ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_pve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); break; case FtdiMpsseCommandsWriteBytesNveMsb: // 0x11 Write bytes with negative edge clock, MSB first */ //spi mode 0,2 @@ -270,12 +271,14 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); //read data ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_pve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); break; case FtdiMpsseCommandsWriteBytesNveLsb: // 0x19 Write bytes with negative edge clock, LSB first */ //spi mode 0,2 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); //read data ftdi_mpsse_get_data(ftdi_mpsse); + ftdi_mpsse_data_write_bytes_nve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_buf_count_byte); break; case FtdiMpsseCommandsWriteBitsPveLsb: // 0x1a Write bits with positive edge clock, LSB first */ //not supported @@ -292,23 +295,35 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { case FtdiMpsseCommandsReadBytesPveMsb: // 0x20 Read bytes with positive edge clock, MSB first */ //spi mode 1,3 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); - //write data - //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + // gpio_state_io = 0xFF; + // do{ + // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + // } while (ftdi_mpsse->data_size--); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_pve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } break; case FtdiMpsseCommandsReadBytesNveMsb: // 0x24 Read bytes with negative edge clock, MSB first */ //spi mode 0,2 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); - if(ftdi_mpsse->data_size >=FTDI_MPSSE_TX_RX_SIZE) { + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; FURI_LOG_E(TAG, "Tx buffer overflow"); - gpio_state_io = 0xFF; - do{ - ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); - } while (ftdi_mpsse->data_size--); + // gpio_state_io = 0xFF; + // do{ + // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + // } while (ftdi_mpsse->data_size--); } else { ftdi_mpsse->data_size++; ftdi_mpsse_data_read_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); } //write data @@ -329,14 +344,32 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { case FtdiMpsseCommandsReadBytesPveLsb: // 0x28 Read bytes with positive edge clock, LSB first */ //spi mode 1,3 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); - //write data - //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + // gpio_state_io = 0xFF; + // do{ + // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); + // } while (ftdi_mpsse->data_size--); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_pve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } break; case FtdiMpsseCommandsReadBytesNveLsb: // 0x2c Read bytes with negative edge clock, LSB first */ //spi mode 0,2 ftdi_mpsse->data_size = ftdi_mpsse_get_data_size(ftdi_mpsse); - //write data - //ftdi_mpssse_set_data_stream(ftdi_mpsse, 0xFF, ftdi_mpsse->data_size); + if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { + ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; + FURI_LOG_E(TAG, "Tx buffer overflow"); + } else { + ftdi_mpsse->data_size++; + ftdi_mpsse_data_read_bytes_nve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpssse_set_data_stream(ftdi_mpsse, ftdi_mpsse->data_buf, ftdi_mpsse->data_size); + ftdi_mpsse_immediate(ftdi_mpsse); + } break; case FtdiMpsseCommandsReadBitsPveLsb: // 0x2a Read bits with positive edge clock, LSB first */ //not supported diff --git a/flip_tdi/helpers/ftdi_mpsse_data.h b/flip_tdi/helpers/ftdi_mpsse_data.h index d0afa2d..f92f20d 100644 --- a/flip_tdi/helpers/ftdi_mpsse_data.h +++ b/flip_tdi/helpers/ftdi_mpsse_data.h @@ -23,8 +23,65 @@ static inline void ftdi_mpsse_data_write_bytes_nve_msb(uint8_t* data, uint32_t s } static inline void ftdi_mpsse_data_read_bytes_nve_msb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + for(uint32_t i = 0; i < size; i++) { + uint8_t mask = 0x80; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask >>= 1; + } + } +} + +static inline void ftdi_mpsse_data_write_bytes_nve_lsb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x01; + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(1); + data[i] >>= 1; + ftdi_gpio_set_b0(0); + } + } + ftdi_gpio_set_b0(0); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_nve_lsb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + for(uint32_t i = 0; i < size; i++) { + uint8_t mask = 0x01; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask <<= 1; + } + } +} + +static inline void ftdi_mpsse_data_write_bytes_pve_msb(uint8_t* data, uint32_t size) { uint8_t mask = 0x80; + ftdi_gpio_set_b0(1); + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(0); + data[i] <<= 1; + ftdi_gpio_set_b0(1); + } + } + ftdi_gpio_set_b0(1); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_pve_msb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + ftdi_gpio_set_b0(0); for(uint32_t i = 0; i < size; i++) { + uint16_t mask = 0x80; for(uint8_t j = 0; j < 8; j++) { ftdi_gpio_set_b0(1); data[i] |= ftdi_gpio_get_b2() ? mask : 0; @@ -32,4 +89,35 @@ static inline void ftdi_mpsse_data_read_bytes_nve_msb(uint8_t* data, uint32_t si mask >>= 1; } } + ftdi_gpio_set_b0(1); +} + +static inline void ftdi_mpsse_data_write_bytes_pve_lsb(uint8_t* data, uint32_t size) { + uint8_t mask = 0x01; + ftdi_gpio_set_b0(1); + for(uint32_t i = 0; i < size; i++) { + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b1(data[i] & mask); + ftdi_gpio_set_b0(0); + data[i] >>= 1; + ftdi_gpio_set_b0(1); + } + } + ftdi_gpio_set_b0(1); + ftdi_gpio_set_b1(0); +} + +static inline void ftdi_mpsse_data_read_bytes_pve_lsb(uint8_t* data, uint32_t size) { + memset(data, 0, size); + ftdi_gpio_set_b0(0); + for(uint32_t i = 0; i < size; i++) { + uint16_t mask = 0x01; + for(uint8_t j = 0; j < 8; j++) { + ftdi_gpio_set_b0(1); + data[i] |= ftdi_gpio_get_b2() ? mask : 0; + ftdi_gpio_set_b0(0); + mask <<= 1; + } + } + ftdi_gpio_set_b0(1); } \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index f9c6bf3..860fe6e 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -111,7 +111,7 @@ static int32_t ftdi_thread_worker(void* context) { if(flags & EventTxComplete) { ftdi_usb->tx_complete = true; if((ftdi_usb->tx_immediate) || - (ftdi_available_tx_buf(ftdi_usb->ftdi) >= FTDI_USB_TX_MAX_SIZE)) { + (ftdi_available_tx_buf(ftdi_usb->ftdi) != 0)) { ftdi_reset_latency_timer(ftdi_usb->ftdi); flags |= EventTx; } diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c index 1b3387d..45754e8 100644 --- a/flip_tdi/scenes/flip_tdi_scene_about.c +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -26,8 +26,10 @@ void flip_tdi_scene_about_on_enter(void* context) { furi_string_cat_printf(temp_str, "\e#%s\n", "What it can do:"); furi_string_cat_printf(temp_str, "- Emulate FT232H VCP mode\n"); - furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitbang mode, max freq 100kHz \n"); - furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitbang mode, max freq 100kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitbang mode, max freq 50kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitbang mode, max freq 70kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H MPSSE mode, max freq 70kHz \n"); + furi_string_cat_printf(temp_str, "- Emulate FT232H SPI mode, fix freq 2MHz \n"); widget_add_text_box_element( app->widget, From 8e63b92f81f60d1e824c0c4e6664fcd24504af3c Mon Sep 17 00:00:00 2001 From: SkorP Date: Sun, 21 Jul 2024 14:28:19 +0400 Subject: [PATCH 16/19] FlipTDI: fix description --- flip_tdi/scenes/flip_tdi_scene_about.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flip_tdi/scenes/flip_tdi_scene_about.c b/flip_tdi/scenes/flip_tdi_scene_about.c index 45754e8..c8d077c 100644 --- a/flip_tdi/scenes/flip_tdi_scene_about.c +++ b/flip_tdi/scenes/flip_tdi_scene_about.c @@ -29,7 +29,9 @@ void flip_tdi_scene_about_on_enter(void* context) { furi_string_cat_printf(temp_str, "- Emulate FT232H Async bitbang mode, max freq 50kHz \n"); furi_string_cat_printf(temp_str, "- Emulate FT232H Sync bitbang mode, max freq 70kHz \n"); furi_string_cat_printf(temp_str, "- Emulate FT232H MPSSE mode, max freq 70kHz \n"); - furi_string_cat_printf(temp_str, "- Emulate FT232H SPI mode, fix freq 2MHz \n"); + furi_string_cat_printf( + temp_str, + "- Emulate FT232H SPI mode (half duplex), fix freq 2MHz, max block size 4096 byte\n"); widget_add_text_box_element( app->widget, From da70c946d8b79e2557b03a165371852a999fab09 Mon Sep 17 00:00:00 2001 From: SkorP Date: Wed, 18 Sep 2024 12:13:08 +0400 Subject: [PATCH 17/19] Add wiring and debug mode --- flip_tdi/flip_tdi_app.c | 1 - flip_tdi/helpers/flip_tdi_event.h | 4 +- flip_tdi/helpers/ftdi.c | 240 +----------------- flip_tdi/helpers/ftdi.h | 5 +- flip_tdi/helpers/ftdi_mpsse.c | 14 +- flip_tdi/helpers/ftdi_uart.c | 17 -- flip_tdi/helpers/ftdi_usb.c | 133 +++------- .../{flip_tdi_wiring.png => flip_tdi.png} | Bin 4884 -> 4156 bytes flip_tdi/images/flip_tdi_wiring_gpio.png | Bin 0 -> 5510 bytes flip_tdi/images/flip_tdi_wiring_spi.png | Bin 0 -> 5130 bytes flip_tdi/images/flip_tdi_wiring_uart.png | Bin 0 -> 4855 bytes flip_tdi/scenes/flip_tdi_scene_config.h | 4 +- flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c | 21 ++ ...e_wiring.c => flip_tdi_scene_wiring_spi.c} | 8 +- flip_tdi/scenes/flip_tdi_scene_wiring_uart.c | 21 ++ flip_tdi/scenes/subghz_scene_menu.c | 16 +- flip_tdi/views/flip_tdi_view_main.c | 16 +- 17 files changed, 116 insertions(+), 384 deletions(-) rename flip_tdi/images/{flip_tdi_wiring.png => flip_tdi.png} (74%) create mode 100644 flip_tdi/images/flip_tdi_wiring_gpio.png create mode 100644 flip_tdi/images/flip_tdi_wiring_spi.png create mode 100644 flip_tdi/images/flip_tdi_wiring_uart.png create mode 100644 flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c rename flip_tdi/scenes/{flip_tdi_scene_wiring.c => flip_tdi_scene_wiring_spi.c} (65%) create mode 100644 flip_tdi/scenes/flip_tdi_scene_wiring_uart.c diff --git a/flip_tdi/flip_tdi_app.c b/flip_tdi/flip_tdi_app.c index df9f16e..f98604c 100644 --- a/flip_tdi/flip_tdi_app.c +++ b/flip_tdi/flip_tdi_app.c @@ -28,7 +28,6 @@ FlipTDIApp* flip_tdi_app_alloc() { // View Dispatcher app->view_dispatcher = view_dispatcher_alloc(); app->scene_manager = scene_manager_alloc(&flip_tdi_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( diff --git a/flip_tdi/helpers/flip_tdi_event.h b/flip_tdi/helpers/flip_tdi_event.h index 6f879bf..a2caff6 100644 --- a/flip_tdi/helpers/flip_tdi_event.h +++ b/flip_tdi/helpers/flip_tdi_event.h @@ -2,7 +2,9 @@ typedef enum { //SubmenuIndex - SubmenuIndexWiring = 10, + SubmenuIndexWiringUart = 10, + SubmenuIndexWiringSpi, + SubmenuIndexWiringGpio, SubmenuIndexAbout, //FlipTDICustomEvent diff --git a/flip_tdi/helpers/ftdi.c b/flip_tdi/helpers/ftdi.c index f44a0c7..cbaef9d 100644 --- a/flip_tdi/helpers/ftdi.c +++ b/flip_tdi/helpers/ftdi.c @@ -6,10 +6,10 @@ #define TAG "FTDI" -#define FTDI_TX_RX_BUF_SIZE (4096UL) -#define FTDI_INTERFACE_A (0x01UL) +#define FTDI_TX_RX_BUF_SIZE (4096UL) +#define FTDI_INTERFACE_A (0x01UL) #define FTDI_DRIVER_INTERFACE_A (0x00UL) -#define FTDI_UART_MAX_TX_SIZE (64UL) +#define FTDI_UART_MAX_TX_SIZE (64UL) struct Ftdi { FtdiModemStatus status; @@ -182,8 +182,8 @@ Step 2 - extract the sub-integer divisor; 16 = 1, 15 = 0, 14 = 1 => sub-integer Step 3 - extract the integer divisor: 13:0 = 0035 Hex = 53 Dec Step 4 - combine the integer and sub-integer divisors: 53.625 Dec Step 5 - divide 3000000 by the divisor => 3000000/53.625 = 55944 baud - */ + void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { if(!ftdi_check_interface(ftdi, index)) { return; @@ -220,7 +220,7 @@ void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { ftdi_uart_set_baudrate(ftdi->ftdi_uart, baudrate); ftdi_bitbang_set_speed(ftdi->ftdi_bitbang, ftdi->bitband_speed); - +#ifdef FTDI_DEBUG furi_log_puts("ftdi_set_baudrate="); char tmp_str2[] = "4294967295"; itoa(baudrate, tmp_str2, 10); @@ -229,6 +229,7 @@ void ftdi_set_baudrate(Ftdi* ftdi, uint16_t value, uint16_t index) { itoa(ftdi->bitband_speed, tmp_str2, 10); furi_log_puts(tmp_str2); furi_log_puts("\r\n"); +#endif } void ftdi_set_data_config(Ftdi* ftdi, uint16_t value, uint16_t index) { @@ -249,83 +250,6 @@ void ftdi_set_flow_ctrl(Ftdi* ftdi, uint16_t index) { } } -/* - def control_set_bitmode(self, wValue: int, wIndex: int, - data: array) -> None: - direction = wValue & 0xff - bitmode = (wValue >> 8) & 0x7F - mode = self.BitMode(bitmode).name - self.log.info('> ftdi bitmode %s: %s', mode, f'{direction:08b}') - self._bitmode = bitmode - with self._cmd_q.lock: - self._cmd_q.q.append((self.Command.SET_BITMODE, self._bitmode)) - self._cmd_q.event.set() - # be sure to wait for the command to be popped out before resuming - loop = 10 # is something goes wrong, do not loop forever - while loop: - if not self._cmd_q.q: - # command queue has been fully processed - break - if not self._resume: - # if the worker threads are ending or have ended, abort - self.log.warning('Premature end of worker') - return - loop -= 1 - # kepp some time for the commands to be processed - sleep(0.05) - else: - raise RuntimeError(f'Command {self._cmd_q.q[-1][0].name} ' - f'not handled') - if bitmode == self.BitMode.CBUS: - self._cbus_dir = direction >> 4 - mask = (1 << self._parent.properties.cbuswidth) - 1 - self._cbus_dir &= mask - # clear output pins - self._cbus &= ~self._cbus_dir & 0xF - # update output pins - output = direction & 0xF & self._cbus_dir - self._cbus |= output - self.log.info('> ftdi cbus dir %s, io %s, mask %s', - f'{self._cbus_dir:04b}', - f'{self._cbus:04b}', - f'{mask:04b}') - elif bitmode == self.BitMode.RESET: - self._direction = ((1 << VirtFtdiPort.UART_PINS.TXD) | - (1 << VirtFtdiPort.UART_PINS.RTS) | - (1 << VirtFtdiPort.UART_PINS.DTR)) - self._pins[0].set_function(VirtualFtdiPin.Function.STREAM) - self._pins[1].set_function(VirtualFtdiPin.Function.STREAM) - for pin in self._pins[2:]: - pin.set_function(VirtualFtdiPin.Function.GPIO) - else: - self._direction = direction - for pin in self._pins: - pin.set_function(VirtualFtdiPin.Function.GPIO) - if bitmode == self.BitMode.MPSSE: - for pin in self._pins: - pin.set_function(VirtualFtdiPin.Function.GPIO) - if not self._mpsse: - self._mpsse = VirtMpsseTracer(self, self._parent.version) -*/ - -//buf0 = 0x02 # magic constant, no idea for now -// if self._bitmode == self.BitMode.RESET: -// cts = 0x01 if self._gpio & 0x08 else 0 -// dtr = 0x02 if self._gpio & 0x20 else 0 -// ri = 0x04 if self._gpio & 0x80 else 0 -// dcd = 0x08 if self._gpio & 0x40 else 0 -// buf0 |= cts | dsr | ri | dcd -// else: -// # another magic constant -// buf0 |= 0x30 -// buf1 = 0 -// rx_fifo = self._fifos.rx -// with rx_fifo.lock: -// if not rx_fifo.q: -// # TX empty -> flag THRE & TEMT ("TX empty") -// buf1 |= 0x40 | 0x20 -// return buf0, buf1 - void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index) { if(!ftdi_check_interface(ftdi, index)) { return; @@ -375,81 +299,6 @@ void ftdi_reset_latency_timer(Ftdi* ftdi) { ftdi_latency_timer_reset(ftdi->ftdi_latency_timer); } -// FTDI modem status - -// Layout of the first byte: -// - B0..B3 - must be 0 -// - B4 Clear to send (CTS) -// 0 = inactive -// 1 = active -// - B5 Data set ready (DTR) -// 0 = inactive -// 1 = active -// - B6 Ring indicator (RI) -// 0 = inactive -// 1 = active -// - B7 Receive line signal detect (RLSD) -// 0 = inactive -// 1 = active - -// Layout of the second byte: -// - B0 Data ready (DR) -// - B1 Overrun error (OE) -// - B2 Parity error (PE) -// - B3 Framing error (FE) -// - B4 Break interrupt (BI) -// - B5 Transmitter holding register (THRE) -// - B6 Transmitter empty (TEMT) -// - B7 Error in RCVR FIFO - -// void ftdi_get_modem_status(uint16_t* status) { -// FtdiModemStatus modem_status = {0}; -// modem_status.RESERVED1 = 1; -// modem_status.TEMT = 1; -// modem_status.THRE = 1; - -// *status = *((uint16_t*)&modem_status); -// } - -// @property -// def modem_status(self) -> Tuple[int, int]: -// # For some reason, B0 high nibble matches the LPC214x UART:UxMSR -// # B0.0 ? -// # B0.1 ? -// # B0.2 ? -// # B0.3 ? -// # B0.4 Clear to send (CTS) -// # B0.5 Data set ready (DTS) -// # B0.6 Ring indicator (RI) -// # B0.7 Receive line signal / Data carrier detect (RLSD/DCD) - -// # For some reason, B1 exactly matches the LPC214x UART:UxLSR -// # B1.0 Data ready (DR) -// # B1.1 Overrun error (OE) -// # B1.2 Parity error (PE) -// # B1.3 Framing error (FE) -// # B1.4 Break interrupt (BI) -// # B1.5 Transmitter holding register (THRE) -// # B1.6 Transmitter empty (TEMT) -// # B1.7 Error in RCVR FIFO -// buf0 = 0x02 # magic constant, no idea for now -// if self._bitmode == self.BitMode.RESET: -// cts = 0x01 if self._gpio & 0x08 else 0 -// dsr = 0x02 if self._gpio & 0x20 else 0 -// ri = 0x04 if self._gpio & 0x80 else 0 -// dcd = 0x08 if self._gpio & 0x40 else 0 -// buf0 |= cts | dsr | ri | dcd -// else: -// # another magic constant -// buf0 |= 0x30 -// buf1 = 0 -// rx_fifo = self._fifos.rx -// with rx_fifo.lock: -// if not rx_fifo.q: -// # TX empty -> flag THRE & TEMT ("TX empty") -// buf1 |= 0x40 | 0x20 -// return buf0, buf1 - uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi) { return (uint16_t*)&ftdi->status; } @@ -470,80 +319,3 @@ uint8_t ftdi_get_bitbang_gpio(Ftdi* ftdi) { ftdi_reset_purge_tx(ftdi); return ftdi_bitbang_get_gpio(ftdi->ftdi_bitbang); } - -/* - def control_set_event_char(self, wValue: int, wIndex: int, - data: array) -> None: - char = wValue & 0xFF - enable = bool(wValue >> 8) - self.log.info('> ftdi %sable event char: 0x%02x', - 'en' if enable else 'dis', char) - - def control_set_error_char(self, wValue: int, wIndex: int, - data: array) -> None: - char = wValue & 0xFF - enable = bool(wValue >> 8) - self.log.info('> ftdi %sable error char: 0x%02x', - 'en' if enable else 'dis', char) - - -elif self._bitmode == self.BitMode.BITBANG: - for byte in data: - # only 8 LSBs are addressable through this command - gpi = self._gpio & ~self._direction & 0xFF - gpo = byte & self._direction & 0xFF - msb = self._gpio & ~0xFF - gpio = gpi | gpo | msb - self._update_gpio(False, gpio) - self.log.debug('. bbw %02x: %s', - self._gpio, f'{self._gpio:08b}') - elif self._bitmode == self.BitMode.SYNCBB: - tx_fifo = self._fifos.tx - lost = 0 - for byte in data: - with tx_fifo.lock: - free_count = tx_fifo.size - len(tx_fifo.q) - if free_count > 0: - tx_fifo.q.append(self._gpio & 0xFF) - else: - lost += 1 - # only 8 LSBs are addressable through this command - gpi = self._gpio & ~self._direction & 0xFF - gpo = byte & self._direction & 0xFF - msb = self._gpio & ~0xFF - gpio = gpi | gpo | msb - self._update_gpio(False, gpio) - self.log.debug('. bbw %02x: %s', - self._gpio, f'{self._gpio:08b}') - if lost: - self.log.debug('%d samples lost, TX full', lost) - else: - try: - mode = self.BitMode(self._bitmode).name - except ValueError: - mode = 'unknown' - self.log.warning('Write buffer discarded, mode %s', mode) - self.log.warning('. (%d) %s', - len(data), hexlify(data).decode()) - - def _tx_worker_generate(self, bitmode) -> None: - tx_fifo = self._fifos.tx - if bitmode == self.BitMode.BITBANG: - ts = now() - # how much time has elapsed since last background action - elapsed = ts-self._last_txw_ts - self._last_txw_ts = ts - # how many bytes should have been captured since last - # action - byte_count = round(self._baudrate*elapsed) - # fill the TX FIFO with as many bytes, stop if full - if byte_count: - with tx_fifo.lock: - free_count = tx_fifo.size - len(tx_fifo.q) - push_count = min(free_count, byte_count) - tx_fifo.q.extend([self._gpio] * push_count) - self.log.debug('in %.3fms -> %d', - elapsed*1000, push_count) - - -*/ \ No newline at end of file diff --git a/flip_tdi/helpers/ftdi.h b/flip_tdi/helpers/ftdi.h index 188b4de..91173c2 100644 --- a/flip_tdi/helpers/ftdi.h +++ b/flip_tdi/helpers/ftdi.h @@ -1,6 +1,8 @@ #pragma once #include "ftdi_usb_define.h" +//#define FTDI_DEBUG + typedef struct Ftdi Ftdi; typedef void (*FtdiCallbackTxImmediate)(void* context); @@ -25,9 +27,6 @@ void ftdi_set_bitmode(Ftdi* ftdi, uint16_t value, uint16_t index); void ftdi_set_latency_timer(Ftdi* ftdi, uint16_t value, uint16_t index); uint8_t ftdi_get_latency_timer(Ftdi* ftdi); void ftdi_reset_latency_timer(Ftdi* ftdi); - -//void ftdi_get_modem_status(uint16_t *status); - uint16_t* ftdi_get_modem_status_uint16_t(Ftdi* ftdi); FtdiModemStatus ftdi_get_modem_status(Ftdi* ftdi); void ftdi_set_modem_status(Ftdi* ftdi, FtdiModemStatus status); diff --git a/flip_tdi/helpers/ftdi_mpsse.c b/flip_tdi/helpers/ftdi_mpsse.c index 4983894..054ee93 100644 --- a/flip_tdi/helpers/ftdi_mpsse.c +++ b/flip_tdi/helpers/ftdi_mpsse.c @@ -161,7 +161,9 @@ uint8_t ftdi_mpsse_get_data_stream(FtdiMpsse* ftdi_mpsse) { FURI_LOG_E(TAG, "Timeout"); ftdi_mpsse->error = FtdiMpsseErrorTimeout; } +#ifdef FTDI_DEBUG FURI_LOG_RAW_I("0x%02X ", data); +#endif return data; } @@ -298,10 +300,6 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; FURI_LOG_E(TAG, "Tx buffer overflow"); - // gpio_state_io = 0xFF; - // do{ - // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); - // } while (ftdi_mpsse->data_size--); } else { ftdi_mpsse->data_size++; ftdi_mpsse_data_read_bytes_pve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); @@ -315,10 +313,6 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; FURI_LOG_E(TAG, "Tx buffer overflow"); - // gpio_state_io = 0xFF; - // do{ - // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); - // } while (ftdi_mpsse->data_size--); } else { ftdi_mpsse->data_size++; ftdi_mpsse_data_read_bytes_nve_msb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); @@ -347,10 +341,6 @@ void ftdi_mpsse_state_machine(FtdiMpsse* ftdi_mpsse) { if(ftdi_mpsse->data_size >= FTDI_MPSSE_TX_RX_SIZE) { ftdi_mpsse->error = FtdiMpsseErrorTxOverflow; FURI_LOG_E(TAG, "Tx buffer overflow"); - // gpio_state_io = 0xFF; - // do{ - // ftdi_mpssse_set_data_stream(ftdi_mpsse, &gpio_state_io, 1); - // } while (ftdi_mpsse->data_size--); } else { ftdi_mpsse->data_size++; ftdi_mpsse_data_read_bytes_pve_lsb(ftdi_mpsse->data_buf, ftdi_mpsse->data_size); diff --git a/flip_tdi/helpers/ftdi_uart.c b/flip_tdi/helpers/ftdi_uart.c index 80d3df8..d751815 100644 --- a/flip_tdi/helpers/ftdi_uart.c +++ b/flip_tdi/helpers/ftdi_uart.c @@ -94,23 +94,6 @@ static int32_t ftdi_uart_echo_worker(void* context) { is_dma_tx = false; } } - - // if(events & WorkerEventRxIdle) { - // //furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect IDLE\r\n", 15); - // } - - // if(events & - // (WorkerEventRxOverrunError | WorkerEventRxFramingError | WorkerEventRxNoiseError)) { - // if(events & WorkerEventRxOverrunError) { - // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect ORE\r\n", 14); - // } - // if(events & WorkerEventRxFramingError) { - // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect FE\r\n", 13); - // } - // if(events & WorkerEventRxNoiseError) { - // furi_hal_serial_tx(ftdi_uart->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13); - // } - // } } ftdi_uart_tx_dma_deinit(ftdi_uart); FURI_LOG_I(TAG, "Worker stopped"); diff --git a/flip_tdi/helpers/ftdi_usb.c b/flip_tdi/helpers/ftdi_usb.c index 860fe6e..fdb5a4c 100644 --- a/flip_tdi/helpers/ftdi_usb.c +++ b/flip_tdi/helpers/ftdi_usb.c @@ -7,15 +7,15 @@ #define FTDI_USB_VID (0x0403) #define FTDI_USB_PID (0x6014) -#define FTDI_USB_EP_IN (0x81) +#define FTDI_USB_EP_IN (0x81) #define FTDI_USB_EP_OUT (0x02) -#define FTDI_USB_EP_IN_SIZE (64UL) +#define FTDI_USB_EP_IN_SIZE (64UL) #define FTDI_USB_EP_OUT_SIZE (64UL) -#define FTDI_USB_RX_MAX_SIZE (FTDI_USB_EP_OUT_SIZE) +#define FTDI_USB_RX_MAX_SIZE (FTDI_USB_EP_OUT_SIZE) #define FTDI_USB_MODEM_STATUS_SIZE (sizeof(uint16_t)) -#define FTDI_USB_TX_MAX_SIZE (FTDI_USB_EP_IN_SIZE - FTDI_USB_MODEM_STATUS_SIZE) +#define FTDI_USB_TX_MAX_SIZE (FTDI_USB_EP_IN_SIZE - FTDI_USB_MODEM_STATUS_SIZE) typedef struct { uint16_t status; @@ -35,21 +35,10 @@ typedef enum { EventTx = (1 << 3), EventTxComplete = (1 << 4), EventResetSio = (1 << 5), - EventResetPurgeRx = (1 << 6), - EventResetPurgeTx = (1 << 7), - EventSetBitmode = (1 << 8), - EventSetLatencyTimer = (1 << 9), - EventSetEventChar = (1 << 10), - EventSetErrorChar = (1 << 11), - EventSetBaudrate = (1 << 12), - EventSetData = (1 << 13), - EventSetFlowCtrl = (1 << 14), - EventTxImmediate = (1 << 15), + EventTxImmediate = (1 << 6), EventAll = EventExit | EventReset | EventRx | EventTx | EventTxComplete | EventResetSio | - EventResetPurgeRx | EventResetPurgeTx | EventSetBitmode | EventSetLatencyTimer | - EventSetEventChar | EventSetErrorChar | EventSetBaudrate | EventSetData | - EventSetFlowCtrl | EventTxImmediate, + EventTxImmediate, } FtdiEvent; struct FtdiUsb { @@ -73,8 +62,6 @@ static int32_t ftdi_thread_worker(void* context) { uint32_t len_data = 0; FtdiTxData tx_data = {0}; - // uint16_t *status = 0; - // ftdi_get_modem_status(&status); uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); tx_data.status = status[0]; @@ -83,8 +70,6 @@ static int32_t ftdi_thread_worker(void* context) { while(true) { uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever); - //furi_hal_gpio_write(&gpio_ext_pa7, 1); - if(flags & EventRx) { //fast flag uint8_t buf[FTDI_USB_RX_MAX_SIZE]; len_data = ftdi_usb_receive(dev, buf, FTDI_USB_RX_MAX_SIZE); @@ -96,7 +81,6 @@ static int32_t ftdi_thread_worker(void* context) { // } if(len_data > 0) { ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); - //ftdi_loopback(ftdi_usb->ftdi); ftdi_start_uart_tx(ftdi_usb->ftdi); } flags &= ~EventRx; // clear flag @@ -104,21 +88,19 @@ static int32_t ftdi_thread_worker(void* context) { if(flags) { if(flags & EventResetSio) { - furi_log_puts("EventResetSio\r\n"); ftdi_reset_sio(ftdi_usb->ftdi); ftdi_usb_send(dev, (uint8_t*)&tx_data, FTDI_USB_MODEM_STATUS_SIZE); } if(flags & EventTxComplete) { ftdi_usb->tx_complete = true; - if((ftdi_usb->tx_immediate) || - (ftdi_available_tx_buf(ftdi_usb->ftdi) != 0)) { + if((ftdi_usb->tx_immediate) || (ftdi_available_tx_buf(ftdi_usb->ftdi) != 0)) { ftdi_reset_latency_timer(ftdi_usb->ftdi); flags |= EventTx; } } if(flags & EventTxImmediate) { - ftdi_usb->tx_immediate= true; + ftdi_usb->tx_immediate = true; if(ftdi_usb->tx_complete) { flags |= EventTx; } @@ -142,27 +124,16 @@ static int32_t ftdi_thread_worker(void* context) { } } - // if(flags & EventResetPurgeRx) { - // ftdi_reset_purge_rx(ftdi_usb->ftdi); - // } - // if(flags & EventResetPurgeTx) { - // ftdi_reset_purge_tx(ftdi_usb->ftdi); - // } - if(flags & EventExit) { FURI_LOG_I(TAG, "exit"); break; } } - - // furi_hal_gpio_write(&gpio_ext_pa7, 0); } return 0; } -// needed in ftdi_usb_deinit, ftdi_usb_suspend, usb_rxtx_ep_callback, ftdi_usb_control, -// where if_ctx isn't passed static FtdiUsb* ftdi_cur = NULL; static void ftdi_usb_callback_tx_immediate(void* context) { @@ -188,8 +159,6 @@ static void ftdi_usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx ftdi_usb->ftdi = ftdi_alloc(); ftdi_set_callback_tx_immediate(ftdi_usb->ftdi, ftdi_usb_callback_tx_immediate, ftdi_usb); - // furi_hal_gpio_init(&gpio_ext_pa7, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); - // furi_hal_gpio_init(&gpio_ext_pa6, GpioModeOutputPushPull, GpioPullDown, GpioSpeedVeryHigh); furi_thread_start(ftdi_usb->thread); } @@ -212,9 +181,6 @@ static void ftdi_usb_deinit(usbd_device* dev) { ftdi_free(ftdi_usb->ftdi); - //furi_hal_gpio_init(&gpio_ext_pa7, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - //furi_hal_gpio_init(&gpio_ext_pa6, GpioModeAnalog, GpioPullNo, GpioSpeedLow); - free(ftdi_usb->usb.str_prod_descr); ftdi_usb->usb.str_prod_descr = NULL; free(ftdi_usb->usb.str_serial_descr); @@ -238,32 +204,14 @@ static void ftdi_usb_wakeup(usbd_device* dev) { static void ftdi_usb_suspend(usbd_device* dev) { FtdiUsb* ftdi_usb = ftdi_cur; if(!ftdi_usb || ftdi_usb->dev != dev) return; - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventReset); } -// static void ftdi_usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { -// UNUSED(ep); -// UNUSED(event); -// FtdiUsb* ftdi_usb = ftdi_cur; -// if(!ftdi_usb || ftdi_usb->dev != dev) return; -// // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRxTx); -// } - static void ftdi_usb_rx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { UNUSED(dev); UNUSED(event); UNUSED(ep); FtdiUsb* ftdi_usb = ftdi_cur; - //furi_hal_gpio_write(&gpio_ext_pa6, 1); furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventRx); - - // uint8_t buf[FTDI_USB_RX_MAX_SIZE]; - // uint32_t len_data = ftdi_usb_receive(dev, buf, FTDI_USB_RX_MAX_SIZE); - // if(len_data > 0) { - // ftdi_set_rx_buf(ftdi_usb->ftdi, buf, len_data); - // // ftdi_loopback(ftdi_usb->ftdi); - // } - //furi_hal_gpio_write(&gpio_ext_pa6, 0); } static void ftdi_usb_tx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { @@ -308,8 +256,8 @@ static usbd_respond return usbd_fail; } +#ifdef FTDI_DEBUG furi_log_puts("-----------\r\n"); - char tmp_str[] = "0xFFFFFFFF"; itoa(req->bmRequestType, tmp_str, 16); furi_log_puts(tmp_str); @@ -330,76 +278,83 @@ static usbd_respond itoa(req->wLength, tmp_str, 16); furi_log_puts(tmp_str); furi_log_puts(" \r\n"); - - // if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) != - // (USB_REQ_INTERFACE | USB_REQ_CLASS)) { - // return usbd_fail; - // } +#endif switch(req->bmRequestType) { case FtdiControlRequestsOut: switch(req->bRequest) { case FtdiRequestsSiOReqReset: +#ifdef FTDI_DEBUG furi_log_puts("ftdi_usb_control OUT\r\n"); +#endif if(req->wValue == FtdiResetSio) { +#ifdef FTDI_DEBUG furi_log_puts("FtdiResetSio\r\n"); - //ftdi_reset_sio(ftdi_usb->ftdi); +#endif furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetSio); } if(req->wValue == FtdiResetPurgeRx) { +#ifdef FTDI_DEBUG furi_log_puts("FtdiResetPurgeRx\r\n"); +#endif ftdi_reset_purge_rx(ftdi_usb->ftdi); - - //furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetPurgeRx); } if(req->wValue == FtdiResetPurgeTx) { +#ifdef FTDI_DEBUG furi_log_puts("FtdiResetPurgeTx\r\n"); +#endif ftdi_reset_purge_tx(ftdi_usb->ftdi); - //furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventResetPurgeTx); } return usbd_ack; break; case FtdiRequestsSiOReqSetBitmode: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetBitmode\r\n"); +#endif ftdi_set_bitmode(ftdi_usb->ftdi, req->wValue, req->wIndex); - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetBitmode); return usbd_ack; break; case FtdiRequestsSiOReqSetLatencyTimer: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetLatencyTimer\r\n"); +#endif ftdi_set_latency_timer(ftdi_usb->ftdi, req->wValue, req->wIndex); - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetLatencyTimer); return usbd_ack; break; case FtdiRequestsSiOReqSetEventChar: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetEventChar\r\n"); +#endif //value?????? bool enable: value |= 1 << 8 - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetEventChar); return usbd_ack; break; case FtdiRequestsSiOReqSetErrorChar: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetErrorChar\r\n"); +#endif //value?????? bool enable: value |= 1 << 8 - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetErrorChar); return usbd_ack; break; case FtdiRequestsSiOReqSetBaudrate: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetBaudrate\r\n"); +#endif ftdi_set_baudrate(ftdi_usb->ftdi, req->wValue, req->wIndex); - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetBaudrate); return usbd_ack; break; case FtdiRequestsSiOReqSetData: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetData\r\n"); +#endif ftdi_set_data_config(ftdi_usb->ftdi, req->wValue, req->wIndex); - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetData); return usbd_ack; break; case FtdiRequestsSiOReqSetFlowCtrl: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqSetFlowCtrl\r\n"); +#endif ftdi_set_flow_ctrl(ftdi_usb->ftdi, req->wIndex); - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetFlowCtrl); return usbd_ack; break; default: @@ -408,20 +363,25 @@ static usbd_respond break; case FtdiControlRequestsIn: +#ifdef FTDI_DEBUG furi_log_puts("ftdi_usb_control IN\r\n"); +#endif switch(req->bRequest) { case FtdiRequestsSiOReqGetLatencyTimer: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqGetLatencyTimer\r\n"); +#endif ftdi_usb->data_recvest[0] = ftdi_get_latency_timer(ftdi_usb->ftdi); ftdi_usb->data_recvest_len = 1; ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; ftdi_usb->dev->status.data_count = ftdi_usb->data_recvest_len; - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventSetLatencyTimer); return usbd_ack; break; case FtdiRequestsSiOReqPollModemStatus: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqPollModemStatus\r\n"); +#endif uint16_t* status = ftdi_get_modem_status_uint16_t(ftdi_usb->ftdi); memcpy(ftdi_usb->data_recvest, status, sizeof(uint16_t)); ftdi_usb->data_recvest_len = req->wLength; @@ -430,7 +390,9 @@ static usbd_respond return usbd_ack; break; case FtdiRequestsSiOReqReadPins: +#ifdef FTDI_DEBUG furi_log_puts("FtdiRequestsSiOReqReadPins\r\n"); +#endif ftdi_usb->data_recvest[0] = ftdi_get_bitbang_gpio(ftdi_usb->ftdi); ftdi_usb->data_recvest_len = 1; ftdi_usb->dev->status.data_ptr = ftdi_usb->data_recvest; @@ -444,21 +406,6 @@ static usbd_respond default: break; } - - // switch(req->bRequest) { - // case USB_MSC_BOT_GET_MAX_LUN: { - // static uint8_t max_lun = 0; - // dev->status.data_ptr = &max_lun; - // dev->status.data_count = 1; - // return usbd_ack; - // }; break; - // case USB_MSC_BOT_RESET: { - // FtdiUsb* ftdi_usb = ftdi_cur; - // if(!ftdi_usb || ftdi_usb->dev != dev) return usbd_fail; - // furi_thread_flags_set(furi_thread_get_id(ftdi_usb->thread), EventReset); - // return usbd_ack; - // }; break; - // } return usbd_fail; } diff --git a/flip_tdi/images/flip_tdi_wiring.png b/flip_tdi/images/flip_tdi.png similarity index 74% rename from flip_tdi/images/flip_tdi_wiring.png rename to flip_tdi/images/flip_tdi.png index eec71af20a8ce7ad997a8a461a9e6f51a8cce3b7..d75ee75739e13ec0e08213b8fdb10b085671ca04 100644 GIT binary patch delta 495 zcmbQDwnt$@1YbSlIZqeIkczmscX~q)8A!M#9@5#x^6s&p-V>o++(JGkMYFd3VR3#} zdv2wPz^xPC&9*!+e8X>c=+?4mM@a??W`=Zz2RsaO7#oZk3fLIVF&w}uSo&SuQL$x< znLfYu_w`%P7rxy0{K~PyvljU$>m?U#6o|VNHGkJ!#%p}5pHAGe+d)3Yi39$ta|ais=m&DyKU8p)eS`_ zcGmA(f9+h>-(@;qBRBVEth#^e`L?#1jE}fu_uZ(p6>nN~o8d{5ukLoy`wMrr?dFNe zs-HgXTj|@!H$(PsJ)e8_w8Iac1cq7j7T5oidAZjqmTlTQL)P{WbFTfXU$x%xEW^eZ zl{=Rf?SA^{?bh3m+-ES%5mVeYTP$t6_wNet@-LY`>IEkDA30cmw%aj{;oHoAGV7Uu zfph1>bB0;Zd9Z}kj!^k@mcMmdSGsQwxp$M{hwrV&(W@S&FzEcL$TIya%)o8G@3NFU xbHc;QqO*_B&uC0`wu$aO|Li%Qpk9z`7B4^N&ALxHD|;A#z|+;wWt~$(69BT*>J$I~ delta 1229 zcmV;;1Ty=)Ae1JsSPp*#rb$FWRCr$Po85BNFbsrSE-3@=|Clh8Tj-wY&MX?qmVc7d zoa&n~NgT&=td;B>;Q8+xPoJEZZ$IA8VqSm$Ot))srmYNL-j79+Kd-;Saz#o26Ci0a zaq9UMDFFybsaje*KR@3bkK-(ABeJJs(equD01RAvacoUCoj-pY{ph=AgRos@Uoc7u zz>skW9M8zt^BG1&a0Dd)0h7$aYY(pbT1L^n)uzSPN9|@Q0Zd?{eJr~K(LN)3Bhy5=rW*v^qkB4yRr=_0W>&z3BYse_)I@& z-xJFSC4dG|2A~7z3|@bI`F=Z&(sC&8t%@z81Rx;d8TC>0la|wFXgPMb)0Gl{fxCYm zEr{}{jH1yU35w3={oTSV0XVcafA-s7%LwZH7HDUkzcYU$lmG}kn*E)1UsX6h5GjZ4pC~)h@qD~&_}Bc?`PYsQUY*j31HPJ zndJhu(N}+u62L)Z1fUMmx>2V@wk+9pqtqT?0y>%{6RjVW62K>?1TYDrp5@Ba9>C$q zO`xUr02f4~pXI8Q04C54;Aq5uG)e%IX!KuM1SNn8==2>K0P8x|_j^$Sm_#!G$_n=s zL45@{ocR@?Pz0Xs`U?0!Fya~gyk4c};3Kj`31EMb)jfa(BJdndaqJdB31ETU&R1F@ z*hvXslHKWFIddtfO`roN*qatO`^Zh8LMMCUh{iWV3BaKx00sXU#Qu+AANl06N|_@zT#3b!U;aea09g8zW-` zQCoj^3BW@%aP$in$3h>{rz3GA|c$C09Le|uAe~O=g%2+S!O=Pdhh44GR7>? z+<>0#);!SX_!AnqU9`|2IhZAsGwL$T{utAHKabfljxakn*?IUf`T{V{qn`8Fs$-Pl zTF-g3?Qqm~S^)2ByH(h?wZm)L!4VOtw%LD%Wu%TaY=1oJIgdQ&Gx}_nZNCFxGBhRe zTI-n3P?*Mm*N#VR2eDeg+BU0jEIJax*X2*Gz_sf!fpCJKUg;@bS8e0WWJSI~D!1|+)F?v*S8c|+7>%4ZlzO{xI z*;palC)(*d?6QA0!<#_6`2cu%6gzo#Qlg3S+QRV}ZF>E!((6;-Q_ru+umB>bc+T}E zC#tjc-cHv7d!F4@yfG?jZ@2BU+44mh7&*b#;W|?XGSN@FJ%^1DjptFBUIJ`%!U^gK rdmX`ccG7#%Pck+}h{p4nJU#sbY&095!x}K#00000NkvXXu0mjfuRKY< diff --git a/flip_tdi/images/flip_tdi_wiring_gpio.png b/flip_tdi/images/flip_tdi_wiring_gpio.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdf6d911ac842afed39acb160407bae81e5693d GIT binary patch literal 5510 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 MwKJ_bcJ{*m04yhY#sB~S literal 0 HcmV?d00001 diff --git a/flip_tdi/scenes/flip_tdi_scene_config.h b/flip_tdi/scenes/flip_tdi_scene_config.h index 12c10ed..188c373 100644 --- a/flip_tdi/scenes/flip_tdi_scene_config.h +++ b/flip_tdi/scenes/flip_tdi_scene_config.h @@ -1,4 +1,6 @@ ADD_SCENE(flip_tdi, main, Main) ADD_SCENE(flip_tdi, menu, Menu) -ADD_SCENE(flip_tdi, wiring, Wiring) +ADD_SCENE(flip_tdi, wiring_uart, WiringUart) +ADD_SCENE(flip_tdi, wiring_spi, WiringSpi) +ADD_SCENE(flip_tdi, wiring_gpio, WiringGpio) ADD_SCENE(flip_tdi, about, About) diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c b/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c new file mode 100644 index 0000000..a783851 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_gpio.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_gpio_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_gpio); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_gpio_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_gpio_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring.c b/flip_tdi/scenes/flip_tdi_scene_wiring_spi.c similarity index 65% rename from flip_tdi/scenes/flip_tdi_scene_wiring.c rename to flip_tdi/scenes/flip_tdi_scene_wiring_spi.c index f4014ab..abdaf60 100644 --- a/flip_tdi/scenes/flip_tdi_scene_wiring.c +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_spi.c @@ -1,19 +1,19 @@ #include "../flip_tdi_app_i.h" -void flip_tdi_scene_wiring_on_enter(void* context) { +void flip_tdi_scene_wiring_spi_on_enter(void* context) { furi_assert(context); FlipTDIApp* app = context; - widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring); + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_spi); view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); } -bool flip_tdi_scene_wiring_on_event(void* context, SceneManagerEvent event) { +bool flip_tdi_scene_wiring_spi_on_event(void* context, SceneManagerEvent event) { UNUSED(context); UNUSED(event); return false; } -void flip_tdi_scene_wiring_on_exit(void* context) { +void flip_tdi_scene_wiring_spi_on_exit(void* context) { furi_assert(context); FlipTDIApp* app = context; diff --git a/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c b/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c new file mode 100644 index 0000000..a849ae1 --- /dev/null +++ b/flip_tdi/scenes/flip_tdi_scene_wiring_uart.c @@ -0,0 +1,21 @@ +#include "../flip_tdi_app_i.h" + +void flip_tdi_scene_wiring_uart_on_enter(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_add_icon_element(app->widget, 0, 0, &I_flip_tdi_wiring_uart); + view_dispatcher_switch_to_view(app->view_dispatcher, FlipTDIViewWidget); +} + +bool flip_tdi_scene_wiring_uart_on_event(void* context, SceneManagerEvent event) { + UNUSED(context); + UNUSED(event); + return false; +} +void flip_tdi_scene_wiring_uart_on_exit(void* context) { + furi_assert(context); + + FlipTDIApp* app = context; + widget_reset(app->widget); +} diff --git a/flip_tdi/scenes/subghz_scene_menu.c b/flip_tdi/scenes/subghz_scene_menu.c index 204bbae..a1039d6 100644 --- a/flip_tdi/scenes/subghz_scene_menu.c +++ b/flip_tdi/scenes/subghz_scene_menu.c @@ -13,7 +13,11 @@ void flip_tdi_scene_menu_on_enter(void* context) { FlipTDIApp* app = context; Submenu* submenu = app->submenu; submenu_add_item( - submenu, "Wiring", SubmenuIndexWiring, flip_tdi_scene_menu_submenu_callback, app); + submenu, "WiringUart", SubmenuIndexWiringUart, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "WiringSpi", SubmenuIndexWiringSpi, flip_tdi_scene_menu_submenu_callback, app); + submenu_add_item( + submenu, "WiringGpio", SubmenuIndexWiringGpio, flip_tdi_scene_menu_submenu_callback, app); submenu_add_item( submenu, "About", SubmenuIndexAbout, flip_tdi_scene_menu_submenu_callback, app); @@ -32,8 +36,14 @@ bool flip_tdi_scene_menu_on_event(void* context, SceneManagerEvent event) { if(event.event == SubmenuIndexAbout) { scene_manager_next_scene(app->scene_manager, FlipTDISceneAbout); consumed = true; - } else if(event.event == SubmenuIndexWiring) { - scene_manager_next_scene(app->scene_manager, FlipTDISceneWiring); + } else if(event.event == SubmenuIndexWiringUart) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringUart); + consumed = true; + } else if(event.event == SubmenuIndexWiringSpi) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringSpi); + consumed = true; + } else if(event.event == SubmenuIndexWiringGpio) { + scene_manager_next_scene(app->scene_manager, FlipTDISceneWiringGpio); consumed = true; } scene_manager_set_scene_state(app->scene_manager, FlipTDIViewSubmenu, event.event); diff --git a/flip_tdi/views/flip_tdi_view_main.c b/flip_tdi/views/flip_tdi_view_main.c index b001e03..6705604 100644 --- a/flip_tdi/views/flip_tdi_view_main.c +++ b/flip_tdi/views/flip_tdi_view_main.c @@ -1,22 +1,9 @@ #include "flip_tdi_view_main.h" #include "../flip_tdi_app_i.h" -//#include #include #include -// #define FIELD_FOUND_WEIGHT 5 - -// typedef enum { -// NfcRfidDetectorTypeFieldPresenceNfc, -// NfcRfidDetectorTypeFieldPresenceRfid, -// } NfcRfidDetectorTypeFieldPresence; - -// static const Icon* FlipTDIViewMainTypeIcons[] = { -// [NfcRfidDetectorTypeFieldPresenceNfc] = &I_NFC_detect_45x30, -// [NfcRfidDetectorTypeFieldPresenceRfid] = &I_Rfid_detect_45x30, -// }; - struct FlipTDIViewMainType { View* view; FlipTDIViewMainTypeCallback callback; @@ -43,8 +30,7 @@ void flip_tdi_view_main_draw(Canvas* canvas, FlipTDIViewMainTypeModel* model) { canvas_set_color(canvas, ColorBlack); canvas_set_font(canvas, FontPrimary); - canvas_draw_str(canvas, 34, 10, "FlipTDI"); - + canvas_draw_icon(canvas, 0, 0, &I_flip_tdi); elements_button_right(canvas, "More"); } From e1e8783dd02cddcdc95915f4fcc57cb1946de40e Mon Sep 17 00:00:00 2001 From: SkorP Date: Wed, 18 Sep 2024 12:34:50 +0400 Subject: [PATCH 18/19] Add screenshots --- flip_tdi/.catalog/screenshots/1.png | Bin 0 -> 4132 bytes flip_tdi/.catalog/screenshots/2.png | Bin 0 -> 5523 bytes flip_tdi/.catalog/screenshots/3.png | Bin 0 -> 5059 bytes flip_tdi/.catalog/screenshots/4.png | Bin 0 -> 4830 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 flip_tdi/.catalog/screenshots/1.png create mode 100644 flip_tdi/.catalog/screenshots/2.png create mode 100644 flip_tdi/.catalog/screenshots/3.png create mode 100644 flip_tdi/.catalog/screenshots/4.png diff --git a/flip_tdi/.catalog/screenshots/1.png b/flip_tdi/.catalog/screenshots/1.png new file mode 100644 index 0000000000000000000000000000000000000000..955105f2ed60ac4ac821b5c5e05e796724cb2507 GIT binary patch literal 4132 zcmbVOc|25Y`#*$G*;2MJrff00ZDftHWM2|xX*OeunQ3MiOO}#QlqE!ViIA-*gjAZ8 zY$2txRS#vEkZ4N3@jUhX-aq=hf4t{&?sK2}I@kAFzV~&V&zO%N($c^TU{a=RF>lbrRCac5h556b{)Ww5L)Av?a5C)+u(M!7SRsV>Y}AcjxrXpAOc z?_t&KR~sQL(e?CMT)qmO-%oYhDJpZ2^D72~-1XTepknUklcF9VWSZldM1C1nx} zRP9orjlJhC%V%l(%c}afX_!ksO+;f;x0!IY>q{=byl&K((1|X)>Gl#gzvFYGy^8h2 zgqnmcsHR+P$uX%qt8?%Stx5gK5qB}vCP3OYP=@B22-cHCBq~e215dsO#%)CWPP{&( zJxV_SkbV&}s!Oh^oWmGSXF)*RJM?g7{VC&FjHRLgu-lVg(PjW6mUAOL}T2(Qt+R zLG%vU$S~_`LC&i9gY~POO*<0@RwbSBNil=Ld(EQeHfxAVg8I3gK0iySZL4rlRM^Dc z^z)3L3;I7u9i@}1u~#_g9#xGqJyMG(nXeCfr3(4@Z20%4Y<^;SY8x{YyL)iZG^;)SaL4^lfdHzmWj31=&-xcbK+cK_5Q+*96 z$L$+m;^?mG8Ht+4{v{D3At_&cP8PiNq2Ooh;*`)c7ptt#rHEeTBV(FVP2Q;?&vk>S zF0#^s7;CxWK$rY-XXz~sLJdj>PghteS_Kz|yXd>%%e@Azb>wa&3|o&Dw)RO*3ov&! zOZc3k?7v+CE2fsH_thX|w{9y*uDc(K10!rNH(ZwLHnCUuu*+Wpbs_r!u5QsYvPyAS z`RPN^+PIOuvo5nxNPM?Pe)z0o!MVRnF|WueM*cD1(;DiQ%N#?5U8vk$B1ydSZ87T4+*e zwt>}2yEK)VRBxO9Ju2kQGZHhqW}q{w+4jTWeQ70+`4`3Ls5SmI$+ctw%zc~tcBdhA z=EYXU7C4*9raiu{ZUeUT20}F8`t783WQ}H*)lh z`;64g?tw#B%Lui|!$=A;Fq=O+7AZq`q$!Nao7|6e(Q}jM0k^wvJ$U6K#-VtpTYHI^ zGl@tfWObeG@_#z~I6qUdR7D4pBLj)^IIz!A;acmF;#&{NBBZVr)f;waGrONcG8mVp z9`ERC=z5bGq3)E$N_WZ}QytU%>C9JJSSC@PQd;XbQGCDK^`q-(X;mr4?jo$UOjzrq zrjTa!L8J1K8iTT{Rqu)}RlAom&B<2T*JjIeuXMSB$HB{esLt4}(Pb9O7?tUj{1U>bZ)#Z+T7oXgJYCK@kvZoV z!aj|gAk_^OvnOLqGgQawhv=^kJlH#2F?{TdIZ*(9KsM_}|8UQ+LY0$OdqdqV$&p>R z*Padc>AhS@m>ZW1rq97?nlp)H+*F&$jzdrJsWUOs89G<&m z8SyQ2esR}l*F{{IZp5?jBVjd5^Iuff1i0>8qm`kh_uLR}N7UP>mLE!0ecl~M`e($y zw|-}FO}F-md2Z+6L27VNts2KRh3xF>><3phP+PU(e3JMpWgvxHWLR{+FCX4jJ6-#s zc8&IeQ_txao=OOod;#78Zc6oW87Z$VzgFIqLDBkJm7CC*08LWzEp6Q!F_bYRTIuFd za3M7b`zSX7!#>MaN{sAXCh5Wdn!G!CY|7sYTGd)b3bYPEN95qynp5_VMmw`Qom59u zXB{s)GHt4CawnQ;l})fTqx6qyAJTuMi>pQ-5OE^gb(TK5mjAAclNQ!WenNgpj-B~7 z6E{EG+W zAG-2DpCzNQBT!p*Y_hJj)VVmt&!B!NN89xzK1&5_x{)- zDMR@pv?Auc+!VT0v=@?0Zjry1bcYb)%Rhd+o_d>fSc=^ck?Vp6tZrqsnX6)d~ zPd@rpRHiso*$DLx&RoCt>6zW_yA+?6*2$G|+B7FGbvm_w-|Rj&mfL*WovbkG#QLMn z)0;8krAi(~t|tdMH|KkUN-|5z*xu}K%9~BwOnc;`SL`0a7Su)aQwrSeE9~{0vK{hr zLJHbGGv7Z?B+c~h^cRiJSZ`Qq>RD2bVtl_5t%nPl6#Us)HfHDhq}^!w*vrcNrkEz{ z8aF>!w@hnlG-DMq6*ShuVWn^JTBuvlGM$c^UY}n2+)*~@RIo0%>GIO|OLDf6ykn*a z&BluX06SGT2A{3#9u7|^$HaIt-KhRd7M4x~%n4K+5o8;LB@tbTSVGu|E}{_tYzig2 zdon#8><#hMASiZ22O1hg^C%T0@y5qx-ddhTz@$5QM%V5@m=$gMM9L zoX$p-$`~BLWYP>_FcymiWuc%{IthkAqtP%p5{5)Vcp4B!7=?)qg-{r~ ze_OC5GVpXVjY+0bKpPgZIBGD{7|aXypDcoC4i5hsrZ9eS#bXT?ilxC2P&h0oXd|`1 zwHZuT;{S*7M{S0C7>x*XB{HbNbUbfM{CEEb^XU8Ujy4o|(HJ_>$-I5R23k__!9hd{ z)7H`$%=-c*kO_wVaD)zCSDyeO=pxY&9X%o*Vt_#CKzK4;1H3MrNYM5FZRbyTeKg7v zZJ}?z-^$7gfxuYq*U`7a=pv8^48jV7z@UC(Z7B>UmVzh#_D$yb{)I*VSFE80orq;p z>F!i&;O|0k4xlori~uSPghW9RAPomBo=n-0Xm0G%KS^5>>EsY1!HP}|0{u<1A^8s> z;0P$9u7SQkgn-r6gXrMkL&)R!_e<_+Ib2wT4K;jfxI z+Nn4}n^RvJRL@Ahy!0Wh~C_{{Y3)5&{4K literal 0 HcmV?d00001 diff --git a/flip_tdi/.catalog/screenshots/2.png b/flip_tdi/.catalog/screenshots/2.png new file mode 100644 index 0000000000000000000000000000000000000000..5399a6861242754d22b2472cc0a0cefce15cecf1 GIT binary patch literal 5523 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 H?l=Ai1kvB( literal 0 HcmV?d00001 diff --git a/flip_tdi/.catalog/screenshots/3.png b/flip_tdi/.catalog/screenshots/3.png new file mode 100644 index 0000000000000000000000000000000000000000..b823ed23d6a5b59fd15213ef95962672ee350f5b GIT binary patch literal 5059 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 literal 0 HcmV?d00001 From eb79eadae028e0f6633d1f6e25609d659b0c76c1 Mon Sep 17 00:00:00 2001 From: SkorP Date: Wed, 2 Oct 2024 12:27:49 +0400 Subject: [PATCH 19/19] update image --- flip_tdi/.catalog/screenshots/2.png | Bin 5523 -> 4457 bytes flip_tdi/.catalog/screenshots/3.png | Bin 5059 -> 4042 bytes flip_tdi/.catalog/screenshots/4.png | Bin 4830 -> 3910 bytes flip_tdi/images/flip_tdi_wiring_gpio.png | Bin 5510 -> 4682 bytes flip_tdi/images/flip_tdi_wiring_spi.png | Bin 5130 -> 4256 bytes flip_tdi/images/flip_tdi_wiring_uart.png | Bin 4855 -> 4134 bytes 6 files changed, 0 insertions(+), 0 deletions(-) diff --git a/flip_tdi/.catalog/screenshots/2.png b/flip_tdi/.catalog/screenshots/2.png index 5399a6861242754d22b2472cc0a0cefce15cecf1..0ee74728971008cc1eb203aaa510c74eb92b98d1 100644 GIT binary patch delta 1743 zcmV;=1~B=PE9oMzuL^$#q)9|URCr$PnoDjYM-W6sFJ}PneF9%__J;Sqpikhvftf9> z7$|^nB7gO1vDM-O3Qbl8D?gQ0-IRE{|MC0%rNP&~{&{;9^Znazr`O&7{QKq0*I(ay ze4z8#?MnyvTk*Pv({cUsc73v$AK(8C+Xz_Hswvh^W$#lLfF^&bO4JP4nP~R)4BWlA zMyCg2fDIG{uF&C{#Wqy{>Oka-I;yWN9Mk5({!9a`F4CgEN=}5%9nDX0G~e&{)=}OC zx0uQ<=-i5i3jl^k9H2e|V;tAl8PM!Vz|X~bEdsU?)aT3>%zL}thUfR_#0;qG=%;T1 zyA}a+j0RRgT}OY9B48U~WN!O<2IP^A?-f1^(2h0`ophh_TR;X937yA?FT5XHU_Y~A zspbM0>*_3@_8}09;I*AF@wl?h63u0O?&`>=D}ddLT)_p{fj5_Tw2#|a2>^q(b;xu&7U_S~mcF)dOq&P$Gff1N$wlVX zf}I()8LYpN&)JyA)GYuN#}=WEW)5Kt^JE%AU1nGP($^W#bnGkK>AZT-zP24y^I#nD zuw_gYKxc9~*$tddS5$LVekomDM0Vr?&QG6K?dg0K%zKru;R4W!D+4+f>8z%s1DP(H zhge;uSL}bnXNBM`*2ryuTJ@JrQJqI*bNjb|DmacmN_PbBaSPxHqRpeLBZoZ%UPad- zIsrJRwK4luv|C7B9XRYE5L`uP!H(yyk9I#E#9QF_huA=kc(?72k?DDA1 zpv9UMZC6JQdkDOWE+c#R!!t+JW?+$4McdVpsb_xxUNZw%(RtW~&kA7|EYPZ8yE-y; z0`Qs{xQZ^Ly8r9Evo=V#P`f%Zbpmt@Ttyc#@({S|7eU=aV;dXUkssOqMuA+;m7GLL`wLwrows~}QWa=59W8f+}3y%D#9gP-KMpp-x zPk?`ntLO-Jyf%ELvwg(-{?9f8eAFV1eD3PV)I;D-N6xOB#TJIPRn+f{j&AA%AUhoa zXV=ZJ>lOhYQKW2<&s`mvdIsnixQZ^K^BF9#Z^yGA*BL6?q^*B@6}1Y$vfYuB>qtjmE%2ENVCf<7Ne&j; zwzKgpfB{BN3+!@jXXF$Gu=`l+fVpn+~M0bDx z$|;cLU^TXfo&Z^3pgZnqFt(Z;Q2CI-9lvq{WcF8lBZn5iiu2WHqj4R}=I@aCFv`sC z@5H#uasj{zIJ1*Z;C4J4do;Tje`kyjxdlYQ0dEX_QQ+Cw3Z5S`%pCuoZH;p$TM7V9 z!MRmzfEsj`5Ic4j^x==K!sY-R8RviW{{k$T)Ue%0s^ekkJ5-T>KH}chtD+^)=gBI7Iw@`!&gu1a$@0K8zrD75XSX9k)r5AQ-F+bHCzYQO z`nk#m>FP!p$wfvt5A8n$aY8twz_D&0=Fp0c=A({PpXn6nx`A7=_&mQo+ub~@^hGf% z?8CKU>hAz>1WiF=^qAs%?c;xkrO`4}8RdO+2X-1$g)*r@P+46GJ?dyW$ z9Zh{<9xDGV!_NR`XV%Y;5?_~YJVOQWQJ5`&D}fWN^Ni6hfG5{vO96jy_vp^k9p&}2 z&(rC>kyY&VMK)IG&Iz5yFl!%iA)d%C$G!L!`q1^CZ21-dL!NQ#K$Iv?iy$>n`<{67 zty0aYaq8<)bWwrLgB_1@V0{?L$+oY7-r{=6iTZ3mg0~64drbiBd3IOvd{orlify}? zHV<~(%L%aljO1iX0YpazZ6Dru`hZvI7TVRMtrGcjG#$G~W{3b2J^h`*ZU0@*h9a8AtCoh7JG#002ovPDHLkV1kqNYdrt} delta 2789 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 diff --git a/flip_tdi/.catalog/screenshots/3.png b/flip_tdi/.catalog/screenshots/3.png index b823ed23d6a5b59fd15213ef95962672ee350f5b..c936f120ae98b862fe00b0758302be97a41cd97c 100644 GIT binary patch delta 1325 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 ImiUAJ25Rco6951J diff --git a/flip_tdi/.catalog/screenshots/4.png b/flip_tdi/.catalog/screenshots/4.png index 3c2e106aa75aa3ce88f220033bd97534b42b6561..2503f9ec9416439e144f4e6374f3a6f3805a8b20 100644 GIT binary patch delta 1192 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 diff --git a/flip_tdi/images/flip_tdi_wiring_gpio.png b/flip_tdi/images/flip_tdi_wiring_gpio.png index bbdf6d911ac842afed39acb160407bae81e5693d..e80941abacea5f03b30664ae0539e7483513c2e9 100644 GIT binary patch delta 2010 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 P7uAjG=~V0h3pwy_>>$~r diff --git a/flip_tdi/images/flip_tdi_wiring_spi.png b/flip_tdi/images/flip_tdi_wiring_spi.png index 3223ef73e2572c9b0467d406ffb0bf58adcb232e..ddb95e603b36d63856557883c426f475e3dda07f 100644 GIT binary patch delta 1581 zcmV+|2GaS8D4-#bB(XGR3V#LwNklv9nXQ_>wjl};rPv(6 zp0TsDQnZ^HFmS+tJ(v5R*Ut_3Jb(S}Qoa28aXX97RjxxYTIY%`NAo@%EpT^vzh8dd zT7Ns*b_GDfYm_uOamwF`p-yK>1puXb{>b0b8amGB;pOfDfDu2jWRW!9Oz47k% z`&$su_s*Yi0C1geIbMQ6wbknjh=yyh{M`eU$DIMZ)TL)5J0EPJW~e%3%y0m(RMZJx zgVoCpR<;;ob!QMHuxZxw%$L7s>o#Nj`?U(Vg;p-q09v!20^!T^w~w+n!&x)NOlN=@ z%Iq?xp8Y%b0apMdqknE^r%RLJ62M(=3okzJ^#-yr?A6;SUDA(lY!RJ3IYvL$mBA&wSwWzANf5*_z8oAGXS~- z(rf$Pa?r~XoTm93-7m=E>80_9ZUQ;#x#lAqAw57NnSveaIe%!B<9l+fCXmX*Zl7rx zK+f78DA7!DB*0e*FwjQppRVi3tVJ^g_YJdyp`}l&1kgs%Zl7=l&<3{F!;$XZ+b$-V zS`NY!bq2Bu;4weTK0rADdLSd+eI`>x9UGzdRsl52TYnKbtI&o6fL8&?MG&;R*V&>4 z4l>>dV0)$L;eYo5GCjbE`vFL9Xi|y(5S_a<2#AX3E>OL+L6`jqq~X_tO8v-VucM0DOhbyW_{B zqozwYp7Q@0yKkQJ(e3a^;GIPq?g1i-X1aS_e|5l-e1Gd$L@$fh>P5WlRh78{|MK?( zXpp@75=Ma9lHoOnB-_g#BlH_NMkoM0!RT$$M3)8g>;OP+V)1ZV={y_&*_ntsY&2a3 zv@T>`aTeH0@2%|Ntb}Ez$hOQAki_a z^ebJvsM=BrfRTMb)sUn4Wfs^i?Mtjm0Z;|qo88{|s{Aa!cl{ze?iqYj1EAu-3hxm> zqSrZ&(4``yv-z0cSfoVB&wIk!Kkya*p7gS^_c# z>=qb1H-O}yQvzVs)E^Dl-JO4009ZipHuJBoVC6;2fsvWMQUE+s6Ifmaa2%ikfYL?N z@kn~~jv%@pQ9lp3TUodQz=#|)8=-U~gLCv)r!;}1jkw0kBLHOpiuCKVCj#!}`reBo z*?;lgAgvuiwBE0lN>P1Kx-&z^^PyP+h>49uoPVZ)m+U?Q@|8YTCjTb@F2{e~aB2m@ zTMyu+34%r5`D!hHjyQ(8C$kju?6YKBHlsSCbS+$o-+LuZ4+r)+b$8JFqWKzlOYus* zA6ZL1JzM%qK2#r+4t4fOIuwVeXpQGG!GGV}0P^=oWWBE(BpuH(IwpT>UOLA#l1|Fz ztTcK{R_f2cSs$r49itg2pC4>tqxH7vP@7x4SsYUP0P+P19JT!39R0scQ=HD|n0%^N z^YSa%DTPToOD?~%Wz_7E@BfiwTC(}Pr=!KIk40?m;ShaVIu!t;z}{sX391FKSAUCF zwlfs~GOf#5pn=k}lV`qeY%DsKA;Goe>g-VkTfPKJW;074OO~Z0a*XWY664w2!yy`s z&`u!N+e~*|bx4#SZGQB)S-z-#N71A9gaY6w<*RZsfx7bpo-Mo(9N++`I_2VD6M-QC zz_Uk2ob~)XmlFE!J!g+X)slhoTYq%um@>tp>9(R*kC)}6y34UrO`ukWAC=My=oHif z%6pMi&v+!wMS=Imly;t7I<$OGca6o60BALd7C5bA86f!;b5WzcfbevY@`X#rL?WEQTdG-o fBC=-CM)CLya3o5QY((es00000NkvXXu0mjf9~%H` delta 2460 zcmbVOdo+~m8vkb838l?uS4@Lyr!hB!nQchMtwIqH`MUHK! zi%W=vB*v|aOP6h{t%jmpN|91FlEO|+_Dr{P)>*Cd$NARxt@Xao_xt^x%lkZQy#*@| zuKAaZp(e}S&m91&5AS>zm{q-!tq%cu0-taZ0GQdTKbUWj%@hCt7GlpZaWJ1LPNE4x zz@5R50TI4j8Vd{pX$)z?4bTMuG$d?lm^jRDGl|aU+SAk+dkI&dVgrDSn?yjPb3id7 z24t~$WaP_BEl32LK}LoW{4jn34=|SPlPm;-p=5t5J()u%GLUYr2p0)SrN9NnG=zj3 z&l8a(WaLL-l4`9sqmhV@5HW|0{1jD~-xh=iUkD$5-Aj*N^Iiv=V!Iw>j1J_&En7qZY; zB9Vy3;Ltc6N`*j)q&zWAg5rs+)e>q23Mirr*#a?}&qJv9v>5&lF&U|f_EQ>MfuG-3 zVV>x7wN!P3&=Q&ejkU+1xm>9SBI3fSS+ve+!6qg%B?2|7svrq#sSHu2e6-j)ZFSb*ey~YLtYkk*W61cnAPm zxxN&4sw8qEr|Xwo=S7~SqY3i5w+o5~)S4=}=#uafk7Ewvgg76e2@hyU{P3!8?P zX65Tw_n@Ks3g}U@QmLE^>BvvUFTaspDP7og*=PDul~o~iWTOn`>gqZfqp+Ly0dj6t z2A$rOY1!XbI%+msCOO|!f;TvsXYnNQ(nF05I%uiax_J_GSiU!jrf`=fS0=5V@2wg| z6=YdJnsp@v?Y)ntL$uVOw#?DWxowWRO)_ZMqA$96?emSyMKhWCp(FG9e=$-~Vc!jw zdz7xrgYUU1y4(c2Ss0sap?jL*{oySdQy74e1Rg;q;>TuE<$Qg^nh(&?H>L~=)e z4dJE3v69VNY5uymYlbs>2H-2)YCPPIR8mK_uRdtHdF3gX`*2ul`}26M_|{zMn=NkP z5ESwx5GLEMY}NLBS9xy3=#Wop``A?P-Mo^c2NFMI8n2x?0|ai3@Rl2AZEZ3`?JrVn z%t*VfJA0ur;c`LfPD7Uv;OE^9$`h}WCo{uKy02uKWJ(85P@Z64g_KX%>TEHW44lk! znJk^U=)H+GB9&92o=O#Ri@ zyLI+PNoV$=qhN`{^(Soue$$v63jg&$zNKU63BlIL>bG%y2F~^5s`e&0rhTw_G|j9r z%C@Lv)Hdh7Rd zHQ_bdrW%Jr0?In3ECGhNp*#Fg?7Hj^2O<}i^eIgRwU1eyJppAmhvoop16QciFuKl^ ze|#gLsSTT7+SL<9)Jv=cntFC!?=d!i6TDSM2%XssWc;wR$P@@VqDt6inJhcOlB8J)iS~uWjp~Rkca?2o0MCP2e8a(O`WIYR1NaO1gjtp&6 zNU7wpGx_?_En~N?vIY_)w-N-`^}DOfJi_m^VZHKyPkA%Au0m-9uws3O>*}j>-NV{s zrTec_v@0z8Dg1qZ?k~_BqbjaAhOe#O25-gQ z3#>Oh9Fs%3b(fwROh%<>D$DT(+#9`5hWd`NpO~*1B6&TFY@KfD{{BUaLF+Yq&cP;& zI{D4_HH=aD8dy2z1i+&AXf`^p_Ry2fWem3UoU$mjyUAfi6hlX8&jt^S;|ohgVVw7s z;g|JlbBtv-JkGv88;tapUJUno$>~ZteLGlTSK|yT+B2keU*zBIIP8-)n*P%!h1m-J zM0Er6h@y3kZoT3CNU*VCVJPGdv#p1%RlJP)7% diff --git a/flip_tdi/images/flip_tdi_wiring_uart.png b/flip_tdi/images/flip_tdi_wiring_uart.png index 264e3c4a7448abf12cabc25ad2d23d025d461ba4..251641fdc24c79893688298ae0a858f3a0d45694 100644 GIT binary patch delta 1458 zcmV;j1x@<*C8i*dB(XGR3V#KLNklA#)-#H zw%rz=RBbQoB+hu^*x8R>e!u^{{O5n4zkCZ3c>DSNg_WB%cDY=_zQiOV_v!Q3sjTR< z`gdspywHXf0{nXWF~nR978!*l5eT6BllJ9*5F_e~A;&8t)G~h~Ab&Fyj#Gcn-~{8z zab!p_wzMw>m!rtZaYn+74kFtGP>8`8GowIUW++^B7IZ5wUV)j-jQ7au(gZMw0p!Ri zF#bvi#TZ@G)7~O$;7Gm+U=U)ABC{XCoc(uaeue*MXx4+W1VQrch|aXvxyl| znt1?#uDyXS%Nm89$*de>bS={{Ko+rgdAMmjK*B5yg>G(Ar3r8on9tiu6QF1t`(Q`0 znY&l(7~sZ1pzV%b+gZ6RquK*x5xd3!&$3t01Sk+7^AvHR`I}a00u(R#6?`u*&_Un{f&{hds42S1;C4^DzAoRUUDVBUqU?^ z5Pv<+@M~DG@-s zD-6Vz323%VfI^uDtty9rX{Q0anKaVdGa`eHb?;d+z~$?4k33h@HnkIowWktYM+)pd z&jvrX-)g#4W{@ml)Gzi0^fs=Sxoy;n0Qf1l%zz`~BjBCM>Hb#wwli68DoOwLyq95!oYg1eksg`=p4(## z92s}TsMY@M?DyJXc5SnmJTw8A*(aJc2xj4}Nx<~$UC#B!msytn0Bu9}8}^18c>n+a M07*qoM6N<$f(e%7e$A&&1g0|0QtxB@I$ zDZvVaY7|5wN)Zah$%Rssj|!2(+zV(30G3)=iU1QNY+{R*a*EK5p=jkQoDD!oxK^sG))j3|5f^2PSqLdS3<}Y*aRoVw z2_daKN17oLLwBcRHG1+0+Yow8VzEyC@3PNix?82 z*bk!9=r9$=UnUu5vgr&q%z~C45*|>eMkH)Lhqn|Dw?avo7^Y%VsRacElmZ4tsg_b< z7K=rt(W!Jg8Ap&cg$hilB`Y+ZW(l(b2i1txG8HCMDj>5h6e;ttP!gW)n=<68goG7g zg=VQ+c;9qXtx!dUDKx5FZZ7UMT!Zn^U-S4+_zGH+Qm8_yd{m>%Q;YFkk$5hX@dmy% z(hAiK#M5DKQOl$tb9HeF4JK5G z(dFPWJopls^*_n%Xf-Otl)5ilC(-U+pDUXsUJD_A8;bPEPYj&{oEtyJ54<1XBS}@)Yyr4 zfplda3`7;Vo;5MXCyCveV>u(?-(GRK>+225u!;#sThdxX&~3>9;?DNNM5Al2t|H8xVDS&`K;GH!=l+5k1IxBP{c(GQYsdIk?PYC; zn)}l|z>P!s-6^YLBdm=QwyBfg$D^3hSiQrhtw*qpO?WvvE&EMVr+A!+b zMSPkwmO~-bNk2J4*Ms_apYCBOYC9Se5A1mC?A4s3ls>)%ZHB+`26Ej`hp!zo2j@P@ zV3i}EZ;_~)Gi@vPRUUBKW^rv{>gaKMmBpFMt&7hNPWU?hyfzBe zTV6FhvK3lkzpWa~nk;wL>&i|pUg{ON*9;V&3G+1^P2~9!e0hOos?n_7nLV+IskkU@ zy7<0_!DvHo&qo@a^<|Sgn)Xi28XABCnmBpUGx?Q!zwyARr)Z|jdVXqTrs#p&o`TSL z+kT^ue$SI4PUGr*34M3gY`PTdSQToZS|JtBk~2{pRELGv5r^ z>-Sr|7o0Qm6Su_5_0#9?M11{WX^GVilI!fg(iRh#9JnBrmOJ-*ZJYy+gk45iuZeH* z@z_y^u_lr)!AqCcv`9AW)h5U-9B8w{J^dpwa*OQ()3u_r_THep1$g~pg62JP8i2cg zig+C3RJBERP<_@!>#rpvV9k$BzHJ)@U1s-4`J>@~;RsQPoLo9uk_?E1loQ6m`r?Ab#kjqqZ4 zeCE?}Bae8oQ+A$y?e8$AYoCC#-JiG@DjTjp0*=+|42ttVP6ZyOoPev#8sA@gtK0Gv zMJ_p~(RIhRcUI2rt2;3q9CNOusrbQeBT)0dupJhSR5k!){)4X^T#nqNCFRuDH})=( zx@(7@2m~fq*|=-$-KII=yvG^l%L2kwNY}Ab%#q1k6AOnpKS*OiU+p{nBU2MzF3&?7 z?Tp$@C+-kQS!tc1jc(FEOKpudsb!0)!Fd%;Q-+QO=MJ`2Te#f=_o>Vq hlY04ZFPxsfXz@+^umWuH$u(aG;<%eRUq)r@{5RRsX9WNN