Просмотр исходного кода

[FL-950] CC1101 Stage1, SPI Refactoring, Drivers layer (#386)

* API HAL SPI: refactoring, split into layers, prepare ST HAL separation. API HAL SubGhz: initialize on start. Drivers: add basic cc1101 driver. Update API usage. Debug: increase max debugger port speed. Remove subghz apps.
* CC1101: chip status handling. ApiHalSpi: increase SubGhz bus speed to 8mhz. F4: backport subghz initialization.
* Api Hal SubGhz: rx path and frequency. CC1101: frequency control.
* SubGhz Application: basic tests
* SubGhz app: tone and packet test. API HAL SUBGHZ: update configs, add missing bits and pieces.
あく 4 лет назад
Родитель
Сommit
5439e232cc
60 измененных файлов с 2316 добавлено и 2452 удалено
  1. 3 19
      applications/applications.c
  2. 4 24
      applications/applications.mk
  3. 0 614
      applications/cc1101-workaround/cc1101-workaround.cpp
  4. 0 485
      applications/cc1101-workaround/cc1101.cpp
  5. 0 174
      applications/cc1101-workaround/cc1101.h
  6. 1 0
      applications/gui/canvas.c
  7. 15 71
      applications/gui/u8g2_periphery.c
  8. 3 3
      applications/lf-rfid/lf-rfid.c
  9. 1 1
      applications/nfc/nfc_worker.c
  10. 1 1
      applications/sd-filesystem/sd-filesystem.c
  11. 0 13
      applications/subghz/scene/subghz-scene-generic.h
  12. 0 48
      applications/subghz/scene/subghz-scene-spectrum-settings.cpp
  13. 0 12
      applications/subghz/scene/subghz-scene-spectrum-settings.h
  14. 0 67
      applications/subghz/scene/subghz-scene-start.cpp
  15. 0 12
      applications/subghz/scene/subghz-scene-start.h
  16. 0 90
      applications/subghz/subghz-app.cpp
  17. 0 37
      applications/subghz/subghz-app.h
  18. 0 21
      applications/subghz/subghz-event.h
  19. 0 81
      applications/subghz/subghz-view-manager.cpp
  20. 0 37
      applications/subghz/subghz-view-manager.h
  21. 98 0
      applications/subghz/subghz.c
  22. 0 10
      applications/subghz/subghz.cpp
  23. 3 0
      applications/subghz/subghz.h
  24. 44 0
      applications/subghz/subghz_i.h
  25. 203 0
      applications/subghz/subghz_test_basic.c
  26. 11 0
      applications/subghz/subghz_test_basic.h
  27. 210 0
      applications/subghz/subghz_test_packet.c
  28. 11 0
      applications/subghz/subghz_test_packet.h
  29. 0 95
      applications/subghz/view/subghz-view-spectrum-settings.cpp
  30. 0 25
      applications/subghz/view/subghz-view-spectrum-settings.h
  31. 3 3
      debug/stm32wbx.cfg
  32. 86 11
      firmware/targets/api-hal-include/api-hal-subghz.h
  33. 77 133
      firmware/targets/f4/Inc/main.h
  34. 1 0
      firmware/targets/f4/api-hal/api-hal-delay.c
  35. 25 19
      firmware/targets/f4/api-hal/api-hal-resources.c
  36. 18 11
      firmware/targets/f4/api-hal/api-hal-resources.h
  37. 6 10
      firmware/targets/f4/api-hal/api-hal-sd.c
  38. 79 25
      firmware/targets/f4/api-hal/api-hal-spi-config.c
  39. 51 2
      firmware/targets/f4/api-hal/api-hal-spi-config.h
  40. 145 34
      firmware/targets/f4/api-hal/api-hal-spi.c
  41. 76 6
      firmware/targets/f4/api-hal/api-hal-spi.h
  42. 9 3
      firmware/targets/f4/api-hal/api-hal-subghz.c
  43. 1 0
      firmware/targets/f4/api-hal/api-hal.c
  44. 77 135
      firmware/targets/f5/Inc/main.h
  45. 1 0
      firmware/targets/f5/api-hal/api-hal-delay.c
  46. 0 5
      firmware/targets/f5/api-hal/api-hal-gpio.c
  47. 6 6
      firmware/targets/f5/api-hal/api-hal-gpio.h
  48. 14 0
      firmware/targets/f5/api-hal/api-hal-resources.c
  49. 19 1
      firmware/targets/f5/api-hal/api-hal-resources.h
  50. 79 25
      firmware/targets/f5/api-hal/api-hal-spi-config.c
  51. 51 2
      firmware/targets/f5/api-hal/api-hal-spi-config.h
  52. 145 34
      firmware/targets/f5/api-hal/api-hal-spi.c
  53. 76 6
      firmware/targets/f5/api-hal/api-hal-spi.h
  54. 167 5
      firmware/targets/f5/api-hal/api-hal-subghz.c
  55. 1 0
      firmware/targets/f5/api-hal/api-hal.c
  56. 15 11
      lib/ST25RFAL002/platform.c
  57. 25 25
      lib/ST25RFAL002/platform.h
  58. 171 0
      lib/drivers/cc1101.c
  59. 153 0
      lib/drivers/cc1101.h
  60. 131 0
      lib/drivers/cc1101_regs.h

+ 3 - 19
applications/applications.c

@@ -16,7 +16,6 @@ int32_t gui_task(void* p);
 int32_t backlight_control(void* p);
 int32_t irda(void* p);
 int32_t app_loader(void* p);
-int32_t cc1101_workaround(void* p);
 int32_t lf_rfid_workaround(void* p);
 int32_t nfc_task(void* p);
 int32_t dolphin_task(void* p);
@@ -31,7 +30,7 @@ int32_t music_player(void* p);
 int32_t sdnfc(void* p);
 int32_t floopper_bloopper(void* p);
 int32_t sd_filesystem(void* p);
-int32_t app_subghz(void* p);
+int32_t subghz_app(void* p);
 int32_t gui_test(void* p);
 int32_t keypad_test(void* p);
 
@@ -82,13 +81,6 @@ const FlipperApplication FLIPPER_SERVICES[] = {
     {.app = bt_task, .name = "bt_task", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
-#ifdef APP_CC1101
-    {.app = cc1101_workaround,
-     .name = "cc1101 workaround",
-     .stack_size = 1024,
-     .icon = A_Plugins_14},
-#endif
-
 #ifdef APP_LF_RFID
     {.app = lf_rfid_workaround,
      .name = "lf rfid workaround",
@@ -151,10 +143,6 @@ const FlipperApplication FLIPPER_SERVICES[] = {
     {.app = gui_test, .name = "gui_test", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
-#ifdef APP_SUBGHZ
-    {.app = app_subghz, .name = "app_subghz", .stack_size = 1024, .icon = A_Plugins_14},
-#endif
-
 #ifdef APP_KEYPAD_TEST
     {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 #endif
@@ -164,8 +152,8 @@ const size_t FLIPPER_SERVICES_COUNT = sizeof(FLIPPER_SERVICES) / sizeof(FlipperA
 
 // Main menu APP
 const FlipperApplication FLIPPER_APPS[] = {
-#ifdef BUILD_CC1101
-    {.app = cc1101_workaround, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
+#ifdef BUILD_SUBGHZ
+    {.app = subghz_app, .name = "Sub-1 GHz", .stack_size = 1024, .icon = A_Sub1ghz_14},
 #endif
 
 #ifdef BUILD_LF_RFID
@@ -228,10 +216,6 @@ const FlipperApplication FLIPPER_PLUGINS[] = {
     {.app = gui_test, .name = "gui_test", .stack_size = 1024, .icon = A_Plugins_14},
 #endif
 
-#ifdef BUILD_SUBGHZ
-    {.app = app_subghz, .name = "app_subghz", .stack_size = 1024, .icon = A_Plugins_14},
-#endif
-
 #ifdef BUILD_KEYPAD_TEST
     {.app = keypad_test, .name = "keypad_test", .icon = A_Plugins_14},
 #endif

+ 4 - 24
applications/applications.mk

@@ -20,7 +20,7 @@ APP_DOLPHIN = 1
 BUILD_EXAMPLE_BLINK = 1
 BUILD_EXAMPLE_UART_WRITE = 1
 BUILD_EXAMPLE_INPUT_DUMP = 1
-BUILD_CC1101 = 1
+BUILD_SUBGHZ = 1
 BUILD_LF_RFID = 1
 BUILD_SPEAKER_DEMO = 1
 BUILD_VIBRO_DEMO = 1
@@ -31,7 +31,6 @@ BUILD_FLOOPPER_BLOOPPER = 1
 BUILD_IBUTTON = 1
 BUILD_GUI_TEST = 1
 BUILD_KEYPAD_TEST = 1
-BUILD_SUBGHZ = 1
 endif
 
 APP_NFC ?= 0
@@ -146,16 +145,9 @@ C_SOURCES	+= $(APP_DIR)/examples/u8g2_qrcode.c
 C_SOURCES	+= $(LIB_DIR)/qrcode/qrcode.c
 endif
 
-APP_CC1101 ?= 0
-ifeq ($(APP_CC1101), 1)
-CFLAGS		+= -DAPP_CC1101
-BUILD_CC1101 = 1
-endif
-BUILD_CC1101 ?= 0
-ifeq ($(BUILD_CC1101), 1)
-CFLAGS		+= -DBUILD_CC1101
-C_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.c)
-CPP_SOURCES	+= $(wildcard $(APP_DIR)/cc1101-workaround/*.cpp)
+ifeq ($(BUILD_SUBGHZ), 1)
+CFLAGS		+= -DBUILD_SUBGHZ
+C_SOURCES	+= $(wildcard $(APP_DIR)/subghz/*.c)
 APP_INPUT = 1
 APP_GUI = 1
 APP_CLI = 1
@@ -292,18 +284,6 @@ CFLAGS		+= -DBUILD_GUI_TEST
 C_SOURCES	+= $(wildcard $(APP_DIR)/gui-test/*.c)
 endif
 
-APP_SUBGHZ ?= 0
-ifeq ($(APP_SUBGHZ), 1)
-CFLAGS		+= -DAPP_SUBGHZ
-BUILD_SUBGHZ = 1
-endif
-BUILD_SUBGHZ ?= 0
-ifeq ($(BUILD_SUBGHZ), 1)
-CFLAGS		+= -DBUILD_SUBGHZ
-CPP_SOURCES	+= $(wildcard $(APP_DIR)/subghz/*.cpp)
-CPP_SOURCES	+= $(wildcard $(APP_DIR)/subghz/*/*.cpp)
-endif
-
 APP_SDNFC ?= 0
 ifeq ($(APP_SDNFC), 1)
 CFLAGS		+= -DAPP_SDNFC

+ 0 - 614
applications/cc1101-workaround/cc1101-workaround.cpp

@@ -1,614 +0,0 @@
-#include "cc1101.h"
-#include <furi.h>
-#include <api-hal.h>
-#include <gui/gui.h>
-#include <input/input.h>
-
-#define RSSI_DELAY 5000 //rssi delay in micro second
-#define CHAN_SPA 0.05 // channel spacing
-
-int16_t rssi_to_dbm(uint8_t rssi_dec, uint8_t rssiOffset) {
-    int16_t rssi;
-
-    if(rssi_dec >= 128) {
-        rssi = (int16_t)((int16_t)(rssi_dec - 256) / 2) - rssiOffset;
-    } else {
-        rssi = (rssi_dec / 2) - rssiOffset;
-    }
-
-    return rssi;
-}
-
-typedef struct {
-    float base_freq;
-    uint8_t reg[3]; // FREQ2, FREQ1, FREQ0
-    uint8_t first_channel;
-    uint8_t last_channel;
-    uint8_t rssi_offset;
-} Band;
-
-typedef struct {
-    const Band* band;
-    uint16_t channel;
-} FreqConfig;
-
-void setup_freq(CC1101* cc1101, float freq) {
-    // cc1101->SpiWriteReg(CC1101_MCSM0, 0x08); // disalbe FS_AUTOCAL
-    // cc1101->SpiWriteReg(CC1101_AGCCTRL2, 0x43 | 0x0C); // MAX_DVGA_GAIN to 11 for fast rssi
-    // cc1101->SpiWriteReg(CC1101_AGCCTRL0, 0xB0); // max AGC WAIT_TIME; 0 filter_length
-    // cc1101->SetMod(GFSK); // set to GFSK for fast rssi measurement | +8 is dcfilter off
-
-    uint32_t freq_reg = freq * 1e6 / (F_OSC / 65536);
-    cc1101->SetFreq((freq_reg >> 16) & 0xFF, (freq_reg >> 8) & 0xFF, (freq_reg)&0xFF);
-    cc1101->SetChannel(0);
-
-    /*
-    //set test0 to 0x09
-    cc1101->SpiWriteReg(CC1101_TEST0, 0x09);
-    //set FSCAL2 to 0x2A to force VCO HIGH
-    cc1101->SpiWriteReg(CC1101_FSCAL2, 0x2A);
-
-    // perform a manual calibration by issuing SCAL command
-    cc1101->SpiStrobe(CC1101_SCAL);
-    */
-}
-
-static GpioPin debug_0 = {GPIOB, GPIO_PIN_2};
-
-int16_t rx_rssi(CC1101* cc1101, const FreqConfig* config) {
-    // cc1101->SpiStrobe(CC1101_SFRX);
-    // cc1101->SetReceive();
-
-    // uint8_t begin_size = cc1101->SpiReadStatus(CC1101_RXBYTES);
-    // uint8_t rx_status = cc1101->SpiReadStatus(CC1101_MARCSTATE);
-
-    // delay_us(RSSI_DELAY);
-    // osDelay(15);
-
-    // uint8_t end_size = cc1101->SpiReadStatus(CC1101_RXBYTES);
-
-    // 1.4.8) read PKTSTATUS register while the radio is in RX state
-    /*uint8_t _pkt_status = */ // cc1101->SpiReadStatus(CC1101_PKTSTATUS);
-
-    // 1.4.9) enter IDLE state by issuing a SIDLE command
-    // cc1101->SpiStrobe(CC1101_SIDLE);
-
-    // //read rssi value and converto to dBm form
-    uint8_t rssi_dec = (uint8_t)cc1101->SpiReadStatus(CC1101_RSSI);
-    int16_t rssi_dBm = rssi_to_dbm(rssi_dec, config->band->rssi_offset);
-
-    /*
-    char buf[256];
-    sprintf(buf, "status: %d -> %d, rssi: %d\n", rx_status, cc1101->SpiReadStatus(CC1101_MARCSTATE), rssi_dBm);
-    printf(buf);
-    sprintf(buf, "begin: %d, end: %d\n", begin_size, end_size);
-    printf(buf);
-    */
-
-    // uint8_t rx_data[64];
-    // uint8_t fifo_length = end_size - begin_size;
-
-    /*
-    if(fifo_length < 64) {
-        // cc1101->SpiReadBurstReg(CC1101_RXFIFO, rx_data, fifo_length);
-
-        *
-        printf("FIFO:");
-        for(uint8_t i = 0; i < fifo_length; i++) {
-            for(uint8_t bit = 0; bit < 8; bit++) {
-                printf("%s", (rx_data[i] & (1 << bit)) > 0 ? "1" : "0");
-            }
-            printf(" ");
-        }
-        printf("\r\n");
-        *
-
-        for(uint8_t i = 0; i < fifo_length; i++) {
-            for(uint8_t bit = 0; bit < 8; bit++) {
-                gpio_write((GpioPin*)&debug_0, (rx_data[i] & (1 << bit)) > 0);
-                delay_us(5);
-            }
-        }
-    } else {
-        printf("fifo size over\r\n");
-    }
-    */
-
-    return rssi_dBm;
-}
-
-/*
-void flp_config(CC1101* cc1101) {
-    cc1101->SpiWriteReg(
-        CC1101_MCSM0, 0x18); // calibrate when going from IDLE to RX or TX ; 149 - 155 μs timeout
-    // MCSM0.FS_AUTOCAL[1:0] = 1
-
-    cc1101->SpiWriteReg(CC1101_AGCCTRL2, 0x43);
-    cc1101->SpiWriteReg(CC1101_AGCCTRL1, 0x49);
-    cc1101->SpiWriteReg(CC1101_AGCCTRL0, 0x91);
-
-    //freq synthesizer calibration
-    cc1101->SpiWriteReg(CC1101_FSCAL3, 0xEA);
-    cc1101->SpiWriteReg(CC1101_FSCAL2, 0x2A);
-    cc1101->SpiWriteReg(CC1101_FSCAL1, 0x00);
-    cc1101->SpiWriteReg(CC1101_FSCAL0, 0x1F);
-
-    // async data out
-    cc1101->SpiSetRegValue(CC1101_IOCFG0, 13, 5, 0); // GDO0 Output Pin Configuration
-    cc1101->SpiSetRegValue(CC1101_IOCFG0, 13, 5, 0); // WAT
-
-    // FIFOTHR.ADC_RETENTION = 1
-    cc1101->SpiSetRegValue(CC1101_FIFOTHR, 1, 6, 6);
-
-    // PKTCTRL1.APPEND_STATUS = 0
-    cc1101->SpiSetRegValue(CC1101_PKTCTRL1, 0, 2, 2);
-
-    // PKTCTRL0.WHITE_DATA = 0
-    cc1101->SpiSetRegValue(CC1101_PKTCTRL0, 0, 6, 6);
-
-    // PKTCTRL0.LENGTH_CONFIG = 2 // Infinite packet length mode
-    cc1101->SpiSetRegValue(CC1101_PKTCTRL0, 2, 1, 0);
-
-    // PKTCTRL0.CRC_EN = 0
-    cc1101->SpiSetRegValue(CC1101_PKTCTRL0, 0, 2, 2);
-
-    // PKTCTRL0.PKT_FORMAT = 3
-    cc1101->SpiSetRegValue(CC1101_PKTCTRL0, 3, 5, 4);
-
-    // bandwidth 50-100 kHz
-    if(!cc1101->setRxBandwidth(75.0)) {
-        printf("wrong rx bw\r\n");
-    }
-
-    // datarate ~30 kbps
-    if(!cc1101->setBitRate(100.)) {
-        printf("wrong bitrate\r\n");
-    }
-
-    // mod
-    // MDMCFG2.MOD_FORMAT = 3 (3: OOK, 0: 2-FSK)
-    cc1101->SpiSetRegValue(CC1101_MDMCFG2, 3, 6, 4);
-    // MDMCFG2.SYNC_MODE = 0
-    cc1101->SpiSetRegValue(CC1101_MDMCFG2, 0, 2, 0);
-}
-*/
-
-void tx_config(CC1101* cc1101) {
-    // cc1101->SpiWriteReg(CC1101_IOCFG2,0x0B);  //GDO2 Output Pin Configuration
-    // cc1101->SpiWriteReg(CC1101_IOCFG0,0x0C);  //GDO0 Output Pin Configuration
-    cc1101->SpiSetRegValue(CC1101_IOCFG0, 13, 5, 0); // GDO0 Output Pin Configuration
-
-    cc1101->SpiWriteReg(CC1101_FIFOTHR, 0x47); //RX FIFO and TX FIFO Thresholds
-    cc1101->SpiWriteReg(CC1101_PKTCTRL0, 0x32); //Packet Automation Control
-    cc1101->SpiWriteReg(CC1101_FSCTRL1, 0x06); //Frequency Synthesizer Control
-    cc1101->SpiWriteReg(CC1101_FREQ2, 0x10); //Frequency Control Word, High Byte
-    cc1101->SpiWriteReg(CC1101_FREQ1, 0xB0); //Frequency Control Word, Middle Byte
-    cc1101->SpiWriteReg(CC1101_FREQ0, 0x71); //Frequency Control Word, Low Byte
-    cc1101->SpiWriteReg(CC1101_MDMCFG4, 0x6A); //Modem Configuration
-    cc1101->SpiWriteReg(CC1101_MDMCFG3, 0x2E); //Modem Configuration
-    cc1101->SpiWriteReg(CC1101_MDMCFG2, 0x30); //Modem Configuration
-    cc1101->SpiWriteReg(CC1101_DEVIATN, 0x15); //Modem Deviation Setting
-    cc1101->SpiWriteReg(CC1101_MCSM0, 0x18); //Main Radio Control State Machine Configuration
-    cc1101->SpiWriteReg(CC1101_FOCCFG, 0x16); //Frequency Offset Compensation Configuration
-    cc1101->SpiWriteReg(CC1101_WORCTRL, 0xFB); //Wake On Radio Control
-    cc1101->SpiWriteReg(CC1101_FREND0, 0x11); //Front End TX Configuration
-    cc1101->SpiWriteReg(CC1101_FSCAL3, 0xE9); //Frequency Synthesizer Calibration
-    cc1101->SpiWriteReg(CC1101_FSCAL2, 0x2A); //Frequency Synthesizer Calibration
-    cc1101->SpiWriteReg(CC1101_FSCAL1, 0x00); //Frequency Synthesizer Calibration
-    cc1101->SpiWriteReg(CC1101_FSCAL0, 0x1F); //Frequency Synthesizer Calibration
-
-    /*
-    cc1101->SpiWriteReg(CC1101_TEST2, 0x81); //Various Test Settings
-    cc1101->SpiWriteReg(CC1101_TEST1, 0x35); //Various Test Settings
-    cc1101->SpiWriteReg(CC1101_TEST0, 0x09); //Various Test Settings
-    */
-}
-
-// f = (f_osc/65536) * (FREQ + CHAN * (256 + CH_SP_M) * 2^(CH_SP_E - 2))
-// FREQ = f / (f_osc/65536)
-// CHAN = 0
-// TODO: CHAN number not implemented!
-// TODO: reg values not affetcts
-
-const Band bands[] = {
-    {301., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {315., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {346., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {385., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {433.92, {0x00, 0x00, 0x00}, 0, 255, 74},
-    {438.9, {0x00, 0x00, 0x00}, 0, 255, 74},
-    {463., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {781., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {868., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {915., {0x00, 0x00, 0x00}, 0, 255, 74},
-    {925., {0x00, 0x00, 0x00}, 0, 255, 74},
-};
-
-const FreqConfig FREQ_LIST[] = {
-    {&bands[0], 0},
-    {&bands[1], 0},
-    {&bands[2], 0},
-    {&bands[3], 0},
-    {&bands[4], 0},
-    {&bands[5], 0},
-    {&bands[6], 0},
-    {&bands[7], 0},
-    {&bands[8], 0},
-    {&bands[9], 0},
-    {&bands[10], 0},
-};
-
-extern "C" void cc1101_isr(void* _pin, void* _ctx) {
-    uint32_t pin = (uint32_t)_pin;
-    if(pin == CC1101_G0_Pin) {
-        gpio_write((GpioPin*)&debug_0, gpio_read(&cc1101_g0_gpio));
-    }
-}
-
-typedef enum {
-    EventTypeTick,
-    EventTypeKey,
-} EventType;
-
-typedef struct {
-    union {
-        InputEvent input;
-    } value;
-    EventType type;
-} AppEvent;
-
-typedef enum { ModeRx, ModeTx } Mode;
-
-typedef struct {
-    int16_t dbm;
-    uint8_t reg;
-} TxLevel;
-
-const TxLevel TX_LEVELS[] = {
-    {-10, 0},
-    {-5, 0},
-    {0, 0},
-    {5, 0},
-};
-
-typedef struct {
-    Mode mode;
-    size_t active_freq_idx;
-    float active_freq;
-    int16_t last_rssi;
-    size_t tx_level;
-    bool need_cc1101_conf;
-    RfBand rf_band;
-} State;
-
-static void render_callback(Canvas* canvas, void* ctx) {
-    State* state = (State*)acquire_mutex((ValueMutex*)ctx, 25);
-
-    if(!state) return;
-
-    canvas_clear(canvas);
-    canvas_set_color(canvas, ColorBlack);
-    canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str(canvas, 2, 12, "cc1101 workaround");
-
-    {
-        char buf[24];
-        sprintf(
-            buf,
-            "freq: %ld.%02ld MHz",
-            (uint32_t)state->active_freq,
-            (uint32_t)(state->active_freq * 100.) % 100);
-
-        canvas_set_font(canvas, FontSecondary);
-        canvas_draw_str(canvas, 2, 25, buf);
-    }
-
-    {
-        canvas_set_font(canvas, FontSecondary);
-
-        if(state->need_cc1101_conf) {
-            canvas_draw_str(canvas, 2, 36, "mode: configuring...");
-        } else if(state->mode == ModeRx) {
-            canvas_draw_str(canvas, 2, 36, "mode: RX");
-        } else if(state->mode == ModeTx) {
-            canvas_draw_str(canvas, 2, 36, "mode: TX");
-        } else {
-            canvas_draw_str(canvas, 2, 36, "mode: unknown");
-        }
-    }
-
-    {
-        if(!state->need_cc1101_conf && state->mode == ModeRx) {
-            char buf[24];
-            sprintf(buf, "RSSI: %d dBm", state->last_rssi);
-
-            canvas_set_font(canvas, FontSecondary);
-            canvas_draw_str(canvas, 2, 48, buf);
-        }
-    }
-
-    {
-        char buf[24];
-        // sprintf(buf, "tx level: %d dBm", TX_LEVELS[state->tx_level].dbm);
-        sprintf(buf, "RF band: %d", (uint8_t)state->rf_band);
-
-        canvas_set_font(canvas, FontSecondary);
-        if(state->rf_band == RfBandIsolation) {
-            canvas_draw_str(canvas, 2, 63, "RF band: isolation");
-        } else {
-            canvas_draw_str(canvas, 2, 63, buf);
-        }
-    }
-
-    release_mutex((ValueMutex*)ctx, state);
-}
-
-static void input_callback(InputEvent* input_event, void* ctx) {
-    osMessageQueueId_t event_queue = ctx;
-
-    AppEvent event;
-    event.type = EventTypeKey;
-    event.value.input = *input_event;
-    osMessageQueuePut(event_queue, &event, 0, 0);
-}
-
-extern "C" int32_t cc1101_workaround(void* p) {
-    osMessageQueueId_t event_queue = osMessageQueueNew(8, sizeof(AppEvent), NULL);
-    furi_check(event_queue);
-
-    State _state;
-    _state.mode = ModeRx;
-    _state.active_freq_idx = 4;
-
-    FreqConfig conf = FREQ_LIST[_state.active_freq_idx];
-    _state.active_freq = conf.band->base_freq + CHAN_SPA * conf.channel;
-
-    _state.need_cc1101_conf = true;
-    _state.last_rssi = 0;
-    _state.tx_level = 0;
-    _state.rf_band = RfBand1;
-
-    ValueMutex state_mutex;
-    if(!init_mutex(&state_mutex, &_state, sizeof(State))) {
-        printf("[cc1101] cannot create mutex\r\n");
-        return 255;
-    }
-
-    ViewPort* view_port = view_port_alloc();
-
-    view_port_draw_callback_set(view_port, render_callback, &state_mutex);
-    view_port_input_callback_set(view_port, input_callback, event_queue);
-
-    // Open GUI and register view_port
-    Gui* gui = (Gui*)furi_record_open("gui");
-    if(gui == NULL) {
-        printf("[cc1101] gui is not available\r\n");
-        return 255;
-    }
-    gui_add_view_port(gui, view_port, GuiLayerFullscreen);
-
-    gpio_init(&debug_0, GpioModeOutputPushPull);
-    gpio_write((GpioPin*)&debug_0, false);
-
-    printf("[cc1101] creating device\r\n");
-    GpioPin cs_pin = {CC1101_CS_GPIO_Port, CC1101_CS_Pin};
-
-    gpio_init(&cc1101_g0_gpio, GpioModeInput);
-    api_interrupt_add(cc1101_isr, InterruptTypeExternalInterrupt, NULL);
-    // TODO open record
-    GpioPin* cs_pin_record = &cs_pin;
-    CC1101 cc1101(cs_pin_record);
-    printf("[cc1101] init device\r\n");
-
-    uint8_t address = cc1101.Init();
-    if(address > 0) {
-        printf("[cc1101] init done: %d\r\n", address);
-    } else {
-        printf("[cc1101] init fail\r\n");
-        return 255;
-    }
-
-    cc1101.SpiStrobe(CC1101_SIDLE);
-
-    // flp_config(&cc1101);
-    tx_config(&cc1101);
-    // setup_freq(&cc1101, &FREQ_LIST[4]);
-    // enable_cc1101_irq();
-
-    printf("init ok\r\n");
-
-    const int16_t RSSI_THRESHOLD = -60;
-
-    // setup_freq(&cc1101, &FREQ_LIST[1]);
-
-    cc1101.SetReceive();
-
-    AppEvent event;
-    while(1) {
-        osStatus_t event_status = osMessageQueueGet(event_queue, &event, NULL, 100);
-        State* state = (State*)acquire_mutex_block(&state_mutex);
-
-        if(event_status == osOK) {
-            if(event.type == EventTypeKey) {
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyBack) {
-                    printf("[cc1101] bye!\r\n");
-
-                    cc1101.SpiStrobe(CC1101_SIDLE);
-                    cc1101.SpiStrobe(CC1101_SPWD);
-
-                    printf("[cc1101] go to power down\r\n");
-
-                    // TODO remove all view_ports create by app
-                    view_port_enabled_set(view_port, false);
-                    return 255;
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyDown) {
-                    if(state->active_freq_idx > 0) {
-                        state->active_freq_idx--;
-                    }
-
-                    FreqConfig conf = FREQ_LIST[state->active_freq_idx];
-                    state->active_freq = conf.band->base_freq + CHAN_SPA * conf.channel;
-                    state->need_cc1101_conf = true;
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyUp) {
-                    if(state->active_freq_idx < (sizeof(FREQ_LIST) / sizeof(FREQ_LIST[0]) - 1)) {
-                        state->active_freq_idx++;
-                    }
-
-                    FreqConfig conf = FREQ_LIST[state->active_freq_idx];
-                    state->active_freq = conf.band->base_freq + CHAN_SPA * conf.channel;
-                    state->need_cc1101_conf = true;
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyRight) {
-                    /*
-                    if(state->tx_level < (sizeof(TX_LEVELS) / sizeof(TX_LEVELS[0]) - 1)) {
-                        state->tx_level++;
-                    } else {
-                        state->tx_level = 0;
-                    }
-                    */
-
-                    if(state->rf_band < RfBand3) {
-                        state->rf_band = (RfBand)((uint8_t)state->rf_band + 1);
-                    } else {
-                        state->active_freq += 0.25;
-                    }
-                    state->need_cc1101_conf = true;
-                }
-
-                if(event.value.input.type == InputTypeShort &&
-                   event.value.input.key == InputKeyLeft) {
-                    /*
-                    if(state->tx_level < (sizeof(TX_LEVELS) / sizeof(TX_LEVELS[0]) - 1)) {
-                        state->tx_level++;
-                    } else {
-                        state->tx_level = 0;
-                    }
-                    */
-
-                    if(state->rf_band > RfBandIsolation) {
-                        state->rf_band = (RfBand)((uint8_t)state->rf_band - 1);
-                    } else {
-                        state->active_freq -= 0.25;
-                    }
-                    state->need_cc1101_conf = true;
-                }
-
-                if(event.value.input.key == InputKeyOk) {
-                    if(event.value.input.type == InputTypePress) {
-                        state->mode = ModeTx;
-                        state->need_cc1101_conf = true;
-                    } else if(event.value.input.type == InputTypeRelease) {
-                        state->mode = ModeRx;
-                        state->need_cc1101_conf = true;
-                    }
-                }
-            }
-        } else {
-        }
-
-        if(state->need_cc1101_conf) {
-            if(state->mode == ModeRx) {
-                cc1101.SpiStrobe(CC1101_SIDLE);
-                gpio_init(&cc1101_g0_gpio, GpioModeInput);
-
-                setup_freq(&cc1101, state->active_freq);
-                cc1101.SetReceive();
-
-                state->last_rssi = rx_rssi(&cc1101, &FREQ_LIST[state->active_freq_idx]);
-            } else if(state->mode == ModeTx) {
-                cc1101.SpiStrobe(CC1101_SIDLE);
-
-                setup_freq(&cc1101, state->active_freq);
-                cc1101.SetTransmit();
-                gpio_init(&cc1101_g0_gpio, GpioModeOutputPushPull);
-                gpio_write(&cc1101_g0_gpio, false);
-            }
-
-            api_hal_rf_band_set(state->rf_band);
-
-            state->need_cc1101_conf = false;
-        }
-
-        if(!state->need_cc1101_conf && state->mode == ModeRx) {
-            // TOOD what about rssi offset
-            state->last_rssi = rx_rssi(&cc1101, &FREQ_LIST[state->active_freq_idx]);
-
-            api_hal_light_set(LightGreen, state->last_rssi > RSSI_THRESHOLD ? 0xFF : 0x00);
-        } else if(!state->need_cc1101_conf && state->mode == ModeTx) {
-            /*
-            const uint8_t data = 0xA5;
-
-            for(uint8_t i = 0; i < 8; i++) {
-                gpio_write(&cc1101_g0_gpio, (data & (1 << i)) > 0);
-                osDelay(1);
-            }
-            gpio_write(&cc1101_g0_gpio, false);
-            */
-
-            /*
-            // BELL UDB-Q022-0000
-            const uint16_t HALF_PERIOD = 500;
-
-            for(uint8_t n = 0; n < 4; n++) {
-                for(uint8_t i = 0; i < 4; i++) {
-                    gpio_write(&cc1101_g0_gpio, true);
-                    delay_us(3 * HALF_PERIOD);
-                    gpio_write(&cc1101_g0_gpio, false);
-                    delay_us(HALF_PERIOD);
-                }
-
-                for(uint8_t i = 0; i < 40; i++) {
-                    gpio_write(&cc1101_g0_gpio, true);
-                    delay_us(HALF_PERIOD);
-                    gpio_write(&cc1101_g0_gpio, false);
-                    delay_us(HALF_PERIOD);
-                }
-            }
-            */
-
-            // BELL ERA C61, static code
-            const uint16_t ONE_ON = 150;
-            const uint16_t ONE_OFF = 400;
-            const uint16_t ZERO_ON = 420;
-            const uint16_t ZERO_OFF = 130;
-
-            const bool SEQ[] = {true,  true, false, false, true,  false, true,  false, true,
-                                false, true, true,  true,  false, true,  false, true,  true,
-                                true,  true, true,  false, true,  false, true};
-
-            for(uint8_t n = 0; n < 10; n++) {
-                for(uint8_t i = 0; i < sizeof(SEQ) / sizeof(SEQ[0]); i++) {
-                    if(SEQ[i]) {
-                        gpio_write(&cc1101_g0_gpio, false);
-                        delay_us(ONE_ON);
-                        gpio_write(&cc1101_g0_gpio, true);
-                        delay_us(ONE_OFF);
-                    } else {
-                        gpio_write(&cc1101_g0_gpio, false);
-                        delay_us(ZERO_ON);
-                        gpio_write(&cc1101_g0_gpio, true);
-                        delay_us(ZERO_OFF);
-                    }
-                }
-
-                osDelay(4);
-            }
-
-            gpio_write(&cc1101_g0_gpio, false);
-        }
-
-        release_mutex(&state_mutex, state);
-        view_port_update(view_port);
-    }
-
-    return 0;
-}

+ 0 - 485
applications/cc1101-workaround/cc1101.cpp

@@ -1,485 +0,0 @@
-#include <furi.h>
-#include <api-hal.h>
-
-#include "cc1101-workaround/cc1101.h"
-#include "spi.h"
-#include <math.h>
-
-// ******************************************************************************
-#define WRITE_BURST 0x40
-#define READ_SINGLE 0x80
-#define READ_BURST 0xC0
-#define BYTES_IN_FIFO 0x7F //used to detect FIFO underflow or overflow
-
-/*********************ss_pin as global variable****************************** */
-/*                         cc1101                                       */
-/******************************************************************************/
-GpioPin ss_pin;
-
-CC1101::CC1101(GpioPin* ss_pin) {
-    /*
-    pinMode(gdo0_pin, OUTPUT); //GDO0 as asynchronous serial mode input
-    pinMode(gdo2_pin, INPUT); //GDO2 as asynchronous serial mode output
-    */
-    gpio_init(ss_pin, GpioModeOutputPushPull);
-    this->ss_pin = ss_pin;
-
-    // TODO open record
-    this->miso_pin = MISO_PIN;
-    this->miso_pin_record = &this->miso_pin;
-}
-//******************************************************************************
-//SpiInit
-/******************************************************************************/
-extern SPI_HandleTypeDef SPI_R;
-void CC1101::SpiInit(void) {
-    //initialize spi pins
-
-    //Enable spi master, MSB, SPI mode 0, FOSC/4
-    SpiMode(0);
-
-    CC1101_SPI_Reconfigure();
-}
-
-void CC1101::SpiEnd(void) {
-    /*
-    SPCR = ((0<<SPE) |                  // SPI Enable
-        (0<<SPIE)|                      // SPI Interupt Enable
-        (0<<DORD)|                      // Data Order (0:MSB first / 1:LSB first)
-        (1<<MSTR)|                      // Master/Slave select
-        (0<<SPR1)|(0<<SPR0)|        // SPI Clock Rate ( 0 0 = osc/4; 0 1 = osc/16; 1 0 = osc/64; 1 1= 0sc/128)
-        (0<<CPOL)|                  // Clock Polarity (0:SCK low / 1:SCK hi when idle)
-        (0<<CPHA));                     // Clock Phase (0:leading / 1:trailing edge sampling)
-
-    //SPSR =  (0<<SPI2X);                   // Double Clock Rate
-    */
-}
-/******************************************************************************
-Function: SpiMode
- *INPUT        :        config               mode
-               (0<<CPOL) | (0 << CPHA)       0
-               (0<<CPOL) | (1 << CPHA)       1
-               (1<<CPOL) | (0 << CPHA)       2
-               (1<<CPOL) | (1 << CPHA)       3
-*OUTPUT       :none
-******************************************************************************/
-void CC1101::SpiMode(uint8_t config) {
-    /*
-  uint8_t tmp;
-  // enable SPI master with configuration byte specified
-  SPCR = 0;
-  SPCR = (config & 0x7F) | (1<<SPE) | (1<<MSTR);
-  tmp = SPSR;
-  tmp = SPDR;
-  */
-}
-/****************************************************************
-*FUNCTION NAME:SpiTransfer
-*FUNCTION     :spi transfer
-*INPUT        :value: data to send
-*OUTPUT       :data to receive
-****************************************************************/
-uint8_t CC1101::SpiTransfer(uint8_t value) {
-    uint8_t buf[1] = {value};
-    uint8_t rxbuf[1] = {0};
-
-    HAL_SPI_TransmitReceive(&SPI_R, buf, rxbuf, 1, HAL_MAX_DELAY);
-
-    return rxbuf[0];
-}
-
-uint8_t last_status;
-
-/****************************************************************
-*FUNCTION NAME:SpiWriteReg
-*FUNCTION     :CC1101 write data to register
-*INPUT        :addr: register address; value: register value
-*OUTPUT       :none
-****************************************************************/
-void CC1101::SpiWriteReg(uint8_t addr, uint8_t value) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-
-    last_status = SpiTransfer(addr);
-    last_status = SpiTransfer(value);
-    gpio_write(ss_pin, true);
-}
-
-/****************************************************************
-*FUNCTION NAME:SpiWriteBurstReg
-*FUNCTION     :CC1101 write burst data to register
-*INPUT        :addr: register address; buffer:register value array; num:number to write
-*OUTPUT       :none
-****************************************************************/
-void CC1101::SpiWriteBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(addr | WRITE_BURST);
-    for(uint8_t i = 0; i < num; i++) {
-        last_status = SpiTransfer(buffer[i]);
-    }
-    gpio_write(ss_pin, true);
-}
-
-/****************************************************************
-*FUNCTION NAME:SpiStrobe
-*FUNCTION     :CC1101 Strobe
-*INPUT        :strobe: command; //refer define in CC1101.h//
-*OUTPUT       :none
-****************************************************************/
-void CC1101::SpiStrobe(uint8_t strobe) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(strobe);
-    gpio_write(ss_pin, true);
-}
-
-/****************************************************************
-*FUNCTION NAME:SpiReadReg
-*FUNCTION     :CC1101 read data from register
-*INPUT        :addr: register address
-*OUTPUT       :register value
-****************************************************************/
-uint8_t CC1101::SpiReadReg(uint8_t addr) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(addr | READ_SINGLE);
-    uint8_t value = SpiTransfer(0);
-    gpio_write(ss_pin, true);
-
-    return value;
-}
-
-/****************************************************************
-*FUNCTION NAME:SpiReadBurstReg
-*FUNCTION     :CC1101 read burst data from register
-*INPUT        :addr: register address; buffer:array to store register value; num: number to read
-*OUTPUT       :none
-****************************************************************/
-void CC1101::SpiReadBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(addr | READ_BURST);
-    for(uint8_t i = 0; i < num; i++) {
-        buffer[i] = SpiTransfer(0);
-    }
-    gpio_write(ss_pin, true);
-}
-
-/****************************************************************
-*FUNCTION NAME:SpiReadStatus
-*FUNCTION     :CC1101 read status register
-*INPUT        :addr: register address
-*OUTPUT       :status value
-****************************************************************/
-uint8_t CC1101::SpiReadStatus(uint8_t addr) {
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(addr | READ_BURST);
-    uint8_t value = SpiTransfer(0);
-    gpio_write(ss_pin, true);
-
-    return value;
-}
-
-/****************************************************************
-*FUNCTION NAME:Reset
-*FUNCTION     :CC1101 reset //details refer datasheet of CC1101/CC1100//
-*INPUT        :none
-*OUTPUT       :none
-****************************************************************/
-void CC1101::Reset(void) {
-    gpio_write(ss_pin, false);
-    delay(1);
-    gpio_write(ss_pin, true);
-    delay(1);
-    gpio_write(ss_pin, false);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    last_status = SpiTransfer(CC1101_SRES);
-    while(gpio_read(this->miso_pin_record))
-        ;
-    gpio_write(ss_pin, true);
-}
-
-bool CC1101::SpiSetRegValue(uint8_t reg, uint8_t value, uint8_t msb, uint8_t lsb) {
-    if((msb > 7) || (lsb > 7) || (lsb > msb)) {
-        return false;
-    }
-
-    uint8_t current_value = SpiReadReg(reg);
-    uint8_t mask = ~((0b11111111 << (msb + 1)) | (0b11111111 >> (8 - lsb)));
-    uint8_t new_value = (current_value & ~mask) | (value & mask);
-    SpiWriteReg(reg, new_value);
-
-    return true;
-}
-
-/****************************************************************
-*FUNCTION NAME:Init
-*FUNCTION     :CC1101 initialization
-*INPUT        :none
-*OUTPUT       :none
-****************************************************************/
-uint8_t CC1101::Init(void) {
-#ifdef CC1101_DEBUG
-    printf("Init SPI...\r\n");
-#endif
-    SpiInit(); //spi initialization
-    gpio_write(ss_pin, true);
-// gpio_write(SCK_PIN, true);
-// gpio_write(MOSI_PIN, false);
-#ifdef CC1101_DEBUG
-    printf("Reset CC1101...\r\n");
-#endif
-    Reset(); // CC1101 reset
-
-    osDelay(150);
-
-    uint8_t partnum __attribute__((unused));
-    uint8_t version;
-    partnum = SpiReadStatus(CC1101_PARTNUM);
-    version = SpiReadStatus(CC1101_VERSION);
-
-#ifdef CC1101_DEBUG
-
-    printf("Partnum:0x%02X, Version:0x%02X\n", partnum, version);
-#endif
-
-#ifdef CC1101_DEBUG
-    printf("Init CC1101...");
-#endif
-    // RegConfigSettings(); //CC1101 register config
-
-#ifdef CC1101_DEBUG
-    printf("Done!\r\n");
-#endif
-
-    return version;
-}
-/****************************************************************
-*FUNCTION NAME:SetMod
-*FUNCTION     :CC1101 modulation type
-*INPUT        :byte mode
-*OUTPUT       :none
-****************************************************************/
-void CC1101::SetMod(uint8_t mode) {
-    SpiWriteReg(CC1101_MDMCFG2, mode); //no sync/preamble; ASK/OOK only support up to -1dbm
-    if((mode | 0x30) == ASK) {
-        SpiWriteReg(CC1101_FREND0, 0x11); //use first up to PATABLE(0)
-        uint8_t PaTabel[8] = {0x00, POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-        SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
-    } else {
-        SpiWriteReg(CC1101_FREND0, 0x10); //use first up to PATABLE(0)
-        uint8_t PaTabel[8] = {POWER, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
-        SpiWriteBurstReg(CC1101_PATABLE, PaTabel, 8); //CC1101 PATABLE config
-    }
-
-#ifdef CC1101_DEBUG
-    switch(mode | 0x30) {
-    case GFSK: {
-        printf("CC1101 Modulation: GFSK");
-        break;
-    }
-    case MSK: {
-        printf("CC1101 Modulation: MSK");
-        break;
-    }
-    case ASK: {
-        printf("CC1101 Modulation: ASK/OOK");
-        break;
-    }
-    case FSK2: {
-        printf("CC1101 Modulation: 2-FSK");
-        break;
-    }
-    case FSK4: {
-        printf("CC1101 Modulation: 4-FSK");
-        break;
-    }
-    default: //default to GFSK
-    {
-        printf("Modulation mode not supported");
-        break;
-    }
-    }
-
-    printf("\r\n");
-#endif
-}
-/****************************************************************
-*FUNCTION NAME:RegConfigSettings
-*FUNCTION     :CC1101 register config //details refer datasheet of CC1101/CC1100//
-*INPUT        :none
-*OUTPUT       :none
-****************************************************************/
-void CC1101::RegConfigSettings(void) {
-    SpiWriteReg(CC1101_FSCTRL1, 0x06); //IF frequency
-    SpiWriteReg(CC1101_FSCTRL0, 0x00); //frequency offset before synthesizer
-
-    SpiWriteReg(CC1101_MDMCFG4, 0xCC); // RX filter bandwidth 100k(0xcc)
-    SpiWriteReg(
-        CC1101_MDMCFG3, 0x43); //datarate config 512kBaud  for the purpose of fast rssi measurement
-    SpiWriteReg(CC1101_MDMCFG1, 0x21); //FEC preamble etc. last 2 bits for channel spacing
-    SpiWriteReg(CC1101_MDMCFG0, 0xF8); //100khz channel spacing
-    //CC1101_CHANNR moved to SetChannel func
-
-    //SpiWriteReg(CC1101_DEVIATN,  0x47);
-    SpiWriteReg(
-        CC1101_MCSM0, 0x18); // calibrate when going from IDLE to RX or TX ; 149 - 155 μs timeout
-    SpiWriteReg(CC1101_FOCCFG, 0x16); //frequency compensation
-    //SpiWriteReg(CC1101_BSCFG,    0x1C);   //bit synchronization config
-    SpiWriteReg(CC1101_AGCCTRL2, 0x43);
-    SpiWriteReg(CC1101_AGCCTRL1, 0x49);
-    SpiWriteReg(CC1101_AGCCTRL0, 0x91);
-    //freq synthesizer calibration
-    SpiWriteReg(CC1101_FSCAL3, 0xEA);
-    SpiWriteReg(CC1101_FSCAL2, 0x2A);
-    SpiWriteReg(CC1101_FSCAL1, 0x00);
-    SpiWriteReg(CC1101_FSCAL0, 0x1F);
-    SpiWriteReg(CC1101_TEST2, 0x81);
-    SpiWriteReg(CC1101_TEST1, 0x35);
-    SpiWriteReg(CC1101_TEST0, 0x0B); //should be 0x0B for lower than 430.6MHz and 0x09 for higher
-
-    //SpiWriteReg(CC1101_FREND1,   0x56);
-
-    //SpiWriteReg(CC1101_IOCFG2,   0x0B);   //serial clock.synchronous to the data in synchronous serial mode
-    //SpiWriteReg(CC1101_IOCFG0,   0x06);   //asserts when sync word has been sent/received, and de-asserts at the end of the packet
-    SpiWriteReg(CC1101_IOCFG2, 0x0D); //data output pin for asynchronous mode
-    SpiWriteReg(
-        CC1101_IOCFG0,
-        0x2E); //High impedance (3-state), GDO0 configed as data input for asynchronous mode
-    //SpiWriteReg(CC1101_PKTCTRL0, 0x05);       //whitening off;CRC Enable;variable length packets, packet length configured by the first byte after sync word
-    SpiWriteReg(
-        CC1101_PKTCTRL0, 0x33); //whitening off; asynchronous serial mode; CRC diable;reserved
-    //SpiWriteReg(CC1101_PKTLEN,   0x3D);   //61 bytes max length
-    SpiWriteReg(
-        CC1101_FIFOTHR,
-        0x47); //Adc_retention enabled for RX filter bandwidth less than 325KHz; defalut fifo threthold.
-}
-/****************************************************************
- *FUNCTION NAME:SetFreq
- *FUNCTION     :SetFreq
- *INPUT        :Freq2, Freq1, Freq0
- *OUTPUT       :none
- ****************************************************************/
-void CC1101::SetFreq(uint8_t freq2, uint8_t freq1, uint8_t freq0) {
-    SpiWriteReg(CC1101_FREQ2, freq2);
-    SpiWriteReg(CC1101_FREQ1, freq1);
-    SpiWriteReg(CC1101_FREQ0, freq0);
-}
-/****************************************************************
- *FUNCTION NAME:SetChannel
- *FUNCTION     :SetChannel
- *INPUT        :int channel
- *OUTPUT       :none
- ****************************************************************/
-void CC1101::SetChannel(int channel) {
-#ifdef CC1101_DEBUG
-    printf("Set CC1101 channel to: %d \n", channel);
-#endif
-    SpiWriteReg(CC1101_CHANNR, (uint8_t)channel); //related to channel numbers
-}
-/****************************************************************
- *FUNCTION NAME:SetReceive
- *FUNCTION     :SetReceive
- *INPUT        :none
- *OUTPUT       :none
- ****************************************************************/
-void CC1101::SetReceive(void) {
-    SpiStrobe(CC1101_SRX);
-    while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_RX) {
-        // delay(1);
-        // printf("wait status\r\n");
-    }
-}
-/****************************************************************
- *FUNCTION NAME:SetTransmit
- *FUNCTION     :
- *INPUT        :none
- *OUTPUT       :none
- ****************************************************************/
-void CC1101::SetTransmit(void) {
-    SpiStrobe(CC1101_STX);
-    while(SpiReadStatus(CC1101_MARCSTATE) ^ CC1101_STATUS_TX)
-        ;
-}
-//cc1101 cc1101;
-
-bool CC1101::setRxBandwidth(float bandwidth) {
-    if(bandwidth < 58.0 || bandwidth > 821.0) return false;
-
-    // set mode to standby
-    SpiStrobe(CC1101_SIDLE);
-
-    // calculate exponent and mantissa values
-    for(int8_t e = 3; e >= 0; e--) {
-        for(int8_t m = 3; m >= 0; m--) {
-            float point = (F_OSC) / (8 * (m + 4) * ((uint32_t)1 << e));
-            if(fabs((bandwidth * 1000.0) - point) <= 1000) {
-                // set Rx channel filter bandwidth
-                SpiSetRegValue(CC1101_MDMCFG4, (e << 6) | (m << 4), 7, 4);
-
-                return true;
-            }
-        }
-    }
-
-    return false;
-}
-
-static void getExpMant(
-    float target,
-    uint16_t mantOffset,
-    uint8_t divExp,
-    uint8_t expMax,
-    uint8_t& exp,
-    uint8_t& mant) {
-    // get table origin point (exp = 0, mant = 0)
-    float origin = (mantOffset * F_OSC) / ((uint32_t)1 << divExp);
-
-    // iterate over possible exponent values
-    for(int8_t e = expMax; e >= 0; e--) {
-        // get table column start value (exp = e, mant = 0);
-        float intervalStart = ((uint32_t)1 << e) * origin;
-
-        // check if target value is in this column
-        if(target >= intervalStart) {
-            // save exponent value
-            exp = e;
-
-            // calculate size of step between table rows
-            float stepSize = intervalStart / (float)mantOffset;
-
-            // get target point position (exp = e, mant = m)
-            mant = ((target - intervalStart) / stepSize);
-
-            // we only need the first match, terminate
-            return;
-        }
-    }
-}
-
-bool CC1101::setBitRate(float bitrate) {
-    if(bitrate < 0.6 || bitrate > 500.0) return false;
-
-    // set mode to standby
-    SpiStrobe(CC1101_SIDLE);
-
-    // calculate exponent and mantissa values
-    uint8_t e = 0;
-    uint8_t m = 0;
-    getExpMant(bitrate * 1000.0, 256, 28, 14, e, m);
-
-    // set bit rate value
-    SpiSetRegValue(CC1101_MDMCFG4, e, 3, 0);
-    SpiSetRegValue(CC1101_MDMCFG3, m, 7, 0);
-
-    return true;
-}

+ 0 - 174
applications/cc1101-workaround/cc1101.h

@@ -1,174 +0,0 @@
-#pragma once
-
-#include <furi.h>
-
-#define F_OSC 26e6
-
-/*******************************debug mode*************************************/
-// #define     CC1101_DEBUG 1
-
-//******************************CC1101 defines ********************************
-//******************************config registers  *****************************
-#define CC1101_IOCFG2 0x00 //GDO2 output pin configration
-#define CC1101_IOCFG1 0x01 // GDO1 output pin configuration
-#define CC1101_IOCFG0 0x02 // GDO0 output pin configuration
-#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO thresholds
-#define CC1101_SYNC1 0x04 // Sync word, high INT8U
-#define CC1101_SYNC0 0x05 // Sync word, low INT8U
-#define CC1101_PKTLEN 0x06 // Packet length
-#define CC1101_PKTCTRL1 0x07 // Packet automation control
-#define CC1101_PKTCTRL0 0x08 // Packet automation control
-#define CC1101_ADDR 0x09 // Device address
-#define CC1101_CHANNR 0x0A // Channel number
-#define CC1101_FSCTRL1 0x0B // Frequency synthesizer control
-#define CC1101_FSCTRL0 0x0C // Frequency synthesizer control
-#define CC1101_FREQ2 0x0D // Frequency control word, high INT8U
-#define CC1101_FREQ1 0x0E // Frequency control word, middle INT8U
-#define CC1101_FREQ0 0x0F // Frequency control word, low INT8U
-#define CC1101_MDMCFG4 0x10 // Modem configuration
-#define CC1101_MDMCFG3 0x11 // Modem configuration
-#define CC1101_MDMCFG2 0x12 // Modem configuration
-#define CC1101_MDMCFG1 0x13 // Modem configuration
-#define CC1101_MDMCFG0 0x14 // Modem configuration
-#define CC1101_DEVIATN 0x15 // Modem deviation setting
-#define CC1101_MCSM2 0x16 // Main Radio Control State Machine configuration
-#define CC1101_MCSM1 0x17 // Main Radio Control State Machine configuration
-#define CC1101_MCSM0 0x18 // Main Radio Control State Machine configuration
-#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation configuration
-#define CC1101_BSCFG 0x1A // Bit Synchronization configuration
-#define CC1101_AGCCTRL2 0x1B // AGC control
-#define CC1101_AGCCTRL1 0x1C // AGC control
-#define CC1101_AGCCTRL0 0x1D // AGC control
-#define CC1101_WOREVT1 0x1E // High INT8U Event 0 timeout
-#define CC1101_WOREVT0 0x1F // Low INT8U Event 0 timeout
-#define CC1101_WORCTRL 0x20 // Wake On Radio control
-#define CC1101_FREND1 0x21 // Front end RX configuration
-#define CC1101_FREND0 0x22 // Front end TX configuration
-#define CC1101_FSCAL3 0x23 // Frequency synthesizer calibration
-#define CC1101_FSCAL2 0x24 // Frequency synthesizer calibration
-#define CC1101_FSCAL1 0x25 // Frequency synthesizer calibration
-#define CC1101_FSCAL0 0x26 // Frequency synthesizer calibration
-#define CC1101_RCCTRL1 0x27 // RC oscillator configuration
-#define CC1101_RCCTRL0 0x28 // RC oscillator configuration
-#define CC1101_FSTEST 0x29 // Frequency synthesizer calibration control
-#define CC1101_PTEST 0x2A // Production test
-#define CC1101_AGCTEST 0x2B // AGC test
-#define CC1101_TEST2 0x2C // Various test settings
-#define CC1101_TEST1 0x2D // Various test settings
-#define CC1101_TEST0 0x2E // Various test settings
-
-//*********************CC1101 Strobe commands  *********************************
-#define CC1101_SRES 0x30 // Reset chip.
-
-// Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1).
-// If in RX/TX: Go to a wait state where only the synthesizer is
-// running (for quick RX / TX turnaround).
-#define CC1101_SFSTXON 0x31
-
-#define CC1101_SXOFF 0x32 // Turn off crystal oscillator.
-
-// Calibrate frequency synthesizer and turn it off
-// (enables quick start).
-#define CC1101_SCAL 0x33
-
-// Enable RX. Perform calibration first if coming from IDLE and
-// MCSM0.FS_AUTOCAL=1.
-#define CC1101_SRX 0x34
-
-// In IDLE state: Enable TX. Perform calibration first if
-// MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled:
-// Only go to TX if channel is clear.
-#define CC1101_STX 0x35
-
-// Exit RX / TX, turn off frequency synthesizer and exit
-// Wake-On-Radio mode if applicable.
-#define CC1101_SIDLE 0x36
-
-#define CC1101_SAFC 0x37 // Perform AFC adjustment of the frequency synthesizer
-#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio)
-#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high.
-#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer.
-#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer.
-#define CC1101_SWORRST 0x3C // Reset real time clock.
-
-// No operation. May be used to pad strobe commands to two
-// INT8Us for simpler software.
-#define CC1101_SNOP 0x3D
-
-//**************************CC1101 STATUS REGSITER ****************************
-//use burst read to access
-#define CC1101_PARTNUM 0x30
-#define CC1101_VERSION 0x31
-#define CC1101_FREQEST 0x32
-#define CC1101_LQI 0x33
-#define CC1101_RSSI 0x34
-#define CC1101_MARCSTATE 0x35
-#define CC1101_WORTIME1 0x36
-#define CC1101_WORTIME0 0x37
-#define CC1101_PKTSTATUS 0x38
-#define CC1101_VCO_VC_DAC 0x39
-#define CC1101_TXBYTES 0x3A
-#define CC1101_RXBYTES 0x3B
-#define CC1101_RCCTRL1_STATUS 0x3C
-#define CC1101_RCCTRL_STATUS 0x3D
-/****************************cc1101 status ***********************************/
-#define CC1101_STATUS_RX 0x0D
-#define CC1101_STATUS_TX 0x13
-
-//***********************CC1101 PATABLE,TXFIFO,RXFIFO**************************
-#define CC1101_PATABLE 0x3E
-#define CC1101_TXFIFO 0x3F
-#define CC1101_RXFIFO 0x3F
-
-//******************************* pins ****************************************
-// #define SCK_PIN   13
-// #define MISO_PIN  12
-// #define MOSI_PIN  11
-// #define SS_PIN    10
-// #define GDO0	8	//pin assignment
-// #define GDO2	9
-//*****************************CC1101 Config**********************************
-//no  pa ramping, output power to 10dBm
-#define POWER 0xC0 //output power to maximum
-//modulation
-#define FSK2 0x00
-#define GFSK 0x10
-#define ASK 0x30
-#define FSK4 0x40
-#define MSK 0x70
-//******************************** class **************************************//
-class CC1101 {
-private:
-    GpioPin* ss_pin;
-    GpioPin miso_pin;
-    GpioPin* miso_pin_record;
-    GpioPin* gdo0_pin;
-    GpioPin* gdo2_pin;
-
-private:
-    void SpiMode(uint8_t config);
-    uint8_t SpiTransfer(uint8_t value);
-    void Reset(void);
-    void SpiWriteBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num);
-    uint8_t SpiReadReg(uint8_t addr);
-    void RegConfigSettings(void);
-
-public:
-    CC1101(GpioPin* ss_pin);
-
-    void SpiWriteReg(uint8_t addr, uint8_t value);
-    bool SpiSetRegValue(uint8_t reg, uint8_t value, uint8_t msb, uint8_t lsb);
-    void SpiReadBurstReg(uint8_t addr, uint8_t* buffer, uint8_t num);
-    void SpiInit(void);
-    void SpiEnd(void);
-    void SetMod(uint8_t mode);
-    void SetFreq(uint8_t Freq2, uint8_t Freq1, uint8_t Freq0);
-    uint8_t Init(void);
-    void SpiStrobe(uint8_t strobe);
-    uint8_t SpiReadStatus(uint8_t addr);
-    void SetReceive(void);
-    void SetTransmit(void);
-    void SetChannel(int channel);
-    bool setRxBandwidth(float bandwidth);
-    bool setBitRate(float bitrate);
-};

+ 1 - 0
applications/gui/canvas.c

@@ -34,6 +34,7 @@ void canvas_free(Canvas* canvas) {
 
 void canvas_reset(Canvas* canvas) {
     furi_assert(canvas);
+    canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontSecondary);
 }

+ 15 - 71
applications/gui/u8g2_periphery.c

@@ -1,122 +1,66 @@
 #include "u8g2/u8g2.h"
-#include <furi.h>
 #include <api-hal.h>
+#include <furi.h>
 
-#include <main.h>
-
-extern SPI_HandleTypeDef SPI_D;
-
-// TODO: fix log
-#ifdef DEBUG
-#undef DEBUG
-#endif
+static ApiHalSpiDevice* u8g2_periphery_display = NULL;
 
 uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
     switch(msg) {
-    //Initialize SPI peripheral
     case U8X8_MSG_GPIO_AND_DELAY_INIT:
         /* HAL initialization contains all what we need so we can skip this part. */
         break;
 
-    //Function which implements a delay, arg_int contains the amount of ms
     case U8X8_MSG_DELAY_MILLI:
         osDelay(arg_int);
         break;
 
-    //Function which delays 10us
     case U8X8_MSG_DELAY_10MICRO:
         delay_us(10);
         break;
 
-    //Function which delays 100ns
     case U8X8_MSG_DELAY_100NANO:
         asm("nop");
         break;
 
-    // Function to define the logic level of the RESET line
     case U8X8_MSG_GPIO_RESET:
-#ifdef DEBUG
-        printf("[u8g2] rst %d\n", arg_int);
-#endif
-
-        // TODO change it to FuriRecord pin
-        HAL_GPIO_WritePin(
-            DISPLAY_RST_GPIO_Port, DISPLAY_RST_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
+        hal_gpio_write(&gpio_display_rst, arg_int);
         break;
 
     default:
-#ifdef DEBUG
-        printf("[u8g2] unknown io %d\n", msg);
-#endif
-
-        return 0; //A message was received which is not implemented, return 0 to indicate an error
+        return 0;
     }
 
-    return 1; // command processed successfully.
+    return 1;
 }
 
 uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr) {
     switch(msg) {
     case U8X8_MSG_BYTE_SEND:
-#ifdef DEBUG
-        printf("[u8g2] send %d bytes %02X\n", arg_int, ((uint8_t*)arg_ptr)[0]);
-#endif
-
-        // TODO change it to FuriRecord SPI
-        HAL_SPI_Transmit(&SPI_D, (uint8_t*)arg_ptr, arg_int, 10000);
+        api_hal_spi_bus_tx(u8g2_periphery_display->bus, (uint8_t*)arg_ptr, arg_int, 10000);
         break;
 
     case U8X8_MSG_BYTE_SET_DC:
-#ifdef DEBUG
-        printf("[u8g2] dc %d\n", arg_int);
-#endif
-
-        // TODO change it to FuriRecord pin
-        HAL_GPIO_WritePin(
-            DISPLAY_DI_GPIO_Port, DISPLAY_DI_Pin, arg_int ? GPIO_PIN_SET : GPIO_PIN_RESET);
+        hal_gpio_write(&gpio_display_di, arg_int);
         break;
 
     case U8X8_MSG_BYTE_INIT:
-#ifdef DEBUG
-        printf("[u8g2] init\r\n");
-#endif
-
-        // TODO change it to FuriRecord pin
-        HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_RESET);
         break;
 
     case U8X8_MSG_BYTE_START_TRANSFER:
-#ifdef DEBUG
-        printf("[u8g2] start\r\n");
-#endif
-
-        // TODO: SPI manager
-        api_hal_spi_lock_device(&display_spi);
-
-        // TODO change it to FuriRecord pin
-        HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_RESET);
-        asm("nop");
+        furi_assert(u8g2_periphery_display == NULL);
+        u8g2_periphery_display =
+            (ApiHalSpiDevice*)api_hal_spi_device_get(ApiHalSpiDeviceIdDisplay);
+        hal_gpio_write(u8g2_periphery_display->chip_select, false);
         break;
 
     case U8X8_MSG_BYTE_END_TRANSFER:
-#ifdef DEBUG
-        printf("[u8g2] end\r\n");
-#endif
-
-        asm("nop");
-        // TODO change it to FuriRecord pin
-        HAL_GPIO_WritePin(DISPLAY_CS_GPIO_Port, DISPLAY_CS_Pin, GPIO_PIN_SET);
-
-        // TODO: SPI manager
-        api_hal_spi_unlock_device(&display_spi);
-
+        furi_assert(u8g2_periphery_display);
+        hal_gpio_write(u8g2_periphery_display->chip_select, true);
+        api_hal_spi_device_return(u8g2_periphery_display);
+        u8g2_periphery_display = NULL;
         break;
 
     default:
-#ifdef DEBUG
-        printf("[u8g2] unknown xfer %d\n", msg);
-#endif
-
         return 0;
     }
 

+ 3 - 3
applications/lf-rfid/lf-rfid.c

@@ -37,12 +37,12 @@ static void render_callback(Canvas* canvas, void* ctx) {
 
     canvas_draw_str(canvas, 2, 24, state->on ? "Reading" : "Emulating");
 
-    char buf[14];
+    char buf[30];
 
-    sprintf(buf, "%d kHz", (int)state->freq_khz);
+    snprintf(buf, sizeof(buf), "%d kHz", (int)state->freq_khz);
     canvas_draw_str(canvas, 2, 36, buf);
 
-    sprintf(buf, "%02d:%010ld", state->customer_id, state->em_data);
+    snprintf(buf, sizeof(buf), "%02d:%010ld", state->customer_id, state->em_data);
     canvas_draw_str(canvas, 2, 45, buf);
 
     release_mutex((ValueMutex*)ctx, state);

+ 1 - 1
applications/nfc/nfc_worker.c

@@ -244,7 +244,7 @@ ReturnCode nfc_worker_trx(
 }
 
 void nfc_worker_exchange(NfcWorker* nfc_worker, rfalNfcDevice* nfc_device) {
-    ReturnCode err;
+    ReturnCode err = ERR_NONE;
     uint8_t* rxData;
     uint16_t* rcvLen;
     uint8_t txBuf[100];

+ 1 - 1
applications/sd-filesystem/sd-filesystem.c

@@ -849,4 +849,4 @@ int32_t sd_filesystem(void* p) {
     }
 
     return 0;
-}
+}

+ 0 - 13
applications/subghz/scene/subghz-scene-generic.h

@@ -1,13 +0,0 @@
-#pragma once
-#include "../subghz-event.h"
-
-class SubghzApp;
-
-class SubghzScene {
-public:
-    virtual void on_enter(SubghzApp* app) = 0;
-    virtual bool on_event(SubghzApp* app, SubghzEvent* event) = 0;
-    virtual void on_exit(SubghzApp* app) = 0;
-
-private:
-};

+ 0 - 48
applications/subghz/scene/subghz-scene-spectrum-settings.cpp

@@ -1,48 +0,0 @@
-#include "subghz-scene-spectrum-settings.h"
-#include "../subghz-app.h"
-#include "../subghz-view-manager.h"
-#include "../subghz-event.h"
-#include <callback-connector.h>
-
-void SubghzSceneSpectrumSettings::on_enter(SubghzApp* app) {
-    SubghzAppViewManager* view_manager = app->get_view_manager();
-    SubghzViewSpectrumSettings* spectrum_settings = view_manager->get_spectrum_settings();
-
-    auto callback = cbc::obtain_connector(this, &SubghzSceneSpectrumSettings::ok_callback);
-    spectrum_settings->set_ok_callback(callback, app);
-    spectrum_settings->set_start_freq(433);
-
-    view_manager->switch_to(SubghzAppViewManager::ViewType::SpectrumSettings);
-}
-
-bool SubghzSceneSpectrumSettings::on_event(SubghzApp* app, SubghzEvent* event) {
-    bool consumed = false;
-
-    if(event->type == SubghzEvent::Type::NextScene) {
-        // save data
-        // uint32_t start_freq = app->get_view_manager()->get_spectrum_settings()->get_start_freq();
-        // app->get_spectrum_analyzer()->set_start_freq(start_freq);
-
-        // switch to next scene
-        // app->switch_to_next_scene(SubghzApp::Scene::SceneSpectrumAnalyze);
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void SubghzSceneSpectrumSettings::on_exit(SubghzApp* app) {
-    SubghzAppViewManager* view_manager = app->get_view_manager();
-    SubghzViewSpectrumSettings* spectrum_settings = view_manager->get_spectrum_settings();
-
-    spectrum_settings->set_ok_callback(nullptr, nullptr);
-    spectrum_settings->set_start_freq(0);
-}
-
-void SubghzSceneSpectrumSettings::ok_callback(void* context) {
-    SubghzApp* app = static_cast<SubghzApp*>(context);
-    SubghzEvent event;
-
-    event.type = SubghzEvent::Type::NextScene;
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 12
applications/subghz/scene/subghz-scene-spectrum-settings.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "subghz-scene-generic.h"
-
-class SubghzSceneSpectrumSettings : public SubghzScene {
-public:
-    void on_enter(SubghzApp* app) final;
-    bool on_event(SubghzApp* app, SubghzEvent* event) final;
-    void on_exit(SubghzApp* app) final;
-
-private:
-    void ok_callback(void* context);
-};

+ 0 - 67
applications/subghz/scene/subghz-scene-start.cpp

@@ -1,67 +0,0 @@
-#include "subghz-scene-start.h"
-#include "../subghz-app.h"
-#include "../subghz-view-manager.h"
-#include "../subghz-event.h"
-#include <callback-connector.h>
-
-typedef enum {
-    SubmenuIndexSpectrumAnalyzer,
-    SubmenuIndexFrequencyScanner,
-    SubmenuIndexSignalAnalyzer,
-    SubmenuIndexSignalTransmitter,
-    SubmenuIndexApplications,
-} SubmenuIndex;
-
-void SubghzSceneStart::on_enter(SubghzApp* app) {
-    SubghzAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-    auto callback = cbc::obtain_connector(this, &SubghzSceneStart::submenu_callback);
-
-    submenu_add_item(submenu, "Spectrum Analyzer", SubmenuIndexSpectrumAnalyzer, callback, app);
-    submenu_add_item(submenu, "Frequency Scanner", SubmenuIndexFrequencyScanner, callback, app);
-    submenu_add_item(submenu, "Signal Analyzer", SubmenuIndexSignalAnalyzer, callback, app);
-    submenu_add_item(submenu, "Signal Transmitter", SubmenuIndexSignalTransmitter, callback, app);
-    submenu_add_item(submenu, "Applications", SubmenuIndexApplications, callback, app);
-
-    view_manager->switch_to(SubghzAppViewManager::ViewType::Submenu);
-}
-
-bool SubghzSceneStart::on_event(SubghzApp* app, SubghzEvent* event) {
-    bool consumed = false;
-
-    if(event->type == SubghzEvent::Type::MenuSelected) {
-        switch(event->payload.menu_index) {
-        case SubmenuIndexSpectrumAnalyzer:
-            app->switch_to_next_scene(SubghzApp::Scene::SceneSpectrumSettings);
-            break;
-        case SubmenuIndexFrequencyScanner:
-            break;
-        case SubmenuIndexSignalAnalyzer:
-            break;
-        case SubmenuIndexSignalTransmitter:
-            break;
-        case SubmenuIndexApplications:
-            break;
-        }
-        consumed = true;
-    }
-
-    return consumed;
-}
-
-void SubghzSceneStart::on_exit(SubghzApp* app) {
-    SubghzAppViewManager* view_manager = app->get_view_manager();
-    Submenu* submenu = view_manager->get_submenu();
-
-    submenu_clean(submenu);
-}
-
-void SubghzSceneStart::submenu_callback(void* context, uint32_t index) {
-    SubghzApp* app = static_cast<SubghzApp*>(context);
-    SubghzEvent event;
-
-    event.type = SubghzEvent::Type::MenuSelected;
-    event.payload.menu_index = index;
-
-    app->get_view_manager()->send_event(&event);
-}

+ 0 - 12
applications/subghz/scene/subghz-scene-start.h

@@ -1,12 +0,0 @@
-#pragma once
-#include "subghz-scene-generic.h"
-
-class SubghzSceneStart : public SubghzScene {
-public:
-    void on_enter(SubghzApp* app) final;
-    bool on_event(SubghzApp* app, SubghzEvent* event) final;
-    void on_exit(SubghzApp* app) final;
-
-private:
-    void submenu_callback(void* context, uint32_t index);
-};

+ 0 - 90
applications/subghz/subghz-app.cpp

@@ -1,90 +0,0 @@
-#include "subghz-app.h"
-#include <api-hal-power.h>
-#include <stdarg.h>
-
-void SubghzApp::run(void) {
-    SubghzEvent event;
-    bool consumed;
-    bool exit = false;
-
-    scenes[current_scene]->on_enter(this);
-
-    while(!exit) {
-        view.receive_event(&event);
-
-        consumed = scenes[current_scene]->on_event(this, &event);
-
-        if(!consumed) {
-            if(event.type == SubghzEvent::Type::Back) {
-                exit = switch_to_previous_scene();
-            }
-        }
-    };
-
-    scenes[current_scene]->on_exit(this);
-}
-
-SubghzApp::SubghzApp() {
-    api_hal_power_insomnia_enter();
-}
-
-SubghzApp::~SubghzApp() {
-    api_hal_power_insomnia_exit();
-}
-
-SubghzAppViewManager* SubghzApp::get_view_manager() {
-    return &view;
-}
-
-void SubghzApp::switch_to_next_scene(Scene next_scene) {
-    previous_scenes_list.push_front(current_scene);
-
-    if(next_scene != Scene::SceneExit) {
-        scenes[current_scene]->on_exit(this);
-        current_scene = next_scene;
-        scenes[current_scene]->on_enter(this);
-    }
-}
-
-void SubghzApp::search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list) {
-    Scene previous_scene = Scene::SceneStart;
-    bool scene_found = false;
-
-    while(!scene_found) {
-        previous_scene = get_previous_scene();
-        for(Scene element : scenes_list) {
-            if(previous_scene == element || previous_scene == Scene::SceneStart) {
-                scene_found = true;
-                break;
-            }
-        }
-    }
-
-    scenes[current_scene]->on_exit(this);
-    current_scene = previous_scene;
-    scenes[current_scene]->on_enter(this);
-}
-
-bool SubghzApp::switch_to_previous_scene(uint8_t count) {
-    Scene previous_scene = Scene::SceneStart;
-
-    for(uint8_t i = 0; i < count; i++) {
-        previous_scene = get_previous_scene();
-        if(previous_scene == Scene::SceneExit) break;
-    }
-
-    if(previous_scene == Scene::SceneExit) {
-        return true;
-    } else {
-        scenes[current_scene]->on_exit(this);
-        current_scene = previous_scene;
-        scenes[current_scene]->on_enter(this);
-        return false;
-    }
-}
-
-SubghzApp::Scene SubghzApp::get_previous_scene() {
-    Scene scene = previous_scenes_list.front();
-    previous_scenes_list.pop_front();
-    return scene;
-}

+ 0 - 37
applications/subghz/subghz-app.h

@@ -1,37 +0,0 @@
-#pragma once
-#include <map>
-#include <list>
-#include "subghz-view-manager.h"
-
-#include "scene/subghz-scene-start.h"
-#include "scene/subghz-scene-spectrum-settings.h"
-
-class SubghzApp {
-public:
-    void run(void);
-
-    SubghzApp();
-    ~SubghzApp();
-
-    enum class Scene : uint8_t {
-        SceneExit,
-        SceneStart,
-        SceneSpectrumSettings,
-    };
-
-    SubghzAppViewManager* get_view_manager();
-    void switch_to_next_scene(Scene index);
-    void search_and_switch_to_previous_scene(std::initializer_list<Scene> scenes_list);
-    bool switch_to_previous_scene(uint8_t count = 1);
-    Scene get_previous_scene();
-
-private:
-    std::list<Scene> previous_scenes_list = {Scene::SceneExit};
-    Scene current_scene = Scene::SceneStart;
-    SubghzAppViewManager view;
-
-    std::map<Scene, SubghzScene*> scenes = {
-        {Scene::SceneStart, new SubghzSceneStart()},
-        {Scene::SceneSpectrumSettings, new SubghzSceneSpectrumSettings()},
-    };
-};

+ 0 - 21
applications/subghz/subghz-event.h

@@ -1,21 +0,0 @@
-#pragma once
-#include <stdint.h>
-
-class SubghzEvent {
-public:
-    // events enum
-    enum class Type : uint8_t {
-        Tick,
-        Back,
-        MenuSelected,
-        NextScene,
-    };
-
-    // payload
-    union {
-        uint32_t menu_index;
-    } payload;
-
-    // event type
-    Type type;
-};

+ 0 - 81
applications/subghz/subghz-view-manager.cpp

@@ -1,81 +0,0 @@
-#include "subghz-view-manager.h"
-#include "subghz-event.h"
-#include <callback-connector.h>
-
-SubghzAppViewManager::SubghzAppViewManager() {
-    event_queue = osMessageQueueNew(10, sizeof(SubghzEvent), NULL);
-
-    view_dispatcher = view_dispatcher_alloc();
-    auto callback = cbc::obtain_connector(this, &SubghzAppViewManager::previous_view_callback);
-
-    // allocate views
-    submenu = submenu_alloc();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(SubghzAppViewManager::ViewType::Submenu),
-        submenu_get_view(submenu));
-
-    spectrum_settings = new SubghzViewSpectrumSettings();
-    view_dispatcher_add_view(
-        view_dispatcher,
-        static_cast<uint32_t>(SubghzAppViewManager::ViewType::SpectrumSettings),
-        spectrum_settings->get_view());
-
-    gui = static_cast<Gui*>(furi_record_open("gui"));
-    view_dispatcher_attach_to_gui(view_dispatcher, gui, ViewDispatcherTypeFullscreen);
-
-    // set previous view callback for all views
-    view_set_previous_callback(submenu_get_view(submenu), callback);
-    view_set_previous_callback(spectrum_settings->get_view(), callback);
-}
-
-SubghzAppViewManager::~SubghzAppViewManager() {
-    // remove views
-    view_dispatcher_remove_view(
-        view_dispatcher, static_cast<uint32_t>(SubghzAppViewManager::ViewType::Submenu));
-    view_dispatcher_remove_view(
-        view_dispatcher, static_cast<uint32_t>(SubghzAppViewManager::ViewType::SpectrumSettings));
-
-    // free view modules
-    submenu_free(submenu);
-    free(spectrum_settings);
-
-    // free dispatcher
-    view_dispatcher_free(view_dispatcher);
-
-    // free event queue
-    osMessageQueueDelete(event_queue);
-}
-
-void SubghzAppViewManager::switch_to(ViewType type) {
-    view_dispatcher_switch_to_view(view_dispatcher, static_cast<uint32_t>(type));
-}
-
-Submenu* SubghzAppViewManager::get_submenu() {
-    return submenu;
-}
-
-SubghzViewSpectrumSettings* SubghzAppViewManager::get_spectrum_settings() {
-    return spectrum_settings;
-}
-
-void SubghzAppViewManager::receive_event(SubghzEvent* event) {
-    if(osMessageQueueGet(event_queue, event, NULL, 100) != osOK) {
-        event->type = SubghzEvent::Type::Tick;
-    }
-}
-
-void SubghzAppViewManager::send_event(SubghzEvent* event) {
-    osStatus_t result = osMessageQueuePut(event_queue, event, 0, 0);
-    furi_check(result == osOK);
-}
-
-uint32_t SubghzAppViewManager::previous_view_callback(void* context) {
-    if(event_queue != NULL) {
-        SubghzEvent event;
-        event.type = SubghzEvent::Type::Back;
-        send_event(&event);
-    }
-
-    return VIEW_IGNORE;
-}

+ 0 - 37
applications/subghz/subghz-view-manager.h

@@ -1,37 +0,0 @@
-#pragma once
-#include <furi.h>
-#include <gui/view_dispatcher.h>
-#include <gui/modules/submenu.h>
-#include "subghz-event.h"
-#include "view/subghz-view-spectrum-settings.h"
-
-class SubghzAppViewManager {
-public:
-    enum class ViewType : uint8_t {
-        Submenu,
-        SpectrumSettings,
-    };
-
-    osMessageQueueId_t event_queue;
-
-    SubghzAppViewManager();
-    ~SubghzAppViewManager();
-
-    void switch_to(ViewType type);
-
-    void receive_event(SubghzEvent* event);
-    void send_event(SubghzEvent* event);
-
-    Submenu* get_submenu();
-    SubghzViewSpectrumSettings* get_spectrum_settings();
-
-private:
-    ViewDispatcher* view_dispatcher;
-    Gui* gui;
-
-    uint32_t previous_view_callback(void* context);
-
-    // view elements
-    Submenu* submenu;
-    SubghzViewSpectrumSettings* spectrum_settings;
-};

+ 98 - 0
applications/subghz/subghz.c

@@ -0,0 +1,98 @@
+#include "subghz_i.h"
+
+osThreadId subghz_thread_id = NULL;
+
+void subghz_menu_callback(void* context, uint32_t index) {
+    furi_assert(context);
+
+    SubGhz* subghz = context;
+
+    if(index == 0) {
+        view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestBasic);
+    } else if(index == 1) {
+        view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewTestPacket);
+    }
+}
+
+uint32_t subghz_exit(void* context) {
+    osThreadResume(subghz_thread_id);
+    return VIEW_NONE;
+}
+
+SubGhz* subghz_alloc() {
+    SubGhz* subghz = furi_alloc(sizeof(SubGhz));
+
+    // Thread id
+    subghz_thread_id = osThreadGetId();
+
+    // GUI
+    subghz->gui = furi_record_open("gui");
+
+    // View Dispatcher
+    subghz->view_dispatcher = view_dispatcher_alloc();
+    view_dispatcher_attach_to_gui(
+        subghz->view_dispatcher, subghz->gui, ViewDispatcherTypeFullscreen);
+
+    // Menu
+    subghz->submenu = submenu_alloc();
+    submenu_add_item(subghz->submenu, "Basic Test", 0, subghz_menu_callback, subghz);
+    submenu_add_item(subghz->submenu, "Packet Test", 1, subghz_menu_callback, subghz);
+    View* submenu_view = submenu_get_view(subghz->submenu);
+    view_set_previous_callback(submenu_view, subghz_exit);
+    view_dispatcher_add_view(subghz->view_dispatcher, SubGhzViewMenu, submenu_view);
+
+    // Basic Test Module
+    subghz->subghz_test_basic = subghz_test_basic_alloc();
+    view_dispatcher_add_view(
+        subghz->view_dispatcher,
+        SubGhzViewTestBasic,
+        subghz_test_basic_get_view(subghz->subghz_test_basic));
+
+    // Packet Test
+    subghz->subghz_test_packet = subghz_test_packet_alloc();
+    view_dispatcher_add_view(
+        subghz->view_dispatcher,
+        SubGhzViewTestPacket,
+        subghz_test_packet_get_view(subghz->subghz_test_packet));
+
+    // Switch to menu
+    view_dispatcher_switch_to_view(subghz->view_dispatcher, SubGhzViewMenu);
+
+    return subghz;
+}
+
+void subghz_free(SubGhz* subghz) {
+    furi_assert(subghz);
+
+    // Packet Test
+    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTestPacket);
+    subghz_test_packet_free(subghz->subghz_test_packet);
+
+    // Basic Test
+    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewTestBasic);
+    subghz_test_basic_free(subghz->subghz_test_basic);
+
+    // Submenu
+    view_dispatcher_remove_view(subghz->view_dispatcher, SubGhzViewMenu);
+    submenu_free(subghz->submenu);
+
+    // View Dispatcher
+    view_dispatcher_free(subghz->view_dispatcher);
+
+    // GUI
+    furi_record_close("gui");
+    subghz->gui = NULL;
+
+    // The rest
+    free(subghz);
+}
+
+int32_t subghz_app(void* context) {
+    SubGhz* subghz = subghz_alloc();
+
+    osThreadSuspend(subghz_thread_id);
+
+    subghz_free(subghz);
+
+    return 0;
+}

+ 0 - 10
applications/subghz/subghz.cpp

@@ -1,10 +0,0 @@
-#include "subghz-app.h"
-
-// app enter function
-extern "C" int32_t app_subghz(void* p) {
-    SubghzApp* app = new SubghzApp();
-    app->run();
-    delete app;
-
-    return 255;
-}

+ 3 - 0
applications/subghz/subghz.h

@@ -0,0 +1,3 @@
+#pragma once
+
+typedef struct SubGhz SubGhz;

+ 44 - 0
applications/subghz/subghz_i.h

@@ -0,0 +1,44 @@
+#pragma once
+
+#include "subghz.h"
+#include "subghz_test_basic.h"
+#include "subghz_test_packet.h"
+
+#include <furi.h>
+#include <gui/gui.h>
+#include <gui/view_dispatcher.h>
+#include <gui/modules/submenu.h>
+
+static const uint32_t subghz_frequencies[] = {
+    301000000,
+    315000000,
+    346000000,
+    385000000,
+    433920000,
+    438900000,
+    463000000,
+    781000000,
+    868000000,
+    915000000,
+    925000000,
+};
+
+static const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t);
+
+struct SubGhz {
+    Gui* gui;
+
+    ViewDispatcher* view_dispatcher;
+
+    Submenu* submenu;
+
+    SubghzTestBasic* subghz_test_basic;
+
+    SubghzTestPacket* subghz_test_packet;
+};
+
+typedef enum {
+    SubGhzViewMenu,
+    SubGhzViewTestBasic,
+    SubGhzViewTestPacket,
+} SubGhzView;

+ 203 - 0
applications/subghz/subghz_test_basic.c

@@ -0,0 +1,203 @@
+#include "subghz_test_basic.h"
+#include "subghz_i.h"
+
+#include <math.h>
+#include <furi.h>
+#include <api-hal.h>
+#include <input/input.h>
+
+struct SubghzTestBasic {
+    View* view;
+    osTimerId timer;
+};
+
+typedef enum {
+    SubghzTestBasicModelStatusRx,
+    SubghzTestBasicModelStatusTx,
+} SubghzTestBasicModelStatus;
+
+typedef struct {
+    uint8_t frequency;
+    uint32_t real_frequency;
+    ApiHalSubGhzPath path;
+    float rssi;
+    SubghzTestBasicModelStatus status;
+} SubghzTestBasicModel;
+
+void subghz_test_basic_draw(Canvas* canvas, SubghzTestBasicModel* model) {
+    char buffer[64];
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 2, 12, "CC1101 Basic Test");
+
+    canvas_set_font(canvas, FontSecondary);
+    // Frequency
+    snprintf(
+        buffer,
+        sizeof(buffer),
+        "Freq: %03ld.%03ld.%03ld Hz",
+        model->real_frequency / 1000000 % 1000,
+        model->real_frequency / 1000 % 1000,
+        model->real_frequency % 1000);
+    canvas_draw_str(canvas, 2, 24, buffer);
+    // Path
+    char* path_name = "Unknown";
+    if(model->path == ApiHalSubGhzPathIsolate) {
+        path_name = "isolate";
+    } else if(model->path == ApiHalSubGhzPath1) {
+        path_name = "433MHz";
+    } else if(model->path == ApiHalSubGhzPath2) {
+        path_name = "315MHz";
+    } else if(model->path == ApiHalSubGhzPath3) {
+        path_name = "868MHz";
+    }
+    snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name);
+    canvas_draw_str(canvas, 2, 36, buffer);
+    if(model->status == SubghzTestBasicModelStatusRx) {
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "RSSI: %ld.%ld dBm",
+            (int32_t)(model->rssi),
+            (int32_t)fabs(model->rssi * 10) % 10);
+        canvas_draw_str(canvas, 2, 48, buffer);
+    } else {
+        canvas_draw_str(canvas, 2, 48, "TX");
+    }
+}
+
+bool subghz_test_basic_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    SubghzTestBasic* subghz_test_basic = context;
+
+    if(event->key == InputKeyBack) {
+        return false;
+    }
+
+    with_view_model(
+        subghz_test_basic->view, (SubghzTestBasicModel * model) {
+            osTimerStop(subghz_test_basic->timer);
+            api_hal_subghz_idle();
+
+            if(event->type == InputTypeShort) {
+                if(event->key == InputKeyLeft) {
+                    if(model->frequency > 0) model->frequency--;
+                } else if(event->key == InputKeyRight) {
+                    if(model->frequency < subghz_frequencies_count - 1) model->frequency++;
+                } else if(event->key == InputKeyDown) {
+                    if(model->path > 0) model->path--;
+                } else if(event->key == InputKeyUp) {
+                    if(model->path < ApiHalSubGhzPath3) model->path++;
+                }
+
+                model->real_frequency =
+                    api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]);
+                api_hal_subghz_set_path(model->path);
+            }
+
+            if(event->key == InputKeyOk) {
+                if(event->type == InputTypePress) {
+                    model->status = SubghzTestBasicModelStatusTx;
+                } else if(event->type == InputTypeRelease) {
+                    model->status = SubghzTestBasicModelStatusRx;
+                }
+            }
+
+            if(model->status == SubghzTestBasicModelStatusRx) {
+                gpio_init(&cc1101_g0_gpio, GpioModeInput);
+                api_hal_subghz_rx();
+                osTimerStart(subghz_test_basic->timer, 1024 / 4);
+            } else {
+                gpio_init(&cc1101_g0_gpio, GpioModeOutputPushPull);
+                gpio_write(&cc1101_g0_gpio, false);
+                api_hal_subghz_tx();
+            }
+
+            return true;
+        });
+
+    return true;
+}
+
+void subghz_test_basic_enter(void* context) {
+    furi_assert(context);
+    SubghzTestBasic* subghz_test_basic = context;
+
+    api_hal_subghz_reset();
+    api_hal_subghz_load_preset(ApiHalSubGhzPresetOokAsync);
+
+    gpio_init(&cc1101_g0_gpio, GpioModeInput);
+
+    with_view_model(
+        subghz_test_basic->view, (SubghzTestBasicModel * model) {
+            model->frequency = 4; // 433
+            model->real_frequency =
+                api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]);
+            model->path = ApiHalSubGhzPathIsolate; // isolate
+            model->rssi = 0.0f;
+            model->status = SubghzTestBasicModelStatusRx;
+            return true;
+        });
+
+    api_hal_subghz_rx();
+
+    osTimerStart(subghz_test_basic->timer, 1024 / 4);
+}
+
+void subghz_test_basic_exit(void* context) {
+    furi_assert(context);
+    SubghzTestBasic* subghz_test_basic = context;
+
+    osTimerStop(subghz_test_basic->timer);
+
+    // Reinitialize IC to default state
+    api_hal_subghz_init();
+}
+
+void subghz_test_basic_rssi_timer_callback(void* context) {
+    furi_assert(context);
+    SubghzTestBasic* subghz_test_basic = context;
+
+    with_view_model(
+        subghz_test_basic->view, (SubghzTestBasicModel * model) {
+            model->rssi = api_hal_subghz_get_rssi();
+            return true;
+        });
+}
+
+uint32_t subghz_test_basic_back(void* context) {
+    return SubGhzViewMenu;
+}
+
+SubghzTestBasic* subghz_test_basic_alloc() {
+    SubghzTestBasic* subghz_test_basic = furi_alloc(sizeof(SubghzTestBasic));
+
+    // View allocation and configuration
+    subghz_test_basic->view = view_alloc();
+    view_allocate_model(
+        subghz_test_basic->view, ViewModelTypeLockFree, sizeof(SubghzTestBasicModel));
+    view_set_context(subghz_test_basic->view, subghz_test_basic);
+    view_set_draw_callback(subghz_test_basic->view, (ViewDrawCallback)subghz_test_basic_draw);
+    view_set_input_callback(subghz_test_basic->view, subghz_test_basic_input);
+    view_set_enter_callback(subghz_test_basic->view, subghz_test_basic_enter);
+    view_set_exit_callback(subghz_test_basic->view, subghz_test_basic_exit);
+    view_set_previous_callback(subghz_test_basic->view, subghz_test_basic_back);
+
+    subghz_test_basic->timer = osTimerNew(
+        subghz_test_basic_rssi_timer_callback, osTimerPeriodic, subghz_test_basic, NULL);
+
+    return subghz_test_basic;
+}
+
+void subghz_test_basic_free(SubghzTestBasic* subghz_test_basic) {
+    furi_assert(subghz_test_basic);
+    osTimerDelete(subghz_test_basic->timer);
+    view_free(subghz_test_basic->view);
+    free(subghz_test_basic);
+}
+
+View* subghz_test_basic_get_view(SubghzTestBasic* subghz_test_basic) {
+    furi_assert(subghz_test_basic);
+    return subghz_test_basic->view;
+}

