| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
- * See the LICENSE file for information about the license. */
- #include "app.h"
- #include <cc1101_regs.h>
- 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 USEC_PER_PIXEL_SMALL_CHANGE 5
- #define USEC_PER_PIXEL_LARGE_CHANGE 25
- #define USEC_PER_PIXEL_MIN 5
- #define USEC_PER_PIXEL_MAX 300
- typedef struct {
- 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
- bool show_usage_info;
- } DirectSamplingViewPrivData;
- /* Read directly from the G0 CC1101 pin, and draw a black or white
- * dot depending on the level. */
- void render_view_direct_sampling(Canvas *const canvas, ProtoViewApp *app) {
- DirectSamplingViewPrivData *privdata = app->view_privdata;
- if (!app->direct_sampling_enabled && privdata->show_usage_info) {
- canvas_set_font(canvas, FontSecondary);
- 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_draw_str(canvas,5,60,"To start/stop, press OK");
- return;
- }
- privdata->show_usage_info = false;
- /* Draw on screen. */
- int idx = 0;
- for (int y = 0; y < 64; y++) {
- for (int x = 0; x < 128; x++) {
- bool level = bitmap_get(privdata->captured,
- CAPTURED_BITMAP_BYTES,idx++);
- if (level) canvas_draw_dot(canvas,x,y);
- }
- }
- char buf[32];
- snprintf(buf,sizeof(buf),"%lu usec/px", privdata->usec_per_pixel);
- canvas_set_font(canvas, FontSecondary);
- canvas_draw_str_with_border(canvas,1,60,buf,ColorWhite,ColorBlack);
- }
- /* Handle input */
- void process_input_direct_sampling(ProtoViewApp *app, InputEvent input) {
- DirectSamplingViewPrivData *privdata = app->view_privdata;
- if (input.type == InputTypePress && input.key == InputKeyOk) {
- app->direct_sampling_enabled = !app->direct_sampling_enabled;
- }
- if ((input.key == InputKeyUp || input.key == InputKeyDown) &&
- (input.type == InputTypePress || input.type == InputTypeRepeat))
- {
- uint32_t change = input.type == InputTypePress ?
- USEC_PER_PIXEL_SMALL_CHANGE :
- USEC_PER_PIXEL_LARGE_CHANGE;
- if (input.key == InputKeyUp) change = -change;
- privdata->usec_per_pixel += change;
- if (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)
- privdata->usec_per_pixel = USEC_PER_PIXEL_MAX;
- /* Update the timer frequency. */
- direct_sampling_timer_stop(app);
- direct_sampling_timer_start(app);
- }
- }
- /* Enter view. Stop the subghz thread to prevent access as we read
- * the CC1101 data directly. */
- void view_enter_direct_sampling(ProtoViewApp *app) {
- /* Set view defaults. */
- DirectSamplingViewPrivData *privdata = app->view_privdata;
- 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 &&
- !app->txrx->debug_timer_sampling)
- {
- subghz_devices_stop_async_rx(app->radio_device);
- /* To read data asynchronously directly from the view, we need
- * to put the CC1101 back into reception mode (the previous call
- * to stop the async RX will put it into idle) and configure the
- * G0 pin for reading. */
- subghz_devices_set_rx(app->radio_device);
- furi_hal_gpio_init(
- subghz_devices_get_data_gpio(app->radio_device),
- GpioModeInput,
- GpioPullNo,
- GpioSpeedLow);
- } else {
- raw_sampling_worker_stop(app);
- }
- // Start the timer to capture raw data
- direct_sampling_timer_start(app);
- }
- /* Exit view. Restore the subghz thread. */
- void view_exit_direct_sampling(ProtoViewApp *app) {
- DirectSamplingViewPrivData *privdata = app->view_privdata;
- if (privdata->captured) free(privdata->captured);
- app->direct_sampling_enabled = false;
- direct_sampling_timer_stop(app);
- /* Restart normal data feeding. */
- if (app->txrx->txrx_state == TxRxStateRx &&
- !app->txrx->debug_timer_sampling)
- {
- subghz_devices_start_async_rx(app->radio_device, protoview_rx_callback, NULL);
- } else {
- furi_hal_gpio_init(
- subghz_devices_get_data_gpio(app->radio_device),
- GpioModeInput,
- GpioPullNo,
- GpioSpeedLow);
- 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(subghz_devices_get_data_gpio(app->radio_device));
- 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;
- furi_hal_bus_enable(FuriHalBusTIM2);
- 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);
- furi_hal_bus_disable(FuriHalBusTIM2);
- FURI_CRITICAL_EXIT();
- }
|