| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456 |
- commit 2a6a3a1bf7ba1ecb42b8cbfc1b1856a54f2878b7
- Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com>
- Date: Wed Nov 30 15:41:23 2022 +0400
- [FL-2955], [FL-2953] SubGhz: fix RAW "Send never ends" (#1979)
-
- * SubGhz: fix RAW "Send never ends"
- * SubGhz: delete comments
- * SubGhz: RAW file parsing speed increase
- * SubGhz: fix level_duration_is_wait
- * SubGhz: modification furi_hal_subghz_async_tx_refill
- * SubGhz: furi_hal_subghz_stop_async_rx
- * SubGhz: hal unit test and better async tx yield handling
- * FuriHal: proper async tx end in subghz, vibro on power off
- * FuriHal: variable naming in subghz
- * SubGhz,FuriHal: extreme timings in subghz hal unit tests, remove memset in async tx buffer fill routine
- * FuriHal: small refinements in subghz
-
- Co-authored-by: あく <alleteam@gmail.com>
- diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
- index 3052448b7..fe834c606 100644
- --- a/applications/debug/unit_tests/subghz/subghz_test.c
- +++ b/applications/debug/unit_tests/subghz/subghz_test.c
- @@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) {
- "Test keystore error");
- }
-
- +typedef enum {
- + SubGhzHalAsyncTxTestTypeNormal,
- + SubGhzHalAsyncTxTestTypeInvalidStart,
- + SubGhzHalAsyncTxTestTypeInvalidMid,
- + SubGhzHalAsyncTxTestTypeInvalidEnd,
- + SubGhzHalAsyncTxTestTypeResetStart,
- + SubGhzHalAsyncTxTestTypeResetMid,
- + SubGhzHalAsyncTxTestTypeResetEnd,
- +} SubGhzHalAsyncTxTestType;
- +
- +typedef struct {
- + SubGhzHalAsyncTxTestType type;
- + size_t pos;
- +} SubGhzHalAsyncTxTest;
- +
- +#define SUBGHZ_HAL_TEST_DURATION 1
- +
- +static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
- + SubGhzHalAsyncTxTest* test = context;
- + bool is_odd = test->pos % 2;
- +
- + if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
- + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {
- + if(test->pos == 0) {
- + test->pos++;
- + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
- + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
- + test->pos++;
- + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
- + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
- + test->pos++;
- + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {
- + if(test->pos == 0) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
- + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
- + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
- + test->pos++;
- + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
- + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
- + test->pos++;
- + return level_duration_reset();
- + } else {
- + furi_crash("Yield after reset");
- + }
- + } else {
- + furi_crash("Programming error");
- + }
- +}
- +
- +bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
- + SubGhzHalAsyncTxTest test = {0};
- + test.type = type;
- + furi_hal_subghz_reset();
- + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
- + furi_hal_subghz_set_frequency_and_path(433920000);
- +
- + furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test);
- + while(!furi_hal_subghz_is_async_tx_complete()) {
- + furi_delay_ms(10);
- + }
- + furi_hal_subghz_stop_async_tx();
- + furi_hal_subghz_sleep();
- +
- + return true;
- +}
- +
- +MU_TEST(subghz_hal_async_tx_test) {
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),
- + "Test furi_hal_async_tx normal");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),
- + "Test furi_hal_async_tx invalid start");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),
- + "Test furi_hal_async_tx invalid mid");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),
- + "Test furi_hal_async_tx invalid end");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),
- + "Test furi_hal_async_tx reset start");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),
- + "Test furi_hal_async_tx reset mid");
- + mu_assert(
- + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),
- + "Test furi_hal_async_tx reset end");
- +}
- +
- //test decoders
- MU_TEST(subghz_decoder_came_atomo_test) {
- mu_assert(
- @@ -579,6 +722,8 @@ MU_TEST_SUITE(subghz) {
- subghz_test_init();
- MU_RUN_TEST(subghz_keystore_test);
-
- + MU_RUN_TEST(subghz_hal_async_tx_test);
- +
- MU_RUN_TEST(subghz_decoder_came_atomo_test);
- MU_RUN_TEST(subghz_decoder_came_test);
- MU_RUN_TEST(subghz_decoder_came_twee_test);
- diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c
- index 88e191e9e..ddb056906 100644
- --- a/firmware/targets/f7/furi_hal/furi_hal_power.c
- +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c
- @@ -1,6 +1,7 @@
- #include <furi_hal_power.h>
- #include <furi_hal_clock.h>
- #include <furi_hal_bt.h>
- +#include <furi_hal_vibro.h>
- #include <furi_hal_resources.h>
- #include <furi_hal_uart.h>
-
- @@ -308,11 +309,13 @@ void furi_hal_power_shutdown() {
- void furi_hal_power_off() {
- // Crutch: shutting down with ext 3V3 off is causing LSE to stop
- furi_hal_power_enable_external_3_3v();
- - furi_delay_us(1000);
- + furi_hal_vibro_on(true);
- + furi_delay_us(50000);
- // Send poweroff to charger
- furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
- bq25896_poweroff(&furi_hal_i2c_handle_power);
- furi_hal_i2c_release(&furi_hal_i2c_handle_power);
- + furi_hal_vibro_on(false);
- }
-
- void furi_hal_power_reset() {
- diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c
- index 5eeb4e9a5..726b2d7fa 100644
- --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c
- +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c
- @@ -488,13 +488,9 @@ void furi_hal_subghz_stop_async_rx() {
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
- }
-
- -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256)
- -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
- -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 333
- -
- typedef struct {
- uint32_t* buffer;
- - bool flip_flop;
- + LevelDuration carry_ld;
- FuriHalSubGhzAsyncTxCallback callback;
- void* callback_context;
- uint64_t duty_high;
- @@ -504,37 +500,48 @@ typedef struct {
- static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0};
-
- static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
- + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
- while(samples > 0) {
- bool is_odd = samples % 2;
- - LevelDuration ld =
- - furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
- + LevelDuration ld;
- + if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) {
- + ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
- + } else {
- + ld = furi_hal_subghz_async_tx.carry_ld;
- + furi_hal_subghz_async_tx.carry_ld = level_duration_reset();
- + }
-
- if(level_duration_is_wait(ld)) {
- - return;
- + *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- + buffer++;
- + samples--;
- } else if(level_duration_is_reset(ld)) {
- - // One more even sample required to end at low level
- - if(is_odd) {
- - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- - buffer++;
- - samples--;
- - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- - }
- + *buffer = 0;
- + buffer++;
- + samples--;
- + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1);
- + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
- + LL_TIM_EnableIT_UPDATE(TIM2);
- break;
- } else {
- - // Inject guard time if level is incorrect
- bool level = level_duration_get_level(ld);
- - if(is_odd == level) {
- +
- + // Inject guard time if level is incorrect
- + if(is_odd != level) {
- *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- buffer++;
- samples--;
- - if(!level) {
- + if(is_odd) {
- furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- } else {
- furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
- }
- - // This code must be invoked only once: when encoder starts with low level.
- - // Otherwise whole thing will crash.
- - furi_check(samples > 0);
- +
- + // Special case: prevent buffer overflow if sample is last
- + if(samples == 0) {
- + furi_hal_subghz_async_tx.carry_ld = ld;
- + break;
- + }
- }
-
- uint32_t duration = level_duration_get_duration(ld);
- @@ -543,22 +550,17 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
- buffer++;
- samples--;
-
- - if(level) {
- + if(is_odd) {
- furi_hal_subghz_async_tx.duty_high += duration;
- } else {
- furi_hal_subghz_async_tx.duty_low += duration;
- }
- }
- }
- -
- - memset(buffer, 0, samples * sizeof(uint32_t));
- }
-
- static void furi_hal_subghz_async_tx_dma_isr() {
- - furi_assert(
- - furi_hal_subghz.state == SubGhzStateAsyncTx ||
- - furi_hal_subghz.state == SubGhzStateAsyncTxEnd ||
- - furi_hal_subghz.state == SubGhzStateAsyncTxLast);
- + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
- if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
- LL_DMA_ClearFlag_HT1(DMA1);
- furi_hal_subghz_async_tx_refill(
- @@ -578,11 +580,14 @@ static void furi_hal_subghz_async_tx_timer_isr() {
- if(LL_TIM_GetAutoReload(TIM2) == 0) {
- if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
- furi_hal_subghz.state = SubGhzStateAsyncTxLast;
- + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
- + } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) {
- + furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
- //forcibly pulls the pin to the ground so that there is no carrier
- furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow);
- - } else {
- - furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
- LL_TIM_DisableCounter(TIM2);
- + } else {
- + furi_crash(NULL);
- }
- }
- }
- @@ -605,8 +610,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
-
- furi_hal_subghz_async_tx.buffer =
- malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t));
- - furi_hal_subghz_async_tx_refill(
- - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
-
- // Connect CC1101_GD0 to TIM2 as output
- furi_hal_gpio_init_ex(
- @@ -647,14 +650,16 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
- TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
- TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
- TIM_OC_InitStruct.CompareValue = 0;
- - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
- + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
- LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
- LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2);
- LL_TIM_DisableMasterSlaveMode(TIM2);
-
- furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL);
-
- - LL_TIM_EnableIT_UPDATE(TIM2);
- + furi_hal_subghz_async_tx_refill(
- + furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
- +
- LL_TIM_EnableDMAReq_UPDATE(TIM2);
- LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
-
- @@ -673,8 +678,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
- &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
-
- const GpioPin* gpio = &SUBGHZ_DEBUG_CC1101_PIN;
- - subghz_debug_gpio_buff[0] = gpio->pin;
- - subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER;
- + subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER;
- + subghz_debug_gpio_buff[1] = gpio->pin;
-
- dma_config.MemoryOrM2MDstAddress = (uint32_t)subghz_debug_gpio_buff;
- dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR);
- diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h
- index d610b01b7..1f99386c0 100644
- --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h
- +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h
- @@ -14,6 +14,11 @@
- extern "C" {
- #endif
-
- +/** Low level buffer dimensions and guard times */
- +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256)
- +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
- +#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999
- +
- /** Radio Presets */
- typedef enum {
- FuriHalSubGhzPresetIDLE, /**< default configuration */
- diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c
- index f987430d6..abc33188f 100644
- --- a/lib/subghz/subghz_file_encoder_worker.c
- +++ b/lib/subghz/subghz_file_encoder_worker.c
- @@ -18,6 +18,7 @@ struct SubGhzFileEncoderWorker {
- volatile bool worker_running;
- volatile bool worker_stoping;
- bool level;
- + bool is_storage_slow;
- FuriString* str_data;
- FuriString* file_path;
-
- @@ -86,7 +87,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
- if(ret == sizeof(int32_t)) {
- LevelDuration level_duration = {.level = LEVEL_DURATION_RESET};
- if(duration < 0) {
- - level_duration = level_duration_make(false, duration * -1);
- + level_duration = level_duration_make(false, -duration);
- } else if(duration > 0) {
- level_duration = level_duration_make(true, duration);
- } else if(duration == 0) {
- @@ -96,7 +97,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
- }
- return level_duration;
- } else {
- - FURI_LOG_E(TAG, "Slow flash read");
- + instance->is_storage_slow = true;
- return level_duration_wait();
- }
- }
- @@ -110,6 +111,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
- SubGhzFileEncoderWorker* instance = context;
- FURI_LOG_I(TAG, "Worker start");
- bool res = false;
- + instance->is_storage_slow = false;
- Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
- do {
- if(!flipper_format_file_open_existing(
- @@ -139,21 +141,21 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
- furi_string_trim(instance->str_data);
- if(!subghz_file_encoder_worker_data_parse(
- instance, furi_string_get_cstr(instance->str_data))) {
- - //to stop DMA correctly
- subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
- - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
- -
- break;
- }
- } else {
- - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
- subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
- break;
- }
- + } else {
- + furi_delay_ms(1);
- }
- - furi_delay_ms(5);
- }
- //waiting for the end of the transfer
- + if(instance->is_storage_slow) {
- + FURI_LOG_E(TAG, "Storage is slow");
- + }
- FURI_LOG_I(TAG, "End read file");
- while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) {
- furi_delay_ms(5);
|