+ 11 - 0
applications/subghz/subghz_test_basic.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct SubghzTestBasic SubghzTestBasic;
+
+SubghzTestBasic* subghz_test_basic_alloc();
+
+void subghz_test_basic_free(SubghzTestBasic* subghz_test_basic);
+
+View* subghz_test_basic_get_view(SubghzTestBasic* subghz_test_basic);

+ 210 - 0
applications/subghz/subghz_test_packet.c

@@ -0,0 +1,210 @@
+#include "subghz_test_packet.h"
+#include "subghz_i.h"
+
+#include <math.h>
+#include <furi.h>
+#include <api-hal.h>
+#include <input/input.h>
+
+static const uint8_t subghz_test_packet_data[] = {
+    0x30, // 48bytes to transmit
+    'F',  'L', 'I', 'P', 'P', 'E', 'R', ' ', 'T', 'E', 'S', 'T', ' ', 'P', 'A', 'C',
+    'K',  'E', 'T', ' ', 'D', 'A', 'T', 'A', ' ', 'A', 'N', 'D', ' ', 'P', 'A', 'D',
+    'F',  'L', 'I', 'P', 'P', 'E', 'R', ' ', 'T', 'E', 'S', 'T', ' ', 'P', 'A', 'C',
+
+};
+
+struct SubghzTestPacket {
+    View* view;
+    osTimerId timer;
+};
+
+typedef enum {
+    SubghzTestPacketModelStatusRx,
+    SubghzTestPacketModelStatusTx,
+} SubghzTestPacketModelStatus;
+
+typedef struct {
+    uint8_t frequency;
+    uint32_t real_frequency;
+    ApiHalSubGhzPath path;
+    float rssi;
+    SubghzTestPacketModelStatus status;
+} SubghzTestPacketModel;
+
+void subghz_test_packet_draw(Canvas* canvas, SubghzTestPacketModel* model) {
+    char buffer[64];
+
+    canvas_set_color(canvas, ColorBlack);
+    canvas_set_font(canvas, FontPrimary);
+    canvas_draw_str(canvas, 2, 12, "CC1101 Packet Test");
+
+    canvas_set_font(canvas, FontSecondary);
+    // Frequency
+    snprintf(
+        buffer,
+        sizeof(buffer),
+        "Freq: %03ld.%03ld.%03ld Hz",
+        model->real_frequency / 1000000 % 1000,
+        model->real_frequency / 1000 % 1000,
+        model->real_frequency % 1000);
+    canvas_draw_str(canvas, 2, 24, buffer);
+    // Path
+    char* path_name = "Unknown";
+    if(model->path == ApiHalSubGhzPathIsolate) {
+        path_name = "isolate";
+    } else if(model->path == ApiHalSubGhzPath1) {
+        path_name = "433MHz";
+    } else if(model->path == ApiHalSubGhzPath2) {
+        path_name = "315MHz";
+    } else if(model->path == ApiHalSubGhzPath3) {
+        path_name = "868MHz";
+    }
+    snprintf(buffer, sizeof(buffer), "Path: %d - %s", model->path, path_name);
+    canvas_draw_str(canvas, 2, 36, buffer);
+    if(model->status == SubghzTestPacketModelStatusRx) {
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "RSSI: %ld.%ld dBm",
+            (int32_t)(model->rssi),
+            (int32_t)fabs(model->rssi * 10) % 10);
+        canvas_draw_str(canvas, 2, 48, buffer);
+    } else {
+        canvas_draw_str(canvas, 2, 48, "TX");
+    }
+}
+
+bool subghz_test_packet_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    SubghzTestPacket* subghz_test_packet = context;
+
+    if(event->key == InputKeyBack) {
+        return false;
+    }
+
+    with_view_model(
+        subghz_test_packet->view, (SubghzTestPacketModel * model) {
+            osTimerStop(subghz_test_packet->timer);
+            api_hal_subghz_idle();
+
+            if(event->type == InputTypeShort) {
+                if(event->key == InputKeyLeft) {
+                    if(model->frequency > 0) model->frequency--;
+                } else if(event->key == InputKeyRight) {
+                    if(model->frequency < subghz_frequencies_count - 1) model->frequency++;
+                } else if(event->key == InputKeyDown) {
+                    if(model->path > 0) model->path--;
+                } else if(event->key == InputKeyUp) {
+                    if(model->path < ApiHalSubGhzPath3) model->path++;
+                }
+
+                model->real_frequency =
+                    api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]);
+                api_hal_subghz_set_path(model->path);
+            }
+
+            if(event->key == InputKeyOk) {
+                if(event->type == InputTypePress) {
+                    model->status = SubghzTestPacketModelStatusTx;
+                } else if(event->type == InputTypeRelease) {
+                    model->status = SubghzTestPacketModelStatusRx;
+                }
+            }
+
+            if(model->status == SubghzTestPacketModelStatusRx) {
+                api_hal_subghz_rx();
+                osTimerStart(subghz_test_packet->timer, 1024 / 4);
+            } else {
+                api_hal_subghz_write_packet(
+                    subghz_test_packet_data, sizeof(subghz_test_packet_data));
+                api_hal_subghz_tx();
+            }
+
+            return true;
+        });
+
+    return true;
+}
+
+void subghz_test_packet_enter(void* context) {
+    furi_assert(context);
+    SubghzTestPacket* subghz_test_packet = context;
+
+    api_hal_subghz_reset();
+    api_hal_subghz_load_preset(ApiHalSubGhzPreset2FskPacket);
+
+    gpio_init(&cc1101_g0_gpio, GpioModeInput);
+
+    with_view_model(
+        subghz_test_packet->view, (SubghzTestPacketModel * model) {
+            model->frequency = 4; // 433
+            model->real_frequency =
+                api_hal_subghz_set_frequency(subghz_frequencies[model->frequency]);
+            model->path = ApiHalSubGhzPathIsolate; // isolate
+            model->rssi = 0.0f;
+            model->status = SubghzTestPacketModelStatusRx;
+            return true;
+        });
+
+    api_hal_subghz_rx();
+
+    osTimerStart(subghz_test_packet->timer, 1024 / 4);
+}
+
+void subghz_test_packet_exit(void* context) {
+    furi_assert(context);
+    SubghzTestPacket* subghz_test_packet = context;
+
+    osTimerStop(subghz_test_packet->timer);
+
+    // Reinitialize IC to default state
+    api_hal_subghz_init();
+}
+
+void subghz_test_packet_rssi_timer_callback(void* context) {
+    furi_assert(context);
+    SubghzTestPacket* subghz_test_packet = context;
+
+    with_view_model(
+        subghz_test_packet->view, (SubghzTestPacketModel * model) {
+            model->rssi = api_hal_subghz_get_rssi();
+            return true;
+        });
+}
+
+uint32_t subghz_test_packet_back(void* context) {
+    return SubGhzViewMenu;
+}
+
+SubghzTestPacket* subghz_test_packet_alloc() {
+    SubghzTestPacket* subghz_test_packet = furi_alloc(sizeof(SubghzTestPacket));
+
+    // View allocation and configuration
+    subghz_test_packet->view = view_alloc();
+    view_allocate_model(
+        subghz_test_packet->view, ViewModelTypeLockFree, sizeof(SubghzTestPacketModel));
+    view_set_context(subghz_test_packet->view, subghz_test_packet);
+    view_set_draw_callback(subghz_test_packet->view, (ViewDrawCallback)subghz_test_packet_draw);
+    view_set_input_callback(subghz_test_packet->view, subghz_test_packet_input);
+    view_set_enter_callback(subghz_test_packet->view, subghz_test_packet_enter);
+    view_set_exit_callback(subghz_test_packet->view, subghz_test_packet_exit);
+    view_set_previous_callback(subghz_test_packet->view, subghz_test_packet_back);
+
+    subghz_test_packet->timer = osTimerNew(
+        subghz_test_packet_rssi_timer_callback, osTimerPeriodic, subghz_test_packet, NULL);
+
+    return subghz_test_packet;
+}
+
+void subghz_test_packet_free(SubghzTestPacket* subghz_test_packet) {
+    furi_assert(subghz_test_packet);
+    osTimerDelete(subghz_test_packet->timer);
+    view_free(subghz_test_packet->view);
+    free(subghz_test_packet);
+}
+
+View* subghz_test_packet_get_view(SubghzTestPacket* subghz_test_packet) {
+    furi_assert(subghz_test_packet);
+    return subghz_test_packet->view;
+}

