[FL-1496] SubGhz: Library, Cli, Application (#543)

* ApiHal: set frequency and path in one go. Drivers: proper frequency registers calculation for CC1101. Update subghz cli to match new api.
* SubGhz: preparation for parsers porting, tim2 sharing
* ApiHal: add interrupts API, move all TIM2 related things there.
* SubGhz: refactor protocol lib and add keeloq.
* SubGhz: proper init_set for keeloq manafacture key
* SubGhz: port more protocols to lib
* SubGhz: load keeloq manufacture keys from sd card (if any).
* SubGhz: format output from protocols.
* SubGhz: use default frequency in subghz_rx cli command.
* SubGhz: keeloq key types
* Fix compillation error when internal storage disabled
* SubGhz: minor cleanup
* SubGhz: properly handle timeout and reset signal in subghz_rx
* SubGhz: Worker, Capture View. Furi: emulate thread join.
* SubGhz: free strings on keeloq key load end
* SubGhz: update protocols reporting API, app refactoring and capture view, update API HAL usage.
* SubGhz: update dump formatting
* ApiHal: backport subghz preset to F5
* ApiHal: backport subghz frequency range to F5
This commit is contained in:
あく
2021-06-30 00:19:20 +03:00
committed by GitHub
parent dce3665f63
commit e8211226f3
46 changed files with 2114 additions and 265 deletions

View File

@@ -0,0 +1,42 @@
#include "api-hal-interrupt.h"
#include <furi.h>
#include <main.h>
#include <stm32wbxx_ll_tim.h>
volatile ApiHalInterruptISR api_hal_tim_tim2_isr = NULL;
void TIM2_IRQHandler(void) {
if (api_hal_tim_tim2_isr) {
api_hal_tim_tim2_isr();
} else {
HAL_TIM_IRQHandler(&htim2);
}
}
void api_hal_interrupt_set_timer_isr(TIM_TypeDef *timer, ApiHalInterruptISR isr) {
if (timer == TIM2) {
if (isr) {
furi_assert(api_hal_tim_tim2_isr == NULL);
} else {
furi_assert(api_hal_tim_tim2_isr != NULL);
}
api_hal_tim_tim2_isr = isr;
} else {
furi_check(0);
}
}
extern void api_interrupt_call(InterruptType type, void* hw);
/* ST HAL symbols */
/* Comparator trigger event */
void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {
api_interrupt_call(InterruptTypeComparatorTrigger, hcomp);
}
/* Timer update event */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {
api_interrupt_call(InterruptTypeTimerUpdate, htim);
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include <stm32wbxx_ll_tim.h>
#ifdef __cplusplus
extern "C" {
#endif
/** Timer ISR */
typedef void (*ApiHalInterruptISR)();
/** Set Timer ISR
* By default ISR is serviced by ST HAL. Use this function to override it.
* We don't clear interrupt flags for you, do it by your self.
* @timer - timer instance
* @isr - your interrupt service routine or use NULL to clear
*/
void api_hal_interrupt_set_timer_isr(TIM_TypeDef *timer, ApiHalInterruptISR isr);
#ifdef __cplusplus
}
#endif

View File

@@ -1,21 +1,25 @@
#include "cmsis_os.h"
#include "api-hal-tim_i.h"
#include "api-hal-interrupt.h"
#include "api-hal-irda.h"
#include <stm32wbxx_ll_tim.h>
#include <stm32wbxx_ll_gpio.h>
#include <stdio.h>
#include <furi.h>
#include "main.h"
#include "api-hal-pwm.h"
static struct{
TimerISRCallback callback;
void *ctx;
} timer_irda;
typedef enum{
TimerIRQSourceCCI1,
TimerIRQSourceCCI2,
} TimerIRQSource;
void api_hal_irda_tim_isr(TimerIRQSource source)
static void api_hal_irda_handle_capture(TimerIRQSource source)
{
uint32_t duration = 0;
bool level = 0;
@@ -39,6 +43,33 @@ void api_hal_irda_tim_isr(TimerIRQSource source)
timer_irda.callback(timer_irda.ctx, level, duration);
}
static void api_hal_irda_isr() {
if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) {
LL_TIM_ClearFlag_CC1(TIM2);
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
// input capture
api_hal_irda_handle_capture(TimerIRQSourceCCI1);
} else {
// output compare
// HAL_TIM_OC_DelayElapsedCallback(htim);
// HAL_TIM_PWM_PulseFinishedCallback(htim);
}
}
if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) {
LL_TIM_ClearFlag_CC2(TIM2);
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
// input capture
api_hal_irda_handle_capture(TimerIRQSourceCCI2);
} else {
// output compare
// HAL_TIM_OC_DelayElapsedCallback(htim);
// HAL_TIM_PWM_PulseFinishedCallback(htim);
}
}
}
void api_hal_irda_rx_irq_init(void)
{
LL_TIM_InitTypeDef TIM_InitStruct = {0};
@@ -86,15 +117,14 @@ void api_hal_irda_rx_irq_init(void)
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
api_hal_interrupt_set_timer_isr(TIM2, api_hal_irda_isr);
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
NVIC_EnableIRQ(TIM2_IRQn);
}
void api_hal_irda_rx_irq_deinit(void) {
LL_TIM_DisableIT_CC1(TIM2);
LL_TIM_DisableIT_CC2(TIM2);
LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
LL_TIM_DeInit(TIM2);
api_hal_interrupt_set_timer_isr(TIM2, NULL);
}
bool api_hal_irda_rx_irq_is_busy(void) {
@@ -115,4 +145,3 @@ void api_hal_irda_pwm_set(float value, float freq) {
void api_hal_irda_pwm_stop() {
hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH);
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include "api-hal-tim_i.h"
/**
* Function to handle IRDA timer ISR.
*
* @param source - reason for interrupt request.
*/
void api_hal_irda_tim_isr(TimerIRQSource source);

View File

@@ -2,7 +2,9 @@
#include <api-hal-gpio.h>
#include <api-hal-spi.h>
#include <api-hal-interrupt.h>
#include <api-hal-resources.h>
#include <furi.h>
#include <cc1101.h>
#include <stdio.h>
@@ -10,12 +12,13 @@
static const uint8_t api_hal_subghz_preset_ook_async_regs[][2] = {
/* Base setting */
{ CC1101_IOCFG0, 0x0D }, // GD0 as async serial data output/input
{ CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz
{ CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
/* Async OOK Specific things */
/* Async OOK Specific things */
{ CC1101_MDMCFG2, 0x30 }, // ASK/OOK, No preamble/sync
{ CC1101_PKTCTRL0, 0x32 }, // Async, no CRC, Infinite
{ CC1101_FREND0, 0x01 }, // OOK/ASK PATABLE
/* End */
{ 0, 0 },
};
@@ -24,15 +27,54 @@ static const uint8_t api_hal_subghz_preset_ook_async_patable[8] = {
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t api_hal_subghz_preset_mp_regs[][2] = {
{ CC1101_IOCFG0, 0x0D },
{ CC1101_FIFOTHR, 0x07 },
{ CC1101_PKTCTRL0, 0x32 },
//{ CC1101_FSCTRL1, 0x0E },
{ CC1101_FSCTRL1, 0x06 },
{ CC1101_FREQ2, 0x10 },
{ CC1101_FREQ1, 0xB0 },
{ CC1101_FREQ0, 0x7F },
{ CC1101_MDMCFG4, 0x17 },
{ CC1101_MDMCFG3, 0x32 },
{ CC1101_MDMCFG2, 0x30 }, //<---OOK/ASK
{ CC1101_MDMCFG1, 0x23 },
{ CC1101_MDMCFG0, 0xF8 },
{ CC1101_MCSM0, 0x18 },
{ CC1101_FOCCFG, 0x18 },
{ CC1101_AGCTRL2, 0x07 },
{ CC1101_AGCTRL1, 0x00 },
{ CC1101_AGCTRL0, 0x91 },
{ CC1101_WORCTRL, 0xFB },
{ CC1101_FREND1, 0xB6 },
//{ CC1101_FREND0, 0x11 },
{ CC1101_FREND0, 0x01 },
{ CC1101_FSCAL3, 0xE9 },
{ CC1101_FSCAL2, 0x2A },
{ CC1101_FSCAL1, 0x00 },
{ CC1101_FSCAL0, 0x1F },
{ CC1101_TEST2, 0x88 },
{ CC1101_TEST1, 0x31 },
{ CC1101_TEST0, 0x09 },
/* End */
{ 0, 0 },
};
static const uint8_t api_hal_subghz_preset_mp_patable[8] = {
0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static const uint8_t api_hal_subghz_preset_2fsk_packet_regs[][2] = {
/* Base setting */
{ CC1101_IOCFG0, 0x06 }, // GD0 as async serial data output/input
{ CC1101_FSCTRL1, 0x06 }, // Set IF 26m/2^10*2=2.2MHz
{ CC1101_MCSM0, 0x18 }, // Autocalibrate on idle to TRX, ~150us OSC guard time
{ CC1101_TEST2, 0x81},
{ CC1101_TEST1, 0x35},
{ CC1101_TEST0, 0x09},
/* Magic */
{ CC1101_TEST2, 0x81},
{ CC1101_TEST1, 0x35},
{ CC1101_TEST0, 0x09},
/* End */
{ 0, 0 },
@@ -46,20 +88,26 @@ void api_hal_subghz_init() {
const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
// Reset and shutdown
cc1101_reset(device);
// Prepare GD0 for power on self test
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
// GD0 low
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW);
while(hal_gpio_read(&gpio_cc1101_g0) != false);
// GD0 high
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV);
while(hal_gpio_read(&gpio_cc1101_g0) != true);
// Reset GD0 to floating state
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance);
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
// RF switches
hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW);
// Turn off oscillator
cc1101_shutdown(device);
api_hal_spi_device_return(device);
@@ -82,6 +130,9 @@ void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) {
} else if(preset == ApiHalSubGhzPreset2FskPacket) {
api_hal_subghz_load_registers(api_hal_subghz_preset_2fsk_packet_regs);
api_hal_subghz_load_patable(api_hal_subghz_preset_2fsk_packet_patable);
} else if(preset == ApiHalSubGhzPresetMP) {
api_hal_subghz_load_registers(api_hal_subghz_preset_mp_regs);
api_hal_subghz_load_patable(api_hal_subghz_preset_mp_patable);
}
}
@@ -176,6 +227,20 @@ float api_hal_subghz_get_rssi() {
return rssi;
}
uint32_t api_hal_subghz_set_frequency_and_path(uint32_t value) {
value = api_hal_subghz_set_frequency(value);
if(value >= 300000000 && value <= 348000335) {
api_hal_subghz_set_path(ApiHalSubGhzPath315);
} else if(value >= 387000000 && value <= 464000000) {
api_hal_subghz_set_path(ApiHalSubGhzPath433);
} else if(value >= 779000000 && value <= 928000000) {
api_hal_subghz_set_path(ApiHalSubGhzPath868);
} else {
furi_check(0);
}
return value;
}
uint32_t api_hal_subghz_set_frequency(uint32_t value) {
const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
@@ -211,3 +276,94 @@ void api_hal_subghz_set_path(ApiHalSubGhzPath path) {
}
api_hal_spi_device_return(device);
}
volatile uint32_t api_hal_subghz_capture_delta_duration = 0;
volatile ApiHalSubGhzCaptureCallback api_hal_subghz_capture_callback = NULL;
volatile void* api_hal_subghz_capture_callback_context = NULL;
void api_hal_subghz_set_capture_callback(ApiHalSubGhzCaptureCallback callback, void* context) {
api_hal_subghz_capture_callback = callback;
api_hal_subghz_capture_callback_context = context;
}
static void api_hal_subghz_capture_ISR() {
// Channel 1
if(LL_TIM_IsActiveFlag_CC1(TIM2)) {
LL_TIM_ClearFlag_CC1(TIM2);
api_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2);
if (api_hal_subghz_capture_callback) {
api_hal_subghz_capture_callback(
ApiHalSubGhzCaptureLevelHigh,
api_hal_subghz_capture_delta_duration,
(void*)api_hal_subghz_capture_callback_context
);
}
}
// Channel 2
if(LL_TIM_IsActiveFlag_CC2(TIM2)) {
LL_TIM_ClearFlag_CC2(TIM2);
if (api_hal_subghz_capture_callback) {
api_hal_subghz_capture_callback(
ApiHalSubGhzCaptureLevelLow,
LL_TIM_IC_GetCaptureCH2(TIM2) - api_hal_subghz_capture_delta_duration,
(void*)api_hal_subghz_capture_callback_context
);
}
}
}
void api_hal_subghz_enable_capture() {
/* Peripheral clock enable */
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
hal_gpio_init_ex(&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
// Timer: base
LL_TIM_InitTypeDef TIM_InitStruct = {0};
TIM_InitStruct.Prescaler = 64-1;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0xFFFFFFFF;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_Init(TIM2, &TIM_InitStruct);
// Timer: advanced and channel
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM2);
LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2);
LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET);
LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
LL_TIM_DisableIT_TRIG(TIM2);
LL_TIM_DisableDMAReq_TRIG(TIM2);
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
LL_TIM_EnableMasterSlaveMode(TIM2);
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI);
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING);
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
// ISR setup
api_hal_interrupt_set_timer_isr(TIM2, api_hal_subghz_capture_ISR);
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
NVIC_EnableIRQ(TIM2_IRQn);
// Interrupts and channels
LL_TIM_EnableIT_CC1(TIM2);
LL_TIM_EnableIT_CC2(TIM2);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
// Start timer
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
}
void api_hal_subghz_disable_capture() {
LL_TIM_DeInit(TIM2);
api_hal_interrupt_set_timer_isr(TIM2, NULL);
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}

