diff --git a/ion/src/device/Makefile b/ion/src/device/Makefile index 37c40dc8e..4606a7b5f 100644 --- a/ion/src/device/Makefile +++ b/ion/src/device/Makefile @@ -18,6 +18,7 @@ objs += $(addprefix ion/src/device/, \ sd_card.o\ swd.o \ usb.o \ + wake_up.o \ ) # When using the register.h C++ file in production mode, we expect the compiler diff --git a/ion/src/device/battery.cpp b/ion/src/device/battery.cpp index 60df14463..a7b742bda 100644 --- a/ion/src/device/battery.cpp +++ b/ion/src/device/battery.cpp @@ -80,23 +80,6 @@ void shutdown() { RCC.APB2ENR()->setADC1EN(false); } -void generateWakeUpEventForChargingState() { - initGPIO(); - - /* Warning: pins with the same number in different groups cannot be set as - * source input for EXTI at the same time. Here, EXTICR1 register is filled - * between position 0-3 (charging pin = 0) with - * 0000 (ChargingGPIO = group A). */ - SYSCFG.EXTICR1()->setEXTI(ChargingPin, ChargingGPIO); - - EXTI.EMR()->set(ChargingPin, true); - - /* We need to detect when the battery starts and stops charging. We set the - * wake up event on the falling and rising edge. */ - EXTI.FTSR()->set(ChargingPin, true); - EXTI.RTSR()->set(ChargingPin, true); -} - } } } diff --git a/ion/src/device/battery.h b/ion/src/device/battery.h index 1829b0ad1..ef558dc86 100644 --- a/ion/src/device/battery.h +++ b/ion/src/device/battery.h @@ -17,7 +17,6 @@ void init(); void shutdown(); void initGPIO(); void initADC(); -void generateWakeUpEventForChargingState(); constexpr GPIO ChargingGPIO = GPIOA; constexpr uint8_t ChargingPin = 0; diff --git a/ion/src/device/device.cpp b/ion/src/device/device.cpp index 8430286a4..cc6d32f11 100644 --- a/ion/src/device/device.cpp +++ b/ion/src/device/device.cpp @@ -115,6 +115,7 @@ void init() { bool consolePeerConnectedOnBoot = Ion::Console::Device::peerConnected(); initPeripherals(); + LED::Device::init(); if (consolePeerConnectedOnBoot) { Ion::Device::Bench::run(); @@ -122,15 +123,16 @@ void init() { } void shutdown() { - shutdownPeripherals(false); + shutdownPeripherals(); shutdownClocks(); } void initPeripherals() { + /* WARNING: it never inits the LED that is manually switch on/off in sleep + * mode when needed. */ Display::Device::init(); Backlight::Device::init(); Keyboard::Device::init(); - LED::Device::init(); Battery::Device::init(); USB::Device::init(); #if USE_SD_CARD @@ -140,7 +142,9 @@ void initPeripherals() { SWD::Device::init(); } -void shutdownPeripherals(bool persitingLED) { +void shutdownPeripherals() { + /* WARNING: it never shutdowns the LED that can be switched on/off in sleep + * mode. */ SWD::Device::shutdown(); Console::Device::shutdown(); #if USE_SD_CARD @@ -148,9 +152,6 @@ void shutdownPeripherals(bool persitingLED) { #endif USB::Device::shutdown(); Battery::Device::shutdown(); - if (!persitingLED) { - LED::Device::shutdown(); - } Keyboard::Device::shutdown(); Backlight::Device::shutdown(); Display::Device::shutdown(); @@ -216,37 +217,29 @@ void initClocks() { RCC.AHB3ENR()->setFSMCEN(true); } -void initStandbyClock () { - // AHB1 bus - class RCC::AHB1ENR ahb1enr(0); // Reset value - ahb1enr.setGPIOAEN(true); - ahb1enr.setGPIOBEN(true); - ahb1enr.setGPIOCEN(true); - ahb1enr.setGPIOEEN(true); - RCC.AHB1ENR()->set(ahb1enr); - - // APB1 bus - // We're using TIM3 - RCC.APB1ENR()->setTIM3EN(true); - RCC.APB1ENR()->setPWREN(true); +void shutdownClocks() { + /* Reset values, everything off except LED timer clock */ // APB2 bus - class RCC::APB2ENR apb2enr(0x00008000); // Reset value - apb2enr.setSYSCFGEN(true); - RCC.APB2ENR()->set(apb2enr); -} + RCC.APB2ENR()->set(0x00008000); // Reset value -void shutdownClocks() { - // Reset values, everything off - RCC.APB2ENR()->set(0x00008000); - RCC.APB1ENR()->set(0x00000400); + /* AHB1 + * TIM3 clock is needed on to drive the LED */ + class RCC::APB1ENR apb1enr(0x00000400); // Reset value + apb1enr.setTIM3EN(true); + RCC.APB1ENR()->set(apb1enr); // AHB1 bus - RCC.AHB1ENR()->set(0); + RCC.AHB1ENR()->set(0); // Reset value RCC.AHB3ENR()->setFSMCEN(false); } +void shutdownLEDClocks() { + /* AHB1 */ + RCC.APB1ENR()->set(0x00000400); // Reset value +} + } } diff --git a/ion/src/device/device.h b/ion/src/device/device.h index d28d46e74..c22584bea 100644 --- a/ion/src/device/device.h +++ b/ion/src/device/device.h @@ -9,10 +9,10 @@ void shutdown(); void initFPU(); void initPeripherals(); -void shutdownPeripherals(bool persistingLED); +void shutdownPeripherals(); void initClocks(); -void initStandbyClock(); void shutdownClocks(); +void shutdownLEDClocks(); /* Pin | Role | Mode | Function * -----+-------------------+-----------------------+---------- diff --git a/ion/src/device/keyboard.cpp b/ion/src/device/keyboard.cpp index 5969ea399..02fcf41af 100644 --- a/ion/src/device/keyboard.cpp +++ b/ion/src/device/keyboard.cpp @@ -117,33 +117,6 @@ void shutdown() { } } -void generateWakeUpEventForPowerKey() { - Key key = Key::B2; - uint8_t rowPin = RowPins[rowForKey(key)]; - RowGPIO.MODER()->setMode(rowPin, GPIO::MODER::Mode::Output); - RowGPIO.OTYPER()->setType(rowPin, GPIO::OTYPER::Type::OpenDrain); - RowGPIO.ODR()->set(rowPin, 0); - - uint8_t column = columnForKey(key); - uint8_t columnPin = ColumnPins[column]; - - ColumnGPIO.MODER()->setMode(columnPin, GPIO::MODER::Mode::Input); - ColumnGPIO.PUPDR()->setPull(columnPin, GPIO::PUPDR::Pull::Up); - - /* Warning: pins with the same number in different groups cannot be set as - * source input for EXTI at the same time. Here, EXTICR1 register is filled - * between position 4-7 (column pin = 1) with 0010 (ColumnGPIO = group C). */ - - SYSCFG.EXTICR1()->setEXTI(columnPin, ColumnGPIO); - - EXTI.EMR()->set(columnPin, true); - - /* When the key is pressed, it will go from 1 (because it's pulled up) to - * zero (because it's connected to the open-drain output. In other words, - * we're waiting for a falling edge. */ - EXTI.FTSR()->set(columnPin, true); -} - } } } diff --git a/ion/src/device/keyboard.h b/ion/src/device/keyboard.h index 3d8d59d65..b87d85b38 100644 --- a/ion/src/device/keyboard.h +++ b/ion/src/device/keyboard.h @@ -45,8 +45,6 @@ inline uint8_t columnForKey(Key key) { return (int)key%numberOfColumns; } -void generateWakeUpEventForPowerKey(); - } } } diff --git a/ion/src/device/power.cpp b/ion/src/device/power.cpp index bf56352d1..26e695b06 100644 --- a/ion/src/device/power.cpp +++ b/ion/src/device/power.cpp @@ -6,59 +6,62 @@ #include "keyboard.h" #include "led.h" #include "usb.h" +#include "wake_up.h" void Ion::Power::suspend() { - Device::shutdownPeripherals(USB::isPlugged()); + // Shutdown all peripherals except the LED + Device::shutdownPeripherals(); 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(true); - Device::shutdownClocks(); + WakeUp::Device::onChargingEvent(); + WakeUp::Device::onUSBPlugging(); - msleep(300); + while (1) { + /* Update LEDS + * 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); + KDColor LEDColor = KDColorBlack; + if (Battery::isCharging()) { + LEDColor = KDColorRed; + } else if (USB::isPlugged()) { + LEDColor = KDColorGreen; + } else { + LEDColor = KDColorBlack; + } + if (LEDColor != KDColorBlack) { + LED::Device::init(); + } else { + LED::Device::shutdown(); + } + LED::setColor(LEDColor); - /* To enter sleep, we need to issue a WFE instruction, which waits for the + WakeUp::Device::onPowerKeyDown(); + + Device::shutdownClocks();//Shutdown all except LED + if (LEDColor == KDColorBlack) { + Device::shutdownLEDClocks(); + } + + /* 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 * already be on. So the safest way to make sure we actually wait for a new * event is to force the event flag to on (SEV instruction), use a first WFE * to clear it, and then a second WFE to wait for a _new_ event. */ - - Device::initStandbyClock(); - - //TODO: optimize stand by consumption - while (1) { - Keyboard::Device::generateWakeUpEventForPowerKey(); - Battery::Device::generateWakeUpEventForChargingState(); - USB::Device::generateWakeUpEventForUSBPlug(); - asm("sev"); asm("wfe"); msleep(1); asm("wfe"); + Device::initClocks(); + Keyboard::Device::init(); Keyboard::State scan = Keyboard::scan(); Keyboard::Device::shutdown(); - // Update LEDS - KDColor LEDColor = KDColorBlack; - if (Battery::isCharging()) { - LEDColor = KDColorOrange; - } else if (Ion::USB::isPlugged()) { - LEDColor = KDColorGreen; - } else { - LEDColor = KDColorBlack; - } - if (LEDColor == KDColorBlack) { - LED::setColor(LEDColor); - //TODO: understand why only shutdown the LED is inefficient - Device::shutdownPeripherals(false); - } else { - LED::Device::init(); - LED::setColor(LEDColor); - } - Ion::Keyboard::State OnlyPowerKeyDown = (1 << (uint8_t)Keyboard::Key::B2); if (scan == OnlyPowerKeyDown) { // Wake up @@ -69,4 +72,5 @@ void Ion::Power::suspend() { Device::initClocks(); Device::initPeripherals(); + LED::Device::init(); } diff --git a/ion/src/device/regs/rcc.h b/ion/src/device/regs/rcc.h index 5db37c773..2b13f4399 100644 --- a/ion/src/device/regs/rcc.h +++ b/ion/src/device/regs/rcc.h @@ -73,6 +73,7 @@ public: class APB1ENR : public Register32 { public: + using Register32::Register32; REGS_BOOL_FIELD(TIM3EN, 1); REGS_BOOL_FIELD(SPI3EN, 15); REGS_BOOL_FIELD(USART3EN, 18); diff --git a/ion/src/device/usb.cpp b/ion/src/device/usb.cpp index 0ea0786e6..9c906f51a 100644 --- a/ion/src/device/usb.cpp +++ b/ion/src/device/usb.cpp @@ -33,17 +33,6 @@ void shutdown() { VbusPin.group().PUPDR()->setPull(VbusPin.pin(), GPIO::PUPDR::Pull::None); } -void generateWakeUpEventForUSBPlug() { - initGPIO(); - /* Warning: pins with the same number in different groups cannot be set as - * source input for EXTI at the same time. Here, EXTICR3 register is - * filled between position 4-7 (Vbus pin = 9) with 0000 (Vbus GPIO = group A). */ - SYSCFG.EXTICR3()->setEXTI(VbusPin.pin(), VbusPin.group()); - - EXTI.EMR()->set(VbusPin.pin(), true); - EXTI.FTSR()->set(VbusPin.pin(), true); - EXTI.RTSR()->set(VbusPin.pin(), true); -} } } } diff --git a/ion/src/device/usb.h b/ion/src/device/usb.h index 8d92d4fa2..b538fde0d 100644 --- a/ion/src/device/usb.h +++ b/ion/src/device/usb.h @@ -15,7 +15,6 @@ namespace Device { void init(); void initGPIO(); void shutdown(); -void generateWakeUpEventForUSBPlug(); constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9); diff --git a/ion/src/device/wake_up.cpp b/ion/src/device/wake_up.cpp new file mode 100644 index 000000000..bb2733312 --- /dev/null +++ b/ion/src/device/wake_up.cpp @@ -0,0 +1,67 @@ +#include "wake_up.h" +#include "regs/regs.h" +#include "battery.h" +#include "usb.h" +#include "keyboard.h" + +namespace Ion { +namespace WakeUp { +namespace Device { + +void onChargingEvent() { + Battery::Device::initGPIO(); + + /* Warning: pins with the same number in different groups cannot be set as + * source input for EXTI at the same time. Here, EXTICR1 register is filled + * between position 0-3 (charging pin = 0) with + * 0000 (ChargingGPIO = group A). */ + SYSCFG.EXTICR1()->setEXTI(Battery::Device::ChargingPin, Battery::Device::ChargingGPIO); + + EXTI.EMR()->set(Battery::Device::ChargingPin, true); + + /* We need to detect when the battery stops charging. We set the + * wake up event on the rising edge. */ + EXTI.RTSR()->set(Battery::Device::ChargingPin, true); +} + +void onUSBPlugging() { + USB::Device::initGPIO(); + /* Here, EXTICR3 register is filled between position 4-7 (Vbus pin = 9) with + * 0000 (Vbus GPIO = group A). */ + SYSCFG.EXTICR3()->setEXTI(USB::Device::VbusPin.pin(), USB::Device::VbusPin.group()); + + EXTI.EMR()->set(USB::Device::VbusPin.pin(), true); + EXTI.FTSR()->set(USB::Device::VbusPin.pin(), true); + EXTI.RTSR()->set(USB::Device::VbusPin.pin(), true); +} + + +void onPowerKeyDown() { + Keyboard::Key key = Keyboard::Key::B2; + uint8_t rowPin = Keyboard::Device::RowPins[Keyboard::Device::rowForKey(key)]; + Keyboard::Device::RowGPIO.MODER()->setMode(rowPin, GPIO::MODER::Mode::Output); + Keyboard::Device::RowGPIO.OTYPER()->setType(rowPin, GPIO::OTYPER::Type::OpenDrain); + Keyboard::Device::RowGPIO.ODR()->set(rowPin, 0); + + uint8_t column = Keyboard::Device::columnForKey(key); + uint8_t columnPin = Keyboard::Device::ColumnPins[column]; + + Keyboard::Device::ColumnGPIO.MODER()->setMode(columnPin, GPIO::MODER::Mode::Input); + Keyboard::Device::ColumnGPIO.PUPDR()->setPull(columnPin, GPIO::PUPDR::Pull::Up); + + /* Here, EXTICR1 register is filled between position 4-7 (column pin = 1) with + * 0010 (ColumnGPIO = group C). */ + + SYSCFG.EXTICR1()->setEXTI(columnPin, Keyboard::Device::ColumnGPIO); + + EXTI.EMR()->set(columnPin, true); + + /* When the key is pressed, it will go from 1 (because it's pulled up) to + * zero (because it's connected to the open-drain output. In other words, + * we're waiting for a falling edge. */ + EXTI.FTSR()->set(columnPin, true); +} + +} +} +} diff --git a/ion/src/device/wake_up.h b/ion/src/device/wake_up.h new file mode 100644 index 000000000..30116fd2a --- /dev/null +++ b/ion/src/device/wake_up.h @@ -0,0 +1,18 @@ +#ifndef ION_DEVICE_WAKE_UP_H +#define ION_DEVICE_WAKE_UP_H + +#include "regs/regs.h" + +namespace Ion { +namespace WakeUp { +namespace Device { + +void onChargingEvent(); +void onUSBPlugging(); +void onPowerKeyDown(); + +} +} +} + +#endif