This commit is contained in:
VerstreuteSeele
2023-01-29 12:28:52 +01:00
33 changed files with 486 additions and 49 deletions

View File

@@ -52,7 +52,7 @@ Almost everything in flipper firmware is built around this concept.
## Naming
### Type names are CamelCase
### Type names are PascalCase
Examples:

View File

@@ -3,7 +3,7 @@
#include <furi.h>
#include <furi_hal.h>
#define WS_VERSION_APP "0.6.1"
#define WS_VERSION_APP "0.7"
#define WS_DEVELOPED "SkorP"
#define WS_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"

View File

@@ -0,0 +1,329 @@
#include "lacrosse_tx.h"
#define TAG "WSProtocolLaCrosse_TX"
/*
* Help
* https://github.com/merbanan/rtl_433/blob/master/src/devices/lacrosse.c
*
*
* LaCrosse TX 433 Mhz Temperature and Humidity Sensors.
* - Tested: TX-7U and TX-6U (Temperature only)
* - Not Tested but should work: TX-3, TX-4
* - also TFA Dostmann 30.3120.90 sensor (for e.g. 35.1018.06 (WS-9015) station)
* - also TFA Dostmann 30.3121 sensor
* Protocol Documentation: http://www.f6fbb.org/domo/sensors/tx3_th.php
* Message is 44 bits, 11 x 4 bit nybbles:
* [00] [cnt = 10] [type] [addr] [addr + parity] [v1] [v2] [v3] [iv1] [iv2] [check]
* Notes:
* - Zero Pulses are longer (1,400 uS High, 1,000 uS Low) = 2,400 uS
* - One Pulses are shorter ( 550 uS High, 1,000 uS Low) = 1,600 uS
* - Sensor id changes when the battery is changed
* - Primary Value are BCD with one decimal place: vvv = 12.3
* - Secondary value is integer only intval = 12, seems to be a repeat of primary
* This may actually be an additional data check because the 4 bit checksum
* and parity bit is pretty week at detecting errors.
* - Temperature is in Celsius with 50.0 added (to handle negative values)
* - Humidity values appear to be integer precision, decimal always 0.
* - There is a 4 bit checksum and a parity bit covering the three digit value
* - Parity check for TX-3 and TX-4 might be different.
* - Msg sent with one repeat after 30 mS
* - Temperature and humidity are sent as separate messages
* - Frequency for each sensor may be could be off by as much as 50-75 khz
* - LaCrosse Sensors in other frequency ranges (915 Mhz) use FSK not OOK
* so they can't be decoded by rtl_433 currently.
* - Temperature and Humidity are sent in different messages bursts.
*/
#define LACROSSE_TX_GAP 1000
#define LACROSSE_TX_BIT_SIZE 44
#define LACROSSE_TX_SUNC_PATTERN 0x0A000000000
#define LACROSSE_TX_SUNC_MASK 0x0F000000000
#define LACROSSE_TX_MSG_TYPE_TEMP 0x00
#define LACROSSE_TX_MSG_TYPE_HUM 0x0E
static const SubGhzBlockConst ws_protocol_lacrosse_tx_const = {
.te_short = 550,
.te_long = 1300,
.te_delta = 120,
.min_count_bit_for_found = 40,
};
struct WSProtocolDecoderLaCrosse_TX {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
WSBlockGeneric generic;
uint16_t header_count;
};
struct WSProtocolEncoderLaCrosse_TX {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
WSBlockGeneric generic;
};
typedef enum {
LaCrosse_TXDecoderStepReset = 0,
LaCrosse_TXDecoderStepCheckPreambule,
LaCrosse_TXDecoderStepSaveDuration,
LaCrosse_TXDecoderStepCheckDuration,
} LaCrosse_TXDecoderStep;
const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder = {
.alloc = ws_protocol_decoder_lacrosse_tx_alloc,
.free = ws_protocol_decoder_lacrosse_tx_free,
.feed = ws_protocol_decoder_lacrosse_tx_feed,
.reset = ws_protocol_decoder_lacrosse_tx_reset,
.get_hash_data = ws_protocol_decoder_lacrosse_tx_get_hash_data,
.serialize = ws_protocol_decoder_lacrosse_tx_serialize,
.deserialize = ws_protocol_decoder_lacrosse_tx_deserialize,
.get_string = ws_protocol_decoder_lacrosse_tx_get_string,
};
const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol ws_protocol_lacrosse_tx = {
.name = WS_PROTOCOL_LACROSSE_TX_NAME,
.type = SubGhzProtocolWeatherStation,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
.decoder = &ws_protocol_lacrosse_tx_decoder,
.encoder = &ws_protocol_lacrosse_tx_encoder,
};
void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
WSProtocolDecoderLaCrosse_TX* instance = malloc(sizeof(WSProtocolDecoderLaCrosse_TX));
instance->base.protocol = &ws_protocol_lacrosse_tx;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void ws_protocol_decoder_lacrosse_tx_free(void* context) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
free(instance);
}
void ws_protocol_decoder_lacrosse_tx_reset(void* context) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
instance->header_count = 0;
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
static bool ws_protocol_lacrosse_tx_check_crc(WSProtocolDecoderLaCrosse_TX* instance) {
if(!instance->decoder.decode_data) return false;
uint8_t msg[] = {
(instance->decoder.decode_data >> 36) & 0x0F,
(instance->decoder.decode_data >> 32) & 0x0F,
(instance->decoder.decode_data >> 28) & 0x0F,
(instance->decoder.decode_data >> 24) & 0x0F,
(instance->decoder.decode_data >> 20) & 0x0F,
(instance->decoder.decode_data >> 16) & 0x0F,
(instance->decoder.decode_data >> 12) & 0x0F,
(instance->decoder.decode_data >> 8) & 0x0F,
(instance->decoder.decode_data >> 4) & 0x0F};
uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9);
return ((crc & 0x0F) == ((instance->decoder.decode_data) & 0x0F));
}
/**
* Analysis of received data
* @param instance Pointer to a WSBlockGeneric* instance
*/
static void ws_protocol_lacrosse_tx_remote_controller(WSBlockGeneric* instance) {
uint8_t msg_type = (instance->data >> 32) & 0x0F;
instance->id = (((instance->data >> 28) & 0x0F) << 3) | (((instance->data >> 24) & 0x0F) >> 1);
float msg_value = (float)((instance->data >> 20) & 0x0F) * 10.0f +
(float)((instance->data >> 16) & 0x0F) +
(float)((instance->data >> 12) & 0x0F) * 0.1f;
if(msg_type == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051
instance->temp = msg_value - 50.0f;
instance->humidity = WS_NO_HUMIDITY;
} else if(msg_type == LACROSSE_TX_MSG_TYPE_HUM) {
//ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard
instance->humidity = (uint8_t)msg_value;
} else {
furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type.");
}
instance->btn = WS_NO_BTN;
instance->battery_low = WS_NO_BATT;
instance->channel = WS_NO_CHANNEL;
}
void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
switch(instance->decoder.parser_step) {
case LaCrosse_TXDecoderStepReset:
if((!level) && (DURATION_DIFF(duration, LACROSSE_TX_GAP) <
ws_protocol_lacrosse_tx_const.te_delta * 2)) {
instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckPreambule;
instance->header_count = 0;
}
break;
case LaCrosse_TXDecoderStepCheckPreambule:
if(level) {
if((DURATION_DIFF(duration, ws_protocol_lacrosse_tx_const.te_short) <
ws_protocol_lacrosse_tx_const.te_delta) &&
(instance->header_count > 1)) {
instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.te_last = duration;
} else if(duration > (ws_protocol_lacrosse_tx_const.te_long * 2)) {
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, LACROSSE_TX_GAP) <
ws_protocol_lacrosse_tx_const.te_delta * 2) {
instance->decoder.te_last = duration;
instance->header_count++;
} else {
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
}
break;
case LaCrosse_TXDecoderStepSaveDuration:
if(level) {
instance->decoder.te_last = duration;
instance->decoder.parser_step = LaCrosse_TXDecoderStepCheckDuration;
} else {
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
break;
case LaCrosse_TXDecoderStepCheckDuration:
if(!level) {
if(duration > LACROSSE_TX_GAP * 3) {
if(DURATION_DIFF(
instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) <
ws_protocol_lacrosse_tx_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration;
} else if(
DURATION_DIFF(
instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) <
ws_protocol_lacrosse_tx_const.te_delta) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration;
}
if((instance->decoder.decode_data & LACROSSE_TX_SUNC_MASK) ==
LACROSSE_TX_SUNC_PATTERN) {
if(ws_protocol_lacrosse_tx_check_crc(instance)) {
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = LACROSSE_TX_BIT_SIZE;
ws_protocol_lacrosse_tx_remote_controller(&instance->generic);
if(instance->base.callback)
instance->base.callback(&instance->base, instance->base.context);
}
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->header_count = 0;
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
break;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_short) <
ws_protocol_lacrosse_tx_const.te_delta) &&
(DURATION_DIFF(duration, LACROSSE_TX_GAP) <
ws_protocol_lacrosse_tx_const.te_delta * 2)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration;
} else if(
(DURATION_DIFF(instance->decoder.te_last, ws_protocol_lacrosse_tx_const.te_long) <
ws_protocol_lacrosse_tx_const.te_delta) &&
(DURATION_DIFF(duration, LACROSSE_TX_GAP) <
ws_protocol_lacrosse_tx_const.te_delta * 2)) {
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
instance->decoder.parser_step = LaCrosse_TXDecoderStepSaveDuration;
} else {
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
} else {
instance->decoder.parser_step = LaCrosse_TXDecoderStepReset;
}
break;
}
}
uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
}
bool ws_protocol_decoder_lacrosse_tx_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
return ws_block_generic_serialize(&instance->generic, flipper_format, preset);
}
bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
bool ret = false;
do {
if(!ws_block_generic_deserialize(&instance->generic, flipper_format)) {
break;
}
if(instance->generic.data_count_bit !=
ws_protocol_lacrosse_tx_const.min_count_bit_for_found) {
FURI_LOG_E(TAG, "Wrong number of bits in key");
break;
}
ret = true;
} while(false);
return ret;
}
void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output) {
furi_assert(context);
WSProtocolDecoderLaCrosse_TX* instance = context;
furi_string_printf(
output,
"%s %dbit\r\n"
"Key:0x%lX%08lX\r\n"
"Sn:0x%lX Ch:%d Bat:%d\r\n"
"Temp:%3.1f C Hum:%d%%",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(uint32_t)(instance->generic.data >> 32),
(uint32_t)(instance->generic.data),
instance->generic.id,
instance->generic.channel,
instance->generic.battery_low,
(double)instance->generic.temp,
instance->generic.humidity);
}

