|
|
@@ -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);
|