1
0
Fork 0
mirror of https://github.com/betaflight/betaflight.git synced 2025-07-20 14:55:21 +03:00

Merge pull request #3373 from DanNixon/generic_osd_timers

Generic OSD timers (plus high precision timer)
This commit is contained in:
Michael Keller 2017-07-08 10:21:39 +12:00 committed by GitHub
commit ea5a7cd998
8 changed files with 379 additions and 135 deletions

View file

@ -134,7 +134,6 @@ static OSD_Entry menuMainEntries[] =
{"FEATURES", OME_Submenu, cmsMenuChange, &menuFeatures, 0}, {"FEATURES", OME_Submenu, cmsMenuChange, &menuFeatures, 0},
#ifdef OSD #ifdef OSD
{"OSD", OME_Submenu, cmsMenuChange, &cmsx_menuOsd, 0}, {"OSD", OME_Submenu, cmsMenuChange, &cmsx_menuOsd, 0},
{"ALARMS", OME_Submenu, cmsMenuChange, &cmsx_menuAlarms, 0},
#endif #endif
{"FC&FW INFO", OME_Submenu, cmsMenuChange, &menuInfo, 0}, {"FC&FW INFO", OME_Submenu, cmsMenuChange, &menuInfo, 0},
{"MISC", OME_Submenu, cmsMenuChange, &cmsx_menuMisc, 0}, {"MISC", OME_Submenu, cmsMenuChange, &cmsx_menuMisc, 0},

View file

