/*
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 .
*/
#pragma once
#if HAL_RCINPUT_WITH_AP_RADIO
/*
AP_Radio implementation for Cypress 2.4GHz radio.
With thanks to the SuperBitRF project
See http://wiki.paparazziuav.org/wiki/SuperbitRF
This implementation uses the DSMX protocol on a CYRF6936
*/
#include "AP_Radio_backend.h"
#include "hal.h"
#include "telem_structure.h"
class AP_Radio_cypress : public AP_Radio_backend
{
public:
AP_Radio_cypress(AP_Radio &radio);
// init - initialise radio
bool init(void) override;
// rest radio
bool reset(void) override;
// send a packet
bool send(const uint8_t *pkt, uint16_t len) override;
// start bind process as a receiver
void start_recv_bind(void) override;
// return time in microseconds of last received R/C packet
uint32_t last_recv_us(void) override;
// return number of input channels
uint8_t num_channels(void) override;
// return current PWM of a channel
uint16_t read(uint8_t chan) override;
// handle a data96 mavlink packet for fw upload
void handle_data_packet(mavlink_channel_t chan, const mavlink_data96_t &m) override;
// update status
void update(void) override;
// get TX fw version
uint32_t get_tx_version(void) override
{
// pack date into 16 bits for vendor_id in AUTOPILOT_VERSION
return (uint16_t(dsm.tx_firmware_year)<<12) + (uint16_t(dsm.tx_firmware_month)<<8) + dsm.tx_firmware_day;
}
// get radio statistics structure
const AP_Radio::stats &get_stats(void) override;
// set the 2.4GHz wifi channel used by companion computer, so it can be avoided
void set_wifi_channel(uint8_t channel) override
{
t_status.wifi_chan = channel;
}
private:
AP_HAL::OwnPtr dev;
static AP_Radio_cypress *radio_singleton;
void radio_init(void);
void dump_registers(uint8_t n);
void force_initial_state(void);
void set_channel(uint8_t channel);
uint8_t read_status_debounced(uint8_t adr);
uint8_t read_register(uint8_t reg);
void write_register(uint8_t reg, uint8_t value);
void write_multiple(uint8_t reg, uint8_t n, const uint8_t *data);
enum {
STATE_RECV,
STATE_BIND,
STATE_AUTOBIND,
STATE_SEND_TELEM,
STATE_SEND_TELEM_WAIT,
STATE_SEND_FCC
} state;
struct config {
uint8_t reg;
uint8_t value;
};
static const uint8_t pn_codes[5][9][8];
static const uint8_t pn_bind[];
static const config cyrf_config[];
static const config cyrf_bind_config[];
static const config cyrf_transfer_config[];
virtual_timer_t timeout_vt;
static thread_t *_irq_handler_ctx;
void radio_set_config(const struct config *config, uint8_t size);
void start_receive(void);
// main IRQ handler
void irq_handler(void);
// IRQ handler for packet receive
void irq_handler_recv(uint8_t rx_status);
// handle timeout IRQ
void irq_timeout(void);
// trampoline functions to take us from static IRQ function to class functions
static void irq_handler_thd(void* arg);
static void trigger_irq_radio_event(void);
static void trigger_timeout_event(void *arg);
static const uint8_t max_channels = 16;
uint32_t last_debug_print_ms;
void print_debug_info(void);
AP_Radio::stats stats;
AP_Radio::stats last_stats;
enum dsm_protocol {
DSM_NONE = 0, // not bound yet
DSM_DSM2_1 = 0x01, // The original DSM2 protocol with 1 packet of data
DSM_DSM2_2 = 0x02, // The original DSM2 protocol with 2 packets of data
DSM_DSMX_1 = 0xA2, // The original DSMX protocol with 1 packet of data
DSM_DSMX_2 = 0xB2, // The original DSMX protocol with 2 packets of data
};
enum dsm2_sync {
DSM2_SYNC_A,
DSM2_SYNC_B,
DSM2_OK
};
// semaphore between ISR and main thread
HAL_Semaphore sem;
// dsm config data and status
struct {
uint8_t channels[23];
enum dsm_protocol protocol;
uint8_t mfg_id[4];
uint8_t current_channel;
uint8_t current_rf_channel;
uint16_t crc_seed;
uint8_t sop_col;
uint8_t data_col;
uint8_t last_sop_code[8];
uint8_t last_data_code[16];
uint32_t receive_start_us;
uint32_t receive_timeout_msec;
uint32_t last_recv_us;
uint32_t last_parse_us;
uint32_t last_recv_chan;
uint32_t last_chan_change_us;
uint16_t num_channels;
uint16_t pwm_channels[max_channels];
bool need_bind_save;
enum dsm2_sync sync;
uint32_t crc_errors;
float rssi;
bool last_discrc;
uint8_t last_transmit_power;
uint32_t send_irq_count;
uint32_t send_count;
uint16_t pkt_time1 = 3000;
uint16_t pkt_time2 = 7000;
uint8_t tx_firmware_year;
uint8_t tx_firmware_month;
uint8_t tx_firmware_day;
int8_t forced_channel = -1;
uint8_t tx_rssi;
uint8_t tx_pps;
uint32_t last_autobind_send;
bool have_tx_pps;
uint32_t telem_send_count;
uint8_t tx_bl_version;
} dsm;
struct {
mavlink_channel_t chan;
bool need_ack;
uint8_t counter;
uint8_t sequence;
uint32_t offset;
uint32_t length;
uint32_t acked;
uint8_t len;
enum telem_type fw_type;
uint8_t pending_data[92];
} fwupload;
// bind structure saved to storage
static const uint16_t bind_magic = 0x43F6;
struct PACKED bind_info {
uint16_t magic;
uint8_t mfg_id[4];
enum dsm_protocol protocol;
};
struct telem_status t_status;
// DSM specific functions
void dsm_set_channel(uint8_t channel, bool is_dsm2, uint8_t sop_col, uint8_t data_col, uint16_t crc_seed);
// generate DSMX channels
void dsm_generate_channels_dsmx(uint8_t mfg_id[4], uint8_t channels[23]);
// setup for DSMX transfers
void dsm_setup_transfer_dsmx(void);
// choose channel to receive on
void dsm_choose_channel(void);
// map for mode1/mode2
void map_stick_mode(uint16_t *channels);
// parse DSM channels from a packet
bool parse_dsm_channels(const uint8_t *data);
// process an incoming packet
void process_packet(const uint8_t *pkt, uint8_t len);
// process an incoming bind packet
void process_bind(const uint8_t *pkt, uint8_t len);
// load bind info from storage
void load_bind_info(void);
// save bind info to storage
void save_bind_info(void);
bool is_DSM2(void);
// send a 16 byte packet
void transmit16(const uint8_t data[16]);
void send_telem_packet(void);
void irq_handler_send(uint8_t tx_status);
void send_FCC_test_packet(void);
// check sending of fw upload ack
void check_fw_ack(void);
// re-sync DSM2
void dsm2_start_sync(void);
// check for double binding
void check_double_bind(void);
// setup a timeout handler
void setup_timeout(uint32_t timeout_ms);
};
#endif