3 Commits

View File

@@ -4,19 +4,21 @@
#include <stdlib.h>
#include <FastLED.h>
static const uint8_t GAUGE_COUNT = 4;
static const uint8_t GAUGE_COUNT = 5;
// Backlight/status LEDs and indicator LEDs use separate data strips because
// their LED chipsets are not compatible on one chain. The command protocol
// Backlight/status LEDs use an addressable strip. Indicator LEDs are
// single-colour active-high outputs on per-gauge pins. The command protocol
// still exposes one logical LED segment per gauge.
static const uint8_t LED_DATA_PIN = 22;
static const uint8_t INDICATOR_LED_DATA_PIN = 36;
static const uint8_t BREATHE_FRAME_MS = 16;
static const uint8_t LED_SHOW_MIN_INTERVAL_MS = 16;
static const uint8_t LED_SHOW_MOTION_INTERVAL_MS = 50;
static const uint8_t RX_LINE_MAX = 120;
static const uint16_t STEPPER_TIMER_HZ = 20000;
static const uint8_t STEPPER_TIMER_PRESCALER = 8;
static const uint32_t STEPPER_RATE_SCALE = 65536UL;
static const uint32_t LED_SHOW_FULL_RATE_LIMIT_Q16 = (500UL * STEPPER_RATE_SCALE) / STEPPER_TIMER_HZ;
static const uint32_t LED_SHOW_PAUSE_RATE_Q16 = (1500UL * STEPPER_RATE_SCALE) / STEPPER_TIMER_HZ;
// For now, command and debug traffic share the same serial port.
#define CMD_PORT Serial1
@@ -31,14 +33,17 @@ struct GaugePins {
bool stepActiveHigh;
bool enableActiveLow;
const char* ledOrder; // one char per LED: 'G' = GRB, 'R' = RGB; length defines ledCount
int8_t indicatorRedPin; // logical LED index 3; -1 means not fitted
int8_t indicatorGreenPin; // logical LED index 4; -1 means not fitted
};
constexpr GaugePins gaugePins[GAUGE_COUNT] = {
// dir, step, en, dirInv, stepHigh, enActiveLow, ledOrder
{48, 49, -1, false, true, true, "RRRGGRR"}, // Gauge 0
{8, 9, -1, true, true, true, "GGGRRRR"}, // Gauge 1
{52, 53, -1, false, true, true, "GGGRRRR"}, // Gauge 2
{50, 51, -1, false, true, true, "GGGRRRR"}, // Gauge 3
// dir, step, en, dirInv, stepHigh, enActiveLow, ledOrder, indRed, indGreen
{44, 45, -1, false, true, true, "RRRGGRR", 2, 3}, // Gauge 0
{46, 47, -1, false, true, true, "RRRGGRR", 4, 5}, // Gauge 1
{48, 49, -1, true, true, true, "GGGRRRR", 6, 7}, // Gauge 2
{50, 51, -1, false, true, true, "GGGRRRR", 8, 9}, // Gauge 3
{52, 53, -1, false, true, true, "GGGRRRR", 10,11 }, // Gauge 4
};
constexpr uint8_t cstrLen(const char* s) {
@@ -146,19 +151,17 @@ bool rxOverflowed = false;
CRGB logicalLeds[TOTAL_LEDS];
CRGB mainLeds[TOTAL_MAIN_LEDS];
CRGB indicatorLeds[TOTAL_INDICATOR_LEDS];
CLEDController* mainLedController = nullptr;
CLEDController* indicatorLedController = nullptr;
uint8_t gaugeLedOffset[GAUGE_COUNT];
uint8_t gaugeLedCount[GAUGE_COUNT];
uint8_t gaugeMainLedOffset[GAUGE_COUNT];
uint8_t gaugeIndicatorLedOffset[GAUGE_COUNT];
uint8_t ledPhysicalIdx[TOTAL_LEDS];
uint8_t ledGaugeIdx[TOTAL_LEDS];
uint8_t ledLocalIdx[TOTAL_LEDS];
bool ledIsIndicator[TOTAL_LEDS];
bool ledRgSwap[TOTAL_LEDS];
BlinkState blinkState[TOTAL_LEDS];
bool mainLedsDirty = false;
bool indicatorLedsDirty = false;
unsigned long lastLedShowMs = 0;
// FastLED drives the shared strip as RGB. Each gauge's ledOrder string marks per-LED
@@ -176,12 +179,32 @@ inline CRGB encodeForStrip(uint8_t globalIdx, CRGB color) {
return color;
}
inline int8_t indicatorPinFor(uint8_t gaugeIdx, uint8_t localIdx) {
if (localIdx == 3) return gaugePins[gaugeIdx].indicatorRedPin;
if (localIdx == 4) return gaugePins[gaugeIdx].indicatorGreenPin;
return -1;
}
inline bool indicatorIsOn(uint8_t localIdx, CRGB color) {
if (localIdx == 3) return color.r >= 128;
if (localIdx == 4) return color.g >= 128;
return false;
}
inline void writeIndicatorLed(uint8_t globalIdx, CRGB color) {
uint8_t gaugeIdx = ledGaugeIdx[globalIdx];
uint8_t localIdx = ledLocalIdx[globalIdx];
int8_t pin = indicatorPinFor(gaugeIdx, localIdx);
if (pin >= 0) {
digitalWrite(pin, indicatorIsOn(localIdx, color) ? HIGH : LOW);
}
}
inline void writeLed(uint8_t globalIdx, CRGB color) {
logicalLeds[globalIdx] = color;
if (ledIsIndicator[globalIdx]) {
indicatorLeds[ledPhysicalIdx[globalIdx]] = encodeForStrip(globalIdx, color);
indicatorLedsDirty = true;
writeIndicatorLed(globalIdx, color);
} else {
mainLeds[ledPhysicalIdx[globalIdx]] = encodeForStrip(globalIdx, color);
mainLedsDirty = true;
@@ -192,20 +215,39 @@ inline CRGB readLed(uint8_t globalIdx) {
return logicalLeds[globalIdx];
}
uint32_t maxStepperRateQ16() {
uint32_t maxRate = 0;
uint8_t oldSreg = SREG;
cli();
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
uint32_t rate = steppers[i].rateQ16;
if (steppers[i].enabled && steppers[i].dir != 0 && rate > maxRate) {
maxRate = rate;
}
}
SREG = oldSreg;
return maxRate;
}
void showDirtyLeds() {
if (!mainLedsDirty && !indicatorLedsDirty) return;
if (!mainLedsDirty) return;
uint32_t maxStepRate = maxStepperRateQ16();
if (maxStepRate >= LED_SHOW_PAUSE_RATE_Q16) return;
unsigned long nowMs = millis();
if (nowMs - lastLedShowMs < LED_SHOW_MIN_INTERVAL_MS) return;
uint8_t intervalMs = (maxStepRate > LED_SHOW_FULL_RATE_LIMIT_Q16)
? LED_SHOW_MOTION_INTERVAL_MS
: LED_SHOW_MIN_INTERVAL_MS;
if (nowMs - lastLedShowMs < intervalMs) return;
if (mainLedsDirty && mainLedController != nullptr) {
mainLedController->showLeds(255);
mainLedsDirty = false;
} else {
return;
}
if (indicatorLedsDirty && indicatorLedController != nullptr) {
indicatorLedController->showLeds(255);
indicatorLedsDirty = false;
}
lastLedShowMs = nowMs;
}
@@ -1159,43 +1201,48 @@ void setup() {
pinMode(gaugePins[i].enablePin, OUTPUT);
setEnable(i, true);
}
if (gaugePins[i].indicatorRedPin >= 0) {
pinMode(gaugePins[i].indicatorRedPin, OUTPUT);
digitalWrite(gaugePins[i].indicatorRedPin, LOW);
}
if (gaugePins[i].indicatorGreenPin >= 0) {
pinMode(gaugePins[i].indicatorGreenPin, OUTPUT);
digitalWrite(gaugePins[i].indicatorGreenPin, LOW);
}
initStepperRuntime(i);
setStepperLimits(i, gauges[i].minPos, gauges[i].maxPos);
gauges[i].lastUpdateMicros = micros();
}
// Flatten the per-gauge LED counts into logical offsets and separate
// physical offsets for the main and indicator strips.
// Flatten the per-gauge LED counts into logical offsets and physical
// offsets for the addressable main strip.
uint8_t ledOff = 0;
uint8_t mainLedOff = 0;
uint8_t indicatorLedOff = 0;
for (uint8_t i = 0; i < GAUGE_COUNT; i++) {
gaugeLedCount[i] = cstrLen(gaugePins[i].ledOrder);
gaugeLedOffset[i] = ledOff;
gaugeMainLedOffset[i] = mainLedOff;
gaugeIndicatorLedOffset[i] = indicatorLedOff;
for (uint8_t localIdx = 0; localIdx < gaugeLedCount[i]; localIdx++) {
uint8_t globalIdx = ledOff + localIdx;
bool indicator = isIndicatorLedIndex(localIdx);
ledGaugeIdx[globalIdx] = i;
ledLocalIdx[globalIdx] = localIdx;
ledIsIndicator[globalIdx] = indicator;
ledRgSwap[globalIdx] = gaugePins[i].ledOrder[localIdx] == 'G' ||
gaugePins[i].ledOrder[localIdx] == 'g';
ledPhysicalIdx[globalIdx] = indicator
? indicatorLedOff + (localIdx - 3)
? 0
: mainLedOff + localIdx - (localIdx > 4 ? 2 : 0);
}
ledOff += gaugeLedCount[i];
indicatorLedOff += countIndicatorLedsForGauge(i);
mainLedOff += gaugeLedCount[i] - countIndicatorLedsForGauge(i);
}
mainLedController = &FastLED.addLeds<WS2812, LED_DATA_PIN, RGB>(mainLeds, TOTAL_MAIN_LEDS);
indicatorLedController = &FastLED.addLeds<WS2812B, INDICATOR_LED_DATA_PIN, RGB>(indicatorLeds, TOTAL_INDICATOR_LEDS);
FastLED.setBrightness(255);
mainLedController->showLeds(255);
indicatorLedController->showLeds(255);
setupStepperTimer();
requestHomeAll();