2016-03-25 07:19:46 +01:00
|
|
|
/**
|
2016-03-24 19:01:20 +01:00
|
|
|
* Marlin 3D Printer Firmware
|
|
|
|
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
|
|
|
*
|
|
|
|
* Based on Sprinter and grbl.
|
|
|
|
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* 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. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-02-01 01:43:33 +01:00
|
|
|
#include "../inc/MarlinConfigPre.h"
|
2017-09-06 13:28:32 +02:00
|
|
|
|
2015-07-31 07:26:53 +02:00
|
|
|
#if ENABLED(ULTRA_LCD)
|
2017-09-06 13:28:32 +02:00
|
|
|
|
2018-05-15 06:54:50 +02:00
|
|
|
#include <stdarg.h>
|
|
|
|
|
2017-09-06 13:28:32 +02:00
|
|
|
#include "ultralcd.h"
|
2018-10-23 23:00:34 +02:00
|
|
|
#include "lcdprint.h"
|
2017-09-06 13:28:32 +02:00
|
|
|
|
|
|
|
#include "../sd/cardreader.h"
|
|
|
|
#include "../module/temperature.h"
|
|
|
|
#include "../module/planner.h"
|
|
|
|
#include "../module/stepper.h"
|
2017-09-08 05:33:16 +02:00
|
|
|
#include "../module/motion.h"
|
2017-09-08 22:35:25 +02:00
|
|
|
#include "../module/probe.h"
|
2017-09-09 07:18:43 +02:00
|
|
|
#include "../module/printcounter.h"
|
2017-09-08 05:33:16 +02:00
|
|
|
#include "../gcode/gcode.h"
|
|
|
|
#include "../gcode/queue.h"
|
2017-09-06 13:28:32 +02:00
|
|
|
#include "../module/configuration_store.h"
|
2018-09-17 08:06:22 +02:00
|
|
|
#include "../module/tool_change.h"
|
|
|
|
|
2017-09-06 13:28:32 +02:00
|
|
|
#include "../Marlin.h"
|
2012-11-06 12:06:41 +01:00
|
|
|
|
2018-01-04 12:06:34 +01:00
|
|
|
#if ENABLED(ADVANCED_PAUSE_FEATURE)
|
|
|
|
#include "../feature/pause.h"
|
|
|
|
#endif
|
|
|
|
|
2018-04-22 02:41:26 +02:00
|
|
|
#if ENABLED(POWER_LOSS_RECOVERY)
|
|
|
|
#include "../feature/power_loss_recovery.h"
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-10-23 23:00:34 +02:00
|
|
|
void menu_job_recovery();
|
|
|
|
#endif
|
2018-04-22 02:41:26 +02:00
|
|
|
#endif
|
|
|
|
|
2017-09-09 07:18:43 +02:00
|
|
|
#if ENABLED(PRINTCOUNTER) && ENABLED(LCD_INFO_MENU)
|
2017-09-06 13:28:32 +02:00
|
|
|
#include "../libs/duration_t.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLED(FILAMENT_LCD_DISPLAY)
|
|
|
|
#include "../feature/filwidth.h"
|
|
|
|
#endif
|
|
|
|
|
2017-05-01 03:49:00 +02:00
|
|
|
#if ENABLED(BLTOUCH)
|
2017-09-06 13:28:32 +02:00
|
|
|
#include "../module/endstops.h"
|
2017-05-01 03:49:00 +02:00
|
|
|
#endif
|
|
|
|
|
2017-09-06 13:28:32 +02:00
|
|
|
#if HAS_LEVELING
|
|
|
|
#include "../feature/bedlevel/bedlevel.h"
|
2017-06-13 01:26:49 +02:00
|
|
|
#endif
|
|
|
|
|
2017-09-14 22:33:07 +02:00
|
|
|
#if DISABLED(LCD_USE_I2C_BUZZER)
|
|
|
|
#include "../libs/buzzer.h"
|
|
|
|
#endif
|
|
|
|
|
2018-10-28 08:59:21 +01:00
|
|
|
// Buttons
|
|
|
|
volatile uint8_t buttons;
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
|
|
|
|
uint8_t lcd_sd_status;
|
|
|
|
#endif
|
|
|
|
|
2017-05-28 18:12:12 +02:00
|
|
|
#if ENABLED(STATUS_MESSAGE_SCROLLING)
|
2018-10-23 23:00:34 +02:00
|
|
|
uint8_t status_scroll_offset = 0;
|
2018-03-08 04:56:37 +01:00
|
|
|
#if LONG_FILENAME_LENGTH > CHARSIZE * 2 * (LCD_WIDTH)
|
|
|
|
#define MAX_MESSAGE_LENGTH LONG_FILENAME_LENGTH
|
|
|
|
#else
|
|
|
|
#define MAX_MESSAGE_LENGTH CHARSIZE * 2 * (LCD_WIDTH)
|
|
|
|
#endif
|
2018-02-09 06:03:47 +01:00
|
|
|
#else
|
|
|
|
#define MAX_MESSAGE_LENGTH CHARSIZE * (LCD_WIDTH)
|
2017-05-28 18:12:12 +02:00
|
|
|
#endif
|
2017-12-06 23:56:30 +01:00
|
|
|
|
2018-02-14 12:09:44 +01:00
|
|
|
char lcd_status_message[MAX_MESSAGE_LENGTH + 1];
|
2018-05-01 04:16:31 +02:00
|
|
|
uint8_t lcd_status_update_delay = 1, // First update one loop delayed
|
|
|
|
lcd_status_message_level; // Higher level blocks lower level
|
2018-02-09 06:03:47 +01:00
|
|
|
|
2018-05-01 04:16:31 +02:00
|
|
|
#if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
|
|
|
|
millis_t previous_lcd_status_ms = 0;
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU && ENABLED(SDSUPPORT) && ENABLED(SCROLL_LONG_FILENAMES)
|
2018-10-23 23:00:34 +02:00
|
|
|
uint8_t filename_scroll_pos, filename_scroll_max;
|
2017-10-19 05:15:33 +02:00
|
|
|
#endif
|
2011-12-12 19:34:37 +01:00
|
|
|
|
2017-10-15 09:15:19 +02:00
|
|
|
#if ENABLED(LCD_SET_PROGRESS_MANUALLY)
|
|
|
|
uint8_t progress_bar_percent;
|
|
|
|
#endif
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
millis_t next_button_update_ms;
|
|
|
|
|
|
|
|
#if HAS_GRAPHICAL_LCD
|
2018-05-23 09:37:09 +02:00
|
|
|
bool drawing_screen, first_page; // = false
|
2013-03-19 20:59:21 +01:00
|
|
|
#endif
|
2011-12-12 19:34:37 +01:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
// Encoder Handling
|
|
|
|
#if HAS_ENCODER_ACTION
|
|
|
|
uint32_t encoderPosition;
|
|
|
|
volatile int8_t encoderDiff; // Updated in lcd_buttons_update, added to encoderPosition every LCD update
|
|
|
|
#if ENABLED(ENCODER_RATE_MULTIPLIER)
|
|
|
|
bool encoderRateMultiplierEnabled;
|
|
|
|
#endif
|
|
|
|
#if ENABLED(REVERSE_MENU_DIRECTION)
|
|
|
|
int8_t encoderDirection = 1;
|
|
|
|
#endif
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if HAS_LCD_MENU
|
|
|
|
#include "menu/menu.h"
|
2017-12-30 11:08:26 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
screenFunc_t currentScreen = lcd_status_screen;
|
2018-05-01 04:16:31 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#if ENABLED(ENCODER_RATE_MULTIPLIER)
|
|
|
|
millis_t lastEncoderMovementMillis = 0;
|
|
|
|
#endif
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
bool lcd_clicked, wait_for_unclick;
|
2018-10-27 23:45:37 +02:00
|
|
|
float move_menu_scale;
|
2018-05-01 04:16:31 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
bool use_click() {
|
|
|
|
const bool click = lcd_clicked;
|
|
|
|
lcd_clicked = false;
|
|
|
|
return click;
|
|
|
|
}
|
2018-11-09 07:07:16 +01:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
constexpr bool lcd_clicked = false;
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif
|
2018-05-01 04:16:31 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
void lcd_init() {
|
2017-12-31 06:57:12 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd_implementation_init();
|
2015-09-11 11:18:42 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(NEWPANEL)
|
|
|
|
#if BUTTON_EXISTS(EN1)
|
|
|
|
SET_INPUT_PULLUP(BTN_EN1);
|
2016-06-30 11:24:29 +02:00
|
|
|
#endif
|
2018-10-23 23:00:34 +02:00
|
|
|
#if BUTTON_EXISTS(EN2)
|
|
|
|
SET_INPUT_PULLUP(BTN_EN2);
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(ENC)
|
|
|
|
SET_INPUT_PULLUP(BTN_ENC);
|
2018-01-04 12:06:34 +01:00
|
|
|
#endif
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(REPRAPWORLD_KEYPAD) && DISABLED(ADC_KEYPAD)
|
|
|
|
SET_OUTPUT(SHIFT_CLK);
|
|
|
|
OUT_WRITE(SHIFT_LD, HIGH);
|
|
|
|
SET_INPUT_PULLUP(SHIFT_OUT);
|
|
|
|
#endif
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if BUTTON_EXISTS(UP)
|
|
|
|
SET_INPUT(BTN_UP);
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(DWN)
|
|
|
|
SET_INPUT(BTN_DWN);
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(LFT)
|
|
|
|
SET_INPUT(BTN_LFT);
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(RT)
|
|
|
|
SET_INPUT(BTN_RT);
|
|
|
|
#endif
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#else // !NEWPANEL
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(SR_LCD_2W_NL) // Non latching 2 wire shift register
|
|
|
|
SET_OUTPUT(SR_DATA_PIN);
|
|
|
|
SET_OUTPUT(SR_CLK_PIN);
|
|
|
|
#elif defined(SHIFT_CLK)
|
|
|
|
SET_OUTPUT(SHIFT_CLK);
|
|
|
|
OUT_WRITE(SHIFT_LD, HIGH);
|
|
|
|
OUT_WRITE(SHIFT_EN, LOW);
|
|
|
|
SET_INPUT_PULLUP(SHIFT_OUT);
|
|
|
|
#endif // SR_LCD_2W_NL
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif // !NEWPANEL
|
2017-04-17 04:32:52 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
|
|
|
|
SET_INPUT_PULLUP(SD_DETECT_PIN);
|
|
|
|
lcd_sd_status = 2; // UNKNOWN
|
2015-03-28 04:29:05 +01:00
|
|
|
#endif
|
2017-04-17 04:32:52 +02:00
|
|
|
|
|
|
|
#if ENABLED(LCD_HAS_SLOW_BUTTONS)
|
2018-10-23 23:00:34 +02:00
|
|
|
slow_buttons = 0;
|
2017-10-08 01:16:41 +02:00
|
|
|
#endif
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd_buttons_update();
|
2016-04-03 01:09:56 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_ENCODER_ACTION
|
2018-10-23 23:00:34 +02:00
|
|
|
encoderDiff = 0;
|
2017-04-17 04:32:52 +02:00
|
|
|
#endif
|
2018-10-23 23:00:34 +02:00
|
|
|
}
|
2012-12-12 11:47:03 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
bool lcd_blink() {
|
|
|
|
static uint8_t blink = 0;
|
|
|
|
static millis_t next_blink_ms = 0;
|
|
|
|
millis_t ms = millis();
|
|
|
|
if (ELAPSED(ms, next_blink_ms)) {
|
|
|
|
blink ^= 0xFF;
|
|
|
|
next_blink_ms = ms + 1000 - (LCD_UPDATE_INTERVAL) / 2;
|
2017-12-27 05:12:05 +01:00
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
return blink != 0;
|
|
|
|
}
|
2017-12-27 05:12:05 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
////////////////////////////////////////////
|
|
|
|
///////////// Keypad Handling //////////////
|
|
|
|
////////////////////////////////////////////
|
2017-03-03 02:15:21 +01:00
|
|
|
|
2018-10-28 08:59:21 +01:00
|
|
|
#if ENABLED(REPRAPWORLD_KEYPAD)
|
|
|
|
volatile uint8_t buttons_reprapworld_keypad;
|
|
|
|
#endif
|
|
|
|
|
2018-11-01 22:13:33 +01:00
|
|
|
#if ENABLED(ADC_KEYPAD)
|
2017-03-03 02:15:21 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
inline bool handle_adc_keypad() {
|
|
|
|
#define ADC_MIN_KEY_DELAY 100
|
|
|
|
if (buttons_reprapworld_keypad) {
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_ENCODER_ACTION
|
|
|
|
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
|
|
|
|
if (encoderDirection == -1) { // side effect which signals we are inside a menu
|
|
|
|
#if HAS_LCD_MENU
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_DOWN)) encoderPosition -= ENCODER_STEPS_PER_MENU_ITEM;
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_UP)) encoderPosition += ENCODER_STEPS_PER_MENU_ITEM;
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_LEFT)) { menu_item_back::action(); lcd_quick_feedback(true); }
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_RIGHT)) { lcd_return_to_status(); lcd_quick_feedback(true); }
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_DOWN)) encoderPosition += ENCODER_PULSES_PER_STEP;
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_UP)) encoderPosition -= ENCODER_PULSES_PER_STEP;
|
|
|
|
else if (RRK(EN_REPRAPWORLD_KEYPAD_RIGHT)) encoderPosition = 0;
|
|
|
|
#endif
|
2018-10-23 23:00:34 +02:00
|
|
|
next_button_update_ms = millis() + ADC_MIN_KEY_DELAY;
|
|
|
|
return true;
|
2016-06-11 23:12:00 +02:00
|
|
|
}
|
Allow Edit menu to call fn after edit; Fix PID Ki and Kd display in menus; Actually use changed PID and Max Accel values
Add new 'callback' edit-menu types that call a function after the edit is done. Use this to display and edit Ki and Kd correctly (removing the scaling first and reapplying it after). Also use it to reset maximum stepwise acceleration rates, after updating mm/s^2 rates via menus. (Previously, changes did nothing to affect planner unless saved back to EEPROM, and the machine reset).
Add calls to updatePID() so that PID loop uses updated values whether set by gcode (it already did this), or by restoring defaults, or loading from EEPROM (it didn't do those last two). Similarly, update the maximum step/s^2 accel rates when the mm/s^2 values are changed - whether by menu edits, restore defaults, or EEPROM read.
Refactor the acceleration rate update logic, and the PID scaling logic, into new functions that can be called from wherever, including the callbacks.
Add menu items to allow the z jerk and e jerk to be viewed/edited in the Control->Motion menu, as per xy jerk.
Conflicts:
Marlin/language.h
2013-03-19 15:05:11 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
return false;
|
2016-12-13 06:56:05 +01:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#elif ENABLED(REPRAPWORLD_KEYPAD)
|
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_LCD_MENU
|
|
|
|
|
|
|
|
void lcd_move_x();
|
|
|
|
void lcd_move_y();
|
|
|
|
void lcd_move_z();
|
|
|
|
|
|
|
|
void _reprapworld_keypad_move(const AxisEnum axis, const int16_t dir) {
|
|
|
|
move_menu_scale = REPRAPWORLD_KEYPAD_MOVE_STEP;
|
|
|
|
encoderPosition = dir;
|
|
|
|
switch (axis) {
|
|
|
|
case X_AXIS: lcd_move_x(); break;
|
|
|
|
case Y_AXIS: lcd_move_y(); break;
|
|
|
|
case Z_AXIS: lcd_move_z();
|
|
|
|
default: break;
|
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
}
|
2018-11-09 07:07:16 +01:00
|
|
|
|
|
|
|
#endif
|
2017-05-29 19:19:02 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
inline void handle_reprapworld_keypad() {
|
2012-12-12 11:47:03 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
static uint8_t keypad_debounce = 0;
|
2016-03-09 12:52:05 +01:00
|
|
|
|
2018-11-03 04:29:32 +01:00
|
|
|
if (!RRK( EN_REPRAPWORLD_KEYPAD_F1 | EN_REPRAPWORLD_KEYPAD_F2
|
|
|
|
| EN_REPRAPWORLD_KEYPAD_F3 | EN_REPRAPWORLD_KEYPAD_DOWN
|
|
|
|
| EN_REPRAPWORLD_KEYPAD_RIGHT | EN_REPRAPWORLD_KEYPAD_MIDDLE
|
|
|
|
| EN_REPRAPWORLD_KEYPAD_UP | EN_REPRAPWORLD_KEYPAD_LEFT )
|
|
|
|
) {
|
2018-10-23 23:00:34 +02:00
|
|
|
if (keypad_debounce > 0) keypad_debounce--;
|
2016-06-11 23:12:00 +02:00
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
else if (!keypad_debounce) {
|
|
|
|
keypad_debounce = 2;
|
2016-04-03 06:12:18 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
const bool homed = all_axes_homed();
|
2017-08-16 09:27:50 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-10-23 23:00:34 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_MIDDLE)) lcd_goto_screen(menu_move);
|
|
|
|
|
|
|
|
#if DISABLED(DELTA) && Z_HOME_DIR == -1
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_F2)) _reprapworld_keypad_move(Z_AXIS, 1);
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif
|
2018-11-09 07:07:16 +01:00
|
|
|
|
|
|
|
if (homed) {
|
|
|
|
#if ENABLED(DELTA) || Z_HOME_DIR != -1
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_F2)) _reprapworld_keypad_move(Z_AXIS, 1);
|
|
|
|
#endif
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_F3)) _reprapworld_keypad_move(Z_AXIS, -1);
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_LEFT)) _reprapworld_keypad_move(X_AXIS, -1);
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_RIGHT)) _reprapworld_keypad_move(X_AXIS, 1);
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_DOWN)) _reprapworld_keypad_move(Y_AXIS, 1);
|
|
|
|
if (RRK(EN_REPRAPWORLD_KEYPAD_UP)) _reprapworld_keypad_move(Y_AXIS, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // HAS_LCD_MENU
|
|
|
|
|
|
|
|
if (!homed && RRK(EN_REPRAPWORLD_KEYPAD_F1)) enqueue_and_echo_commands_P(PSTR("G28"));
|
2018-03-06 06:05:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif // REPRAPWORLD_KEYPAD
|
2016-03-09 12:52:05 +01:00
|
|
|
|
2015-04-28 04:11:25 +02:00
|
|
|
/**
|
2018-10-23 23:00:34 +02:00
|
|
|
* Status Screen
|
2015-04-28 04:11:25 +02:00
|
|
|
*
|
|
|
|
* This is very display-dependent, so the lcd implementation draws this.
|
|
|
|
*/
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if ENABLED(LCD_PROGRESS_BAR)
|
|
|
|
millis_t progress_bar_ms = 0; // Start millis of the current progress bar cycle
|
|
|
|
#if PROGRESS_MSG_EXPIRE > 0
|
|
|
|
static millis_t expire_status_ms = 0;
|
|
|
|
void dontExpireStatus() { expire_status_ms = 0; }
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if LCD_INFO_SCREEN_STYLE == 0
|
|
|
|
void lcd_impl_status_screen_0();
|
|
|
|
#elif LCD_INFO_SCREEN_STYLE == 1
|
|
|
|
void lcd_impl_status_screen_1();
|
|
|
|
#endif
|
|
|
|
|
2016-11-24 03:43:01 +01:00
|
|
|
void lcd_status_screen() {
|
2016-06-11 23:12:00 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2016-06-11 23:12:00 +02:00
|
|
|
ENCODER_DIRECTION_NORMAL();
|
2017-04-17 04:32:52 +02:00
|
|
|
ENCODER_RATE_MULTIPLY(false);
|
2016-06-11 23:12:00 +02:00
|
|
|
#endif
|
2015-03-31 03:00:54 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if ENABLED(LCD_SET_PROGRESS_MANUALLY) && ENABLED(SDSUPPORT) && (ENABLED(LCD_PROGRESS_BAR) || HAS_GRAPHICAL_LCD)
|
2018-02-22 02:25:50 +01:00
|
|
|
// Progress bar % comes from SD when actively printing
|
2018-10-19 20:52:44 +02:00
|
|
|
if (IS_SD_PRINTING())
|
2018-02-22 02:25:50 +01:00
|
|
|
progress_bar_percent = card.percentDone();
|
|
|
|
#endif
|
|
|
|
|
2015-07-31 07:26:53 +02:00
|
|
|
#if ENABLED(LCD_PROGRESS_BAR)
|
2017-10-15 09:15:19 +02:00
|
|
|
|
|
|
|
//
|
|
|
|
// HD44780 implements the following message blinking and
|
|
|
|
// message expiration because Status Line and Progress Bar
|
|
|
|
// share the same line on the display.
|
|
|
|
//
|
|
|
|
|
2015-04-13 03:07:08 +02:00
|
|
|
millis_t ms = millis();
|
2017-10-15 09:15:19 +02:00
|
|
|
|
|
|
|
// If the message will blink rather than expire...
|
2015-08-03 21:30:37 +02:00
|
|
|
#if DISABLED(PROGRESS_MSG_ONCE)
|
2017-10-15 09:15:19 +02:00
|
|
|
if (ELAPSED(ms, progress_bar_ms + PROGRESS_BAR_MSG_TIME + PROGRESS_BAR_BAR_TIME))
|
2015-04-25 07:16:09 +02:00
|
|
|
progress_bar_ms = ms;
|
2014-12-28 07:26:14 +01:00
|
|
|
#endif
|
2017-10-15 09:15:19 +02:00
|
|
|
|
2014-12-28 07:26:14 +01:00
|
|
|
#if PROGRESS_MSG_EXPIRE > 0
|
2017-10-15 09:15:19 +02:00
|
|
|
|
2015-03-31 03:00:54 +02:00
|
|
|
// Handle message expire
|
2015-04-25 06:51:10 +02:00
|
|
|
if (expire_status_ms > 0) {
|
2017-10-15 09:15:19 +02:00
|
|
|
|
|
|
|
#if DISABLED(LCD_SET_PROGRESS_MANUALLY)
|
|
|
|
const uint8_t progress_bar_percent = card.percentDone();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Expire the message if a job is active and the bar has ticks
|
|
|
|
if (progress_bar_percent > 2 && !print_job_timer.isPaused()) {
|
|
|
|
if (ELAPSED(ms, expire_status_ms)) {
|
|
|
|
lcd_status_message[0] = '\0';
|
2015-09-05 02:40:12 +02:00
|
|
|
expire_status_ms = 0;
|
2014-12-28 07:26:14 +01:00
|
|
|
}
|
2017-10-15 09:15:19 +02:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Defer message expiration before bar appears
|
|
|
|
// and during any pause (not just SD)
|
|
|
|
expire_status_ms += LCD_UPDATE_INTERVAL;
|
|
|
|
}
|
2014-12-28 07:26:14 +01:00
|
|
|
}
|
2017-10-15 09:15:19 +02:00
|
|
|
|
|
|
|
#endif // PROGRESS_MSG_EXPIRE
|
|
|
|
|
2017-05-09 19:35:43 +02:00
|
|
|
#endif // LCD_PROGRESS_BAR
|
2014-12-28 07:26:14 +01:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2014-11-25 01:56:37 +01:00
|
|
|
|
2017-12-27 05:12:05 +01:00
|
|
|
if (use_click()) {
|
2016-12-27 21:38:36 +01:00
|
|
|
#if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
|
2016-08-07 01:24:26 +02:00
|
|
|
previous_lcd_status_ms = millis(); // get status message to show up for a while
|
|
|
|
#endif
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd_goto_screen(menu_main);
|
2018-10-30 22:34:45 +01:00
|
|
|
lcd_implementation_init(); // May revive the LCD if static electricity killed it
|
2017-05-30 02:09:38 +02:00
|
|
|
return;
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2013-06-07 00:49:25 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#endif // HAS_LCD_MENU
|
|
|
|
|
|
|
|
#if ENABLED(ULTIPANEL_FEEDMULTIPLY)
|
|
|
|
|
|
|
|
const int16_t new_frm = feedrate_percentage + (int32_t)encoderPosition;
|
|
|
|
// Dead zone at 100% feedrate
|
|
|
|
if ((feedrate_percentage < 100 && new_frm > 100) || (feedrate_percentage > 100 && new_frm < 100)) {
|
|
|
|
feedrate_percentage = 100;
|
|
|
|
encoderPosition = 0;
|
|
|
|
}
|
|
|
|
else if (feedrate_percentage == 100) {
|
|
|
|
if ((int32_t)encoderPosition > ENCODER_FEEDRATE_DEADZONE) {
|
|
|
|
feedrate_percentage += (int32_t)encoderPosition - (ENCODER_FEEDRATE_DEADZONE);
|
2016-04-05 23:06:52 +02:00
|
|
|
encoderPosition = 0;
|
2015-04-11 13:45:04 +02:00
|
|
|
}
|
2018-11-09 07:07:16 +01:00
|
|
|
else if ((int32_t)encoderPosition < -(ENCODER_FEEDRATE_DEADZONE)) {
|
|
|
|
feedrate_percentage += (int32_t)encoderPosition + ENCODER_FEEDRATE_DEADZONE;
|
2013-06-07 00:49:25 +02:00
|
|
|
encoderPosition = 0;
|
2015-04-11 13:45:04 +02:00
|
|
|
}
|
2018-11-09 07:07:16 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
feedrate_percentage = new_frm;
|
|
|
|
encoderPosition = 0;
|
|
|
|
}
|
2015-04-11 13:45:04 +02:00
|
|
|
|
2016-07-16 03:49:34 +02:00
|
|
|
feedrate_percentage = constrain(feedrate_percentage, 10, 999);
|
2013-06-07 00:49:25 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#endif // ULTIPANEL_FEEDMULTIPLY
|
2017-05-30 02:09:38 +02:00
|
|
|
|
2018-10-18 18:34:52 +02:00
|
|
|
#if LCD_INFO_SCREEN_STYLE == 0
|
|
|
|
lcd_impl_status_screen_0();
|
|
|
|
#elif LCD_INFO_SCREEN_STYLE == 1
|
|
|
|
lcd_impl_status_screen_1();
|
|
|
|
#endif
|
2011-12-12 19:34:37 +01:00
|
|
|
}
|
|
|
|
|
2018-04-22 07:16:30 +02:00
|
|
|
/**
|
|
|
|
* Reset the status message
|
|
|
|
*/
|
|
|
|
void lcd_reset_status() {
|
|
|
|
static const char paused[] PROGMEM = MSG_PRINT_PAUSED;
|
|
|
|
static const char printing[] PROGMEM = MSG_PRINTING;
|
|
|
|
static const char welcome[] PROGMEM = WELCOME_MSG;
|
2018-10-01 06:44:33 +02:00
|
|
|
PGM_P msg;
|
2018-04-22 07:16:30 +02:00
|
|
|
if (print_job_timer.isPaused())
|
|
|
|
msg = paused;
|
|
|
|
#if ENABLED(SDSUPPORT)
|
2018-11-07 03:52:39 +01:00
|
|
|
else if (IS_SD_PRINTING())
|
2018-07-13 08:44:27 +02:00
|
|
|
return lcd_setstatus(card.longest_filename(), true);
|
2018-04-22 07:16:30 +02:00
|
|
|
#endif
|
|
|
|
else if (print_job_timer.isRunning())
|
|
|
|
msg = printing;
|
|
|
|
else
|
|
|
|
msg = welcome;
|
|
|
|
|
|
|
|
lcd_setstatusPGM(msg, -1);
|
|
|
|
}
|
2017-05-30 03:12:10 +02:00
|
|
|
|
2018-10-01 06:44:33 +02:00
|
|
|
void kill_screen(PGM_P lcd_msg) {
|
2016-07-10 04:50:45 +02:00
|
|
|
lcd_init();
|
2017-05-30 03:12:10 +02:00
|
|
|
lcd_setalertstatusPGM(lcd_msg);
|
2017-11-03 11:58:20 +01:00
|
|
|
lcd_kill_screen();
|
2016-07-10 04:50:45 +02:00
|
|
|
}
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if HAS_BUZZER
|
|
|
|
void lcd_buzz(const long duration, const uint16_t freq) {
|
2017-03-20 12:10:31 +01:00
|
|
|
#if ENABLED(LCD_USE_I2C_BUZZER)
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd.buzz(duration, freq);
|
2017-03-20 12:10:31 +01:00
|
|
|
#elif PIN_EXISTS(BEEPER)
|
2018-10-23 23:00:34 +02:00
|
|
|
buzzer.tone(duration, freq);
|
2017-03-20 12:10:31 +01:00
|
|
|
#endif
|
2016-05-30 01:56:39 +02:00
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif
|
2011-12-12 19:34:37 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
void lcd_quick_feedback(const bool clear_buttons) {
|
2018-04-22 02:41:26 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd_refresh();
|
|
|
|
if (clear_buttons) buttons = 0;
|
|
|
|
next_button_update_ms = millis() + 500;
|
|
|
|
#else
|
|
|
|
UNUSED(clear_buttons);
|
|
|
|
#endif
|
2015-06-17 16:31:14 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
// Buzz and wait. The delay is needed for buttons to settle!
|
|
|
|
lcd_buzz(LCD_FEEDBACK_FREQUENCY_DURATION_MS, LCD_FEEDBACK_FREQUENCY_HZ);
|
2015-09-05 02:40:12 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(LCD_USE_I2C_BUZZER)
|
|
|
|
delay(10);
|
|
|
|
#elif PIN_EXISTS(BEEPER)
|
|
|
|
for (int8_t i = 5; i--;) { buzzer.tick(); delay(2); }
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
2015-09-05 02:40:12 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2016-06-11 23:12:00 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
extern bool no_reentry; // Flag to prevent recursion into menu handlers
|
2016-06-11 23:12:00 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
int8_t manual_move_axis = (int8_t)NO_AXIS;
|
|
|
|
millis_t manual_move_start_time = 0;
|
2016-06-11 23:12:00 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if IS_KINEMATIC
|
|
|
|
bool processing_manual_move = false;
|
|
|
|
float manual_move_offset = 0;
|
|
|
|
#endif
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if !IS_KINEMATIC || (IS_KINEMATIC && EXTRUDERS > 1)
|
|
|
|
int8_t manual_move_e_index = 0;
|
|
|
|
#else
|
|
|
|
constexpr int8_t manual_move_e_index = 0;
|
|
|
|
#endif
|
2011-12-12 19:34:37 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
/**
|
|
|
|
* If the most recent manual move hasn't been fed to the planner yet,
|
|
|
|
* and the planner can accept one, send a move immediately.
|
|
|
|
*/
|
|
|
|
void manage_manual_move() {
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
if (processing_manual_move) return;
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
if (manual_move_axis != (int8_t)NO_AXIS && ELAPSED(millis(), manual_move_start_time) && !planner.is_full()) {
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if IS_KINEMATIC
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
const float old_feedrate = feedrate_mm_s;
|
|
|
|
feedrate_mm_s = MMM_TO_MMS(manual_feedrate_mm_m[manual_move_axis]);
|
2015-12-13 02:57:36 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if EXTRUDERS > 1
|
|
|
|
const int8_t old_extruder = active_extruder;
|
|
|
|
if (manual_move_axis == E_AXIS) active_extruder = manual_move_e_index;
|
|
|
|
#endif
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
// Set movement on a single axis
|
|
|
|
set_destination_from_current();
|
|
|
|
destination[manual_move_axis] += manual_move_offset;
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
// Reset for the next move
|
|
|
|
manual_move_offset = 0;
|
|
|
|
manual_move_axis = (int8_t)NO_AXIS;
|
2013-10-30 11:45:32 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
// DELTA and SCARA machines use segmented moves, which could fill the planner during the call to
|
|
|
|
// move_to_destination. This will cause idle() to be called, which can then call this function while the
|
|
|
|
// previous invocation is being blocked. Modifications to manual_move_offset shouldn't be made while
|
|
|
|
// processing_manual_move is true or the planner will get out of sync.
|
|
|
|
processing_manual_move = true;
|
|
|
|
prepare_move_to_destination(); // will call set_current_from_destination()
|
|
|
|
processing_manual_move = false;
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
feedrate_mm_s = old_feedrate;
|
|
|
|
#if EXTRUDERS > 1
|
|
|
|
active_extruder = old_extruder;
|
|
|
|
#endif
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#else
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
planner.buffer_line(current_position, MMM_TO_MMS(manual_feedrate_mm_m[manual_move_axis]), manual_move_axis == E_AXIS ? manual_move_e_index : active_extruder);
|
|
|
|
manual_move_axis = (int8_t)NO_AXIS;
|
2011-12-12 19:34:37 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#endif
|
|
|
|
}
|
2016-03-29 04:33:04 +02:00
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#endif // HAS_LCD_MENU
|
2016-03-29 04:33:04 +02:00
|
|
|
|
2015-04-28 04:11:25 +02:00
|
|
|
/**
|
|
|
|
* Update the LCD, read encoder buttons, etc.
|
|
|
|
* - Read button states
|
|
|
|
* - Check the SD Card slot state
|
|
|
|
* - Act on RepRap World keypad input
|
|
|
|
* - Update the encoder position
|
|
|
|
* - Apply acceleration to the encoder position
|
2016-04-21 04:49:00 +02:00
|
|
|
* - Set lcdDrawUpdate = LCDVIEW_CALL_REDRAW_NOW on controller events
|
2015-04-28 04:11:25 +02:00
|
|
|
* - Reset the Info Screen timeout if there's any input
|
|
|
|
* - Update status indicators, if any
|
|
|
|
*
|
2016-04-03 01:09:56 +02:00
|
|
|
* Run the current LCD menu handler callback function:
|
2016-04-09 04:52:40 +02:00
|
|
|
* - Call the handler only if lcdDrawUpdate != LCDVIEW_NONE
|
|
|
|
* - Before calling the handler, LCDVIEW_CALL_NO_REDRAW => LCDVIEW_NONE
|
2016-04-03 01:09:56 +02:00
|
|
|
* - Call the menu handler. Menu handlers should do the following:
|
2016-04-21 04:49:00 +02:00
|
|
|
* - If a value changes, set lcdDrawUpdate to LCDVIEW_REDRAW_NOW and draw the value
|
|
|
|
* (Encoder events automatically set lcdDrawUpdate for you.)
|
2016-04-03 01:09:56 +02:00
|
|
|
* - if (lcdDrawUpdate) { redraw }
|
|
|
|
* - Before exiting the handler set lcdDrawUpdate to:
|
2016-04-09 04:52:40 +02:00
|
|
|
* - LCDVIEW_CLEAR_CALL_REDRAW to clear screen and set LCDVIEW_CALL_REDRAW_NEXT.
|
2017-05-25 22:13:57 +02:00
|
|
|
* - LCDVIEW_REDRAW_NOW to draw now (including remaining stripes).
|
|
|
|
* - LCDVIEW_CALL_REDRAW_NEXT to draw now and get LCDVIEW_REDRAW_NOW on the next loop.
|
|
|
|
* - LCDVIEW_CALL_NO_REDRAW to draw now and get LCDVIEW_NONE on the next loop.
|
2016-04-21 04:49:00 +02:00
|
|
|
* - NOTE: For graphical displays menu handlers may be called 2 or more times per loop,
|
|
|
|
* so don't change lcdDrawUpdate without considering this.
|
2016-04-03 01:09:56 +02:00
|
|
|
*
|
|
|
|
* After the menu handler callback runs (or not):
|
2016-04-09 04:52:40 +02:00
|
|
|
* - Clear the LCD if lcdDrawUpdate == LCDVIEW_CLEAR_CALL_REDRAW
|
2016-04-21 21:40:22 +02:00
|
|
|
* - Update lcdDrawUpdate for the next loop (i.e., move one state down, usually)
|
2016-04-03 01:09:56 +02:00
|
|
|
*
|
2018-10-23 23:00:34 +02:00
|
|
|
* This function is only called from the main thread.
|
2015-04-28 04:11:25 +02:00
|
|
|
*/
|
2018-10-23 23:00:34 +02:00
|
|
|
|
|
|
|
LCDViewAction lcdDrawUpdate = LCDVIEW_CLEAR_CALL_REDRAW;
|
|
|
|
|
|
|
|
#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(G26_MESH_VALIDATION)
|
|
|
|
bool lcd_external_control; // = false
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLED(LCD_HAS_SLOW_BUTTONS)
|
|
|
|
volatile uint8_t slow_buttons;
|
|
|
|
#endif
|
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
bool lcd_detected() {
|
|
|
|
return
|
|
|
|
#if (ENABLED(LCD_I2C_TYPE_MCP23017) || ENABLED(LCD_I2C_TYPE_MCP23008)) && defined(DETECT_DEVICE)
|
|
|
|
lcd.LcdDetected() == 1
|
|
|
|
#else
|
|
|
|
true
|
|
|
|
#endif
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2015-02-22 02:38:56 +01:00
|
|
|
void lcd_update() {
|
2016-06-11 23:12:00 +02:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
static uint16_t max_display_update_time = 0;
|
|
|
|
static millis_t next_lcd_update_ms;
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-11-05 01:06:00 +01:00
|
|
|
|
|
|
|
#if LCD_TIMEOUT_TO_STATUS
|
|
|
|
static millis_t return_to_status_ms = 0;
|
|
|
|
#endif
|
2017-11-21 07:18:46 +01:00
|
|
|
|
|
|
|
// Handle any queued Move Axis motion
|
2016-06-10 23:36:57 +02:00
|
|
|
manage_manual_move();
|
2015-02-16 13:53:58 +01:00
|
|
|
|
2018-11-01 21:05:59 +01:00
|
|
|
// Update button states for LCD_CLICKED(), etc.
|
2017-11-21 07:18:46 +01:00
|
|
|
// After state changes the next button update
|
|
|
|
// may be delayed 300-500ms.
|
2016-10-26 08:42:52 +02:00
|
|
|
lcd_buttons_update();
|
|
|
|
|
2017-03-18 16:15:54 +01:00
|
|
|
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
2017-11-21 07:18:46 +01:00
|
|
|
// Don't run the debouncer if UBL owns the display
|
2017-11-24 00:59:43 +01:00
|
|
|
#define UBL_CONDITION !lcd_external_control
|
2017-03-18 16:15:54 +01:00
|
|
|
#else
|
2017-11-21 07:18:46 +01:00
|
|
|
#define UBL_CONDITION true
|
2017-03-18 16:15:54 +01:00
|
|
|
#endif
|
|
|
|
|
2016-10-26 08:42:52 +02:00
|
|
|
// If the action button is pressed...
|
2018-11-01 21:05:59 +01:00
|
|
|
if (UBL_CONDITION && LCD_CLICKED()) {
|
2016-10-26 08:42:52 +02:00
|
|
|
if (!wait_for_unclick) { // If not waiting for a debounce release:
|
|
|
|
wait_for_unclick = true; // Set debounce flag to ignore continous clicks
|
2018-01-04 12:06:34 +01:00
|
|
|
lcd_clicked = !wait_for_user && !no_reentry; // Keep the click if not waiting for a user-click
|
2016-10-26 08:42:52 +02:00
|
|
|
wait_for_user = false; // Any click clears wait for user
|
2018-01-16 18:08:00 +01:00
|
|
|
lcd_quick_feedback(true); // Always make a click sound
|
2016-10-26 08:42:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else wait_for_unclick = false;
|
2018-02-27 05:52:57 +01:00
|
|
|
|
|
|
|
#if BUTTON_EXISTS(BACK)
|
|
|
|
if (LCD_BACK_CLICKED) {
|
2018-03-01 03:21:33 +01:00
|
|
|
lcd_quick_feedback(true);
|
2018-02-27 05:52:57 +01:00
|
|
|
lcd_goto_previous_menu();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#endif // HAS_LCD_MENU
|
2013-12-08 21:34:56 +01:00
|
|
|
|
2015-09-05 02:40:12 +02:00
|
|
|
#if ENABLED(SDSUPPORT) && PIN_EXISTS(SD_DETECT)
|
|
|
|
|
2018-10-19 20:52:44 +02:00
|
|
|
const uint8_t sd_status = (uint8_t)IS_SD_INSERTED();
|
2015-09-12 06:53:07 +02:00
|
|
|
if (sd_status != lcd_sd_status && lcd_detected()) {
|
2013-12-08 21:34:56 +01:00
|
|
|
|
2018-05-01 04:09:50 +02:00
|
|
|
uint8_t old_sd_status = lcd_sd_status; // prevent re-entry to this block!
|
2018-03-19 00:57:54 +01:00
|
|
|
lcd_sd_status = sd_status;
|
|
|
|
|
2015-09-14 01:43:14 +02:00
|
|
|
if (sd_status) {
|
2018-05-01 08:01:02 +02:00
|
|
|
safe_delay(500); // Some boards need a delay to get settled
|
2015-02-22 02:38:56 +01:00
|
|
|
card.initsd();
|
2018-05-01 08:01:02 +02:00
|
|
|
if (old_sd_status == 2)
|
|
|
|
card.beginautostart(); // Initial boot
|
|
|
|
else
|
|
|
|
LCD_MESSAGEPGM(MSG_SD_INSERTED);
|
2015-02-22 02:38:56 +01:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
card.release();
|
2018-03-19 00:57:54 +01:00
|
|
|
if (old_sd_status != 2) LCD_MESSAGEPGM(MSG_SD_REMOVED);
|
2015-02-22 02:38:56 +01:00
|
|
|
}
|
2015-09-14 01:43:14 +02:00
|
|
|
|
2018-04-22 07:16:30 +02:00
|
|
|
lcd_refresh();
|
2018-10-30 22:34:45 +01:00
|
|
|
lcd_implementation_init(); // May revive the LCD if static electricity killed it
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2015-09-05 02:40:12 +02:00
|
|
|
|
2017-05-09 19:35:43 +02:00
|
|
|
#endif // SDSUPPORT && SD_DETECT_PIN
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2018-04-22 02:41:26 +02:00
|
|
|
#if ENABLED(POWER_LOSS_RECOVERY)
|
|
|
|
if (job_recovery_commands_count && job_recovery_phase == JOB_RECOVERY_IDLE) {
|
2018-10-23 23:00:34 +02:00
|
|
|
lcd_goto_screen(menu_job_recovery);
|
2018-04-22 02:41:26 +02:00
|
|
|
job_recovery_phase = JOB_RECOVERY_MAYBE; // Waiting for a response
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-04-17 04:32:52 +02:00
|
|
|
const millis_t ms = millis();
|
2016-12-12 14:35:02 +01:00
|
|
|
if (ELAPSED(ms, next_lcd_update_ms)
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_GRAPHICAL_LCD
|
2016-12-12 14:35:02 +01:00
|
|
|
|| drawing_screen
|
|
|
|
#endif
|
2017-12-27 05:12:05 +01:00
|
|
|
) {
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2016-04-09 04:52:40 +02:00
|
|
|
next_lcd_update_ms = ms + LCD_UPDATE_INTERVAL;
|
|
|
|
|
|
|
|
#if ENABLED(LCD_HAS_STATUS_INDICATORS)
|
|
|
|
lcd_implementation_update_indicators();
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2016-08-21 01:12:57 +02:00
|
|
|
#if ENABLED(LCD_HAS_SLOW_BUTTONS)
|
|
|
|
slow_buttons = lcd_implementation_read_slow_buttons(); // buttons which take too long to read in interrupt context
|
|
|
|
#endif
|
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#endif // HAS_LCD_MENU
|
|
|
|
|
|
|
|
#if HAS_ENCODER_ACTION
|
|
|
|
|
2017-06-10 07:12:18 +02:00
|
|
|
#if ENABLED(ADC_KEYPAD)
|
|
|
|
|
2018-11-05 01:06:00 +01:00
|
|
|
if (handle_adc_keypad()) {
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_LCD_MENU && LCD_TIMEOUT_TO_STATUS
|
2018-11-05 01:06:00 +01:00
|
|
|
return_to_status_ms = ms + LCD_TIMEOUT_TO_STATUS;
|
|
|
|
#endif
|
|
|
|
}
|
2017-06-10 07:12:18 +02:00
|
|
|
|
|
|
|
#elif ENABLED(REPRAPWORLD_KEYPAD)
|
|
|
|
|
2016-12-13 06:51:46 +01:00
|
|
|
handle_reprapworld_keypad();
|
2017-06-10 07:12:18 +02:00
|
|
|
|
2016-12-13 06:51:46 +01:00
|
|
|
#endif
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-05-13 08:10:34 +02:00
|
|
|
const bool encoderPastThreshold = (ABS(encoderDiff) >= ENCODER_PULSES_PER_STEP);
|
2016-10-26 08:42:52 +02:00
|
|
|
if (encoderPastThreshold || lcd_clicked) {
|
2015-02-22 02:38:56 +01:00
|
|
|
if (encoderPastThreshold) {
|
2015-02-22 05:18:49 +01:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_LCD_MENU && ENABLED(ENCODER_RATE_MULTIPLIER)
|
|
|
|
|
|
|
|
int32_t encoderMultiplier = 1;
|
2015-02-22 05:18:49 +01:00
|
|
|
|
|
|
|
if (encoderRateMultiplierEnabled) {
|
2018-05-13 08:10:34 +02:00
|
|
|
int32_t encoderMovementSteps = ABS(encoderDiff) / ENCODER_PULSES_PER_STEP;
|
2015-02-22 05:18:49 +01:00
|
|
|
|
2017-06-10 07:12:18 +02:00
|
|
|
if (lastEncoderMovementMillis) {
|
2017-06-13 01:26:49 +02:00
|
|
|
// Note that the rate is always calculated between two passes through the
|
2015-02-22 05:18:49 +01:00
|
|
|
// loop and that the abs of the encoderDiff value is tracked.
|
2018-07-01 22:20:28 +02:00
|
|
|
float encoderStepRate = float(encoderMovementSteps) / float(ms - lastEncoderMovementMillis) * 1000;
|
2015-02-22 05:18:49 +01:00
|
|
|
|
|
|
|
if (encoderStepRate >= ENCODER_100X_STEPS_PER_SEC) encoderMultiplier = 100;
|
|
|
|
else if (encoderStepRate >= ENCODER_10X_STEPS_PER_SEC) encoderMultiplier = 10;
|
|
|
|
|
2015-07-31 07:26:53 +02:00
|
|
|
#if ENABLED(ENCODER_RATE_MULTIPLIER_DEBUG)
|
2017-06-09 17:51:23 +02:00
|
|
|
SERIAL_ECHO_START();
|
2016-06-28 01:29:35 +02:00
|
|
|
SERIAL_ECHOPAIR("Enc Step Rate: ", encoderStepRate);
|
|
|
|
SERIAL_ECHOPAIR(" Multiplier: ", encoderMultiplier);
|
|
|
|
SERIAL_ECHOPAIR(" ENCODER_10X_STEPS_PER_SEC: ", ENCODER_10X_STEPS_PER_SEC);
|
|
|
|
SERIAL_ECHOPAIR(" ENCODER_100X_STEPS_PER_SEC: ", ENCODER_100X_STEPS_PER_SEC);
|
2017-06-09 17:51:23 +02:00
|
|
|
SERIAL_EOL();
|
2018-11-05 08:23:23 +01:00
|
|
|
#endif
|
2015-02-22 05:18:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
lastEncoderMovementMillis = ms;
|
2015-03-31 01:39:47 +02:00
|
|
|
} // encoderRateMultiplierEnabled
|
2018-11-09 07:07:16 +01:00
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
constexpr int32_t encoderMultiplier = 1;
|
|
|
|
|
2017-05-09 19:35:43 +02:00
|
|
|
#endif // ENCODER_RATE_MULTIPLIER
|
2015-02-22 05:18:49 +01:00
|
|
|
|
|
|
|
encoderPosition += (encoderDiff * encoderMultiplier) / ENCODER_PULSES_PER_STEP;
|
2015-02-22 02:38:56 +01:00
|
|
|
encoderDiff = 0;
|
2012-08-22 00:46:10 +02:00
|
|
|
}
|
2018-11-09 07:07:16 +01:00
|
|
|
#if HAS_LCD_MENU && LCD_TIMEOUT_TO_STATUS
|
2018-11-05 01:06:00 +01:00
|
|
|
return_to_status_ms = ms + LCD_TIMEOUT_TO_STATUS;
|
|
|
|
#endif
|
2017-05-25 22:13:57 +02:00
|
|
|
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
|
2015-02-22 02:38:56 +01:00
|
|
|
}
|
2018-10-23 23:00:34 +02:00
|
|
|
|
2018-11-09 07:07:16 +01:00
|
|
|
#endif
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
// This runs every ~100ms when idling often enough.
|
|
|
|
// Instead of tracking changes just redraw the Status Screen once per second.
|
2016-12-12 14:35:02 +01:00
|
|
|
if (
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2016-11-19 08:09:32 +01:00
|
|
|
currentScreen == lcd_status_screen &&
|
2016-08-14 09:29:46 +02:00
|
|
|
#endif
|
2016-12-12 14:35:02 +01:00
|
|
|
!lcd_status_update_delay--
|
2016-11-19 08:09:32 +01:00
|
|
|
) {
|
2016-12-12 14:35:02 +01:00
|
|
|
lcd_status_update_delay = 9
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_GRAPHICAL_LCD
|
2016-12-14 13:20:33 +01:00
|
|
|
+ 3
|
|
|
|
#endif
|
2016-12-12 14:35:02 +01:00
|
|
|
;
|
|
|
|
max_display_update_time--;
|
|
|
|
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
|
|
|
|
}
|
2015-04-13 03:07:08 +02:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU && ENABLED(SCROLL_LONG_FILENAMES)
|
2017-10-19 05:15:33 +02:00
|
|
|
// If scrolling of long file names is enabled and we are in the sd card menu,
|
|
|
|
// cause a refresh to occur until all the text has scrolled into view.
|
2018-10-23 23:00:34 +02:00
|
|
|
if (currentScreen == menu_sdcard && filename_scroll_pos < filename_scroll_max && !lcd_status_update_delay--) {
|
2017-10-19 05:15:33 +02:00
|
|
|
lcd_status_update_delay = 6;
|
|
|
|
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
|
|
|
|
filename_scroll_pos++;
|
2018-11-05 01:06:00 +01:00
|
|
|
#if LCD_TIMEOUT_TO_STATUS
|
|
|
|
return_to_status_ms = ms + LCD_TIMEOUT_TO_STATUS;
|
|
|
|
#endif
|
2017-10-19 05:15:33 +02:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-12-15 12:50:22 +01:00
|
|
|
// then we want to use 1/2 of the time only.
|
|
|
|
uint16_t bbr2 = planner.block_buffer_runtime() >> 1;
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_GRAPHICAL_LCD
|
2018-10-23 23:00:34 +02:00
|
|
|
const bool &is_drawing = drawing_screen;
|
2017-10-30 01:44:49 +01:00
|
|
|
#else
|
2018-10-23 23:00:34 +02:00
|
|
|
constexpr bool is_drawing = false;
|
2017-10-30 01:44:49 +01:00
|
|
|
#endif
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
if ((lcdDrawUpdate || is_drawing) && (!bbr2 || bbr2 > max_display_update_time)) {
|
2017-11-08 05:26:33 +01:00
|
|
|
|
2017-12-27 05:12:05 +01:00
|
|
|
// Change state of drawing flag between screen updates
|
2018-10-23 23:00:34 +02:00
|
|
|
if (!is_drawing) switch (lcdDrawUpdate) {
|
2017-11-08 05:26:33 +01:00
|
|
|
case LCDVIEW_CALL_NO_REDRAW:
|
|
|
|
lcdDrawUpdate = LCDVIEW_NONE;
|
|
|
|
break;
|
2017-12-27 05:12:05 +01:00
|
|
|
case LCDVIEW_CLEAR_CALL_REDRAW:
|
|
|
|
case LCDVIEW_CALL_REDRAW_NEXT:
|
2017-11-08 05:26:33 +01:00
|
|
|
lcdDrawUpdate = LCDVIEW_REDRAW_NOW;
|
|
|
|
case LCDVIEW_REDRAW_NOW: // set above, or by a handler through LCDVIEW_CALL_REDRAW_NEXT
|
|
|
|
case LCDVIEW_NONE:
|
|
|
|
break;
|
|
|
|
} // switch
|
2017-06-10 07:12:18 +02:00
|
|
|
|
|
|
|
#if ENABLED(ADC_KEYPAD)
|
|
|
|
buttons_reprapworld_keypad = 0;
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2017-12-27 05:12:05 +01:00
|
|
|
#define CURRENTSCREEN() (*currentScreen)()
|
2016-12-12 14:35:02 +01:00
|
|
|
#else
|
|
|
|
#define CURRENTSCREEN() lcd_status_screen()
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_GRAPHICAL_LCD
|
2018-02-14 22:43:54 +01:00
|
|
|
#if ENABLED(LIGHTWEIGHT_UI)
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2018-02-14 22:43:54 +01:00
|
|
|
const bool in_status = currentScreen == lcd_status_screen;
|
|
|
|
#else
|
|
|
|
constexpr bool in_status = true;
|
|
|
|
#endif
|
|
|
|
const bool do_u8g_loop = !in_status;
|
|
|
|
lcd_in_status(in_status);
|
|
|
|
if (in_status) lcd_status_screen();
|
|
|
|
#else
|
|
|
|
constexpr bool do_u8g_loop = true;
|
|
|
|
#endif
|
|
|
|
if (do_u8g_loop) {
|
|
|
|
if (!drawing_screen) { // If not already drawing pages
|
|
|
|
u8g.firstPage(); // Start the first page
|
2018-03-06 06:05:15 +01:00
|
|
|
drawing_screen = first_page = true; // Flag as drawing pages
|
2018-02-14 22:43:54 +01:00
|
|
|
}
|
|
|
|
lcd_setFont(FONT_MENU); // Setup font for every page draw
|
|
|
|
u8g.setColorIndex(1); // And reset the color
|
|
|
|
CURRENTSCREEN(); // Draw and process the current screen
|
2018-03-06 06:05:15 +01:00
|
|
|
first_page = false;
|
2018-02-14 22:43:54 +01:00
|
|
|
|
|
|
|
// The screen handler can clear drawing_screen for an action that changes the screen.
|
|
|
|
// If still drawing and there's another page, update max-time and return now.
|
|
|
|
// The nextPage will already be set up on the next call.
|
|
|
|
if (drawing_screen && (drawing_screen = u8g.nextPage())) {
|
|
|
|
NOLESS(max_display_update_time, millis() - ms);
|
|
|
|
return;
|
|
|
|
}
|
2016-12-12 14:35:02 +01:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
CURRENTSCREEN();
|
|
|
|
#endif
|
2017-11-08 05:26:33 +01:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2017-12-27 05:12:05 +01:00
|
|
|
lcd_clicked = false;
|
|
|
|
#endif
|
|
|
|
|
2017-11-08 05:26:33 +01:00
|
|
|
// Keeping track of the longest time for an individual LCD update.
|
|
|
|
// Used to do screen throttling when the planner starts to fill up.
|
2016-12-14 13:20:33 +01:00
|
|
|
NOLESS(max_display_update_time, millis() - ms);
|
2016-12-12 14:35:02 +01:00
|
|
|
}
|
|
|
|
|
2018-11-05 01:06:00 +01:00
|
|
|
#if HAS_LCD_MENU && LCD_TIMEOUT_TO_STATUS
|
2016-12-12 14:35:02 +01:00
|
|
|
// Return to Status Screen after a timeout
|
|
|
|
if (currentScreen == lcd_status_screen || defer_return_to_status)
|
|
|
|
return_to_status_ms = ms + LCD_TIMEOUT_TO_STATUS;
|
|
|
|
else if (ELAPSED(ms, return_to_status_ms))
|
|
|
|
lcd_return_to_status();
|
2018-11-05 01:06:00 +01:00
|
|
|
#endif
|
2016-12-12 14:35:02 +01:00
|
|
|
|
2017-12-27 05:12:05 +01:00
|
|
|
// Change state of drawing flag between screen updates
|
2018-10-23 23:00:34 +02:00
|
|
|
if (!is_drawing) switch (lcdDrawUpdate) {
|
2017-11-08 05:26:33 +01:00
|
|
|
case LCDVIEW_CLEAR_CALL_REDRAW:
|
2017-12-27 05:12:05 +01:00
|
|
|
lcd_implementation_clear(); break;
|
2017-11-08 05:26:33 +01:00
|
|
|
case LCDVIEW_REDRAW_NOW:
|
|
|
|
lcdDrawUpdate = LCDVIEW_NONE;
|
|
|
|
case LCDVIEW_NONE:
|
2017-12-27 05:12:05 +01:00
|
|
|
case LCDVIEW_CALL_REDRAW_NEXT:
|
|
|
|
case LCDVIEW_CALL_NO_REDRAW:
|
|
|
|
default: break;
|
2017-11-08 05:26:33 +01:00
|
|
|
} // switch
|
|
|
|
|
Distribute GLCD screen updates in time
Currently we draw and send the screens for a graphical LCD all at once.
We draw in two or four parts but draw them directly behind each other.
For the tested status screen this takes 59-62ms in a single block.
During this time nothing else (except the interrupts) can be done.
When printing a sequence of very short moves the buffer drains - sometimes until it's empty.
This PR splits the screen update into parts.
Currently we have 10 time slots. During the first one the complete screen is drawn. (60,0,0,0,0,0,0,0,0,0,0)
Here i introduce pauses for doing other things. (30,30,0,0,0,0,0,0) or (15,15,15,15,0,0,0,0,0,0)
Drawing in consecutive time slots prevents from lagging too much. Even with a 4 stripe display all the drawing is done after 400ms.
Previous experiments with a even better distribution of the time slots like
(30,0,0,0,0,30,0,0,0,0) and (15,0,15,0,15,0,15,0,0,0) did not feel good when using the menu, because of too much lag.
Because of the previous PRs to speed up the display updates and especially reducing the difference between drawing 2 or 4 stripes,
it now makes sense for the REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER to go from 2 to 4 stripes. This costs about 1-2ms per complete
screen update, but is payed back by having partial updates lasting only the half time and two additional brakes. Also ~256 byte of
framebuffer are saved in RAM.
13:45:59.213 : echo: #:17 >:13 s:30; #:16 >:13 s:29; S#:33 S>:26 S:59
13:46:00.213 : echo: #:16 >:14 s:30; #:17 >:13 s:30; S#:33 S>:27 S:60
13:46:01.215 : echo: #:17 >:13 s:30; #:16 >:13 s:29; S#:33 S>:26 S:59
13:46:02.215 : echo: #:16 >:13 s:29; #:16 >:14 s:30; S#:32 S>:27 S:59
13:46:03.214 : echo: #:17 >:13 s:30; #:17 >:13 s:30; S#:34 S>:26 S:60
13:46:04.214 : echo: #:16 >:13 s:29; #:16 >:14 s:30; S#:32 S>:27 S:59
13:46:05.212 : echo: #:16 >:14 s:30; #:17 >:13 s:30; S#:33 S>:27 S:60
13:46:06.212 : echo: #:17 >:13 s:30; #:16 >:13 s:29; S#:33 S>:26 S:59
03:30:36.779 : echo: #:8 >:7 s:15; #:10 >:7 s:17; #:8 >:6 s:14; #:8 >:7 s:15; S#:34 S>:27 S:61
03:30:37.778 : echo: #:8 >:6 s:14; #:10 >:7 s:17; #:9 >:7 s:16; #:8 >:6 s:14; S#:35 S>:26 S:61
03:30:38.778 : echo: #:8 >:6 s:14; #:11 >:7 s:18; #:8 >:6 s:14; #:8 >:7 s:15; S#:35 S>:26 S:61
03:30:39.777 : echo: #:8 >:6 s:14; #:10 >:7 s:17; #:8 >:8 s:16; #:8 >:6 s:14; S#:34 S>:27 S:61
03:30:40.780 : echo: #:8 >:6 s:14; #:11 >:7 s:18; #:8 >:6 s:14; #:8 >:6 s:14; S#:35 S>:25 S:60
03:30:41.780 : echo: #:9 >:6 s:15; #:10 >:7 s:17; #:8 >:6 s:14; #:9 >:6 s:15; S#:36 S>:25 S:61
03:30:42.779 : echo: #:8 >:6 s:14; #:10 >:8 s:18; #:8 >:6 s:14; #:8 >:6 s:14; S#:34 S>:26 S:60
03:30:43.778 : echo: #:9 >:6 s:15; #:10 >:7 s:17; #:8 >:7 s:15; #:9 >:6 s:15; S#:36 S>:26 S:62
#: draw a stripe
>: transfer a stripe
s: sum of of draw and transfer for one stripe
S#: sum of draws for a complete screen
S>: sum of transfers for a complete screen
S: time to draw and transfer a complete screen
2016-11-24 21:17:25 +01:00
|
|
|
} // ELAPSED(ms, next_lcd_update_ms)
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2012-08-22 00:46:10 +02:00
|
|
|
|
2017-06-16 03:48:07 +02:00
|
|
|
void lcd_finishstatus(const bool persist=false) {
|
2017-06-01 06:09:44 +02:00
|
|
|
|
2016-04-02 23:28:17 +02:00
|
|
|
#if !(ENABLED(LCD_PROGRESS_BAR) && (PROGRESS_MSG_EXPIRE > 0))
|
|
|
|
UNUSED(persist);
|
|
|
|
#endif
|
|
|
|
|
2015-07-31 07:26:53 +02:00
|
|
|
#if ENABLED(LCD_PROGRESS_BAR)
|
2015-04-25 07:16:09 +02:00
|
|
|
progress_bar_ms = millis();
|
2014-12-28 07:26:14 +01:00
|
|
|
#if PROGRESS_MSG_EXPIRE > 0
|
2015-04-25 07:16:09 +02:00
|
|
|
expire_status_ms = persist ? 0 : progress_bar_ms + PROGRESS_MSG_EXPIRE;
|
2014-12-28 07:26:14 +01:00
|
|
|
#endif
|
|
|
|
#endif
|
2018-10-30 22:34:45 +01:00
|
|
|
|
2018-04-22 07:16:30 +02:00
|
|
|
lcd_refresh();
|
2014-12-28 07:26:14 +01:00
|
|
|
|
2016-12-27 21:38:36 +01:00
|
|
|
#if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
|
2015-04-13 03:07:08 +02:00
|
|
|
previous_lcd_status_ms = millis(); //get status message to show up for a while
|
2014-12-28 07:26:14 +01:00
|
|
|
#endif
|
2017-05-28 18:12:12 +02:00
|
|
|
|
|
|
|
#if ENABLED(STATUS_MESSAGE_SCROLLING)
|
2018-05-26 06:32:37 +02:00
|
|
|
status_scroll_offset = 0;
|
2017-05-28 18:12:12 +02:00
|
|
|
#endif
|
2014-12-28 07:26:14 +01:00
|
|
|
}
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2015-04-25 05:13:01 +02:00
|
|
|
bool lcd_hasstatus() { return (lcd_status_message[0] != '\0'); }
|
|
|
|
|
2017-04-02 07:45:39 +02:00
|
|
|
void lcd_setstatus(const char * const message, const bool persist) {
|
2015-02-22 02:38:56 +01:00
|
|
|
if (lcd_status_message_level > 0) return;
|
2018-05-26 06:32:37 +02:00
|
|
|
|
|
|
|
// Here we have a problem. The message is encoded in UTF8, so
|
|
|
|
// arbitrarily cutting it will be a problem. We MUST be sure
|
|
|
|
// that there is no cutting in the middle of a multibyte character!
|
|
|
|
|
|
|
|
// Get a pointer to the null terminator
|
|
|
|
const char* pend = message + strlen(message);
|
|
|
|
|
|
|
|
// If length of supplied UTF8 string is greater than
|
|
|
|
// our buffer size, start cutting whole UTF8 chars
|
|
|
|
while ((pend - message) > MAX_MESSAGE_LENGTH) {
|
|
|
|
--pend;
|
|
|
|
while (!START_OF_UTF8_CHAR(*pend)) --pend;
|
|
|
|
};
|
|
|
|
|
|
|
|
// At this point, we have the proper cut point. Use it
|
|
|
|
uint8_t maxLen = pend - message;
|
|
|
|
strncpy(lcd_status_message, message, maxLen);
|
|
|
|
lcd_status_message[maxLen] = '\0';
|
|
|
|
|
2015-03-31 03:00:54 +02:00
|
|
|
lcd_finishstatus(persist);
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-01 06:44:33 +02:00
|
|
|
void lcd_setstatusPGM(PGM_P const message, int8_t level) {
|
2017-05-29 18:51:29 +02:00
|
|
|
if (level < 0) level = lcd_status_message_level = 0;
|
2016-07-17 20:33:01 +02:00
|
|
|
if (level < lcd_status_message_level) return;
|
|
|
|
lcd_status_message_level = level;
|
2018-05-26 06:32:37 +02:00
|
|
|
|
|
|
|
// Here we have a problem. The message is encoded in UTF8, so
|
|
|
|
// arbitrarily cutting it will be a problem. We MUST be sure
|
|
|
|
// that there is no cutting in the middle of a multibyte character!
|
|
|
|
|
|
|
|
// Get a pointer to the null terminator
|
2018-10-01 06:44:33 +02:00
|
|
|
PGM_P pend = message + strlen_P(message);
|
2018-05-26 06:32:37 +02:00
|
|
|
|
|
|
|
// If length of supplied UTF8 string is greater than
|
|
|
|
// our buffer size, start cutting whole UTF8 chars
|
|
|
|
while ((pend - message) > MAX_MESSAGE_LENGTH) {
|
|
|
|
--pend;
|
|
|
|
while (!START_OF_UTF8_CHAR(pgm_read_byte(pend))) --pend;
|
|
|
|
};
|
|
|
|
|
|
|
|
// At this point, we have the proper cut point. Use it
|
|
|
|
uint8_t maxLen = pend - message;
|
|
|
|
strncpy_P(lcd_status_message, message, maxLen);
|
|
|
|
lcd_status_message[maxLen] = '\0';
|
|
|
|
|
2016-07-17 20:33:01 +02:00
|
|
|
lcd_finishstatus(level > 0);
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2018-10-01 06:44:33 +02:00
|
|
|
void lcd_status_printf_P(const uint8_t level, PGM_P const fmt, ...) {
|
2017-02-20 09:51:07 +01:00
|
|
|
if (level < lcd_status_message_level) return;
|
|
|
|
lcd_status_message_level = level;
|
|
|
|
va_list args;
|
2017-04-02 07:46:37 +02:00
|
|
|
va_start(args, fmt);
|
2018-02-19 17:09:08 +01:00
|
|
|
vsnprintf_P(lcd_status_message, MAX_MESSAGE_LENGTH, fmt, args);
|
2017-02-20 09:51:07 +01:00
|
|
|
va_end(args);
|
|
|
|
lcd_finishstatus(level > 0);
|
|
|
|
}
|
|
|
|
|
2018-10-01 06:44:33 +02:00
|
|
|
void lcd_setalertstatusPGM(PGM_P const message) {
|
2017-05-29 18:51:29 +02:00
|
|
|
lcd_setstatusPGM(message, 1);
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2012-12-12 11:47:03 +01:00
|
|
|
lcd_return_to_status();
|
2015-02-22 02:38:56 +01:00
|
|
|
#endif
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2012-08-22 00:46:10 +02:00
|
|
|
|
2015-02-22 02:38:56 +01:00
|
|
|
void lcd_reset_alert_level() { lcd_status_message_level = 0; }
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(ADC_KEYPAD)
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
uint16_t ADCKeyValueMin, ADCKeyValueMax;
|
|
|
|
uint8_t ADCKeyNo;
|
|
|
|
} _stADCKeypadTable_;
|
|
|
|
|
|
|
|
static const _stADCKeypadTable_ stADCKeyTable[] PROGMEM = {
|
|
|
|
// VALUE_MIN, VALUE_MAX, KEY
|
2018-11-03 04:29:32 +01:00
|
|
|
{ 4000, 4096, 1 + BLEN_REPRAPWORLD_KEYPAD_F1 }, // F1
|
|
|
|
{ 4000, 4096, 1 + BLEN_REPRAPWORLD_KEYPAD_F2 }, // F2
|
|
|
|
{ 4000, 4096, 1 + BLEN_REPRAPWORLD_KEYPAD_F3 }, // F3
|
|
|
|
{ 300, 500, 1 + BLEN_REPRAPWORLD_KEYPAD_LEFT }, // LEFT
|
|
|
|
{ 1900, 2200, 1 + BLEN_REPRAPWORLD_KEYPAD_RIGHT }, // RIGHT
|
|
|
|
{ 570, 870, 1 + BLEN_REPRAPWORLD_KEYPAD_UP }, // UP
|
|
|
|
{ 2670, 2870, 1 + BLEN_REPRAPWORLD_KEYPAD_DOWN }, // DOWN
|
|
|
|
{ 1150, 1450, 1 + BLEN_REPRAPWORLD_KEYPAD_MIDDLE }, // ENTER
|
2018-10-23 23:00:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
uint8_t get_ADC_keyValue(void) {
|
|
|
|
if (thermalManager.ADCKey_count >= 16) {
|
|
|
|
const uint16_t currentkpADCValue = thermalManager.current_ADCKey_raw >> 2;
|
|
|
|
thermalManager.current_ADCKey_raw = 0;
|
|
|
|
thermalManager.ADCKey_count = 0;
|
|
|
|
if (currentkpADCValue < 4000)
|
|
|
|
for (uint8_t i = 0; i < ADC_KEY_NUM; i++) {
|
|
|
|
const uint16_t lo = pgm_read_word(&stADCKeyTable[i].ADCKeyValueMin),
|
|
|
|
hi = pgm_read_word(&stADCKeyTable[i].ADCKeyValueMax);
|
|
|
|
if (WITHIN(currentkpADCValue, lo, hi)) return pgm_read_byte(&stADCKeyTable[i].ADCKeyNo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#if HAS_LCD_MENU
|
2015-02-22 02:38:56 +01:00
|
|
|
|
2015-04-28 04:11:25 +02:00
|
|
|
/**
|
|
|
|
* Setup Rotary Encoder Bit Values (for two pin encoders to indicate movement)
|
|
|
|
* These values are independent of which pins are used for EN_A and EN_B indications
|
|
|
|
* The rotary encoder part is also independent to the chipset used for the LCD
|
|
|
|
*/
|
|
|
|
#if defined(EN_A) && defined(EN_B)
|
|
|
|
#define encrot0 0
|
|
|
|
#define encrot1 2
|
|
|
|
#define encrot2 3
|
|
|
|
#define encrot3 1
|
2015-06-12 13:12:55 +02:00
|
|
|
#endif
|
2015-04-28 04:11:25 +02:00
|
|
|
|
2017-11-21 07:20:01 +01:00
|
|
|
#define GET_SHIFT_BUTTON_STATES(DST) \
|
2016-03-27 13:25:33 +02:00
|
|
|
uint8_t new_##DST = 0; \
|
|
|
|
WRITE(SHIFT_LD, LOW); \
|
|
|
|
WRITE(SHIFT_LD, HIGH); \
|
|
|
|
for (int8_t i = 0; i < 8; i++) { \
|
|
|
|
new_##DST >>= 1; \
|
|
|
|
if (READ(SHIFT_OUT)) SBI(new_##DST, 7); \
|
|
|
|
WRITE(SHIFT_CLK, HIGH); \
|
|
|
|
WRITE(SHIFT_CLK, LOW); \
|
|
|
|
} \
|
|
|
|
DST = ~new_##DST; //invert it, because a pressed switch produces a logical 0
|
|
|
|
|
2018-10-23 23:00:34 +02:00
|
|
|
#if ENABLED(G26_MESH_VALIDATION)
|
|
|
|
void lcd_chirp() {
|
|
|
|
lcd_buzz(LCD_FEEDBACK_FREQUENCY_DURATION_MS, LCD_FEEDBACK_FREQUENCY_HZ);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(G26_MESH_VALIDATION)
|
2018-11-01 21:05:59 +01:00
|
|
|
bool is_lcd_clicked() { return LCD_CLICKED(); }
|
2018-10-23 23:00:34 +02:00
|
|
|
void wait_for_release() {
|
|
|
|
while (is_lcd_clicked()) safe_delay(50);
|
|
|
|
safe_delay(50);
|
|
|
|
}
|
|
|
|
#endif
|
2016-03-27 13:25:33 +02:00
|
|
|
|
2015-04-28 04:11:25 +02:00
|
|
|
/**
|
|
|
|
* Read encoder buttons from the hardware registers
|
|
|
|
* Warning: This function is called from interrupt context!
|
|
|
|
*/
|
|
|
|
void lcd_buttons_update() {
|
2017-04-17 04:32:52 +02:00
|
|
|
static uint8_t lastEncoderBits;
|
2017-11-21 07:18:46 +01:00
|
|
|
const millis_t now = millis();
|
2016-09-13 23:09:50 +02:00
|
|
|
if (ELAPSED(now, next_button_update_ms)) {
|
|
|
|
|
|
|
|
#if ENABLED(NEWPANEL)
|
|
|
|
uint8_t newbutton = 0;
|
|
|
|
|
|
|
|
#if BUTTON_EXISTS(EN1)
|
|
|
|
if (BUTTON_PRESSED(EN1)) newbutton |= EN_A;
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(EN2)
|
|
|
|
if (BUTTON_PRESSED(EN2)) newbutton |= EN_B;
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(ENC)
|
|
|
|
if (BUTTON_PRESSED(ENC)) newbutton |= EN_C;
|
|
|
|
#endif
|
2018-02-27 05:52:57 +01:00
|
|
|
#if BUTTON_EXISTS(BACK)
|
|
|
|
if (BUTTON_PRESSED(BACK)) newbutton |= EN_D;
|
|
|
|
#endif
|
2016-09-13 23:09:50 +02:00
|
|
|
|
2017-11-21 07:18:46 +01:00
|
|
|
//
|
|
|
|
// Directional buttons
|
|
|
|
//
|
2016-09-13 23:09:50 +02:00
|
|
|
#if LCD_HAS_DIRECTIONAL_BUTTONS
|
2016-05-03 21:50:49 +02:00
|
|
|
|
2016-10-22 14:40:02 +02:00
|
|
|
#if ENABLED(REVERSE_MENU_DIRECTION)
|
|
|
|
#define _ENCODER_UD_STEPS (ENCODER_STEPS_PER_MENU_ITEM * encoderDirection)
|
|
|
|
#else
|
|
|
|
#define _ENCODER_UD_STEPS ENCODER_STEPS_PER_MENU_ITEM
|
|
|
|
#endif
|
|
|
|
#if ENABLED(REVERSE_ENCODER_DIRECTION)
|
|
|
|
#define ENCODER_UD_STEPS _ENCODER_UD_STEPS
|
|
|
|
#define ENCODER_LR_PULSES ENCODER_PULSES_PER_STEP
|
|
|
|
#else
|
|
|
|
#define ENCODER_UD_STEPS -(_ENCODER_UD_STEPS)
|
|
|
|
#define ENCODER_LR_PULSES -(ENCODER_PULSES_PER_STEP)
|
|
|
|
#endif
|
|
|
|
|
2016-05-03 21:50:49 +02:00
|
|
|
if (false) {
|
|
|
|
// for the else-ifs below
|
2015-12-13 18:40:48 +01:00
|
|
|
}
|
2016-05-03 21:50:49 +02:00
|
|
|
#if BUTTON_EXISTS(UP)
|
|
|
|
else if (BUTTON_PRESSED(UP)) {
|
2016-10-22 14:40:02 +02:00
|
|
|
encoderDiff = -(ENCODER_UD_STEPS);
|
2016-05-03 21:50:49 +02:00
|
|
|
next_button_update_ms = now + 300;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(DWN)
|
|
|
|
else if (BUTTON_PRESSED(DWN)) {
|
2016-10-22 14:40:02 +02:00
|
|
|
encoderDiff = ENCODER_UD_STEPS;
|
2016-05-03 21:50:49 +02:00
|
|
|
next_button_update_ms = now + 300;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(LFT)
|
|
|
|
else if (BUTTON_PRESSED(LFT)) {
|
2016-10-22 14:40:02 +02:00
|
|
|
encoderDiff = -(ENCODER_LR_PULSES);
|
2016-05-03 21:50:49 +02:00
|
|
|
next_button_update_ms = now + 300;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#if BUTTON_EXISTS(RT)
|
|
|
|
else if (BUTTON_PRESSED(RT)) {
|
2016-10-22 14:40:02 +02:00
|
|
|
encoderDiff = ENCODER_LR_PULSES;
|
2016-05-03 21:50:49 +02:00
|
|
|
next_button_update_ms = now + 300;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2016-09-13 23:09:50 +02:00
|
|
|
#endif // LCD_HAS_DIRECTIONAL_BUTTONS
|
2016-05-03 21:50:49 +02:00
|
|
|
|
2017-12-06 23:56:30 +01:00
|
|
|
buttons = newbutton;
|
|
|
|
#if ENABLED(LCD_HAS_SLOW_BUTTONS)
|
|
|
|
buttons |= slow_buttons;
|
|
|
|
#endif
|
|
|
|
|
2017-06-10 07:12:18 +02:00
|
|
|
#if ENABLED(ADC_KEYPAD)
|
|
|
|
|
|
|
|
uint8_t newbutton_reprapworld_keypad = 0;
|
|
|
|
buttons = 0;
|
|
|
|
if (buttons_reprapworld_keypad == 0) {
|
|
|
|
newbutton_reprapworld_keypad = get_ADC_keyValue();
|
|
|
|
if (WITHIN(newbutton_reprapworld_keypad, 1, 8))
|
|
|
|
buttons_reprapworld_keypad = _BV(newbutton_reprapworld_keypad - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#elif ENABLED(REPRAPWORLD_KEYPAD)
|
|
|
|
|
2017-11-21 07:20:01 +01:00
|
|
|
GET_SHIFT_BUTTON_STATES(buttons_reprapworld_keypad);
|
2017-06-10 07:12:18 +02:00
|
|
|
|
2016-09-13 23:09:50 +02:00
|
|
|
#endif
|
2017-06-10 07:12:18 +02:00
|
|
|
|
2017-11-21 07:20:01 +01:00
|
|
|
#else // !NEWPANEL
|
|
|
|
|
|
|
|
GET_SHIFT_BUTTON_STATES(buttons);
|
|
|
|
|
|
|
|
#endif
|
2016-09-13 23:09:50 +02:00
|
|
|
|
|
|
|
} // next_button_update_ms
|
2015-04-28 04:11:25 +02:00
|
|
|
|
2016-07-11 02:28:58 +02:00
|
|
|
// Manage encoder rotation
|
2016-05-12 00:39:28 +02:00
|
|
|
#if ENABLED(REVERSE_MENU_DIRECTION) && ENABLED(REVERSE_ENCODER_DIRECTION)
|
|
|
|
#define ENCODER_DIFF_CW (encoderDiff -= encoderDirection)
|
|
|
|
#define ENCODER_DIFF_CCW (encoderDiff += encoderDirection)
|
|
|
|
#elif ENABLED(REVERSE_MENU_DIRECTION)
|
2016-03-09 10:04:24 +01:00
|
|
|
#define ENCODER_DIFF_CW (encoderDiff += encoderDirection)
|
|
|
|
#define ENCODER_DIFF_CCW (encoderDiff -= encoderDirection)
|
2016-05-12 00:39:28 +02:00
|
|
|
#elif ENABLED(REVERSE_ENCODER_DIRECTION)
|
|
|
|
#define ENCODER_DIFF_CW (encoderDiff--)
|
|
|
|
#define ENCODER_DIFF_CCW (encoderDiff++)
|
2016-03-09 10:04:24 +01:00
|
|
|
#else
|
|
|
|
#define ENCODER_DIFF_CW (encoderDiff++)
|
|
|
|
#define ENCODER_DIFF_CCW (encoderDiff--)
|
|
|
|
#endif
|
|
|
|
#define ENCODER_SPIN(_E1, _E2) switch (lastEncoderBits) { case _E1: ENCODER_DIFF_CW; break; case _E2: ENCODER_DIFF_CCW; }
|
|
|
|
|
2015-10-03 08:08:58 +02:00
|
|
|
uint8_t enc = 0;
|
2015-04-28 04:11:25 +02:00
|
|
|
if (buttons & EN_A) enc |= B01;
|
|
|
|
if (buttons & EN_B) enc |= B10;
|
|
|
|
if (enc != lastEncoderBits) {
|
2015-10-03 08:08:58 +02:00
|
|
|
switch (enc) {
|
2016-03-09 10:04:24 +01:00
|
|
|
case encrot0: ENCODER_SPIN(encrot3, encrot1); break;
|
|
|
|
case encrot1: ENCODER_SPIN(encrot0, encrot2); break;
|
|
|
|
case encrot2: ENCODER_SPIN(encrot1, encrot3); break;
|
|
|
|
case encrot3: ENCODER_SPIN(encrot2, encrot0); break;
|
2015-04-28 04:11:25 +02:00
|
|
|
}
|
2017-03-18 16:15:54 +01:00
|
|
|
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
2017-11-24 00:59:43 +01:00
|
|
|
if (lcd_external_control) {
|
2018-02-05 00:39:32 +01:00
|
|
|
ubl.encoder_diff = encoderDiff; // Make encoder rotation available to UBL G29 mesh editing.
|
|
|
|
encoderDiff = 0; // Hide the encoder event from the current screen handler.
|
2017-03-18 16:15:54 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
lastEncoderBits = enc;
|
2012-12-12 11:47:03 +01:00
|
|
|
}
|
2015-02-22 02:38:56 +01:00
|
|
|
}
|
2014-03-03 22:01:27 +01:00
|
|
|
|
2018-10-30 22:34:45 +01:00
|
|
|
#endif // HAS_LCD_MENU
|
2015-06-15 00:12:02 +02:00
|
|
|
|
2015-04-09 13:32:14 +02:00
|
|
|
#endif // ULTRA_LCD
|