Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions boards/heltec-rc52.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"build": {
"arduino": {
"ldscript": "nrf52840_s140_v6.ld"
},
"core": "nRF5",
"cpu": "cortex-m4",
"extra_flags": "-DNRF52840_XXAA",
"f_cpu": "64000000L",
"hwids": [
["0x239A", "0x8071"],
["0x239A", "0x0071"],
["0x239A", "0x8072"]
],
"usb_product": "HT-n5262",
"mcu": "nrf52840",
"variant": "heltec_rc52",
"bsp": {
"name": "adafruit"
},
"softdevice": {
"sd_flags": "-DS140",
"sd_name": "s140",
"sd_version": "6.1.1",
"sd_fwid": "0x00B6"
},
"bootloader": {
"settings_addr": "0xFF000"
}
},
"connectivity": [
"bluetooth",
"lora"
],
"debug": {
"jlink_device": "nRF52840_xxAA",
"svd_path": "nrf52840.svd",
"openocd_target": "nrf52.cfg"
},
"frameworks": [
"arduino"
],
"name": "Heltec RC52",
"upload": {
"maximum_ram_size": 237568,
"maximum_size": 815104,
"speed": 115200,
"protocol": "nrfutil",
"protocols": [
"jlink",
"nrfjprog",
"nrfutil",
"stlink"
],
"use_1200bps_touch": true,
"require_upload_port": true,
"wait_for_upload_port": true
},
"url": "https://heltec.org/",
"vendor": "Heltec"
}
6 changes: 6 additions & 0 deletions src/helpers/ui/NV3001BDisplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,13 @@ bool NV3001BDisplay::begin() {
digitalWrite(PIN_TFT_DC, HIGH);
delay(20);

#if defined(NRF52_PLATFORM)
spi.begin();
#elif defined(ESP_PLATFORM) || defined(ESP32_PLATFORM)
spi.begin(PIN_TFT_SCL, PIN_TFT_MISO, PIN_TFT_SDA, PIN_TFT_CS);
#else
spi.begin();
#endif
if (PIN_TFT_RST >= 0) {
pinMode(PIN_TFT_RST, OUTPUT);
digitalWrite(PIN_TFT_RST, HIGH);
Expand Down
20 changes: 16 additions & 4 deletions src/helpers/ui/NV3001BDisplay.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
#define NV3001B_PANEL_HEIGHT 220
#endif

class NV3001BDisplay : public DisplayDriver {
#if defined(ESP_PLATFORM) || defined(ESP32_PLATFORM)
#ifndef NV3001B_SPI_HOST
#define NV3001B_SPI_HOST HSPI
#endif

class NV3001BDisplay : public DisplayDriver {
SPIClass spi;
SPIClass owned_spi;
#endif
SPIClass& spi;
RefCountedDigitalPin* periph_power;
bool is_on = false;
uint16_t color = 0xffff;
Expand All @@ -43,8 +45,18 @@ class NV3001BDisplay : public DisplayDriver {
void drawChar(int x, int y, char ch);

public:
#if defined(ESP_PLATFORM) || defined(ESP32_PLATFORM)
NV3001BDisplay(RefCountedDigitalPin* power = nullptr) :
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT), spi(NV3001B_SPI_HOST), periph_power(power) { }
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT),
owned_spi(NV3001B_SPI_HOST), spi(owned_spi), periph_power(power) { }

NV3001BDisplay(SPIClass* displaySpi, RefCountedDigitalPin* power = nullptr) :
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT),
owned_spi(NV3001B_SPI_HOST), spi(displaySpi ? *displaySpi : owned_spi), periph_power(power) { }
#else
NV3001BDisplay(SPIClass* displaySpi = &SPI, RefCountedDigitalPin* power = nullptr) :
DisplayDriver(NV3001B_LOGICAL_WIDTH, NV3001B_LOGICAL_HEIGHT), spi(displaySpi ? *displaySpi : SPI), periph_power(power) { }
#endif

bool begin();
static const char* driverName() { return "NV3001B"; }
Expand Down
95 changes: 95 additions & 0 deletions variants/heltec_rc52/HeltecRC52Board.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "HeltecRC52Board.h"

#include <Arduino.h>
#include <Wire.h>

extern void variant_shutdown();

#ifdef NRF52_POWER_MANAGEMENT
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK};

void HeltecRC52Board::initiateShutdown(uint8_t reason)
{
shutdownPeripherals();

bool enable_lpcomp = (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
reason == SHUTDOWN_REASON_BOOT_PROTECT);
pinMode(PIN_ADC_CTRL, OUTPUT);
digitalWrite(PIN_ADC_CTRL, enable_lpcomp ? ADC_CTRL_ENABLED : !ADC_CTRL_ENABLED);

if (enable_lpcomp) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
}

enterSystemOff(reason);
}
#endif

