MX 2 лет назад
Родитель
Сommit
25e960cddd
8 измененных файлов с 635 добавлено и 264 удалено
  1. 143 0
      app_defines.h
  2. 1 1
      application.fam
  3. 491 165
      gpio_controller.c
  4. 0 69
      gpio_items.c
  5. 0 29
      gpio_items.h
  6. BIN
      images/analog_box.png
  7. BIN
      images/digi_one.png
  8. BIN
      images/digi_zero.png

+ 143 - 0
app_defines.h

@@ -0,0 +1,143 @@
+#ifndef APP_DEFINES_H
+#define APP_DEFINES_H
+
+#define GPIO_PIN_COUNT 8
+#define ANIMATE_FRAME_TIME_MS 133
+#define FRAME_TIME 66.666666 
+
+typedef void (*DrawView)(Canvas* canvas, void* ctx);
+typedef void (*HandleInput)(InputEvent* event, void* ctx);
+
+typedef enum {
+    MAIN_VIEW,
+    CONFIG_MENU_VIEW
+}enum_view;
+
+typedef enum {
+    GPIO_MODE_INPUT,
+    GPIO_MODE_INPUT_PULLUP,
+    GPIO_MODE_OUTPUT,
+    GPIO_MODE_UNSET
+}GpioUserMode;
+
+typedef enum {
+    GPIO_VALUE_TRUE,
+    GPIO_VALUE_FALSE,
+    GPIO_VALUE_INPUT,
+    GPIO_VALUE_NONE
+}GpioUserValue;
+
+typedef enum {
+    CONFIG_MENU_MODE,
+    CONFIG_MENU_VALUE,
+    CONFIG_MENU_INPUT
+}ConfigMenuOptions;
+
+typedef struct {
+    GpioUserMode mode;
+    GpioUserValue value;
+    int gp_idx_input;
+    bool changed;
+    GpioUserMode prev_mode;
+}GPIOPinUserSelection;
+
+typedef struct {
+    int selected;
+    enum_view view;
+    int wiggle_frame;
+    size_t prev_frame_time;
+    size_t elapsed_time;
+    double result;
+    double freq_var;
+    double elapsed_var;
+    ConfigMenuOptions config_menu_selected;
+} ViewerState;
+
+//  5V  A7  A6  A4  B3  B2  C3 GND SET
+//
+//
+//  3V SWC GND SIO  TX  RX  C1  C0  1W GND
+
+typedef enum {
+    PIN_5V = 0,
+    PIN_A7,
+    PIN_A6,
+    PIN_A4,
+    PIN_B3,
+    PIN_B2,
+    PIN_C3,
+    GEARIC,
+    PIN_3V,
+    PIN_SWC,
+    PIN_SIO,
+    PIN_TX,
+    PIN_RX,
+    PIN_C1,
+    PIN_C0,
+    PIN_1W,
+    PIN_GND_08,
+    PIN_GND_11,
+    PIN_GND_18,
+    NONE
+}enum_view_element;
+
+typedef struct {
+    enum_view_element element;
+    enum_view_element opposite;
+    bool selectable;
+    bool editable;
+    bool top_row;
+    bool pull_out;
+    int gp_idx;
+    uint8_t x_pos;
+    uint8_t y_pos;
+    const char* name;
+    Icon* icon;
+    Icon* selected_icon;
+}ViewElement;
+
+typedef struct {
+    uint8_t element_idx;
+    const GpioPin* pin;
+    GpioMode mode;
+    GpioPull pull;
+    GpioSpeed speed;
+    double value;
+    const char* name;
+    bool unset;
+    bool found;
+    bool input;
+    GPIOPinUserSelection user;
+}GPIOPin;
+
+// GPIO enums from firmware/targets/f7/furi_hal/furi_hal_gpio.h
+
+// /**
+//  * Gpio modes
+//  */
+// typedef enum {
+//     *GpioModeInput,
+//     *GpioModeOutputPushPull,
+//     GpioModeOutputOpenDrain,
+//     GpioModeAltFunctionPushPull,
+//     GpioModeAltFunctionOpenDrain,
+//     *GpioModeAnalog,
+//     GpioModeInterruptRise,
+//     GpioModeInterruptFall,
+//     GpioModeInterruptRiseFall,
+//     GpioModeEventRise,
+//     GpioModeEventFall,
+//     GpioModeEventRiseFall,
+// } GpioMode;
+
+// /**
+//  * Gpio pull modes
+//  */
+// typedef enum {
+//     GpioPullNo,
+//     GpioPullUp,
+//     GpioPullDown,
+// } GpioPull;
+
+
+#endif 

