AP_OSD: added OSD_TYPE2 param to enable dual OSDs backend support

Co-authored-by:HWurzburg(hurzburg@yahoo.com)

up to 2 OSD instances can run at the same time sharing a single OSD thread

)
This commit is contained in:
yaapu 2023-07-07 13:30:54 -05:00 committed by Peter Barker
parent 3923dcb63e
commit 4f69f9cc23
8 changed files with 164 additions and 41 deletions

View File

@ -232,6 +232,15 @@ const AP_Param::GroupInfo AP_OSD::var_info[] = {
AP_SUBGROUPINFO2(screen[2], "3_", 29, AP_OSD, AP_OSD_Screen), AP_SUBGROUPINFO2(screen[2], "3_", 29, AP_OSD, AP_OSD_Screen),
AP_SUBGROUPINFO2(screen[3], "4_", 30, AP_OSD, AP_OSD_Screen), AP_SUBGROUPINFO2(screen[3], "4_", 30, AP_OSD, AP_OSD_Screen),
#endif #endif
// @Param: _TYPE2
// @DisplayName: OSD type 2
// @Description: OSD type 2. TXONLY makes the OSD parameter selection available to other modules even if there is no native OSD support on the board, for instance CRSF.
// @Values: 0:None,1:MAX7456,2:SITL,3:MSP,4:TXONLY,5:MSP_DISPLAYPORT
// @User: Standard
// @RebootRequired: True
AP_GROUPINFO("_TYPE2", 32, AP_OSD, osd_type2, 0),
AP_GROUPEND AP_GROUPEND
}; };
@ -263,7 +272,29 @@ AP_OSD::AP_OSD()
void AP_OSD::init() void AP_OSD::init()
{ {
switch (osd_types(osd_type.get())) { const AP_OSD::osd_types types[OSD_MAX_INSTANCES] = {
osd_types(osd_type.get()),
osd_types(osd_type2.get())
};
for (uint8_t instance = 0; instance < OSD_MAX_INSTANCES; instance++) {
if (init_backend(types[instance], instance)) {
_backend_count++;
}
}
if (_backend_count > 0) {
hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_OSD::osd_thread, void), "OSD", 1280, AP_HAL::Scheduler::PRIORITY_IO, 1);
}
}
bool AP_OSD::init_backend(const AP_OSD::osd_types type, const uint8_t instance)
{
// check if we can run this backend instance in parallel with backend instance 0
if (instance > 0) {
if (_backends[0] && !_backends[0]->is_compatible_with_backend_type(type)) {
return false;
}
}
switch (type) {
case OSD_NONE: case OSD_NONE:
case OSD_TXONLY: case OSD_TXONLY:
default: default:
@ -276,9 +307,9 @@ void AP_OSD::init()
break; break;
} }
#if HAL_WITH_OSD_BITMAP #if HAL_WITH_OSD_BITMAP
backend = AP_OSD_MAX7456::probe(*this, std::move(spi_dev)); _backends[instance] = AP_OSD_MAX7456::probe(*this, std::move(spi_dev));
#endif #endif
if (backend == nullptr) { if (_backends[instance] == nullptr) {
break; break;
} }
DEV_PRINTF("Started MAX7456 OSD\n"); DEV_PRINTF("Started MAX7456 OSD\n");
@ -288,8 +319,8 @@ void AP_OSD::init()
#ifdef WITH_SITL_OSD #ifdef WITH_SITL_OSD
case OSD_SITL: { case OSD_SITL: {
backend = AP_OSD_SITL::probe(*this); _backends[instance] = AP_OSD_SITL::probe(*this);
if (backend == nullptr) { if (_backends[instance] == nullptr) {
break; break;
} }
DEV_PRINTF("Started SITL OSD\n"); DEV_PRINTF("Started SITL OSD\n");
@ -297,8 +328,8 @@ void AP_OSD::init()
} }
#endif #endif
case OSD_MSP: { case OSD_MSP: {
backend = AP_OSD_MSP::probe(*this); _backends[instance] = AP_OSD_MSP::probe(*this);
if (backend == nullptr) { if (_backends[instance] == nullptr) {
break; break;
} }
DEV_PRINTF("Started MSP OSD\n"); DEV_PRINTF("Started MSP OSD\n");
@ -306,8 +337,8 @@ void AP_OSD::init()
} }
#if HAL_WITH_MSP_DISPLAYPORT #if HAL_WITH_MSP_DISPLAYPORT
case OSD_MSP_DISPLAYPORT: { case OSD_MSP_DISPLAYPORT: {
backend = AP_OSD_MSP_DisplayPort::probe(*this); _backends[instance] = AP_OSD_MSP_DisplayPort::probe(*this);
if (backend == nullptr) { if (_backends[instance] == nullptr) {
break; break;
} }
DEV_PRINTF("Started MSP DisplayPort OSD\n"); DEV_PRINTF("Started MSP DisplayPort OSD\n");
@ -316,12 +347,12 @@ void AP_OSD::init()
#endif #endif
} }
#if OSD_ENABLED #if OSD_ENABLED
if (backend != nullptr) { if (_backends[instance] != nullptr) {
// populate the fonts lookup table // populate the fonts lookup table
backend->init_symbol_set(AP_OSD_AbstractScreen::symbols_lookup_table, AP_OSD_NUM_SYMBOLS); _backends[instance]->init_symbol_set(AP_OSD_AbstractScreen::symbols_lookup_table, AP_OSD_NUM_SYMBOLS);
// create thread as higher priority than IO for all backends return true;
hal.scheduler->thread_create(FUNCTOR_BIND_MEMBER(&AP_OSD::osd_thread, void), "OSD", 1280, AP_HAL::Scheduler::PRIORITY_IO, 1);
} }
return false;
#endif #endif
} }
@ -329,30 +360,36 @@ void AP_OSD::init()
void AP_OSD::osd_thread() void AP_OSD::osd_thread()
{ {
// initialize thread specific code once // initialize thread specific code once
backend->osd_thread_run_once(); for (uint8_t instance = 0; instance < _backend_count; instance++) {
_backends[instance]->osd_thread_run_once();
}
while (true) { while (true) {
hal.scheduler->delay(100); hal.scheduler->delay(100);
if (!_disable) {
update_stats();
update_current_screen();
}
update_osd(); update_osd();
} }
} }
void AP_OSD::update_osd() void AP_OSD::update_osd()
{ {
backend->clear(); for (uint8_t instance = 0; instance < _backend_count; instance++) {
_backends[instance]->clear();
if (!_disable) { if (!_disable) {
update_stats(); get_screen(current_screen).set_backend(_backends[instance]);
update_current_screen();
get_screen(current_screen).set_backend(backend);
// skip drawing for MSP OSD backends to save some resources // skip drawing for MSP OSD backends to save some resources
if (osd_types(osd_type.get()) != OSD_MSP) { if (_backends[instance]->get_backend_type() != OSD_MSP) {
get_screen(current_screen).draw(); get_screen(current_screen).draw();
} }
} }
backend->flush(); _backends[instance]->flush();
}
} }
//update maximums and totals //update maximums and totals
@ -545,6 +582,28 @@ void AP_OSD::set_nav_info(NavInfo &navinfo)
// do this without a lock for now // do this without a lock for now
nav_info = navinfo; nav_info = navinfo;
} }
// pre_arm_check - returns true if all pre-takeoff checks have completed successfully
bool AP_OSD::pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const
{
#if OSD_PARAM_ENABLED
// currently in the OSD menu, do not allow arming
if (!is_readonly_screen()) {
hal.util->snprintf(failure_msg, failure_msg_len, "In OSD menu");
return false;
}
#endif
//check if second backend was requested by user but not instantiated
if (osd_type.get() != OSD_NONE && _backend_count == 1 && osd_type2.get() != OSD_NONE) {
hal.util->snprintf(failure_msg, failure_msg_len, "OSD_TYPE2 not compatible with first OSD");
return false;
}
// if we got this far everything must be ok
return true;
}
#endif // OSD_ENABLED #endif // OSD_ENABLED
// handle OSD parameter configuration // handle OSD parameter configuration

