ソースを参照

Lightweight status screen for ST7920

- This status screen uses the ST7920 character generator to greatly
  reduce SPI traffic and MCU load when updating the status screen.

- Has been tested with the RepRapDiscount Full Graphics Smart Controller
  but should work with any LCD that uses an ST7920 or fully compatible
  controller.
Marcio Teixeira 6年前
コミット
532bb3aaa1

+ 1
- 1
.travis.yml ファイルの表示

@@ -236,7 +236,7 @@ script:
236 236
   #
237 237
   - restore_configs
238 238
   - opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER SDSUPPORT
239
-  - opt_enable_adv SDCARD_SORT_ALPHA STATUS_MESSAGE_SCROLLING SCROLL_LONG_FILENAMES
239
+  - opt_enable_adv SDCARD_SORT_ALPHA STATUS_MESSAGE_SCROLLING SCROLL_LONG_FILENAMES LIGHTWEIGHT_UI
240 240
   - build_marlin_pio ${TRAVIS_BUILD_DIR} ${TEST_PLATFORM}
241 241
   #
242 242
   # REPRAPWORLD_KEYPAD

+ 16
- 0
Marlin/Configuration_adv.h ファイルの表示

@@ -656,6 +656,22 @@
656 656
   // Swap the CW/CCW indicators in the graphics overlay
657 657
   //#define OVERLAY_GFX_REVERSE
658 658
 
659
+  #if ENABLED(U8GLIB_ST7920)
660
+    /**
661
+     * ST7920-based LCDs can emulate a 16 x 4 character display using
662
+     * the ST7920 character-generator for very fast screen updates.
663
+     * Enable LIGHTWEIGHT_UI to use this special display mode.
664
+     *
665
+     * Since LIGHTWEIGHT_UI has limited space, the position and status
666
+     * message occupy the same line. Set STATUS_EXPIRE_SECONDS to the
667
+     * length of time to display the status message before clearing.
668
+     */
669
+    //#define LIGHTWEIGHT_UI
670
+    #if ENABLED(LIGHTWEIGHT_UI)
671
+      #define STATUS_EXPIRE_SECONDS 20
672
+    #endif
673
+  #endif
674
+
659 675
 #endif // DOGLCD
660 676
 
661 677
 // @section safety

+ 16
- 0
Marlin/src/config/default/Configuration_adv.h ファイルの表示

@@ -656,6 +656,22 @@
656 656
   // Swap the CW/CCW indicators in the graphics overlay
657 657
   //#define OVERLAY_GFX_REVERSE
658 658
 
659
+  #if ENABLED(U8GLIB_ST7920)
660
+    /**
661
+     * ST7920-based LCDs can emulate a 16 x 4 character display using
662
+     * the ST7920 character-generator for very fast screen updates.
663
+     * Enable LIGHTWEIGHT_UI to use this special display mode.
664
+     *
665
+     * Since LIGHTWEIGHT_UI has limited space, the position and status
666
+     * message occupy the same line. Set STATUS_EXPIRE_SECONDS to the
667
+     * length of time to display the status message before clearing.
668
+     */
669
+    //#define LIGHTWEIGHT_UI
670
+    #if ENABLED(LIGHTWEIGHT_UI)
671
+      #define STATUS_EXPIRE_SECONDS 20
672
+    #endif
673
+  #endif
674
+
659 675
 #endif // DOGLCD
660 676
 
661 677
 // @section safety

+ 442
- 0
Marlin/src/lcd/dogm/status_screen_DOGM.h ファイルの表示