View File

@@ -0,0 +1,79 @@
#pragma once
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include "ws_generic.h"
#include <lib/subghz/blocks/math.h>
#define WS_PROTOCOL_LACROSSE_TX_NAME "LaCrosse_TX"
typedef struct WSProtocolDecoderLaCrosse_TX WSProtocolDecoderLaCrosse_TX;
typedef struct WSProtocolEncoderLaCrosse_TX WSProtocolEncoderLaCrosse_TX;
extern const SubGhzProtocolDecoder ws_protocol_lacrosse_tx_decoder;
extern const SubGhzProtocolEncoder ws_protocol_lacrosse_tx_encoder;
extern const SubGhzProtocol ws_protocol_lacrosse_tx;
/**
* Allocate WSProtocolDecoderLaCrosse_TX.
* @param environment Pointer to a SubGhzEnvironment instance
* @return WSProtocolDecoderLaCrosse_TX* pointer to a WSProtocolDecoderLaCrosse_TX instance
*/
void* ws_protocol_decoder_lacrosse_tx_alloc(SubGhzEnvironment* environment);
/**
* Free WSProtocolDecoderLaCrosse_TX.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
*/
void ws_protocol_decoder_lacrosse_tx_free(void* context);
/**
* Reset decoder WSProtocolDecoderLaCrosse_TX.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
*/
void ws_protocol_decoder_lacrosse_tx_reset(void* context);
/**
* Parse a raw sequence of levels and durations received from the air.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
* @param level Signal level true-high false-low
* @param duration Duration of this level in, us
*/
void ws_protocol_decoder_lacrosse_tx_feed(void* context, bool level, uint32_t duration);
/**
* Getting the hash sum of the last randomly received parcel.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
* @return hash Hash sum
*/
uint8_t ws_protocol_decoder_lacrosse_tx_get_hash_data(void* context);
/**
* Serialize data WSProtocolDecoderLaCrosse_TX.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
* @param flipper_format Pointer to a FlipperFormat instance
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
* @return true On success
*/
bool ws_protocol_decoder_lacrosse_tx_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
/**
* Deserialize data WSProtocolDecoderLaCrosse_TX.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
* @param flipper_format Pointer to a FlipperFormat instance
* @return true On success
*/
bool ws_protocol_decoder_lacrosse_tx_deserialize(void* context, FlipperFormat* flipper_format);
/**
* Getting a textual representation of the received data.
* @param context Pointer to a WSProtocolDecoderLaCrosse_TX instance
* @param output Resulting text
*/
void ws_protocol_decoder_lacrosse_tx_get_string(void* context, FuriString* output);

