MX 2 лет назад
Родитель
Сommit
a7c091eb13
8 измененных файлов с 178 добавлено и 29 удалено
  1. 25 0
      README.md
  2. 2 2
      application.fam
  3. 1 0
      scenes/scope_scene_config.h
  4. 39 25
      scenes/scope_scene_run.c
  5. 85 0
      scenes/scope_scene_save.c
  6. 1 0
      scenes/scope_types.h
  7. 9 1
      scope.c
  8. 16 1
      scope_app_i.h

+ 25 - 0
README.md

@@ -33,6 +33,31 @@ Also see [Derek Jamison's demonstration](https://www.youtube.com/watch?v=iC5fBGw
 
 ![Flipper Zero running flipperscope](photos/volt.jpg)
 
+## Processing captures
+
+You can use the following simple Python script, for processing the captured waveforms, from flipperscope.
+
+```
+import matplotlib.pyplot as plt
+import numpy as np
+import struct
+
+fig, ax = plt.subplots()
+data = open("Sine.dat", "rb").read()
+y = [(float(x[0]) / 2500) * 2.5 for x in struct.iter_unpack("<H", data)]
+x = np.arange(len(y))
+
+ax.plot(x, y, label='Voltage')
+ax.set_xlabel('Sample')
+ax.set_ylabel('Voltage / V')
+ax.set_title('ADC Voltage')
+ax.set_ylim([0, 2.5])
+ax.legend()
+plt.show()
+```
+
+![Captured waveform](photos/sine.png)
+
 ## To Do
 
 * Customisable input pin

+ 2 - 2
application.fam

@@ -9,6 +9,6 @@ App(
     fap_category="GPIO",
     fap_icon="scope_10px.png",
     fap_icon_assets="icons",
-    fap_version="0.1",
-    fap_description="Oscilloscope application - apply signal to pin 16/PC0, with a voltage ranging from 0V to 2.5V and ground to pin 18/GND",
+    fap_version="0.2",
+    fap_description="Oscilloscope application - apply signal to pin 16/PC0, with a voltage ranging from 0V to 2.5V and ground to pin 18/GND"
 )

+ 1 - 0
scenes/scope_scene_config.h

@@ -1,4 +1,5 @@
 ADD_SCENE(scope, start, Start)
 ADD_SCENE(scope, run, Run)
+ADD_SCENE(scope, save, Save)
 ADD_SCENE(scope, setup, Setup)
 ADD_SCENE(scope, about, About)

+ 39 - 25
scenes/scope_scene_run.c

@@ -8,6 +8,7 @@
 #include <gui/modules/submenu.h>
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/widget.h>
+#include <gui/elements.h>
 #include <notification/notification_messages.h>
 
 #include "stm32wbxx_ll_adc.h"
@@ -27,7 +28,6 @@
 #include "flipperscope_icons.h"
 
 #define DIGITAL_SCALE_12BITS ((uint32_t)0xFFF)
-#define ADC_CONVERTED_DATA_BUFFER_SIZE ((uint32_t)128)
 #define VAR_CONVERTED_DATA_INIT_VALUE (DIGITAL_SCALE_12BITS + 1)
 #define VAR_CONVERTED_DATA_INIT_VALUE_16BITS (0xFFFF + 1U)
 #define __ADC_CALC_DATA_VOLTAGE(__VREFANALOG_VOLTAGE__, __ADC_DATA__) \
@@ -353,6 +353,15 @@ static void app_draw_callback(Canvas* canvas, void* ctx) {
     float min = FLT_MAX;
     int count = 0;
 
+    if(type == m_capture) {
+        if(!pause)
+            elements_button_center(canvas, "Stop");
+        else {
+            elements_button_center(canvas, "REC");
+            elements_button_right(canvas, "Save");
+        }
+    }
+
     if(pause)
         canvas_draw_icon(canvas, 115, 0, &I_pause_10x10);
     else
@@ -436,13 +445,6 @@ static void app_input_callback(InputEvent* input_event, void* ctx) {
     furi_message_queue_put(event_queue, input_event, FuriWaitForever);
 }
 
-void scope_scene_run_widget_callback(GuiButtonType result, InputType type, void* context) {
-    ScopeApp* app = context;
-    if(type == InputTypeShort) {
-        view_dispatcher_send_custom_event(app->view_dispatcher, result);
-    }
-}
-
 void scope_scene_run_on_enter(void* context) {
     ScopeApp* app = context;
 
@@ -460,10 +462,6 @@ void scope_scene_run_on_enter(void* context) {
     // What type of measurement are we performing
     type = app->measurement;
 
-    // Pause capture, when first started, if capturing
-    if(type == m_capture)
-        pause = 1;
-
     // Copy vector table, modify to use our own IRQ handlers
     __disable_irq();
     memcpy(ramVector, (uint32_t*)(FLASH_BASE | SCB->VTOR), sizeof(uint32_t) * TABLE_SIZE);
@@ -516,16 +514,23 @@ void scope_scene_run_on_enter(void* context) {
     // Register view port in GUI
     Gui* gui = furi_record_open(RECORD_GUI);
     gui_add_view_port(gui, view_port, GuiLayerFullscreen);
+
     InputEvent event;
     bool running = true;
+    bool save = false;
     while(running) {
         if(furi_message_queue_get(event_queue, &event, 100) == FuriStatusOk) {
             if((event.type == InputTypePress) || (event.type == InputTypeRepeat)) {
                 switch(event.key) {
                 case InputKeyLeft:
                     break;
-                case InputKeyRight:
-                    break;
+                case InputKeyRight: {
+                    // Save data if in capture mode
+                    if(type == m_capture && pause == 1) {
+                        running = false;
+                        save = true;
+                    }
+                } break;
                 case InputKeyUp:
                     break;
                 case InputKeyDown:
@@ -555,22 +560,31 @@ void scope_scene_run_on_enter(void* context) {
     SCB->VTOR = 0;
     __enable_irq();
 
-    view_port_enabled_set(view_port, false);
-    gui_remove_view_port(gui, view_port);
-    view_port_free(view_port);
+    if(!save) {
+        view_port_enabled_set(view_port, false);
+        gui_remove_view_port(gui, view_port);
+        view_port_free(view_port);
 
-    // Switch back to original scene
-    furi_record_close(RECORD_GUI);
-    scene_manager_previous_scene(app->scene_manager);
-    submenu_set_selected_item(app->submenu, 0);
+        // Switch back to original scene
+        furi_record_close(RECORD_GUI);
+        scene_manager_previous_scene(app->scene_manager);
+        submenu_set_selected_item(app->submenu, 0);
+    } else {
+        view_port_enabled_set(view_port, false);
+        gui_remove_view_port(gui, view_port);
+        view_port_free(view_port);
+
+        app->data = malloc(sizeof(uint16_t) * ADC_CONVERTED_DATA_BUFFER_SIZE);
+        memcpy(
+            app->data, (uint16_t*)mvoltDisplay, sizeof(uint16_t) * ADC_CONVERTED_DATA_BUFFER_SIZE);
+        scene_manager_next_scene(app->scene_manager, ScopeSceneSave);
+    }
 }
 
 bool scope_scene_run_on_event(void* context, SceneManagerEvent event) {
-    ScopeApp* app = context;
-    bool consumed = false;
-    UNUSED(app);
+    UNUSED(context);
     UNUSED(event);
-    return consumed;
+    return false;
 }
 
 void scope_scene_run_on_exit(void* context) {

+ 85 - 0
scenes/scope_scene_save.c

@@ -0,0 +1,85 @@
+#include <float.h>
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_resources.h>
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/scene_manager.h>
+#include <gui/modules/submenu.h>
+#include <gui/modules/variable_item_list.h>
+#include <gui/modules/widget.h>
+#include <gui/elements.h>
+#include <notification/notification_messages.h>
+#include <flipper_format/flipper_format.h>
+#include "../scope_app_i.h"
+
+void scope_scene_save_text_input_callback(void* context) {
+    ScopeApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, ScopeCustomEventTextInputDone);
+}
+
+void scope_scene_save_on_enter(void* context) {
+    ScopeApp* app = context;
+    // Setup view
+    TextInput* text_input = app->text_input;
+    FuriString* file_name = furi_string_alloc();
+    FuriString* dir_name = furi_string_alloc();
+
+    text_input_set_header_text(text_input, "Name signal");
+    text_input_set_result_callback(
+        text_input,
+        scope_scene_save_text_input_callback,
+        app,
+        app->file_name_tmp,
+        MAX_LEN_NAME,
+        false);
+
+    furi_string_free(file_name);
+    furi_string_free(dir_name);
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, ScopeViewSave);
+}
+
+bool scope_scene_save_on_event(void* context, SceneManagerEvent event) {
+    ScopeApp* app = context;
+    UNUSED(app);
+    UNUSED(event);
+
+    bool consumed = false;
+    if(event.type == SceneManagerEventTypeCustom && event.event == ScopeCustomEventTextInputDone) {
+        if(strcmp(app->file_name_tmp, "") != 0) {
+            FuriString* temp_str = furi_string_alloc();
+            furi_string_printf(
+                temp_str,
+                "%s/%s%s",
+                APP_DATA_PATH(""),
+                app->file_name_tmp,
+                FLIPPERSCOPE_APP_EXTENSION);
+            Storage* storage = furi_record_open(RECORD_STORAGE);
+            File* file = storage_file_alloc(storage);
+            if(!storage_file_open(
+                   file, furi_string_get_cstr(temp_str), FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+                // Todo: Display error
+            }
+            if(!storage_file_write(
+                   file, app->data, sizeof(uint16_t) * ADC_CONVERTED_DATA_BUFFER_SIZE)) {
+                // Todo: Display error
+            }
+            storage_file_close(file);
+            storage_file_free(file);
+            furi_record_close(RECORD_STORAGE);
+            free(app->data);
+            app->data = NULL;
+            scene_manager_previous_scene(app->scene_manager);
+            consumed = true;
+        }
+    }
+
+    return consumed;
+}
+
+void scope_scene_save_on_exit(void* context) {
+    ScopeApp* app = context;
+    // Clear views
+    text_input_reset(app->text_input);
+}

+ 1 - 0
scenes/scope_types.h

@@ -10,4 +10,5 @@ typedef enum {
     ScopeViewVariableItemList,
     ScopeViewSubmenu,
     ScopeViewWidget,
+    ScopeViewSave,
 } ScopeView;

+ 9 - 1
scope.c

@@ -75,6 +75,10 @@ ScopeApp* scope_app_alloc() {
     app->widget = widget_alloc();
     view_dispatcher_add_view(app->view_dispatcher, ScopeViewWidget, widget_get_view(app->widget));
 
+    app->text_input = text_input_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, ScopeViewSave, text_input_get_view(app->text_input));
+
     app->time = 0.001;
     app->measurement = m_time;
 
@@ -93,8 +97,12 @@ void scope_app_free(ScopeApp* app) {
     view_dispatcher_remove_view(app->view_dispatcher, ScopeViewVariableItemList);
     variable_item_list_free(app->variable_item_list);
 
-    //  Widget
+    // Text input
     view_dispatcher_remove_view(app->view_dispatcher, ScopeViewWidget);
+    text_input_free(app->text_input);
+
+    //  Widget
+    view_dispatcher_remove_view(app->view_dispatcher, ScopeViewSave);
     widget_free(app->widget);
 
     // View dispatcher

+ 16 - 1
scope_app_i.h

@@ -9,8 +9,13 @@
 #include <gui/modules/submenu.h>
 #include <gui/modules/variable_item_list.h>
 #include <gui/modules/widget.h>
+#include <gui/modules/text_input.h>
 #include <notification/notification_messages.h>
 
+#define ADC_CONVERTED_DATA_BUFFER_SIZE ((uint32_t)128)
+#define FLIPPERSCOPE_APP_EXTENSION ".dat"
+#define MAX_LEN_NAME 30
+
 typedef struct ScopeApp ScopeApp;
 
 typedef struct {
@@ -28,7 +33,10 @@ typedef struct {
     char* str;
 } measurement;
 
-static const measurement measurement_list[] = {{m_time, "Time"}, {m_voltage, "Voltage"}, {m_capture, "Capture"}};
+static const measurement measurement_list[] = {
+    {m_time, "Time"},
+    {m_voltage, "Voltage"},
+    {m_capture, "Capture"}};
 
 struct ScopeApp {
     Gui* gui;
@@ -38,6 +46,13 @@ struct ScopeApp {
     VariableItemList* variable_item_list;
     Submenu* submenu;
     Widget* widget;
+    TextInput* text_input;
     double time;
     enum measureenum measurement;
+    char file_name_tmp[MAX_LEN_NAME];
+    uint16_t* data;
+};
+
+enum ScopeCustomEvent {
+    ScopeCustomEventTextInputDone,
 };