[FL-873] Add F5 target, lp5562 driver and api-hal-light (#347)

* Add F5 target, lp5562 driver and api-hal-light. Update api-usage, switch to F5 by default.
* API HAL: add i2c and hardware version api. Dolphin: show hardware version.
* OTP version generator and flashing utility.
* Assets script: fix code formatting
* Backport F5 changes to F4
* F4: disable insomnia, prevent damage to BLE RX path
* F5 HAL API Light: remove magic delay to fix magic BLE
* Dolphin: HW target validation on start
* invert RSSI indication in sub-1
* API HAL: rename board to body in version api
* Gpio tester: detach and release viewport on exit

Co-authored-by: aanper <mail@s3f.ru>
This commit is contained in:
あく
2021-02-18 15:49:32 +03:00
committed by GitHub
parent da91482b7d
commit 68a3f6b4b7
214 changed files with 25577 additions and 184 deletions

View File

@@ -0,0 +1,13 @@
#include <api-hal-boot.h>
#include <stm32wbxx_ll_rtc.h>
#define BOOT_REQUEST_NONE 0x00000000
#define BOOT_REQUEST_DFU 0xDF00B000
void api_hal_boot_set_mode(ApiHalBootMode mode) {
if (mode == ApiHalBootModeNormal) {
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_NONE);
} else if (mode == ApiHalBootModeDFU) {
LL_RTC_BAK_SetRegister(RTC, LL_RTC_BKP_DR0, BOOT_REQUEST_DFU);
}
}

View File

@@ -0,0 +1,78 @@
#include <api-hal-bt.h>
#include <app_entry.h>
#include <ble.h>
#include <stm32wbxx.h>
#include <shci.h>
#include <cmsis_os2.h>
void api_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();
}
void api_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 api_hal_bt_is_alive() {
return APPE_Status() == BleGlueStatusStarted;
}
bool api_hal_bt_wait_transition() {
uint8_t counter = 0;
while (APPE_Status() == BleGlueStatusStartup) {
osDelay(10);
counter++;
if (counter > 1000) {
return false;
}
}
return true;
}
bool api_hal_bt_lock_flash() {
if (!api_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 api_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);
}
}

View File

@@ -0,0 +1,27 @@
#include <api-hal-clock.h>
#include <stm32wbxx_ll_rcc.h>
void api_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 api_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);
}

View File

@@ -0,0 +1,7 @@
#pragma once
/* Switch to HSI clock */
void api_hal_clock_switch_to_hsi();
/* Switch to PLL clock */
void api_hal_clock_switch_to_pll();

View File

@@ -0,0 +1,27 @@
#include "api-hal-delay.h"
#include "assert.h"
#include "cmsis_os2.h"
static uint32_t clk_per_microsecond;
void delay_us_init_DWT(void) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
DWT->CYCCNT = 0U;
clk_per_microsecond = SystemCoreClock / 1000000.0f;
}
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);
assert(result == osOK);
}

View File

@@ -0,0 +1,35 @@
#include <api-hal-flash.h>
#include <api-hal-bt.h>
#include <stm32wbxx.h>
bool api_hal_flash_erase(uint8_t page, uint8_t count) {
if (!api_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);
api_hal_bt_unlock_flash();
return status == HAL_OK;
}
bool api_hal_flash_write_dword(size_t address, uint64_t data) {
if (!api_hal_bt_lock_flash()) {
return false;
}
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, address, data);
api_hal_bt_unlock_flash();
return status == HAL_OK;
}
bool api_hal_flash_write_row(size_t address, size_t source_address) {
if (!api_hal_bt_lock_flash()) {
return false;
}
HAL_StatusTypeDef status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FAST, address, source_address);
api_hal_bt_unlock_flash();
return status == HAL_OK;
}

View File

@@ -0,0 +1,29 @@
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
/*
* Erase Flash
* Locking operation, uses HSEM to manage shared access.
* @param page, page number
* @param count, page count to erase
*/
bool api_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 api_hal_flash_write_dword(size_t address, uint64_t data);
/*
* Write page (4096 bytes or 64 rows of double words).
* Locking operation, uses HSEM to manage shared access.
* @param address - destination address, must be page aligned
* @param source_address - source address
*/
bool api_hal_flash_write_page(size_t address, size_t source_address);

View File

