Просмотр исходного кода

First draft of ColecoVision controller app.

Aaron Mavrinac 3 лет назад
Родитель
Сommit
98b3cf2a01
1 измененных файлов с 290 добавлено и 0 удалено
  1. 290 0
      coleco.c

+ 290 - 0
coleco.c

@@ -1,8 +1,298 @@
 #include <furi.h>
+#include <furi_hal_gpio.h>
+#include <gui/gui.h>
+#include <gui/modules/button_panel.h>
+
+const GpioPin* const pin_up = &gpio_ext_pa4;
+const GpioPin* const pin_down = &gpio_ext_pb2;
+const GpioPin* const pin_right = &gpio_ext_pa7;
+const GpioPin* const pin_left = &gpio_ext_pa6;
+const GpioPin* const pin_fire = &gpio_ext_pc3;
+const GpioPin* const pin_mode = &gpio_ext_pc1;
+
+typedef enum
+{
+  EventTypeTick,
+  EventTypeKey,
+} EventType;
+
+typedef struct
+{
+  EventType type;
+  InputEvent input;
+} PluginEvent;
+
+typedef struct
+{
+  bool mode;
+
+  bool button_up;
+  bool button_down;
+  bool button_right;
+  bool button_left;
+  bool button_fire;
+  unsigned int button_code;
+
+  ButtonPanel* button_panel;
+  bool dpad;
+} Coleco;
+
+static void output_update(Coleco* coleco)
+{
+  // TODO: check mode, possibly flipped
+  if (coleco->mode)
+  {
+    furi_hal_gpio_write(pin_up, !coleco->button_up);
+    furi_hal_gpio_write(pin_down, !coleco->button_down);
+    furi_hal_gpio_write(pin_right, !coleco->button_right);
+    furi_hal_gpio_write(pin_left, !coleco->button_left);
+  }
+  else
+  {
+    // TODO: check ordering of bits
+    furi_hal_gpio_write(pin_left, (coleco->button_code & 1));
+    furi_hal_gpio_write(pin_down, (coleco->button_code & 2));
+    furi_hal_gpio_write(pin_right, (coleco->button_code & 4));
+    furi_hal_gpio_write(pin_up, (coleco->button_code & 8));
+  }
+  furi_hal_gpio_write(pin_fire, !coleco->button_fire);
+}
+
+static void mode_isr(void* context)
+{
+  Coleco* coleco = acquire_mutex((ValueMutex*)context, 25);
+  if (coleco == NULL)
+  {
+    return;
+  }
+
+  coleco->mode = furi_hal_gpio_read(&gpio_ext_pc1);
+  output_update(coleco);
+}
+
+static void render_callback(Canvas* const canvas, void* context)
+{
+  Coleco* coleco = acquire_mutex((ValueMutex*)context, 25);
+  if (coleco == NULL)
+  {
+    return;
+  }
+
+  canvas_set_font(canvas, FontPrimary);
+  canvas_draw_str_aligned(canvas, 64, 8, AlignCenter, AlignBottom, "ColecoVision");
+  if (coleco->dpad)
+  {
+    canvas_set_font(canvas, FontSecondary);
+    canvas_draw_str_aligned(canvas, 64, 18, AlignCenter, AlignBottom, "D-pad");
+  }
+
+  release_mutex((ValueMutex*)context, coleco);
+}
+
+static void input_callback(InputEvent* input_event, FuriMessageQueue* event_queue)
+{
+  furi_assert(event_queue);
+
+  PluginEvent event = {.type = EventTypeKey, .input = *input_event};
+  furi_message_queue_put(event_queue, &event, FuriWaitForever);
+}
+
+static Coleco* coleco_alloc()
+{
+  Coleco* coleco = malloc(sizeof(Coleco));
+
+  coleco->button_panel = button_panel_alloc();
+
+  coleco->mode = true;
+  coleco->button_up = false;
+  coleco->button_down = false;
+  coleco->button_right = false;
+  coleco->button_left = false;
+  coleco->button_fire = false;
+  coleco->button_code = 0x0F;
+  coleco->dpad = false;
+
+  // configure output pins
+  furi_hal_gpio_init(pin_up, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+  furi_hal_gpio_init(pin_down, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+  furi_hal_gpio_init(pin_right, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+  furi_hal_gpio_init(pin_left, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+  furi_hal_gpio_init(pin_fire, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+
+  output_update(coleco);
+
+  // configure mode pin
+  furi_hal_gpio_init(pin_mode, GpioModeInterruptRiseFall, GpioPullNo, GpioSpeedVeryHigh);
+
+  return coleco;
+}
+
+static void coleco_free(Coleco* coleco)
+{
+  furi_assert(coleco);
+
+  button_panel_free(coleco->button_panel);
+
+  free(coleco);
+}
 
 int32_t coleco_app(void* p)
 {
   UNUSED(p);
 
+  FuriMessageQueue* event_queue = furi_message_queue_alloc(8, sizeof(PluginEvent));
+
+  Coleco* coleco = coleco_alloc();
+
+  ValueMutex coleco_mutex;
+  if (!init_mutex(&coleco_mutex, coleco, sizeof(Coleco)))
+  {
+    FURI_LOG_E("Coleco", "cannot create mutex\r\n");
+    coleco_free(coleco);
+    return 255;
+  }
+
+  // set system callbacks
+  ViewPort* view_port = view_port_alloc();
+  view_port_draw_callback_set(view_port, render_callback, &coleco_mutex);
+  view_port_input_callback_set(view_port, input_callback, event_queue);
+
+  // open GUI and register view_port
+  Gui* gui = furi_record_open("gui");
+  gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
+  // enable mode ISR
+  furi_hal_gpio_add_int_callback(pin_mode, mode_isr, &coleco_mutex);
+
+  PluginEvent event;
+  for (bool processing = true; processing;)
+  {
+    FuriStatus event_status = furi_message_queue_get(event_queue, &event, 100);
+
+    Coleco* coleco = (Coleco*)acquire_mutex_block(&coleco_mutex);
+
+    if (event_status == FuriStatusOk)
+    {
+      // press events
+      if (event.type == EventTypeKey)
+      {
+        switch (event.input.key)
+        {
+          case InputKeyUp:
+            if (coleco->dpad)
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_up = true;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_up = false;
+              }
+            }
+            else  // FIXME: hack to allow the 1 to be pressed
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_code = 0x0D;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_code = 0x0F;
+              }
+            }
+            break;
+          case InputKeyDown:
+            if (coleco->dpad)
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_down = true;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_down = false;
+              }
+            }
+            break;
+          case InputKeyRight:
+            if (coleco->dpad)
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_right = true;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_right = false;
+              }
+            }
+            break;
+          case InputKeyLeft:
+            if (coleco->dpad)
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_left = true;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_left = false;
+              }
+            }
+            break;
+          case InputKeyOk:
+            if (coleco->dpad)
+            {
+              if (event.input.type == InputTypePress)
+              {
+                coleco->button_fire = true;
+              }
+              else if (event.input.type == InputTypeRelease)
+              {
+                coleco->button_fire = false;
+              }
+            }
+            else
+            {
+              coleco->dpad = true;
+            }
+            break;
+          case InputKeyBack:
+            if (event.input.type == InputTypePress)
+            {
+              if (coleco->dpad)
+              {
+                coleco->dpad = false;
+              }
+              else
+              {
+                processing = false;
+              }
+            }
+            break;
+          default:
+            break;
+        }
+      }
+    }
+    else
+    {
+      FURI_LOG_D("Coleco", "FuriMessageQueue: event timeout");
+    }
+
+    output_update(coleco);
+    view_port_update(view_port);
+    release_mutex(&coleco_mutex, coleco);
+  }
+
+  furi_hal_gpio_remove_int_callback(pin_mode);
+  view_port_enabled_set(view_port, false);
+  gui_remove_view_port(gui, view_port);
+  furi_record_close("gui");
+  view_port_free(view_port);
+  furi_message_queue_free(event_queue);
+  delete_mutex(&coleco_mutex);
+  coleco_free(coleco);
   return 0;
 }