Сохранено: страница POWER (батарея/ADC/reboot), настройка радио с разрядами частоты, темы и кириллица на дисплеях, меню уведомлений (тихие часы, режимы каналов), стартовый экран MESHINFO, свои варианты плат. Адаптировано под новый API 1.18.0: Listener (onMessageRecv/onChannelMessageRecv/ onQueueSizeChanged) вместо msgRead/newMsg, фильтрация уведомлений переехала в UITask, в ChannelDetails добавлен индекс канала. Отклонения от upstream: убран пин toolchain-gccarmnoneeabi@^1.140201.0 (такой версии нет в реестре PlatformIO, сборка nRF52 висела) и не добавлен NRF52_POWER_MANAGEMENT (upstream отключил его как ломающий не-Li-Ion батареи).
196 lines
5.8 KiB
C++
196 lines
5.8 KiB
C++
#include "T096Board.h"
|
|
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
|
|
#ifdef NRF52_POWER_MANAGEMENT
|
|
// Static configuration for power management
|
|
// Values come from variant.h defines
|
|
const PowerMgtConfig power_config = {
|
|
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
|
|
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
|
|
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
|
|
};
|
|
|
|
void T096Board::initiateShutdown(uint8_t reason) {
|
|
#if ENV_INCLUDE_GPS == 1
|
|
pinMode(PIN_GPS_EN, OUTPUT);
|
|
digitalWrite(PIN_GPS_EN, !PIN_GPS_EN_ACTIVE);
|
|
#endif
|
|
variant_shutdown();
|
|
|
|
bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
|
|
reason == SHUTDOWN_REASON_BOOT_PROTECT);
|
|
pinMode(PIN_BAT_CTL, OUTPUT);
|
|
digitalWrite(PIN_BAT_CTL, enable_lpcomp ? HIGH : LOW);
|
|
|
|
if (enable_lpcomp) {
|
|
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
|
|
}
|
|
|
|
enterSystemOff(reason);
|
|
}
|
|
#endif // NRF52_POWER_MANAGEMENT
|
|
|
|
void T096Board::begin() {
|
|
NRF52Board::begin();
|
|
|
|
#ifdef NRF52_POWER_MANAGEMENT
|
|
// Boot voltage protection check (may not return if voltage too low)
|
|
checkBootVoltage(&power_config);
|
|
#endif
|
|
|
|
#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
|
|
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
|
|
#endif
|
|
|
|
Wire.begin();
|
|
|
|
pinMode(P_LORA_TX_LED, OUTPUT);
|
|
digitalWrite(P_LORA_TX_LED, LOW);
|
|
|
|
periph_power.begin();
|
|
loRaFEMControl.init();
|
|
delay(1);
|
|
|
|
// ADC батареи настраиваем один раз: analogReference()/analogReadResolution() сбрасывают
|
|
// ADC, и первое чтение после них отдаёт мусор — из-за этого напряжение на экране скакало
|
|
analogReadResolution(12);
|
|
analogReference(AR_INTERNAL_3_0);
|
|
pinMode(PIN_VBAT_READ, INPUT);
|
|
pinMode(PIN_BAT_CTL, OUTPUT);
|
|
digitalWrite(PIN_BAT_CTL, 0); // делитель обесточен, пока не измеряем
|
|
}
|
|
|
|
void T096Board::onBeforeTransmit() {
|
|
digitalWrite(P_LORA_TX_LED, HIGH); // turn TX LED on
|
|
loRaFEMControl.setTxModeEnable();
|
|
}
|
|
|
|
void T096Board::onAfterTransmit() {
|
|
digitalWrite(P_LORA_TX_LED, LOW); //turn TX LED off
|
|
loRaFEMControl.setRxModeEnable();
|
|
}
|
|
|
|
uint16_t T096Board::getBattMilliVolts() {
|
|
digitalWrite(PIN_BAT_CTL, 1);
|
|
delay(10); // делителю нужно устояться после включения
|
|
|
|
// одного отсчёта мало: шум ADC давал скачки в последнем разряде
|
|
uint32_t sum = 0;
|
|
const int N = 8;
|
|
for (int i = 0; i < N; i++) {
|
|
sum += analogRead(PIN_VBAT_READ);
|
|
}
|
|
digitalWrite(PIN_BAT_CTL, 0);
|
|
|
|
return (uint16_t)((float)(sum / N) * MV_LSB * adc_mult);
|
|
}
|
|
|
|
// Коррекция АЦП батареи (страница POWER в ui-new): полный множитель делителя
|
|
bool T096Board::setAdcMultiplier(float multiplier) {
|
|
if (multiplier <= 0.0f) return false;
|
|
adc_mult = multiplier;
|
|
return true;
|
|
}
|
|
void T096Board::variant_shutdown() {
|
|
nrf_gpio_cfg_default(PIN_VEXT_EN);
|
|
nrf_gpio_cfg_default(PIN_TFT_CS);
|
|
nrf_gpio_cfg_default(PIN_TFT_DC);
|
|
nrf_gpio_cfg_default(PIN_TFT_SDA);
|
|
nrf_gpio_cfg_default(PIN_TFT_SCL);
|
|
nrf_gpio_cfg_default(PIN_TFT_RST);
|
|
nrf_gpio_cfg_default(PIN_TFT_LEDA_CTL);
|
|
|
|
nrf_gpio_cfg_default(PIN_LED);
|
|
|
|
nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CSD);
|
|
nrf_gpio_cfg_default(P_LORA_KCT8103L_PA_CTX);
|
|
pinMode(P_LORA_PA_POWER, OUTPUT);
|
|
digitalWrite(P_LORA_PA_POWER, LOW);
|
|
|
|
digitalWrite(PIN_BAT_CTL, LOW);
|
|
nrf_gpio_cfg_default(LORA_CS);
|
|
nrf_gpio_cfg_default(SX126X_DIO1);
|
|
nrf_gpio_cfg_default(SX126X_BUSY);
|
|
nrf_gpio_cfg_default(SX126X_RESET);
|
|
|
|
nrf_gpio_cfg_default(PIN_SPI_MISO);
|
|
nrf_gpio_cfg_default(PIN_SPI_MOSI);
|
|
nrf_gpio_cfg_default(PIN_SPI_SCK);
|
|
|
|
// nrf_gpio_cfg_default(PIN_GPS_PPS);
|
|
nrf_gpio_cfg_default(PIN_GPS_RESET);
|
|
nrf_gpio_cfg_default(PIN_GPS_EN);
|
|
nrf_gpio_cfg_default(PIN_GPS_RX);
|
|
nrf_gpio_cfg_default(PIN_GPS_TX);
|
|
}
|
|
|
|
void T096Board::powerOff() {
|
|
loRaFEMControl.setSleepModeEnable();
|
|
nrf_gpio_cfg_default(PIN_GPS_EN); // 363uA down to 39uA
|
|
NRF52Board::powerOff();
|
|
}
|
|
|
|
const char* T096Board::getManufacturerName() const {
|
|
return "Heltec T096";
|
|
}
|
|
|
|
bool T096Board::setLoRaFemLnaEnabled(bool enable) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
return false;
|
|
}
|
|
|
|
loRaFEMControl.setLNAEnable(enable);
|
|
loRaFEMControl.setRxModeEnable();
|
|
return true;
|
|
}
|
|
|
|
bool T096Board::isLoRaFemLnaEnabled() const {
|
|
return loRaFEMControl.isLNAEnabled();
|
|
}
|
|
|
|
void T096Board::attachDynamicPrefs(KeyValueStore* prefs) {
|
|
_prefs = prefs;
|
|
|
|
char radio_fem_rxgain[8] = { 0 };
|
|
_prefs->getByKey("fem_rxgain", radio_fem_rxgain, 7); // get initial values
|
|
|
|
setLoRaFemLnaEnabled(strcmp(radio_fem_rxgain, "1") == 0);
|
|
}
|
|
|
|
bool T096Board::handleCommand(const char* command, uint32_t sender_timestamp, char* reply) {
|
|
if (strcmp(command, "get radio.fem.rxgain") == 0) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
strcpy(reply, "Error: unsupported");
|
|
} else {
|
|
sprintf(reply, "> %s", isLoRaFemLnaEnabled() ? "on" : "off");
|
|
}
|
|
return true;
|
|
}
|
|
if (memcmp(command, "set radio.fem.rxgain ", 21) == 0) {
|
|
if (!loRaFEMControl.isLnaCanControl()) {
|
|
strcpy(reply, "Error: unsupported");
|
|
} else if (memcmp(&command[21], "on", 2) == 0) {
|
|
if (setLoRaFemLnaEnabled(true)) {
|
|
_prefs->setByKey("fem_rxgain", "1");
|
|
strcpy(reply, "OK - LoRa FEM RX gain on");
|
|
} else {
|
|
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
|
|
}
|
|
} else if (memcmp(&command[21], "off", 3) == 0) {
|
|
if (setLoRaFemLnaEnabled(false)) {
|
|
_prefs->setByKey("fem_rxgain", "0");
|
|
strcpy(reply, "OK - LoRa FEM RX gain off");
|
|
} else {
|
|
strcpy(reply, "Error: failed to apply LoRa FEM RX gain");
|
|
}
|
|
} else {
|
|
strcpy(reply, "Error: state must be on or off");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
return false; // not handled
|
|
}
|