FuriHal: UART refactoring (#3211)

* FuriHal: UART refactoring
* ApiSymbols: add furi_record_destroy
* FuriHal: cleanup serial API, add logging configuration in RTC
* FuriHal: hide private part in _i header. Toolbox: cleanup value index. SystemSettings: logging device and baudrate.
* FuriHal: RTC logging method documentation
* Synchronize API Symbols
* Furi: mark HEAP_PRINT_DEBUG as broken
* FuriHal: furi_hal_serial, add custom IRQ func
* Fix PR review issues
* FuriHal: UART add reception DMA (#3220)
* FuriHal: add DMA serial rx mode
* usb_uart_bridge: switch to working with DMA
* Sync api symbol versions
* FuriHal: update serial docs and api
* FuriHal: Selial added similar API for simple reception mode as with DMA
* FuriHal: Update API target H18
* API: ver API H7
* FuriHal: Serial error processing
* FuriHal: fix furi_hal_serial set baudrate
* Sync api symbols
* FuriHal: cleanup serial isr and various flag handling procedures
* FuriHal: cleanup and simplify serial API
* Debug: update UART Echo serial related flags
* FuriHal: update serial API symbols naming
* FuriHalSerial: various improvements and PR review fixes
* FuriHal: proper ISR function signatures

---------

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
Co-authored-by: hedger <hedger@users.noreply.github.com>
Co-authored-by: SkorP <skorpionm@yandex.ru>
Co-authored-by: Skorpionm <85568270+Skorpionm@users.noreply.github.com>
This commit is contained in:
あく
2024-01-16 08:09:37 +09:00
committed by GitHub
parent d73d007797
commit fc043da9c6
37 changed files with 1966 additions and 759 deletions

View File

@@ -33,7 +33,7 @@ void furi_hal_init() {
furi_hal_mpu_init();
furi_hal_clock_init();
furi_hal_random_init();
furi_hal_console_init();
furi_hal_serial_control_init();
furi_hal_rtc_init();
furi_hal_interrupt_init();
furi_hal_flash_init();

View File

@@ -1,99 +0,0 @@
#include <furi_hal_console.h>
#include <furi_hal_uart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_gpio.h>
#include <stm32wbxx_ll_usart.h>
#include <furi.h>
#define TAG "FuriHalConsole"
#ifdef HEAP_PRINT_DEBUG
#define CONSOLE_BAUDRATE 1843200
#else
#define CONSOLE_BAUDRATE 230400
#endif
typedef struct {
bool alive;
FuriHalConsoleTxCallback tx_callback;
void* tx_callback_context;
} FuriHalConsole;
FuriHalConsole furi_hal_console = {
.alive = false,
.tx_callback = NULL,
.tx_callback_context = NULL,
};
void furi_hal_console_init() {
furi_hal_uart_init(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
furi_hal_console.alive = true;
}
void furi_hal_console_enable() {
furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
while(!LL_USART_IsActiveFlag_TC(USART1))
;
furi_hal_uart_set_br(FuriHalUartIdUSART1, CONSOLE_BAUDRATE);
furi_hal_console.alive = true;
}
void furi_hal_console_disable() {
while(!LL_USART_IsActiveFlag_TC(USART1))
;
furi_hal_console.alive = false;
}
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context) {
FURI_CRITICAL_ENTER();
furi_hal_console.tx_callback = callback;
furi_hal_console.tx_callback_context = context;
FURI_CRITICAL_EXIT();
}
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size) {
if(!furi_hal_console.alive) return;
FURI_CRITICAL_ENTER();
// Transmit data
if(furi_hal_console.tx_callback) {
furi_hal_console.tx_callback(buffer, buffer_size, furi_hal_console.tx_callback_context);
}
furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
// Wait for TC flag to be raised for last char
while(!LL_USART_IsActiveFlag_TC(USART1))
;
FURI_CRITICAL_EXIT();
}
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size) {
if(!furi_hal_console.alive) return;
FURI_CRITICAL_ENTER();
// Transmit data
furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)buffer, buffer_size);
// Transmit new line symbols
furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t*)"\r\n", 2);
// Wait for TC flag to be raised for last char
while(!LL_USART_IsActiveFlag_TC(USART1))
;
FURI_CRITICAL_EXIT();
}
void furi_hal_console_printf(const char format[], ...) {
FuriString* string;
va_list args;
va_start(args, format);
string = furi_string_alloc_vprintf(format, args);
va_end(args);
furi_hal_console_tx((const uint8_t*)furi_string_get_cstr(string), furi_string_size(string));
furi_string_free(string);
}
void furi_hal_console_puts(const char* data) {
furi_hal_console_tx((const uint8_t*)data, strlen(data));
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*FuriHalConsoleTxCallback)(const uint8_t* buffer, size_t size, void* context);
void furi_hal_console_init();
void furi_hal_console_enable();
void furi_hal_console_disable();
void furi_hal_console_set_tx_callback(FuriHalConsoleTxCallback callback, void* context);
void furi_hal_console_tx(const uint8_t* buffer, size_t buffer_size);
void furi_hal_console_tx_with_new_line(const uint8_t* buffer, size_t buffer_size);
/**
* Printf-like plain uart interface
* @warning Will not work in ISR context
* @param format
* @param ...
*/
void furi_hal_console_printf(const char format[], ...) _ATTRIBUTE((__format__(__printf__, 1, 2)));
void furi_hal_console_puts(const char* data);
#ifdef __cplusplus
}
#endif

View File

