diff --git a/apps/exam_pop_up_controller.cpp b/apps/exam_pop_up_controller.cpp index 47ab4629b..7e8736a74 100644 --- a/apps/exam_pop_up_controller.cpp +++ b/apps/exam_pop_up_controller.cpp @@ -58,10 +58,9 @@ ExamPopUpController::ContentView::ContentView(Responder * parentResponder) : AppsContainer * container = (AppsContainer *)controller->app()->container(); if (controller->isActivatingExamMode()) { container->reset(); - Ion::LED::setBlinking(0.5, true, false, false); - Ion::LED::setLockState(true); + Ion::LED::setColor(KDColorRed); + Ion::LED::setBlinking(1.0f, 0.1f); } else { - Ion::LED::setLockState(false); Ion::LED::setColor(KDColorBlack); } container->refreshPreferences(); diff --git a/ion/include/ion/led.h b/ion/include/ion/led.h index b2d40cad7..dfb69c420 100644 --- a/ion/include/ion/led.h +++ b/ion/include/ion/led.h @@ -6,16 +6,9 @@ namespace Ion { namespace LED { +KDColor getColor(); void setColor(KDColor c); -void setBlinking(float blinkLength, bool red, bool green, bool blue); -void setCharging(bool isPluggedIn, bool isCharging); - -/* - * This is just a flag to see if we should mess with the LEDs on our own. - * Especially useful for keeping the LED blinking while in exam mode. - */ -bool getLockState(); -void setLockState(bool state); +void setBlinking(float period, float dutyCycle); } } diff --git a/ion/src/device/device.cpp b/ion/src/device/device.cpp index 485fc6ca3..d23dd35d9 100644 --- a/ion/src/device/device.cpp +++ b/ion/src/device/device.cpp @@ -169,7 +169,7 @@ void initPeripherals() { SWD::Device::init(); } -void shutdownPeripherals() { +void shutdownPeripherals(bool keepLEDAwake) { SWD::Device::shutdown(); Console::Device::shutdown(); #if USE_SD_CARD @@ -177,7 +177,9 @@ void shutdownPeripherals() { #endif USB::Device::shutdown(); Battery::Device::shutdown(); - LED::Device::shutdown(); + if (!keepLEDAwake) { + LED::Device::shutdown(); + } Keyboard::Device::shutdown(); Backlight::Device::shutdown(); Display::Device::shutdown(); @@ -279,16 +281,33 @@ void initClocks() { RCC.APB2ENR()->set(apb2enr); } -void shutdownClocks() { +void shutdownClocks(bool keepLEDAwake) { // APB2 bus RCC.APB2ENR()->set(0x00008000); // Reset value - // APB1 - RCC.APB1ENR()->set(0x00000402); - RCC.APB1LPENR()->setTIM3LPEN(true); // Keep the LED going + if (keepLEDAwake) { + /* TODO: enter sleep mode even if the LED is used and enable LED in low + * power mode. */ + // Keep the LED going: peripheral clock enable in low power mode register + /*RCC.APB1LPENR()->setTIM3LPEN(true); + RCC.AHB1LPENR()->setGPIOBLPEN(true); + RCC.AHB1LPENR()->setGPIOCLPEN(true);*/ - // AHB1 bus - RCC.AHB1ENR()->set(0); // Reset value + // APB1 + class RCC::APB1ENR apb1enr(0x00000400); + apb1enr.setTIM3EN(true); + RCC.APB1ENR()->set(apb1enr); + // AHB1 bus + class RCC::AHB1ENR ahb1enr(0); // Reset value + ahb1enr.setGPIOBEN(true); + ahb1enr.setGPIOCEN(true); + RCC.AHB1ENR()->set(ahb1enr); + } else { + // APB1 + RCC.APB1ENR()->set(0x00000400); + // AHB1 bus + RCC.AHB1ENR()->set(0); // Reset value + } RCC.AHB3ENR()->setFSMCEN(false); } diff --git a/ion/src/device/device.h b/ion/src/device/device.h index 16af422b6..a95bf85e7 100644 --- a/ion/src/device/device.h +++ b/ion/src/device/device.h @@ -14,9 +14,9 @@ void coreReset(); void jumpReset(); void initPeripherals(); -void shutdownPeripherals(); +void shutdownPeripherals(bool keepLEDAwake = false); void initClocks(); -void shutdownClocks(); +void shutdownClocks(bool keepLEDAwake = false); /* The serial number is 96 bits long. That's equal to 16 digits in base 64. We * expose a convenient "copySerialNumber" routine which can be called without diff --git a/ion/src/device/led.cpp b/ion/src/device/led.cpp index 3557ffe79..26cce0243 100644 --- a/ion/src/device/led.cpp +++ b/ion/src/device/led.cpp @@ -6,35 +6,32 @@ // Public Ion::LED methods -void Ion::LED::setColor(KDColor c) { - if (getLockState()) { - return; - } +static KDColor sLedColor = KDColorBlack; - initTimer(Ion::LED::Device::Mode::PWM); +KDColor Ion::LED::getColor() { + return sLedColor; +} + +void Ion::LED::setColor(KDColor c) { + sLedColor = c; Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::RED, true); Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::GREEN, true); Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::BLUE, true); - TIM3.CCR2()->set(Device::dutyCycleForUInt8(c.red())); - TIM3.CCR3()->set(Device::dutyCycleForUInt8(c.blue())); - TIM3.CCR4()->set(Device::dutyCycleForUInt8(c.green())); + constexpr float maxColorValue = (float)((1 << 8) -1); + Device::setPeriodAndDutyCycles(Device::Mode::PWM, c.red()/maxColorValue, c.green()/maxColorValue, c.blue()/maxColorValue); } -void Ion::LED::setBlinking(float blinkPeriod, bool red, bool green, bool blue) { - if (getLockState()) { - return; - } +void Ion::LED::setBlinking(float period, float dutyCycle) { + Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::RED, sLedColor.red() > 0); + Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::GREEN, sLedColor.green() > 0); + Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::BLUE, sLedColor.blue() > 0); - initTimer(Ion::LED::Device::Mode::BLINK, blinkPeriod); - - Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::RED, red); - Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::GREEN, green); - Ion::LED::Device::setColorStatus(Ion::LED::Device::Color::BLUE, blue); + Device::setPeriodAndDutyCycles(Device::Mode::BLINK, dutyCycle, dutyCycle, dutyCycle, period); } -void Ion::LED::setCharging(bool isPluggedIn, bool isCharging) { +/*void Ion::LED::setCharging(bool isPluggedIn, bool isCharging) { if (!isPluggedIn) { Ion::LED::setColor(KDColorBlack); } @@ -46,17 +43,7 @@ void Ion::LED::setCharging(bool isPluggedIn, bool isCharging) { Ion::LED::setColor(KDColorGreen); } } -} - -static bool lockState = false; - -bool Ion::LED::getLockState() { - return lockState; -} - -void Ion::LED::setLockState(bool state) { - lockState = state; -} +}*/ // Private Ion::Device::LED methods @@ -66,9 +53,12 @@ namespace Device { void init() { initGPIO(); + initTimer(); } void shutdown() { + shutdownTimer(); + shutdownGPIO(); } void initGPIO() { @@ -88,68 +78,7 @@ void shutdownGPIO() { } } -void initTimer(Mode mode, float blinkPeriod) { - constexpr int TIM3_FREQ = 4*1000; - - /* Pulse width modulation mode allows you to generate a signal with a - * frequency determined by the value of the TIMx_ARR register and a duty cycle - * determined by the value of the TIMx_CCRx register. */ - switch (mode) { - case Ion::LED::Device::Mode::PWM: - /* Let's set the prescaler to 1. Increasing the prescaler would slow down the - * modulation, which can be useful when debugging. */ - TIM3.PSC()->set(1); - TIM3.ARR()->set(PWMPeriod); - TIM3.CCR2()->set(0); - TIM3.CCR3()->set(0); - TIM3.CCR4()->set(0); - break; - case Ion::LED::Device::Mode::BLINK: - /* We still want to do PWM, but at a rate slow enough to blink. */ - TIM3.PSC()->set(Ion::Device::SYSBUS_FREQ / TIM3_FREQ); - TIM3.ARR()->set(blinkPeriod * TIM3_FREQ); - TIM3.CCR2()->set(blinkPeriod * TIM3_FREQ / 2); - TIM3.CCR3()->set(blinkPeriod * TIM3_FREQ / 2); - TIM3.CCR4()->set(blinkPeriod * TIM3_FREQ / 2); - break; - } - - // Set Channels 2-4 as outputs, PWM mode 1 - TIM3.CCMR()->setOC2M(TIM::CCMR::OCM::PWM1); - TIM3.CCMR()->setOC3M(TIM::CCMR::OCM::PWM1); - TIM3.CCMR()->setOC4M(TIM::CCMR::OCM::PWM1); - - // Output preload enable for channels 2-4 - TIM3.CCMR()->setOC2PE(true); - TIM3.CCMR()->setOC3PE(true); - TIM3.CCMR()->setOC4PE(true); - - // Auto-reload preload enable - TIM3.CR1()->setARPE(true); - - // Enable Capture/Compare for channel 2 to 4 - TIM3.CCER()->setCC2E(true); - TIM3.CCER()->setCC3E(true); - TIM3.CCER()->setCC4E(true); - - TIM3.BDTR()->setMOE(true); - - TIM3.CR1()->setCEN(true); -} - -void initTimerColor() { - /* Let's set the prescaler to 1. Increasing the prescaler would slow down the - * modulation, which can be useful when debugging. */ - TIM3.PSC()->set(1); - - /* Pulse width modulation mode allows you to generate a signal with a - * frequency determined by the value of the TIMx_ARR register and a duty cycle - * determined by the value of the TIMx_CCRx register. */ - TIM3.ARR()->set(PWMPeriod); - TIM3.CCR2()->set(0); - TIM3.CCR3()->set(0); - TIM3.CCR4()->set(0); - +void initTimer() { // Output preload enable for channels 2-4 TIM3.CCMR()->setOC2PE(true); TIM3.CCMR()->setOC3PE(true); @@ -174,6 +103,33 @@ void shutdownTimer() { setColorStatus(Ion::LED::Device::Color::BLUE, false); } +/* Pulse width modulation mode allows you to generate a signal with a + * frequency determined by the value of the TIMx_ARR register and a duty cycle + * determined by the value of the TIMx_CCRx register. */ + +void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, float period) { + constexpr int TIM3_FREQ = 4*1000; + switch (mode) { + case Mode::PWM: + /* Let's set the prescaler to 1. Increasing the prescaler would slow down the + * modulation, which can be useful when debugging. */ + TIM3.PSC()->set(1); + TIM3.ARR()->set(PWMPeriod); + period = PWMPeriod; + break; + case Mode::BLINK: + /* We still want to do PWM, but at a rate slow enough to blink. */ + TIM3.PSC()->set(Ion::Device::SYSBUS_FREQ / TIM3_FREQ); + TIM3.ARR()->set(period * TIM3_FREQ); + period *= TIM3_FREQ; // as we pre-scaled, we update the period + break; + } + + TIM3.CCR2()->set(dutyCycleRed*period); + TIM3.CCR3()->set(dutyCycleBlue*period); + TIM3.CCR4()->set(dutyCycleGreen*period); +} + void setColorStatus(Color color, bool enable) { switch (color) { case Ion::LED::Device::Color::RED: @@ -188,7 +144,7 @@ void setColorStatus(Color color, bool enable) { } } -void enforceState(bool red, bool green, bool blue) { +/*void enforceState(bool red, bool green, bool blue) { bool states[3] = {red, green, blue}; for (int i=0; i<3; i++) { GPIOPin p = RGBPins[i]; @@ -200,7 +156,7 @@ void enforceState(bool red, bool green, bool blue) { p.group().PUPDR()->setPull(p.pin(), GPIO::PUPDR::Pull::None); } } -} +}*/ } } diff --git a/ion/src/device/led.h b/ion/src/device/led.h index 19a918366..426ce9607 100644 --- a/ion/src/device/led.h +++ b/ion/src/device/led.h @@ -17,23 +17,24 @@ namespace Device { enum Mode { PWM, BLINK -} ; +}; enum Color { RED, GREEN, BLUE -} ; +}; void init(); void shutdown(); +void setPeriodAndDutyCycles(Mode mode, float dutyCycleRed, float dutyCycleGreen, float dutyCycleBlue, float period = 0.0f); void setColorStatus(Color color, bool enable); /* This call bypasses the timer, and immediately enforces a given LED state. */ -void enforceState(bool red, bool green, bool blue); +//void enforceState(bool red, bool green, bool blue); void initGPIO(); void shutdownGPIO(); -void initTimer(Mode mode, float blinkPeriod = 0); +void initTimer(); void shutdownTimer(); constexpr static GPIOPin RGBPins[] = { @@ -43,11 +44,6 @@ constexpr static GPIOPin RGBPins[] = { constexpr uint16_t PWMPeriod = 40000; -inline uint16_t dutyCycleForUInt8(uint8_t value) { - /* This function is a linear function from colors [0->255] to duty cycles [0->PWMPeriod].*/ - return ((uint32_t)value)*(PWMPeriod/255); -} - } } } diff --git a/ion/src/device/power.cpp b/ion/src/device/power.cpp index 0d0cacf93..021d75cc6 100644 --- a/ion/src/device/power.cpp +++ b/ion/src/device/power.cpp @@ -9,6 +9,7 @@ #include "regs/regs.h" void Ion::Power::suspend(bool checkIfPowerKeyReleased) { + bool isLEDActive = Ion::LED::getColor() != KDColorBlack; if (checkIfPowerKeyReleased) { /* Wait until power is released to avoid restarting just after suspending */ bool isPowerDown = true; @@ -17,11 +18,11 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) { isPowerDown = scan.keyDown(Keyboard::Key::B2); } } - Device::shutdownPeripherals(); + Device::shutdownPeripherals(isLEDActive); PWR.CR()->setLPDS(true); // Turn the regulator off. Takes longer to wake up. PWR.CR()->setFPDS(true); // Put the flash to sleep. Takes longer to wake up. - CM4.SCR()->setSLEEPDEEP(!Ion::LED::getLockState()); + CM4.SCR()->setSLEEPDEEP(!isLEDActive); while (1) { #if EPSILON_LED_WHILE_CHARGING @@ -29,7 +30,7 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) { * if the standby mode was stopped due to a "stop charging" event, we wait * a while to be sure that the plug state of the USB is up-to-date. */ msleep(200); - Ion::LED::setCharging(Ion::USB::isPlugged(), Ion::Battery::isCharging()); + //Ion::LED::setCharging(Ion::USB::isPlugged(), Ion::Battery::isCharging()); #endif WakeUp::Device::onPowerKeyDown(); @@ -38,7 +39,7 @@ void Ion::Power::suspend(bool checkIfPowerKeyReleased) { WakeUp::Device::onChargingEvent(); #endif - Device::shutdownClocks(); + Device::shutdownClocks(isLEDActive); /* To enter sleep, we need to issue a WFE instruction, which waits for the * event flag to be set and then clears it. However, the event flag might diff --git a/ion/src/shared/dummy/led.cpp b/ion/src/shared/dummy/led.cpp index 985b049a2..725c7c29d 100644 --- a/ion/src/shared/dummy/led.cpp +++ b/ion/src/shared/dummy/led.cpp @@ -1,17 +1,9 @@ #include -void Ion::LED::setColor(KDColor c) { +KDColor getColor() { + return KDColorBlack; } -void Ion::LED::setBlinking(float blinkPeriod, bool red, bool green, bool blue) { -} +void setColor(KDColor c) {} -void Ion::LED::setCharging(bool isPlugged, bool isCharging) { -} - -bool Ion::LED::getLockState() { - return false; -} - -void Ion::LED::setLockState(bool state) { -} +void setBlinking(float period, float dutyCycle) {} \ No newline at end of file diff --git a/ion/src/simulator/Makefile b/ion/src/simulator/Makefile index 9f73f59b1..9185e96b4 100644 --- a/ion/src/simulator/Makefile +++ b/ion/src/simulator/Makefile @@ -1,6 +1,5 @@ objs += $(addprefix ion/src/simulator/, \ init.o\ - led.o\ ) objs += $(addprefix ion/src/simulator/boot/, main.o) @@ -17,6 +16,7 @@ objs += $(addprefix ion/src/shared/, \ dummy/backlight.o \ dummy/battery.o \ dummy/fcc_id.o \ + dummy/led.o \ dummy/serial_number.o \ dummy/usb.o \ ) diff --git a/ion/src/simulator/led.cpp b/ion/src/simulator/led.cpp deleted file mode 100644 index 985b049a2..000000000 --- a/ion/src/simulator/led.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include - -void Ion::LED::setColor(KDColor c) { -} - -void Ion::LED::setBlinking(float blinkPeriod, bool red, bool green, bool blue) { -} - -void Ion::LED::setCharging(bool isPlugged, bool isCharging) { -} - -bool Ion::LED::getLockState() { - return false; -} - -void Ion::LED::setLockState(bool state) { -}