@@ -0,0 +1,442 @@
1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Based on Sprinter and grbl.
6
+ * Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
7
+ *
8
+ * This program is free software: you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation, either version 3 of the License, or
11
+ * (at your option) any later version.
12
+ *
13
+ * This program is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
+ *
21
+ */
22
+
23
+/**
24
+ * status_screen_DOGM.h
25
+ *
26
+ * Standard Status Screen for Graphical Display
27
+ */
28
+
29
+#ifndef _STATUS_SCREEN_DOGM_H_
30
+#define _STATUS_SCREEN_DOGM_H_
31
+
32
+FORCE_INLINE void _draw_centered_temp(const int16_t temp, const uint8_t x, const uint8_t y) {
33
+  const char * const str = itostr3(temp);
34
+  u8g.setPrintPos(x - (str[0] != ' ' ? 0 : str[1] != ' ' ? 1 : 2) * DOG_CHAR_WIDTH / 2, y);
35
+  lcd_print(str);
36
+  lcd_printPGM(PSTR(LCD_STR_DEGREE " "));
37
+}
38
+
39
+#ifndef HEAT_INDICATOR_X
40
+  #define HEAT_INDICATOR_X 8
41
+#endif
42
+
43
+FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, const bool blink) {
44
+  #if !HEATER_IDLE_HANDLER
45
+    UNUSED(blink);
46
+  #endif
47
+
48
+  #if HAS_TEMP_BED
49
+    const bool isBed = heater < 0;
50
+  #else
51
+    constexpr bool isBed = false;
52
+  #endif
53
+
54
+  if (PAGE_UNDER(7)) {
55
+    #if HEATER_IDLE_HANDLER
56
+      const bool is_idle = (!isBed ? thermalManager.is_heater_idle(heater) :
57
+        #if HAS_TEMP_BED
58
+          thermalManager.is_bed_idle()
59
+        #else
60
+          false
61
+        #endif
62
+      );
63
+
64
+      if (blink || !is_idle)
65
+    #endif
66
+    _draw_centered_temp((isBed ? thermalManager.degTargetBed() : thermalManager.degTargetHotend(heater)) + 0.5, x, 7); }
67
+
68
+  if (PAGE_CONTAINS(21, 28))
69
+    _draw_centered_temp((isBed ? thermalManager.degBed() : thermalManager.degHotend(heater)) + 0.5, x, 28);
70
+
71
+  if (PAGE_CONTAINS(17, 20)) {
72
+    const uint8_t h = isBed ? 7 : HEAT_INDICATOR_X,
73
+                  y = isBed ? 18 : 17;
74
+    if (isBed ? thermalManager.isHeatingBed() : thermalManager.isHeatingHotend(heater)) {
75
+      u8g.setColorIndex(0); // white on black
76
+      u8g.drawBox(x + h, y, 2, 2);
77
+      u8g.setColorIndex(1); // black on white
78
+    }
79
+    else {
80
+      u8g.drawBox(x + h, y, 2, 2);
81
+    }
82
+  }
83
+}
84
+
85
+FORCE_INLINE void _draw_axis_label(const AxisEnum axis, const char* const pstr, const bool blink) {
86
+  if (blink)
87
+    lcd_printPGM(pstr);
88
+  else {
89
+    if (!axis_homed[axis])
90
+      u8g.print('?');
91
+    else {
92
+      #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
93
+        if (!axis_known_position[axis])
94
+          u8g.print(' ');
95
+        else
96
+      #endif
97
+          lcd_printPGM(pstr);
98
+    }
99
+  }
100
+}
101
+
102
+inline void lcd_implementation_status_message(const bool blink) {
103
+  #if ENABLED(STATUS_MESSAGE_SCROLLING)
104
+    static bool last_blink = false;
105
+    const uint8_t slen = lcd_strlen(lcd_status_message);
106
+    const char *stat = lcd_status_message + status_scroll_pos;
107
+    if (slen <= LCD_WIDTH)
108
+      lcd_print_utf(stat);                                      // The string isn't scrolling
109
+    else {
110
+      if (status_scroll_pos <= slen - LCD_WIDTH)
111
+        lcd_print_utf(stat);                                    // The string fills the screen
112
+      else {
113
+        uint8_t chars = LCD_WIDTH;
114
+        if (status_scroll_pos < slen) {                         // First string still visible
115
+          lcd_print_utf(stat);                                  // The string leaves space
116
+          chars -= slen - status_scroll_pos;                    // Amount of space left
117
+        }
118
+        u8g.print('.');                                         // Always at 1+ spaces left, draw a dot
119
+        if (--chars) {
120
+          if (status_scroll_pos < slen + 1)                     // Draw a second dot if there's space
121
+            --chars, u8g.print('.');
122
+          if (chars) lcd_print_utf(lcd_status_message, chars);  // Print a second copy of the message
123
+        }
124
+      }
125
+      if (last_blink != blink) {
126
+        last_blink = blink;
127
+        // Skip any non-printing bytes
128
+        if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++;
129
+        if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0;
130
+      }
131
+    }
132
+  #else
133
+    UNUSED(blink);
134
+    lcd_print_utf(lcd_status_message);
135
+  #endif
136
+}
137
+
138
+static void lcd_implementation_status_screen() {
139
+
140
+  const bool blink = lcd_blink();
141
+
142
+  #if FAN_ANIM_FRAMES > 2
143
+    static bool old_blink;
144
+    static uint8_t fan_frame;
145
+    if (old_blink != blink) {
146
+      old_blink = blink;
147
+      if (!fanSpeeds[0] || ++fan_frame >= FAN_ANIM_FRAMES) fan_frame = 0;
148
+    }
149
+  #endif
150
+
151
+  // Status Menu Font
152
+  lcd_setFont(FONT_STATUSMENU);
153
+
154
+  //
155
+  // Fan Animation
156
+  //
157
+  // Draws the whole heading image as a B/W bitmap rather than
158
+  // drawing the elements separately.
159
+  // This was done as an optimization, as it was slower to draw
160
+  // multiple parts compared to a single bitmap.
161
+  //
162
+  // The bitmap:
163
+  // - May be offset in X
164
+  // - Includes all nozzle(s), bed(s), and the fan.
165
+  //
166
+  // TODO:
167
+  //
168
+  // - Only draw the whole header on the first
169
+  //   entry to the status screen. Nozzle, bed, and
170
+  //   fan outline bits don't change.
171
+  //
172
+  if (PAGE_UNDER(STATUS_SCREENHEIGHT + 1)) {
173
+
174
+    u8g.drawBitmapP(
175
+      STATUS_SCREEN_X, STATUS_SCREEN_Y,
176
+      (STATUS_SCREENWIDTH + 7) / 8, STATUS_SCREENHEIGHT,
177
+      #if HAS_FAN0
178
+        #if FAN_ANIM_FRAMES > 2
179
+          fan_frame == 1 ? status_screen1_bmp :
180
+          fan_frame == 2 ? status_screen2_bmp :
181
+          #if FAN_ANIM_FRAMES > 3
182
+            fan_frame == 3 ? status_screen3_bmp :
183
+          #endif
184
+        #else
185
+          blink && fanSpeeds[0] ? status_screen1_bmp :
186
+        #endif
187
+      #endif
188
+      status_screen0_bmp
189
+    );
190
+
191
+  }
192
+
193
+  //
194
+  // Temperature Graphics and Info
195
+  //
196
+
197
+  if (PAGE_UNDER(28)) {
198
+    // Extruders
199
+    HOTEND_LOOP() _draw_heater_status(STATUS_SCREEN_HOTEND_TEXT_X(e), e, blink);
200
+
201
+    // Heated bed
202
+    #if HOTENDS < 4 && HAS_TEMP_BED
203
+      _draw_heater_status(STATUS_SCREEN_BED_TEXT_X, -1, blink);
204
+    #endif
205
+
206
+    #if HAS_FAN0
207
+      if (PAGE_CONTAINS(20, 27)) {
208
+        // Fan
209
+        const int16_t per = ((fanSpeeds[0] + 1) * 100) / 256;
210
+        if (per) {
211
+          u8g.setPrintPos(STATUS_SCREEN_FAN_TEXT_X, STATUS_SCREEN_FAN_TEXT_Y);
212
+          lcd_print(itostr3(per));
213
+          u8g.print('%');
214
+        }
215
+      }
216
+    #endif
217
+  }
218
+
219
+  #if ENABLED(SDSUPPORT)
220
+    //
221
+    // SD Card Symbol
222
+    //
223
+    if (card.isFileOpen() && PAGE_CONTAINS(42 - (TALL_FONT_CORRECTION), 51 - (TALL_FONT_CORRECTION))) {
224
+      // Upper box
225
+      u8g.drawBox(42, 42 - (TALL_FONT_CORRECTION), 8, 7);     // 42-48 (or 41-47)
226
+      // Right edge
227
+      u8g.drawBox(50, 44 - (TALL_FONT_CORRECTION), 2, 5);     // 44-48 (or 43-47)
228
+      // Bottom hollow box
229
+      u8g.drawFrame(42, 49 - (TALL_FONT_CORRECTION), 10, 4);  // 49-52 (or 48-51)
230
+      // Corner pixel
231
+      u8g.drawPixel(50, 43 - (TALL_FONT_CORRECTION));         // 43 (or 42)
232
+    }
233
+  #endif // SDSUPPORT
234
+
235
+  #if ENABLED(SDSUPPORT) || ENABLED(LCD_SET_PROGRESS_MANUALLY)
236
+    //
237
+    // Progress bar frame
238
+    //
239
+    #define PROGRESS_BAR_X 54
240
+    #define PROGRESS_BAR_WIDTH (LCD_PIXEL_WIDTH - PROGRESS_BAR_X)
241
+
242
+    if (PAGE_CONTAINS(49, 52 - (TALL_FONT_CORRECTION)))       // 49-52 (or 49-51)
243
+      u8g.drawFrame(
244
+        PROGRESS_BAR_X, 49,
245
+        PROGRESS_BAR_WIDTH, 4 - (TALL_FONT_CORRECTION)
246
+      );
247
+
248
+    #if DISABLED(LCD_SET_PROGRESS_MANUALLY)
249
+      const uint8_t progress_bar_percent = card.percentDone();
250
+    #endif
251
+
252
+    if (progress_bar_percent > 1) {
253
+
254
+      //
255
+      // Progress bar solid part
256
+      //
257
+
258
+      if (PAGE_CONTAINS(50, 51 - (TALL_FONT_CORRECTION)))     // 50-51 (or just 50)
259
+        u8g.drawBox(
260
+          PROGRESS_BAR_X + 1, 50,
261
+          (uint16_t)((PROGRESS_BAR_WIDTH - 2) * progress_bar_percent * 0.01), 2 - (TALL_FONT_CORRECTION)
262
+        );
263
+
264
+      //
265
+      // SD Percent Complete
266
+      //
267
+
268
+      #if ENABLED(DOGM_SD_PERCENT)
269
+        if (PAGE_CONTAINS(41, 48)) {
270
+          // Percent complete
271
+          u8g.setPrintPos(55, 48);
272
+          u8g.print(itostr3(progress_bar_percent));
273
+          u8g.print('%');
274
+        }
275
+      #endif
276
+    }
277
+
278
+    //
279
+    // Elapsed Time
280
+    //
281
+
282
+    #if DISABLED(DOGM_SD_PERCENT)
283
+      #define SD_DURATION_X (PROGRESS_BAR_X + (PROGRESS_BAR_WIDTH / 2) - len * (DOG_CHAR_WIDTH / 2))
284
+    #else
285
+      #define SD_DURATION_X (LCD_PIXEL_WIDTH - len * DOG_CHAR_WIDTH)
286
+    #endif
287
+
288
+    if (PAGE_CONTAINS(41, 48)) {
289
+      char buffer[10];
290
+      duration_t elapsed = print_job_timer.duration();
291
+      bool has_days = (elapsed.value >= 60*60*24L);
292
+      uint8_t len = elapsed.toDigital(buffer, has_days);
293
+      u8g.setPrintPos(SD_DURATION_X, 48);
294
+      lcd_print(buffer);
295
+    }
296
+
297
+  #endif // SDSUPPORT || LCD_SET_PROGRESS_MANUALLY
298
+
299
+  //
300
+  // XYZ Coordinates
301
+  //
302
+
303
+  #if ENABLED(USE_SMALL_INFOFONT)
304
+    #define INFO_FONT_HEIGHT 7
305
+  #else
306
+    #define INFO_FONT_HEIGHT 8
307
+  #endif
308
+
309
+  #define XYZ_BASELINE (30 + INFO_FONT_HEIGHT)
310
+
311
+  #define X_LABEL_POS  3
312
+  #define X_VALUE_POS 11
313
+  #define XYZ_SPACING 40
314
+
315
+  #if ENABLED(XYZ_HOLLOW_FRAME)
316
+    #define XYZ_FRAME_TOP 29
317
+    #define XYZ_FRAME_HEIGHT INFO_FONT_HEIGHT + 3
318
+  #else
319
+    #define XYZ_FRAME_TOP 30
320
+    #define XYZ_FRAME_HEIGHT INFO_FONT_HEIGHT + 1
321
+  #endif
322
+
323
+  // Before homing the axis letters are blinking 'X' <-> '?'.
324
+  // When axis is homed but axis_known_position is false the axis letters are blinking 'X' <-> ' '.
325
+  // When everything is ok you see a constant 'X'.
326
+
327
+  static char xstring[5], ystring[5], zstring[7];
328
+  #if ENABLED(FILAMENT_LCD_DISPLAY)
329
+    static char wstring[5], mstring[4];
330
+  #endif
331
+
332
+  // At the first page, regenerate the XYZ strings
333
+  if (page.page == 0) {
334
+    strcpy(xstring, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])));
335
+    strcpy(ystring, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])));
336
+    strcpy(zstring, ftostr52sp(FIXFLOAT(LOGICAL_Z_POSITION(current_position[Z_AXIS]))));
337
+    #if ENABLED(FILAMENT_LCD_DISPLAY)
338
+      strcpy(wstring, ftostr12ns(filament_width_meas));
339
+      strcpy(mstring, itostr3(100.0 * (
340
+          parser.volumetric_enabled
341
+            ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
342
+            : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
343
+        )
344
+      ));
345
+    #endif
346
+  }
347
+
348
+  if (PAGE_CONTAINS(XYZ_FRAME_TOP, XYZ_FRAME_TOP + XYZ_FRAME_HEIGHT - 1)) {
349
+
350
+    #if ENABLED(XYZ_HOLLOW_FRAME)
351
+      u8g.drawFrame(0, XYZ_FRAME_TOP, LCD_PIXEL_WIDTH, XYZ_FRAME_HEIGHT); // 8: 29-40  7: 29-39
352
+    #else
353
+      u8g.drawBox(0, XYZ_FRAME_TOP, LCD_PIXEL_WIDTH, XYZ_FRAME_HEIGHT);   // 8: 30-39  7: 30-37
354
+    #endif
355
+
356
+    if (PAGE_CONTAINS(XYZ_BASELINE - (INFO_FONT_HEIGHT - 1), XYZ_BASELINE)) {
357
+
358
+      #if DISABLED(XYZ_HOLLOW_FRAME)
359
+        u8g.setColorIndex(0); // white on black
360
+      #endif
361
+
362
+      u8g.setPrintPos(0 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
363
+      _draw_axis_label(X_AXIS, PSTR(MSG_X), blink);
364
+      u8g.setPrintPos(0 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
365
+      lcd_print(xstring);
366
+
367
+      u8g.setPrintPos(1 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
368
+      _draw_axis_label(Y_AXIS, PSTR(MSG_Y), blink);
369
+      u8g.setPrintPos(1 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
370
+      lcd_print(ystring);
371
+
372
+      u8g.setPrintPos(2 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
373
+      _draw_axis_label(Z_AXIS, PSTR(MSG_Z), blink);
374
+      u8g.setPrintPos(2 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
375
+      lcd_print(zstring);
376
+
377
+      #if DISABLED(XYZ_HOLLOW_FRAME)
378
+        u8g.setColorIndex(1); // black on white
379
+      #endif
380
+    }
381
+  }
382
+
383
+  //
384
+  // Feedrate
385
+  //
386
+
387
+  if (PAGE_CONTAINS(51 - INFO_FONT_HEIGHT, 49)) {
388
+    lcd_setFont(FONT_MENU);
389
+    u8g.setPrintPos(3, 50);
390
+    lcd_print(LCD_STR_FEEDRATE[0]);
391
+
392
+    lcd_setFont(FONT_STATUSMENU);
393
+    u8g.setPrintPos(12, 50);
394
+    lcd_print(itostr3(feedrate_percentage));
395
+    u8g.print('%');
396
+
397
+    //
398
+    // Filament sensor display if SD is disabled
399
+    //
400
+    #if ENABLED(FILAMENT_LCD_DISPLAY) && DISABLED(SDSUPPORT)
401
+      u8g.setPrintPos(56, 50);
402
+      lcd_print(wstring);
403
+      u8g.setPrintPos(102, 50);
404
+      lcd_print(mstring);
405
+      u8g.print('%');
406
+      lcd_setFont(FONT_MENU);
407
+      u8g.setPrintPos(47, 50);
408
+      lcd_print(LCD_STR_FILAM_DIA);
409
+      u8g.setPrintPos(93, 50);
410
+      lcd_print(LCD_STR_FILAM_MUL);
411
+    #endif
412
+  }
413
+
414
+  //
415
+  // Status line
416
+  //
417
+
418
+  #define STATUS_BASELINE (55 + INFO_FONT_HEIGHT)
419
+
420
+  if (PAGE_CONTAINS(STATUS_BASELINE - (INFO_FONT_HEIGHT - 1), STATUS_BASELINE)) {
421
+    u8g.setPrintPos(0, STATUS_BASELINE);
422
+
423
+    #if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
424
+      if (PENDING(millis(), previous_lcd_status_ms + 5000UL)) {  //Display both Status message line and Filament display on the last line
425
+        lcd_implementation_status_message(blink);
426
+      }
427
+      else {
428
+        lcd_printPGM(PSTR(LCD_STR_FILAM_DIA));
429
+        u8g.print(':');
430
+        lcd_print(wstring);
431
+        lcd_printPGM(PSTR("  " LCD_STR_FILAM_MUL));
432
+        u8g.print(':');
433
+        lcd_print(mstring);
434
+        u8g.print('%');
435
+      }
436
+    #else
437
+      lcd_implementation_status_message(blink);
438
+    #endif
439
+  }
440
+}
441
+
442
+#endif // _STATUS_SCREEN_DOGM_H_

+ 853
- 0
Marlin/src/lcd/dogm/status_screen_lite_ST7920.h ファイルの表示

@@ -0,0 +1,853 @@
1
+/**
2
+ * Lightweight Status Screen for the RepRapDiscount Full
3
+ * Graphics Smart Controller (ST7920-based 128x64 LCD)
4
+ *
5
+ * (c) 2017 Aleph Objects, Inc.
6
+ *
7
+ * The code in this page is free software: you can
8
+ * redistribute it and/or modify it under the terms of the GNU
9
+ * General Public License (GNU GPL) as published by the Free Software
10
+ * Foundation, either version 3 of the License, or (at your option)
11
+ * any later version.  The code is distributed WITHOUT ANY WARRANTY;
12
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ * FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
14
+ *
15
+ */
16
+
17
+/**
18
+ * Implementation of a Status Screen for the RepRapDiscount
19
+ * Full Graphics Smart Controller using native ST7920 commands
20
+ * instead of U8Glib.
21
+ *
22
+ * This alternative Status Screen makes use of the built-in character
23
+ * generation capabilities of the ST7920 to update the Status Screen
24
+ * with less SPI traffic and CPU use. In particular:
25
+ *
26
+ *  - The fan and bed animations are handled using custom characters
27
+ *    that are stored in CGRAM. This allows for the animation to be
28
+ *    updated by writing a single character to the text-buffer (DDRAM).
29
+ *
30
+ *  - All the information in the Status Screen is text that is written
31
+ *    to DDRAM, so the work of generating the bitmaps is offloaded to
32
+ *    the ST7920 rather than being render by U8Glib on the MCU.
33
+ *
34
+ *  - The graphics buffer (GDRAM) is only used for static graphics
35
+ *    elements (nozzle and feedrate bitmaps) and for the progress
36
+ *    bar, so updates are sporadic.
37
+ */
38
+
39
+#include "status_screen_lite_ST7920_class.h"
40
+
41
+#include "../../libs/duration_t.h"
42
+
43
+#define BUFFER_WIDTH   256
44
+#define BUFFER_HEIGHT  32
45
+
46
+#define DDRAM_LINE_1   0x00
47
+#define DDRAM_LINE_2   0x10
48
+#define DDRAM_LINE_3   0x08
49
+#define DDRAM_LINE_4   0x18
50
+
51
+ST7920_Lite_Status_Screen::st7920_state_t ST7920_Lite_Status_Screen::current_bits;
52
+
53
+void ST7920_Lite_Status_Screen::cmd(const uint8_t cmd) {
54
+  if (!current_bits.synced || !current_bits.cmd) {
55
+    current_bits.synced = true;
56
+    current_bits.cmd    = true;
57
+    sync_cmd();
58
+  }
59
+  write_byte(cmd);
60
+}
61
+
62
+void ST7920_Lite_Status_Screen::begin_data() {
63
+  if (!current_bits.synced || current_bits.cmd) {
64
+    current_bits.synced = true;
65
+    current_bits.cmd    = false;
66
+    sync_dat();
67
+  }
68
+}
69
+
70
+void ST7920_Lite_Status_Screen::write_str(const char *str) {
71
+  while (*str) write_byte(*str++);
72
+}
73
+
74
+void ST7920_Lite_Status_Screen::write_str(const char *str, uint8_t len) {
75
+  while (*str && len--) write_byte(*str++);
76
+}
77
+
78
+void ST7920_Lite_Status_Screen::write_str_P(const char * const str) {
79
+  const char *p_str = (const char *)str;
80
+  while (char c = pgm_read_byte_near(p_str++)) write_byte(c);
81
+}
82
+
83
+void ST7920_Lite_Status_Screen::write_str(progmem_str str) {
84
+  write_str_P((const char*)str);
85
+}
86
+
87
+void ST7920_Lite_Status_Screen::write_number(const int16_t value, const uint8_t digits/*=3*/) {
88
+  char str[7];
89
+  const char *fmt;
90
+  switch (digits) {
91
+    case 6: fmt = PSTR("%6d"); break;
92
+    case 5: fmt = PSTR("%5d"); break;
93
+    case 4: fmt = PSTR("%4d"); break;
94
+    case 3: fmt = PSTR("%3d"); break;
95
+    case 2: fmt = PSTR("%2d"); break;
96
+    case 1: fmt = PSTR("%1d"); break;
97
+  }
98
+  sprintf_P(str, fmt, value);
99
+  write_str(str);
100
+}
101
+
102
+void ST7920_Lite_Status_Screen::display_status(const bool display_on, const bool cursor_on, const bool blink_on) {
103
+  extended_function_set(false);
104
+  cmd(0b00001000 |
105
+    (display_on ? 0b0100 : 0) |
106
+    (cursor_on  ? 0b0010 : 0) |
107
+    (blink_on   ? 0b0001 : 0)
108
+  );
109
+}
110
+
111
+// Sets the extended and graphics bits simultaneously, regardless of
112
+// the current state. This is a helper function for extended_function_set()
113
+// and graphics()
114
+void ST7920_Lite_Status_Screen::_extended_function_set(const bool extended, const bool graphics) {
115
+  cmd(  0b00100000 |
116
+    (extended   ? 0b00000100 : 0) |
117
+    (graphics   ? 0b00000010 : 0)
118
+  );
119
+  current_bits.extended = extended;
120
+  current_bits.graphics = graphics;
121
+}
122
+
123
+void ST7920_Lite_Status_Screen::extended_function_set(const bool extended) {
124
+  if (extended != current_bits.extended)
125
+    _extended_function_set(extended, current_bits.graphics);
126
+}
127
+
128
+void ST7920_Lite_Status_Screen::graphics(const bool graphics) {
129
+  if (graphics != current_bits.graphics)
130
+    _extended_function_set(current_bits.extended, graphics);
131
+}
132
+
133
+void ST7920_Lite_Status_Screen::entry_mode_select(const bool ac_increase, const bool shift) {
134
+  extended_function_set(false);
135
+  cmd(0b00000100 |
136
+    (ac_increase ? 0b00000010 : 0) |
137
+    (shift       ? 0b00000001 : 0)
138
+  );
139
+}
140
+
141
+// Sets the sa bit regardless of the current state. This is a helper
142
+// function for scroll_or_addr_select()
143
+void ST7920_Lite_Status_Screen::_scroll_or_addr_select(const bool sa) {
144
+  extended_function_set(true);
145
+  cmd(0b00100010 |
146
+    (sa   ? 0b000001 : 0)
147
+  );
148
+  current_bits.sa = sa;
149
+}
150
+
151
+void ST7920_Lite_Status_Screen::scroll_or_addr_select(const bool sa) {
152
+  if (sa != current_bits.sa)
153
+    _scroll_or_addr_select(sa);
154
+}
155
+
156
+void ST7920_Lite_Status_Screen::set_ddram_address(const uint8_t addr) {
157
+  extended_function_set(false);
158
+  cmd(0b10000000 | (addr & 0b00111111));
159
+}
160
+
161
+void ST7920_Lite_Status_Screen::set_cgram_address(const uint8_t addr) {
162
+  extended_function_set(false);
163
+  cmd(0b01000000 | (addr & 0b00111111));
164
+}
165
+
166
+void ST7920_Lite_Status_Screen::set_gdram_address(const uint8_t x, const uint8_t y) {
167
+  extended_function_set(true);
168
+  cmd(0b10000000 | (y & 0b01111111));
169
+  cmd(0b10000000 | (x & 0b00001111));
170
+}
171
+
172
+void ST7920_Lite_Status_Screen::clear() {
173
+  extended_function_set(false);
174
+  cmd(0x00000001);
175
+  delay(15);                 //delay for CGRAM clear
176
+}
177
+
178
+void ST7920_Lite_Status_Screen::home() {
179
+  extended_function_set(false);
180
+  cmd(0x00000010);
181
+}
182
+
183
+/* This fills the entire text buffer with spaces */
184
+void ST7920_Lite_Status_Screen::clear_ddram() {
185
+  set_ddram_address(DDRAM_LINE_1);
186
+  begin_data();
187
+  for (uint8_t i = 64; i--;) write_byte(' ');
188
+}
189
+
190
+/* This fills the entire graphics buffer with zeros */
191
+void ST7920_Lite_Status_Screen::clear_gdram() {
192
+  for (uint8_t y = 0; y < BUFFER_HEIGHT; y++) {
193
+    set_gdram_address(0, y);
194
+    begin_data();
195
+    for (uint8_t i = (BUFFER_WIDTH) / 16; i--;) write_word(0);
196
+  }
197
+}
198
+
199
+void ST7920_Lite_Status_Screen::load_cgram_icon(const uint16_t addr, const void *data) {
200
+  const uint16_t *p_word = (const uint16_t *)data;
201
+  set_cgram_address(addr);
202
+  begin_data();
203
+  for (uint8_t i = 16; i--;)
204
+    write_word(pgm_read_word_near(p_word++));
205
+}
206
+
207
+/**
208
+ * Draw an icon in GDRAM. Position specified in DDRAM
209
+ * coordinates. i.e., X from 1 to 8, Y from 1 to 4.
210
+ */
211
+void ST7920_Lite_Status_Screen::draw_gdram_icon(uint8_t x, uint8_t y, const void *data) {
212
+  const uint16_t *p_word = (const uint16_t *)data;
213
+  if (y > 2) { // Handle display folding
214
+    y -= 2;
215
+    x += 8;
216
+  }
217
+  --x;
218
+  --y;
219
+  for (int i = 0; i < 16; i++) {
220
+    set_gdram_address(x, i + y * 16);
221
+    begin_data();
222
+    write_word(pgm_read_word_near(p_word++));
223
+  }
224
+}
225
+
226
+/************************** ICON DEFINITIONS *************************************/
227
+
228
+#define CGRAM_ICON_1_ADDR 0x00
229
+#define CGRAM_ICON_2_ADDR 0x10
230
+#define CGRAM_ICON_3_ADDR 0x20
231
+#define CGRAM_ICON_4_ADDR 0x30
232
+
233
+#define CGRAM_ICON_1_WORD 0x00
234
+#define CGRAM_ICON_2_WORD 0x02
235
+#define CGRAM_ICON_3_WORD 0x04
236
+#define CGRAM_ICON_4_WORD 0x06
237
+
238
+const uint16_t nozzle_icon[] PROGMEM = {
239
+  0b0000000000000000,
240
+  0b0000000000000000,
241
+  0b0000111111110000,
242
+  0b0001111111111000,
243
+  0b0001111111111000,
244
+  0b0001111111111000,
245
+  0b0000111111110000,
246
+  0b0000111111110000,
247
+  0b0001111111111000,
248
+  0b0001111111111000,
249
+  0b0001111111111000,
250
+  0b0000011111100000,
251
+  0b0000001111000000,
252
+  0b0000000110000000,
253
+  0b0000000000000000,
254
+  0b0000000000000000
255
+};
256
+
257
+const uint16_t bed_icon[] PROGMEM = {
258
+  0b0000000000000000,
259
+  0b0000000000000000,
260
+  0b0000000000000000,
261
+  0b0000000000000000,
262
+  0b0000000000000000,
263
+  0b0000000000000000,
264
+  0b0000000000000000,
265
+  0b0000000000000000,
266
+  0b0000000000000000,
267
+  0b0000000000000000,
268
+  0b0000000000000000,
269
+  0b0111111111111110,
270
+  0b0111111111111110,
271
+  0b0110000000000110,
272
+  0b0000000000000000,
273
+  0b0000000000000000
274
+};
275
+
276
+const uint16_t heat1_icon[] PROGMEM = {
277
+  0b0000000000000000,
278
+  0b0010001000100000,
279
+  0b0001000100010000,
280
+  0b0000100010001000,
281
+  0b0000100010001000,
282
+  0b0001000100010000,
283
+  0b0010001000100000,
284
+  0b0010001000100000,
285
+  0b0001000100010000,
286
+  0b0000100010001000,
287
+  0b0000000000000000,
288
+  0b0000000000000000,
289
+  0b0000000000000000,
290
+  0b0000000000000000,
291
+  0b0000000000000000,
292
+  0b0000000000000000
293
+};
294
+
295
+const uint16_t heat2_icon[] PROGMEM = {
296
+  0b0000000000000000,
297
+  0b0000100010001000,
298
+  0b0000100010001000,
299
+  0b0001000100010000,
300
+  0b0010001000100000,
301
+  0b0010001000100000,
302
+  0b0001000100010000,
303
+  0b0000100010001000,
304
+  0b0000100010001000,
305
+  0b0001000100010000,
306
+  0b0000000000000000,
307
+  0b0000000000000000,
308
+  0b0000000000000000,
309
+  0b0000000000000000,
310
+  0b0000000000000000,
311
+  0b0000000000000000
312
+};
313
+
314
+const uint16_t fan1_icon[] PROGMEM = {
315
+  0b0000000000000000,
316
+  0b0111111111111110,
317
+  0b0111000000001110,
318
+  0b0110001111000110,
319
+  0b0100001111000010,
320
+  0b0100000110000010,
321
+  0b0101100000011010,
322
+  0b0101110110111010,
323
+  0b0101100000011010,
324
+  0b0100000110000010,
325
+  0b0100001111000010,
326
+  0b0110001111000110,
327
+  0b0111000000001110,
328
+  0b0111111111111110,
329
+  0b0000000000000000,
330
+  0b0000000000000000
331
+};
332
+
333
+const uint16_t fan2_icon[] PROGMEM = {
334
+  0b0000000000000000,
335
+  0b0111111111111110,
336
+  0b0111000000001110,
337
+  0b0110010000100110,
338
+  0b0100111001110010,
339
+  0b0101111001111010,
340
+  0b0100110000110010,
341
+  0b0100000110000010,
342
+  0b0100110000110010,
343
+  0b0101111001111010,
344
+  0b0100111001110010,
345
+  0b0110010000100110,
346
+  0b0111000000001110,
347
+  0b0111111111111110,
348
+  0b0000000000000000,
349
+  0b0000000000000000
350
+};
351
+
352
+const uint16_t feedrate_icon[] PROGMEM = {
353
+  0b0000000000000000,
354
+  0b0111111000000000,
355
+  0b0100000000000000,
356
+  0b0100000000000000,
357
+  0b0100000000000000,
358
+  0b0111111011111000,
359
+  0b0100000010000100,
360
+  0b0100000010000100,
361
+  0b0100000010000100,
362
+  0b0100000011111000,
363
+  0b0000000010001000,
364
+  0b0000000010000100,
365
+  0b0000000010000100,
366
+  0b0000000010000010,
367
+  0b0000000000000000,
368
+  0b0000000000000000
369
+};
370
+
371
+/************************** MAIN SCREEN *************************************/
372
+
373
+void ST7920_Lite_Status_Screen::draw_static_elements() {
374
+  scroll_or_addr_select(0);
375
+
376
+  // Load the animated bed and fan icons
377
+  load_cgram_icon(CGRAM_ICON_1_ADDR, heat1_icon);
378
+  load_cgram_icon(CGRAM_ICON_2_ADDR, heat2_icon);
379
+  load_cgram_icon(CGRAM_ICON_3_ADDR, fan1_icon);
380
+  load_cgram_icon(CGRAM_ICON_4_ADDR, fan2_icon);
381
+
382
+  // Draw the static icons in GDRAM
383
+  draw_gdram_icon(1, 1, nozzle_icon);
384
+  #if EXTRUDERS == 2
385
+    draw_gdram_icon(1,2,nozzle_icon);
386
+    draw_gdram_icon(1,3,bed_icon);
387
+  #else
388
+    draw_gdram_icon(1,2,bed_icon);
389
+  #endif
390
+  draw_gdram_icon(6,2,feedrate_icon);
391
+
392
+  // Draw the initial fan icon
393
+  draw_fan_icon(false);
394
+}
395
+
396
+/**
397
+ * Although this is undocumented, the ST7920 allows the character
398
+ * data buffer (DDRAM) to be used in conjunction with the graphics
399
+ * bitmap buffer (CGRAM). The contents of the graphics buffer is
400
+ * XORed with the data from the character generator. This allows
401
+ * us to make the progess bar out of graphical data (the bar) and
402
+ * text data (the percentage).
403
+ */
404
+void ST7920_Lite_Status_Screen::draw_progress_bar(const uint8_t value) {
405
+  #if EXTRUDERS == 1
406
+    // If we have only one extruder, draw a long progress bar on the third line
407
+    const uint8_t top     = 1,         // Top in pixels
408
+                  bottom  = 13,        // Bottom in pixels
409
+                  left    = 8,         // Left edge, in 16-bit words
410
+                  width   = 5;         // Width of progress bar, in 16-bit words
411
+  #else
412
+    const uint8_t top     = 16 + 1,    // Top in pixels
413
+                  bottom  = 16 + 13,   // Bottom in pixels
414
+                  left    = 5,         // Left edge, in 16-bit words
415
+                  width   = 3;         // Width of progress bar, in 16-bit words
416
+  #endif
417
+  const uint8_t char_pcnt  = 100 / width; // How many percent does each 16-bit word represent?
418
+
419
+  // Draw the progress bar as a bitmap in CGRAM
420
+  for (uint8_t y = top; y <= bottom; y++) {
421
+    set_gdram_address(left, y);
422
+    begin_data();
423
+    for (uint8_t x = 0; x < width; x++) {
424
+      uint16_t gfx_word = 0x0000;
425
+      if ((x + 1) * char_pcnt <= value)
426
+        gfx_word = 0xFFFF;                                              // Draw completely filled bytes
427
+      else if ((x * char_pcnt) < value)
428
+        gfx_word = int(0x8000) >> (value % char_pcnt) * 16 / char_pcnt; // Draw partially filled bytes
429
+
430
+      // Draw the frame around the progress bar
431
+      if (y == top || y == bottom)
432
+        gfx_word = 0xFFFF;        // Draw top/bottom border
433
+      else if (x == width - 1)
434
+        gfx_word |= 0x0001;       // Draw right border
435
+      else if (x == 0)
436
+        gfx_word |= 0x8000;       // Draw left border
437
+      write_word(gfx_word);
438
+    }
439
+  }
440
+
441
+  // Draw the percentage as text in DDRAM
442
+  set_ddram_address(
443
+    #if EXTRUDERS == 1
444
+      DDRAM_LINE_3 + 1
445
+    #else
446
+      DDRAM_LINE_2 + left
447
+    #endif
448
+  );
449
+
450
+  begin_data();
451
+  if (value > 9) {
452
+    write_number(value, 4);
453
+    write_str(F("% "));
454
+  }
455
+  else {
456
+    write_number(value, 3);
457
+    write_str(F("%  "));
458
+  }
459
+}
460
+
461
+void ST7920_Lite_Status_Screen::draw_fan_icon(const bool whichIcon) {
462
+  set_ddram_address(DDRAM_LINE_1 + 5);
463
+  begin_data();
464
+  write_word(whichIcon ? CGRAM_ICON_3_WORD : CGRAM_ICON_4_WORD);
465
+}
466
+
467
+void ST7920_Lite_Status_Screen::draw_heat_icon(const bool whichIcon, const bool heating) {
468
+  set_ddram_address(
469
+    #if EXTRUDERS == 1
470
+      DDRAM_LINE_2
471
+    #else
472
+      DDRAM_LINE_3
473
+    #endif
474
+  );
475
+  begin_data();
476
+  if (heating)
477
+    write_word(whichIcon ? CGRAM_ICON_1_WORD : CGRAM_ICON_2_WORD);
478
+  else {
479
+    write_byte(' ');
480
+    write_byte(' ');
481
+  }
482
+}
483
+
484
+#define FAR(a,b) (((a > b) ? (a-b) : (b-a)) > 1)
485
+
486
+void ST7920_Lite_Status_Screen::draw_extruder_1_temp(const int16_t temp, const int16_t target) {
487
+  set_ddram_address(DDRAM_LINE_1 + 1);
488
+  begin_data();
489
+  write_number(temp);
490
+  if (target && FAR(temp, target)) {
491
+    write_str(F("\x1A"));
492
+    write_number(target);
493
+  }
494
+  else
495
+    write_str(F("    "));
496
+}
497
+
498
+void ST7920_Lite_Status_Screen::draw_extruder_2_temp(const int16_t temp, const int16_t target) {
499
+  set_ddram_address(DDRAM_LINE_2 + 1);
500
+  begin_data();
501
+  write_number(temp);
502
+  if (target && FAR(temp, target)) {
503
+    write_str(F("\x1A"));
504
+    write_number(target);
505
+  }
506
+  else
507
+    write_str(F("    "));
508
+}
509
+
510
+void ST7920_Lite_Status_Screen::draw_bed_temp(const int16_t temp, const int16_t target) {
511
+  set_ddram_address(
512
+    #if EXTRUDERS == 1
513
+      DDRAM_LINE_2 + 1
514
+    #else
515
+      DDRAM_LINE_3 + 1
516
+    #endif
517
+  );
518
+  begin_data();
519
+  write_number(temp);
520
+  if (target && FAR(temp, target)) {
521
+    write_str(F("\x1A"));
522
+    write_number(target);
523
+  }
524
+  else
525
+    write_str(F("    "));
526
+}
527
+
528
+void ST7920_Lite_Status_Screen::draw_fan_speed(const uint8_t value) {
529
+  set_ddram_address(DDRAM_LINE_1 + 6);
530
+  begin_data();
531
+  write_number(value, 4);
532
+}
533
+
534
+void ST7920_Lite_Status_Screen::draw_print_time(const uint32_t elapsed) {
535
+  const uint8_t hrs = elapsed / 3600,
536
+                min = (elapsed / 60) % 60;
537
+  char str[7];
538
+  sprintf_P(str, hrs > 99 ? PSTR("%03d:%02d") : PSTR(" %02d:%02d"), hrs, min);
539
+
540
+  set_ddram_address(DDRAM_LINE_3 + 5);
541
+  begin_data();
542
+  write_str(str);
543
+}
544
+
545
+void ST7920_Lite_Status_Screen::draw_feedrate_percentage(const uint8_t percentage) {
546
+  // We only have enough room for the feedrate when
547
+  // we have one extruder
548
+  #if EXTRUDERS == 1
549
+    set_ddram_address(DDRAM_LINE_2 + 6);
550
+    begin_data();
551
+    write_number(percentage, 4);
552
+  #endif
553
+}
554
+
555
+void ST7920_Lite_Status_Screen::draw_status_message(const char *str) {
556
+  set_ddram_address(DDRAM_LINE_4);
557
+  begin_data();
558
+  #if ENABLED(STATUS_MESSAGE_SCROLLING)
559
+    const uint8_t lcd_len = 16;
560
+    const uint8_t padding = 2;
561
+    uint8_t str_len = strlen(str);
562
+
563
+    // Trim whitespace at the end of the str, as for some reason
564
+    // messages like "Card Inserted" are padded with many spaces
565
+    while (str_len > 0 && str[str_len - 1] == ' ') str_len--;
566
+
567
+    if (str_len <= lcd_len) {
568
+      // It all fits on the LCD without scrolling
569
+      write_str(str);
570
+    }
571
+    else {
572
+      // Print the message repeatedly until covering the LCD
573
+      uint8_t c = status_scroll_pos;
574
+      for (uint8_t n = 0; n < lcd_len; n++) {
575
+        write_byte(c < str_len ? str[c] : ' ');
576
+        c++;
577
+        c %= str_len + padding; // Wrap around
578
+      }
579
+
580
+      // Scroll the message
581
+      if (status_scroll_pos == str_len + padding)
582
+        status_scroll_pos = 0;
583
+      else
584
+        status_scroll_pos++;
585
+    }
586
+  #else
587
+    write_str(str, 16);
588
+  #endif
589
+}
590
+
591
+void ST7920_Lite_Status_Screen::draw_position(const float x, const float y, const float z, bool position_known) {
592
+  char str[7];
593
+  set_ddram_address(DDRAM_LINE_4);
594
+  begin_data();
595
+
596
+  // If position is unknown, flash the labels.
597
+  const unsigned char alt_label = position_known ? 0 : (lcd_blink() ? ' ' : 0);
598
+
599
+  dtostrf(x, -4, 0, str);
600
+  write_byte(alt_label ? alt_label : 'X');
601
+  write_str(str, 4);
602
+
603
+  dtostrf(y, -4, 0, str);
604
+  write_byte(alt_label ? alt_label : 'Y');
605
+  write_str(str, 4);
606
+
607
+  dtostrf(z, -5, 1, str);
608
+  write_byte(alt_label ? alt_label : 'Z');
609
+  write_str(str, 5);
610
+}
611
+
612
+bool ST7920_Lite_Status_Screen::indicators_changed() {
613
+  // We only add the target temperatures to the checksum
614
+  // because the actual temps fluctuate so by updating
615
+  // them only during blinks we gain a bit of stability.
616
+  const bool       blink             = lcd_blink();
617
+  const uint8_t    feedrate_perc     = feedrate_percentage;
618
+  const uint8_t    fan_speed         = ((fanSpeeds[0] + 1) * 100) / 256;
619
+  const float      extruder_1_target = thermalManager.degTargetHotend(0);
620
+  #if EXTRUDERS == 2
621
+    const float    extruder_2_target = thermalManager.degTargetHotend(1);
622
+  #endif
623
+  const float      bed_target        = thermalManager.degTargetBed();
624
+
625
+  static uint8_t last_checksum = 0;
626
+
627
+  const  uint8_t checksum =
628
+    uint8_t(blink) ^
629
+    uint8_t(feedrate_perc) ^
630
+    uint8_t(fan_speed) ^
631
+    uint8_t(extruder_1_target) ^
632
+    #if EXTRUDERS == 2
633
+      uint8_t(extruder_2_target) ^
634
+    #endif
635
+    uint8_t(bed_target);
636
+
637
+  if (last_checksum == checksum) return false;
638
+
639
+  last_checksum = checksum;
640
+  return true;
641
+}
642
+
643
+void ST7920_Lite_Status_Screen::update_indicators(const bool forceUpdate) {
644
+  if (forceUpdate || indicators_changed()) {
645
+    const bool       blink             = lcd_blink();
646
+    const duration_t elapsed           = print_job_timer.duration();
647
+    const uint32_t   seconds_elapsed   = elapsed.value;
648
+    const uint8_t    feedrate_perc     = feedrate_percentage;
649
+    const uint8_t    fan_speed         = ((fanSpeeds[0] + 1) * 100) / 256;
650
+    const float      extruder_1_temp   = thermalManager.degHotend(0);
651
+    const float      extruder_1_target = thermalManager.degTargetHotend(0);
652
+    #if EXTRUDERS == 2
653
+      const float    extruder_2_temp   = thermalManager.degHotend(1);
654
+      const float    extruder_2_target = thermalManager.degTargetHotend(1);
655
+    #endif
656
+    const float      bed_temp          = thermalManager.degBed();
657
+    const float      bed_target        = thermalManager.degTargetBed();
658
+
659
+    draw_extruder_1_temp(extruder_1_temp, extruder_1_target);
660
+    #if EXTRUDERS == 2
661
+      draw_extruder_2_temp(extruder_2_temp, extruder_2_target);
662
+    #endif
663
+    draw_bed_temp(bed_temp, bed_target);
664
+    draw_fan_speed(fan_speed);
665
+    draw_print_time(seconds_elapsed);
666
+    draw_feedrate_percentage(feedrate_perc);
667
+
668
+    // Update the fan and bed animations
669
+    if (fan_speed > 0) draw_fan_icon(blink);
670
+    if (bed_target > 0)
671
+      draw_heat_icon(blink, true);
672
+    else
673
+      draw_heat_icon(false, false);
674
+  }
675
+}
676
+
677
+bool ST7920_Lite_Status_Screen::position_changed() {
678
+  const float x_pos = current_position[X_AXIS],
679
+              y_pos = current_position[Y_AXIS],
680
+              z_pos = current_position[Z_AXIS];
681
+  const uint8_t checksum = uint8_t(x_pos) ^ uint8_t(y_pos) ^ uint8_t(z_pos);
682
+
683
+  static uint8_t last_checksum = 0;
684
+  if (last_checksum == checksum) return false;
685
+  last_checksum = checksum;
686
+  return true;
687
+}
688
+
689
+bool ST7920_Lite_Status_Screen::status_changed() {
690
+  uint8_t checksum = 0;
691
+  for (const char *p = lcd_status_message; *p; p++) checksum ^= *p;
692
+
693
+  static uint8_t last_checksum = 0;
694
+  if (last_checksum == checksum) return false;
695
+  last_checksum = checksum;
696
+  return true;
697
+}
698
+
699
+bool ST7920_Lite_Status_Screen::blink_changed() {
700
+  static uint8_t last_blink = 0;
701
+  const bool blink = lcd_blink();
702
+  if (last_blink == blink) return false;
703
+  last_blink = blink;
704
+  return true;
705
+}
706
+
707
+void ST7920_Lite_Status_Screen::update_status_or_position(bool forceUpdate) {
708
+  static uint8_t countdown = 0;
709
+
710
+  /**
711
+   * There is only enough room in the display for either the
712
+   * status message or the position, not both, so we choose
713
+   * one or another. Whenever the status message changes,
714
+   * we show it for a number of consecutive seconds, but
715
+   * then go back to showing the position as soon as the
716
+   * head moves, i.e:
717
+   *
718
+   *    countdown > 1    -- Show status
719
+   *    countdown = 1    -- Show status, until movement
720
+   *    countdown = 0    -- Show position
721
+   */
722
+  if (forceUpdate || status_changed()) {
723
+    #if ENABLED(STATUS_MESSAGE_SCROLLING)
724
+      status_scroll_pos = 0;
725
+    #endif
726
+    #ifndef STATUS_EXPIRE_SECONDS
727
+      #define STATUS_EXPIRE_SECONDS 20
728
+    #endif
729
+    countdown = lcd_strlen(lcd_status_message) ? STATUS_EXPIRE_SECONDS : 0;
730
+    draw_status_message(lcd_status_message);
731
+    blink_changed(); // Clear changed flag
732
+  }
733
+  else if (countdown > 1 && blink_changed()) {
734
+    countdown--;
735
+    #if ENABLED(STATUS_MESSAGE_SCROLLING)
736
+      draw_status_message(lcd_status_message);
737
+    #endif
738
+  }
739
+  else if (countdown > 0 && blink_changed()) {
740
+    if (position_changed()) {
741
+      countdown--;
742
+      forceUpdate = true;
743
+    }
744
+    #if ENABLED(STATUS_MESSAGE_SCROLLING)
745
+      draw_status_message(lcd_status_message);
746
+    #endif
747
+  }
748
+  if (countdown == 0 && (forceUpdate || position_changed() ||
749
+    #if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
750
+      blink_changed()
751
+    #endif
752
+  )) {
753
+    draw_position(
754
+      current_position[X_AXIS],
755
+      current_position[Y_AXIS],
756
+      current_position[Z_AXIS],
757
+      #if ENABLED(DISABLE_REDUCED_ACCURACY_WARNING)
758
+        true
759
+      #else
760
+        axis_known_position[X_AXIS] &&
761
+        axis_known_position[Y_AXIS] &&
762
+        axis_known_position[Z_AXIS]
763
+      #endif
764
+    );
765
+  }
766
+}
767
+
768
+void ST7920_Lite_Status_Screen::update_progress(const bool forceUpdate) {
769
+  const uint8_t percent_done =
770
+    #if ENABLED(SDSUPPORT)
771
+      card.percentDone()
772
+    #else
773
+      0
774
+    #endif
775
+  ;
776
+
777
+  // Since the progress bar involves writing
778
+  // quite a few bytes to GDRAM, only do this
779
+  // when an update is actually necessary.
780
+
781
+  static uint8_t last_progress = 0;
782
+  if (!forceUpdate && last_progress == percent_done) return;
783
+  last_progress = percent_done;
784
+
785
+  draw_progress_bar(percent_done);
786
+}
787
+
788
+void ST7920_Lite_Status_Screen::update(const bool forceUpdate) {
789
+  cs();
790
+  update_indicators(forceUpdate);
791
+  update_status_or_position(forceUpdate);
792
+  update_progress(forceUpdate);
793
+  ncs();
794
+}
795
+
796
+void ST7920_Lite_Status_Screen::reset_state_from_unknown() {
797
+  _extended_function_set(true, true); // Do it twice as only one bit
798
+  _extended_function_set(true, true); // get set at a time.
799
+  _scroll_or_addr_select(false);
800
+}
801
+
802
+void ST7920_Lite_Status_Screen::on_entry() {
803
+  cs();
804
+  reset_state_from_unknown();
805
+  clear();
806
+  clear_gdram();
807
+  draw_static_elements();
808
+  update(true);
809
+  ncs();
810
+}
811
+
812
+void ST7920_Lite_Status_Screen::on_exit() {
813
+  cs();
814
+  clear();
815
+  _extended_function_set(true, true); // Restore state to what u8g expects.
816
+  ncs();
817
+}
818
+
819
+// This is called prior to the KILL screen to
820
+// clear the screen so we don't end up with a
821
+// garbled display.
822
+void ST7920_Lite_Status_Screen::clear_text_buffer() {
823
+  cs();
824
+  reset_state_from_unknown();
825
+  clear();
826
+  _extended_function_set(true, true); // Restore state to what u8g expects.
827
+  ncs();
828
+}
829
+
830
+static void lcd_implementation_status_screen() {
831
+  ST7920_Lite_Status_Screen::update(false);
832
+}
833
+
834
+/**
835
+ * In order to properly update the lite Status Screen,
836
+ * we must know when we have entered and left the
837
+ * Status Screen. Since the ultralcd code is not
838
+ * set up for doing this, we call this function before
839
+ * each update indicating whether the current screen
840
+ * is the Status Screen.
841
+ *
842
+ * This function keeps track of whether we have left or
843
+ * entered the Status Screen and calls the on_entry()
844
+ * and on_exit() methods for cleanup.
845
+ */
846
+static void lcd_in_status(const bool inStatus) {
847
+  static bool lastInStatus = false;
848
+  if (lastInStatus == inStatus) return;
849
+  if ((lastInStatus = inStatus))
850
+    ST7920_Lite_Status_Screen::on_entry();
851
+  else
852
+    ST7920_Lite_Status_Screen::on_exit();
853
+}

+ 108
- 0
Marlin/src/lcd/dogm/status_screen_lite_ST7920_class.h ファイルの表示

@@ -0,0 +1,108 @@
1
+/**
2
+ * Lightweight Status Screen for the RepRapDiscount Full
3
+ * Graphics Smart Controller (ST7920-based 128x64 LCD)
4
+ *
5
+ * (c) 2017 Aleph Objects, Inc.
6
+ *
7
+ * The code in this page is free software: you can
8
+ * redistribute it and/or modify it under the terms of the GNU
9
+ * General Public License (GNU GPL) as published by the Free Software
10
+ * Foundation, either version 3 of the License, or (at your option)
11
+ * any later version.  The code is distributed WITHOUT ANY WARRANTY;
12
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ * FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
14
+ *
15
+ */
16
+
17
+#ifndef STATUS_SCREEN_LITE_ST7920_CLASS_H
18
+#define STATUS_SCREEN_LITE_ST7920_CLASS_H
19
+
20
+#include "../../core/macros.h"
21
+
22
+typedef const __FlashStringHelper *progmem_str;
23
+
24
+class ST7920_Lite_Status_Screen {
25
+  private:
26
+    static struct st7920_state_t {
27
+      uint8_t synced   : 1; // Whether a sync has been sent
28
+      uint8_t cmd      : 1; // Whether the sync was cmd or data
29
+      uint8_t extended : 1;
30
+      uint8_t graphics : 1;
31
+      uint8_t sa       : 1;
32
+    } current_bits;
33
+
34
+    static void cs();
35
+    static void ncs();
36
+    static void sync_cmd();
37
+    static void sync_dat();
38
+    static void write_byte(const uint8_t w);
39
+
40
+    FORCE_INLINE static void write_word(const uint16_t w) {
41
+      write_byte((w >> 8) & 0xFF);
42
+      write_byte((w >> 0) & 0xFF);
43
+    }
44
+
45
+    static void cmd(const uint8_t cmd);
46
+    static void begin_data();
47
+
48
+    static void write_str(const char *str);
49
+    static void write_str(const char *str, const uint8_t len);
50
+    static void write_str_P(const char * const str);
51
+    static void write_str(progmem_str str);
52
+    static void write_number(const int16_t value, const uint8_t digits=3);
53
+
54
+    static void _extended_function_set(const bool extended, const bool graphics);
55
+    static void _scroll_or_addr_select(const bool sa);
56
+    static void reset_state_from_unknown();
57
+
58
+    static void home();
59
+    static void display_status(const bool display_on, const bool cursor_on, const bool blink_on);
60
+    static void extended_function_set(const bool extended);
61
+    static void graphics(const bool graphics);
62
+    static void entry_mode_select(const bool ac_increase, const bool shift);
63
+    static void scroll_or_addr_select(const bool sa);
64
+    static void set_ddram_address(const uint8_t addr);
65
+    static void set_cgram_address(const uint8_t addr);
66
+    static void set_gdram_address(const uint8_t x, const uint8_t y);
67
+
68
+    static void clear();
69
+    static void clear_ddram();
70
+    static void clear_gdram();
71
+
72
+    static void load_cgram_icon(const uint16_t addr, const void *data);
73
+    static void draw_gdram_icon(uint8_t x, uint8_t y, const void *data);
74
+
75
+    static uint8_t string_checksum(const char *str);
76
+
77
+  protected:
78
+    static void draw_static_elements();
79
+    static void draw_progress_bar(const uint8_t value);
80
+    static void draw_fan_icon(const bool whichIcon);
81
+    static void draw_heat_icon(const bool whichIcon, const bool heating);
82
+    static void draw_extruder_1_temp(const int16_t temp, const int16_t target);
83
+    static void draw_extruder_2_temp(const int16_t temp, const int16_t target);
84
+    static void draw_bed_temp(const int16_t temp, const int16_t target);
85
+    static void draw_fan_speed(const uint8_t value);
86
+    static void draw_print_time(const uint32_t elapsed);
87
+    static void draw_feedrate_percentage(const uint8_t percentage);
88
+    static void draw_status_message(const char *str);
89
+    static void draw_position(const float x, const float y, const float z, bool position_known = true);
90
+
91
+    static bool indicators_changed();
92
+    static bool position_changed();
93
+    static bool blink_changed();
94
+    static bool status_changed();
95
+
96
+    static void update_indicators(const bool forceUpdate);
97
+    static void update_position(const bool forceUpdate, bool resetChecksum);
98
+    static void update_status_or_position(bool forceUpdate);
99
+    static void update_progress(const bool forceUpdate);
100
+
101
+  public:
102
+    static void update(const bool forceUpdate);
103
+    static void on_entry();
104
+    static void on_exit();
105
+    static void clear_text_buffer();
106
+};
107
+
108
+#endif // STATUS_SCREEN_LITE_ST7920_CLASS_H

+ 39
- 0
Marlin/src/lcd/dogm/status_screen_lite_ST7920_spi.h ファイルの表示

@@ -0,0 +1,39 @@
1
+/**
2
+ * Lightweight Status Screen for the RepRapDiscount Full
3
+ * Graphics Smart Controller (ST7920-based 128x64 LCD)
4
+ *
5
+ * (c) 2017 Aleph Objects, Inc.
6
+ *
7
+ * The code in this page is free software: you can
8
+ * redistribute it and/or modify it under the terms of the GNU
9
+ * General Public License (GNU GPL) as published by the Free Software
10
+ * Foundation, either version 3 of the License, or (at your option)
11
+ * any later version.  The code is distributed WITHOUT ANY WARRANTY;
12
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
13
+ * FOR A PARTICULAR PURPOSE.  See the GNU GPL for more details.
14
+ *
15
+ */
16
+
17
+#include "status_screen_lite_ST7920_class.h"
18
+
19
+void ST7920_Lite_Status_Screen::cs() {
20
+  ST7920_CS();
21
+  current_bits.synced = false;
22
+}
23
+
24
+void ST7920_Lite_Status_Screen::ncs() {
25
+  ST7920_NCS();
26
+  current_bits.synced = false;
27
+}
28
+
29
+void ST7920_Lite_Status_Screen::sync_cmd() {
30
+  ST7920_SET_CMD();
31
+}
32
+
33
+void ST7920_Lite_Status_Screen::sync_dat() {
34
+  ST7920_SET_DAT();
35
+}
36
+
37
+void ST7920_Lite_Status_Screen::write_byte(const uint8_t data) {
38
+  ST7920_WRITE_BYTE(data);
39
+}

+ 11
- 3
Marlin/src/lcd/dogm/ultralcd_st7920_u8glib_rrd_AVR.cpp ファイルの表示

@@ -125,10 +125,12 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
125 125
       ST7920_CS();
126 126
       u8g_Delay(120);                 //initial delay for boot up
127 127
       ST7920_SET_CMD();
128
+      ST7920_WRITE_BYTE(0x20);       //non-extended mode
128 129
       ST7920_WRITE_BYTE(0x08);       //display off, cursor+blink off
129
-      ST7920_WRITE_BYTE(0x01);       //clear CGRAM ram
130
-      u8g_Delay(15);                 //delay for CGRAM clear
131
-      ST7920_WRITE_BYTE(0x3E);       //extended mode + GDRAM active
130
+      ST7920_WRITE_BYTE(0x01);       //clear DDRAM ram
131
+      u8g_Delay(15);                    //delay for DDRAM clear
132
+      ST7920_WRITE_BYTE(0x24);       //extended mode
133
+      ST7920_WRITE_BYTE(0x26);       //extended mode + GDRAM active
132 134
       for (y = 0; y < (LCD_PIXEL_HEIGHT) / 2; y++) { //clear GDRAM
133 135
         ST7920_WRITE_BYTE(0x80 | y); //set y
134 136
         ST7920_WRITE_BYTE(0x80);     //set x = 0
@@ -182,6 +184,12 @@ uint8_t   u8g_dev_st7920_128x64_rrd_buf[(LCD_PIXEL_WIDTH) * (PAGE_HEIGHT) / 8] U
182 184
 u8g_pb_t  u8g_dev_st7920_128x64_rrd_pb = {{PAGE_HEIGHT, LCD_PIXEL_HEIGHT, 0, 0, 0}, LCD_PIXEL_WIDTH, u8g_dev_st7920_128x64_rrd_buf};
183 185
 u8g_dev_t u8g_dev_st7920_128x64_rrd_sw_spi = {u8g_dev_rrd_st7920_128x64_fn, &u8g_dev_st7920_128x64_rrd_pb, &u8g_com_null_fn};
184 186
 
187
+#if ENABLED(LIGHTWEIGHT_UI)
188
+  // We have to include the code for the lightweight UI here
189
+  // as it relies on macros that are only defined in this file.
190
+  #include "status_screen_lite_ST7920_spi.h"
191
+#endif
192
+
185 193
 #pragma GCC reset_options
186 194
 
187 195
 #endif // U8G_HAL_LINKS

+ 28
- 14
Marlin/src/lcd/ultralcd.cpp ファイルの表示

@@ -5141,20 +5141,34 @@ void lcd_update() {
5141 5141
       #endif
5142 5142
 
5143 5143
       #if ENABLED(DOGLCD)
5144
-        if (!drawing_screen) {                        // If not already drawing pages
5145
-          u8g.firstPage();                            // Start the first page
5146
-          drawing_screen = 1;                         // Flag as drawing pages
5147
-        }
5148
-        lcd_setFont(FONT_MENU);                       // Setup font for every page draw
5149
-        u8g.setColorIndex(1);                         // And reset the color
5150
-        CURRENTSCREEN();                              // Draw and process the current screen
5151
-
5152
-        // The screen handler can clear drawing_screen for an action that changes the screen.
5153
-        // If still drawing and there's another page, update max-time and return now.
5154
-        // The nextPage will already be set up on the next call.
5155
-        if (drawing_screen && (drawing_screen = u8g.nextPage())) {
5156
-          NOLESS(max_display_update_time, millis() - ms);
5157
-          return;
5144
+        #if ENABLED(LIGHTWEIGHT_UI)
5145
+          #if ENABLED(ULTIPANEL)
5146
+            const bool in_status = currentScreen == lcd_status_screen;
5147
+          #else
5148
+            constexpr bool in_status = true;
5149
+          #endif
5150
+          const bool do_u8g_loop = !in_status;
5151
+          lcd_in_status(in_status);
5152
+          if (in_status) lcd_status_screen();
5153
+        #else
5154
+          constexpr bool do_u8g_loop = true;
5155
+        #endif
5156
+        if (do_u8g_loop) {
5157
+          if (!drawing_screen) {                        // If not already drawing pages
5158
+            u8g.firstPage();                            // Start the first page
5159
+            drawing_screen = 1;                         // Flag as drawing pages
5160
+          }
5161
+          lcd_setFont(FONT_MENU);                       // Setup font for every page draw
5162
+          u8g.setColorIndex(1);                         // And reset the color
5163
+          CURRENTSCREEN();                              // Draw and process the current screen
5164
+
5165
+          // The screen handler can clear drawing_screen for an action that changes the screen.
5166
+          // If still drawing and there's another page, update max-time and return now.
5167
+          // The nextPage will already be set up on the next call.
5168
+          if (drawing_screen && (drawing_screen = u8g.nextPage())) {
5169
+            NOLESS(max_display_update_time, millis() - ms);
5170
+            return;
5171
+          }
5158 5172
         }
5159 5173
       #else
5160 5174
         CURRENTSCREEN();

+ 9
- 414
Marlin/src/lcd/ultralcd_impl_DOGM.h ファイルの表示

@@ -353,6 +353,12 @@ void lcd_printPGM_utf(const char *str, uint8_t n=LCD_WIDTH) {
353 353
 
354 354
 #endif // SHOW_BOOTSCREEN
355 355
 
356
+#if ENABLED(LIGHTWEIGHT_UI)
357
+  #include "dogm/status_screen_lite_ST7920.h"
358
+#else
359
+  #include "dogm/status_screen_DOGM.h"
360
+#endif
361
+
356 362
 // Initialize or re-initialize the LCD
357 363
 static void lcd_implementation_init() {
358 364
 
@@ -383,6 +389,9 @@ static void lcd_implementation_init() {
383 389
 
384 390
 // The kill screen is displayed for unrecoverable conditions
385 391
 void lcd_kill_screen() {
392
+  #if ENABLED(LIGHTWEIGHT_UI)
393
+    ST7920_Lite_Status_Screen::clear_text_buffer();
394
+  #endif
386 395
   const uint8_t h4 = u8g.getHeight() / 4;
387 396
   u8g.firstPage();
388 397
   do {
@@ -398,420 +407,6 @@ void lcd_kill_screen() {
398 407
 
399 408
 void lcd_implementation_clear() { } // Automatically cleared by Picture Loop
400 409
 
401
-//
402
-// Status Screen
403
-//
404
-
405
-FORCE_INLINE void _draw_centered_temp(const int16_t temp, const uint8_t x, const uint8_t y) {
406
-  const char * const str = itostr3(temp);
407
-  u8g.setPrintPos(x - (str[0] != ' ' ? 0 : str[1] != ' ' ? 1 : 2) * DOG_CHAR_WIDTH / 2, y);
408
-  lcd_print(str);
409
-  lcd_printPGM(PSTR(LCD_STR_DEGREE " "));
410
-}
411
-
412
-#ifndef HEAT_INDICATOR_X
413
-  #define HEAT_INDICATOR_X 8
414
-#endif
415
-
416
-FORCE_INLINE void _draw_heater_status(const uint8_t x, const int8_t heater, const bool blink) {
417
-  #if !HEATER_IDLE_HANDLER
418
-    UNUSED(blink);
419
-  #endif
420
-
421
-  #if HAS_TEMP_BED
422
-    const bool isBed = heater < 0;
423
-  #else
424
-    constexpr bool isBed = false;
425
-  #endif
426
-
427
-  if (PAGE_UNDER(7)) {
428
-    #if HEATER_IDLE_HANDLER
429
-      const bool is_idle = (!isBed ? thermalManager.is_heater_idle(heater) :
430
-        #if HAS_TEMP_BED
431
-          thermalManager.is_bed_idle()
432
-        #else
433
-          false
434
-        #endif
435
-      );
436
-
437
-      if (blink || !is_idle)
438
-    #endif
439
-    _draw_centered_temp((isBed ? thermalManager.degTargetBed() : thermalManager.degTargetHotend(heater)) + 0.5, x, 7); }
440
-
441
-  if (PAGE_CONTAINS(21, 28))
442
-    _draw_centered_temp((isBed ? thermalManager.degBed() : thermalManager.degHotend(heater)) + 0.5, x, 28);
443
-
444
-  if (PAGE_CONTAINS(17, 20)) {
445
-    const uint8_t h = isBed ? 7 : HEAT_INDICATOR_X,
446
-                  y = isBed ? 18 : 17;
447
-    if (isBed ? thermalManager.isHeatingBed() : thermalManager.isHeatingHotend(heater)) {
448
-      u8g.setColorIndex(0); // white on black
449
-      u8g.drawBox(x + h, y, 2, 2);
450
-      u8g.setColorIndex(1); // black on white
451
-    }
452
-    else {
453
-      u8g.drawBox(x + h, y, 2, 2);
454
-    }
455
-  }
456
-}
457
-
458
-FORCE_INLINE void _draw_axis_label(const AxisEnum axis, const char* const pstr, const bool blink) {
459
-  if (blink)
460
-    lcd_printPGM(pstr);
461
-  else {
462
-    if (!axis_homed[axis])
463
-      u8g.print('?');
464
-    else {
465
-      #if DISABLED(HOME_AFTER_DEACTIVATE) && DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
466
-        if (!axis_known_position[axis])
467
-          u8g.print(' ');
468
-        else
469
-      #endif
470
-          lcd_printPGM(pstr);
471
-    }
472
-  }
473
-}
474
-
475
-inline void lcd_implementation_status_message(const bool blink) {
476
-  #if ENABLED(STATUS_MESSAGE_SCROLLING)
477
-    static bool last_blink = false;
478
-    const uint8_t slen = lcd_strlen(lcd_status_message);
479
-    const char *stat = lcd_status_message + status_scroll_pos;
480
-    if (slen <= LCD_WIDTH)
481
-      lcd_print_utf(stat);                                      // The string isn't scrolling
482
-    else {
483
-      if (status_scroll_pos <= slen - LCD_WIDTH)
484
-        lcd_print_utf(stat);                                    // The string fills the screen
485
-      else {
486
-        uint8_t chars = LCD_WIDTH;
487
-        if (status_scroll_pos < slen) {                         // First string still visible
488
-          lcd_print_utf(stat);                                  // The string leaves space
489
-          chars -= slen - status_scroll_pos;                    // Amount of space left
490
-        }
491
-        u8g.print('.');                                         // Always at 1+ spaces left, draw a dot
492
-        if (--chars) {
493
-          if (status_scroll_pos < slen + 1)                     // Draw a second dot if there's space
494
-            --chars, u8g.print('.');
495
-          if (chars) lcd_print_utf(lcd_status_message, chars);  // Print a second copy of the message
496
-        }
497
-      }
498
-      if (last_blink != blink) {
499
-        last_blink = blink;
500
-        // Skip any non-printing bytes
501
-        if (status_scroll_pos < slen) while (!PRINTABLE(lcd_status_message[status_scroll_pos])) status_scroll_pos++;
502
-        if (++status_scroll_pos >= slen + 2) status_scroll_pos = 0;
503
-      }
504
-    }
505
-  #else
506
-    UNUSED(blink);
507
-    lcd_print_utf(lcd_status_message);
508
-  #endif
509
-}
510
-
511
-static void lcd_implementation_status_screen() {
512
-
513
-  const bool blink = lcd_blink();
514
-
515
-  #if FAN_ANIM_FRAMES > 2
516
-    static bool old_blink;
517
-    static uint8_t fan_frame;
518
-    if (old_blink != blink) {
519
-      old_blink = blink;
520
-      if (!fanSpeeds[0] || ++fan_frame >= FAN_ANIM_FRAMES) fan_frame = 0;
521
-    }
522
-  #endif
523
-
524
-  // Status Menu Font
525
-  lcd_setFont(FONT_STATUSMENU);
526
-
527
-  //
528
-  // Fan Animation
529
-  //
530
-  // Draws the whole heading image as a B/W bitmap rather than
531
-  // drawing the elements separately.
532
-  // This was done as an optimization, as it was slower to draw
533
-  // multiple parts compared to a single bitmap.
534
-  //
535
-  // The bitmap:
536
-  // - May be offset in X
537
-  // - Includes all nozzle(s), bed(s), and the fan.
538
-  //
539
-  // TODO:
540
-  //
541
-  // - Only draw the whole header on the first
542
-  //   entry to the status screen. Nozzle, bed, and
543
-  //   fan outline bits don't change.
544
-  //
545
-  if (PAGE_UNDER(STATUS_SCREENHEIGHT + 1)) {
546
-
547
-    u8g.drawBitmapP(
548
-      STATUS_SCREEN_X, STATUS_SCREEN_Y,
549
-      (STATUS_SCREENWIDTH + 7) / 8, STATUS_SCREENHEIGHT,
550
-      #if HAS_FAN0
551
-        #if FAN_ANIM_FRAMES > 2
552
-          fan_frame == 1 ? status_screen1_bmp :
553
-          fan_frame == 2 ? status_screen2_bmp :
554
-          #if FAN_ANIM_FRAMES > 3
555
-            fan_frame == 3 ? status_screen3_bmp :
556
-          #endif
557
-        #else
558
-          blink && fanSpeeds[0] ? status_screen1_bmp :
559
-        #endif
560
-      #endif
561
-      status_screen0_bmp
562
-    );
563
-
564
-  }
565
-
566
-  //
567
-  // Temperature Graphics and Info
568
-  //
569
-
570
-  if (PAGE_UNDER(28)) {
571
-    // Extruders
572
-    HOTEND_LOOP() _draw_heater_status(STATUS_SCREEN_HOTEND_TEXT_X(e), e, blink);
573
-
574
-    // Heated bed
575
-    #if HOTENDS < 4 && HAS_TEMP_BED
576
-      _draw_heater_status(STATUS_SCREEN_BED_TEXT_X, -1, blink);
577
-    #endif
578
-
579
-    #if HAS_FAN0
580
-      if (PAGE_CONTAINS(20, 27)) {
581
-        // Fan
582
-        const int16_t per = ((fanSpeeds[0] + 1) * 100) / 256;
583
-        if (per) {
584
-          u8g.setPrintPos(STATUS_SCREEN_FAN_TEXT_X, STATUS_SCREEN_FAN_TEXT_Y);
585
-          lcd_print(itostr3(per));
586
-          u8g.print('%');
587
-        }
588
-      }
589
-    #endif
590
-  }
591
-
592
-  #if ENABLED(SDSUPPORT)
593
-    //
594
-    // SD Card Symbol
595
-    //
596
-    if (card.isFileOpen() && PAGE_CONTAINS(42 - (TALL_FONT_CORRECTION), 51 - (TALL_FONT_CORRECTION))) {
597
-      // Upper box
598
-      u8g.drawBox(42, 42 - (TALL_FONT_CORRECTION), 8, 7);     // 42-48 (or 41-47)
599
-      // Right edge
600
-      u8g.drawBox(50, 44 - (TALL_FONT_CORRECTION), 2, 5);     // 44-48 (or 43-47)
601
-      // Bottom hollow box
602
-      u8g.drawFrame(42, 49 - (TALL_FONT_CORRECTION), 10, 4);  // 49-52 (or 48-51)
603
-      // Corner pixel
604
-      u8g.drawPixel(50, 43 - (TALL_FONT_CORRECTION));         // 43 (or 42)
605
-    }
606
-  #endif // SDSUPPORT
607
-
608
-  #if ENABLED(SDSUPPORT) || ENABLED(LCD_SET_PROGRESS_MANUALLY)
609
-    //
610
-    // Progress bar frame
611
-    //
612
-    #define PROGRESS_BAR_X 54
613
-    #define PROGRESS_BAR_WIDTH (LCD_PIXEL_WIDTH - PROGRESS_BAR_X)
614
-
615
-    if (PAGE_CONTAINS(49, 52 - (TALL_FONT_CORRECTION)))       // 49-52 (or 49-51)
616
-      u8g.drawFrame(
617
-        PROGRESS_BAR_X, 49,
618
-        PROGRESS_BAR_WIDTH, 4 - (TALL_FONT_CORRECTION)
619
-      );
620
-
621
-    #if DISABLED(LCD_SET_PROGRESS_MANUALLY)
622
-      const uint8_t progress_bar_percent = card.percentDone();
623
-    #endif
624
-
625
-    if (progress_bar_percent > 1) {
626
-
627
-      //
628
-      // Progress bar solid part
629
-      //
630
-
631
-      if (PAGE_CONTAINS(50, 51 - (TALL_FONT_CORRECTION)))     // 50-51 (or just 50)
632
-        u8g.drawBox(
633
-          PROGRESS_BAR_X + 1, 50,
634
-          (uint16_t)((PROGRESS_BAR_WIDTH - 2) * progress_bar_percent * 0.01), 2 - (TALL_FONT_CORRECTION)
635
-        );
636
-
637
-      //
638
-      // SD Percent Complete
639
-      //
640
-
641
-      #if ENABLED(DOGM_SD_PERCENT)
642
-        if (PAGE_CONTAINS(41, 48)) {
643
-          // Percent complete
644
-          u8g.setPrintPos(55, 48);
645
-          u8g.print(itostr3(progress_bar_percent));
646
-          u8g.print('%');
647
-        }
648
-      #endif
649
-    }
650
-
651
-    //
652
-    // Elapsed Time
653
-    //
654
-
655
-    #if DISABLED(DOGM_SD_PERCENT)
656
-      #define SD_DURATION_X (PROGRESS_BAR_X + (PROGRESS_BAR_WIDTH / 2) - len * (DOG_CHAR_WIDTH / 2))
657
-    #else
658
-      #define SD_DURATION_X (LCD_PIXEL_WIDTH - len * DOG_CHAR_WIDTH)
659
-    #endif
660
-
661
-    if (PAGE_CONTAINS(41, 48)) {
662
-      char buffer[10];
663
-      duration_t elapsed = print_job_timer.duration();
664
-      bool has_days = (elapsed.value >= 60*60*24L);
665
-      uint8_t len = elapsed.toDigital(buffer, has_days);
666
-      u8g.setPrintPos(SD_DURATION_X, 48);
667
-      lcd_print(buffer);
668
-    }
669
-
670
-  #endif // SDSUPPORT || LCD_SET_PROGRESS_MANUALLY
671
-
672
-  //
673
-  // XYZ Coordinates
674
-  //
675
-
676
-  #if ENABLED(USE_SMALL_INFOFONT)
677
-    #define INFO_FONT_HEIGHT 7
678
-  #else
679
-    #define INFO_FONT_HEIGHT 8
680
-  #endif
681
-
682
-  #define XYZ_BASELINE (30 + INFO_FONT_HEIGHT)
683
-
684
-  #define X_LABEL_POS  3
685
-  #define X_VALUE_POS 11
686
-  #define XYZ_SPACING 40
687
-
688
-  #if ENABLED(XYZ_HOLLOW_FRAME)
689
-    #define XYZ_FRAME_TOP 29
690
-    #define XYZ_FRAME_HEIGHT INFO_FONT_HEIGHT + 3
691
-  #else
692
-    #define XYZ_FRAME_TOP 30
693
-    #define XYZ_FRAME_HEIGHT INFO_FONT_HEIGHT + 1
694
-  #endif
695
-
696
-  // Before homing the axis letters are blinking 'X' <-> '?'.
697
-  // When axis is homed but axis_known_position is false the axis letters are blinking 'X' <-> ' '.
698
-  // When everything is ok you see a constant 'X'.
699
-
700
-  static char xstring[5], ystring[5], zstring[7];
701
-  #if ENABLED(FILAMENT_LCD_DISPLAY)
702
-    static char wstring[5], mstring[4];
703
-  #endif
704
-
705
-  // At the first page, regenerate the XYZ strings
706
-  if (page.page == 0) {
707
-    strcpy(xstring, ftostr4sign(LOGICAL_X_POSITION(current_position[X_AXIS])));
708
-    strcpy(ystring, ftostr4sign(LOGICAL_Y_POSITION(current_position[Y_AXIS])));
709
-    strcpy(zstring, ftostr52sp(FIXFLOAT(LOGICAL_Z_POSITION(current_position[Z_AXIS]))));
710
-    #if ENABLED(FILAMENT_LCD_DISPLAY)
711
-      strcpy(wstring, ftostr12ns(filament_width_meas));
712
-      strcpy(mstring, itostr3(100.0 * (
713
-          parser.volumetric_enabled
714
-            ? planner.volumetric_area_nominal / planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
715
-            : planner.volumetric_multiplier[FILAMENT_SENSOR_EXTRUDER_NUM]
716
-        )
717
-      ));
718
-    #endif
719
-  }
720
-
721
-  if (PAGE_CONTAINS(XYZ_FRAME_TOP, XYZ_FRAME_TOP + XYZ_FRAME_HEIGHT - 1)) {
722
-
723
-    #if ENABLED(XYZ_HOLLOW_FRAME)
724
-      u8g.drawFrame(0, XYZ_FRAME_TOP, LCD_PIXEL_WIDTH, XYZ_FRAME_HEIGHT); // 8: 29-40  7: 29-39
725
-    #else
726
-      u8g.drawBox(0, XYZ_FRAME_TOP, LCD_PIXEL_WIDTH, XYZ_FRAME_HEIGHT);   // 8: 30-39  7: 30-37
727
-    #endif
728
-
729
-    if (PAGE_CONTAINS(XYZ_BASELINE - (INFO_FONT_HEIGHT - 1), XYZ_BASELINE)) {
730
-
731
-      #if DISABLED(XYZ_HOLLOW_FRAME)
732
-        u8g.setColorIndex(0); // white on black
733
-      #endif
734
-
735
-      u8g.setPrintPos(0 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
736
-      _draw_axis_label(X_AXIS, PSTR(MSG_X), blink);
737
-      u8g.setPrintPos(0 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
738
-      lcd_print(xstring);
739
-
740
-      u8g.setPrintPos(1 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
741
-      _draw_axis_label(Y_AXIS, PSTR(MSG_Y), blink);
742
-      u8g.setPrintPos(1 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
743
-      lcd_print(ystring);
744
-
745
-      u8g.setPrintPos(2 * XYZ_SPACING + X_LABEL_POS, XYZ_BASELINE);
746
-      _draw_axis_label(Z_AXIS, PSTR(MSG_Z), blink);
747
-      u8g.setPrintPos(2 * XYZ_SPACING + X_VALUE_POS, XYZ_BASELINE);
748
-      lcd_print(zstring);
749
-
750
-      #if DISABLED(XYZ_HOLLOW_FRAME)
751
-        u8g.setColorIndex(1); // black on white
752
-      #endif
753
-    }
754
-  }
755
-
756
-  //
757
-  // Feedrate
758
-  //
759
-
760
-  if (PAGE_CONTAINS(51 - INFO_FONT_HEIGHT, 49)) {
761
-    lcd_setFont(FONT_MENU);
762
-    u8g.setPrintPos(3, 50);
763
-    lcd_print(LCD_STR_FEEDRATE[0]);
764
-
765
-    lcd_setFont(FONT_STATUSMENU);
766
-    u8g.setPrintPos(12, 50);
767
-    lcd_print(itostr3(feedrate_percentage));
768
-    u8g.print('%');
769
-
770
-    //
771
-    // Filament sensor display if SD is disabled
772
-    //
773
-    #if ENABLED(FILAMENT_LCD_DISPLAY) && DISABLED(SDSUPPORT)
774
-      u8g.setPrintPos(56, 50);
775
-      lcd_print(wstring);
776
-      u8g.setPrintPos(102, 50);
777
-      lcd_print(mstring);
778
-      u8g.print('%');
779
-      lcd_setFont(FONT_MENU);
780
-      u8g.setPrintPos(47, 50);
781
-      lcd_print(LCD_STR_FILAM_DIA);
782
-      u8g.setPrintPos(93, 50);
783
-      lcd_print(LCD_STR_FILAM_MUL);
784
-    #endif
785
-  }
786
-
787
-  //
788
-  // Status line
789
-  //
790
-
791
-  #define STATUS_BASELINE (55 + INFO_FONT_HEIGHT)
792
-
793
-  if (PAGE_CONTAINS(STATUS_BASELINE - (INFO_FONT_HEIGHT - 1), STATUS_BASELINE)) {
794
-    u8g.setPrintPos(0, STATUS_BASELINE);
795
-
796
-    #if ENABLED(FILAMENT_LCD_DISPLAY) && ENABLED(SDSUPPORT)
797
-      if (PENDING(millis(), previous_lcd_status_ms + 5000UL)) {  //Display both Status message line and Filament display on the last line
798
-        lcd_implementation_status_message(blink);
799
-      }
800
-      else {
801
-        lcd_printPGM(PSTR(LCD_STR_FILAM_DIA));
802
-        u8g.print(':');
803
-        lcd_print(wstring);
804
-        lcd_printPGM(PSTR("  " LCD_STR_FILAM_MUL));
805
-        u8g.print(':');
806
-        lcd_print(mstring);
807
-        u8g.print('%');
808
-      }
809
-    #else
810
-      lcd_implementation_status_message(blink);
811
-    #endif
812
-  }
813
-}
814
-
815 410
 #if ENABLED(ULTIPANEL)
816 411
 
817 412
   uint8_t row_y1, row_y2;

読み込み中…
キャンセル
保存