+ 11 - 0
applications/subghz/subghz_test_packet.h

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <gui/view.h>
+
+typedef struct SubghzTestPacket SubghzTestPacket;
+
+SubghzTestPacket* subghz_test_packet_alloc();
+
+void subghz_test_packet_free(SubghzTestPacket* subghz_test_packet);
+
+View* subghz_test_packet_get_view(SubghzTestPacket* subghz_test_packet);

+ 0 - 95
applications/subghz/view/subghz-view-spectrum-settings.cpp

@@ -1,95 +0,0 @@
-#include "subghz-view-spectrum-settings.h"
-#include <callback-connector.h>
-
-struct SpectrumSettingsModel {
-    uint32_t start_freq;
-};
-
-/***************************************************************************************/
-
-static void draw_callback(Canvas* canvas, void* _model) {
-    SpectrumSettingsModel* model = static_cast<SpectrumSettingsModel*>(_model);
-    const uint8_t str_size = 64;
-    char str_buffer[str_size];
-
-    canvas_clear(canvas);
-    snprintf(str_buffer, str_size, "Start freq < %ld > MHz", model->start_freq);
-    canvas_draw_str_aligned(canvas, 64, 32, AlignCenter, AlignCenter, str_buffer);
-}
-
-static bool input_callback(InputEvent* event, void* context) {
-    SubghzViewSpectrumSettings* _this = static_cast<SubghzViewSpectrumSettings*>(context);
-
-    bool consumed = false;
-
-    // Process key presses only
-    if(event->type == InputTypeShort) {
-        if(event->key == InputKeyOk) {
-            _this->call_ok_callback();
-            consumed = true;
-        } else if(event->key == InputKeyLeft) {
-            with_view_model_cpp(_this->get_view(), SpectrumSettingsModel, model, {
-                model->start_freq--;
-                return true;
-            });
-            consumed = true;
-        } else if(event->key == InputKeyRight) {
-            with_view_model_cpp(_this->get_view(), SpectrumSettingsModel, model, {
-                model->start_freq++;
-                return true;
-            });
-            consumed = true;
-        }
-    }
-
-    return consumed;
-}
-
-/***************************************************************************************/
-
-View* SubghzViewSpectrumSettings::get_view() {
-    return view;
-}
-
-void SubghzViewSpectrumSettings::set_ok_callback(OkCallback callback, void* context) {
-    ok_callback = callback;
-    ok_callback_context = context;
-}
-
-void SubghzViewSpectrumSettings::call_ok_callback() {
-    if(ok_callback != nullptr) {
-        ok_callback(ok_callback_context);
-    }
-}
-
-void SubghzViewSpectrumSettings::set_start_freq(uint32_t start_freq) {
-    with_view_model_cpp(view, SpectrumSettingsModel, model, {
-        model->start_freq = start_freq;
-        return true;
-    });
-}
-
-uint32_t SubghzViewSpectrumSettings::get_start_freq() {
-    uint32_t result;
-
-    with_view_model_cpp(view, SpectrumSettingsModel, model, {
-        result = model->start_freq;
-        return false;
-    });
-
-    return result;
-}
-
-SubghzViewSpectrumSettings::SubghzViewSpectrumSettings() {
-    view = view_alloc();
-    view_set_context(view, this);
-    view_allocate_model(view, ViewModelTypeLocking, sizeof(SpectrumSettingsModel));
-
-    view_set_draw_callback(view, draw_callback);
-
-    view_set_input_callback(view, input_callback);
-}
-
-SubghzViewSpectrumSettings::~SubghzViewSpectrumSettings() {
-    view_free(view);
-}