+ 1 - 1
application.fam

@@ -7,5 +7,5 @@ App(
     stack_size=1 * 1024,
     fap_category="GPIO",
     fap_icon="icon10px.png",
-    fap_icon_assets="images",
+    fap_icon_assets="images"
 )

+ 491 - 165
gpio_controller.c

@@ -1,6 +1,10 @@
 #include <furi.h>
 #include <furi_hal.h>
 
+#include <storage/storage.h>
+#include <toolbox/stream/stream.h>
+#include <toolbox/stream/file_stream.h>
+
 #include <gui/gui.h>
 #include <input/input.h>
 
@@ -8,160 +12,506 @@
  * Just set fap_icon_assets in application.fam and #include {APPID}_icons.h */
 #include "gpio_controller_icons.h"
 
-#include "gpio_items.h"
-
-typedef struct {
-    int selected;
-    GPIOItems* gpio_items;
-} ViewerState;
-
-static ViewerState vstate = {.selected = 0};
-
-typedef enum {
-    PIN_5V = 0,
-    PIN_A7,
-    PIN_A6,
-    PIN_A4,
-    PIN_B3,
-    PIN_B2,
-    PIN_C3,
-    GEARIC,
-    PIN_3V,
-    PIN_SWC,
-    PIN_SIO,
-    PIN_TX,
-    PIN_RX,
-    PIN_C1,
-    PIN_C0,
-    PIN_1W,
-    PIN_GND_08,
-    PIN_GND_11,
-    PIN_GND_18,
-    NONE
-}ViewElement;
-
- //  5V  A7  A6  A4  B3  B2  C3 GND SET
- //
- //
- //  3V SWC GND SIO  TX  RX  C1  C0  1W GND
-
-// next element when pressing up or down from a given element
-// ground pins cannot be selected
-static uint8_t y_mapping[] = { 
-    PIN_3V,  // <- PIN_5V
-    PIN_SWC, // <- PIN_A7
-    NONE,    // <- PIN_A6
-    PIN_SIO, // <- PIN_A4
-    PIN_TX,  // <- PIN_B3
-    PIN_RX,  // <- PIN_B2
-    PIN_C1,  // <- PIN_C3
-    PIN_1W,  // <- GEARIC
-    PIN_5V,  // <- PIN_3V
-    PIN_A7,  // <- PIN_SWC
-    PIN_A4,  // <- PIN_SIO
-    PIN_B3,  // <- PIN_TX
-    PIN_B2,  // <- PIN_RX
-    PIN_C3,  // <- PIN_C1
-    NONE,    // <- PIN_C0
-    GEARIC   // <- PIN_1W
-};
+#include "app_defines.h"
+
+static void draw_main_view(Canvas* canvas, void* ctx);
+static void draw_config_menu_view(Canvas* canvas, void* ctx);
 