@@ -0,0 +1,53 @@
#include <api-hal-gpio.h>
#include <api-hal-spi.h>
#include <api-hal-resources.h>
#include <api-hal-delay.h>
// init GPIO
void hal_gpio_init(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed) {
// TODO: Alternate Functions
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = gpio->pin;
GPIO_InitStruct.Mode = mode;
GPIO_InitStruct.Pull = pull;
GPIO_InitStruct.Speed = speed;
HAL_GPIO_Init(gpio->port, &GPIO_InitStruct);
}
bool hal_gpio_read_sd_detect(void) {
bool result = false;
// TODO open record
const GpioPin* sd_cs_record = &sd_cs_gpio;
// TODO: SPI manager
api_hal_spi_lock(sd_fast_spi.spi);
// configure pin as input
gpio_init_ex(sd_cs_record, GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
delay(1);
// if gpio_read == 0 return true else return false
result = !gpio_read(sd_cs_record);
// configure pin back
gpio_init_ex(sd_cs_record, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
gpio_write(sd_cs_record, 1);
delay(1);
// TODO: SPI manager
api_hal_spi_unlock(sd_fast_spi.spi);
return result;
}
void enable_cc1101_irq() {
HAL_NVIC_SetPriority(EXTI4_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}

View File

@@ -0,0 +1,77 @@
#pragma once
#include "main.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
// this defined in xx_hal_gpio.c, so...
#define GPIO_NUMBER (16U)
typedef enum {
GpioModeInput = GPIO_MODE_INPUT,
GpioModeOutputPushPull = GPIO_MODE_OUTPUT_PP,
GpioModeOutputOpenDrain = GPIO_MODE_OUTPUT_OD,
GpioModeAltFunctionPushPull = GPIO_MODE_AF_PP,
GpioModeAltFunctionOpenDrain = GPIO_MODE_AF_OD,
GpioModeAnalog = GPIO_MODE_ANALOG,
GpioModeInterruptRise = GPIO_MODE_IT_RISING,
GpioModeInterruptFall = GPIO_MODE_IT_FALLING,
GpioModeInterruptRiseFall = GPIO_MODE_IT_RISING_FALLING,
GpioModeEventRise = GPIO_MODE_EVT_RISING,
GpioModeEventFall = GPIO_MODE_EVT_FALLING,
GpioModeEventRiseFall = GPIO_MODE_EVT_RISING_FALLING,
} GpioMode;
typedef enum {
GpioSpeedLow = GPIO_SPEED_FREQ_LOW,
GpioSpeedMedium = GPIO_SPEED_FREQ_MEDIUM,
GpioSpeedHigh = GPIO_SPEED_FREQ_HIGH,
GpioSpeedVeryHigh = GPIO_SPEED_FREQ_VERY_HIGH,
} GpioSpeed;
typedef enum {
GpioPullNo = GPIO_NOPULL,
GpioPullUp = GPIO_PULLUP,
GpioPullDown = GPIO_PULLDOWN,
} GpioPull;
typedef struct {
GPIO_TypeDef* port;
uint16_t pin;
} GpioPin;
// init GPIO
void hal_gpio_init(
const GpioPin* gpio,
const GpioMode mode,
const GpioPull pull,
const GpioSpeed speed);
// write value to GPIO, false = LOW, true = HIGH
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;
}
}
// read value from GPIO, false = LOW, true = HIGH
static inline bool hal_gpio_read(const GpioPin* gpio) {
if((gpio->port->IDR & gpio->pin) != 0x00U) {
return true;
} else {
return false;
}
}
bool hal_gpio_read_sd_detect(void);
void enable_cc1101_irq();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,17 @@
#include <api-hal-i2c.h>
#include <furi.h>
osMutexId_t api_hal_i2c_mutex = NULL;
void api_hal_i2c_init() {
api_hal_i2c_mutex = osMutexNew(NULL);
furi_check(api_hal_i2c_mutex);
}
void api_hal_i2c_lock() {
furi_check(osMutexAcquire(api_hal_i2c_mutex, osWaitForever) == osOK);
}
void api_hal_i2c_unlock() {
furi_check(osMutexRelease(api_hal_i2c_mutex) == osOK);
}

View File

@@ -0,0 +1,43 @@
#include <api-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 api_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();
}
void api_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;
}
}

View File

