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 /********************/
16 /** A7105 routines **/
17 /********************/
18 #ifdef A7105_INSTALLED
19 #include "iface_a7105.h"
21 void A7105_WriteData(uint8_t len, uint8_t channel)
25 SPI_Write(A7105_RST_WRPTR);
26 SPI_Write(A7105_05_FIFO_DATA);
27 for (i = 0; i < len; i++)
30 if(protocol!=PROTO_WFLY2)
32 if(!(protocol==PROTO_FLYSKY || (protocol==PROTO_KYOSHO && sub_protocol==KYOSHO_HYPE)))
34 A7105_Strobe(A7105_STANDBY); //Force standby mode, ie cancel any TX or RX...
35 A7105_SetTxRxMode(TX_EN); //Switch to PA
37 A7105_WriteReg(A7105_0F_PLL_I, channel);
38 A7105_Strobe(A7105_TX);
42 void A7105_ReadData(uint8_t len)
45 A7105_Strobe(A7105_RST_RDPTR);
47 SPI_Write(0x40 | A7105_05_FIFO_DATA); //bit 6 =1 for reading
49 packet[i]=SPI_SDI_Read();
53 void A7105_WriteReg(uint8_t address, uint8_t data) {
61 uint8_t A7105_ReadReg(uint8_t address)
65 SPI_Write(address |=0x40); //bit 6 =1 for reading
66 result = SPI_SDI_Read();
71 //------------------------
72 void A7105_SetTxRxMode(uint8_t mode)
76 A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x33);
77 A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x31);
82 A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x31);
83 A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x33);
87 //The A7105 seems to some with a cross-wired power-amp (A7700)
88 //On the XL7105-D03, TX_EN -> RXSW and RX_EN -> TXSW
89 //This means that sleep mode is wired as RX_EN = 1 and TX_EN = 1
90 //If there are other amps in use, we'll need to fix this
91 A7105_WriteReg(A7105_0B_GPIO1_PIN1, 0x33);
92 A7105_WriteReg(A7105_0C_GPIO2_PIN_II, 0x33);
96 //------------------------
101 A7105_WriteReg(A7105_00_MODE, 0x00);
102 delayMilliseconds(1);
103 A7105_SetTxRxMode(TXRX_OFF); //Set both GPIO as output and low
104 result=A7105_ReadReg(A7105_10_PLL_II) == 0x9E; //check if is reset.
105 A7105_Strobe(A7105_STANDBY);
109 void A7105_WriteID(uint32_t ida)
112 SPI_Write(A7105_06_ID_DATA); //ex id=0x5475c52a ;txid3txid2txid1txid0
113 SPI_Write((ida>>24)&0xff); //54
114 SPI_Write((ida>>16)&0xff); //75
115 SPI_Write((ida>>8)&0xff); //c5
116 SPI_Write((ida>>0)&0xff); //2a
121 static void A7105_SetPower_Value(int power)
123 //Power amp is ~+16dBm so:
124 //TXPOWER_100uW = -23dBm == PAC=0 TBG=0
125 //TXPOWER_300uW = -20dBm == PAC=0 TBG=1
126 //TXPOWER_1mW = -16dBm == PAC=0 TBG=2
127 //TXPOWER_3mW = -11dBm == PAC=0 TBG=4
128 //TXPOWER_10mW = -6dBm == PAC=1 TBG=5
129 //TXPOWER_30mW = 0dBm == PAC=2 TBG=7
130 //TXPOWER_100mW = 1dBm == PAC=3 TBG=7
131 //TXPOWER_150mW = 1dBm == PAC=3 TBG=7
134 case 0: pac = 0; tbg = 0; break;
135 case 1: pac = 0; tbg = 1; break;
136 case 2: pac = 0; tbg = 2; break;
137 case 3: pac = 0; tbg = 4; break;
138 case 4: pac = 1; tbg = 5; break;
139 case 5: pac = 2; tbg = 7; break;
140 case 6: pac = 3; tbg = 7; break;
141 case 7: pac = 3; tbg = 7; break;
142 default: pac = 0; tbg = 0; break;
144 A7105_WriteReg(0x28, (pac << 3) | tbg);
148 void A7105_SetPower()
150 uint8_t power=A7105_BIND_POWER;
152 #ifdef A7105_ENABLE_LOW_POWER
153 power=IS_POWER_FLAG_on?A7105_HIGH_POWER:A7105_LOW_POWER;
155 power=A7105_HIGH_POWER;
158 power=A7105_RANGE_POWER;
159 if(prev_power != power)
161 A7105_WriteReg(A7105_28_TX_TEST, power);
166 void A7105_Strobe(uint8_t address) {
172 // Fine tune A7105 LO base frequency
173 // this is required for some A7105 modules and/or RXs with inaccurate crystal oscillator
174 void A7105_AdjustLOBaseFreq(uint8_t cmd)
176 static int16_t old_offset=2048;
179 { // Called at init of the A7105
184 #ifdef FORCE_HUBSAN_TUNING
185 offset=(int16_t)FORCE_HUBSAN_TUNING;
189 #ifdef FORCE_BUGS_TUNING
190 offset=(int16_t)FORCE_BUGS_TUNING;
194 #ifdef FORCE_FLYSKY_TUNING
195 offset=(int16_t)FORCE_FLYSKY_TUNING;
199 #ifdef FORCE_HEIGHT_TUNING
200 offset=(int16_t)FORCE_HEIGHT_TUNING;
204 #ifdef FORCE_PELIKAN_TUNING
205 offset=(int16_t)FORCE_PELIKAN_TUNING;
209 #ifdef FORCE_KYOSHO_TUNING
210 offset=(int16_t)FORCE_KYOSHO_TUNING;
214 #ifdef FORCE_JOYSWAY_TUNING
215 offset=(int16_t)FORCE_JOYSWAY_TUNING;
219 #ifdef FORCE_WFLY2_TUNING
220 offset=(int16_t)FORCE_WFLY2_TUNING;
224 case PROTO_AFHDS2A_RX:
225 #ifdef FORCE_AFHDS2A_TUNING
226 offset=(int16_t)FORCE_AFHDS2A_TUNING;
231 if(offset==1024) // Use channel 15 as an input
232 offset=convert_channel_16b_nolimit(CH15,-300,300,false);
234 if(old_offset==offset) // offset is the same as before...
238 // LO base frequency = 32e6*(bip+(bfp/(2^16)))
239 uint8_t bip; // LO base frequency integer part
240 uint16_t bfp; // LO base frequency fractional part
241 offset++; // as per datasheet, not sure why recommended, but that's a +1kHz drift only ...
245 bip = 0x4a; // 2368 MHz
246 bfp = 0xffff + offset;
250 bip = 0x4b; // 2400 MHz (default)
253 A7105_WriteReg( A7105_11_PLL_III, bip);
254 A7105_WriteReg( A7105_12_PLL_IV, (bfp >> 8) & 0xff);
255 A7105_WriteReg( A7105_13_PLL_V, bfp & 0xff);
256 //debugln("Channel: %d, offset: %d, bip: %2x, bfp: %4x", Channel_data[14], offset, bip, bfp);
259 static void __attribute__((unused)) A7105_SetVCOBand(uint8_t vb1, uint8_t vb2)
260 { // Set calibration band value to best match
261 uint8_t diff1, diff2;
273 if (diff1 == diff2 || diff1 > diff2)
274 A7105_WriteReg(A7105_25_VCO_SBCAL_I, vb1 | 0x08);
276 A7105_WriteReg(A7105_25_VCO_SBCAL_I, vb2 | 0x08);
279 #if defined(AFHDS2A_A7105_INO) || defined(AFHDS2A_RX_A7105_INO)
280 const uint8_t PROGMEM AFHDS2A_A7105_regs[] = {
281 0xFF, 0x42 | (1<<5), 0x00, 0x25, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x3c, 0x05, 0x00, 0x50, // 00 - 0f
282 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x4f, 0x62, 0x80, 0xFF, 0xFF, 0x2a, 0x32, 0xc3, 0x1f, // 10 - 1f
283 0x1e, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
284 0x01, 0x0f // 30 - 31
287 #ifdef BUGS_A7105_INO
288 const uint8_t PROGMEM BUGS_A7105_regs[] = {
289 0xFF, 0x42, 0x00, 0x15, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x05, 0x01, 0x50, // 00 - 0f
290 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f, // 10 - 1f
291 0x16, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x3b, 0x00, 0x0b, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
292 0x01, 0x0f // 30 - 31
295 #ifdef FLYSKY_A7105_INO
296 const uint8_t PROGMEM FLYSKY_A7105_regs[] = {
297 0xff, 0x42, 0x00, 0x14, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50, // 00 - 0f
298 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x0f, // 10 - 1f
299 0x13, 0xc3, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
300 0x01, 0x0f // 30 - 31
303 #ifdef HEIGHT_A7105_INO
304 const uint8_t PROGMEM HEIGHT_A7105_regs[] = {
305 0xff, 0x42, 0x00, 0x07, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x01, 0x50, // 00 - 0f
306 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x1f, // 10 - 1f
307 0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3a, 0x00, 0x3f, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
308 0x01, 0x0f // 30 - 31
311 #ifdef HUBSAN_A7105_INO
312 const uint8_t PROGMEM HUBSAN_A7105_regs[] = {
313 0xFF, 0x63, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF ,0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x05, 0x04, 0xFF, // 00 - 0f
314 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x2B, 0xFF, 0xFF, 0x62, 0x80, 0xFF, 0xFF, 0x0A, 0xFF, 0xFF, 0x07, // 10 - 1f
315 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x47, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 20 - 2f
316 0xFF, 0xFF // 30 - 31
319 #ifdef PELIKAN_A7105_INO
320 const uint8_t PROGMEM PELIKAN_A7105_regs[] = {
321 0xff, 0x42, 0x00, 0x0F, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x01, 0x50, // 00 - 0f
322 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0xc3, 0x07, // 10 - 1f
323 0x16, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3b, 0x00, 0x1f, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
324 0x01, 0x0f // 30 - 31
327 #ifdef KYOSHO_A7105_INO
328 const uint8_t PROGMEM KYOSHO_A7105_regs[] = {
329 0xff, 0x42, 0xff, 0x25, 0x00, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x21, 0x05, 0x00, 0x50, // 00 - 0f
330 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0x03, 0x1f, // 10 - 1f
331 0x1e, 0x00, 0x00, 0xff, 0x00, 0x00, 0x23, 0x70, 0x1F, 0x47, 0x80, 0x57, 0x01, 0x45, 0x19, 0x00, // 20 - 2f
332 0x01, 0x0f // 30 - 31
334 const uint8_t PROGMEM KYOSHO_HYPE_A7105_regs[] = {
335 0xff, 0x42, 0x00, 0x10, 0xC0, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x05, 0x01, 0x04, // 00 - 0f
336 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x00, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x96, 0xc2, 0x1f, // 10 - 1f
337 0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x3a, 0x00, 0x17, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f
338 0x01, 0x0f // 30 - 31
341 #ifdef WFLY2_A7105_INO //A7106 values
342 const uint8_t PROGMEM WFLY2_A7105_regs[] = {
343 0xff, 0x62, 0xff, 0x1F, 0x40, 0xff, 0xff ,0x00, 0x00, 0x00, 0x00, 0x33, 0x33, 0x05, 0x00, 0x64, // 00 - 0f Changes: 0B:19->33, 0C:01,33
344 0x9e, 0x4b, 0x00, 0x02, 0x16, 0x2b, 0x12, 0x40, 0x62, 0x80, 0x80, 0x00, 0x0a, 0x32, 0x03, 0x0f, // 10 - 1f 1C:4A->0A
345 0x12, 0x00, 0x00, 0xff, 0x00, 0x00, 0x23, 0x70, 0x15, 0x47, 0x80, 0x03, 0x01, 0x45, 0x18, 0x00, // 20 - 2f 2B:77->03, 2E:19->18
346 0x01, 0x0f // 30 - 31
349 #ifdef JOYSWAY_A7105_INO
350 const uint8_t PROGMEM JOYSWAY_A7105_regs[] = {
351 0xff, 0x62, 0xff, 0x0F, 0x00, 0xff, 0xff ,0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0xF5, 0x00, 0x15, // 00 - 0f
352 0x9E, 0x4B, 0x00, 0x03, 0x56, 0x2B, 0x12, 0x4A, 0x02, 0x80, 0x80, 0x00, 0x0E, 0x91, 0x03, 0x0F, // 10 - 1f
353 0x16, 0x2A, 0x00, 0xff, 0xff, 0xff, 0x3A, 0x06, 0x1F, 0x47, 0x80, 0x01, 0x05, 0x45, 0x18, 0x00, // 20 - 2f
354 0x01, 0x0f // 30 - 31
358 #define ID_NORMAL 0x55201041
359 #define ID_PLUS 0xAA201041
360 void A7105_Init(void)
362 uint8_t *A7105_Regs=0;
363 uint8_t vco_calibration0, vco_calibration1;
365 #ifdef JOYSWAY_A7105_INO
366 if(protocol==PROTO_JOYSWAY)
368 A7105_Regs=(uint8_t*)JOYSWAY_A7105_regs;
372 #ifdef WFLY2_A7105_INO
373 if(protocol==PROTO_WFLY2)
375 A7105_Regs=(uint8_t*)WFLY2_A7105_regs;
379 #ifdef HEIGHT_A7105_INO
380 if(protocol==PROTO_HEIGHT)
382 A7105_Regs=(uint8_t*)HEIGHT_A7105_regs;
383 A7105_WriteID(0x25A53C45);
387 #ifdef PELIKAN_A7105_INO
388 if(protocol==PROTO_PELIKAN)
390 A7105_Regs=(uint8_t*)PELIKAN_A7105_regs;
391 A7105_WriteID(0x06230623);
395 #ifdef BUGS_A7105_INO
396 if(protocol==PROTO_BUGS)
397 A7105_Regs=(uint8_t*)BUGS_A7105_regs;
400 #ifdef HUBSAN_A7105_INO
401 if(protocol==PROTO_HUBSAN)
403 A7105_WriteID(ID_NORMAL);
404 A7105_Regs=(uint8_t*)HUBSAN_A7105_regs;
409 A7105_WriteID(0x5475c52A);//0x2Ac57554
410 #ifdef FLYSKY_A7105_INO
411 if(protocol==PROTO_FLYSKY)
412 A7105_Regs=(uint8_t*)FLYSKY_A7105_regs;
414 #if defined(AFHDS2A_A7105_INO) || defined(AFHDS2A_RX_A7105_INO)
415 if(protocol==PROTO_AFHDS2A || protocol==PROTO_AFHDS2A_RX)
416 A7105_Regs=(uint8_t*)AFHDS2A_A7105_regs;
418 #ifdef KYOSHO_A7105_INO
419 if(protocol==PROTO_KYOSHO)
421 if(sub_protocol==KYOSHO_HYPE)
422 A7105_Regs=(uint8_t*)KYOSHO_HYPE_A7105_regs;
423 else //FHSS && SYNCRO
424 A7105_Regs=(uint8_t*)KYOSHO_A7105_regs;
429 for (uint8_t i = 0; i < 0x32; i++)
431 uint8_t val=pgm_read_byte_near(&A7105_Regs[i]);
432 #ifdef FLYSKY_A7105_INO
433 if(protocol==PROTO_FLYSKY && sub_protocol==CX20)
435 if(i==0x0E) val=0x01;
436 if(i==0x1F) val=0x1F;
437 if(i==0x20) val=0x1E;
440 #ifdef HEIGHT_A7105_INO
441 if(protocol==PROTO_HEIGHT && sub_protocol==HEIGHT_8CH)
442 if(i==0x03) val=0x0A;
445 A7105_WriteReg(i, val);
447 A7105_Strobe(A7105_STANDBY);
449 if(protocol==PROTO_KYOSHO && sub_protocol!=KYOSHO_HYPE)
450 {//strange calibration...
451 //IF Filter Bank Calibration
452 A7105_WriteReg(A7105_02_CALC,0x0F);
453 while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
454 // A7105_ReadReg(A7105_22_IF_CALIB_I);
455 // A7105_ReadReg(A7105_24_VCO_CURCAL);
456 // A7105_ReadReg(25_VCO_SBCAL_I);
457 // A7105_ReadReg(1A_RX_GAIN_II);
458 // A7105_ReadReg(1B_RX_GAIN_III);
462 //IF Filter Bank Calibration
463 A7105_WriteReg(A7105_02_CALC,1);
464 while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
465 // A7105_ReadReg(A7105_22_IF_CALIB_I);
466 // A7105_ReadReg(A7105_24_VCO_CURCAL);
468 if(protocol!=PROTO_HUBSAN)
470 //VCO Current Calibration
471 A7105_WriteReg(A7105_24_VCO_CURCAL,0x13); //Recommended calibration from A7105 Datasheet
472 //VCO Bank Calibration
473 A7105_WriteReg(A7105_26_VCO_SBCAL_II,0x3b); //Recommended calibration from A7105 Datasheet
476 //VCO Bank Calibrate channel 0
477 A7105_WriteReg(A7105_0F_CHANNEL, 0);
478 A7105_WriteReg(A7105_02_CALC,2);
479 while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
480 vco_calibration0 = A7105_ReadReg(A7105_25_VCO_SBCAL_I);
482 //VCO Bank Calibrate channel A0
483 A7105_WriteReg(A7105_0F_CHANNEL, 0xa0);
484 A7105_WriteReg(A7105_02_CALC, 2);
485 while(A7105_ReadReg(A7105_02_CALC)); // Wait for calibration to end
486 vco_calibration1 = A7105_ReadReg(A7105_25_VCO_SBCAL_I);
488 if(protocol==PROTO_BUGS || protocol==PROTO_WFLY2)
489 A7105_SetVCOBand(vco_calibration0 & 0x07, vco_calibration1 & 0x07); // Set calibration band value to best match
491 if(protocol!=PROTO_HUBSAN)
496 vco_calibration1=0x08;
499 vco_calibration1=0x02;
502 if(sub_protocol == PELIKAN_SCX24)
504 vco_calibration1=0x0A;
507 case PROTO_KYOSHO: //sub_protocol Hype
508 vco_calibration1=0x0C;
511 vco_calibration1=0x09;
514 vco_calibration1=0x0A;
517 A7105_WriteReg(A7105_25_VCO_SBCAL_I,vco_calibration1); //Reset VCO Band calibration
520 A7105_SetTxRxMode(TX_EN);
523 #ifdef USE_A7105_CH15_TUNING
524 A7105_AdjustLOBaseFreq(0);
527 A7105_Strobe(A7105_STANDBY);