SubGhz: add subghz tx_from_file CLI cmd, major TX flow refactoring, various improvements and bug fixes (#3302)

* SubGhz: add cmd CLI "subghz tx_from_file"
* SubGhz:  add sending raw.sub files
* SubGhz: add load custom preset
* SubGhz: remove unnecessary files
* SubGhz: change message
* SubGhz: fix printf formatting
* SubGhz: Cli refactoring code
* FuriHal: add furi_hal_subghz Tx Rx IDLE state switching test
* SubGhz: remove debug code, fix ext driver compilation
* SubGhz: cleanup code, move wait status routine to cc1101 driver
* SubGhz: proper pin mode transition in tx stop isr routine, proper DMA and ISR priorities, fix issue with async tx stuck
* SubGhz: simplify async tx stop flow, fix ISR ARR check condition race
* SubGhz: check ARR only when we transmitting
* SubGhz: check ARR only when we transmitting for ext cc1101
* SubGhz: lower ISR priorities to safe level
* SubGhz: proper gpio config, comments update

Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
Skorpionm 2024-01-15 09:38:43 +04:00 committed by GitHub
parent 3fd5f15e7f
commit d73d007797
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 364 additions and 113 deletions

View file

@ -45,7 +45,6 @@ typedef enum {
SubGhzDeviceCC1101ExtStateIdle, /**< Idle, energy save mode */
SubGhzDeviceCC1101ExtStateAsyncRx, /**< Async RX started */
SubGhzDeviceCC1101ExtStateAsyncTx, /**< Async TX started, DMA and timer is on */
SubGhzDeviceCC1101ExtStateAsyncTxEnd, /**< Async TX complete, cleanup needed */
} SubGhzDeviceCC1101ExtState;
/** SubGhz regulation, receive transmission on the current frequency for the
@ -392,12 +391,18 @@ void subghz_device_cc1101_ext_reset() {
void subghz_device_cc1101_ext_idle() {
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_switch_to_idle(subghz_device_cc1101_ext->spi_bus_handle);
//waiting for the chip to switch to IDLE mode
furi_check(cc1101_wait_status_state(
subghz_device_cc1101_ext->spi_bus_handle, CC1101StateIDLE, 10000));
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
}
void subghz_device_cc1101_ext_rx() {
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_switch_to_rx(subghz_device_cc1101_ext->spi_bus_handle);
//waiting for the chip to switch to Rx mode
furi_check(
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateRX, 10000));
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
}
@ -405,6 +410,9 @@ bool subghz_device_cc1101_ext_tx() {
if(subghz_device_cc1101_ext->regulation != SubGhzDeviceCC1101ExtRegulationTxRx) return false;
furi_hal_spi_acquire(subghz_device_cc1101_ext->spi_bus_handle);
cc1101_switch_to_tx(subghz_device_cc1101_ext->spi_bus_handle);
//waiting for the chip to switch to Tx mode
furi_check(
cc1101_wait_status_state(subghz_device_cc1101_ext->spi_bus_handle, CC1101StateTX, 10000));
furi_hal_spi_release(subghz_device_cc1101_ext->spi_bus_handle);
return true;
}
@ -653,7 +661,6 @@ static void subghz_device_cc1101_ext_async_tx_refill(uint32_t* buffer, size_t sa
if(LL_DMA_IsActiveFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA)) {
LL_DMA_ClearFlag_TC3(SUBGHZ_DEVICE_CC1101_EXT_DMA);
}
LL_TIM_EnableIT_UPDATE(TIM17);
break;
} else {
// Lowest possible value is 4us
@ -689,22 +696,6 @@ static void subghz_device_cc1101_ext_async_tx_dma_isr() {
#endif
}
static void subghz_device_cc1101_ext_async_tx_timer_isr() {
if(LL_TIM_IsActiveFlag_UPDATE(TIM17)) {
if(LL_TIM_GetAutoReload(TIM17) == 0) {
if(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) {
LL_DMA_DisableChannel(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF);
subghz_device_cc1101_ext->state = SubGhzDeviceCC1101ExtStateAsyncTxEnd;
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
if(subghz_device_cc1101_ext->async_mirror_pin != NULL)
furi_hal_gpio_write(subghz_device_cc1101_ext->async_mirror_pin, false);
LL_TIM_DisableCounter(TIM17);
}
}
LL_TIM_ClearFlag_UPDATE(TIM17);
}
}
bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callback, void* context) {
furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateIdle);
furi_assert(callback);
@ -733,7 +724,7 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF,
LL_DMA_DIRECTION_MEMORY_TO_PERIPH | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_WORD | LL_DMA_MDATAALIGN_WORD |
LL_DMA_MODE_NORMAL);
LL_DMA_PRIORITY_VERYHIGH);
LL_DMA_SetDataLength(
SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, SUBGHZ_DEVICE_CC1101_EXT_ASYNC_TX_BUFFER_FULL);
LL_DMA_SetPeriphRequest(SUBGHZ_DEVICE_CC1101_EXT_DMA_CH3_DEF, LL_DMAMUX_REQ_TIM17_UP);
@ -756,9 +747,6 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
LL_TIM_SetClockSource(TIM17, LL_TIM_CLOCKSOURCE_INTERNAL);
LL_TIM_DisableARRPreload(TIM17);
furi_hal_interrupt_set_isr(
FuriHalInterruptIdTim1TrgComTim17, subghz_device_cc1101_ext_async_tx_timer_isr, NULL);
subghz_device_cc1101_ext_async_tx_middleware_idle(
&subghz_device_cc1101_ext->async_tx.middleware);
subghz_device_cc1101_ext_async_tx_refill(
@ -816,22 +804,21 @@ bool subghz_device_cc1101_ext_start_async_tx(SubGhzDeviceCC1101ExtCallback callb
}
bool subghz_device_cc1101_ext_is_async_tx_complete() {
return subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd;
return (
(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx) &&
(LL_TIM_GetAutoReload(TIM17) == 0));
}
void subghz_device_cc1101_ext_stop_async_tx() {
furi_assert(
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx ||
subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTxEnd);
// Deinitialize GPIO
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
furi_hal_gpio_init(
subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
furi_assert(subghz_device_cc1101_ext->state == SubGhzDeviceCC1101ExtStateAsyncTx);
// Shutdown radio
subghz_device_cc1101_ext_idle();
// Deinitialize GPIO
furi_hal_gpio_write(subghz_device_cc1101_ext->g0_pin, false);
furi_hal_gpio_init(subghz_device_cc1101_ext->g0_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
// Deinitialize Timer
furi_hal_bus_disable(FuriHalBusTIM17);
furi_hal_interrupt_set_isr(FuriHalInterruptIdTim1TrgComTim17, NULL, NULL);

View file

@ -121,7 +121,7 @@ static void nfc_scene_read_menu_on_enter_mf_classic(NfcApp* instance) {
}
}
static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) {
static void nfc_scene_read_success_on_enter_mf_classic(NfcApp* instance) { //-V524
const NfcDevice* device = instance->nfc_device;
const MfClassicData* data = nfc_device_get_data(device, NfcProtocolMfClassic);

View file

@ -72,7 +72,6 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
uint32_t frequency = 0;
float rssi_temp = -127.0f;
uint32_t frequency_temp = 0;
CC1101Status status;
//Start CC1101
furi_hal_subghz_reset();
@ -123,9 +122,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
subghz_setting_get_frequency(instance->setting, i));
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
do {
status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
} while(status.STATE != CC1101StateIDLE);
furi_check(cc1101_wait_status_state(
&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
@ -168,9 +167,9 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i);
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
do {
status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
} while(status.STATE != CC1101StateIDLE);
furi_check(cc1101_wait_status_state(
&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);

View file

@ -49,6 +49,28 @@ static void subghz_cli_radio_device_power_off() {
if(furi_hal_power_is_otg_enabled()) furi_hal_power_disable_otg();
}
static SubGhzEnvironment* subghz_cli_environment_init(void) {
SubGhzEnvironment* environment = subghz_environment_alloc();
if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) {
printf("Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n");
} else {
printf("Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n");
}
if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) {
printf("Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n");
} else {
printf("Load_keystore keeloq_mfcodes_user \033[0;33mAbsent\033[0m\r\n");
}
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
return environment;
}
void subghz_cli_command_tx_carrier(Cli* cli, FuriString* args, void* context) {
UNUSED(context);
uint32_t frequency = 433920000;
@ -324,16 +346,7 @@ void subghz_cli_command_rx(Cli* cli, FuriString* args, void* context) {
furi_stream_buffer_alloc(sizeof(LevelDuration) * 1024, sizeof(LevelDuration));
furi_check(instance->stream);
SubGhzEnvironment* environment = subghz_environment_alloc();
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME);
subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME);
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzEnvironment* environment = subghz_cli_environment_init();
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@ -517,25 +530,7 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
// Allocate context
SubGhzCliCommandRx* instance = malloc(sizeof(SubGhzCliCommandRx));
SubGhzEnvironment* environment = subghz_environment_alloc();
if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_NAME)) {
printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;32mOK\033[0m\r\n");
} else {
printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes \033[0;31mERROR\033[0m\r\n");
}
if(subghz_environment_load_keystore(environment, SUBGHZ_KEYSTORE_DIR_USER_NAME)) {
printf("SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;32mOK\033[0m\r\n");
} else {
printf(
"SubGhz decode_raw: Load_keystore keeloq_mfcodes_user \033[0;31mERROR\033[0m\r\n");
}
subghz_environment_set_came_atomo_rainbow_table_file_name(
environment, SUBGHZ_CAME_ATOMO_DIR_NAME);
subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
environment, SUBGHZ_ALUTECH_AT_4N_DIR_NAME);
subghz_environment_set_nice_flor_s_rainbow_table_file_name(
environment, SUBGHZ_NICE_FLOR_S_DIR_NAME);
subghz_environment_set_protocol_registry(environment, (void*)&subghz_protocol_registry);
SubGhzEnvironment* environment = subghz_cli_environment_init();
SubGhzReceiver* receiver = subghz_receiver_alloc_init(environment);
subghz_receiver_set_filter(receiver, SubGhzProtocolFlag_Decodable);
@ -580,6 +575,262 @@ void subghz_cli_command_decode_raw(Cli* cli, FuriString* args, void* context) {
furi_string_free(file_name);
}
static FuriHalSubGhzPreset subghz_cli_get_preset_name(const char* preset_name) {
FuriHalSubGhzPreset preset = FuriHalSubGhzPresetIDLE;
if(!strcmp(preset_name, "FuriHalSubGhzPresetOok270Async")) {
preset = FuriHalSubGhzPresetOok270Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetOok650Async")) {
preset = FuriHalSubGhzPresetOok650Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev238Async")) {
preset = FuriHalSubGhzPreset2FSKDev238Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPreset2FSKDev476Async")) {
preset = FuriHalSubGhzPreset2FSKDev476Async;
} else if(!strcmp(preset_name, "FuriHalSubGhzPresetCustom")) {
preset = FuriHalSubGhzPresetCustom;
} else {
printf("subghz tx_from_file: unknown preset");
}
return preset;
}
void subghz_cli_command_tx_from_file(Cli* cli, FuriString* args, void* context) { // -V524
UNUSED(context);
FuriString* file_name;
file_name = furi_string_alloc();
furi_string_set(file_name, ANY_PATH("subghz/test.sub"));
uint32_t repeat = 10;
uint32_t device_ind = 0; // 0 - CC1101_INT, 1 - CC1101_EXT
Storage* storage = furi_record_open(RECORD_STORAGE);
FlipperFormat* fff_data_file = flipper_format_file_alloc(storage);
FlipperFormat* fff_data_raw = flipper_format_string_alloc();
FuriString* temp_str;
temp_str = furi_string_alloc();
uint32_t temp_data32;
bool check_file = false;
const SubGhzDevice* device = NULL;
uint32_t frequency = 0;
SubGhzTransmitter* transmitter = NULL;
subghz_devices_init();
SubGhzEnvironment* environment = subghz_cli_environment_init();
do {
if(furi_string_size(args)) {
if(!args_read_string_and_trim(args, file_name)) {
cli_print_usage(
"subghz tx_from_file: ",
"<file_name: path_file> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",
furi_string_get_cstr(args));
break;
}
}
if(furi_string_size(args)) {
int ret = sscanf(furi_string_get_cstr(args), "%lu %lu", &repeat, &device_ind);
if(ret != 2) {
printf("sscanf returned %d, repeat: %lu device: %lu\r\n", ret, repeat, device_ind);
cli_print_usage(
"subghz tx_from_file:",
"<file_name: path_file> <Repeat count> <Device: 0 - CC1101_INT, 1 - CC1101_EXT>",
furi_string_get_cstr(args));
break;
}
}
device = subghz_cli_command_get_device(&device_ind);
if(device == NULL) {
printf("subghz tx_from_file: \033[0;31mError device not found\033[0m\r\n");
break;
}
if(!flipper_format_file_open_existing(fff_data_file, furi_string_get_cstr(file_name))) {
printf(
"subghz tx_from_file: \033[0;31mError open file\033[0m %s\r\n",
furi_string_get_cstr(file_name));
break;
}
if(!flipper_format_read_header(fff_data_file, temp_str, &temp_data32)) {
printf("subghz tx_from_file: \033[0;31mMissing or incorrect header\033[0m\r\n");
break;
}
if(((!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_KEY_FILE_TYPE)) ||
(!strcmp(furi_string_get_cstr(temp_str), SUBGHZ_RAW_FILE_TYPE))) &&
temp_data32 == SUBGHZ_KEY_FILE_VERSION) {
} else {
printf("subghz tx_from_file: \033[0;31mType or version mismatch\033[0m\r\n");
break;
}
//Load frequency
if(!flipper_format_read_uint32(fff_data_file, "Frequency", &frequency, 1)) {
printf("subghz tx_from_file: \033[0;31mMissing Frequency\033[0m\r\n");
break;
}
if(!subghz_devices_is_frequency_valid(device, frequency)) {
printf("subghz tx_from_file: \033[0;31mFrequency not supported\033[0m\r\n");
break;
}
//Load preset
if(!flipper_format_read_string(fff_data_file, "Preset", temp_str)) {
printf("subghz tx_from_file: \033[0;31mMissing Preset\033[0m\r\n");
break;
}
subghz_devices_begin(device);
subghz_devices_reset(device);
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
uint8_t* custom_preset_data;
uint32_t custom_preset_data_size;
if(!flipper_format_get_value_count(fff_data_file, "Custom_preset_data", &temp_data32))
break;
if(!temp_data32 || (temp_data32 % 2)) {
printf("subghz tx_from_file: \033[0;31mCustom_preset_data size error\033[0m\r\n");
break;
}
custom_preset_data_size = sizeof(uint8_t) * temp_data32;
custom_preset_data = malloc(custom_preset_data_size);
if(!flipper_format_read_hex(
fff_data_file,
"Custom_preset_data",
custom_preset_data,
custom_preset_data_size)) {
printf("subghz tx_from_file: \033[0;31mCustom_preset_data read error\033[0m\r\n");
break;
}
subghz_devices_load_preset(
device,
subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)),
custom_preset_data);
free(custom_preset_data);
} else {
subghz_devices_load_preset(
device, subghz_cli_get_preset_name(furi_string_get_cstr(temp_str)), NULL);
}
subghz_devices_set_frequency(device, frequency);
//Load protocol
if(!flipper_format_read_string(fff_data_file, "Protocol", temp_str)) {
printf("subghz tx_from_file: \033[0;31mMissing protocol\033[0m\r\n");
break;
}
SubGhzProtocolStatus status;
bool is_init_protocol = true;
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) { // if RAW protocol
subghz_protocol_raw_gen_fff_data(
fff_data_raw, furi_string_get_cstr(file_name), subghz_devices_get_name(device));
transmitter =
subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str));
if(transmitter == NULL) {
printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n");
is_init_protocol = false;
}
if(is_init_protocol) {
status = subghz_transmitter_deserialize(transmitter, fff_data_raw);
if(status != SubGhzProtocolStatusOk) {
printf(
"subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n",
status);
is_init_protocol = false;
}
}
} else { //if not RAW protocol
flipper_format_insert_or_update_uint32(fff_data_file, "Repeat", &repeat, 1);
transmitter =
subghz_transmitter_alloc_init(environment, furi_string_get_cstr(temp_str));
if(transmitter == NULL) {
printf("subghz tx_from_file: \033[0;31mError transmitter\033[0m\r\n");
is_init_protocol = false;
}
if(is_init_protocol) {
status = subghz_transmitter_deserialize(transmitter, fff_data_file);
if(status != SubGhzProtocolStatusOk) {
printf(
"subghz tx_from_file: \033[0;31mError deserialize protocol\033[0m %d\r\n",
status);
is_init_protocol = false;
}
}
flipper_format_delete_key(fff_data_file, "Repeat");
}
if(is_init_protocol) {
check_file = true;
} else {
subghz_devices_sleep(device);
subghz_devices_end(device);
subghz_transmitter_free(transmitter);
}
} while(false);
flipper_format_free(fff_data_file);
furi_record_close(RECORD_STORAGE);
if(check_file) {
furi_hal_power_suppress_charge_enter();
printf(
"Listening at \033[0;33m%s\033[0m. Frequency=%lu, Protocol=%s\r\n\r\nPress CTRL+C to stop\r\n\r\n",
furi_string_get_cstr(file_name),
frequency,
furi_string_get_cstr(temp_str));
do {
//delay in downloading files and other preparatory processes
furi_delay_ms(200);
if(subghz_devices_start_async_tx(device, subghz_transmitter_yield, transmitter)) {
while(
!(subghz_devices_is_async_complete_tx(device) ||
cli_cmd_interrupt_received(cli))) {
printf(".");
fflush(stdout);
furi_delay_ms(333);
}
subghz_devices_stop_async_tx(device);
} else {
printf("Transmission on this frequency is restricted in your region\r\n");
}
if(!strcmp(furi_string_get_cstr(temp_str), "RAW")) {
subghz_transmitter_stop(transmitter);
repeat--;
if(!cli_cmd_interrupt_received(cli) && repeat)
subghz_transmitter_deserialize(transmitter, fff_data_raw);
}
} while(!cli_cmd_interrupt_received(cli) &&
(repeat && !strcmp(furi_string_get_cstr(temp_str), "RAW")));
subghz_devices_sleep(device);
subghz_devices_end(device);
subghz_cli_radio_device_power_off();
furi_hal_power_suppress_charge_exit();
subghz_transmitter_free(transmitter);
}
flipper_format_free(fff_data_raw);
furi_string_free(file_name);
furi_string_free(temp_str);
subghz_devices_deinit();
subghz_environment_free(environment);
}
static void subghz_cli_command_print_usage() {
printf("Usage:\r\n");
printf("subghz <cmd> <args>\r\n");
@ -592,11 +843,13 @@ static void subghz_cli_command_print_usage() {
printf("\trx <frequency:in Hz> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\t - Receive\r\n");
printf("\trx_raw <frequency:in Hz>\t - Receive RAW\r\n");
printf("\tdecode_raw <file_name: path_RAW_file>\t - Testing\r\n");
printf(
"\ttx_from_file <file_name: path_file> <repeat: count> <device: 0 - CC1101_INT, 1 - CC1101_EXT>\t - Transmitting from file\r\n");
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
printf("\r\n");
printf(" debug cmd:\r\n");
printf("\ttx_carrier <frequency:in Hz>\t - Transmit carrier\r\n");
printf("\ttx_carrier <frequency:in Hz>\t - Transmitting carrier\r\n");
printf("\trx_carrier <frequency:in Hz>\t - Receive carrier\r\n");
printf(
"\tencrypt_keeloq <path_decrypted_file> <path_encrypted_file> <IV:16 bytes in hex>\t - Encrypt keeloq manufacture keys\r\n");
@ -915,6 +1168,11 @@ static void subghz_cli_command(Cli* cli, FuriString* args, void* context) {
break;
}
if(furi_string_cmp_str(cmd, "tx_from_file") == 0) {
subghz_cli_command_tx_from_file(cli, args, context);
break;
}
if(furi_hal_rtc_is_flag_set(FuriHalRtcFlagDebug)) {
if(furi_string_cmp_str(cmd, "encrypt_keeloq") == 0) {
subghz_cli_command_encrypt_keeloq(cli, args);

View file

@ -78,6 +78,20 @@ CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle) {
return cc1101_strobe(handle, CC1101_STROBE_SNOP);
}
bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us) {
bool result = false;
CC1101Status status = {0};
FuriHalCortexTimer timer = furi_hal_cortex_timer_get(timeout_us);
while(!furi_hal_cortex_timer_is_expired(timer)) {
status = cc1101_strobe(handle, CC1101_STROBE_SNOP);
if(status.STATE == state) {
result = true;
break;
}
}
return result;
}
CC1101Status cc1101_shutdown(FuriHalSpiBusHandle* handle) {
return cc1101_strobe(handle, CC1101_STROBE_SPWD);
}

View file

@ -59,6 +59,16 @@ CC1101Status cc1101_reset(FuriHalSpiBusHandle* handle);
*/
CC1101Status cc1101_get_status(FuriHalSpiBusHandle* handle);
/** Wait specific chip state
*
* @param handle The SPI bus handle
* @param[in] state The state to wait
* @param[in] timeout_us The timeout in microseconds
*
* @return true on success, false otherwise
*/
bool cc1101_wait_status_state(FuriHalSpiBusHandle* handle, CC1101State state, uint32_t timeout_us);
/** Enable shutdown mode
*
* @param handle - pointer to FuriHalSpiHandle

View file

@ -2,6 +2,7 @@
#include <furi_hal_os.h>
#include <furi.h>
#include <FreeRTOS.h>
#include <stm32wbxx.h>
#include <stm32wbxx_ll_tim.h>
@ -10,7 +11,7 @@
#define TAG "FuriHalInterrupt"
#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY 5
#define FURI_HAL_INTERRUPT_DEFAULT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY)
typedef struct {
FuriHalInterruptISR isr;

View file

@ -36,7 +36,6 @@ typedef enum {
SubGhzStateAsyncRx, /**< Async RX started */
SubGhzStateAsyncTx, /**< Async TX started, DMA and timer is on */
SubGhzStateAsyncTxEnd, /**< Async TX complete, cleanup needed */
} SubGhzState;
@ -277,12 +276,16 @@ void furi_hal_subghz_reset() {
void furi_hal_subghz_idle() {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
//waiting for the chip to switch to IDLE mode
furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
}
void furi_hal_subghz_rx() {
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
//waiting for the chip to switch to Rx mode
furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateRX, 10000));
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
}
@ -290,6 +293,8 @@ bool furi_hal_subghz_tx() {
if(furi_hal_subghz.regulation != SubGhzRegulationTxRx) return false;
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
cc1101_switch_to_tx(&furi_hal_spi_bus_handle_subghz);
//waiting for the chip to switch to Tx mode
furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateTX, 10000));
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
return true;
}
@ -352,10 +357,7 @@ uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
uint32_t real_frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, value);
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
while(true) {
CC1101Status status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
if(status.STATE == CC1101StateIDLE) break;
}
furi_check(cc1101_wait_status_state(&furi_hal_spi_bus_handle_subghz, CC1101StateIDLE, 10000));
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
return real_frequency;
@ -624,7 +626,6 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) {
if(LL_DMA_IsActiveFlag_TC1(SUBGHZ_DMA)) {
LL_DMA_ClearFlag_TC1(SUBGHZ_DMA);
}
LL_TIM_EnableIT_UPDATE(TIM2);
break;
} else {
// Lowest possible value is 2us
@ -666,21 +667,6 @@ static void furi_hal_subghz_async_tx_dma_isr() {
#endif
}
static void furi_hal_subghz_async_tx_timer_isr() {
if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) {
LL_TIM_ClearFlag_UPDATE(TIM2);
if(LL_TIM_GetAutoReload(TIM2) == 0) {
if(furi_hal_subghz.state == SubGhzStateAsyncTx) {
furi_hal_subghz.state = SubGhzStateAsyncTxEnd;
LL_DMA_DisableChannel(SUBGHZ_DMA_CH1_DEF);
//forcibly pulls the pin to the ground so that there is no carrier
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
LL_TIM_DisableCounter(TIM2);
}
}
}
}
bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void* context) {
furi_assert(furi_hal_subghz.state == SubGhzStateIdle);
furi_assert(callback);
@ -701,7 +687,7 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
// Connect CC1101_GD0 to TIM2 as output
furi_hal_gpio_init_ex(
&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2);
&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
// Configure DMA
LL_DMA_InitTypeDef dma_config = {0};
@ -715,7 +701,8 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
dma_config.NbData = FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL;
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
dma_config.Priority = LL_DMA_MODE_NORMAL;
dma_config.Priority =
LL_DMA_PRIORITY_VERYHIGH; // Ensure that ARR is updated before anyone else try to check it
LL_DMA_Init(SUBGHZ_DMA_CH1_DEF, &dma_config);
furi_hal_interrupt_set_isr(SUBGHZ_DMA_CH1_IRQ, furi_hal_subghz_async_tx_dma_isr, NULL);
LL_DMA_EnableIT_TC(SUBGHZ_DMA_CH1_DEF);
@ -743,8 +730,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
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);
furi_hal_subghz_async_tx_middleware_idle(&furi_hal_subghz_async_tx.middleware);
furi_hal_subghz_async_tx_refill(
furi_hal_subghz_async_tx.buffer, FURI_HAL_SUBGHZ_ASYNC_TX_BUFFER_FULL);
@ -752,15 +737,6 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
LL_TIM_EnableDMAReq_UPDATE(TIM2);
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
// Start counter
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true);
#endif
furi_hal_subghz_tx();
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
// Start debug
if(furi_hal_subghz_start_debug()) {
const GpioPin* gpio = furi_hal_subghz.async_mirror_pin;
@ -777,30 +753,36 @@ bool furi_hal_subghz_start_async_tx(FuriHalSubGhzAsyncTxCallback callback, void*
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
dma_config.NbData = 2;
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
dma_config.Priority = LL_DMA_PRIORITY_HIGH; // Ensure that it's updated after ARR
LL_DMA_Init(SUBGHZ_DMA_CH2_DEF, &dma_config);
LL_DMA_SetDataLength(SUBGHZ_DMA_CH2_DEF, 2);
LL_DMA_EnableChannel(SUBGHZ_DMA_CH2_DEF);
}
// Start counter
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true);
#endif
furi_hal_subghz_tx();
LL_TIM_SetCounter(TIM2, 0);
LL_TIM_EnableCounter(TIM2);
return true;
}
bool furi_hal_subghz_is_async_tx_complete() {
return furi_hal_subghz.state == SubGhzStateAsyncTxEnd;
return (furi_hal_subghz.state == SubGhzStateAsyncTx) && (LL_TIM_GetAutoReload(TIM2) == 0);
}
void furi_hal_subghz_stop_async_tx() {
furi_assert(
furi_hal_subghz.state == SubGhzStateAsyncTx ||
furi_hal_subghz.state == SubGhzStateAsyncTxEnd);
// Deinitialize GPIO
// Keep in mind that cc1101 will try to pull it up in idle.
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullDown, GpioSpeedLow);
furi_assert(furi_hal_subghz.state == SubGhzStateAsyncTx);
// Shutdown radio
furi_hal_subghz_idle();
// Deinitialize GPIO
furi_hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
furi_hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false);
#endif