2 This project is free software: you can redistribute it and/or modify
3 it under the terms of the GNU General Public License as published by
4 the Free Software Foundation, either version 3 of the License, or
5 (at your option) any later version.
7 Multiprotocol is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
12 You should have received a copy of the GNU General Public License
13 along with Multiprotocol. If not, see <http://www.gnu.org/licenses/>.
15 // Radiolink surface protocol. TXs: RC4GS,RC6GS. Compatible RXs:R7FG(Std),R6FG,R6F,R8EF,R8FM,R8F,R4FGM
17 #if defined(RLINK_CC2500_INO)
19 #include "iface_cc2500.h"
22 //#define RLINK_DEBUG_TELEM
24 //#define RLINK_FORCE_ID
25 //#define RLINK_RC4G_FORCE_ID
27 #define RLINK_TX_PACKET_LEN 33
28 #define RLINK_RX_PACKET_LEN 15
29 #define RLINK_TX_ID_LEN 4
41 static uint32_t __attribute__((unused)) RLINK_prng_next(uint32_t r)
43 return 0xA5E2A705 * r + 0x754DB79B;
46 static void __attribute__((unused)) RLINK_init_random(uint32_t id)
51 for (uint8_t i=0; i<31; i++)
52 result = RLINK_prng_next(result);
56 static uint8_t __attribute__((unused)) RLINK_next_random_swap()
58 uint8_t result = (RLINK_rand2 >> 16) + RLINK_rand2 + (RLINK_rand1 >> 16) + RLINK_rand1;
60 RLINK_rand2 = RLINK_prng_next(RLINK_rand2);
61 RLINK_rand1 = RLINK_prng_next(RLINK_rand1);
66 static uint32_t __attribute__((unused)) RLINK_compute_start_id(uint32_t id)
68 return id * 0xF65EF9F9u + 0x2EDDF6CAu;
71 static void __attribute__((unused)) RLINK_shuffle_freqs(uint32_t seed)
73 RLINK_init_random(seed);
75 for(uint8_t i=0; i<RLINK_HOP; i++)
77 uint8_t r = RLINK_next_random_swap();
78 uint8_t tmp = hopping_frequency[r];
79 hopping_frequency[r] = hopping_frequency[i];
80 hopping_frequency[i] = tmp;
84 static void __attribute__((unused)) RLINK_hop()
86 uint8_t inc=3*(rx_tx_addr[0]&3);
89 for(uint8_t i=0; i<RLINK_HOP; i++)
90 hopping_frequency[i] = (12*i) + inc;
93 RLINK_shuffle_freqs(RLINK_compute_start_id(rx_tx_addr[0] + (rx_tx_addr[1] << 8)));
94 RLINK_shuffle_freqs(RLINK_compute_start_id(rx_tx_addr[2] + (rx_tx_addr[3] << 8)));
96 // replace one of the channel randomely
97 rf_ch_num=random(0xfefefefe)%0x11; // 0x00..0x10
98 if(inc==9) inc=6; // frequency exception
99 hopping_frequency[rf_ch_num]=12*16+inc;
102 static void __attribute__((unused)) RLINK_TXID_init()
104 #ifdef RLINK_RC4G_FORCE_ID
106 if(sub_protocol==RLINK_RC4G)
113 #ifdef RLINK_FORCE_ID
114 if(sub_protocol==RLINK_SURFACE)
115 memcpy(rx_tx_addr,"\x3A\x99\x22\x3A",RLINK_TX_ID_LEN); //surface RC6GS
117 memcpy(rx_tx_addr,"\xFC\x11\x0D\x20",RLINK_TX_ID_LEN); //air T8FB
119 // channels order depend on ID
120 if(sub_protocol!=RLINK_RC4G)
124 // Find 2 unused channels
125 // first channel is a multiple of 3 between 00 and 5D
126 // second channel is a multiple of 3 between 63 and BD
127 CC2500_Strobe(CC2500_SIDLE);
128 CC2500_WriteReg(CC2500_17_MCSM1,0x3C);
129 CC2500_Strobe(CC2500_SFRX);
130 CC2500_SetTxRxMode(RX_EN);
131 CC2500_Strobe(CC2500_SRX);
132 delayMilliseconds(1); //wait for RX mode
134 uint8_t val_low = 0xFF;
135 hopping_frequency[0] = 0x00;
136 hopping_frequency[1] = 0x63;
137 for(uint8_t ch=0; ch<=0xBD; ch+=3)
140 val_low = 0xFF; //init for second block
142 continue; //skip channel
143 CC2500_WriteReg(CC2500_0A_CHANNR, ch); //switch channel
144 delayMicroseconds(370); //wait to read
146 for(uint8_t i=0;i<16;i++)
147 val += CC2500_ReadReg(CC2500_34_RSSI | CC2500_READ_BURST);
149 debug("C:%02X RSSI:%02X",ch,val);
154 hopping_frequency[ch<0x63?0:1]=ch; //save best channel
158 CC2500_WriteReg(CC2500_17_MCSM1,0x30);
159 CC2500_Strobe(CC2500_SIDLE);
160 CC2500_SetTxRxMode(TX_EN);
161 #ifdef RLINK_RC4G_FORCE_ID
162 hopping_frequency[0] = 0x03;
163 hopping_frequency[1] = 0x6F;
169 for(uint8_t i=0;i<RLINK_TX_ID_LEN;i++)
170 debug(" 0x%02X",rx_tx_addr[i]);
172 debug("Hop(%d):", rf_ch_num);
173 for(uint8_t i=0;i<RLINK_HOP;i++)
174 debug(" 0x%02X",hopping_frequency[i]);
179 const PROGMEM uint8_t RLINK_init_values[] = {
180 /* 00 */ 0x5B, 0x06, 0x5C, 0x07, 0xAB, 0xCD, 0x40, 0x04,
181 /* 08 */ 0x45, 0x00, 0x00, 0x06, 0x00, 0x5C, 0x62, 0x76,
182 /* 10 */ 0x7A, 0x7F, 0x13, 0x23, 0xF8, 0x44, 0x07, 0x30,
183 /* 18 */ 0x18, 0x16, 0x6C, 0x43, 0x40, 0x91, 0x87, 0x6B,
184 /* 20 */ 0xF8, 0x56, 0x10, 0xA9, 0x0A, 0x00, 0x11
187 static void __attribute__((unused)) RLINK_rf_init()
189 CC2500_Strobe(CC2500_SIDLE);
191 for (uint8_t i = 0; i < 39; ++i)
192 CC2500_WriteReg(i, pgm_read_byte_near(&RLINK_init_values[i]));
194 if(sub_protocol==RLINK_DUMBORC)
196 CC2500_WriteReg(4, 0xBA);
197 CC2500_WriteReg(5, 0xDC);
199 else if(sub_protocol==RLINK_RC4G)
200 CC2500_WriteReg(5, 0xA5);
202 CC2500_WriteReg(CC2500_0C_FSCTRL0, option);
204 CC2500_SetTxRxMode(TX_EN);
207 static void __attribute__((unused)) RLINK_send_packet()
209 static uint32_t pseudo=0;
211 uint8_t bitsavailable = 0;
214 CC2500_Strobe(CC2500_SIDLE);
217 packet[0] = RLINK_TX_PACKET_LEN;
220 packet[1] = 0x02; // 0x02 telemetry request flag
222 packet[1] = 0x00; // no telemetry
228 //radiolink additionnal ID which is working only on a small set of RXs
229 //if(RX_num) packet[1] |= ((RX_num+2)<<4)+4; // RX number limited to 10 values, 0 is a wildcard
232 packet[1] |= 0x21; //air 0x21 on dump but it looks to support telemetry at least RSSI
235 packet[1] = 0x00; //always 0x00 on dump
240 memcpy(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN);
242 // pack 16 channels on 11 bits values between 170 and 1876, 1023 middle. The last 8 channels are failsafe values associated to the first 8 values.
243 for (uint8_t i = 0; i < 16; i++)
245 uint32_t val = convert_channel_16b_nolimit(i,170,1876,false); // allow extended limits
251 bits |= val << bitsavailable;
253 while (bitsavailable >= 8) {
254 packet[idx++] = bits & 0xff;
261 pseudo=((pseudo * 0xAA) + 0x03) % 0x7673; // calc next pseudo random value
262 CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[pseudo & 0x0F]);
264 packet[29]= pseudo >> 8;
265 packet[30]= 0x00; // unknown
266 packet[31]= 0x00; // unknown
267 packet[32]= rf_ch_num; // index of value changed in the RF table
271 for(uint8_t i=1;i<33;i++)
276 CC2500_WriteData(packet, RLINK_TX_PACKET_LEN+1);
280 if(packet_count>5) packet_count=0;
283 debugln("C= 0x%02X",hopping_frequency[pseudo & 0x0F]);
285 for(uint8_t i=1;i<RLINK_TX_PACKET_LEN+1;i++)
286 debug(" 0x%02X",packet[i]);
292 static void __attribute__((unused)) RLINK_RC4G_send_packet()
296 CC2500_WriteReg(CC2500_0A_CHANNR, hopping_frequency[packet_count>>1]);
298 debug("C= 0x%02X ",hopping_frequency[packet_count>>1]);
303 memcpy(&packet[1], &rx_tx_addr[1], 3);
305 for(uint8_t i=0;i<2;i++)
307 val = Channel_data[2*i ] +400 -24;
309 packet[8+i ] = val>>8;
310 val = Channel_data[2*i+1] +400 -24;
312 packet[8+i ] |= (val>>4) & 0xF0;
314 //special channel which is linked to gyro on the orginal TX but allocating it on CH5 here
315 packet[10] = convert_channel_16b_limit(CH5,0,100);
317 for(uint8_t i=0;i<4;i++)
318 packet[11+i] = convert_channel_16b_limit(CH6+i,0,200);
321 packet_count &= 0x03;
322 packet[15] = hopping_frequency[packet_count>>1];
324 CC2500_WriteData(packet, 16);
328 for(uint8_t i=1;i<16;i++)
329 debug(" 0x%02X",packet[i]);
335 #define RLINK_TIMING_PROTO 20000-100 // -100 for compatibility with R8EF
336 #define RLINK_TIMING_RFSEND 10500
337 #define RLINK_TIMING_CHECK 2000
338 #define RLINK_RC4G_TIMING_PROTO 14460
339 uint16_t RLINK_callback()
341 if(sub_protocol == RLINK_RC4G)
345 telemetry_set_input_sync(RLINK_RC4G_TIMING_PROTO);
348 CC2500_SetFreqOffset();
349 RLINK_RC4G_send_packet();
353 return RLINK_RC4G_TIMING_PROTO;
359 telemetry_set_input_sync(RLINK_TIMING_PROTO);
362 CC2500_SetFreqOffset();
364 #if not defined RLINK_HUB_TELEMETRY
365 return RLINK_TIMING_PROTO;
367 if(!(packet[1]&0x02))
368 return RLINK_TIMING_PROTO; //Normal packet
371 return RLINK_TIMING_RFSEND;
373 CC2500_Strobe(CC2500_SIDLE);
374 CC2500_Strobe(CC2500_SFRX);
375 CC2500_SetTxRxMode(RX_EN);
376 CC2500_Strobe(CC2500_SRX);
378 return RLINK_TIMING_PROTO-RLINK_TIMING_RFSEND-RLINK_TIMING_CHECK;
380 len = CC2500_ReadReg(CC2500_3B_RXBYTES | CC2500_READ_BURST) & 0x7F;
381 if (len == RLINK_RX_PACKET_LEN + 1 + 2) //Telemetry frame is 15 bytes + 1 byte for length + 2 bytes for RSSI&LQI&CRC
383 #ifdef RLINK_DEBUG_TELEM
386 CC2500_ReadData(packet_in, len);
387 if(packet_in[0]==RLINK_RX_PACKET_LEN && (packet_in[len-1] & 0x80) && memcmp(&packet[2],rx_tx_addr,RLINK_TX_ID_LEN)==0 && packet_in[6]==packet[1])
388 {//Correct telemetry received: length, CRC, ID and type
389 #ifdef RLINK_DEBUG_TELEM
390 for(uint8_t i=0;i<len;i++)
391 debug(" %02X",packet_in[i]);
393 TX_RSSI = packet_in[len-2];
398 RX_RSSI=packet_in[7]&0x7F; //Should be packet_in[7]-256 but since it's an uint8_t...
399 v_lipo1=packet_in[8]<<1; //RX Batt
400 v_lipo2=packet_in[9]; //Batt
401 telemetry_link=1; //Send telemetry out
405 #ifdef RLINK_DEBUG_TELEM
409 if (millis() - pps_timer >= 2000)
410 {//1 telemetry packet every 100ms
411 pps_timer = millis();
416 debugln("%d pps", pps_counter);
417 TX_LQI = pps_counter; //0..100%
420 CC2500_SetTxRxMode(TX_EN);
421 phase=RLINK_DATA; // DATA
422 return RLINK_TIMING_CHECK;
430 BIND_DONE; // Not a TX bind protocol