@@ -0,0 +1,95 @@
#pragma once
#include <stm32wbxx_ll_lptim.h>
#include <stdbool.h>
static inline void assert(bool value) {
if (!value) asm("bkpt 1");
}
// Timer used for system ticks
#define API_HAL_OS_TIMER_MAX 0xFFFF
#define API_HAL_OS_TIMER_REG_LOAD_DLY 0x1
#define API_HAL_OS_TIMER LPTIM2
#define API_HAL_OS_TIMER_IRQ LPTIM2_IRQn
#define API_HAL_OS_TIMER_CLOCK_INIT() \
{ \
LL_RCC_SetLPTIMClockSource(LL_RCC_LPTIM2_CLKSOURCE_LSE); \
LL_APB1_GRP2_EnableClock(LL_APB1_GRP2_PERIPH_LPTIM2); \
} \
static inline void api_hal_os_timer_init() {
API_HAL_OS_TIMER_CLOCK_INIT();
LL_LPTIM_Enable(API_HAL_OS_TIMER);
while(!LL_LPTIM_IsEnabled(API_HAL_OS_TIMER)) {}
LL_LPTIM_SetClockSource(API_HAL_OS_TIMER, LL_LPTIM_CLK_SOURCE_INTERNAL);
LL_LPTIM_SetPrescaler(API_HAL_OS_TIMER, LL_LPTIM_PRESCALER_DIV1);
LL_LPTIM_SetPolarity(API_HAL_OS_TIMER, LL_LPTIM_OUTPUT_POLARITY_REGULAR);
LL_LPTIM_SetUpdateMode(API_HAL_OS_TIMER, LL_LPTIM_UPDATE_MODE_IMMEDIATE);
LL_LPTIM_SetCounterMode(API_HAL_OS_TIMER, LL_LPTIM_COUNTER_MODE_INTERNAL);
LL_LPTIM_TrigSw(API_HAL_OS_TIMER);
LL_LPTIM_SetInput1Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT1_SRC_GPIO);
LL_LPTIM_SetInput2Src(API_HAL_OS_TIMER, LL_LPTIM_INPUT2_SRC_GPIO);
NVIC_SetPriority(API_HAL_OS_TIMER_IRQ, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 15, 0));
NVIC_EnableIRQ(API_HAL_OS_TIMER_IRQ);
}
static inline uint32_t api_hal_os_timer_get_cnt() {
uint32_t counter = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
uint32_t counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
while(counter != counter_shadow) {
counter = counter_shadow;
counter_shadow = LL_LPTIM_GetCounter(API_HAL_OS_TIMER);
}
return counter;
}
static inline bool api_hal_os_timer_arr_is_ok() {
return LL_LPTIM_IsActiveFlag_ARROK(API_HAL_OS_TIMER);
}
static inline uint32_t api_hal_os_timer_get_arr() {
return LL_LPTIM_GetAutoReload(API_HAL_OS_TIMER);;
}
static inline void api_hal_os_timer_set_arr(uint32_t value) {
value &= API_HAL_OS_TIMER_MAX;
if (value != api_hal_os_timer_get_arr()) {
assert(api_hal_os_timer_arr_is_ok());
LL_LPTIM_ClearFlag_ARROK(API_HAL_OS_TIMER);
LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, value);
}
}
static inline bool api_hal_os_timer_cmp_is_ok() {
return LL_LPTIM_IsActiveFlag_CMPOK(API_HAL_OS_TIMER);
}
static inline uint32_t api_hal_os_timer_get_cmp() {
return LL_LPTIM_GetCompare(API_HAL_OS_TIMER);
}
static inline void api_hal_os_timer_set_cmp(uint32_t value) {
value &= API_HAL_OS_TIMER_MAX;
if (value != api_hal_os_timer_get_cmp()) {
assert(api_hal_os_timer_cmp_is_ok());
LL_LPTIM_ClearFlag_CMPOK(API_HAL_OS_TIMER);
LL_LPTIM_SetCompare(API_HAL_OS_TIMER, value);
}
}
static inline bool api_hal_os_timer_is_safe() {
uint16_t cmp = api_hal_os_timer_get_cmp();
uint16_t cnt = api_hal_os_timer_get_cnt();
uint16_t margin = (cmp > cnt) ? cmp - cnt : cnt - cmp;
if (margin < 8) {
return false;
}
if (!api_hal_os_timer_cmp_is_ok()) {
return false;
}
return true;
}

View File

