anfractuosity 3 лет назад
Родитель
Сommit
16ffbd1c68

+ 15 - 0
helpers/scope_types.h

@@ -0,0 +1,15 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+
+#define S_VERSION_APP "0.0.1"
+#define S_DEVELOPED "anfractuosity"
+#define S_GITHUB "https://github.com/anfractuosity/flipperscope"
+
+typedef enum {
+    ScopeViewVariableItemList,
+    ScopeViewSubmenu,
+    ScopeViewWidget,
+} ScopeView;
+

+ 30 - 0
scenes/scope_scene.c

@@ -0,0 +1,30 @@
+#include "../scope_app_i.h"
+
+// Generate scene on_enter handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter,
+void (*const scope_scene_on_enter_handlers[])(void*) = {
+#include "scope_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_event handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event,
+bool (*const scope_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = {
+#include "scope_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Generate scene on_exit handlers array
+#define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit,
+void (*const scope_scene_on_exit_handlers[])(void* context) = {
+#include "scope_scene_config.h"
+};
+#undef ADD_SCENE
+
+// Initialize scene handlers configuration structure
+const SceneManagerHandlers scope_scene_handlers = {
+    .on_enter_handlers = scope_scene_on_enter_handlers,
+    .on_event_handlers = scope_scene_on_event_handlers,
+    .on_exit_handlers = scope_scene_on_exit_handlers,
+    .scene_num = ScopeSceneNum,
+};

+ 29 - 0
scenes/scope_scene.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <gui/scene_manager.h>
+
+// Generate scene id and total number
+#define ADD_SCENE(prefix, name, id) ScopeScene##id,
+typedef enum {
+#include "scope_scene_config.h"
+    ScopeSceneNum,
+} ScopeScene;
+#undef ADD_SCENE
+
+extern const SceneManagerHandlers scope_scene_handlers;
+
+// Generate scene on_enter handlers declaration
+#define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*);
+#include "scope_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 "scope_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 "scope_scene_config.h"
+#undef ADD_SCENE

+ 63 - 0
scenes/scope_scene_about.c

@@ -0,0 +1,63 @@
+#include "../scope_app_i.h"
+#include "../helpers/scope_types.h"
+
+void scope_scene_about_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_about_on_enter(void* context) {
+    ScopeApp* app = context;
+
+    FuriString* temp_str;
+    temp_str = furi_string_alloc();
+    furi_string_printf(temp_str, "\e#%s\n", "Information");
+
+    furi_string_cat_printf(temp_str, "Version: %s\n", S_VERSION_APP);
+    furi_string_cat_printf(temp_str, "Developed by: %s\n", S_DEVELOPED);
+    furi_string_cat_printf(temp_str, "Github: %s\n\n", S_GITHUB);
+
+    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!         Flipperscope            \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, ScopeViewWidget);
+}
+
+bool scope_scene_about_on_event(void* context, SceneManagerEvent event) {
+    ScopeApp* app = context;
+    bool consumed = false;
+    UNUSED(app);
+    UNUSED(event);
+    return consumed;
+}
+
+void scope_scene_about_on_exit(void* context) {
+    ScopeApp* app = context;
+    // Clear views
+    widget_reset(app->widget);
+}

+ 4 - 0
scenes/scope_scene_config.h

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

+ 409 - 0
scenes/scope_scene_run.c

@@ -0,0 +1,409 @@
+#include "../scope_app_i.h"
+#include "../helpers/scope_types.h"
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_resources.h>
+#include <gui/gui.h>
+#include <gui/modules/submenu.h>
+#include <input/input.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "stm32wbxx_hal.h"
+#include "stm32wbxx_hal_tim.h"
+#include "stm32wbxx_nucleo.h"
+#include "stm32wbxx_hal_adc.h"
+#include "scope_icons.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 <notification/notification_messages.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__)       \
+  ((__ADC_DATA__) * (__VREFANALOG_VOLTAGE__) / DIGITAL_SCALE_12BITS)
+#define VDDA_APPLI                       ((uint32_t)3300)
+
+const uint32_t AHBPrescTable[16UL] = {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL};
+const uint32_t APBPrescTable[8UL]  = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL};
+const uint32_t MSIRangeTable[16UL] = {100000UL, 200000UL, 400000UL, 800000UL, 1000000UL, 2000000UL, \
+                                      4000000UL, 8000000UL, 16000000UL, 24000000UL, 32000000UL, 48000000UL, 0UL, 0UL, 0UL, 0UL}; /* 0UL values are incorrect cases */
+
+typedef struct {
+    uint8_t x, y;
+} ImagePosition;
+
+double time;
+
+void Error_Handler()
+{
+    while (1) {
+    }
+}
+
+uint32_t timebase = 1000;
+
+static ADC_HandleTypeDef hadc1;
+static DMA_HandleTypeDef hdma_adc1;
+static TIM_HandleTypeDef htim2;
+
+__IO uint16_t aADCxConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE];       /* ADC group regular conversion data (array of data) */
+__IO uint16_t aADCxConvertedData_Voltage_mVoltA[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* Value of voltage calculated from ADC conversion data (unit: mV) (array of data) */
+__IO uint16_t aADCxConvertedData_Voltage_mVoltB[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* Value of voltage calculated from ADC conversion data (unit: mV) (array of data) */
+__IO uint8_t ubDmaTransferStatus = 2;   /* Variable set into DMA interruption callback */
+__IO uint16_t *mvoltWrite = &aADCxConvertedData_Voltage_mVoltA[0];
+__IO uint16_t *mvoltDisplay = &aADCxConvertedData_Voltage_mVoltB[0];
+
+
+void HAL_ADC_MspInit(ADC_HandleTypeDef * hadc)
+{
+    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
+    if (hadc->Instance == ADC1) {
+        __HAL_RCC_ADC_CLK_ENABLE();
+        __HAL_RCC_GPIOC_CLK_ENABLE();
+        GPIO_InitStruct.Pin = GPIO_PIN_0;
+        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
+        GPIO_InitStruct.Pull = GPIO_NOPULL;
+        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
+
+        hdma_adc1.Instance = DMA1_Channel1;
+        hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
+        hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
+        hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
+        hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
+        hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
+        hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
+        hdma_adc1.Init.Mode = DMA_CIRCULAR;
+        hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
+        if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) {
+            Error_Handler();
+        }
+
+        __HAL_LINKDMA(hadc, DMA_Handle, hdma_adc1);
+
+        HAL_NVIC_SetPriority(ADC1_IRQn, 15, 0);
+        HAL_NVIC_EnableIRQ(ADC1_IRQn);
+    }
+}
+
+void HAL_ADC_MspDeInit(ADC_HandleTypeDef * hadc)
+{
+    if (hadc->Instance == ADC1) {
+        __HAL_RCC_ADC_CLK_DISABLE();
+        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0);
+        HAL_DMA_DeInit(hadc->DMA_Handle);
+        HAL_NVIC_DisableIRQ(ADC1_IRQn);
+    }
+}
+
+void HAL_TIM_Base_MspInit(TIM_HandleTypeDef * htim_base)
+{
+    if (htim_base->Instance == TIM2) {
+        __HAL_RCC_TIM2_CLK_ENABLE();
+        HAL_NVIC_SetPriority(TIM2_IRQn, 15, 0);
+        HAL_NVIC_EnableIRQ(TIM2_IRQn);
+    }
+}
+
+void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef * htim_base)
+{
+    if (htim_base->Instance == TIM2) {
+        __HAL_RCC_TIM2_CLK_DISABLE();
+        HAL_NVIC_DisableIRQ(TIM2_IRQn);
+    }
+}
+
+void DMA1_Channel1_IRQHandler(void)
+{
+    HAL_DMA_IRQHandler(&hdma_adc1);
+}
+
+void ADC1_IRQHandler(void)
+{
+    HAL_ADC_IRQHandler(&hadc1);
+}
+
+void TIM2_IRQHandler(void)
+{
+    HAL_TIM_IRQHandler(&htim2);
+}
+
+static void MX_ADC1_Init(void)
+{
+    ADC_ChannelConfTypeDef sConfig = { 0 };
+    hadc1.Instance = ADC1;
+    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
+    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
+    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
+    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
+    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
+    hadc1.Init.LowPowerAutoWait = DISABLE;
+    hadc1.Init.ContinuousConvMode = DISABLE;
+    hadc1.Init.NbrOfConversion = 1;
+    hadc1.Init.DiscontinuousConvMode = DISABLE;
+    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
+    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
+    hadc1.Init.DMAContinuousRequests = ENABLE;
+    hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
+    hadc1.Init.OversamplingMode = DISABLE;
+    if (HAL_ADC_Init(&hadc1) != HAL_OK) {
+        Error_Handler();
+    }
+
+    sConfig.Channel = ADC_CHANNEL_1;
+    sConfig.Rank = ADC_REGULAR_RANK_1;
+    sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLE_5;     ////ADC_SAMPLETIME_640CYCLES_5;
+    sConfig.SingleDiff = ADC_SINGLE_ENDED;
+    sConfig.OffsetNumber = ADC_OFFSET_NONE;
+    sConfig.Offset = 0;
+    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
+        Error_Handler();
+    }
+}
+
+static void MX_TIM2_Init(uint32_t period)
+{
+
+    TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
+    TIM_MasterConfigTypeDef sMasterConfig = { 0 };
+    htim2.Instance = TIM2;
+    htim2.Init.Prescaler = 1;
+    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
+    htim2.Init.Period = period;
+    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
+    if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
+        Error_Handler();
+    }
+    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
+    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
+        Error_Handler();
+    }
+    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
+    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
+    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) !=
+        HAL_OK) {
+        Error_Handler();
+    }
+}
+
+static void MX_DMA_Init(void)
+{
+    __HAL_RCC_DMAMUX1_CLK_ENABLE();
+    __HAL_RCC_DMA1_CLK_ENABLE();
+    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 15, 0);
+    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
+}
+
+static void MX_GPIO_Init(void)
+{
+    __HAL_RCC_GPIOC_CLK_ENABLE();
+}
+
+
+void swap(__IO uint16_t **a, __IO uint16_t **b){
+    __IO uint16_t *tmp;
+    tmp = *a;
+    *a = *b;
+    *b = tmp;
+}
+
+void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * hadc)
+{
+    UNUSED(hadc);
+    uint32_t tmp_index = 0;
+
+    for (tmp_index = (ADC_CONVERTED_DATA_BUFFER_SIZE / 2);
+         tmp_index < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index++) {
+        mvoltWrite[tmp_index] =
+            __ADC_CALC_DATA_VOLTAGE(VDDA_APPLI,
+                                    aADCxConvertedData[tmp_index]);
+    }
+    ubDmaTransferStatus = 1;
+    swap(&mvoltWrite, &mvoltDisplay);
+}
+
+void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef * hadc)
+{
+    UNUSED(hadc);
+    uint32_t tmp_index = 0;
+
+    for (tmp_index = 0; tmp_index < (ADC_CONVERTED_DATA_BUFFER_SIZE / 2);
+         tmp_index++) {
+        mvoltWrite[tmp_index] =
+            __ADC_CALC_DATA_VOLTAGE(VDDA_APPLI,
+                                    aADCxConvertedData[tmp_index]);
+    }
+    ubDmaTransferStatus = 0;
+}
+
+void HAL_ADC_ErrorCallback(ADC_HandleTypeDef * hadc)
+{
+    UNUSED(hadc);
+    Error_Handler();
+}
+
+
+// Screen is 128x64 px
+static void app_draw_callback(Canvas * canvas, void *ctx)
+{
+    UNUSED(ctx);
+
+    char buf[50];
+    snprintf(buf, 50, "Time: %.3f", time);
+
+    canvas_draw_str(canvas, 10, 10, buf);
+    for(uint32_t x = 1; x < ADC_CONVERTED_DATA_BUFFER_SIZE; x++){
+        uint32_t prev = 64 - (mvoltDisplay[x-1] / (VDDA_APPLI / 64));
+        uint32_t cur = 64 - (mvoltDisplay[x] / (VDDA_APPLI / 64));
+        canvas_draw_line(canvas, x - 1, prev, x, cur);
+    }
+    canvas_draw_line(canvas, 0, 0, 0, 63);
+    canvas_draw_line(canvas, 0, 63, 128, 63);
+}
+
+static void app_input_callback(InputEvent * input_event, void *ctx)
+{
+    furi_assert(ctx);
+    FuriMessageQueue *event_queue = ctx;
+    furi_message_queue_put(event_queue, input_event, FuriWaitForever);
+}
+
+// ramVector found from - https://community.nxp.com/t5/i-MX-Processors/Relocate-vector-table-to-ITCM/m-p/1302304
+// the aligned aspect is key!
+#define TABLE_SIZE	79
+uint32_t ramVector[TABLE_SIZE+1] __attribute__((aligned(512)));
+
+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;
+    time = app->time;
+    UNUSED(app);
+
+    __disable_irq();
+    memcpy(ramVector, (uint32_t*)(FLASH_BASE | SCB->VTOR), sizeof(uint32_t) * TABLE_SIZE);
+    SCB->VTOR = (uint32_t)ramVector;
+    ramVector[27] = (uint32_t)DMA1_Channel1_IRQHandler;
+    ramVector[34] = (uint32_t)ADC1_IRQHandler;
+    ramVector[44] = (uint32_t)TIM2_IRQHandler;
+    __enable_irq();
+
+    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
+
+    FuriMessageQueue *event_queue =
+        furi_message_queue_alloc(8, sizeof(InputEvent));
+
+    uint32_t tmp_index_adc_converted_data = 0;
+    MX_GPIO_Init();
+    MX_DMA_Init();
+
+    uint32_t period = (uint32_t)((double)HAL_RCC_GetPCLK1Freq() * app->time);
+    MX_TIM2_Init(period);
+
+    VREFBUF->CSR |= VREFBUF_CSR_ENVR;
+    VREFBUF->CSR &= ~VREFBUF_CSR_HIZ;
+    VREFBUF->CSR |= VREFBUF_CSR_VRS;
+    while (!(VREFBUF->CSR & VREFBUF_CSR_VRR)) {
+    };
+
+    MX_ADC1_Init();
+    for (tmp_index_adc_converted_data = 0;
+         tmp_index_adc_converted_data < ADC_CONVERTED_DATA_BUFFER_SIZE;
+         tmp_index_adc_converted_data++) {
+        aADCxConvertedData[tmp_index_adc_converted_data] =
+            VAR_CONVERTED_DATA_INIT_VALUE;
+    }
+
+    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK) {
+        Error_Handler();
+    }
+
+    if (HAL_TIM_Base_Start(&htim2) != HAL_OK) {
+        Error_Handler();
+    }
+
+    if (HAL_ADC_Start_DMA(&hadc1,
+                          (uint32_t *) aADCxConvertedData,
+                          ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK) {
+        Error_Handler();
+    }
+
+    ViewPort *view_port = view_port_alloc();
+    view_port_draw_callback_set(view_port, app_draw_callback, view_port);
+    view_port_input_callback_set(view_port, app_input_callback, event_queue);
+
+    // 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;
+    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 InputKeyUp:
+                    break;
+                case InputKeyDown:
+                    break;
+                default:
+                    running = false;
+                    break;
+                }
+            }
+        }
+        view_port_update(view_port);
+    }
+
+    HAL_ADC_Stop_DMA (&hadc1);
+    __disable_irq();
+    SCB->VTOR = 0;
+    __enable_irq();
+
+    view_port_enabled_set(view_port, false);
+    gui_remove_view_port(gui, view_port);
+    view_port_free(view_port);
+
+    furi_record_close(RECORD_GUI);
+    scene_manager_previous_scene(app->scene_manager);
+    submenu_set_selected_item(
+        app->submenu, 0);
+}
+
+bool scope_scene_run_on_event(void* context, SceneManagerEvent event) {
+    ScopeApp* app = context;
+    bool consumed = false;
+    UNUSED(app);
+    UNUSED(event);
+    return consumed;
+}
+
+void scope_scene_run_on_exit(void* context) {
+    ScopeApp* app = context;
+
+    // Clear views
+    widget_reset(app->widget);
+}