-static uint8_t x_pos[] = {
-      0, // PIN_5V
-     14, // PIN_A7
-     28, // PIN_A6
-     42, // PIN_A4
-     56, // PIN_B3
-     70, // PIN_B2
-     84, // PIN_C3
-    112, // GEARIC
-      0, // PIN_3V
-     14, // PIN_SWC
-     42, // PIN_SIO
-     56, // PIN_TX
-     70, // PIN_RX
-     84, // PIN_C1
-     98, // PIN_C0
-    112, // PIN_1W
-     98, // PIN_GND_08
-     28, // PIN_GND_11
-    126  // PIN_GND_18
+static DrawView draw_view_funcs[] = {
+    draw_main_view,
+    draw_config_menu_view
 };
 
-static int gp_pins[] = {
-     -1, // PIN_5V
-      0, // PIN_A7
-      1, // PIN_A6
-      2, // PIN_A4
-      3, // PIN_B3
-      4, // PIN_B2
-      5, // PIN_C3
-     -1, // GEARIC
-     -1, // PIN_3V
-     -1, // PIN_SWC
-     -1, // PIN_SIO
-     -1, // PIN_TX
-     -1, // PIN_RX
-      6, // PIN_C1
-      7, // PIN_C0
-     -1, // PIN_1W
+static void handle_main_input(InputEvent* event, void* ctx);
+static void handle_config_menu_input(InputEvent* event, void* ctx);
+
+static HandleInput input_handlers[] = {
+    handle_main_input,
+    handle_config_menu_input
 };
 
-static Icon * icons[] = {
-    (Icon*)&I_5v_pin,   // PIN_5V
-    (Icon*)&I_a7_pin,   // PIN_A7
-    (Icon*)&I_a6_pin,   // PIN_A6
-    (Icon*)&I_a4_pin,   // PIN_A4
-    (Icon*)&I_b3_pin,   // PIN_B3
-    (Icon*)&I_b2_pin,   // PIN_B2
-    (Icon*)&I_c3_pin,   // PIN_C3
-    (Icon*)&I_gear_unhighlighted, // GEARIC
-    (Icon*)&I_3v_pin,   // PIN_3V
-    (Icon*)&I_swc_pin,  // PIN_SWC
-    (Icon*)&I_sio_pin,  // PIN_SIO
-    (Icon*)&I_tx_pin,   // PIN_TX
-    (Icon*)&I_rx_pin,   // PIN_RX
-    (Icon*)&I_c1_pin,   // PIN_C1
-    (Icon*)&I_c0_pin,   // PIN_C0
-    (Icon*)&I_1w_pin    // PIN_1W
+static ViewerState vstate;
+
+static int wiggle[] = {-1,1,-1,1};
+static uint32_t wiggle_frame_count = 4;
+
+static ViewElement elements[] = {
+    {PIN_5V,     PIN_3V,  true, false,  true,  true, -1,   0,   0, "5V" ,               (Icon*)&I_5v_pin, NULL},
+    {PIN_A7,     PIN_SWC, true, false,  true,  true, -1,  14,   0, "PA7",               (Icon*)&I_a7_pin, NULL},
+    {PIN_A6,     NONE,    true, false,  true,  true, -1,  28,   0, "PA6",               (Icon*)&I_a6_pin, NULL},
+    {PIN_A4,     PIN_SIO, true, false,  true,  true, -1,  42,   0, "PA4",               (Icon*)&I_a4_pin, NULL},
+    {PIN_B3,     PIN_TX,  true, false,  true,  true, -1,  56,   0, "PB3",               (Icon*)&I_b3_pin, NULL},
+    {PIN_B2,     PIN_RX,  true, false,  true,  true, -1,  70,   0, "PB2",               (Icon*)&I_b2_pin, NULL},
+    {PIN_C3,     PIN_C1,  true, false,  true,  true, -1,  84,   0, "PC3",               (Icon*)&I_c3_pin, NULL}, 
+    {GEARIC,     PIN_1W,  true,  true,  true, false, -1, 112,   0, "Settings",          (Icon*)&I_gear_unhighlighted, (Icon*)&I_gear_highlighted},
+    {PIN_3V,     PIN_5V,  true, false, false,  true, -1,   0,  48, "3.3V",              (Icon*)&I_3v_pin, NULL},
+    {PIN_SWC,    PIN_A7,  true, false, false,  true, -1,  14,  48, "Serial Wire Clock", (Icon*)&I_swc_pin, NULL},
+    {PIN_SIO,    PIN_A4,  true, false, false,  true, -1,  42,  48, "Serial IO",         (Icon*)&I_sio_pin, NULL},
+    {PIN_TX,     PIN_B3,  true, false, false,  true, -1,  56,  48, "UART - Transmit",   (Icon*)&I_tx_pin, NULL},
+    {PIN_RX,     PIN_B2,  true, false, false,  true, -1,  70,  48, "UART - Receive",    (Icon*)&I_rx_pin, NULL},
+    {PIN_C1,     PIN_C3,  true, false, false,  true, -1,  84,  48, "PC1",               (Icon*)&I_c1_pin, NULL},
+    {PIN_C0,     NONE,    true, false, false,  true, -1,  98,  48, "PC0",               (Icon*)&I_c0_pin, NULL},
+    {PIN_1W,     GEARIC,  true,  true, false,  true, -1, 112,  48, "1-Wire",            (Icon*)&I_1w_pin, NULL},
+    {PIN_GND_08, NONE,   false, false,  true, false, -1,  98,  -1, "GND (Ground)",      (Icon*)&I_gnd_pin, NULL},
+    {PIN_GND_11, NONE,   false, false, false, false, -1,  28,  48, "GND (Ground)",      (Icon*)&I_gnd_pin, NULL},
+    {PIN_GND_18, NONE,   false, false, false, false, -1, 126,  48, "GND (Ground)",      (Icon*)&I_gnd_pin, NULL},
 };
 
-static uint8_t bot_row_y = 48;
+static GPIOPin gpio_pin_config[GPIO_PIN_COUNT];
 
-// Screen is 128x64 px
-static void app_draw_callback(Canvas* canvas, void* ctx) {
+static int element_count = NONE; // The NONE enum will a value equal to the number of elements defined in enum_view_element
+
+size_t strnlen(const char *str, size_t maxlen) {
+    size_t len = 0;
+    while (len < maxlen && str[len] != '\0') {
+        len++;
+    }
+    return len;
+}
+
+static void init_state()
+{
+    vstate.selected = PIN_A7;
+    vstate.wiggle_frame=-1;
+    vstate.view = MAIN_VIEW;
+}
+
+static void init_gpio()
+{
+    int count = 0;
+    for(size_t i = 0; i < gpio_pins_count; i++) {
+        if(!gpio_pins[i].debug) {
+            for(int j = 0; j < element_count; j++) {
+                if( strcmp(elements[j].name,gpio_pins[i].name) == 0 )
+                {
+                        gpio_pin_config[count].element_idx     = j;
+                        gpio_pin_config[count].pin             = gpio_pins[i].pin;
+                        gpio_pin_config[count].mode            = GpioModeOutputPushPull;
+                        gpio_pin_config[count].pull            = GpioPullNo;
+                        gpio_pin_config[count].speed           = GpioSpeedVeryHigh;
+                        gpio_pin_config[count].value           = 0;
+                        gpio_pin_config[count].name            = gpio_pins[i].name;
+                        gpio_pin_config[count].unset           = true;
+                        gpio_pin_config[count].found           = true;
+                        gpio_pin_config[count].input           = false;
+
+                        gpio_pin_config[count].user.mode = GPIO_MODE_UNSET;
+                        gpio_pin_config[count].user.value = GPIO_VALUE_FALSE;
+                        gpio_pin_config[count].user.gp_idx_input = -1;
+                        gpio_pin_config[count].user.changed = false;
+
+                        elements[j].gp_idx   = i;
+                        elements[j].editable = true;
+
+                        count++;
+                }
+            }
+        }
+    }
+
+    vstate.result = 0;
+}
+
+static void update_gpio()
+{
+    // read from gpio pins
+    for(int i = 0; i < GPIO_PIN_COUNT; i++) {
+        GPIOPin* gpc = &gpio_pin_config[i];
+        if( !gpc->unset )
+        {
+            if( gpc->mode == GpioModeInput ) {
+                gpc->value = furi_hal_gpio_read(gpc->pin) ? 1 : 0;
+            }
+        }
+    }
+}
+
+#define TOGGLECOLOR(state, canvas, setting, selected_col, deselected_col) \
+    canvas_set_color(canvas, (state == setting) ? selected_col : deselected_col)
+
+
+const char* gpio_user_mode_strs[] = {"INPUT","INPUT_PULLUP","OUTPUT","UNSET"};
+const char* gpio_user_value_strs[] = {"TRUE","FALSE","INPUT"};
+
+static void draw_config_menu_view(Canvas* canvas, void* ctx)
+{
+    UNUSED(ctx);
+
+    int gp_idx = elements[vstate.selected].gp_idx;
+    GPIOPin* gpc = &gpio_pin_config[gp_idx];
+
+    UNUSED(gpc);
+
+    canvas_set_font(canvas, FontSecondary);
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_draw_rframe(canvas, 1, 1, 126, 62, 0);
+    
+    TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_MODE, ColorBlack, ColorWhite);
+    canvas_draw_box(canvas, 2, 2, 124, 15);
+
+    TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_MODE, ColorWhite, ColorBlack);
+    canvas_draw_str(canvas, 6, 12, "Mode");
+
+    if( gpc->user.mode > 0 ) canvas_draw_str(canvas, 34, 12, "<");
+
+    canvas_draw_str(canvas, 45, 12, gpio_user_mode_strs[gpc->user.mode]);
+
+    if( gpc->user.mode < GPIO_MODE_UNSET ) canvas_draw_str(canvas, 120, 12, ">");
+
+    if( gpc->user.mode == GPIO_MODE_OUTPUT )
+    {
+        TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_VALUE, ColorBlack, ColorWhite);
+        canvas_draw_box(canvas, 2, 16, 124, 15);
+
+        TOGGLECOLOR(vstate.config_menu_selected, canvas, CONFIG_MENU_VALUE, ColorWhite, ColorBlack);
+        canvas_draw_str(canvas, 6, 12 + 16, "Value");
+
+        if( gpc->user.value > 0 ) canvas_draw_str(canvas, 34, 12 + 16, "<");
+
+        canvas_draw_str(canvas, 45, 12 + 16, gpio_user_value_strs[gpc->user.value]);
+
+        if( gpc->user.value < GPIO_VALUE_INPUT ) canvas_draw_str(canvas, 120, 12 + 16, ">");    
+    }
+
+}
+
+// TODO: Determine the lowest frame delta we can get away with. 
+// TODO: Redraw only what changes.
+//       - clear previous (drawn) selected pin
+//       - clear newly selected pin
+
+static void draw_main_view(Canvas* canvas, void* ctx)
+{
     UNUSED(ctx);
 
     canvas_clear(canvas);
 
-    // draw ground pins
-    canvas_draw_icon(canvas, x_pos[PIN_GND_08], -1, &I_gnd_pin);
-    canvas_draw_icon(canvas, x_pos[PIN_GND_11], bot_row_y, &I_gnd_pin);
-    canvas_draw_icon(canvas, x_pos[PIN_GND_18], bot_row_y, &I_gnd_pin);
+    size_t current_frame_time = furi_get_tick();
+    size_t delta_cycles = (current_frame_time > vstate.prev_frame_time ? current_frame_time - vstate.prev_frame_time : 0);
+    size_t delta_time_ms = delta_cycles * 1000 / furi_kernel_get_tick_frequency();
+
+    // delay until desired delta time and recalculate
+    if( delta_time_ms < FRAME_TIME )
+    {
+        furi_delay_ms(FRAME_TIME-delta_time_ms);
+        current_frame_time = furi_get_tick();
+        delta_cycles = (current_frame_time > vstate.prev_frame_time ? current_frame_time - vstate.prev_frame_time : 0);
+        delta_time_ms = delta_cycles * 1000 / furi_kernel_get_tick_frequency();
+    }
+    
+    vstate.elapsed_time += delta_time_ms;
+    vstate.prev_frame_time = current_frame_time;
+
+    canvas_set_font(canvas, FontSecondary);
+
+    char hex_string[3];
+
+    // draw values
+    for(int i = 0; i < GPIO_PIN_COUNT; i++) {
+        if( !gpio_pin_config[i].unset )
+        {
+            ViewElement e = elements[gpio_pin_config[i].element_idx];
+
+            // draw wire
+            if(e.top_row)
+            {
+                canvas_draw_line(canvas, e.x_pos + 6, e.y_pos + 16, e.x_pos + 6, e.y_pos + 16 + 8);
+            }
+            else
+            {
+                canvas_draw_line(canvas, e.x_pos + 6, e.y_pos, e.x_pos + 6, e.y_pos - 8);
+            }
+            
+            if(gpio_pin_config[i].mode == GpioModeAnalog)
+            {
+                snprintf(hex_string, sizeof(hex_string), "%02X", (int)gpio_pin_config[i].value);
+                if(e.top_row)
+                {
+                    canvas_draw_icon(canvas, e.x_pos - 1, e.y_pos + 20, &I_analog_box);
+                    canvas_draw_str(canvas, e.x_pos + 1, e.y_pos + 22 + 7, hex_string);
+
+                }   
+                else
+                {
+                    canvas_draw_icon(canvas, e.x_pos - 1, e.y_pos - 15, &I_analog_box);
+                    canvas_draw_str(canvas, e.x_pos + 1, e.y_pos - 6, hex_string);
+                }
+            }
+            else
+            {
+                const Icon* icon = (int)gpio_pin_config[i].value ? &I_digi_one : &I_digi_zero;
+                if(e.top_row)
+                {
+                    canvas_draw_icon(canvas, e.x_pos + 2, e.y_pos + 20, icon);
+                }   
+                else
+                {
+                    canvas_draw_icon(canvas, e.x_pos + 2, e.y_pos - 13, icon);
+                }
+                
+            }
+        }
+    }
+
+    for(int i = 0; i < element_count; i++)
+    {
+        ViewElement e = elements[i];
+        int x = e.x_pos;
+        int y = e.y_pos + (e.top_row && e.pull_out ? -3 : 0);
+        Icon* icon = e.icon;
+
+        if( vstate.selected == i )
+        {
+            if( e.pull_out )
+            {
+                y += e.top_row ? 3 : -3;
+            }
+            if( e.selected_icon != NULL )
+            {
+                icon = e.selected_icon;
+            }
+
+            if(vstate.wiggle_frame >= 0)
+            {
+                x += wiggle[vstate.wiggle_frame];
 
-    // draw gear
-    canvas_draw_icon(canvas, x_pos[GEARIC], 0, (vstate.selected == GEARIC ? &I_gear_highlighted : &I_gear_unhighlighted));
+                if(vstate.elapsed_time >= ANIMATE_FRAME_TIME_MS)
+                {
+                    vstate.wiggle_frame++;
+                    if ((unsigned int)(vstate.wiggle_frame) >= wiggle_frame_count)
+                    {
+                        vstate.wiggle_frame = -1;
+                    }
+                    vstate.elapsed_time = 0;
+                }
+            }
+        }
+
+        canvas_draw_icon(canvas, x, y, icon);
+    }
+
+    // draw arrows
+    for(int i = 0; i < GPIO_PIN_COUNT; i++) {
+        if( !gpio_pin_config[i].unset )
+        {
+            ViewElement e = elements[gpio_pin_config[i].element_idx];
 
-    // draw top row of pins
-    for( int i = 0; i < GEARIC; i++ )
+            bool selected = vstate.selected == gpio_pin_config[i].element_idx;
+
+            // draw arrow
+            if(e.top_row)
+            {   
+                int offset = selected ? 3 : 0;
+                const Icon* arrow_icon = gpio_pin_config[i].input ? &I_arrow_up : &I_arrow_down;
+                canvas_draw_icon(canvas, e.x_pos + 3, e.y_pos + 8 + offset, arrow_icon);
+            }   
+            else
+            {
+                int offset = selected ? 0 : 3;
+                const Icon* arrow_icon = gpio_pin_config[i].input ? &I_arrow_down : &I_arrow_up;
+                canvas_draw_icon(canvas, e.x_pos + 3, e.y_pos + -1 + offset, arrow_icon);
+            }
+        }
+    }
+
+    
+
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str(canvas, 0, 42, elements[vstate.selected].name);
+}
+
+static void handle_main_input(InputEvent* event, void* ctx) {
+    if( vstate.wiggle_frame < 0 )
     {
-        int y = vstate.selected == i ? 0 : -3;
-        canvas_draw_icon(canvas, x_pos[i], y, icons[i]);
+        furi_assert(ctx);
+        FuriMessageQueue* event_queue = ctx;
+
+        // place in queue to handle backing out of app
+        furi_message_queue_put(event_queue, event, FuriWaitForever);
+
+        if( (event->type == InputTypePress || event->type == InputTypeRelease) && event->key == InputKeyOk )
+        {
+            if( event->type == InputTypePress && elements[vstate.selected].gp_idx < 0 )
+            {
+                vstate.wiggle_frame = 0;
+                vstate.elapsed_time = 0;
+            }
+            else if( elements[vstate.selected].gp_idx >= 0 && (event->type == InputTypePress || event->type == InputTypeRelease) )
+            {
+                int gp_idx = elements[vstate.selected].gp_idx;
+                gpio_pin_config[gp_idx].user.prev_mode = gpio_pin_config[gp_idx].user.mode;
+
+                vstate.view = CONFIG_MENU_VIEW;
+                vstate.config_menu_selected = CONFIG_MENU_MODE;
+            }
+        }
+        else if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+            switch(event->key) {
+            case InputKeyLeft:
+                vstate.selected--;
+                if(vstate.selected == GEARIC) vstate.selected = PIN_1W;
+                else if(vstate.selected < 0) vstate.selected = GEARIC;
+                break;
+            case InputKeyRight:
+                if(vstate.selected <= GEARIC)
+                {
+                    vstate.selected++;
+                    vstate.selected = vstate.selected > GEARIC ? PIN_5V : vstate.selected;
+                }
+                else
+                {
+                    vstate.selected++;
+                    vstate.selected = vstate.selected > PIN_1W ? PIN_3V : vstate.selected;
+                }
+                break;
+            case InputKeyUp:
+            case InputKeyDown:
+                if (elements[vstate.selected].opposite != NONE) vstate.selected = elements[vstate.selected].opposite;
+                break;
+            default:
+                break;
+            }
+        }
     }
+}
 
