antirez 3 лет назад
Родитель
Сommit
362ef7c58b
3 измененных файлов с 105 добавлено и 2 удалено
  1. 2 0
      app.h
  2. 34 0
      app_subghz.c
  3. 69 2
      view_info.c

+ 2 - 0
app.h

@@ -36,6 +36,7 @@ typedef struct ProtoViewApp ProtoViewApp;
 typedef enum {
     TxRxStateIDLE,
     TxRxStateRx,
+    TxRxStateTx,
     TxRxStateSleep,
 } TxRxState;
 
@@ -185,6 +186,7 @@ void radio_rx_end(ProtoViewApp* app);
 void radio_sleep(ProtoViewApp* app);
 void raw_sampling_worker_start(ProtoViewApp *app);
 void raw_sampling_worker_stop(ProtoViewApp *app);
+void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx);
 
 /* signal.c */
 uint32_t duration_delta(uint32_t a, uint32_t b);

+ 34 - 0
app_subghz.c

@@ -48,6 +48,8 @@ void radio_begin(ProtoViewApp* app) {
     app->txrx->txrx_state = TxRxStateIDLE;
 }
 
+/* ================================= Reception ============================== */
+
 /* Setup subghz to start receiving using a background worker. */
 uint32_t radio_rx(ProtoViewApp* app) {
     furi_assert(app);
@@ -78,6 +80,7 @@ uint32_t radio_rx(ProtoViewApp* app) {
 /* Stop subghz worker (if active), put radio on idle state. */
 void radio_rx_end(ProtoViewApp* app) {
     furi_assert(app);
+
     if (app->txrx->txrx_state == TxRxStateRx) {
         if (!app->txrx->debug_timer_sampling) {
             if(subghz_worker_is_running(app->txrx->worker)) {
@@ -102,6 +105,37 @@ void radio_sleep(ProtoViewApp* app) {
     }
     furi_hal_subghz_sleep();
     app->txrx->txrx_state = TxRxStateSleep;
+
+}
+
+/* =============================== Transmission ============================= */
+
+/* This function suspends the current RX state, switches to TX mode,
+ * transmits the signal provided by the callback data_feeder, and later
+ * restores the RX state if there was one. */
+void radio_tx_signal(ProtoViewApp *app, FuriHalSubGhzAsyncTxCallback data_feeder, void *ctx) {
+    TxRxState oldstate = app->txrx->txrx_state;
+
+    if (oldstate == TxRxStateRx) radio_rx_end(app);
+//    furi_hal_power_suppress_charge_enter();
+
+    radio_begin(app);
+
+    furi_hal_subghz_idle();
+    uint32_t value = furi_hal_subghz_set_frequency_and_path(app->frequency);
+    FURI_LOG_E(TAG, "Switched to frequency: %lu", value);
+    furi_hal_gpio_write(&gpio_cc1101_g0, false);
+    furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
+
+    furi_hal_subghz_start_async_tx(data_feeder, ctx);
+    while(!furi_hal_subghz_is_async_tx_complete()) furi_delay_ms(10);
+    furi_hal_subghz_stop_async_tx();
+    furi_hal_subghz_idle();
+
+//    furi_hal_power_suppress_charge_exit();
+
+    radio_begin(app);
+    if (oldstate == TxRxStateRx) radio_rx(app);
 }
 
 /* ============================= Raw sampling mode =============================

+ 69 - 2
view_info.c

@@ -11,6 +11,17 @@ enum {
     SubViewInfoLast, /* Just a sentinel. */
 };
 
+/* This is the context we pass to the data yield callback for
+ * asynchronous tx. */
+#define SENDSIGNAL_CURPOS_START_GAP UINT32_MAX-1
+#define SENDSIGNAL_CURPOS_END_GAP UINT32_MAX-2
+typedef struct {
+    uint32_t curpos;        // Current bit position of data to send
+    ProtoViewApp *app;      // App reference.
+    uint32_t start_gap_dur;
+    uint32_t end_gap_dur;
+} SendSignalState;
+
 /* Our view private data. */
 #define SAVE_FILENAME_LEN 64
 typedef struct {
@@ -83,7 +94,7 @@ static void render_subview_save(Canvas *const canvas, ProtoViewApp *app) {
     }
 
     canvas_set_font(canvas, FontSecondary);
-    canvas_draw_str(canvas, 0, 6, "ok: save, < >: slide rows");
+    canvas_draw_str(canvas, 0, 6, "ok: send, long ok: save");
 }
 
 /* Render the selected subview of this view. */
@@ -136,6 +147,54 @@ void set_signal_random_filename(ProtoViewApp *app, char *buf, size_t buflen) {
     str_replace(buf,'/','_');
 }
 
+/* Send signal data feeder callback. When the asynchronous transmission is
+ * active, this function is called to return new samples as LevelDuration
+ * types (that is a structure with level, that is pulse or gap, and
+ * duration in microseconds). The position into the transmission is stored
+ * in the context 'ctx':
+ *
+ * In the SendSignalState structure 'ss' we remember at which bit of the
+ * message we are in ss->curoff, however this offset has two special
+ * values to indicate we need to send the initial and final gap.
+ */
+LevelDuration radio_tx_feed_data(void *ctx) {
+    SendSignalState *ss = ctx;
+    uint32_t dur = 0, j;
+    uint32_t level = 0;
+
+    if (ss->start_gap_dur) {
+        LevelDuration ld = level_duration_make(0,ss->start_gap_dur);
+        ss->start_gap_dur = 0;
+        ss->curpos = 0;
+        return ld;
+    }
+
+    if (ss->curpos >= ss->app->msg_info->pulses_count) {
+        if (ss->end_gap_dur) {
+            LevelDuration ld = level_duration_make(0,ss->end_gap_dur);
+            ss->end_gap_dur = 0;
+            return ld;
+        } else {
+            return level_duration_reset();
+        }
+    }
+
+    for (j = 0; ss->curpos+j < ss->app->msg_info->pulses_count; j++) {
+        uint32_t l = bitmap_get(ss->app->msg_info->bits,
+                            ss->app->msg_info->bits_bytes,
+                            ss->curpos+j);
+        if (j == 0) {
+            level = l;
+            dur += ss->app->msg_info->short_pulse_dur;
+            continue;
+        }
+        if (l != level) break;
+        dur += ss->app->msg_info->short_pulse_dur;
+    }
+    ss->curpos += j;
+    return level_duration_make(level, dur);
+}
+
 /* Handle input for the info view. */
 void process_input_info(ProtoViewApp *app, InputEvent input) {
     if (process_subview_updown(app,input,SubViewInfoLast)) return;
@@ -155,11 +214,19 @@ void process_input_info(ProtoViewApp *app, InputEvent input) {
         } else if (input.type == InputTypePress && input.key == InputKeyLeft) {
             if (privdata->signal_display_start_row != 0)
                 privdata->signal_display_start_row--;
-        } else if (input.type == InputTypePress && input.key == InputKeyOk) {
+        } else if (input.type == InputTypeLong && input.key == InputKeyOk)
+        {
             privdata->filename = malloc(SAVE_FILENAME_LEN);
             set_signal_random_filename(app,privdata->filename,SAVE_FILENAME_LEN);
             show_keyboard(app, privdata->filename, SAVE_FILENAME_LEN,
                           text_input_done_callback);
+        } else if (input.type == InputTypeShort && input.key == InputKeyOk) {
+            SendSignalState send_state;
+            send_state.curpos = SENDSIGNAL_CURPOS_START_GAP;
+            send_state.app = app;
+            send_state.start_gap_dur = 10000;
+            send_state.end_gap_dur = 10000;
+            radio_tx_signal(app,radio_tx_feed_data,&send_state);
         }
     }
 }