+ 71 - 0
scenes/scope_scene_setup.c

@@ -0,0 +1,71 @@
+#include "../scope_app_i.h"
+#include "../helpers/scope_types.h"
+
+static const double time_list[] = {
+    1.0,
+    0.1,
+    0.01,
+    0.001,
+    0.0005
+};
+
+void scope_scene_setup_widget_callback(
+    GuiButtonType result,
+    InputType type,
+    void* context) {
+    ScopeApp* app = context;
+    if(type == InputTypeShort) {
+        view_dispatcher_send_custom_event(app->view_dispatcher, result);
+    }
+}
+
+static void timeperiod_cb(VariableItem* item) {
+    UNUSED(item);
+    ScopeApp* app = variable_item_get_context(item);
+    furi_assert(app);
+    uint8_t index = variable_item_get_current_value_index(item);
+    char tmp[8];
+    snprintf(tmp, 7, "%3.4f", time_list[index]);
+    variable_item_set_current_value_text(item, tmp);
+    app->time = time_list[index];
+}
+
+void scope_scene_setup_on_enter(void* context) {
+    ScopeApp* app = context;
+    VariableItemList* var_item_list = app->variable_item_list;
+    VariableItem* item;
+    item = variable_item_list_add(
+        var_item_list,
+        "Time period",
+        COUNT_OF(time_list),
+        timeperiod_cb,
+        app);
+
+    char tmp[8];
+    snprintf(tmp, 7, "%3.4f", app->time);
+
+    for (uint32_t i = 0; i < COUNT_OF(time_list); i++){
+        if(time_list[i] == app->time){
+            variable_item_set_current_value_index(item, i);
+            variable_item_set_current_value_text(item, tmp);
+            break;
+        }
+    }
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, ScopeViewVariableItemList);
+}
+
+bool scope_scene_setup_on_event(void* context, SceneManagerEvent event) {
+    ScopeApp* app = context;
+    bool consumed = false;
+    UNUSED(app);
+    UNUSED(event);
+    return consumed;
+}
+
+void scope_scene_setup_on_exit(void* context) {
+    ScopeApp* app = context;
+    variable_item_list_reset(app->variable_item_list);
+    // Clear views
+    widget_reset(app->widget);
+}