@@ -0,0 +1,169 @@
#include <api-hal-os.h>
#include <api-hal-os-timer.h>
#include <api-hal-power.h>
#include <FreeRTOS.h>
#include <cmsis_os.h>
#define API_HAL_OS_CLK_FREQUENCY 32768
#define API_HAL_OS_TICK_PER_SECOND 1024
#define API_HAL_OS_CLK_PER_TICK (API_HAL_OS_CLK_FREQUENCY / API_HAL_OS_TICK_PER_SECOND)
#define API_HAL_OS_TICK_PER_EPOCH (API_HAL_OS_TIMER_MAX / API_HAL_OS_CLK_PER_TICK)
#define API_HAL_OS_MAX_SLEEP (API_HAL_OS_TICK_PER_EPOCH - 1)
#ifdef API_HAL_OS_DEBUG
#include <stm32wbxx_ll_gpio.h>
#define LED_GREEN_PORT GPIOA
#define LED_GREEN_PIN LL_GPIO_PIN_7
#endif
typedef struct {
// Tick counters
volatile uint32_t in_sleep;
volatile uint32_t in_awake;
// Error counters
volatile uint32_t sleep_error;
volatile uint32_t awake_error;
} ApiHalOs;
ApiHalOs api_hal_os = {
.in_sleep = 0,
.in_awake = 0,
.sleep_error = 0,
.awake_error = 0,
};
void api_hal_os_init() {
api_hal_os_timer_init();
LL_DBGMCU_APB1_GRP2_FreezePeriph(LL_DBGMCU_APB1_GRP2_LPTIM2_STOP);
LL_LPTIM_EnableIT_CMPM(API_HAL_OS_TIMER);
LL_LPTIM_EnableIT_ARRM(API_HAL_OS_TIMER);
LL_LPTIM_SetAutoReload(API_HAL_OS_TIMER, API_HAL_OS_TIMER_MAX);
LL_LPTIM_SetCompare(API_HAL_OS_TIMER, API_HAL_OS_CLK_PER_TICK);
LL_LPTIM_StartCounter(API_HAL_OS_TIMER, LL_LPTIM_OPERATING_MODE_CONTINUOUS);
}
void LPTIM2_IRQHandler(void) {
// Autoreload
const bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
if(arrm_flag) {
LL_LPTIM_ClearFLAG_ARRM(API_HAL_OS_TIMER);
}
if(LL_LPTIM_IsActiveFlag_CMPM(API_HAL_OS_TIMER)) {
LL_LPTIM_ClearFLAG_CMPM(API_HAL_OS_TIMER);
// Store important value
uint16_t cnt = api_hal_os_timer_get_cnt();
uint16_t cmp = api_hal_os_timer_get_cmp();
uint16_t current_tick = cnt / API_HAL_OS_CLK_PER_TICK;
uint16_t compare_tick = cmp / API_HAL_OS_CLK_PER_TICK;
// Calculate error
// happens when HAL or other high priority IRQ takes our time
int32_t error = (int32_t)compare_tick - current_tick;
api_hal_os.awake_error += ((error>0) ? error : -error);
// Calculate and set next tick
uint16_t next_tick = current_tick + 1;
api_hal_os_timer_set_cmp(next_tick * API_HAL_OS_CLK_PER_TICK);
// Notify OS
api_hal_os.in_awake ++;
if (xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED) {
xPortSysTickHandler();
}
}
}
static inline uint32_t api_hal_os_sleep(TickType_t expected_idle_ticks) {
// Store important value before going to sleep
const uint16_t before_cnt = api_hal_os_timer_get_cnt();
const uint16_t before_tick = before_cnt / API_HAL_OS_CLK_PER_TICK;
// Calculate and set next wakeup compare value
const uint16_t expected_cnt = (before_tick + expected_idle_ticks - 2) * API_HAL_OS_CLK_PER_TICK;
api_hal_os_timer_set_cmp(expected_cnt);
HAL_SuspendTick();
// Go to stop2 mode
#ifdef API_HAL_OS_DEBUG
LL_GPIO_SetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
#endif
api_hal_power_deep_sleep();
#ifdef API_HAL_OS_DEBUG
LL_GPIO_ResetOutputPin(LED_GREEN_PORT, LED_GREEN_PIN);
#endif
HAL_ResumeTick();
// Spin till we are in timer safe zone
while(!api_hal_os_timer_is_safe()) {}
// Store current counter value, calculate current tick
const uint16_t after_cnt = api_hal_os_timer_get_cnt();
const uint16_t after_tick = after_cnt / API_HAL_OS_CLK_PER_TICK;
// Store and clear interrupt flags
// we don't want handler to be called after renabling IRQ
bool arrm_flag = LL_LPTIM_IsActiveFlag_ARRM(API_HAL_OS_TIMER);
// Calculate and set next wakeup compare value
const uint16_t next_cmp = (after_tick + 1) * API_HAL_OS_CLK_PER_TICK;
api_hal_os_timer_set_cmp(next_cmp);
// Calculate ticks count spent in sleep and perform sanity checks
int32_t completed_ticks = arrm_flag ? (int32_t)before_tick - after_tick : (int32_t)after_tick - before_tick;
return completed_ticks;
}
void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
if (!api_hal_power_deep_available()) {
return;
}
// Limit mount of ticks to maximum that timer can count
if (expected_idle_ticks > API_HAL_OS_MAX_SLEEP) {
expected_idle_ticks = API_HAL_OS_MAX_SLEEP;
}
// Stop IRQ handling, no one should disturb us till we finish
__disable_irq();
// Confirm OS that sleep is still possible
// And check if timer is in safe zone
// (8 clocks till any IRQ event or ongoing synchronization)
if (eTaskConfirmSleepModeStatus() == eAbortSleep
|| !api_hal_os_timer_is_safe()) {
__enable_irq();
return;
}
uint32_t completed_ticks = api_hal_os_sleep(expected_idle_ticks);
assert(completed_ticks >= 0);
// Reenable IRQ
__enable_irq();
// Notify system about time spent in sleep
if (completed_ticks > 0) {
api_hal_os.in_sleep += completed_ticks;
if (completed_ticks > expected_idle_ticks) {
// We are late, count error
api_hal_os.sleep_error += (completed_ticks - expected_idle_ticks);
// Freertos is not happy when we overleep
// But we are not going to tell her
vTaskStepTick(expected_idle_ticks);
} else {
vTaskStepTick(completed_ticks);
}
}
}
void vApplicationStackOverflowHook(TaskHandle_t xTask, signed char *pcTaskName) {
asm("bkpt 1");
while(1) {};
}

