2 #include "CRSFHandset.h"
7 #if defined(CRSF_TX_MODULE) && !defined(UNIT_TEST)
10 #if defined(PLATFORM_ESP32)
12 #include <soc/uart_reg.h>
13 // UART0 is used since for DupleTX we can connect directly through IO_MUX and not the Matrix
14 // for better performance, and on other targets (mostly using pin 13), it always uses Matrix
15 HardwareSerial
CRSFHandset::Port(0);
16 RTC_DATA_ATTR
int rtcModelId
= 0;
17 #elif defined(PLATFORM_ESP8266)
18 HardwareSerial
CRSFHandset::Port(0);
19 #elif defined(PLATFORM_STM32)
20 HardwareSerial
CRSFHandset::Port(GPIO_PIN_RCSIGNAL_RX
, GPIO_PIN_RCSIGNAL_TX
);
21 #if defined(STM32F3) || defined(STM32F3xx)
22 #include "stm32f3xx_hal.h"
23 #include "stm32f3xx_hal_gpio.h"
24 #elif defined(STM32F1) || defined(STM32F1xx)
25 #include "stm32f1xx_hal.h"
26 #include "stm32f1xx_hal_gpio.h"
28 #elif defined(TARGET_NATIVE)
29 HardwareSerial
CRSFHandset::Port
= Serial
;
32 static constexpr int HANDSET_TELEMETRY_FIFO_SIZE
= 128; // this is the smallest telemetry FIFO size in ETX with CRSF defined
34 /// Out FIFO to buffer messages///
35 static constexpr auto CRSF_SERIAL_OUT_FIFO_SIZE
= 256U;
36 static FIFO
<CRSF_SERIAL_OUT_FIFO_SIZE
> SerialOutFIFO
;
38 Stream
*CRSFHandset::PortSecondary
;
41 uint32_t CRSFHandset::GoodPktsCountResult
= 0;
42 uint32_t CRSFHandset::BadPktsCountResult
= 0;
44 uint8_t CRSFHandset::modelId
= 0;
45 bool CRSFHandset::ForwardDevicePings
= false;
46 bool CRSFHandset::elrsLUAmode
= false;
48 /// OpenTX mixer sync ///
49 static const int32_t OpenTXsyncPacketInterval
= 200; // in ms
50 static const int32_t OpenTXsyncOffsetSafeMargin
= 1000; // 100us
53 static const int32_t TxToHandsetBauds
[] = {400000, 115200, 5250000, 3750000, 1870000, 921600, 2250000};
54 uint8_t CRSFHandset::UARTcurrentBaudIdx
= 6; // only used for baud-cycling, initialized to the end so the next one we try is the first in the list
55 uint32_t CRSFHandset::UARTrequestedBaud
= 5250000;
57 // for the UART wdt, every 1000ms we change bauds when connect is lost
58 static const int UARTwdtInterval
= 1000;
60 void CRSFHandset::Begin()
62 DBGLN("About to start CRSF task...");
64 UARTwdtLastChecked
= millis() + UARTwdtInterval
; // allows a delay before the first time the UARTwdt() function is called
66 #if defined(PLATFORM_ESP32)
67 portDISABLE_INTERRUPTS();
68 if (GPIO_PIN_RCSIGNAL_RX
!= GPIO_PIN_RCSIGNAL_TX
)
70 UARTinverted
= false; // on a full UART we will start uninverted checking first
72 CRSFHandset::Port
.begin(UARTrequestedBaud
, SERIAL_8N1
,
73 GPIO_PIN_RCSIGNAL_RX
, GPIO_PIN_RCSIGNAL_TX
,
75 CRSFHandset::duplex_set_RX();
76 portENABLE_INTERRUPTS();
78 if (esp_reset_reason() != ESP_RST_POWERON
)
81 if (RecvModelUpdate
) RecvModelUpdate();
83 #elif defined(PLATFORM_ESP8266)
84 // Uses default UART pins
85 CRSFHandset::Port
.begin(UARTrequestedBaud
);
86 // Invert RX/TX (not done, connection is full duplex uninverted)
87 //USC0(UART0) |= BIT(UCRXI) | BIT(UCTXI);
88 // No log message because this is our only UART
90 #elif defined(PLATFORM_STM32)
91 DBGLN("Start STM32 R9M TX CRSF UART");
93 CRSFHandset::Port
.setTx(GPIO_PIN_RCSIGNAL_TX
);
94 CRSFHandset::Port
.setRx(GPIO_PIN_RCSIGNAL_RX
);
96 #if defined(GPIO_PIN_BUFFER_OE) && (GPIO_PIN_BUFFER_OE != UNDEF_PIN)
97 pinMode(GPIO_PIN_BUFFER_OE
, OUTPUT
);
98 digitalWrite(GPIO_PIN_BUFFER_OE
, LOW
^ GPIO_PIN_BUFFER_OE_INVERTED
); // RX mode default
99 #elif (GPIO_PIN_RCSIGNAL_TX == GPIO_PIN_RCSIGNAL_RX)
100 CRSFHandset::Port
.setHalfDuplex();
103 CRSFHandset::Port
.begin(UARTrequestedBaud
);
105 #if defined(TARGET_TX_GHOST)
106 USART1
->CR1
&= ~USART_CR1_UE
;
107 USART1
->CR3
|= USART_CR3_HDSEL
;
108 USART1
->CR2
|= USART_CR2_RXINV
| USART_CR2_TXINV
| USART_CR2_SWAP
; //inverted/swapped
109 USART1
->CR1
|= USART_CR1_UE
;
110 #elif defined(TARGET_TX_FM30_MINI)
111 LL_GPIO_SetPinPull(GPIOA
, GPIO_PIN_2
, LL_GPIO_PULL_DOWN
); // default is PULLUP
112 USART2
->CR1
&= ~USART_CR1_UE
;
113 USART2
->CR2
|= USART_CR2_RXINV
| USART_CR2_TXINV
; //inverted
114 USART2
->CR1
|= USART_CR1_UE
;
116 DBGLN("STM32 CRSF UART LISTEN TASK STARTED");
117 CRSFHandset::Port
.flush();
122 void CRSFHandset::End()
124 uint32_t startTime
= millis();
125 while (SerialOutFIFO
.peek() > 0)
128 if (millis() - startTime
> 1000)
133 //CRSFHandset::Port.end(); // don't call serial.end(), it causes some sort of issue with the 900mhz hardware using gpio2 for serial
134 DBGLN("CRSF UART END");
137 void CRSFHandset::flush_port_input()
139 // Make sure there is no garbage on the UART at the start
140 while (CRSFHandset::Port
.available())
142 CRSFHandset::Port
.read();
146 void CRSFHandset::makeLinkStatisticsPacket(uint8_t buffer
[LinkStatisticsFrameLength
+ 4])
148 buffer
[0] = CRSF_ADDRESS_RADIO_TRANSMITTER
;
149 buffer
[1] = LinkStatisticsFrameLength
+ 2;
150 buffer
[2] = CRSF_FRAMETYPE_LINK_STATISTICS
;
151 for (uint8_t i
= 0; i
< LinkStatisticsFrameLength
; i
++)
153 buffer
[i
+ 3] = ((uint8_t *)&CRSF::LinkStatistics
)[i
];
155 uint8_t crc
= crsf_crc
.calc(buffer
[2]);
156 buffer
[LinkStatisticsFrameLength
+ 3] = crsf_crc
.calc((byte
*)&CRSF::LinkStatistics
, LinkStatisticsFrameLength
, crc
);
160 * Build a an extended type packet and queue it in the SerialOutFIFO
161 * This is just a regular packet with 2 extra bytes with the sub src and target
163 void CRSFHandset::packetQueueExtended(uint8_t type
, void *data
, uint8_t len
)
167 CRSF_ADDRESS_RADIO_TRANSMITTER
,
170 CRSF_ADDRESS_RADIO_TRANSMITTER
,
171 CRSF_ADDRESS_CRSF_TRANSMITTER
174 // CRC - Starts at type, ends before CRC
175 uint8_t crc
= crsf_crc
.calc(&buf
[3], sizeof(buf
)-3);
176 crc
= crsf_crc
.calc((byte
*)data
, len
, crc
);
178 SerialOutFIFO
.lock();
179 if (SerialOutFIFO
.ensure(buf
[0] + 1))
181 SerialOutFIFO
.pushBytes(buf
, sizeof(buf
));
182 SerialOutFIFO
.pushBytes((byte
*)data
, len
);
183 SerialOutFIFO
.push(crc
);
185 SerialOutFIFO
.unlock();
188 void CRSFHandset::sendTelemetryToTX(uint8_t *data
)
190 if (controllerConnected
)
192 uint8_t size
= CRSF_FRAME_SIZE(data
[CRSF_TELEMETRY_LENGTH_INDEX
]);
193 if (size
> CRSF_MAX_PACKET_LEN
)
199 data
[0] = CRSF_ADDRESS_RADIO_TRANSMITTER
;
200 SerialOutFIFO
.lock();
201 if (SerialOutFIFO
.ensure(size
+ 1))
203 SerialOutFIFO
.push(size
); // length
204 SerialOutFIFO
.pushBytes(data
, size
);
206 SerialOutFIFO
.unlock();
210 void ICACHE_RAM_ATTR
CRSFHandset::setPacketInterval(int32_t PacketInterval
)
212 RequestedRCpacketInterval
= PacketInterval
;
213 OpenTXsyncOffset
= 0;
214 OpenTXsyncWindow
= 0;
215 OpenTXsyncWindowSize
= std::max((int32_t)1, (int32_t)(20000/RequestedRCpacketInterval
));
216 OpenTXsyncLastSent
-= OpenTXsyncPacketInterval
;
217 adjustMaxPacketSize();
220 void ICACHE_RAM_ATTR
CRSFHandset::JustSentRFpacket()
222 // read them in this order to prevent a potential race condition
223 uint32_t last
= dataLastRecv
;
224 uint32_t m
= micros();
225 auto delta
= (int32_t)(m
- last
);
227 if (delta
>= (int32_t)RequestedRCpacketInterval
)
229 // missing/late packet, force resync
230 OpenTXsyncOffset
= -(delta
% RequestedRCpacketInterval
) * 10;
231 OpenTXsyncWindow
= 0;
232 OpenTXsyncLastSent
-= OpenTXsyncPacketInterval
;
233 #ifdef DEBUG_OPENTX_SYNC
234 DBGLN("Missed packet, forced resync (%d)!", delta
);
239 // The number of packets in the sync window is how many will fit in 20ms.
240 // This gives quite quite coarse changes for 50Hz, but more fine grained changes at 1000Hz.
241 OpenTXsyncWindow
= std::min(OpenTXsyncWindow
+ 1, (int32_t)OpenTXsyncWindowSize
);
242 OpenTXsyncOffset
= ((OpenTXsyncOffset
* (OpenTXsyncWindow
-1)) + delta
* 10) / OpenTXsyncWindow
;
246 void CRSFHandset::sendSyncPacketToTX() // in values in us.
248 uint32_t now
= millis();
249 if (controllerConnected
&& (now
- OpenTXsyncLastSent
) >= OpenTXsyncPacketInterval
)
251 int32_t packetRate
= RequestedRCpacketInterval
* 10; //convert from us to right format
252 int32_t offset
= OpenTXsyncOffset
- OpenTXsyncOffsetSafeMargin
; // offset so that opentx always has some headroom
253 #ifdef DEBUG_OPENTX_SYNC
254 DBGLN("Offset %d", offset
); // in 10ths of us (OpenTX sync unit)
258 uint8_t extendedType
; // CRSF_FRAMETYPE_OPENTX_SYNC
259 uint32_t rate
; // Big-Endian
260 uint32_t offset
; // Big-Endian
263 uint8_t buffer
[sizeof(otxSyncData
)];
264 auto * const sync
= (struct otxSyncData
* const)buffer
;
266 sync
->extendedType
= CRSF_FRAMETYPE_OPENTX_SYNC
;
267 sync
->rate
= htobe32(packetRate
);
268 sync
->offset
= htobe32(offset
);
270 packetQueueExtended(CRSF_FRAMETYPE_RADIO_ID
, buffer
, sizeof(buffer
));
272 OpenTXsyncLastSent
= now
;
276 void CRSFHandset::RcPacketToChannelsData() // data is packed as 11 bits per channel
278 // for monitoring arming state
279 uint32_t prev_AUX1
= ChannelData
[4];
281 auto payload
= (uint8_t const * const)&inBuffer
.asRCPacket_t
.channels
;
282 constexpr unsigned srcBits
= 11;
283 constexpr unsigned dstBits
= 11;
284 constexpr unsigned inputChannelMask
= (1 << srcBits
) - 1;
285 constexpr unsigned precisionShift
= dstBits
- srcBits
;
287 // code from BetaFlight rx/crsf.cpp / bitpacker_unpack
288 uint8_t bitsMerged
= 0;
289 uint32_t readValue
= 0;
290 unsigned readByteIndex
= 0;
291 for (uint32_t & n
: ChannelData
)
293 while (bitsMerged
< srcBits
)
295 uint8_t readByte
= payload
[readByteIndex
++];
296 readValue
|= ((uint32_t) readByte
) << bitsMerged
;
299 //printf("rv=%x(%x) bm=%u\n", readValue, (readValue & inputChannelMask), bitsMerged);
300 n
= (readValue
& inputChannelMask
) << precisionShift
;
301 readValue
>>= srcBits
;
302 bitsMerged
-= srcBits
;
305 if (prev_AUX1
!= ChannelData
[4])
307 #if defined(PLATFORM_ESP32)
308 devicesTriggerEvent();
313 bool CRSFHandset::processInternalCrsfPackage(uint8_t *package
)
315 const crsf_ext_header_t
*header
= (crsf_ext_header_t
*)package
;
316 const crsf_frame_type_e packetType
= (crsf_frame_type_e
)header
->type
;
318 // Enter Binding Mode
319 if (packetType
== CRSF_FRAMETYPE_COMMAND
320 && header
->frame_size
>= 6 // official CRSF is 7 bytes with two CRCs
321 && header
->dest_addr
== CRSF_ADDRESS_CRSF_TRANSMITTER
322 && header
->orig_addr
== CRSF_ADDRESS_RADIO_TRANSMITTER
323 && header
->payload
[0] == CRSF_COMMAND_SUBCMD_RX
324 && header
->payload
[1] == CRSF_COMMAND_SUBCMD_RX_BIND
)
326 if (OnBindingCommand
) OnBindingCommand();
330 if (packetType
>= CRSF_FRAMETYPE_DEVICE_PING
&&
331 (header
->dest_addr
== CRSF_ADDRESS_CRSF_TRANSMITTER
|| header
->dest_addr
== CRSF_ADDRESS_BROADCAST
) &&
332 (header
->orig_addr
== CRSF_ADDRESS_RADIO_TRANSMITTER
|| header
->orig_addr
== CRSF_ADDRESS_ELRS_LUA
))
334 elrsLUAmode
= header
->orig_addr
== CRSF_ADDRESS_ELRS_LUA
;
336 if (packetType
== CRSF_FRAMETYPE_COMMAND
&& header
->payload
[0] == CRSF_COMMAND_SUBCMD_RX
&& header
->payload
[1] == CRSF_COMMAND_MODEL_SELECT_ID
)
338 modelId
= header
->payload
[2];
339 #if defined(PLATFORM_ESP32)
340 rtcModelId
= modelId
;
342 if (RecvModelUpdate
) RecvModelUpdate();
346 if (RecvParameterUpdate
) RecvParameterUpdate(packetType
, header
->payload
[0], header
->payload
[1]);
355 bool CRSFHandset::ProcessPacket()
357 bool packetReceived
= false;
359 CRSFHandset::dataLastRecv
= micros();
361 if (!controllerConnected
)
363 controllerConnected
= true;
364 DBGLN("CRSF UART Connected");
365 if (connected
) connected();
368 const uint8_t packetType
= inBuffer
.asRCPacket_t
.header
.type
;
369 uint8_t *SerialInBuffer
= inBuffer
.asUint8_t
;
371 if (packetType
== CRSF_FRAMETYPE_RC_CHANNELS_PACKED
)
373 RCdataLastRecv
= micros();
374 RcPacketToChannelsData();
375 packetReceived
= true;
377 // check for all extended frames that are a broadcast or a message to the FC
378 else if (packetType
>= CRSF_FRAMETYPE_DEVICE_PING
&&
379 (SerialInBuffer
[3] == CRSF_ADDRESS_FLIGHT_CONTROLLER
|| SerialInBuffer
[3] == CRSF_ADDRESS_BROADCAST
|| SerialInBuffer
[3] == CRSF_ADDRESS_CRSF_RECEIVER
))
381 // Some types trigger telemburst to attempt a connection even with telm off
382 // but for pings (which are sent when the user loads Lua) do not forward
384 if (ForwardDevicePings
|| packetType
!= CRSF_FRAMETYPE_DEVICE_PING
)
386 const uint8_t length
= inBuffer
.asRCPacket_t
.header
.frame_size
+ 2;
387 CRSF::AddMspMessage(length
, SerialInBuffer
);
389 packetReceived
= true;
392 packetReceived
|= processInternalCrsfPackage(SerialInBuffer
);
394 return packetReceived
;
397 void CRSFHandset::handleInput()
399 uint8_t *SerialInBuffer
= inBuffer
.asUint8_t
;
406 while (CRSFHandset::Port
.available())
408 if (!CRSFframeActive
)
410 unsigned char const inChar
= CRSFHandset::Port
.read();
411 // stage 1 wait for sync byte //
412 if (inChar
== CRSF_ADDRESS_CRSF_TRANSMITTER
||
413 inChar
== CRSF_SYNC_BYTE
)
415 // we got sync, reset write pointer
416 SerialInPacketPtr
= 0;
417 SerialInPacketLen
= 0;
418 CRSFframeActive
= true;
419 SerialInBuffer
[SerialInPacketPtr
] = inChar
;
423 else // frame is active so we do the processing
425 // first if things have gone wrong //
426 if (SerialInPacketPtr
> CRSF_MAX_PACKET_LEN
- 1)
428 // we reached the maximum allowable packet length, so start again because shit fucked up hey.
429 SerialInPacketPtr
= 0;
430 SerialInPacketLen
= 0;
431 CRSFframeActive
= false;
435 // special case where we save the expected pkt len to buffer //
436 if (SerialInPacketPtr
== 1)
438 unsigned char const inChar
= CRSFHandset::Port
.read();
439 if (inChar
<= CRSF_MAX_PACKET_LEN
)
441 SerialInPacketLen
= inChar
;
442 SerialInBuffer
[SerialInPacketPtr
] = inChar
;
447 SerialInPacketPtr
= 0;
448 SerialInPacketLen
= 0;
449 CRSFframeActive
= false;
454 int toRead
= (SerialInPacketLen
+ 2) - SerialInPacketPtr
;
455 #if defined(PLATFORM_ESP32)
456 auto count
= (int)CRSFHandset::Port
.read(&SerialInBuffer
[SerialInPacketPtr
], toRead
);
459 auto avail
= (int)CRSFHandset::Port
.available();
460 while (count
< toRead
&& count
< avail
)
462 SerialInBuffer
[SerialInPacketPtr
+ count
] = CRSFHandset::Port
.read();
466 SerialInPacketPtr
+= count
;
468 if (SerialInPacketPtr
>= (SerialInPacketLen
+ 2)) // plus 2 because the packlen is referenced from the start of the 'type' flag, IE there are an extra 2 bytes.
470 uint8_t CalculatedCRC
= crsf_crc
.calc(SerialInBuffer
+ 2, SerialInPacketPtr
- 3);
472 if (CalculatedCRC
== SerialInBuffer
[SerialInPacketPtr
-1])
477 //delayMicroseconds(50);
479 if (RCdataCallback
) RCdataCallback();
484 DBGLN("UART CRC failure");
485 // cleanup input buffer
489 CRSFframeActive
= false;
490 SerialInPacketPtr
= 0;
491 SerialInPacketLen
= 0;
497 void CRSFHandset::handleOutput()
499 static uint8_t CRSFoutBuffer
[CRSF_MAX_PACKET_LEN
] = {0};
500 // both static to split up larger packages
501 static uint8_t packageLengthRemaining
= 0;
502 static uint8_t sendingOffset
= 0;
504 if (!controllerConnected
)
506 SerialOutFIFO
.lock();
507 SerialOutFIFO
.flush();
508 SerialOutFIFO
.unlock();
512 if (packageLengthRemaining
== 0 && SerialOutFIFO
.size() == 0)
514 sendSyncPacketToTX(); // calculate mixer sync packet if needed
517 // if partial package remaining, or data in the output FIFO that needs to be written
518 if (packageLengthRemaining
> 0 || SerialOutFIFO
.size() > 0) {
521 uint8_t periodBytesRemaining
= maxPeriodBytes
;
522 while (periodBytesRemaining
)
524 SerialOutFIFO
.lock();
525 // no package is in transit so get new data from the fifo
526 if (packageLengthRemaining
== 0) {
527 packageLengthRemaining
= SerialOutFIFO
.pop();
528 SerialOutFIFO
.popBytes(CRSFoutBuffer
, packageLengthRemaining
);
531 SerialOutFIFO
.unlock();
533 // if the package is long we need to split it up so it fits in the sending interval
535 if (packageLengthRemaining
> periodBytesRemaining
) {
536 if (periodBytesRemaining
< maxPeriodBytes
) { // only start to send a split packet as the first packet
539 writeLength
= periodBytesRemaining
;
541 writeLength
= packageLengthRemaining
;
544 // write the packet out, if it's a large package the offset holds the starting position
545 CRSFHandset::Port
.write(CRSFoutBuffer
+ sendingOffset
, writeLength
);
546 if (CRSFHandset::PortSecondary
)
547 CRSFHandset::PortSecondary
->write(CRSFoutBuffer
+ sendingOffset
, writeLength
);
549 sendingOffset
+= writeLength
;
550 packageLengthRemaining
-= writeLength
;
551 periodBytesRemaining
-= writeLength
;
553 // No bytes left to send, exit
554 if (SerialOutFIFO
.size() == 0)
557 CRSFHandset::Port
.flush();
560 // make sure there is no garbage on the UART left over
565 void CRSFHandset::duplex_set_RX() const
567 #if defined(PLATFORM_ESP32)
568 if (GPIO_PIN_RCSIGNAL_TX
== GPIO_PIN_RCSIGNAL_RX
)
570 ESP_ERROR_CHECK(gpio_set_direction((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
, GPIO_MODE_INPUT
));
573 gpio_matrix_in((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
, U0RXD_IN_IDX
, true);
574 gpio_pulldown_en((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
);
575 gpio_pullup_dis((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
);
579 gpio_matrix_in((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
, U0RXD_IN_IDX
, false);
580 gpio_pullup_en((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
);
581 gpio_pulldown_dis((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
);
584 #elif defined(PLATFORM_ESP8266)
585 // Enable loopback on UART0 to connect the RX pin to the TX pin (not done, connection is full duplex uninverted)
586 //USC0(UART0) |= BIT(UCLBE);
587 #elif defined(GPIO_PIN_BUFFER_OE) && (GPIO_PIN_BUFFER_OE != UNDEF_PIN)
588 digitalWrite(GPIO_PIN_BUFFER_OE
, LOW
^ GPIO_PIN_BUFFER_OE_INVERTED
);
589 #elif (GPIO_PIN_RCSIGNAL_TX == GPIO_PIN_RCSIGNAL_RX)
590 CRSFHandset::Port
.enableHalfDuplexRx();
594 void CRSFHandset::duplex_set_TX() const
596 #if defined(PLATFORM_ESP32)
597 if (GPIO_PIN_RCSIGNAL_TX
== GPIO_PIN_RCSIGNAL_RX
)
599 ESP_ERROR_CHECK(gpio_set_pull_mode((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, GPIO_FLOATING
));
600 ESP_ERROR_CHECK(gpio_set_pull_mode((gpio_num_t
)GPIO_PIN_RCSIGNAL_RX
, GPIO_FLOATING
));
603 ESP_ERROR_CHECK(gpio_set_level((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, 0));
604 ESP_ERROR_CHECK(gpio_set_direction((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, GPIO_MODE_OUTPUT
));
605 constexpr uint8_t MATRIX_DETACH_IN_LOW
= 0x30; // routes 0 to matrix slot
606 gpio_matrix_in(MATRIX_DETACH_IN_LOW
, U0RXD_IN_IDX
, false); // Disconnect RX from all pads
607 gpio_matrix_out((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, U0TXD_OUT_IDX
, true, false);
611 ESP_ERROR_CHECK(gpio_set_level((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, 1));
612 ESP_ERROR_CHECK(gpio_set_direction((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, GPIO_MODE_OUTPUT
));
613 constexpr uint8_t MATRIX_DETACH_IN_HIGH
= 0x38; // routes 1 to matrix slot
614 gpio_matrix_in(MATRIX_DETACH_IN_HIGH
, U0RXD_IN_IDX
, false); // Disconnect RX from all pads
615 gpio_matrix_out((gpio_num_t
)GPIO_PIN_RCSIGNAL_TX
, U0TXD_OUT_IDX
, false, false);
618 #elif defined(PLATFORM_ESP8266)
619 // Disable loopback to disconnect the RX pin from the TX pin (not done, connection is full duplex uninverted)
620 //USC0(UART0) &= ~BIT(UCLBE);
621 #elif defined(GPIO_PIN_BUFFER_OE) && (GPIO_PIN_BUFFER_OE != UNDEF_PIN)
622 digitalWrite(GPIO_PIN_BUFFER_OE
, HIGH
^ GPIO_PIN_BUFFER_OE_INVERTED
);
623 #elif (GPIO_PIN_RCSIGNAL_TX == GPIO_PIN_RCSIGNAL_RX)
624 // writing to the port switches the mode
628 void ICACHE_RAM_ATTR
CRSFHandset::adjustMaxPacketSize()
630 // baud / 10bits-per-byte / 2 windows (1RX, 1TX) / rate * 0.80 (leeway)
631 maxPeriodBytes
= UARTrequestedBaud
/ 10 / 2 / (1000000/RequestedRCpacketInterval
) * 80 / 100;
632 maxPeriodBytes
= maxPeriodBytes
> HANDSET_TELEMETRY_FIFO_SIZE
? HANDSET_TELEMETRY_FIFO_SIZE
: maxPeriodBytes
;
633 // we need a minimum of 10 bytes otherwise our LUA will not make progress and at 8 we'd get a divide by 0!
634 maxPeriodBytes
= maxPeriodBytes
< 10 ? 10 : maxPeriodBytes
;
635 maxPacketBytes
= maxPeriodBytes
> CRSF_MAX_PACKET_LEN
? CRSF_MAX_PACKET_LEN
: maxPeriodBytes
;
636 DBGLN("Adjusted max packet size %u-%u", maxPacketBytes
, maxPeriodBytes
);
639 #if defined(PLATFORM_ESP32_S3)
640 uint32_t CRSFHandset::autobaud()
642 static enum { INIT
, MEASURED
, INVERTED
} state
;
644 if (state
== MEASURED
)
646 UARTinverted
= !UARTinverted
;
648 return UARTrequestedBaud
;
650 if (state
== INVERTED
)
652 UARTinverted
= !UARTinverted
;
656 if (REG_GET_BIT(UART_CONF0_REG(0), UART_AUTOBAUD_EN
) == 0)
658 REG_WRITE(UART_RX_FILT_REG(0), (4 << UART_GLITCH_FILT_S
) | UART_GLITCH_FILT_EN
); // enable, glitch filter 4
659 REG_WRITE(UART_LOWPULSE_REG(0), 4095); // reset register to max value
660 REG_WRITE(UART_HIGHPULSE_REG(0), 4095); // reset register to max value
661 REG_SET_BIT(UART_CONF0_REG(0), UART_AUTOBAUD_EN
); // enable autobaud
664 if (REG_GET_BIT(UART_CONF0_REG(0), UART_AUTOBAUD_EN
) && REG_READ(UART_RXD_CNT_REG(0)) < 300)
671 const uint32_t low_period
= REG_READ(UART_LOWPULSE_REG(0));
672 const uint32_t high_period
= REG_READ(UART_HIGHPULSE_REG(0));
673 REG_CLR_BIT(UART_CONF0_REG(0), UART_AUTOBAUD_EN
); // disable autobaud
674 REG_CLR_BIT(UART_RX_FILT_REG(0), UART_GLITCH_FILT_EN
); // disable glitch filtering
676 DBGLN("autobaud: low %d, high %d", low_period
, high_period
);
677 // According to the tecnnical reference
678 const int32_t calulatedBaud
= UART_CLK_FREQ
/ (low_period
+ high_period
+ 2);
679 int32_t bestBaud
= TxToHandsetBauds
[0];
680 for(int i
=0 ; i
<ARRAY_SIZE(TxToHandsetBauds
) ; i
++)
682 if (abs(calulatedBaud
- bestBaud
) > abs(calulatedBaud
- TxToHandsetBauds
[i
]))
684 bestBaud
= TxToHandsetBauds
[i
];
689 #elif defined(PLATFORM_ESP32)
690 uint32_t CRSFHandset::autobaud()
692 static enum { INIT
, MEASURED
, INVERTED
} state
;
694 if (state
== MEASURED
) {
695 UARTinverted
= !UARTinverted
;
697 return UARTrequestedBaud
;
699 if (state
== INVERTED
) {
700 UARTinverted
= !UARTinverted
;
704 if (REG_GET_BIT(UART_AUTOBAUD_REG(0), UART_AUTOBAUD_EN
) == 0) {
705 REG_WRITE(UART_AUTOBAUD_REG(0), 4 << UART_GLITCH_FILT_S
| UART_AUTOBAUD_EN
); // enable, glitch filter 4
708 if (REG_GET_BIT(UART_AUTOBAUD_REG(0), UART_AUTOBAUD_EN
) && REG_READ(UART_RXD_CNT_REG(0)) < 300)
715 auto low_period
= (int32_t)REG_READ(UART_LOWPULSE_REG(0));
716 auto high_period
= (int32_t)REG_READ(UART_HIGHPULSE_REG(0));
717 REG_CLR_BIT(UART_AUTOBAUD_REG(0), UART_AUTOBAUD_EN
); // disable autobaud
719 DBGLN("autobaud: low %d, high %d", low_period
, high_period
);
720 // sample code at https://github.com/espressif/esp-idf/issues/3336
721 // says baud rate = 80000000/min(UART_LOWPULSE_REG, UART_HIGHPULSE_REG);
722 // Based on testing use max and add 2 for lowest deviation
723 int32_t calculatedBaud
= 80000000 / (max(low_period
, high_period
) + 3);
724 auto bestBaud
= TxToHandsetBauds
[0];
725 for(int TxToHandsetBaud
: TxToHandsetBauds
)
727 if (abs(calculatedBaud
- bestBaud
) > abs(calculatedBaud
- (int32_t)TxToHandsetBaud
))
729 bestBaud
= (int32_t)TxToHandsetBaud
;
735 uint32_t CRSFHandset::autobaud() {
736 UARTcurrentBaudIdx
= (UARTcurrentBaudIdx
+ 1) % ARRAY_SIZE(TxToHandsetBauds
);
737 return TxToHandsetBauds
[UARTcurrentBaudIdx
];
741 bool CRSFHandset::UARTwdt()
744 #if !defined(DEBUG_TX_FREERUN)
745 uint32_t now
= millis();
746 if (now
>= (UARTwdtLastChecked
+ UARTwdtInterval
))
748 if (BadPktsCount
>= GoodPktsCount
|| !controllerConnected
)
750 DBGLN("Too many bad UART RX packets!");
752 if (controllerConnected
)
754 DBGLN("CRSF UART Disconnected");
755 if (disconnected
) disconnected();
756 controllerConnected
= false;
759 UARTrequestedBaud
= autobaud();
760 if (UARTrequestedBaud
!= 0)
762 DBGLN("UART WDT: Switch to: %d baud", UARTrequestedBaud
);
764 adjustMaxPacketSize();
766 SerialOutFIFO
.flush();
767 #if defined(PLATFORM_ESP8266) || defined(PLATFORM_ESP32)
768 CRSFHandset::Port
.flush();
769 CRSFHandset::Port
.updateBaudRate(UARTrequestedBaud
);
770 #elif defined(TARGET_TX_GHOST)
771 CRSFHandset::Port
.begin(UARTrequestedBaud
);
772 USART1
->CR1
&= ~USART_CR1_UE
;
773 USART1
->CR3
|= USART_CR3_HDSEL
;
774 USART1
->CR2
|= USART_CR2_RXINV
| USART_CR2_TXINV
| USART_CR2_SWAP
; //inverted/swapped
775 USART1
->CR1
|= USART_CR1_UE
;
776 #elif defined(TARGET_TX_FM30_MINI)
777 CRSFHandset::Port
.begin(UARTrequestedBaud
);
778 LL_GPIO_SetPinPull(GPIOA
, GPIO_PIN_2
, LL_GPIO_PULL_DOWN
); // default is PULLUP
779 USART2
->CR1
&= ~USART_CR1_UE
;
780 USART2
->CR2
|= USART_CR2_RXINV
| USART_CR2_TXINV
; //inverted
781 USART2
->CR1
|= USART_CR1_UE
;
783 CRSFHandset::Port
.begin(UARTrequestedBaud
);
786 // cleanup input buffer
791 #ifdef DEBUG_OPENTX_SYNC
792 if (abs((int)((1000000 / (ExpressLRS_currAirRate_Modparams
->interval
* ExpressLRS_currAirRate_Modparams
->numOfSends
)) - (int)GoodPktsCount
)) > 1)
794 DBGLN("UART STATS Bad:Good = %u:%u", BadPktsCount
, GoodPktsCount
);
796 UARTwdtLastChecked
= now
;
799 // Speed up the cycling
800 UARTwdtLastChecked
-= 3 * (UARTwdtInterval
>> 2);
803 GoodPktsCountResult
= GoodPktsCount
;
804 BadPktsCountResult
= BadPktsCount
;
812 #endif // CRSF_TX_MODULE