+ 70 - 0
scenes/scope_scene_start.c

@@ -0,0 +1,70 @@
+#include "../scope_app_i.h"
+
+typedef enum {
+    SubmenuIndexScopeRun,
+    SubmenuIndexScopeSetup,
+    SubmenuIndexScopeAbout,
+} SubmenuIndex;
+
+void scope_scene_start_submenu_callback(void* context, uint32_t index) {
+    ScopeApp* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, index);
+}
+
+void scope_scene_start_on_enter(void* context) {
+    UNUSED(context);
+    ScopeApp* app = context;
+    Submenu* submenu = app->submenu;
+
+    submenu_add_item(
+        submenu,
+        "Run",
+        SubmenuIndexScopeRun,
+        scope_scene_start_submenu_callback,
+        app);
+
+    submenu_add_item(
+        submenu,
+        "Setup",
+        SubmenuIndexScopeSetup,
+        scope_scene_start_submenu_callback,
+        app);
+
+    submenu_add_item(
+        submenu,
+        "About",
+        SubmenuIndexScopeAbout,
+        scope_scene_start_submenu_callback,
+        app);
+
+    submenu_set_selected_item(
+        submenu, scene_manager_get_scene_state(app->scene_manager, ScopeSceneStart));
+
+    view_dispatcher_switch_to_view(app->view_dispatcher, ScopeViewSubmenu);
+}
+
+bool scope_scene_start_on_event(void* context, SceneManagerEvent event) {
+    ScopeApp* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        if(event.event == SubmenuIndexScopeAbout) {
+            scene_manager_next_scene(app->scene_manager, ScopeSceneAbout);
+            consumed = true;
+        } else if(event.event == SubmenuIndexScopeRun) {
+            scene_manager_next_scene(app->scene_manager, ScopeSceneRun);
+            consumed = true;
+        } else if(event.event == SubmenuIndexScopeSetup) {
+            scene_manager_next_scene(app->scene_manager, ScopeSceneSetup);
+            consumed = true;
+        }
+        scene_manager_set_scene_state(app->scene_manager, ScopeSceneStart, event.event);
+    }
+
+    return consumed;
+}
+
+void scope_scene_start_on_exit(void* context) {
+    ScopeApp* app = context;
+    submenu_reset(app->submenu);
+}