View File

@@ -0,0 +1,17 @@
#pragma once
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Initialize OS helpers
* Configure and start tick timer
*/
void api_hal_os_init();
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,194 @@
#include <api-hal-power.h>
#include <api-hal-clock.h>
#include <api-hal-bt.h>
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_pwr.h>
#include <stm32wbxx_ll_hsem.h>
#include <stm32wbxx_ll_cortex.h>
#include <main.h>
#include <hw_conf.h>
#include <bq27220.h>
#include <bq25896.h>
volatile uint32_t api_hal_power_insomnia = 1;
void HAL_RCC_CSSCallback(void) {
LL_RCC_ForceBackupDomainReset();
LL_RCC_ReleaseBackupDomainReset();
NVIC_SystemReset();
}
void api_hal_power_init() {
LL_PWR_SMPS_SetMode(LL_PWR_SMPS_STEP_DOWN);
bq27220_init();
bq25896_init();
}
uint16_t api_hal_power_insomnia_level() {
return api_hal_power_insomnia;
}
void api_hal_power_insomnia_enter() {
api_hal_power_insomnia++;
}
void api_hal_power_insomnia_exit() {
api_hal_power_insomnia--;
}
bool api_hal_power_deep_available() {
return api_hal_bt_is_alive() && api_hal_power_insomnia == 0;
}
void api_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
api_hal_clock_switch_to_hsi();
}
} else {
/**
* The switch on HSI before entering Stop Mode is required
*/
api_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_STOP2);
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) {
api_hal_clock_switch_to_pll();
}
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
}
uint8_t api_hal_power_get_pct() {
return bq27220_get_state_of_charge();
}
bool api_hal_power_is_charging() {
return bq25896_is_charging();
}
void api_hal_power_off() {
bq25896_poweroff();
}
void api_hal_power_enable_otg() {
bq25896_enable_otg();
}
void api_hal_power_disable_otg() {
bq25896_disable_otg();
}
uint32_t api_hal_power_get_battery_remaining_capacity() {
return bq27220_get_remaining_capacity();
}
uint32_t api_hal_power_get_battery_full_capacity() {
return bq27220_get_full_charge_capacity();
}
float api_hal_power_get_battery_voltage(ApiHalPowerIC ic) {
if (ic == ApiHalPowerICCharger) {
return (float)bq25896_get_vbat_voltage() / 1000.0f;
} else if (ic == ApiHalPowerICFuelGauge) {
return (float)bq27220_get_voltage() / 1000.0f;
} else {
return 0.0f;
}
}
float api_hal_power_get_battery_current(ApiHalPowerIC ic) {
if (ic == ApiHalPowerICCharger) {
return (float)bq25896_get_vbat_current() / 1000.0f;
} else if (ic == ApiHalPowerICFuelGauge) {
return (float)bq27220_get_current() / 1000.0f;
} else {
return 0.0f;
}
}
float api_hal_power_get_battery_temperature(ApiHalPowerIC ic) {
if (ic == ApiHalPowerICCharger) {
// Linear approximation, +/- 5 C
return (71.0f - (float)bq25896_get_ntc_mpct()/1000) / 0.6f;
} else if (ic == ApiHalPowerICFuelGauge) {
return ((float)bq27220_get_temperature() - 2731.0f) / 10.0f;
} else {
return 0.0f;
}
}
void api_hal_power_dump_state(string_t buffer) {
BatteryStatus battery_status;
OperationStatus operation_status;
if (bq27220_get_battery_status(&battery_status) == BQ27220_ERROR
|| bq27220_get_operation_status(&operation_status) == BQ27220_ERROR) {
string_cat_printf(buffer, "Failed to get bq27220 status. Communication error.\r\n");
} else {
string_cat_printf(buffer,
"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
string_cat_printf(buffer,
"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
string_cat_printf(buffer,
"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
string_cat_printf(buffer,
"bq27220: Full capacity: %dmAh, Remaining capacity: %dmAh, State of Charge: %d%%\r\n",
bq27220_get_full_charge_capacity(), bq27220_get_remaining_capacity(),
bq27220_get_state_of_charge()
);
string_cat_printf(buffer,
"bq27220: Voltage: %dmV, Current: %dmA, Temperature: %dC\r\n",
bq27220_get_voltage(), bq27220_get_current(), (int)api_hal_power_get_battery_temperature(ApiHalPowerICFuelGauge)
);
}
string_cat_printf(buffer,
"bq25896: VBUS: %d, VSYS: %d, VBAT: %d, Current: %d, NTC: %dm%%\r\n",
bq25896_get_vbus_voltage(), bq25896_get_vsys_voltage(),
bq25896_get_vbat_voltage(), bq25896_get_vbat_current(),
bq25896_get_ntc_mpct()
);
}

View File

@@ -0,0 +1,57 @@
#include "api-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);
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);
}
void irda_pwm_set(float value, float freq){
hal_pwmn_set(value, freq, &IRDA_TX_TIM, IRDA_TX_CH);
}
void irda_pwm_stop(){
hal_pwmn_stop(&IRDA_TX_TIM, IRDA_TX_CH);
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include "main.h"
#include "stdbool.h"
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);
void irda_pwm_set(float value, float freq);
void irda_pwm_stop();

View File

@@ -0,0 +1,31 @@
#include <api-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 sd_cs_gpio = {SD_CS_GPIO_Port, SD_CS_Pin};
const GpioPin vibro_gpio = {VIBRO_GPIO_Port, VIBRO_Pin};
const GpioPin ibutton_gpio = {iBTN_GPIO_Port, iBTN_Pin};
const GpioPin cc1101_g0_gpio = {CC1101_G0_GPIO_Port, CC1101_G0_Pin};

View File

@@ -0,0 +1,48 @@
#pragma once
#include "main.h"
#include <furi.h>
#ifdef __cplusplus
extern "C" {
#endif
/* 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 sd_cs_gpio;
extern const GpioPin vibro_gpio;
extern const GpioPin ibutton_gpio;
extern const GpioPin cc1101_g0_gpio;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,68 @@
#include "main.h"
#include "api-hal-spi-config.h"
extern SPI_HandleTypeDef SPI_R;
extern SPI_HandleTypeDef SPI_D;
/**
* SD Card in fast mode (after init)
*/
const SPIDevice sd_fast_spi = {
.spi = &SPI_D,
.config = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_8BIT,
.CLKPolarity = SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_1EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_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 SPIDevice sd_slow_spi = {
.spi = &SPI_D,
.config = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_8BIT,
.CLKPolarity = SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_1EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_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,
}};
/**
* Display
*/
const SPIDevice display_spi = {
.spi = &SPI_D,
.config = {
.Mode = SPI_MODE_MASTER,
.Direction = SPI_DIRECTION_2LINES,
.DataSize = SPI_DATASIZE_8BIT,
.CLKPolarity = SPI_POLARITY_LOW,
.CLKPhase = SPI_PHASE_1EDGE,
.NSS = SPI_NSS_SOFT,
.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16,
.FirstBit = SPI_FIRSTBIT_MSB,
.TIMode = SPI_TIMODE_DISABLE,
.CRCCalculation = SPI_CRCCALCULATION_DISABLE,
.CRCPolynomial = 7,
.CRCLength = SPI_CRC_LENGTH_DATASIZE,
.NSSPMode = SPI_NSS_PULSE_ENABLE,
}};

View File

@@ -0,0 +1,18 @@
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
SPI_HandleTypeDef* spi;
const SPI_InitTypeDef config;
} SPIDevice;
extern const SPIDevice sd_fast_spi;
extern const SPIDevice sd_slow_spi;
extern const SPIDevice display_spi;
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,69 @@
#include "api-hal-spi.h"
#include <cmsis_os2.h>
#include <stdbool.h>
#include <string.h>
osMutexId_t spi_mutex_r;
osMutexId_t spi_mutex_d;
extern SPI_HandleTypeDef SPI_R;
extern SPI_HandleTypeDef SPI_D;
extern void Enable_SPI(SPI_HandleTypeDef* spi);
void api_hal_spi_init() {
spi_mutex_r = osMutexNew(NULL);
spi_mutex_d = osMutexNew(NULL);
}
void api_hal_spi_apply_config(const SPIDevice* device) {
osKernelLock();
memcpy(&device->spi->Init, &device->config, sizeof(SPI_InitTypeDef));
if(HAL_SPI_Init(device->spi) != HAL_OK) {
Error_Handler();
}
Enable_SPI(device->spi);
osKernelUnlock();
}
bool api_hal_spi_config_are_actual(const SPIDevice* device) {
return (memcmp(&device->config, &device->spi->Init, sizeof(SPI_InitTypeDef)) == 0);
}
void api_hal_spi_config_device(const SPIDevice* device) {
if(!api_hal_spi_config_are_actual(device)) {
api_hal_spi_apply_config(device);
}
}
void api_hal_spi_lock(SPI_HandleTypeDef* spi) {
if(spi == &SPI_D) {
osMutexAcquire(spi_mutex_d, osWaitForever);
} else if(spi == &SPI_R) {
osMutexAcquire(spi_mutex_r, osWaitForever);
} else {
Error_Handler();
}
}
void api_hal_spi_unlock(SPI_HandleTypeDef* spi) {
if(spi == &SPI_D) {
osMutexRelease(spi_mutex_d);
} else if(spi == &SPI_R) {
osMutexRelease(spi_mutex_r);
} else {
Error_Handler();
}
}
void api_hal_spi_lock_device(const SPIDevice* device) {
api_hal_spi_lock(device->spi);
api_hal_spi_config_device(device);
}
void api_hal_spi_unlock_device(const SPIDevice* device) {
api_hal_spi_unlock(device->spi);
}

View File

@@ -0,0 +1,36 @@
#pragma once
#include "main.h"
#include "api-hal-spi-config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Init SPI API
*/
void api_hal_spi_init();
/**
* Lock SPI bus
*/
void api_hal_spi_lock(SPI_HandleTypeDef* spi);
/**
* Unlock SPI bus
*/
void api_hal_spi_unlock(SPI_HandleTypeDef* spi);
/**
* Lock SPI device bus and apply config if needed
*/
void api_hal_spi_lock_device(const SPIDevice* device);
/**
* Unlock SPI device bus
*/
void api_hal_spi_unlock_device(const SPIDevice* device);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,54 @@
#include "cmsis_os.h"
#include "api-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();
}