View File

@ -49,6 +49,7 @@ class AP_MSP;
#define PARAM_TOKEN_INDEX(token) PARAM_INDEX(AP_Param::get_persistent_key(token.key), token.idx, token.group_element) #define PARAM_TOKEN_INDEX(token) PARAM_INDEX(AP_Param::get_persistent_key(token.key), token.idx, token.group_element)
#define AP_OSD_NUM_SYMBOLS 91 #define AP_OSD_NUM_SYMBOLS 91
#define OSD_MAX_INSTANCES 2
/* /*
class to hold one setting class to hold one setting
*/ */
@ -506,6 +507,9 @@ public:
OSD_TXONLY=4, OSD_TXONLY=4,
OSD_MSP_DISPLAYPORT=5 OSD_MSP_DISPLAYPORT=5
}; };
bool init_backend(const osd_types type, const uint8_t instance);
enum switch_method { enum switch_method {
TOGGLE=0, TOGGLE=0,
PWM_RANGE=1, PWM_RANGE=1,
@ -513,6 +517,7 @@ public:
}; };
AP_Int8 osd_type; AP_Int8 osd_type;
AP_Int8 osd_type2; // additional backend active in parallel
AP_Int8 font_num; AP_Int8 font_num;
AP_Int32 options; AP_Int32 options;
@ -649,7 +654,8 @@ private:
StatsInfo _stats; StatsInfo _stats;
#endif #endif
AP_OSD_Backend *backend; AP_OSD_Backend *_backends[OSD_MAX_INSTANCES];
uint8_t _backend_count;
static AP_OSD *_singleton; static AP_OSD *_singleton;
// multi-thread access support // multi-thread access support

View File