@ -39,51 +39,6 @@
#include "io/displayport_max7456.h" #include "io/displayport_max7456.h"
#include "io/osd.h" #include "io/osd.h"
static uint8_t osdConfig_rssi_alarm;
static uint16_t osdConfig_cap_alarm;
static uint16_t osdConfig_time_alarm;
static uint16_t osdConfig_alt_alarm;
static long cmsx_menuAlarmsOnEnter(void)
{
osdConfig_rssi_alarm = osdConfig()->rssi_alarm;
osdConfig_cap_alarm = osdConfig()->cap_alarm;
osdConfig_time_alarm = osdConfig()->time_alarm;
osdConfig_alt_alarm = osdConfig()->alt_alarm;
return 0;
}
static long cmsx_menuAlarmsOnExit(const OSD_Entry *self)
{
UNUSED(self);
osdConfigMutable()->rssi_alarm = osdConfig_rssi_alarm;
osdConfigMutable()->cap_alarm = osdConfig_cap_alarm;
osdConfigMutable()->time_alarm = osdConfig_time_alarm;
osdConfigMutable()->alt_alarm = osdConfig_alt_alarm;
return 0;
}
OSD_Entry cmsx_menuAlarmsEntries[] =
{
{"--- ALARMS ---", OME_Label, NULL, NULL, 0},
{"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0},
{"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0},
{"FLY TIME", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_time_alarm, 1, 200, 1}, 0},
{"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0},
{"BACK", OME_Back, NULL, NULL, 0},
{NULL, OME_END, NULL, NULL, 0}
};
CMS_Menu cmsx_menuAlarms = {
.GUARD_text = "MENUALARMS",
.GUARD_type = OME_MENU,
.onEnter = cmsx_menuAlarmsOnEnter,
.onExit = cmsx_menuAlarmsOnExit,
.onGlobalExit = NULL,
.entries = cmsx_menuAlarmsEntries,
};
#ifndef DISABLE_EXTENDED_CMS_OSD_MENU #ifndef DISABLE_EXTENDED_CMS_OSD_MENU
static uint16_t osdConfig_item_pos[OSD_ITEM_COUNT]; static uint16_t osdConfig_item_pos[OSD_ITEM_COUNT];
@ -111,8 +66,8 @@ OSD_Entry menuOsdActiveElemsEntries[] =
{"CROSSHAIRS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CROSSHAIRS], 0}, {"CROSSHAIRS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CROSSHAIRS], 0},
{"HORIZON", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARTIFICIAL_HORIZON], 0}, {"HORIZON", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARTIFICIAL_HORIZON], 0},
{"HORIZON SIDEBARS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HORIZON_SIDEBARS], 0}, {"HORIZON SIDEBARS", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_HORIZON_SIDEBARS], 0},
{"UPTIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ONTIME], 0}, {"TIMER 1", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_TIMER_1], 0},
{"FLY TIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYTIME], 0}, {"TIMER 2", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_TIMER_2], 0},
{"FLY MODE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYMODE], 0}, {"FLY MODE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_FLYMODE], 0},
{"NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CRAFT_NAME], 0}, {"NAME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_CRAFT_NAME], 0},
{"THROTTLE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_THROTTLE_POS], 0}, {"THROTTLE", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_THROTTLE_POS], 0},
@ -141,7 +96,6 @@ OSD_Entry menuOsdActiveElemsEntries[] =
{"DISARMED", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DISARMED], 0}, {"DISARMED", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_DISARMED], 0},
{"PIT ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PITCH_ANGLE], 0}, {"PIT ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_PITCH_ANGLE], 0},
{"ROL ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ROLL_ANGLE], 0}, {"ROL ANG", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ROLL_ANGLE], 0},
{"ARMED TIME", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_ARMED_TIME], 0},
{"HEADING", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_HEADING], 0}, {"HEADING", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_HEADING], 0},
{"VARIO", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_VARIO], 0}, {"VARIO", OME_VISIBLE, NULL, &osdConfig_item_pos[OSD_NUMERICAL_VARIO], 0},
{"BACK", OME_Back, NULL, NULL, 0}, {"BACK", OME_Back, NULL, NULL, 0},
@ -156,6 +110,100 @@ CMS_Menu menuOsdActiveElems = {
.onGlobalExit = NULL, .onGlobalExit = NULL,
.entries = menuOsdActiveElemsEntries .entries = menuOsdActiveElemsEntries
}; };
static uint8_t osdConfig_rssi_alarm;
static uint16_t osdConfig_cap_alarm;
static uint16_t osdConfig_alt_alarm;
static long menuAlarmsOnEnter(void)
{
osdConfig_rssi_alarm = osdConfig()->rssi_alarm;
osdConfig_cap_alarm = osdConfig()->cap_alarm;
osdConfig_alt_alarm = osdConfig()->alt_alarm;
return 0;
}
static long menuAlarmsOnExit(const OSD_Entry *self)
{
UNUSED(self);
osdConfigMutable()->rssi_alarm = osdConfig_rssi_alarm;
osdConfigMutable()->cap_alarm = osdConfig_cap_alarm;
osdConfigMutable()->alt_alarm = osdConfig_alt_alarm;
return 0;
}
OSD_Entry menuAlarmsEntries[] =
{
{"--- ALARMS ---", OME_Label, NULL, NULL, 0},
{"RSSI", OME_UINT8, NULL, &(OSD_UINT8_t){&osdConfig_rssi_alarm, 5, 90, 5}, 0},
{"MAIN BAT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_cap_alarm, 50, 30000, 50}, 0},
{"MAX ALT", OME_UINT16, NULL, &(OSD_UINT16_t){&osdConfig_alt_alarm, 1, 200, 1}, 0},
{"BACK", OME_Back, NULL, NULL, 0},
{NULL, OME_END, NULL, NULL, 0}
};
CMS_Menu menuAlarms = {
.GUARD_text = "MENUALARMS",
.GUARD_type = OME_MENU,
.onEnter = menuAlarmsOnEnter,
.onExit = menuAlarmsOnExit,
.onGlobalExit = NULL,
.entries = menuAlarmsEntries,
};
osd_timer_source_e timerSource[OSD_TIMER_COUNT];
osd_timer_precision_e timerPrecision[OSD_TIMER_COUNT];
uint8_t timerAlarm[OSD_TIMER_COUNT];
static long menuTimersOnEnter(void)
{
for (int i = 0; i < OSD_TIMER_COUNT; i++) {
const uint16_t timer = osdConfig()->timers[i];
timerSource[i] = OSD_TIMER_SRC(timer);
timerPrecision[i] = OSD_TIMER_PRECISION(timer);
timerAlarm[i] = OSD_TIMER_ALARM(timer);
}
return 0;
}
static long menuTimersOnExit(const OSD_Entry *self)
{
UNUSED(self);
for (int i = 0; i < OSD_TIMER_COUNT; i++) {
osdConfigMutable()->timers[i] = OSD_TIMER(timerSource[i], timerPrecision[i], timerAlarm[i]);
}
return 0;
}
static const char * osdTimerPrecisionNames[] = {"SCND", "HDTH"};
OSD_Entry menuTimersEntries[] =
{
{"--- TIMERS ---", OME_Label, NULL, NULL, 0},
{"1 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_1], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 },
{"1 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_1], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0},
{"1 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_1], 0, 0xFF, 1}, 0},
{"2 SRC", OME_TAB, NULL, &(OSD_TAB_t){&timerSource[OSD_TIMER_2], OSD_TIMER_SRC_COUNT - 1, osdTimerSourceNames}, 0 },
{"2 PREC", OME_TAB, NULL, &(OSD_TAB_t){&timerPrecision[OSD_TIMER_2], OSD_TIMER_PREC_COUNT - 1, osdTimerPrecisionNames}, 0},
{"2 ALARM", OME_UINT8, NULL, &(OSD_UINT8_t){&timerAlarm[OSD_TIMER_2], 0, 0xFF, 1}, 0},
{"BACK", OME_Back, NULL, NULL, 0},
{NULL, OME_END, NULL, NULL, 0}
};
CMS_Menu menuTimers = {
.GUARD_text = "MENUTIMERS",
.GUARD_type = OME_MENU,
.onEnter = menuTimersOnEnter,
.onExit = menuTimersOnExit,
.onGlobalExit = NULL,
.entries = menuTimersEntries,
};
#endif /* DISABLE_EXTENDED_CMS_OSD_MENU */ #endif /* DISABLE_EXTENDED_CMS_OSD_MENU */
#ifdef USE_MAX7456 #ifdef USE_MAX7456
@ -193,6 +241,8 @@ OSD_Entry cmsx_menuOsdEntries[] =
{"---OSD---", OME_Label, NULL, NULL, 0}, {"---OSD---", OME_Label, NULL, NULL, 0},
#ifndef DISABLE_EXTENDED_CMS_OSD_MENU #ifndef DISABLE_EXTENDED_CMS_OSD_MENU
{"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems, 0}, {"ACTIVE ELEM", OME_Submenu, cmsMenuChange, &menuOsdActiveElems, 0},
{"TIMERS", OME_Submenu, cmsMenuChange, &menuTimers, 0},
{"ALARMS", OME_Submenu, cmsMenuChange, &menuAlarms, 0},
#endif #endif
#ifdef USE_MAX7456 #ifdef USE_MAX7456
{"INVERT", OME_Bool, NULL, &displayPortProfileMax7456_invert, 0}, {"INVERT", OME_Bool, NULL, &displayPortProfileMax7456_invert, 0},

View file

@ -17,5 +17,4 @@
#pragma once #pragma once
extern CMS_Menu cmsx_menuAlarms;
extern CMS_Menu cmsx_menuOsd; extern CMS_Menu cmsx_menuOsd;

View file

@ -840,17 +840,32 @@ static bool mspCommonProcessOutCommand(uint8_t cmdMSP, sbuf_t *dst, mspPostProce
#ifdef OSD #ifdef OSD
// OSD specific, not applicable to OSD slaves. // OSD specific, not applicable to OSD slaves.
// Configuration
sbufWriteU8(dst, osdConfig()->units); sbufWriteU8(dst, osdConfig()->units);
// Alarms
sbufWriteU8(dst, osdConfig()->rssi_alarm); sbufWriteU8(dst, osdConfig()->rssi_alarm);
sbufWriteU16(dst, osdConfig()->cap_alarm); sbufWriteU16(dst, osdConfig()->cap_alarm);
sbufWriteU16(dst, osdConfig()->time_alarm); sbufWriteU16(dst, 0);
sbufWriteU16(dst, osdConfig()->alt_alarm); sbufWriteU16(dst, osdConfig()->alt_alarm);
// Element position and visibility
for (int i = 0; i < OSD_ITEM_COUNT; i++) { for (int i = 0; i < OSD_ITEM_COUNT; i++) {
sbufWriteU16(dst, osdConfig()->item_pos[i]); sbufWriteU16(dst, osdConfig()->item_pos[i]);
} }
// Post flight statistics
sbufWriteU8(dst, OSD_STAT_COUNT);
for (int i = 0; i < OSD_STAT_COUNT; i++ ) { for (int i = 0; i < OSD_STAT_COUNT; i++ ) {
sbufWriteU8(dst, osdConfig()->enabled_stats[i]); sbufWriteU8(dst, osdConfig()->enabled_stats[i]);
} }
// Timers
sbufWriteU8(dst, OSD_TIMER_COUNT);
for (int i = 0; i < OSD_TIMER_COUNT; i++) {
sbufWriteU16(dst, osdConfig()->timers[i]);
}
#endif #endif
break; break;
} }
@ -2154,11 +2169,23 @@ static mspResult_e mspCommonProcessInCommand(uint8_t cmdMSP, sbuf_t *src)
#endif #endif
#if defined(OSD) #if defined(OSD)
osdConfigMutable()->units = sbufReadU8(src); osdConfigMutable()->units = sbufReadU8(src);
// Alarms
osdConfigMutable()->rssi_alarm = sbufReadU8(src); osdConfigMutable()->rssi_alarm = sbufReadU8(src);
osdConfigMutable()->cap_alarm = sbufReadU16(src); osdConfigMutable()->cap_alarm = sbufReadU16(src);
osdConfigMutable()->time_alarm = sbufReadU16(src); sbufReadU16(src); // Skip unused (previously fly timer)
osdConfigMutable()->alt_alarm = sbufReadU16(src); osdConfigMutable()->alt_alarm = sbufReadU16(src);
#endif #endif
} else if ((int8_t)addr == -2) {
#if defined(OSD)
// Timers
uint8_t index = sbufReadU8(src);
if (index > OSD_TIMER_COUNT) {
return MSP_RESULT_ERROR;
}
osdConfigMutable()->timers[index] = sbufReadU16(src);
#endif
return MSP_RESULT_ERROR;
} else { } else {
#if defined(OSD) #if defined(OSD)
const uint16_t value = sbufReadU16(src); const uint16_t value = sbufReadU16(src);
@ -2172,6 +2199,8 @@ static mspResult_e mspCommonProcessInCommand(uint8_t cmdMSP, sbuf_t *src)
} else if (addr < OSD_ITEM_COUNT) { } else if (addr < OSD_ITEM_COUNT) {
/* Set element positions */ /* Set element positions */
osdConfigMutable()->item_pos[addr] = value; osdConfigMutable()->item_pos[addr] = value;
} else {
return MSP_RESULT_ERROR;
} }
#else #else
return MSP_RESULT_ERROR; return MSP_RESULT_ERROR;

View file

@ -634,13 +634,15 @@ const clivalue_t valueTable[] = {
{ "osd_rssi_alarm", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, 100 }, PG_OSD_CONFIG, offsetof(osdConfig_t, rssi_alarm) }, { "osd_rssi_alarm", VAR_UINT8 | MASTER_VALUE, .config.minmax = { 0, 100 }, PG_OSD_CONFIG, offsetof(osdConfig_t, rssi_alarm) },
{ "osd_cap_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 20000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, cap_alarm) }, { "osd_cap_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 20000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, cap_alarm) },
{ "osd_time_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 60 }, PG_OSD_CONFIG, offsetof(osdConfig_t, time_alarm) },
{ "osd_alt_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 10000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, alt_alarm) }, { "osd_alt_alarm", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, 10000 }, PG_OSD_CONFIG, offsetof(osdConfig_t, alt_alarm) },
{ "osd_tim1", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, (uint16_t)0xFFFF }, PG_OSD_CONFIG, offsetof(osdConfig_t, timers[OSD_TIMER_1]) },
{ "osd_tim2", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, (uint16_t)0xFFFF }, PG_OSD_CONFIG, offsetof(osdConfig_t, timers[OSD_TIMER_2]) },
{ "osd_vbat_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_VOLTAGE]) }, { "osd_vbat_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_VOLTAGE]) },
{ "osd_rssi_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_RSSI_VALUE]) }, { "osd_rssi_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_RSSI_VALUE]) },
{ "osd_flytimer_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_FLYTIME]) }, { "osd_tim_1_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ITEM_TIMER_1]) },
{ "osd_ontimer_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ONTIME]) }, { "osd_tim_2_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ITEM_TIMER_2]) },
{ "osd_flymode_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_FLYMODE]) }, { "osd_flymode_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_FLYMODE]) },
{ "osd_throttle_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_THROTTLE_POS]) }, { "osd_throttle_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_THROTTLE_POS]) },
{ "osd_vtx_channel_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_VTX_CHANNEL]) }, { "osd_vtx_channel_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_VTX_CHANNEL]) },
@ -668,7 +670,6 @@ const clivalue_t valueTable[] = {
{ "osd_pit_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_PITCH_ANGLE]) }, { "osd_pit_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_PITCH_ANGLE]) },
{ "osd_rol_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ROLL_ANGLE]) }, { "osd_rol_ang_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ROLL_ANGLE]) },
{ "osd_battery_usage_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_USAGE]) }, { "osd_battery_usage_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_MAIN_BATT_USAGE]) },
{ "osd_arm_time_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_ARMED_TIME]) },
{ "osd_disarmed_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_DISARMED]) }, { "osd_disarmed_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_DISARMED]) },
{ "osd_nheading_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_HEADING]) }, { "osd_nheading_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_HEADING]) },
{ "osd_nvario_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_VARIO]) }, { "osd_nvario_pos", VAR_UINT16 | MASTER_VALUE, .config.minmax = { 0, OSD_POSCFG_MAX }, PG_OSD_CONFIG, offsetof(osdConfig_t, item_pos[OSD_NUMERICAL_VARIO]) },
@ -684,9 +685,9 @@ const clivalue_t valueTable[] = {
{ "osd_stat_max_alt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_MAX_ALTITUDE])}, { "osd_stat_max_alt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_MAX_ALTITUDE])},
{ "osd_stat_bbox", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX])}, { "osd_stat_bbox", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX])},
{ "osd_stat_endbatt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_END_BATTERY])}, { "osd_stat_endbatt", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_END_BATTERY])},
{ "osd_stat_flytime", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_FLYTIME])},
{ "osd_stat_armtime", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_ARMEDTIME])},
{ "osd_stat_bb_no", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX_NUMBER])}, { "osd_stat_bb_no", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_BLACKBOX_NUMBER])},
{ "osd_stat_tim_1", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_TIMER_1])},
{ "osd_stat_tim_2", VAR_UINT8 | MASTER_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_OSD_CONFIG, offsetof(osdConfig_t, enabled_stats[OSD_STAT_TIMER_2])},
#endif #endif
// PG_SYSTEM_CONFIG // PG_SYSTEM_CONFIG

