bccu.cpp 6.73 KB
/*
 * Copyright (c) 2015-2016, Arkadiusz Materek (arekmat@poczta.fm)
 *
 * Licensed under GNU General Public License 3.0 or later.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

#include "bccu.hpp"

#include "bccu_config.h"
#include "clock.hpp"

#define LINPRES_MAX 1023
#define LINPRES_MAGIC 8192

#define DIMMING_MAX 1023
#define DIMMING_MAGIC_UP 20479
#define DIMMING_MAGIC_DOWN 20734

#define MSEC_PER_SEC 1000

namespace xmc {
namespace {

BCCU_Type* const BCCU = BCCU0;

BCCU_DE_Type* const BCCU_DEs[] = { BCCU0_DE0, BCCU0_DE1, BCCU0_DE2 };

BCCU_CH_Type* const BCCU_CHs[] = {
    BCCU0_CH0,
    BCCU0_CH1,
    BCCU0_CH2,
    BCCU0_CH3,
    BCCU0_CH4,
    BCCU0_CH5,
    BCCU0_CH6,
    BCCU0_CH7,
    BCCU0_CH8, };

uint16_t calculateDimmingPrescaller(uint32_t timeMs, uint32_t magic) {
  uint32_t dclk_ps = BCCU->GLOBCLK & BCCU_GLOBCLK_DCLK_PS_Msk;
  dclk_ps >>= BCCU_GLOBCLK_DCLK_PS_Pos;

  uint32_t prescaler = ((uint32_t) (CPU_CLOCK / MSEC_PER_SEC) * timeMs / dclk_ps + magic / 2) / magic;
  if (prescaler > DIMMING_MAX) {
    return 0;
  }
  return (uint16_t) prescaler;
}

uint16_t calculateLinearPrescaller(uint32_t timeMs) {
  uint32_t fclk_ps = BCCU->GLOBCLK & BCCU_GLOBCLK_FCLK_PS_Msk;
  fclk_ps >>= BCCU_GLOBCLK_FCLK_PS_Pos;

  uint32_t prescaler = ((uint32_t) (CPU_CLOCK / MSEC_PER_SEC) * timeMs / fclk_ps + LINPRES_MAGIC / 2) / LINPRES_MAGIC;
  if (prescaler > LINPRES_MAX) {
    return 0;
  }
  return (uint16_t) prescaler;
}

bool gBccuConfigured = false;

void configureBccuGlobal() {
  if (gBccuConfigured) {
    return;
  }

  XMC_BCCU_GlobalInit(BCCU, &kBCCUGlobalConfig);
  BCCU->CHTRIG = 0;

#ifdef XMC_BCCU_CH0_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH0_PIN, XMC_BCCU_CH0_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH1_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH1_PIN, XMC_BCCU_CH1_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH2_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH2_PIN, XMC_BCCU_CH2_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH3_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH3_PIN, XMC_BCCU_CH3_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH4_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH4_PIN, XMC_BCCU_CH4_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH5_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH5_PIN, XMC_BCCU_CH5_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH6_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH6_PIN, XMC_BCCU_CH6_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH7_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH7_PIN, XMC_BCCU_CH7_PIN_MODE);
#endif
#ifdef XMC_BCCU_CH8_PIN
  XMC_GPIO_SetMode(XMC_BCCU_CH8_PIN, XMC_BCCU_CH8_PIN_MODE);
#endif

  gBccuConfigured = true;
}

void configureBccuDimmingEngine(BCCU_DE_Type* BCCU_DE) {
  configureBccuGlobal();

  XMC_BCCU_DIM_Init(BCCU_DE, &kBCCUDimmingConfig);
  XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, 0);
}

void configureBccuChannel(BCCU_CH_Type* BCCU_CH, Bccu::DimmingEngine engine,
    const XMC_BCCU_CH_CONFIG_t* channelConfig) {
  XMC_BCCU_CH_Init(BCCU_CH, channelConfig);
  XMC_BCCU_CH_SelectDimEngine(BCCU_CH, (XMC_BCCU_CH_DIMMING_SOURCE_t) engine);
}

} // namespace

Bccu::Bccu(DimmingEngine de, uint32_t engineMask, uint32_t channelsMask) :
    BCCU_DE(BCCU_DEs[de]), mEngineMask(engineMask), mChannelsMask(channelsMask), mLastFadeTime(0), mLastChangeTime(0) {
}

void Bccu::enable() {
  XMC_BCCU_ConcurrentEnableChannels(BCCU, mChannelsMask);
  for (uint16_t i = 0; i < 9; ++i) {
    if (mChannelsMask & (1 << i)) {
      BCCU_CH_Type* BCCU_CH = BCCU_CHs[i];
      XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH, 0);
      XMC_BCCU_CH_SetTargetIntensity(BCCU_CH, 0);
    }
  }
  XMC_BCCU_ConcurrentStartLinearWalk(BCCU, mChannelsMask);
  XMC_BCCU_ConcurrentEnableDimmingEngine(BCCU, mEngineMask);
  XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask);
}

void Bccu::disable() {
  XMC_BCCU_ConcurrentAbortLinearWalk(BCCU, mChannelsMask);
  XMC_BCCU_ConcurrentDisableChannels(BCCU, mChannelsMask);

  XMC_BCCU_ConcurrentAbortDimming(BCCU, mEngineMask);
  XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, 0);
  XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask);
  while (isFading()) {
  }
  XMC_BCCU_ConcurrentDisableDimmingEngine(BCCU, mEngineMask);
}

void Bccu::setLevel(uint16_t level, uint32_t fadeTime) {
  // ASSERT(fadeTime < 2^31)
  if (isFading()) {
    abortFading();
  }
  uint32_t up = getLevel() > level ? 1 : -1;
  int32_t _fadeTime = up * (int32_t) fadeTime;
  if (mLastFadeTime != _fadeTime) {
    mLastFadeTime = _fadeTime;
    uint32_t prescaler = calculateDimmingPrescaller(fadeTime, up ? DIMMING_MAGIC_UP : DIMMING_MAGIC_DOWN);
    XMC_BCCU_DIM_SetDimDivider(BCCU_DE, prescaler);
  }
  XMC_BCCU_DIM_SetTargetDimmingLevel(BCCU_DE, level);
  XMC_BCCU_ConcurrentStartDimming(BCCU, mEngineMask);
}

uint16_t Bccu::getLevel() {
  return XMC_BCCU_DIM_ReadDimmingLevel(BCCU_DE);
}

bool Bccu::isFading() {
  return (BCCU->DESTRCON & mEngineMask) != 0;
}

void Bccu::abortFading() {
  XMC_BCCU_ConcurrentAbortDimming(BCCU, mEngineMask);
}

bool Bccu::isColorChanging() {
  return (BCCU->CHSTRCON & mChannelsMask) != 0;
}

void Bccu::abortColorChanging() {
  XMC_BCCU_ConcurrentAbortLinearWalk(BCCU, mChannelsMask);
}

BccuLampRGB::BccuLampRGB(DimmingEngine de, Channel r, Channel g, Channel b, const XMC_BCCU_CH_CONFIG_t* channelConfigR,
    const XMC_BCCU_CH_CONFIG_t* channelConfigG, const XMC_BCCU_CH_CONFIG_t* channelConfigB) :
    Bccu(de, (1 << de), ((1 << r) | (1 << g) | (1 << b))), BCCU_CH_R(BCCU_CHs[r]), BCCU_CH_G(BCCU_CHs[g]),
    BCCU_CH_B(BCCU_CHs[b]) {
  configureBccuDimmingEngine(BCCU_DE);
  configureBccuChannel(BCCU_CH_R, de, channelConfigR);
  configureBccuChannel(BCCU_CH_G, de, channelConfigG);
  configureBccuChannel(BCCU_CH_B, de, channelConfigB);
}

void BccuLampRGB::setColor(uint16_t r, uint16_t g, uint16_t b, uint32_t changeTime) {
  if (isColorChanging()) {
    abortColorChanging();
  }
  if (mLastChangeTime != (int32_t) changeTime) {
    mLastChangeTime = (int32_t) changeTime;
    uint32_t prescaler = calculateLinearPrescaller(changeTime);
    XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_R, prescaler);
    XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_G, prescaler);
    XMC_BCCU_CH_SetLinearWalkPrescaler(BCCU_CH_B, prescaler);
  }

  XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_R, r);
  XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_G, g);
  XMC_BCCU_CH_SetTargetIntensity(BCCU_CH_B, b);

  XMC_BCCU_ConcurrentStartLinearWalk(BCCU, mChannelsMask);
}

uint16_t BccuLampRGB::getColorR() {
  return XMC_BCCU_CH_ReadIntensity(BCCU_CH_R);
}

uint16_t BccuLampRGB::getColorG() {
  return XMC_BCCU_CH_ReadIntensity(BCCU_CH_G);
}

uint16_t BccuLampRGB::getColorB() {
  return XMC_BCCU_CH_ReadIntensity(BCCU_CH_B);
}

} // namespace xmc