@@ -59,6 +59,12 @@ const IRQn_Type furi_hal_interrupt_irqn[FuriHalInterruptIdMax] = {
// LPTIMx
[FuriHalInterruptIdLpTim1] = LPTIM1_IRQn,
[FuriHalInterruptIdLpTim2] = LPTIM2_IRQn,
// UARTx
[FuriHalInterruptIdUart1] = USART1_IRQn,
// LPUARTx
[FuriHalInterruptIdLpUart1] = LPUART1_IRQn,
};
__attribute__((always_inline)) static inline void
@@ -329,3 +335,11 @@ void LPTIM1_IRQHandler() {
void LPTIM2_IRQHandler() {
furi_hal_interrupt_call(FuriHalInterruptIdLpTim2);
}
void USART1_IRQHandler(void) {
furi_hal_interrupt_call(FuriHalInterruptIdUart1);
}
void LPUART1_IRQHandler(void) {
furi_hal_interrupt_call(FuriHalInterruptIdLpUart1);
}

View File

@@ -49,6 +49,12 @@ typedef enum {
FuriHalInterruptIdLpTim1,
FuriHalInterruptIdLpTim2,
//UARTx
FuriHalInterruptIdUart1,
//LPUARTx
FuriHalInterruptIdLpUart1,
// Service value
FuriHalInterruptIdMax,
} FuriHalInterruptId;

View File

@@ -1,6 +1,5 @@
#include <furi_hal_os.h>
#include <furi_hal_clock.h>
#include <furi_hal_console.h>
#include <furi_hal_power.h>
#include <furi_hal_gpio.h>
#include <furi_hal_resources.h>
@@ -208,8 +207,8 @@ void vPortSuppressTicksAndSleep(TickType_t expected_idle_ticks) {
void vApplicationStackOverflowHook(TaskHandle_t xTask, char* pcTaskName) {
UNUSED(xTask);
furi_hal_console_puts("\r\n\r\n stack overflow in ");
furi_hal_console_puts(pcTaskName);
furi_hal_console_puts("\r\n\r\n");
furi_log_puts("\r\n\r\n stack overflow in ");
furi_log_puts(pcTaskName);
furi_log_puts("\r\n\r\n");
furi_crash("StackOverflow");
}

View File

@@ -3,7 +3,7 @@
#include <furi_hal_bt.h>
#include <furi_hal_vibro.h>
#include <furi_hal_resources.h>
#include <furi_hal_uart.h>
#include <furi_hal_serial_control.h>
#include <furi_hal_rtc.h>
#include <furi_hal_debug.h>
@@ -178,14 +178,12 @@ static inline void furi_hal_power_light_sleep() {
static inline void furi_hal_power_suspend_aux_periphs() {
// Disable USART
furi_hal_uart_suspend(FuriHalUartIdUSART1);
furi_hal_uart_suspend(FuriHalUartIdLPUART1);
furi_hal_serial_control_suspend();
}
static inline void furi_hal_power_resume_aux_periphs() {
// Re-enable USART
furi_hal_uart_resume(FuriHalUartIdUSART1);
furi_hal_uart_resume(FuriHalUartIdLPUART1);
furi_hal_serial_control_resume();
}
static inline void furi_hal_power_deep_sleep() {

View File

@@ -1,6 +1,7 @@
#include <furi_hal_rtc.h>
#include <furi_hal_light.h>
#include <furi_hal_debug.h>
#include <furi_hal_serial_control.h>
#include <stm32wbxx_ll_pwr.h>
#include <stm32wbxx_ll_bus.h>
@@ -34,7 +35,9 @@ typedef struct {
FuriHalRtcLocaleUnits locale_units : 1;
FuriHalRtcLocaleTimeFormat locale_timeformat : 1;
FuriHalRtcLocaleDateFormat locale_dateformat : 2;
uint8_t reserved : 6;
FuriHalRtcLogDevice log_device : 2;
FuriHalRtcLogBaudRate log_baud_rate : 3;
uint8_t reserved : 1;
} SystemReg;
_Static_assert(sizeof(SystemReg) == 4, "SystemReg size mismatch");
@@ -51,6 +54,24 @@ static const uint8_t furi_hal_rtc_days_per_month[2][FURI_HAL_RTC_MONTHS_COUNT] =
static const uint16_t furi_hal_rtc_days_per_year[] = {365, 366};
static const FuriHalSerialId furi_hal_rtc_log_devices[] = {
[FuriHalRtcLogDeviceUsart] = FuriHalSerialIdUsart,
[FuriHalRtcLogDeviceLpuart] = FuriHalSerialIdLpuart,
[FuriHalRtcLogDeviceReserved] = FuriHalSerialIdMax,
[FuriHalRtcLogDeviceNone] = FuriHalSerialIdMax,
};
static const uint32_t furi_hal_rtc_log_baud_rates[] = {
[FuriHalRtcLogBaudRate230400] = 230400,
[FuriHalRtcLogBaudRate9600] = 9600,
[FuriHalRtcLogBaudRate38400] = 38400,
[FuriHalRtcLogBaudRate57600] = 57600,
[FuriHalRtcLogBaudRate115200] = 115200,
[FuriHalRtcLogBaudRate460800] = 460800,
[FuriHalRtcLogBaudRate921600] = 921600,
[FuriHalRtcLogBaudRate1843200] = 1843200,
};
static void furi_hal_rtc_reset() {
LL_RCC_ForceBackupDomainReset();
LL_RCC_ReleaseBackupDomainReset();
@@ -153,6 +174,9 @@ void furi_hal_rtc_init() {
LL_RTC_Init(RTC, &RTC_InitStruct);
furi_log_set_level(furi_hal_rtc_get_log_level());
furi_hal_serial_control_set_logging_config(
furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
FURI_LOG_I(TAG, "Init OK");
}
@@ -199,6 +223,40 @@ uint8_t furi_hal_rtc_get_log_level() {
return data->log_level;
}
void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device) {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
SystemReg* data = (SystemReg*)&data_reg;
data->log_device = device;
furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
furi_hal_serial_control_set_logging_config(
furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
}
FuriHalRtcLogDevice furi_hal_rtc_get_log_device() {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
SystemReg* data = (SystemReg*)&data_reg;
return data->log_device;
}
void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate) {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
SystemReg* data = (SystemReg*)&data_reg;
data->log_baud_rate = baud_rate;
furi_hal_rtc_set_register(FuriHalRtcRegisterSystem, data_reg);
furi_hal_serial_control_set_logging_config(
furi_hal_rtc_log_devices[furi_hal_rtc_get_log_device()],
furi_hal_rtc_log_baud_rates[furi_hal_rtc_get_log_baud_rate()]);
}
FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate() {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
SystemReg* data = (SystemReg*)&data_reg;
return data->log_baud_rate;
}
void furi_hal_rtc_set_flag(FuriHalRtcFlag flag) {
uint32_t data_reg = furi_hal_rtc_get_register(FuriHalRtcRegisterSystem);
SystemReg* data = (SystemReg*)&data_reg;

View File

@@ -0,0 +1,341 @@
/**
* @file furi_hal_rtc.h
* Furi Hal RTC API
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
// Time
uint8_t hour; /**< Hour in 24H format: 0-23 */
uint8_t minute; /**< Minute: 0-59 */
uint8_t second; /**< Second: 0-59 */
// Date
uint8_t day; /**< Current day: 1-31 */
uint8_t month; /**< Current month: 1-12 */
uint16_t year; /**< Current year: 2000-2099 */
uint8_t weekday; /**< Current weekday: 1-7 */
} FuriHalRtcDateTime;
typedef enum {
FuriHalRtcFlagDebug = (1 << 0),
FuriHalRtcFlagStorageFormatInternal = (1 << 1),
FuriHalRtcFlagLock = (1 << 2),
FuriHalRtcFlagC2Update = (1 << 3),
FuriHalRtcFlagHandOrient = (1 << 4),
FuriHalRtcFlagLegacySleep = (1 << 5),
FuriHalRtcFlagStealthMode = (1 << 6),
FuriHalRtcFlagDetailedFilename = (1 << 7),
} FuriHalRtcFlag;
typedef enum {
FuriHalRtcBootModeNormal = 0, /**< Normal boot mode, default value */
FuriHalRtcBootModeDfu, /**< Boot to DFU (MCU bootloader by ST) */
FuriHalRtcBootModePreUpdate, /**< Boot to Update, pre update */
FuriHalRtcBootModeUpdate, /**< Boot to Update, main */
FuriHalRtcBootModePostUpdate, /**< Boot to Update, post update */
} FuriHalRtcBootMode;
typedef enum {
FuriHalRtcHeapTrackModeNone = 0, /**< Disable allocation tracking */
FuriHalRtcHeapTrackModeMain, /**< Enable allocation tracking for main application thread */
FuriHalRtcHeapTrackModeTree, /**< Enable allocation tracking for main and children application threads */
FuriHalRtcHeapTrackModeAll, /**< Enable allocation tracking for all threads */
} FuriHalRtcHeapTrackMode;
typedef enum {
FuriHalRtcRegisterHeader, /**< RTC structure header */
FuriHalRtcRegisterSystem, /**< Various system bits */
FuriHalRtcRegisterVersion, /**< Pointer to Version */
FuriHalRtcRegisterLfsFingerprint, /**< LFS geometry fingerprint */
FuriHalRtcRegisterFaultData, /**< Pointer to last fault message */
FuriHalRtcRegisterPinFails, /**< Failed pins count */
/* Index of FS directory entry corresponding to FW update to be applied */
FuriHalRtcRegisterUpdateFolderFSIndex,
FuriHalRtcRegisterMAX, /**< Service value, do not use */
} FuriHalRtcRegister;
typedef enum {
FuriHalRtcLocaleUnitsMetric = 0x0, /**< Metric measurement units */
FuriHalRtcLocaleUnitsImperial = 0x1, /**< Imperial measurement units */
} FuriHalRtcLocaleUnits;
typedef enum {
FuriHalRtcLocaleTimeFormat24h = 0x0, /**< 24-hour format */
FuriHalRtcLocaleTimeFormat12h = 0x1, /**< 12-hour format */
} FuriHalRtcLocaleTimeFormat;
typedef enum {
FuriHalRtcLocaleDateFormatDMY = 0x0, /**< Day/Month/Year */
FuriHalRtcLocaleDateFormatMDY = 0x1, /**< Month/Day/Year */
FuriHalRtcLocaleDateFormatYMD = 0x2, /**< Year/Month/Day */
} FuriHalRtcLocaleDateFormat;
typedef enum {
FuriHalRtcLogDeviceUsart = 0x0, /**< Default: USART */
FuriHalRtcLogDeviceLpuart = 0x1, /**< Default: LPUART */
FuriHalRtcLogDeviceReserved = 0x2, /**< Reserved for future use */
FuriHalRtcLogDeviceNone = 0x3, /**< None, disable serial logging */
} FuriHalRtcLogDevice;
typedef enum {
FuriHalRtcLogBaudRate230400 = 0x0, /**< 230400 baud */
FuriHalRtcLogBaudRate9600 = 0x1, /**< 9600 baud */
FuriHalRtcLogBaudRate38400 = 0x2, /**< 38400 baud */
FuriHalRtcLogBaudRate57600 = 0x3, /**< 57600 baud */
FuriHalRtcLogBaudRate115200 = 0x4, /**< 115200 baud */
FuriHalRtcLogBaudRate460800 = 0x5, /**< 460800 baud */
FuriHalRtcLogBaudRate921600 = 0x6, /**< 921600 baud */
FuriHalRtcLogBaudRate1843200 = 0x7, /**< 1843200 baud */
} FuriHalRtcLogBaudRate;
/** Early initialization */
void furi_hal_rtc_init_early(void);
/** Early de-initialization */
void furi_hal_rtc_deinit_early(void);
/** Initialize RTC subsystem */
void furi_hal_rtc_init(void);
/** Force sync shadow registers */
void furi_hal_rtc_sync_shadow(void);
/** Reset ALL RTC registers content */
void furi_hal_rtc_reset_registers();
/** Get RTC register content
*
* @param[in] reg The register identifier
*
* @return content of the register
*/
uint32_t furi_hal_rtc_get_register(FuriHalRtcRegister reg);
/** Set register content
*
* @param[in] reg The register identifier
* @param[in] value The value to store into register
*/
void furi_hal_rtc_set_register(FuriHalRtcRegister reg, uint32_t value);
/** Set Log Level value
*
* @param[in] level The level to store
*/
void furi_hal_rtc_set_log_level(uint8_t level);
/** Get Log Level value
*
* @return The Log Level value
*/
uint8_t furi_hal_rtc_get_log_level(void);
/** Set logging device
*
* @param[in] device The device
*/
void furi_hal_rtc_set_log_device(FuriHalRtcLogDevice device);
/** Get logging device
*
* @return The furi hal rtc log device.
*/
FuriHalRtcLogDevice furi_hal_rtc_get_log_device(void);
/** Set logging baud rate
*
* @param[in] baud_rate The baud rate
*/
void furi_hal_rtc_set_log_baud_rate(FuriHalRtcLogBaudRate baud_rate);
/** Get logging baud rate
*
* @return The furi hal rtc log baud rate.
*/
FuriHalRtcLogBaudRate furi_hal_rtc_get_log_baud_rate(void);
/** Set RTC Flag
*
* @param[in] flag The flag to set
*/
void furi_hal_rtc_set_flag(FuriHalRtcFlag flag);
/** Reset RTC Flag
*
* @param[in] flag The flag to reset
*/
void furi_hal_rtc_reset_flag(FuriHalRtcFlag flag);
/** Check if RTC Flag is set
*
* @param[in] flag The flag to check
*
* @return true if set
*/
bool furi_hal_rtc_is_flag_set(FuriHalRtcFlag flag);
/** Set RTC boot mode
*
* @param[in] mode The mode to set
*/
void furi_hal_rtc_set_boot_mode(FuriHalRtcBootMode mode);
/** Get RTC boot mode
*
* @return The RTC boot mode.
*/
FuriHalRtcBootMode furi_hal_rtc_get_boot_mode(void);
/** Set Heap Track mode
*
* @param[in] mode The mode to set
*/
void furi_hal_rtc_set_heap_track_mode(FuriHalRtcHeapTrackMode mode);
/** Get RTC Heap Track mode
*
* @return The RTC heap track mode.
*/
FuriHalRtcHeapTrackMode furi_hal_rtc_get_heap_track_mode(void);
/** Set locale units
*
* @param[in] mode The RTC Locale Units
*/
void furi_hal_rtc_set_locale_units(FuriHalRtcLocaleUnits value);
/** Get RTC Locale Units
*
* @return The RTC Locale Units.
*/
FuriHalRtcLocaleUnits furi_hal_rtc_get_locale_units(void);
/** Set RTC Locale Time Format
*
* @param[in] value The RTC Locale Time Format
*/
void furi_hal_rtc_set_locale_timeformat(FuriHalRtcLocaleTimeFormat value);
/** Get RTC Locale Time Format
*
* @return The RTC Locale Time Format.
*/
FuriHalRtcLocaleTimeFormat furi_hal_rtc_get_locale_timeformat(void);
/** Set RTC Locale Date Format
*
* @param[in] value The RTC Locale Date Format
*/
void furi_hal_rtc_set_locale_dateformat(FuriHalRtcLocaleDateFormat value);
/** Get RTC Locale Date Format
*
* @return The RTC Locale Date Format
*/
FuriHalRtcLocaleDateFormat furi_hal_rtc_get_locale_dateformat(void);
/** Set RTC Date Time
*
* @param datetime The date time to set
*/
void furi_hal_rtc_set_datetime(FuriHalRtcDateTime* datetime);
/** Get RTC Date Time
*
* @param datetime The datetime
*/
void furi_hal_rtc_get_datetime(FuriHalRtcDateTime* datetime);
/** Validate Date Time
*
* @param datetime The datetime to validate
*
* @return { description_of_the_return_value }
*/
bool furi_hal_rtc_validate_datetime(FuriHalRtcDateTime* datetime);
/** Set RTC Fault Data
*
* @param[in] value The value
*/
void furi_hal_rtc_set_fault_data(uint32_t value);
/** Get RTC Fault Data
*
* @return RTC Fault Data value
*/
uint32_t furi_hal_rtc_get_fault_data(void);
/** Set Pin Fails count
*
* @param[in] value The Pin Fails count
*/
void furi_hal_rtc_set_pin_fails(uint32_t value);
/** Get Pin Fails count
*
* @return Pin Fails Count
*/
uint32_t furi_hal_rtc_get_pin_fails(void);
/** Get UNIX Timestamp
*
* @return Unix Timestamp in seconds from UNIX epoch start
*/
uint32_t furi_hal_rtc_get_timestamp(void);
/** Convert DateTime to UNIX timestamp
*
* @warning Mind timezone when perform conversion
*
* @param datetime The datetime (UTC)
*
* @return UNIX Timestamp in seconds from UNIX epoch start
*/
uint32_t furi_hal_rtc_datetime_to_timestamp(FuriHalRtcDateTime* datetime);
/** Convert UNIX timestamp to DateTime
*
* @warning Mind timezone when perform conversion
*
* @param[in] timestamp UNIX Timestamp in seconds from UNIX epoch start
* @param[out] datetime The datetime (UTC)
*/
void furi_hal_rtc_timestamp_to_datetime(uint32_t timestamp, FuriHalRtcDateTime* datetime);
/** Gets the number of days in the year according to the Gregorian calendar.
*
* @param year Input year.
*
* @return number of days in `year`.
*/
uint16_t furi_hal_rtc_get_days_per_year(uint16_t year);
/** Check if a year a leap year in the Gregorian calendar.
*
* @param year Input year.
*
* @return true if `year` is a leap year.
*/
bool furi_hal_rtc_is_leap_year(uint16_t year);
/** Get the number of days in the month.
*
* @param leap_year true to calculate based on leap years
* @param month month to check, where 1 = January
* @return the number of days in the month
*/
uint8_t furi_hal_rtc_get_days_per_month(bool leap_year, uint8_t month);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,838 @@
#include <furi_hal_serial.h>
#include "furi_hal_serial_types_i.h"
#include <stdbool.h>
#include <stm32wbxx_ll_lpuart.h>
#include <stm32wbxx_ll_usart.h>
#include <stm32wbxx_ll_rcc.h>
#include <stm32wbxx_ll_dma.h>
#include <furi_hal_resources.h>
#include <furi_hal_interrupt.h>
#include <furi_hal_bus.h>
#include <furi.h>
#define FURI_HAL_SERIAL_USART_OVERSAMPLING LL_USART_OVERSAMPLING_16
#define FURI_HAL_SERIAL_USART_DMA_INSTANCE (DMA1)
#define FURI_HAL_SERIAL_USART_DMA_CHANNEL (LL_DMA_CHANNEL_6)
#define FURI_HAL_SERIAL_LPUART_DMA_INSTANCE (DMA1)
#define FURI_HAL_SERIAL_LPUART_DMA_CHANNEL (LL_DMA_CHANNEL_7)
typedef struct {
uint8_t* buffer_rx_ptr;
size_t buffer_rx_index_write;
size_t buffer_rx_index_read;
bool enabled;
FuriHalSerialHandle* handle;
FuriHalSerialAsyncRxCallback rx_byte_callback;
FuriHalSerialDmaRxCallback rx_dma_callback;
void* context;
} FuriHalSerial;
static FuriHalSerial furi_hal_serial[FuriHalSerialIdMax] = {0};
static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch);
static void furi_hal_serial_async_rx_configure(
FuriHalSerialHandle* handle,
FuriHalSerialAsyncRxCallback callback,
void* context);
static void furi_hal_serial_usart_irq_callback(void* context) {
UNUSED(context);
FuriHalSerialRxEvent event = 0;
// Notification flags
if(USART1->ISR & USART_ISR_RXNE_RXFNE) {
event |= FuriHalSerialRxEventData;
}
if(USART1->ISR & USART_ISR_IDLE) {
USART1->ICR = USART_ICR_IDLECF;
event |= FuriHalSerialRxEventIdle;
}
// Error flags
if(USART1->ISR & USART_ISR_ORE) {
USART1->ICR = USART_ICR_ORECF;
event |= FuriHalSerialRxEventOverrunError;
}
if(USART1->ISR & USART_ISR_NE) {
USART1->ICR = USART_ICR_NECF;
event |= FuriHalSerialRxEventNoiseError;
}
if(USART1->ISR & USART_ISR_FE) {
USART1->ICR = USART_ICR_FECF;
event |= FuriHalSerialRxEventFrameError;
}
if(USART1->ISR & USART_ISR_PE) {
USART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
}
if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) {
if(furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback) {
furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback(
furi_hal_serial[FuriHalSerialIdUsart].handle,
event,
furi_hal_serial[FuriHalSerialIdUsart].context);
}
} else {
if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdUsart].handle,
event,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
furi_hal_serial[FuriHalSerialIdUsart].context);
}
}
}
static void furi_hal_serial_usart_dma_rx_isr(void* context) {
UNUSED(context);
#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6
if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) {
LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write =
FURI_HAL_SERIAL_DMA_BUFFER_SIZE -
LL_DMA_GetDataLength(
FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
if((furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read >
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write) ||
(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read <
FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) {
if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdUsart].handle,
FuriHalSerialRxEventData,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
furi_hal_serial[FuriHalSerialIdUsart].context);
}
}
} else if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE)) {
LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read <
FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) {
if(furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdUsart].handle,
FuriHalSerialRxEventData,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdUsart),
furi_hal_serial[FuriHalSerialIdUsart].context);
}
}
}
#else
#error Update this code. Would you kindly?
#endif
}
static void furi_hal_serial_usart_init_dma_rx(void) {
/* USART1_RX_DMA Init */
furi_check(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL);
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_write = 0;
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_index_read = 0;
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
LL_DMA_SetMemoryAddress(
FURI_HAL_SERIAL_USART_DMA_INSTANCE,
FURI_HAL_SERIAL_USART_DMA_CHANNEL,
(uint32_t)furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr);
LL_DMA_SetPeriphAddress(
FURI_HAL_SERIAL_USART_DMA_INSTANCE,
FURI_HAL_SERIAL_USART_DMA_CHANNEL,
(uint32_t) & (USART1->RDR));
LL_DMA_ConfigTransfer(
FURI_HAL_SERIAL_USART_DMA_INSTANCE,
FURI_HAL_SERIAL_USART_DMA_CHANNEL,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE |
LL_DMA_PRIORITY_HIGH);
LL_DMA_SetDataLength(
FURI_HAL_SERIAL_USART_DMA_INSTANCE,
FURI_HAL_SERIAL_USART_DMA_CHANNEL,
FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
LL_DMA_SetPeriphRequest(
FURI_HAL_SERIAL_USART_DMA_INSTANCE,
FURI_HAL_SERIAL_USART_DMA_CHANNEL,
LL_DMAMUX_REQ_USART1_RX);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, furi_hal_serial_usart_dma_rx_isr, NULL);
#if FURI_HAL_SERIAL_USART_DMA_CHANNEL == LL_DMA_CHANNEL_6
if(LL_DMA_IsActiveFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
if(LL_DMA_IsActiveFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
if(LL_DMA_IsActiveFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE))
LL_DMA_ClearFlag_TE6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
#else
#error Update this code. Would you kindly?
#endif
LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_DMA_EnableChannel(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_USART_EnableDMAReq_RX(USART1);
LL_USART_EnableIT_IDLE(USART1);
}
static void furi_hal_serial_usart_deinit_dma_rx(void) {
if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr != NULL) {
LL_DMA_DisableChannel(
FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_USART_DisableDMAReq_RX(USART1);
LL_USART_DisableIT_IDLE(USART1);
LL_DMA_DisableIT_TC(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_DMA_DisableIT_HT(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
LL_DMA_ClearFlag_TC6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
LL_DMA_ClearFlag_HT6(FURI_HAL_SERIAL_USART_DMA_INSTANCE);
LL_DMA_DeInit(FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch6, NULL, NULL);
free(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr);
furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr = NULL;
}
}
static void furi_hal_serial_usart_init(FuriHalSerialHandle* handle, uint32_t baud) {
furi_hal_bus_enable(FuriHalBusUSART1);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
furi_hal_gpio_init_ex(
&gpio_usart_tx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn7USART1);
furi_hal_gpio_init_ex(
&gpio_usart_rx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn7USART1);
LL_USART_InitTypeDef USART_InitStruct;
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = baud;
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_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = FURI_HAL_SERIAL_USART_OVERSAMPLING;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_EnableFIFO(USART1);
LL_USART_ConfigAsyncMode(USART1);
LL_USART_Enable(USART1);
while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
;
furi_hal_serial_set_br(handle, baud);
LL_USART_DisableIT_ERROR(USART1);
furi_hal_serial[handle->id].enabled = true;
}
static void furi_hal_serial_lpuart_irq_callback(void* context) {
UNUSED(context);
FuriHalSerialRxEvent event = 0;
// Notification flags
if(LPUART1->ISR & USART_ISR_RXNE_RXFNE) {
event |= FuriHalSerialRxEventData;
}
if(LPUART1->ISR & USART_ISR_IDLE) {
LPUART1->ICR = USART_ICR_IDLECF;
event |= FuriHalSerialRxEventIdle;
}
// Error flags
if(LPUART1->ISR & USART_ISR_ORE) {
LPUART1->ICR = USART_ICR_ORECF;
event |= FuriHalSerialRxEventOverrunError;
}
if(LPUART1->ISR & USART_ISR_NE) {
LPUART1->ICR = USART_ICR_NECF;
event |= FuriHalSerialRxEventNoiseError;
}
if(LPUART1->ISR & USART_ISR_FE) {
LPUART1->ICR = USART_ICR_FECF;
event |= FuriHalSerialRxEventFrameError;
}
if(LPUART1->ISR & USART_ISR_PE) {
LPUART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
}
if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) {
if(furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback) {
furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback(
furi_hal_serial[FuriHalSerialIdLpuart].handle,
event,
furi_hal_serial[FuriHalSerialIdLpuart].context);
}
} else {
if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdLpuart].handle,
event,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
furi_hal_serial[FuriHalSerialIdLpuart].context);
}
}
}
static void furi_hal_serial_lpuart_dma_rx_isr(void* context) {
UNUSED(context);
#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7
if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) {
LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write =
FURI_HAL_SERIAL_DMA_BUFFER_SIZE -
LL_DMA_GetDataLength(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
if((furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read >
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write) ||
(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read <
FURI_HAL_SERIAL_DMA_BUFFER_SIZE / 4)) {
if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdLpuart].handle,
FuriHalSerialRxEventData,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
furi_hal_serial[FuriHalSerialIdLpuart].context);
}
}
} else if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE)) {
LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read <
FURI_HAL_SERIAL_DMA_BUFFER_SIZE * 3 / 4) {
if(furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback) {
furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback(
furi_hal_serial[FuriHalSerialIdLpuart].handle,
FuriHalSerialRxEventData,
furi_hal_serial_dma_bytes_available(FuriHalSerialIdLpuart),
furi_hal_serial[FuriHalSerialIdLpuart].context);
}
}
}
#else
#error Update this code. Would you kindly?
#endif
}
static void furi_hal_serial_lpuart_init_dma_rx(void) {
/* LPUART1_RX_DMA Init */
furi_check(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL);
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_write = 0;
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_index_read = 0;
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = malloc(FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
LL_DMA_SetMemoryAddress(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
(uint32_t)furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr);
LL_DMA_SetPeriphAddress(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
(uint32_t) & (LPUART1->RDR));
LL_DMA_ConfigTransfer(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
LL_DMA_DIRECTION_PERIPH_TO_MEMORY | LL_DMA_MODE_CIRCULAR | LL_DMA_PERIPH_NOINCREMENT |
LL_DMA_MEMORY_INCREMENT | LL_DMA_PDATAALIGN_BYTE | LL_DMA_MDATAALIGN_BYTE |
LL_DMA_PRIORITY_HIGH);
LL_DMA_SetDataLength(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
FURI_HAL_SERIAL_DMA_BUFFER_SIZE);
LL_DMA_SetPeriphRequest(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE,
FURI_HAL_SERIAL_LPUART_DMA_CHANNEL,
LL_DMAMUX_REQ_LPUART1_RX);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, furi_hal_serial_lpuart_dma_rx_isr, NULL);
#if FURI_HAL_SERIAL_LPUART_DMA_CHANNEL == LL_DMA_CHANNEL_7
if(LL_DMA_IsActiveFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
if(LL_DMA_IsActiveFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
if(LL_DMA_IsActiveFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE))
LL_DMA_ClearFlag_TE7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
#else
#error Update this code. Would you kindly?
#endif
LL_DMA_EnableIT_TC(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_DMA_EnableIT_HT(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_DMA_EnableChannel(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_USART_EnableDMAReq_RX(LPUART1);
LL_USART_EnableIT_IDLE(LPUART1);
}
static void furi_hal_serial_lpuart_deinit_dma_rx(void) {
if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr != NULL) {
LL_DMA_DisableChannel(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_USART_DisableDMAReq_RX(LPUART1);
LL_USART_DisableIT_IDLE(LPUART1);
LL_DMA_DisableIT_TC(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_DMA_DisableIT_HT(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
LL_DMA_ClearFlag_TC7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
LL_DMA_ClearFlag_HT7(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE);
LL_DMA_DeInit(FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
furi_hal_interrupt_set_isr(FuriHalInterruptIdDma1Ch7, NULL, NULL);
free(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr);
furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr = NULL;
}
}
static void furi_hal_serial_lpuart_init(FuriHalSerialHandle* handle, uint32_t baud) {
furi_hal_bus_enable(FuriHalBusLPUART1);
LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
furi_hal_gpio_init_ex(
&gpio_ext_pc0,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn8LPUART1);
furi_hal_gpio_init_ex(
&gpio_ext_pc1,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn8LPUART1);
LL_LPUART_InitTypeDef LPUART_InitStruct;
LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
LPUART_InitStruct.BaudRate = baud;
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
LL_LPUART_EnableFIFO(LPUART1);
LL_LPUART_Enable(LPUART1);
while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1))
;
furi_hal_serial_set_br(handle, baud);
LL_LPUART_DisableIT_ERROR(LPUART1);
furi_hal_serial[handle->id].enabled = true;
}
void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud) {
furi_check(handle);
if(handle->id == FuriHalSerialIdLpuart) {
furi_hal_serial_lpuart_init(handle, baud);
} else if(handle->id == FuriHalSerialIdUsart) {
furi_hal_serial_usart_init(handle, baud);
}
}
static uint32_t furi_hal_serial_get_prescaler(FuriHalSerialHandle* handle, uint32_t baud) {
uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
uint32_t divisor = (uartclk / baud);
uint32_t prescaler = 0;
if(handle->id == FuriHalSerialIdUsart) {
if(FURI_HAL_SERIAL_USART_OVERSAMPLING == LL_USART_OVERSAMPLING_16) {
divisor = (divisor / 16) >> 12;
} else {
divisor = (divisor / 8) >> 12;
}
if(divisor < 1) {
prescaler = LL_USART_PRESCALER_DIV1;
} else if(divisor < 2) {
prescaler = LL_USART_PRESCALER_DIV2;
} else if(divisor < 4) {
prescaler = LL_USART_PRESCALER_DIV4;
} else if(divisor < 6) {
prescaler = LL_USART_PRESCALER_DIV6;
} else if(divisor < 8) {
prescaler = LL_USART_PRESCALER_DIV8;
} else if(divisor < 10) {
prescaler = LL_USART_PRESCALER_DIV10;
} else if(divisor < 12) {
prescaler = LL_USART_PRESCALER_DIV12;
} else if(divisor < 16) {
prescaler = LL_USART_PRESCALER_DIV16;
} else if(divisor < 32) {
prescaler = LL_USART_PRESCALER_DIV32;
} else if(divisor < 64) {
prescaler = LL_USART_PRESCALER_DIV64;
} else if(divisor < 128) {
prescaler = LL_USART_PRESCALER_DIV128;
} else {
prescaler = LL_USART_PRESCALER_DIV256;
}
} else if(handle->id == FuriHalSerialIdLpuart) {
divisor >>= 12;
if(divisor < 1) {
prescaler = LL_LPUART_PRESCALER_DIV1;
} else if(divisor < 2) {
prescaler = LL_LPUART_PRESCALER_DIV2;
} else if(divisor < 4) {
prescaler = LL_LPUART_PRESCALER_DIV4;
} else if(divisor < 6) {
prescaler = LL_LPUART_PRESCALER_DIV6;
} else if(divisor < 8) {
prescaler = LL_LPUART_PRESCALER_DIV8;
} else if(divisor < 10) {
prescaler = LL_LPUART_PRESCALER_DIV10;
} else if(divisor < 12) {
prescaler = LL_LPUART_PRESCALER_DIV12;
} else if(divisor < 16) {
prescaler = LL_LPUART_PRESCALER_DIV16;
} else if(divisor < 32) {
prescaler = LL_LPUART_PRESCALER_DIV32;
} else if(divisor < 64) {
prescaler = LL_LPUART_PRESCALER_DIV64;
} else if(divisor < 128) {
prescaler = LL_LPUART_PRESCALER_DIV128;
} else {
prescaler = LL_LPUART_PRESCALER_DIV256;
}
}
return prescaler;
}
void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) {
furi_check(handle);
uint32_t prescaler = furi_hal_serial_get_prescaler(handle, baud);
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while(!LL_USART_IsActiveFlag_TC(USART1))
;
LL_USART_Disable(USART1);
uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
LL_USART_SetPrescaler(USART1, prescaler);
LL_USART_SetBaudRate(
USART1, uartclk, prescaler, FURI_HAL_SERIAL_USART_OVERSAMPLING, baud);
LL_USART_Enable(USART1);
}
} else if(handle->id == FuriHalSerialIdLpuart) {
if(LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
LL_LPUART_Disable(LPUART1);
uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE);
LL_LPUART_SetPrescaler(LPUART1, prescaler);
LL_LPUART_SetBaudRate(LPUART1, uartclk, prescaler, baud);
LL_LPUART_Enable(LPUART1);
}
}
}
void furi_hal_serial_deinit(FuriHalSerialHandle* handle) {
furi_check(handle);
furi_hal_serial_async_rx_configure(handle, NULL, NULL);
if(handle->id == FuriHalSerialIdUsart) {
if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) {
furi_hal_bus_disable(FuriHalBusUSART1);
}
if(LL_USART_IsEnabled(USART1)) {
LL_USART_Disable(USART1);
}
furi_hal_serial_usart_deinit_dma_rx();
furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
} else if(handle->id == FuriHalSerialIdLpuart) {
if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) {
furi_hal_bus_disable(FuriHalBusLPUART1);
}
if(LL_LPUART_IsEnabled(LPUART1)) {
LL_LPUART_Disable(LPUART1);
}
furi_hal_serial_lpuart_deinit_dma_rx();
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
} else {
furi_crash();
}
furi_hal_serial[handle->id].enabled = false;
}
void furi_hal_serial_suspend(FuriHalSerialHandle* handle) {
furi_check(handle);
if(handle->id == FuriHalSerialIdLpuart && LL_LPUART_IsEnabled(LPUART1)) {
LL_LPUART_Disable(LPUART1);
} else if(handle->id == FuriHalSerialIdUsart && LL_USART_IsEnabled(USART1)) {
LL_USART_Disable(USART1);
}
furi_hal_serial[handle->id].enabled = false;
}
void furi_hal_serial_resume(FuriHalSerialHandle* handle) {
furi_check(handle);
if(!furi_hal_serial[handle->id].enabled) {
if(handle->id == FuriHalSerialIdLpuart) {
LL_LPUART_Enable(LPUART1);
} else if(handle->id == FuriHalSerialIdUsart) {
LL_USART_Enable(USART1);
}
furi_hal_serial[handle->id].enabled = true;
}
}
void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size) {
furi_check(handle);
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabled(USART1) == 0) return;
while(buffer_size > 0) {
while(!LL_USART_IsActiveFlag_TXE(USART1))
;
LL_USART_TransmitData8(USART1, *buffer);
buffer++;
buffer_size--;
}
} else if(handle->id == FuriHalSerialIdLpuart) {
if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
while(buffer_size > 0) {
while(!LL_LPUART_IsActiveFlag_TXE(LPUART1))
;
LL_LPUART_TransmitData8(LPUART1, *buffer);
buffer++;
buffer_size--;
}
}
}
void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle) {
furi_check(handle);
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabled(USART1) == 0) return;
while(!LL_USART_IsActiveFlag_TC(USART1))
;
} else if(handle->id == FuriHalSerialIdLpuart) {
if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
}
}
static void furi_hal_serial_event_init(FuriHalSerialHandle* handle, bool report_errors) {
if(handle->id == FuriHalSerialIdUsart) {
LL_USART_EnableIT_IDLE(USART1);
} else if(handle->id == FuriHalSerialIdLpuart) {
LL_LPUART_EnableIT_IDLE(LPUART1);
}
if(report_errors) {
if(handle->id == FuriHalSerialIdUsart) {
LL_USART_EnableIT_ERROR(USART1);
} else if(handle->id == FuriHalSerialIdLpuart) {
LL_LPUART_EnableIT_ERROR(LPUART1);
}
}
}
static void furi_hal_serial_event_deinit(FuriHalSerialHandle* handle) {
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabledIT_IDLE(USART1)) LL_USART_DisableIT_IDLE(USART1);
if(LL_USART_IsEnabledIT_ERROR(USART1)) LL_USART_DisableIT_ERROR(USART1);
} else if(handle->id == FuriHalSerialIdLpuart) {
if(LL_LPUART_IsEnabledIT_IDLE(LPUART1)) LL_LPUART_DisableIT_IDLE(LPUART1);
if(LL_LPUART_IsEnabledIT_ERROR(LPUART1)) LL_LPUART_DisableIT_ERROR(LPUART1);
}
}
static void furi_hal_serial_async_rx_configure(
FuriHalSerialHandle* handle,
FuriHalSerialAsyncRxCallback callback,
void* context) {
if(handle->id == FuriHalSerialIdUsart) {
if(callback) {
furi_hal_serial_usart_deinit_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
LL_USART_EnableIT_RXNE_RXFNE(USART1);
} else {
furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
furi_hal_serial_usart_deinit_dma_rx();
LL_USART_DisableIT_RXNE_RXFNE(USART1);
}
} else if(handle->id == FuriHalSerialIdLpuart) {
if(callback) {
furi_hal_serial_lpuart_deinit_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
} else {
furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
furi_hal_serial_lpuart_deinit_dma_rx();
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
}
}
furi_hal_serial[handle->id].rx_byte_callback = callback;
furi_hal_serial[handle->id].handle = handle;
furi_hal_serial[handle->id].rx_dma_callback = NULL;
furi_hal_serial[handle->id].context = context;
}
void furi_hal_serial_async_rx_start(
FuriHalSerialHandle* handle,
FuriHalSerialAsyncRxCallback callback,
void* context,
bool report_errors) {
furi_check(handle);
furi_check(callback);
furi_hal_serial_event_init(handle, report_errors);
furi_hal_serial_async_rx_configure(handle, callback, context);
// Assign different functions to different UARTs
furi_check(
furi_hal_serial[FuriHalSerialIdUsart].rx_byte_callback !=
furi_hal_serial[FuriHalSerialIdLpuart].rx_byte_callback);
}
void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle) {
furi_check(handle);
furi_hal_serial_event_deinit(handle);
furi_hal_serial_async_rx_configure(handle, NULL, NULL);
}
uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle) {
furi_check(FURI_IS_IRQ_MODE());
furi_assert(handle->id < FuriHalSerialIdMax);
if(handle->id == FuriHalSerialIdUsart) {
return LL_USART_ReceiveData8(USART1);
}
return LL_LPUART_ReceiveData8(LPUART1);
}
static size_t furi_hal_serial_dma_bytes_available(FuriHalSerialId ch) {
size_t dma_remain = 0;
if(ch == FuriHalSerialIdUsart) {
dma_remain = LL_DMA_GetDataLength(
FURI_HAL_SERIAL_USART_DMA_INSTANCE, FURI_HAL_SERIAL_USART_DMA_CHANNEL);
} else if(ch == FuriHalSerialIdLpuart) {
dma_remain = LL_DMA_GetDataLength(
FURI_HAL_SERIAL_LPUART_DMA_INSTANCE, FURI_HAL_SERIAL_LPUART_DMA_CHANNEL);
} else {
furi_crash();
}
furi_hal_serial[ch].buffer_rx_index_write = FURI_HAL_SERIAL_DMA_BUFFER_SIZE - dma_remain;
if(furi_hal_serial[ch].buffer_rx_index_write >= furi_hal_serial[ch].buffer_rx_index_read) {
return furi_hal_serial[ch].buffer_rx_index_write -
furi_hal_serial[ch].buffer_rx_index_read;
} else {
return FURI_HAL_SERIAL_DMA_BUFFER_SIZE - furi_hal_serial[ch].buffer_rx_index_read +
furi_hal_serial[ch].buffer_rx_index_write;
}
}
static uint8_t furi_hal_serial_dma_rx_read_byte(FuriHalSerialHandle* handle) {
uint8_t data = 0;
data =
furi_hal_serial[handle->id].buffer_rx_ptr[furi_hal_serial[handle->id].buffer_rx_index_read];
furi_hal_serial[handle->id].buffer_rx_index_read++;
if(furi_hal_serial[handle->id].buffer_rx_index_read >= FURI_HAL_SERIAL_DMA_BUFFER_SIZE) {
furi_hal_serial[handle->id].buffer_rx_index_read = 0;
}
return data;
}
size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len) {
furi_check(FURI_IS_IRQ_MODE());
furi_assert(furi_hal_serial[handle->id].buffer_rx_ptr != NULL);
size_t i = 0;
size_t available = furi_hal_serial_dma_bytes_available(handle->id);
if(available < len) {
len = available;
}
for(i = 0; i < len; i++) {
data[i] = furi_hal_serial_dma_rx_read_byte(handle);
}
return i;
}
static void furi_hal_serial_dma_configure(
FuriHalSerialHandle* handle,
FuriHalSerialDmaRxCallback callback,
void* context) {
furi_check(handle);
if(handle->id == FuriHalSerialIdUsart) {
if(callback) {
furi_hal_serial_usart_init_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL);
} else {
LL_USART_DisableIT_RXNE_RXFNE(USART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL);
furi_hal_serial_usart_deinit_dma_rx();
}
} else if(handle->id == FuriHalSerialIdLpuart) {
if(callback) {
furi_hal_serial_lpuart_init_dma_rx();
furi_hal_interrupt_set_isr(
FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL);
} else {
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL);
furi_hal_serial_lpuart_deinit_dma_rx();
}
}
furi_hal_serial[handle->id].rx_byte_callback = NULL;
furi_hal_serial[handle->id].handle = handle;
furi_hal_serial[handle->id].rx_dma_callback = callback;
furi_hal_serial[handle->id].context = context;
}
void furi_hal_serial_dma_rx_start(
FuriHalSerialHandle* handle,
FuriHalSerialDmaRxCallback callback,
void* context,
bool report_errors) {
furi_check(handle);
furi_check(callback);
furi_hal_serial_event_init(handle, report_errors);
furi_hal_serial_dma_configure(handle, callback, context);
// Assign different functions to different UARTs
furi_check(
furi_hal_serial[FuriHalSerialIdUsart].rx_dma_callback !=
furi_hal_serial[FuriHalSerialIdLpuart].rx_dma_callback);
}
void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle) {
furi_check(handle);
furi_hal_serial_event_deinit(handle);
furi_hal_serial_dma_configure(handle, NULL, NULL);
}