+ 84 - 331
scope.c

@@ -2,378 +2,131 @@
 #include <furi_hal.h>
 #include <furi_hal.h>
 #include <furi_hal_resources.h>
 #include <furi_hal_resources.h>
 #include <gui/gui.h>
 #include <gui/gui.h>
+#include <gui/modules/submenu.h>
 #include <input/input.h>
 #include <input/input.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
 
 
-#include "stm32wbxx_hal.h"
-#include "stm32wbxx_hal_tim.h"
-#include "stm32wbxx_nucleo.h"
-#include "stm32wbxx_hal_adc.h"
 #include "scope_icons.h"
 #include "scope_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__)       \
-  ((__ADC_DATA__) * (__VREFANALOG_VOLTAGE__) / DIGITAL_SCALE_12BITS)
-#define VDDA_APPLI                       ((uint32_t)3300)
+#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 <notification/notification_messages.h>
 
 
-const uint32_t AHBPrescTable[16UL] = {1UL, 3UL, 5UL, 1UL, 1UL, 6UL, 10UL, 32UL, 2UL, 4UL, 8UL, 16UL, 64UL, 128UL, 256UL, 512UL};
-const uint32_t APBPrescTable[8UL]  = {0UL, 0UL, 0UL, 0UL, 1UL, 2UL, 3UL, 4UL};
-const uint32_t MSIRangeTable[16UL] = {100000UL, 200000UL, 400000UL, 800000UL, 1000000UL, 2000000UL, \
-                                      4000000UL, 8000000UL, 16000000UL, 24000000UL, 32000000UL, 48000000UL, 0UL, 0UL, 0UL, 0UL}; /* 0UL values are incorrect cases */
+#include "scope_app_i.h"
+#define TAG "Scope"
 
 
-void Error_Handler()
-{
-    while (1) {
-    }
+static bool scope_app_custom_event_callback(void* context, uint32_t event) {
+    furi_assert(context);
+    ScopeApp* app = context;
+    return scene_manager_handle_custom_event(app->scene_manager, event);
 }
 }
 
 