@ -55,6 +55,8 @@ public:
// copy the backend specific symbol set to the OSD lookup table // copy the backend specific symbol set to the OSD lookup table
virtual void init_symbol_set(uint8_t *symbols, const uint8_t size); virtual void init_symbol_set(uint8_t *symbols, const uint8_t size);
virtual bool is_compatible_with_backend_type(AP_OSD::osd_types type) const = 0;
virtual AP_OSD::osd_types get_backend_type() const = 0;
// called by the OSD thread once // called by the OSD thread once
virtual void osd_thread_run_once() { return; } virtual void osd_thread_run_once() { return; }

View File

@ -41,6 +41,24 @@ public:
// return a correction factor used to display angles correctly // return a correction factor used to display angles correctly
float get_aspect_ratio_correction() const override; float get_aspect_ratio_correction() const override;
bool is_compatible_with_backend_type(AP_OSD::osd_types type) const override {
switch(type) {
case AP_OSD::osd_types::OSD_MAX7456:
case AP_OSD::osd_types::OSD_SITL:
return false;
case AP_OSD::osd_types::OSD_NONE:
case AP_OSD::osd_types::OSD_TXONLY:
case AP_OSD::osd_types::OSD_MSP:
case AP_OSD::osd_types::OSD_MSP_DISPLAYPORT:
return true;
}
return false;
}
AP_OSD::osd_types get_backend_type() const override {
return AP_OSD::osd_types::OSD_MAX7456;
}
private: private:
//constructor //constructor

View File

@ -19,6 +19,23 @@ public:
//clear framebuffer //clear framebuffer
void clear() override {}; void clear() override {};
bool is_compatible_with_backend_type(AP_OSD::osd_types type) const override {
switch(type) {
case AP_OSD::osd_types::OSD_MSP:
case AP_OSD::osd_types::OSD_MSP_DISPLAYPORT:
return false;
case AP_OSD::osd_types::OSD_NONE:
case AP_OSD::osd_types::OSD_TXONLY:
case AP_OSD::osd_types::OSD_MAX7456:
case AP_OSD::osd_types::OSD_SITL:
return true;
}
return false;
}
AP_OSD::osd_types get_backend_type() const override {
return AP_OSD::osd_types::OSD_MSP;
}
private: private:
void setup_defaults(void); void setup_defaults(void);
}; };

View File

@ -31,6 +31,23 @@ public:
// return a correction factor used to display angles correctly // return a correction factor used to display angles correctly
float get_aspect_ratio_correction() const override; float get_aspect_ratio_correction() const override;
bool is_compatible_with_backend_type(AP_OSD::osd_types type) const override {
switch(type) {
case AP_OSD::osd_types::OSD_MSP:
case AP_OSD::osd_types::OSD_MSP_DISPLAYPORT:
return false;
case AP_OSD::osd_types::OSD_NONE:
case AP_OSD::osd_types::OSD_TXONLY:
case AP_OSD::osd_types::OSD_MAX7456:
case AP_OSD::osd_types::OSD_SITL:
return true;
}
return false;
}
AP_OSD::osd_types get_backend_type() const override {
return AP_OSD::osd_types::OSD_MSP_DISPLAYPORT;
}
protected: protected:
uint8_t format_string_for_osd(char* dst, uint8_t size, bool decimal_packed, const char *fmt, va_list ap) override; uint8_t format_string_for_osd(char* dst, uint8_t size, bool decimal_packed, const char *fmt, va_list ap) override;

View File

@ -595,19 +595,6 @@ void AP_OSD_ParamScreen::draw(void)
} }
#endif #endif
// pre_arm_check - returns true if all pre-takeoff checks have completed successfully
bool AP_OSD::pre_arm_check(char *failure_msg, const uint8_t failure_msg_len) const
{
// currently in the OSD menu, do not allow arming
if (!is_readonly_screen()) {
hal.util->snprintf(failure_msg, failure_msg_len, "In OSD menu");
return false;
}
// if we got this far everything must be ok
return true;
}
#endif // OSD_ENABLED #endif // OSD_ENABLED
// save all of the parameters // save all of the parameters

View File

@ -34,7 +34,7 @@ public:
//draw given text to framebuffer //draw given text to framebuffer
void write(uint8_t x, uint8_t y, const char* text) override; void write(uint8_t x, uint8_t y, const char* text) override;
//initilize display port and underlying hardware //initialize display port and underlying hardware
bool init() override; bool init() override;
//flush framebuffer to screen //flush framebuffer to screen
@ -43,6 +43,23 @@ public:
//clear framebuffer //clear framebuffer
void clear() override; void clear() override;
bool is_compatible_with_backend_type(AP_OSD::osd_types type) const override {
switch(type) {
case AP_OSD::osd_types::OSD_MAX7456:
case AP_OSD::osd_types::OSD_SITL:
return false;
case AP_OSD::osd_types::OSD_NONE:
case AP_OSD::osd_types::OSD_TXONLY:
case AP_OSD::osd_types::OSD_MSP:
case AP_OSD::osd_types::OSD_MSP_DISPLAYPORT:
return true;
}
return false;
}
AP_OSD::osd_types get_backend_type() const override {
return AP_OSD::osd_types::OSD_SITL;
}
private: private:
//constructor //constructor
AP_OSD_SITL(AP_OSD &osd); AP_OSD_SITL(AP_OSD &osd);