View File

@@ -1,45 +0,0 @@
#include "api-hal-tim_i.h"
#include "api-hal-irda_i.h"
#include <stm32wbxx_ll_tim.h>
#include <furi.h>
void TIM2_IRQHandler(void) {
bool consumed = false;
if(LL_TIM_IsActiveFlag_CC1(TIM2) == 1) {
if(LL_TIM_IsEnabledIT_CC1(TIM2)) {
LL_TIM_ClearFlag_CC1(TIM2);
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
// input capture
api_hal_irda_tim_isr(TimerIRQSourceCCI1);
consumed = true;
} else {
// output compare
// HAL_TIM_OC_DelayElapsedCallback(htim);
// HAL_TIM_PWM_PulseFinishedCallback(htim);
}
}
}
if(LL_TIM_IsActiveFlag_CC2(TIM2) == 1) {
if(LL_TIM_IsEnabledIT_CC2(TIM2)) {
LL_TIM_ClearFlag_CC2(TIM2);
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
// input capture
api_hal_irda_tim_isr(TimerIRQSourceCCI2);
consumed = true;
} else {
// output compare
// HAL_TIM_OC_DelayElapsedCallback(htim);
// HAL_TIM_PWM_PulseFinishedCallback(htim);
}
}
}
// TODO move all timers on LL hal
if(!consumed) {
// currently backed up with a crutch, we need more bicycles
HAL_TIM_IRQHandler(&htim2);
}
}

View File

@@ -1,7 +0,0 @@
#pragma once
typedef enum{
TimerIRQSourceCCI1,
TimerIRQSourceCCI2,
} TimerIRQSource;

View File

@@ -1,16 +0,0 @@
#include "api-hal/api-interrupt-mgr.h"
#include <main.h>
extern void api_interrupt_call(InterruptType type, void* hw);
/* interrupts */
/* Comparator trigger event */
void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {
api_interrupt_call(InterruptTypeComparatorTrigger, hcomp);
}
/* Timer update event */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {
api_interrupt_call(InterruptTypeTimerUpdate, htim);
}