123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569 |
- /*
- driver for TI CC2500 radio
- Many thanks to the cleanflight and betaflight projects
- */
- #include <AP_HAL/AP_HAL.h>
- // #pragma GCC optimize("O0")
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- #if HAL_RCINPUT_WITH_AP_RADIO
- #include <AP_Math/AP_Math.h>
- #include "AP_Radio_cc2500.h"
- #include <utility>
- #include <stdio.h>
- #include <StorageManager/StorageManager.h>
- #include <AP_Notify/AP_Notify.h>
- #include <GCS_MAVLink/GCS_MAVLink.h>
- #include <AP_Math/crc.h>
- #include <AP_Param/AP_Param.h>
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- #define TIMEOUT_PRIORITY 185
- #define EVT_TIMEOUT EVENT_MASK(0)
- #define EVT_IRQ EVENT_MASK(1)
- #define EVT_BIND EVENT_MASK(2)
- #endif
- extern const AP_HAL::HAL& hal;
- #define Debug(level, fmt, args...) do { if ((level) <= get_debug_level()) { gcs().send_text(MAV_SEVERITY_INFO, fmt, ##args); }} while (0)
- // object instance for trampoline
- AP_Radio_cc2500 *AP_Radio_cc2500::radio_singleton;
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- thread_t *AP_Radio_cc2500::_irq_handler_ctx;
- virtual_timer_t AP_Radio_cc2500::timeout_vt;
- uint32_t AP_Radio_cc2500::irq_time_us;
- #endif
- #define USE_D16_FORMAT 0
- /*
- we are setup for a channel spacing of 0.3MHz, with channel 0 being 2403.6MHz
- For D16 protocol we select 47 channels from a max of 235 channels
- For SRT protocol we select 23 channels from a max of 235 channels,
- and avoid channels near to the WiFi channel of the Sonix video board
- */
- #if USE_D16_FORMAT
- #define NUM_CHANNELS 47
- #define MAX_CHANNEL_NUMBER 0xEB
- #define INTER_PACKET_MS 9
- #define INTER_PACKET_INITIAL_MS (INTER_PACKET_MS+2)
- #define PACKET_SENT_DELAY_US 3300
- #else
- #define NUM_CHANNELS 23
- #define MAX_CHANNEL_NUMBER 0xEB
- #define INTER_PACKET_MS 9
- #define INTER_PACKET_INITIAL_MS (INTER_PACKET_MS+5)
- #define PACKET_SENT_DELAY_US 2800
- #endif
- #define SEARCH_START_PKTS 40
- #define AUTOBIND_CHANNEL 100
- /*
- constructor
- */
- AP_Radio_cc2500::AP_Radio_cc2500(AP_Radio &_radio) :
- AP_Radio_backend(_radio),
- cc2500(hal.spi->get_device("cc2500"))
- {
- // link to instance for irq_trampoline
- radio_singleton = this;
- }
- /*
- initialise radio
- */
- bool AP_Radio_cc2500::init(void)
- {
- #if CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
- if (_irq_handler_ctx != nullptr) {
- AP_HAL::panic("AP_Radio_cc2500: double instantiation of irq_handler\n");
- }
- chVTObjectInit(&timeout_vt);
- _irq_handler_ctx = chThdCreateFromHeap(NULL,
- THD_WORKING_AREA_SIZE(2048),
- "radio_cc2500",
- TIMEOUT_PRIORITY,
- irq_handler_thd,
- NULL);
- #endif
- return reset();
- }
- /*
- reset radio
- */
- bool AP_Radio_cc2500::reset(void)
- {
- if (!cc2500.lock_bus()) {
- return false;
- }
- radio_init();
- cc2500.unlock_bus();
- return true;
- }
- /*
- return statistics structure from radio
- */
- const AP_Radio::stats &AP_Radio_cc2500::get_stats(void)
- {
- return stats;
- }
- /*
- read one pwm channel from radio
- */
- uint16_t AP_Radio_cc2500::read(uint8_t chan)
- {
- if (chan >= CC2500_MAX_PWM_CHANNELS) {
- return 0;
- }
- return pwm_channels[chan];
- }
- /*
- update status - called from main thread
- */
- void AP_Radio_cc2500::update(void)
- {
- check_fw_ack();
- }
- /*
- return number of active channels
- */
- uint8_t AP_Radio_cc2500::num_channels(void)
- {
- uint32_t now = AP_HAL::millis();
- uint8_t chan = get_rssi_chan();
- if (chan > 0) {
- pwm_channels[chan-1] = t_status.rssi;
- chan_count = MAX(chan_count, chan);
- }
- chan = get_pps_chan();
- if (chan > 0) {
- pwm_channels[chan-1] = t_status.pps;
- chan_count = MAX(chan_count, chan);
- }
- chan = get_tx_rssi_chan();
- if (chan > 0) {
- pwm_channels[chan-1] = tx_rssi;
- chan_count = MAX(chan_count, chan);
- }
- chan = get_tx_pps_chan();
- if (chan > 0) {
- pwm_channels[chan-1] = tx_pps;
- chan_count = MAX(chan_count, chan);
- }
- pwm_channels[11] = (stats.recv_packets % 1000);
- chan_count = MAX(chan_count, 12);
- if (now - last_pps_ms > 1000) {
- last_pps_ms = now;
- t_status.pps = stats.recv_packets - last_stats.recv_packets;
- last_stats = stats;
- if (lost != 0 || timeouts != 0) {
- Debug(timeouts!=0?2:3,"lost=%u timeouts=%u TS=%u\n",
- unsigned(lost), unsigned(timeouts), sizeof(struct telem_packet_cc2500));
- }
- lost=0;
- timeouts=0;
- }
- return chan_count;
- }
- /*
- return time of last receive in microseconds
- */
- uint32_t AP_Radio_cc2500::last_recv_us(void)
- {
- return packet_timer;
- }
- /*
- send len bytes as a single packet
- */
- bool AP_Radio_cc2500::send(const uint8_t *pkt, uint16_t len)
- {
- // disabled for now
- return false;
- }
- const AP_Radio_cc2500::config AP_Radio_cc2500::radio_config_GFSK[] = {
- /*
- radio config for GFSK with 57kHz deviation
- */
- {CC2500_00_IOCFG2, 0x01}, // GD2 high on RXFIFO filled or end of packet
- {CC2500_17_MCSM1, 0x03}, // RX->IDLE, CCA always, TX -> IDLE
- {CC2500_18_MCSM0, 0x08}, // XOSC expire 64, cal never
- {CC2500_06_PKTLEN, 0x0D}, // packet length 13
- {CC2500_07_PKTCTRL1, 0x0C}, // enable RSSI+LQI, no addr check, CRC autoflush, PQT=0
- {CC2500_08_PKTCTRL0, 0x44}, // fixed length mode, CRC, FIFO enable, whitening
- {CC2500_3E_PATABLE, 0xFF}, // initially max power
- {CC2500_0B_FSCTRL1, 0x0A}, // IF=253.90625kHz assuming 26MHz crystal
- {CC2500_0C_FSCTRL0, 0x00}, // freqoffs = 0
- {CC2500_0D_FREQ2, 0x5C}, // freq control high
- {CC2500_0E_FREQ1, 0x76}, // freq control middle
- {CC2500_0F_FREQ0, 0x27}, // freq control low
- {CC2500_10_MDMCFG4, 0x8C}, // filter bandwidth 203kHz
- {CC2500_11_MDMCFG3, 0x2F}, // data rate 120kbaud
- {CC2500_12_MDMCFG2, 0x13}, // 30/32 sync word bits, no manchester, GFSK, DC filter enabled
- {CC2500_13_MDMCFG1, 0xA3}, // chan spacing exponent 3, preamble 4 bytes, FEC enabled
- {CC2500_14_MDMCFG0, 0x7A}, // chan spacing 299.926757kHz for 26MHz crystal
- {CC2500_15_DEVIATN, 0x51}, // modem deviation 57kHz for 26MHz crystal
- {CC2500_19_FOCCFG, 0x16}, // frequency offset compensation
- {CC2500_1A_BSCFG, 0x6C}, // bit sync config
- {CC2500_1B_AGCCTRL2, 0x43}, // target amplitude 33dB
- {CC2500_1C_AGCCTRL1, 0x40}, // AGC control 2
- {CC2500_1D_AGCCTRL0, 0x91}, // AGC control 0
- {CC2500_21_FREND1, 0x56}, // frontend config1
- {CC2500_22_FREND0, 0x10}, // frontend config0
- {CC2500_23_FSCAL3, 0xA9}, // frequency synth cal3
- {CC2500_24_FSCAL2, 0x0A}, // frequency synth cal2
- {CC2500_25_FSCAL1, 0x00}, // frequency synth cal1
- {CC2500_26_FSCAL0, 0x11}, // frequency synth cal0
- {CC2500_29_FSTEST, 0x59}, // test bits
- {CC2500_2C_TEST2, 0x88}, // test settings
- {CC2500_2D_TEST1, 0x31}, // test settings
- {CC2500_2E_TEST0, 0x0B}, // test settings
- {CC2500_03_FIFOTHR, 0x07}, // TX fifo threashold 33, RX fifo threshold 32
- {CC2500_09_ADDR, 0x00}, // device address 0 (broadcast)
- };
- const AP_Radio_cc2500::config AP_Radio_cc2500::radio_config[] = {
- /* config for both TX and RX (from SmartRF Studio)
- setup for MSK at 120kbaud, FEC enabled, whitening enabled, base freq 2403.999756MHz
- channel spacing 299.926758, crystal 26MHz, RX filter bw 203.125kHz
- */
- {CC2500_06_PKTLEN, 0x0D},
- {CC2500_07_PKTCTRL1, 0x0C},
- {CC2500_08_PKTCTRL0, 0x44},
- {CC2500_0B_FSCTRL1, 0x0A},
- {CC2500_0D_FREQ2, 0x5C},
- {CC2500_0E_FREQ1, 0x76},
- {CC2500_0F_FREQ0, 0x27},
- {CC2500_11_MDMCFG3, 0x2F},
- {CC2500_12_MDMCFG2, 0x73},
- {CC2500_13_MDMCFG1, 0xA3},
- {CC2500_14_MDMCFG0, 0x7A},
- {CC2500_15_DEVIATN, 0x70},
- {CC2500_17_MCSM1, 0x03},
- {CC2500_18_MCSM0, 0x08},
- {CC2500_19_FOCCFG, 0x16},
- {CC2500_1B_AGCCTRL2, 0x43},
- {CC2500_23_FSCAL3, 0xEA},
- {CC2500_25_FSCAL1, 0x00},
- {CC2500_26_FSCAL0, 0x11},
- {CC2500_2B_AGCTEST, 0x3E},
- {CC2500_03_FIFOTHR, 0x07}, // TX fifo threashold 33, RX fifo threshold 32
- // config specific to RX
- {CC2500_00_IOCFG2, 0x01}, // GD2 high on RXFIFO filled or end of packet
- {CC2500_17_MCSM1, 0x03}, // RX->IDLE, CCA always, TX -> IDLE
- {CC2500_18_MCSM0, 0x08}, // XOSC expire 64, cal never
- {CC2500_3E_PATABLE, 0xFF}, // initially max power
- };
- const uint16_t CRCTable[] = {
- 0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
- 0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
- 0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
- 0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
- 0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
- 0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
- 0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
- 0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
- 0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
- 0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
- 0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
- 0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
- 0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
- 0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
- 0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
- 0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
- 0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
- 0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
- 0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
- 0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
- 0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
- 0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
- 0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
- 0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
- 0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
- 0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
- 0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
- 0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
- 0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
- 0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
- 0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
- 0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78
- };
- /*
- static probe function for radio auto-detect
- */
- bool AP_Radio_cc2500::probe(void)
- {
- auto dev = hal.spi->get_device("cc2500");
- if (!dev->get_semaphore()->take(HAL_SEMAPHORE_BLOCK_FOREVER)) {
- return false;
- }
- uint8_t r1=0, r2=0;
- if (!dev->read_registers(CC2500_30_PARTNUM | CC2500_READ_BURST | CC2500_READ_SINGLE, &r1, 1) || r1 != 0x80 ||
- !dev->read_registers(CC2500_31_VERSION | CC2500_READ_BURST | CC2500_READ_SINGLE, &r2, 1) || r2 != 0x03) {
- dev->get_semaphore()->give();
- return false;
- }
- dev->get_semaphore()->give();
- return true;
- }
- /*
- initialise the radio
- */
- void AP_Radio_cc2500::radio_init(void)
- {
- if (cc2500.ReadReg(CC2500_30_PARTNUM | CC2500_READ_BURST) != 0x80 ||
- cc2500.ReadReg(CC2500_31_VERSION | CC2500_READ_BURST) != 0x03) {
- Debug(1, "cc2500: radio not found\n");
- return;
- }
- Debug(1, "cc2500: radio_init starting\n");
- cc2500.Reset();
- hal.scheduler->delay_microseconds(100);
- if (get_protocol() == AP_Radio::PROTOCOL_CC2500_GFSK) {
- Debug(1,"Using GFSK configuration\n");
- for (uint8_t i=0; i<ARRAY_SIZE(radio_config_GFSK); i++) {
- cc2500.WriteRegCheck(radio_config_GFSK[i].reg, radio_config_GFSK[i].value);
- }
- } else {
- for (uint8_t i=0; i<ARRAY_SIZE(radio_config); i++) {
- cc2500.WriteRegCheck(radio_config[i].reg, radio_config[i].value);
- }
- }
- cc2500.Strobe(CC2500_SIDLE); // Go to idle...
- hal.scheduler->delay_microseconds(10*1000);
- // setup handler for rising edge of IRQ pin
- hal.gpio->attach_interrupt(HAL_GPIO_RADIO_IRQ, trigger_irq_radio_event, AP_HAL::GPIO::INTERRUPT_RISING);
- // fill in rxid for use in double bind prevention
- char sysid[40] {};
- hal.util->get_system_id(sysid);
- uint16_t sysid_crc = calc_crc((const uint8_t *)sysid, strnlen(sysid, sizeof(sysid)));
- if (sysid_crc == 0) {
- sysid_crc = 1;
- }
- t_status.rxid[0] = sysid_crc & 0xFF;
- t_status.rxid[1] = sysid_crc >> 8;
- initTuneRx();
- if (load_bind_info()) {
- Debug(3,"Loaded bind info\n");
- } else {
- listLength = NUM_CHANNELS;
- bindOffset = 0;
- setup_hopping_table_SRT();
- }
- uint8_t factory_test = get_factory_test();
- if (factory_test != 0) {
- bindTxId[0] = uint8_t(factory_test * 17);
- bindTxId[1] = uint8_t(~bindTxId[0]);
- setup_hopping_table_SRT();
- }
- // we go straight into search, and rely on autobind
- initialiseData(0);
- protocolState = STATE_SEARCH;
- packet_timer = AP_HAL::micros();
- chanskip = 1;
- nextChannel(1);
- // set default autobind power to suit the cc2500
- AP_Param::set_default_by_name("BRD_RADIO_ABLVL", 90);
- chVTSet(&timeout_vt, chTimeMS2I(INTER_PACKET_INITIAL_MS), trigger_timeout_event, nullptr);
- }
- void AP_Radio_cc2500::trigger_irq_radio_event()
- {
- //we are called from ISR context
- chSysLockFromISR();
- irq_time_us = AP_HAL::micros();
- chEvtSignalI(_irq_handler_ctx, EVT_IRQ);
- chSysUnlockFromISR();
- }
- void AP_Radio_cc2500::trigger_timeout_event(void *arg)
- {
- (void)arg;
- //we are called from ISR context
- chSysLockFromISR();
- chVTSetI(&timeout_vt, chTimeMS2I(INTER_PACKET_INITIAL_MS), trigger_timeout_event, nullptr);
- chEvtSignalI(_irq_handler_ctx, EVT_TIMEOUT);
- chSysUnlockFromISR();
- }
- void AP_Radio_cc2500::start_recv_bind(void)
- {
- chan_count = 0;
- packet_timer = AP_HAL::micros();
- chEvtSignal(_irq_handler_ctx, EVT_BIND);
- Debug(1,"Starting bind\n");
- }
- // handle a data96 mavlink packet for fw upload
- void AP_Radio_cc2500::handle_data_packet(mavlink_channel_t chan, const mavlink_data96_t &m)
- {
- uint32_t ofs=0;
- memcpy(&ofs, &m.data[0], 4);
- Debug(4, "got data96 of len %u from chan %u at offset %u\n", m.len, chan, unsigned(ofs));
- if (sem.take_nonblocking()) {
- fwupload.chan = chan;
- fwupload.need_ack = false;
- fwupload.offset = ofs;
- fwupload.length = MIN(m.len-4, 92);
- fwupload.acked = 0;
- fwupload.sequence++;
- if (m.type == 43) {
- // sending a tune to play - for development testing
- fwupload.fw_type = TELEM_PLAY;
- fwupload.length = MIN(m.len, 90);
- fwupload.offset = 0;
- memcpy(&fwupload.pending_data[0], &m.data[0], fwupload.length);
- } else {
- // sending a chunk of firmware OTA upload
- fwupload.fw_type = TELEM_FW;
- memcpy(&fwupload.pending_data[0], &m.data[4], fwupload.length);
- }
- sem.give();
- }
- }
- /*
- handle a FrSky D16 packet
- */
- bool AP_Radio_cc2500::handle_D16_packet(const uint8_t *packet)
- {
- if (packet[0] != 0x1D) {
- return false;
- }
- if (packet[1] != bindTxId[0] ||
- packet[2] != bindTxId[1]) {
- Debug(3, "p1=%02x p2=%02x p6=%02x\n", packet[1], packet[2], packet[6]);
- // not for us
- return false;
- }
- if (packet[7] == 0x00 ||
- packet[7] == 0x20 ||
- packet[7] == 0x10 ||
- packet[7] == 0x12 ||
- packet[7] == 0x14 ||
- packet[7] == 0x16 ||
- packet[7] == 0x18 ||
- packet[7] == 0x1A ||
- packet[7] == 0x1C ||
- packet[7] == 0x1E) {
- // channel packet or range check packet
- parse_frSkyX(packet);
- packet3 = packet[3];
- uint8_t hop_chan = packet[4] & 0x3F;
- uint8_t skip = (packet[4]>>6) | (packet[5]<<2);
- if (channr != hop_chan) {
- Debug(2, "channr=%u hop_chan=%u\n", channr, hop_chan);
- }
- channr = hop_chan;
- if (chanskip != skip) {
- Debug(2, "chanskip=%u skip=%u\n", chanskip, skip);
- }
- chanskip = skip;
- return true;
- }
- return false;
- }
- /*
- handle a SRT packet
- */
- bool AP_Radio_cc2500::handle_SRT_packet(const uint8_t *packet)
- {
- const struct srt_packet *pkt = (const struct srt_packet *)packet;
- if (pkt->length != sizeof(struct srt_packet)-1 ||
- pkt->txid[0] != bindTxId[0] ||
- pkt->txid[1] != bindTxId[1]) {
- Debug(3, "len=%u p1=%02x p2=%02x\n", pkt->length, pkt->txid[0], pkt->txid[1]);
- // not for us
- return false;
- }
- if (pkt->version != 1) {
- // only support version 1 so far
- return false;
- }
- uint16_t chan_new[CC2500_MAX_PWM_CHANNELS];
- memcpy(chan_new, pwm_channels, sizeof(pwm_channels));
- chan_new[0] = pkt->chan1 + 1000 + ((pkt->chan_high&0xC0)<<2);
- chan_new[1] = pkt->chan2 + 1000 + ((pkt->chan_high&0x30)<<4);
- chan_new[2] = pkt->chan3 + 1000 + ((pkt->chan_high&0x0C)<<6);
- chan_new[3] = pkt->chan4 + 1000 + ((pkt->chan_high&0x03)<<8);
- // we map the buttons onto two PWM channels for ease of integration with ArduPilot
- chan_new[4] = 1000 + (pkt->buttons & 0x7) * 100;
- chan_new[5] = 1000 + (pkt->buttons >> 3) * 100;
- // cope with mode1/mode2
- map_stick_mode(chan_new);
- memcpy(pwm_channels, chan_new, sizeof(pwm_channels));
- uint8_t data = pkt->data;
- /*
- decode special data field
- */
- switch (pkt->pkt_type) {
- case PKTYPE_VOLTAGE:
- // voltage from TX is in 0.025 volt units. Convert to 0.01 volt units for easier display
- pwm_channels[6] = data * 4;
- break;
- case PKTYPE_YEAR:
- tx_date.firmware_year = data;
- break;
- case PKTYPE_MONTH:
- tx_date.firmware_month = data;
- break;
- case PKTYPE_DAY:
- tx_date.firmware_day = data;
- break;
- case PKTYPE_TELEM_RSSI:
- tx_rssi = data;
- break;
- case PKTYPE_TELEM_PPS:
- tx_pps = data;
- if (!have_tx_pps) {
- check_double_bind();
- }
- have_tx_pps = true;
- break;
- case PKTYPE_BL_VERSION:
- // unused so far for cc2500
- break;
- case PKTYPE_RXID1:
- if (data != t_status.rxid[0]) {
- Debug(4, "Double bind1 - disconnecting\n");
- start_recv_bind();
- }
- break;
- case PKTYPE_RXID2:
- if (data != t_status.rxid[1]) {
- Debug(4, "Double bind2 - disconnecting\n");
- start_recv_bind();
- }
- break;
- case PKTYPE_FW_ACK: {
- // got an fw upload ack
- Debug(4, "ack %u seq=%u acked=%u length=%u len=%u\n",
- data, fwupload.sequence, unsigned(fwupload.acked), unsigned(fwupload.length), fwupload.len);
- if (fwupload.sequence == data && sem.take_nonblocking()) {
- fwupload.sequence++;
- fwupload.acked += fwupload.len;
- if (fwupload.acked == fwupload.length) {
- // trigger send of DATA16 ack to client
- fwupload.need_ack = true;
- }
- sem.give();
- }
- break;
- }
- }
- if (chan_count < 7) {
- chan_count = 7;
- }
- if (pkt->channr != channr) {
- Debug(2, "channr=%u hop_chan=%u\n", channr, pkt->channr);
- channr = pkt->channr;
- }
- if (pkt->chanskip != chanskip) {
- Debug(2, "chanskip=%u skip=%u\n", chanskip, pkt->chanskip);
- chanskip = pkt->chanskip;
- }
- return true;
- }
- /*
- see if we have already assigned a channel
- */
- bool AP_Radio_cc2500::have_channel(uint8_t channel, uint8_t count, uint8_t loop)
- {
- uint8_t i;
- for (i=0; i<count; i++) {
- if (bindHopData[i] == channel) {
- return true;
- }
- if (loop < 5) {
- int separation = ((int)bindHopData[i]) - (int)channel;
- if (separation < 0) {
- separation = -separation;
- }
- if (separation < 4) {
- // try if possible to stay at least 4 channels from existing channels
- return true;
- }
- }
- }
- return false;
- }
- /*
- mapping from WiFi channel number minus 1 to cc2500 channel
- number. WiFi channels are separated by 5MHz starting at 2412 MHz,
- except for channel 14, which has a 12MHz separation. We represent
- channel 14 as 255 as we want to keep this table 8 bit.
- */
- static const uint8_t wifi_chan_map[14] = {
- 28, 44, 61, 78, 94, 111, 128, 144, 161, 178, 194, 211, 228, 255
- };
- /*
- create hopping table for SRT protocol
- */
- void AP_Radio_cc2500::setup_hopping_table_SRT(void)
- {
- uint8_t val;
- uint8_t channel = bindTxId[0] % 127;
- uint8_t channel_spacing = bindTxId[1] % 127;
- uint8_t i;
- uint8_t wifi_chan = t_status.wifi_chan;
- uint8_t cc_wifi_mid, cc_wifi_low, cc_wifi_high;
- const uint8_t wifi_separation = 65;
- if (wifi_chan == 0 || wifi_chan > 14) {
- wifi_chan = 9;
- }
- cc_wifi_mid = wifi_chan_map[wifi_chan-1];
- if (cc_wifi_mid < wifi_separation) {
- cc_wifi_low = 0;
- } else {
- cc_wifi_low = cc_wifi_mid - wifi_separation;
- }
- if (cc_wifi_mid > 225) {
- cc_wifi_high = 255;
- } else {
- cc_wifi_high = cc_wifi_mid + wifi_separation;
- }
- if (channel_spacing < 7) {
- channel_spacing += 7;
- }
- for (i=0; i<NUM_CHANNELS; i++) {
- // loop is to prevent any possibility of non-completion
- uint8_t loop = 0;
- do {
- channel = (channel+channel_spacing) % MAX_CHANNEL_NUMBER;
- if ((channel <= cc_wifi_low || channel >= cc_wifi_high) && !have_channel(channel, i, loop)) {
- // accept if not in wifi range and not already allocated
- break;
- }
- } while (loop++ < 100);
- val=channel;
- // channels to avoid from D16 code, not properly understood
- if ((val==0x00) || (val==0x5A) || (val==0xDC)) {
- val++;
- }
- bindHopData[i] = val;
- }
- if (get_protocol() != AP_Radio::PROTOCOL_CC2500_GFSK) {
- // additional loop to fix any close channels
- for (i=0; i<NUM_CHANNELS; i++) {
- // first loop only accepts channels that are outside wifi band
- if (have_channel(bindHopData[i], i, 0)) {
- uint8_t c;
- for (c = 0; c<MAX_CHANNEL_NUMBER; c++) {
- if ((channel <= cc_wifi_low || channel >= cc_wifi_high) && !have_channel(c, i, 0)) {
- bindHopData[i] = c;
- break;
- }
- }
- }
- // if that fails then accept channels within the wifi band
- if (have_channel(bindHopData[i], i, 0)) {
- uint8_t c;
- for (c = 0; c<MAX_CHANNEL_NUMBER; c++) {
- if (!have_channel(c, i, 0)) {
- bindHopData[i] = c;
- break;
- }
- }
- }
- }
- }
- for (i=0; i<NUM_CHANNELS; i++) {
- Debug(3, "%u ", bindHopData[i]);
- }
- Debug(3, "\n");
- last_wifi_channel = t_status.wifi_chan;
- Debug(2, "Setup hopping for 0x%x:0x%0x WiFi %u %u-%u spc:%u\n",
- bindTxId[0], bindTxId[1],
- wifi_chan, cc_wifi_low, cc_wifi_high, channel_spacing);
- }
- /*
- handle a autobind packet
- */
- bool AP_Radio_cc2500::handle_autobind_packet(const uint8_t *packet, uint8_t lqi)
- {
- if (get_factory_test() != 0) {
- // no autobind in factory test mode
- return false;
- }
- const struct autobind_packet_cc2500 *pkt = (const struct autobind_packet_cc2500 *)packet;
- if (stats.recv_packets != 0) {
- // don't process autobind packets once we're connected
- Debug(4,"autobind discard\n");
- return false;
- }
- if (pkt->length != sizeof(struct autobind_packet_cc2500)-1 ||
- pkt->magic1 != 0xC5 ||
- pkt->magic2 != 0xA2 ||
- pkt->txid[0] != uint8_t(~pkt->txid_inverse[0]) ||
- pkt->txid[1] != uint8_t(~pkt->txid_inverse[1])) {
- Debug(3, "AB l=%u el=%u m1=%02x m2=%02x %02x:%02x %02x:%02x %02x:%02x\n",
- pkt->length, sizeof(struct autobind_packet_cc2500)-1, pkt->magic1, pkt->magic2,
- pkt->txid[0], pkt->txid[1], uint8_t(~pkt->txid_inverse[0]), uint8_t(~pkt->txid_inverse[1]),
- pkt->crc[0], pkt->crc[1]);
- // not a valid autobind packet
- return false;
- }
- for (uint8_t i=0; i<sizeof(pkt->pad); i++) {
- if (pkt->pad[i] != i+1) {
- Debug(3, "AB pad[%u]=%u\n", i, pkt->pad[i]);
- return false;
- }
- }
- uint16_t lcrc = calc_crc(packet,sizeof(*pkt)-2);
- if ((lcrc>>8)!=pkt->crc[0] || (lcrc&0x00FF)!=pkt->crc[1]) {
- Debug(3, "AB bad CRC\n");
- return false;
- }
- uint8_t rssi_raw = packet[sizeof(struct autobind_packet_cc2500)];
- uint8_t rssi_dbm = map_RSSI_to_dBm(rssi_raw);
- if (lqi >= 50) {
- Debug(3,"autobind bad LQI %u\n", lqi);
- return false;
- }
- if (rssi_dbm < get_autobind_rssi()) {
- Debug(1,"autobind RSSI %u needs %u\n", (unsigned)rssi_dbm, (unsigned)get_autobind_rssi());
- return false;
- }
- Debug(1,"autobind RSSI %u above %u lqi=%u bofs=%d\n", (unsigned)rssi_dbm, (unsigned)get_autobind_rssi(), lqi, auto_bindOffset);
- bindOffset = auto_bindOffset;
- bindTxId[0] = pkt->txid[0];
- bindTxId[1] = pkt->txid[1];
- listLength = NUM_CHANNELS;
- t_status.wifi_chan = pkt->wifi_chan;
- setup_hopping_table_SRT();
- Debug(1,"Saved bind data\n");
- save_bind_info();
- return true;
- }
- /*
- map a raw RSSI value to a dBm value
- */
- uint8_t AP_Radio_cc2500::map_RSSI_to_dBm(uint8_t rssi_raw)
- {
- float rssi_dbm;
- if (rssi_raw >= 128) {
- rssi_dbm = ((((uint16_t)rssi_raw) * 18) >> 5) - 82;
- } else {
- rssi_dbm = ((((uint16_t)rssi_raw) * 18) >> 5) + 65;
- }
- return rssi_dbm;
- }
- // main IRQ handler
- void AP_Radio_cc2500::irq_handler(void)
- {
- uint8_t ccLen;
- bool matched = false;
- do {
- ccLen = cc2500.ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST);
- hal.scheduler->delay_microseconds(20);
- uint8_t ccLen2 = cc2500.ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST);
- matched = (ccLen == ccLen2);
- } while (!matched);
- if (ccLen & 0x80) {
- Debug(6,"Fifo overflow %02x\n", ccLen);
- // RX FIFO overflow
- cc2500.Strobe(CC2500_SFRX);
- cc2500.Strobe(CC2500_SRX);
- return;
- }
- uint8_t packet[ccLen];
- cc2500.ReadFifo(packet, ccLen);
- if (get_fcc_test() != 0) {
- // don't process interrupts in FCCTEST mode
- return;
- }
- if (ccLen != 32 &&
- ccLen != sizeof(srt_packet)+2 &&
- ccLen != sizeof(autobind_packet_cc2500)+2) {
- cc2500.Strobe(CC2500_SFRX);
- cc2500.Strobe(CC2500_SRX);
- Debug(4, "bad len %u SRT=%u AB=%u\n", ccLen,
- sizeof(srt_packet)+2,
- sizeof(autobind_packet_cc2500)+2);
- return;
- }
- if (get_debug_level() > 6) {
- Debug(6, "CC2500 IRQ state=%u\n", unsigned(protocolState));
- Debug(6,"len=%u\n", ccLen);
- for (uint8_t i=0; i<ccLen; i++) {
- Debug(6, "%02x:%02x ", i, packet[i]);
- if ((i+1) % 16 == 0) {
- Debug(6, "\n");
- }
- }
- if (ccLen % 16 != 0) {
- Debug(6, "\n");
- }
- }
- if (!check_crc(ccLen, packet)) {
- Debug(4, "bad CRC ccLen=%u\n", ccLen);
- return;
- }
- switch (protocolState) {
- case STATE_BIND_TUNING:
- tuneRx(ccLen, packet);
- break;
- case STATE_BIND_BINDING:
- if (getBindData(ccLen, packet)) {
- Debug(2,"Bind complete\n");
- protocolState = STATE_BIND_COMPLETE;
- }
- break;
- case STATE_BIND_COMPLETE:
- protocolState = STATE_STARTING;
- save_bind_info();
- Debug(3,"listLength=%u\n", listLength);
- Debug(3,"Saved bind info\n");
- break;
- case STATE_STARTING:
- listLength = NUM_CHANNELS;
- initialiseData(0);
- protocolState = STATE_SEARCH;
- chanskip = 1;
- nextChannel(1);
- break;
- case STATE_SEARCH:
- protocolState = STATE_DATA;
- // fallthrough
- case STATE_DATA: {
- bool ok = false;
- if (ccLen == 32) {
- ok = handle_D16_packet(packet);
- } else if (ccLen == sizeof(srt_packet)+2) {
- ok = handle_SRT_packet(packet);
- if (!ok) {
- uint8_t Lqi = packet[ccLen - 1] & 0x7F;
- ok = handle_autobind_packet(packet, Lqi);
- }
- }
- if (ok) {
- // get RSSI value from status byte
- uint8_t rssi_raw = packet[ccLen-2];
- float rssi_dbm = map_RSSI_to_dBm(rssi_raw);
- rssi_filtered = 0.95 * rssi_filtered + 0.05 * rssi_dbm;
- t_status.rssi = uint8_t(MAX(rssi_filtered, 1));
- if (stats.recv_packets == 0) {
- Debug(3,"cc2500: got 1st packet\n");
- }
- stats.recv_packets++;
- packet_timer = irq_time_us;
- chVTSet(&timeout_vt, chTimeMS2I(INTER_PACKET_INITIAL_MS), trigger_timeout_event, nullptr);
- cc2500.Strobe(CC2500_SIDLE);
- if (get_telem_enable()) {
- cc2500.SetPower(get_transmit_power());
- if (ccLen == 32 || get_protocol() == AP_Radio::PROTOCOL_D16) {
- send_D16_telemetry();
- } else {
- if (have_tx_pps) {
- /* we don't start sending telemetry until we have the tx_pps rate. This allows us
- to reliably detect double-bind, where one TX is bound to multiple RX
- */
- send_SRT_telemetry();
- }
- }
- // now we sleep for enough time for the packet to be
- // transmitted. We can safely sleep here as we have a
- // dedicated thread for radio processing.
- cc2500.unlock_bus();
- hal.scheduler->delay_microseconds(PACKET_SENT_DELAY_US);
- cc2500.lock_bus();
- }
- nextChannel(chanskip);
- }
- break;
- }
- case STATE_FCCTEST:
- // nothing to do, all done in timeout code
- Debug(3,"IRQ in FCCTEST state\n");
- break;
- default:
- Debug(2,"state %u\n", (unsigned)protocolState);
- break;
- }
- }
- /*
- setup for the 6 possible FCC channel values (3 normal, 3 CW)
- */
- void AP_Radio_cc2500::set_fcc_channel(void)
- {
- uint8_t chan = MAX_CHANNEL_NUMBER/2;
- switch (get_fcc_test()) {
- case 1:
- case 4:
- chan = 0;
- break;
- case 2:
- case 5:
- chan = MAX_CHANNEL_NUMBER/2;
- break;
- case 3:
- case 6:
- chan = MAX_CHANNEL_NUMBER-1;
- break;
- }
- setChannel(chan);
- }
- // handle timeout IRQ
- void AP_Radio_cc2500::irq_timeout(void)
- {
- if (get_fcc_test() != 0 && protocolState != STATE_FCCTEST) {
- protocolState = STATE_FCCTEST;
- last_fcc_chan = 0;
- set_fcc_channel();
- send_SRT_telemetry();
- }
- switch (protocolState) {
- case STATE_BIND_TUNING: {
- if (bindOffset >= 126) {
- if (check_best_LQI()) {
- return;
- }
- bindOffset = -126;
- }
- uint32_t now = AP_HAL::millis();
- if (now - timeTunedMs > 20) {
- timeTunedMs = now;
- bindOffset += 5;
- Debug(6,"bindOffset=%d\n", int(bindOffset));
- cc2500.Strobe(CC2500_SIDLE);
- cc2500.WriteRegCheck(CC2500_0C_FSCTRL0, (uint8_t)bindOffset);
- cc2500.Strobe(CC2500_SFRX);
- cc2500.Strobe(CC2500_SRX);
- }
- break;
- }
- case STATE_DATA: {
- uint32_t now = AP_HAL::micros();
- if (now - packet_timer > SEARCH_START_PKTS*INTER_PACKET_MS*1000UL) {
- Debug(3,"searching %u\n", unsigned(now - packet_timer));
- cc2500.Strobe(CC2500_SIDLE);
- cc2500.Strobe(CC2500_SFRX);
- nextChannel(1);
- cc2500.Strobe(CC2500_SRX);
- timeouts++;
- protocolState = STATE_SEARCH;
- search_count = 0;
- } else {
- // to keep the timeouts a constant time behind the
- // expected time we need to set the timeout to the
- // inter-packet delay again now
- chVTSet(&timeout_vt, chTimeMS2I(INTER_PACKET_MS), trigger_timeout_event, nullptr);
- nextChannel(chanskip);
- lost++;
- }
- break;
- }
- case STATE_SEARCH: {
- uint32_t now = AP_HAL::millis();
- search_count++;
- if (stats.recv_packets == 0 &&
- get_autobind_time() != 0 &&
- get_factory_test() == 0 &&
- (AP_HAL::micros() - packet_timer) > get_autobind_time() * 1000UL*1000UL &&
- (search_count & 1) == 0) {
- // try for an autobind packet every 2nd packet, waiting 3 packet delays
- static uint32_t cc;
- auto_bindOffset += 5;
- if (auto_bindOffset >= 126) {
- auto_bindOffset = -126;
- }
- Debug(4,"ab recv %u boffset=%d", unsigned(cc), int(auto_bindOffset));
- cc++;
- cc2500.WriteRegCheck(CC2500_0C_FSCTRL0, (uint8_t)auto_bindOffset);
- setChannelRX(AUTOBIND_CHANNEL);
- autobind_start_recv_ms = now;
- chVTSet(&timeout_vt, chTimeMS2I(60), trigger_timeout_event, nullptr);
- } else {
- // shift by one channel at a time when searching
- if (autobind_start_recv_ms == 0 || now - autobind_start_recv_ms > 50) {
- autobind_start_recv_ms = 0;
- cc2500.WriteRegCheck(CC2500_0C_FSCTRL0, (uint8_t)bindOffset);
- nextChannel(1);
- }
- }
- break;
- }
- case STATE_FCCTEST: {
- if (get_fcc_test() == 0) {
- protocolState = STATE_DATA;
- Debug(1,"Ending FCCTEST\n");
- }
- // send every 9ms
- chVTSet(&timeout_vt, chTimeMS2I(INTER_PACKET_MS), trigger_timeout_event, nullptr);
- cc2500.SetPower(get_transmit_power());
- if (get_fcc_test() < 4 || last_fcc_chan != get_fcc_test()) {
- set_fcc_channel();
- send_SRT_telemetry();
- }
- if (last_fcc_chan != get_fcc_test() && get_fcc_test() != 0) {
- Debug(1,"Starting FCCTEST %u at power %u\n", unsigned(get_fcc_test()), unsigned(get_transmit_power()));
- }
- last_fcc_chan = get_fcc_test();
- break;
- }
- default:
- break;
- }
- }
- void AP_Radio_cc2500::irq_handler_thd(void *arg)
- {
- (void)arg;
- while (true) {
- eventmask_t evt = chEvtWaitAny(ALL_EVENTS);
- radio_singleton->cc2500.lock_bus();
- switch (evt) {
- case EVT_IRQ:
- if (radio_singleton->protocolState == STATE_FCCTEST) {
- hal.console->printf("IRQ FCC\n");
- }
- radio_singleton->irq_handler();
- break;
- case EVT_TIMEOUT:
- if (radio_singleton->cc2500.ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x80) {
- irq_time_us = AP_HAL::micros();
- radio_singleton->irq_handler();
- } else {
- radio_singleton->irq_timeout();
- }
- break;
- case EVT_BIND:
- // clear the current bind information
- radio_singleton->bindTxId[0] = 1;
- radio_singleton->bindTxId[1] = 1;
- radio_singleton->setup_hopping_table_SRT();
- radio_singleton->protocolState = STATE_SEARCH;
- radio_singleton->packet_timer = AP_HAL::micros();
- radio_singleton->stats.recv_packets = 0;
- radio_singleton->chanskip = 1;
- radio_singleton->nextChannel(1);
- radio_singleton->save_bind_info();
- break;
- default:
- break;
- }
- radio_singleton->cc2500.unlock_bus();
- }
- }
- void AP_Radio_cc2500::initTuneRx(void)
- {
- cc2500.WriteReg(CC2500_19_FOCCFG, 0x14);
- timeTunedMs = AP_HAL::millis();
- bindOffset = -126;
- best_lqi = 255;
- best_bindOffset = bindOffset;
- cc2500.WriteReg(CC2500_0C_FSCTRL0, (uint8_t)bindOffset);
- //cc2500.WriteReg(CC2500_07_PKTCTRL1, 0x0C);
- //cc2500.WriteReg(CC2500_18_MCSM0, 0x8);
- setChannelRX(0);
- }
- void AP_Radio_cc2500::initialiseData(uint8_t adr)
- {
- cc2500.WriteRegCheck(CC2500_0C_FSCTRL0, bindOffset);
- //cc2500.WriteRegCheck(CC2500_18_MCSM0, 0x8);
- //cc2500.WriteRegCheck(CC2500_07_PKTCTRL1, 0x0D); // address check, no broadcast, autoflush, status enable
- cc2500.WriteRegCheck(CC2500_19_FOCCFG, 0x16);
- hal.scheduler->delay_microseconds(10*1000);
- }
- void AP_Radio_cc2500::initGetBind(void)
- {
- setChannelRX(0);
- hal.scheduler->delay_microseconds(20); // waiting flush FIFO
- cc2500.Strobe(CC2500_SRX);
- listLength = 0;
- }
- /*
- we've wrapped in the search for the best bind offset. Accept the
- best so far if its good enough
- */
- bool AP_Radio_cc2500::check_best_LQI(void)
- {
- if (best_lqi >= 50) {
- return false;
- }
- bindOffset = best_bindOffset;
- initGetBind();
- initialiseData(1);
- protocolState = STATE_BIND_BINDING;
- bind_mask = 0;
- listLength = 0;
- Debug(2,"Bind tuning %d with Lqi %u\n", best_bindOffset, best_lqi);
- return true;
- }
- /*
- check if we have received a packet with sufficiently good link
- quality to start binding
- */
- bool AP_Radio_cc2500::tuneRx(uint8_t ccLen, uint8_t *packet)
- {
- if (bindOffset >= 126) {
- // we've scanned the whole range, if any were below 50 then
- // accept it
- if (check_best_LQI()) {
- return true;
- }
- bindOffset = -126;
- }
- if ((packet[ccLen - 1] & 0x80) && packet[2] == 0x01) {
- uint8_t Lqi = packet[ccLen - 1] & 0x7F;
- if (Lqi < best_lqi) {
- best_lqi = Lqi;
- best_bindOffset = bindOffset;
- }
- }
- return false;
- }
- /*
- get a block of hopping data from a bind packet
- */
- bool AP_Radio_cc2500::getBindData(uint8_t ccLen, uint8_t *packet)
- {
- // parse a bind data packet */
- if ((packet[ccLen - 1] & 0x80) && packet[2] == 0x01) {
- if (bind_mask == 0) {
- bindTxId[0] = packet[3];
- bindTxId[1] = packet[4];
- } else if (bindTxId[0] != packet[3] ||
- bindTxId[1] != packet[4]) {
- Debug(2,"Bind restart\n");
- bind_mask = 0;
- listLength = 0;
- }
- for (uint8_t n = 0; n < 5; n++) {
- uint8_t c = packet[5] + n;
- if (c < sizeof(bindHopData)) {
- bindHopData[c] = packet[6 + n];
- bind_mask |= (uint64_t(1)<<c);
- listLength = MAX(listLength, c+1);
- }
- }
- // bind has finished when we have hopping data for all channels
- return (listLength == NUM_CHANNELS && bind_mask == ((uint64_t(1)<<NUM_CHANNELS)-1));
- }
- return false;
- }
- void AP_Radio_cc2500::setChannel(uint8_t channel)
- {
- cc2500.Strobe(CC2500_SIDLE);
- cc2500.WriteReg(CC2500_0A_CHANNR, channel);
- // manually recalibrate the PLL for the new channel. This
- // allows for temperature change and voltage fluctuation on
- // the flight board
- cc2500.Strobe(CC2500_SCAL);
- hal.scheduler->delay_microseconds(730);
- }
- void AP_Radio_cc2500::setChannelRX(uint8_t channel)
- {
- setChannel(channel);
- cc2500.Strobe(CC2500_SFRX);
- cc2500.Strobe(CC2500_SRX);
- }
- void AP_Radio_cc2500::nextChannel(uint8_t skip)
- {
- channr = (channr + skip) % listLength;
- setChannelRX(bindHopData[channr]);
- }
- void AP_Radio_cc2500::parse_frSkyX(const uint8_t *packet)
- {
- uint16_t c[8];
- c[0] = (uint16_t)((packet[10] <<8)& 0xF00) | packet[9];
- c[1] = (uint16_t)((packet[11]<<4)&0xFF0) | (packet[10]>>4);
- c[2] = (uint16_t)((packet[13] <<8)& 0xF00) | packet[12];
- c[3] = (uint16_t)((packet[14]<<4)&0xFF0) | (packet[13]>>4);
- c[4] = (uint16_t)((packet[16] <<8)& 0xF00) | packet[15];
- c[5] = (uint16_t)((packet[17]<<4)&0xFF0) | (packet[16]>>4);
- c[6] = (uint16_t)((packet[19] <<8)& 0xF00) | packet[18];
- c[7] = (uint16_t)((packet[20]<<4)&0xFF0) | (packet[19]>>4);
- uint8_t j;
- for (uint8_t i=0; i<8; i++) {
- if (c[i] > 2047) {
- j = 8;
- c[i] = c[i] - 2048;
- } else {
- j = 0;
- }
- if (c[i] == 0) {
- continue;
- }
- uint16_t word_temp = (((c[i]-64)<<1)/3+860);
- if ((word_temp > 800) && (word_temp < 2200)) {
- uint8_t chan = i+j;
- if (chan < CC2500_MAX_PWM_CHANNELS) {
- pwm_channels[chan] = word_temp;
- if (chan >= chan_count) {
- chan_count = chan+1;
- }
- }
- }
- }
- }
- uint16_t AP_Radio_cc2500::calc_crc(const uint8_t *data, uint8_t len)
- {
- uint16_t crc = 0;
- for (uint8_t i=0; i < len; i++) {
- crc = (crc<<8) ^ (CRCTable[((uint8_t)(crc>>8) ^ *data++) & 0xFF]);
- }
- return crc;
- }
- bool AP_Radio_cc2500::check_crc(uint8_t ccLen, uint8_t *packet)
- {
- if (ccLen == sizeof(srt_packet)+2 ||
- ccLen == sizeof(autobind_packet_cc2500)+2) {
- // using hardware CRC
- return true;
- } else if (ccLen == 32) {
- // D16 packet
- uint16_t lcrc = calc_crc(&packet[3],(ccLen-7));
- return ((lcrc >>8)==packet[ccLen-4] && (lcrc&0x00FF)==packet[ccLen-3]);
- }
- return false;
- }
- /*
- save bind info
- */
- void AP_Radio_cc2500::save_bind_info(void)
- {
- // access to storage for bind information
- StorageAccess bind_storage(StorageManager::StorageBindInfo);
- struct bind_info info;
- info.magic = bind_magic;
- info.bindTxId[0] = bindTxId[0];
- info.bindTxId[1] = bindTxId[1];
- info.bindOffset = bindOffset;
- info.wifi_chan = t_status.wifi_chan;
- memcpy(info.bindHopData, bindHopData, sizeof(info.bindHopData));
- bind_storage.write_block(0, &info, sizeof(info));
- }
- /*
- load bind info
- */
- bool AP_Radio_cc2500::load_bind_info(void)
- {
- // access to storage for bind information
- StorageAccess bind_storage(StorageManager::StorageBindInfo);
- struct bind_info info;
- if (!bind_storage.read_block(&info, 0, sizeof(info)) || info.magic != bind_magic) {
- return false;
- }
- bindTxId[0] = info.bindTxId[0];
- bindTxId[1] = info.bindTxId[1];
- bindOffset = info.bindOffset;
- listLength = NUM_CHANNELS;
- t_status.wifi_chan = info.wifi_chan;
- memcpy(bindHopData, info.bindHopData, sizeof(bindHopData));
- setup_hopping_table_SRT();
- return true;
- }
- /*
- send a D16 telemetry packet
- */
- void AP_Radio_cc2500::send_D16_telemetry(void)
- {
- uint8_t frame[15];
- memset(frame, 0, sizeof(frame));
- frame[0] = sizeof(frame)-1;
- frame[1] = bindTxId[0];
- frame[2] = bindTxId[1];
- frame[3] = packet3;
- if (telem_send_rssi) {
- frame[4] = MAX(MIN(t_status.rssi, 0x7f),1) | 0x80;
- } else {
- frame[4] = uint8_t(hal.analogin->board_voltage() * 10) & 0x7F;
- }
- telem_send_rssi = !telem_send_rssi;
- uint16_t lcrc = calc_crc(&frame[3], 10);
- frame[13] = lcrc>>8;
- frame[14] = lcrc;
- cc2500.Strobe(CC2500_SIDLE);
- cc2500.Strobe(CC2500_SFTX);
- if (get_fcc_test() <= 3) {
- // in CW FCC test modes we don't write to the FIFO, which
- // gives continuous transmission
- cc2500.WriteFifo(frame, sizeof(frame));
- }
- cc2500.Strobe(CC2500_STX);
- }
- /*
- send a SRT telemetry packet
- */
- void AP_Radio_cc2500::send_SRT_telemetry(void)
- {
- struct telem_packet_cc2500 pkt {};
- pkt.length = sizeof(pkt)-1;
- t_status.flags = 0;
- t_status.flags |= AP_Notify::flags.gps_status >= 3?TELEM_FLAG_GPS_OK:0;
- t_status.flags |= AP_Notify::flags.pre_arm_check?TELEM_FLAG_ARM_OK:0;
- t_status.flags |= AP_Notify::flags.failsafe_battery?0:TELEM_FLAG_BATT_OK;
- t_status.flags |= hal.util->get_soft_armed()?TELEM_FLAG_ARMED:0;
- t_status.flags |= AP_Notify::flags.have_pos_abs?TELEM_FLAG_POS_OK:0;
- t_status.flags |= AP_Notify::flags.video_recording?TELEM_FLAG_VIDEO:0;
- t_status.flight_mode = AP_Notify::flags.flight_mode;
- t_status.tx_max = get_tx_max_power();
- t_status.note_adjust = get_tx_buzzer_adjust();
- // send fw update packet for 7/8 of packets if any data pending
- if (fwupload.length != 0 &&
- fwupload.length > fwupload.acked &&
- ((fwupload.counter++ & 0x07) != 0) &&
- sem.take_nonblocking()) {
- pkt.type = fwupload.fw_type;
- pkt.payload.fw.seq = fwupload.sequence;
- uint32_t len = fwupload.length>fwupload.acked?fwupload.length - fwupload.acked:0;
- const uint8_t maxlen = sizeof(pkt.payload.fw.data);
- pkt.payload.fw.len = len<=maxlen?len:maxlen;
- pkt.payload.fw.offset = fwupload.offset+fwupload.acked;
- memcpy(&pkt.payload.fw.data[0], &fwupload.pending_data[fwupload.acked], pkt.payload.fw.len);
- fwupload.len = pkt.payload.fw.len;
- Debug(4, "sent fw seq=%u offset=%u len=%u type=%u\n",
- pkt.payload.fw.seq,
- pkt.payload.fw.offset,
- pkt.payload.fw.len,
- pkt.type);
- sem.give();
- } else {
- pkt.type = TELEM_STATUS;
- pkt.payload.status = t_status;
- }
- pkt.txid[0] = bindTxId[0];
- pkt.txid[1] = bindTxId[1];
- cc2500.Strobe(CC2500_SIDLE);
- cc2500.Strobe(CC2500_SFTX);
- if (get_fcc_test() <= 3) {
- // in CW FCC test modes we don't write to the FIFO, which gives
- // continuous transmission
- cc2500.WriteFifo((const uint8_t *)&pkt, sizeof(pkt));
- }
- cc2500.Strobe(CC2500_STX);
- if (last_wifi_channel != t_status.wifi_chan) {
- setup_hopping_table_SRT();
- save_bind_info();
- }
- telem_send_count++;
- }
- /*
- send a fwupload ack if needed
- */
- void AP_Radio_cc2500::check_fw_ack(void)
- {
- if (fwupload.need_ack && sem.take_nonblocking()) {
- // ack the send of a DATA96 fw packet to TX
- fwupload.need_ack = false;
- uint8_t data16[16] {};
- uint32_t ack_to = fwupload.offset + fwupload.acked;
- memcpy(&data16[0], &ack_to, 4);
- mavlink_msg_data16_send(fwupload.chan, 42, 4, data16);
- Debug(4,"sent ack DATA16\n");
- sem.give();
- }
- }
- /*
- support all 4 rc input modes by swapping channels.
- */
- void AP_Radio_cc2500::map_stick_mode(uint16_t *channels)
- {
- switch (get_stick_mode()) {
- case 1: {
- // mode1
- uint16_t tmp = channels[1];
- channels[1] = 3000 - channels[2];
- channels[2] = 3000 - tmp;
- break;
- }
- case 3: {
- // mode3
- uint16_t tmp = channels[1];
- channels[1] = 3000 - channels[2];
- channels[2] = 3000 - tmp;
- tmp = channels[0];
- channels[0] = channels[3];
- channels[3] = tmp;
- break;
- }
- case 4: {
- // mode4
- uint16_t tmp = channels[0];
- channels[0] = channels[3];
- channels[3] = tmp;
- break;
- }
- case 2:
- default:
- // nothing to do, transmitter is natively mode2
- break;
- }
- }
- /*
- check if we are the 2nd RX bound to this TX
- */
- void AP_Radio_cc2500::check_double_bind(void)
- {
- if (tx_pps <= telem_send_count ||
- get_autobind_time() == 0) {
- return;
- }
- // the TX has received more telemetry packets in the last second
- // than we have ever sent. There must be another RX sending
- // telemetry packets. We will reset our mfg_id and go back waiting
- // for a new bind packet, hopefully with the right TX
- Debug(1,"Double-bind detected\n");
- // clear the current bind information
- radio_singleton->bindTxId[0] = 1;
- radio_singleton->bindTxId[1] = 1;
- radio_singleton->setup_hopping_table_SRT();
- radio_singleton->protocolState = STATE_SEARCH;
- radio_singleton->packet_timer = AP_HAL::micros();
- radio_singleton->stats.recv_packets = 0;
- radio_singleton->chanskip = 1;
- radio_singleton->nextChannel(1);
- }
- #endif // HAL_RCINPUT_WITH_AP_RADIO
- #endif // CONFIG_HAL_BOARD == HAL_BOARD_CHIBIOS
|