mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-19 00:37:25 +01:00
[ion] Improve suspend implementation
Change-Id: Ic43b58f34379292c53a82ab6c85674c6f7a9b381
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ void init();
|
||||
void shutdown();
|
||||
void initGPIO();
|
||||
void initADC();
|
||||
void generateWakeUpEventForChargingState();
|
||||
|
||||
constexpr GPIO ChargingGPIO = GPIOA;
|
||||
constexpr uint8_t ChargingPin = 0;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
* -----+-------------------+-----------------------+----------
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,8 +45,6 @@ inline uint8_t columnForKey(Key key) {
|
||||
return (int)key%numberOfColumns;
|
||||
}
|
||||
|
||||
void generateWakeUpEventForPowerKey();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Device {
|
||||
void init();
|
||||
void initGPIO();
|
||||
void shutdown();
|
||||
void generateWakeUpEventForUSBPlug();
|
||||
|
||||
constexpr static GPIOPin VbusPin = GPIOPin(GPIOA, 9);
|
||||
|
||||
|
||||
67
ion/src/device/wake_up.cpp
Normal file
67
ion/src/device/wake_up.cpp
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
18
ion/src/device/wake_up.h
Normal file
18
ion/src/device/wake_up.h
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user