Added Heltec V4.3 support/FEM autodetection

This commit is contained in:
2026-04-26 14:08:23 +03:00
parent 0ef379595f
commit bc60028852
5 changed files with 146 additions and 26 deletions

View File

@@ -275,6 +275,21 @@ void sx126x::setPacketParams(long preamble_symbols, uint8_t headermode, uint8_t
buf[7] = 0x00;
buf[8] = 0x00;
executeOpcode(OP_PACKET_PARAMS_6X, buf, 9);
// SX1262 errata section 15.4: IQ polarity is inverted compared to
// SX1276. The SetPacketParams command resets register 0x0736 to an
// incorrect default. For standard IQ (no inversion), bit 2 must be
// SET after every SetPacketParams call. For inverted IQ, bit 2 must
// be CLEARED. Without this fix, LoRa RX demodulation fails silently
// while TX continues to work.
uint8_t iqreg = readRegister(0x0736);
if (buf[5] == 0x00) {
// Standard IQ: set bit 2
writeRegister(0x0736, iqreg | 0x04);
} else {
// Inverted IQ: clear bit 2
writeRegister(0x0736, iqreg & ~0x04);
}
}
void sx126x::reset(void) {
@@ -287,6 +302,22 @@ void sx126x::reset(void) {
}
}
void sx126x::setDCDCRegulator(void) {
// Documentation
// 5. Power Distribution -> 5.1 Selecting DC-DC Converter or LDO Regulation
// 13.1.11 SetRegulatorMode
uint8_t mode_byte = MODE_STDBY_RC_6X;
executeOpcode(OP_STANDBY_6X, &mode_byte, 1);
// Enable DC-DC regulator for high power operation
uint8_t reg_mode = 0x01; // 0x00 = LDO, 0x01 = DC-DC
executeOpcode(OP_REGULATOR_MODE_6X, &reg_mode, 1);
delay(5);
waitOnBusy();
}
void sx126x::calibrate(void) {
// Put in STDBY_RC mode before calibration
uint8_t mode_byte = MODE_STDBY_RC_6X;
@@ -319,10 +350,19 @@ int sx126x::begin(long frequency) {
if (_rxen != -1) { pinMode(_rxen, OUTPUT); }
if (_txen != -1) { pinMode(_txen, OUTPUT); }
//TODO: if it works, make it optional
//#ifdef SX1262_USE_DCDC_REGULATOR
setDCDCRegulator();
//#endif
calibrate();
calibrate_image(frequency);
enableTCXO();
#if HAS_TCXO
enableTCXO();
//13.1.15 SetRxTxFallbackMode to STDBY_XOSC
uint8_t fallback_mode = 0x30; // STDBY_XOSC after TX/RX
executeOpcode(OP_RX_TX_FALLBACK_MODE_6X, &fallback_mode, 1);
#endif
loraMode();
standby();
@@ -339,7 +379,9 @@ int sx126x::begin(long frequency) {
setFrequency(frequency);
setTxPower(2);
enableCrc();
writeRegister(REG_LNA_6X, 0x96); // Set LNA boost
writeRegister(REG_LNA_6X, 0x96); // Set LNA boosted gain mode
// Undocumented SX1262 register patch recommended by Heltec/Semtech for improved RX sensitivity.
writeRegister(0x08B5, readRegister(0x08B5) | 0x01);
uint8_t basebuf[2] = {0}; // Set base addresses
executeOpcode(OP_BUFFER_BASE_ADDR_6X, basebuf, 2);
@@ -347,7 +389,22 @@ int sx126x::begin(long frequency) {
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
#if HAS_LORA_PA
#if LORA_PA_GC1109
if (lora_pa_model == LORA_PA_UNKNOWN) {
#if BOARD_MODEL == BOARD_HELTEC32_V4
pinMode(LORA_PA_PWR_EN, OUTPUT);
pinMode(LORA_PA_CSD, INPUT);
digitalWrite(LORA_PA_PWR_EN, HIGH); delay(5);
if (digitalRead(LORA_PA_CSD) == HIGH) {
lora_pa_model = LORA_PA_KCT8103L;
lora_lna_gain = LORA_LNA_KCT8103L_GAIN;
} else {
lora_pa_model = LORA_PA_GC1109;
}
#endif
}
if (lora_pa_model == LORA_PA_GC1109) {
// Enable Vfem_ctl for supply to
// PA power net.
pinMode(LORA_PA_PWR_EN, OUTPUT);
@@ -372,7 +429,26 @@ int sx126x::begin(long frequency) {
// is driven by the SX1262 DIO2
// pin directly, so we do not
// need to manually raise this.
#endif
} else if (lora_pa_model == LORA_PA_KCT8103L) {
// Enable Vfem_ctl for supply to
// PA power net.
pinMode(LORA_PA_PWR_EN, OUTPUT);
digitalWrite(LORA_PA_PWR_EN, HIGH);
// Enable KCT8103L chip
pinMode(LORA_PA_CSD, OUTPUT);
digitalWrite(LORA_PA_CSD, HIGH);
// Enable receive LNA
pinMode(LORA_PA_CTX, OUTPUT);
digitalWrite(LORA_PA_CTX, LOW);
// On Heltec V4.3, the PA CPS pin
// is driven by the SX1262 DIO2
// pin directly, so we do not
// need to manually raise this.
}
#endif
return 1;
@@ -382,14 +458,17 @@ void sx126x::end() { sleep(); SPI.end(); _preinit_done = false; }
int sx126x::beginPacket(int implicitHeader) {
#if HAS_LORA_PA
#if LORA_PA_GC1109
if (lora_pa_model == LORA_PA_GC1109) {
// Enable PA CPS for transmit
// digitalWrite(LORA_PA_CPS, HIGH);
// Disabled since we're keeping it
// on permanently as long as the
// radio is powered up.
#endif
} else if (lora_pa_model == LORA_PA_KCT8103L) {
digitalWrite(LORA_PA_CTX, HIGH);
}
#endif
if (_txen != -1) { digitalWrite(_txen, HIGH); } //Set TXen high when transmitting
standby();
@@ -405,6 +484,7 @@ int sx126x::beginPacket(int implicitHeader) {
int sx126x::endPacket() {
setPacketParams(_preambleLength, _implicitHeaderMode, _payloadLength, _crcMode);
if (_rxen != -1) { digitalWrite(_rxen, LOW); } //Set RXen low when transmitting
uint8_t timeout[3] = {0}; // Put in single TX mode
@@ -479,7 +559,7 @@ int ISR_VECT sx126x::currentRssi() {
executeOpcodeRead(OP_CURRENT_RSSI_6X, &byte, 1);
int rssi = -(int(byte)) / 2;
#if HAS_LORA_LNA
rssi -= LORA_LNA_GAIN;
rssi -= lora_lna_gain;
#endif
return rssi;
}
@@ -495,7 +575,7 @@ int ISR_VECT sx126x::packetRssi() {
executeOpcodeRead(OP_PACKET_STATUS_6X, buf, 3);
int pkt_rssi = -buf[0] / 2;
#if HAS_LORA_LNA
pkt_rssi -= LORA_LNA_GAIN;
pkt_rssi -= lora_lna_gain;
#endif
return pkt_rssi;
}
@@ -602,7 +682,7 @@ void sx126x::onReceive(void(*callback)(int)){
void sx126x::receive(int size) {
#if HAS_LORA_PA
#if LORA_PA_GC1109
if (lora_pa_model == LORA_PA_GC1109) {
// Disable PA CPS for receive
// digitalWrite(LORA_PA_CPS, LOW);
// That turned out to be a bad idea.
@@ -610,7 +690,9 @@ void sx126x::receive(int size) {
// on and off too quickly. We'll keep
// it on permanently, as long as the
// radio is powered up.
#endif
} else if (lora_pa_model == LORA_PA_KCT8103L) {
digitalWrite(LORA_PA_CTX, LOW);
}
#endif
if (size > 0) {