flipperzero-firmware_official_dev 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. commit 2a6a3a1bf7ba1ecb42b8cbfc1b1856a54f2878b7
  2. Author: Skorpionm <85568270+Skorpionm@users.noreply.github.com>
  3. Date: Wed Nov 30 15:41:23 2022 +0400
  4. [FL-2955], [FL-2953] SubGhz: fix RAW "Send never ends" (#1979)
  5. * SubGhz: fix RAW "Send never ends"
  6. * SubGhz: delete comments
  7. * SubGhz: RAW file parsing speed increase
  8. * SubGhz: fix level_duration_is_wait
  9. * SubGhz: modification furi_hal_subghz_async_tx_refill
  10. * SubGhz: furi_hal_subghz_stop_async_rx
  11. * SubGhz: hal unit test and better async tx yield handling
  12. * FuriHal: proper async tx end in subghz, vibro on power off
  13. * FuriHal: variable naming in subghz
  14. * SubGhz,FuriHal: extreme timings in subghz hal unit tests, remove memset in async tx buffer fill routine
  15. * FuriHal: small refinements in subghz
  16. Co-authored-by: あく <alleteam@gmail.com>
  17. diff --git a/applications/debug/unit_tests/subghz/subghz_test.c b/applications/debug/unit_tests/subghz/subghz_test.c
  18. index 3052448b7..fe834c606 100644
  19. --- a/applications/debug/unit_tests/subghz/subghz_test.c
  20. +++ b/applications/debug/unit_tests/subghz/subghz_test.c
  21. @@ -209,6 +209,149 @@ MU_TEST(subghz_keystore_test) {
  22. "Test keystore error");
  23. }
  24. +typedef enum {
  25. + SubGhzHalAsyncTxTestTypeNormal,
  26. + SubGhzHalAsyncTxTestTypeInvalidStart,
  27. + SubGhzHalAsyncTxTestTypeInvalidMid,
  28. + SubGhzHalAsyncTxTestTypeInvalidEnd,
  29. + SubGhzHalAsyncTxTestTypeResetStart,
  30. + SubGhzHalAsyncTxTestTypeResetMid,
  31. + SubGhzHalAsyncTxTestTypeResetEnd,
  32. +} SubGhzHalAsyncTxTestType;
  33. +
  34. +typedef struct {
  35. + SubGhzHalAsyncTxTestType type;
  36. + size_t pos;
  37. +} SubGhzHalAsyncTxTest;
  38. +
  39. +#define SUBGHZ_HAL_TEST_DURATION 1
  40. +
  41. +static LevelDuration subghz_hal_async_tx_test_yield(void* context) {
  42. + SubGhzHalAsyncTxTest* test = context;
  43. + bool is_odd = test->pos % 2;
  44. +
  45. + if(test->type == SubGhzHalAsyncTxTestTypeNormal) {
  46. + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  47. + test->pos++;
  48. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  49. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  50. + test->pos++;
  51. + return level_duration_reset();
  52. + } else {
  53. + furi_crash("Yield after reset");
  54. + }
  55. + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidStart) {
  56. + if(test->pos == 0) {
  57. + test->pos++;
  58. + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
  59. + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  60. + test->pos++;
  61. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  62. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  63. + test->pos++;
  64. + return level_duration_reset();
  65. + } else {
  66. + furi_crash("Yield after reset");
  67. + }
  68. + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidMid) {
  69. + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
  70. + test->pos++;
  71. + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
  72. + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  73. + test->pos++;
  74. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  75. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  76. + test->pos++;
  77. + return level_duration_reset();
  78. + } else {
  79. + furi_crash("Yield after reset");
  80. + }
  81. + } else if(test->type == SubGhzHalAsyncTxTestTypeInvalidEnd) {
  82. + if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
  83. + test->pos++;
  84. + return level_duration_make(!is_odd, SUBGHZ_HAL_TEST_DURATION);
  85. + } else if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  86. + test->pos++;
  87. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  88. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * 8) {
  89. + test->pos++;
  90. + return level_duration_reset();
  91. + } else {
  92. + furi_crash("Yield after reset");
  93. + }
  94. + } else if(test->type == SubGhzHalAsyncTxTestTypeResetStart) {
  95. + if(test->pos == 0) {
  96. + test->pos++;
  97. + return level_duration_reset();
  98. + } else {
  99. + furi_crash("Yield after reset");
  100. + }
  101. + } else if(test->type == SubGhzHalAsyncTxTestTypeResetMid) {
  102. + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
  103. + test->pos++;
  104. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  105. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF / 2) {
  106. + test->pos++;
  107. + return level_duration_reset();
  108. + } else {
  109. + furi_crash("Yield after reset");
  110. + }
  111. + } else if(test->type == SubGhzHalAsyncTxTestTypeResetEnd) {
  112. + if(test->pos < API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
  113. + test->pos++;
  114. + return level_duration_make(is_odd, SUBGHZ_HAL_TEST_DURATION);
  115. + } else if(test->pos == API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL - 1) {
  116. + test->pos++;
  117. + return level_duration_reset();
  118. + } else {
  119. + furi_crash("Yield after reset");
  120. + }
  121. + } else {
  122. + furi_crash("Programming error");
  123. + }
  124. +}
  125. +
  126. +bool subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestType type) {
  127. + SubGhzHalAsyncTxTest test = {0};
  128. + test.type = type;
  129. + furi_hal_subghz_reset();
  130. + furi_hal_subghz_load_preset(FuriHalSubGhzPresetOok650Async);
  131. + furi_hal_subghz_set_frequency_and_path(433920000);
  132. +
  133. + furi_hal_subghz_start_async_tx(subghz_hal_async_tx_test_yield, &test);
  134. + while(!furi_hal_subghz_is_async_tx_complete()) {
  135. + furi_delay_ms(10);
  136. + }
  137. + furi_hal_subghz_stop_async_tx();
  138. + furi_hal_subghz_sleep();
  139. +
  140. + return true;
  141. +}
  142. +
  143. +MU_TEST(subghz_hal_async_tx_test) {
  144. + mu_assert(
  145. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeNormal),
  146. + "Test furi_hal_async_tx normal");
  147. + mu_assert(
  148. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidStart),
  149. + "Test furi_hal_async_tx invalid start");
  150. + mu_assert(
  151. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidMid),
  152. + "Test furi_hal_async_tx invalid mid");
  153. + mu_assert(
  154. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeInvalidEnd),
  155. + "Test furi_hal_async_tx invalid end");
  156. + mu_assert(
  157. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetStart),
  158. + "Test furi_hal_async_tx reset start");
  159. + mu_assert(
  160. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetMid),
  161. + "Test furi_hal_async_tx reset mid");
  162. + mu_assert(
  163. + subghz_hal_async_tx_test_run(SubGhzHalAsyncTxTestTypeResetEnd),
  164. + "Test furi_hal_async_tx reset end");
  165. +}
  166. +
  167. //test decoders
  168. MU_TEST(subghz_decoder_came_atomo_test) {
  169. mu_assert(
  170. @@ -579,6 +722,8 @@ MU_TEST_SUITE(subghz) {
  171. subghz_test_init();
  172. MU_RUN_TEST(subghz_keystore_test);
  173. + MU_RUN_TEST(subghz_hal_async_tx_test);
  174. +
  175. MU_RUN_TEST(subghz_decoder_came_atomo_test);
  176. MU_RUN_TEST(subghz_decoder_came_test);
  177. MU_RUN_TEST(subghz_decoder_came_twee_test);
  178. diff --git a/firmware/targets/f7/furi_hal/furi_hal_power.c b/firmware/targets/f7/furi_hal/furi_hal_power.c
  179. index 88e191e9e..ddb056906 100644
  180. --- a/firmware/targets/f7/furi_hal/furi_hal_power.c
  181. +++ b/firmware/targets/f7/furi_hal/furi_hal_power.c
  182. @@ -1,6 +1,7 @@
  183. #include <furi_hal_power.h>
  184. #include <furi_hal_clock.h>
  185. #include <furi_hal_bt.h>
  186. +#include <furi_hal_vibro.h>
  187. #include <furi_hal_resources.h>
  188. #include <furi_hal_uart.h>
  189. @@ -308,11 +309,13 @@ void furi_hal_power_shutdown() {
  190. void furi_hal_power_off() {
  191. // Crutch: shutting down with ext 3V3 off is causing LSE to stop
  192. furi_hal_power_enable_external_3_3v();
  193. - furi_delay_us(1000);
  194. + furi_hal_vibro_on(true);
  195. + furi_delay_us(50000);
  196. // Send poweroff to charger
  197. furi_hal_i2c_acquire(&furi_hal_i2c_handle_power);
  198. bq25896_poweroff(&furi_hal_i2c_handle_power);
  199. furi_hal_i2c_release(&furi_hal_i2c_handle_power);
  200. + furi_hal_vibro_on(false);
  201. }
  202. void furi_hal_power_reset() {
  203. diff --git a/firmware/targets/f7/furi_hal/furi_hal_subghz.c b/firmware/targets/f7/furi_hal/furi_hal_subghz.c
  204. index 5eeb4e9a5..726b2d7fa 100644
  205. --- a/firmware/targets/f7/furi_hal/furi_hal_subghz.c
  206. +++ b/firmware/targets/f7/furi_hal/furi_hal_subghz.c
  207. @@ -488,13 +488,9 @@ void furi_hal_subghz_stop_async_rx() {
  208. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
  209. }
  210. -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256)
  211. -#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
  212. -#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 333
  213. -
  214. typedef struct {
  215. uint32_t* buffer;
  216. - bool flip_flop;
  217. + LevelDuration carry_ld;
  218. FuriHalSubGhzAsyncTxCallback callback;
  219. void* callback_context;
  220. uint64_t duty_high;
  221. @@ -504,37 +500,48 @@ typedef struct {
  222. static FuriHalSubGhzAsyncTx furi_hal_subghz_async_tx = {0};
  223. static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
  224. + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
  225. while(samples > 0) {
  226. bool is_odd = samples % 2;
  227. - LevelDuration ld =
  228. - furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
  229. + LevelDuration ld;
  230. + if(level_duration_is_reset(furi_hal_subghz_async_tx.carry_ld)) {
  231. + ld = furi_hal_subghz_async_tx.callback(furi_hal_subghz_async_tx.callback_context);
  232. + } else {
  233. + ld = furi_hal_subghz_async_tx.carry_ld;
  234. + furi_hal_subghz_async_tx.carry_ld = level_duration_reset();
  235. + }
  236. if(level_duration_is_wait(ld)) {
  237. - return;
  238. + *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  239. + buffer++;
  240. + samples--;
  241. } else if(level_duration_is_reset(ld)) {
  242. - // One more even sample required to end at low level
  243. - if(is_odd) {
  244. - *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  245. - buffer++;
  246. - samples--;
  247. - furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  248. - }
  249. + *buffer = 0;
  250. + buffer++;
  251. + samples--;
  252. + LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_1);
  253. + LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
  254. + LL_TIM_EnableIT_UPDATE(TIM2);
  255. break;
  256. } else {
  257. - // Inject guard time if level is incorrect
  258. bool level = level_duration_get_level(ld);
  259. - if(is_odd == level) {
  260. +
  261. + // Inject guard time if level is incorrect
  262. + if(is_odd != level) {
  263. *buffer = API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  264. buffer++;
  265. samples--;
  266. - if(!level) {
  267. + if(is_odd) {
  268. furi_hal_subghz_async_tx.duty_high += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  269. } else {
  270. furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME;
  271. }
  272. - // This code must be invoked only once: when encoder starts with low level.
  273. - // Otherwise whole thing will crash.
  274. - furi_check(samples > 0);
  275. +
  276. + // Special case: prevent buffer overflow if sample is last
  277. + if(samples == 0) {
  278. + furi_hal_subghz_async_tx.carry_ld = ld;
  279. + break;
  280. + }
  281. }
  282. uint32_t duration = level_duration_get_duration(ld);
  283. @@ -543,22 +550,17 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
  284. buffer++;
  285. samples--;
  286. - if(level) {
  287. + if(is_odd) {
  288. furi_hal_subghz_async_tx.duty_high += duration;
  289. } else {
  290. furi_hal_subghz_async_tx.duty_low += duration;
  291. }
  292. }
  293. }
  294. -
  295. - memset(buffer, 0, samples * sizeof(uint32_t));
  296. }
  297. static void furi_hal_subghz_async_tx_dma_isr() {
  298. - furi_assert(
  299. - furi_hal_subghz.state == SubGhzStateAsyncTx ||
  300. - furi_hal_subghz.state == SubGhzStateAsyncTxEnd ||
  301. - furi_hal_subghz.state == SubGhzStateAsyncTxLast);
  302. + furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
  303. if(LL_DMA_IsActiveFlag_HT1(DMA1)) {
  304. LL_DMA_ClearFlag_HT1(DMA1);
  305. furi_hal_subghz_async_tx_refill(
  306. @@ -578,11 +580,14 @@ static void furi_hal_subghz_async_tx_timer_isr() {
  307. if(LL_TIM_GetAutoReload(TIM2) == 0) {
  308. if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
  309. furi_hal_subghz.state = SubGhzStateAsyncTxLast;
  310. + LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
  311. + } else if(furi_hal_subghz.state == SubGhzStateAsyncTxLast) {
  312. + furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
  313. //forcibly pulls the pin to the ground so that there is no carrier
  314. furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullDown, GpioSpeedLow);
  315. - } else {
  316. - furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
  317. LL_TIM_DisableCounter(TIM2);
  318. + } else {
  319. + furi_crash(NULL);
  320. }
  321. }
  322. }
  323. @@ -605,8 +610,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
  324. furi_hal_subghz_async_tx.buffer =
  325. malloc(API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL * sizeof(uint32_t));
  326. - furi_hal_subghz_async_tx_refill(
  327. - furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
  328. // Connect CC1101_GD0 to TIM2 as output
  329. furi_hal_gpio_init_ex(
  330. @@ -647,14 +650,16 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
  331. TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
  332. TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
  333. TIM_OC_InitStruct.CompareValue = 0;
  334. - TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
  335. + TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_LOW;
  336. LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
  337. LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2);
  338. LL_TIM_DisableMasterSlaveMode(TIM2);
  339. furi_hal_interrupt_set_isr(FuriHalInterruptIdTIM2, furi_hal_subghz_async_tx_timer_isr, NULL);
  340. - LL_TIM_EnableIT_UPDATE(TIM2);
  341. + furi_hal_subghz_async_tx_refill(
  342. + furi_hal_subghz_async_tx.buffer, API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
  343. +
  344. LL_TIM_EnableDMAReq_UPDATE(TIM2);
  345. LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
  346. @@ -673,8 +678,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
  347. &SUBGHZ_DEBUG_CC1101_PIN, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
  348. const GpioPin* gpio = &SUBGHZ_DEBUG_CC1101_PIN;
  349. - subghz_debug_gpio_buff[0] = gpio->pin;
  350. - subghz_debug_gpio_buff[1] = (uint32_t)gpio->pin << GPIO_NUMBER;
  351. + subghz_debug_gpio_buff[0] = (uint32_t)gpio->pin << GPIO_NUMBER;
  352. + subghz_debug_gpio_buff[1] = gpio->pin;
  353. dma_config.MemoryOrM2MDstAddress = (uint32_t)subghz_debug_gpio_buff;
  354. dma_config.PeriphOrM2MSrcAddress = (uint32_t) & (gpio->port->BSRR);
  355. diff --git a/firmware/targets/furi_hal_include/furi_hal_subghz.h b/firmware/targets/furi_hal_include/furi_hal_subghz.h
  356. index d610b01b7..1f99386c0 100644
  357. --- a/firmware/targets/furi_hal_include/furi_hal_subghz.h
  358. +++ b/firmware/targets/furi_hal_include/furi_hal_subghz.h
  359. @@ -14,6 +14,11 @@
  360. extern "C" {
  361. #endif
  362. +/** Low level buffer dimensions and guard times */
  363. +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL (256)
  364. +#define API_HAL_SUBGHZ_ASYNC_TX_BUFFER_HALF (API_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL / 2)
  365. +#define API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME 999
  366. +
  367. /** Radio Presets */
  368. typedef enum {
  369. FuriHalSubGhzPresetIDLE, /**< default configuration */
  370. diff --git a/lib/subghz/subghz_file_encoder_worker.c b/lib/subghz/subghz_file_encoder_worker.c
  371. index f987430d6..abc33188f 100644
  372. --- a/lib/subghz/subghz_file_encoder_worker.c
  373. +++ b/lib/subghz/subghz_file_encoder_worker.c
  374. @@ -18,6 +18,7 @@ struct SubGhzFileEncoderWorker {
  375. volatile bool worker_running;
  376. volatile bool worker_stoping;
  377. bool level;
  378. + bool is_storage_slow;
  379. FuriString* str_data;
  380. FuriString* file_path;
  381. @@ -86,7 +87,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
  382. if(ret == sizeof(int32_t)) {
  383. LevelDuration level_duration = {.level = LEVEL_DURATION_RESET};
  384. if(duration < 0) {
  385. - level_duration = level_duration_make(false, duration * -1);
  386. + level_duration = level_duration_make(false, -duration);
  387. } else if(duration > 0) {
  388. level_duration = level_duration_make(true, duration);
  389. } else if(duration == 0) {
  390. @@ -96,7 +97,7 @@ LevelDuration subghz_file_encoder_worker_get_level_duration(void* context) {
  391. }
  392. return level_duration;
  393. } else {
  394. - FURI_LOG_E(TAG, "Slow flash read");
  395. + instance->is_storage_slow = true;
  396. return level_duration_wait();
  397. }
  398. }
  399. @@ -110,6 +111,7 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
  400. SubGhzFileEncoderWorker* instance = context;
  401. FURI_LOG_I(TAG, "Worker start");
  402. bool res = false;
  403. + instance->is_storage_slow = false;
  404. Stream* stream = flipper_format_get_raw_stream(instance->flipper_format);
  405. do {
  406. if(!flipper_format_file_open_existing(
  407. @@ -139,21 +141,21 @@ static int32_t subghz_file_encoder_worker_thread(void* context) {
  408. furi_string_trim(instance->str_data);
  409. if(!subghz_file_encoder_worker_data_parse(
  410. instance, furi_string_get_cstr(instance->str_data))) {
  411. - //to stop DMA correctly
  412. subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
  413. - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
  414. -
  415. break;
  416. }
  417. } else {
  418. - subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
  419. subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET);
  420. break;
  421. }
  422. + } else {
  423. + furi_delay_ms(1);
  424. }
  425. - furi_delay_ms(5);
  426. }
  427. //waiting for the end of the transfer
  428. + if(instance->is_storage_slow) {
  429. + FURI_LOG_E(TAG, "Storage is slow");
  430. + }
  431. FURI_LOG_I(TAG, "End read file");
  432. while(!furi_hal_subghz_is_async_tx_complete() && instance->worker_running) {
  433. furi_delay_ms(5);