Rename api-hal to furi-hal (#629)
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
#include <furi-hal-boot.h>
|
||||
#include <stm32wbxx_ll_rtc.h>
|
||||
#include <furi.h>
|
||||
|
||||
// Boot request enum
|
||||
#define BOOT_REQUEST_TAINTED 0x00000000
|
||||
#define BOOT_REQUEST_CLEAN 0xDADEDADE
|
||||
#define BOOT_REQUEST_DFU 0xDF00B000
|
||||
|
||||
void furi_hal_boot_init() {
|
||||
#ifndef DEBUG
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_TAINTED);
|
||||
#endif
|
||||
FURI_LOG_I("FuriHalBoot", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_boot_set_mode(FuriHalBootMode mode) {
|
||||
if (mode == FuriHalBootModeNormal) {
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_CLEAN);
|
||||
} else if (mode == FuriHalBootModeDFU) {
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_DFU);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_boot_set_flags(FuriHalBootFlag flags) {
|
||||
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR2, flags);
|
||||
}
|
||||
|
||||
FuriHalBootFlag furi_hal_boot_get_flags() {
|
||||
return LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR2);
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
#include <furi-hal-bt.h>
|
||||
#include <app_entry.h>
|
||||
#include <ble.h>
|
||||
#include <stm32wbxx.h>
|
||||
#include <shci.h>
|
||||
#include <cmsis_os2.h>
|
||||
#include <app_ble.h>
|
||||
|
||||
void furi_hal_bt_init() {
|
||||
// Explicitly tell that we are in charge of CLK48 domain
|
||||
HAL_HSEM_FastTake(CFG_HW_CLK48_CONFIG_SEMID);
|
||||
// Start Core2, init HCI and start GAP/GATT
|
||||
APPE_Init();
|
||||
}
|
||||
|
||||
bool furi_hal_bt_start_app() {
|
||||
return APP_BLE_Start();
|
||||
}
|
||||
|
||||
void furi_hal_bt_dump_state(string_t buffer) {
|
||||
BleGlueStatus status = APPE_Status();
|
||||
if (status == BleGlueStatusStarted) {
|
||||
uint8_t HCI_Version;
|
||||
uint16_t HCI_Revision;
|
||||
uint8_t LMP_PAL_Version;
|
||||
uint16_t Manufacturer_Name;
|
||||
uint16_t LMP_PAL_Subversion;
|
||||
|
||||
tBleStatus ret = hci_read_local_version_information(
|
||||
&HCI_Version, &HCI_Revision, &LMP_PAL_Version, &Manufacturer_Name, &LMP_PAL_Subversion
|
||||
);
|
||||
|
||||
string_cat_printf(buffer,
|
||||
"Ret: %d, HCI_Version: %d, HCI_Revision: %d, LMP_PAL_Version: %d, Manufacturer_Name: %d, LMP_PAL_Subversion: %d",
|
||||
ret, HCI_Version, HCI_Revision, LMP_PAL_Version, Manufacturer_Name, LMP_PAL_Subversion
|
||||
);
|
||||
} else {
|
||||
string_cat_printf(buffer, "BLE not ready");
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_hal_bt_is_alive() {
|
||||
return APPE_Status() == BleGlueStatusStarted;
|
||||
}
|
||||
|
||||
bool furi_hal_bt_wait_transition() {
|
||||
uint8_t counter = 0;
|
||||
while (APPE_Status() == BleGlueStatusStartup) {
|
||||
osDelay(10);
|
||||
counter++;
|
||||
if (counter > 1000) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_bt_lock_flash() {
|
||||
if (!furi_hal_bt_wait_transition()) {
|
||||
return false;
|
||||
}
|
||||
if (APPE_Status() == BleGlueStatusUninitialized) {
|
||||
HAL_FLASH_Unlock();
|
||||
} else {
|
||||
while (HAL_HSEM_FastTake(CFG_HW_FLASH_SEMID) != HAL_OK) {
|
||||
osDelay(1);
|
||||
}
|
||||
SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_ON);
|
||||
HAL_FLASH_Unlock();
|
||||
while(LL_FLASH_IsOperationSuspended()) {};
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void furi_hal_bt_unlock_flash() {
|
||||
if (APPE_Status() == BleGlueStatusUninitialized) {
|
||||
HAL_FLASH_Lock();
|
||||
} else {
|
||||
SHCI_C2_FLASH_EraseActivity(ERASE_ACTIVITY_OFF);
|
||||
HAL_FLASH_Lock();
|
||||
HAL_HSEM_Release(CFG_HW_FLASH_SEMID, HSEM_CPU1_COREID);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_bt_start_tone_tx(uint8_t channel, uint8_t power) {
|
||||
aci_hal_set_tx_power_level(0, power);
|
||||
aci_hal_tone_start(channel, 0);
|
||||
}
|
||||
|
||||
void furi_hal_bt_stop_tone_tx() {
|
||||
aci_hal_tone_stop();
|
||||
}
|
||||
|
||||
void furi_hal_bt_start_packet_tx(uint8_t channel, uint8_t pattern, uint8_t datarate) {
|
||||
hci_le_enhanced_transmitter_test(channel, 0x25, pattern, datarate);
|
||||
}
|
||||
|
||||
void furi_hal_bt_start_packet_rx(uint8_t channel, uint8_t datarate) {
|
||||
hci_le_enhanced_receiver_test(channel, datarate, 0);
|
||||
}
|
||||
|
||||
uint16_t furi_hal_bt_stop_packet_test() {
|
||||
uint16_t num_of_packets;
|
||||
hci_le_test_end(&num_of_packets);
|
||||
return num_of_packets;
|
||||
}
|
||||
|
||||
void furi_hal_bt_start_rx(uint8_t channel) {
|
||||
aci_hal_rx_start(channel);
|
||||
}
|
||||
|
||||
float furi_hal_bt_get_rssi() {
|
||||
float val;
|
||||
uint8_t rssi_raw[3];
|
||||
|
||||
if (aci_hal_read_raw_rssi(rssi_raw) != BLE_STATUS_SUCCESS) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Some ST magic with rssi
|
||||
uint8_t agc = rssi_raw[2] & 0xFF;
|
||||
int rssi = (((int)rssi_raw[1] << 8) & 0xFF00) + (rssi_raw[0] & 0xFF);
|
||||
if(rssi == 0 || agc > 11) {
|
||||
val = -127.0;
|
||||
} else {
|
||||
val = agc * 6.0f - 127.0f;
|
||||
while(rssi > 30) {
|
||||
val += 6.0;
|
||||
rssi >>=1;
|
||||
}
|
||||
val += (417 * rssi + 18080) >> 10;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
uint32_t furi_hal_bt_get_transmitted_packets() {
|
||||
uint32_t packets = 0;
|
||||
aci_hal_le_tx_test_packet_number(&packets);
|
||||
return packets;
|
||||
}
|
||||
|
||||
void furi_hal_bt_stop_rx() {
|
||||
aci_hal_rx_stop();
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#include <furi-hal-clock.h>
|
||||
|
||||
#include <main.h>
|
||||
#include <stm32wbxx_ll_pwr.h>
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_utils.h>
|
||||
|
||||
#define HS_CLOCK_IS_READY() (LL_RCC_HSE_IsReady() && LL_RCC_HSI_IsReady())
|
||||
#define LS_CLOCK_IS_READY() (LL_RCC_LSE_IsReady() && LL_RCC_LSI1_IsReady())
|
||||
|
||||
void furi_hal_clock_init() {
|
||||
/* Prepare Flash memory for 64mHz system clock */
|
||||
LL_FLASH_SetLatency(LL_FLASH_LATENCY_3);
|
||||
while(LL_FLASH_GetLatency() != LL_FLASH_LATENCY_3);
|
||||
|
||||
/* HSE and HSI configuration and activation */
|
||||
LL_RCC_HSE_SetCapacitorTuning(0x26);
|
||||
LL_RCC_HSE_Enable();
|
||||
LL_RCC_HSI_Enable();
|
||||
while(!HS_CLOCK_IS_READY());
|
||||
LL_RCC_HSE_EnableCSS();
|
||||
|
||||
/* LSE and LSI1 configuration and activation */
|
||||
LL_PWR_EnableBkUpAccess();
|
||||
LL_RCC_LSE_SetDriveCapability(LL_RCC_LSEDRIVE_HIGH);
|
||||
LL_RCC_LSE_Enable();
|
||||
LL_RCC_LSI1_Enable();
|
||||
while(!LS_CLOCK_IS_READY());
|
||||
LL_EXTI_EnableIT_0_31(LL_EXTI_LINE_18); /* Why? Because that's why. See RM0434, Table 61. CPU1 vector table. */
|
||||
LL_EXTI_EnableRisingTrig_0_31(LL_EXTI_LINE_18);
|
||||
LL_RCC_EnableIT_LSECSS();
|
||||
LL_RCC_LSE_EnableCSS();
|
||||
|
||||
/* Main PLL configuration and activation */
|
||||
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 8, LL_RCC_PLLR_DIV_2);
|
||||
LL_RCC_PLL_Enable();
|
||||
LL_RCC_PLL_EnableDomain_SYS();
|
||||
while(LL_RCC_PLL_IsReady() != 1);
|
||||
|
||||
LL_RCC_PLLSAI1_ConfigDomain_48M(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1Q_DIV_2);
|
||||
LL_RCC_PLLSAI1_ConfigDomain_ADC(LL_RCC_PLLSOURCE_HSE, LL_RCC_PLLM_DIV_2, 6, LL_RCC_PLLSAI1R_DIV_2);
|
||||
LL_RCC_PLLSAI1_Enable();
|
||||
LL_RCC_PLLSAI1_EnableDomain_48M();
|
||||
LL_RCC_PLLSAI1_EnableDomain_ADC();
|
||||
while(LL_RCC_PLLSAI1_IsReady() != 1);
|
||||
|
||||
/* Sysclk activation on the main PLL */
|
||||
/* Set CPU1 prescaler*/
|
||||
LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
/* Set CPU2 prescaler*/
|
||||
LL_C2_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_2);
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
||||
while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL);
|
||||
|
||||
/* Set AHB SHARED prescaler*/
|
||||
LL_RCC_SetAHB4Prescaler(LL_RCC_SYSCLK_DIV_1);
|
||||
|
||||
/* Set APB1 prescaler*/
|
||||
LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
|
||||
|
||||
/* Set APB2 prescaler*/
|
||||
LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
|
||||
|
||||
/* Disable MSI */
|
||||
LL_RCC_MSI_Disable();
|
||||
while(LL_RCC_MSI_IsReady() != 0);
|
||||
|
||||
/* Update CMSIS variable (which can be updated also through SystemCoreClockUpdate function) */
|
||||
LL_SetSystemCoreClock(64000000);
|
||||
|
||||
/* Update the time base */
|
||||
if (HAL_InitTick (TICK_INT_PRIORITY) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
if(LL_RCC_GetRTCClockSource() != LL_RCC_RTC_CLKSOURCE_LSE) {
|
||||
LL_RCC_ForceBackupDomainReset();
|
||||
LL_RCC_ReleaseBackupDomainReset();
|
||||
LL_RCC_SetRTCClockSource(LL_RCC_RTC_CLKSOURCE_LSE);
|
||||
}
|
||||
|
||||
LL_RCC_EnableRTC();
|
||||
|
||||
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
|
||||
LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_PLLSAI1);
|
||||
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
|
||||
LL_RCC_SetRNGClockSource(LL_RCC_RNG_CLKSOURCE_CLK48);
|
||||
LL_RCC_SetUSBClockSource(LL_RCC_USB_CLKSOURCE_PLLSAI1);
|
||||
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
|
||||
LL_RCC_SetSMPSPrescaler(LL_RCC_SMPS_DIV_1);
|
||||
LL_RCC_SetRFWKPClockSource(LL_RCC_RFWKP_CLKSOURCE_LSE);
|
||||
|
||||
// AHB1
|
||||
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMAMUX1);
|
||||
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_DMA1);
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);
|
||||
|
||||
// AHB2
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOC);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOD);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOE);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOH);
|
||||
|
||||
// APB1
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_RTCAPB);
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
|
||||
// APB2
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_USART1);
|
||||
}
|
||||
|
||||
void furi_hal_clock_switch_to_hsi() {
|
||||
LL_RCC_HSI_Enable( );
|
||||
|
||||
while(!LL_RCC_HSI_IsReady());
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
|
||||
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI);
|
||||
|
||||
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI);
|
||||
}
|
||||
|
||||
void furi_hal_clock_switch_to_pll() {
|
||||
LL_RCC_HSE_Enable();
|
||||
LL_RCC_PLL_Enable();
|
||||
|
||||
while(!LL_RCC_HSE_IsReady());
|
||||
while(!LL_RCC_PLL_IsReady());
|
||||
|
||||
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);
|
||||
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
|
||||
|
||||
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
/** Initialize clocks */
|
||||
void furi_hal_clock_init();
|
||||
|
||||
/** Switch to HSI clock */
|
||||
void furi_hal_clock_switch_to_hsi();
|
||||
|
||||
/** Switch to PLL clock */
|
||||
void furi_hal_clock_switch_to_pll();
|
||||
@@ -0,0 +1,58 @@
|
||||
#include <furi-hal-console.h>
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_usart.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
volatile bool furi_hal_console_alive = false;
|
||||
|
||||
void furi_hal_console_init() {
|
||||
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
GPIO_InitStruct.Pin = LL_GPIO_PIN_6|LL_GPIO_PIN_7;
|
||||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
|
||||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
GPIO_InitStruct.Alternate = LL_GPIO_AF_7;
|
||||
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
|
||||
|
||||
LL_USART_InitTypeDef USART_InitStruct = {0};
|
||||
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
|
||||
USART_InitStruct.BaudRate = 230400;
|
||||
USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_8B;
|
||||
USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
|
||||
USART_InitStruct.Parity = LL_USART_PARITY_NONE;
|
||||
USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX;
|
||||
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
|
||||
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
|
||||
LL_USART_Init(USART1, &USART_InitStruct);
|
||||
LL_USART_SetTXFIFOThreshold(USART1, LL_USART_FIFOTHRESHOLD_1_2);
|
||||
LL_USART_EnableFIFO(USART1);
|
||||
LL_USART_ConfigAsyncMode(USART1);
|
||||
|
||||
LL_USART_Enable(USART1);
|
||||
|
||||
while(!LL_USART_IsActiveFlag_TEACK(USART1)) ;
|
||||
furi_hal_console_alive = true;
|
||||
|
||||
FURI_LOG_I("FuriHalConsole", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
|
||||
if (!furi_hal_console_alive)
|
||||
return;
|
||||
|
||||
while(buffer_size > 0) {
|
||||
while (!LL_USART_IsActiveFlag_TXE(USART1));
|
||||
|
||||
LL_USART_TransmitData8(USART1, *buffer);
|
||||
|
||||
buffer++;
|
||||
buffer_size--;
|
||||
}
|
||||
|
||||
/* Wait for TC flag to be raised for last char */
|
||||
while (!LL_USART_IsActiveFlag_TC(USART1));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void furi_hal_console_init();
|
||||
|
||||
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "furi-hal-delay.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <cmsis_os2.h>
|
||||
|
||||
static uint32_t clk_per_microsecond;
|
||||
|
||||
void furi_hal_delay_init(void) {
|
||||
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
|
||||
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
|
||||
DWT->CYCCNT = 0U;
|
||||
clk_per_microsecond = SystemCoreClock / 1000000.0f;
|
||||
FURI_LOG_I("FuriHalDelay", "Init OK");
|
||||
}
|
||||
|
||||
void delay_us(float microseconds) {
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
uint32_t time_ticks = microseconds * clk_per_microsecond;
|
||||
while((DWT->CYCCNT - start) < time_ticks) {
|
||||
};
|
||||
}
|
||||
|
||||
// cannot be used in ISR
|
||||
// TODO add delay_ISR variant
|
||||
void delay(float milliseconds) {
|
||||
uint32_t ticks = milliseconds / (1000.0f / osKernelGetTickFreq());
|
||||
osStatus_t result = osDelay(ticks);
|
||||
(void)result;
|
||||
furi_assert(result == osOK);
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
#include <furi-hal-flash.h>
|
||||
#include <furi-hal-bt.h>
|
||||
#include <stm32wbxx.h>
|
||||
#include <furi.h>
|
||||
|
||||
/* Free flash space borders, exported by linker */
|
||||
extern const void __free_flash_start__;
|
||||
extern const void __free_flash_end__;
|
||||
|
||||
#define FURI_HAL_FLASH_READ_BLOCK 8
|
||||
#define FURI_HAL_FLASH_WRITE_BLOCK 8
|
||||
#define FURI_HAL_FLASH_PAGE_SIZE 4096
|
||||
#define FURI_HAL_FLASH_CYCLES_COUNT 10000
|
||||
|
||||
size_t furi_hal_flash_get_base() {
|
||||
return FLASH_BASE;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_read_block_size() {
|
||||
return FURI_HAL_FLASH_READ_BLOCK;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_write_block_size() {
|
||||
return FURI_HAL_FLASH_WRITE_BLOCK;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_page_size() {
|
||||
return FURI_HAL_FLASH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_cycles_count() {
|
||||
return FURI_HAL_FLASH_CYCLES_COUNT;
|
||||
}
|
||||
|
||||
const void* furi_hal_flash_get_free_start_address() {
|
||||
return &__free_flash_start__;
|
||||
}
|
||||
|
||||
const void* furi_hal_flash_get_free_end_address() {
|
||||
return &__free_flash_end__;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_free_page_start_address() {
|
||||
size_t start = (size_t)furi_hal_flash_get_free_start_address();
|
||||
size_t page_start = start - start % FURI_HAL_FLASH_PAGE_SIZE;
|
||||
if (page_start != start) {
|
||||
page_start += FURI_HAL_FLASH_PAGE_SIZE;
|
||||
}
|
||||
return page_start;
|
||||
}
|
||||
|
||||
size_t furi_hal_flash_get_free_page_count() {
|
||||
size_t end = (size_t)furi_hal_flash_get_free_end_address();
|
||||
size_t page_start = (size_t)furi_hal_flash_get_free_page_start_address();
|
||||
return (end-page_start) / FURI_HAL_FLASH_PAGE_SIZE;
|
||||
}
|
||||
|
||||
bool furi_hal_flash_erase(uint8_t page, uint8_t count) {
|
||||
if (!furi_hal_bt_lock_flash()) {
|
||||
return false;
|
||||
}
|
||||
FLASH_EraseInitTypeDef erase;
|
||||
erase.TypeErase = FLASH_TYPEERASE_PAGES;
|
||||
erase.Page = page;
|
||||
erase.NbPages = count;
|
||||
uint32_t error;
|
||||
HAL_StatusTypeDef status = HAL_FLASHEx_Erase(&erase, &error);
|
||||
furi_hal_bt_unlock_flash();
|
||||
return status == HAL_OK;
|
||||
}
|
||||
|
||||
bool furi_hal_flash_write_dword(size_t address, uint64_t data) {
|
||||
if (!furi_hal_bt_lock_flash()) {
|
||||
return false;
|
||||
}
|
||||
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data);
|
||||
furi_hal_bt_unlock_flash();
|
||||
return status == HAL_OK;
|
||||
}
|
||||
|
||||
bool furi_hal_flash_write_dword_from(size_t address, size_t source_address) {
|
||||
if (!furi_hal_bt_lock_flash()) {
|
||||
return false;
|
||||
}
|
||||
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address);
|
||||
furi_hal_bt_unlock_flash();
|
||||
return status == HAL_OK;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/** Get flash base address
|
||||
* @return pointer to flash base
|
||||
*/
|
||||
size_t furi_hal_flash_get_base();
|
||||
|
||||
/** Get flash read block size
|
||||
* @return size in bytes
|
||||
*/
|
||||
size_t furi_hal_flash_get_read_block_size();
|
||||
|
||||
/** Get flash write block size
|
||||
* @return size in bytes
|
||||
*/
|
||||
size_t furi_hal_flash_get_write_block_size();
|
||||
|
||||
/** Get flash page size
|
||||
* @return size in bytes
|
||||
*/
|
||||
size_t furi_hal_flash_get_page_size();
|
||||
|
||||
/** Get expected flash cycles count
|
||||
* @return count of erase-write operations
|
||||
*/
|
||||
size_t furi_hal_flash_get_cycles_count();
|
||||
|
||||
/** Get free flash start address
|
||||
* @return pointer to free region start
|
||||
*/
|
||||
const void* furi_hal_flash_get_free_start_address();
|
||||
|
||||
/** Get free flash end address
|
||||
* @return pointer to free region end
|
||||
*/
|
||||
const void* furi_hal_flash_get_free_end_address();
|
||||
|
||||
/** Get first free page start address
|
||||
* @return first free page memory address
|
||||
*/
|
||||
size_t furi_hal_flash_get_free_page_start_address();
|
||||
|
||||
/** Get free page count
|
||||
* @return free page count
|
||||
*/
|
||||
size_t furi_hal_flash_get_free_page_count();
|
||||
|
||||
/*
|
||||
* Erase Flash
|
||||
* Locking operation, uses HSEM to manage shared access.
|
||||
* @param page, page number
|
||||
* @param count, page count to erase
|
||||
*/
|
||||
bool furi_hal_flash_erase(uint8_t page, uint8_t count);
|
||||
|
||||
/*
|
||||
* Write double word (64 bits)
|
||||
* Locking operation, uses HSEM to manage shared access.
|
||||
* @param address - destination address, must be double word aligned.
|
||||
* @param data - data to write
|
||||
*/
|
||||
bool furi_hal_flash_write_dword(size_t address, uint64_t data);
|
||||
|
||||
/*
|
||||
* Write double word (64 bits) from address
|
||||
* Locking operation, uses HSEM to manage shared access.
|
||||
* @param address - destination address, must be block aligned
|
||||
* @param source_address - source address
|
||||
*/
|
||||
bool furi_hal_flash_write_dword_from(size_t address, size_t source_address);
|
||||
@@ -0,0 +1,298 @@
|
||||
#include <furi.h>
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <furi-hal-version.h>
|
||||
|
||||
#define GET_SYSCFG_EXTI_PORT(gpio) \
|
||||
(((gpio) == (GPIOA)) ? LL_SYSCFG_EXTI_PORTA : \
|
||||
((gpio) == (GPIOB)) ? LL_SYSCFG_EXTI_PORTB : \
|
||||
((gpio) == (GPIOC)) ? LL_SYSCFG_EXTI_PORTC : \
|
||||
((gpio) == (GPIOD)) ? LL_SYSCFG_EXTI_PORTD : \
|
||||
((gpio) == (GPIOE)) ? LL_SYSCFG_EXTI_PORTE : \
|
||||
LL_SYSCFG_EXTI_PORTH)
|
||||
|
||||
#define GPIO_PIN_MAP(pin, prefix) \
|
||||
(((pin) == (LL_GPIO_PIN_0)) ? prefix##0 : \
|
||||
((pin) == (LL_GPIO_PIN_1)) ? prefix##1 : \
|
||||
((pin) == (LL_GPIO_PIN_2)) ? prefix##2 : \
|
||||
((pin) == (LL_GPIO_PIN_3)) ? prefix##3 : \
|
||||
((pin) == (LL_GPIO_PIN_4)) ? prefix##4 : \
|
||||
((pin) == (LL_GPIO_PIN_5)) ? prefix##5 : \
|
||||
((pin) == (LL_GPIO_PIN_6)) ? prefix##6 : \
|
||||
((pin) == (LL_GPIO_PIN_7)) ? prefix##7 : \
|
||||
((pin) == (LL_GPIO_PIN_8)) ? prefix##8 : \
|
||||
((pin) == (LL_GPIO_PIN_9)) ? prefix##9 : \
|
||||
((pin) == (LL_GPIO_PIN_10)) ? prefix##10 : \
|
||||
((pin) == (LL_GPIO_PIN_11)) ? prefix##11 : \
|
||||
((pin) == (LL_GPIO_PIN_12)) ? prefix##12 : \
|
||||
((pin) == (LL_GPIO_PIN_13)) ? prefix##13 : \
|
||||
((pin) == (LL_GPIO_PIN_14)) ? prefix##14 : \
|
||||
prefix##15)
|
||||
|
||||
#define GET_SYSCFG_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_SYSCFG_EXTI_LINE)
|
||||
#define GET_EXTI_LINE(pin) GPIO_PIN_MAP(pin, LL_EXTI_LINE_)
|
||||
|
||||
static volatile GpioInterrupt gpio_interrupt[GPIO_NUMBER];
|
||||
|
||||
static uint8_t hal_gpio_get_pin_num(const GpioPin* gpio) {
|
||||
uint8_t pin_num = 0;
|
||||
for(pin_num = 0; pin_num < GPIO_NUMBER; pin_num++) {
|
||||
if(gpio->pin & (1 << pin_num)) break;
|
||||
}
|
||||
return pin_num;
|
||||
}
|
||||
|
||||
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode) {
|
||||
hal_gpio_init(gpio, mode, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed) {
|
||||
// we cannot set alternate mode in this function
|
||||
furi_assert(mode != GpioModeAltFunctionPushPull);
|
||||
furi_assert(mode != GpioModeAltFunctionOpenDrain);
|
||||
|
||||
hal_gpio_init_ex(gpio, mode, pull, speed, GpioAltFnUnused);
|
||||
}
|
||||
|
||||
void hal_gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed,
|
||||
const GpioAltFn alt_fn) {
|
||||
uint32_t sys_exti_port = GET_SYSCFG_EXTI_PORT(gpio->port);
|
||||
uint32_t sys_exti_line = GET_SYSCFG_EXTI_LINE(gpio->pin);
|
||||
uint32_t exti_line = GET_EXTI_LINE(gpio->pin);
|
||||
|
||||
// Configure gpio with interrupts disabled
|
||||
__disable_irq();
|
||||
// Set gpio speed
|
||||
if(speed == GpioSpeedLow) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_LOW);
|
||||
} else if(speed == GpioSpeedMedium) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_MEDIUM);
|
||||
} else if(speed == GpioSpeedHigh) {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_HIGH);
|
||||
} else {
|
||||
LL_GPIO_SetPinSpeed(gpio->port, gpio->pin, LL_GPIO_SPEED_FREQ_VERY_HIGH);
|
||||
}
|
||||
// Set gpio pull mode
|
||||
if(pull == GpioPullNo) {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_NO);
|
||||
} else if(pull == GpioPullUp) {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_UP);
|
||||
} else {
|
||||
LL_GPIO_SetPinPull(gpio->port, gpio->pin, LL_GPIO_PULL_DOWN);
|
||||
}
|
||||
// Set gpio mode
|
||||
if(mode >= GpioModeInterruptRise) {
|
||||
// Set pin in interrupt mode
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
|
||||
LL_SYSCFG_SetEXTISource(sys_exti_port, sys_exti_line);
|
||||
if(mode == GpioModeInterruptRise || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableIT_0_31(exti_line);
|
||||
LL_EXTI_EnableRisingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeInterruptFall || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableIT_0_31(exti_line);
|
||||
LL_EXTI_EnableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeEventRise || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableEvent_0_31(exti_line);
|
||||
LL_EXTI_EnableRisingTrig_0_31(exti_line);
|
||||
}
|
||||
if(mode == GpioModeEventFall || mode == GpioModeInterruptRiseFall) {
|
||||
LL_EXTI_EnableEvent_0_31(exti_line);
|
||||
LL_EXTI_EnableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
} else {
|
||||
// Disable interrupt if it was set
|
||||
if(LL_SYSCFG_GetEXTISource(sys_exti_line) == sys_exti_port &&
|
||||
LL_EXTI_IsEnabledIT_0_31(exti_line)) {
|
||||
LL_EXTI_DisableIT_0_31(exti_line);
|
||||
LL_EXTI_DisableRisingTrig_0_31(exti_line);
|
||||
LL_EXTI_DisableFallingTrig_0_31(exti_line);
|
||||
}
|
||||
// Set not interrupt pin modes
|
||||
if(mode == GpioModeInput) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_INPUT);
|
||||
} else if(mode == GpioModeOutputPushPull || mode == GpioModeAltFunctionPushPull) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_PUSHPULL);
|
||||
} else if(mode == GpioModeOutputOpenDrain || mode == GpioModeAltFunctionOpenDrain) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(gpio->port, gpio->pin, LL_GPIO_OUTPUT_OPENDRAIN);
|
||||
} else if(mode == GpioModeAnalog) {
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ANALOG);
|
||||
}
|
||||
}
|
||||
|
||||
if(mode == GpioModeAltFunctionPushPull || mode == GpioModeAltFunctionOpenDrain) {
|
||||
// enable alternate mode
|
||||
LL_GPIO_SetPinMode(gpio->port, gpio->pin, LL_GPIO_MODE_ALTERNATE);
|
||||
|
||||
// set alternate function
|
||||
if(hal_gpio_get_pin_num(gpio) < 8) {
|
||||
LL_GPIO_SetAFPin_0_7(gpio->port, gpio->pin, alt_fn);
|
||||
} else {
|
||||
LL_GPIO_SetAFPin_8_15(gpio->port, gpio->pin, alt_fn);
|
||||
}
|
||||
}
|
||||
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx) {
|
||||
furi_assert(gpio);
|
||||
furi_assert(cb);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].callback = cb;
|
||||
gpio_interrupt[pin_num].context = ctx;
|
||||
gpio_interrupt[pin_num].ready = true;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_enable_int_callback(const GpioPin* gpio) {
|
||||
furi_assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
if(gpio_interrupt[pin_num].callback) {
|
||||
gpio_interrupt[pin_num].ready = true;
|
||||
}
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_disable_int_callback(const GpioPin* gpio) {
|
||||
furi_assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].ready = false;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
void hal_gpio_remove_int_callback(const GpioPin* gpio) {
|
||||
furi_assert(gpio);
|
||||
|
||||
__disable_irq();
|
||||
uint8_t pin_num = hal_gpio_get_pin_num(gpio);
|
||||
gpio_interrupt[pin_num].callback = NULL;
|
||||
gpio_interrupt[pin_num].context = NULL;
|
||||
gpio_interrupt[pin_num].ready = false;
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
static void hal_gpio_int_call(uint16_t pin_num) {
|
||||
if(gpio_interrupt[pin_num].callback && gpio_interrupt[pin_num].ready) {
|
||||
gpio_interrupt[pin_num].callback(gpio_interrupt[pin_num].context);
|
||||
}
|
||||
}
|
||||
|
||||
/* Interrupt handlers */
|
||||
void EXTI0_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);
|
||||
hal_gpio_int_call(0);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI1_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1);
|
||||
hal_gpio_int_call(1);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI2_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2);
|
||||
hal_gpio_int_call(2);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI3_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
|
||||
hal_gpio_int_call(3);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI4_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4);
|
||||
hal_gpio_int_call(4);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI9_5_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5);
|
||||
hal_gpio_int_call(5);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6);
|
||||
hal_gpio_int_call(6);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7);
|
||||
hal_gpio_int_call(7);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8);
|
||||
hal_gpio_int_call(8);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9);
|
||||
hal_gpio_int_call(9);
|
||||
}
|
||||
}
|
||||
|
||||
void EXTI15_10_IRQHandler(void) {
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10);
|
||||
hal_gpio_int_call(10);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11);
|
||||
hal_gpio_int_call(11);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12);
|
||||
hal_gpio_int_call(12);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
|
||||
hal_gpio_int_call(13);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
|
||||
hal_gpio_int_call(14);
|
||||
}
|
||||
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) {
|
||||
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15);
|
||||
hal_gpio_int_call(15);
|
||||
}
|
||||
}
|
||||
|
||||
extern COMP_HandleTypeDef hcomp1;
|
||||
|
||||
bool hal_gpio_get_rfid_in_level() {
|
||||
bool value = false;
|
||||
if(furi_hal_version_get_hw_version() > 7) {
|
||||
value = (HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUT_LEVEL_LOW);
|
||||
} else {
|
||||
value = (HAL_COMP_GetOutputLevel(&hcomp1) == COMP_OUTPUT_LEVEL_HIGH);
|
||||
}
|
||||
|
||||
#ifdef INVERT_RFID_IN
|
||||
return !value;
|
||||
#else
|
||||
return value;
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include "stdbool.h"
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_system.h>
|
||||
#include <stm32wbxx_ll_exti.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Number of gpio on one port
|
||||
*/
|
||||
#define GPIO_NUMBER (16U)
|
||||
|
||||
/**
|
||||
* Interrupt callback prototype
|
||||
*/
|
||||
typedef void (*GpioExtiCallback)(void* ctx);
|
||||
|
||||
/**
|
||||
* Gpio interrupt type
|
||||
*/
|
||||
typedef struct {
|
||||
GpioExtiCallback callback;
|
||||
void* context;
|
||||
volatile bool ready;
|
||||
} GpioInterrupt;
|
||||
|
||||
/**
|
||||
* Gpio modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioModeInput,
|
||||
GpioModeOutputPushPull,
|
||||
GpioModeOutputOpenDrain,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioModeAltFunctionOpenDrain,
|
||||
GpioModeAnalog,
|
||||
GpioModeInterruptRise,
|
||||
GpioModeInterruptFall,
|
||||
GpioModeInterruptRiseFall,
|
||||
GpioModeEventRise,
|
||||
GpioModeEventFall,
|
||||
GpioModeEventRiseFall,
|
||||
} GpioMode;
|
||||
|
||||
/**
|
||||
* Gpio pull modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioPullNo,
|
||||
GpioPullUp,
|
||||
GpioPullDown,
|
||||
} GpioPull;
|
||||
|
||||
/**
|
||||
* Gpio speed modes
|
||||
*/
|
||||
typedef enum {
|
||||
GpioSpeedLow,
|
||||
GpioSpeedMedium,
|
||||
GpioSpeedHigh,
|
||||
GpioSpeedVeryHigh,
|
||||
} GpioSpeed;
|
||||
|
||||
/**
|
||||
* Gpio alternate functions
|
||||
*/
|
||||
typedef enum {
|
||||
GpioAltFn0MCO = 0, /*!< MCO Alternate Function mapping */
|
||||
GpioAltFn0LSCO = 0, /*!< LSCO Alternate Function mapping */
|
||||
GpioAltFn0JTMS_SWDIO = 0, /*!< JTMS-SWDIO Alternate Function mapping */
|
||||
GpioAltFn0JTCK_SWCLK = 0, /*!< JTCK-SWCLK Alternate Function mapping */
|
||||
GpioAltFn0JTDI = 0, /*!< JTDI Alternate Function mapping */
|
||||
GpioAltFn0RTC_OUT = 0, /*!< RCT_OUT Alternate Function mapping */
|
||||
GpioAltFn0JTD_TRACE = 0, /*!< JTDO-TRACESWO Alternate Function mapping */
|
||||
GpioAltFn0NJTRST = 0, /*!< NJTRST Alternate Function mapping */
|
||||
GpioAltFn0RTC_REFIN = 0, /*!< RTC_REFIN Alternate Function mapping */
|
||||
GpioAltFn0TRACED0 = 0, /*!< TRACED0 Alternate Function mapping */
|
||||
GpioAltFn0TRACED1 = 0, /*!< TRACED1 Alternate Function mapping */
|
||||
GpioAltFn0TRACED2 = 0, /*!< TRACED2 Alternate Function mapping */
|
||||
GpioAltFn0TRACED3 = 0, /*!< TRACED3 Alternate Function mapping */
|
||||
GpioAltFn0TRIG_INOUT = 0, /*!< TRIG_INOUT Alternate Function mapping */
|
||||
GpioAltFn0TRACECK = 0, /*!< TRACECK Alternate Function mapping */
|
||||
GpioAltFn0SYS = 0, /*!< System Function mapping */
|
||||
|
||||
GpioAltFn1TIM1 = 1, /*!< TIM1 Alternate Function mapping */
|
||||
GpioAltFn1TIM2 = 1, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn1LPTIM1 = 1, /*!< LPTIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn2TIM2 = 2, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn2TIM1 = 2, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn3SAI1 = 3, /*!< SAI1_CK1 Alternate Function mapping */
|
||||
GpioAltFn3SPI2 = 3, /*!< SPI2 Alternate Function mapping */
|
||||
GpioAltFn3TIM1 = 3, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn4I2C1 = 4, /*!< I2C1 Alternate Function mapping */
|
||||
GpioAltFn4I2C3 = 4, /*!< I2C3 Alternate Function mapping */
|
||||
|
||||
GpioAltFn5SPI1 = 5, /*!< SPI1 Alternate Function mapping */
|
||||
GpioAltFn5SPI2 = 5, /*!< SPI2 Alternate Function mapping */
|
||||
|
||||
GpioAltFn6MCO = 6, /*!< MCO Alternate Function mapping */
|
||||
GpioAltFn6LSCO = 6, /*!< LSCO Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB0 = 6, /*!< RF_DTB0 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB1 = 6, /*!< RF_DTB1 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB2 = 6, /*!< RF_DTB2 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB3 = 6, /*!< RF_DTB3 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB4 = 6, /*!< RF_DTB4 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB5 = 6, /*!< RF_DTB5 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB6 = 6, /*!< RF_DTB6 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB7 = 6, /*!< RF_DTB7 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB8 = 6, /*!< RF_DTB8 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB9 = 6, /*!< RF_DTB9 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB10 = 6, /*!< RF_DTB10 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB11 = 6, /*!< RF_DTB11 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB12 = 6, /*!< RF_DTB12 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB13 = 6, /*!< RF_DTB13 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB14 = 6, /*!< RF_DTB14 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB15 = 6, /*!< RF_DTB15 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB16 = 6, /*!< RF_DTB16 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB17 = 6, /*!< RF_DTB17 Alternate Function mapping */
|
||||
GpioAltFn6RF_DTB18 = 6, /*!< RF_DTB18 Alternate Function mapping */
|
||||
GpioAltFn6RF_MISO = 6, /*!< RF_MISO Alternate Function mapping */
|
||||
GpioAltFn6RF_MOSI = 6, /*!< RF_MOSI Alternate Function mapping */
|
||||
GpioAltFn6RF_SCK = 6, /*!< RF_SCK Alternate Function mapping */
|
||||
GpioAltFn6RF_NSS = 6, /*!< RF_NSS Alternate Function mapping */
|
||||
|
||||
GpioAltFn7USART1 = 7, /*!< USART1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn8LPUART1 = 8, /*!< LPUART1 Alternate Function mapping */
|
||||
GpioAltFn8IR = 8, /*!< IR Alternate Function mapping */
|
||||
|
||||
GpioAltFn9TSC = 9, /*!< TSC Alternate Function mapping */
|
||||
|
||||
GpioAltFn10QUADSPI = 10, /*!< QUADSPI Alternate Function mapping */
|
||||
GpioAltFn10USB = 10, /*!< USB Alternate Function mapping */
|
||||
|
||||
GpioAltFn11LCD = 11, /*!< LCD Alternate Function mapping */
|
||||
|
||||
GpioAltFn12COMP1 = 12, /*!< COMP1 Alternate Function mapping */
|
||||
GpioAltFn12COMP2 = 12, /*!< COMP2 Alternate Function mapping */
|
||||
GpioAltFn12TIM1 = 12, /*!< TIM1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn13SAI1 = 13, /*!< SAI1 Alternate Function mapping */
|
||||
|
||||
GpioAltFn14TIM2 = 14, /*!< TIM2 Alternate Function mapping */
|
||||
GpioAltFn14TIM16 = 14, /*!< TIM16 Alternate Function mapping */
|
||||
GpioAltFn14TIM17 = 14, /*!< TIM17 Alternate Function mapping */
|
||||
GpioAltFn14LPTIM2 = 14, /*!< LPTIM2 Alternate Function mapping */
|
||||
|
||||
GpioAltFn15EVENTOUT = 15, /*!< EVENTOUT Alternate Function mapping */
|
||||
|
||||
GpioAltFnUnused = 16, /*!< just dummy value */
|
||||
} GpioAltFn;
|
||||
|
||||
/**
|
||||
* Gpio structure
|
||||
*/
|
||||
typedef struct {
|
||||
GPIO_TypeDef* port;
|
||||
uint16_t pin;
|
||||
} GpioPin;
|
||||
|
||||
/**
|
||||
* GPIO initialization function, simple version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
*/
|
||||
void hal_gpio_init_simple(const GpioPin* gpio, const GpioMode mode);
|
||||
|
||||
/**
|
||||
* GPIO initialization function, normal version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
* @param pull GpioPull
|
||||
* @param speed GpioSpeed
|
||||
*/
|
||||
void hal_gpio_init(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed);
|
||||
|
||||
/**
|
||||
* GPIO initialization function, extended version
|
||||
* @param gpio GpioPin
|
||||
* @param mode GpioMode
|
||||
* @param pull GpioPull
|
||||
* @param speed GpioSpeed
|
||||
* @param alt_fn GpioAltFn
|
||||
*/
|
||||
void hal_gpio_init_ex(
|
||||
const GpioPin* gpio,
|
||||
const GpioMode mode,
|
||||
const GpioPull pull,
|
||||
const GpioSpeed speed,
|
||||
const GpioAltFn alt_fn);
|
||||
|
||||
/**
|
||||
* Add and enable interrupt
|
||||
* @param gpio GpioPin
|
||||
* @param cb GpioExtiCallback
|
||||
* @param ctx context for callback
|
||||
*/
|
||||
void hal_gpio_add_int_callback(const GpioPin* gpio, GpioExtiCallback cb, void* ctx);
|
||||
|
||||
/**
|
||||
* Enable interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_enable_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* Disable interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_disable_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* Remove interrupt
|
||||
* @param gpio GpioPin
|
||||
*/
|
||||
void hal_gpio_remove_int_callback(const GpioPin* gpio);
|
||||
|
||||
/**
|
||||
* GPIO write pin
|
||||
* @param gpio GpioPin
|
||||
* @param state true / false
|
||||
*/
|
||||
static inline void hal_gpio_write(const GpioPin* gpio, const bool state) {
|
||||
// writing to BSSR is an atomic operation
|
||||
if(state == true) {
|
||||
gpio->port->BSRR = gpio->pin;
|
||||
} else {
|
||||
gpio->port->BSRR = (uint32_t)gpio->pin << GPIO_NUMBER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GPIO read pin
|
||||
* @param gpio GpioPin
|
||||
* @return true / false
|
||||
*/
|
||||
static inline bool hal_gpio_read(const GpioPin* gpio) {
|
||||
if((gpio->port->IDR & gpio->pin) != 0x00U) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get RFID IN level
|
||||
* @return false = LOW, true = HIGH
|
||||
*/
|
||||
bool hal_gpio_get_rfid_in_level();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,152 @@
|
||||
#include <furi-hal-i2c.h>
|
||||
#include <furi-hal-version.h>
|
||||
|
||||
#include <stm32wbxx_ll_i2c.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
#include <furi.h>
|
||||
|
||||
osMutexId_t furi_hal_i2c_mutex = NULL;
|
||||
|
||||
void furi_hal_i2c_init() {
|
||||
furi_hal_i2c_mutex = osMutexNew(NULL);
|
||||
furi_check(furi_hal_i2c_mutex);
|
||||
|
||||
LL_I2C_InitTypeDef I2C_InitStruct = {0};
|
||||
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
|
||||
|
||||
LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
|
||||
|
||||
GPIO_InitStruct.Pin = POWER_I2C_SCL_Pin | POWER_I2C_SDA_Pin;
|
||||
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
|
||||
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
|
||||
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
|
||||
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
|
||||
GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
|
||||
LL_GPIO_Init(GPIOA, &GPIO_InitStruct);
|
||||
|
||||
I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
|
||||
I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
|
||||
I2C_InitStruct.DigitalFilter = 0;
|
||||
I2C_InitStruct.OwnAddress1 = 0;
|
||||
I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
|
||||
I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
|
||||
if (furi_hal_version_get_hw_version() > 10) {
|
||||
I2C_InitStruct.Timing = POWER_I2C_TIMINGS_400;
|
||||
} else {
|
||||
I2C_InitStruct.Timing = POWER_I2C_TIMINGS_100;
|
||||
}
|
||||
LL_I2C_Init(POWER_I2C, &I2C_InitStruct);
|
||||
LL_I2C_EnableAutoEndMode(POWER_I2C);
|
||||
LL_I2C_SetOwnAddress2(POWER_I2C, 0, LL_I2C_OWNADDRESS2_NOMASK);
|
||||
LL_I2C_DisableOwnAddress2(POWER_I2C);
|
||||
LL_I2C_DisableGeneralCall(POWER_I2C);
|
||||
LL_I2C_EnableClockStretching(POWER_I2C);
|
||||
FURI_LOG_I("FuriHalI2C", "Init OK");
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_tx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
const uint8_t* data,
|
||||
uint8_t size,
|
||||
uint32_t timeout) {
|
||||
uint32_t time_left = timeout;
|
||||
bool ret = true;
|
||||
|
||||
while(LL_I2C_IsActiveFlag_BUSY(instance))
|
||||
;
|
||||
|
||||
LL_I2C_HandleTransfer(
|
||||
instance,
|
||||
address,
|
||||
LL_I2C_ADDRSLAVE_7BIT,
|
||||
size,
|
||||
LL_I2C_MODE_AUTOEND,
|
||||
LL_I2C_GENERATE_START_WRITE);
|
||||
|
||||
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
|
||||
if(LL_I2C_IsActiveFlag_TXIS(instance)) {
|
||||
LL_I2C_TransmitData8(instance, (*data));
|
||||
data++;
|
||||
size--;
|
||||
time_left = timeout;
|
||||
}
|
||||
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
if(--time_left == 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_I2C_ClearFlag_STOP(instance);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_rx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
uint8_t* data,
|
||||
uint8_t size,
|
||||
uint32_t timeout) {
|
||||
uint32_t time_left = timeout;
|
||||
bool ret = true;
|
||||
|
||||
while(LL_I2C_IsActiveFlag_BUSY(instance))
|
||||
;
|
||||
|
||||
LL_I2C_HandleTransfer(
|
||||
instance,
|
||||
address,
|
||||
LL_I2C_ADDRSLAVE_7BIT,
|
||||
size,
|
||||
LL_I2C_MODE_AUTOEND,
|
||||
LL_I2C_GENERATE_START_READ);
|
||||
|
||||
while(!LL_I2C_IsActiveFlag_STOP(instance) || size > 0) {
|
||||
if(LL_I2C_IsActiveFlag_RXNE(instance)) {
|
||||
*data = LL_I2C_ReceiveData8(instance);
|
||||
data++;
|
||||
size--;
|
||||
time_left = timeout;
|
||||
}
|
||||
|
||||
if(LL_SYSTICK_IsActiveCounterFlag()) {
|
||||
if(--time_left == 0) {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LL_I2C_ClearFlag_STOP(instance);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool furi_hal_i2c_trx(
|
||||
I2C_TypeDef* instance,
|
||||
uint8_t address,
|
||||
const uint8_t* tx_data,
|
||||
uint8_t tx_size,
|
||||
uint8_t* rx_data,
|
||||
uint8_t rx_size,
|
||||
uint32_t timeout) {
|
||||
if(furi_hal_i2c_tx(instance, address, tx_data, tx_size, timeout) &&
|
||||
furi_hal_i2c_rx(instance, address, rx_data, rx_size, timeout)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_i2c_lock() {
|
||||
furi_check(osMutexAcquire(furi_hal_i2c_mutex, osWaitForever) == osOK);
|
||||
}
|
||||
|
||||
void furi_hal_i2c_unlock() {
|
||||
furi_check(osMutexRelease(furi_hal_i2c_mutex) == osOK);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#include <furi-hal-ibutton.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
void furi_hal_ibutton_start() {
|
||||
furi_hal_ibutton_pin_high();
|
||||
hal_gpio_init(&ibutton_gpio, GpioModeOutputOpenDrain, GpioSpeedLow, GpioPullNo);
|
||||
}
|
||||
|
||||
void furi_hal_ibutton_stop() {
|
||||
furi_hal_ibutton_pin_high();
|
||||
hal_gpio_init(&ibutton_gpio, GpioModeAnalog, GpioSpeedLow, GpioPullNo);
|
||||
}
|
||||
|
||||
void furi_hal_ibutton_pin_low() {
|
||||
hal_gpio_write(&ibutton_gpio, false);
|
||||
}
|
||||
|
||||
void furi_hal_ibutton_pin_high() {
|
||||
hal_gpio_write(&ibutton_gpio, true);
|
||||
}
|
||||
|
||||
bool furi_hal_ibutton_pin_get_level() {
|
||||
return hal_gpio_read(&ibutton_gpio);
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
#include "furi-hal-interrupt.h"
|
||||
|
||||
#include <furi.h>
|
||||
#include <main.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
volatile FuriHalInterruptISR furi_hal_tim_tim2_isr = NULL;
|
||||
volatile FuriHalInterruptISR furi_hal_tim_tim1_isr = NULL;
|
||||
|
||||
#define FURI_HAL_INTERRUPT_DMA_COUNT 2
|
||||
#define FURI_HAL_INTERRUPT_DMA_CHANNELS_COUNT 8
|
||||
|
||||
volatile FuriHalInterruptISR furi_hal_dma_channel_isr[FURI_HAL_INTERRUPT_DMA_COUNT][FURI_HAL_INTERRUPT_DMA_CHANNELS_COUNT] = {0};
|
||||
|
||||
void furi_hal_interrupt_init() {
|
||||
NVIC_SetPriority(RCC_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
|
||||
NVIC_EnableIRQ(RCC_IRQn);
|
||||
|
||||
NVIC_SetPriority(TAMP_STAMP_LSECSS_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 0, 0));
|
||||
NVIC_EnableIRQ(TAMP_STAMP_LSECSS_IRQn);
|
||||
|
||||
NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
|
||||
|
||||
FURI_LOG_I("FuriHalInterrupt", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_interrupt_set_timer_isr(TIM_TypeDef* timer, FuriHalInterruptISR isr) {
|
||||
if (timer == TIM2) {
|
||||
if (isr) {
|
||||
furi_assert(furi_hal_tim_tim2_isr == NULL);
|
||||
} else {
|
||||
furi_assert(furi_hal_tim_tim2_isr != NULL);
|
||||
}
|
||||
furi_hal_tim_tim2_isr = isr;
|
||||
} else if (timer == TIM1) {
|
||||
if (isr) {
|
||||
furi_assert(furi_hal_tim_tim1_isr == NULL);
|
||||
} else {
|
||||
furi_assert(furi_hal_tim_tim1_isr != NULL);
|
||||
}
|
||||
furi_hal_tim_tim1_isr = isr;
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_interrupt_set_dma_channel_isr(DMA_TypeDef* dma, uint32_t channel, FuriHalInterruptISR isr) {
|
||||
--channel; // Pascal
|
||||
furi_check(dma);
|
||||
furi_check(channel < FURI_HAL_INTERRUPT_DMA_CHANNELS_COUNT);
|
||||
if (dma == DMA1) {
|
||||
furi_hal_dma_channel_isr[0][channel] = isr;
|
||||
} else if (dma == DMA2) {
|
||||
furi_hal_dma_channel_isr[1][channel] = isr;
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
}
|
||||
|
||||
extern void api_interrupt_call(InterruptType type, void* hw);
|
||||
|
||||
/* ST HAL symbols */
|
||||
|
||||
/* Comparator trigger event */
|
||||
void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {
|
||||
api_interrupt_call(InterruptTypeComparatorTrigger, hcomp);
|
||||
}
|
||||
|
||||
/* Timer update event */
|
||||
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {
|
||||
api_interrupt_call(InterruptTypeTimerUpdate, htim);
|
||||
}
|
||||
|
||||
/* Timer 2 */
|
||||
void TIM2_IRQHandler(void) {
|
||||
if (furi_hal_tim_tim2_isr) {
|
||||
furi_hal_tim_tim2_isr();
|
||||
} else {
|
||||
HAL_TIM_IRQHandler(&htim2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Timer 1 Update */
|
||||
void TIM1_UP_TIM16_IRQHandler(void) {
|
||||
if (furi_hal_tim_tim1_isr) {
|
||||
furi_hal_tim_tim1_isr();
|
||||
} else {
|
||||
HAL_TIM_IRQHandler(&htim1);
|
||||
}
|
||||
}
|
||||
|
||||
/* DMA 1 */
|
||||
void DMA1_Channel1_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][0]) furi_hal_dma_channel_isr[0][0]();
|
||||
}
|
||||
|
||||
void DMA1_Channel2_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][1]) furi_hal_dma_channel_isr[0][1]();
|
||||
}
|
||||
|
||||
void DMA1_Channel3_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][2]) furi_hal_dma_channel_isr[0][2]();
|
||||
}
|
||||
|
||||
void DMA1_Channel4_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][3]) furi_hal_dma_channel_isr[0][3]();
|
||||
}
|
||||
|
||||
void DMA1_Channel5_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][4]) furi_hal_dma_channel_isr[0][4]();
|
||||
}
|
||||
|
||||
void DMA1_Channel6_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][5]) furi_hal_dma_channel_isr[0][5]();
|
||||
}
|
||||
|
||||
void DMA1_Channel7_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][6]) furi_hal_dma_channel_isr[0][6]();
|
||||
}
|
||||
|
||||
void DMA1_Channel8_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[0][7]) furi_hal_dma_channel_isr[0][7]();
|
||||
}
|
||||
|
||||
/* DMA 2 */
|
||||
void DMA2_Channel1_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][0]) furi_hal_dma_channel_isr[1][0]();
|
||||
}
|
||||
|
||||
void DMA2_Channel2_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][1]) furi_hal_dma_channel_isr[1][1]();
|
||||
}
|
||||
|
||||
void DMA2_Channel3_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][2]) furi_hal_dma_channel_isr[1][2]();
|
||||
}
|
||||
|
||||
void DMA2_Channel4_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][3]) furi_hal_dma_channel_isr[1][3]();
|
||||
}
|
||||
|
||||
void DMA2_Channel5_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][4]) furi_hal_dma_channel_isr[1][4]();
|
||||
}
|
||||
|
||||
void DMA2_Channel6_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][5]) furi_hal_dma_channel_isr[1][5]();
|
||||
}
|
||||
|
||||
void DMA2_Channel7_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][6]) furi_hal_dma_channel_isr[1][6]();
|
||||
}
|
||||
|
||||
void DMA2_Channel8_IRQHandler(void) {
|
||||
if (furi_hal_dma_channel_isr[1][7]) furi_hal_dma_channel_isr[1][7]();
|
||||
}
|
||||
|
||||
|
||||
void TAMP_STAMP_LSECSS_IRQHandler(void) {
|
||||
if (LL_RCC_IsActiveFlag_LSECSS()) {
|
||||
LL_RCC_ClearFlag_LSECSS();
|
||||
if (!LL_RCC_LSE_IsReady()) {
|
||||
FURI_LOG_E("FuriHalInterrupt", "LSE CSS fired: resetting system");
|
||||
NVIC_SystemReset();
|
||||
} else {
|
||||
FURI_LOG_E("FuriHalInterrupt", "LSE CSS fired: but LSE is alive");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RCC_IRQHandler(void) {
|
||||
}
|
||||
|
||||
|
||||
void NMI_Handler(void) {
|
||||
if (LL_RCC_IsActiveFlag_HSECSS()) {
|
||||
LL_RCC_ClearFlag_HSECSS();
|
||||
FURI_LOG_E("FuriHalInterrupt", "HSE CSS fired: resetting system");
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
}
|
||||
|
||||
void HardFault_Handler(void) {
|
||||
if ((*(volatile uint32_t *)CoreDebug_BASE) & (1 << 0)) {
|
||||
__asm("bkpt 1");
|
||||
}
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void MemManage_Handler(void) {
|
||||
__asm("bkpt 1");
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void BusFault_Handler(void) {
|
||||
__asm("bkpt 1");
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void UsageFault_Handler(void) {
|
||||
__asm("bkpt 1");
|
||||
while (1) {}
|
||||
}
|
||||
|
||||
void DebugMon_Handler(void) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Timer ISR */
|
||||
typedef void (*FuriHalInterruptISR)();
|
||||
|
||||
/** Initialize interrupt subsystem */
|
||||
void furi_hal_interrupt_init();
|
||||
|
||||
/** Set DMA Channel ISR
|
||||
* We don't clear interrupt flags for you, do it by your self.
|
||||
* @param dma - DMA instance
|
||||
* @param channel - DMA channel
|
||||
* @param isr - your interrupt service routine or use NULL to clear
|
||||
*/
|
||||
void furi_hal_interrupt_set_dma_channel_isr(DMA_TypeDef* dma, uint32_t channel, FuriHalInterruptISR isr);
|
||||
|
||||
/** Set Timer ISR
|
||||
* By default ISR is serviced by ST HAL. Use this function to override it.
|
||||
* We don't clear interrupt flags for you, do it by your self.
|
||||
* @param timer - timer instance
|
||||
* @param isr - your interrupt service routine or use NULL to clear
|
||||
*/
|
||||
void furi_hal_interrupt_set_timer_isr(TIM_TypeDef *timer, FuriHalInterruptISR isr);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,584 @@
|
||||
#include "furi-hal-irda.h"
|
||||
#include "furi-hal-delay.h"
|
||||
#include "furi/check.h"
|
||||
#include "stm32wbxx_ll_dma.h"
|
||||
#include "sys/_stdint.h"
|
||||
#include <cmsis_os2.h>
|
||||
#include <furi-hal-interrupt.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stm32wbxx_ll_tim.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <furi.h>
|
||||
#include <math.h>
|
||||
#include <main.h>
|
||||
#include <furi-hal-pwm.h>
|
||||
|
||||
#define IRDA_TIM_TX_DMA_BUFFER_SIZE 200
|
||||
#define IRDA_POLARITY_SHIFT 1
|
||||
|
||||
#define IRDA_TX_CCMR_HIGH (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_PWM2) /* Mark time - enable PWM2 mode */
|
||||
#define IRDA_TX_CCMR_LOW (TIM_CCMR2_OC3PE | LL_TIM_OCMODE_FORCED_INACTIVE) /* Space time - force low */
|
||||
|
||||
typedef struct{
|
||||
FuriHalIrdaRxCaptureCallback capture_callback;
|
||||
void *capture_context;
|
||||
FuriHalIrdaRxTimeoutCallback timeout_callback;
|
||||
void *timeout_context;
|
||||
} IrdaTimRx;
|
||||
|
||||
typedef struct{
|
||||
uint8_t* polarity;
|
||||
uint16_t* data;
|
||||
size_t size;
|
||||
bool packet_end;
|
||||
bool last_packet_end;
|
||||
} IrdaTxBuf;
|
||||
|
||||
typedef struct {
|
||||
float cycle_duration;
|
||||
FuriHalIrdaTxGetDataCallback data_callback;
|
||||
void* data_context;
|
||||
IrdaTxBuf buffer[2];
|
||||
osSemaphoreId_t stop_semaphore;
|
||||
} IrdaTimTx;
|
||||
|
||||
typedef enum {
|
||||
IrdaStateIdle, /** Furi Hal Irda is ready to start RX or TX */
|
||||
IrdaStateAsyncRx, /** Async RX started */
|
||||
IrdaStateAsyncTx, /** Async TX started, DMA and timer is on */
|
||||
IrdaStateAsyncTxStopReq, /** Async TX started, async stop request received */
|
||||
IrdaStateAsyncTxStopInProgress, /** Async TX started, stop request is processed and we wait for last data to be sent */
|
||||
IrdaStateAsyncTxStopped, /** Async TX complete, cleanup needed */
|
||||
IrdaStateMAX,
|
||||
} IrdaState;
|
||||
|
||||
static volatile IrdaState furi_hal_irda_state = IrdaStateIdle;
|
||||
static IrdaTimTx irda_tim_tx;
|
||||
static IrdaTimRx irda_tim_rx;
|
||||
|
||||
static bool furi_hal_irda_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift);
|
||||
static void furi_hal_irda_async_tx_free_resources(void);
|
||||
static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift);
|
||||
static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num);
|
||||
static void furi_hal_irda_tx_fill_buffer_last(uint8_t buf_num);
|
||||
static uint8_t furi_hal_irda_get_current_dma_tx_buffer(void);
|
||||
static void furi_hal_irda_tx_dma_polarity_isr();
|
||||
static void furi_hal_irda_tx_dma_isr();
|
||||
|
||||
static void furi_hal_irda_tim_rx_isr() {
|
||||
|
||||
/* Timeout */
|
||||
if(LL_TIM_IsActiveFlag_CC3(TIM2)) {
|
||||
LL_TIM_ClearFlag_CC3(TIM2);
|
||||
furi_assert(furi_hal_irda_state == IrdaStateAsyncRx);
|
||||
|
||||
/* Timers CNT register starts to counting from 0 to ARR, but it is
|
||||
* reseted when Channel 1 catches interrupt. It is not reseted by
|
||||
* channel 2, though, so we have to distract it's values (see TimerIRQSourceCCI1 ISR).
|
||||
* This can cause false timeout: when time is over, but we started
|
||||
* receiving new signal few microseconds ago, because CNT register
|
||||
* is reseted once per period, not per sample. */
|
||||
if (LL_GPIO_IsInputPinSet(gpio_irda_rx.port, gpio_irda_rx.pin) != 0) {
|
||||
if (irda_tim_rx.timeout_callback)
|
||||
irda_tim_rx.timeout_callback(irda_tim_rx.timeout_context);
|
||||
}
|
||||
}
|
||||
|
||||
/* Rising Edge */
|
||||
if(LL_TIM_IsActiveFlag_CC1(TIM2)) {
|
||||
LL_TIM_ClearFlag_CC1(TIM2);
|
||||
furi_assert(furi_hal_irda_state == IrdaStateAsyncRx);
|
||||
|
||||
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC1S)) {
|
||||
/* Low pin level is a Mark state of IRDA signal. Invert level for further processing. */
|
||||
uint32_t duration = LL_TIM_IC_GetCaptureCH1(TIM2) - LL_TIM_IC_GetCaptureCH2(TIM2);
|
||||
if (irda_tim_rx.capture_callback)
|
||||
irda_tim_rx.capture_callback(irda_tim_rx.capture_context, 1, duration);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* Falling Edge */
|
||||
if(LL_TIM_IsActiveFlag_CC2(TIM2)) {
|
||||
LL_TIM_ClearFlag_CC2(TIM2);
|
||||
furi_assert(furi_hal_irda_state == IrdaStateAsyncRx);
|
||||
|
||||
if(READ_BIT(TIM2->CCMR1, TIM_CCMR1_CC2S)) {
|
||||
/* High pin level is a Space state of IRDA signal. Invert level for further processing. */
|
||||
uint32_t duration = LL_TIM_IC_GetCaptureCH2(TIM2);
|
||||
if (irda_tim_rx.capture_callback)
|
||||
irda_tim_rx.capture_callback(irda_tim_rx.capture_context, 0, duration);
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_rx_start(void) {
|
||||
furi_assert(furi_hal_irda_state == IrdaStateIdle);
|
||||
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOA);
|
||||
|
||||
hal_gpio_init_ex(&gpio_irda_rx, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
|
||||
|
||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||
TIM_InitStruct.Prescaler = 64 - 1;
|
||||
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||
TIM_InitStruct.Autoreload = 0x7FFFFFFE;
|
||||
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
||||
LL_TIM_Init(TIM2, &TIM_InitStruct);
|
||||
|
||||
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
LL_TIM_DisableARRPreload(TIM2);
|
||||
LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI1FP1);
|
||||
LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET);
|
||||
LL_TIM_CC_DisableChannel(TIM2, LL_TIM_CHANNEL_CH2);
|
||||
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
|
||||
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_FALLING);
|
||||
LL_TIM_DisableIT_TRIG(TIM2);
|
||||
LL_TIM_DisableDMAReq_TRIG(TIM2);
|
||||
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
|
||||
LL_TIM_EnableMasterSlaveMode(TIM2);
|
||||
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_DIRECTTI);
|
||||
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
|
||||
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
|
||||
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_RISING);
|
||||
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_INDIRECTTI);
|
||||
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
|
||||
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_irda_tim_rx_isr);
|
||||
furi_hal_irda_state = IrdaStateAsyncRx;
|
||||
|
||||
LL_TIM_EnableIT_CC1(TIM2);
|
||||
LL_TIM_EnableIT_CC2(TIM2);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
|
||||
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
|
||||
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(TIM2_IRQn);
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_rx_stop(void) {
|
||||
furi_assert(furi_hal_irda_state == IrdaStateAsyncRx);
|
||||
LL_TIM_DeInit(TIM2);
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, NULL);
|
||||
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
furi_hal_irda_state = IrdaStateIdle;
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_rx_set_timeout(uint32_t timeout_ms) {
|
||||
LL_TIM_OC_SetCompareCH3(TIM2, timeout_ms * 1000);
|
||||
LL_TIM_OC_SetMode(TIM2, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_ACTIVE);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH3);
|
||||
LL_TIM_EnableIT_CC3(TIM2);
|
||||
}
|
||||
|
||||
bool furi_hal_irda_is_busy(void) {
|
||||
return furi_hal_irda_state != IrdaStateIdle;
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_rx_set_capture_isr_callback(FuriHalIrdaRxCaptureCallback callback, void *ctx) {
|
||||
irda_tim_rx.capture_callback = callback;
|
||||
irda_tim_rx.capture_context = ctx;
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_rx_set_timeout_isr_callback(FuriHalIrdaRxTimeoutCallback callback, void *ctx) {
|
||||
irda_tim_rx.timeout_callback = callback;
|
||||
irda_tim_rx.timeout_context = ctx;
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_dma_terminate(void) {
|
||||
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
furi_assert(furi_hal_irda_state == IrdaStateAsyncTxStopInProgress);
|
||||
|
||||
LL_DMA_DisableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_TIM_DisableCounter(TIM1);
|
||||
osStatus_t status = osSemaphoreRelease(irda_tim_tx.stop_semaphore);
|
||||
furi_check(status == osOK);
|
||||
furi_hal_irda_state = IrdaStateAsyncTxStopped;
|
||||
}
|
||||
|
||||
static uint8_t furi_hal_irda_get_current_dma_tx_buffer(void) {
|
||||
uint8_t buf_num = 0;
|
||||
uint32_t buffer_adr = LL_DMA_GetMemoryAddress(DMA1, LL_DMA_CHANNEL_2);
|
||||
if (buffer_adr == (uint32_t) irda_tim_tx.buffer[0].data) {
|
||||
buf_num = 0;
|
||||
} else if (buffer_adr == (uint32_t) irda_tim_tx.buffer[1].data) {
|
||||
buf_num = 1;
|
||||
} else {
|
||||
furi_assert(0);
|
||||
}
|
||||
return buf_num;
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_dma_polarity_isr() {
|
||||
if (LL_DMA_IsActiveFlag_TE1(DMA1)) {
|
||||
LL_DMA_ClearFlag_TE1(DMA1);
|
||||
furi_check(0);
|
||||
}
|
||||
if (LL_DMA_IsActiveFlag_TC1(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_1)) {
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
|
||||
furi_check((furi_hal_irda_state == IrdaStateAsyncTx)
|
||||
|| (furi_hal_irda_state == IrdaStateAsyncTxStopReq)
|
||||
|| (furi_hal_irda_state == IrdaStateAsyncTxStopInProgress));
|
||||
/* actually TC2 is processed and buffer is next buffer */
|
||||
uint8_t next_buf_num = furi_hal_irda_get_current_dma_tx_buffer();
|
||||
furi_hal_irda_tx_dma_set_polarity(next_buf_num, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_dma_isr() {
|
||||
if (LL_DMA_IsActiveFlag_TE2(DMA1)) {
|
||||
LL_DMA_ClearFlag_TE2(DMA1);
|
||||
furi_check(0);
|
||||
}
|
||||
if (LL_DMA_IsActiveFlag_HT2(DMA1) && LL_DMA_IsEnabledIT_HT(DMA1, LL_DMA_CHANNEL_2)) {
|
||||
LL_DMA_ClearFlag_HT2(DMA1);
|
||||
uint8_t buf_num = furi_hal_irda_get_current_dma_tx_buffer();
|
||||
uint8_t next_buf_num = !buf_num;
|
||||
if (irda_tim_tx.buffer[buf_num].last_packet_end) {
|
||||
LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
|
||||
} else if (!irda_tim_tx.buffer[buf_num].packet_end || (furi_hal_irda_state == IrdaStateAsyncTx)) {
|
||||
bool result = furi_hal_irda_tx_fill_buffer(next_buf_num, 0);
|
||||
if (irda_tim_tx.buffer[next_buf_num].last_packet_end) {
|
||||
LL_DMA_DisableIT_HT(DMA1, LL_DMA_CHANNEL_2);
|
||||
}
|
||||
if (!result) {
|
||||
furi_assert(0);
|
||||
furi_hal_irda_state = IrdaStateAsyncTxStopReq;
|
||||
}
|
||||
} else if (furi_hal_irda_state == IrdaStateAsyncTxStopReq) {
|
||||
/* fallthrough */
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
}
|
||||
if (LL_DMA_IsActiveFlag_TC2(DMA1) && LL_DMA_IsEnabledIT_TC(DMA1, LL_DMA_CHANNEL_2)) {
|
||||
LL_DMA_ClearFlag_TC2(DMA1);
|
||||
furi_check((furi_hal_irda_state == IrdaStateAsyncTxStopInProgress)
|
||||
|| (furi_hal_irda_state == IrdaStateAsyncTxStopReq)
|
||||
|| (furi_hal_irda_state == IrdaStateAsyncTx));
|
||||
|
||||
uint8_t buf_num = furi_hal_irda_get_current_dma_tx_buffer();
|
||||
uint8_t next_buf_num = !buf_num;
|
||||
if (furi_hal_irda_state == IrdaStateAsyncTxStopInProgress) {
|
||||
furi_hal_irda_tx_dma_terminate();
|
||||
} else if (irda_tim_tx.buffer[buf_num].last_packet_end
|
||||
|| (irda_tim_tx.buffer[buf_num].packet_end && (furi_hal_irda_state == IrdaStateAsyncTxStopReq))) {
|
||||
furi_hal_irda_state = IrdaStateAsyncTxStopInProgress;
|
||||
furi_hal_irda_tx_fill_buffer_last(next_buf_num);
|
||||
furi_hal_irda_tx_dma_set_buffer(next_buf_num);
|
||||
} else {
|
||||
/* if it's not end of the packet - continue receiving */
|
||||
furi_hal_irda_tx_dma_set_buffer(next_buf_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_irda_configure_tim_pwm_tx(uint32_t freq, float duty_cycle)
|
||||
{
|
||||
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM1);
|
||||
/* LL_DBGMCU_APB2_GRP1_FreezePeriph(LL_DBGMCU_APB2_GRP1_TIM1_STOP); */
|
||||
|
||||
LL_TIM_DisableCounter(TIM1);
|
||||
LL_TIM_SetRepetitionCounter(TIM1, 0);
|
||||
LL_TIM_SetCounter(TIM1, 0);
|
||||
LL_TIM_SetPrescaler(TIM1, 0);
|
||||
LL_TIM_SetCounterMode(TIM1, LL_TIM_COUNTERMODE_UP);
|
||||
LL_TIM_EnableARRPreload(TIM1);
|
||||
LL_TIM_SetAutoReload(TIM1, __LL_TIM_CALC_ARR(SystemCoreClock, LL_TIM_GetPrescaler(TIM1), freq));
|
||||
LL_TIM_OC_SetCompareCH3(TIM1, ( (LL_TIM_GetAutoReload(TIM1) + 1 ) * (1 - duty_cycle)));
|
||||
LL_TIM_OC_EnablePreload(TIM1, LL_TIM_CHANNEL_CH3);
|
||||
/* LL_TIM_OCMODE_PWM2 set by DMA */
|
||||
LL_TIM_OC_SetMode(TIM1, LL_TIM_CHANNEL_CH3, LL_TIM_OCMODE_FORCED_INACTIVE);
|
||||
LL_TIM_OC_SetPolarity(TIM1, LL_TIM_CHANNEL_CH3N, LL_TIM_OCPOLARITY_HIGH);
|
||||
LL_TIM_OC_DisableFast(TIM1, LL_TIM_CHANNEL_CH3);
|
||||
LL_TIM_CC_EnableChannel(TIM1, LL_TIM_CHANNEL_CH3N);
|
||||
LL_TIM_DisableIT_CC3(TIM1);
|
||||
LL_TIM_DisableMasterSlaveMode(TIM1);
|
||||
LL_TIM_EnableAllOutputs(TIM1);
|
||||
LL_TIM_DisableIT_UPDATE(TIM1);
|
||||
LL_TIM_EnableDMAReq_UPDATE(TIM1);
|
||||
|
||||
NVIC_SetPriority(TIM1_UP_TIM16_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(TIM1_UP_TIM16_IRQn);
|
||||
}
|
||||
|
||||
static void furi_hal_irda_configure_tim_cmgr2_dma_tx(void) {
|
||||
LL_C2_AHB1_GRP1_EnableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1);
|
||||
|
||||
LL_DMA_InitTypeDef dma_config = {0};
|
||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t)&(TIM1->CCMR2);
|
||||
dma_config.MemoryOrM2MDstAddress = (uint32_t) NULL;
|
||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
||||
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
/* fill word to have other bits set to 0 */
|
||||
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_BYTE;
|
||||
dma_config.NbData = 0;
|
||||
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
|
||||
dma_config.Priority = LL_DMA_PRIORITY_VERYHIGH;
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, furi_hal_irda_tx_dma_polarity_isr);
|
||||
LL_DMA_ClearFlag_TE1(DMA1);
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
||||
|
||||
NVIC_SetPriority(DMA1_Channel1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 4, 0));
|
||||
NVIC_EnableIRQ(DMA1_Channel1_IRQn);
|
||||
}
|
||||
|
||||
static void furi_hal_irda_configure_tim_rcr_dma_tx(void) {
|
||||
LL_C2_AHB1_GRP1_EnableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1);
|
||||
|
||||
LL_DMA_InitTypeDef dma_config = {0};
|
||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t)&(TIM1->RCR);
|
||||
dma_config.MemoryOrM2MDstAddress = (uint32_t) NULL;
|
||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
dma_config.Mode = LL_DMA_MODE_NORMAL;
|
||||
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_HALFWORD;
|
||||
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_HALFWORD;
|
||||
dma_config.NbData = 0;
|
||||
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM1_UP;
|
||||
dma_config.Priority = LL_DMA_PRIORITY_MEDIUM;
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_2, &dma_config);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_2, furi_hal_irda_tx_dma_isr);
|
||||
LL_DMA_ClearFlag_TC2(DMA1);
|
||||
LL_DMA_ClearFlag_HT2(DMA1);
|
||||
LL_DMA_ClearFlag_TE2(DMA1);
|
||||
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_EnableIT_HT(DMA1, LL_DMA_CHANNEL_2);
|
||||
LL_DMA_EnableIT_TE(DMA1, LL_DMA_CHANNEL_2);
|
||||
|
||||
NVIC_SetPriority(DMA1_Channel2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
|
||||
NVIC_EnableIRQ(DMA1_Channel2_IRQn);
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_fill_buffer_last(uint8_t buf_num) {
|
||||
furi_assert(buf_num < 2);
|
||||
furi_assert(furi_hal_irda_state != IrdaStateAsyncRx);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
furi_assert(irda_tim_tx.data_callback);
|
||||
IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num];
|
||||
furi_assert(buffer->data != NULL);
|
||||
furi_assert(buffer->polarity != NULL);
|
||||
|
||||
irda_tim_tx.buffer[buf_num].data[0] = 0; // 1 pulse
|
||||
irda_tim_tx.buffer[buf_num].polarity[0] = IRDA_TX_CCMR_LOW;
|
||||
irda_tim_tx.buffer[buf_num].data[1] = 0; // 1 pulse
|
||||
irda_tim_tx.buffer[buf_num].polarity[1] = IRDA_TX_CCMR_LOW;
|
||||
irda_tim_tx.buffer[buf_num].size = 2;
|
||||
irda_tim_tx.buffer[buf_num].last_packet_end = true;
|
||||
irda_tim_tx.buffer[buf_num].packet_end = true;
|
||||
}
|
||||
|
||||
static bool furi_hal_irda_tx_fill_buffer(uint8_t buf_num, uint8_t polarity_shift) {
|
||||
furi_assert(buf_num < 2);
|
||||
furi_assert(furi_hal_irda_state != IrdaStateAsyncRx);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
furi_assert(irda_tim_tx.data_callback);
|
||||
IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num];
|
||||
furi_assert(buffer->data != NULL);
|
||||
furi_assert(buffer->polarity != NULL);
|
||||
|
||||
FuriHalIrdaTxGetDataState status = FuriHalIrdaTxGetDataStateOk;
|
||||
uint32_t duration = 0;
|
||||
bool level = 0;
|
||||
size_t *size = &buffer->size;
|
||||
size_t polarity_counter = 0;
|
||||
while (polarity_shift--) {
|
||||
buffer->polarity[polarity_counter++] = IRDA_TX_CCMR_LOW;
|
||||
}
|
||||
|
||||
for (*size = 0; (*size < IRDA_TIM_TX_DMA_BUFFER_SIZE) && (status == FuriHalIrdaTxGetDataStateOk); ++(*size), ++polarity_counter) {
|
||||
status = irda_tim_tx.data_callback(irda_tim_tx.data_context, &duration, &level);
|
||||
if (status == FuriHalIrdaTxGetDataStateError) {
|
||||
furi_assert(0);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t num_of_impulses = roundf(duration / irda_tim_tx.cycle_duration);
|
||||
|
||||
if ((buffer->data[*size] + num_of_impulses - 1) > 0xFFFF) {
|
||||
furi_assert(0);
|
||||
status = FuriHalIrdaTxGetDataStateError;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer->polarity[polarity_counter] = level ? IRDA_TX_CCMR_HIGH : IRDA_TX_CCMR_LOW;
|
||||
buffer->data[*size] = num_of_impulses - 1;
|
||||
}
|
||||
|
||||
buffer->last_packet_end = (status == FuriHalIrdaTxGetDataStateLastDone);
|
||||
buffer->packet_end = buffer->last_packet_end || (status == FuriHalIrdaTxGetDataStateDone);
|
||||
|
||||
return status != FuriHalIrdaTxGetDataStateError;
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_dma_set_polarity(uint8_t buf_num, uint8_t polarity_shift) {
|
||||
furi_assert(buf_num < 2);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num];
|
||||
furi_assert(buffer->polarity != NULL);
|
||||
|
||||
__disable_irq();
|
||||
bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
if (channel_enabled) {
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
}
|
||||
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_1, (uint32_t) buffer->polarity);
|
||||
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_1, buffer->size + polarity_shift);
|
||||
if (channel_enabled) {
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
}
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
static void furi_hal_irda_tx_dma_set_buffer(uint8_t buf_num) {
|
||||
furi_assert(buf_num < 2);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
IrdaTxBuf* buffer = &irda_tim_tx.buffer[buf_num];
|
||||
furi_assert(buffer->data != NULL);
|
||||
|
||||
/* non-circular mode requires disabled channel before setup */
|
||||
__disable_irq();
|
||||
bool channel_enabled = LL_DMA_IsEnabledChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
if (channel_enabled) {
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
}
|
||||
LL_DMA_SetMemoryAddress(DMA1, LL_DMA_CHANNEL_2, (uint32_t)buffer->data);
|
||||
LL_DMA_SetDataLength(DMA1, LL_DMA_CHANNEL_2, buffer->size);
|
||||
if (channel_enabled) {
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
}
|
||||
__enable_irq();
|
||||
}
|
||||
|
||||
static void furi_hal_irda_async_tx_free_resources(void) {
|
||||
furi_assert((furi_hal_irda_state == IrdaStateIdle) || (furi_hal_irda_state == IrdaStateAsyncTxStopped));
|
||||
osStatus_t status;
|
||||
|
||||
hal_gpio_init_ex(&gpio_irda_tx, GpioModeOutputOpenDrain, GpioPullDown, GpioSpeedLow, 0);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, NULL);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_2, NULL);
|
||||
LL_TIM_DeInit(TIM1);
|
||||
LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_TIM1);
|
||||
LL_C2_AHB1_GRP1_DisableClock(LL_C2_AHB1_GRP1_PERIPH_DMA1);
|
||||
|
||||
status = osSemaphoreDelete(irda_tim_tx.stop_semaphore);
|
||||
furi_check(status == osOK);
|
||||
free(irda_tim_tx.buffer[0].data);
|
||||
free(irda_tim_tx.buffer[1].data);
|
||||
free(irda_tim_tx.buffer[0].polarity);
|
||||
free(irda_tim_tx.buffer[1].polarity);
|
||||
|
||||
irda_tim_tx.buffer[0].data = NULL;
|
||||
irda_tim_tx.buffer[1].data = NULL;
|
||||
irda_tim_tx.buffer[0].polarity = NULL;
|
||||
irda_tim_tx.buffer[1].polarity = NULL;
|
||||
}
|
||||
|
||||
bool furi_hal_irda_async_tx_start(uint32_t freq, float duty_cycle) {
|
||||
if ((duty_cycle > 1) || (duty_cycle < 0) || (freq > 40000) || (freq < 10000) || (irda_tim_tx.data_callback == NULL)) {
|
||||
furi_assert(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
furi_assert(furi_hal_irda_state == IrdaStateIdle);
|
||||
furi_assert(irda_tim_tx.buffer[0].data == NULL);
|
||||
furi_assert(irda_tim_tx.buffer[1].data == NULL);
|
||||
furi_assert(irda_tim_tx.buffer[0].polarity == NULL);
|
||||
furi_assert(irda_tim_tx.buffer[1].polarity == NULL);
|
||||
|
||||
size_t alloc_size_data = IRDA_TIM_TX_DMA_BUFFER_SIZE * sizeof(uint16_t);
|
||||
irda_tim_tx.buffer[0].data = furi_alloc(alloc_size_data);
|
||||
irda_tim_tx.buffer[1].data = furi_alloc(alloc_size_data);
|
||||
|
||||
size_t alloc_size_polarity = (IRDA_TIM_TX_DMA_BUFFER_SIZE + IRDA_POLARITY_SHIFT) * sizeof(uint8_t);
|
||||
irda_tim_tx.buffer[0].polarity = furi_alloc(alloc_size_polarity);
|
||||
irda_tim_tx.buffer[1].polarity = furi_alloc(alloc_size_polarity);
|
||||
|
||||
irda_tim_tx.stop_semaphore = osSemaphoreNew(1, 0, NULL);
|
||||
irda_tim_tx.cycle_duration = 1000000.0 / freq;
|
||||
|
||||
bool result = furi_hal_irda_tx_fill_buffer(0, IRDA_POLARITY_SHIFT);
|
||||
|
||||
if (result) {
|
||||
furi_hal_irda_configure_tim_pwm_tx(freq, duty_cycle);
|
||||
furi_hal_irda_configure_tim_cmgr2_dma_tx();
|
||||
furi_hal_irda_configure_tim_rcr_dma_tx();
|
||||
furi_hal_irda_tx_dma_set_polarity(0, IRDA_POLARITY_SHIFT);
|
||||
furi_hal_irda_tx_dma_set_buffer(0);
|
||||
|
||||
furi_hal_irda_state = IrdaStateAsyncTx;
|
||||
|
||||
LL_TIM_ClearFlag_UPDATE(TIM1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_2);
|
||||
delay_us(5);
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM1); /* DMA -> TIMx_RCR */
|
||||
delay_us(5);
|
||||
LL_GPIO_ResetOutputPin(gpio_irda_tx.port, gpio_irda_tx.pin); /* when disable it prevents false pulse */
|
||||
hal_gpio_init_ex(&gpio_irda_tx, GpioModeAltFunctionPushPull, GpioPullUp, GpioSpeedHigh, GpioAltFn1TIM1);
|
||||
|
||||
__disable_irq();
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM1); /* TIMx_RCR -> Repetition counter */
|
||||
LL_TIM_EnableCounter(TIM1);
|
||||
__enable_irq();
|
||||
|
||||
} else {
|
||||
furi_hal_irda_async_tx_free_resources();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_tx_wait_termination(void) {
|
||||
furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
|
||||
osStatus_t status;
|
||||
status = osSemaphoreAcquire(irda_tim_tx.stop_semaphore, osWaitForever);
|
||||
furi_check(status == osOK);
|
||||
furi_hal_irda_async_tx_free_resources();
|
||||
furi_hal_irda_state = IrdaStateIdle;
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_tx_stop(void) {
|
||||
furi_assert(furi_hal_irda_state >= IrdaStateAsyncTx);
|
||||
furi_assert(furi_hal_irda_state < IrdaStateMAX);
|
||||
|
||||
__disable_irq();
|
||||
if (furi_hal_irda_state == IrdaStateAsyncTx)
|
||||
furi_hal_irda_state = IrdaStateAsyncTxStopReq;
|
||||
__enable_irq();
|
||||
|
||||
furi_hal_irda_async_tx_wait_termination();
|
||||
}
|
||||
|
||||
void furi_hal_irda_async_tx_set_data_isr_callback(FuriHalIrdaTxGetDataCallback callback, void* context) {
|
||||
furi_assert(furi_hal_irda_state == IrdaStateIdle);
|
||||
irda_tim_tx.data_callback = callback;
|
||||
irda_tim_tx.data_context = context;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
#include <furi-hal-light.h>
|
||||
#include <lp5562.h>
|
||||
|
||||
#define LED_CURRENT_RED 50
|
||||
#define LED_CURRENT_GREEN 50
|
||||
#define LED_CURRENT_BLUE 50
|
||||
#define LED_CURRENT_WHITE 150
|
||||
|
||||
void furi_hal_light_init() {
|
||||
lp5562_reset();
|
||||
|
||||
lp5562_set_channel_current(LP5562ChannelRed, LED_CURRENT_RED);
|
||||
lp5562_set_channel_current(LP5562ChannelGreen, LED_CURRENT_GREEN);
|
||||
lp5562_set_channel_current(LP5562ChannelBlue, LED_CURRENT_BLUE);
|
||||
lp5562_set_channel_current(LP5562ChannelWhite, LED_CURRENT_WHITE);
|
||||
|
||||
lp5562_set_channel_value(LP5562ChannelRed, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelGreen, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelBlue, 0x00);
|
||||
lp5562_set_channel_value(LP5562ChannelWhite, 0x00);
|
||||
|
||||
lp5562_enable();
|
||||
lp5562_configure();
|
||||
FURI_LOG_I("FuriHalLight", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_light_set(Light light, uint8_t value) {
|
||||
switch(light) {
|
||||
case LightRed:
|
||||
lp5562_set_channel_value(LP5562ChannelRed, value);
|
||||
break;
|
||||
case LightGreen:
|
||||
lp5562_set_channel_value(LP5562ChannelGreen, value);
|
||||
break;
|
||||
case LightBlue:
|
||||
lp5562_set_channel_value(LP5562ChannelBlue, value);
|
||||
break;
|
||||
case LightBacklight:
|
||||
lp5562_set_channel_value(LP5562ChannelWhite, value);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
#include "furi-hal-nfc.h"
|
||||
#include <st25r3916.h>
|
||||
|
||||
static const uint32_t clocks_in_ms = 64 * 1000;
|
||||
|
||||
void furi_hal_nfc_init() {
|
||||
ReturnCode ret = rfalNfcInitialize();
|
||||
if(ret == ERR_NONE) {
|
||||
furi_hal_nfc_start_sleep();
|
||||
FURI_LOG_I("FuriHalNfc", "Init OK");
|
||||
} else {
|
||||
FURI_LOG_W("FuriHalNfc", "Initialization failed, RFAL returned: %d", ret);
|
||||
}
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_is_busy() {
|
||||
return rfalNfcGetState() != RFAL_NFC_STATE_IDLE;
|
||||
}
|
||||
|
||||
void furi_hal_nfc_field_on() {
|
||||
furi_hal_nfc_exit_sleep();
|
||||
st25r3916TxRxOn();
|
||||
}
|
||||
|
||||
void furi_hal_nfc_field_off() {
|
||||
st25r3916TxRxOff();
|
||||
furi_hal_nfc_start_sleep();
|
||||
}
|
||||
|
||||
void furi_hal_nfc_start_sleep() {
|
||||
rfalLowPowerModeStart();
|
||||
}
|
||||
|
||||
void furi_hal_nfc_exit_sleep() {
|
||||
rfalLowPowerModeStop();
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_detect(rfalNfcDevice **dev_list, uint8_t* dev_cnt, uint32_t timeout, bool deactivate) {
|
||||
furi_assert(dev_list);
|
||||
furi_assert(dev_cnt);
|
||||
|
||||
rfalLowPowerModeStop();
|
||||
rfalNfcState state = rfalNfcGetState();
|
||||
if(state == RFAL_NFC_STATE_NOTINIT) {
|
||||
rfalNfcInitialize();
|
||||
}
|
||||
rfalNfcDiscoverParam params;
|
||||
params.compMode = RFAL_COMPLIANCE_MODE_EMV;
|
||||
params.techs2Find = RFAL_NFC_POLL_TECH_A | RFAL_NFC_POLL_TECH_B | RFAL_NFC_POLL_TECH_F |
|
||||
RFAL_NFC_POLL_TECH_V | RFAL_NFC_POLL_TECH_AP2P | RFAL_NFC_POLL_TECH_ST25TB;
|
||||
params.totalDuration = 1000;
|
||||
params.devLimit = 3;
|
||||
params.wakeupEnabled = false;
|
||||
params.wakeupConfigDefault = true;
|
||||
params.nfcfBR = RFAL_BR_212;
|
||||
params.ap2pBR = RFAL_BR_424;
|
||||
params.maxBR = RFAL_BR_KEEP;
|
||||
params.GBLen = RFAL_NFCDEP_GB_MAX_LEN;
|
||||
params.notifyCb = NULL;
|
||||
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
rfalNfcDiscover(¶ms);
|
||||
while(state != RFAL_NFC_STATE_ACTIVATED) {
|
||||
rfalNfcWorker();
|
||||
state = rfalNfcGetState();
|
||||
FURI_LOG_D("HAL NFC", "Current state %d", state);
|
||||
if(state == RFAL_NFC_STATE_POLL_ACTIVATION) {
|
||||
start = DWT->CYCCNT;
|
||||
continue;
|
||||
}
|
||||
if(state == RFAL_NFC_STATE_POLL_SELECT) {
|
||||
rfalNfcSelect(0);
|
||||
}
|
||||
if(DWT->CYCCNT - start > timeout * clocks_in_ms) {
|
||||
rfalNfcDeactivate(true);
|
||||
FURI_LOG_D("HAL NFC", "Timeout");
|
||||
return false;
|
||||
}
|
||||
osThreadYield();
|
||||
}
|
||||
rfalNfcGetDevicesFound(dev_list, dev_cnt);
|
||||
if(deactivate) {
|
||||
rfalNfcDeactivate(false);
|
||||
rfalLowPowerModeStart();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool furi_hal_nfc_listen(uint8_t* uid, uint8_t uid_len, uint8_t* atqa, uint8_t sak, uint32_t timeout) {
|
||||
rfalNfcState state = rfalNfcGetState();
|
||||
|
||||
if(state == RFAL_NFC_STATE_NOTINIT) {
|
||||
rfalNfcInitialize();
|
||||
} else if(state >= RFAL_NFC_STATE_ACTIVATED) {
|
||||
rfalNfcDeactivate(false);
|
||||
}
|
||||
rfalLowPowerModeStop();
|
||||
rfalNfcDiscoverParam params = {
|
||||
.compMode = RFAL_COMPLIANCE_MODE_NFC,
|
||||
.techs2Find = RFAL_NFC_LISTEN_TECH_A,
|
||||
.totalDuration = 1000,
|
||||
.devLimit = 1,
|
||||
.wakeupEnabled = false,
|
||||
.wakeupConfigDefault = true,
|
||||
.nfcfBR = RFAL_BR_212,
|
||||
.ap2pBR = RFAL_BR_424,
|
||||
.maxBR = RFAL_BR_KEEP,
|
||||
.GBLen = RFAL_NFCDEP_GB_MAX_LEN,
|
||||
.notifyCb = NULL,
|
||||
};
|
||||
params.lmConfigPA.nfcidLen = uid_len;
|
||||
memcpy(params.lmConfigPA.nfcid, uid, uid_len);
|
||||
params.lmConfigPA.SENS_RES[0] = atqa[0];
|
||||
params.lmConfigPA.SENS_RES[1] = atqa[1];
|
||||
params.lmConfigPA.SEL_RES = sak;
|
||||
rfalNfcDiscover(¶ms);
|
||||
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
while(state != RFAL_NFC_STATE_ACTIVATED) {
|
||||
rfalNfcWorker();
|
||||
state = rfalNfcGetState();
|
||||
FURI_LOG_D("HAL NFC", "Current state %d", state);
|
||||
if(DWT->CYCCNT - start > timeout * clocks_in_ms) {
|
||||
rfalNfcDeactivate(true);
|
||||
return false;
|
||||
}
|
||||
osThreadYield();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ReturnCode furi_hal_nfc_data_exchange(uint8_t* tx_buff, uint16_t tx_len, uint8_t** rx_buff, uint16_t** rx_len, bool deactivate) {
|
||||
furi_assert(rx_buff);
|
||||
furi_assert(rx_len);
|
||||
|
||||
ReturnCode ret;
|
||||
rfalNfcState state = RFAL_NFC_STATE_ACTIVATED;
|
||||
ret = rfalNfcDataExchangeStart(tx_buff, tx_len, rx_buff, rx_len, 0);
|
||||
if(ret != ERR_NONE) {
|
||||
return ret;
|
||||
}
|
||||
uint32_t start = DWT->CYCCNT;
|
||||
while(state != RFAL_NFC_STATE_DATAEXCHANGE_DONE) {
|
||||
rfalNfcWorker();
|
||||
state = rfalNfcGetState();
|
||||
ret = rfalNfcDataExchangeGetStatus();
|
||||
FURI_LOG_D("HAL NFC", "Nfc st: %d Data st: %d", state, ret);
|
||||
if(ret > ERR_SLEEP_REQ) {
|
||||
return ret;
|
||||
}
|
||||
if(ret == ERR_BUSY) {
|
||||
if(DWT->CYCCNT - start > 1000 * clocks_in_ms) {
|
||||
return ERR_TIMEOUT;
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
start = DWT->CYCCNT;
|
||||
}
|
||||
taskYIELD();
|
||||
}
|
||||
if(deactivate) {
|
||||
rfalNfcDeactivate(false);
|
||||
rfalLowPowerModeStart();
|
||||
}
|
||||
return ERR_NONE;
|
||||
}
|
||||
|
||||
void furi_hal_nfc_deactivate() {
|
||||
rfalNfcDeactivate(false);
|
||||
rfalLowPowerModeStart();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include <stm32wbxx_ll_lptim.h>
|
||||
#include <stm32wbxx_ll_bus.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Timer used for system ticks
|
||||
#define FURI_HAL_OS_TIMER_MAX 0xFFFF
|
||||
#define FURI_HAL_OS_TIMER_REG_LOAD_DLY 0x1
|
||||
#define FURI_HAL_OS_TIMER LPTIM2
|
||||
#define FURI_HAL_OS_TIMER_IRQ LPTIM2_IRQn
|
||||
|
||||
static inline void furi_hal_os_timer_init() {
|
||||
// Configure clock source
|
||||
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE);
|
||||
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2);
|
||||
// Set interrupt priority and enable them
|
||||
NVIC_SetPriority(FURI_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
|
||||
NVIC_EnableIRQ(FURI_HAL_OS_TIMER_IRQ);
|
||||
}
|
||||
|
||||
static inline void furi_hal_os_timer_continuous(uint32_t count) {
|
||||
// Enable timer
|
||||
LL_LPTIM_Enable(FURI_HAL_OS_TIMER);
|
||||
while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER));
|
||||
|
||||
// Enable rutoreload match interrupt
|
||||
LL_LPTIM_EnableIT_ARRM(FURI_HAL_OS_TIMER);
|
||||
|
||||
// Set autoreload and start counter
|
||||
LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count);
|
||||
LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
|
||||
}
|
||||
|
||||
static inline void furi_hal_os_timer_single(uint32_t count) {
|
||||
// Enable timer
|
||||
LL_LPTIM_Enable(FURI_HAL_OS_TIMER);
|
||||
while(!LL_LPTIM_IsEnabled(FURI_HAL_OS_TIMER));
|
||||
|
||||
// Enable compare match interrupt
|
||||
LL_LPTIM_EnableIT_CMPM(FURI_HAL_OS_TIMER);
|
||||
|
||||
// Set compare, autoreload and start counter
|
||||
// Include some marging to workaround ARRM behaviour
|
||||
LL_LPTIM_SetCompare(FURI_HAL_OS_TIMER, count-3);
|
||||
LL_LPTIM_SetAutoReload(FURI_HAL_OS_TIMER, count);
|
||||
LL_LPTIM_StartCounter(FURI_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_ONESHOT);
|
||||
}
|
||||
|
||||
static inline void furi_hal_os_timer_reset() {
|
||||
// Hard reset timer
|
||||
// THE ONLY RELIABLEWAY to stop it according to errata
|
||||
LL_LPTIM_DeInit(FURI_HAL_OS_TIMER);
|
||||
}
|
||||
|
||||
static inline uint32_t furi_hal_os_timer_get_cnt() {
|
||||
uint32_t counter = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
|
||||
uint32_t counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
|
||||
while(counter != counter_shadow) {
|
||||
counter = counter_shadow;
|
||||
counter_shadow = LL_LPTIM_GetCounter(FURI_HAL_OS_TIMER);
|
||||
}
|
||||
return counter;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
#include <furi-hal-os.h>
|
||||
#include <furi-hal-os-timer.h>
|
||||
#include <furi-hal-power.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#define FURI_HAL_OS_CLK_FREQUENCY 32768
|
||||
#define FURI_HAL_OS_TICK_PER_SECOND 1024
|
||||
#define FURI_HAL_OS_CLK_PER_TICK (FURI_HAL_OS_CLK_FREQUENCY / FURI_HAL_OS_TICK_PER_SECOND)
|
||||
#define FURI_HAL_OS_TICK_PER_EPOCH (FURI_HAL_OS_TIMER_MAX / FURI_HAL_OS_CLK_PER_TICK)
|
||||
#define FURI_HAL_OS_MAX_SLEEP (FURI_HAL_OS_TICK_PER_EPOCH - 1)
|
||||
|
||||
#ifdef FURI_HAL_OS_DEBUG
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
|
||||
#define LED_SLEEP_PORT GPIOA
|
||||
#define LED_SLEEP_PIN LL_GPIO_PIN_7
|
||||
#define LED_TICK_PORT GPIOA
|
||||
#define LED_TICK_PIN LL_GPIO_PIN_6
|
||||
#define LED_SECOND_PORT GPIOA
|
||||
#define LED_SECOND_PIN LL_GPIO_PIN_4
|
||||
|
||||
void furi_hal_os_timer_callback() {
|
||||
LL_GPIO_TogglePin(LED_SECOND_PORT, LED_SECOND_PIN);
|
||||
}
|
||||
#endif
|
||||
|
||||
volatile uint32_t furi_hal_os_skew = 0;
|
||||
|
||||
void furi_hal_os_init() {
|
||||
LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
|
||||
|
||||
furi_hal_os_timer_init();
|
||||
furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK);
|
||||
|
||||
#ifdef FURI_HAL_OS_DEBUG
|
||||
LL_GPIO_SetPinMode(LED_SLEEP_PORT, LED_SLEEP_PIN, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinMode(LED_TICK_PORT, LED_TICK_PIN, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinMode(LED_SECOND_PORT, LED_SECOND_PIN, LL_GPIO_MODE_OUTPUT);
|
||||
osTimerId_t second_timer = osTimerNew(furi_hal_os_timer_callback, osTimerPeriodic, NULL, NULL);
|
||||
osTimerStart(second_timer, 1024);
|
||||
#endif
|
||||
|
||||
FURI_LOG_I("FuriHalOs", "Init OK");
|
||||
}
|
||||
|
||||
void LPTIM2_IRQHandler(void) {
|
||||
// Autoreload
|
||||
if(LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER)) {
|
||||
LL_LPTIM_ClearFLAG_ARRM(FURI_HAL_OS_TIMER);
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
|
||||
#ifdef FURI_HAL_OS_DEBUG
|
||||
LL_GPIO_TogglePin(LED_TICK_PORT, LED_TICK_PIN);
|
||||
#endif
|
||||
xPortSysTickHandler();
|
||||
}
|
||||
}
|
||||
if(LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER)) {
|
||||
LL_LPTIM_ClearFLAG_CMPM(FURI_HAL_OS_TIMER);
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint32_t furi_hal_os_sleep(TickType_t expected_idle_ticks) {
|
||||
// Stop ticks
|
||||
furi_hal_os_timer_reset();
|
||||
LL_SYSTICK_DisableIT();
|
||||
|
||||
// Start wakeup timer
|
||||
furi_hal_os_timer_single(expected_idle_ticks * FURI_HAL_OS_CLK_PER_TICK);
|
||||
|
||||
#ifdef FURI_HAL_OS_DEBUG
|
||||
LL_GPIO_ResetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN);
|
||||
#endif
|
||||
|
||||
// Go to sleep mode
|
||||
furi_hal_power_sleep();
|
||||
|
||||
#ifdef FURI_HAL_OS_DEBUG
|
||||
LL_GPIO_SetOutputPin(LED_SLEEP_PORT, LED_SLEEP_PIN);
|
||||
#endif
|
||||
|
||||
// Calculate how much time we spent in the sleep
|
||||
uint32_t after_cnt = furi_hal_os_timer_get_cnt() + furi_hal_os_skew;
|
||||
uint32_t after_tick = after_cnt / FURI_HAL_OS_CLK_PER_TICK;
|
||||
furi_hal_os_skew = after_cnt % FURI_HAL_OS_CLK_PER_TICK;
|
||||
|
||||
bool cmpm = LL_LPTIM_IsActiveFlag_CMPM(FURI_HAL_OS_TIMER);
|
||||
bool arrm = LL_LPTIM_IsActiveFlag_ARRM(FURI_HAL_OS_TIMER);
|
||||
if (cmpm && arrm) after_tick += expected_idle_ticks;
|
||||
|
||||
// Prepare tick timer for new round
|
||||
furi_hal_os_timer_reset();
|
||||
|
||||
// Resume ticks
|
||||
LL_SYSTICK_EnableIT();
|
||||
furi_hal_os_timer_continuous(FURI_HAL_OS_CLK_PER_TICK);
|
||||
|
||||
return after_tick;
|
||||
}
|
||||
|
||||
void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
|
||||
if(!furi_hal_power_sleep_available()) {
|
||||
__WFI();
|
||||
return;
|
||||
}
|
||||
|
||||
// Limit mount of ticks to maximum that timer can count
|
||||
if (expected_idle_ticks > FURI_HAL_OS_MAX_SLEEP) {
|
||||
expected_idle_ticks = FURI_HAL_OS_MAX_SLEEP;
|
||||
}
|
||||
|
||||
// Stop IRQ handling, no one should disturb us till we finish
|
||||
__disable_irq();
|
||||
|
||||
// Confirm OS that sleep is still possible
|
||||
if (eTaskConfirmSleepModeStatus() == eAbortSleep) {
|
||||
__enable_irq();
|
||||
return;
|
||||
}
|
||||
|
||||
// Sleep and track how much ticks we spent sleeping
|
||||
uint32_t completed_ticks = furi_hal_os_sleep(expected_idle_ticks);
|
||||
|
||||
// Reenable IRQ
|
||||
__enable_irq();
|
||||
|
||||
// Notify system about time spent in sleep
|
||||
if (completed_ticks > 0) {
|
||||
if (completed_ticks > expected_idle_ticks) {
|
||||
vTaskStepTick(expected_idle_ticks);
|
||||
} else {
|
||||
vTaskStepTick(completed_ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
|
||||
asm("bkpt 1");
|
||||
while(1) {};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initialize OS helpers
|
||||
* Configure and start tick timer
|
||||
*/
|
||||
void furi_hal_os_init();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,280 @@
|
||||
#include <furi-hal-power.h>
|
||||
#include <furi-hal-clock.h>
|
||||
#include <furi-hal-bt.h>
|
||||
|
||||
#include <stm32wbxx_ll_rcc.h>
|
||||
#include <stm32wbxx_ll_pwr.h>
|
||||
#include <stm32wbxx_ll_hsem.h>
|
||||
#include <stm32wbxx_ll_cortex.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
|
||||
#include <main.h>
|
||||
#include <hw_conf.h>
|
||||
#include <bq27220.h>
|
||||
#include <bq25896.h>
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
typedef struct {
|
||||
volatile uint32_t insomnia;
|
||||
volatile uint32_t deep_insomnia;
|
||||
} FuriHalPower;
|
||||
|
||||
static volatile FuriHalPower furi_hal_power = {
|
||||
.insomnia = 0,
|
||||
.deep_insomnia = 1,
|
||||
};
|
||||
|
||||
const ParamCEDV cedv = {
|
||||
.cedv_conf.gauge_conf = {
|
||||
.CCT = 1,
|
||||
.CSYNC = 0,
|
||||
.EDV_CMP = 0,
|
||||
.SC = 1,
|
||||
.FIXED_EDV0 = 1,
|
||||
.FCC_LIM = 1,
|
||||
.FC_FOR_VDQ = 1,
|
||||
.IGNORE_SD = 1,
|
||||
.SME0 = 0,
|
||||
},
|
||||
.full_charge_cap = 2100,
|
||||
.design_cap = 2100,
|
||||
.EDV0 = 3300,
|
||||
.EDV1 = 3321,
|
||||
.EDV2 = 3355,
|
||||
.EMF = 3679,
|
||||
.C0 = 430,
|
||||
.C1 = 0,
|
||||
.R1 = 408,
|
||||
.R0 = 334,
|
||||
.T0 = 4626,
|
||||
.TC = 11,
|
||||
.DOD0 = 4044,
|
||||
.DOD10 = 3905,
|
||||
.DOD20 = 3807,
|
||||
.DOD30 = 3718,
|
||||
.DOD40 = 3642,
|
||||
.DOD50 = 3585,
|
||||
.DOD60 = 3546,
|
||||
.DOD70 = 3514,
|
||||
.DOD80 = 3477,
|
||||
.DOD90 = 3411,
|
||||
.DOD100 = 3299,
|
||||
};
|
||||
|
||||
void HAL_RCC_CSSCallback(void) {
|
||||
// TODO: notify user about issue with HSE
|
||||
furi_hal_power_reset();
|
||||
}
|
||||
|
||||
void furi_hal_power_init() {
|
||||
LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
|
||||
LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
|
||||
bq27220_init(&cedv);
|
||||
bq25896_init();
|
||||
FURI_LOG_I("FuriHalPower", "Init OK");
|
||||
}
|
||||
|
||||
uint16_t furi_hal_power_insomnia_level() {
|
||||
return furi_hal_power.insomnia;
|
||||
}
|
||||
|
||||
void furi_hal_power_insomnia_enter() {
|
||||
furi_hal_power.insomnia++;
|
||||
}
|
||||
|
||||
void furi_hal_power_insomnia_exit() {
|
||||
furi_hal_power.insomnia--;
|
||||
}
|
||||
|
||||
bool furi_hal_power_sleep_available() {
|
||||
return furi_hal_power.insomnia == 0;
|
||||
}
|
||||
|
||||
bool furi_hal_power_deep_sleep_available() {
|
||||
return furi_hal_bt_is_alive() && furi_hal_power.deep_insomnia == 0;
|
||||
}
|
||||
|
||||
void furi_hal_power_light_sleep() {
|
||||
__WFI();
|
||||
}
|
||||
|
||||
void furi_hal_power_deep_sleep() {
|
||||
while( LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
|
||||
|
||||
if (!LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID)) {
|
||||
if(LL_PWR_IsActiveFlag_C2DS()) {
|
||||
// Release ENTRY_STOP_MODE semaphore
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
|
||||
|
||||
// The switch on HSI before entering Stop Mode is required
|
||||
furi_hal_clock_switch_to_hsi();
|
||||
}
|
||||
} else {
|
||||
/**
|
||||
* The switch on HSI before entering Stop Mode is required
|
||||
*/
|
||||
furi_hal_clock_switch_to_hsi();
|
||||
}
|
||||
|
||||
/* Release RCC semaphore */
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
|
||||
|
||||
// Prepare deep sleep
|
||||
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP1);
|
||||
LL_LPM_EnableDeepSleep();
|
||||
|
||||
#if defined ( __CC_ARM)
|
||||
// Force store operations
|
||||
__force_stores();
|
||||
#endif
|
||||
|
||||
__WFI();
|
||||
|
||||
/* Release ENTRY_STOP_MODE semaphore */
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
|
||||
|
||||
while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
|
||||
|
||||
if(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) {
|
||||
furi_hal_clock_switch_to_pll();
|
||||
}
|
||||
|
||||
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
|
||||
}
|
||||
|
||||
void furi_hal_power_sleep() {
|
||||
if(furi_hal_power_deep_sleep_available()) {
|
||||
furi_hal_power_deep_sleep();
|
||||
} else {
|
||||
furi_hal_power_light_sleep();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint8_t furi_hal_power_get_pct() {
|
||||
return bq27220_get_state_of_charge();
|
||||
}
|
||||
|
||||
uint8_t furi_hal_power_get_bat_health_pct() {
|
||||
return bq27220_get_state_of_health();
|
||||
}
|
||||
|
||||
bool furi_hal_power_is_charging() {
|
||||
return bq25896_is_charging();
|
||||
}
|
||||
|
||||
void furi_hal_power_off() {
|
||||
bq25896_poweroff();
|
||||
}
|
||||
|
||||
void furi_hal_power_reset() {
|
||||
NVIC_SystemReset();
|
||||
}
|
||||
|
||||
void furi_hal_power_enable_otg() {
|
||||
bq25896_enable_otg();
|
||||
}
|
||||
|
||||
void furi_hal_power_disable_otg() {
|
||||
bq25896_disable_otg();
|
||||
}
|
||||
|
||||
uint32_t furi_hal_power_get_battery_remaining_capacity() {
|
||||
return bq27220_get_remaining_capacity();
|
||||
}
|
||||
|
||||
uint32_t furi_hal_power_get_battery_full_capacity() {
|
||||
return bq27220_get_full_charge_capacity();
|
||||
}
|
||||
|
||||
float furi_hal_power_get_battery_voltage(FuriHalPowerIC ic) {
|
||||
if (ic == FuriHalPowerICCharger) {
|
||||
return (float)bq25896_get_vbat_voltage() / 1000.0f;
|
||||
} else if (ic == FuriHalPowerICFuelGauge) {
|
||||
return (float)bq27220_get_voltage() / 1000.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float furi_hal_power_get_battery_current(FuriHalPowerIC ic) {
|
||||
if (ic == FuriHalPowerICCharger) {
|
||||
return (float)bq25896_get_vbat_current() / 1000.0f;
|
||||
} else if (ic == FuriHalPowerICFuelGauge) {
|
||||
return (float)bq27220_get_current() / 1000.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float furi_hal_power_get_battery_temperature(FuriHalPowerIC ic) {
|
||||
if (ic == FuriHalPowerICCharger) {
|
||||
// Linear approximation, +/- 5 C
|
||||
return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
|
||||
} else if (ic == FuriHalPowerICFuelGauge) {
|
||||
return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
float furi_hal_power_get_usb_voltage(){
|
||||
return (float)bq25896_get_vbus_voltage() / 1000.0f;
|
||||
}
|
||||
|
||||
void furi_hal_power_dump_state() {
|
||||
BatteryStatus battery_status;
|
||||
OperationStatus operation_status;
|
||||
if (bq27220_get_battery_status(&battery_status) == BQ27220_ERROR
|
||||
|| bq27220_get_operation_status(&operation_status) == BQ27220_ERROR) {
|
||||
printf("Failed to get bq27220 status. Communication error.\r\n");
|
||||
} else {
|
||||
printf(
|
||||
"bq27220: CALMD: %d, SEC0: %d, SEC1: %d, EDV2: %d, VDQ: %d, INITCOMP: %d, SMTH: %d, BTPINT: %d, CFGUPDATE: %d\r\n",
|
||||
operation_status.CALMD, operation_status.SEC0, operation_status.SEC1,
|
||||
operation_status.EDV2, operation_status.VDQ, operation_status.INITCOMP,
|
||||
operation_status.SMTH, operation_status.BTPINT, operation_status.CFGUPDATE
|
||||
);
|
||||
// Battery status register, part 1
|
||||
printf(
|
||||
"bq27220: CHGINH: %d, FC: %d, OTD: %d, OTC: %d, SLEEP: %d, OCVFAIL: %d, OCVCOMP: %d, FD: %d\r\n",
|
||||
battery_status.CHGINH, battery_status.FC, battery_status.OTD,
|
||||
battery_status.OTC, battery_status.SLEEP, battery_status.OCVFAIL,
|
||||
battery_status.OCVCOMP, battery_status.FD
|
||||
);
|
||||
// Battery status register, part 2
|
||||
printf(
|
||||
"bq27220: DSG: %d, SYSDWN: %d, TDA: %d, BATTPRES: %d, AUTH_GD: %d, OCVGD: %d, TCA: %d, RSVD: %d\r\n",
|
||||
battery_status.DSG, battery_status.SYSDWN, battery_status.TDA,
|
||||
battery_status.BATTPRES, battery_status.AUTH_GD, battery_status.OCVGD,
|
||||
battery_status.TCA, battery_status.RSVD
|
||||
);
|
||||
// Voltage and current info
|
||||
printf(
|
||||
"bq27220: Full capacity: %dmAh, Design capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%, State of health: %d%%\r\n",
|
||||
bq27220_get_full_charge_capacity(), bq27220_get_design_capacity(), bq27220_get_remaining_capacity(),
|
||||
bq27220_get_state_of_charge(), bq27220_get_state_of_health()
|
||||
);
|
||||
printf(
|
||||
"bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
|
||||
bq27220_get_voltage(), bq27220_get_current(), (int)furi_hal_power_get_battery_temperature(FuriHalPowerICFuelGauge)
|
||||
);
|
||||
}
|
||||
|
||||
printf(
|
||||
"bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %ldm%%\r\n",
|
||||
bq25896_get_vbus_voltage(), bq25896_get_vsys_voltage(),
|
||||
bq25896_get_vbat_voltage(), bq25896_get_vbat_current(),
|
||||
bq25896_get_ntc_mpct()
|
||||
);
|
||||
}
|
||||
|
||||
void furi_hal_power_enable_external_3_3v(){
|
||||
LL_GPIO_SetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin);
|
||||
}
|
||||
|
||||
void furi_hal_power_disable_external_3_3v(){
|
||||
LL_GPIO_ResetOutputPin(PERIPH_POWER_GPIO_Port, PERIPH_POWER_Pin);
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "furi-hal-pwm.h"
|
||||
|
||||
void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel) {
|
||||
tim->Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq) - 1;
|
||||
tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
HAL_TIM_PWM_Init(tim);
|
||||
|
||||
TIM_OC_InitTypeDef sConfigOC;
|
||||
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = (uint16_t)(tim->Init.Period * value);
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
HAL_TIM_PWM_ConfigChannel(tim, &sConfigOC, channel);
|
||||
HAL_TIM_PWM_Start(tim, channel);
|
||||
}
|
||||
|
||||
void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel) {
|
||||
tim->Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
tim->Init.Period = (uint32_t)((SystemCoreClock / (tim->Init.Prescaler + 1)) / freq) - 1;
|
||||
tim->Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
tim->Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
HAL_TIM_PWM_Init(tim);
|
||||
|
||||
TIM_OC_InitTypeDef sConfigOC;
|
||||
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = (uint16_t)(tim->Init.Period * value);
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
HAL_TIM_PWM_ConfigChannel(tim, &sConfigOC, channel);
|
||||
HAL_TIMEx_PWMN_Start(tim, channel);
|
||||
}
|
||||
|
||||
void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel) {
|
||||
HAL_TIM_PWM_Stop(tim, channel);
|
||||
}
|
||||
|
||||
void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel) {
|
||||
HAL_TIMEx_PWMN_Stop(tim, channel);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include "stdbool.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void hal_pwm_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel);
|
||||
void hal_pwmn_set(float value, float freq, TIM_HandleTypeDef* tim, uint32_t channel);
|
||||
void hal_pwm_stop(TIM_HandleTypeDef* tim, uint32_t channel);
|
||||
void hal_pwmn_stop(TIM_HandleTypeDef* tim, uint32_t channel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,56 @@
|
||||
#include <furi-hal-resources.h>
|
||||
#include "main.h"
|
||||
#include <furi.h>
|
||||
|
||||
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_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_OK_GPIO_Port, .pin = BUTTON_OK_Pin, .key = InputKeyOk, .inverted = false},
|
||||
{.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 vibro_gpio = {.port = VIBRO_GPIO_Port, .pin = VIBRO_Pin};
|
||||
const GpioPin ibutton_gpio = {.port = iBTN_GPIO_Port, .pin = iBTN_Pin};
|
||||
|
||||
const GpioPin gpio_cc1101_g0 = {.port = CC1101_G0_GPIO_Port, .pin = CC1101_G0_Pin};
|
||||
const GpioPin gpio_rf_sw_0 = {.port = RF_SW_0_GPIO_Port, .pin = RF_SW_0_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};
|
||||
|
||||
const GpioPin gpio_ext_pc0 = {.port = GPIOC, .pin = GPIO_PIN_0};
|
||||
const GpioPin gpio_ext_pc1 = {.port = GPIOC, .pin = GPIO_PIN_1};
|
||||
const GpioPin gpio_ext_pc3 = {.port = GPIOC, .pin = GPIO_PIN_3};
|
||||
const GpioPin gpio_ext_pb2 = {.port = GPIOB, .pin = GPIO_PIN_2};
|
||||
const GpioPin gpio_ext_pb3 = {.port = GPIOB, .pin = GPIO_PIN_3};
|
||||
const GpioPin gpio_ext_pa4 = {.port = GPIOA, .pin = GPIO_PIN_4};
|
||||
const GpioPin gpio_ext_pa6 = {.port = GPIOA, .pin = GPIO_PIN_6};
|
||||
const GpioPin gpio_ext_pa7 = {.port = GPIOA, .pin = GPIO_PIN_7};
|
||||
|
||||
const GpioPin gpio_rfid_pull = {.port = RFID_PULL_GPIO_Port, .pin = RFID_PULL_Pin};
|
||||
const GpioPin gpio_rfid_carrier_out = {.port = RFID_OUT_GPIO_Port, .pin = RFID_OUT_Pin};
|
||||
const GpioPin gpio_rfid_data_in = {.port = RFID_RF_IN_GPIO_Port, .pin = RFID_RF_IN_Pin};
|
||||
|
||||
const GpioPin gpio_irda_rx = {.port = IR_RX_GPIO_Port, .pin = IR_RX_Pin};
|
||||
const GpioPin gpio_irda_tx = {.port = IR_TX_GPIO_Port, .pin = IR_TX_Pin};
|
||||
|
||||
const GpioPin gpio_usart_tx = {.port = USART1_TX_Port, .pin = USART1_TX_Pin};
|
||||
const GpioPin gpio_usart_rx = {.port = USART1_RX_Port, .pin = USART1_RX_Pin};
|
||||
@@ -0,0 +1,103 @@
|
||||
#pragma once
|
||||
|
||||
#include "main.h"
|
||||
#include <furi.h>
|
||||
|
||||
#include <stm32wbxx.h>
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define POWER_I2C_SCL_Pin LL_GPIO_PIN_9
|
||||
#define POWER_I2C_SCL_GPIO_Port GPIOA
|
||||
#define POWER_I2C_SDA_Pin LL_GPIO_PIN_10
|
||||
#define POWER_I2C_SDA_GPIO_Port GPIOA
|
||||
|
||||
#define POWER_I2C I2C1
|
||||
/** Timing register value is computed with the STM32CubeMX Tool,
|
||||
* Standard Mode @100kHz with I2CCLK = 64 MHz,
|
||||
* rise time = 0ns, fall time = 0ns
|
||||
*/
|
||||
#define POWER_I2C_TIMINGS_100 0x10707DBC
|
||||
|
||||
/** Timing register value is computed with the STM32CubeMX Tool,
|
||||
* Fast Mode @400kHz with I2CCLK = 64 MHz,
|
||||
* rise time = 0ns, fall time = 0ns
|
||||
*/
|
||||
#define POWER_I2C_TIMINGS_400 0x00602173
|
||||
|
||||
/* Input Related Constants */
|
||||
#define INPUT_DEBOUNCE_TICKS 20
|
||||
|
||||
/* Input Keys */
|
||||
typedef enum {
|
||||
InputKeyUp,
|
||||
InputKeyDown,
|
||||
InputKeyRight,
|
||||
InputKeyLeft,
|
||||
InputKeyOk,
|
||||
InputKeyBack,
|
||||
} InputKey;
|
||||
|
||||
/* Light */
|
||||
typedef enum {
|
||||
LightRed,
|
||||
LightGreen,
|
||||
LightBlue,
|
||||
LightBacklight,
|
||||
} Light;
|
||||
|
||||
typedef struct {
|
||||
const GPIO_TypeDef* port;
|
||||
const uint16_t pin;
|
||||
const InputKey key;
|
||||
const bool inverted;
|
||||
} InputPin;
|
||||
|
||||
extern const InputPin input_pins[];
|
||||
extern const size_t input_pins_count;
|
||||
|
||||
extern const GpioPin vibro_gpio;
|
||||
extern const GpioPin ibutton_gpio;
|
||||
|
||||
extern const GpioPin gpio_cc1101_g0;
|
||||
extern const GpioPin gpio_rf_sw_0;
|
||||
|
||||
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;
|
||||
|
||||
extern const GpioPin gpio_ext_pc0;
|
||||
extern const GpioPin gpio_ext_pc1;
|
||||
extern const GpioPin gpio_ext_pc3;
|
||||
extern const GpioPin gpio_ext_pb2;
|
||||
extern const GpioPin gpio_ext_pb3;
|
||||
extern const GpioPin gpio_ext_pa4;
|
||||
extern const GpioPin gpio_ext_pa6;
|
||||
extern const GpioPin gpio_ext_pa7;
|
||||
|
||||
extern const GpioPin gpio_rfid_pull;
|
||||
extern const GpioPin gpio_rfid_carrier_out;
|
||||
extern const GpioPin gpio_rfid_data_in;
|
||||
|
||||
extern const GpioPin gpio_irda_rx;
|
||||
extern const GpioPin gpio_irda_tx;
|
||||
|
||||
extern const GpioPin gpio_usart_tx;
|
||||
extern const GpioPin gpio_usart_rx;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,278 @@
|
||||
#include <furi-hal-rfid.h>
|
||||
#include <furi-hal-ibutton.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#define LFRFID_READ_TIM htim1
|
||||
#define LFRFID_READ_CHANNEL TIM_CHANNEL_1
|
||||
#define LFRFID_EMULATE_TIM htim2
|
||||
#define LFRFID_EMULATE_CHANNEL TIM_CHANNEL_3
|
||||
|
||||
void furi_hal_rfid_pins_reset() {
|
||||
// ibutton bus disable
|
||||
furi_hal_ibutton_stop();
|
||||
|
||||
// pulldown rfid antenna
|
||||
hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo);
|
||||
hal_gpio_write(&gpio_rfid_carrier_out, false);
|
||||
|
||||
// from both sides
|
||||
hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo);
|
||||
hal_gpio_write(&gpio_rfid_pull, true);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_pins_emulate() {
|
||||
// ibutton low
|
||||
furi_hal_ibutton_start();
|
||||
furi_hal_ibutton_pin_low();
|
||||
|
||||
// pull pin to timer out
|
||||
hal_gpio_init_ex(
|
||||
&gpio_rfid_pull, GpioModeAltFunctionPushPull, GpioSpeedLow, GpioPullNo, GpioAltFn1TIM2);
|
||||
|
||||
// pull rfid antenna from carrier side
|
||||
hal_gpio_init(&gpio_rfid_carrier_out, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo);
|
||||
hal_gpio_write(&gpio_rfid_carrier_out, false);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_pins_read() {
|
||||
// ibutton low
|
||||
furi_hal_ibutton_start();
|
||||
furi_hal_ibutton_pin_low();
|
||||
|
||||
// dont pull rfid antenna
|
||||
hal_gpio_init(&gpio_rfid_pull, GpioModeOutputPushPull, GpioSpeedLow, GpioPullNo);
|
||||
hal_gpio_write(&gpio_rfid_pull, false);
|
||||
|
||||
// carrier pin to timer out
|
||||
hal_gpio_init_ex(
|
||||
&gpio_rfid_carrier_out,
|
||||
GpioModeAltFunctionPushPull,
|
||||
GpioSpeedLow,
|
||||
GpioPullNo,
|
||||
GpioAltFn1TIM1);
|
||||
|
||||
// comparator in
|
||||
hal_gpio_init(&gpio_rfid_data_in, GpioModeAnalog, GpioSpeedLow, GpioPullNo);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_read(float freq, float duty_cycle) {
|
||||
// TODO LL init
|
||||
uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1;
|
||||
|
||||
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
|
||||
|
||||
// basic PWM setup with needed freq and internal clock
|
||||
LFRFID_READ_TIM.Init.Prescaler = 0;
|
||||
LFRFID_READ_TIM.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
LFRFID_READ_TIM.Init.Period = period;
|
||||
LFRFID_READ_TIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
LFRFID_READ_TIM.Init.RepetitionCounter = 0;
|
||||
LFRFID_READ_TIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
|
||||
if(HAL_TIM_Base_Init(&LFRFID_READ_TIM) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
|
||||
if(HAL_TIM_ConfigClockSource(&LFRFID_READ_TIM, &sClockSourceConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
if(HAL_TIM_PWM_Init(&LFRFID_READ_TIM) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// no master-slave mode
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if(HAL_TIMEx_MasterConfigSynchronization(&LFRFID_READ_TIM, &sMasterConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// pwm config
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = (uint32_t)(LFRFID_READ_TIM.Init.Period * duty_cycle);
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
if(HAL_TIM_OC_ConfigChannel(&LFRFID_READ_TIM, &sConfigOC, LFRFID_READ_CHANNEL) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// no deadtime
|
||||
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
|
||||
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
|
||||
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
|
||||
sBreakDeadTimeConfig.DeadTime = 0;
|
||||
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
|
||||
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
|
||||
sBreakDeadTimeConfig.BreakFilter = 0;
|
||||
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
|
||||
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
|
||||
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
|
||||
sBreakDeadTimeConfig.Break2Filter = 0;
|
||||
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
|
||||
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
|
||||
if(HAL_TIMEx_ConfigBreakDeadTime(&LFRFID_READ_TIM, &sBreakDeadTimeConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_read_start() {
|
||||
HAL_TIMEx_PWMN_Start(&LFRFID_READ_TIM, LFRFID_READ_CHANNEL);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_read_stop() {
|
||||
HAL_TIMEx_PWMN_Stop(&LFRFID_READ_TIM, LFRFID_READ_CHANNEL);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_emulate(float freq) {
|
||||
// TODO LL init
|
||||
uint32_t prescaler = (uint32_t)((SystemCoreClock) / freq) - 1;
|
||||
|
||||
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
|
||||
TIM_MasterConfigTypeDef sMasterConfig = {0};
|
||||
TIM_OC_InitTypeDef sConfigOC = {0};
|
||||
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
|
||||
|
||||
// basic PWM setup with needed freq and internal clock
|
||||
LFRFID_EMULATE_TIM.Init.Prescaler = prescaler;
|
||||
LFRFID_EMULATE_TIM.Init.CounterMode = TIM_COUNTERMODE_UP;
|
||||
LFRFID_EMULATE_TIM.Init.Period = 1;
|
||||
LFRFID_EMULATE_TIM.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
|
||||
LFRFID_EMULATE_TIM.Init.RepetitionCounter = 0;
|
||||
LFRFID_EMULATE_TIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
|
||||
if(HAL_TIM_Base_Init(&LFRFID_EMULATE_TIM) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
|
||||
if(HAL_TIM_ConfigClockSource(&LFRFID_EMULATE_TIM, &sClockSourceConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
if(HAL_TIM_PWM_Init(&LFRFID_EMULATE_TIM) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// no master-slave mode
|
||||
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
|
||||
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
|
||||
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
|
||||
if(HAL_TIMEx_MasterConfigSynchronization(&LFRFID_EMULATE_TIM, &sMasterConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// pwm config
|
||||
sConfigOC.OCMode = TIM_OCMODE_PWM1;
|
||||
sConfigOC.Pulse = 1;
|
||||
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
|
||||
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
|
||||
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
|
||||
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
|
||||
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
|
||||
if(HAL_TIM_PWM_ConfigChannel(&LFRFID_EMULATE_TIM, &sConfigOC, LFRFID_EMULATE_CHANNEL) !=
|
||||
HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
|
||||
// no deadtime
|
||||
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
|
||||
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
|
||||
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
|
||||
sBreakDeadTimeConfig.DeadTime = 0;
|
||||
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
|
||||
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
|
||||
sBreakDeadTimeConfig.BreakFilter = 0;
|
||||
sBreakDeadTimeConfig.BreakAFMode = TIM_BREAK_AFMODE_INPUT;
|
||||
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
|
||||
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
|
||||
sBreakDeadTimeConfig.Break2Filter = 0;
|
||||
sBreakDeadTimeConfig.Break2AFMode = TIM_BREAK_AFMODE_INPUT;
|
||||
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
|
||||
if(HAL_TIMEx_ConfigBreakDeadTime(&LFRFID_EMULATE_TIM, &sBreakDeadTimeConfig) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_emulate_start() {
|
||||
// TODO make api for interrupts priority
|
||||
for(size_t i = WWDG_IRQn; i <= DMAMUX1_OVR_IRQn; i++) {
|
||||
HAL_NVIC_SetPriority(i, 15, 0);
|
||||
}
|
||||
|
||||
HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0);
|
||||
HAL_NVIC_EnableIRQ(TIM2_IRQn);
|
||||
|
||||
HAL_TIM_PWM_Start_IT(&LFRFID_EMULATE_TIM, LFRFID_EMULATE_CHANNEL);
|
||||
HAL_TIM_Base_Start_IT(&LFRFID_EMULATE_TIM);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_emulate_stop() {
|
||||
HAL_TIM_Base_Stop(&LFRFID_EMULATE_TIM);
|
||||
HAL_TIM_PWM_Stop(&LFRFID_EMULATE_TIM, LFRFID_EMULATE_CHANNEL);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_tim_reset() {
|
||||
HAL_TIM_Base_DeInit(&LFRFID_READ_TIM);
|
||||
HAL_TIM_Base_DeInit(&LFRFID_EMULATE_TIM);
|
||||
}
|
||||
|
||||
bool furi_hal_rfid_is_tim_emulate(TIM_HandleTypeDef* hw) {
|
||||
return (hw == &LFRFID_EMULATE_TIM);
|
||||
}
|
||||
|
||||
void furi_hal_rfid_set_emulate_period(uint32_t period) {
|
||||
LFRFID_EMULATE_TIM.Instance->ARR = period;
|
||||
}
|
||||
|
||||
void furi_hal_rfid_set_emulate_pulse(uint32_t pulse) {
|
||||
switch(LFRFID_EMULATE_CHANNEL) {
|
||||
case TIM_CHANNEL_1:
|
||||
LFRFID_EMULATE_TIM.Instance->CCR1 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_2:
|
||||
LFRFID_EMULATE_TIM.Instance->CCR2 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_3:
|
||||
LFRFID_EMULATE_TIM.Instance->CCR3 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_4:
|
||||
LFRFID_EMULATE_TIM.Instance->CCR4 = pulse;
|
||||
break;
|
||||
default:
|
||||
furi_check(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_rfid_set_read_period(uint32_t period) {
|
||||
LFRFID_TIM.Instance->ARR = period;
|
||||
}
|
||||
|
||||
void furi_hal_rfid_set_read_pulse(uint32_t pulse) {
|
||||
switch(LFRFID_READ_CHANNEL) {
|
||||
case TIM_CHANNEL_1:
|
||||
LFRFID_TIM.Instance->CCR1 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_2:
|
||||
LFRFID_TIM.Instance->CCR2 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_3:
|
||||
LFRFID_TIM.Instance->CCR3 = pulse;
|
||||
break;
|
||||
case TIM_CHANNEL_4:
|
||||
LFRFID_TIM.Instance->CCR4 = pulse;
|
||||
break;
|
||||
default:
|
||||
furi_check(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_rfid_change_read_config(float freq, float duty_cycle) {
|
||||
uint32_t period = (uint32_t)((SystemCoreClock) / freq) - 1;
|
||||
furi_hal_rfid_set_read_period(period);
|
||||
furi_hal_rfid_set_read_pulse(period * duty_cycle);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "furi-hal-sd.h"
|
||||
#include <stm32wbxx_ll_gpio.h>
|
||||
#include <furi.h>
|
||||
|
||||
void hal_sd_detect_init(void) {
|
||||
// low speed input with pullup
|
||||
LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_INPUT);
|
||||
LL_GPIO_SetPinSpeed(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_SPEED_FREQ_LOW);
|
||||
LL_GPIO_SetPinPull(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_PULL_UP);
|
||||
}
|
||||
|
||||
void hal_sd_detect_set_low(void) {
|
||||
// low speed input with pullup
|
||||
LL_GPIO_SetPinMode(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_MODE_OUTPUT);
|
||||
LL_GPIO_SetPinOutputType(SD_CD_GPIO_Port, SD_CD_Pin, LL_GPIO_OUTPUT_OPENDRAIN);
|
||||
LL_GPIO_ResetOutputPin(SD_CD_GPIO_Port, SD_CD_Pin);
|
||||
}
|
||||
|
||||
bool hal_sd_detect(void) {
|
||||
bool result = !(LL_GPIO_IsInputPinSet(SD_CD_GPIO_Port, SD_CD_Pin));
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
#include <furi-hal-spi-config.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
extern SPI_HandleTypeDef SPI_R;
|
||||
extern SPI_HandleTypeDef SPI_D;
|
||||
|
||||
const SPI_InitTypeDef furi_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 furi_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 furi_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,
|
||||
};
|
||||
|
||||
/**
|
||||
* SD Card in fast mode (after init)
|
||||
*/
|
||||
const SPI_InitTypeDef furi_hal_spi_config_sd_fast = {
|
||||
.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_2,
|
||||
.FirstBit = SPI_FIRSTBIT_MSB,
|
||||
.TIMode = SPI_TIMODE_DISABLE,
|
||||
.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
|
||||
.CRCPolynomial = 7,
|
||||
.CRCLength = SPI_CRC_LENGTH_DATASIZE,
|
||||
.NSSPMode = SPI_NSS_PULSE_ENABLE,
|
||||
};
|
||||
|
||||
/**
|
||||
* SD Card in slow mode (before init)
|
||||
*/
|
||||
const SPI_InitTypeDef furi_hal_spi_config_sd_slow = {
|
||||
.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_32,
|
||||
.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 FuriHalSpiBus 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 FuriHalSpiBus 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 FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax] = {
|
||||
{ .bus=&spi_r, .config=&furi_hal_spi_config_subghz, .chip_select=&gpio_subghz_cs, },
|
||||
{ .bus=&spi_d, .config=&furi_hal_spi_config_display, .chip_select=&gpio_display_cs, },
|
||||
{ .bus=&spi_d, .config=&furi_hal_spi_config_sd_fast, .chip_select=&gpio_sdcard_cs, },
|
||||
{ .bus=&spi_d, .config=&furi_hal_spi_config_sd_slow, .chip_select=&gpio_sdcard_cs, },
|
||||
{ .bus=&spi_r, .config=&furi_hal_spi_config_nfc, .chip_select=&gpio_nfc_cs },
|
||||
};
|
||||
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <cmsis_os2.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const SPI_InitTypeDef furi_hal_spi_config_nfc;
|
||||
extern const SPI_InitTypeDef furi_hal_spi_config_subghz;
|
||||
extern const SPI_InitTypeDef furi_hal_spi_config_display;
|
||||
extern const SPI_InitTypeDef furi_hal_spi_config_sd_fast;
|
||||
extern const SPI_InitTypeDef furi_hal_spi_config_sd_slow;
|
||||
|
||||
/** FURI 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;
|
||||
} FuriHalSpiBus;
|
||||
|
||||
/** FURI HAL SPI Device handler
|
||||
* Structure content may change at some point
|
||||
*/
|
||||
typedef struct {
|
||||
const FuriHalSpiBus* bus;
|
||||
const SPI_InitTypeDef* config;
|
||||
const GpioPin* chip_select;
|
||||
} FuriHalSpiDevice;
|
||||
|
||||
/** FURI HAL SPI Standard Device IDs */
|
||||
typedef enum {
|
||||
FuriHalSpiDeviceIdSubGhz, /** SubGhz: CC1101, non-standard SPI usage */
|
||||
FuriHalSpiDeviceIdDisplay, /** Display: ERC12864, only have MOSI */
|
||||
FuriHalSpiDeviceIdSdCardFast, /** SDCARD: fast mode, after initialization */
|
||||
FuriHalSpiDeviceIdSdCardSlow, /** SDCARD: slow mode, before initialization */
|
||||
FuriHalSpiDeviceIdNfc, /** NFC: ST25R3916, pretty standard, but RFAL makes it complex */
|
||||
|
||||
FuriHalSpiDeviceIdMax, /** Service Value, do not use */
|
||||
} FuriHalSpiDeviceId;
|
||||
|
||||
/** Furi Hal Spi Bus R
|
||||
* CC1101, Nfc
|
||||
*/
|
||||
extern const FuriHalSpiBus spi_r;
|
||||
|
||||
/** Furi Hal Spi Bus D
|
||||
* Display, SdCard
|
||||
*/
|
||||
extern const FuriHalSpiBus spi_d;
|
||||
|
||||
/** Furi Hal Spi devices */
|
||||
extern const FuriHalSpiDevice furi_hal_spi_devices[FuriHalSpiDeviceIdMax];
|
||||
|
||||
typedef struct {
|
||||
const FuriHalSpiBus* bus;
|
||||
const SPI_InitTypeDef config;
|
||||
} SPIDevice;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,168 @@
|
||||
#include "furi-hal-spi.h"
|
||||
#include <furi-hal-resources.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <spi.h>
|
||||
#include <furi.h>
|
||||
|
||||
|
||||
extern void Enable_SPI(SPI_HandleTypeDef* spi);
|
||||
|
||||
void furi_hal_spi_init() {
|
||||
// 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<FuriHalSpiDeviceIdMax; ++i) {
|
||||
hal_gpio_init(
|
||||
furi_hal_spi_devices[i].chip_select,
|
||||
GpioModeOutputPushPull,
|
||||
GpioPullNo,
|
||||
GpioSpeedVeryHigh
|
||||
);
|
||||
}
|
||||
FURI_LOG_I("FuriHalSpi", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus) {
|
||||
furi_assert(bus);
|
||||
if (bus->mutex) {
|
||||
osMutexAcquire(*bus->mutex, osWaitForever);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus) {
|
||||
furi_assert(bus);
|
||||
if (bus->mutex) {
|
||||
osMutexRelease(*bus->mutex);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const SPI_InitTypeDef* config) {
|
||||
furi_assert(bus);
|
||||
|
||||
if(memcmp(&bus->spi->Init, config, sizeof(SPI_InitTypeDef))) {
|
||||
memcpy((SPI_InitTypeDef*)&bus->spi->Init, config, sizeof(SPI_InitTypeDef));
|
||||
if(HAL_SPI_Init((SPI_HandleTypeDef*)bus->spi) != HAL_OK) {
|
||||
Error_Handler();
|
||||
}
|
||||
Enable_SPI((SPI_HandleTypeDef*)bus->spi);
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_spi_bus_reset(const FuriHalSpiBus* bus) {
|
||||
furi_assert(bus);
|
||||
|
||||
HAL_SPI_DeInit((SPI_HandleTypeDef*)bus->spi);
|
||||
HAL_SPI_Init((SPI_HandleTypeDef*)bus->spi);
|
||||
Enable_SPI((SPI_HandleTypeDef*)bus->spi);
|
||||
}
|
||||
|
||||
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* 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 furi_hal_spi_bus_tx(const FuriHalSpiBus* 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 furi_hal_spi_bus_trx(const FuriHalSpiBus* 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 FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id) {
|
||||
furi_assert(device_id < FuriHalSpiDeviceIdMax);
|
||||
|
||||
const FuriHalSpiDevice* device = &furi_hal_spi_devices[device_id];
|
||||
assert(device);
|
||||
furi_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 furi_hal_spi_device_return(const FuriHalSpiDevice* device) {
|
||||
furi_hal_spi_bus_unlock(device->bus);
|
||||
}
|
||||
|
||||
bool furi_hal_spi_device_rx(const FuriHalSpiDevice* 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 = furi_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 furi_hal_spi_device_tx(const FuriHalSpiDevice* 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 = furi_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 furi_hal_spi_device_trx(const FuriHalSpiDevice* 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 = furi_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;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include "furi-hal-spi-config.h"
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Init SPI API
|
||||
*/
|
||||
void furi_hal_spi_init();
|
||||
|
||||
/* Bus Level API */
|
||||
|
||||
/** Lock SPI bus
|
||||
* Takes bus mutex, if used
|
||||
*/
|
||||
void furi_hal_spi_bus_lock(const FuriHalSpiBus* bus);
|
||||
|
||||
/** Unlock SPI bus
|
||||
* Releases BUS mutex, if used
|
||||
*/
|
||||
void furi_hal_spi_bus_unlock(const FuriHalSpiBus* bus);
|
||||
|
||||
/**
|
||||
* Configure SPI bus
|
||||
* @param bus - spi bus handler
|
||||
* @param config - spi configuration structure
|
||||
*/
|
||||
void furi_hal_spi_bus_configure(const FuriHalSpiBus* bus, const SPI_InitTypeDef* config);
|
||||
|
||||
/**
|
||||
* Reset SPI bus
|
||||
* @param bus - spi bus handler
|
||||
*/
|
||||
void furi_hal_spi_bus_reset(const FuriHalSpiBus* bus);
|
||||
|
||||
/** SPI Receive
|
||||
* @param bus - spi bus handler
|
||||
* @param buffer - receive buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_bus_rx(const FuriHalSpiBus* 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 furi_hal_spi_bus_tx(const FuriHalSpiBus* 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 furi_hal_spi_bus_trx(const FuriHalSpiBus* 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 FuriHalSpiDevice* furi_hal_spi_device_get(FuriHalSpiDeviceId device_id);
|
||||
|
||||
/** Return Device handle
|
||||
* And unlock access to the corresponding SPI BUS
|
||||
* @param device - device handle
|
||||
*/
|
||||
void furi_hal_spi_device_return(const FuriHalSpiDevice* device);
|
||||
|
||||
/** SPI Recieve
|
||||
* @param device - device handle
|
||||
* @param buffer - receive buffer
|
||||
* @param size - transaction size
|
||||
* @param timeout - bus operation timeout in ms
|
||||
*/
|
||||
bool furi_hal_spi_device_rx(const FuriHalSpiDevice* 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 furi_hal_spi_device_tx(const FuriHalSpiDevice* 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 furi_hal_spi_device_trx(const FuriHalSpiDevice* device, uint8_t* tx_buffer, uint8_t* rx_buffer, size_t size, uint32_t timeout);
|
||||
|
||||
|
||||
/**
|
||||
* Lock SPI device bus and apply config if needed
|
||||
*/
|
||||
void furi_hal_spi_lock_device(const SPIDevice* device);
|
||||
|
||||
/**
|
||||
* Unlock SPI device bus
|
||||
*/
|
||||
void furi_hal_spi_unlock_device(const SPIDevice* device);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,530 @@
|
||||
#include "furi-hal-subghz.h"
|
||||
|
||||
#include <furi-hal-gpio.h>
|
||||
#include <furi-hal-spi.h>
|
||||
#include <furi-hal-interrupt.h>
|
||||
#include <furi-hal-resources.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <cc1101.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static volatile SubGhzState furi_hal_subghz_state = SubGhzStateInit;
|
||||
|
||||
static const uint8_t furi_hal_subghz_preset_ook_async_regs[][2] = {
|
||||
// https://e2e.ti.com/support/wireless-connectivity/sub-1-ghz-group/sub-1-ghz/f/sub-1-ghz-forum/382066/cc1101---don-t-know-the-correct-registers-configuration
|
||||
|
||||
/* GPIO GD0 */
|
||||
{ CC1101_IOCFG0, 0x0D }, // GD0 as async serial data output/input
|
||||
|
||||
/* FIFO and internals */
|
||||
{ CC1101_FIFOTHR, 0x47 }, // The only important bit is ADC_RETENTION
|
||||
|
||||
/* Packet engine */
|
||||
{ CC1101_PKTCTRL0, 0x32 }, // Async, continious, no whitening
|
||||
|
||||
/* Frequency Synthesizer Control */
|
||||
{ CC1101_FSCTRL1, 0x06 }, // IF = (26*10^6) / (2^10) * 0x06 = 152343.75Hz
|
||||
|
||||
// Modem Configuration
|
||||
{ CC1101_MDMCFG0, 0x00 }, // Channel spacing is 25kHz
|
||||
{ CC1101_MDMCFG1, 0x00 }, // Channel spacing is 25kHz
|
||||
{ CC1101_MDMCFG2, 0x30 }, // Format ASK/OOK, No preamble/sync
|
||||
{ CC1101_MDMCFG3, 0x32 }, // Data rate is 3.79372 kBaud
|
||||
{ CC1101_MDMCFG4, 0x67 }, // Rx BW filter is 270.833333kHz
|
||||
|
||||
/* Main Radio Control State Machine */
|
||||
{ CC1101_MCSM0, 0x18 }, // Autocalibrate on idle-to-rx/tx, PO_TIMEOUT is 64 cycles(149-155us)
|
||||
|
||||
/* Frequency Offset Compensation Configuration */
|
||||
{ CC1101_FOCCFG, 0x18 }, // no frequency offset compensation, POST_K same as PRE_K, PRE_K is 4K, GATE is off
|
||||
|
||||
/* Automatic Gain Control */
|
||||
{ CC1101_AGCTRL1, 0x00 }, // LNA 2 gain is decreased to minimum before decreasing LNA gain
|
||||
{ CC1101_AGCTRL2, 0x07 }, // MAGN_TARGET is 42 dB
|
||||
|
||||
/* Wake on radio and timeouts control */
|
||||
{ CC1101_WORCTRL, 0xFB }, // WOR_RES is 2^15 periods (0.91 - 0.94 s) 16.5 - 17.2 hours
|
||||
|
||||
/* Frontend configuration */
|
||||
{ CC1101_FREND0, 0x11 }, // Adjusts current TX LO buffer + high is PATABLE[1]
|
||||
{ CC1101_FREND1, 0xB6 }, //
|
||||
|
||||
/* Frequency Synthesizer Calibration, valid for 433.92 */
|
||||
{ CC1101_FSCAL3, 0xE9 },
|
||||
{ CC1101_FSCAL2, 0x2A },
|
||||
{ CC1101_FSCAL1, 0x00 },
|
||||
{ CC1101_FSCAL0, 0x1F },
|
||||
|
||||
/* Magic f4ckery */
|
||||
{ CC1101_TEST2, 0x81 }, // FIFOTHR ADC_RETENTION=1 matched value
|
||||
{ CC1101_TEST1, 0x35 }, // FIFOTHR ADC_RETENTION=1 matched value
|
||||
{ CC1101_TEST0, 0x09 }, // VCO selection calibration stage is disabled
|
||||
|
||||
/* End */
|
||||
{ 0, 0 },
|
||||
};
|
||||
|
||||
static const uint8_t furi_hal_subghz_preset_ook_async_patable[8] = {
|
||||
0x00,
|
||||
0xC0, // 10dBm 0xC0, 7dBm 0xC8, 5dBm 0x84, 0dBm 0x60, -10dBm 0x34, -15dBm 0x1D, -20dBm 0x0E, -30dBm 0x12
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00,
|
||||
0x00
|
||||
};
|
||||
|
||||
void furi_hal_subghz_init() {
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateInit);
|
||||
furi_hal_subghz_state = SubGhzStateIdle;
|
||||
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
|
||||
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
|
||||
hal_gpio_init(&FURI_HAL_SUBGHZ_TX_GPIO, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
#endif
|
||||
|
||||
// Reset
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
cc1101_reset(device);
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
||||
|
||||
// Prepare GD0 for power on self test
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeInput, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
// GD0 low
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW);
|
||||
while(hal_gpio_read(&gpio_cc1101_g0) != false);
|
||||
|
||||
// GD0 high
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHW | CC1101_IOCFG_INV);
|
||||
while(hal_gpio_read(&gpio_cc1101_g0) != true);
|
||||
|
||||
// Reset GD0 to floating state
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
// RF switches
|
||||
hal_gpio_init(&gpio_rf_sw_0, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW);
|
||||
|
||||
// Go to sleep
|
||||
cc1101_shutdown(device);
|
||||
|
||||
furi_hal_spi_device_return(device);
|
||||
FURI_LOG_I("FuriHalSubGhz", "Init OK");
|
||||
}
|
||||
|
||||
void furi_hal_subghz_sleep() {
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateIdle);
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
|
||||
cc1101_switch_to_idle(device);
|
||||
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
cc1101_shutdown(device);
|
||||
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_dump_state() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
printf(
|
||||
"[furi_hal_subghz] cc1101 chip %d, version %d\r\n",
|
||||
cc1101_get_partnumber(device),
|
||||
cc1101_get_version(device)
|
||||
);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_load_preset(FuriHalSubGhzPreset preset) {
|
||||
if(preset == FuriHalSubGhzPresetOokAsync) {
|
||||
furi_hal_subghz_load_registers(furi_hal_subghz_preset_ook_async_regs);
|
||||
furi_hal_subghz_load_patable(furi_hal_subghz_preset_ook_async_patable);
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t furi_hal_subghz_get_status() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
CC1101StatusRaw st;
|
||||
st.status = cc1101_get_status(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
return st.status_raw;
|
||||
}
|
||||
|
||||
void furi_hal_subghz_load_registers(const uint8_t data[][2]) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_reset(device);
|
||||
uint32_t i = 0;
|
||||
while (data[i][0]) {
|
||||
cc1101_write_reg(device, data[i][0], data[i][1]);
|
||||
i++;
|
||||
}
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_load_patable(const uint8_t data[8]) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_set_pa_table(device, data);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_write_packet(const uint8_t* data, uint8_t size) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_flush_tx(device);
|
||||
cc1101_write_fifo(device, data, size);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_flush_rx() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_flush_rx(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_read_packet(uint8_t* data, uint8_t* size) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_read_fifo(device, data, size);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_shutdown() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
// Reset and shutdown
|
||||
cc1101_shutdown(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_reset() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
cc1101_switch_to_idle(device);
|
||||
cc1101_reset(device);
|
||||
cc1101_write_reg(device, CC1101_IOCFG0, CC1101IocfgHighImpedance);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_idle() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_switch_to_idle(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_rx() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_switch_to_rx(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_tx() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
cc1101_switch_to_tx(device);
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
float furi_hal_subghz_get_rssi() {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
int32_t rssi_dec = cc1101_get_rssi(device);
|
||||
furi_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;
|
||||
}
|
||||
|
||||
bool furi_hal_subghz_is_frequency_valid(uint32_t value) {
|
||||
if(!(value >= 299999755 && value <= 348000335) &&
|
||||
!(value >= 386999938 && value <= 464000000) &&
|
||||
!(value >= 778999847 && value <= 928000000)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t furi_hal_subghz_set_frequency_and_path(uint32_t value) {
|
||||
value = furi_hal_subghz_set_frequency(value);
|
||||
if(value >= 299999755 && value <= 348000335) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath315);
|
||||
} else if(value >= 386999938 && value <= 464000000) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath433);
|
||||
} else if(value >= 778999847 && value <= 928000000) {
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPath868);
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
uint32_t furi_hal_subghz_set_frequency(uint32_t value) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
|
||||
uint32_t real_frequency = cc1101_set_frequency(device, value);
|
||||
cc1101_calibrate(device);
|
||||
|
||||
furi_hal_spi_device_return(device);
|
||||
|
||||
return real_frequency;
|
||||
}
|
||||
|
||||
void furi_hal_subghz_set_path(FuriHalSubGhzPath path) {
|
||||
const FuriHalSpiDevice* device = furi_hal_spi_device_get(FuriHalSpiDeviceIdSubGhz);
|
||||
if (path == FuriHalSubGhzPath433) {
|
||||
hal_gpio_write(&gpio_rf_sw_0, 0);
|
||||
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);
|
||||
} else if (path == FuriHalSubGhzPath315) {
|
||||
hal_gpio_write(&gpio_rf_sw_0, 1);
|
||||
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW);
|
||||
} else if (path == FuriHalSubGhzPath868) {
|
||||
hal_gpio_write(&gpio_rf_sw_0, 1);
|
||||
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW | CC1101_IOCFG_INV);
|
||||
} else if (path == FuriHalSubGhzPathIsolate) {
|
||||
hal_gpio_write(&gpio_rf_sw_0, 0);
|
||||
cc1101_write_reg(device, CC1101_IOCFG2, CC1101IocfgHW);
|
||||
} else {
|
||||
furi_check(0);
|
||||
}
|
||||
furi_hal_spi_device_return(device);
|
||||
}
|
||||
|
||||
volatile uint32_t furi_hal_subghz_capture_delta_duration = 0;
|
||||
volatile FuriHalSubGhzCaptureCallback furi_hal_subghz_capture_callback = NULL;
|
||||
volatile void* furi_hal_subghz_capture_callback_context = NULL;
|
||||
|
||||
static void furi_hal_subghz_capture_ISR() {
|
||||
// Channel 1
|
||||
if(LL_TIM_IsActiveFlag_CC1(TIM2)) {
|
||||
LL_TIM_ClearFlag_CC1(TIM2);
|
||||
furi_hal_subghz_capture_delta_duration = LL_TIM_IC_GetCaptureCH1(TIM2);
|
||||
if (furi_hal_subghz_capture_callback) {
|
||||
furi_hal_subghz_capture_callback(true, furi_hal_subghz_capture_delta_duration,
|
||||
(void*)furi_hal_subghz_capture_callback_context
|
||||
);
|
||||
}
|
||||
}
|
||||
// Channel 2
|
||||
if(LL_TIM_IsActiveFlag_CC2(TIM2)) {
|
||||
LL_TIM_ClearFlag_CC2(TIM2);
|
||||
if (furi_hal_subghz_capture_callback) {
|
||||
furi_hal_subghz_capture_callback(false, LL_TIM_IC_GetCaptureCH2(TIM2) - furi_hal_subghz_capture_delta_duration,
|
||||
(void*)furi_hal_subghz_capture_callback_context
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_subghz_set_async_rx_callback(FuriHalSubGhzCaptureCallback callback, void* context) {
|
||||
furi_hal_subghz_capture_callback = callback;
|
||||
furi_hal_subghz_capture_callback_context = context;
|
||||
}
|
||||
|
||||
void furi_hal_subghz_start_async_rx() {
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateIdle);
|
||||
furi_hal_subghz_state = SubGhzStateAsyncRx;
|
||||
|
||||
hal_gpio_init_ex(&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullNo, GpioSpeedLow, GpioAltFn1TIM2);
|
||||
|
||||
// Timer: base
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||
TIM_InitStruct.Prescaler = 64-1;
|
||||
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||
TIM_InitStruct.Autoreload = 0x7FFFFFFE;
|
||||
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
||||
LL_TIM_Init(TIM2, &TIM_InitStruct);
|
||||
|
||||
// Timer: advanced
|
||||
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
LL_TIM_DisableARRPreload(TIM2);
|
||||
LL_TIM_SetTriggerInput(TIM2, LL_TIM_TS_TI2FP2);
|
||||
LL_TIM_SetSlaveMode(TIM2, LL_TIM_SLAVEMODE_RESET);
|
||||
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
|
||||
LL_TIM_EnableMasterSlaveMode(TIM2);
|
||||
LL_TIM_DisableDMAReq_TRIG(TIM2);
|
||||
LL_TIM_DisableIT_TRIG(TIM2);
|
||||
|
||||
// Timer: channel 1 indirect
|
||||
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ACTIVEINPUT_INDIRECTTI);
|
||||
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_ICPSC_DIV1);
|
||||
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_POLARITY_FALLING);
|
||||
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH1, LL_TIM_IC_FILTER_FDIV1);
|
||||
|
||||
// Timer: channel 2 direct
|
||||
LL_TIM_IC_SetActiveInput(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ACTIVEINPUT_DIRECTTI);
|
||||
LL_TIM_IC_SetPrescaler(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_ICPSC_DIV1);
|
||||
LL_TIM_IC_SetPolarity(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_POLARITY_RISING);
|
||||
LL_TIM_IC_SetFilter(TIM2, LL_TIM_CHANNEL_CH2, LL_TIM_IC_FILTER_FDIV1);
|
||||
|
||||
// ISR setup
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_capture_ISR);
|
||||
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
|
||||
NVIC_EnableIRQ(TIM2_IRQn);
|
||||
|
||||
// Interrupts and channels
|
||||
LL_TIM_EnableIT_CC1(TIM2);
|
||||
LL_TIM_EnableIT_CC2(TIM2);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH1);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
|
||||
|
||||
// Enable NVIC
|
||||
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
|
||||
NVIC_EnableIRQ(TIM2_IRQn);
|
||||
|
||||
// Start timer
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
|
||||
// Switch to RX
|
||||
furi_hal_subghz_rx();
|
||||
}
|
||||
|
||||
void furi_hal_subghz_stop_async_rx() {
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateAsyncRx);
|
||||
furi_hal_subghz_state = SubGhzStateIdle;
|
||||
|
||||
// Shutdown radio
|
||||
furi_hal_subghz_idle();
|
||||
|
||||
LL_TIM_DeInit(TIM2);
|
||||
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, NULL);
|
||||
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
}
|
||||
|
||||
volatile size_t furi_hal_subghz_tx_repeat = 0;
|
||||
|
||||
static void furi_hal_subghz_tx_dma_isr() {
|
||||
if (LL_DMA_IsActiveFlag_TC1(DMA1)) {
|
||||
LL_DMA_ClearFlag_TC1(DMA1);
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateAsyncTx);
|
||||
if (--furi_hal_subghz_tx_repeat == 0) {
|
||||
furi_hal_subghz_state = SubGhzStateAsyncTxLast;
|
||||
LL_DMA_DisableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_subghz_tx_timer_isr() {
|
||||
if(LL_TIM_IsActiveFlag_UPDATE(TIM2)) {
|
||||
LL_TIM_ClearFlag_UPDATE(TIM2);
|
||||
if (furi_hal_subghz_state == SubGhzStateAsyncTxLast) {
|
||||
LL_TIM_DisableCounter(TIM2);
|
||||
furi_hal_subghz_state = SubGhzStateAsyncTxEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void furi_hal_subghz_start_async_tx(uint32_t* buffer, size_t buffer_size, size_t repeat) {
|
||||
furi_assert(furi_hal_subghz_state == SubGhzStateIdle);
|
||||
furi_hal_subghz_state = SubGhzStateAsyncTx;
|
||||
furi_hal_subghz_tx_repeat = repeat;
|
||||
|
||||
// Connect CC1101_GD0 to TIM2 as output
|
||||
hal_gpio_init_ex(&gpio_cc1101_g0, GpioModeAltFunctionPushPull, GpioPullDown, GpioSpeedLow, GpioAltFn1TIM2);
|
||||
|
||||
// Configure DMA
|
||||
LL_DMA_InitTypeDef dma_config = {0};
|
||||
dma_config.PeriphOrM2MSrcAddress = (uint32_t)&(TIM2->ARR);
|
||||
dma_config.MemoryOrM2MDstAddress = (uint32_t)buffer;
|
||||
dma_config.Direction = LL_DMA_DIRECTION_MEMORY_TO_PERIPH;
|
||||
dma_config.Mode = LL_DMA_MODE_CIRCULAR;
|
||||
dma_config.PeriphOrM2MSrcIncMode = LL_DMA_PERIPH_NOINCREMENT;
|
||||
dma_config.MemoryOrM2MDstIncMode = LL_DMA_MEMORY_INCREMENT;
|
||||
dma_config.PeriphOrM2MSrcDataSize = LL_DMA_PDATAALIGN_WORD;
|
||||
dma_config.MemoryOrM2MDstDataSize = LL_DMA_MDATAALIGN_WORD;
|
||||
dma_config.NbData = buffer_size / sizeof(uint32_t);
|
||||
dma_config.PeriphRequest = LL_DMAMUX_REQ_TIM2_UP;
|
||||
dma_config.Priority = LL_DMA_MODE_NORMAL;
|
||||
LL_DMA_Init(DMA1, LL_DMA_CHANNEL_1, &dma_config);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, furi_hal_subghz_tx_dma_isr);
|
||||
LL_DMA_EnableIT_TC(DMA1, LL_DMA_CHANNEL_1);
|
||||
LL_DMA_EnableChannel(DMA1, LL_DMA_CHANNEL_1);
|
||||
|
||||
// Configure TIM2
|
||||
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
LL_TIM_InitTypeDef TIM_InitStruct = {0};
|
||||
TIM_InitStruct.Prescaler = 64-1;
|
||||
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
|
||||
TIM_InitStruct.Autoreload = 1000;
|
||||
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
|
||||
LL_TIM_Init(TIM2, &TIM_InitStruct);
|
||||
LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
|
||||
LL_TIM_EnableARRPreload(TIM2);
|
||||
|
||||
// Configure TIM2 CH2
|
||||
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
|
||||
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_TOGGLE;
|
||||
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
|
||||
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
|
||||
TIM_OC_InitStruct.CompareValue = 0;
|
||||
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
|
||||
LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH2, &TIM_OC_InitStruct);
|
||||
LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH2);
|
||||
LL_TIM_DisableMasterSlaveMode(TIM2);
|
||||
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, furi_hal_subghz_tx_timer_isr);
|
||||
LL_TIM_EnableIT_UPDATE(TIM2);
|
||||
LL_TIM_EnableDMAReq_UPDATE(TIM2);
|
||||
LL_TIM_CC_EnableChannel(TIM2, LL_TIM_CHANNEL_CH2);
|
||||
|
||||
// Start counter
|
||||
LL_TIM_GenerateEvent_UPDATE(TIM2);
|
||||
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
|
||||
hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, true);
|
||||
#endif
|
||||
furi_hal_subghz_tx();
|
||||
|
||||
// Enable NVIC
|
||||
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),5, 0));
|
||||
NVIC_EnableIRQ(TIM2_IRQn);
|
||||
|
||||
LL_TIM_SetCounter(TIM2, 0);
|
||||
LL_TIM_EnableCounter(TIM2);
|
||||
}
|
||||
|
||||
size_t furi_hal_subghz_get_async_tx_repeat_left() {
|
||||
return furi_hal_subghz_tx_repeat;
|
||||
}
|
||||
|
||||
void furi_hal_subghz_wait_async_tx() {
|
||||
while(furi_hal_subghz_state != SubGhzStateAsyncTxEnd) osDelay(1);
|
||||
}
|
||||
|
||||
void furi_hal_subghz_stop_async_tx() {
|
||||
furi_assert(
|
||||
furi_hal_subghz_state == SubGhzStateAsyncTx
|
||||
|| furi_hal_subghz_state == SubGhzStateAsyncTxLast
|
||||
|| furi_hal_subghz_state == SubGhzStateAsyncTxEnd
|
||||
);
|
||||
|
||||
// Shutdown radio
|
||||
furi_hal_subghz_idle();
|
||||
#ifdef FURI_HAL_SUBGHZ_TX_GPIO
|
||||
hal_gpio_write(&FURI_HAL_SUBGHZ_TX_GPIO, false);
|
||||
#endif
|
||||
|
||||
// Deinitialize Timer
|
||||
LL_TIM_DeInit(TIM2);
|
||||
LL_APB1_GRP1_DisableClock(LL_APB1_GRP1_PERIPH_TIM2);
|
||||
furi_hal_interrupt_set_timer_isr(TIM2, NULL);
|
||||
|
||||
// Deinitialize DMA
|
||||
LL_DMA_DeInit(DMA1, LL_DMA_CHANNEL_1);
|
||||
furi_hal_interrupt_set_dma_channel_isr(DMA1, LL_DMA_CHANNEL_1, NULL);
|
||||
|
||||
// Deinitialize GPIO
|
||||
hal_gpio_init(&gpio_cc1101_g0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
|
||||
|
||||
furi_hal_subghz_state = SubGhzStateIdle;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
#include "cmsis_os.h"
|
||||
#include "furi-hal-task.h"
|
||||
|
||||
//-----------------------------cmsis_os2.c-------------------------------
|
||||
// helpers to get isr context
|
||||
// get arch
|
||||
#ifndef __ARM_ARCH_6M__
|
||||
#define __ARM_ARCH_6M__ 0
|
||||
#endif
|
||||
#ifndef __ARM_ARCH_7M__
|
||||
#define __ARM_ARCH_7M__ 0
|
||||
#endif
|
||||
#ifndef __ARM_ARCH_7EM__
|
||||
#define __ARM_ARCH_7EM__ 0
|
||||
#endif
|
||||
#ifndef __ARM_ARCH_8M_MAIN__
|
||||
#define __ARM_ARCH_8M_MAIN__ 0
|
||||
#endif
|
||||
#ifndef __ARM_ARCH_7A__
|
||||
#define __ARM_ARCH_7A__ 0
|
||||
#endif
|
||||
|
||||
// get masks
|
||||
#if((__ARM_ARCH_7M__ == 1U) || (__ARM_ARCH_7EM__ == 1U) || (__ARM_ARCH_8M_MAIN__ == 1U))
|
||||
#define IS_IRQ_MASKED() ((__get_PRIMASK() != 0U) || (__get_BASEPRI() != 0U))
|
||||
#elif(__ARM_ARCH_6M__ == 1U)
|
||||
#define IS_IRQ_MASKED() (__get_PRIMASK() != 0U)
|
||||
#elif(__ARM_ARCH_7A__ == 1U)
|
||||
/* CPSR mask bits */
|
||||
#define CPSR_MASKBIT_I 0x80U
|
||||
|
||||
#define IS_IRQ_MASKED() ((__get_CPSR() & CPSR_MASKBIT_I) != 0U)
|
||||
#else
|
||||
#define IS_IRQ_MASKED() (__get_PRIMASK() != 0U)
|
||||
#endif
|
||||
|
||||
// get is irq mode
|
||||
#if(__ARM_ARCH_7A__ == 1U)
|
||||
/* CPSR mode bitmasks */
|
||||
#define CPSR_MODE_USER 0x10U
|
||||
#define CPSR_MODE_SYSTEM 0x1FU
|
||||
|
||||
#define IS_IRQ_MODE() ((__get_mode() != CPSR_MODE_USER) && (__get_mode() != CPSR_MODE_SYSTEM))
|
||||
#else
|
||||
#define IS_IRQ_MODE() (__get_IPSR() != 0U)
|
||||
#endif
|
||||
|
||||
// added osKernelGetState(), because KernelState is a static var
|
||||
#define IS_IRQ() (IS_IRQ_MODE() || (IS_IRQ_MASKED() && (osKernelGetState() == osKernelRunning)))
|
||||
//-------------------------end of cmsis_os2.c----------------------------
|
||||
|
||||
bool task_is_isr_context(void) {
|
||||
return IS_IRQ();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
#include "main.h"
|
||||
#include <cmsis_os2.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// Task stack size in bytes
|
||||
#define DEFAULT_STACK_SIZE 4096
|
||||
|
||||
// Max system tasks count
|
||||
#define MAX_TASK_COUNT 14
|
||||
|
||||
bool task_is_isr_context(void);
|
||||
@@ -0,0 +1,106 @@
|
||||
#include <furi-hal-vcp.h>
|
||||
#include <usbd_cdc_if.h>
|
||||
#include <furi.h>
|
||||
#include <stream_buffer.h>
|
||||
|
||||
#define FURI_HAL_VCP_RX_BUFFER_SIZE 600
|
||||
|
||||
typedef struct {
|
||||
StreamBufferHandle_t rx_stream;
|
||||
osSemaphoreId_t tx_semaphore;
|
||||
volatile bool alive;
|
||||
volatile bool underrun;
|
||||
} FuriHalVcp;
|
||||
|
||||
static FuriHalVcp* furi_hal_vcp = NULL;
|
||||
|
||||
static const uint8_t ascii_soh = 0x01;
|
||||
static const uint8_t ascii_eot = 0x04;
|
||||
|
||||
void _furi_hal_vcp_init();
|
||||
void _furi_hal_vcp_deinit();
|
||||
void _furi_hal_vcp_control_line(uint8_t state);
|
||||
void _furi_hal_vcp_rx_callback(const uint8_t* buffer, size_t size);
|
||||
void _furi_hal_vcp_tx_complete(size_t size);
|
||||
|
||||
void furi_hal_vcp_init() {
|
||||
furi_hal_vcp = furi_alloc(sizeof(FuriHalVcp));
|
||||
furi_hal_vcp->rx_stream = xStreamBufferCreate(FURI_HAL_VCP_RX_BUFFER_SIZE, 1);
|
||||
furi_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL);
|
||||
furi_hal_vcp->alive = false;
|
||||
furi_hal_vcp->underrun = false;
|
||||
FURI_LOG_I("FuriHalVcp", "Init OK");
|
||||
}
|
||||
|
||||
void _furi_hal_vcp_init() {
|
||||
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
|
||||
}
|
||||
|
||||
void _furi_hal_vcp_deinit() {
|
||||
furi_hal_vcp->alive = false;
|
||||
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
|
||||
}
|
||||
|
||||
void _furi_hal_vcp_control_line(uint8_t state) {
|
||||
// bit 0: DTR state, bit 1: RTS state
|
||||
// bool dtr = state & 0b01;
|
||||
bool dtr = state & 0b1;
|
||||
|
||||
if (dtr) {
|
||||
if (!furi_hal_vcp->alive) {
|
||||
furi_hal_vcp->alive = true;
|
||||
_furi_hal_vcp_rx_callback(&ascii_soh, 1); // SOH
|
||||
}
|
||||
} else {
|
||||
if (furi_hal_vcp->alive) {
|
||||
_furi_hal_vcp_rx_callback(&ascii_eot, 1); // EOT
|
||||
furi_hal_vcp->alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
|
||||
}
|
||||
|
||||
void _furi_hal_vcp_rx_callback(const uint8_t* buffer, size_t size) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
size_t ret = xStreamBufferSendFromISR(furi_hal_vcp->rx_stream, buffer, size, &xHigherPriorityTaskWoken);
|
||||
if (ret != size) {
|
||||
furi_hal_vcp->underrun = true;
|
||||
}
|
||||
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
|
||||
}
|
||||
|
||||
void _furi_hal_vcp_tx_complete(size_t size) {
|
||||
osSemaphoreRelease(furi_hal_vcp->tx_semaphore);
|
||||
}
|
||||
|
||||
size_t furi_hal_vcp_rx(uint8_t* buffer, size_t size) {
|
||||
furi_assert(furi_hal_vcp);
|
||||
return xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, portMAX_DELAY);
|
||||
}
|
||||
|
||||
size_t furi_hal_vcp_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) {
|
||||
furi_assert(furi_hal_vcp);
|
||||
return xStreamBufferReceive(furi_hal_vcp->rx_stream, buffer, size, timeout);
|
||||
}
|
||||
|
||||
void furi_hal_vcp_tx(const uint8_t* buffer, size_t size) {
|
||||
furi_assert(furi_hal_vcp);
|
||||
|
||||
while (size > 0 && furi_hal_vcp->alive) {
|
||||
furi_check(osSemaphoreAcquire(furi_hal_vcp->tx_semaphore, osWaitForever) == osOK);
|
||||
|
||||
size_t batch_size = size;
|
||||
if (batch_size > APP_TX_DATA_SIZE) {
|
||||
batch_size = APP_TX_DATA_SIZE;
|
||||
}
|
||||
|
||||
if (CDC_Transmit_FS((uint8_t*)buffer, batch_size) == USBD_OK) {
|
||||
size -= batch_size;
|
||||
buffer += batch_size;
|
||||
} else {
|
||||
// Shouldn't be there
|
||||
osDelay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
#include <furi-hal-version.h>
|
||||
|
||||
#include <furi.h>
|
||||
#include <stm32wbxx.h>
|
||||
#include <stm32wbxx_ll_rtc.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include "ble.h"
|
||||
|
||||
#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
|
||||
#define FURI_HAL_VERSION_NAME_LENGTH 8
|
||||
#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
|
||||
/** BLE symbol + "Flipper " + name */
|
||||
#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
|
||||
#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
|
||||
|
||||
/** OTP Versions enum */
|
||||
typedef enum {
|
||||
FuriHalVersionOtpVersion0=0x00,
|
||||
FuriHalVersionOtpVersion1=0x01,
|
||||
FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
|
||||
FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
|
||||
} FuriHalVersionOtpVersion;
|
||||
|
||||
/** OTP V0 Structure: prototypes and early EVT */
|
||||
typedef struct {
|
||||
uint8_t board_version;
|
||||
uint8_t board_target;
|
||||
uint8_t board_body;
|
||||
uint8_t board_connect;
|
||||
uint32_t header_timestamp;
|
||||
char name[FURI_HAL_VERSION_NAME_LENGTH];
|
||||
} FuriHalVersionOTPv0;
|
||||
|
||||
/** OTP V1 Structure: late EVT, DVT, PVT, Production */
|
||||
typedef struct {
|
||||
/* First 64 bits: header */
|
||||
uint16_t header_magic;
|
||||
uint8_t header_version;
|
||||
uint8_t header_reserved;
|
||||
uint32_t header_timestamp;
|
||||
|
||||
/* Second 64 bits: board info */
|
||||
uint8_t board_version; /** Board version */
|
||||
uint8_t board_target; /** Board target firmware */
|
||||
uint8_t board_body; /** Board body */
|
||||
uint8_t board_connect; /** Board interconnect */
|
||||
uint8_t board_color; /** Board color */
|
||||
uint8_t board_region; /** Board region */
|
||||
uint16_t board_reserved; /** Reserved for future use, 0x0000 */
|
||||
|
||||
/* Third 64 bits: Unique Device Name */
|
||||
char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
|
||||
} FuriHalVersionOTPv1;
|
||||
|
||||
/** Represenation Model: */
|
||||
typedef struct {
|
||||
FuriHalVersionOtpVersion otp_version;
|
||||
|
||||
uint32_t timestamp;
|
||||
|
||||
uint8_t board_version; /** Board version */
|
||||
uint8_t board_target; /** Board target firmware */
|
||||
uint8_t board_body; /** Board body */
|
||||
uint8_t board_connect; /** Board interconnect */
|
||||
uint8_t board_color; /** Board color */
|
||||
uint8_t board_region; /** Board region */
|
||||
|
||||
char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
|
||||
char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
|
||||
uint8_t ble_mac[6];
|
||||
} FuriHalVersion;
|
||||
|
||||
static FuriHalVersion furi_hal_version = {0};
|
||||
|
||||
static FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
|
||||
if (*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
|
||||
return FuriHalVersionOtpVersionEmpty;
|
||||
} else {
|
||||
if (((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic == FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
|
||||
return FuriHalVersionOtpVersion1;
|
||||
} else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
|
||||
return FuriHalVersionOtpVersion0;
|
||||
} else {
|
||||
return FuriHalVersionOtpVersionUnknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void furi_hal_version_set_name(const char* name) {
|
||||
if(name != NULL) {
|
||||
strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
|
||||
snprintf(
|
||||
furi_hal_version.device_name,
|
||||
FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
|
||||
"xFlipper %s",
|
||||
furi_hal_version.name);
|
||||
} else {
|
||||
snprintf(
|
||||
furi_hal_version.device_name,
|
||||
FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
|
||||
"xFlipper");
|
||||
}
|
||||
|
||||
furi_hal_version.device_name[0] = AD_TYPE_COMPLETE_LOCAL_NAME;
|
||||
|
||||
// BLE Mac address
|
||||
uint32_t udn = LL_FLASH_GetUDN();
|
||||
uint32_t company_id = LL_FLASH_GetSTCompanyID();
|
||||
uint32_t device_id = LL_FLASH_GetDeviceID();
|
||||
furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
|
||||
furi_hal_version.ble_mac[1] = (uint8_t)( (udn & 0x0000FF00) >> 8 );
|
||||
furi_hal_version.ble_mac[2] = (uint8_t)( (udn & 0x00FF0000) >> 16 );
|
||||
furi_hal_version.ble_mac[3] = (uint8_t)device_id;
|
||||
furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
|
||||
furi_hal_version.ble_mac[5] = (uint8_t)( (company_id & 0x0000FF00) >> 8 );
|
||||
}
|
||||
|
||||
static void furi_hal_version_load_otp_default() {
|
||||
furi_hal_version_set_name(NULL);
|
||||
}
|
||||
|
||||
static void furi_hal_version_load_otp_v0() {
|
||||
const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
|
||||
|
||||
furi_hal_version.timestamp = otp->header_timestamp;
|
||||
furi_hal_version.board_version = otp->board_version;
|
||||
furi_hal_version.board_target = otp->board_target;
|
||||
furi_hal_version.board_body = otp->board_body;
|
||||
furi_hal_version.board_connect = otp->board_connect;
|
||||
furi_hal_version.board_color = 0;
|
||||
furi_hal_version.board_region = 0;
|
||||
|
||||
furi_hal_version_set_name(otp->name);
|
||||
}
|
||||
|
||||
static void furi_hal_version_load_otp_v1() {
|
||||
const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
|
||||
|
||||
furi_hal_version.timestamp = otp->header_timestamp;
|
||||
furi_hal_version.board_version = otp->board_version;
|
||||
furi_hal_version.board_target = otp->board_target;
|
||||
furi_hal_version.board_body = otp->board_body;
|
||||
furi_hal_version.board_connect = otp->board_connect;
|
||||
furi_hal_version.board_color = otp->board_color;
|
||||
furi_hal_version.board_region = otp->board_region;
|
||||
|
||||
furi_hal_version_set_name(otp->name);
|
||||
}
|
||||
|
||||
void furi_hal_version_init() {
|
||||
furi_hal_version.otp_version = furi_hal_version_get_otp_version();
|
||||
switch(furi_hal_version.otp_version) {
|
||||
case FuriHalVersionOtpVersionUnknown:
|
||||
furi_hal_version_load_otp_default();
|
||||
break;
|
||||
case FuriHalVersionOtpVersionEmpty:
|
||||
furi_hal_version_load_otp_default();
|
||||
break;
|
||||
case FuriHalVersionOtpVersion0:
|
||||
furi_hal_version_load_otp_v0();
|
||||
break;
|
||||
case FuriHalVersionOtpVersion1:
|
||||
furi_hal_version_load_otp_v1();
|
||||
break;
|
||||
default: furi_check(0);
|
||||
}
|
||||
FURI_LOG_I("FuriHalVersion", "Init OK");
|
||||
}
|
||||
|
||||
bool furi_hal_version_do_i_belong_here() {
|
||||
return furi_hal_version_get_hw_target() == 6;
|
||||
}
|
||||
|
||||
const char* furi_hal_version_get_model_name() {
|
||||
return "Flipper Zero";
|
||||
}
|
||||
|
||||
const uint8_t furi_hal_version_get_hw_version() {
|
||||
return furi_hal_version.board_version;
|
||||
}
|
||||
|
||||
const uint8_t furi_hal_version_get_hw_target() {
|
||||
return furi_hal_version.board_target;
|
||||
}
|
||||
|
||||
const uint8_t furi_hal_version_get_hw_body() {
|
||||
return furi_hal_version.board_body;
|
||||
}
|
||||
|
||||
const FuriHalVersionColor furi_hal_version_get_hw_color() {
|
||||
return furi_hal_version.board_color;
|
||||
}
|
||||
|
||||
const uint8_t furi_hal_version_get_hw_connect() {
|
||||
return furi_hal_version.board_connect;
|
||||
}
|
||||
|
||||
const FuriHalVersionRegion furi_hal_version_get_hw_region() {
|
||||
return furi_hal_version.board_region;
|
||||
}
|
||||
|
||||
const uint32_t furi_hal_version_get_hw_timestamp() {
|
||||
return furi_hal_version.timestamp;
|
||||
}
|
||||
|
||||
const char* furi_hal_version_get_name_ptr() {
|
||||
return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
|
||||
}
|
||||
|
||||
const char* furi_hal_version_get_device_name_ptr() {
|
||||
return furi_hal_version.device_name + 1;
|
||||
}
|
||||
|
||||
const char* furi_hal_version_get_ble_local_device_name_ptr() {
|
||||
return furi_hal_version.device_name;
|
||||
}
|
||||
|
||||
const uint8_t* furi_hal_version_get_ble_mac() {
|
||||
return furi_hal_version.ble_mac;
|
||||
}
|
||||
|
||||
const struct Version* furi_hal_version_get_firmware_version(void) {
|
||||
return version_get();
|
||||
}
|
||||
|
||||
const struct Version* furi_hal_version_get_boot_version(void) {
|
||||
#ifdef NO_BOOTLOADER
|
||||
return 0;
|
||||
#else
|
||||
/* Backup register which points to structure in flash memory */
|
||||
return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t furi_hal_version_uid_size() {
|
||||
return 64/8;
|
||||
}
|
||||
|
||||
const uint8_t* furi_hal_version_uid() {
|
||||
return (const uint8_t *)UID64_BASE;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
#include <furi-hal-vibro.h>
|
||||
#include <furi-hal-gpio.h>
|
||||
|
||||
void furi_hal_vibro_init() {
|
||||
hal_gpio_init(&vibro_gpio, GpioModeOutputPushPull, GpioPullNo, GpioSpeedLow);
|
||||
hal_gpio_write(&vibro_gpio, false);
|
||||
FURI_LOG_I("FuriHalVibro", "Init OK");
|
||||
|
||||
}
|
||||
|
||||
void furi_hal_vibro_on(bool value) {
|
||||
hal_gpio_write(&vibro_gpio, value);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#include <furi-hal.h>
|
||||
|
||||
#include <adc.h>
|
||||
#include <aes.h>
|
||||
#include <comp.h>
|
||||
#include <crc.h>
|
||||
#include <pka.h>
|
||||
#include <rf.h>
|
||||
#include <rng.h>
|
||||
#include <rtc.h>
|
||||
#include <spi.h>
|
||||
#include <tim.h>
|
||||
#include <usb_device.h>
|
||||
#include <gpio.h>
|
||||
|
||||
void furi_hal_init() {
|
||||
furi_hal_clock_init();
|
||||
furi_hal_console_init();
|
||||
furi_hal_interrupt_init();
|
||||
furi_hal_delay_init();
|
||||
|
||||
MX_GPIO_Init();
|
||||
FURI_LOG_I("HAL", "GPIO OK");
|
||||
|
||||
MX_RTC_Init();
|
||||
FURI_LOG_I("HAL", "RTC OK");
|
||||
furi_hal_boot_init();
|
||||
furi_hal_version_init();
|
||||
|
||||
MX_ADC1_Init();
|
||||
FURI_LOG_I("HAL", "ADC1 OK");
|
||||
|
||||
MX_SPI1_Init();
|
||||
FURI_LOG_I("HAL", "SPI1 OK");
|
||||
MX_SPI2_Init();
|
||||
FURI_LOG_I("HAL", "SPI2 OK");
|
||||
furi_hal_spi_init();
|
||||
|
||||
MX_TIM1_Init();
|
||||
FURI_LOG_I("HAL", "TIM1 OK");
|
||||
MX_TIM2_Init();
|
||||
FURI_LOG_I("HAL", "TIM2 OK");
|
||||
MX_TIM16_Init();
|
||||
FURI_LOG_I("HAL", "TIM16 OK");
|
||||
MX_COMP1_Init();
|
||||
FURI_LOG_I("HAL", "COMP1 OK");
|
||||
MX_RF_Init();
|
||||
FURI_LOG_I("HAL", "RF OK");
|
||||
MX_PKA_Init();
|
||||
FURI_LOG_I("HAL", "PKA OK");
|
||||
MX_RNG_Init();
|
||||
FURI_LOG_I("HAL", "RNG OK");
|
||||
MX_AES1_Init();
|
||||
FURI_LOG_I("HAL", "AES1 OK");
|
||||
MX_AES2_Init();
|
||||
FURI_LOG_I("HAL", "AES2 OK");
|
||||
MX_CRC_Init();
|
||||
FURI_LOG_I("HAL", "CRC OK");
|
||||
|
||||
// VCP + USB
|
||||
furi_hal_vcp_init();
|
||||
MX_USB_Device_Init();
|
||||
FURI_LOG_I("HAL", "USB OK");
|
||||
|
||||
furi_hal_i2c_init();
|
||||
|
||||
// High Level
|
||||
furi_hal_power_init();
|
||||
furi_hal_light_init();
|
||||
furi_hal_vibro_init();
|
||||
furi_hal_subghz_init();
|
||||
furi_hal_nfc_init();
|
||||
|
||||
// FreeRTOS glue
|
||||
furi_hal_os_init();
|
||||
}
|
||||
Reference in New Issue
Block a user