1
mirror of https://github.com/DarkFlippers/unleashed-firmware.git synced 2025-12-12 04:34:43 +04:00

Merge remote-tracking branch 'OFW/dev' into dev

This commit is contained in:
MX
2025-02-21 03:08:00 +03:00
19 changed files with 440 additions and 137 deletions

View File

@@ -16,6 +16,9 @@
#define LINES_ON_SCREEN 6
#define COLUMNS_ON_SCREEN 21
#define DEFAULT_BAUD_RATE 230400
#define DEFAULT_DATA_BITS FuriHalSerialDataBits8
#define DEFAULT_PARITY FuriHalSerialParityNone
#define DEFAULT_STOP_BITS FuriHalSerialStopBits1
typedef struct UartDumpModel UartDumpModel;
@@ -49,11 +52,12 @@ typedef enum {
WorkerEventRxOverrunError = (1 << 4),
WorkerEventRxFramingError = (1 << 5),
WorkerEventRxNoiseError = (1 << 6),
WorkerEventRxParityError = (1 << 7),
} WorkerEventFlags;
#define WORKER_EVENTS_MASK \
(WorkerEventStop | WorkerEventRxData | WorkerEventRxIdle | WorkerEventRxOverrunError | \
WorkerEventRxFramingError | WorkerEventRxNoiseError)
WorkerEventRxFramingError | WorkerEventRxNoiseError | WorkerEventRxParityError)
const NotificationSequence sequence_notification = {
&message_display_backlight_on,
@@ -62,6 +66,13 @@ const NotificationSequence sequence_notification = {
NULL,
};
const NotificationSequence sequence_error = {
&message_display_backlight_on,
&message_red_255,
&message_delay_10,
NULL,
};
static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
UartDumpModel* model = _model;
@@ -133,6 +144,9 @@ static void
if(event & FuriHalSerialRxEventOverrunError) {
flag |= WorkerEventRxOverrunError;
}
if(event & FuriHalSerialRxEventParityError) {
flag |= WorkerEventRxParityError;
}
furi_thread_flags_set(furi_thread_get_id(app->worker_thread), flag);
}
@@ -227,13 +241,21 @@ static int32_t uart_echo_worker(void* context) {
if(events & WorkerEventRxNoiseError) {
furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect NE\r\n", 13);
}
if(events & WorkerEventRxParityError) {
furi_hal_serial_tx(app->serial_handle, (uint8_t*)"\r\nDetect PE\r\n", 13);
}
notification_message(app->notification, &sequence_error);
}
}
return 0;
}
static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
static UartEchoApp* uart_echo_app_alloc(
uint32_t baudrate,
FuriHalSerialDataBits data_bits,
FuriHalSerialParity parity,
FuriHalSerialStopBits stop_bits) {
UartEchoApp* app = malloc(sizeof(UartEchoApp));
app->rx_stream = furi_stream_buffer_alloc(2048, 1);
@@ -275,6 +297,7 @@ static UartEchoApp* uart_echo_app_alloc(uint32_t baudrate) {
app->serial_handle = furi_hal_serial_control_acquire(FuriHalSerialIdUsart);
furi_check(app->serial_handle);
furi_hal_serial_init(app->serial_handle, baudrate);
furi_hal_serial_configure_framing(app->serial_handle, data_bits, parity, stop_bits);
furi_hal_serial_async_rx_start(app->serial_handle, uart_echo_on_irq_cb, app, true);
@@ -319,19 +342,76 @@ static void uart_echo_app_free(UartEchoApp* app) {
free(app);
}
// silences "same-assignment" false positives in the arg parser below
// -V::1048
int32_t uart_echo_app(void* p) {
uint32_t baudrate = DEFAULT_BAUD_RATE;
FuriHalSerialDataBits data_bits = DEFAULT_DATA_BITS;
FuriHalSerialParity parity = DEFAULT_PARITY;
FuriHalSerialStopBits stop_bits = DEFAULT_STOP_BITS;
if(p) {
const char* baudrate_str = p;
if(strint_to_uint32(baudrate_str, NULL, &baudrate, 10) != StrintParseNoError) {
FURI_LOG_E(TAG, "Invalid baudrate: %s", baudrate_str);
baudrate = DEFAULT_BAUD_RATE;
// parse argument
char* parse_ptr = p;
bool parse_success = false;
do {
if(strint_to_uint32(parse_ptr, &parse_ptr, &baudrate, 10) != StrintParseNoError) break;
if(*(parse_ptr++) != '_') break;
uint16_t data_bits_int;
if(strint_to_uint16(parse_ptr, &parse_ptr, &data_bits_int, 10) != StrintParseNoError)
break;
if(data_bits_int == 6)
data_bits = FuriHalSerialDataBits6;
else if(data_bits_int == 7)
data_bits = FuriHalSerialDataBits7;
else if(data_bits_int == 8)
data_bits = FuriHalSerialDataBits8;
else if(data_bits_int == 9)
data_bits = FuriHalSerialDataBits9;
else
break;
char parity_char = *(parse_ptr++);
if(parity_char == 'N')
parity = FuriHalSerialParityNone;
else if(parity_char == 'E')
parity = FuriHalSerialParityEven;
else if(parity_char == 'O')
parity = FuriHalSerialParityOdd;
else
break;
uint16_t stop_bits_int;
if(strint_to_uint16(parse_ptr, &parse_ptr, &stop_bits_int, 10) != StrintParseNoError)
break;
if(stop_bits_int == 5)
stop_bits = FuriHalSerialStopBits0_5;
else if(stop_bits_int == 1)
stop_bits = FuriHalSerialStopBits1;
else if(stop_bits_int == 15)
stop_bits = FuriHalSerialStopBits1_5;
else if(stop_bits_int == 2)
stop_bits = FuriHalSerialStopBits2;
else
break;
parse_success = true;
} while(0);
if(!parse_success) {
FURI_LOG_I(
TAG,
"Couldn't parse baud rate and framing (%s). Applying defaults (%d_8N1)",
(const char*)p,
DEFAULT_BAUD_RATE);
}
}
FURI_LOG_I(TAG, "Using baudrate: %lu", baudrate);
UartEchoApp* app = uart_echo_app_alloc(baudrate);
UartEchoApp* app = uart_echo_app_alloc(baudrate, data_bits, parity, stop_bits);
view_dispatcher_run(app->view_dispatcher);
uart_echo_app_free(app);
return 0;

View File

@@ -12,4 +12,4 @@ tests.assert_eq(false, doesSdkSupport(["abobus", "other-nonexistent-feature"]));
tests.assert_eq("flipperdevices", flipper.firmwareVendor);
tests.assert_eq(0, flipper.jsSdkVersion[0]);
tests.assert_eq(2, flipper.jsSdkVersion[1]);
tests.assert_eq(3, flipper.jsSdkVersion[1]);

View File

@@ -13,10 +13,12 @@ typedef union {
} InfraredSceneState;
#pragma pack(pop)
void infrared_scene_universal_common_item_callback(void* context, uint32_t index) {
void infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type) {
InfraredApp* infrared = context;
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
if(type == InputTypeShort) {
uint32_t event = infrared_custom_event_pack(InfraredCustomEventTypeButtonSelected, index);
view_dispatcher_send_custom_event(infrared->view_dispatcher, event);
}
}
static void infrared_scene_universal_common_progress_input_callback(

View File

@@ -5,4 +5,4 @@
void infrared_scene_universal_common_on_enter(void* context);
bool infrared_scene_universal_common_on_event(void* context, SceneManagerEvent event);
void infrared_scene_universal_common_on_exit(void* context);
void infrared_scene_universal_common_item_callback(void* context, uint32_t index);
void infrared_scene_universal_common_item_callback(void* context, uint32_t index, InputType type);

View File

@@ -2,6 +2,7 @@
#include <gui/canvas.h>
#include <gui/elements.h>
#include <input/input.h>
#include <furi.h>
#include <furi_hal_resources.h>
@@ -46,6 +47,7 @@ ARRAY_DEF(ButtonMatrix, ButtonArray_t);
struct ButtonPanel {
View* view;
bool freeze_input;
};
typedef struct {
@@ -63,7 +65,7 @@ static void button_panel_process_up(ButtonPanel* button_panel);
static void button_panel_process_down(ButtonPanel* button_panel);
static void button_panel_process_left(ButtonPanel* button_panel);
static void button_panel_process_right(ButtonPanel* button_panel);
static void button_panel_process_ok(ButtonPanel* button_panel);
static void button_panel_process_ok(ButtonPanel* button_panel, InputType input);
static void button_panel_view_draw_callback(Canvas* canvas, void* _model);
static bool button_panel_view_input_callback(InputEvent* event, void* context);
@@ -358,7 +360,7 @@ static void button_panel_process_right(ButtonPanel* button_panel) {
true);
}
void button_panel_process_ok(ButtonPanel* button_panel) {
void button_panel_process_ok(ButtonPanel* button_panel, InputType type) {
ButtonItem* button_item = NULL;
with_view_model(
@@ -371,7 +373,7 @@ void button_panel_process_ok(ButtonPanel* button_panel) {
true);
if(button_item && button_item->callback) {
button_item->callback(button_item->callback_context, button_item->index);
button_item->callback(button_item->callback_context, button_item->index, type);
}
}
@@ -379,8 +381,15 @@ static bool button_panel_view_input_callback(InputEvent* event, void* context) {
ButtonPanel* button_panel = context;
furi_assert(button_panel);
bool consumed = false;
if(event->type == InputTypeShort) {
if(event->key == InputKeyOk) {
if((event->type == InputTypePress) || (event->type == InputTypeRelease)) {
button_panel->freeze_input = (event->type == InputTypePress);
}
consumed = true;
button_panel_process_ok(button_panel, event->type);
}
if(!button_panel->freeze_input &&
(!(event->type == InputTypePress) && !(event->type == InputTypeRelease))) {
switch(event->key) {
case InputKeyUp:
consumed = true;
@@ -398,10 +407,6 @@ static bool button_panel_view_input_callback(InputEvent* event, void* context) {
consumed = true;
button_panel_process_right(button_panel);
break;
case InputKeyOk:
consumed = true;
button_panel_process_ok(button_panel);
break;
default:
break;
}

View File

@@ -15,7 +15,7 @@ extern "C" {
typedef struct ButtonPanel ButtonPanel;
/** Callback type to call for handling selecting button_panel items */
typedef void (*ButtonItemCallback)(void* context, uint32_t index);
typedef void (*ButtonItemCallback)(void* context, uint32_t index, InputType type);
/** Allocate new button_panel module.
*

View File

@@ -0,0 +1,15 @@
// This script is like uart_echo, except it uses 8E1 framing (8 data bits, even
// parity, 1 stop bit) as opposed to the default 8N1 (8 data bits, no parity,
// 1 stop bit)
let serial = require("serial");
serial.setup("usart", 230400, { dataBits: "8", parity: "even", stopBits: "1" });
while (1) {
let rx_data = serial.readBytes(1, 1000);
if (rx_data !== undefined) {
serial.write(rx_data);
let data_view = Uint8Array(rx_data);
print("0x" + data_view[0].toString(16));
}
}

View File

@@ -269,6 +269,7 @@ static const char* extra_features[] = {
"baseline", // dummy "feature"
"gpio-pwm",
"gui-widget",
"serial-framing",
// extra modules
"blebeacon",

View File

@@ -12,7 +12,7 @@
#define JS_SDK_VENDOR_FIRMWARE "unleashed"
#define JS_SDK_VENDOR "flipperdevices"
#define JS_SDK_MAJOR 0
#define JS_SDK_MINOR 2
#define JS_SDK_MINOR 3
/**
* @brief Returns the foreign pointer in `obj["_"]`
@@ -82,6 +82,11 @@ typedef enum {
*/
#define JS_AT_LEAST >=
typedef struct {
const char* name;
size_t value;
} JsEnumMapping;
#define JS_ENUM_MAP(var_name, ...) \
static const JsEnumMapping var_name##_mapping[] = { \
{NULL, sizeof(var_name)}, \
@@ -91,8 +96,14 @@ typedef enum {
typedef struct {
const char* name;
size_t value;
} JsEnumMapping;
size_t offset;
} JsObjectMapping;
#define JS_OBJ_MAP(var_name, ...) \
static const JsObjectMapping var_name##_mapping[] = { \
__VA_ARGS__, \
{NULL, 0}, \
};
typedef struct {
void* out;
@@ -200,6 +211,54 @@ static inline void
_js_validate_enum, \
var_name##_mapping})
static inline bool _js_validate_object(struct mjs* mjs, mjs_val_t val, const void* extra) {
for(const JsObjectMapping* mapping = (JsObjectMapping*)extra; mapping->name; mapping++)
if(mjs_get(mjs, val, mapping->name, ~0) == MJS_UNDEFINED) return false;
return true;
}
static inline void
_js_convert_object(struct mjs* mjs, mjs_val_t* val, void* out, const void* extra) {
const JsObjectMapping* mapping = (JsObjectMapping*)extra;
for(; mapping->name; mapping++) {
mjs_val_t field_val = mjs_get(mjs, *val, mapping->name, ~0);
*(mjs_val_t*)((uint8_t*)out + mapping->offset) = field_val;
}
}
#define JS_ARG_OBJECT(var_name, name) \
((_js_arg_decl){ \
&var_name, \
mjs_is_object, \
_js_convert_object, \
name " object", \
_js_validate_object, \
var_name##_mapping})
/**
* @brief Validates and converts a JS value with a declarative interface
*
* Example: `int32_t my_value; JS_CONVERT_OR_RETURN(mjs, &mjs_val, JS_ARG_INT32(&my_value), "value source");`
*
* @warning This macro executes `return;` by design in case of a validation failure
*/
#define JS_CONVERT_OR_RETURN(mjs, value, decl, source, ...) \
if(decl.validator) \
if(!decl.validator(*value)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
if(decl.extended_validator) \
if(!decl.extended_validator(mjs, *value, decl.extra_data)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
source ": expected %s", \
##__VA_ARGS__, \
decl.expected_type); \
decl.converter(mjs, value, decl.out, decl.extra_data);
//-V:JS_FETCH_ARGS_OR_RETURN:1008
/**
* @brief Fetches and validates the arguments passed to a JS function
@@ -209,38 +268,21 @@ static inline void
* @warning This macro executes `return;` by design in case of an argument count
* mismatch or a validation failure
*/
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
int _js_arg_cnt = COUNT_OF(_js_args); \
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"expected %s%d arguments, got %d", \
#arg_operator, \
_js_arg_cnt, \
mjs_nargs(mjs)); \
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
if(_js_args[_i].validator) \
if(!_js_args[_i].validator(_js_arg_vals[_i])) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"argument %d: expected %s", \
_i, \
_js_args[_i].expected_type); \
if(_js_args[_i].extended_validator) \
if(!_js_args[_i].extended_validator(mjs, _js_arg_vals[_i], _js_args[_i].extra_data)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"argument %d: expected %s", \
_i, \
_js_args[_i].expected_type); \
_js_args[_i].converter( \
mjs, &_js_arg_vals[_i], _js_args[_i].out, _js_args[_i].extra_data); \
#define JS_FETCH_ARGS_OR_RETURN(mjs, arg_operator, ...) \
_js_arg_decl _js_args[] = {__VA_ARGS__}; \
int _js_arg_cnt = COUNT_OF(_js_args); \
mjs_val_t _js_arg_vals[_js_arg_cnt]; \
if(!(mjs_nargs(mjs) arg_operator _js_arg_cnt)) \
JS_ERROR_AND_RETURN( \
mjs, \
MJS_BAD_ARGS_ERROR, \
"expected %s%d arguments, got %d", \
#arg_operator, \
_js_arg_cnt, \
mjs_nargs(mjs)); \
for(int _i = 0; _i < _js_arg_cnt; _i++) { \
_js_arg_vals[_i] = mjs_arg(mjs, _i); \
JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
}
/**

View File

@@ -20,14 +20,6 @@ typedef struct {
char* data;
} PatternArrayItem;
static const struct {
const char* name;
const FuriHalSerialId value;
} serial_channels[] = {
{"usart", FuriHalSerialIdUsart},
{"lpuart", FuriHalSerialIdLpuart},
};
ARRAY_DEF(PatternArray, PatternArrayItem, M_POD_OPLIST);
static void
@@ -43,9 +35,54 @@ static void
}
static void js_serial_setup(struct mjs* mjs) {
mjs_val_t obj_inst = mjs_get(mjs, mjs_get_this(mjs), INST_PROP_NAME, ~0);
JsSerialInst* serial = mjs_get_ptr(mjs, obj_inst);
furi_assert(serial);
FuriHalSerialId serial_id;
int32_t baudrate;
JS_ENUM_MAP(serial_id, {"lpuart", FuriHalSerialIdLpuart}, {"usart", FuriHalSerialIdUsart});
JS_FETCH_ARGS_OR_RETURN(
mjs, JS_AT_LEAST, JS_ARG_ENUM(serial_id, "SerialId"), JS_ARG_INT32(&baudrate));
FuriHalSerialDataBits data_bits = FuriHalSerialDataBits8;
FuriHalSerialParity parity = FuriHalSerialParityNone;
FuriHalSerialStopBits stop_bits = FuriHalSerialStopBits1;
if(mjs_nargs(mjs) > 2) {
struct framing {
mjs_val_t data_bits;
mjs_val_t parity;
mjs_val_t stop_bits;
} framing;
JS_OBJ_MAP(
framing,
{"dataBits", offsetof(struct framing, data_bits)},
{"parity", offsetof(struct framing, parity)},
{"stopBits", offsetof(struct framing, stop_bits)});
JS_ENUM_MAP(
data_bits,
{"6", FuriHalSerialDataBits6},
{"7", FuriHalSerialDataBits7},
{"8", FuriHalSerialDataBits8},
{"9", FuriHalSerialDataBits9});
JS_ENUM_MAP(
parity,
{"none", FuriHalSerialParityNone},
{"even", FuriHalSerialParityEven},
{"odd", FuriHalSerialParityOdd});
JS_ENUM_MAP(
stop_bits,
{"0.5", FuriHalSerialStopBits0_5},
{"1", FuriHalSerialStopBits1},
{"1.5", FuriHalSerialStopBits1_5},
{"2", FuriHalSerialStopBits2});
mjs_val_t framing_obj = mjs_arg(mjs, 2);
JS_CONVERT_OR_RETURN(mjs, &framing_obj, JS_ARG_OBJECT(framing, "Framing"), "argument 2");
JS_CONVERT_OR_RETURN(
mjs, &framing.data_bits, JS_ARG_ENUM(data_bits, "DataBits"), "argument 2: dataBits");
JS_CONVERT_OR_RETURN(
mjs, &framing.parity, JS_ARG_ENUM(parity, "Parity"), "argument 2: parity");
JS_CONVERT_OR_RETURN(
mjs, &framing.stop_bits, JS_ARG_ENUM(stop_bits, "StopBits"), "argument 2: stopBits");
}
JsSerialInst* serial = JS_GET_CONTEXT(mjs);
if(serial->setup_done) {
mjs_prepend_errorf(mjs, MJS_INTERNAL_ERROR, "Serial is already configured");
@@ -53,43 +90,6 @@ static void js_serial_setup(struct mjs* mjs) {
return;
}
bool args_correct = false;
FuriHalSerialId serial_id = FuriHalSerialIdMax;
uint32_t baudrate = 0;
do {
if(mjs_nargs(mjs) != 2) break;
mjs_val_t arg = mjs_arg(mjs, 0);
if(!mjs_is_string(arg)) break;
size_t str_len = 0;
const char* arg_str = mjs_get_string(mjs, &arg, &str_len);
for(size_t i = 0; i < COUNT_OF(serial_channels); i++) {
size_t name_len = strlen(serial_channels[i].name);
if(str_len != name_len) continue;
if(strncmp(arg_str, serial_channels[i].name, str_len) == 0) {
serial_id = serial_channels[i].value;
break;
}
}
if(serial_id == FuriHalSerialIdMax) {
break;
}
arg = mjs_arg(mjs, 1);
if(!mjs_is_number(arg)) break;
baudrate = mjs_get_int32(mjs, arg);
args_correct = true;
} while(0);
if(!args_correct) {
mjs_prepend_errorf(mjs, MJS_BAD_ARGS_ERROR, "");
mjs_return(mjs, MJS_UNDEFINED);
return;
}
expansion_disable(furi_record_open(RECORD_EXPANSION));
furi_record_close(RECORD_EXPANSION);
@@ -97,6 +97,7 @@ static void js_serial_setup(struct mjs* mjs) {
if(serial->serial_handle) {
serial->rx_stream = furi_stream_buffer_alloc(RX_BUF_LEN, 1);
furi_hal_serial_init(serial->serial_handle, baudrate);
furi_hal_serial_configure_framing(serial->serial_handle, data_bits, parity, stop_bits);
furi_hal_serial_async_rx_start(
serial->serial_handle, js_serial_on_async_rx, serial, false);
serial->setup_done = true;

View File

@@ -72,7 +72,7 @@
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
@@ -92,7 +92,7 @@ declare function sdkCompatibilityStatus(expectedMajor: number, expectedMinor: nu
* @brief Checks compatibility between the script and the JS SDK that the
* firmware provides in a boolean fashion
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script
@@ -105,7 +105,7 @@ declare function isSdkCompatible(expectedMajor: number, expectedMinor: number):
* @brief Asks the user whether to continue executing the script if the versions
* are not compatible. Does nothing if they are.
*
* @note You're looking at JS SDK v0.1
* @note You're looking at JS SDK v0.3
*
* @param expectedMajor JS SDK major version expected by the script
* @param expectedMinor JS SDK minor version expected by the script

View File

@@ -1,6 +1,6 @@
{
"name": "@darkflippers/fz-sdk-ul",
"version": "0.2.0",
"version": "0.3.0",
"description": "Type declarations and documentation for native JS modules available on Unleashed Custom Firmware for Flipper Zero",
"keywords": [
"unleashed",

View File

@@ -4,16 +4,33 @@
* @module
*/
export interface Framing {
/**
* @note 6 data bits can only be selected when parity is enabled (even or
* odd)
* @note 9 data bits can only be selected when parity is disabled (none)
*/
dataBits: "6" | "7" | "8" | "9";
parity: "none" | "even" | "odd";
/**
* @note LPUART only supports whole stop bit lengths (i.e. 1 and 2 but not
* 0.5 and 1.5)
*/
stopBits: "0.5" | "1" | "1.5" | "2";
}
/**
* @brief Initializes the serial port
*
* Automatically disables Expansion module service to prevent interference.
*
* @param port The port to initialize (`"lpuart"` or `"start"`)
* @param baudRate
* @param port The port to initialize (`"lpuart"` or `"usart"`)
* @param baudRate Baud rate
* @param framing See `Framing` type
* @version Added in JS SDK 0.1
* @version Added `framing` parameter in JS SDK 0.3, extra feature `"serial-framing"`
*/
export declare function setup(port: "lpuart" | "usart", baudRate: number): void;
export declare function setup(port: "lpuart" | "usart", baudRate: number, framing?: Framing): void;
/**
* @brief Writes data to the serial port

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,80.3,,
Version,+,80.4,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
Header,+,applications/services/cli/cli.h,,
@@ -1437,6 +1437,7 @@ Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
Function,+,furi_hal_serial_configure_framing,void,"FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits"
Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
Function,+,furi_hal_serial_control_deinit,void,
Function,+,furi_hal_serial_control_init,void,
1 entry status name type params
2 Version + 80.3 80.4
3 Header + applications/services/bt/bt_service/bt.h
4 Header + applications/services/bt/bt_service/bt_keys_storage.h
5 Header + applications/services/cli/cli.h
1437 Function + furi_hal_serial_async_rx_available _Bool FuriHalSerialHandle*
1438 Function + furi_hal_serial_async_rx_start void FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool
1439 Function + furi_hal_serial_async_rx_stop void FuriHalSerialHandle*
1440 Function + furi_hal_serial_configure_framing void FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits
1441 Function + furi_hal_serial_control_acquire FuriHalSerialHandle* FuriHalSerialId
1442 Function + furi_hal_serial_control_deinit void
1443 Function + furi_hal_serial_control_init void

View File

@@ -1,5 +1,5 @@
entry,status,name,type,params
Version,+,80.3,,
Version,+,80.4,,
Header,+,applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h,,
Header,+,applications/services/bt/bt_service/bt.h,,
Header,+,applications/services/bt/bt_service/bt_keys_storage.h,,
@@ -1662,6 +1662,7 @@ Function,+,furi_hal_serial_async_rx,uint8_t,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_available,_Bool,FuriHalSerialHandle*
Function,+,furi_hal_serial_async_rx_start,void,"FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool"
Function,+,furi_hal_serial_async_rx_stop,void,FuriHalSerialHandle*
Function,+,furi_hal_serial_configure_framing,void,"FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits"
Function,+,furi_hal_serial_control_acquire,FuriHalSerialHandle*,FuriHalSerialId
Function,+,furi_hal_serial_control_deinit,void,
Function,+,furi_hal_serial_control_init,void,
1 entry status name type params
2 Version + 80.3 80.4
3 Header + applications/drivers/subghz/cc1101_ext/cc1101_ext_interconnect.h
4 Header + applications/services/bt/bt_service/bt.h
5 Header + applications/services/bt/bt_service/bt_keys_storage.h
1662 Function + furi_hal_serial_async_rx_available _Bool FuriHalSerialHandle*
1663 Function + furi_hal_serial_async_rx_start void FuriHalSerialHandle*, FuriHalSerialAsyncRxCallback, void*, _Bool
1664 Function + furi_hal_serial_async_rx_stop void FuriHalSerialHandle*
1665 Function + furi_hal_serial_configure_framing void FuriHalSerialHandle*, FuriHalSerialDataBits, FuriHalSerialParity, FuriHalSerialStopBits
1666 Function + furi_hal_serial_control_acquire FuriHalSerialHandle* FuriHalSerialId
1667 Function + furi_hal_serial_control_deinit void
1668 Function + furi_hal_serial_control_init void

View File

@@ -258,85 +258,85 @@ FURI_ALWAYS_INLINE static void furi_hal_gpio_int_call(uint16_t pin_num) {
/* Interrupt handlers */
void EXTI0_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0)) {
furi_hal_gpio_int_call(0);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);
furi_hal_gpio_int_call(0);
}
}
void EXTI1_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_1)) {
furi_hal_gpio_int_call(1);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_1);
furi_hal_gpio_int_call(1);
}
}
void EXTI2_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_2)) {
furi_hal_gpio_int_call(2);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_2);
furi_hal_gpio_int_call(2);
}
}
void EXTI3_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_3)) {
furi_hal_gpio_int_call(3);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_3);
furi_hal_gpio_int_call(3);
}
}
void EXTI4_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_4)) {
furi_hal_gpio_int_call(4);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_4);
furi_hal_gpio_int_call(4);
}
}
void EXTI9_5_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_5)) {
furi_hal_gpio_int_call(5);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_5);
furi_hal_gpio_int_call(5);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_6)) {
furi_hal_gpio_int_call(6);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_6);
furi_hal_gpio_int_call(6);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_7)) {
furi_hal_gpio_int_call(7);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_7);
furi_hal_gpio_int_call(7);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_8)) {
furi_hal_gpio_int_call(8);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_8);
furi_hal_gpio_int_call(8);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_9)) {
furi_hal_gpio_int_call(9);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_9);
furi_hal_gpio_int_call(9);
}
}
void EXTI15_10_IRQHandler(void) {
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_10)) {
furi_hal_gpio_int_call(10);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_10);
furi_hal_gpio_int_call(10);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_11)) {
furi_hal_gpio_int_call(11);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_11);
furi_hal_gpio_int_call(11);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_12)) {
furi_hal_gpio_int_call(12);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_12);
furi_hal_gpio_int_call(12);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)) {
furi_hal_gpio_int_call(13);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
furi_hal_gpio_int_call(13);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_14)) {
furi_hal_gpio_int_call(14);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_14);
furi_hal_gpio_int_call(14);
}
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_15)) {
furi_hal_gpio_int_call(15);
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_15);
furi_hal_gpio_int_call(15);
}
}

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;