-uint32_t timebase = 50;
-
-static ADC_HandleTypeDef hadc1;
-static DMA_HandleTypeDef hdma_adc1;
-static TIM_HandleTypeDef htim2;
-
-__IO uint16_t aADCxConvertedData[ADC_CONVERTED_DATA_BUFFER_SIZE];       /* ADC group regular conversion data (array of data) */
-__IO uint16_t aADCxConvertedData_Voltage_mVoltA[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* Value of voltage calculated from ADC conversion data (unit: mV) (array of data) */
-__IO uint16_t aADCxConvertedData_Voltage_mVoltB[ADC_CONVERTED_DATA_BUFFER_SIZE]; /* Value of voltage calculated from ADC conversion data (unit: mV) (array of data) */
-__IO uint8_t ubDmaTransferStatus = 2;   /* Variable set into DMA interruption callback */
-__IO uint16_t *mvoltWrite = &aADCxConvertedData_Voltage_mVoltA[0];
-__IO uint16_t *mvoltDisplay = &aADCxConvertedData_Voltage_mVoltB[0];
-
-void HAL_ADC_MspInit(ADC_HandleTypeDef * hadc)
-{
-    GPIO_InitTypeDef GPIO_InitStruct = { 0 };
-    if (hadc->Instance == ADC1) {
-        __HAL_RCC_ADC_CLK_ENABLE();
-        __HAL_RCC_GPIOC_CLK_ENABLE();
-        GPIO_InitStruct.Pin = GPIO_PIN_0;
-        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
-        GPIO_InitStruct.Pull = GPIO_NOPULL;
-        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
-
-        hdma_adc1.Instance = DMA1_Channel1;
-        hdma_adc1.Init.Request = DMA_REQUEST_ADC1;
-        hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
-        hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
-        hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
-        hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
-        hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
-        hdma_adc1.Init.Mode = DMA_CIRCULAR;
-        hdma_adc1.Init.Priority = DMA_PRIORITY_LOW;
-        if (HAL_DMA_Init(&hdma_adc1) != HAL_OK) {
-            Error_Handler();
-        }
-
-        __HAL_LINKDMA(hadc, DMA_Handle, hdma_adc1);
-
-        HAL_NVIC_SetPriority(ADC1_IRQn, 15, 0);
-        HAL_NVIC_EnableIRQ(ADC1_IRQn);
-    }
+static bool scope_app_back_event_callback(void* context) {
+    furi_assert(context);
+    ScopeApp* app = context;
+    return scene_manager_handle_back_event(app->scene_manager);
 }
 }
 
 
-void HAL_ADC_MspDeInit(ADC_HandleTypeDef * hadc)
-{
-    if (hadc->Instance == ADC1) {
-        __HAL_RCC_ADC_CLK_DISABLE();
-        HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0);
-        HAL_DMA_DeInit(hadc->DMA_Handle);
-        HAL_NVIC_DisableIRQ(ADC1_IRQn);
-    }
+static void scope_app_tick_event_callback(void* context) {
+    furi_assert(context);
+    ScopeApp* app = context;
+    scene_manager_handle_tick_event(app->scene_manager);
 }
 }
 
 