View File

@@ -0,0 +1,189 @@
/**
* @file furi_hal_serial.h
*
* Serial HAL API
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "furi_hal_serial_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Initialize Serial
*
* Configures GPIO, configures and enables transceiver.
*
* @param handle Serial handle
* @param baud baud rate
*/
void furi_hal_serial_init(FuriHalSerialHandle* handle, uint32_t baud);
/** De-initialize Serial
*
* Configures GPIO to analog, clears callback and callback context, disables
* hardware
*
* @param handle Serial handle
*/
void furi_hal_serial_deinit(FuriHalSerialHandle* handle);
/** Suspend operation
*
* Suspend hardware, settings and callbacks are preserved
*
* @param handle Serial handle
*/
void furi_hal_serial_suspend(FuriHalSerialHandle* handle);
/** Resume operation
*
* Resumes hardware from suspended state
*
* @param handle Serial handle
*/
void furi_hal_serial_resume(FuriHalSerialHandle* handle);
/** Changes baud rate
*
* @param handle Serial handle
* @param baud baud rate
*/
void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud);
/** Transmits data in semi-blocking mode
*
* Fills transmission pipe with data, returns as soon as all bytes from buffer
* are in the pipe.
*
* Real transmission will be completed later. Use
* `furi_hal_serial_tx_wait_complete` to wait for completion if you need it.
*
* @param handle Serial handle
* @param buffer data
* @param buffer_size data size (in bytes)
*/
void furi_hal_serial_tx(FuriHalSerialHandle* handle, const uint8_t* buffer, size_t buffer_size);
/** Wait until transmission is completed
*
* Ensures that all data has been sent.
*
* @param handle Serial handle
*/
void furi_hal_serial_tx_wait_complete(FuriHalSerialHandle* handle);
/** Serial RX events */
typedef enum {
FuriHalSerialRxEventData = (1 << 0), /**< Data: new data available */
FuriHalSerialRxEventIdle = (1 << 1), /**< Idle: bus idle detected */
FuriHalSerialRxEventFrameError = (1 << 2), /**< Framing Error: incorrect frame detected */
FuriHalSerialRxEventNoiseError = (1 << 3), /**< Noise Error: noise on the line detected */
FuriHalSerialRxEventOverrunError = (1 << 4), /**< Overrun Error: no space for received data */
} FuriHalSerialRxEvent;
/** Receive callback
*
* @warning Callback will be called in interrupt context, ensure thread
* safety on your side.
* @param handle Serial handle
* @param event FuriHalSerialRxEvent
* @param context Callback context provided earlier
*/
typedef void (*FuriHalSerialAsyncRxCallback)(
FuriHalSerialHandle* handle,
FuriHalSerialRxEvent event,
void* context);
/** Start and sets Serial Receive callback
*
* @warning Callback will be called in interrupt context, ensure thread
* safety on your side
*
* @param handle Serial handle
* @param callback callback pointer
* @param context callback context
* @param[in] report_errors report RX error
*/
void furi_hal_serial_async_rx_start(
FuriHalSerialHandle* handle,
FuriHalSerialAsyncRxCallback callback,
void* context,
bool report_errors);
/** Stop Serial Receive
*
* @param handle Serial handle
*/
void furi_hal_serial_async_rx_stop(FuriHalSerialHandle* handle);
/** Get data Serial receive
*
* @warning This function must be called only from the callback
* FuriHalSerialAsyncRxCallback
*
* @param handle Serial handle
*
* @return data
*/
uint8_t furi_hal_serial_async_rx(FuriHalSerialHandle* handle);
/* DMA based Serial API */
#define FURI_HAL_SERIAL_DMA_BUFFER_SIZE (256u)
/** Receive DMA callback
*
* @warning DMA Callback will be called in interrupt context, ensure thread
* safety on your side.
*
* @param handle Serial handle
* @param event FuriHalSerialDmaRxEvent
* @param data_len Received data
* @param context Callback context provided earlier
*/
typedef void (*FuriHalSerialDmaRxCallback)(
FuriHalSerialHandle* handle,
FuriHalSerialRxEvent event,
size_t data_len,
void* context);
/** Start and sets Serial event callback receive DMA
*
* @param handle Serial handle
* @param callback callback pointer
* @param context callback context
* @param[in] report_errors report RX error
*/
void furi_hal_serial_dma_rx_start(
FuriHalSerialHandle* handle,
FuriHalSerialDmaRxCallback callback,
void* context,
bool report_errors);
/** Stop Serial receive DMA
*
* @param handle Serial handle
*/
void furi_hal_serial_dma_rx_stop(FuriHalSerialHandle* handle);
/** Get data Serial receive DMA
*
* @warning This function must be called only from the callback
* FuriHalSerialDmaRxCallback
*
* @param handle Serial handle
* @param data pointer to data buffer
* @param len get data size (in bytes)
*
* @return size actual data receive (in bytes)
*/
size_t furi_hal_serial_dma_rx(FuriHalSerialHandle* handle, uint8_t* data, size_t len);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,233 @@
#include "furi_hal_serial_control.h"
#include "furi_hal_serial_types_i.h"
#include "furi_hal_serial.h"
#include <furi.h>
#include <toolbox/api_lock.h>
#define TAG "FuriHalSerialControl"
typedef enum {
FuriHalSerialControlMessageTypeStop,
FuriHalSerialControlMessageTypeSuspend,
FuriHalSerialControlMessageTypeResume,
FuriHalSerialControlMessageTypeAcquire,
FuriHalSerialControlMessageTypeRelease,
FuriHalSerialControlMessageTypeLogging,
} FuriHalSerialControlMessageType;
typedef struct {
FuriHalSerialControlMessageType type;
FuriApiLock api_lock;
void* input;
void* output;
} FuriHalSerialControlMessage;
typedef struct {
const FuriHalSerialId id;
const uint32_t baud_rate;
} FuriHalSerialControlMessageInputLogging;
typedef struct {
FuriHalSerialHandle handles[FuriHalSerialIdMax];
FuriMessageQueue* queue;
FuriThread* thread;
// Logging
FuriHalSerialId log_config_serial_id;
uint32_t log_config_serial_baud_rate;
FuriLogHandler log_handler;
FuriHalSerialHandle* log_serial;
} FuriHalSerialControl;
FuriHalSerialControl* furi_hal_serial_control = NULL;
static void furi_hal_serial_control_log_callback(const uint8_t* data, size_t size, void* context) {
FuriHalSerialHandle* handle = context;
furi_hal_serial_tx(handle, data, size);
}
static void furi_hal_serial_control_log_set_handle(FuriHalSerialHandle* handle) {
if(furi_hal_serial_control->log_serial) {
furi_log_remove_handler(furi_hal_serial_control->log_handler);
furi_hal_serial_deinit(furi_hal_serial_control->log_serial);
furi_hal_serial_control->log_serial = NULL;
}
if(handle) {
furi_hal_serial_control->log_serial = handle;
furi_hal_serial_init(
furi_hal_serial_control->log_serial,
furi_hal_serial_control->log_config_serial_baud_rate);
furi_hal_serial_control->log_handler.callback = furi_hal_serial_control_log_callback;
furi_hal_serial_control->log_handler.context = furi_hal_serial_control->log_serial;
furi_log_add_handler(furi_hal_serial_control->log_handler);
}
}
static int32_t furi_hal_serial_control_thread(void* args) {
UNUSED(args);
bool should_continue = true;
while(should_continue || furi_message_queue_get_count(furi_hal_serial_control->queue) > 0) {
FuriHalSerialControlMessage message = {0};
FuriStatus status =
furi_message_queue_get(furi_hal_serial_control->queue, &message, FuriWaitForever);
furi_check(status == FuriStatusOk);
if(message.type == FuriHalSerialControlMessageTypeStop) {
should_continue = false;
} else if(message.type == FuriHalSerialControlMessageTypeSuspend) {
for(size_t i = 0; i < FuriHalSerialIdMax; i++) {
furi_hal_serial_tx_wait_complete(&furi_hal_serial_control->handles[i]);
furi_hal_serial_suspend(&furi_hal_serial_control->handles[i]);
}
api_lock_unlock(message.api_lock);
} else if(message.type == FuriHalSerialControlMessageTypeResume) {
for(size_t i = 0; i < FuriHalSerialIdMax; i++) {
furi_hal_serial_resume(&furi_hal_serial_control->handles[i]);
}
api_lock_unlock(message.api_lock);
} else if(message.type == FuriHalSerialControlMessageTypeAcquire) {
FuriHalSerialId serial_id = *(FuriHalSerialId*)message.input;
if(furi_hal_serial_control->handles[serial_id].in_use) {
*(FuriHalSerialHandle**)message.output = NULL;
} else {
// Logging
if(furi_hal_serial_control->log_config_serial_id == serial_id) {
furi_hal_serial_control_log_set_handle(NULL);
}
// Return handle
furi_hal_serial_control->handles[serial_id].in_use = true;
*(FuriHalSerialHandle**)message.output =
&furi_hal_serial_control->handles[serial_id];
}
api_lock_unlock(message.api_lock);
} else if(message.type == FuriHalSerialControlMessageTypeRelease) {
FuriHalSerialHandle* handle = *(FuriHalSerialHandle**)message.input;
furi_assert(handle->in_use);
furi_hal_serial_deinit(handle);
handle->in_use = false;
// Return back logging
if(furi_hal_serial_control->log_config_serial_id == handle->id) {
furi_hal_serial_control_log_set_handle(handle);
}
api_lock_unlock(message.api_lock);
} else if(message.type == FuriHalSerialControlMessageTypeLogging) {
// Set new configuration
FuriHalSerialControlMessageInputLogging* message_input = message.input;
furi_hal_serial_control->log_config_serial_id = message_input->id;
furi_hal_serial_control->log_config_serial_baud_rate = message_input->baud_rate;
// Apply new configuration
FuriHalSerialHandle* handle = NULL;
if(furi_hal_serial_control->log_config_serial_id < FuriHalSerialIdMax) {
handle = &furi_hal_serial_control
->handles[furi_hal_serial_control->log_config_serial_id];
}
furi_hal_serial_control_log_set_handle(handle);
api_lock_unlock(message.api_lock);
} else {
furi_crash("Invalid parameter");
}
}
return 0;
}
void furi_hal_serial_control_init(void) {
furi_check(furi_hal_serial_control == NULL);
// Allocate resources
furi_hal_serial_control = malloc(sizeof(FuriHalSerialControl));
furi_hal_serial_control->handles[FuriHalSerialIdUsart].id = FuriHalSerialIdUsart;
furi_hal_serial_control->handles[FuriHalSerialIdLpuart].id = FuriHalSerialIdLpuart;
furi_hal_serial_control->queue =
furi_message_queue_alloc(8, sizeof(FuriHalSerialControlMessage));
furi_hal_serial_control->thread =
furi_thread_alloc_ex("SerialControlDriver", 512, furi_hal_serial_control_thread, NULL);
furi_thread_mark_as_service(furi_hal_serial_control->thread);
furi_thread_set_priority(furi_hal_serial_control->thread, FuriThreadPriorityHighest);
furi_hal_serial_control->log_config_serial_id = FuriHalSerialIdMax;
// Start control plane thread
furi_thread_start(furi_hal_serial_control->thread);
}
void furi_hal_serial_control_deinit(void) {
furi_check(furi_hal_serial_control);
// Stop control plane thread
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeStop;
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
furi_thread_join(furi_hal_serial_control->thread);
// Release resources
furi_thread_free(furi_hal_serial_control->thread);
furi_message_queue_free(furi_hal_serial_control->queue);
free(furi_hal_serial_control);
}
void furi_hal_serial_control_suspend(void) {
furi_check(furi_hal_serial_control);
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeSuspend;
message.api_lock = api_lock_alloc_locked();
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
}
void furi_hal_serial_control_resume(void) {
furi_check(furi_hal_serial_control);
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeResume;
message.api_lock = api_lock_alloc_locked();
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
}
FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id) {
furi_check(furi_hal_serial_control);
FuriHalSerialHandle* output = NULL;
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeAcquire;
message.api_lock = api_lock_alloc_locked();
message.input = &serial_id;
message.output = &output;
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
return output;
}
void furi_hal_serial_control_release(FuriHalSerialHandle* handle) {
furi_check(furi_hal_serial_control);
furi_check(handle);
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeRelease;
message.api_lock = api_lock_alloc_locked();
message.input = &handle;
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
}
void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate) {
furi_check(serial_id <= FuriHalSerialIdMax);
furi_check(baud_rate >= 9600 && baud_rate <= 4000000);
// Very special case of updater, where RTC initialized before kernel start
if(!furi_hal_serial_control) return;
FuriHalSerialControlMessageInputLogging message_input = {
.id = serial_id,
.baud_rate = baud_rate,
};
FuriHalSerialControlMessage message;
message.type = FuriHalSerialControlMessageTypeLogging;
message.api_lock = api_lock_alloc_locked();
message.input = &message_input;
furi_message_queue_put(furi_hal_serial_control->queue, &message, FuriWaitForever);
api_lock_wait_unlock_and_free(message.api_lock);
}

