Merge pull request #10780 from meshtastic/forward-port-t-impulse-plus
Forward-port: Add Lilygo T-Impulse-Plus (#10497) to develop
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"arduino": {
|
||||||
|
"ldscript": "nrf52840_s140_v6.ld"
|
||||||
|
},
|
||||||
|
"core": "nRF5",
|
||||||
|
"cpu": "cortex-m4",
|
||||||
|
"extra_flags": "-DARDUINO_NRF52840_T_IMPULSE_PLUS -DNRF52840_XXAA",
|
||||||
|
"f_cpu": "64000000L",
|
||||||
|
"hwids": [["0x239A", "0x8029"]],
|
||||||
|
"usb_product": "T-Impulse-Plus-nRF52840",
|
||||||
|
"mcu": "nrf52840",
|
||||||
|
"variant": "t-impulse-plus",
|
||||||
|
"variants_dir": "variants",
|
||||||
|
"bsp": {
|
||||||
|
"name": "adafruit"
|
||||||
|
},
|
||||||
|
"softdevice": {
|
||||||
|
"sd_flags": "-DS140",
|
||||||
|
"sd_name": "s140",
|
||||||
|
"sd_version": "6.1.1",
|
||||||
|
"sd_fwid": "0x00B6"
|
||||||
|
},
|
||||||
|
"bootloader": {
|
||||||
|
"settings_addr": "0xFF000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"connectivity": ["bluetooth"],
|
||||||
|
"debug": {
|
||||||
|
"jlink_device": "nRF52840_xxAA",
|
||||||
|
"onboard_tools": ["jlink"],
|
||||||
|
"svd_path": "nrf52840.svd"
|
||||||
|
},
|
||||||
|
"frameworks": ["arduino"],
|
||||||
|
"name": "Lilygo T-Impulse-Plus-nRF52840",
|
||||||
|
"upload": {
|
||||||
|
"maximum_ram_size": 248832,
|
||||||
|
"maximum_size": 815104,
|
||||||
|
"require_upload_port": true,
|
||||||
|
"speed": 115200,
|
||||||
|
"protocol": "nrfutil",
|
||||||
|
"protocols": [
|
||||||
|
"jlink",
|
||||||
|
"nrfjprog",
|
||||||
|
"nrfutil",
|
||||||
|
"stlink",
|
||||||
|
"cmsis-dap",
|
||||||
|
"blackmagic"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"url": "https://www.lilygo.cc/",
|
||||||
|
"vendor": "Lilygo"
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "meshUtils.h"
|
#include "meshUtils.h"
|
||||||
#include "power/PowerHAL.h"
|
#include "power/PowerHAL.h"
|
||||||
|
#include "power/SGM41562.h"
|
||||||
#include "sleep.h"
|
#include "sleep.h"
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
// #include <driver/adc.h>
|
// #include <driver/adc.h>
|
||||||
@@ -545,6 +546,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
// lastly provide a fallback to indicate external power when fully charged.
|
// lastly provide a fallback to indicate external power when fully charged.
|
||||||
virtual bool isVbusIn() override
|
virtual bool isVbusIn() override
|
||||||
{
|
{
|
||||||
|
#ifdef HAS_SGM41562
|
||||||
|
if (sgm41562 && sgm41562->refresh())
|
||||||
|
return sgm41562->isInputPowerGood();
|
||||||
|
#endif
|
||||||
#ifdef EXT_PWR_DETECT
|
#ifdef EXT_PWR_DETECT
|
||||||
return digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE;
|
return digitalRead(EXT_PWR_DETECT) == EXT_PWR_DETECT_VALUE;
|
||||||
|
|
||||||
@@ -561,6 +566,10 @@ class AnalogBatteryLevel : public HasBatteryLevel
|
|||||||
/// we can't be smart enough to say 'full'?
|
/// we can't be smart enough to say 'full'?
|
||||||
virtual bool isCharging() override
|
virtual bool isCharging() override
|
||||||
{
|
{
|
||||||
|
#ifdef HAS_SGM41562
|
||||||
|
if (sgm41562 && sgm41562->refresh())
|
||||||
|
return sgm41562->isCharging();
|
||||||
|
#endif
|
||||||
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
|
#if HAS_TELEMETRY && !MESHTASTIC_EXCLUDE_ENVIRONMENTAL_SENSOR && defined(HAS_RAKPROT) && !defined(HAS_PMU)
|
||||||
if (hasRAK()) {
|
if (hasRAK()) {
|
||||||
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
|
return (rak9154Sensor.isCharging()) ? OptTrue : OptFalse;
|
||||||
@@ -767,6 +776,12 @@ bool Power::analogInit()
|
|||||||
*/
|
*/
|
||||||
bool Power::setup()
|
bool Power::setup()
|
||||||
{
|
{
|
||||||
|
#ifdef HAS_SGM41562
|
||||||
|
// Initialize the charger early so AnalogBatteryLevel can read charging
|
||||||
|
// state from it. The charger does not provide battery voltage / percent —
|
||||||
|
// those still come from the platform ADC via analogInit() below.
|
||||||
|
initSGM41562(SGM41562_WIRE);
|
||||||
|
#endif
|
||||||
bool found = false;
|
bool found = false;
|
||||||
if (axpChipInit()) {
|
if (axpChipInit()) {
|
||||||
found = true;
|
found = true;
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
#include "HapticFeedback.h"
|
||||||
|
|
||||||
|
#ifdef HAPTIC_FEEDBACK_PIN
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifdef HAPTIC_FEEDBACK_ACTIVE_LOW
|
||||||
|
#define HAPTIC_FEEDBACK_ON_STATE LOW
|
||||||
|
#define HAPTIC_FEEDBACK_OFF_STATE HIGH
|
||||||
|
#else
|
||||||
|
#define HAPTIC_FEEDBACK_ON_STATE HIGH
|
||||||
|
#define HAPTIC_FEEDBACK_OFF_STATE LOW
|
||||||
|
#endif
|
||||||
|
|
||||||
|
HapticFeedback *hapticFeedback = nullptr;
|
||||||
|
|
||||||
|
void initHapticFeedback()
|
||||||
|
{
|
||||||
|
if (!hapticFeedback)
|
||||||
|
hapticFeedback = new HapticFeedback();
|
||||||
|
}
|
||||||
|
|
||||||
|
HapticFeedback::HapticFeedback() : concurrency::OSThread("Haptic")
|
||||||
|
{
|
||||||
|
pinMode(HAPTIC_FEEDBACK_PIN, OUTPUT);
|
||||||
|
digitalWrite(HAPTIC_FEEDBACK_PIN, HAPTIC_FEEDBACK_OFF_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HapticFeedback::motorWrite(bool on)
|
||||||
|
{
|
||||||
|
digitalWrite(HAPTIC_FEEDBACK_PIN, on ? HAPTIC_FEEDBACK_ON_STATE : HAPTIC_FEEDBACK_OFF_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HapticFeedback::pulse(uint16_t durationMs)
|
||||||
|
{
|
||||||
|
motorWrite(true);
|
||||||
|
pulseOffAt = millis() + durationMs;
|
||||||
|
if (pulseOffAt == 0) // 0 is the "no pulse" sentinel
|
||||||
|
pulseOffAt = 1;
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HapticFeedback::armDelayedPulse(uint16_t delayMs, uint16_t durationMs)
|
||||||
|
{
|
||||||
|
delayedPulseAt = millis() + delayMs;
|
||||||
|
if (delayedPulseAt == 0)
|
||||||
|
delayedPulseAt = 1;
|
||||||
|
delayedPulseDuration = durationMs;
|
||||||
|
scheduleNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HapticFeedback::cancelDelayedPulse()
|
||||||
|
{
|
||||||
|
delayedPulseAt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HapticFeedback::scheduleNext()
|
||||||
|
{
|
||||||
|
uint32_t now = millis();
|
||||||
|
uint32_t next = 0;
|
||||||
|
if (pulseOffAt != 0)
|
||||||
|
next = pulseOffAt;
|
||||||
|
if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0))
|
||||||
|
next = delayedPulseAt;
|
||||||
|
if (next == 0)
|
||||||
|
return;
|
||||||
|
int32_t delay = (int32_t)(next - now);
|
||||||
|
setIntervalFromNow(delay > 0 ? (unsigned long)delay : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t HapticFeedback::runOnce()
|
||||||
|
{
|
||||||
|
uint32_t now = millis();
|
||||||
|
|
||||||
|
if (pulseOffAt != 0 && (int32_t)(now - pulseOffAt) >= 0) {
|
||||||
|
motorWrite(false);
|
||||||
|
pulseOffAt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delayedPulseAt != 0 && (int32_t)(now - delayedPulseAt) >= 0) {
|
||||||
|
uint16_t dur = delayedPulseDuration;
|
||||||
|
delayedPulseAt = 0;
|
||||||
|
pulse(dur);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t next = 0;
|
||||||
|
if (pulseOffAt != 0)
|
||||||
|
next = pulseOffAt;
|
||||||
|
if (delayedPulseAt != 0 && (next == 0 || (int32_t)(delayedPulseAt - next) < 0))
|
||||||
|
next = delayedPulseAt;
|
||||||
|
if (next == 0)
|
||||||
|
return 60 * 1000;
|
||||||
|
int32_t delay = (int32_t)(next - now);
|
||||||
|
return delay > 0 ? delay : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAPTIC_FEEDBACK_PIN
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#ifdef HAPTIC_FEEDBACK_PIN
|
||||||
|
|
||||||
|
#include "concurrency/OSThread.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// Non-blocking pulses on a GPIO vibration motor. HAPTIC_FEEDBACK_ACTIVE_LOW inverts polarity.
|
||||||
|
class HapticFeedback : public concurrency::OSThread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HapticFeedback();
|
||||||
|
void pulse(uint16_t durationMs = 30);
|
||||||
|
void armDelayedPulse(uint16_t delayMs, uint16_t durationMs = 30);
|
||||||
|
void cancelDelayedPulse();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int32_t runOnce() override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t pulseOffAt = 0;
|
||||||
|
uint32_t delayedPulseAt = 0;
|
||||||
|
uint16_t delayedPulseDuration = 0;
|
||||||
|
|
||||||
|
void motorWrite(bool on);
|
||||||
|
// Reschedule to the soonest pending event so later arms don't clobber earlier wakes.
|
||||||
|
void scheduleNext();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern HapticFeedback *hapticFeedback;
|
||||||
|
void initHapticFeedback();
|
||||||
|
|
||||||
|
#endif // HAPTIC_FEEDBACK_PIN
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "PowerFSM.h" // needed for event trigger
|
#include "PowerFSM.h" // needed for event trigger
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "graphics/Screen.h"
|
#include "graphics/Screen.h"
|
||||||
|
#include "input/HapticFeedback.h"
|
||||||
#include "modules/ExternalNotificationModule.h"
|
#include "modules/ExternalNotificationModule.h"
|
||||||
#ifdef MESHTASTIC_LOCKDOWN
|
#ifdef MESHTASTIC_LOCKDOWN
|
||||||
#include "security/LockdownDisplay.h"
|
#include "security/LockdownDisplay.h"
|
||||||
@@ -256,6 +257,16 @@ void InputBroker::Init()
|
|||||||
}
|
}
|
||||||
touchBacklightActive = false;
|
touchBacklightActive = false;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
#if defined(HAPTIC_FEEDBACK_PIN)
|
||||||
|
// Blip on touch, second blip when long-press fires (500 ms = touchConfig.longPressTime default).
|
||||||
|
touchConfig.suppressLeadUpSound = true;
|
||||||
|
initHapticFeedback();
|
||||||
|
touchConfig.onPress = []() {
|
||||||
|
hapticFeedback->pulse(80);
|
||||||
|
hapticFeedback->armDelayedPulse(500, 80);
|
||||||
|
};
|
||||||
|
touchConfig.onRelease = []() { hapticFeedback->cancelDelayedPulse(); };
|
||||||
#endif
|
#endif
|
||||||
TouchButtonThread->initButton(touchConfig);
|
TouchButtonThread->initButton(touchConfig);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -89,6 +89,8 @@
|
|||||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_CARD
|
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_CARD
|
||||||
#elif defined(TTGO_T_ECHO_PLUS)
|
#elif defined(TTGO_T_ECHO_PLUS)
|
||||||
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_PLUS
|
#define HW_VENDOR meshtastic_HardwareModel_T_ECHO_PLUS
|
||||||
|
#elif defined(T_IMPULSE_PLUS)
|
||||||
|
#define HW_VENDOR meshtastic_HardwareModel_T_IMPULSE_PLUS
|
||||||
#elif defined(ELECROW_ThinkNode_M1)
|
#elif defined(ELECROW_ThinkNode_M1)
|
||||||
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
|
#define HW_VENDOR meshtastic_HardwareModel_THINKNODE_M1
|
||||||
#elif defined(ELECROW_ThinkNode_M3)
|
#elif defined(ELECROW_ThinkNode_M3)
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
#include "SGM41562.h"
|
||||||
|
|
||||||
|
#ifdef HAS_SGM41562
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
SGM41562 *sgm41562 = nullptr;
|
||||||
|
|
||||||
|
bool initSGM41562(TwoWire &wire)
|
||||||
|
{
|
||||||
|
if (sgm41562)
|
||||||
|
return true;
|
||||||
|
sgm41562 = new SGM41562();
|
||||||
|
if (!sgm41562->begin(wire)) {
|
||||||
|
delete sgm41562;
|
||||||
|
sgm41562 = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::readReg(uint8_t reg, uint8_t &value)
|
||||||
|
{
|
||||||
|
wire_->beginTransmission(address_);
|
||||||
|
wire_->write(reg);
|
||||||
|
if (wire_->endTransmission(false) != 0)
|
||||||
|
return false;
|
||||||
|
if (wire_->requestFrom((int)address_, 1) != 1)
|
||||||
|
return false;
|
||||||
|
value = wire_->read();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::writeReg(uint8_t reg, uint8_t value)
|
||||||
|
{
|
||||||
|
wire_->beginTransmission(address_);
|
||||||
|
wire_->write(reg);
|
||||||
|
wire_->write(value);
|
||||||
|
return wire_->endTransmission() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::updateReg(uint8_t reg, uint8_t mask, uint8_t value)
|
||||||
|
{
|
||||||
|
uint8_t cur;
|
||||||
|
if (!readReg(reg, cur))
|
||||||
|
return false;
|
||||||
|
cur = (cur & ~mask) | (value & mask);
|
||||||
|
return writeReg(reg, cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::begin(TwoWire &wire, uint8_t address)
|
||||||
|
{
|
||||||
|
wire_ = &wire;
|
||||||
|
address_ = address;
|
||||||
|
|
||||||
|
uint8_t id;
|
||||||
|
if (!readReg(REG_DEVICE_ID, id)) {
|
||||||
|
LOG_WARN("SGM41562: I2C read failed at 0x%02X", address_);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (id != DEVICE_ID_EXPECTED) {
|
||||||
|
LOG_WARN("SGM41562: unexpected device ID 0x%02X (expected 0x%02X)", id, DEVICE_ID_EXPECTED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG_INFO("SGM41562: detected at 0x%02X (id 0x%02X)", address_, id);
|
||||||
|
|
||||||
|
// Mirror the vendor reference init sequence: PCB OTP off, NTC off,
|
||||||
|
// watchdog off, charger enabled. These match LilyGo's stock firmware
|
||||||
|
// for the T-Impulse Plus.
|
||||||
|
delay(120);
|
||||||
|
writeReg(REG_SYS_VOLTAGE_REG, 0xB7);
|
||||||
|
writeReg(REG_MISC_OP_CONTROL, 0x40);
|
||||||
|
writeReg(REG_CHARGE_TERM_TIMER, 0x1A);
|
||||||
|
writeReg(REG_POWER_ON_CFG, 0xA4);
|
||||||
|
|
||||||
|
return refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::refresh()
|
||||||
|
{
|
||||||
|
uint32_t now = millis();
|
||||||
|
if (lastRefreshMs_ != 0 && (now - lastRefreshMs_) < 250)
|
||||||
|
return true; // cached
|
||||||
|
lastRefreshMs_ = now == 0 ? 1 : now;
|
||||||
|
|
||||||
|
uint8_t status, fault;
|
||||||
|
if (!readReg(REG_SYSTEM_STATUS, status))
|
||||||
|
return false;
|
||||||
|
if (!readReg(REG_FAULT, fault))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
chargeStatus_ = static_cast<ChargeStatus>((status >> SYS_STATUS_CHRG_SHIFT) & SYS_STATUS_CHRG_MASK);
|
||||||
|
inputPowerGood_ = (status & SYS_STATUS_PG) != 0;
|
||||||
|
thermalReg_ = (status & SYS_STATUS_THERM_REG) != 0;
|
||||||
|
faultMask_ = fault & 0x3F; // bits [7:6] are enter_ship_time config, not faults
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::setChargeEnable(bool enable)
|
||||||
|
{
|
||||||
|
return updateReg(REG_POWER_ON_CFG, POWER_ON_CFG_CHG_DISABLE, enable ? 0x00 : POWER_ON_CFG_CHG_DISABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SGM41562::setShippingModeEnable(bool enable)
|
||||||
|
{
|
||||||
|
return updateReg(REG_MISC_OP_CONTROL, MISC_OP_SHIPPING_MODE, enable ? MISC_OP_SHIPPING_MODE : 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // HAS_SGM41562
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
#ifdef HAS_SGM41562
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// SG Micro SGM41562 — single-cell Li-ion buck charger, I²C-controlled, no
|
||||||
|
// fuel gauge. This driver exposes status (charging / input good / fault),
|
||||||
|
// charge enable, and shipping-mode control. Battery voltage/percent still
|
||||||
|
// come from the platform ADC path; the charger is plumbed in as a
|
||||||
|
// side-channel for isCharging()/isVbusIn() in AnalogBatteryLevel.
|
||||||
|
//
|
||||||
|
// Reference: SGM41562 datasheet (Cmd map + bit fields cross-verified against
|
||||||
|
// LilyGo's `Cpp_Bus_Driver::Sgm41562xx` driver, which is what their vendor
|
||||||
|
// example for this board uses).
|
||||||
|
|
||||||
|
#ifndef SGM41562_ADDR
|
||||||
|
#define SGM41562_ADDR 0x03 // Per datasheet — unusual but correct
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SGM41562_WIRE
|
||||||
|
#define SGM41562_WIRE Wire1 // Most boards put the PMU on the secondary bus
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class SGM41562
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ChargeStatus : uint8_t {
|
||||||
|
NotCharging = 0b00,
|
||||||
|
Precharge = 0b01,
|
||||||
|
FastCharge = 0b10,
|
||||||
|
ChargeDone = 0b11,
|
||||||
|
};
|
||||||
|
|
||||||
|
bool begin(TwoWire &wire, uint8_t address = SGM41562_ADDR);
|
||||||
|
|
||||||
|
// Re-read the system status + fault registers. Throttled internally to
|
||||||
|
// at most one I²C transaction per 250 ms — call as often as you like.
|
||||||
|
bool refresh();
|
||||||
|
|
||||||
|
// Status — cached from the most recent refresh().
|
||||||
|
ChargeStatus chargeStatus() const { return chargeStatus_; }
|
||||||
|
bool isCharging() const { return chargeStatus_ == ChargeStatus::Precharge || chargeStatus_ == ChargeStatus::FastCharge; }
|
||||||
|
bool isChargeDone() const { return chargeStatus_ == ChargeStatus::ChargeDone; }
|
||||||
|
bool isInputPowerGood() const { return inputPowerGood_; }
|
||||||
|
bool isThermalRegulation() const { return thermalReg_; }
|
||||||
|
uint8_t faultMask() const { return faultMask_; }
|
||||||
|
|
||||||
|
// Control.
|
||||||
|
bool setChargeEnable(bool enable);
|
||||||
|
bool setShippingModeEnable(bool enable);
|
||||||
|
|
||||||
|
private:
|
||||||
|
TwoWire *wire_ = nullptr;
|
||||||
|
uint8_t address_ = SGM41562_ADDR;
|
||||||
|
uint32_t lastRefreshMs_ = 0;
|
||||||
|
|
||||||
|
ChargeStatus chargeStatus_ = ChargeStatus::NotCharging;
|
||||||
|
bool inputPowerGood_ = false;
|
||||||
|
bool thermalReg_ = false;
|
||||||
|
uint8_t faultMask_ = 0;
|
||||||
|
|
||||||
|
bool readReg(uint8_t reg, uint8_t &value);
|
||||||
|
bool writeReg(uint8_t reg, uint8_t value);
|
||||||
|
bool updateReg(uint8_t reg, uint8_t mask, uint8_t value);
|
||||||
|
|
||||||
|
// SGM41562 register addresses
|
||||||
|
static constexpr uint8_t REG_INPUT_SOURCE = 0x00;
|
||||||
|
static constexpr uint8_t REG_POWER_ON_CFG = 0x01;
|
||||||
|
static constexpr uint8_t REG_CHARGE_CURRENT = 0x02;
|
||||||
|
static constexpr uint8_t REG_DISCHARGE_TERM_CURRENT = 0x03;
|
||||||
|
static constexpr uint8_t REG_CHARGE_VOLTAGE = 0x04;
|
||||||
|
static constexpr uint8_t REG_CHARGE_TERM_TIMER = 0x05;
|
||||||
|
static constexpr uint8_t REG_MISC_OP_CONTROL = 0x06;
|
||||||
|
static constexpr uint8_t REG_SYS_VOLTAGE_REG = 0x07;
|
||||||
|
static constexpr uint8_t REG_SYSTEM_STATUS = 0x08;
|
||||||
|
static constexpr uint8_t REG_FAULT = 0x09;
|
||||||
|
static constexpr uint8_t REG_I2C_ADDR_MISC = 0x0A;
|
||||||
|
static constexpr uint8_t REG_DEVICE_ID = 0x0B;
|
||||||
|
|
||||||
|
// Bit positions in REG_POWER_ON_CFG.
|
||||||
|
static constexpr uint8_t POWER_ON_CFG_CHG_DISABLE = 0x08; // bit 3: 1 = charging disabled
|
||||||
|
// Bit positions in REG_MISC_OP_CONTROL.
|
||||||
|
static constexpr uint8_t MISC_OP_SHIPPING_MODE = 0x20; // bit 5: 1 = enter shipping mode
|
||||||
|
// Bit positions in REG_SYSTEM_STATUS.
|
||||||
|
static constexpr uint8_t SYS_STATUS_CHRG_SHIFT = 3;
|
||||||
|
static constexpr uint8_t SYS_STATUS_CHRG_MASK = 0x03;
|
||||||
|
static constexpr uint8_t SYS_STATUS_PG = 0x02; // bit 1: input power good
|
||||||
|
static constexpr uint8_t SYS_STATUS_THERM_REG = 0x01; // bit 0: thermal regulation
|
||||||
|
|
||||||
|
static constexpr uint8_t DEVICE_ID_EXPECTED = 0x04;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern SGM41562 *sgm41562;
|
||||||
|
|
||||||
|
// Lazy-instantiate the global on the supplied wire. Returns true on success.
|
||||||
|
bool initSGM41562(TwoWire &wire);
|
||||||
|
|
||||||
|
#endif // HAS_SGM41562
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
[env:t-impulse-plus]
|
||||||
|
custom_meshtastic_hw_model = 135
|
||||||
|
custom_meshtastic_hw_model_slug = T_IMPULSE_PLUS
|
||||||
|
custom_meshtastic_architecture = nrf52840
|
||||||
|
custom_meshtastic_actively_supported = true
|
||||||
|
custom_meshtastic_support_level = 1
|
||||||
|
custom_meshtastic_display_name = LILYGO T-Impulse Plus
|
||||||
|
custom_meshtastic_tags = LilyGo
|
||||||
|
custom_meshtastic_requires_dfu = true
|
||||||
|
|
||||||
|
extends = nrf52840_base
|
||||||
|
board = t-impulse-plus
|
||||||
|
board_level = pr
|
||||||
|
|
||||||
|
build_flags = ${nrf52840_base.build_flags}
|
||||||
|
-I variants/nrf52840/t-impulse-plus
|
||||||
|
-D T_IMPULSE_PLUS
|
||||||
|
|
||||||
|
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/t-impulse-plus>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* variant.cpp - Digital pin mapping for LilyGo T-Impulse Plus
|
||||||
|
*
|
||||||
|
* Board: T-Impulse Plus V1.0 (nRF52840)
|
||||||
|
* Hardware:
|
||||||
|
* - SSD1315 OLED
|
||||||
|
* - SX1262 (S62F)
|
||||||
|
* - MIA-M10Q GPS
|
||||||
|
* - ICM20948 IMU
|
||||||
|
* - ZD25WQ32C Flash
|
||||||
|
* - TTP223 Touch Button
|
||||||
|
* - Vibration Motor
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "variant.h"
|
||||||
|
#include "nrf.h"
|
||||||
|
#include "wiring_constants.h"
|
||||||
|
#include "wiring_digital.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
const uint32_t g_ADigitalPinMap[] = {
|
||||||
|
// D0-D6: LoRa SX1262 (S62F module) SPI + control
|
||||||
|
2, // D0 P0.02 SX1262_RST
|
||||||
|
29, // D1 P0.29 SX1262_DIO1
|
||||||
|
31, // D2 P0.31 SX1262_BUSY
|
||||||
|
46, // D3 P1.14 SX1262_CS
|
||||||
|
3, // D4 P0.03 SPI_SCK
|
||||||
|
30, // D5 P0.30 SPI_MISO
|
||||||
|
28, // D6 P0.28 SPI_MOSI
|
||||||
|
|
||||||
|
// D7-D8: RF switch control
|
||||||
|
45, // D7 P1.13 SX1262_RF_VC1 (TXEN)
|
||||||
|
39, // D8 P1.07 SX1262_RF_VC2 (RXEN)
|
||||||
|
|
||||||
|
// D9-D11: GPS (u-blox MIA-M10Q)
|
||||||
|
44, // D9 P1.12 GPS_TX (MCU TX -> GPS RX)
|
||||||
|
43, // D10 P1.11 GPS_RX (MCU RX <- GPS TX)
|
||||||
|
42, // D11 P1.10 GPS_EN
|
||||||
|
|
||||||
|
// D12-D13: Display I2C (SSD1315)
|
||||||
|
20, // D12 P0.20 SCREEN_SDA
|
||||||
|
15, // D13 P0.15 SCREEN_SCL
|
||||||
|
|
||||||
|
// D14-D15: Sensor I2C (ICM20948, SGM41562)
|
||||||
|
40, // D14 P1.08 IMU_SDA
|
||||||
|
11, // D15 P0.11 IMU_SCL
|
||||||
|
|
||||||
|
// D16-D17: Battery management
|
||||||
|
5, // D16 P0.05 BATTERY_ADC
|
||||||
|
25, // D17 P0.25 BATTERY_MEASUREMENT_CONTROL
|
||||||
|
|
||||||
|
// D18: Touch button (TTP223)
|
||||||
|
36, // D18 P1.04 TTP223_KEY
|
||||||
|
|
||||||
|
// D19: Vibration motor
|
||||||
|
22, // D19 P0.22 VIBRATION_MOTOR
|
||||||
|
|
||||||
|
// D20: LDO enable
|
||||||
|
14, // D20 P0.14 RT9080_EN
|
||||||
|
|
||||||
|
// D21-D26: Flash QSPI (ZD25WQ32C)
|
||||||
|
12, // D21 P0.12 FLASH_CS
|
||||||
|
4, // D22 P0.04 FLASH_SCLK
|
||||||
|
6, // D23 P0.06 FLASH_IO0
|
||||||
|
41, // D24 P1.09 FLASH_IO1
|
||||||
|
8, // D25 P0.08 FLASH_IO2
|
||||||
|
26, // D26 P0.26 FLASH_IO3
|
||||||
|
|
||||||
|
// D27-D28: Interrupt lines
|
||||||
|
7, // D27 P0.07 ICM20948_INT
|
||||||
|
16, // D28 P0.16 SGM41562_INT
|
||||||
|
|
||||||
|
// D29: Boot button
|
||||||
|
24, // D29 P0.24 BOOT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void initVariant()
|
||||||
|
{
|
||||||
|
// Flash CS high (deselect)
|
||||||
|
pinMode(PIN_QSPI_CS, OUTPUT);
|
||||||
|
digitalWrite(PIN_QSPI_CS, HIGH);
|
||||||
|
|
||||||
|
// Enable battery voltage measurement
|
||||||
|
pinMode(BAT_READ, OUTPUT);
|
||||||
|
digitalWrite(BAT_READ, HIGH);
|
||||||
|
|
||||||
|
// Enable RT9080 LDO
|
||||||
|
pinMode(D20, OUTPUT);
|
||||||
|
digitalWrite(D20, HIGH);
|
||||||
|
}
|
||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#ifndef _T_IMPULSE_PLUS_H_
|
||||||
|
#define _T_IMPULSE_PLUS_H_
|
||||||
|
#include "WVariant.h"
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Clock Configuration
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define VARIANT_MCK (64000000ul)
|
||||||
|
#define USE_LFXO
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Pin Capacity Definitions
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define PINS_COUNT (30u)
|
||||||
|
#define NUM_DIGITAL_PINS (30u)
|
||||||
|
#define NUM_ANALOG_INPUTS (1u)
|
||||||
|
#define NUM_ANALOG_OUTPUTS (0u)
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Digital Pin Mapping
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define D0 0 // P0.02 SX1262_RST
|
||||||
|
#define D1 1 // P0.29 SX1262_DIO1
|
||||||
|
#define D2 2 // P0.31 SX1262_BUSY
|
||||||
|
#define D3 3 // P1.14 SX1262_CS
|
||||||
|
#define D4 4 // P0.03 SPI_SCK
|
||||||
|
#define D5 5 // P0.30 SPI_MISO
|
||||||
|
#define D6 6 // P0.28 SPI_MOSI
|
||||||
|
#define D7 7 // P1.13 RF_VC1 (TXEN)
|
||||||
|
#define D8 8 // P1.07 RF_VC2 (RXEN)
|
||||||
|
#define D9 9 // P1.12 GPS module TX → MCU RX
|
||||||
|
#define D10 10 // P1.11 GPS module RX ← MCU TX
|
||||||
|
#define D11 11 // P1.10 GPS_EN (active LOW)
|
||||||
|
#define D12 12 // P0.20 SCREEN_SDA
|
||||||
|
#define D13 13 // P0.15 SCREEN_SCL
|
||||||
|
#define D14 14 // P1.08 IMU_SDA
|
||||||
|
#define D15 15 // P0.11 IMU_SCL
|
||||||
|
#define D16 16 // P0.05 BATTERY_ADC
|
||||||
|
#define D17 17 // P0.25 BATTERY_CTL
|
||||||
|
#define D18 18 // P1.04 TTP223_KEY
|
||||||
|
#define D19 19 // P0.22 VIBRATION_MOTOR
|
||||||
|
#define D20 20 // P0.14 RT9080_EN
|
||||||
|
#define D21 21 // P0.12 FLASH_CS
|
||||||
|
#define D22 22 // P0.04 FLASH_SCLK
|
||||||
|
#define D23 23 // P0.06 FLASH_IO0
|
||||||
|
#define D24 24 // P1.09 FLASH_IO1
|
||||||
|
#define D25 25 // P0.08 FLASH_IO2
|
||||||
|
#define D26 26 // P0.26 FLASH_IO3
|
||||||
|
#define D27 27 // P0.07 ICM20948_INT
|
||||||
|
#define D28 28 // P0.16 SGM41562_INT
|
||||||
|
#define D29 29 // P0.24 BOOT
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// LED Configuration (no physical LED)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define LED_STATE_ON 1
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Button Configuration (TTP223 capacitive touch)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define PIN_BUTTON_TOUCH D18
|
||||||
|
#define BUTTON_TOUCH_ACTIVE_LOW true
|
||||||
|
#define BUTTON_TOUCH_ACTIVE_PULLUP false
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Analog Pin Definitions
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define PIN_VBAT D16 // P0.05 Battery voltage sense
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// I2C Configuration
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Primary I2C: Display (SSD1315)
|
||||||
|
#define PIN_WIRE_SDA D12 // P0.20
|
||||||
|
#define PIN_WIRE_SCL D13 // P0.15
|
||||||
|
|
||||||
|
// Secondary I2C: IMU (ICM20948) + PMU (SGM41562)
|
||||||
|
#define WIRE_INTERFACES_COUNT 2
|
||||||
|
#define PIN_WIRE1_SDA D14 // P1.08
|
||||||
|
#define PIN_WIRE1_SCL D15 // P0.11
|
||||||
|
#define I2C_NO_RESCAN
|
||||||
|
|
||||||
|
static const uint8_t SDA = PIN_WIRE_SDA;
|
||||||
|
static const uint8_t SCL = PIN_WIRE_SCL;
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Display (SSD1315, compatible with SSD1306 driver)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define HAS_SCREEN 1
|
||||||
|
#define USE_SSD1306 1
|
||||||
|
#define OLED_TINY
|
||||||
|
#define OLED_GEOMETRY_OVERRIDE GEOMETRY_64_32
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// SPI Configuration (SX1262 LoRa)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define SPI_INTERFACES_COUNT 1
|
||||||
|
#define PIN_SPI_SCK D4 // P0.03
|
||||||
|
#define PIN_SPI_MISO D5 // P0.30
|
||||||
|
#define PIN_SPI_MOSI D6 // P0.28
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// SX1262 LoRa (S62F module)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define USE_SX1262
|
||||||
|
#define SX126X_CS D3
|
||||||
|
#define SX126X_DIO1 D1
|
||||||
|
#define SX126X_BUSY D2
|
||||||
|
#define SX126X_RESET D0
|
||||||
|
#define SX126X_DIO3_TCXO_VOLTAGE 1.8
|
||||||
|
#define SX126X_TXEN D7 // RF_VC1
|
||||||
|
#define SX126X_RXEN D8 // RF_VC2
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Power Management
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define BAT_READ D17 // P0.25 Battery measurement control (HIGH = enable)
|
||||||
|
#define BATTERY_PIN PIN_VBAT
|
||||||
|
#define BATTERY_SENSE_RESOLUTION_BITS 12
|
||||||
|
#define ADC_MULTIPLIER 2.0
|
||||||
|
#define AREF_VOLTAGE 3.6
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// GPS (u-blox MIA-M10Q)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define GPS_UBLOX
|
||||||
|
#define HAS_GPS 1
|
||||||
|
#define GPS_RX_PIN D9 // P1.12 — MCU RX, wired to GPS module TX
|
||||||
|
#define GPS_TX_PIN D10 // P1.11 — MCU TX, wired to GPS module RX
|
||||||
|
#define PIN_GPS_EN D11 // P1.10
|
||||||
|
#define GPS_EN_ACTIVE LOW
|
||||||
|
#define GPS_BAUDRATE 38400
|
||||||
|
#define GPS_THREAD_INTERVAL 50
|
||||||
|
#define PIN_SERIAL1_TX GPS_TX_PIN
|
||||||
|
#define PIN_SERIAL1_RX GPS_RX_PIN
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// On-board QSPI Flash (ZD25WQ32CEIGR)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define PIN_QSPI_SCK D22 // P0.04
|
||||||
|
#define PIN_QSPI_CS D21 // P0.12
|
||||||
|
#define PIN_QSPI_IO0 D23 // P0.06
|
||||||
|
#define PIN_QSPI_IO1 D24 // P1.09
|
||||||
|
#define PIN_QSPI_IO2 D25 // P0.08
|
||||||
|
#define PIN_QSPI_IO3 D26 // P0.26
|
||||||
|
|
||||||
|
#define EXTERNAL_FLASH_DEVICES W25Q32JV_IQ
|
||||||
|
#define EXTERNAL_FLASH_USE_QSPI
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Vibration Motor (GPIO active-high)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define LED_NOTIFICATION D19 // P0.22
|
||||||
|
#define HAPTIC_FEEDBACK_PIN LED_NOTIFICATION
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// IMU (ICM20948 on Wire1)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define HAS_ICM20948
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Charger (SGM41562 on Wire1 @ 0x03)
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#define HAS_SGM41562
|
||||||
|
#define SGM41562_WIRE Wire1
|
||||||
|
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
// Compatibility Definitions
|
||||||
|
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PIN_SERIAL2_RX (-1)
|
||||||
|
#define PIN_SERIAL2_TX (-1)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // _T_IMPULSE_PLUS_H_
|
||||||
Reference in New Issue
Block a user