|
@@ -2,10 +2,13 @@
|
|
|
* See the LICENSE file for information about the license. */
|
|
* See the LICENSE file for information about the license. */
|
|
|
|
|
|
|
|
#include "app.h"
|
|
#include "app.h"
|
|
|
-
|
|
|
|
|
#include <cc1101.h>
|
|
#include <cc1101.h>
|
|
|
|
|
|
|
|
-#define CAPTURED_BITMAP_SIZE 128*64/8
|
|
|
|
|
|
|
+static void direct_sampling_timer_start(ProtoViewApp *app);
|
|
|
|
|
+static void direct_sampling_timer_stop(ProtoViewApp *app);
|
|
|
|
|
+
|
|
|
|
|
+#define CAPTURED_BITMAP_BITS (128*64)
|
|
|
|
|
+#define CAPTURED_BITMAP_BYTES (CAPTURED_BITMAP_BITS/8)
|
|
|
#define DEFAULT_USEC_PER_PIXEL 50
|
|
#define DEFAULT_USEC_PER_PIXEL 50
|
|
|
#define USEC_PER_PIXEL_SMALL_CHANGE 5
|
|
#define USEC_PER_PIXEL_SMALL_CHANGE 5
|
|
|
#define USEC_PER_PIXEL_LARGE_CHANGE 25
|
|
#define USEC_PER_PIXEL_LARGE_CHANGE 25
|
|
@@ -13,7 +16,9 @@
|
|
|
#define USEC_PER_PIXEL_MAX 300
|
|
#define USEC_PER_PIXEL_MAX 300
|
|
|
typedef struct {
|
|
typedef struct {
|
|
|
uint8_t *captured; // Bitmap with the last captured screen.
|
|
uint8_t *captured; // Bitmap with the last captured screen.
|
|
|
|
|
+ uint32_t captured_idx; // Current index to write into the bitmap
|
|
|
uint32_t usec_per_pixel; // Number of useconds a pixel should represent
|
|
uint32_t usec_per_pixel; // Number of useconds a pixel should represent
|
|
|
|
|
+ bool show_usage_info;
|
|
|
} DirectSamplingViewPrivData;
|
|
} DirectSamplingViewPrivData;
|
|
|
|
|
|
|
|
/* Read directly from the G0 CC1101 pin, and draw a black or white
|
|
/* Read directly from the G0 CC1101 pin, and draw a black or white
|
|
@@ -21,41 +26,25 @@ typedef struct {
|
|
|
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
|
|
void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
|
|
|
DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
|
|
|
|
|
|
- if (!app->direct_sampling_enabled && privdata->captured == NULL) {
|
|
|
|
|
|
|
+ if (!app->direct_sampling_enabled && privdata->show_usage_info) {
|
|
|
canvas_set_font(canvas, FontSecondary);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
- canvas_draw_str(canvas,2,9,"Direct sampling is a special");
|
|
|
|
|
- canvas_draw_str(canvas,2,18,"mode that displays the signal");
|
|
|
|
|
- canvas_draw_str(canvas,2,27,"captured in real time. Like in");
|
|
|
|
|
- canvas_draw_str(canvas,2,36,"a old CRT TV. It's very slow.");
|
|
|
|
|
- canvas_draw_str(canvas,2,45,"Can crash your Flipper.");
|
|
|
|
|
|
|
+ canvas_draw_str(canvas,2,9, "Direct sampling displays the");
|
|
|
|
|
+ canvas_draw_str(canvas,2,18,"the captured signal in real");
|
|
|
|
|
+ canvas_draw_str(canvas,2,27,"time, like in a CRT TV set.");
|
|
|
|
|
+ canvas_draw_str(canvas,2,36,"Use UP/DOWN to change the");
|
|
|
|
|
+ canvas_draw_str(canvas,2,45,"resolution (usec/pixel).");
|
|
|
canvas_set_font(canvas, FontPrimary);
|
|
canvas_set_font(canvas, FontPrimary);
|
|
|
- canvas_draw_str(canvas,14,60,"To enable press OK");
|
|
|
|
|
|
|
+ canvas_draw_str(canvas,5,60,"To start/stop, press OK");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- /* Allocate the bitmap only the first time. */
|
|
|
|
|
- if (privdata->captured == NULL)
|
|
|
|
|
- privdata->captured = malloc(CAPTURED_BITMAP_SIZE);
|
|
|
|
|
-
|
|
|
|
|
- /* Read from data from GPIO */
|
|
|
|
|
- if (app->direct_sampling_enabled) {
|
|
|
|
|
- for (int j = 0; j < CAPTURED_BITMAP_SIZE*8; j++) {
|
|
|
|
|
- uint32_t start_time = DWT->CYCCNT;
|
|
|
|
|
- bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
|
|
|
|
- bitmap_set(privdata->captured,CAPTURED_BITMAP_SIZE,j,level);
|
|
|
|
|
- uint32_t period =
|
|
|
|
|
- furi_hal_cortex_instructions_per_microsecond() *
|
|
|
|
|
- privdata->usec_per_pixel;
|
|
|
|
|
- while(DWT->CYCCNT - start_time < period);
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ privdata->show_usage_info = false;
|
|
|
|
|
|
|
|
/* Draw on screen. */
|
|
/* Draw on screen. */
|
|
|
int idx = 0;
|
|
int idx = 0;
|
|
|
for (int y = 0; y < 64; y++) {
|
|
for (int y = 0; y < 64; y++) {
|
|
|
for (int x = 0; x < 128; x++) {
|
|
for (int x = 0; x < 128; x++) {
|
|
|
bool level = bitmap_get(privdata->captured,
|
|
bool level = bitmap_get(privdata->captured,
|
|
|
- CAPTURED_BITMAP_SIZE,idx++);
|
|
|
|
|
|
|
+ CAPTURED_BITMAP_BYTES,idx++);
|
|
|
if (level) canvas_draw_dot(canvas,x,y);
|
|
if (level) canvas_draw_dot(canvas,x,y);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
@@ -63,7 +52,7 @@ void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
|
|
|
char buf[32];
|
|
char buf[32];
|
|
|
snprintf(buf,sizeof(buf),"%lu usec/px", privdata->usec_per_pixel);
|
|
snprintf(buf,sizeof(buf),"%lu usec/px", privdata->usec_per_pixel);
|
|
|
canvas_set_font(canvas, FontSecondary);
|
|
canvas_set_font(canvas, FontSecondary);
|
|
|
- canvas_draw_str_with_border(canvas,0,60,buf,ColorWhite,ColorBlack);
|
|
|
|
|
|
|
+ canvas_draw_str_with_border(canvas,1,60,buf,ColorWhite,ColorBlack);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Handle input */
|
|
/* Handle input */
|
|
@@ -86,6 +75,9 @@ void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) {
|
|
|
privdata->usec_per_pixel = USEC_PER_PIXEL_MIN;
|
|
privdata->usec_per_pixel = USEC_PER_PIXEL_MIN;
|
|
|
else if (privdata->usec_per_pixel > USEC_PER_PIXEL_MAX)
|
|
else if (privdata->usec_per_pixel > USEC_PER_PIXEL_MAX)
|
|
|
privdata->usec_per_pixel = USEC_PER_PIXEL_MAX;
|
|
privdata->usec_per_pixel = USEC_PER_PIXEL_MAX;
|
|
|
|
|
+ /* Update the timer frequency. */
|
|
|
|
|
+ direct_sampling_timer_stop(app);
|
|
|
|
|
+ direct_sampling_timer_start(app);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -95,6 +87,8 @@ void view_enter_direct_sampling(ProtoViewApp *app) {
|
|
|
/* Set view defaults. */
|
|
/* Set view defaults. */
|
|
|
DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
|
privdata->usec_per_pixel = DEFAULT_USEC_PER_PIXEL;
|
|
privdata->usec_per_pixel = DEFAULT_USEC_PER_PIXEL;
|
|
|
|
|
+ privdata->captured = malloc(CAPTURED_BITMAP_BYTES);
|
|
|
|
|
+ privdata->show_usage_info = true;
|
|
|
|
|
|
|
|
if (app->txrx->txrx_state == TxRxStateRx &&
|
|
if (app->txrx->txrx_state == TxRxStateRx &&
|
|
|
!app->txrx->debug_timer_sampling)
|
|
!app->txrx->debug_timer_sampling)
|
|
@@ -111,6 +105,9 @@ void view_enter_direct_sampling(ProtoViewApp *app) {
|
|
|
} else {
|
|
} else {
|
|
|
raw_sampling_worker_stop(app);
|
|
raw_sampling_worker_stop(app);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Start the timer to capture raw data
|
|
|
|
|
+ direct_sampling_timer_start(app);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Exit view. Restore the subghz thread. */
|
|
/* Exit view. Restore the subghz thread. */
|
|
@@ -119,6 +116,8 @@ void view_exit_direct_sampling(ProtoViewApp *app) {
|
|
|
if (privdata->captured) free(privdata->captured);
|
|
if (privdata->captured) free(privdata->captured);
|
|
|
app->direct_sampling_enabled = false;
|
|
app->direct_sampling_enabled = false;
|
|
|
|
|
|
|
|
|
|
+ direct_sampling_timer_stop(app);
|
|
|
|
|
+
|
|
|
/* Restart normal data feeding. */
|
|
/* Restart normal data feeding. */
|
|
|
if (app->txrx->txrx_state == TxRxStateRx &&
|
|
if (app->txrx->txrx_state == TxRxStateRx &&
|
|
|
!app->txrx->debug_timer_sampling)
|
|
!app->txrx->debug_timer_sampling)
|
|
@@ -128,3 +127,47 @@ void view_exit_direct_sampling(ProtoViewApp *app) {
|
|
|
raw_sampling_worker_start(app);
|
|
raw_sampling_worker_start(app);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+/* =========================== Timer implementation ========================= */
|
|
|
|
|
+
|
|
|
|
|
+static void ds_timer_isr(void *ctx) {
|
|
|
|
|
+ ProtoViewApp *app = ctx;
|
|
|
|
|
+ DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
|
|
|
+
|
|
|
|
|
+ if (app->direct_sampling_enabled) {
|
|
|
|
|
+ bool level = furi_hal_gpio_read(&gpio_cc1101_g0);
|
|
|
|
|
+ bitmap_set(privdata->captured,CAPTURED_BITMAP_BYTES,
|
|
|
|
|
+ privdata->captured_idx,level);
|
|
|
|
|
+ privdata->captured_idx = (privdata->captured_idx+1) %
|
|
|
|
|
+ CAPTURED_BITMAP_BITS;
|
|
|
|
|
+ }
|
|
|
|
|
+ LL_TIM_ClearFlag_UPDATE(TIM2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void direct_sampling_timer_start(ProtoViewApp *app) {
|
|
|
|
|
+ DirectSamplingViewPrivData *privdata = app->view_privdata;
|
|
|
|
|
+
|
|
|
|
|
+ LL_TIM_InitTypeDef tim_init = {
|
|
|
|
|
+ .Prescaler = 63, /* CPU frequency is ~64Mhz. */
|
|
|
|
|
+ .CounterMode = LL_TIM_COUNTERMODE_UP,
|
|
|
|
|
+ .Autoreload = privdata->usec_per_pixel
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ LL_TIM_Init(TIM2, &tim_init);
|
|
|
|
|
+ LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
|
|
|
|
|
+ LL_TIM_DisableCounter(TIM2);
|
|
|
|
|
+ LL_TIM_SetCounter(TIM2, 0);
|
|
|
|
|
+ furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, ds_timer_isr, app);
|
|
|
|
|
+ LL_TIM_EnableIT_UPDATE(TIM2);
|
|
|
|
|
+ LL_TIM_EnableCounter(TIM2);
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+static void direct_sampling_timer_stop(ProtoViewApp *app) {
|
|
|
|
|
+ UNUSED(app);
|
|
|
|
|
+ FURI_CRITICAL_ENTER();
|
|
|
|
|
+ LL_TIM_DisableCounter(TIM2);
|
|
|
|
|
+ LL_TIM_DisableIT_UPDATE(TIM2);
|
|
|
|
|
+ furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, NULL, NULL);
|
|
|
|
|
+ LL_TIM_DeInit(TIM2);
|
|
|
|
|
+ FURI_CRITICAL_EXIT();
|
|
|
|
|
+}
|