View File

@@ -0,0 +1,46 @@
#pragma once
#include "furi_hal_serial_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/** Initialize Serial Control */
void furi_hal_serial_control_init(void);
/** De-Initialize Serial Control */
void furi_hal_serial_control_deinit(void);
/** Suspend All Serial Interfaces */
void furi_hal_serial_control_suspend(void);
/** Resume All Serial Interfaces */
void furi_hal_serial_control_resume(void);
/** Acquire Serial Interface Handler
*
* @param[in] serial_id The serial transceiver identifier
*
* @return The Serial Interface Handle or null if interfaces is in use
*/
FuriHalSerialHandle* furi_hal_serial_control_acquire(FuriHalSerialId serial_id);
/** Release Serial Interface Handler
*
* @param handle The handle
*/
void furi_hal_serial_control_release(FuriHalSerialHandle* handle);
/** Acquire Serial Interface Handler
*
* @param[in] serial_id The serial transceiver identifier. Use FuriHalSerialIdMax to disable logging.
* @param[in] baud_rate The baud rate
*
* @return The Serial Interface Handle or null if interfaces is in use
*/
void furi_hal_serial_control_set_logging_config(FuriHalSerialId serial_id, uint32_t baud_rate);
#ifdef __cplusplus
}
#endif

View File

