Ardupilot2/libraries/AP_OSD/AP_OSD.cpp
Alexander Malishev d2e2a5b2ea AP_OSD: fixes in screen swithing methods
Do not switch to the next screen just after initialization.
Reduced auto switch interval to 1s.
Allow switch sreen in autoswitch mode just after transition.
2018-07-01 20:26:53 +10:00

196 lines
5.7 KiB
C++

/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*
* AP_OSD partially based on betaflight and inav osd.c implemention.
* clarity.mcm font is taken from inav configurator.
* Many thanks to their authors.
*/
#include "AP_OSD.h"
#include "AP_OSD_MAX7456.h"
#include <AP_HAL/AP_HAL.h>
#include <AP_HAL/Util.h>
#include <RC_Channel/RC_Channel.h>
#include <utility>
const AP_Param::GroupInfo AP_OSD::var_info[] = {
// @Param: _TYPE
// @DisplayName: OSD type
// @Description: OSD type
// @Values: 0:None,1:MAX7456
// @User: Standard
// @RebootRequired: True
AP_GROUPINFO_FLAGS("_TYPE", 1, AP_OSD, osd_type, 0, AP_PARAM_FLAG_ENABLE),
// @Param: _CHAN
// @DisplayName: Screen switch transmitter channel
// @Description: This sets the channel used to switch different OSD screens.
// @Values: 0:Disable,5:Chan5,6:Chan6,7:Chan7,8:Chan8,9:Chan9,10:Chan10,11:Chan11,12:Chan12,13:Chan13,14:Chan14,15:Chan15,16:Chan16
// @User: Standard
AP_GROUPINFO("_CHAN", 2, AP_OSD, rc_channel, 0),
// @Group: 1_
// @Path: AP_OSD_Screen.cpp
AP_SUBGROUPINFO(screen[0], "1_", 3, AP_OSD, AP_OSD_Screen),
// @Group: 2_
// @Path: AP_OSD_Screen.cpp
AP_SUBGROUPINFO(screen[1], "2_", 4, AP_OSD, AP_OSD_Screen),
// @Group: 3_
// @Path: AP_OSD_Screen.cpp
AP_SUBGROUPINFO(screen[2], "3_", 5, AP_OSD, AP_OSD_Screen),
// @Group: 4_
// @Path: AP_OSD_Screen.cpp
AP_SUBGROUPINFO(screen[3], "4_", 6, AP_OSD, AP_OSD_Screen),
// @Param: _SW_METHOD
// @DisplayName: Screen switch method
// @Description: This sets the method used to switch different OSD screens.
// @Values: 0: switch to next screen if channel value was changed,
// 1: select screen based on pwm ranges specified for each screen,
// 2: switch to next screen after low to high transition and every 1s while channel value is high
// @User: Standard
AP_GROUPINFO("_SW_METHOD", 7, AP_OSD, sw_method, AP_OSD::TOGGLE),
AP_GROUPEND
};
extern const AP_HAL::HAL& hal;
AP_OSD::AP_OSD()
{
AP_Param::setup_object_defaults(this, var_info);
// default first screen enabled
screen[0].enabled = 1;
}
void AP_OSD::init()
{
switch ((enum osd_types)osd_type.get()) {
case OSD_NONE:
default:
break;
case OSD_MAX7456:
AP_HAL::OwnPtr<AP_HAL::Device> spi_dev = std::move(hal.spi->get_device("osd"));
if (!spi_dev) {
break;
}
backend = AP_OSD_MAX7456::probe(*this, std::move(spi_dev));
if (backend == nullptr) {
break;
}
hal.console->printf("Started MAX7456 OSD\n");
hal.scheduler->register_io_process(FUNCTOR_BIND_MEMBER(&AP_OSD::timer, void));
break;
}
}
void AP_OSD::timer()
{
uint32_t now = AP_HAL::millis();
if (now - last_update_ms >= 100) {
last_update_ms = now;
update_osd();
}
}
void AP_OSD::update_osd()
{
backend->clear();
update_current_screen();
screen[current_screen].set_backend(backend);
screen[current_screen].draw();
backend->flush();
}
void AP_OSD::update_current_screen()
{
if (rc_channel == 0) {
return;
}
RC_Channel *channel = RC_Channels::rc_channel(rc_channel-1);
if (channel == nullptr) {
return;
}
int16_t channel_value = channel->get_radio_in();
switch (sw_method) {
//switch to next screen if channel value was changed
default:
case TOGGLE:
if (previous_channel_value == 0) {
//do not switch to the next screen just after initialization
previous_channel_value = channel_value;
}
if (abs(channel_value-previous_channel_value) > 200) {
if (switch_debouncer) {
next_screen();
previous_channel_value = channel_value;
} else {
switch_debouncer = true;
return;
}
}
break;
//select screen based on pwm ranges specified
case PWM_RANGE:
for (int i=0; i<AP_OSD_NUM_SCREENS; i++) {
if (screen[i].enabled && screen[i].channel_min <= channel_value && screen[i].channel_max > channel_value) {
current_screen = i;
break;
}
}
break;
//switch to next screen after low to high transition and every 1s while channel value is high
case AUTO_SWITCH:
if (channel_value > channel->get_radio_trim()) {
if (switch_debouncer) {
uint32_t now = AP_HAL::millis();
if (now - last_switch_ms > 1000) {
next_screen();
last_switch_ms = now;
}
} else {
switch_debouncer = true;
return;
}
} else {
last_switch_ms = 0;
}
break;
}
switch_debouncer = false;
}
//select next avaliable screen, do nothing if all screens disabled
void AP_OSD::next_screen()
{
uint8_t t = current_screen;
do {
t = (t + 1)%AP_OSD_NUM_SCREENS;
} while (t != current_screen && !screen[t].enabled);
current_screen = t;
}