View File

@@ -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);

View File

@@ -0,0 +1,47 @@
#include "cmsis_os.h"
#include "api-hal-tim.h"
/* setup TIM2 CH1 and CH2 to capture rising and falling events */
void tim_irda_rx_init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_IC_InitTypeDef sConfigIC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 64 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 4294967295;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if(HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if(HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if(HAL_TIM_IC_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if(HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
if(HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
if(HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_2) != HAL_OK) {
Error_Handler();
}
HAL_NVIC_SetPriority(TIM2_IRQn, 5, 0);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2);
}

View File

@@ -0,0 +1,4 @@
#pragma once
#include "main.h"
void tim_irda_rx_init(void);

View File

@@ -0,0 +1,10 @@
#include <api-hal-uid.h>
#include <stm32wbxx.h>
size_t api_hal_uid_size() {
return 64/8;
}
const uint8_t* api_hal_uid() {
return (const uint8_t *)UID64_BASE;
}

View File

@@ -0,0 +1,96 @@
#include <api-hal-vcp.h>
#include <usbd_cdc_if.h>
#include <furi.h>
#include <stream_buffer.h>
#define API_HAL_VCP_RX_BUFFER_SIZE 600
typedef struct {
StreamBufferHandle_t rx_stream;
osSemaphoreId_t tx_semaphore;
volatile bool alive;
volatile bool underrun;
} ApiHalVcp;
static ApiHalVcp* api_hal_vcp = NULL;
static const uint8_t ascii_soh = 0x01;
static const uint8_t ascii_eot = 0x04;
void _api_hal_vcp_init();
void _api_hal_vcp_deinit();
void _api_hal_vcp_control_line(uint8_t state);
void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size);
void _api_hal_vcp_tx_complete(size_t size);
void api_hal_vcp_init() {
api_hal_vcp = furi_alloc(sizeof(ApiHalVcp));
api_hal_vcp->rx_stream = xStreamBufferCreate(API_HAL_VCP_RX_BUFFER_SIZE, 1);
api_hal_vcp->tx_semaphore = osSemaphoreNew(1, 1, NULL);
api_hal_vcp->alive = false;
api_hal_vcp->underrun = false;
}
void _api_hal_vcp_init() {
osSemaphoreRelease(api_hal_vcp->tx_semaphore);
}
void _api_hal_vcp_deinit() {
api_hal_vcp->alive = false;
osSemaphoreRelease(api_hal_vcp->tx_semaphore);
}
void _api_hal_vcp_control_line(uint8_t state) {
// bit 0: DTR state, bit 1: RTS state
// bool dtr = state & 0b01;
bool rts = state & 0b10;
if (rts) {
api_hal_vcp->alive = true;
_api_hal_vcp_rx_callback(&ascii_soh, 1); // SOH
} else {
api_hal_vcp->alive = false;
_api_hal_vcp_rx_callback(&ascii_eot, 1); // EOT
}
osSemaphoreRelease(api_hal_vcp->tx_semaphore);
}
void _api_hal_vcp_rx_callback(const uint8_t* buffer, size_t size) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
size_t ret = xStreamBufferSendFromISR(api_hal_vcp->rx_stream, buffer, size, &xHigherPriorityTaskWoken);
if (ret != size) {
api_hal_vcp->underrun = true;
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
void _api_hal_vcp_tx_complete(size_t size) {
osSemaphoreRelease(api_hal_vcp->tx_semaphore);
}
size_t api_hal_vcp_rx(uint8_t* buffer, size_t size) {
furi_assert(api_hal_vcp);
return xStreamBufferReceive(api_hal_vcp->rx_stream, buffer, size, portMAX_DELAY);
}
void api_hal_vcp_tx(const uint8_t* buffer, size_t size) {
furi_assert(api_hal_vcp);
while (size > 0 && api_hal_vcp->alive) {
furi_check(osSemaphoreAcquire(api_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);
}
}
}

View File

@@ -0,0 +1,34 @@
#include <api-hal-version.h>
#include <stm32wbxx.h>
typedef struct {
uint8_t version;
uint8_t target;
uint8_t body;
uint8_t connect;
uint32_t timestamp;
} ApiHalVersionOTP;
bool api_hal_version_do_i_belong_here() {
return api_hal_version_get_hw_target() == 5;
}
const uint8_t api_hal_version_get_hw_version() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->version;
}
const uint8_t api_hal_version_get_hw_target() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->target;
}
const uint8_t api_hal_version_get_hw_body() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->body;
}
const uint8_t api_hal_version_get_hw_connect() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->connect;
}
const uint32_t api_hal_version_get_hw_timestamp() {
return ((ApiHalVersionOTP*)OTP_AREA_BASE)->timestamp;
}

View File

@@ -0,0 +1,9 @@
#include <api-hal.h>
void api_hal_init() {
api_hal_os_init();
api_hal_vcp_init();
api_hal_spi_init();
api_hal_i2c_init();
api_hal_light_init();
}

View File

@@ -0,0 +1,36 @@
#include "api-hal/api-interrupt-mgr.h"
#include <main.h>
extern void api_interrupt_call(InterruptType type, void* hw);
/* interrupts */
/* Comparator trigger event */
void HAL_COMP_TriggerCallback(COMP_HandleTypeDef* hcomp) {
api_interrupt_call(InterruptTypeComparatorTrigger, hcomp);
}
/* Timer input capture event */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef* htim) {
api_interrupt_call(InterruptTypeTimerCapture, htim);
}
/* Output compare event */
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef* htim) {
api_interrupt_call(InterruptTypeTimerOutputCompare, htim);
}
/* Timer update event */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim) {
api_interrupt_call(InterruptTypeTimerUpdate, htim);
// handle HAL ticks
if(htim->Instance == TIM17) {
HAL_IncTick();
}
}
/* External interrupt event */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
api_interrupt_call(InterruptTypeExternalInterrupt, (void*)(uint32_t)GPIO_Pin);
}