-void HAL_TIM_Base_MspInit(TIM_HandleTypeDef * htim_base)
+void assert_failed(uint8_t * file, uint32_t line)
 {
 {
-    if (htim_base->Instance == TIM2) {
-        __HAL_RCC_TIM2_CLK_ENABLE();
-        HAL_NVIC_SetPriority(TIM2_IRQn, 15, 0);
-        HAL_NVIC_EnableIRQ(TIM2_IRQn);
+    UNUSED(file);
+    UNUSED(line);
+    while (1) {
     }
     }
 }
 }
 
 
-void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef * htim_base)
-{
-    if (htim_base->Instance == TIM2) {
-        __HAL_RCC_TIM2_CLK_DISABLE();
-        HAL_NVIC_DisableIRQ(TIM2_IRQn);
-    }
-}
+ScopeApp* scope_app_alloc() {
+    ScopeApp* app = malloc(sizeof(ScopeApp));
 
 
-void DMA1_Channel1_IRQHandler(void)
-{
-    HAL_DMA_IRQHandler(&hdma_adc1);
-}
+    // GUI
+    app->gui = furi_record_open(RECORD_GUI);
 
 
-void ADC1_IRQHandler(void)
-{
-    HAL_ADC_IRQHandler(&hadc1);
-}
+    // View Dispatcher
+    app->view_dispatcher = view_dispatcher_alloc();
+    app->scene_manager = scene_manager_alloc(&scope_scene_handlers, app);
+    view_dispatcher_enable_queue(app->view_dispatcher);
 
 
-void TIM2_IRQHandler(void)
-{
-    HAL_TIM_IRQHandler(&htim2);
-}
+    view_dispatcher_set_event_callback_context(app->view_dispatcher, app);
+    view_dispatcher_set_custom_event_callback(
+        app->view_dispatcher, scope_app_custom_event_callback);
+    view_dispatcher_set_navigation_event_callback(
+        app->view_dispatcher, scope_app_back_event_callback);
+    view_dispatcher_set_tick_event_callback(
+        app->view_dispatcher, scope_app_tick_event_callback, 100);
 
 
-static void MX_ADC1_Init(void)
-{
-    ADC_ChannelConfTypeDef sConfig = { 0 };
-    hadc1.Instance = ADC1;
-    hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
-    hadc1.Init.Resolution = ADC_RESOLUTION_12B;
-    hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
-    hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
-    hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
-    hadc1.Init.LowPowerAutoWait = DISABLE;
-    hadc1.Init.ContinuousConvMode = DISABLE;
-    hadc1.Init.NbrOfConversion = 1;
-    hadc1.Init.DiscontinuousConvMode = DISABLE;
-    hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_T2_TRGO;
-    hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
-    hadc1.Init.DMAContinuousRequests = ENABLE;
-    hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
-    hadc1.Init.OversamplingMode = DISABLE;
-    if (HAL_ADC_Init(&hadc1) != HAL_OK) {
-        Error_Handler();
-    }
+    view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
 
 
-    sConfig.Channel = ADC_CHANNEL_1;
-    sConfig.Rank = ADC_REGULAR_RANK_1;
-    sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLE_5;     ////ADC_SAMPLETIME_640CYCLES_5;
-    sConfig.SingleDiff = ADC_SINGLE_ENDED;
-    sConfig.OffsetNumber = ADC_OFFSET_NONE;
-    sConfig.Offset = 0;
-    if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
-        Error_Handler();
-    }
-}
+    // Open Notification record
+    app->notifications = furi_record_open(RECORD_NOTIFICATION);
 
 
-static void MX_TIM2_Init(void)
-{
+    // Variable Item List
+    app->variable_item_list = variable_item_list_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        ScopeViewVariableItemList,
+        variable_item_list_get_view(app->variable_item_list));
 
 
-    uint32_t period = HAL_RCC_GetPCLK1Freq() / timebase;
-    TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
-    TIM_MasterConfigTypeDef sMasterConfig = { 0 };
-    htim2.Instance = TIM2;
-    htim2.Init.Prescaler = 1;
-    htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
-    htim2.Init.Period = period;
-    htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
-    htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
-    if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
-        Error_Handler();
-    }
-    sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
-    if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
-        Error_Handler();
-    }
-    sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
-    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
-    if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) !=
-        HAL_OK) {
-        Error_Handler();
-    }
-}
+    // SubMenu
+    app->submenu = submenu_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, ScopeViewSubmenu, submenu_get_view(app->submenu));
 
 
-static void MX_DMA_Init(void)
-{
-    __HAL_RCC_DMAMUX1_CLK_ENABLE();
-    __HAL_RCC_DMA1_CLK_ENABLE();
-    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 15, 0);
-    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
-}
+    // Widget
+    app->widget = widget_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher, ScopeViewWidget, widget_get_view(app->widget));
 
 
-static void MX_GPIO_Init(void)
-{
-    __HAL_RCC_GPIOC_CLK_ENABLE();
+    app->time = 0.001;
+    scene_manager_next_scene(app->scene_manager, ScopeSceneStart);
+    return app;
 }
 }
 
 