@@ -0,0 +1,15 @@
#pragma once
#include <furi.h>
/**
* UART channels
*/
typedef enum {
FuriHalSerialIdUsart,
FuriHalSerialIdLpuart,
FuriHalSerialIdMax,
} FuriHalSerialId;
typedef struct FuriHalSerialHandle FuriHalSerialHandle;

View File

@@ -0,0 +1,8 @@
#pragma once
#include <furi_hal_serial_types.h>
struct FuriHalSerialHandle {
FuriHalSerialId id;
bool in_use;
};

View File

@@ -1,244 +0,0 @@
#include <furi_hal_uart.h>
#include <stdbool.h>
#include <stm32wbxx_ll_lpuart.h>
#include <stm32wbxx_ll_usart.h>
#include <stm32wbxx_ll_rcc.h>
#include <furi_hal_resources.h>
#include <furi_hal_bus.h>
#include <furi.h>
static bool furi_hal_usart_prev_enabled[2];
static void (*irq_cb[2])(uint8_t ev, uint8_t data, void* context);
static void* irq_ctx[2];
static void furi_hal_usart_init(uint32_t baud) {
furi_hal_bus_enable(FuriHalBusUSART1);
LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_PCLK2);
furi_hal_gpio_init_ex(
&gpio_usart_tx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn7USART1);
furi_hal_gpio_init_ex(
&gpio_usart_rx,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn7USART1);
LL_USART_InitTypeDef USART_InitStruct;
USART_InitStruct.PrescalerValue = LL_USART_PRESCALER_DIV1;
USART_InitStruct.BaudRate = baud;
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_RX;
USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
LL_USART_Init(USART1, &USART_InitStruct);
LL_USART_EnableFIFO(USART1);
LL_USART_ConfigAsyncMode(USART1);
LL_USART_Enable(USART1);
while(!LL_USART_IsActiveFlag_TEACK(USART1) || !LL_USART_IsActiveFlag_REACK(USART1))
;
LL_USART_DisableIT_ERROR(USART1);
NVIC_SetPriority(USART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
}
static void furi_hal_lpuart_init(uint32_t baud) {
furi_hal_bus_enable(FuriHalBusLPUART1);
LL_RCC_SetLPUARTClockSource(LL_RCC_LPUART1_CLKSOURCE_PCLK1);
furi_hal_gpio_init_ex(
&gpio_ext_pc0,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn8LPUART1);
furi_hal_gpio_init_ex(
&gpio_ext_pc1,
GpioModeAltFunctionPushPull,
GpioPullUp,
GpioSpeedVeryHigh,
GpioAltFn8LPUART1);
LL_LPUART_InitTypeDef LPUART_InitStruct;
LPUART_InitStruct.PrescalerValue = LL_LPUART_PRESCALER_DIV1;
LPUART_InitStruct.BaudRate = 115200;
LPUART_InitStruct.DataWidth = LL_LPUART_DATAWIDTH_8B;
LPUART_InitStruct.StopBits = LL_LPUART_STOPBITS_1;
LPUART_InitStruct.Parity = LL_LPUART_PARITY_NONE;
LPUART_InitStruct.TransferDirection = LL_LPUART_DIRECTION_TX_RX;
LPUART_InitStruct.HardwareFlowControl = LL_LPUART_HWCONTROL_NONE;
LL_LPUART_Init(LPUART1, &LPUART_InitStruct);
LL_LPUART_EnableFIFO(LPUART1);
LL_LPUART_Enable(LPUART1);
while(!LL_LPUART_IsActiveFlag_TEACK(LPUART1) || !LL_LPUART_IsActiveFlag_REACK(LPUART1))
;
furi_hal_uart_set_br(FuriHalUartIdLPUART1, baud);
LL_LPUART_DisableIT_ERROR(LPUART1);
NVIC_SetPriority(LPUART1_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 5, 0));
}
void furi_hal_uart_init(FuriHalUartId ch, uint32_t baud) {
if(ch == FuriHalUartIdLPUART1) {
furi_hal_lpuart_init(baud);
} else if(ch == FuriHalUartIdUSART1) {
furi_hal_usart_init(baud);
}
}
void furi_hal_uart_set_br(FuriHalUartId ch, uint32_t baud) {
if(ch == FuriHalUartIdUSART1) {
if(LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while(!LL_USART_IsActiveFlag_TC(USART1))
;
LL_USART_Disable(USART1);
uint32_t uartclk = LL_RCC_GetUSARTClockFreq(LL_RCC_USART1_CLKSOURCE);
LL_USART_SetBaudRate(
USART1, uartclk, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, baud);
LL_USART_Enable(USART1);
}
} else if(ch == FuriHalUartIdLPUART1) {
if(LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
LL_LPUART_Disable(LPUART1);
uint32_t uartclk = LL_RCC_GetLPUARTClockFreq(LL_RCC_LPUART1_CLKSOURCE);
if(uartclk / baud > 4095) {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV32);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV32, baud);
} else {
LL_LPUART_SetPrescaler(LPUART1, LL_LPUART_PRESCALER_DIV1);
LL_LPUART_SetBaudRate(LPUART1, uartclk, LL_LPUART_PRESCALER_DIV1, baud);
}
LL_LPUART_Enable(LPUART1);
}
}
}
void furi_hal_uart_deinit(FuriHalUartId ch) {
furi_hal_uart_set_irq_cb(ch, NULL, NULL);
if(ch == FuriHalUartIdUSART1) {
if(furi_hal_bus_is_enabled(FuriHalBusUSART1)) {
furi_hal_bus_disable(FuriHalBusUSART1);
}
furi_hal_gpio_init(&gpio_usart_tx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(&gpio_usart_rx, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
} else if(ch == FuriHalUartIdLPUART1) {
if(furi_hal_bus_is_enabled(FuriHalBusLPUART1)) {
furi_hal_bus_disable(FuriHalBusLPUART1);
}
furi_hal_gpio_init(&gpio_ext_pc0, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
furi_hal_gpio_init(&gpio_ext_pc1, GpioModeAnalog, GpioPullNo, GpioSpeedLow);
}
}
void furi_hal_uart_suspend(FuriHalUartId channel) {
if(channel == FuriHalUartIdLPUART1 && LL_LPUART_IsEnabled(LPUART1)) {
LL_LPUART_Disable(LPUART1);
furi_hal_usart_prev_enabled[channel] = true;
} else if(channel == FuriHalUartIdUSART1 && LL_USART_IsEnabled(USART1)) {
LL_USART_Disable(USART1);
furi_hal_usart_prev_enabled[channel] = true;
}
}
void furi_hal_uart_resume(FuriHalUartId channel) {
if(!furi_hal_usart_prev_enabled[channel]) {
return;
} else if(channel == FuriHalUartIdLPUART1) {
LL_LPUART_Enable(LPUART1);
} else if(channel == FuriHalUartIdUSART1) {
LL_USART_Enable(USART1);
}
furi_hal_usart_prev_enabled[channel] = false;
}
void furi_hal_uart_tx(FuriHalUartId ch, uint8_t* buffer, size_t buffer_size) {
if(ch == FuriHalUartIdUSART1) {
if(LL_USART_IsEnabled(USART1) == 0) return;
while(buffer_size > 0) {
while(!LL_USART_IsActiveFlag_TXE(USART1))
;
LL_USART_TransmitData8(USART1, *buffer);
buffer++;
buffer_size--;
}
} else if(ch == FuriHalUartIdLPUART1) {
if(LL_LPUART_IsEnabled(LPUART1) == 0) return;
while(buffer_size > 0) {
while(!LL_LPUART_IsActiveFlag_TXE(LPUART1))
;
LL_LPUART_TransmitData8(LPUART1, *buffer);
buffer++;
buffer_size--;
}
}
}
void furi_hal_uart_set_irq_cb(
FuriHalUartId ch,
void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx),
void* ctx) {
if(cb == NULL) {
if(ch == FuriHalUartIdUSART1) {
NVIC_DisableIRQ(USART1_IRQn);
LL_USART_DisableIT_RXNE_RXFNE(USART1);
} else if(ch == FuriHalUartIdLPUART1) {
NVIC_DisableIRQ(LPUART1_IRQn);
LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1);
}
irq_cb[ch] = cb;
irq_ctx[ch] = ctx;
} else {
irq_ctx[ch] = ctx;
irq_cb[ch] = cb;
if(ch == FuriHalUartIdUSART1) {
NVIC_EnableIRQ(USART1_IRQn);
LL_USART_EnableIT_RXNE_RXFNE(USART1);
} else if(ch == FuriHalUartIdLPUART1) {
NVIC_EnableIRQ(LPUART1_IRQn);
LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1);
}
}
}
void LPUART1_IRQHandler(void) {
if(LL_LPUART_IsActiveFlag_RXNE_RXFNE(LPUART1)) {
uint8_t data = LL_LPUART_ReceiveData8(LPUART1);
irq_cb[FuriHalUartIdLPUART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdLPUART1]);
} else if(LL_LPUART_IsActiveFlag_ORE(LPUART1)) {
LL_LPUART_ClearFlag_ORE(LPUART1);
}
}
void USART1_IRQHandler(void) {
if(LL_USART_IsActiveFlag_RXNE_RXFNE(USART1)) {
uint8_t data = LL_USART_ReceiveData8(USART1);
irq_cb[FuriHalUartIdUSART1](UartIrqEventRXNE, data, irq_ctx[FuriHalUartIdUSART1]);
} else if(LL_USART_IsActiveFlag_ORE(USART1)) {
LL_USART_ClearFlag_ORE(USART1);
}
}