View File

@@ -8,6 +8,7 @@ const SubGhzProtocol* weather_station_protocol_registry_items[] = {
&ws_protocol_gt_wt_03,
&ws_protocol_acurite_606tx,
&ws_protocol_acurite_609txc,
&ws_protocol_lacrosse_tx,
&ws_protocol_lacrosse_tx141thbv2,
&ws_protocol_oregon2,
&ws_protocol_acurite_592txr,

View File

@@ -8,6 +8,7 @@
#include "gt_wt_03.h"
#include "acurite_606tx.h"
#include "acurite_609txc.h"
#include "lacrosse_tx.h"
#include "lacrosse_tx141thbv2.h"
#include "oregon2.h"
#include "acurite_592txr.h"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1416,6 +1416,7 @@ Function,+,furi_hal_version_uid_size,size_t,
Function,-,furi_hal_vibro_init,void,
Function,+,furi_hal_vibro_on,void,_Bool
Function,-,furi_init,void,
Function,+,furi_kernel_is_irq_or_masked,_Bool,
Function,+,furi_kernel_get_tick_frequency,uint32_t,
Function,+,furi_kernel_lock,int32_t,
Function,+,furi_kernel_restore_lock,int32_t,int32_t
1 entry status name type params
1416 Function - furi_hal_vibro_init void
1417 Function + furi_hal_vibro_on void _Bool
1418 Function - furi_init void
1419 Function + furi_kernel_is_irq_or_masked _Bool
1420 Function + furi_kernel_get_tick_frequency uint32_t
1421 Function + furi_kernel_lock int32_t
1422 Function + furi_kernel_restore_lock int32_t int32_t

View File

@@ -52,30 +52,6 @@ extern "C" {
}
#endif
static inline bool furi_is_irq_context() {
bool irq = false;
BaseType_t state;
if(FURI_IS_IRQ_MODE()) {
/* Called from interrupt context */
irq = true;
} else {
/* Get FreeRTOS scheduler state */
state = xTaskGetSchedulerState();
if(state != taskSCHEDULER_NOT_STARTED) {
/* Scheduler was started */
if(FURI_IS_IRQ_MASKED()) {
/* Interrupts are masked */
irq = true;
}
}
}
/* Return context, 0: thread context, 1: IRQ context */
return (irq);
}
#ifdef __cplusplus
}
#endif

