commit 6642b62ddd58ad39c168b02a9375148c1344b0d5
parent 35e1ace1151de86a0f2836c849398d05c8fd1bb5
Author: mntmn <lukas@mntre.com>
Date: Mon, 27 Sep 2021 20:48:17 +0000
Merge branch 'wip-lpc-powersave' into 'master'
Deep Sleep/Powersave modes for Keyboard/Motherboard LPC Tandem
See merge request reform/reform!20
Diffstat:
3 files changed, 354 insertions(+), 194 deletions(-)
diff --git a/reform2-keyboard-fw/Keyboard.c b/reform2-keyboard-fw/Keyboard.c
@@ -41,11 +41,28 @@
#include <stdlib.h>
#include <avr/sleep.h>
-#define KBD_FW_REV "R1 20210815"
+#define KBD_FW_REV "R1 20210927"
//#define KBD_VARIANT_STANDALONE
#define KBD_VARIANT_QWERTY_US
//#define KBD_VARIANT_NEO2
+#define COLS 14
+#define ROWS 6
+
+uint8_t matrix[COLS*6+2] = {
+ KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, HID_KEYBOARD_SC_EXSEL,
+
+ KEY_GRAVE_ACCENT_AND_TILDE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS_AND_UNDERSCORE, KEY_EQUAL_AND_PLUS, KEY_BACKSPACE,
+
+ KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_OPENING_BRACKET_AND_OPENING_BRACE, KEY_CLOSING_BRACKET_AND_CLOSING_BRACE, KEY_BACKSLASH_AND_PIPE,
+
+ HID_KEYBOARD_SC_LEFT_CONTROL, HID_KEYBOARD_SC_APPLICATION, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON_AND_COLON, KEY_APOSTROPHE_AND_QUOTE, KEY_ENTER,
+
+ HID_KEYBOARD_SC_LEFT_SHIFT, HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN, HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN, KEY_SLASH_AND_QUESTION_MARK, HID_KEYBOARD_SC_UP_ARROW, HID_KEYBOARD_SC_RIGHT_SHIFT,
+
+ HID_KEYBOARD_SC_RIGHT_GUI, HID_KEYBOARD_SC_LEFT_GUI, HID_KEYBOARD_SC_RIGHT_CONTROL, KEY_SPACE, HID_KEYBOARD_SC_LEFT_ALT, HID_KEYBOARD_SC_RIGHT_ALT, KEY_SPACE, HID_KEYBOARD_SC_PAGE_UP, HID_KEYBOARD_SC_PAGE_DOWN, HID_KEYBOARD_SC_LEFT_ARROW, HID_KEYBOARD_SC_DOWN_ARROW, HID_KEYBOARD_SC_RIGHT_ARROW, 0xfe,0xed,0xca,0xfe
+};
+
/** Buffer to hold the previously generated Keyboard HID report, for comparison purposes inside the HID class driver. */
static uint8_t PrevKeyboardHIDReportBuffer[sizeof(USB_KeyboardReport_Data_t)];
@@ -75,22 +92,9 @@ USB_ClassInfo_HID_Device_t Keyboard_HID_Interface =
#define set_input(portdir,pin) portdir &= ~(1<<pin)
#define set_output(portdir,pin) portdir |= (1<<pin)
-uint8_t matrix[15*6] = {
- KEY_ESCAPE, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, HID_KEYBOARD_SC_EXSEL, HID_KEYBOARD_SC_EXSEL,
-
- KEY_GRAVE_ACCENT_AND_TILDE, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0, KEY_MINUS_AND_UNDERSCORE, KEY_EQUAL_AND_PLUS, KEY_BACKSPACE, 0,
-
- KEY_TAB, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, KEY_P, KEY_OPENING_BRACKET_AND_OPENING_BRACE, KEY_CLOSING_BRACKET_AND_CLOSING_BRACE, KEY_BACKSLASH_AND_PIPE, 0,
-
- HID_KEYBOARD_SC_LEFT_CONTROL, HID_KEYBOARD_SC_APPLICATION, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON_AND_COLON, KEY_APOSTROPHE_AND_QUOTE, KEY_ENTER, 0,
-
- HID_KEYBOARD_SC_LEFT_SHIFT, HID_KEYBOARD_SC_NON_US_BACKSLASH_AND_PIPE, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, HID_KEYBOARD_SC_COMMA_AND_LESS_THAN_SIGN, HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN, KEY_SLASH_AND_QUESTION_MARK, HID_KEYBOARD_SC_UP_ARROW, HID_KEYBOARD_SC_RIGHT_SHIFT, 0,
-
- HID_KEYBOARD_SC_RIGHT_GUI, HID_KEYBOARD_SC_LEFT_GUI, HID_KEYBOARD_SC_RIGHT_CONTROL, KEY_SPACE, HID_KEYBOARD_SC_LEFT_ALT, HID_KEYBOARD_SC_RIGHT_ALT, KEY_SPACE, HID_KEYBOARD_SC_PAGE_UP, HID_KEYBOARD_SC_PAGE_DOWN, HID_KEYBOARD_SC_LEFT_ARROW, HID_KEYBOARD_SC_DOWN_ARROW, HID_KEYBOARD_SC_RIGHT_ARROW, 0,0,0
-};
-
-uint8_t matrix_debounce[15*6];
-uint8_t matrix_state[15*6];
+uint8_t matrix_debounce[COLS*6];
+uint8_t matrix_state[COLS*6];
+uint8_t remote_som_power_expected_state = 0;
// f8 = sleep
// 49 = mute
@@ -220,9 +224,68 @@ void insert_bat_icon(char* str, int x, float v) {
str[x+1] = 4*32+icon+1;
}
-void remote_get_voltages(void) {
+void remote_try_wakeup(void) {
+ char buf[64];
+
+ for (int i=0; i<1000; i++) {
+ if (i%10 == 0) {
+ gfx_clear();
+ sprintf(buf, "Waking up LPC... %d%%", i/4);
+ gfx_poke_str(0, 0, buf);
+ gfx_flush();
+ }
+
+ Serial_SendByte('a');
+ Serial_SendByte('\r');
+
+ if (Serial_ReceiveByte()>0) {
+ remote_receive_string(0);
+ break;
+ }
+
+ Delay_MS(25);
+ }
+ Serial_SendByte('\r');
+ Delay_MS(10);
+ while (remote_receive_string(0)) {
+ Delay_MS(25);
+ }
+}
+
+int remote_try_command(char* cmd, int print_response) {
+ int ok = 0;
+
empty_serial();
+ for (int tries=0; tries<2; tries++) {
+ for (int i=0; i<strlen(cmd); i++) {
+ Serial_SendByte(cmd[i]);
+ }
+ Serial_SendByte('\r');
+ Delay_MS(1);
+ if (print_response) {
+ term_x = 0;
+ term_y = 0;
+ }
+ ok = remote_receive_string(print_response);
+
+ if (!ok && tries == 0) {
+ remote_try_wakeup();
+ empty_serial();
+ }
+ if (ok) break;
+ }
+ if (!ok) {
+ gfx_clear();
+ gfx_poke_str(0, 0, "No response from LPC.");
+ gfx_flush();
+ }
+
+ empty_serial();
+ return ok;
+}
+
+void remote_get_voltages(void) {
term_x = 0;
term_y = 0;
@@ -230,15 +293,13 @@ void remote_get_voltages(void) {
float bat_amps = 0;
char bat_gauge[5] = {0,0,0,0,0};
- Serial_SendByte('c');
- Serial_SendByte('\r');
- Delay_MS(1);
- remote_receive_string(0);
+ int ok = remote_try_command("c", 0);
+ if (!ok) return;
- // lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???%
- // | | | | | | | | | | | |
- // 0 3 6 9 12 15 18 21 24| | |
- // 26 33 39
+ // lpc format: 32 32 32 32 32 32 32 32 mA 0256mV26143 ???% P1
+ // | | | | | | | | | | | | |
+ // 0 3 6 9 12 15 18 21 24| | | |
+ // 26 33 39 44
// |
// `- can be a minus
float sum_volts = 0;
@@ -260,21 +321,34 @@ void remote_get_voltages(void) {
int gauge_offset = volts_offset+5+1;
strncpy(bat_gauge, &response[gauge_offset], 4);
+ char* power_str = " ";
+ int syspower_offset = gauge_offset+5;
+ char power_digit = response[syspower_offset+1];
+ if (power_digit == '1') {
+ power_str = " On";
+ } else if (power_digit == '0') {
+ power_str = "Off";
+ }
+
// plot
gfx_clear();
char str[32];
- sprintf(str,"[] %.1f [] %.1f %s",voltages[0],voltages[4],bat_gauge);
+ sprintf(str,"[] %.1f [] %.1f %s",voltages[0],voltages[4],bat_gauge);
insert_bat_icon(str,0,voltages[0]);
insert_bat_icon(str,8,voltages[4]);
gfx_poke_str(0,0,str);
- sprintf(str,"[] %.1f [] %.1f ",voltages[1],voltages[5]);
+ sprintf(str,"[] %.1f [] %.1f %s",voltages[1],voltages[5],power_str);
insert_bat_icon(str,0,voltages[1]);
insert_bat_icon(str,8,voltages[5]);
gfx_poke_str(0,1,str);
- sprintf(str,"[] %.1f [] %.1f %2.2fA",voltages[2],voltages[6],bat_amps);
+ if (bat_amps>=0) {
+ sprintf(str,"[] %.1f [] %.1f %2.3fA",voltages[2],voltages[6],bat_amps);
+ } else {
+ sprintf(str,"[] %.1f [] %.1f %2.2fA",voltages[2],voltages[6],bat_amps);
+ }
insert_bat_icon(str,0,voltages[2]);
insert_bat_icon(str,8,voltages[6]);
gfx_poke_str(0,2,str);
@@ -297,9 +371,11 @@ void remote_check_for_low_battery(void) {
Serial_SendByte('c');
Serial_SendByte('\r');
Delay_MS(1);
- remote_receive_string(0);
+ int ok = remote_receive_string(0);
+ if (!ok) return;
for (int i=0; i<8; i++) {
+ // TODO: only accept digits
voltages[i] = ((float)((response[i*3]-'0')*10 + (response[i*3+1]-'0')))/10.0;
if (voltages[i]<0) voltages[i]=0;
if (voltages[i]>=10) voltages[i]=9.9;
@@ -319,6 +395,25 @@ void remote_check_for_low_battery(void) {
low_battery_alert = 1;
}
}
+
+ int syspower_offset = gauge_offset+5;
+ if (response[syspower_offset] == 'P') {
+ char digit = response[syspower_offset+1];
+ if (digit == '0' || digit == '1') {
+ int is_computer_on = (digit == '1');
+ if (!is_computer_on && remote_som_power_expected_state == 1) {
+ // LPC says the computer is off, but we didn't expect it to be.
+ // the only way this happens is if LPC turned off the system
+ // due to a low battery condition.
+ //
+ // The keyboard will then go to sleep accordingly.
+
+ EnterPowerOff();
+ reset_keyboard_state();
+ }
+ remote_som_power_expected_state = is_computer_on;
+ }
+ }
}
void remote_get_status(void) {
@@ -331,12 +426,8 @@ void remote_get_status(void) {
gfx_flush();
#ifndef KBD_VARIANT_STANDALONE
- term_x = 0;
- term_y = 0;
- Serial_SendByte('s');
- Serial_SendByte('\r');
- Delay_MS(1);
- remote_receive_string(1);
+ int ok = remote_try_command("s", 1);
+ if (!ok) return;
#endif
}
@@ -378,7 +469,7 @@ void kbd_brightness_dec(void) {
}
void kbd_brightness_set(int brite) {
- pwmval=brite;
+ pwmval = brite;
if (pwmval<0) pwmval = 0;
if (pwmval>=10) pwmval = 10;
OCR0A = pwmval;
@@ -386,113 +477,60 @@ void kbd_brightness_set(int brite) {
void remote_turn_on_som(void) {
gfx_clear();
- empty_serial();
- term_x = 0;
- term_y = 0;
+ int ok = remote_try_command("1p", 0);
+ if (!ok) return;
- Serial_SendByte('1');
- Serial_SendByte('p');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
anim_hello();
kbd_brightness_init();
+
+ remote_som_power_expected_state = 1;
}
void remote_turn_off_som(void) {
anim_goodbye();
- empty_serial();
- term_x = 0;
- term_y = 0;
+ int ok = remote_try_command("0p", 0);
+ if (!ok) return;
- Serial_SendByte('0');
- Serial_SendByte('p');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ remote_som_power_expected_state = 0;
}
void remote_reset_som(void) {
- empty_serial();
-
- term_x = 0;
- term_y = 0;
-
- Serial_SendByte('2');
- Serial_SendByte('p');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("2p", 0);
+ if (!ok) return;
}
void remote_wake_som(void) {
- empty_serial();
-
- term_x = 0;
- term_y = 0;
-
- Serial_SendByte('1');
- Serial_SendByte('w');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
- Serial_SendByte('0');
- Serial_SendByte('w');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("1w", 0);
+ if (!ok) return;
+ ok = remote_try_command("0w", 0);
+ if (!ok) return;
}
void remote_turn_off_aux(void) {
- empty_serial();
-
- Serial_SendByte('3');
- Serial_SendByte('p');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("3p", 0);
+ if (!ok) return;
}
void remote_turn_on_aux(void) {
- empty_serial();
-
- Serial_SendByte('4');
- Serial_SendByte('p');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("4p", 0);
+ if (!ok) return;
}
void remote_report_voltages(void) {
- empty_serial();
-
- Serial_SendByte('0');
- Serial_SendByte('c');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("0c", 0);
+ if (!ok) return;
}
void remote_enable_som_uart(void) {
- empty_serial();
-
- Serial_SendByte('1');
- Serial_SendByte('u');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("1u", 0);
+ if (!ok) return;
}
void remote_disable_som_uart(void) {
- empty_serial();
-
- Serial_SendByte('0');
- Serial_SendByte('u');
- Serial_SendByte('\r');
- Delay_MS(1);
- empty_serial();
+ int ok = remote_try_command("0u", 0);
+ if (!ok) return;
}
typedef struct MenuItem {
@@ -509,7 +547,7 @@ const MenuItem menu_items[] = {
{ "System Status s", KEY_S }
};
#else
-#define MENU_NUM_ITEMS 10
+#define MENU_NUM_ITEMS 9
const MenuItem menu_items[] = {
{ "Exit Menu ESC", KEY_ESCAPE },
{ "Power On 1", KEY_1 },
@@ -520,7 +558,11 @@ const MenuItem menu_items[] = {
{ "Key Backlight+ F2", KEY_F2 },
{ "Wake SPC", KEY_SPACE },
{ "System Status s", KEY_S },
- { "KBD Power-Off p", KEY_P }
+
+ // Only needed for debugging.
+ // The keyboard will go to sleep when turning off
+ // main system power.
+ { "KBD Power-Off p", KEY_P },
};
#endif
@@ -553,6 +595,8 @@ int execute_meta_function(int keycode) {
// TODO: are you sure?
remote_turn_off_som();
EnterPowerOff();
+ // Directly enter menu again
+ return 2;
}
else if (keycode == KEY_1) {
remote_turn_on_som();
@@ -565,10 +609,7 @@ int execute_meta_function(int keycode) {
else if (keycode == KEY_SPACE) {
remote_wake_som();
}
- /*else if (keycode == KEY_X) {
- remote_turn_on_aux();
- }
- else if (keycode == KEY_V) {
+ /*else if (keycode == KEY_V) {
remote_turn_off_aux();
}*/
else if (keycode == KEY_B) {
@@ -611,6 +652,8 @@ int execute_meta_function(int keycode) {
}
else if (keycode == KEY_P) {
EnterPowerOff();
+ // Directly enter menu again
+ return 2;
}
gfx_clear();
@@ -621,14 +664,30 @@ int execute_meta_function(int keycode) {
uint8_t last_meta_key = 0;
+// enter the menu
+void enter_meta_mode(void) {
+ current_scroll_y = 0;
+ current_menu_y = 0;
+ active_meta_mode = 1;
+ // render menu
+ render_menu(current_scroll_y);
+}
+
+void reset_keyboard_state(void) {
+ for (int i=0; i<COLS*ROWS; i++) {
+ matrix_debounce[i] = 0;
+ matrix_state[i] = 0;
+ }
+ last_meta_key = 0;
+}
+
void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardReport) {
// how many keys are pressed this round
uint8_t total_pressed = 0;
uint8_t used_key_codes = 0;
// pull ROWs low one after the other
- for (int y=0; y<6; y++) {
-
+ for (int y=0; y<ROWS; y++) {
switch (y) {
case 0: output_low(PORTB, 6); break;
case 1: output_low(PORTB, 5); break;
@@ -642,9 +701,9 @@ void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardR
// TODO maybe not necessary
_delay_us(10);
- // check input COLs
+ // check input COLs
for (int x=0; x<14; x++) {
- uint16_t loc = y*15+x;
+ uint16_t loc = y*COLS+x;
uint16_t keycode = matrix[loc];
uint8_t pressed = 0;
uint8_t debounced_pressed = 0;
@@ -684,16 +743,12 @@ void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardR
// circle key?
if (keycode == HID_KEYBOARD_SC_EXSEL) {
if (!active_meta_mode && !last_meta_key) {
- current_scroll_y = 0;
- current_menu_y = 0;
- active_meta_mode = 1;
- // render menu
- render_menu(current_scroll_y);
+ enter_meta_mode();
}
} else {
if (active_meta_mode) {
// not holding the same key?
- if (last_meta_key!=keycode) {
+ if (last_meta_key != keycode) {
// hyper/circle/menu functions
int stay_meta = execute_meta_function(keycode);
// don't repeat action while key is held down
@@ -703,6 +758,13 @@ void process_keyboard(char usb_report_mode, USB_KeyboardReport_Data_t* KeyboardR
if (!stay_meta) {
active_meta_mode = 0;
}
+
+ // after wake-up from sleep mode, skip further keymap processing
+ if (stay_meta == 2) {
+ reset_keyboard_state();
+ enter_meta_mode();
+ return;
+ }
}
} else if (!last_meta_key) {
// not meta mode, regular key: report keypress via USB
@@ -749,16 +811,17 @@ void process_alerts(void) {
int main(void)
{
#ifdef KBD_VARIANT_QWERTY_US
- matrix[15*4+1]=KEY_DELETE;
+ matrix[COLS*4+1]=KEY_DELETE;
#endif
#ifdef KBD_VARIANT_NEO2
- matrix[15*3+0]=HID_KEYBOARD_SC_CAPS_LOCK; // M3
- matrix[15*2+13]=KEY_ENTER;
- matrix[15*3+13]=KEY_BACKSLASH_AND_PIPE; // M3
+ matrix[COLS*3+0]=HID_KEYBOARD_SC_CAPS_LOCK; // M3
+ matrix[COLS*2+13]=KEY_ENTER;
+ matrix[COLS*3+13]=KEY_BACKSLASH_AND_PIPE; // M3
#endif
SetupHardware();
GlobalInterruptEnable();
+ anim_hello();
int counter = 0;
@@ -769,7 +832,7 @@ int main(void)
USB_USBTask();
counter++;
#ifndef KBD_VARIANT_STANDALONE
- if (counter>=30000) {
+ if (counter>=100000) {
remote_check_for_low_battery();
counter = 0;
}
@@ -780,7 +843,7 @@ int main(void)
}
}
-void SetupHardware()
+void SetupHardware(void)
{
// Disable watchdog if enabled by bootloader/fuses
MCUSR &= ~(1 << WDRF);
@@ -810,8 +873,6 @@ void SetupHardware()
kbd_brightness_init();
gfx_init(false);
- anim_hello();
-
Serial_Init(57600, false);
USB_Init();
}
@@ -828,26 +889,28 @@ void SetupHardware()
void EnterPowerOff(void)
{
USB_Disable(); // Stop USB stack so it doesn't wake us up
-
- kbd_brightness_set(0);
+
+ // turn off backlight, but don't overwrite setting
+ OCR0A = 0;
+
// Turn off OLED to save power
gfx_clear_screen();
gfx_off();
// Disable ADC to save even more power
ADCSRA=0;
- cli(); // No interrupts
+ cli(); // No interrupts
// Set all ports not floating if possible, leaving pullups alone
PORTB=0x3F; // Leave pull-up on all the columns on PB0-3, drive rows 2-3 high, 1-low
- PORTC=0xC0;
+ PORTC=0xC0;
PORTD=0xF0; // Keep pullup on PD5 like setup did, drive rows 4,5,6 high
PORTE=0x40; // Pullup on PE6
PORTF=0xFF; // Pullups on PF (columns)
// ROW1 is the only row driven low and left low, thus is always ready to be read out
// We just need to check COL14 (PC6) if it is low (pressed) or high
- // Unfortunatly the circle key is on COL14(PC6) which doesn't have pin change interrupt
+ // Unfortunately the circle key is on COL14(PC6) which doesn't have pin change interrupt
// capabilities, so we need to wake up every so often to check if it is pressed, and
// if so bring us out of power-off
// We can use the Watchdog timer to do this.
@@ -855,7 +918,7 @@ void EnterPowerOff(void)
do {
wdt_reset();
WDTCSR = (1<<WDCE) | (1<<WDE); // Enable writes to watchdog
- WDTCSR = (1<<WDIE) | (1<<WDE) | (1<<WDP2) | (1<<WDP1); // Interrupt mode, 1s timeout
+ WDTCSR = (1<<WDIE) | (1<<WDE) | (0<<WDP3) | (1<<WDP2) | (1<<WDP1) | (0<<WDP0); // Interrupt mode, 1s timeout
// Enter Power-save mode
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
@@ -864,11 +927,11 @@ void EnterPowerOff(void)
sleep_cpu(); // Actually go to sleep
// Zzzzzz
sleep_disable(); // We've woken up
- sei();
+ sei();
// Check if circle key has been pressed (active-low)
// If not reset the watchdog and try again
} while(PINC&(1<<6));
-
+
// Resume and reinitialize hardware
SetupHardware();
}
@@ -978,6 +1041,8 @@ void CALLBACK_HID_Device_ProcessHIDReport(USB_ClassInfo_HID_Device_t* const HIDI
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='0') {
// PWR0: shutdown (turn off power rails)
remote_turn_off_som();
+ EnterPowerOff();
+ reset_keyboard_state();
}
else if (data[0]=='P' && data[1]=='W' && data[2]=='R' && data[3]=='3') {
// PWR3: aux power off
diff --git a/reform2-keyboard-fw/Keyboard.h b/reform2-keyboard-fw/Keyboard.h
@@ -67,7 +67,8 @@
/* Function Prototypes: */
void SetupHardware(void);
- void EnterPowerOff(void);
+ void EnterPowerOff(void);
+ void reset_keyboard_state(void);
void EVENT_USB_Device_Connect(void);
void EVENT_USB_Device_Disconnect(void);
diff --git a/reform2-lpc-fw/src/boards/reform2/board_reform2.c b/reform2-lpc-fw/src/boards/reform2/board_reform2.c
@@ -37,9 +37,12 @@
#define REFORM_MBREV_R3 13 // R2 with "NTC instead of RNG/SS" fix
// don't forget to set this to the correct rev for your motherboard!
-#define REFORM_MOTHERBOARD_REV REFORM_MBREV_R2
+#define REFORM_MOTHERBOARD_REV REFORM_MBREV_R3
//#define REF2_DEBUG 1
-#define FW_REV "MREF2LPC R2 20210419"
+#define FW_REV "MREF2LPC R3 20210925"
+
+#define POWERSAVE_SLEEP_SECONDS 1
+#define POWERSAVE_HOLDOFF_CYCLES (60*15)
#define INA260_ADDRESS 0x4e
#define LTC4162F_ADDRESS 0x68
@@ -134,7 +137,8 @@ enum state_t {
ST_OVERVOLTED,
ST_UNDERVOLTED,
ST_MISSING,
- ST_FULLY_CHARGED
+ ST_FULLY_CHARGED,
+ ST_POWERSAVE
};
// charging state machine
@@ -143,6 +147,7 @@ int cycles_in_state = 0;
int charge_current = 1;
uint32_t cur_second = 0;
uint32_t last_second = 0;
+int powersave_holdoff_cycles = POWERSAVE_HOLDOFF_CYCLES;
// 1.8A x 3600 seconds/hour
#define MAX_CAPACITY (1.8)*3600.0
@@ -377,6 +382,7 @@ uint16_t charger_alerts;
uint16_t status_alerts;
float chg_vin;
float chg_vbat;
+int som_is_powered = 0;
void turn_som_power_on(void) {
LPC_GPIO->CLR[1] = (1 << 28); // hold in reset
@@ -400,6 +406,8 @@ void turn_som_power_on(void) {
LPC_GPIO->SET[0] = (1 << 7); // AUX 3v3 on (R1+)
LPC_GPIO->SET[1] = (1 << 28); // release reset
+
+ som_is_powered = 1;
}
void turn_som_power_off(void) {
@@ -417,6 +425,8 @@ void turn_som_power_off(void) {
LPC_GPIO->CLR[1] = (1 << 19); // 1v2 off
LPC_GPIO->CLR[1] = (1 << 31); // USB 5v off (R1+)
LPC_GPIO->CLR[0] = (1 << 7); // AUX 3v3 off (R1+)
+
+ som_is_powered = 0;
}
// just a reset pulse to IMX, no power toggling
@@ -447,33 +457,15 @@ void brownout_setup(void) {
}
void watchdog_feed(void) {
+ __disable_irq();
LPC_WWDT->FEED = 0xAA;
LPC_WWDT->FEED = 0x55;
+ __enable_irq();
}
#define WWDT_WDMOD_WDEN ((uint32_t) (1 << 0))
#define WWDT_WDMOD_WDRESET ((uint32_t) (1 << 1))
-void watchdog_setup(void) {
- LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable
-
- LPC_SYSCON->WDTOSCCTRL =
- (1<<5) | // FREQSEL 0.6MHz
- 31; // DIVSEL 64 (31+1)*2
-
- LPC_SYSCON->PDRUNCFG &= ~(1<<6); // WDTOSC_PD disable
-
- LPC_WWDT->CLKSEL = 1; // WDOSC
-
- LPC_WWDT->TC = 0xffff/5; // timeout counter, ~5 seconds
-
- LPC_WWDT->MOD = 0;
- LPC_WWDT->MOD |= WWDT_WDMOD_WDRESET; // enable WDRESET (watchdog resets system)
- LPC_WWDT->MOD |= WWDT_WDMOD_WDEN; // watchdog enable
-
- watchdog_feed();
-}
-
void boardInit(void)
{
SystemCoreClockUpdate();
@@ -545,10 +537,14 @@ uint8_t remote_arg = 0;
unsigned char cmd_state = ST_EXPECT_DIGIT_0;
unsigned int cmd_number = 0;
int cmd_echo = 0;
+int force_sleep = 0;
void handle_commands() {
if (!uartRxBufferDataPending()) return;
+ // reset sleep counter on any interaction
+ powersave_holdoff_cycles = POWERSAVE_HOLDOFF_CYCLES;
+
char chr = uartRxBufferRead();
if (cmd_echo) {
@@ -638,6 +634,12 @@ void handle_commands() {
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
}
+ else if (remote_cmd == 'x') {
+ // test sleep
+ force_sleep = cmd_number;
+ sprintf(uartBuffer,"sleep: %d\r\n", force_sleep);
+ uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
+ }
else if (remote_cmd == 'a') {
// get system current (mA)
sprintf(uartBuffer,"%d\r\n",(int)(current*1000.0));
@@ -668,9 +670,12 @@ void handle_commands() {
sprintf(uartBuffer,FW_REV"cell missing,%d,%d,%d\r",cycles_in_state,min_mah,acc_mah);
} else if (state == ST_FULLY_CHARGED) {
sprintf(uartBuffer,FW_REV"full charge,%d,%d,%d\r",cycles_in_state,min_mah,acc_mah);
+ } else if (state == ST_POWERSAVE) {
+ sprintf(uartBuffer,FW_REV"powersave,%d,%d,%d\r",cycles_in_state,min_mah,acc_mah);
} else {
sprintf(uartBuffer,FW_REV"unknown:%d,%d,%d,%d\r",state,cycles_in_state,min_mah,acc_mah);
}
+
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'u') {
@@ -719,7 +724,7 @@ void handle_commands() {
}
int mV = (int)(volts*1000.0);
- sprintf(uartBuffer,"%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%cmA%c%04dmV%05d %s\r\n",
+ sprintf(uartBuffer,"%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%c%02d%cmA%c%04dmV%05d %s P%d\r\n",
(int)(cells_v[0]*10),
(discharge_bits &(1<<0))?'!':' ',
(int)(cells_v[1]*10),
@@ -739,7 +744,8 @@ void handle_commands() {
mA_sign,
mA,
mV,
- gauge);
+ gauge,
+ som_is_powered);
uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
}
else if (remote_cmd == 'S') {
@@ -798,6 +804,67 @@ void report_to_spi(void)
ssp0Send((uint8_t*)report, strlen(report));
}
+void WDT_IRQHandler(void)
+{
+ // Disable WDT interrupt
+ NVIC_DisableIRQ(WDT_IRQn);
+ NVIC_ClearPendingIRQ(WDT_IRQn);
+}
+
+// WARNING: take care not to overflow TC (11786 * secs)
+void deep_sleep_seconds(int secs) {
+ // make WWDTINT wake the LPC up from sleep
+ // STARTERP1 WWDTINT bit 12
+ LPC_SYSCON->STARTERP1 |= (1 << 12);
+
+ NVIC_DisableIRQ(WDT_IRQn);
+
+ // Configure watchdog timer to wake us up
+ LPC_SYSCON->SYSAHBCLKCTRL |= (1<<15); // WWDT enable
+
+ // 9375Hz??
+ LPC_SYSCON->WDTOSCCTRL =
+ (1<<5) | // FREQSEL 0.6MHz
+ 31; // DIVSEL 64 (31+1)*2
+
+ // Power configuration register
+ LPC_SYSCON->PDRUNCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable)
+ LPC_SYSCON->PDSLEEPCFG &= ~(1<<6); // WDTOSC_PD disable (power down disable)
+ LPC_SYSCON->PDAWAKECFG = LPC_SYSCON->PDRUNCFG; // when waking up, power up the default blocks
+
+ LPC_WWDT->CLKSEL = 1; // WDOSC
+
+ //LPC_WWDT->TC = 0xffff/5; // timeout counter, ~5 seconds
+ // FIXME isn't that 1.39s? (0xffff/5.0937/5.0)
+ // no, apparently it is 5.56s (x4)
+
+ LPC_WWDT->TC = 11786 * secs; // timeout counter, ~1 second
+
+ LPC_WWDT->MOD = 0;
+ //LPC_WWDT->MOD |= WWDT_WDMOD_WDRESET; // WDRESET (watchdog resets system)
+ LPC_WWDT->MOD |= WWDT_WDMOD_WDEN; // watchdog enable
+
+ // counter value that triggers interrupt
+ LPC_WWDT->WARNINT = 0;
+
+ // need to feed WD once to apply WDMOD values
+ watchdog_feed();
+
+ // NVIC exception 25 (WWDT) interrupt source:
+ // ISER SETENA bit 25 -> 0xE000E100
+ NVIC_ClearPendingIRQ(WDT_IRQn);
+ NVIC_EnableIRQ(WDT_IRQn);
+
+ // Go to deep sleep mode
+ LPC_PMU->PCON = 1<<11; // clear DPDFLAG
+ LPC_PMU->PCON = 1; // select deep power down mode
+ SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
+ __WFI();
+
+ NVIC_DisableIRQ(WDT_IRQn);
+ LPC_WWDT->MOD = 0;
+}
+
int main(void)
{
boardInit();
@@ -808,10 +875,7 @@ int main(void)
last_second = delayGetSecondsActive();
- // WIP, not yet tested
- //watchdog_setup();
- //sprintf(uartBuffer, "\r\nwatchdog_setup() completed.\r\n");
- //uartSend((uint8_t*)uartBuffer, strlen(uartBuffer));
+ int next_state = state;
while (1)
{
@@ -827,8 +891,6 @@ int main(void)
// charge current 2: ~0.2A
- //watchdog_feed();
-
measure_and_accumulate_current();
measure_cell_voltages_and_control_discharge();
calculate_capacity_percentage();
@@ -836,23 +898,29 @@ int main(void)
if (state == ST_CHARGE) {
reset_discharge_bits();
- if (num_missing_cells > 0) {
+ if (force_sleep) {
+ // debug sleeping
+ if (powersave_holdoff_cycles <= 0) {
+ next_state = ST_POWERSAVE;
+ cycles_in_state = 0;
+ }
+ } else if ((num_missing_cells >= 1) && (num_missing_cells <= 7)) {
missing_reason = missing_bits;
// if cells were unplugged, we don't know the capacity anymore.
reached_full_charge = 0;
- state = ST_MISSING;
+ next_state = ST_MISSING;
cycles_in_state = 0;
}
- else if (num_undervolted_cells > 0) {
+ else if (current >= 0 && num_undervolted_cells > 0) {
// when transitioning to undervoltage, we assume we reached the bottom
// of usable capacity, so record it
// but only if we reached top charge once, or our counter will
// be off.
- if (cycles_in_state > 5) {
+ if (cycles_in_state > 2) {
if (reached_full_charge > 0) {
capacity_min_ampsecs = capacity_accu_ampsecs;
}
- state = ST_UNDERVOLTED;
+ next_state = ST_UNDERVOLTED;
cycles_in_state = 0;
}
}
@@ -860,7 +928,7 @@ int main(void)
if (cycles_in_state > 5) {
// when transitioning to fully charged, we assume that we're at max capacity
capacity_accu_ampsecs = capacity_max_ampsecs;
- state = ST_FULLY_CHARGED;
+ next_state = ST_FULLY_CHARGED;
reached_full_charge = 1;
cycles_in_state = 0;
}
@@ -868,14 +936,21 @@ int main(void)
else if (num_overvolted_cells > 0) {
if (cycles_in_state > 5) {
// some cool-off time
- state = ST_OVERVOLTED;
+ next_state = ST_OVERVOLTED;
+ cycles_in_state = 0;
+ }
+ }
+ else if (current < 0.05 && current >= 0 && !som_is_powered) {
+ // if not charging and the system is off, we can sleep regularly to save power
+ if (powersave_holdoff_cycles <= 0) {
+ next_state = ST_POWERSAVE;
cycles_in_state = 0;
}
}
}
else if (state == ST_UNDERVOLTED) {
- // TODO: issue alert -- switch off system if critical
reset_discharge_bits();
+ deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS);
if (cycles_in_state > 1) {
// TODO: find safe heuristic. here we turn off if half
@@ -884,7 +959,7 @@ int main(void)
turn_som_power_off();
}
- state = ST_CHARGE;
+ next_state = ST_CHARGE;
cycles_in_state = 0;
}
}
@@ -893,7 +968,7 @@ int main(void)
missing_reason = missing_bits;
// if cells were unplugged, we don't know the capacity anymore.
reached_full_charge = 0;
- state = ST_MISSING;
+ next_state = ST_MISSING;
cycles_in_state = 0;
} else {
discharge_overvolted_cells();
@@ -902,17 +977,18 @@ int main(void)
if (cycles_in_state > 1 && (num_overvolted_cells==0 || num_undervolted_cells>0)) {
reset_discharge_bits();
- state = ST_CHARGE;
+ next_state = ST_CHARGE;
cycles_in_state = 0;
}
}
}
else if (state == ST_MISSING) {
reset_discharge_bits();
+ deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS);
- if (cycles_in_state > 5) {
- if (num_missing_cells < 1) {
- state = ST_CHARGE;
+ if (cycles_in_state > 1) {
+ if (num_missing_cells == 0 || num_missing_cells == 8) {
+ next_state = ST_CHARGE;
cycles_in_state = 0;
}
}
@@ -923,19 +999,30 @@ int main(void)
if (cycles_in_state > 5) {
// if none of the cells are fully charged anymore, allow charging again
if (num_fully_charged_cells < 1) {
- state = ST_CHARGE;
+ next_state = ST_CHARGE;
cycles_in_state = 0;
}
else if (num_overvolted_cells > 0) {
- state = ST_OVERVOLTED;
+ next_state = ST_OVERVOLTED;
cycles_in_state = 0;
}
}
}
+ else if (state == ST_POWERSAVE) {
+ deep_sleep_seconds(POWERSAVE_SLEEP_SECONDS);
+ next_state = ST_CHARGE;
+ cycles_in_state = 0;
+ }
// handle keyboard commands
+ // this also resets powersave holdoff counter
handle_commands();
- cur_second = delayGetSecondsActive();
+
+ if (state == ST_POWERSAVE || state == ST_MISSING || state == ST_UNDERVOLTED) {
+ cur_second += POWERSAVE_SLEEP_SECONDS;
+ } else {
+ cur_second = delayGetSecondsActive();
+ }
if (last_second != cur_second) {
if (cur_second-last_second<10) {
@@ -947,7 +1034,14 @@ int main(void)
// report_to_spi();
}
last_second = cur_second;
+
+ if (powersave_holdoff_cycles > 0) {
+ powersave_holdoff_cycles--;
+ }
}
+
+ state = next_state;
+
}
}