-    // draw bottom row of pins
-    for( int i = PIN_3V; i <= PIN_1W; i++ )
+static void set_GPIO_pin_via_user(int gp_idx)
+{
+    GPIOPin* gpc = &gpio_pin_config[gp_idx];
+
+    if(gpc->user.changed)
     {
-        int y = bot_row_y - (vstate.selected == i ? 3 : 0);
-        canvas_draw_icon(canvas, x_pos[i], y, icons[i]);
+        // update attributes
+        switch(gpc->user.mode)
+        {
+        case GPIO_MODE_INPUT:
+            gpc->mode = GpioModeInput;
+            gpc->pull = GpioPullNo;
+            gpc->input = true;
+            break;
+        case GPIO_MODE_INPUT_PULLUP:
+            gpc->mode = GpioModeInput;
+            gpc->pull = GpioPullUp;
+            gpc->input = true;
+            break;
+        case GPIO_MODE_OUTPUT:
+            gpc->mode = GpioModeOutputPushPull;
+            gpc->pull = GpioPullNo;
+            gpc->input = false;
+            break;
+        default:
+            break;
+        }
+
+        switch(gpc->user.value)
+        {
+        case GPIO_VALUE_TRUE:
+            gpc->value = (double)1.0;
+            break;
+        case GPIO_VALUE_FALSE:
+        case GPIO_VALUE_INPUT:
+        case GPIO_VALUE_NONE:
+            gpc->value = (double)0.0;
+            break;
+        default:
+            break;
+        }
+
+        furi_hal_gpio_write(gpc->pin, gpc->value != (double)0.0 ? true : false);
+        if( gpc->user.mode != gpc->user.prev_mode) {
+            furi_hal_gpio_init(gpc->pin, gpc->mode, gpc->pull, gpc->speed);
+            gpc->unset = false;
+        }
+        
+        gpc->user.changed = false;
     }
 }
 