View File

@@ -7,8 +7,32 @@
#include CMSIS_device_header
bool furi_kernel_is_irq_or_masked() {
bool irq = false;
BaseType_t state;
if(FURI_IS_IRQ_MODE()) {
/* Called from interrupt context */
irq = true;
} else {
/* Get FreeRTOS scheduler state */
state = xTaskGetSchedulerState();
if(state != taskSCHEDULER_NOT_STARTED) {
/* Scheduler was started */
if(FURI_IS_IRQ_MASKED()) {
/* Interrupts are masked */
irq = true;
}
}
}
/* Return context, 0: thread context, 1: IRQ context */
return (irq);
}
int32_t furi_kernel_lock() {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
int32_t lock;
@@ -33,7 +57,7 @@ int32_t furi_kernel_lock() {
}
int32_t furi_kernel_unlock() {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
int32_t lock;
@@ -63,7 +87,7 @@ int32_t furi_kernel_unlock() {
}
int32_t furi_kernel_restore_lock(int32_t lock) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
switch(xTaskGetSchedulerState()) {
case taskSCHEDULER_SUSPENDED:
@@ -99,7 +123,7 @@ uint32_t furi_kernel_get_tick_frequency() {
}
void furi_delay_tick(uint32_t ticks) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
if(ticks == 0U) {
taskYIELD();
} else {
@@ -108,7 +132,7 @@ void furi_delay_tick(uint32_t ticks) {
}
FuriStatus furi_delay_until_tick(uint32_t tick) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
TickType_t tcnt, delay;
FuriStatus stat;
@@ -137,7 +161,7 @@ FuriStatus furi_delay_until_tick(uint32_t tick) {
uint32_t furi_get_tick() {
TickType_t ticks;
if(furi_is_irq_context() != 0U) {
if(furi_kernel_is_irq_or_masked() != 0U) {
ticks = xTaskGetTickCountFromISR();
} else {
ticks = xTaskGetTickCount();

View File

@@ -10,19 +10,42 @@
extern "C" {
#endif
/** Check if CPU is in IRQ or kernel running and IRQ is masked
*
* Originally this primitive was born as a workaround for FreeRTOS kernel primitives shenanigans with PRIMASK.
*
* Meaningful use cases are:
*
* - When kernel is started and you want to ensure that you are not in IRQ or IRQ is not masked(like in critical section)
* - When kernel is not started and you want to make sure that you are not in IRQ mode, ignoring PRIMASK.
*
* As you can see there will be edge case when kernel is not started and PRIMASK is not 0 that may cause some funky behavior.
* Most likely it will happen after kernel primitives being used, but control not yet passed to kernel.
* It's up to you to figure out if it is safe for your code or not.
*
* @return true if CPU is in IRQ or kernel running and IRQ is masked
*/
bool furi_kernel_is_irq_or_masked();
/** Lock kernel, pause process scheduling
*
* @warning This should never be called in interrupt request context.
*
* @return previous lock state(0 - unlocked, 1 - locked)
*/
int32_t furi_kernel_lock();
/** Unlock kernel, resume process scheduling
*
* @warning This should never be called in interrupt request context.
*
* @return previous lock state(0 - unlocked, 1 - locked)
*/
int32_t furi_kernel_unlock();
/** Restore kernel lock state
*
* @warning This should never be called in interrupt request context.
*
* @param[in] lock The lock state
*
@@ -37,7 +60,9 @@ int32_t furi_kernel_restore_lock(int32_t lock);
uint32_t furi_kernel_get_tick_frequency();
/** Delay execution
*
*
* @warning This should never be called in interrupt request context.
*
* Also keep in mind delay is aliased to scheduler timer intervals.
*
* @param[in] ticks The ticks count to pause
@@ -45,6 +70,8 @@ uint32_t furi_kernel_get_tick_frequency();
void furi_delay_tick(uint32_t ticks);
/** Delay until tick
*
* @warning This should never be called in interrupt request context.
*
* @param[in] ticks The tick until which kerel should delay task execution
*

View File

@@ -1,11 +1,11 @@
#include "kernel.h"
#include "message_queue.h"
#include "core/common_defines.h"
#include <FreeRTOS.h>
#include <queue.h>
#include "check.h"
FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size) {
furi_assert((furi_is_irq_context() == 0U) && (msg_count > 0U) && (msg_size > 0U));
furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (msg_count > 0U) && (msg_size > 0U));
QueueHandle_t handle = xQueueCreate(msg_count, msg_size);
furi_check(handle);
@@ -14,7 +14,7 @@ FuriMessageQueue* furi_message_queue_alloc(uint32_t msg_count, uint32_t msg_size
}
void furi_message_queue_free(FuriMessageQueue* instance) {
furi_assert(furi_is_irq_context() == 0U);
furi_assert(furi_kernel_is_irq_or_masked() == 0U);
furi_assert(instance);
vQueueDelete((QueueHandle_t)instance);
@@ -28,7 +28,7 @@ FuriStatus
stat = FuriStatusOk;
if(furi_is_irq_context() != 0U) {
if(furi_kernel_is_irq_or_masked() != 0U) {
if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter;
} else {
@@ -65,7 +65,7 @@ FuriStatus furi_message_queue_get(FuriMessageQueue* instance, void* msg_ptr, uin
stat = FuriStatusOk;
if(furi_is_irq_context() != 0U) {
if(furi_kernel_is_irq_or_masked() != 0U) {
if((hQueue == NULL) || (msg_ptr == NULL) || (timeout != 0U)) {
stat = FuriStatusErrorParameter;
} else {
@@ -131,7 +131,7 @@ uint32_t furi_message_queue_get_count(FuriMessageQueue* instance) {
if(hQueue == NULL) {
count = 0U;
} else if(furi_is_irq_context() != 0U) {
} else if(furi_kernel_is_irq_or_masked() != 0U) {
count = uxQueueMessagesWaitingFromISR(hQueue);
} else {
count = uxQueueMessagesWaiting(hQueue);
@@ -148,7 +148,7 @@ uint32_t furi_message_queue_get_space(FuriMessageQueue* instance) {
if(mq == NULL) {
space = 0U;
} else if(furi_is_irq_context() != 0U) {
} else if(furi_kernel_is_irq_or_masked() != 0U) {
isrm = taskENTER_CRITICAL_FROM_ISR();
/* space = pxQueue->uxLength - pxQueue->uxMessagesWaiting; */
@@ -167,7 +167,7 @@ FuriStatus furi_message_queue_reset(FuriMessageQueue* instance) {
QueueHandle_t hQueue = (QueueHandle_t)instance;
FuriStatus stat;
if(furi_is_irq_context() != 0U) {
if(furi_kernel_is_irq_or_masked() != 0U) {
stat = FuriStatusErrorISR;
} else if(hQueue == NULL) {
stat = FuriStatusErrorParameter;

View File

@@ -3,7 +3,6 @@
#include "memmgr.h"
#include "kernel.h"
#include "core/common_defines.h"
#include <FreeRTOS.h>
#include <timers.h>
@@ -27,7 +26,7 @@ static void TimerCallback(TimerHandle_t hTimer) {
}
FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* context) {
furi_assert((furi_is_irq_context() == 0U) && (func != NULL));
furi_assert((furi_kernel_is_irq_or_masked() == 0U) && (func != NULL));
TimerHandle_t hTimer;
TimerCallback_t* callb;
@@ -60,7 +59,7 @@ FuriTimer* furi_timer_alloc(FuriTimerCallback func, FuriTimerType type, void* co
}
void furi_timer_free(FuriTimer* instance) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
@@ -82,7 +81,7 @@ void furi_timer_free(FuriTimer* instance) {
}
FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
@@ -99,7 +98,7 @@ FuriStatus furi_timer_start(FuriTimer* instance, uint32_t ticks) {
}
FuriStatus furi_timer_stop(FuriTimer* instance) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;
@@ -117,7 +116,7 @@ FuriStatus furi_timer_stop(FuriTimer* instance) {
}
uint32_t furi_timer_is_running(FuriTimer* instance) {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(instance);
TimerHandle_t hTimer = (TimerHandle_t)instance;

View File

@@ -3,7 +3,7 @@
#include "queue.h"
void furi_init() {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED);
furi_log_init();
@@ -11,7 +11,7 @@ void furi_init() {
}
void furi_run() {
furi_assert(!furi_is_irq_context());
furi_assert(!furi_kernel_is_irq_or_masked());
furi_assert(xTaskGetSchedulerState() == taskSCHEDULER_NOT_STARTED);
#if(__ARM_ARCH_7A__ == 0U)