void HeltecRC52Board::begin()
{
NRF52BoardDCDC::begin();

pinMode(PIN_VBAT_READ, INPUT);
pinMode(PIN_ADC_CTRL, OUTPUT);
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED);

#if defined(PIN_BOARD_SDA) && defined(PIN_BOARD_SCL)
Wire.setPins(PIN_BOARD_SDA, PIN_BOARD_SCL);
#endif
Wire.begin();

pinMode(SENSOR_RST_PIN, OUTPUT);
digitalWrite(SENSOR_RST_PIN, HIGH);
pinMode(SENSOR_INT, INPUT);


#ifdef NRF52_POWER_MANAGEMENT
checkBootVoltage(&power_config);
#endif
pinMode(P_LORA_TX_LED, OUTPUT);
digitalWrite(P_LORA_TX_LED, LOW); // off by default, active high

periph_power.begin();
periph_power.claim();
}

void HeltecRC52Board::shutdownPeripherals()
{
NRF52Board::shutdownPeripherals();
variant_shutdown();
}

void HeltecRC52Board::onBeforeTransmit() {
digitalWrite(P_LORA_TX_LED, HIGH);
}

void HeltecRC52Board::onAfterTransmit() {
digitalWrite(P_LORA_TX_LED, LOW);
}

uint16_t HeltecRC52Board::getBattMilliVolts()
{
analogReadResolution(12);
analogReference(AR_INTERNAL_3_0);
pinMode(PIN_VBAT_READ, INPUT);
pinMode(PIN_ADC_CTRL, OUTPUT);
digitalWrite(PIN_ADC_CTRL, ADC_CTRL_ENABLED);

delay(10);
uint32_t raw = 0;
for (int i = 0; i < 8; i++) {
raw += analogRead(PIN_VBAT_READ);
}
raw /= 8;
digitalWrite(PIN_ADC_CTRL, !ADC_CTRL_ENABLED);

return (uint16_t)((float)raw * MV_LSB * ADC_MULTIPLIER);
}

const char* HeltecRC52Board::getManufacturerName() const
{
return "Heltec RC52";
}
25 changes: 25 additions & 0 deletions variants/heltec_rc52/HeltecRC52Board.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#include <Arduino.h>
#include <MeshCore.h>
#include <helpers/NRF52Board.h>
#include <helpers/RefCountedDigitalPin.h>

class HeltecRC52Board : public NRF52BoardDCDC {
protected:
#ifdef NRF52_POWER_MANAGEMENT
void initiateShutdown(uint8_t reason) override;
#endif

public:
RefCountedDigitalPin periph_power;

HeltecRC52Board() : NRF52Board("RC52_OTA"), periph_power(SENSOR_POWER_CTRL_PIN, SENSOR_POWER_ON) {}

void begin() override;
void onBeforeTransmit() override;
void onAfterTransmit() override;
void shutdownPeripherals() override;
uint16_t getBattMilliVolts() override;
const char* getManufacturerName() const override;
};
128 changes: 128 additions & 0 deletions variants/heltec_rc52/HeltecRC52RotaryInput.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#include "HeltecRC52RotaryInput.h"

#include <Arduino.h>
#include <Wire.h>

namespace {
constexpr uint8_t TCA6408_ADDR = 0x20;
constexpr uint8_t TCA6408_INPUT_REG = 0x00;
constexpr uint8_t TCA6408_POLARITY_REG = 0x02;
constexpr uint8_t TCA6408_CONFIG_REG = 0x03;
constexpr uint8_t TCA6408_ROTARY_A_MASK = 0x01;
constexpr uint8_t TCA6408_ROTARY_B_MASK = 0x02;
constexpr uint8_t TCA6408_ROTARY_MASK = TCA6408_ROTARY_A_MASK | TCA6408_ROTARY_B_MASK;
constexpr uint32_t TCA6408_DEBOUNCE_MS = 5;
}