-static void app_input_callback(InputEvent* input_event, void* ctx) {
-    furi_assert(ctx);
+static void handle_config_menu_input(InputEvent* event, void* ctx) {
+    UNUSED(ctx);
+
+    int gp_idx = elements[vstate.selected].gp_idx;
+    GPIOPin* gpc = &gpio_pin_config[gp_idx];
+
+    if(event->type == InputTypePress || event->type == InputTypeRepeat) {
+        switch(event->key) {
+        case InputKeyLeft:
+            switch(vstate.config_menu_selected)
+            {
+            case CONFIG_MENU_MODE:
+                if(gpc->user.mode > 0) {
+                    gpc->user.mode--;
+                    gpc->user.changed = true;
+                }
+                break;
+            case CONFIG_MENU_VALUE:
+                if(gpc->user.value > 0) {
+                    gpc->user.value--;
+                    gpc->user.changed = true;
+                }
+                break;
+            case CONFIG_MENU_INPUT:
+                break;
+            default:
+                break;
+            }
+            break;
+        case InputKeyRight:
+            switch(vstate.config_menu_selected)
+            {
+            case CONFIG_MENU_MODE:
+                if(gpc->user.mode < GPIO_MODE_UNSET) {
+                    gpc->user.mode++;
+                    gpc->user.changed = true;
+                }
+                break;
+            case CONFIG_MENU_VALUE:
+                if(gpc->user.value < GPIO_VALUE_FALSE) {
+                    gpc->user.value++;
+                    gpc->user.changed = true;
+                }
+                break;
+            case CONFIG_MENU_INPUT:
+                break;
+            default:
+                break;
+            }
+            break;
+        case InputKeyUp:
+            if(gpc->user.mode == GPIO_MODE_OUTPUT )
+            { 
+                if( vstate.config_menu_selected == 0 ) vstate.config_menu_selected = CONFIG_MENU_VALUE;
+                else vstate.config_menu_selected--;
+            }
+            break;
+        case InputKeyDown:
+            if(gpc->user.mode == GPIO_MODE_OUTPUT )
+            {
+                if( vstate.config_menu_selected == CONFIG_MENU_VALUE ) vstate.config_menu_selected = 0;
+                else vstate.config_menu_selected++;
+            }
+            break;
+        case InputKeyBack:
+            
+            // Set new pin configuration
+            set_GPIO_pin_via_user(gp_idx);
+
+            vstate.view = MAIN_VIEW;
+
+            break;
+        default:
+            break;
+        }
+    }
+}
 
-    FuriMessageQueue* event_queue = ctx;
-    furi_message_queue_put(event_queue, input_event, FuriWaitForever);
+static void app_draw_callback(Canvas* canvas, void* ctx) {
+    draw_view_funcs[vstate.view](canvas,ctx);
+}
+
+static void app_input_callback(InputEvent* input_event, void* ctx) {
+    input_handlers[vstate.view](input_event,ctx);
 }
 
 int32_t gpio_controller_main(void* p) {
@@ -179,35 +529,18 @@ int32_t gpio_controller_main(void* p) {
 
     InputEvent event;
 
-    vstate.gpio_items = gpio_items_alloc();
-    gpio_items_configure_all_pins(vstate.gpio_items, GpioModeOutputPushPull);
+    init_state();
+    init_gpio();
+
+    vstate.prev_frame_time = furi_get_tick();
+    vstate.elapsed_time = 0;
 
     bool running = true;
     while(running) {
         if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
-            if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
+
+            if(event.type == InputTypePress || event.type == InputTypeRepeat) {
                 switch(event.key) {
-                case InputKeyLeft:
-                    vstate.selected--;
-                    if(vstate.selected == GEARIC) vstate.selected = PIN_1W;
-                    else if(vstate.selected < 0) vstate.selected = GEARIC;
-                    break;
-                case InputKeyRight:
-                    if(vstate.selected <= GEARIC)
-                    {
-                        vstate.selected++;
-                        vstate.selected = vstate.selected > GEARIC ? PIN_5V : vstate.selected;
-                    }
-                    else
-                    {
-                        vstate.selected++;
-                        vstate.selected = vstate.selected > PIN_1W ? PIN_3V : vstate.selected;
-                    }
-                    break;
-                case InputKeyUp:
-                case InputKeyDown:
-                    if (y_mapping[vstate.selected] != NONE) vstate.selected = y_mapping[vstate.selected];
-                    break;
                 case InputKeyBack:
                     running = false;
                     break;
@@ -216,18 +549,11 @@ int32_t gpio_controller_main(void* p) {
                 }
             }
         }
-        else if(event.key == InputKeyOk)
-        {
-            if( gp_pins[vstate.selected] >= 0 && (event.type == InputTypePress || event.type == InputTypeRelease) )
-            {
-                gpio_items_set_pin(vstate.gpio_items, gp_pins[vstate.selected], event.type == InputTypePress);
-            }
-        }
+
+        update_gpio();
         view_port_update(view_port);
     }
 
-    gpio_items_free(vstate.gpio_items);
-
     view_port_enabled_set(view_port, false);
     gui_remove_view_port(gui, view_port);
     view_port_free(view_port);

+ 0 - 69
gpio_items.c

@@ -1,69 +0,0 @@
-#include "gpio_items.h"
-
-#include <furi_hal_resources.h>
-
-struct GPIOItems {
-    GpioPinRecord* pins;
-    size_t count;
-};
-
-GPIOItems* gpio_items_alloc() {
-    GPIOItems* items = malloc(sizeof(GPIOItems));
-
-    items->count = 0;
-    for(size_t i = 0; i < gpio_pins_count; i++) {
-        if(!gpio_pins[i].debug) {
-            items->count++;
-        }
-    }
-
-    items->pins = malloc(sizeof(GpioPinRecord) * items->count);
-    for(size_t i = 0; i < items->count; i++) {
-        if(!gpio_pins[i].debug) {
-            items->pins[i].pin = gpio_pins[i].pin;
-            items->pins[i].name = gpio_pins[i].name;
-        }
-    }
-    return items;
-}
-
-void gpio_items_free(GPIOItems* items) {
-    free(items->pins);
-    free(items);
-}
-
-uint8_t gpio_items_get_count(GPIOItems* items) {
-    return items->count;
-}
-
-void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode) {
-    furi_assert(index < items->count);
-    furi_hal_gpio_write(items->pins[index].pin, false);
-    furi_hal_gpio_init(items->pins[index].pin, mode, GpioPullNo, GpioSpeedVeryHigh);
-}
-
-void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode) {
-    for(uint8_t i = 0; i < items->count; i++) {
-        gpio_items_configure_pin(items, i, mode);
-    }
-}
-
-void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level) {
-    furi_assert(index < items->count);
-    furi_hal_gpio_write(items->pins[index].pin, level);
-}
-
-void gpio_items_set_all_pins(GPIOItems* items, bool level) {
-    for(uint8_t i = 0; i < items->count; i++) {
-        gpio_items_set_pin(items, i, level);
-    }
-}
-
-const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index) {
-    furi_assert(index < items->count + 1);
-    if(index == items->count) {
-        return "ALL";
-    } else {
-        return items->pins[index].name;
-    }
-}

+ 0 - 29
gpio_items.h

@@ -1,29 +0,0 @@
-#pragma once
-
-#include <furi_hal_gpio.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-typedef struct GPIOItems GPIOItems;
-
-GPIOItems* gpio_items_alloc();
-
-void gpio_items_free(GPIOItems* items);
-
-uint8_t gpio_items_get_count(GPIOItems* items);
-
-void gpio_items_configure_pin(GPIOItems* items, uint8_t index, GpioMode mode);
-
-void gpio_items_configure_all_pins(GPIOItems* items, GpioMode mode);
-
-void gpio_items_set_pin(GPIOItems* items, uint8_t index, bool level);
-
-void gpio_items_set_all_pins(GPIOItems* items, bool level);
-
-const char* gpio_items_get_pin_name(GPIOItems* items, uint8_t index);
-
-#ifdef __cplusplus
-}
-#endif

BIN
images/analog_box.png


BIN
images/digi_one.png


BIN
images/digi_zero.png