[FL-3734] UART framing mode selection (#4121)

* HAL: feat: uart framing
* JS: feat: uart framing
* fix formatting
* fix pvs warning
* HAL: flash impact reduction attempt 1
* HAL: flash impact reduction attempt 2
* fix compile error
* HAL: finalize flash impact reduction
* HAL: remove user-facing magic numbers

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
Anna Antonenko
2025-02-20 23:54:38 +04:00
committed by GitHub
parent 290a6dc1eb
commit 7c5c5d4749
15 changed files with 404 additions and 108 deletions

View File

@@ -120,7 +120,7 @@ static void furi_hal_serial_usart_irq_callback(void* context) {
}
if(USART1->ISR & USART_ISR_PE) {
USART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
event |= FuriHalSerialRxEventParityError;
}
if(furi_hal_serial[FuriHalSerialIdUsart].buffer_rx_ptr == NULL) {
@@ -321,7 +321,7 @@ static void furi_hal_serial_lpuart_irq_callback(void* context) {
}
if(LPUART1->ISR & USART_ISR_PE) {
LPUART1->ICR = USART_ICR_PECF;
event |= FuriHalSerialRxEventFrameError;
event |= FuriHalSerialRxEventParityError;
}
if(furi_hal_serial[FuriHalSerialIdLpuart].buffer_rx_ptr == NULL) {
@@ -605,6 +605,92 @@ void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud) {
}
}
// Avoid duplicating look-up tables between USART and LPUART
static_assert(LL_LPUART_DATAWIDTH_7B == LL_USART_DATAWIDTH_7B);
static_assert(LL_LPUART_DATAWIDTH_8B == LL_USART_DATAWIDTH_8B);
static_assert(LL_LPUART_DATAWIDTH_9B == LL_USART_DATAWIDTH_9B);
static_assert(LL_LPUART_PARITY_NONE == LL_USART_PARITY_NONE);
static_assert(LL_LPUART_PARITY_EVEN == LL_USART_PARITY_EVEN);
static_assert(LL_LPUART_PARITY_ODD == LL_USART_PARITY_ODD);
static_assert(LL_LPUART_STOPBITS_1 == LL_USART_STOPBITS_1);
static_assert(LL_LPUART_STOPBITS_2 == LL_USART_STOPBITS_2);
static const uint32_t serial_data_bits_lut[] = {
[FuriHalSerialDataBits7] = LL_USART_DATAWIDTH_7B,
[FuriHalSerialDataBits8] = LL_USART_DATAWIDTH_8B,
[FuriHalSerialDataBits9] = LL_USART_DATAWIDTH_9B,
};
static const uint32_t serial_parity_lut[] = {
[FuriHalSerialParityNone] = LL_USART_PARITY_NONE,
[FuriHalSerialParityEven] = LL_USART_PARITY_EVEN,
[FuriHalSerialParityOdd] = LL_USART_PARITY_ODD,
};
static const uint32_t serial_stop_bits_lut[] = {
[FuriHalSerialStopBits0_5] = LL_USART_STOPBITS_0_5,
[FuriHalSerialStopBits1] = LL_USART_STOPBITS_1,
[FuriHalSerialStopBits1_5] = LL_USART_STOPBITS_1_5,
[FuriHalSerialStopBits2] = LL_USART_STOPBITS_2,
};
static void furi_hal_serial_usart_configure_framing(
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
LL_USART_SetDataWidth(USART1, serial_data_bits_lut[data_bits]);
LL_USART_SetParity(USART1, serial_parity_lut[parity]);
LL_USART_SetStopBitsLength(USART1, serial_stop_bits_lut[stop_bits]);
}
static void furi_hal_serial_lpuart_configure_framing(
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
LL_LPUART_SetDataWidth(LPUART1, serial_data_bits_lut[data_bits]);
LL_LPUART_SetParity(LPUART1, serial_parity_lut[parity]);
// Unsupported non-whole stop bit numbers have been furi_check'ed away
LL_LPUART_SetStopBitsLength(LPUART1, serial_stop_bits_lut[stop_bits]);
}
void furi_hal_serial_configure_framing(
FuriHalSerialHandle* handle,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
furi_check(handle);
// Unsupported combinations
if(data_bits == FuriHalSerialDataBits9) furi_check(parity == FuriHalSerialParityNone);
if(data_bits == FuriHalSerialDataBits6) furi_check(parity != FuriHalSerialParityNone);
// Extend data word to account for parity bit
if(parity != FuriHalSerialParityNone) data_bits++;
if(handle->id == FuriHalSerialIdUsart) {
if(LL_USART_IsEnabled(USART1)) {
// Wait for transfer complete flag
while(!LL_USART_IsActiveFlag_TC(USART1))
;
LL_USART_Disable(USART1);
furi_hal_serial_usart_configure_framing(data_bits, parity, stop_bits);
LL_USART_Enable(USART1);
}
} else if(handle->id == FuriHalSerialIdLpuart) {
// Unsupported configurations
furi_check(stop_bits == FuriHalSerialStopBits1 || stop_bits == FuriHalSerialStopBits2);
if(LL_LPUART_IsEnabled(LPUART1)) {
// Wait for transfer complete flag
while(!LL_LPUART_IsActiveFlag_TC(LPUART1))
;
LL_LPUART_Disable(LPUART1);
furi_hal_serial_lpuart_configure_framing(data_bits, parity, stop_bits);
LL_LPUART_Enable(LPUART1);
}
}
}
void furi_hal_serial_deinit(FuriHalSerialHandle* handle) {
furi_check(handle);
furi_hal_serial_async_rx_configure(handle, NULL, NULL);

View File

@@ -16,7 +16,9 @@ extern "C" {
/** Initialize Serial
*
* Configures GPIO, configures and enables transceiver.
* Configures GPIO, configures and enables transceiver. Default framing settings
* are used: 8 data bits, no parity, 1 stop bit. Override them with
* `furi_hal_serial_configure_framing`.
*
* @param handle Serial handle
* @param baud baud rate
@@ -64,6 +66,20 @@ bool furi_hal_serial_is_baud_rate_supported(FuriHalSerialHandle* handle, uint32_
*/
void furi_hal_serial_set_br(FuriHalSerialHandle* handle, uint32_t baud);
/**
* @brief Configures framing of a serial interface
*
* @param handle Serial handle
* @param data_bits Data bits
* @param parity Parity
* @param stop_bits Stop bits
*/
void furi_hal_serial_configure_framing(
FuriHalSerialHandle* handle,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits);
/** Transmits data in semi-blocking mode
*
* Fills transmission pipe with data, returns as soon as all bytes from buffer
@@ -93,6 +109,7 @@ typedef enum {
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 */
FuriHalSerialRxEventParityError = (1 << 5), /**< Parity Error: incorrect parity bit received */
} FuriHalSerialRxEvent;
/** Receive callback
@@ -172,7 +189,7 @@ typedef void (*FuriHalSerialDmaRxCallback)(
void* context);
/**
* @brief Enable an input/output directon
* @brief Enable an input/output direction
*
* Takes over the respective pin by reconfiguring it to
* the appropriate alternative function.
@@ -185,7 +202,7 @@ void furi_hal_serial_enable_direction(
FuriHalSerialDirection direction);
/**
* @brief Disable an input/output directon
* @brief Disable an input/output direction
*
* Releases the respective pin by reconfiguring it to
* initial state, making possible its use for other purposes.

View File

@@ -19,4 +19,39 @@ typedef enum {
FuriHalSerialDirectionMax,
} FuriHalSerialDirection;
/**
* @brief Actual data bits, i.e. not including start/stop and parity bits
* @note 6 data bits are only permitted when parity is enabled
* @note 9 data bits are only permitted when parity is disabled
*/
typedef enum {
FuriHalSerialDataBits6,
FuriHalSerialDataBits7,
FuriHalSerialDataBits8,
FuriHalSerialDataBits9,
FuriHalSerialDataBitsMax,
} FuriHalSerialDataBits;
typedef enum {
FuriHalSerialParityNone,
FuriHalSerialParityEven,
FuriHalSerialParityOdd,
FuriHalSerialParityMax,
} FuriHalSerialParity;
/**
* @brief Stop bit length
* @note LPUART only supports whole stop bit lengths (i.e. 1 and 2, but not 0.5 and 1.5)
*/
typedef enum {
FuriHalSerialStopBits0_5,
FuriHalSerialStopBits1,
FuriHalSerialStopBits1_5,
FuriHalSerialStopBits2,
FuriHalSerialStopBits2Max,
} FuriHalSerialStopBits;
typedef struct FuriHalSerialHandle FuriHalSerialHandle;