+ 0 - 25
applications/subghz/view/subghz-view-spectrum-settings.h

@@ -1,25 +0,0 @@
-#include <gui/view.h>
-
-class SubghzViewSpectrumSettings {
-public:
-    SubghzViewSpectrumSettings();
-    ~SubghzViewSpectrumSettings();
-
-    View* get_view();
-
-    // ok callback methods
-    typedef void (*OkCallback)(void* context);
-    void set_ok_callback(OkCallback callback, void* context);
-    void call_ok_callback();
-
-    // model data getters/setters
-    void set_start_freq(uint32_t start_freq);
-    uint32_t get_start_freq();
-
-private:
-    View* view;
-
-    // ok callback data
-    OkCallback ok_callback = nullptr;
-    void* ok_callback_context = nullptr;
-};

+ 3 - 3
debug/stm32wbx.cfg

@@ -55,7 +55,7 @@ flash bank $_FLASHNAME stm32l4x 0 0 0 0 $_TARGETNAME
 #
 # Note that there is a pretty wide band where things are
 # more or less stable, see http://openocd.zylin.com/#/c/3366/
-adapter speed 8000
+adapter speed 24000
 
 adapter srst delay 100
 if {[using_jtag]} {
@@ -77,12 +77,12 @@ $_TARGETNAME configure -event reset-init {
     mmw 0x58004000 0x00000102 0  ;# FLASH_ACR |= PRFTBE | 2(Latency)
     mmw 0x58000000 0x00000091 0  ;# RCC_CR = MSI_ON | MSI Range 24 MHz
     # Boost JTAG frequency
-    adapter speed 8000
+    adapter speed 24000
 }
 
 $_TARGETNAME configure -event reset-start {
     # Reset clock is MSI (4 MHz)
-    adapter speed 8000
+    adapter speed 24000
 }
 
 $_TARGETNAME configure -event examine-end {

+ 86 - 11
firmware/targets/api-hal-include/api-hal-subghz.h

@@ -1,23 +1,98 @@
 #pragma once
 
+#include <stdbool.h>
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/** Sub-GHz band type */
+/** Radio Presets */
+typedef enum {
+    ApiHalSubGhzPresetOokAsync,     /** OOK, asynchronous */
+    ApiHalSubGhzPreset2FskPacket,   /** 2FSK, 115kBaud, variable packet length */
+} ApiHalSubGhzPreset;
+
+/**  Switchable Radio Paths */
 typedef enum {
-    RfBandIsolation = 0,
-    RfBand1 = 1,
-    RfBand2 = 2,
-    RfBand3 = 3
-} RfBand;
+    ApiHalSubGhzPathIsolate,        /** Isolate Radio from antenna */
+    ApiHalSubGhzPath1,              /** Path 1: SW1RF1-SW2RF2, LCLCL */
+    ApiHalSubGhzPath2,              /** Path 2: SW1RF2-SW2RF1, LCLCLCL */
+    ApiHalSubGhzPath3,              /** Path 3: SW1RF3-SW2RF3, LCLC */
+} ApiHalSubGhzPath;
+
+/** Initialize and switch to power save mode
+ * Used by internal API-HAL initalization routine
+ * Can be used to reinitialize device to safe state and send it to sleep
+ */
+void api_hal_subghz_init();
+
+/** Dump info to stdout */
+void api_hal_subghz_dump_state();
+
+/** Load registers from preset by preset name 
+ * @param preset to load
+ */
+void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset);
+
+/** Load registers
+ * @param register-value pairs array, terminated with {0,0}
+ */
+void api_hal_subghz_load_registers(const uint8_t data[][2]);
+
+/** Load PATABLE
+ * @param data, 8 uint8_t values
+ */
+void api_hal_subghz_load_patable(const uint8_t data[8]);
+
+/** Write packet to FIFO
+ * @param data, bytes array
+ * @param size, size
+ */
+void api_hal_subghz_write_packet(const uint8_t* data, uint8_t size);
 
