mirror of
https://github.com/DarkFlippers/unleashed-firmware.git
synced 2025-12-12 04:34:43 +04:00
[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:
@@ -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);
|
||||
|
||||
@@ -318,19 +341,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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -269,6 +269,7 @@ static const char* extra_features[] = {
|
||||
"baseline", // dummy "feature"
|
||||
"gpio-pwm",
|
||||
"gui-widget",
|
||||
"serial-framing",
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
#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["_"]`
|
||||
@@ -81,6 +81,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)}, \
|
||||
@@ -90,8 +95,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;
|
||||
@@ -199,6 +210,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
|
||||
@@ -222,24 +281,7 @@ static inline void
|
||||
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); \
|
||||
JS_CONVERT_OR_RETURN(mjs, &_js_arg_vals[_i], _js_args[_i], "argument %d", _i); \
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"start": "npm run build && node node_modules/@flipperdevices/fz-sdk/sdk.js upload"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flipperdevices/fz-sdk": "^0.1",
|
||||
"@flipperdevices/fz-sdk": "^0.3",
|
||||
"typescript": "^5.6.3"
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@flipperdevices/fz-sdk",
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"description": "Type declarations and documentation for native JS modules available on Flipper Zero",
|
||||
"keywords": [
|
||||
"flipper",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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,,
|
||||
@@ -1627,6 +1627,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,
|
||||
|
||||
|
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user