View File

@@ -1,89 +0,0 @@
/**
* @file furi_hal_uart.h
* @version 1.0
* @date 2021-11-19
*
* UART HAL api interface
*/
#pragma once
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* UART channels
*/
typedef enum {
FuriHalUartIdUSART1,
FuriHalUartIdLPUART1,
} FuriHalUartId;
/**
* UART events
*/
typedef enum {
UartIrqEventRXNE,
} UartIrqEvent;
/**
* Init UART
* Configures GPIO to UART function, сonfigures UART hardware, enables UART hardware
* @param channel UART channel
* @param baud baudrate
*/
void furi_hal_uart_init(FuriHalUartId channel, uint32_t baud);
/**
* Deinit UART
* Configures GPIO to analog, clears callback and callback context, disables UART hardware
* @param channel UART channel
*/
void furi_hal_uart_deinit(FuriHalUartId channel);
/**
* Suspend UART operation
* Disables UART hardware, settings and callbacks are preserved
* @param channel UART channel
*/
void furi_hal_uart_suspend(FuriHalUartId channel);
/**
* Resume UART operation
* Resumes UART hardware from suspended state
* @param channel UART channel
*/
void furi_hal_uart_resume(FuriHalUartId channel);
/**
* Changes UART baudrate
* @param channel UART channel
* @param baud baudrate
*/
void furi_hal_uart_set_br(FuriHalUartId channel, uint32_t baud);
/**
* Transmits data
* @param channel UART channel
* @param buffer data
* @param buffer_size data size (in bytes)
*/
void furi_hal_uart_tx(FuriHalUartId channel, uint8_t* buffer, size_t buffer_size);
/**
* Sets UART event callback
* @param channel UART channel
* @param callback callback pointer
* @param context callback context
*/
void furi_hal_uart_set_irq_cb(
FuriHalUartId channel,
void (*callback)(UartIrqEvent event, uint8_t data, void* context),
void* context);
#ifdef __cplusplus
}
#endif