-/**
- * Set Sub-GHz band
- * @param band RfBand
+/** Read packet from FIFO
+ * @param data, pointer
+ * @param size, size
  */
-void api_hal_rf_band_set(RfBand band);
+void api_hal_subghz_read_packet(uint8_t* data, uint8_t size);
+
+/** Shutdown
+ * Issue spwd command
+ * @warning registers content will be lost
+ */
+void api_hal_subghz_shutdown();
+
+/** Reset
+ * Issue reset command
+ * @warning registers content will be lost
+ */
+void api_hal_subghz_reset();
+
+/** Switch to Idle */
+void api_hal_subghz_idle();
+
+/** Switch to Recieve */
+void api_hal_subghz_rx();
+
+/** Switch to Transmit */
+void api_hal_subghz_tx();
+
+/** Get RSSI value in dBm */
+float api_hal_subghz_get_rssi();
+
+/** Set frequency
+ * @param frequency in herz
+ * @return real frequency in herz
+ */
+uint32_t api_hal_subghz_set_frequency(uint32_t value);
+
+/** Set path
+ * @param radio path to use
+ */
+void api_hal_subghz_set_path(ApiHalSubGhzPath path);
+
 
 #ifdef __cplusplus
 }
-#endif
+#endif

+ 77 - 133
firmware/targets/f4/Inc/main.h

@@ -1,153 +1,103 @@
-/* USER CODE BEGIN Header */
-/**
-  ******************************************************************************
-  * @file           : main.h
-  * @brief          : Header for main.c file.
-  *                   This file contains the common defines of the application.
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
-  ******************************************************************************
-  */
-/* USER CODE END Header */
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __MAIN_H
-#define __MAIN_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Includes ------------------------------------------------------------------*/
 #include "stm32wbxx_hal.h"
 
-/* Private includes ----------------------------------------------------------*/
-/* USER CODE BEGIN Includes */
-
-/* USER CODE END Includes */
-
-/* Exported types ------------------------------------------------------------*/
-/* USER CODE BEGIN ET */
-
-/* USER CODE END ET */
-
-/* Exported constants --------------------------------------------------------*/
-/* USER CODE BEGIN EC */
-
-/* USER CODE END EC */
-
-/* Exported macro ------------------------------------------------------------*/
-/* USER CODE BEGIN EM */
-
-/* USER CODE END EM */
-
-/* Exported functions prototypes ---------------------------------------------*/
 void Error_Handler(void);
 
-/* USER CODE BEGIN EFP */
-
-/* USER CODE END EFP */
-
-/* Private defines -----------------------------------------------------------*/
-#define BUTTON_BACK_Pin GPIO_PIN_13
-#define BUTTON_BACK_GPIO_Port GPIOC
 #define BUTTON_BACK_EXTI_IRQn EXTI15_10_IRQn
-#define QUARTZ_32MHZ_IN_Pin GPIO_PIN_14
-#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
-#define QUARTZ_32MHZ_OUT_Pin GPIO_PIN_15
-#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
-#define BUTTON_OK_Pin GPIO_PIN_3
-#define BUTTON_OK_GPIO_Port GPIOH
+#define BUTTON_BACK_GPIO_Port GPIOC
+#define BUTTON_BACK_Pin GPIO_PIN_13
+#define BUTTON_DOWN_EXTI_IRQn EXTI1_IRQn
+#define BUTTON_DOWN_GPIO_Port GPIOB
+#define BUTTON_DOWN_Pin GPIO_PIN_1
+#define BUTTON_LEFT_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_LEFT_GPIO_Port GPIOB
+#define BUTTON_LEFT_Pin GPIO_PIN_11
 #define BUTTON_OK_EXTI_IRQn EXTI3_IRQn
-#define SPEAKER_Pin GPIO_PIN_8
-#define SPEAKER_GPIO_Port GPIOB
-#define IR_TX_Pin GPIO_PIN_9
-#define IR_TX_GPIO_Port GPIOB
-#define PC0_Pin GPIO_PIN_0
-#define PC0_GPIO_Port GPIOC
-#define PC1_Pin GPIO_PIN_1
-#define PC1_GPIO_Port GPIOC
-#define PC3_Pin GPIO_PIN_3
-#define PC3_GPIO_Port GPIOC
-#define IR_RX_Pin GPIO_PIN_0
+#define BUTTON_OK_GPIO_Port GPIOH
+#define BUTTON_OK_Pin GPIO_PIN_3
+#define BUTTON_RIGHT_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_RIGHT_GPIO_Port GPIOB
+#define BUTTON_RIGHT_Pin GPIO_PIN_12
+#define BUTTON_UP_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_UP_GPIO_Port GPIOB
+#define BUTTON_UP_Pin GPIO_PIN_10
+#define CC1101_CS_GPIO_Port GPIOD
+#define CC1101_CS_Pin GPIO_PIN_0
+#define CC1101_G0_GPIO_Port GPIOC
+#define CC1101_G0_Pin GPIO_PIN_4
+#define DISPLAY_BACKLIGHT_GPIO_Port GPIOA
+#define DISPLAY_BACKLIGHT_Pin GPIO_PIN_15
+#define DISPLAY_CS_GPIO_Port GPIOC
+#define DISPLAY_CS_Pin GPIO_PIN_11
+#define DISPLAY_DI_GPIO_Port GPIOC
+#define DISPLAY_DI_Pin GPIO_PIN_6
+#define DISPLAY_RST_GPIO_Port GPIOB
+#define DISPLAY_RST_Pin GPIO_PIN_0
 #define IR_RX_GPIO_Port GPIOA
-#define LED_RED_Pin GPIO_PIN_1
-#define LED_RED_GPIO_Port GPIOA
-#define LED_GREEN_Pin GPIO_PIN_2
-#define LED_GREEN_GPIO_Port GPIOA
-#define LED_BLUE_Pin GPIO_PIN_3
+#define IR_RX_Pin GPIO_PIN_0
+#define IR_TX_GPIO_Port GPIOB
+#define IR_TX_Pin GPIO_PIN_9
 #define LED_BLUE_GPIO_Port GPIOA
-#define PA4_Pin GPIO_PIN_4
+#define LED_BLUE_Pin GPIO_PIN_3
+#define LED_GREEN_GPIO_Port GPIOA
+#define LED_GREEN_Pin GPIO_PIN_2
+#define LED_RED_GPIO_Port GPIOA
+#define LED_RED_Pin GPIO_PIN_1
+#define NFC_CS_GPIO_Port GPIOE
+#define NFC_CS_Pin GPIO_PIN_4
 #define PA4_GPIO_Port GPIOA
-#define SPI_R_SCK_Pin GPIO_PIN_5
-#define SPI_R_SCK_GPIO_Port GPIOA
-#define PA6_Pin GPIO_PIN_6
+#define PA4_Pin GPIO_PIN_4
 #define PA6_GPIO_Port GPIOA
-#define PA7_Pin GPIO_PIN_7
+#define PA6_Pin GPIO_PIN_6
 #define PA7_GPIO_Port GPIOA
-#define RFID_PULL_Pin GPIO_PIN_8
-#define RFID_PULL_GPIO_Port GPIOA
-#define RFID_PULL_EXTI_IRQn EXTI9_5_IRQn
-#define CC1101_G0_Pin GPIO_PIN_4
-#define CC1101_G0_GPIO_Port GPIOC
-#define RFID_RF_IN_Pin GPIO_PIN_5
-#define RFID_RF_IN_GPIO_Port GPIOC
-#define PB2_Pin GPIO_PIN_2
+#define PA7_Pin GPIO_PIN_7
 #define PB2_GPIO_Port GPIOB
-#define BUTTON_UP_Pin GPIO_PIN_10
-#define BUTTON_UP_GPIO_Port GPIOB
-#define BUTTON_UP_EXTI_IRQn EXTI15_10_IRQn
-#define BUTTON_LEFT_Pin GPIO_PIN_11
-#define BUTTON_LEFT_GPIO_Port GPIOB
-#define BUTTON_LEFT_EXTI_IRQn EXTI15_10_IRQn
-#define DISPLAY_RST_Pin GPIO_PIN_0
-#define DISPLAY_RST_GPIO_Port GPIOB
-#define BUTTON_DOWN_Pin GPIO_PIN_1
-#define BUTTON_DOWN_GPIO_Port GPIOB
-#define BUTTON_DOWN_EXTI_IRQn EXTI1_IRQn
-#define NFC_CS_Pin GPIO_PIN_4
-#define NFC_CS_GPIO_Port GPIOE
-#define BUTTON_RIGHT_Pin GPIO_PIN_12
-#define BUTTON_RIGHT_GPIO_Port GPIOB
-#define BUTTON_RIGHT_EXTI_IRQn EXTI15_10_IRQn
-#define RFID_OUT_Pin GPIO_PIN_13
+#define PB2_Pin GPIO_PIN_2
+#define PB3_GPIO_Port GPIOB
+#define PB3_Pin GPIO_PIN_3
+#define PC0_GPIO_Port GPIOC
+#define PC0_Pin GPIO_PIN_0
+#define PC10_GPIO_Port GPIOC
+#define PC10_Pin GPIO_PIN_10
+#define PC1_GPIO_Port GPIOC
+#define PC1_Pin GPIO_PIN_1
+#define PC3_GPIO_Port GPIOC
+#define PC3_Pin GPIO_PIN_3
+#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
+#define QUARTZ_32MHZ_IN_Pin GPIO_PIN_14
+#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
+#define QUARTZ_32MHZ_OUT_Pin GPIO_PIN_15
 #define RFID_OUT_GPIO_Port GPIOB
-#define iBTN_Pin GPIO_PIN_14
+#define RFID_OUT_Pin GPIO_PIN_13
+#define RFID_PULL_EXTI_IRQn EXTI9_5_IRQn
+#define RFID_PULL_GPIO_Port GPIOA
+#define RFID_PULL_Pin GPIO_PIN_8
+#define RFID_RF_IN_GPIO_Port GPIOC
+#define RFID_RF_IN_Pin GPIO_PIN_5
+#define SD_CS_GPIO_Port GPIOC
+#define SD_CS_Pin GPIO_PIN_12
+#define SPEAKER_GPIO_Port GPIOB
+#define SPEAKER_Pin GPIO_PIN_8
 #define iBTN_GPIO_Port GPIOB
-#define SPI_D_MOSI_Pin GPIO_PIN_15
+#define iBTN_Pin GPIO_PIN_14
+
+#define SPI_D_MISO_GPIO_Port GPIOC
+#define SPI_D_MISO_Pin GPIO_PIN_2
 #define SPI_D_MOSI_GPIO_Port GPIOB
-#define DISPLAY_DI_Pin GPIO_PIN_6
-#define DISPLAY_DI_GPIO_Port GPIOC
-#define DISPLAY_BACKLIGHT_Pin GPIO_PIN_15
-#define DISPLAY_BACKLIGHT_GPIO_Port GPIOA
-#define PC10_Pin GPIO_PIN_10
-#define PC10_GPIO_Port GPIOC
-#define DISPLAY_CS_Pin GPIO_PIN_11
-#define DISPLAY_CS_GPIO_Port GPIOC
-#define SD_CS_Pin GPIO_PIN_12
-#define SD_CS_GPIO_Port GPIOC
-#define CC1101_CS_Pin GPIO_PIN_0
-#define CC1101_CS_GPIO_Port GPIOD
-#define SPI_D_SCK_Pin GPIO_PIN_1
+#define SPI_D_MOSI_Pin GPIO_PIN_15
 #define SPI_D_SCK_GPIO_Port GPIOD
-#define PB3_Pin GPIO_PIN_3
-#define PB3_GPIO_Port GPIOB
-#define SPI_R_MISO_Pin GPIO_PIN_4
+#define SPI_D_SCK_Pin GPIO_PIN_1
 #define SPI_R_MISO_GPIO_Port GPIOB
-#define SPI_R_MOSI_Pin GPIO_PIN_5
+#define SPI_R_MISO_Pin GPIO_PIN_4
 #define SPI_R_MOSI_GPIO_Port GPIOB
-/* USER CODE BEGIN Private defines */
-
-#define MISO_PIN GpioPin{.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}
+#define SPI_R_MOSI_Pin GPIO_PIN_5
+#define SPI_R_SCK_GPIO_Port GPIOA
+#define SPI_R_SCK_Pin GPIO_PIN_5
 
 #define SPI_R hspi1
 #define SPI_D hspi2
@@ -183,12 +133,6 @@ extern TIM_HandleTypeDef htim16;
 #define VIBRO_Pin GPIO_PIN_10
 #define VIBRO_GPIO_Port GPIOC
 
-/* USER CODE END Private defines */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __MAIN_H */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 1 - 0
firmware/targets/f4/api-hal/api-hal-delay.c

@@ -23,5 +23,6 @@ void delay_us(float microseconds) {
 void delay(float milliseconds) {
     uint32_t ticks = milliseconds / (1000.0f / osKernelGetTickFreq());
     osStatus_t result = osDelay(ticks);
+    (void)result;
     assert(result == osOK);
 }

+ 25 - 19
firmware/targets/f4/api-hal/api-hal-resources.c

@@ -4,35 +4,41 @@
 
 const InputPin input_pins[] = {
     {.port = BUTTON_UP_GPIO_Port, .pin = BUTTON_UP_Pin, .key = InputKeyUp, .inverted = true},
-    {.port = BUTTON_DOWN_GPIO_Port, .pin = BUTTON_DOWN_Pin, .key = InputKeyDown, .inverted = true},
+    {.port = BUTTON_DOWN_GPIO_Port,
+     .pin = BUTTON_DOWN_Pin,
+     .key = InputKeyDown,
+     .inverted = true},
     {.port = BUTTON_RIGHT_GPIO_Port,
      .pin = BUTTON_RIGHT_Pin,
      .key = InputKeyRight,
      .inverted = true},
-    {.port = BUTTON_LEFT_GPIO_Port, .pin = BUTTON_LEFT_Pin, .key = InputKeyLeft, .inverted = true},
+    {.port = BUTTON_LEFT_GPIO_Port,
+     .pin = BUTTON_LEFT_Pin,
+     .key = InputKeyLeft,
+     .inverted = true},
     {.port = BUTTON_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false},
-    {.port = BUTTON_BACK_GPIO_Port, .pin = BUTTON_BACK_Pin, .key = InputKeyBack, .inverted = true},
+    {.port = BUTTON_BACK_GPIO_Port,
+     .pin = BUTTON_BACK_Pin,
+     .key = InputKeyBack,
+     .inverted = true},
 };
 
 const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin);
 
-const GpioPin led_gpio[3] = {
-    {LED_RED_GPIO_Port, LED_RED_Pin},
-    {LED_GREEN_GPIO_Port, LED_GREEN_Pin},
-    {LED_BLUE_GPIO_Port, LED_BLUE_Pin}};
-
-const GpioPin backlight_gpio = {DISPLAY_BACKLIGHT_GPIO_Port, DISPLAY_BACKLIGHT_Pin};
-const GpioPin sd_cs_gpio = {SD_CS_GPIO_Port, SD_CS_Pin};
 const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin};
 const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin};
 const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin};
 
-// external gpio's
-const GpioPin ext_pc0_gpio = {GPIOC, GPIO_PIN_0};
-const GpioPin ext_pc1_gpio = {GPIOC, GPIO_PIN_1};
-const GpioPin ext_pc3_gpio = {GPIOC, GPIO_PIN_3};
-const GpioPin ext_pb2_gpio = {GPIOB, GPIO_PIN_2};
-const GpioPin ext_pb3_gpio = {GPIOB, GPIO_PIN_3};
-const GpioPin ext_pa4_gpio = {GPIOA, GPIO_PIN_4};
-const GpioPin ext_pa6_gpio = {GPIOA, GPIO_PIN_6};
-const GpioPin ext_pa7_gpio = {GPIOA, GPIO_PIN_7};
+const GpioPin gpio_subghz_cs = { .port=CC1101_CS_GPIO_Port, .pin=CC1101_CS_Pin };
+const GpioPin gpio_display_cs = { .port=DISPLAY_CS_GPIO_Port, .pin=DISPLAY_CS_Pin };
+const GpioPin gpio_display_rst = { .port=DISPLAY_RST_GPIO_Port, .pin=DISPLAY_RST_Pin };
+const GpioPin gpio_display_di = { .port=DISPLAY_DI_GPIO_Port, .pin=DISPLAY_DI_Pin };
+const GpioPin gpio_sdcard_cs = { .port=SD_CS_GPIO_Port, .pin=SD_CS_Pin };
+const GpioPin gpio_nfc_cs = { .port=NFC_CS_GPIO_Port, .pin=NFC_CS_Pin };
+
+const GpioPin gpio_spi_d_miso = { .port=SPI_D_MISO_GPIO_Port, .pin=SPI_D_MISO_Pin };
+const GpioPin gpio_spi_d_mosi = { .port=SPI_D_MOSI_GPIO_Port, .pin=SPI_D_MOSI_Pin };
+const GpioPin gpio_spi_d_sck = { .port=SPI_D_SCK_GPIO_Port, .pin=SPI_D_SCK_Pin };
+const GpioPin gpio_spi_r_miso = { .port=SPI_R_MISO_GPIO_Port, .pin=SPI_R_MISO_Pin };
+const GpioPin gpio_spi_r_mosi = { .port=SPI_R_MOSI_GPIO_Port, .pin=SPI_R_MOSI_Pin };
+const GpioPin gpio_spi_r_sck = { .port=SPI_R_SCK_GPIO_Port, .pin=SPI_R_SCK_Pin };

+ 18 - 11
firmware/targets/f4/api-hal/api-hal-resources.h