bool HeltecRC52RotaryInput::begin()
{
initialized = true;
ready = false;
input_state = TCA6408_ROTARY_MASK;
active_low_phase = false;

if (periph_power && !power_claimed) {
periph_power->claim();
power_claimed = true;
delay(12);
}

if (!writeRegister(TCA6408_POLARITY_REG, 0x00) || !writeRegister(TCA6408_CONFIG_REG, 0xFF)) {
return false;
}

uint8_t state = 0;
if (!readInput(state)) {
return false;
}

input_state = state & TCA6408_ROTARY_MASK;
ready = true;
return true;
}

RotaryInputEvent HeltecRC52RotaryInput::poll()
{
if (!initialized) {
begin();
return RotaryInputEvent::None;
}

if (!ready) {
return RotaryInputEvent::None;
}

uint8_t new_state = 0;
if (!readInput(new_state)) {
return RotaryInputEvent::None;
}

new_state &= TCA6408_ROTARY_MASK;
RotaryInputEvent event = handleTransition(new_state);
input_state = new_state;
return event;
}

bool HeltecRC52RotaryInput::writeRegister(uint8_t reg, uint8_t value)
{
Wire.beginTransmission(TCA6408_ADDR);
Wire.write(reg);
Wire.write(value);
return Wire.endTransmission() == 0;
}

bool HeltecRC52RotaryInput::readInput(uint8_t& value)
{
Wire.beginTransmission(TCA6408_ADDR);
Wire.write(TCA6408_INPUT_REG);
if (Wire.endTransmission(false) != 0) {
return false;
}
if (Wire.requestFrom(TCA6408_ADDR, static_cast<uint8_t>(1)) != 1) {
return false;
}

value = Wire.read();
return true;
}

RotaryInputEvent HeltecRC52RotaryInput::handleTransition(uint8_t newState)
{
uint8_t changed = (input_state ^ newState) & TCA6408_ROTARY_MASK;
RotaryInputEvent event = RotaryInputEvent::None;
bool a_low = (newState & TCA6408_ROTARY_A_MASK) == 0;
bool b_low = (newState & TCA6408_ROTARY_B_MASK) == 0;

if (!a_low && !b_low) {
active_low_phase = false;
}

if (!active_low_phase && (changed & TCA6408_ROTARY_A_MASK) && a_low && !b_low) {
event = RotaryInputEvent::Prev;
active_low_phase = true;
} else if (!active_low_phase && (changed & TCA6408_ROTARY_B_MASK) && b_low && !a_low) {
event = RotaryInputEvent::Next;
active_low_phase = true;
}

if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_A_MASK)) {
bool a_rising = (newState & TCA6408_ROTARY_A_MASK) != 0;
if (a_rising && b_low) {
event = RotaryInputEvent::Prev;
}
}

if (event == RotaryInputEvent::None && !active_low_phase && (changed & TCA6408_ROTARY_B_MASK)) {
bool b_rising = (newState & TCA6408_ROTARY_B_MASK) != 0;
if (b_rising && a_low) {
event = RotaryInputEvent::Next;
}
}

if (event == RotaryInputEvent::None || (millis() - last_event_ms) < TCA6408_DEBOUNCE_MS) {
return RotaryInputEvent::None;
}

last_event_ms = millis();
return event;
}
26 changes: 26 additions & 0 deletions variants/heltec_rc52/HeltecRC52RotaryInput.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include <helpers/RefCountedDigitalPin.h>
#include <helpers/ui/RotaryInput.h>

class HeltecRC52RotaryInput : public RotaryInput {
public:
explicit HeltecRC52RotaryInput(RefCountedDigitalPin* periphPower = nullptr) : periph_power(periphPower) { }

bool begin() override;
RotaryInputEvent poll() override;
bool isReady() const override { return ready; }

private:
bool writeRegister(uint8_t reg, uint8_t value);
bool readInput(uint8_t& value);
RotaryInputEvent handleTransition(uint8_t newState);

uint8_t input_state = 0x03;
uint32_t last_event_ms = 0;
bool ready = false;
bool initialized = false;
bool power_claimed = false;
bool active_low_phase = false;
RefCountedDigitalPin* periph_power = nullptr;
};
Loading