Rename api-hal to furi-hal (#629)

This commit is contained in:
あく
2021-08-08 21:03:25 +03:00
committed by GitHub
parent 7907cb232b
commit 0a97d6913c
192 changed files with 2276 additions and 2212 deletions
@@ -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);
}
+144
View File
@@ -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
+152
View File
@@ -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;
}
}
+171
View File
@@ -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(&params);
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(&params);
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;
}
+141
View File
@@ -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
+168
View File
@@ -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;
}
+119
View File
@@ -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);
+106
View File
@@ -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);
}
+76
View File
@@ -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();
}