@@ -16,7 +16,7 @@ extern "C" {
 #define POWER_I2C_SDA_GPIO_Port GPIOA
 
 #define POWER_I2C I2C1
-/* Timing register value is computed with the STM32CubeMX Tool,
+/** Timing register value is computed with the STM32CubeMX Tool,
   * Fast Mode @100kHz with I2CCLK = 64 MHz,
   * rise time = 0ns, fall time = 0ns
   */
@@ -53,20 +53,27 @@ typedef struct {
 extern const InputPin input_pins[];
 extern const size_t input_pins_count;
 
-extern const GpioPin sd_cs_gpio;
 extern const GpioPin vibro_gpio;
 extern const GpioPin ibutton_gpio;
 extern const GpioPin cc1101_g0_gpio;
 
-// external gpio's
-extern const GpioPin ext_pc0_gpio;
-extern const GpioPin ext_pc1_gpio;
-extern const GpioPin ext_pc3_gpio;
-extern const GpioPin ext_pb2_gpio;
-extern const GpioPin ext_pb3_gpio;
-extern const GpioPin ext_pa4_gpio;
-extern const GpioPin ext_pa6_gpio;
-extern const GpioPin ext_pa7_gpio;
+extern const GpioPin gpio_subghz_cs;
+extern const GpioPin gpio_display_cs;
+
+extern const GpioPin gpio_subghz_cs;
+extern const GpioPin gpio_display_cs;
+extern const GpioPin gpio_display_rst;
+extern const GpioPin gpio_display_di;
+extern const GpioPin gpio_sdcard_cs;
+extern const GpioPin gpio_nfc_cs;
+
+extern const GpioPin gpio_spi_d_miso;
+extern const GpioPin gpio_spi_d_mosi;
+extern const GpioPin gpio_spi_d_sck;
+extern const GpioPin gpio_spi_r_miso;
+extern const GpioPin gpio_spi_r_mosi;
+extern const GpioPin gpio_spi_r_sck;
+
 
 #ifdef __cplusplus
 }

+ 6 - 10
firmware/targets/f4/api-hal/api-hal-sd.c

@@ -16,25 +16,21 @@ bool hal_sd_detect(void) {
     bool result = false;
 
     // TODO open record
-    const GpioPin* sd_cs_record = &sd_cs_gpio;
-
-    // TODO: SPI manager
-    api_hal_spi_lock(sd_fast_spi.spi);
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSdCard);
 
     // configure pin as input
-    gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
+    gpio_init_ex(device->chip_select, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
     delay(1);
 
     // if gpio_read == 0 return true else return false
-    result = !gpio_read(sd_cs_record);
+    result = !gpio_read(device->chip_select);
 
     // configure pin back
-    gpio_init_ex(sd_cs_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
-    gpio_write(sd_cs_record, 1);
+    gpio_init_ex(device->chip_select, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+    gpio_write(device->chip_select, 1);
     delay(1);
 
-    // TODO: SPI manager
-    api_hal_spi_unlock(sd_fast_spi.spi);
+    api_hal_spi_device_return(device);
 
     return result;
 }

+ 79 - 25
firmware/targets/f4/api-hal/api-hal-spi-config.c

@@ -1,14 +1,89 @@
-#include "main.h"
-#include "api-hal-spi-config.h"
+#include <api-hal-spi-config.h>
+#include <api-hal-resources.h>
 
 extern SPI_HandleTypeDef SPI_R;
 extern SPI_HandleTypeDef SPI_D;
 
+const SPI_InitTypeDef api_hal_spi_config_nfc = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_2EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_DISABLE,
+};
+
+const SPI_InitTypeDef api_hal_spi_config_subghz = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_1EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_DISABLE,
+};
+
+const SPI_InitTypeDef api_hal_spi_config_display = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_1EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_ENABLE,
+};
+
+osMutexId_t spi_mutex_d = NULL;
+osMutexId_t spi_mutex_r = NULL;
+
+const ApiHalSpiBus spi_r = {
+    .spi=&SPI_R,
+    .mutex=&spi_mutex_r,
+    .miso=&gpio_spi_r_miso,
+    .mosi=&gpio_spi_r_mosi,
+    .clk=&gpio_spi_r_sck,
+};
+
+const ApiHalSpiBus spi_d = {
+    .spi=&SPI_D,
+    .mutex=&spi_mutex_d,
+    .miso=&gpio_spi_d_miso,
+    .mosi=&gpio_spi_d_mosi,
+    .clk=&gpio_spi_d_sck,
+};
+
+const ApiHalSpiDevice api_hal_spi_devices[ApiHalSpiDeviceIdMax] = {
+    { .bus=&spi_r, .config=&api_hal_spi_config_subghz, .chip_select=&gpio_subghz_cs, },
+    { .bus=&spi_d, .config=&api_hal_spi_config_display, .chip_select=&gpio_display_cs, },
+    { .bus=&spi_d, .config=NULL, .chip_select=&gpio_sdcard_cs, },
+    { .bus=&spi_r, .config=&api_hal_spi_config_nfc, .chip_select=&gpio_nfc_cs },
+};
+
+
 /**
  * SD Card in fast mode (after init)
  */
 const SPIDevice sd_fast_spi = {
-    .spi = &SPI_D,
+    .bus= &spi_d,
     .config = {
         .Mode = SPI_MODE_MASTER,
         .Direction = SPI_DIRECTION_2LINES,
@@ -29,7 +104,7 @@ const SPIDevice sd_fast_spi = {
  * SD Card in slow mode (before init)
  */
 const SPIDevice sd_slow_spi = {
-    .spi = &SPI_D,
+    .bus= &spi_d,
     .config = {
         .Mode = SPI_MODE_MASTER,
         .Direction = SPI_DIRECTION_2LINES,
@@ -45,24 +120,3 @@ const SPIDevice sd_slow_spi = {
         .CRCLength = SPI_CRC_LENGTH_DATASIZE,
         .NSSPMode = SPI_NSS_PULSE_ENABLE,
     }};
-
-/**
- * Display
- */
-const SPIDevice display_spi = {
-    .spi = &SPI_D,
-    .config = {
-        .Mode = SPI_MODE_MASTER,
-        .Direction = SPI_DIRECTION_2LINES,
-        .DataSize = SPI_DATASIZE_8BIT,
-        .CLKPolarity = SPI_POLARITY_LOW,
-        .CLKPhase = SPI_PHASE_1EDGE,
-        .NSS = SPI_NSS_SOFT,
-        .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
-        .FirstBit = SPI_FIRSTBIT_MSB,
-        .TIMode = SPI_TIMODE_DISABLE,
-        .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
-        .CRCPolynomial = 7,
-        .CRCLength = SPI_CRC_LENGTH_DATASIZE,
-        .NSSPMode = SPI_NSS_PULSE_ENABLE,
-    }};

+ 51 - 2
firmware/targets/f4/api-hal/api-hal-spi-config.h

@@ -1,17 +1,66 @@
 #pragma once
 
+#include <api-hal-gpio.h>
+#include <cmsis_os2.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+extern const SPI_InitTypeDef api_hal_spi_config_nfc;
+extern const SPI_InitTypeDef api_hal_spi_config_subghz;
+extern const SPI_InitTypeDef api_hal_spi_config_display;
+
+/** API HAL SPI BUS handler
+ * Structure content may change at some point
+ */
+typedef struct {
+    const SPI_HandleTypeDef* spi;
+    const osMutexId_t* mutex;
+    const GpioPin* miso;
+    const GpioPin* mosi;
+    const GpioPin* clk;
+} ApiHalSpiBus;
+
+/** API HAL SPI Device handler
+ * Structure content may change at some point
+ */
+typedef struct {
+    const ApiHalSpiBus* bus;
+    const SPI_InitTypeDef* config;
+    const GpioPin* chip_select;
+} ApiHalSpiDevice;
+
+/** API HAL SPI Standard Device IDs */
+typedef enum {
+    ApiHalSpiDeviceIdSubGhz,    /** SubGhz: CC1101, non-standard SPI usage */
+    ApiHalSpiDeviceIdDisplay,   /** Display: ERC12864, only have MOSI */
+    ApiHalSpiDeviceIdSdCard,    /** SDCARD: no default bus config, bus must explicitly be configured */
+    ApiHalSpiDeviceIdNfc,       /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */
+
+    ApiHalSpiDeviceIdMax,       /** Service Value, do not use */
+} ApiHalSpiDeviceId;
+
+/** Api Hal Spi Bus R
+ * CC1101, Nfc
+ */
+extern const ApiHalSpiBus spi_r;
+
+/** Api Hal Spi Bus D
+ * Display, SdCard
+ */
+extern const ApiHalSpiBus spi_d;
+
+/** Api Hal Spi devices */
+extern const ApiHalSpiDevice api_hal_spi_devices[ApiHalSpiDeviceIdMax];
+
 typedef struct {
-    SPI_HandleTypeDef* spi;
+    const ApiHalSpiBus* bus;
     const SPI_InitTypeDef config;
 } SPIDevice;
 
 extern const SPIDevice sd_fast_spi;
 extern const SPIDevice sd_slow_spi;
-extern const SPIDevice display_spi;
 
 #ifdef __cplusplus
 }

+ 145 - 34
firmware/targets/f4/api-hal/api-hal-spi.c

@@ -1,36 +1,167 @@
 #include "api-hal-spi.h"
-#include <cmsis_os2.h>
+#include <api-hal-resources.h>
 #include <stdbool.h>
 #include <string.h>
+#include <spi.h>
+#include <furi.h>
 
-osMutexId_t spi_mutex_r;
-osMutexId_t spi_mutex_d;
 
-extern SPI_HandleTypeDef SPI_R;
-extern SPI_HandleTypeDef SPI_D;
 extern void Enable_SPI(SPI_HandleTypeDef* spi);
 
 void api_hal_spi_init() {
-    spi_mutex_r = osMutexNew(NULL);
-    spi_mutex_d = osMutexNew(NULL);
+    // Spi structure is const, but mutex is not
+    // Need some hell-ish casting to make it work
+    *(osMutexId_t*)spi_r.mutex = osMutexNew(NULL);
+    *(osMutexId_t*)spi_d.mutex = osMutexNew(NULL);
+    // 
+    for (size_t i=0; i<ApiHalSpiDeviceIdMax; ++i) {
+        hal_gpio_init(
+            api_hal_spi_devices[i].chip_select,
+            GpioModeOutputPushPull,
+            GpioPullNo,
+            GpioSpeedVeryHigh
+        );
+    }
+}
+
+void api_hal_spi_bus_lock(const ApiHalSpiBus* bus) {
+    furi_assert(bus);
+    if (bus->mutex) {
+        osMutexAcquire(*bus->mutex, osWaitForever);
+    }
+}
+
+void api_hal_spi_bus_unlock(const ApiHalSpiBus* bus) {
+    furi_assert(bus);
+    if (bus->mutex) {
+        osMutexRelease(*bus->mutex);
+    }
+}
+
+bool api_hal_spi_bus_rx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_Receive((SPI_HandleTypeDef *)bus->spi, buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+bool api_hal_spi_bus_tx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_Transmit((SPI_HandleTypeDef *)bus->spi, buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+bool api_hal_spi_bus_trx(const ApiHalSpiBus* bus, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(tx_buffer);
+    furi_assert(rx_buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_TransmitReceive((SPI_HandleTypeDef *)bus->spi, tx_buffer, rx_buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+const ApiHalSpiDevice* api_hal_spi_device_get(ApiHalSpiDeviceId device_id) {
+    furi_assert(device_id < ApiHalSpiDeviceIdMax);
+
+    const ApiHalSpiDevice* device = &api_hal_spi_devices[device_id];
+    assert(device);
+    api_hal_spi_bus_lock(device->bus);
+
+    if (device->config) {
+        memcpy((SPI_InitTypeDef*)&device->bus->spi->Init, device->config, sizeof(SPI_InitTypeDef));
+        if(HAL_SPI_Init((SPI_HandleTypeDef *)device->bus->spi) != HAL_OK) {
+            Error_Handler();
+        }
+        Enable_SPI((SPI_HandleTypeDef *)device->bus->spi);
+    }
+
+    return device;
+}
+
+void api_hal_spi_device_return(const ApiHalSpiDevice* device) {
+    api_hal_spi_bus_unlock(device->bus);
+}
+
+bool api_hal_spi_device_rx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_rx(device->bus, buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
+}
+
+bool api_hal_spi_device_tx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_tx(device->bus, buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
+}
+
+bool api_hal_spi_device_trx(const ApiHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(tx_buffer);
+    furi_assert(rx_buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_trx(device->bus, tx_buffer, rx_buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
 }
 
 void api_hal_spi_apply_config(const SPIDevice* device) {
     osKernelLock();
 
-    memcpy(&device->spi->Init, &device->config, sizeof(SPI_InitTypeDef));
+    memcpy((SPI_InitTypeDef*)&device->bus->spi->Init, &device->config, sizeof(SPI_InitTypeDef));
 
-    if(HAL_SPI_Init(device->spi) != HAL_OK) {
+    if(HAL_SPI_Init((SPI_HandleTypeDef*)device->bus->spi) != HAL_OK) {
         Error_Handler();
     }
 
-    Enable_SPI(device->spi);
+    Enable_SPI((SPI_HandleTypeDef*)device->bus->spi);
 
     osKernelUnlock();
 }
 
 bool api_hal_spi_config_are_actual(const SPIDevice* device) {
-    return (memcmp(&device->config, &device->spi->Init, sizeof(SPI_InitTypeDef)) == 0);
+    return (memcmp(&device->config, &device->bus->spi->Init, sizeof(SPI_InitTypeDef)) == 0);
 }
 
 void api_hal_spi_config_device(const SPIDevice* device) {
@@ -39,31 +170,11 @@ void api_hal_spi_config_device(const SPIDevice* device) {
     }
 }
 
-void api_hal_spi_lock(SPI_HandleTypeDef* spi) {
-    if(spi == &SPI_D) {
-        osMutexAcquire(spi_mutex_d, osWaitForever);
-    } else if(spi == &SPI_R) {
-        osMutexAcquire(spi_mutex_r, osWaitForever);
-    } else {
-        Error_Handler();
-    }
-}
-
-void api_hal_spi_unlock(SPI_HandleTypeDef* spi) {
-    if(spi == &SPI_D) {
-        osMutexRelease(spi_mutex_d);
-    } else if(spi == &SPI_R) {
-        osMutexRelease(spi_mutex_r);
-    } else {
-        Error_Handler();
-    }
-}
-
 void api_hal_spi_lock_device(const SPIDevice* device) {
-    api_hal_spi_lock(device->spi);
+    api_hal_spi_bus_lock(device->bus);
     api_hal_spi_config_device(device);
 }
 
 void api_hal_spi_unlock_device(const SPIDevice* device) {
-    api_hal_spi_unlock(device->spi);
-}
+    api_hal_spi_bus_unlock(device->bus);
+}

+ 76 - 6
firmware/targets/f4/api-hal/api-hal-spi.h

@@ -1,6 +1,8 @@
 #pragma once
 #include "main.h"
 #include "api-hal-spi-config.h"
+#include <api-hal-gpio.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -11,15 +13,83 @@ extern "C" {
  */
 void api_hal_spi_init();
 
-/**
- * Lock SPI bus
+/* Bus Level API */
+
+/** Lock SPI bus
+ * Takes bus mutex, if used
  */
-void api_hal_spi_lock(SPI_HandleTypeDef* spi);
+void api_hal_spi_bus_lock(const ApiHalSpiBus* bus);
 
-/**
- * Unlock SPI bus
+/** Unlock SPI bus
+ * Releases BUS mutex, if used
  */
-void api_hal_spi_unlock(SPI_HandleTypeDef* spi);
+void api_hal_spi_bus_unlock(const ApiHalSpiBus* bus);
+
+/** SPI Receive
+ * @param bus - spi bus handler
+ * @param buffer - receive buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_rx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit
+ * @param bus - spi bus handler
+ * @param buffer - transmit buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_tx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit and Receive
+ * @param bus - spi bus handlere
+ * @param tx_buffer - device handle
+ * @param rx_buffer - device handle
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_trx(const ApiHalSpiBus* bus, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
+
+/* Device Level API */
+
+/** Get Device handle
+ * And lock access to the corresponding SPI BUS
+ * @param device_id - device identifier
+ * @return device handle
+ */
+const ApiHalSpiDevice* api_hal_spi_device_get(ApiHalSpiDeviceId device_id);
+
+/** Return Device handle
+ * And unlock access to the corresponding SPI BUS
+ * @param device - device handle
+ */
+void api_hal_spi_device_return(const ApiHalSpiDevice* device);
+
+/** SPI Recieve
+ * @param device - device handle
+ * @param buffer - receive buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_rx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit
+ * @param device - device handle
+ * @param buffer - transmit buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_tx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit and Receive
+ * @param device - device handle
+ * @param tx_buffer - device handle
+ * @param rx_buffer - device handle
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_trx(const ApiHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
+
 
 /**
  * Lock SPI device bus and apply config if needed

+ 9 - 3
firmware/targets/f4/api-hal/api-hal-subghz.c

@@ -1,5 +1,11 @@
 #include "api-hal-subghz.h"
+#include <api-hal-spi.h>
+#include <cc1101.h>
 
-void api_hal_rf_band_set(RfBand band) {
-    
-}
+void api_hal_subghz_init() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_init(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_band_set(RfBand band) {}

+ 1 - 0
firmware/targets/f4/api-hal/api-hal.c

@@ -8,4 +8,5 @@ void api_hal_init() {
     api_hal_power_init();
     api_hal_light_init();
     api_hal_vibro_init();
+    api_hal_subghz_init();
 }

+ 77 - 135
firmware/targets/f5/Inc/main.h

@@ -1,155 +1,103 @@
-/* USER CODE BEGIN Header */
-/**
-  ******************************************************************************
-  * @file           : main.h
-  * @brief          : Header for main.c file.
-  *                   This file contains the common defines of the application.
-  ******************************************************************************
-  * @attention
-  *
-  * <h2><center>&copy; Copyright (c) 2020 STMicroelectronics.
-  * All rights reserved.</center></h2>
-  *
-  * This software component is licensed by ST under Ultimate Liberty license
-  * SLA0044, the "License"; You may not use this file except in compliance with
-  * the License. You may obtain a copy of the License at:
-  *                             www.st.com/SLA0044
-  *
-  ******************************************************************************
-  */
-/* USER CODE END Header */
-
-/* Define to prevent recursive inclusion -------------------------------------*/
-#ifndef __MAIN_H
-#define __MAIN_H
+#pragma once
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-/* Includes ------------------------------------------------------------------*/
 #include "stm32wbxx_hal.h"
 
-/* Private includes ----------------------------------------------------------*/
-/* USER CODE BEGIN Includes */
-
-/* USER CODE END Includes */
-
-/* Exported types ------------------------------------------------------------*/
-/* USER CODE BEGIN ET */
-
-/* USER CODE END ET */
-
-/* Exported constants --------------------------------------------------------*/
-/* USER CODE BEGIN EC */
-
-/* USER CODE END EC */
-
-/* Exported macro ------------------------------------------------------------*/
-/* USER CODE BEGIN EM */
-
-/* USER CODE END EM */
-
-/* Exported functions prototypes ---------------------------------------------*/
 void Error_Handler(void);
 
-/* USER CODE BEGIN EFP */
-
-/* USER CODE END EFP */
-
-/* Private defines -----------------------------------------------------------*/
-#define BUTTON_BACK_Pin GPIO_PIN_13
-#define BUTTON_BACK_GPIO_Port GPIOC
 #define BUTTON_BACK_EXTI_IRQn EXTI15_10_IRQn
-#define QUARTZ_32MHZ_IN_Pin GPIO_PIN_14
-#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
-#define QUARTZ_32MHZ_OUT_Pin GPIO_PIN_15
-#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
-#define BUTTON_OK_Pin GPIO_PIN_3
-#define BUTTON_OK_GPIO_Port GPIOH
+#define BUTTON_BACK_GPIO_Port GPIOC
+#define BUTTON_BACK_Pin GPIO_PIN_13
+#define BUTTON_DOWN_EXTI_IRQn EXTI1_IRQn
+#define BUTTON_DOWN_GPIO_Port GPIOB
+#define BUTTON_DOWN_Pin GPIO_PIN_1
+#define BUTTON_LEFT_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_LEFT_GPIO_Port GPIOB
+#define BUTTON_LEFT_Pin GPIO_PIN_11
 #define BUTTON_OK_EXTI_IRQn EXTI3_IRQn
-#define SPEAKER_Pin GPIO_PIN_8
-#define SPEAKER_GPIO_Port GPIOB
-#define IR_TX_Pin GPIO_PIN_9
+#define BUTTON_OK_GPIO_Port GPIOH
+#define BUTTON_OK_Pin GPIO_PIN_3
+#define BUTTON_RIGHT_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_RIGHT_GPIO_Port GPIOB
+#define BUTTON_RIGHT_Pin GPIO_PIN_12
+#define BUTTON_UP_EXTI_IRQn EXTI15_10_IRQn
+#define BUTTON_UP_GPIO_Port GPIOB
+#define BUTTON_UP_Pin GPIO_PIN_10
+#define CC1101_CS_GPIO_Port GPIOD
+#define CC1101_CS_Pin GPIO_PIN_0
+#define CC1101_G0_GPIO_Port GPIOC
+#define CC1101_G0_Pin GPIO_PIN_4
+#define DISPLAY_CS_GPIO_Port GPIOC
+#define DISPLAY_CS_Pin GPIO_PIN_11
+#define DISPLAY_DI_GPIO_Port GPIOC
+#define DISPLAY_DI_Pin GPIO_PIN_6
+#define DISPLAY_RST_GPIO_Port GPIOB
+#define DISPLAY_RST_Pin GPIO_PIN_0
+#define IR_RX_GPIO_Port GPIOA
+#define IR_RX_Pin GPIO_PIN_0
 #define IR_TX_GPIO_Port GPIOB
-#define PC0_Pin GPIO_PIN_0
+#define IR_TX_Pin GPIO_PIN_9
+#define NFC_CS_GPIO_Port GPIOE
+#define NFC_CS_Pin GPIO_PIN_4
+#define PA4_GPIO_Port GPIOA
+#define PA4_Pin GPIO_PIN_4
+#define PA6_GPIO_Port GPIOA
+#define PA6_Pin GPIO_PIN_6
+#define PA7_GPIO_Port GPIOA
+#define PA7_Pin GPIO_PIN_7
+#define PB2_GPIO_Port GPIOB
+#define PB2_Pin GPIO_PIN_2
+#define PB3_GPIO_Port GPIOB
+#define PB3_Pin GPIO_PIN_3
 #define PC0_GPIO_Port GPIOC
-#define PC1_Pin GPIO_PIN_1
+#define PC0_Pin GPIO_PIN_0
 #define PC1_GPIO_Port GPIOC
-#define SPI_D_MISO_Pin GPIO_PIN_2
-#define SPI_D_MISO_GPIO_Port GPIOC
-#define PC3_Pin GPIO_PIN_3
+#define PC1_Pin GPIO_PIN_1
 #define PC3_GPIO_Port GPIOC
-#define IR_RX_Pin GPIO_PIN_0
-#define IR_RX_GPIO_Port GPIOA
-#define RF_SW_0_Pin GPIO_PIN_1
-#define RF_SW_0_GPIO_Port GPIOA
-#define RF_SW_1_Pin GPIO_PIN_2
-#define RF_SW_1_GPIO_Port GPIOA
-#define PERIPH_POWER_Pin GPIO_PIN_3
+#define PC3_Pin GPIO_PIN_3
 #define PERIPH_POWER_GPIO_Port GPIOA
-#define PA4_Pin GPIO_PIN_4
-#define PA4_GPIO_Port GPIOA
-#define SPI_R_SCK_Pin GPIO_PIN_5
-#define SPI_R_SCK_GPIO_Port GPIOA
-#define PA6_Pin GPIO_PIN_6
-#define PA6_GPIO_Port GPIOA
-#define PA7_Pin GPIO_PIN_7
-#define PA7_GPIO_Port GPIOA
-#define RFID_PULL_Pin GPIO_PIN_8
-#define RFID_PULL_GPIO_Port GPIOA
+#define PERIPH_POWER_Pin GPIO_PIN_3
+#define QUARTZ_32MHZ_IN_GPIO_Port GPIOC
+#define QUARTZ_32MHZ_IN_Pin GPIO_PIN_14
+#define QUARTZ_32MHZ_OUT_GPIO_Port GPIOC
+#define QUARTZ_32MHZ_OUT_Pin GPIO_PIN_15
+#define RFID_OUT_GPIO_Port GPIOB
+#define RFID_OUT_Pin GPIO_PIN_13
 #define RFID_PULL_EXTI_IRQn EXTI9_5_IRQn
-#define CC1101_G0_Pin GPIO_PIN_4
-#define CC1101_G0_GPIO_Port GPIOC
-#define RFID_RF_IN_Pin GPIO_PIN_5
+#define RFID_PULL_GPIO_Port GPIOA
+#define RFID_PULL_Pin GPIO_PIN_8
 #define RFID_RF_IN_GPIO_Port GPIOC
-#define PB2_Pin GPIO_PIN_2
-#define PB2_GPIO_Port GPIOB
-#define BUTTON_UP_Pin GPIO_PIN_10
-#define BUTTON_UP_GPIO_Port GPIOB
-#define BUTTON_UP_EXTI_IRQn EXTI15_10_IRQn
-#define BUTTON_LEFT_Pin GPIO_PIN_11
-#define BUTTON_LEFT_GPIO_Port GPIOB
-#define BUTTON_LEFT_EXTI_IRQn EXTI15_10_IRQn
-#define DISPLAY_RST_Pin GPIO_PIN_0
-#define DISPLAY_RST_GPIO_Port GPIOB
-#define BUTTON_DOWN_Pin GPIO_PIN_1
-#define BUTTON_DOWN_GPIO_Port GPIOB
-#define BUTTON_DOWN_EXTI_IRQn EXTI1_IRQn
-#define NFC_CS_Pin GPIO_PIN_4
-#define NFC_CS_GPIO_Port GPIOE
-#define BUTTON_RIGHT_Pin GPIO_PIN_12
-#define BUTTON_RIGHT_GPIO_Port GPIOB
-#define BUTTON_RIGHT_EXTI_IRQn EXTI15_10_IRQn
-#define RFID_OUT_Pin GPIO_PIN_13
-#define RFID_OUT_GPIO_Port GPIOB
-#define iBTN_Pin GPIO_PIN_14
-#define iBTN_GPIO_Port GPIOB
-#define SPI_D_MOSI_Pin GPIO_PIN_15
-#define SPI_D_MOSI_GPIO_Port GPIOB
-#define DISPLAY_DI_Pin GPIO_PIN_6
-#define DISPLAY_DI_GPIO_Port GPIOC
-#define SD_CD_Pin GPIO_PIN_15
+#define RFID_RF_IN_Pin GPIO_PIN_5
+#define RF_SW_0_GPIO_Port GPIOA
+#define RF_SW_0_Pin GPIO_PIN_1
+#define RF_SW_1_GPIO_Port GPIOA
+#define RF_SW_1_Pin GPIO_PIN_2
 #define SD_CD_GPIO_Port GPIOA
-#define VIBRO_Pin GPIO_PIN_10
-#define VIBRO_GPIO_Port GPIOC
-#define DISPLAY_CS_Pin GPIO_PIN_11
-#define DISPLAY_CS_GPIO_Port GPIOC
-#define SD_CS_Pin GPIO_PIN_12
+#define SD_CD_Pin GPIO_PIN_15
 #define SD_CS_GPIO_Port GPIOC
-#define CC1101_CS_Pin GPIO_PIN_0
-#define CC1101_CS_GPIO_Port GPIOD
-#define SPI_D_SCK_Pin GPIO_PIN_1
+#define SD_CS_Pin GPIO_PIN_12
+#define SPEAKER_GPIO_Port GPIOB
+#define SPEAKER_Pin GPIO_PIN_8
+#define VIBRO_GPIO_Port GPIOC
+#define VIBRO_Pin GPIO_PIN_10
+#define iBTN_GPIO_Port GPIOB
+#define iBTN_Pin GPIO_PIN_14
+
+#define SPI_D_MISO_GPIO_Port GPIOC
+#define SPI_D_MISO_Pin GPIO_PIN_2
+#define SPI_D_MOSI_GPIO_Port GPIOB
+#define SPI_D_MOSI_Pin GPIO_PIN_15
 #define SPI_D_SCK_GPIO_Port GPIOD
-#define PB3_Pin GPIO_PIN_3
-#define PB3_GPIO_Port GPIOB
-#define SPI_R_MISO_Pin GPIO_PIN_4
+#define SPI_D_SCK_Pin GPIO_PIN_1
 #define SPI_R_MISO_GPIO_Port GPIOB
-#define SPI_R_MOSI_Pin GPIO_PIN_5
+#define SPI_R_MISO_Pin GPIO_PIN_4
 #define SPI_R_MOSI_GPIO_Port GPIOB
-/* USER CODE BEGIN Private defines */
-
-#define MISO_PIN GpioPin{.port = SPI_R_MISO_GPIO_Port, .pin = SPI_R_MISO_Pin}
+#define SPI_R_MOSI_Pin GPIO_PIN_5
+#define SPI_R_SCK_GPIO_Port GPIOA
+#define SPI_R_SCK_Pin GPIO_PIN_5
 
 #define SPI_R hspi1
 #define SPI_D hspi2
@@ -182,12 +130,6 @@ extern TIM_HandleTypeDef htim16;
 #define NFC_IRQ_Pin RFID_PULL_Pin
 #define NFC_IRQ_GPIO_Port RFID_PULL_GPIO_Port
 
-/* USER CODE END Private defines */
-
 #ifdef __cplusplus
 }
 #endif
-
-#endif /* __MAIN_H */
-
-/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

+ 1 - 0
firmware/targets/f5/api-hal/api-hal-delay.c

@@ -23,5 +23,6 @@ void delay_us(float microseconds) {
 void delay(float milliseconds) {
     uint32_t ticks = milliseconds / (1000.0f / osKernelGetTickFreq());
     osStatus_t result = osDelay(ticks);
+    (void)result;
     assert(result == osOK);
 }

+ 0 - 5
firmware/targets/f5/api-hal/api-hal-gpio.c

@@ -20,11 +20,6 @@ void hal_gpio_init(
     HAL_GPIO_Init(gpio->port, &GPIO_InitStruct);
 }
 
-void enable_cc1101_irq() {
-    HAL_NVIC_SetPriority(EXTI4_IRQn, 5, 0);
-    HAL_NVIC_EnableIRQ(EXTI4_IRQn);
-}
-
 extern COMP_HandleTypeDef hcomp1;
 
 bool get_rfid_in_level() {

+ 6 - 6
firmware/targets/f5/api-hal/api-hal-gpio.h

@@ -24,6 +24,12 @@ typedef enum {
     GpioModeEventRiseFall = GPIO_MODE_EVT_RISING_FALLING,
 } GpioMode;
 
+typedef enum {
+    GpioPullNo = GPIO_NOPULL,
+    GpioPullUp = GPIO_PULLUP,
+    GpioPullDown = GPIO_PULLDOWN,
+} GpioPull;
+
 typedef enum {
     GpioSpeedLow = GPIO_SPEED_FREQ_LOW,
     GpioSpeedMedium = GPIO_SPEED_FREQ_MEDIUM,
@@ -31,12 +37,6 @@ typedef enum {
     GpioSpeedVeryHigh = GPIO_SPEED_FREQ_VERY_HIGH,
 } GpioSpeed;
 
-typedef enum {
-    GpioPullNo = GPIO_NOPULL,
-    GpioPullUp = GPIO_PULLUP,
-    GpioPullDown = GPIO_PULLDOWN,
-} GpioPull;
-
 typedef struct {
     GPIO_TypeDef* port;
     uint16_t pin;

+ 14 - 0
firmware/targets/f5/api-hal/api-hal-resources.c

@@ -28,3 +28,17 @@ const size_t input_pins_count = sizeof(input_pins) / sizeof(InputPin);
 const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin};
 const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin};
 const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin};
+
+const GpioPin gpio_subghz_cs = { .port=CC1101_CS_GPIO_Port, .pin=CC1101_CS_Pin };
+const GpioPin gpio_display_cs = { .port=DISPLAY_CS_GPIO_Port, .pin=DISPLAY_CS_Pin };
+const GpioPin gpio_display_rst = { .port=DISPLAY_RST_GPIO_Port, .pin=DISPLAY_RST_Pin };
+const GpioPin gpio_display_di = { .port=DISPLAY_DI_GPIO_Port, .pin=DISPLAY_DI_Pin };
+const GpioPin gpio_sdcard_cs = { .port=SD_CS_GPIO_Port, .pin=SD_CS_Pin };
+const GpioPin gpio_nfc_cs = { .port=NFC_CS_GPIO_Port, .pin=NFC_CS_Pin };
+
+const GpioPin gpio_spi_d_miso = { .port=SPI_D_MISO_GPIO_Port, .pin=SPI_D_MISO_Pin };
+const GpioPin gpio_spi_d_mosi = { .port=SPI_D_MOSI_GPIO_Port, .pin=SPI_D_MOSI_Pin };
+const GpioPin gpio_spi_d_sck = { .port=SPI_D_SCK_GPIO_Port, .pin=SPI_D_SCK_Pin };
+const GpioPin gpio_spi_r_miso = { .port=SPI_R_MISO_GPIO_Port, .pin=SPI_R_MISO_Pin };
+const GpioPin gpio_spi_r_mosi = { .port=SPI_R_MOSI_GPIO_Port, .pin=SPI_R_MOSI_Pin };
+const GpioPin gpio_spi_r_sck = { .port=SPI_R_SCK_GPIO_Port, .pin=SPI_R_SCK_Pin };

+ 19 - 1
firmware/targets/f5/api-hal/api-hal-resources.h

@@ -16,7 +16,7 @@ extern "C" {
 #define POWER_I2C_SDA_GPIO_Port GPIOA
 
 #define POWER_I2C I2C1
-/* Timing register value is computed with the STM32CubeMX Tool,
+/** Timing register value is computed with the STM32CubeMX Tool,
   * Fast Mode @100kHz with I2CCLK = 64 MHz,
   * rise time = 0ns, fall time = 0ns
   */
@@ -57,6 +57,24 @@ extern const GpioPin vibro_gpio;
 extern const GpioPin ibutton_gpio;
 extern const GpioPin cc1101_g0_gpio;
 
+extern const GpioPin gpio_subghz_cs;
+extern const GpioPin gpio_display_cs;
+
+extern const GpioPin gpio_subghz_cs;
+extern const GpioPin gpio_display_cs;
+extern const GpioPin gpio_display_rst;
+extern const GpioPin gpio_display_di;
+extern const GpioPin gpio_sdcard_cs;
+extern const GpioPin gpio_nfc_cs;
+
+extern const GpioPin gpio_spi_d_miso;
+extern const GpioPin gpio_spi_d_mosi;
+extern const GpioPin gpio_spi_d_sck;
+extern const GpioPin gpio_spi_r_miso;
+extern const GpioPin gpio_spi_r_mosi;
+extern const GpioPin gpio_spi_r_sck;
+
+
 #ifdef __cplusplus
 }
 #endif

+ 79 - 25
firmware/targets/f5/api-hal/api-hal-spi-config.c

@@ -1,14 +1,89 @@
-#include "main.h"
-#include "api-hal-spi-config.h"
+#include <api-hal-spi-config.h>
+#include <api-hal-resources.h>
 
 extern SPI_HandleTypeDef SPI_R;
 extern SPI_HandleTypeDef SPI_D;
 
+const SPI_InitTypeDef api_hal_spi_config_nfc = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_2EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_DISABLE,
+};
+
+const SPI_InitTypeDef api_hal_spi_config_subghz = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_1EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_DISABLE,
+};
+
+const SPI_InitTypeDef api_hal_spi_config_display = {
+    .Mode = SPI_MODE_MASTER,
+    .Direction = SPI_DIRECTION_2LINES,
+    .DataSize = SPI_DATASIZE_8BIT,
+    .CLKPolarity = SPI_POLARITY_LOW,
+    .CLKPhase = SPI_PHASE_1EDGE,
+    .NSS = SPI_NSS_SOFT,
+    .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
+    .FirstBit = SPI_FIRSTBIT_MSB,
+    .TIMode = SPI_TIMODE_DISABLE,
+    .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
+    .CRCPolynomial = 7,
+    .CRCLength = SPI_CRC_LENGTH_DATASIZE,
+    .NSSPMode = SPI_NSS_PULSE_ENABLE,
+};
+
+osMutexId_t spi_mutex_d = NULL;
+osMutexId_t spi_mutex_r = NULL;
+
+const ApiHalSpiBus spi_r = {
+    .spi=&SPI_R,
+    .mutex=&spi_mutex_r,
+    .miso=&gpio_spi_r_miso,
+    .mosi=&gpio_spi_r_mosi,
+    .clk=&gpio_spi_r_sck,
+};
+
+const ApiHalSpiBus spi_d = {
+    .spi=&SPI_D,
+    .mutex=&spi_mutex_d,
+    .miso=&gpio_spi_d_miso,
+    .mosi=&gpio_spi_d_mosi,
+    .clk=&gpio_spi_d_sck,
+};
+
+const ApiHalSpiDevice api_hal_spi_devices[ApiHalSpiDeviceIdMax] = {
+    { .bus=&spi_r, .config=&api_hal_spi_config_subghz, .chip_select=&gpio_subghz_cs, },
+    { .bus=&spi_d, .config=&api_hal_spi_config_display, .chip_select=&gpio_display_cs, },
+    { .bus=&spi_d, .config=NULL, .chip_select=&gpio_sdcard_cs, },
+    { .bus=&spi_r, .config=&api_hal_spi_config_nfc, .chip_select=&gpio_nfc_cs },
+};
+
+
 /**
  * SD Card in fast mode (after init)
  */
 const SPIDevice sd_fast_spi = {
-    .spi = &SPI_D,
+    .bus= &spi_d,
     .config = {
         .Mode = SPI_MODE_MASTER,
         .Direction = SPI_DIRECTION_2LINES,
@@ -29,7 +104,7 @@ const SPIDevice sd_fast_spi = {
  * SD Card in slow mode (before init)
  */
 const SPIDevice sd_slow_spi = {
-    .spi = &SPI_D,
+    .bus= &spi_d,
     .config = {
         .Mode = SPI_MODE_MASTER,
         .Direction = SPI_DIRECTION_2LINES,
@@ -45,24 +120,3 @@ const SPIDevice sd_slow_spi = {
         .CRCLength = SPI_CRC_LENGTH_DATASIZE,
         .NSSPMode = SPI_NSS_PULSE_ENABLE,
     }};
-
-/**
- * Display
- */
-const SPIDevice display_spi = {
-    .spi = &SPI_D,
-    .config = {
-        .Mode = SPI_MODE_MASTER,
-        .Direction = SPI_DIRECTION_2LINES,
-        .DataSize = SPI_DATASIZE_8BIT,
-        .CLKPolarity = SPI_POLARITY_LOW,
-        .CLKPhase = SPI_PHASE_1EDGE,
-        .NSS = SPI_NSS_SOFT,
-        .BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
-        .FirstBit = SPI_FIRSTBIT_MSB,
-        .TIMode = SPI_TIMODE_DISABLE,
-        .CRCCalculation = SPI_CRCCALCULATION_DISABLE,
-        .CRCPolynomial = 7,
-        .CRCLength = SPI_CRC_LENGTH_DATASIZE,
-        .NSSPMode = SPI_NSS_PULSE_ENABLE,
-    }};

+ 51 - 2
firmware/targets/f5/api-hal/api-hal-spi-config.h

@@ -1,17 +1,66 @@
 #pragma once
 
+#include <api-hal-gpio.h>
+#include <cmsis_os2.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+extern const SPI_InitTypeDef api_hal_spi_config_nfc;
+extern const SPI_InitTypeDef api_hal_spi_config_subghz;
+extern const SPI_InitTypeDef api_hal_spi_config_display;
+
+/** API HAL SPI BUS handler
+ * Structure content may change at some point
+ */
+typedef struct {
+    const SPI_HandleTypeDef* spi;
+    const osMutexId_t* mutex;
+    const GpioPin* miso;
+    const GpioPin* mosi;
+    const GpioPin* clk;
+} ApiHalSpiBus;
+
+/** API HAL SPI Device handler
+ * Structure content may change at some point
+ */
+typedef struct {
+    const ApiHalSpiBus* bus;
+    const SPI_InitTypeDef* config;
+    const GpioPin* chip_select;
+} ApiHalSpiDevice;
+
+/** API HAL SPI Standard Device IDs */
+typedef enum {
+    ApiHalSpiDeviceIdSubGhz,    /** SubGhz: CC1101, non-standard SPI usage */
+    ApiHalSpiDeviceIdDisplay,   /** Display: ERC12864, only have MOSI */
+    ApiHalSpiDeviceIdSdCard,    /** SDCARD: no default bus config, bus must explicitly be configured */
+    ApiHalSpiDeviceIdNfc,       /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */
+
+    ApiHalSpiDeviceIdMax,       /** Service Value, do not use */
+} ApiHalSpiDeviceId;
+
+/** Api Hal Spi Bus R
+ * CC1101, Nfc
+ */
+extern const ApiHalSpiBus spi_r;
+
+/** Api Hal Spi Bus D
+ * Display, SdCard
+ */
+extern const ApiHalSpiBus spi_d;
+
+/** Api Hal Spi devices */
+extern const ApiHalSpiDevice api_hal_spi_devices[ApiHalSpiDeviceIdMax];
+
 typedef struct {
-    SPI_HandleTypeDef* spi;
+    const ApiHalSpiBus* bus;
     const SPI_InitTypeDef config;
 } SPIDevice;
 
 extern const SPIDevice sd_fast_spi;
 extern const SPIDevice sd_slow_spi;
-extern const SPIDevice display_spi;
 
 #ifdef __cplusplus
 }

+ 145 - 34
firmware/targets/f5/api-hal/api-hal-spi.c

@@ -1,36 +1,167 @@
 #include "api-hal-spi.h"
-#include <cmsis_os2.h>
+#include <api-hal-resources.h>
 #include <stdbool.h>
 #include <string.h>
+#include <spi.h>
+#include <furi.h>
 
-osMutexId_t spi_mutex_r;
-osMutexId_t spi_mutex_d;
 
-extern SPI_HandleTypeDef SPI_R;
-extern SPI_HandleTypeDef SPI_D;
 extern void Enable_SPI(SPI_HandleTypeDef* spi);
 
 void api_hal_spi_init() {
-    spi_mutex_r = osMutexNew(NULL);
-    spi_mutex_d = osMutexNew(NULL);
+    // Spi structure is const, but mutex is not
+    // Need some hell-ish casting to make it work
+    *(osMutexId_t*)spi_r.mutex = osMutexNew(NULL);
+    *(osMutexId_t*)spi_d.mutex = osMutexNew(NULL);
+    // 
+    for (size_t i=0; i<ApiHalSpiDeviceIdMax; ++i) {
+        hal_gpio_init(
+            api_hal_spi_devices[i].chip_select,
+            GpioModeOutputPushPull,
+            GpioPullNo,
+            GpioSpeedVeryHigh
+        );
+    }
+}
+
+void api_hal_spi_bus_lock(const ApiHalSpiBus* bus) {
+    furi_assert(bus);
+    if (bus->mutex) {
+        osMutexAcquire(*bus->mutex, osWaitForever);
+    }
+}
+
+void api_hal_spi_bus_unlock(const ApiHalSpiBus* bus) {
+    furi_assert(bus);
+    if (bus->mutex) {
+        osMutexRelease(*bus->mutex);
+    }
+}
+
+bool api_hal_spi_bus_rx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_Receive((SPI_HandleTypeDef *)bus->spi, buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+bool api_hal_spi_bus_tx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_Transmit((SPI_HandleTypeDef *)bus->spi, buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+bool api_hal_spi_bus_trx(const ApiHalSpiBus* bus, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) {
+    furi_assert(bus);
+    furi_assert(tx_buffer);
+    furi_assert(rx_buffer);
+    furi_assert(size > 0);
+
+    HAL_StatusTypeDef ret = HAL_SPI_TransmitReceive((SPI_HandleTypeDef *)bus->spi, tx_buffer, rx_buffer, size, HAL_MAX_DELAY);
+
+    return ret == HAL_OK;
+}
+
+const ApiHalSpiDevice* api_hal_spi_device_get(ApiHalSpiDeviceId device_id) {
+    furi_assert(device_id < ApiHalSpiDeviceIdMax);
+
+    const ApiHalSpiDevice* device = &api_hal_spi_devices[device_id];
+    assert(device);
+    api_hal_spi_bus_lock(device->bus);
+
+    if (device->config) {
+        memcpy((SPI_InitTypeDef*)&device->bus->spi->Init, device->config, sizeof(SPI_InitTypeDef));
+        if(HAL_SPI_Init((SPI_HandleTypeDef *)device->bus->spi) != HAL_OK) {
+            Error_Handler();
+        }
+        Enable_SPI((SPI_HandleTypeDef *)device->bus->spi);
+    }
+
+    return device;
+}
+
+void api_hal_spi_device_return(const ApiHalSpiDevice* device) {
+    api_hal_spi_bus_unlock(device->bus);
+}
+
+bool api_hal_spi_device_rx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_rx(device->bus, buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
+}
+
+bool api_hal_spi_device_tx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_tx(device->bus, buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
+}
+
+bool api_hal_spi_device_trx(const ApiHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout) {
+    furi_assert(device);
+    furi_assert(tx_buffer);
+    furi_assert(rx_buffer);
+    furi_assert(size > 0);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, false);
+    }
+
+    bool ret = api_hal_spi_bus_trx(device->bus, tx_buffer, rx_buffer, size, HAL_MAX_DELAY);
+
+    if (device->chip_select) {
+        hal_gpio_write(device->chip_select, true);
+    }
+
+    return ret;
 }
 
 void api_hal_spi_apply_config(const SPIDevice* device) {
     osKernelLock();
 
-    memcpy(&device->spi->Init, &device->config, sizeof(SPI_InitTypeDef));
+    memcpy((SPI_InitTypeDef*)&device->bus->spi->Init, &device->config, sizeof(SPI_InitTypeDef));
 
-    if(HAL_SPI_Init(device->spi) != HAL_OK) {
+    if(HAL_SPI_Init((SPI_HandleTypeDef*)device->bus->spi) != HAL_OK) {
         Error_Handler();
     }
 
-    Enable_SPI(device->spi);
+    Enable_SPI((SPI_HandleTypeDef*)device->bus->spi);
 
     osKernelUnlock();
 }
 
 bool api_hal_spi_config_are_actual(const SPIDevice* device) {
-    return (memcmp(&device->config, &device->spi->Init, sizeof(SPI_InitTypeDef)) == 0);
+    return (memcmp(&device->config, &device->bus->spi->Init, sizeof(SPI_InitTypeDef)) == 0);
 }
 
 void api_hal_spi_config_device(const SPIDevice* device) {
@@ -39,31 +170,11 @@ void api_hal_spi_config_device(const SPIDevice* device) {
     }
 }
 
-void api_hal_spi_lock(SPI_HandleTypeDef* spi) {
-    if(spi == &SPI_D) {
-        osMutexAcquire(spi_mutex_d, osWaitForever);
-    } else if(spi == &SPI_R) {
-        osMutexAcquire(spi_mutex_r, osWaitForever);
-    } else {
-        Error_Handler();
-    }
-}
-
-void api_hal_spi_unlock(SPI_HandleTypeDef* spi) {
-    if(spi == &SPI_D) {
-        osMutexRelease(spi_mutex_d);
-    } else if(spi == &SPI_R) {
-        osMutexRelease(spi_mutex_r);
-    } else {
-        Error_Handler();
-    }
-}
-
 void api_hal_spi_lock_device(const SPIDevice* device) {
-    api_hal_spi_lock(device->spi);
+    api_hal_spi_bus_lock(device->bus);
     api_hal_spi_config_device(device);
 }
 
 void api_hal_spi_unlock_device(const SPIDevice* device) {
-    api_hal_spi_unlock(device->spi);
-}
+    api_hal_spi_bus_unlock(device->bus);
+}

+ 76 - 6
firmware/targets/f5/api-hal/api-hal-spi.h

@@ -1,6 +1,8 @@
 #pragma once
 #include "main.h"
 #include "api-hal-spi-config.h"
+#include <api-hal-gpio.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -11,15 +13,83 @@ extern "C" {
  */
 void api_hal_spi_init();
 
-/**
- * Lock SPI bus
+/* Bus Level API */
+
+/** Lock SPI bus
+ * Takes bus mutex, if used
  */
-void api_hal_spi_lock(SPI_HandleTypeDef* spi);
+void api_hal_spi_bus_lock(const ApiHalSpiBus* bus);
 
-/**
- * Unlock SPI bus
+/** Unlock SPI bus
+ * Releases BUS mutex, if used
  */
-void api_hal_spi_unlock(SPI_HandleTypeDef* spi);
+void api_hal_spi_bus_unlock(const ApiHalSpiBus* bus);
+
+/** SPI Receive
+ * @param bus - spi bus handler
+ * @param buffer - receive buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_rx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit
+ * @param bus - spi bus handler
+ * @param buffer - transmit buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_tx(const ApiHalSpiBus* bus, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit and Receive
+ * @param bus - spi bus handlere
+ * @param tx_buffer - device handle
+ * @param rx_buffer - device handle
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_bus_trx(const ApiHalSpiBus* bus, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
+
+/* Device Level API */
+
+/** Get Device handle
+ * And lock access to the corresponding SPI BUS
+ * @param device_id - device identifier
+ * @return device handle
+ */
+const ApiHalSpiDevice* api_hal_spi_device_get(ApiHalSpiDeviceId device_id);
+
+/** Return Device handle
+ * And unlock access to the corresponding SPI BUS
+ * @param device - device handle
+ */
+void api_hal_spi_device_return(const ApiHalSpiDevice* device);
+
+/** SPI Recieve
+ * @param device - device handle
+ * @param buffer - receive buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_rx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit
+ * @param device - device handle
+ * @param buffer - transmit buffer
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_tx(const ApiHalSpiDevice* device, uint8_t* buffer, size_t size, uint32_t timeout);
+
+/** SPI Transmit and Receive
+ * @param device - device handle
+ * @param tx_buffer - device handle
+ * @param rx_buffer - device handle
+ * @param size - transaction size
+ * @param timeout - bus operation timeout in ms
+ */
+bool api_hal_spi_device_trx(const ApiHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
+
 
 /**
  * Lock SPI device bus and apply config if needed

+ 167 - 5
firmware/targets/f5/api-hal/api-hal-subghz.c

@@ -1,19 +1,181 @@
 #include "api-hal-subghz.h"
 #include <stm32wbxx_ll_gpio.h>
+#include <api-hal-gpio.h>
+#include <api-hal-spi.h>
+#include <cc1101.h>
+#include <stdio.h>
 #include "main.h"
 
-void api_hal_rf_band_set(RfBand band) {
-    if (band == RfBand1) {
+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  */
+    { CC1101_MDMCFG2,   0x30 }, // ASK/OOK, No preamble/sync
+    { CC1101_PKTCTRL0,  0x32 }, // Async, no CRC, Infinite
+    { CC1101_FREND0,    0x01 }, // OOK/ASK PATABLE
+    /* End  */
+    { 0, 0 },
+};
+
+static const uint8_t api_hal_subghz_preset_ook_async_patable[8] = {
+    0x60, 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
+    /* End */
+    { 0, 0 },
+};
+
+static const uint8_t api_hal_subghz_preset_2fsk_packet_patable[8] = {
+    0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+void api_hal_subghz_init() {
+    LL_GPIO_SetPinMode(RF_SW_0_GPIO_Port, RF_SW_0_Pin, LL_GPIO_MODE_OUTPUT);
+    LL_GPIO_SetPinSpeed(RF_SW_0_GPIO_Port, RF_SW_0_Pin, LL_GPIO_SPEED_FREQ_LOW);
+    LL_GPIO_SetPinOutputType(RF_SW_0_GPIO_Port, RF_SW_0_Pin, LL_GPIO_OUTPUT_PUSHPULL);
+    LL_GPIO_SetPinMode(RF_SW_1_GPIO_Port, RF_SW_1_Pin, LL_GPIO_MODE_OUTPUT);
+    LL_GPIO_SetPinSpeed(RF_SW_1_GPIO_Port, RF_SW_1_Pin, LL_GPIO_SPEED_FREQ_LOW);
+    LL_GPIO_SetPinOutputType(RF_SW_1_GPIO_Port, RF_SW_1_Pin, LL_GPIO_OUTPUT_PUSHPULL);
+
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    // Reset and shutdown
+    cc1101_reset(device);
+    cc1101_write_reg(device, CC1101_IOCFG0, 0x2E); // High impedance 3-state
+    cc1101_shutdown(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_dump_state() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    printf(
+        "[api_hal_subghz] cc1101 chip %d, version %d\r\n",
+        cc1101_get_partnumber(device),
+        cc1101_get_version(device)
+    );
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_load_preset(ApiHalSubGhzPreset preset) {
+    if(preset == ApiHalSubGhzPresetOokAsync) {
+        api_hal_subghz_load_registers(api_hal_subghz_preset_ook_async_regs);
+        api_hal_subghz_load_patable(api_hal_subghz_preset_ook_async_patable);
+    } 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);
+    }
+}
+
+void api_hal_subghz_load_registers(const uint8_t data[][2]) {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_reset(device);
+    uint32_t i = 0;
+    while (data[i][0]) {
+        cc1101_write_reg(device, data[i][0], data[i][1]);
+        i++;
+    }
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_load_patable(const uint8_t data[8]) {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_set_pa_table(device, data);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_write_packet(const uint8_t* data, uint8_t size) {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_flush_tx(device);
+    cc1101_write_fifo(device, data, size);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_read_packet(uint8_t* data, uint8_t size) {
+
+}
+
+void api_hal_subghz_shutdown() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    // Reset and shutdown
+    cc1101_shutdown(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_reset() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_reset(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_idle() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_switch_to_idle(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_rx() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_switch_to_rx(device);
+    api_hal_spi_device_return(device);
+}
+
+void api_hal_subghz_tx() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    cc1101_switch_to_tx(device);
+    api_hal_spi_device_return(device);
+}
+
+float api_hal_subghz_get_rssi() {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+    int32_t rssi_dec = cc1101_get_rssi(device);
+    api_hal_spi_device_return(device);
+
+    float rssi = rssi_dec;
+    if(rssi_dec >= 128) {
+        rssi = ((rssi - 256.0f) / 2.0f) - 74.0f;
+    } else {
+        rssi = (rssi / 2.0f) - 74.0f;
+    }
+
+    return rssi;
+}
+
+uint32_t api_hal_subghz_set_frequency(uint32_t value) {
+    const ApiHalSpiDevice* device = api_hal_spi_device_get(ApiHalSpiDeviceIdSubGhz);
+
+    // Compensate rounding
+    if (value % cc1101_get_frequency_step(device) > (cc1101_get_frequency_step(device) / 2)) {
+        value += cc1101_get_frequency_step(device);
+    }
+
+    uint32_t real_frequency = cc1101_set_frequency(device, value);
+    cc1101_calibrate(device);
+
+    api_hal_spi_device_return(device);
+
+    return real_frequency;
+}
+
+void api_hal_subghz_set_path(ApiHalSubGhzPath path) {
+    if (path == ApiHalSubGhzPath1) {
         LL_GPIO_ResetOutputPin(RF_SW_0_GPIO_Port, RF_SW_0_Pin);
         LL_GPIO_SetOutputPin(RF_SW_1_GPIO_Port, RF_SW_1_Pin);
-    } else if (band == RfBand2) {
+    } else if (path == ApiHalSubGhzPath2) {
         LL_GPIO_SetOutputPin(RF_SW_0_GPIO_Port, RF_SW_0_Pin);
         LL_GPIO_ResetOutputPin(RF_SW_1_GPIO_Port, RF_SW_1_Pin);
-    } else if (band == RfBand3) {
+    } else if (path == ApiHalSubGhzPath3) {
         LL_GPIO_SetOutputPin(RF_SW_0_GPIO_Port, RF_SW_0_Pin);
         LL_GPIO_SetOutputPin(RF_SW_1_GPIO_Port, RF_SW_1_Pin);
-    } else if (band == RfBandIsolation) {
+    } else if (path == ApiHalSubGhzPathIsolate) {
         LL_GPIO_ResetOutputPin(RF_SW_0_GPIO_Port, RF_SW_0_Pin);
         LL_GPIO_ResetOutputPin(RF_SW_1_GPIO_Port, RF_SW_1_Pin);
+    } else {
+
     }
 }

+ 1 - 0
firmware/targets/f5/api-hal/api-hal.c

@@ -8,4 +8,5 @@ void api_hal_init() {
     api_hal_power_init();
     api_hal_light_init();
     api_hal_vibro_init();
+    api_hal_subghz_init();
 }

+ 15 - 11
lib/ST25RFAL002/platform.c

@@ -7,6 +7,7 @@
 static osThreadAttr_t platform_irq_thread_attr;
 static volatile osThreadId_t platform_irq_thread_id = NULL;
 static volatile PlatformIrqCallback platform_irq_callback = NULL;
+static ApiHalSpiDevice* platform_st25r3916 = NULL;
 
 void nfc_isr(void* _pin, void* _ctx) {
     uint32_t pin = (uint32_t)_pin;
@@ -36,27 +37,30 @@ void platformSetIrqCallback(PlatformIrqCallback callback) {
 }
 
 HAL_StatusTypeDef platformSpiTxRx(const uint8_t *txBuf, uint8_t *rxBuf, uint16_t len) {
-    HAL_StatusTypeDef ret;
+    bool ret = false;
     if (txBuf && rxBuf) {
-        ret = HAL_SPI_TransmitReceive(&SPI_R, (uint8_t*)txBuf, rxBuf, len, HAL_MAX_DELAY);
+        ret = api_hal_spi_bus_trx(platform_st25r3916->bus, (uint8_t*)txBuf, rxBuf, len, 1000);
     } else if (txBuf) {
-        ret = HAL_SPI_Transmit(&SPI_R, (uint8_t*)txBuf, len, HAL_MAX_DELAY);
+        ret = api_hal_spi_bus_tx(platform_st25r3916->bus, (uint8_t*)txBuf, len, 1000);
     } else if (rxBuf) {
-        ret = HAL_SPI_Receive(&SPI_R, (uint8_t*)rxBuf, len, HAL_MAX_DELAY);
+        ret = api_hal_spi_bus_rx(platform_st25r3916->bus, (uint8_t*)rxBuf, len, 1000);
     }
-    
-    if(ret != HAL_OK) {
+
+    if(!ret) {
         asm("bkpt 1");
-        exit(255);
+        return HAL_ERROR;
+    } else {
+        return HAL_OK;
     }
-    return ret;
 }
 
 void platformProtectST25RComm() {
-    api_hal_spi_lock(&SPI_R);
-    NFC_SPI_Reconfigure();
+    furi_assert(platform_st25r3916 == NULL);
+    platform_st25r3916 = (ApiHalSpiDevice*)api_hal_spi_device_get(ApiHalSpiDeviceIdNfc);
 }
 
 void platformUnprotectST25RComm() {
-    api_hal_spi_unlock(&SPI_R);
+    furi_assert(platform_st25r3916);
+    api_hal_spi_device_return(platform_st25r3916);
+    platform_st25r3916 = NULL;
 }

+ 25 - 25
lib/ST25RFAL002/platform.h

@@ -58,40 +58,40 @@ void platformUnprotectST25RComm();
 
 #define platformIrqST25RSetCallback( cb )           platformSetIrqCallback(cb)
 
-#define platformProtectST25RIrqStatus()               platformProtectST25RComm()                               /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */
-#define platformUnprotectST25RIrqStatus()             platformUnprotectST25RComm()                             /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment         */
+#define platformProtectST25RIrqStatus()             platformProtectST25RComm()                          /*!< Protect unique access to IRQ status var - IRQ disable on single thread environment (MCU) ; Mutex lock on a multi thread environment */
+#define platformUnprotectST25RIrqStatus()           platformUnprotectST25RComm()                        /*!< Unprotect the IRQ status var - IRQ enable on a single thread environment (MCU) ; Mutex unlock on a multi thread environment         */
 
-#define platformLedOff( port, pin )                   api_hal_light_set(pin, 0x00)
-#define platformLedOn( port, pin )                    api_hal_light_set(pin, 0xFF)
+#define platformLedOff( port, pin )                 api_hal_light_set(pin, 0x00)
+#define platformLedOn( port, pin )                  api_hal_light_set(pin, 0xFF)
 
-#define platformGpioSet( port, pin )                  HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET)               /*!< Turns the given GPIO High                   */
-#define platformGpioClear( port, pin )                HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET)             /*!< Turns the given GPIO Low                    */
-#define platformGpioToogle( port, pin )               HAL_GPIO_TogglePin(port, pin)                            /*!< Toogles the given GPIO                      */
-#define platformGpioIsHigh( port, pin )               (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)            /*!< Checks if the given LED is High             */
-#define platformGpioIsLow( port, pin )                (!platformGpioIsHigh(port, pin))                         /*!< Checks if the given LED is Low              */
+#define platformGpioSet( port, pin )                HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET)          /*!< Turns the given GPIO High                   */
+#define platformGpioClear( port, pin )              HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET)        /*!< Turns the given GPIO Low                    */
+#define platformGpioToogle( port, pin )             HAL_GPIO_TogglePin(port, pin)                       /*!< Toogles the given GPIO                      */
+#define platformGpioIsHigh( port, pin )             (HAL_GPIO_ReadPin(port, pin) == GPIO_PIN_SET)       /*!< Checks if the given LED is High             */
+#define platformGpioIsLow( port, pin )              (!platformGpioIsHigh(port, pin))                    /*!< Checks if the given LED is Low              */
 
-#define platformTimerCreate( t )                      timerCalculateTimer(t)                                    /*!< Create a timer with the given time (ms)     */
-#define platformTimerIsExpired( timer )               timerIsExpired(timer)                                     /*!< Checks if the given timer is expired        */
-#define platformDelay( t )                            osDelay( t )                                              /*!< Performs a delay for the given time (ms)    */
+#define platformTimerCreate( t )                    timerCalculateTimer(t)                              /*!< Create a timer with the given time (ms)     */
+#define platformTimerIsExpired( timer )             timerIsExpired(timer)                               /*!< Checks if the given timer is expired        */
+#define platformDelay( t )                          osDelay( t )                                        /*!< Performs a delay for the given time (ms)    */
 
-#define platformGetSysTick()                          osKernelGetTickCount()                                    /*!< Get System Tick (1 tick = 1 ms)             */
+#define platformGetSysTick()                        osKernelGetTickCount()                              /*!< Get System Tick (1 tick = 1 ms)             */
 
-#define platformAssert( exp )                         assert_param( exp )                                      /*!< Asserts whether the given expression is true*/
-// #define platformErrorHandle()                         Error_Handler()                                           /*!< Global error handle\trap                    */
+#define platformAssert( exp )                       assert_param( exp )                                 /*!< Asserts whether the given expression is true*/
+#define platformErrorHandle()                       Error_Handler()                                     /*!< Global error handle\trap                    */
 
-#define platformSpiSelect()                           platformGpioClear( ST25R_SS_PORT, ST25R_SS_PIN )         /*!< SPI SS\CS: Chip|Slave Select                */
-#define platformSpiDeselect()                         platformGpioSet( ST25R_SS_PORT, ST25R_SS_PIN )           /*!< SPI SS\CS: Chip|Slave Deselect              */
+#define platformSpiSelect()                         platformGpioClear( ST25R_SS_PORT, ST25R_SS_PIN )    /*!< SPI SS\CS: Chip|Slave Select                */
+#define platformSpiDeselect()                       platformGpioSet( ST25R_SS_PORT, ST25R_SS_PIN )      /*!< SPI SS\CS: Chip|Slave Deselect              */
 
 
-#define platformI2CTx( txBuf, len, last, txOnly )                                                              /*!< I2C Transmit                                */
-#define platformI2CRx( txBuf, len )                                                                            /*!< I2C Receive                                 */
-#define platformI2CStart()                                                                                     /*!< I2C Start condition                         */
-#define platformI2CStop()                                                                                      /*!< I2C Stop condition                          */
-#define platformI2CRepeatStart()                                                                               /*!< I2C Repeat Start                            */
-#define platformI2CSlaveAddrWR(add)                                                                            /*!< I2C Slave address for Write operation       */
-#define platformI2CSlaveAddrRD(add)                                                                            /*!< I2C Slave address for Read operation        */
+#define platformI2CTx( txBuf, len, last, txOnly )                                                       /*!< I2C Transmit                                */
+#define platformI2CRx( txBuf, len )                                                                     /*!< I2C Receive                                 */
+#define platformI2CStart()                                                                              /*!< I2C Start condition                         */
+#define platformI2CStop()                                                                               /*!< I2C Stop condition                          */
+#define platformI2CRepeatStart()                                                                        /*!< I2C Repeat Start                            */
+#define platformI2CSlaveAddrWR(add)                                                                     /*!< I2C Slave address for Write operation       */
+#define platformI2CSlaveAddrRD(add)                                                                     /*!< I2C Slave address for Read operation        */
 
-#define platformLog(...)                                                                                       /*!< Log  method                                 */
+#define platformLog(...)                                                                                /*!< Log  method                                 */
 
 /*
  ******************************************************************************

+ 171 - 0
lib/drivers/cc1101.c

@@ -0,0 +1,171 @@
+#include "cc1101.h"
+#include <cmsis_os2.h>
+#include <api-hal-delay.h>
+#include <assert.h>
+#include <string.h>
+
+CC1101Status cc1101_strobe(const ApiHalSpiDevice* device, uint8_t strobe) {
+    uint8_t tx[1] = { strobe };
+    CC1101Status rx[1] = { 0 };
+
+    hal_gpio_write(device->chip_select, false);
+    while(hal_gpio_read(device->bus->miso));
+    api_hal_spi_bus_trx(device->bus, tx, (uint8_t*)rx, 1, CC1101_TIMEOUT);
+    hal_gpio_write(device->chip_select, true);
+
+    assert(rx[0].CHIP_RDYn == 0);
+    return rx[0];
+}
+
+CC1101Status cc1101_write_reg(const ApiHalSpiDevice* device, uint8_t reg, uint8_t data) {
+    uint8_t tx[2] = { reg, data };
+    CC1101Status rx[2] = { 0 };
+
+    hal_gpio_write(device->chip_select, false);
+    while(hal_gpio_read(device->bus->miso));
+    api_hal_spi_bus_trx(device->bus, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
+    hal_gpio_write(device->chip_select, true);
+
+    assert((rx[0].CHIP_RDYn|rx[1].CHIP_RDYn) == 0);
+    return rx[1];
+}
+
+CC1101Status cc1101_read_reg(const ApiHalSpiDevice* device, uint8_t reg, uint8_t* data) {
+    assert(sizeof(CC1101Status) == 1);
+    uint8_t tx[2] = { reg|CC1101_READ, 0};
+    CC1101Status rx[2] = { 0 };
+
+    hal_gpio_write(device->chip_select, false);
+    while(hal_gpio_read(device->bus->miso));
+    api_hal_spi_bus_trx(device->bus, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
+    hal_gpio_write(device->chip_select, true);
+
+    assert((rx[0].CHIP_RDYn) == 0);
+    *data = *(uint8_t*)&rx[1];
+    return rx[0];
+}
+
+uint8_t cc1101_get_partnumber(const ApiHalSpiDevice* device) {
+    uint8_t partnumber=0;
+    cc1101_read_reg(device, CC1101_STATUS_PARTNUM|CC1101_BURST, &partnumber);
+    return partnumber;
+}
+
+uint8_t cc1101_get_version(const ApiHalSpiDevice* device) {
+    uint8_t version=0;
+    cc1101_read_reg(device, CC1101_STATUS_VERSION|CC1101_BURST, &version);
+    return version;
+}
+
+uint8_t cc1101_get_rssi(const ApiHalSpiDevice* device) {
+    uint8_t rssi=0;
+    cc1101_read_reg(device, CC1101_STATUS_RSSI|CC1101_BURST, &rssi);
+    return rssi;
+}
+
+void cc1101_reset(const ApiHalSpiDevice* device) {
+    hal_gpio_write(device->chip_select, false);
+    delay_us(1000);
+    hal_gpio_write(device->chip_select, true);
+    delay_us(1000);
+    cc1101_strobe(device, CC1101_STROBE_SRES);
+}
+
+void cc1101_shutdown(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SPWD);
+}
+
+void cc1101_calibrate(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SCAL);
+}
+
+void cc1101_switch_to_idle(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SIDLE);
+}
+
+void cc1101_switch_to_rx(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SRX);
+}
+
+void cc1101_switch_to_tx(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_STX);
+}
+
+void cc1101_flush_rx(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SFRX);
+}
+
+void cc1101_flush_tx(const ApiHalSpiDevice* device) {
+    cc1101_strobe(device, CC1101_STROBE_SFTX);
+}
+
+uint32_t cc1101_set_frequency(const ApiHalSpiDevice* device, uint32_t value) {
+    uint64_t real_value = (uint64_t)value * 0xFFFF / CC1101_QUARTZ;
+
+    // Sanity check
+    assert((real_value & 0xFFFFFF) == real_value);
+
+    cc1101_write_reg(device, CC1101_FREQ2, (real_value >> 16) & 0xFF);
+    cc1101_write_reg(device, CC1101_FREQ1, (real_value >> 8 ) & 0xFF);
+    cc1101_write_reg(device, CC1101_FREQ0, (real_value >> 0 ) & 0xFF);
+
+    uint64_t real_frequency = real_value * CC1101_QUARTZ / 0xFFFF;
+
+    return (uint32_t)real_frequency;
+}
+
+uint32_t cc1101_get_frequency_step(const ApiHalSpiDevice* device) {
+    return CC1101_QUARTZ / 0xFFFF;
+}
+
+uint32_t cc1101_set_frequency_offset(const ApiHalSpiDevice* device, uint32_t value) {
+    uint64_t real_value = value * 0x4000 / CC1101_QUARTZ;
+    assert((real_value & 0xFF) == real_value);
+
+    cc1101_write_reg(device, CC1101_FSCTRL0, (real_value >> 0 ) & 0xFF);
+
+    uint64_t real_frequency = real_value * CC1101_QUARTZ / 0x4000;
+
+    return (uint32_t)real_frequency;
+}
+
+uint32_t cc1101_get_frequency_offset_step(const ApiHalSpiDevice* device) {
+    return CC1101_QUARTZ / 0x4000;
+}
+
+void cc1101_set_pa_table(const ApiHalSpiDevice* device, const uint8_t value[8]) {
+    uint8_t tx[9] = { CC1101_PATABLE | CC1101_BURST };
+    CC1101Status rx[9] = { 0 };
+
+    memcpy(&tx[1], &value[0], 8);
+
+    hal_gpio_write(device->chip_select, false);
+    while(hal_gpio_read(device->bus->miso));
+    api_hal_spi_bus_trx(device->bus, tx, (uint8_t*)rx, 2, CC1101_TIMEOUT);
+    hal_gpio_write(device->chip_select, true);
+
+    assert((rx[0].CHIP_RDYn|rx[8].CHIP_RDYn) == 0);
+}
+
+uint8_t cc1101_write_fifo(const ApiHalSpiDevice* device, const uint8_t* data, uint8_t size) {
+    uint8_t tx = CC1101_FIFO | CC1101_BURST;
+    CC1101Status rx = { 0 };
+
+    // Start transaction
+    hal_gpio_write(device->chip_select, false);
+    // Wait IC to become ready
+    while(hal_gpio_read(device->bus->miso));
+    // Tell IC what we want
+    api_hal_spi_bus_trx(device->bus, &tx, (uint8_t*)&rx, 1, CC1101_TIMEOUT);
+    assert((rx.CHIP_RDYn) == 0);
+    // Transmit data
+    api_hal_spi_bus_tx(device->bus, (uint8_t*)data, size, CC1101_TIMEOUT);
+    // Finish transaction
+    hal_gpio_write(device->chip_select, true);
+
+    return size;
+}
+
+uint8_t cc1101_read_fifo(const ApiHalSpiDevice* device, uint8_t* data, uint8_t size) {
+    return size;
+}

+ 153 - 0
lib/drivers/cc1101.h

@@ -0,0 +1,153 @@
+#pragma once
+
+#include "cc1101_regs.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <api-hal-spi.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Low level API */
+
+/** Strobe command to the device
+ * @param device - pointer to ApiHalSpiDevice
+ * @param strobe - command to execute
+ * @return device status
+ */
+CC1101Status cc1101_strobe(const ApiHalSpiDevice* device, uint8_t strobe);
+
+/** Write device register
+ * @param device - pointer to ApiHalSpiDevice
+ * @param reg - register
+ * @param data - data to write
+ * @return device status
+ */
+CC1101Status cc1101_write_reg(const ApiHalSpiDevice* device, uint8_t reg, uint8_t data);
+
+/** Read device register
+ * @param device - pointer to ApiHalSpiDevice
+ * @param reg - register
+ * @param[out] data - pointer to data
+ * @return device status
+ */
+CC1101Status cc1101_read_reg(const ApiHalSpiDevice* device, uint8_t reg, uint8_t* data);
+
+/* High level API */
+
+/** Reset
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_reset(const ApiHalSpiDevice* device);
+
+/** Enable shutdown mode
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_shutdown(const ApiHalSpiDevice* device);
+
+/** Get Partnumber
+ * @param device - pointer to ApiHalSpiDevice
+ */
+uint8_t cc1101_get_partnumber(const ApiHalSpiDevice* device);
+
+/** Get Version
+ * @param device - pointer to ApiHalSpiDevice
+ */
+uint8_t cc1101_get_version(const ApiHalSpiDevice* device);
+
+/** Get raw RSSI value
+ * @param device - pointer to ApiHalSpiDevice
+ */
+uint8_t cc1101_get_rssi(const ApiHalSpiDevice* device);
+
+/** Calibrate oscillator
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_calibrate(const ApiHalSpiDevice* device);
+
+/** Switch to idle
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_switch_to_idle(const ApiHalSpiDevice* device);
+
+/** Switch to RX
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_switch_to_rx(const ApiHalSpiDevice* device);
+
+/** Switch to TX
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_switch_to_tx(const ApiHalSpiDevice* device);
+
+/** Flush RX FIFO
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_flush_rx(const ApiHalSpiDevice* device);
+
+/** Flush TX FIFO
+ * @param device - pointer to ApiHalSpiDevice
+ */
+void cc1101_flush_tx(const ApiHalSpiDevice* device);
+
+/** Set Frequency
+ * Is not 100% precise, depends on quartz used
+ * @param device - pointer to ApiHalSpiDevice
+ * @param value - frequency in herz
+ * @return real frequency that were set
+ */
+uint32_t cc1101_set_frequency(const ApiHalSpiDevice* device, uint32_t value);
+
+/** Get Frequency Step
+ * @param device - pointer to ApiHalSpiDevice
+ * @return frequency step
+ */
+uint32_t cc1101_get_frequency_step(const ApiHalSpiDevice* device);
+
+/** Set Frequency Offset
+ * Is not 100% precise, depends on quartz used
+ * @param device - pointer to ApiHalSpiDevice
+ * @param value - frequency offset in herz
+ * @return real frequency that were set
+ */
+uint32_t cc1101_set_frequency_offset(const ApiHalSpiDevice* device, uint32_t value);
+
+/** Get Frequency Offset Step
+ * @param device - pointer to ApiHalSpiDevice
+ * @return frequency offset step
+ */
+uint32_t cc1101_get_frequency_offset_step(const ApiHalSpiDevice* device);
+
+/** Set Power Amplifier level table, ramp
+ * @param device - pointer to ApiHalSpiDevice
+ * @param value - array of power level values
+ */
+void cc1101_set_pa_table(const ApiHalSpiDevice* device, const uint8_t value[8]);
+
+/** Set Power Amplifier level table, ramp
+ * @param device - pointer to ApiHalSpiDevice
+ * @param value - array of power level values
+ */
+void cc1101_set_pa_table(const ApiHalSpiDevice* device, const uint8_t value[8]);
+
+/** Write FIFO
+ * @param device - pointer to ApiHalSpiDevice
+ * @param data, pointer to byte array
+ * @param size, write bytes count
+ * @return size, written bytes count
+ */
+uint8_t cc1101_write_fifo(const ApiHalSpiDevice* device, const uint8_t* data, uint8_t size);
+
+/** Read FIFO
+ * @param device - pointer to ApiHalSpiDevice
+ * @param data, pointer to byte array
+ * @param size, bytes to read from fifo
+ * @return size, read bytes count
+ */
+uint8_t cc1101_read_fifo(const ApiHalSpiDevice* device, uint8_t* data, uint8_t size);
+
+#ifdef __cplusplus
+}
+#endif

+ 131 - 0
lib/drivers/cc1101_regs.h

@@ -0,0 +1,131 @@
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CC1101_QUARTZ                   26000000
+
+#define CC1101_TIMEOUT                  500
+
+#define CC1101_READ                     (1<<7)  /** Read Bit */
+#define CC1101_BURST                    (1<<6)  /** Burst Bit */
+
+/* Common registers, CC1101_BURST and CC1101_WRITE behaves as expected  */
+#define CC1101_IOCFG2                   0x00    /** GDO2 output pin configuration */
+#define CC1101_IOCFG1                   0x01    /** GDO1 output pin configuration */
+#define CC1101_IOCFG0                   0x02    /** GDO0 output pin configuration */
+#define CC1101_FIFOTHR                  0x03    /** RX FIFO and TX FIFO thresholds */
+#define CC1101_SYNC1                    0x04    /** Sync word, high byte */
+#define CC1101_SYNC0                    0x05    /** Sync word, low byte */
+#define CC1101_PKTLEN                   0x06    /** Packet length */
+#define CC1101_PKTCTRL1                 0x07    /** Packet automation control */
+#define CC1101_PKTCTRL0                 0x08    /** Packet automation control */
+#define CC1101_ADDR                     0x09    /** Device address */
+#define CC1101_CHANNR                   0x0A    /** Channel number */
+#define CC1101_FSCTRL1                  0x0B    /** Frequency synthesizer control */
+#define CC1101_FSCTRL0                  0x0C    /** Frequency synthesizer control */
+#define CC1101_FREQ2                    0x0D    /** Frequency control word, high byte */
+#define CC1101_FREQ1                    0x0E    /** Frequency control word, middle byte */
+#define CC1101_FREQ0                    0x0F    /** Frequency control word, low byte */
+#define CC1101_MDMCFG4                  0x10    /** Modem configuration */
+#define CC1101_MDMCFG3                  0x11    /** Modem configuration */
+#define CC1101_MDMCFG2                  0x12    /** Modem configuration */
+#define CC1101_MDMCFG1                  0x13    /** Modem configuration */
+#define CC1101_MDMCFG0                  0x14    /** Modem configuration */
+#define CC1101_DEVIATN                  0x15    /** Modem deviation setting */
+#define CC1101_MCSM2                    0x16    /** Main Radio Control State Machine configuration */
+#define CC1101_MCSM1                    0x17    /** Main Radio Control State Machine configuration */
+#define CC1101_MCSM0                    0x18    /** Main Radio Control State Machine configuration */
+#define CC1101_FOCCFG                   0x19    /** Frequency Offset Compensation configuration */
+#define CC1101_BSCFG                    0x1A    /** Bit Synchronization configuration */
+#define CC1101_AGCTRL2                  0x1B    /** AGC control */
+#define CC1101_AGCTRL1                  0x1C    /** AGC control */
+#define CC1101_AGCTRL0                  0x1D    /** AGC control */
+#define CC1101_WOREVT1                  0x1E    /** High byte Event 0 timeout */
+#define CC1101_WOREVT0                  0x1F    /** Low byte Event 0 timeout */
+#define CC1101_WORCTRL                  0x20    /** Wake On Radio control */
+#define CC1101_FREND1                   0x21    /** Front end RX configuration */
+#define CC1101_FREND0                   0x22    /** Front end TX configuration */
+#define CC1101_FSCAL3                   0x23    /** Frequency synthesizer calibration */
+#define CC1101_FSCAL2                   0x24    /** Frequency synthesizer calibration */
+#define CC1101_FSCAL1                   0x25    /** Frequency synthesizer calibration */
+#define CC1101_FSCAL0                   0x26    /** Frequency synthesizer calibration */
+#define CC1101_RCCTRL1                  0x27    /** RC oscillator configuration */
+#define CC1101_RCCTRL0                  0x28    /** RC oscillator configuration */
+#define CC1101_FSTEST                   0x29    /** Frequency synthesizer calibration control */
+#define CC1101_PTEST                    0x2A    /** Production test */
+#define CC1101_AGCTEST                  0x2B    /** AGC test */
+#define CC1101_TEST2                    0x2C    /** Various test settings */
+#define CC1101_TEST1                    0x2D    /** Various test settings */
+#define CC1101_TEST0                    0x2E    /** Various test settings */
+
+/* Strobe registers, CC1101_BURST is not available, CC1101_WRITE ignored */
+#define CC1101_STROBE_SRES              0x30    /** Reset chip. */
+#define CC1101_STROBE_SFSTXON           0x31    /** Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround). */
+#define CC1101_STROBE_SXOFF             0x32    /** Turn off crystal oscillator. */
+#define CC1101_STROBE_SCAL              0x33    /** Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without setting manual calibration mode (MCSM0.FS_AUTOCAL=0) */
+#define CC1101_STROBE_SRX               0x34    /** Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1. */
+#define CC1101_STROBE_STX               0x35    /** In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: Only go to TX if channel is clear. */
+#define CC1101_STROBE_SIDLE             0x36    /** Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable. */
+#define CC1101_STROBE_SWOR              0x38    /** Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0. */
+/* 0x37 is unused */
+#define CC1101_STROBE_SPWD              0x39    /** Enter power down mode when CSn goes high. */
+#define CC1101_STROBE_SFRX              0x3A    /** Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states. */
+#define CC1101_STROBE_SFTX              0x3B    /** Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states. */
+#define CC1101_STROBE_SWORRST           0x3C    /** Reset real time clock to Event1 value. */
+#define CC1101_STROBE_SNOP              0x3D    /** No operation. May be used to get access to the chip status byte.*/
+
+/* Status registers, must be accessed with CC1101_BURST, but one by one */
+#define CC1101_STATUS_PARTNUM           0x30    /** Chip ID Part Number */
+#define CC1101_STATUS_VERSION           0x31    /** Chip ID Version */
+#define CC1101_STATUS_FREQEST           0x32    /** Frequency Offset Estimate from Demodulator */
+#define CC1101_STATUS_LQI               0x33    /** Demodulator Estimate for Link Quality */
+#define CC1101_STATUS_RSSI              0x34    /** Received Signal Strength Indication */
+#define CC1101_STATUS_MARCSTATE         0x35    /** Main Radio Control State Machine State */
+#define CC1101_STATUS_WORTIME1          0x36    /** High Byte of WOR Time */
+#define CC1101_STATUS_WORTIME0          0x37    /** Low Byte of WOR Time */
+#define CC1101_STATUS_PKTSTATUS         0x38    /** Current GDOx Status and Packet Status */
+#define CC1101_STATUS_VCO_VC_DAC        0x39    /** Current Setting from PLL Calibration Module */
+#define CC1101_STATUS_TXBYTES           0x3A    /** Underflow and Number of Bytes */
+#define CC1101_STATUS_RXBYTES           0x3B    /** Overflow and Number of Bytes */
+#define CC1101_STATUS_RCCTRL1_STATUS    0x3C    /** Last RC Oscillator Calibration Result */
+#define CC1101_STATUS_RCCTRL0_STATUS    0x3D    /** Last RC Oscillator Calibration Result */
+
+/* Some special registers, use CC1101_BURST to read/write data */
+#define CC1101_PATABLE                  0x3E    /** PATABLE register number, an 8-byte table that defines the PA control settings */
+#define CC1101_FIFO                     0x3F    /** FIFO register nunmber, can be combined with CC1101_WRITE and/or CC1101_BURST */
+
+typedef enum {
+    CC1101StateIDLE=0b000,              /** IDLE state */
+    CC1101StateRX=0b001,                /** Receive mode */
+    CC1101StateTX=0b010,                /** Transmit mode */
+    CC1101StateFSTXON=0b011,            /** Fast TX ready */
+    CC1101StateCALIBRATE=0b100,         /** Frequency synthesizer calibration is running */
+    CC1101StateSETTLING=0b101,          /** PLL is settling */
+    CC1101StateRXFIFO_OVERFLOW=0b110,   /** RX FIFO has overflowed. Read out any useful data, then flush the FIFO with SFRX */
+    CC1101StateTXFIFO_UNDERFLOW=0b111,  /** TX FIFO has underflowed. Acknowledge with SFTX */
+} CC1101State;
+
+typedef struct {
+    uint8_t FIFO_BYTES_AVAILABLE:4;
+    CC1101State STATE:3;
+    bool CHIP_RDYn:1;
+} CC1101Status;
+
+typedef struct {
+    uint8_t NUM_TXBYTES:7;
+    bool TXFIFO_UNDERFLOW:1;
+} CC1101TxBytes;
+
+typedef struct {
+    uint8_t NUM_RXBYTES:7;
+    bool RXFIFO_OVERFLOW:1;
+} CC1101RxBytes;
+
+#ifdef __cplusplus
+}
+#endif