+void scope_app_free(ScopeApp* app) {
+    furi_assert(app);
 
 
-void swap(__IO uint16_t **a, __IO uint16_t **b){
-    __IO uint16_t *tmp;
-    tmp = *a;
-    *a = *b;
-    *b = tmp;
-}
+    // Submenu
+    view_dispatcher_remove_view(app->view_dispatcher, ScopeViewSubmenu);
+    submenu_free(app->submenu);
 
 
-void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * hadc)
-{
-    UNUSED(hadc);
-    uint32_t tmp_index = 0;
+    // Variable Item List
+    view_dispatcher_remove_view(app->view_dispatcher, ScopeViewVariableItemList);
+    variable_item_list_free(app->variable_item_list);
 
 
-    for (tmp_index = (ADC_CONVERTED_DATA_BUFFER_SIZE / 2);
-         tmp_index < ADC_CONVERTED_DATA_BUFFER_SIZE; tmp_index++) {
-        mvoltWrite[tmp_index] =
-            __ADC_CALC_DATA_VOLTAGE(VDDA_APPLI,
-                                    aADCxConvertedData[tmp_index]);
-    }
-    ubDmaTransferStatus = 1;
-    swap(&mvoltWrite, &mvoltDisplay);
-}
+    //  Widget
+    view_dispatcher_remove_view(app->view_dispatcher, ScopeViewWidget);
+    widget_free(app->widget);
 
 
-void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef * hadc)
-{
-    UNUSED(hadc);
-    uint32_t tmp_index = 0;
-
-    for (tmp_index = 0; tmp_index < (ADC_CONVERTED_DATA_BUFFER_SIZE / 2);
-         tmp_index++) {
-        mvoltWrite[tmp_index] =
-            __ADC_CALC_DATA_VOLTAGE(VDDA_APPLI,
-                                    aADCxConvertedData[tmp_index]);
-    }
-    ubDmaTransferStatus = 0;
-}
+    // View dispatcher
+    view_dispatcher_free(app->view_dispatcher);
+    scene_manager_free(app->scene_manager);
 
 
-void HAL_ADC_ErrorCallback(ADC_HandleTypeDef * hadc)
-{
-    UNUSED(hadc);
-    Error_Handler();
-}
+    // Notifications
+    furi_record_close(RECORD_NOTIFICATION);
+    app->notifications = NULL;
 
 
-typedef struct {
-    uint8_t x, y;
-} ImagePosition;
-
-static ImagePosition image_position = {.x = 0,.y = 0 };
-
-void assert_failed(uint8_t * file, uint32_t line)
-{
-    UNUSED(file);
-    UNUSED(line);
-    while (1) {
-    }
-}
-
-// Screen is 128x64 px
-static void app_draw_callback(Canvas * canvas, void *ctx)
-{
-    UNUSED(ctx);
-    char buf[50];
-    snprintf(buf, 50, "Time: %.3f", (double)1.0/((double)timebase));
-
-    canvas_draw_str(canvas, 10, 10, buf);
-    for(uint32_t x = 1; x < ADC_CONVERTED_DATA_BUFFER_SIZE; x++){
-        uint32_t prev = 64 - (mvoltDisplay[x-1] / (VDDA_APPLI / 64));
-        uint32_t cur = 64 - (mvoltDisplay[x] / (VDDA_APPLI / 64));
-        canvas_draw_line(canvas, x - 1, prev, x, cur);
-    }
-    canvas_draw_line(canvas, 0, 0, 0, 63);
-    canvas_draw_line(canvas, 0, 63, 128, 63);
-}
+    // Close records
+    furi_record_close(RECORD_GUI);
 
 
-static void app_input_callback(InputEvent * input_event, void *ctx)
-{
-    furi_assert(ctx);
-    FuriMessageQueue *event_queue = ctx;
-    furi_message_queue_put(event_queue, input_event, FuriWaitForever);
+    free(app);
 }
 }
 
 
