1 #if defined(HAS_VTX_SPI)
12 #define SYNTHESIZER_REGISTER_A 0x00
13 #define SYNTHESIZER_REGISTER_B 0x01
14 #define SYNTHESIZER_REGISTER_C 0x02
15 #define RF_VCO_DFC_CONTROL_REGISTER 0x03
16 #define VCO_CONTROL_REGISTER 0x04
17 #define VCO_CONTROL_REGISTER_CONT 0x05
18 #define AUDIO_MODULATOR_CONTROL_REGISTER 0x06
19 #define PRE_DRIVER_AND_PA_CONTROL_REGISTER 0x07
20 #define STATE_REGISTER 0x0F
22 #define SYNTH_REG_A_DEFAULT 0x0190
24 #define POWER_AMP_ON 0b00000100111110111111
25 #define POWER_AMP_OFF 0x00
26 // ESP32 DAC pins are 0-255
27 #define MIN_DAC 1 // Testing required.
28 #define MAX_DAC 250 // Absolute max is 255. But above 250 does nothing.
30 #define MIN_PWM 2000 // could be even higher than that, depends on HW.
31 #define MAX_PWM 3700 // Absolute max is 4095. But above 3600 does nothing.
36 #define WRITE_BIT 0x01
38 #define RTC6705_BOOT_DELAY 350
39 #define RTC6705_PLL_SETTLE_TIME_MS 500 // ms - after changing frequency turn amps back on after this time for clean switching
40 #define VTX_POWER_INTERVAL_MS 20
42 #define BUF_PACKET_SIZE 4 // 25b packet in 4 bytes
44 #if defined(PLATFORM_ESP32)
45 pwm_channel_t rfAmpPwmChannel
= -1;
48 uint16_t vtxSPIFrequency
= 6000;
49 static uint16_t vtxSPIFrequencyCurrent
= 6000;
51 uint8_t vtxSPIPowerIdx
= 0;
52 static uint8_t vtxSPIPowerIdxCurrent
= 0;
54 uint8_t vtxSPIPitmode
= 1;
55 static uint8_t vtxSPIPitmodeCurrent
= 1;
57 bool vtxPowerAmpEnable
= false;
58 bool vtxPowerAmpEnableCurrent
= false;
60 static uint8_t RfAmpVrefState
= 0;
62 static uint16_t vtxSPIPWM
= MAX_PWM
;
63 static uint16_t vtxPreviousSPIPWM
= 0;
65 static uint16_t vtxMinPWM
= MIN_PWM
;
66 static uint16_t vtxMaxPWM
= MAX_PWM
;
68 static uint16_t VpdSetPoint
= 0;
69 static uint16_t Vpd
= 0;
71 static bool stopVtxMonitoring
= false;
73 #define VPD_SETPOINT_0_MW VPD_BUFFER // to avoid overflow
74 #define VPD_SETPOINT_YOLO_MW 2250
75 const uint16_t *VpdSetPointArray25mW
= nullptr;
76 const uint16_t *VpdSetPointArray100mW
= nullptr;
77 const uint16_t *PwmArray25mW
= nullptr;
78 const uint16_t *PwmArray100mW
= nullptr;
80 uint16_t VpdFreqArray
[] = {5650, 5750, 5850, 5950};
81 uint8_t VpdSetPointCount
= ARRAY_SIZE(VpdFreqArray
);
83 static SPIClass
*vtxSPI
;
85 static void rtc6705WriteRegister(uint32_t regData
)
87 // When sharing the SPI Bus control of the NSS pin is done by us
88 if (GPIO_PIN_SPI_VTX_SCK
== GPIO_PIN_SCK
)
90 vtxSPI
->setBitOrder(LSBFIRST
);
91 digitalWrite(GPIO_PIN_SPI_VTX_NSS
, LOW
);
94 // On some ESP32 MCUs there's a silicon bug which affects all 8n+1 bit and 1 bit transfers where the
95 // last bit sent is corrupt.
96 // See: https://github.com/ExpressLRS/ExpressLRS/pull/2406#issuecomment-1722573356
98 // To reproduce, use an ESP32S3 and 25 bit transfers, change from channel A4 to A1, then A1 to A4 (ok),
99 // then A4 to A3, then A3 to A4 (fail)
101 // 12816, 9286833 appears on the scope when changing from A:1->A:4
102 // 12816, 26064049 appears on the scope when changing from A:3->A:4
104 // 9286833 = 0_1000_1101_1011_0100_1011_0001
105 // 26064049 = 1_1000_1101_1011_0100_1011_0001
107 // Since the RTC6705 just ignores the extra bits, we send 32 bits and the RTC6705 ignores the last 7 bits.
108 vtxSPI
->transfer32(regData
);
110 if (GPIO_PIN_SPI_VTX_SCK
== GPIO_PIN_SCK
)
112 digitalWrite(GPIO_PIN_SPI_VTX_NSS
, HIGH
);
113 vtxSPI
->setBitOrder(MSBFIRST
);
117 static void rtc6705ResetSynthRegA()
119 uint32_t regData
= SYNTHESIZER_REGISTER_A
| (WRITE_BIT
<< 4) | (SYNTH_REG_A_DEFAULT
<< 5);
120 rtc6705WriteRegister(regData
);
123 static void rtc6705SetFrequency(uint32_t freq
)
125 rtc6705ResetSynthRegA();
127 VTxOutputMinimum(); // Set power to zero for clear channel switching
129 uint32_t f
= 25 * freq
;
130 uint32_t SYN_RF_N_REG
= f
/ 64;
131 uint32_t SYN_RF_A_REG
= f
% 64;
133 uint32_t regData
= SYNTHESIZER_REGISTER_B
| (WRITE_BIT
<< 4) | (SYN_RF_A_REG
<< 5) | (SYN_RF_N_REG
<< 12);
134 rtc6705WriteRegister(regData
);
137 static void rtc6705PowerAmpOn()
139 uint32_t regData
= PRE_DRIVER_AND_PA_CONTROL_REGISTER
| (WRITE_BIT
<< 4) | (POWER_AMP_ON
<< 5);
140 rtc6705WriteRegister(regData
);
143 static void RfAmpVrefOn()
145 if (!RfAmpVrefState
) digitalWrite(GPIO_PIN_RF_AMP_VREF
, HIGH
);
150 static void RfAmpVrefOff()
152 if (RfAmpVrefState
) digitalWrite(GPIO_PIN_RF_AMP_VREF
, LOW
);
159 if (vtxSPIPWM
== vtxPreviousSPIPWM
) {
162 vtxPreviousSPIPWM
= vtxSPIPWM
;
163 #if defined(PLATFORM_ESP32_S3) || defined(PLATFORM_ESP32_C3)
164 PWM
.setDuty(rfAmpPwmChannel
, vtxSPIPWM
* 1000 / 4096);
165 #elif defined(PLATFORM_ESP32)
166 if (GPIO_PIN_RF_AMP_PWM
== 25 || GPIO_PIN_RF_AMP_PWM
== 26)
168 dacWrite(GPIO_PIN_RF_AMP_PWM
, vtxSPIPWM
>> 4);
172 PWM
.setDuty(rfAmpPwmChannel
, vtxSPIPWM
* 1000 / 4096);
175 analogWrite(GPIO_PIN_RF_AMP_PWM
, vtxSPIPWM
);
179 void VTxOutputMinimum()
183 vtxSPIPWM
= vtxMaxPWM
;
187 static void VTxOutputIncrease()
189 if (vtxSPIPWM
> vtxMinPWM
) vtxSPIPWM
-= 1;
193 static void VTxOutputDecrease()
195 if (vtxSPIPWM
< vtxMaxPWM
) vtxSPIPWM
+= 1;
199 static uint16_t LinearInterpVpdSetPointArray(const uint16_t VpdSetPointArray
[])
203 if (vtxSPIFrequencyCurrent
<= VpdFreqArray
[0])
205 newVpd
= VpdSetPointArray
[0];
207 else if (vtxSPIFrequencyCurrent
>= VpdFreqArray
[VpdSetPointCount
- 1])
209 newVpd
= VpdSetPointArray
[VpdSetPointCount
- 1];
213 for (uint8_t i
= 0; i
< (VpdSetPointCount
- 1); i
++)
215 if (vtxSPIFrequencyCurrent
< VpdFreqArray
[i
+ 1])
217 newVpd
= VpdSetPointArray
[i
] + ((VpdSetPointArray
[i
+ 1]-VpdSetPointArray
[i
])/(VpdFreqArray
[i
+ 1]-VpdFreqArray
[i
])) * (vtxSPIFrequencyCurrent
- VpdFreqArray
[i
]);
225 static uint16_t LinearInterpSetPwm(const uint16_t PwmArray
[])
229 if (vtxSPIFrequencyCurrent
<= VpdFreqArray
[0])
231 newPwm
= PwmArray
[0];
233 else if (vtxSPIFrequencyCurrent
>= VpdFreqArray
[VpdSetPointCount
- 1])
235 newPwm
= PwmArray
[VpdSetPointCount
- 1];
239 for (uint8_t i
= 0; i
< (VpdSetPointCount
- 1); i
++)
241 if (vtxSPIFrequencyCurrent
< VpdFreqArray
[i
+ 1])
243 newPwm
= PwmArray
[i
] + ((PwmArray
[i
+ 1]-PwmArray
[i
])/(VpdFreqArray
[i
+ 1]-VpdFreqArray
[i
])) * (vtxSPIFrequencyCurrent
- VpdFreqArray
[i
]);
251 static void SetVpdSetPoint()
253 switch (vtxSPIPowerIdx
)
256 VpdSetPoint
= VPD_SETPOINT_0_MW
;
257 vtxSPIPWM
= vtxMaxPWM
;
262 VpdSetPoint
= LinearInterpVpdSetPointArray(VpdSetPointArray25mW
);
263 vtxSPIPWM
= LinearInterpSetPwm(PwmArray25mW
);
267 VpdSetPoint
= LinearInterpVpdSetPointArray(VpdSetPointArray100mW
);
268 vtxSPIPWM
= LinearInterpSetPwm(PwmArray100mW
);
272 VpdSetPoint
= VPD_SETPOINT_YOLO_MW
;
273 vtxSPIPWM
= vtxMinPWM
;
278 DBGLN("VTX: Setting new VPD setpoint: %d, initial PWM: %d", VpdSetPoint
, vtxSPIPWM
);
281 static void checkOutputPower()
283 if (vtxSPIPitmodeCurrent
)
291 uint16_t VpdReading
= analogRead(GPIO_PIN_RF_AMP_VPD
); // WARNING - Max input 1.0V !!!!
293 Vpd
= (8 * Vpd
+ 2 * VpdReading
) / 10; // IIR filter
295 if (Vpd
< (VpdSetPoint
- VPD_BUFFER
))
299 else if (Vpd
> (VpdSetPoint
+ VPD_BUFFER
))
304 //DBGLN("VTX: VPD setpoint=%d, raw=%d, filtered=%d, PWM=%d", VpdSetPoint, VpdReading, Vpd, vtxSPIPWM);
308 #if defined(VTX_OUTPUT_CALIBRATION)
310 int calibFreqIndex
= 0;
311 #define CALIB_SAMPLES 10
313 static int gatherOutputCalibrationData()
315 if (VpdSetPoint
<= VPD_SETPOINT_YOLO_MW
&& calibFreqIndex
< VpdSetPointCount
)
319 DBGLN("VTX Freq=%d, VPD setpoint=%d, VPD=%d, PWM=%d, sample=%d", VpdFreqArray
[calibFreqIndex
], VpdSetPoint
, Vpd
, vtxSPIPWM
, sampleCount
);
320 if (sampleCount
>= CALIB_SAMPLES
)
322 VpdSetPoint
+= VPD_BUFFER
;
326 if (VpdSetPoint
> VPD_SETPOINT_YOLO_MW
)
329 rtc6705SetFrequency(VpdFreqArray
[calibFreqIndex
]);
330 VpdSetPoint
= VPD_BUFFER
;
331 return RTC6705_PLL_SETTLE_TIME_MS
;
333 return VTX_POWER_INTERVAL_MS
;
335 return DURATION_NEVER
;
341 stopVtxMonitoring
= true;
346 static void initialize()
348 VpdSetPointArray25mW
= VPD_VALUES_25MW
;
349 VpdSetPointArray100mW
= VPD_VALUES_100MW
;
350 PwmArray25mW
= PWM_VALUES_25MW
;
351 PwmArray100mW
= PWM_VALUES_100MW
;
353 if (GPIO_PIN_SPI_VTX_NSS
!= UNDEF_PIN
)
355 if (GPIO_PIN_SPI_VTX_SCK
!= UNDEF_PIN
&& GPIO_PIN_SPI_VTX_SCK
!= GPIO_PIN_SCK
)
357 vtxSPI
= new SPIClass(HSPI
);
358 vtxSPI
->begin(GPIO_PIN_SPI_VTX_SCK
, GPIO_PIN_SPI_VTX_MISO
, GPIO_PIN_SPI_VTX_MOSI
, GPIO_PIN_SPI_VTX_NSS
);
359 vtxSPI
->setHwCs(true);
360 vtxSPI
->setBitOrder(LSBFIRST
);
365 pinMode(GPIO_PIN_SPI_VTX_NSS
, OUTPUT
);
366 digitalWrite(GPIO_PIN_SPI_VTX_NSS
, HIGH
);
369 pinMode(GPIO_PIN_RF_AMP_VREF
, OUTPUT
);
370 digitalWrite(GPIO_PIN_RF_AMP_VREF
, LOW
);
372 #if defined(PLATFORM_ESP32_S3)
373 rfAmpPwmChannel
= PWM
.allocate(GPIO_PIN_RF_AMP_PWM
, 10000);
374 #elif defined(PLATFORM_ESP32)
375 // If using a DAC pin then adjust min/max and initial value
376 if (GPIO_PIN_RF_AMP_PWM
== 25 || GPIO_PIN_RF_AMP_PWM
== 26)
380 vtxSPIPWM
= vtxMaxPWM
;
384 rfAmpPwmChannel
= PWM
.allocate(GPIO_PIN_RF_AMP_PWM
, 10000);
387 pinMode(GPIO_PIN_RF_AMP_PWM
, OUTPUT
);
388 analogWriteFreq(10000); // 10kHz
389 analogWriteResolution(12); // 0 - 4095
397 if (GPIO_PIN_SPI_VTX_NSS
== UNDEF_PIN
)
399 return DURATION_NEVER
;
402 #if defined(VTX_OUTPUT_CALIBRATION)
403 rtc6705SetFrequency(VpdFreqArray
[calibFreqIndex
]); // Set to the first calib frequency
404 vtxSPIPitmodeCurrent
= 0;
405 VpdSetPoint
= VPD_SETPOINT_0_MW
;
407 return RTC6705_PLL_SETTLE_TIME_MS
;
410 return RTC6705_BOOT_DELAY
;
415 if ((GPIO_PIN_SPI_VTX_NSS
== UNDEF_PIN
) || stopVtxMonitoring
)
417 return DURATION_NEVER
;
420 if (hwTimer::running
&& !hwTimer::isTick
)
422 // Dont run spi and analog reads during rx hopping, wifi or updating
423 return DURATION_IMMEDIATELY
;
426 #if defined(VTX_OUTPUT_CALIBRATION)
427 return gatherOutputCalibrationData();
430 if (vtxSPIFrequencyCurrent
!= vtxSPIFrequency
)
432 rtc6705SetFrequency(vtxSPIFrequency
);
433 vtxSPIFrequencyCurrent
= vtxSPIFrequency
;
434 vtxPowerAmpEnable
= true;
436 DBGLN("VTX: Set frequency: %d", vtxSPIFrequency
);
438 return RTC6705_PLL_SETTLE_TIME_MS
;
441 // Note: it's important that the PA is handled after the frequency.
442 if (vtxPowerAmpEnableCurrent
!= vtxPowerAmpEnable
)
444 DBGLN("VTX: Changing internal PA, old: %d, new: %d", vtxPowerAmpEnableCurrent
, vtxPowerAmpEnable
);
445 if (vtxPowerAmpEnable
)
449 vtxPowerAmpEnableCurrent
= vtxPowerAmpEnable
;
451 return VTX_POWER_INTERVAL_MS
;
454 if (vtxSPIPowerIdxCurrent
!= vtxSPIPowerIdx
)
456 DBGLN("VTX: Set power: %d", vtxSPIPowerIdx
);
458 vtxSPIPowerIdxCurrent
= vtxSPIPowerIdx
;
461 if (vtxSPIPitmodeCurrent
!= vtxSPIPitmode
)
463 DBGLN("VTX: Set PIT mode: %d", vtxSPIPitmode
);
464 vtxSPIPitmodeCurrent
= vtxSPIPitmode
;
469 return VTX_POWER_INTERVAL_MS
;
472 device_t VTxSPI_device
= {
473 .initialize
= initialize
,