mirror of https://github.com/ArduPilot/ardupilot
AP_VideoTX: Tramp VTX support
Tramp enabled on 2Mb boards with OSD via AP_TRAMP_ENABLED
This commit is contained in:
parent
58ea8d6b7b
commit
6ab6aa1ef4
|
@ -0,0 +1,498 @@
|
||||||
|
/*
|
||||||
|
This program 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 program 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/>.
|
||||||
|
|
||||||
|
Code by Andy Piper, ported from betaflight vtx_tramp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "AP_Tramp.h"
|
||||||
|
#include <AP_Math/crc.h>
|
||||||
|
#include <GCS_MAVLink/GCS.h>
|
||||||
|
#include <AP_HAL/utility/sparse-endian.h>
|
||||||
|
|
||||||
|
#if AP_TRAMP_ENABLED
|
||||||
|
|
||||||
|
#define AP_TRAMP_UART_BAUD 9600
|
||||||
|
// request and response size is 16 bytes
|
||||||
|
#define AP_TRAMP_UART_BUFSIZE_RX 32
|
||||||
|
#define AP_TRAMP_UART_BUFSIZE_TX 32
|
||||||
|
|
||||||
|
// Define periods between requests
|
||||||
|
#define TRAMP_MIN_REQUEST_PERIOD_US (200 * 1000) // 200ms
|
||||||
|
#define TRAMP_STATUS_REQUEST_PERIOD_US (1000 * 1000) // 1s
|
||||||
|
|
||||||
|
//#define TRAMP_DEBUG
|
||||||
|
#ifdef TRAMP_DEBUG
|
||||||
|
# define debug(fmt, args...) do { hal.console->printf("TRAMP: " fmt "\n", ##args); } while (0)
|
||||||
|
#else
|
||||||
|
# define debug(fmt, args...) do {} while(0)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern const AP_HAL::HAL &hal;
|
||||||
|
|
||||||
|
AP_Tramp::AP_Tramp()
|
||||||
|
{
|
||||||
|
singleton = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
AP_Tramp *AP_Tramp::singleton;
|
||||||
|
|
||||||
|
// Calculate tramp protocol checksum of provided buffer
|
||||||
|
uint8_t AP_Tramp::checksum(uint8_t *buf)
|
||||||
|
{
|
||||||
|
uint8_t cksum = 0;
|
||||||
|
|
||||||
|
for (int i = 1 ; i < TRAMP_BUF_SIZE - 2; i++) {
|
||||||
|
cksum += buf[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return cksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send tramp protocol frame to device
|
||||||
|
void AP_Tramp::send_command(uint8_t cmd, uint16_t param)
|
||||||
|
{
|
||||||
|
if (port == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(request_buffer, 0, ARRAY_SIZE(request_buffer));
|
||||||
|
request_buffer[0] = 0x0F;
|
||||||
|
request_buffer[1] = cmd;
|
||||||
|
request_buffer[2] = param & 0xFF;
|
||||||
|
request_buffer[3] = (param >> 8) & 0xFF;
|
||||||
|
request_buffer[14] = checksum(request_buffer);
|
||||||
|
|
||||||
|
port->write(request_buffer, TRAMP_BUF_SIZE);
|
||||||
|
port->flush();
|
||||||
|
|
||||||
|
debug("send command '%c': %u", cmd, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process response and return code if valid else 0
|
||||||
|
char AP_Tramp::handle_response(void)
|
||||||
|
{
|
||||||
|
const uint8_t respCode = response_buffer[1];
|
||||||
|
|
||||||
|
switch (respCode) {
|
||||||
|
case 'r': {
|
||||||
|
const uint16_t min_freq = response_buffer[2]|(response_buffer[3] << 8);
|
||||||
|
// Check we're not reading the request (indicated by freq zero)
|
||||||
|
if (min_freq != 0) {
|
||||||
|
// Got response, update device limits
|
||||||
|
device_limits.rf_freq_min = min_freq;
|
||||||
|
device_limits.rf_freq_max = response_buffer[4]|(response_buffer[5] << 8);
|
||||||
|
device_limits.rf_power_max = response_buffer[6]|(response_buffer[7] << 8);
|
||||||
|
debug("device limits: min freq: %u, max freq: %u, max power %u",
|
||||||
|
unsigned(device_limits.rf_freq_min), unsigned(device_limits.rf_freq_max), unsigned(device_limits.rf_power_max));
|
||||||
|
return 'r';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'v': {
|
||||||
|
const uint16_t freq = response_buffer[2]|(response_buffer[3] << 8);
|
||||||
|
// Check we're not reading the request (indicated by freq zero)
|
||||||
|
if (freq != 0) {
|
||||||
|
// Got response, update device status
|
||||||
|
const uint16_t power = response_buffer[4]|(response_buffer[5] << 8);
|
||||||
|
cur_control_mode = response_buffer[6]; // Currently only used for race lock
|
||||||
|
const bool pit_mode = response_buffer[7];
|
||||||
|
cur_act_power = response_buffer[8]|(response_buffer[9] << 8);
|
||||||
|
|
||||||
|
// update the vtx
|
||||||
|
AP_VideoTX& vtx = AP::vtx();
|
||||||
|
vtx.set_frequency_mhz(freq);
|
||||||
|
|
||||||
|
AP_VideoTX::VideoBand band;
|
||||||
|
uint8_t channel;
|
||||||
|
if (vtx.get_band_and_channel(freq, band, channel)) {
|
||||||
|
vtx.set_band(band);
|
||||||
|
vtx.set_channel(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
vtx.set_power_mw(power);
|
||||||
|
if (pit_mode) {
|
||||||
|
vtx.set_options(vtx.get_options() | uint8_t(AP_VideoTX::VideoOptions::VTX_PITMODE));
|
||||||
|
} else {
|
||||||
|
vtx.set_options(vtx.get_options() & ~uint8_t(AP_VideoTX::VideoOptions::VTX_PITMODE));
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the configured values now reflect reality
|
||||||
|
vtx.set_defaults();
|
||||||
|
|
||||||
|
debug("device config: freq: %u, power: %u, pitmode: %u",
|
||||||
|
unsigned(freq), unsigned(power), unsigned(pit_mode));
|
||||||
|
|
||||||
|
|
||||||
|
return 'v';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 's': {
|
||||||
|
const uint16_t temp = (int16_t)(response_buffer[6]|(response_buffer[7] << 8));
|
||||||
|
// Check we're not reading the request (indicated by temp zero)
|
||||||
|
if (temp != 0) {
|
||||||
|
// Got response, update device status
|
||||||
|
cur_temp = temp;
|
||||||
|
return 's';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Likely reading a request, return zero to indicate not accepted
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset receiver state machine
|
||||||
|
void AP_Tramp::reset_receiver(void)
|
||||||
|
{
|
||||||
|
receive_state = ReceiveState::S_WAIT_LEN;
|
||||||
|
receive_pos = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns completed response code or 0
|
||||||
|
char AP_Tramp::receive_response()
|
||||||
|
{
|
||||||
|
if (port == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for complete packet
|
||||||
|
const uint16_t bytesNeeded = TRAMP_BUF_SIZE - receive_pos;
|
||||||
|
if (port->available() < bytesNeeded) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if (bytesNeeded == 0) {
|
||||||
|
reset_receiver();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < bytesNeeded; i++) {
|
||||||
|
const int16_t b = port->read();
|
||||||
|
if (b < 0) {
|
||||||
|
// uart claimed bytes available, but there were none
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const uint8_t c = uint8_t(b);
|
||||||
|
response_buffer[receive_pos++] = c;
|
||||||
|
|
||||||
|
switch (receive_state) {
|
||||||
|
case ReceiveState::S_WAIT_LEN: {
|
||||||
|
if (c == 0x0F) {
|
||||||
|
// Found header byte, advance to wait for code
|
||||||
|
receive_state = ReceiveState::S_WAIT_CODE;
|
||||||
|
} else {
|
||||||
|
// Unexpected header, reset state machine
|
||||||
|
reset_receiver();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReceiveState::S_WAIT_CODE: {
|
||||||
|
if (c == 'r' || c == 'v' || c == 's') {
|
||||||
|
// Code is for response is one we're interested in, advance to data
|
||||||
|
receive_state = ReceiveState::S_DATA;
|
||||||
|
} else {
|
||||||
|
// Unexpected code, reset state machine
|
||||||
|
reset_receiver();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ReceiveState::S_DATA: {
|
||||||
|
if (receive_pos == TRAMP_BUF_SIZE) {
|
||||||
|
// Buffer is full, calculate checksum
|
||||||
|
const uint8_t cksum = checksum(response_buffer);
|
||||||
|
|
||||||
|
// Reset state machine ready for next response
|
||||||
|
reset_receiver();
|
||||||
|
|
||||||
|
if ((response_buffer[TRAMP_BUF_SIZE-2] == cksum) && (response_buffer[TRAMP_BUF_SIZE-1] == 0)) {
|
||||||
|
// Checksum is correct, process response
|
||||||
|
const char r = handle_response();
|
||||||
|
|
||||||
|
// Check response valid else keep on reading
|
||||||
|
if (r != 0) {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Invalid state, reset state machine
|
||||||
|
reset_receiver();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP_Tramp::send_query(uint8_t cmd)
|
||||||
|
{
|
||||||
|
// Reset receive buffer and issue command
|
||||||
|
reset_receiver();
|
||||||
|
send_command(cmd, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP_Tramp::set_status(TrampStatus _status)
|
||||||
|
{
|
||||||
|
status = _status;
|
||||||
|
#ifdef TRAMP_DEBUG
|
||||||
|
switch (status) {
|
||||||
|
case TrampStatus::TRAMP_STATUS_OFFLINE:
|
||||||
|
debug("status: OFFLINE");
|
||||||
|
break;
|
||||||
|
case TrampStatus::TRAMP_STATUS_INIT:
|
||||||
|
debug("status: INIT");
|
||||||
|
break;
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT:
|
||||||
|
debug("status: ONLINE_MONITOR_FREQPWRPIT");
|
||||||
|
break;
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_TEMP:
|
||||||
|
debug("status: ONLINE_MONITOR_TEMP");
|
||||||
|
break;
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_CONFIG:
|
||||||
|
debug("status: ONLINE_CONFIG");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP_Tramp::process_requests()
|
||||||
|
{
|
||||||
|
if (port == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configUpdateRequired = false;
|
||||||
|
|
||||||
|
// Read response from device
|
||||||
|
const char replyCode = receive_response();
|
||||||
|
const uint32_t now = AP_HAL::micros();
|
||||||
|
|
||||||
|
#ifdef TRAMP_DEBUG
|
||||||
|
if (replyCode != 0) {
|
||||||
|
debug("receive response '%c'", replyCode);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Act on state
|
||||||
|
switch (status) {
|
||||||
|
case TrampStatus::TRAMP_STATUS_OFFLINE: {
|
||||||
|
// Offline, check for response
|
||||||
|
if (replyCode == 'r') {
|
||||||
|
// Device replied to reset? request, enter init
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_INIT);
|
||||||
|
} else if ((now - last_time_us) >= TRAMP_MIN_REQUEST_PERIOD_US) {
|
||||||
|
// Min request period exceeded, issue another reset?
|
||||||
|
send_query('r');
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TrampStatus::TRAMP_STATUS_INIT: {
|
||||||
|
// Initializing, check for response
|
||||||
|
if (replyCode == 'v') {
|
||||||
|
// Device replied to freq / power / pit query, enter online
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT);
|
||||||
|
} else if ((now - last_time_us) >= TRAMP_MIN_REQUEST_PERIOD_US) {
|
||||||
|
// Min request period exceeded, issue another query
|
||||||
|
send_query('v');
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT: {
|
||||||
|
// Note after config a status update request is made, a new status
|
||||||
|
// request is made, this request is handled above and should prevent
|
||||||
|
// subsequent config updates if the config is now correct
|
||||||
|
if (retry_count > 0 && ((now - last_time_us) >= TRAMP_MIN_REQUEST_PERIOD_US)) {
|
||||||
|
AP_VideoTX& vtx = AP::vtx();
|
||||||
|
// Config retries remain and min request period exceeded, check freq
|
||||||
|
if (!is_race_lock_enabled() && vtx.update_frequency()) {
|
||||||
|
// Freq can be and needs to be updated, issue request
|
||||||
|
send_command('F', vtx.get_configured_frequency_mhz());
|
||||||
|
|
||||||
|
// Set flag
|
||||||
|
configUpdateRequired = true;
|
||||||
|
} else if (!is_race_lock_enabled() && vtx.update_power()) {
|
||||||
|
// Power can be and needs to be updated, issue request
|
||||||
|
send_command('P', vtx.get_configured_power_mw());
|
||||||
|
|
||||||
|
// Set flag
|
||||||
|
configUpdateRequired = true;
|
||||||
|
} else if (vtx.update_options()) {
|
||||||
|
// Pit mode needs to be updated, issue request
|
||||||
|
send_command('I', vtx.has_option(AP_VideoTX::VideoOptions::VTX_PITMODE) ? 0 : 1);
|
||||||
|
|
||||||
|
// Set flag
|
||||||
|
configUpdateRequired = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configUpdateRequired) {
|
||||||
|
// Update required, decrement retry count
|
||||||
|
retry_count--;
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
|
||||||
|
// Advance state
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_CONFIG);
|
||||||
|
} else {
|
||||||
|
// No update required, reset retry count
|
||||||
|
retry_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Was a config update made? */
|
||||||
|
if (!configUpdateRequired) {
|
||||||
|
/* No, look to continue monitoring */
|
||||||
|
if ((now - last_time_us) >= TRAMP_STATUS_REQUEST_PERIOD_US) {
|
||||||
|
// Request period exceeded, issue freq/power/pit query
|
||||||
|
send_query('v');
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
} else if (replyCode == 'v') {
|
||||||
|
// Got reply, issue temp query
|
||||||
|
send_query('s');
|
||||||
|
|
||||||
|
// Wait for reply
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_TEMP);
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_TEMP: {
|
||||||
|
// Check request time
|
||||||
|
if (replyCode == 's') {
|
||||||
|
// Got reply, return to request freq/power/pit
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_TEMP);
|
||||||
|
} else if ((now - last_time_us) >= TRAMP_MIN_REQUEST_PERIOD_US) {
|
||||||
|
// Timed out after min request period, return to request freq/power/pit query
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case TrampStatus::TRAMP_STATUS_ONLINE_CONFIG: {
|
||||||
|
// Param should now be set, check time
|
||||||
|
if ((now - last_time_us) >= TRAMP_MIN_REQUEST_PERIOD_US) {
|
||||||
|
// Min request period exceeded, re-query
|
||||||
|
send_query('v');
|
||||||
|
|
||||||
|
// Advance state
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT);
|
||||||
|
|
||||||
|
// Update last time
|
||||||
|
last_time_us = now;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Invalid state, reset
|
||||||
|
set_status(TrampStatus::TRAMP_STATUS_OFFLINE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AP_Tramp::is_device_ready()
|
||||||
|
{
|
||||||
|
return status >= TrampStatus::TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP_Tramp::set_frequency(uint16_t freq)
|
||||||
|
{
|
||||||
|
uint8_t freqValid;
|
||||||
|
|
||||||
|
// Check frequency valid
|
||||||
|
if (device_limits.rf_freq_min != 0 && device_limits.rf_freq_max != 0) {
|
||||||
|
freqValid = (freq >= device_limits.rf_freq_min && freq <= device_limits.rf_freq_max);
|
||||||
|
} else {
|
||||||
|
freqValid = (freq >= VTX_TRAMP_MIN_FREQUENCY_MHZ && freq <= VTX_TRAMP_MAX_FREQUENCY_MHZ);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is frequency valid?
|
||||||
|
if (freqValid) {
|
||||||
|
// Requested freq changed, reset retry count
|
||||||
|
retry_count = VTX_TRAMP_MAX_RETRIES;
|
||||||
|
} else {
|
||||||
|
debug("requested frequency %u is invalid", freq);
|
||||||
|
// not valid reset to default
|
||||||
|
AP::vtx().set_configured_frequency_mhz(AP::vtx().get_frequency_mhz());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AP_Tramp::update()
|
||||||
|
{
|
||||||
|
if (port == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AP_VideoTX& vtx = AP::vtx();
|
||||||
|
|
||||||
|
if (vtx.have_params_changed() && retry_count == 0) {
|
||||||
|
// check changes in the order they will be processed
|
||||||
|
if (vtx.update_frequency() || vtx.update_band() || vtx.update_channel()) {
|
||||||
|
if (vtx.update_frequency()) {
|
||||||
|
vtx.update_configured_channel_and_band();
|
||||||
|
} else {
|
||||||
|
vtx.update_configured_frequency();
|
||||||
|
}
|
||||||
|
set_frequency(vtx.get_configured_frequency_mhz());
|
||||||
|
}
|
||||||
|
else if (vtx.update_power()) {
|
||||||
|
retry_count = VTX_TRAMP_MAX_RETRIES;
|
||||||
|
}
|
||||||
|
else if (vtx.update_options()) {
|
||||||
|
retry_count = VTX_TRAMP_MAX_RETRIES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
process_requests();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AP_Tramp::init(void)
|
||||||
|
{
|
||||||
|
if (AP::vtx().get_enabled() == 0) {
|
||||||
|
debug("protocol is not active");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// init uart
|
||||||
|
port = AP::serialmanager().find_serial(AP_SerialManager::SerialProtocol_Tramp, 0);
|
||||||
|
if (port != nullptr) {
|
||||||
|
port->configure_parity(0);
|
||||||
|
port->set_stop_bits(1);
|
||||||
|
port->set_flow_control(AP_HAL::UARTDriver::FLOW_CONTROL_DISABLE);
|
||||||
|
port->set_options((port->get_options() & ~AP_HAL::UARTDriver::OPTION_RXINV));
|
||||||
|
|
||||||
|
port->begin(AP_TRAMP_UART_BAUD, AP_TRAMP_UART_BUFSIZE_RX, AP_TRAMP_UART_BUFSIZE_TX);
|
||||||
|
debug("port opened");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // VTX_TRAMP
|
|
@ -0,0 +1,142 @@
|
||||||
|
/*
|
||||||
|
This program 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 program 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/>.
|
||||||
|
|
||||||
|
Code by Andy Piper, ported from betaflight vtx_tramp
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <AP_HAL/AP_HAL.h>
|
||||||
|
#include <AP_OSD/AP_OSD.h>
|
||||||
|
|
||||||
|
#ifndef AP_TRAMP_ENABLED
|
||||||
|
#define AP_TRAMP_ENABLED OSD_ENABLED && BOARD_FLASH_SIZE>1024 && !HAL_MINIMIZE_FEATURES
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if AP_TRAMP_ENABLED
|
||||||
|
|
||||||
|
#include <AP_Param/AP_Param.h>
|
||||||
|
#include <AP_SerialManager/AP_SerialManager.h>
|
||||||
|
#include <AP_HAL/utility/RingBuffer.h>
|
||||||
|
#include "AP_VideoTX.h"
|
||||||
|
|
||||||
|
#define VTX_TRAMP_POWER_COUNT 5
|
||||||
|
|
||||||
|
#define VTX_TRAMP_MIN_FREQUENCY_MHZ 5000 //min freq in MHz
|
||||||
|
#define VTX_TRAMP_MAX_FREQUENCY_MHZ 5999 //max freq in MHz
|
||||||
|
// Maximum number of requests sent to try a config change
|
||||||
|
// Some VTX fail to respond to every request (like Matek FCHUB-VTX) so
|
||||||
|
// we sometimes need multiple retries to get the VTX to respond.
|
||||||
|
#define VTX_TRAMP_MAX_RETRIES (20)
|
||||||
|
// Race lock - settings can't be changed
|
||||||
|
#define TRAMP_CONTROL_RACE_LOCK (0x01)
|
||||||
|
|
||||||
|
class AP_Tramp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AP_Tramp();
|
||||||
|
~AP_Tramp() {}
|
||||||
|
|
||||||
|
/* Do not allow copies */
|
||||||
|
AP_Tramp(const AP_Tramp &other) = delete;
|
||||||
|
AP_Tramp &operator=(const AP_Tramp&) = delete;
|
||||||
|
|
||||||
|
static AP_Tramp *get_singleton(void) {
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool init(void);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
uint16_t get_current_actual_power();
|
||||||
|
uint16_t get_current_temp();
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint8_t checksum(uint8_t *buf);
|
||||||
|
// Check if race lock is enabled
|
||||||
|
bool is_race_lock_enabled(void) {
|
||||||
|
return cur_control_mode & TRAMP_CONTROL_RACE_LOCK;
|
||||||
|
}
|
||||||
|
void send_command(uint8_t cmd, uint16_t param);
|
||||||
|
char handle_response();
|
||||||
|
void reset_receiver();
|
||||||
|
char receive_response();
|
||||||
|
void send_query(uint8_t cmd);
|
||||||
|
void process_requests();
|
||||||
|
bool is_device_ready();
|
||||||
|
void set_frequency(uint16_t freq);
|
||||||
|
void set_power(uint16_t power);
|
||||||
|
void set_pit_mode(uint8_t onoff);
|
||||||
|
|
||||||
|
// serial interface
|
||||||
|
AP_HAL::UARTDriver *port; // UART used to send data to Tramp VTX
|
||||||
|
|
||||||
|
//Pointer to singleton
|
||||||
|
static AP_Tramp* singleton;
|
||||||
|
|
||||||
|
const static uint16_t TRAMP_BUF_SIZE = 16;
|
||||||
|
|
||||||
|
// Serial transmit and receive buffers
|
||||||
|
uint8_t request_buffer[TRAMP_BUF_SIZE];
|
||||||
|
uint8_t response_buffer[TRAMP_BUF_SIZE];
|
||||||
|
|
||||||
|
// Module state machine
|
||||||
|
enum class TrampStatus {
|
||||||
|
// Offline - device hasn't responded yet
|
||||||
|
TRAMP_STATUS_OFFLINE = 0,
|
||||||
|
// Init - fetching current settings from device
|
||||||
|
TRAMP_STATUS_INIT,
|
||||||
|
// Online - device is ready and being monitored - freq/power/pitmode
|
||||||
|
TRAMP_STATUS_ONLINE_MONITOR_FREQPWRPIT,
|
||||||
|
// Online - device is ready and being monitored - temperature
|
||||||
|
TRAMP_STATUS_ONLINE_MONITOR_TEMP,
|
||||||
|
// Online - device is ready and config has just been updated
|
||||||
|
TRAMP_STATUS_ONLINE_CONFIG
|
||||||
|
};
|
||||||
|
|
||||||
|
TrampStatus status = TrampStatus::TRAMP_STATUS_OFFLINE;
|
||||||
|
|
||||||
|
void set_status(TrampStatus stat);
|
||||||
|
|
||||||
|
// Device limits, read from device during init
|
||||||
|
struct {
|
||||||
|
uint32_t rf_freq_min;
|
||||||
|
uint32_t rf_freq_max;
|
||||||
|
uint32_t rf_power_max;
|
||||||
|
} device_limits;
|
||||||
|
|
||||||
|
uint16_t cur_act_power; // Actual power
|
||||||
|
int16_t cur_temp;
|
||||||
|
uint8_t cur_control_mode;
|
||||||
|
|
||||||
|
// Retry count
|
||||||
|
uint8_t retry_count = VTX_TRAMP_MAX_RETRIES;
|
||||||
|
|
||||||
|
// Receive state machine
|
||||||
|
enum class ReceiveState {
|
||||||
|
S_WAIT_LEN = 0, // Waiting for a packet len
|
||||||
|
S_WAIT_CODE, // Waiting for a response code
|
||||||
|
S_DATA, // Waiting for rest of the packet.
|
||||||
|
};
|
||||||
|
|
||||||
|
ReceiveState receive_state = ReceiveState::S_WAIT_LEN;
|
||||||
|
|
||||||
|
// Receive buffer index
|
||||||
|
int16_t receive_pos;
|
||||||
|
|
||||||
|
// Last action time
|
||||||
|
uint32_t last_time_us;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in New Issue