-// ramVector found from - https://community.nxp.com/t5/i-MX-Processors/Relocate-vector-table-to-ITCM/m-p/1302304
-// the aligned aspect is key!
-#define TABLE_SIZE	79
-uint32_t ramVector[TABLE_SIZE+1] __attribute__((aligned(512)));
-
 int32_t scope_main(void *p)
 int32_t scope_main(void *p)
 {
 {
-    __disable_irq();
-    memcpy(ramVector, (uint32_t*)(FLASH_BASE | SCB->VTOR), sizeof(uint32_t) * TABLE_SIZE);
-    SCB->VTOR = (uint32_t)ramVector;
-    ramVector[27] = (uint32_t)DMA1_Channel1_IRQHandler;
-    ramVector[34] = (uint32_t)ADC1_IRQHandler;
-    ramVector[44] = (uint32_t)TIM2_IRQHandler;
-    __enable_irq();
-    UNUSED(p);
-    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
-    FuriMessageQueue *event_queue =
-        furi_message_queue_alloc(8, sizeof(InputEvent));
-
-    uint32_t tmp_index_adc_converted_data = 0;
-    MX_GPIO_Init();
-    MX_DMA_Init();
-    MX_TIM2_Init();
-
-    VREFBUF->CSR |= VREFBUF_CSR_ENVR;
-    VREFBUF->CSR &= ~VREFBUF_CSR_HIZ;
-    VREFBUF->CSR |= VREFBUF_CSR_VRS;
-    while (!(VREFBUF->CSR & VREFBUF_CSR_VRR)) {
-    };
-
-    MX_ADC1_Init();
-    for (tmp_index_adc_converted_data = 0;
-         tmp_index_adc_converted_data < ADC_CONVERTED_DATA_BUFFER_SIZE;
-         tmp_index_adc_converted_data++) {
-        aADCxConvertedData[tmp_index_adc_converted_data] =
-            VAR_CONVERTED_DATA_INIT_VALUE;
-    }
-
-    if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK) {
-        Error_Handler();
-    }
-
-    if (HAL_TIM_Base_Start(&htim2) != HAL_OK) {
-        Error_Handler();
-    }
-
-    if (HAL_ADC_Start_DMA(&hadc1,
-                          (uint32_t *) aADCxConvertedData,
-                          ADC_CONVERTED_DATA_BUFFER_SIZE) != HAL_OK) {
-        Error_Handler();
-    }
-
-    ViewPort *view_port = view_port_alloc();
-    view_port_draw_callback_set(view_port, app_draw_callback, view_port);
-    view_port_input_callback_set(view_port, app_input_callback,
-                                 event_queue);
-
-    // 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;
-    
-    while (running) {
-        if (furi_message_queue_get(event_queue, &event, 1) ==
-            FuriStatusOk) {
-            if ((event.type == InputTypePress)
-                || (event.type == InputTypeRepeat)) {
-                switch (event.key) {
-                case InputKeyLeft:
-                    image_position.x -= 2;
-                    break;
-                case InputKeyRight:
-                    image_position.x += 2;
-                    break;
-                case InputKeyUp:
-                    image_position.y -= 2;
-                    break;
-                case InputKeyDown:
-                    image_position.y += 2;
-                    break;
-                default:
-                    running = false;
-                    break;
-                }
-            }
-        }
-
-        view_port_update(view_port);
-    }
-
-    HAL_ADC_Stop_DMA (&hadc1);
-    __disable_irq();
-    SCB->VTOR = 0;
-    __enable_irq();
 
 
     UNUSED(p);
     UNUSED(p);
-    view_port_enabled_set(view_port, false);
-    gui_remove_view_port(gui, view_port);
-    view_port_free(view_port);
-    furi_message_queue_free(event_queue);
-
-    furi_record_close(RECORD_GUI);
-
+    ScopeApp* scope_app = scope_app_alloc();
+    view_dispatcher_run(scope_app->view_dispatcher);
+    scope_app_free(scope_app);
     return 0;
     return 0;
 }
 }

+ 4 - 0
scope_app_i.c

@@ -0,0 +1,4 @@
+#include "scope_app_i.h"
+
+#define TAG "Scope"
+#include <flipper_format/flipper_format_i.h>

+ 26 - 0
scope_app_i.h

@@ -0,0 +1,26 @@
+#pragma once
+
+#include "helpers/scope_types.h"
+#include "scenes/scope_scene.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 <notification/notification_messages.h>
+
+typedef struct ScopeApp ScopeApp;
+
+struct ScopeApp {
+    Gui* gui;
+    ViewDispatcher* view_dispatcher;
+    SceneManager* scene_manager;
+    NotificationApp* notifications;
+    VariableItemList* variable_item_list;
+    Submenu* submenu;
+    Widget* widget;
+    double time ;
+};
+