View file

@ -89,6 +89,12 @@
#define VIDEO_BUFFER_CHARS_PAL 480 #define VIDEO_BUFFER_CHARS_PAL 480
const char * const osdTimerSourceNames[] = {
"ON TIME ",
"TOTAL ARM",
"LAST ARM "
};
// Blink control // Blink control
static bool blinkState = true; static bool blinkState = true;
@ -106,7 +112,7 @@ static uint32_t blinkBits[(OSD_ITEM_COUNT + 31)/32];
#define IS_LO(X) (rcData[X] < 1250) #define IS_LO(X) (rcData[X] < 1250)
#define IS_MID(X) (rcData[X] > 1250 && rcData[X] < 1750) #define IS_MID(X) (rcData[X] > 1250 && rcData[X] < 1750)
static uint16_t flyTime = 0; static timeUs_t flyTime = 0;
static uint8_t statRssi; static uint8_t statRssi;
typedef struct statistic_s { typedef struct statistic_s {
@ -116,7 +122,7 @@ typedef struct statistic_s {
int16_t min_rssi; int16_t min_rssi;
int16_t max_altitude; int16_t max_altitude;
int16_t max_distance; int16_t max_distance;
uint16_t armed_time; timeUs_t armed_time;
} statistic_t; } statistic_t;
static statistic_t stats; static statistic_t stats;
@ -226,6 +232,65 @@ static uint8_t osdGetDirectionSymbolFromHeading(int heading)
return SYM_ARROW_SOUTH + heading; return SYM_ARROW_SOUTH + heading;
} }
static char osdGetTimerSymbol(osd_timer_source_e src)
{
switch (src) {
case OSD_TIMER_SRC_ON:
return SYM_ON_M;
case OSD_TIMER_SRC_TOTAL_ARMED:
case OSD_TIMER_SRC_LAST_ARMED:
return SYM_FLY_M;
default:
return ' ';
}
}
static timeUs_t osdGetTimerValue(osd_timer_source_e src)
{
switch (src) {
case OSD_TIMER_SRC_ON:
return micros();
case OSD_TIMER_SRC_TOTAL_ARMED:
return flyTime;
case OSD_TIMER_SRC_LAST_ARMED:
return stats.armed_time;
default:
return 0;
}
}
STATIC_UNIT_TESTED void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time)
{
int seconds = time / 1000000;
const int minutes = seconds / 60;
seconds = seconds % 60;
switch (precision) {
case OSD_TIMER_PREC_SECOND:
default:
tfp_sprintf(buff, "%02d:%02d", minutes, seconds);
break;
case OSD_TIMER_PREC_HUNDREDTHS:
{
const int hundredths = (time / 10000) % 100;
tfp_sprintf(buff, "%02d:%02d.%02d", minutes, seconds, hundredths);
break;
}
}
}
STATIC_UNIT_TESTED void osdFormatTimer(char *buff, bool showSymbol, int timerIndex)
{
const uint16_t timer = osdConfig()->timers[timerIndex];
const uint8_t src = OSD_TIMER_SRC(timer);
if (showSymbol) {
*(buff++) = osdGetTimerSymbol(src);
}
osdFormatTime(buff, OSD_TIMER_PRECISION(timer), osdGetTimerValue(src));
}
static void osdDrawSingleElement(uint8_t item) static void osdDrawSingleElement(uint8_t item)
{ {
if (!VISIBLE(osdConfig()->item_pos[item]) || BLINK(item)) { if (!VISIBLE(osdConfig()->item_pos[item]) || BLINK(item)) {
@ -235,7 +300,7 @@ static void osdDrawSingleElement(uint8_t item)
uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]); uint8_t elemPosX = OSD_X(osdConfig()->item_pos[item]);
uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]); uint8_t elemPosY = OSD_Y(osdConfig()->item_pos[item]);
uint8_t elemOffsetX = 0; uint8_t elemOffsetX = 0;
char buff[32]; char buff[OSD_ELEMENT_BUFFER_LENGTH];
switch (item) { switch (item) {
case OSD_RSSI_VALUE: case OSD_RSSI_VALUE:
@ -350,24 +415,14 @@ static void osdDrawSingleElement(uint8_t item)
break; break;
} }
case OSD_ONTIME: case OSD_ITEM_TIMER_1:
case OSD_ITEM_TIMER_2:
{ {
const uint32_t seconds = micros() / 1000000; const int timer = item - OSD_ITEM_TIMER_1;
buff[0] = SYM_ON_M; osdFormatTimer(buff, true, timer);
tfp_sprintf(buff + 1, "%02d:%02d", seconds / 60, seconds % 60);
break; break;
} }
case OSD_FLYTIME:
buff[0] = SYM_FLY_M;
tfp_sprintf(buff + 1, "%02d:%02d", flyTime / 60, flyTime % 60);
break;
case OSD_ARMED_TIME:
buff[0] = SYM_FLY_M;
tfp_sprintf(buff + 1, "%02d:%02d", stats.armed_time / 60, stats.armed_time % 60);
break;
case OSD_FLYMODE: case OSD_FLYMODE:
{ {
char *p = "ACRO"; char *p = "ACRO";
@ -658,8 +713,8 @@ static void osdDrawElements(void)
osdDrawSingleElement(OSD_MAIN_BATT_VOLTAGE); osdDrawSingleElement(OSD_MAIN_BATT_VOLTAGE);
osdDrawSingleElement(OSD_RSSI_VALUE); osdDrawSingleElement(OSD_RSSI_VALUE);
osdDrawSingleElement(OSD_CROSSHAIRS); osdDrawSingleElement(OSD_CROSSHAIRS);
osdDrawSingleElement(OSD_FLYTIME); osdDrawSingleElement(OSD_ITEM_TIMER_1);
osdDrawSingleElement(OSD_ONTIME); osdDrawSingleElement(OSD_ITEM_TIMER_2);
osdDrawSingleElement(OSD_FLYMODE); osdDrawSingleElement(OSD_FLYMODE);
osdDrawSingleElement(OSD_THROTTLE_POS); osdDrawSingleElement(OSD_THROTTLE_POS);
osdDrawSingleElement(OSD_VTX_CHANNEL); osdDrawSingleElement(OSD_VTX_CHANNEL);
@ -678,7 +733,6 @@ static void osdDrawElements(void)
osdDrawSingleElement(OSD_PITCH_ANGLE); osdDrawSingleElement(OSD_PITCH_ANGLE);
osdDrawSingleElement(OSD_ROLL_ANGLE); osdDrawSingleElement(OSD_ROLL_ANGLE);
osdDrawSingleElement(OSD_MAIN_BATT_USAGE); osdDrawSingleElement(OSD_MAIN_BATT_USAGE);
osdDrawSingleElement(OSD_ARMED_TIME);
osdDrawSingleElement(OSD_DISARMED); osdDrawSingleElement(OSD_DISARMED);
osdDrawSingleElement(OSD_NUMERICAL_HEADING); osdDrawSingleElement(OSD_NUMERICAL_HEADING);
osdDrawSingleElement(OSD_NUMERICAL_VARIO); osdDrawSingleElement(OSD_NUMERICAL_VARIO);
@ -710,8 +764,8 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
osdConfig->item_pos[OSD_CROSSHAIRS] = OSD_POS(8, 6) | VISIBLE_FLAG; osdConfig->item_pos[OSD_CROSSHAIRS] = OSD_POS(8, 6) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | VISIBLE_FLAG; osdConfig->item_pos[OSD_ARTIFICIAL_HORIZON] = OSD_POS(8, 6) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | VISIBLE_FLAG; osdConfig->item_pos[OSD_HORIZON_SIDEBARS] = OSD_POS(8, 6) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_ONTIME] = OSD_POS(22, 1) | VISIBLE_FLAG; osdConfig->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(22, 1) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_FLYTIME] = OSD_POS(1, 1) | VISIBLE_FLAG; osdConfig->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_FLYMODE] = OSD_POS(13, 10) | VISIBLE_FLAG; osdConfig->item_pos[OSD_FLYMODE] = OSD_POS(13, 10) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_CRAFT_NAME] = OSD_POS(10, 11) | VISIBLE_FLAG; osdConfig->item_pos[OSD_CRAFT_NAME] = OSD_POS(10, 11) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_THROTTLE_POS] = OSD_POS(1, 7) | VISIBLE_FLAG; osdConfig->item_pos[OSD_THROTTLE_POS] = OSD_POS(1, 7) | VISIBLE_FLAG;
@ -737,7 +791,6 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
osdConfig->item_pos[OSD_HOME_DIR] = OSD_POS(14, 9) | VISIBLE_FLAG; osdConfig->item_pos[OSD_HOME_DIR] = OSD_POS(14, 9) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_COMPASS_BAR] = OSD_POS(10, 8) | VISIBLE_FLAG; osdConfig->item_pos[OSD_COMPASS_BAR] = OSD_POS(10, 8) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_MAIN_BATT_USAGE] = OSD_POS(8, 12) | VISIBLE_FLAG; osdConfig->item_pos[OSD_MAIN_BATT_USAGE] = OSD_POS(8, 12) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_ARMED_TIME] = OSD_POS(1, 2) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_DISARMED] = OSD_POS(10, 4) | VISIBLE_FLAG; osdConfig->item_pos[OSD_DISARMED] = OSD_POS(10, 4) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_NUMERICAL_HEADING] = OSD_POS(23, 9) | VISIBLE_FLAG; osdConfig->item_pos[OSD_NUMERICAL_HEADING] = OSD_POS(23, 9) | VISIBLE_FLAG;
osdConfig->item_pos[OSD_NUMERICAL_VARIO] = OSD_POS(23, 8) | VISIBLE_FLAG; osdConfig->item_pos[OSD_NUMERICAL_VARIO] = OSD_POS(23, 8) | VISIBLE_FLAG;
@ -752,16 +805,18 @@ void pgResetFn_osdConfig(osdConfig_t *osdConfig)
osdConfig->enabled_stats[OSD_STAT_MAX_ALTITUDE] = false; osdConfig->enabled_stats[OSD_STAT_MAX_ALTITUDE] = false;
osdConfig->enabled_stats[OSD_STAT_BLACKBOX] = true; osdConfig->enabled_stats[OSD_STAT_BLACKBOX] = true;
osdConfig->enabled_stats[OSD_STAT_END_BATTERY] = false; osdConfig->enabled_stats[OSD_STAT_END_BATTERY] = false;
osdConfig->enabled_stats[OSD_STAT_FLYTIME] = false;
osdConfig->enabled_stats[OSD_STAT_ARMEDTIME] = true;
osdConfig->enabled_stats[OSD_STAT_MAX_DISTANCE] = false; osdConfig->enabled_stats[OSD_STAT_MAX_DISTANCE] = false;
osdConfig->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = true; osdConfig->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = true;
osdConfig->enabled_stats[OSD_STAT_TIMER_1] = false;
osdConfig->enabled_stats[OSD_STAT_TIMER_2] = true;
osdConfig->units = OSD_UNIT_METRIC; osdConfig->units = OSD_UNIT_METRIC;
osdConfig->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_SECOND, 10);
osdConfig->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 10);
osdConfig->rssi_alarm = 20; osdConfig->rssi_alarm = 20;
osdConfig->cap_alarm = 2200; osdConfig->cap_alarm = 2200;
osdConfig->time_alarm = 10; // in minutes
osdConfig->alt_alarm = 100; // meters or feet depend on configuration osdConfig->alt_alarm = 100; // meters or feet depend on configuration
} }
@ -837,10 +892,15 @@ void osdUpdateAlarms(void)
else else
CLR_BLINK(OSD_GPS_SATS); CLR_BLINK(OSD_GPS_SATS);
if (flyTime / 60 >= osdConfig()->time_alarm && ARMING_FLAG(ARMED)) for (int i = 0; i < OSD_TIMER_COUNT; i++) {
SET_BLINK(OSD_FLYTIME); const uint16_t timer = osdConfig()->timers[i];
else const timeUs_t time = osdGetTimerValue(OSD_TIMER_SRC(timer));
CLR_BLINK(OSD_FLYTIME); const timeUs_t alarmTime = OSD_TIMER_ALARM(timer) * 60000000; // convert from minutes to us
if (alarmTime != 0 && time >= alarmTime)
SET_BLINK(OSD_ITEM_TIMER_1 + i);
else
CLR_BLINK(OSD_ITEM_TIMER_1 + i);
}
if (getMAhDrawn() >= osdConfig()->cap_alarm) { if (getMAhDrawn() >= osdConfig()->cap_alarm) {
SET_BLINK(OSD_MAH_DRAWN); SET_BLINK(OSD_MAH_DRAWN);
@ -862,11 +922,12 @@ void osdResetAlarms(void)
CLR_BLINK(OSD_MAIN_BATT_VOLTAGE); CLR_BLINK(OSD_MAIN_BATT_VOLTAGE);
CLR_BLINK(OSD_WARNINGS); CLR_BLINK(OSD_WARNINGS);
CLR_BLINK(OSD_GPS_SATS); CLR_BLINK(OSD_GPS_SATS);
CLR_BLINK(OSD_FLYTIME);
CLR_BLINK(OSD_MAH_DRAWN); CLR_BLINK(OSD_MAH_DRAWN);
CLR_BLINK(OSD_ALTITUDE); CLR_BLINK(OSD_ALTITUDE);
CLR_BLINK(OSD_AVG_CELL_VOLTAGE); CLR_BLINK(OSD_AVG_CELL_VOLTAGE);
CLR_BLINK(OSD_MAIN_BATT_USAGE); CLR_BLINK(OSD_MAIN_BATT_USAGE);
CLR_BLINK(OSD_ITEM_TIMER_1);
CLR_BLINK(OSD_ITEM_TIMER_2);
} }
static void osdResetStats(void) static void osdResetStats(void)
@ -967,14 +1028,14 @@ static void osdShowStats(void)
displayClearScreen(osdDisplayPort); displayClearScreen(osdDisplayPort);
displayWrite(osdDisplayPort, 2, top++, " --- STATS ---"); displayWrite(osdDisplayPort, 2, top++, " --- STATS ---");
if (osdConfig()->enabled_stats[OSD_STAT_ARMEDTIME]) { if (osdConfig()->enabled_stats[OSD_STAT_TIMER_1]) {
tfp_sprintf(buff, "%02d:%02d", stats.armed_time / 60, stats.armed_time % 60); osdFormatTimer(buff, false, OSD_TIMER_1);
osdDisplayStatisticLabel(top++, "ARMED TIME", buff); osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1])], buff);
} }
if (osdConfig()->enabled_stats[OSD_STAT_FLYTIME]) { if (osdConfig()->enabled_stats[OSD_STAT_TIMER_2]) {
tfp_sprintf(buff, "%02d:%02d", flyTime / 60, flyTime % 60); osdFormatTimer(buff, false, OSD_TIMER_2);
osdDisplayStatisticLabel(top++, "FLY TIME", buff); osdDisplayStatisticLabel(top++, osdTimerSourceNames[OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2])], buff);
} }
if (osdConfig()->enabled_stats[OSD_STAT_MAX_SPEED] && STATE(GPS_FIX)) { if (osdConfig()->enabled_stats[OSD_STAT_MAX_SPEED] && STATE(GPS_FIX)) {
@ -1046,8 +1107,7 @@ static void osdShowArmed(void)
STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs) STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
{ {
static uint8_t lastSec = 0; static timeUs_t lastTimeUs = 0;
uint8_t sec;
// detect arm/disarm // detect arm/disarm
if (armState != ARMING_FLAG(ARMED)) { if (armState != ARMING_FLAG(ARMED)) {
@ -1067,13 +1127,12 @@ STATIC_UNIT_TESTED void osdRefresh(timeUs_t currentTimeUs)
osdUpdateStats(); osdUpdateStats();
sec = currentTimeUs / 1000000; if (ARMING_FLAG(ARMED)) {
timeUs_t deltaT = currentTimeUs - lastTimeUs;
if (ARMING_FLAG(ARMED) && sec != lastSec) { flyTime += deltaT;
flyTime++; stats.armed_time += deltaT;
stats.armed_time++;
lastSec = sec;
} }
lastTimeUs = currentTimeUs;
if (resumeRefreshAt) { if (resumeRefreshAt) {
if (cmp32(currentTimeUs, resumeRefreshAt) < 0) { if (cmp32(currentTimeUs, resumeRefreshAt) < 0) {

View file

@ -21,27 +21,38 @@
#include "common/time.h" #include "common/time.h"
#include "config/parameter_group.h" #include "config/parameter_group.h"
#define OSD_NUM_TIMER_TYPES 3
extern const char * const osdTimerSourceNames[OSD_NUM_TIMER_TYPES];
#define OSD_ELEMENT_BUFFER_LENGTH 32
#define VISIBLE_FLAG 0x0800 #define VISIBLE_FLAG 0x0800
#define VISIBLE(x) (x & VISIBLE_FLAG) #define VISIBLE(x) (x & VISIBLE_FLAG)
#define OSD_POS_MAX 0x3FF #define OSD_POS_MAX 0x3FF
#define OSD_POSCFG_MAX (VISIBLE_FLAG|0x3FF) // For CLI values #define OSD_POSCFG_MAX (VISIBLE_FLAG|0x3FF) // For CLI values
// Character coordinate // Character coordinate
#define OSD_POSITION_BITS 5 // 5 bits gives a range 0-31 #define OSD_POSITION_BITS 5 // 5 bits gives a range 0-31
#define OSD_POSITION_XY_MASK ((1 << OSD_POSITION_BITS) - 1) #define OSD_POSITION_XY_MASK ((1 << OSD_POSITION_BITS) - 1)
#define OSD_POS(x,y) ((x & OSD_POSITION_XY_MASK) | ((y & OSD_POSITION_XY_MASK) << OSD_POSITION_BITS)) #define OSD_POS(x,y) ((x & OSD_POSITION_XY_MASK) | ((y & OSD_POSITION_XY_MASK) << OSD_POSITION_BITS))
#define OSD_X(x) (x & OSD_POSITION_XY_MASK) #define OSD_X(x) (x & OSD_POSITION_XY_MASK)
#define OSD_Y(x) ((x >> OSD_POSITION_BITS) & OSD_POSITION_XY_MASK) #define OSD_Y(x) ((x >> OSD_POSITION_BITS) & OSD_POSITION_XY_MASK)
// Timer configuration
// Stored as 15[alarm:8][precision:4][source:4]0
#define OSD_TIMER(src, prec, alarm) ((src & 0x0F) | ((prec & 0x0F) << 4) | ((alarm & 0xFF ) << 8))
#define OSD_TIMER_SRC(timer) (timer & 0x0F)
#define OSD_TIMER_PRECISION(timer) ((timer >> 4) & 0x0F)
#define OSD_TIMER_ALARM(timer) ((timer >> 8) & 0xFF)
typedef enum { typedef enum {
OSD_RSSI_VALUE, OSD_RSSI_VALUE,
OSD_MAIN_BATT_VOLTAGE, OSD_MAIN_BATT_VOLTAGE,
OSD_CROSSHAIRS, OSD_CROSSHAIRS,
OSD_ARTIFICIAL_HORIZON, OSD_ARTIFICIAL_HORIZON,
OSD_HORIZON_SIDEBARS, OSD_HORIZON_SIDEBARS,
OSD_ONTIME, OSD_ITEM_TIMER_1,
OSD_FLYTIME, OSD_ITEM_TIMER_2,
OSD_FLYMODE, OSD_FLYMODE,
OSD_CRAFT_NAME, OSD_CRAFT_NAME,
OSD_THROTTLE_POS, OSD_THROTTLE_POS,
@ -64,7 +75,6 @@ typedef enum {
OSD_PITCH_ANGLE, OSD_PITCH_ANGLE,
OSD_ROLL_ANGLE, OSD_ROLL_ANGLE,
OSD_MAIN_BATT_USAGE, OSD_MAIN_BATT_USAGE,
OSD_ARMED_TIME,
OSD_DISARMED, OSD_DISARMED,
OSD_HOME_DIR, OSD_HOME_DIR,
OSD_HOME_DIST, OSD_HOME_DIST,
@ -85,8 +95,8 @@ typedef enum {
OSD_STAT_MAX_ALTITUDE, OSD_STAT_MAX_ALTITUDE,
OSD_STAT_BLACKBOX, OSD_STAT_BLACKBOX,
OSD_STAT_END_BATTERY, OSD_STAT_END_BATTERY,
OSD_STAT_FLYTIME, OSD_STAT_TIMER_1,
OSD_STAT_ARMEDTIME, OSD_STAT_TIMER_2,
OSD_STAT_MAX_DISTANCE, OSD_STAT_MAX_DISTANCE,
OSD_STAT_BLACKBOX_NUMBER, OSD_STAT_BLACKBOX_NUMBER,
OSD_STAT_COUNT // MUST BE LAST OSD_STAT_COUNT // MUST BE LAST
@ -97,6 +107,25 @@ typedef enum {
OSD_UNIT_METRIC OSD_UNIT_METRIC
} osd_unit_e; } osd_unit_e;
typedef enum {
OSD_TIMER_1,
OSD_TIMER_2,
OSD_TIMER_COUNT
} osd_timer_e;
typedef enum {
OSD_TIMER_SRC_ON,
OSD_TIMER_SRC_TOTAL_ARMED,
OSD_TIMER_SRC_LAST_ARMED,
OSD_TIMER_SRC_COUNT
} osd_timer_source_e;
typedef enum {
OSD_TIMER_PREC_SECOND,
OSD_TIMER_PREC_HUNDREDTHS,
OSD_TIMER_PREC_COUNT
} osd_timer_precision_e;
typedef struct osdConfig_s { typedef struct osdConfig_s {
uint16_t item_pos[OSD_ITEM_COUNT]; uint16_t item_pos[OSD_ITEM_COUNT];
bool enabled_stats[OSD_STAT_COUNT]; bool enabled_stats[OSD_STAT_COUNT];
@ -104,10 +133,11 @@ typedef struct osdConfig_s {
// Alarms // Alarms
uint8_t rssi_alarm; uint8_t rssi_alarm;
uint16_t cap_alarm; uint16_t cap_alarm;
uint16_t time_alarm;
uint16_t alt_alarm; uint16_t alt_alarm;
osd_unit_e units; osd_unit_e units;
uint16_t timers[OSD_TIMER_COUNT];
} osdConfig_t; } osdConfig_t;
extern uint32_t resumeRefreshAt; extern uint32_t resumeRefreshAt;

View file

@ -47,6 +47,8 @@ extern "C" {
#include "rx/rx.h" #include "rx/rx.h"
void osdRefresh(timeUs_t currentTimeUs); void osdRefresh(timeUs_t currentTimeUs);
void osdFormatTime(char * buff, osd_timer_precision_e precision, timeUs_t time);
void osdFormatTimer(char *buff, bool showSymbol, int timerIndex);
uint16_t rssi; uint16_t rssi;
attitudeEulerAngles_t attitude; attitudeEulerAngles_t attitude;
@ -269,8 +271,8 @@ TEST(OsdTest, TestStatsImperial)
osdConfigMutable()->enabled_stats[OSD_STAT_MAX_ALTITUDE] = true; osdConfigMutable()->enabled_stats[OSD_STAT_MAX_ALTITUDE] = true;
osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX] = false; osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX] = false;
osdConfigMutable()->enabled_stats[OSD_STAT_END_BATTERY] = true; osdConfigMutable()->enabled_stats[OSD_STAT_END_BATTERY] = true;
osdConfigMutable()->enabled_stats[OSD_STAT_FLYTIME] = true; osdConfigMutable()->enabled_stats[OSD_STAT_TIMER_1] = true;
osdConfigMutable()->enabled_stats[OSD_STAT_ARMEDTIME] = true; osdConfigMutable()->enabled_stats[OSD_STAT_TIMER_2] = true;
osdConfigMutable()->enabled_stats[OSD_STAT_MAX_DISTANCE] = true; osdConfigMutable()->enabled_stats[OSD_STAT_MAX_DISTANCE] = true;
osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = false; osdConfigMutable()->enabled_stats[OSD_STAT_BLACKBOX_NUMBER] = false;
@ -278,6 +280,14 @@ TEST(OsdTest, TestStatsImperial)
// using imperial unit system // using imperial unit system
osdConfigMutable()->units = OSD_UNIT_IMPERIAL; osdConfigMutable()->units = OSD_UNIT_IMPERIAL;
// and
// this timer 1 configuration
osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_HUNDREDTHS, 0);
// and
// this timer 2 configuration
osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_LAST_ARMED, OSD_TIMER_PREC_SECOND, 0);
// and // and
// a GPS fix is present // a GPS fix is present
stateFlags |= GPS_FIX | GPS_FIX_HOME; stateFlags |= GPS_FIX | GPS_FIX_HOME;
@ -318,14 +328,15 @@ TEST(OsdTest, TestStatsImperial)
// then // then
// statistics screen should display the following // statistics screen should display the following
displayPortTestBufferSubstring(2, 3, "ARMED TIME : 00:04"); int row = 3;
displayPortTestBufferSubstring(2, 4, "FLY TIME : 00:07"); displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:05.00");
displayPortTestBufferSubstring(2, 5, "MAX SPEED : 28"); displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:03");
displayPortTestBufferSubstring(2, 6, "MAX DISTANCE : 328%c", SYM_FT); displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
displayPortTestBufferSubstring(2, 7, "MIN BATTERY : 14.7%c", SYM_VOLT); displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 328%c", SYM_FT);
displayPortTestBufferSubstring(2, 8, "END BATTERY : 15.2%c", SYM_VOLT); displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.7%c", SYM_VOLT);
displayPortTestBufferSubstring(2, 9, "MIN RSSI : 25%%"); displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.2%c", SYM_VOLT);
displayPortTestBufferSubstring(2, 10, "MAX ALTITUDE : 6.5%c", SYM_FT); displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 6.5%c", SYM_FT);
} }
/* /*
@ -367,14 +378,15 @@ TEST(OsdTest, TestStatsMetric)
// then // then
// statistics screen should display the following // statistics screen should display the following
displayPortTestBufferSubstring(2, 3, "ARMED TIME : 00:02"); int row = 3;
displayPortTestBufferSubstring(2, 4, "FLY TIME : 00:09"); displayPortTestBufferSubstring(2, row++, "TOTAL ARM : 00:07.50");
displayPortTestBufferSubstring(2, 5, "MAX SPEED : 28"); displayPortTestBufferSubstring(2, row++, "LAST ARM : 00:02");
displayPortTestBufferSubstring(2, 6, "MAX DISTANCE : 100%c", SYM_M); displayPortTestBufferSubstring(2, row++, "MAX SPEED : 28");
displayPortTestBufferSubstring(2, 7, "MIN BATTERY : 14.7%c", SYM_VOLT); displayPortTestBufferSubstring(2, row++, "MAX DISTANCE : 100%c", SYM_M);
displayPortTestBufferSubstring(2, 8, "END BATTERY : 15.2%c", SYM_VOLT); displayPortTestBufferSubstring(2, row++, "MIN BATTERY : 14.7%c", SYM_VOLT);
displayPortTestBufferSubstring(2, 9, "MIN RSSI : 25%%"); displayPortTestBufferSubstring(2, row++, "END BATTERY : 15.2%c", SYM_VOLT);
displayPortTestBufferSubstring(2, 10, "MAX ALTITUDE : 2.0%c", SYM_M); displayPortTestBufferSubstring(2, row++, "MIN RSSI : 25%%");
displayPortTestBufferSubstring(2, row++, "MAX ALTITUDE : 2.0%c", SYM_M);
} }
/* /*
@ -390,16 +402,30 @@ TEST(OsdTest, TestAlarms)
// the following OSD elements are visible // the following OSD elements are visible
osdConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_RSSI_VALUE] = OSD_POS(8, 1) | VISIBLE_FLAG;
osdConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_MAIN_BATT_VOLTAGE] = OSD_POS(12, 1) | VISIBLE_FLAG;
osdConfigMutable()->item_pos[OSD_FLYTIME] = OSD_POS(1, 1) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_ITEM_TIMER_1] = OSD_POS(20, 1) | VISIBLE_FLAG;
osdConfigMutable()->item_pos[OSD_ITEM_TIMER_2] = OSD_POS(1, 1) | VISIBLE_FLAG;
osdConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | VISIBLE_FLAG; osdConfigMutable()->item_pos[OSD_ALTITUDE] = OSD_POS(23, 7) | VISIBLE_FLAG;
// and // and
// this set of alarm values // this set of alarm values
osdConfigMutable()->rssi_alarm = 20; osdConfigMutable()->rssi_alarm = 20;
osdConfigMutable()->cap_alarm = 2200; osdConfigMutable()->cap_alarm = 2200;
osdConfigMutable()->time_alarm = 1; // in minutes
osdConfigMutable()->alt_alarm = 100; // meters osdConfigMutable()->alt_alarm = 100; // meters
// and
// this timer 1 configuration
osdConfigMutable()->timers[OSD_TIMER_1] = OSD_TIMER(OSD_TIMER_SRC_ON, OSD_TIMER_PREC_HUNDREDTHS, 2);
EXPECT_EQ(OSD_TIMER_SRC_ON, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_1]));
EXPECT_EQ(OSD_TIMER_PREC_HUNDREDTHS, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_1]));
EXPECT_EQ(2, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_1]));
// and
// this timer 2 configuration
osdConfigMutable()->timers[OSD_TIMER_2] = OSD_TIMER(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_PREC_SECOND, 1);
EXPECT_EQ(OSD_TIMER_SRC_TOTAL_ARMED, OSD_TIMER_SRC(osdConfig()->timers[OSD_TIMER_2]));
EXPECT_EQ(OSD_TIMER_PREC_SECOND, OSD_TIMER_PRECISION(osdConfig()->timers[OSD_TIMER_2]));
EXPECT_EQ(1, OSD_TIMER_ALARM(osdConfig()->timers[OSD_TIMER_2]));
// and // and
// using the metric unit system // using the metric unit system
osdConfigMutable()->units = OSD_UNIT_METRIC; osdConfigMutable()->units = OSD_UNIT_METRIC;
@ -421,6 +447,7 @@ TEST(OsdTest, TestAlarms)
displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI); displayPortTestBufferSubstring(8, 1, "%c99", SYM_RSSI);
displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT); displayPortTestBufferSubstring(12, 1, "%c16.8%c", SYM_BATT_FULL, SYM_VOLT);
displayPortTestBufferSubstring(1, 1, "%c00:", SYM_FLY_M); // only test the minute part of the timer displayPortTestBufferSubstring(1, 1, "%c00:", SYM_FLY_M); // only test the minute part of the timer
displayPortTestBufferSubstring(20, 1, "%c01:", SYM_ON_M); // only test the minute part of the timer
displayPortTestBufferSubstring(23, 7, " 0.0%c", SYM_M); displayPortTestBufferSubstring(23, 7, " 0.0%c", SYM_M);
} }
@ -430,11 +457,8 @@ TEST(OsdTest, TestAlarms)
simulationBatteryState = BATTERY_CRITICAL; simulationBatteryState = BATTERY_CRITICAL;
simulationBatteryVoltage = 135; simulationBatteryVoltage = 135;
simulationAltitude = 12000; simulationAltitude = 12000;
// Fly timer is incremented on periodic calls to osdRefresh, can't simply just increment the simulated system clock simulationTime += 60e6;
for (int i = 0; i < 60; i++) { osdRefresh(simulationTime);
simulationTime += 1e6;
osdRefresh(simulationTime);
}
// then // then
// elements showing values in alarm range should flash // elements showing values in alarm range should flash
@ -451,6 +475,7 @@ TEST(OsdTest, TestAlarms)
displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI); displayPortTestBufferSubstring(8, 1, "%c12", SYM_RSSI);
displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT); displayPortTestBufferSubstring(12, 1, "%c13.5%c", SYM_MAIN_BATT, SYM_VOLT);
displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer displayPortTestBufferSubstring(1, 1, "%c01:", SYM_FLY_M); // only test the minute part of the timer
displayPortTestBufferSubstring(20, 1, "%c02:", SYM_ON_M); // only test the minute part of the timer
displayPortTestBufferSubstring(23, 7, " 120.0%c", SYM_M); displayPortTestBufferSubstring(23, 7, " 120.0%c", SYM_M);
} else { } else {
displayPortTestBufferIsEmpty(); displayPortTestBufferIsEmpty();
@ -492,6 +517,58 @@ TEST(OsdTest, TestElementRssi)
displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI); displayPortTestBufferSubstring(8, 1, "%c50", SYM_RSSI);
} }
/*
* Tests the time string formatting function with a series of precision settings and time values.
*/
TEST(OsdTest, TestFormatTimeString)
{
char buff[OSD_ELEMENT_BUFFER_LENGTH];
/* Seconds precision, 0 us */
osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0);
EXPECT_EQ(0, strcmp("00:00", buff));
/* Seconds precision, 0.9 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 0.9e6);
EXPECT_EQ(0, strcmp("00:00", buff));
/* Seconds precision, 10 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 10e6);
EXPECT_EQ(0, strcmp("00:10", buff));
/* Seconds precision, 1 minute */
osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 60e6);
EXPECT_EQ(0, strcmp("01:00", buff));
/* Seconds precision, 1 minute 59 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_SECOND, 119e6);
EXPECT_EQ(0, strcmp("01:59", buff));
/* Hundredths precision, 0 us */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0);
EXPECT_EQ(0, strcmp("00:00.00", buff));
/* Hundredths precision, 10 milliseconds (one 100th of a second) */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e3);
EXPECT_EQ(0, strcmp("00:00.01", buff));
/* Hundredths precision, 0.9 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 0.9e6);
EXPECT_EQ(0, strcmp("00:00.90", buff));
/* Hundredths precision, 10 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 10e6);
EXPECT_EQ(0, strcmp("00:10.00", buff));
/* Hundredths precision, 1 minute */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 60e6);
EXPECT_EQ(0, strcmp("01:00.00", buff));
/* Hundredths precision, 1 minute 59 seconds */
osdFormatTime(buff, OSD_TIMER_PREC_HUNDREDTHS, 119e6);
EXPECT_EQ(0, strcmp("01:59.00", buff));
}
// STUBS // STUBS
extern "C" { extern "C" {