Improvements to the Hibiscus LCD and CLCD.

- The B&W LCD will now support scrolling of long status messages
- The B&W LCD code was improved to be more efficient
- The Color LCD code now allows you to touch the temperatures to go into the temp tuning screens
  or the positions to go into the move axis screen.
master
Marcio Teixeira 6 years ago
parent 68ae8bc8ac
commit 8ad9a2720e

@ -411,6 +411,7 @@ void AboutScreen::onIdle() {
void StatusScreen::static_axis_position() {
CLCD::CommandFifo cmd;
BTN_TAG(6)
#if defined(LCD_PORTRAIT)
THEME(axis_label) BTN( BTN_POS(1,5), BTN_SIZE(2,1), F(""), FONT_LRG, OPT_FLAT);
THEME(axis_label) BTN( BTN_POS(1,6), BTN_SIZE(2,1), F(""), FONT_LRG, OPT_FLAT);
@ -449,6 +450,7 @@ void StatusScreen::dynamic_axis_position() {
strcat_P(y_str, PSTR(" mm"));
strcat_P(z_str, PSTR(" mm"));
BTN_TAG(6)
#if defined(LCD_PORTRAIT)
BTX( BTN_POS(2,5), BTN_SIZE(2,1), x_str, FONT_MED);
BTX( BTN_POS(2,6), BTN_SIZE(2,1), y_str, FONT_MED);
@ -471,17 +473,20 @@ void StatusScreen::dynamic_axis_position() {
void StatusScreen::static_temperature() {
CLCD::CommandFifo cmd;
BTN_TAG(0)
#if defined(LCD_PORTRAIT)
BTN_TAG(5)
THEME(temp) BTN( BTN_POS(1,1), BTN_SIZE(4,2), F(""), FONT_SML, OPT_FLAT);
THEME(temp) BTN( BTN_POS(1,1), BTN_SIZE(8,1), F(""), FONT_SML, OPT_FLAT);
THEME(fan_speed) BTN( BTN_POS(5,2), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
BTN_TAG(0)
THEME(progress) BTN( BTN_POS(1,3), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
THEME(progress) BTN( BTN_POS(5,3), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
#else
BTN_TAG(5)
THEME(temp) BTN( BTN_POS(1,1), BTN_SIZE(4,2), F(""), FONT_SML, OPT_FLAT);
THEME(temp) BTN( BTN_POS(1,1), BTN_SIZE(8,1), F(""), FONT_SML, OPT_FLAT);
THEME(fan_speed) BTN( BTN_POS(5,2), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
BTN_TAG(0)
THEME(progress) BTN( BTN_POS(9,1), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
THEME(progress) BTN( BTN_POS(9,2), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
#endif
@ -492,7 +497,7 @@ void StatusScreen::static_temperature() {
cmd.Cmd_Bitmap_Layout(Extruder_Icon_Info);
cmd.Cmd_Bitmap_Size (Extruder_Icon_Info);
BTN_TAG(0)
BTN_TAG(5)
BTI(BTN_POS(1,1), BTN_SIZE(1,1), Extruder_Icon_Info, Theme::icon_scale);
BTI(BTN_POS(5,1), BTN_SIZE(1,1), Extruder_Icon_Info, Theme::icon_scale);
@ -501,7 +506,6 @@ void StatusScreen::static_temperature() {
cmd.Cmd_Bitmap_Layout(Bed_Heat_Icon_Info);
cmd.Cmd_Bitmap_Size (Bed_Heat_Icon_Info);
BTN_TAG(0)
BTI(BTN_POS(1,2), BTN_SIZE(1,1), Bed_Heat_Icon_Info, Theme::icon_scale);
// Draw Fan Percent Bitmap on Bed Heat Button
@ -510,7 +514,6 @@ void StatusScreen::static_temperature() {
cmd.Cmd_Bitmap_Layout(Fan_Icon_Info);
cmd.Cmd_Bitmap_Size (Fan_Icon_Info);
BTN_TAG(0)
BTI(BTN_POS(5,2), BTN_SIZE(1,1), Fan_Icon_Info, Theme::icon_scale);
}
@ -558,6 +561,7 @@ void StatusScreen::dynamic_temperature() {
);
#endif
BTN_TAG(5)
BTX( BTN_POS(2,1), BTN_SIZE(3,1), e0_str, FONT_MED);
BTX( BTN_POS(6,1), BTN_SIZE(3,1), e1_str, FONT_MED);
BTX( BTN_POS(2,2), BTN_SIZE(3,1), bed_str, FONT_MED);
@ -567,6 +571,7 @@ void StatusScreen::dynamic_temperature() {
void StatusScreen::static_progress() {
CLCD::CommandFifo cmd;
BTN_TAG(0)
#if defined(LCD_PORTRAIT)
THEME(progress) BTN( BTN_POS(1,3), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
THEME(progress) BTN( BTN_POS(5,3), BTN_SIZE(4,1), F(""), FONT_SML, OPT_FLAT);
@ -697,7 +702,9 @@ void StatusScreen::onIdle() {
void StatusScreen::onTouchStart(uint8_t tag) {
switch(tag) {
case 4: GOTO_SCREEN(MenuScreen); break;
case 4: GOTO_SCREEN(MenuScreen); break;
case 5: GOTO_SCREEN(TemperatureScreen); break;
case 6: GOTO_SCREEN(MoveAxisScreen); break;
}
}

@ -13,7 +13,7 @@
* got disabled.
*/
#define LULZBOT_FW_VERSION ".6" // Change this with each update
#define LULZBOT_FW_VERSION ".7" // Change this with each update
#if ( \
!defined(LULZBOT_Gladiola_Mini) && \
@ -246,6 +246,9 @@
// Back port of upstream https://github.com/MarlinFirmware/Marlin/commit/6ed284061580ffc6eef40df391afb30d2f8b45f5
#define LULZBOT_OCTOPRINT_RX_BUFFER_OVERFLOW_WORKAROUND delay(2);
// Fix for bug where the LCD is not being properly cleared at startup
#define LULZBOT_LCD_CLEAR_WORKAROUND
/************************* EXPERIMENTAL FEATURES ******************************/
#if defined(LULZBOT_USE_EXPERIMENTAL_FEATURES)
@ -1905,17 +1908,14 @@
NOLESS(max_display_update_time, millis() - ms); \
return; \
}
#define LULZBOT_LCD_CLEAR_DECL void lcd_clear_text_buffer();
#define LULZBOT_LCD_CLEAR lcd_clear_text_buffer();
#if defined(LULZBOT_IS_MINI)
#define WELCOME_MSG _UxGT("Mini 2 ready.")
#else
#define WELCOME_MSG _UxGT("TAZ 7 ready.")
#endif
#define LULZBOT_DELAY_TO_SHOW_POSITION 20
#else
#define LULZBOT_ABOUT_TO_DRAW_SCREEN(a)
#define LULZBOT_LCD_CLEAR
#define LULZBOT_LCD_CLEAR_DECL
#endif
#if defined(LULZBOT_USE_TOUCH_UI)

@ -720,7 +720,6 @@ void lcd_reset_status() { lcd_setstatusPGM(PSTR(""), -1); }
void kill_screen(const char* lcd_msg) {
lcd_init();
lcd_setalertstatusPGM(lcd_msg);
LULZBOT_LCD_CLEAR
lcd_kill_screen();
}

@ -44,6 +44,11 @@
*/
#include "ultralcd.h"
#if ENABLED(LULZBOT_MODERN_UI)
typedef const __FlashStringHelper *progmem_str;
#include "ultralcd_impl_st7920_lite_status_screen_impl.h"
#endif
#if ENABLED(U8GLIB_ST7920)
#include "ultralcd_st7920_u8glib_rrd.h"
#endif
@ -365,6 +370,9 @@ static void lcd_implementation_init() {
// The kill screen is displayed for unrecoverable conditions
void lcd_kill_screen() {
#if ENABLED(LULZBOT_MODERN_UI)
ST7920_Lite_Status_Screen::clear_text_buffer();
#endif
u8g.firstPage();
do {
lcd_setFont(FONT_MENU);
@ -487,9 +495,7 @@ inline void lcd_implementation_status_message(const bool blink) {
//#define DOGM_SD_PERCENT
#if defined(LULZBOT_MODERN_UI)
#include "ultralcd_lulzbot.h"
#else
#if !defined(LULZBOT_MODERN_UI)
static void lcd_implementation_status_screen() {
const bool blink = lcd_blink();

@ -0,0 +1,247 @@
/*
* Lightweight Status Screen for the RepRapDiscount Full
* Graphics Smart Controller (ST7920-based 128x64 LCD)
*
* (c) 2017 Aleph Objects, Inc.
*
* The code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License (GNU GPL) as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. The code is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*
*/
#ifndef ULTRALCD_ST7920_LITE_STATUS_H
#define ULTRALCD_ST7920_LITE_STATUS_H
class ST7920_Lite_Status_Screen {
private:
static struct st7920_state_t {
uint8_t synced : 1; // Whether a sync has been sent
uint8_t cmd : 1; // Whether the sync was cmd or data
uint8_t extended : 1;
uint8_t graphics : 1;
uint8_t sa : 1;
} current_bits;
static void cs();
static void ncs();
static void sync_cmd();
static void sync_dat();
static void write_byte(uint8_t w);
static void cmd(uint8_t cmd);
static void begin_data();
static void write_word(uint16_t w);
static void write_str(const char *str);
static void write_str(const char *str, uint8_t len);
static void write_str_P(const char *str);
static void write_str(progmem_str str);
static void write_number(uint8_t value, uint8_t digits=3);
static void _extended_function_set(bool extended, bool graphics);
static void _scroll_or_addr_select(bool sa);
static void reset_state_from_unknown();
static void home();
static void display_status(bool display_on, bool cursor_on, bool blink_on);
static void extended_function_set(bool extended);
static void graphics(bool graphics);
static void entry_mode_select(bool ac_increase, bool shift);
static void scroll_or_addr_select(bool sa);
static void set_ddram_address(uint8_t addr);
static void set_cgram_address(uint8_t addr);
static void set_gdram_address(uint8_t x, uint8_t y);
static void clear();
static void clear_ddram();
static void clear_gdram();
static void load_cgram_icon(uint16_t addr, const void *data);
static void draw_gdram_icon(uint8_t x, uint8_t y, const void *data);
static uint8_t string_checksum(const char *str);
protected:
static void draw_static_elements();
static void draw_progress_bar(uint8_t value);
static void draw_fan_icon(bool whichIcon);
static void draw_heat_icon(bool whichIcon, bool heating);
static void draw_extruder_1_temp(uint8_t temp, uint8_t target);
static void draw_extruder_2_temp(uint8_t temp, uint8_t target);
static void draw_bed_temp(uint8_t temp, uint8_t target);
static void draw_fan_speed(uint8_t value);
static void draw_print_time(uint32_t elapsed);
static void draw_feedrate_percentage(uint8_t percentage);
static void draw_status_message(const char *str);
static void draw_position(const float x, const float y, const float z, bool position_known = true);
static bool indicators_changed();
static bool position_changed();
static bool blink_changed();
static bool status_changed();
static void update_indicators(bool forceUpdate);
static void update_position(bool forceUpdate, bool resetChecksum);
static void update_status_or_position(bool forceUpdate);
static void update_progress(bool forceUpdate);
public:
static void update(bool forceUpdate);
static void on_entry();
static void on_exit();
static void clear_text_buffer();
};
/************************** ICON DEFINITIONS *************************************/
#define CGRAM_ICON_1_ADDR 0x00
#define CGRAM_ICON_2_ADDR 0x10
#define CGRAM_ICON_3_ADDR 0x20
#define CGRAM_ICON_4_ADDR 0x30
#define CGRAM_ICON_1_WORD 0x00
#define CGRAM_ICON_2_WORD 0x02
#define CGRAM_ICON_3_WORD 0x04
#define CGRAM_ICON_4_WORD 0x06
PROGMEM const uint16_t nozzle_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000111111110000,
0b0001111111111000,
0b0001111111111000,
0b0001111111111000,
0b0000111111110000,
0b0000111111110000,
0b0001111111111000,
0b0001111111111000,
0b0001111111111000,
0b0000011111100000,
0b0000001111000000,
0b0000000110000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t bed_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0111111111111110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t heat1_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0010001000100000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t heat2_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0010001000100000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t fan1_icon[] = {
0b0000000000000000,
0b0111111111111110,
0b0111000000001110,
0b0110001111000110,
0b0100001111000010,
0b0100000110000010,
0b0101100000011010,
0b0101110110111010,
0b0101100000011010,
0b0100000110000010,
0b0100001111000010,
0b0110001111000110,
0b0111000000001110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t fan2_icon[] = {
0b0000000000000000,
0b0111111111111110,
0b0111000000001110,
0b0110010000100110,
0b0100111001110010,
0b0101111001111010,
0b0100110000110010,
0b0100000110000010,
0b0100110000110010,
0b0101111001111010,
0b0100111001110010,
0b0110010000100110,
0b0111000000001110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t feedrate_icon[] = {
0b0000000000000000,
0b0111111000000000,
0b0100000000000000,
0b0100000000000000,
0b0100000000000000,
0b0111111011111000,
0b0100000010000100,
0b0100000010000100,
0b0100000010000100,
0b0100000011111000,
0b0000000010001000,
0b0000000010000100,
0b0000000010000100,
0b0000000010000010,
0b0000000000000000,
0b0000000000000000
};
static void lcd_implementation_status_screen();
static void lcd_in_status(bool inStatus);
#endif // ULTRALCD_ST7920_LITE_STATUS_H

@ -0,0 +1,757 @@
/*
* Lightweight Status Screen for the RepRapDiscount Full
* Graphics Smart Controller (ST7920-based 128x64 LCD)
*
* (c) 2017 Aleph Objects, Inc.
*
* The code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License (GNU GPL) as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. The code is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*
*/
/* This is an implementation of a status screen for the RepRapDiscount
* Full Graphics Smart Controller using native ST7920 commands rather
* than using U8Glib.
*
* This alternative status screen makes use of the built-in character
* generation capabilities of the ST7920 to update the status screen
* with less SPI traffic and CPU use. In particular:
*
* - The fan and bed animations are handled using custom characters
* that are stored in CGRAM. This allows for the animation to be
* updated by writing a single character to the text-buffer (DDRAM).
*
* - All the information in the status screen is text that is written
* to DDRAM, so the work of generating the bitmaps is offloaded to
* the ST7920 rather than being render by U8Glib on the MCU.
*
* - The graphics buffer (GDRAM) is only used for static graphics
* elements (nozzle and feedrate bitmaps) and for the progress
* bar, so updates are sporadic.
*/
#include "duration_t.h"
typedef const __FlashStringHelper *progmem_str;
#include "ultralcd_impl_st7920_lite_status_screen.h"
#define BUFFER_WIDTH 256
#define BUFFER_HEIGHT 32
#define DDRAM_LINE_1 0x00
#define DDRAM_LINE_2 0x10
#define DDRAM_LINE_3 0x08
#define DDRAM_LINE_4 0x18
ST7920_Lite_Status_Screen::st7920_state_t ST7920_Lite_Status_Screen::current_bits;
void ST7920_Lite_Status_Screen::cmd(uint8_t cmd) {
if(!current_bits.synced || !current_bits.cmd) {
current_bits.synced = true;
current_bits.cmd = true;
sync_cmd();
}
write_byte(cmd);
}
void ST7920_Lite_Status_Screen::begin_data() {
extended_function_set(false);
if(!current_bits.synced || current_bits.cmd) {
current_bits.synced = true;
current_bits.cmd = false;
sync_dat();
}
}
void ST7920_Lite_Status_Screen::write_word(uint16_t w) {
write_byte((w >> 8) & 0xFF);
write_byte((w >> 0) & 0xFF);
}
void ST7920_Lite_Status_Screen::write_str(const char *str) {
while(*str) {
write_byte(*str++);
}
}
void ST7920_Lite_Status_Screen::write_str(const char *str, uint8_t len) {
while(*str && len--) {
write_byte(*str++);
}
}
void ST7920_Lite_Status_Screen::write_str_P(const char *str) {
const char *p_str = (const char *)str;
char c = pgm_read_byte_near(p_str++);
while(c) {
write_byte(c);
c = pgm_read_byte_near(p_str++);
}
}
void ST7920_Lite_Status_Screen::write_str(progmem_str str) {
write_str_P((const char*)str);
}
void ST7920_Lite_Status_Screen::write_number(uint8_t value, uint8_t digits) {
char str[7];
const char *fmt;
switch(digits) {
case 6: fmt = PSTR("%6d"); break;
case 5: fmt = PSTR("%5d"); break;
case 4: fmt = PSTR("%4d"); break;
case 3: fmt = PSTR("%3d"); break;
case 2: fmt = PSTR("%2d"); break;
case 1: fmt = PSTR("%1d"); break;
}
sprintf_P(str,fmt,value);
write_str(str);
}
void ST7920_Lite_Status_Screen::display_status(bool display_on, bool cursor_on, bool blink_on) {
extended_function_set(false);
cmd(0b00001000 |
(display_on ? 0b0100 : 0) |
(cursor_on ? 0b0010 : 0) |
(blink_on ? 0b0001 : 0)
);
}
// Sets the extended and graphics bits simultaneously, regardless of
// the current state. This is a helper function for extended_function_set()
// and graphics()
void ST7920_Lite_Status_Screen::_extended_function_set(bool extended, bool graphics) {
cmd( 0b00100000 |
(extended ? 0b00000100 : 0) |
(graphics ? 0b00000010 : 0)
);
current_bits.extended = extended;
current_bits.graphics = graphics;
}
void ST7920_Lite_Status_Screen::extended_function_set(bool extended) {
if(extended != current_bits.extended) {
_extended_function_set(extended, current_bits.graphics);
}
}
void ST7920_Lite_Status_Screen::graphics(bool graphics) {
if(graphics != current_bits.graphics) {
_extended_function_set(current_bits.extended, graphics);
}
}
void ST7920_Lite_Status_Screen::entry_mode_select(bool ac_increase, bool shift) {
extended_function_set(false);
cmd(0b00000100 |
(ac_increase ? 0b00000010 : 0) |
(shift ? 0b00000001 : 0)
);
}
// Sets the sa bit regardless of the current state. This is a helper
// function for scroll_or_addr_select()
void ST7920_Lite_Status_Screen::_scroll_or_addr_select(bool sa) {
extended_function_set(true);
cmd(0b00100010 |
(sa ? 0b000001 : 0)
);
current_bits.sa = sa;
}
void ST7920_Lite_Status_Screen::scroll_or_addr_select(bool sa) {
if(sa != current_bits.sa) {
_scroll_or_addr_select(sa);
}
}
void ST7920_Lite_Status_Screen::set_ddram_address(uint8_t addr) {
extended_function_set(false);
cmd(0b10000000 | (addr & 0b00111111));
}
void ST7920_Lite_Status_Screen::set_cgram_address(uint8_t addr) {
extended_function_set(false);
cmd(0b01000000 | (addr & 0b00111111));
}
void ST7920_Lite_Status_Screen::set_gdram_address(uint8_t x, uint8_t y) {
extended_function_set(true);
cmd(0b10000000 | (y & 0b01111111));
cmd(0b10000000 | (x & 0b00001111));
}
void ST7920_Lite_Status_Screen::clear() {
extended_function_set(false);
cmd(0x00000001);
delay(15); //delay for CGRAM clear
}
void ST7920_Lite_Status_Screen::home() {
extended_function_set(false);
cmd(0x00000010);
}
/* This fills the entire text buffer with spaces */
void ST7920_Lite_Status_Screen::clear_ddram()
{
set_ddram_address(DDRAM_LINE_1);
begin_data();
for(int i=0; i < 64;i++) {
write_byte(' ');
}
}
/* This fills the entire graphics buffer with zeros */
void ST7920_Lite_Status_Screen::clear_gdram()
{
for(int y = 0; y < BUFFER_HEIGHT; y++) {
set_gdram_address(0,y);
begin_data();
for(int i = 0; i < (BUFFER_WIDTH / 16); i++) {
write_byte(0);
write_byte(0);
}
}
}
void ST7920_Lite_Status_Screen::load_cgram_icon(uint16_t addr, const void *data) {
const uint16_t *p_word = (const uint16_t *)data;
set_cgram_address(addr);
begin_data();
for(int i = 0; i < 16; i++) {
uint16_t word = pgm_read_word_near(p_word++);
write_byte((word & 0xFF00) >> 8);
write_byte((word & 0x00FF) >> 0);
}
}
/* Draws an icon in GDRAM. The position is specified in
as if they were DDRAM coordinates, i.e. the x position
is [1-8], while the y position is [1-4] */
void ST7920_Lite_Status_Screen::draw_gdram_icon(uint8_t x, uint8_t y, const void *data) {
const uint16_t *p_word = (const uint16_t *)data;
if(y > 2) {
// Handle display folding
y -= 2;
x += 8;
}
x -= 1;
y -= 1;
for(int i = 0; i < 16; i++) {
uint16_t word = pgm_read_word_near(p_word++);
set_gdram_address(x,i+y*16);
begin_data();
write_byte((word & 0xFF00) >> 8);
write_byte((word & 0x00FF) >> 0);
}
}
/************************** MAIN SCREEN *************************************/
void ST7920_Lite_Status_Screen::draw_static_elements() {
scroll_or_addr_select(0);
// Load the animated bed and fan icons
load_cgram_icon(CGRAM_ICON_1_ADDR, heat1_icon);
load_cgram_icon(CGRAM_ICON_2_ADDR, heat2_icon);
load_cgram_icon(CGRAM_ICON_3_ADDR, fan1_icon);
load_cgram_icon(CGRAM_ICON_4_ADDR, fan2_icon);
// Draw the static icons in GDRAM
draw_gdram_icon(1,1,nozzle_icon);
#if EXTRUDERS == 2
draw_gdram_icon(1,2,nozzle_icon);
#endif
draw_gdram_icon(6,2,feedrate_icon);
draw_gdram_icon(1,2,bed_icon);
// Draw the initial fan icon
draw_fan_icon(false);
}
/* Although this is undocumented, the ST7920 allows the character
* data buffer (DDRAM) to be used in conjunction with the graphics
* bitmap buffer (CGRAM). The contents of the graphics buffer is
* XORed with the data from the character generator. This allows
* us to make the progess bar out of graphical data (the bar) and
* text data (the percentage).
*/
void ST7920_Lite_Status_Screen::draw_progress_bar(uint8_t value) {
#if EXTRUDERS == 1
// If we have only one extruder, draw a long progress bar on the third line
const int top = 1; // Top in pixels
const int bottom = 13; // Bottom in pixels
const int left = 8; // Left edge, in 16-bit words
const int width = 5; // Width of progress bar, in 16-bit words
#else
const int top = 16 + 1; // Top in pixels
const int bottom = 16 + 13; // Bottom in pixels
const int left = 5; // Left edge, in 16-bit words
const int width = 3; // Width of progress bar, in 16-bit words
#endif
const int char_pcnt = 100/width; // How many percent does each 16-bit word represent?
// Draw the progress bar as a bitmap in CGRAM
for(int y = top; y <= bottom; y++) {
set_gdram_address(left,y);
begin_data();
for(int x = 0; x < width; x++) {
uint16_t gfx_word = 0x0000;
if((x+1)*char_pcnt <= value) {
// Draw completely filled bytes
gfx_word = 0xFFFF;
} else if((x*char_pcnt) < value) {
// Draw partially filled bytes
gfx_word = int(0x8000) >> (value % char_pcnt)*16/char_pcnt;
}
// Draw the frame around the progress bar
if(y == top || y == bottom) {
// Draw top/bottom border
gfx_word = 0xFFFF;
} else if (x == (width-1)) {
// Draw right border
gfx_word |= 0x0001;
} else if (x == 0) {
// Draw left border
gfx_word |= 0x8000;
}
write_word(gfx_word);
}
}
// Draw the percentage as text in DDRAM
#if EXTRUDERS == 1
set_ddram_address(DDRAM_LINE_3 + 1);
#else
set_ddram_address(DDRAM_LINE_2 + left);
#endif
begin_data();
if(value > 9) {
write_number(value,4);
write_str(F("% "));
} else {
write_number(value,3);
write_str(F("% "));
}
}
void ST7920_Lite_Status_Screen::draw_fan_icon(bool whichIcon) {
set_ddram_address(DDRAM_LINE_1+5);
begin_data();
write_word(whichIcon ? CGRAM_ICON_3_WORD : CGRAM_ICON_4_WORD);
}
void ST7920_Lite_Status_Screen::draw_heat_icon(bool whichIcon, bool heating) {
#if EXTRUDERS == 1
set_ddram_address(DDRAM_LINE_2);
#else
set_ddram_address(DDRAM_LINE_3);
#endif
begin_data();
if(heating) {
write_word(whichIcon ? CGRAM_ICON_1_WORD : CGRAM_ICON_2_WORD);
} else {
write_byte(' ');
write_byte(' ');
}
}
#define FAR(a,b) (((a > b) ? (a-b) : (b-a)) > 1)
void ST7920_Lite_Status_Screen::draw_extruder_1_temp(uint8_t temp, uint8_t target) {
set_ddram_address(DDRAM_LINE_1+1);
begin_data();
write_number(temp);
if(target && FAR(temp, target)) {
write_str(F("\x1A"));
write_number(target);
} else {
write_str(F(" "));
}
}
void ST7920_Lite_Status_Screen::draw_extruder_2_temp(uint8_t temp, uint8_t target) {
set_ddram_address(DDRAM_LINE_2+1);
begin_data();
write_number(temp);
if(target && FAR(temp, target)) {
write_str(F("\x1A"));
write_number(target);
} else {
write_str(F(" "));
}
}
void ST7920_Lite_Status_Screen::draw_bed_temp(uint8_t temp, uint8_t target) {
#if EXTRUDERS == 1
set_ddram_address(DDRAM_LINE_2+1);
#else
set_ddram_address(DDRAM_LINE_3+1);
#endif
begin_data();
write_number(temp);
if(target && FAR(temp, target)) {
write_str(F("\x1A"));
write_number(target);
} else {
write_str(F(" "));
}
}
void ST7920_Lite_Status_Screen::draw_fan_speed(uint8_t value) {
set_ddram_address(DDRAM_LINE_1+6);
begin_data();
write_number(value,4);
}
void ST7920_Lite_Status_Screen::draw_print_time(uint32_t elapsed) {
const uint8_t hrs = elapsed/3600;
const uint8_t min = (elapsed/60)%60;
char str[7];
sprintf_P(str,hrs > 99 ? PSTR("%03d:%02d") : PSTR(" %02d:%02d"),hrs,min);
set_ddram_address(DDRAM_LINE_3+5);
begin_data();
write_str(str);
}
void ST7920_Lite_Status_Screen::draw_feedrate_percentage(uint8_t percentage) {
// We only have enough room for the feedrate when
// we have one extruder
#if EXTRUDERS == 1
set_ddram_address(DDRAM_LINE_2+6);
begin_data();
write_number(percentage,4);
#endif
}
void ST7920_Lite_Status_Screen::draw_status_message(const char *str) {
set_ddram_address(DDRAM_LINE_4);
begin_data();
#if ENABLED(STATUS_MESSAGE_SCROLLING)
const uint8_t lcd_len = 16;
const uint8_t padding = 2;
uint8_t str_len = strlen(str);
// Trim whitespace at the end of the str, as for some reason
// messages like "Card Inserted" are padded with many spaces
while(str_len > 0 && str[str_len-1] == ' ') {
str_len--;
}
if(str_len <= lcd_len) {
// It all fits on the LCD without scrolling
write_str(str);
} else {
// Print the message repeatedly until covering the LCD
uint8_t c = status_scroll_pos;
for(uint8_t n = 0; n < lcd_len; n++) {
write_byte(c < str_len ? str[c] : ' ');
c++;
c %= str_len + padding; // Wrap around
}
// Scroll the message
if(status_scroll_pos == str_len + padding) {
status_scroll_pos = 0;
} else {
status_scroll_pos++;
}
}
#else
write_str(str, 16);
#endif
}
void ST7920_Lite_Status_Screen::draw_position(const float x, const float y, const float z, bool position_known) {
char str[7];
set_ddram_address(DDRAM_LINE_4);
begin_data();
// If position is unknown, flash the labels.
const unsigned char alt_label = position_known ? 0 : (lcd_blink() ? ' ' : 0);
dtostrf(x, -4, 0, str);
write_byte(alt_label ? alt_label : 'X');
write_str(str, 4);
dtostrf(y, -4, 0, str);
write_byte(alt_label ? alt_label : 'Y');
write_str(str, 4);
dtostrf(z, -5, 1, str);
write_byte(alt_label ? alt_label : 'Z');
write_str(str, 5);
}
bool ST7920_Lite_Status_Screen::indicators_changed() {
// We only add the target temperatures to the checksum
// because the actual temps fluctuate so by updating
// them only during blinks we gain a bit of stability.
const bool blink = lcd_blink();
const uint8_t feedrate_perc = feedrate_percentage;
const uint8_t fan_speed = ((fanSpeeds[0] + 1) * 100) / 256;
const float extruder_1_target = thermalManager.degTargetHotend(0);
#if EXTRUDERS == 2
const float extruder_2_target = thermalManager.degTargetHotend(1);
#endif
const float bed_target = thermalManager.degTargetBed();
static uint8_t last_checksum = 0;
const uint8_t checksum =
uint8_t(blink) ^
uint8_t(feedrate_perc) ^
uint8_t(fan_speed) ^
uint8_t(extruder_1_target) ^
#if EXTRUDERS == 2
uint8_t(extruder_2_target) ^
#endif
uint8_t(bed_target);
if(last_checksum == checksum) {
return false;
} else {
last_checksum = checksum;
return true;
}
}
void ST7920_Lite_Status_Screen::update_indicators(bool forceUpdate) {
if(forceUpdate || indicators_changed()) {
const bool blink = lcd_blink();
const duration_t elapsed = print_job_timer.duration();
const uint32_t seconds_elapsed = elapsed.value;
const uint8_t feedrate_perc = feedrate_percentage;
const uint8_t fan_speed = ((fanSpeeds[0] + 1) * 100) / 256;
const float extruder_1_temp = thermalManager.degHotend(0);
const float extruder_1_target = thermalManager.degTargetHotend(0);
#if EXTRUDERS == 2
const float extruder_2_temp = thermalManager.degHotend(1);
const float extruder_2_target = thermalManager.degTargetHotend(1);
#endif
const float bed_temp = thermalManager.degBed();
const float bed_target = thermalManager.degTargetBed();
draw_extruder_1_temp(extruder_1_temp, extruder_1_target);
#if EXTRUDERS == 2
draw_extruder_2_temp(extruder_2_temp, extruder_2_target);
#endif
draw_bed_temp(bed_temp, bed_target);
draw_fan_speed(fan_speed);
draw_print_time(seconds_elapsed);
draw_feedrate_percentage(feedrate_perc);
// Update the fan and bed animations
if(fan_speed > 0) {
draw_fan_icon(blink);
}
if(bed_target > 0) {
draw_heat_icon(blink, true);
} else {
draw_heat_icon(false, false);
}
}
}
bool ST7920_Lite_Status_Screen::position_changed() {
const float x_pos = current_position[X_AXIS];
const float y_pos = current_position[Y_AXIS];
const float z_pos = current_position[Z_AXIS];
const uint8_t checksum = uint8_t(x_pos) ^ uint8_t(y_pos) ^ uint8_t(z_pos);
static uint8_t last_checksum = 0;
if(last_checksum == checksum) {
return false;
} else {
last_checksum = checksum;
return true;
}
}
bool ST7920_Lite_Status_Screen::status_changed() {
uint8_t checksum = 0;
for(const char *p = lcd_status_message; *p; p++) {
checksum ^= *p;
}
static uint8_t last_checksum = 0;
if(last_checksum == checksum) {
return false;
} else {
last_checksum = checksum;
return true;
}
}
bool ST7920_Lite_Status_Screen::blink_changed() {
static uint8_t last_blink = 0;
const bool blink = lcd_blink();
if(last_blink == blink) {
return false;
} else {
last_blink = blink;
return true;
}
}
void ST7920_Lite_Status_Screen::update_status_or_position(bool forceUpdate) {
static uint8_t countdown = 0;
/* There is only enough room in the display for either the
* status message or the position, not both, so we choose
* one or another. Whenever the status message changes,
* we show it for a number of consecutive seconds, but
* then go back to showing the position as soon as the
* head moves, i.e:
*
* countdown > 1 -- Show status
* countdown = 1 -- Show status, until movement
* countdown = 0 -- Show position
*/
if( forceUpdate || status_changed() ) {
#if ENABLED(STATUS_MESSAGE_SCROLLING)
status_scroll_pos = 0;
#endif
if(lcd_strlen(lcd_status_message)) {
countdown = LULZBOT_DELAY_TO_SHOW_POSITION;
} else {
countdown = 0;
}
draw_status_message(lcd_status_message);
blink_changed(); // Clear changed flag
}
else if(countdown > 1 && blink_changed() ) {
countdown--;
#if ENABLED(STATUS_MESSAGE_SCROLLING)
draw_status_message(lcd_status_message);
#endif
}
else if(countdown > 0 && blink_changed() ) {
if(position_changed()) {
countdown--;
forceUpdate = true;
}
#if ENABLED(STATUS_MESSAGE_SCROLLING)
draw_status_message(lcd_status_message);
#endif
}
if(countdown == 0 && (forceUpdate || position_changed() ||
#if DISABLED(DISABLE_REDUCED_ACCURACY_WARNING)
blink_changed()
#endif
)) {
draw_position(
current_position[X_AXIS],
current_position[Y_AXIS],
current_position[Z_AXIS],
#if ENABLED(DISABLE_REDUCED_ACCURACY_WARNING)
true
#else
axis_known_position[X_AXIS] &&
axis_known_position[Y_AXIS] &&
axis_known_position[Z_AXIS]
#endif
);
}
}
void ST7920_Lite_Status_Screen::update_progress(bool forceUpdate) {
#if ENABLED(SDSUPPORT)
const uint8_t percent_done = card.percentDone();
#else
const uint8_t percent_done = 0;
#endif
// Since the progress bar involves writing
// quite a few bytes to GDRAM, only do this
// when an update is actually necessary.
static uint8_t last_progress = 0;
if(!forceUpdate && last_progress == percent_done)
return;
last_progress = percent_done;
draw_progress_bar(percent_done);
}
void ST7920_Lite_Status_Screen::update(bool forceUpdate) {
cs();
update_indicators(forceUpdate);
update_status_or_position(forceUpdate);
update_progress(forceUpdate);
ncs();
}
void ST7920_Lite_Status_Screen::reset_state_from_unknown() {
_extended_function_set(true, true); // Do it twice as only one bit
_extended_function_set(true, true); // get set at a time.
_scroll_or_addr_select(false);
}
void ST7920_Lite_Status_Screen::on_entry() {
cs();
reset_state_from_unknown();
clear();
clear_gdram();
draw_static_elements();
update(true);
ncs();
}
void ST7920_Lite_Status_Screen::on_exit() {
cs();
clear();
_extended_function_set(true, true); // Restore state to what u8g expects.
ncs();
}
// This is called prior to the KILL screen to
// clear the screen so we don't end up with a
// garbled display.
void ST7920_Lite_Status_Screen::clear_text_buffer() {
cs();
reset_state_from_unknown();
clear();
_extended_function_set(true, true); // Restore state to what u8g expects.
ncs();
}
static void lcd_implementation_status_screen() {
ST7920_Lite_Status_Screen::update(false);
}
/* In order to properly update the lite status screen,
* we must know when we have entered and left the
* status screen. Since the ultralcd code is not
* set up for doing this, we call this function before
* each update indicating whether the current screen
* is the status screen.
*
* This function keeps track of whether we have left or
* entered the status screen and calls the on_entry()
* and on_exit() methods for cleanup.
*/
static void lcd_in_status(bool inStatus) {
static bool lastInStatus = false;
if(!lastInStatus && inStatus) {
ST7920_Lite_Status_Screen::on_entry();
lastInStatus = true;
}
if(lastInStatus && !inStatus) {
ST7920_Lite_Status_Screen::on_exit();
lastInStatus = false;
}
}

@ -0,0 +1,39 @@
/*
* Lightweight Status Screen for the RepRapDiscount Full
* Graphics Smart Controller (ST7920-based 128x64 LCD)
*
* (c) 2017 Aleph Objects, Inc.
*
* The code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License (GNU GPL) as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. The code is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*
*/
#include "ultralcd_impl_st7920_lite_status_screen.h"
void ST7920_Lite_Status_Screen::cs() {
ST7920_CS();
current_bits.synced = false;
}
void ST7920_Lite_Status_Screen::ncs() {
ST7920_NCS();
current_bits.synced = false;
}
void ST7920_Lite_Status_Screen::sync_cmd() {
ST7920_SET_CMD();
}
void ST7920_Lite_Status_Screen::sync_dat() {
ST7920_SET_DAT();
}
void ST7920_Lite_Status_Screen::write_byte(uint8_t data) {
ST7920_WRITE_BYTE(data);
}

@ -1,694 +0,0 @@
/*
* Modern Interface for the RepRapDiscount Full
* Graphics Smart Controller (ST7920-based 128x64 LCD)
*
* (c) 2017 Aleph Objects, Inc.
*
* The code in this page is free software: you can
* redistribute it and/or modify it under the terms of the GNU
* General Public License (GNU GPL) as published by the Free Software
* Foundation, either version 3 of the License, or (at your option)
* any later version. The code is distributed WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
*
*/
#define BUFFER_WIDTH 256
#define BUFFER_HEIGHT 32
#define DDRAM_LINE_1 0x00
#define DDRAM_LINE_2 0x10
#define DDRAM_LINE_3 0x08
#define DDRAM_LINE_4 0x18
//set optimization so ARDUINO optimizes this file
#pragma GCC optimize (3)
typedef const __FlashStringHelper *progmem_str;
void lcd_data(uint8_t data) {
ST7920_WRITE_BYTE(data);
}
void lcd_cmd(uint8_t data) {
ST7920_SET_CMD();
ST7920_WRITE_BYTE(data);
}
void lcd_write_begin() {
ST7920_SET_DAT();
}
void lcd_write_byte(uint8_t w) {
lcd_data(w & 0xFF);
}
void lcd_write_word(uint16_t w) {
lcd_data((w >> 8) & 0xFF);
lcd_data((w >> 0) & 0xFF);
}
void lcd_write_str(const char *str) {
while(*str) {
lcd_write_byte(*str++);
}
}
void lcd_write_str(const char *str, uint8_t len) {
while(*str && len--) {
lcd_write_byte(*str++);
}
}
void lcd_write_str_P(const char *str) {
const char *p_str = (const char *)str;
char c = pgm_read_byte_near(p_str++);
while(c) {
lcd_write_byte(c);
c = pgm_read_byte_near(p_str++);
}
}
void lcd_write_str(progmem_str str) {
lcd_write_str_P((const char*)str);
}
void lcd_write_number(uint8_t value, uint8_t digits=3) {
char str[7];
const char *fmt;
switch(digits) {
case 6: fmt = PSTR("%6d"); break;
case 5: fmt = PSTR("%5d"); break;
case 4: fmt = PSTR("%4d"); break;
case 3: fmt = PSTR("%3d"); break;
case 2: fmt = PSTR("%2d"); break;
case 1: fmt = PSTR("%1d"); break;
}
sprintf_P(str,fmt,value);
lcd_write_str(str);
}
void lcd_clear() {
lcd_cmd(0x00000001);
u8g_Delay(15); //delay for CGRAM clear
}
void lcd_display_status(bool display_on, bool cursor_on, bool blink_on) {
lcd_cmd(0b00001000 |
(display_on ? 0b0100 : 0) |
(cursor_on ? 0b0010 : 0) |
(blink_on ? 0b0001 : 0)
);
}
void lcd_extended_function_set(bool extended, bool graphics) {
lcd_cmd( 0b00100000 |
(extended ? 0b00000100 : 0) |
(graphics ? 0b00000010 : 0)
);
}
void lcd_entry_mode_select(bool ac_increase, bool shift) {
lcd_cmd(0b00000100 |
(ac_increase ? 0b00000010 : 0) |
(shift ? 0b00000001 : 0)
);
}
void lcd_scroll_or_addr_select(bool sa) {
lcd_cmd(0b00100010 |
(sa ? 0b000001 : 0)
);
}
void lcd_set_ddram_address(uint8_t addr) {
lcd_cmd(0b10000000 | (addr & 0b00111111));
}
void lcd_set_cgram_address(uint8_t addr) {
lcd_cmd(0b01000000 | (addr & 0b00111111));
}
void lcd_set_gdram_address(uint8_t x, uint8_t y) {
lcd_cmd(0b10000000 | (y & 0b01111111));
lcd_cmd(0b10000000 | (x & 0b00001111));
}
void clear_ddram()
{
lcd_extended_function_set(false, true);
lcd_set_ddram_address(DDRAM_LINE_1);
lcd_write_begin();
for(int i=0; i < 64;i++) {
lcd_write_byte(' ');
}
lcd_extended_function_set(true, true);
}
/* This fills the entire graphics buffer with zeros
*/
void clear_gdram()
{
for(int y = 0; y < BUFFER_HEIGHT; y++) {
lcd_extended_function_set(true, true);
lcd_set_gdram_address(0,y);
lcd_extended_function_set(false, true);
lcd_write_begin();
for(int i = 0; i < (BUFFER_WIDTH / 16); i++) {
lcd_write_byte(0);
lcd_write_byte(0);
}
}
lcd_extended_function_set(false, true);
}
void load_cgram_icon(uint16_t addr, const void *data) {
const uint16_t *p_word = (const uint16_t *)data;
lcd_set_cgram_address(addr);
lcd_write_begin();
for(int i = 0; i < 16; i++) {
uint16_t word = pgm_read_word_near(p_word++);
lcd_write_byte((word & 0xFF00) >> 8);
lcd_write_byte((word & 0x00FF) >> 0);
}
}
/* Draws an icon in GDRAM. The position is specified in
as if they were DDRAM coordinates, i.e. the x position
is [1-8], while the y position is [1-4] */
void draw_gdram_icon(uint8_t x, uint8_t y, const void *data) {
const uint16_t *p_word = (const uint16_t *)data;
if(y > 2) {
// Handle display folding
y -= 2;
x += 8;
}
x -= 1;
y -= 1;
for(int i = 0; i < 16; i++) {
uint16_t word = pgm_read_word_near(p_word++);
lcd_extended_function_set(true, true);
lcd_set_gdram_address(x,i+y*16);
lcd_extended_function_set(false, true);
lcd_write_begin();
lcd_write_byte((word & 0xFF00) >> 8);
lcd_write_byte((word & 0x00FF) >> 0);
}
}
/************************** ICON DEFINITIONS *************************************/
#define CGRAM_ICON_1_ADDR 0x00
#define CGRAM_ICON_2_ADDR 0x10
#define CGRAM_ICON_3_ADDR 0x20
#define CGRAM_ICON_4_ADDR 0x30
#define CGRAM_ICON_1_WORD 0x00
#define CGRAM_ICON_2_WORD 0x02
#define CGRAM_ICON_3_WORD 0x04
#define CGRAM_ICON_4_WORD 0x06
#define N_ELEMENTS(a) sizeof(a)/sizeof(a[0])
PROGMEM const uint16_t nozzle_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000111111110000,
0b0001111111111000,
0b0001111111111000,
0b0001111111111000,
0b0000111111110000,
0b0000111111110000,
0b0001111111111000,
0b0001111111111000,
0b0001111111111000,
0b0000011111100000,
0b0000001111000000,
0b0000000110000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t bed_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0111111111111110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t heat1_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0010001000100000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t heat2_icon[] = {
0b0000000000000000,
0b0000000000000000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0010001000100000,
0b0010001000100000,
0b0001000100010000,
0b0000100010001000,
0b0000100010001000,
0b0001000100010000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t fan1_icon[] = {
0b0000000000000000,
0b0111111111111110,
0b0111000000001110,
0b0110001111000110,
0b0100001111000010,
0b0100000110000010,
0b0101100000011010,
0b0101110110111010,
0b0101100000011010,
0b0100000110000010,
0b0100001111000010,
0b0110001111000110,
0b0111000000001110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t fan2_icon[] = {
0b0000000000000000,
0b0111111111111110,
0b0111000000001110,
0b0110010000100110,
0b0100111001110010,
0b0101111001111010,
0b0100110000110010,
0b0100000110000010,
0b0100110000110010,
0b0101111001111010,
0b0100111001110010,
0b0110010000100110,
0b0111000000001110,
0b0111111111111110,
0b0000000000000000,
0b0000000000000000
};
PROGMEM const uint16_t feedrate_icon[] = {
0b0000000000000000,
0b0111111000000000,
0b0100000000000000,
0b0100000000000000,
0b0100000000000000,
0b0111111011111000,
0b0100000010000100,
0b0100000010000100,
0b0100000010000100,
0b0100000011111000,
0b0000000010001000,
0b0000000010000100,
0b0000000010000100,
0b0000000010000010,
0b0000000000000000,
0b0000000000000000
};
/************************** MAIN SCREEN *************************************/
/* Although this is undocumented, the ST7920 allows the character
* data buffer (DDRAM) to be used in conjunction with the graphics
* bitmap buffer (CGRAM). The contents of the graphics buffer is
* XORed with the data from the character generator. This allows
* us to make the progess bar out of graphical data (the bar) and
* text data (the percentage).
*/
void draw_progress_bar(uint8_t value) {
#if EXTRUDERS == 1
// If we have only one extruder, draw a long progress bar on the third line
const int top = 1; // Top in pixels
const int bottom = 13; // Bottom in pixels
const int left = 8; // Left edge, in 16-bit words
const int width = 5; // Width of progress bar, in 16-bit words
#else
const int top = 16 + 1; // Top in pixels
const int bottom = 16 + 13; // Bottom in pixels
const int left = 5; // Left edge, in 16-bit words
const int width = 3; // Width of progress bar, in 16-bit words
#endif
const int char_pcnt = 100/width; // How many percent does each 16-bit word represent?
// Draw the progress bar as a bitmap in CGRAM
lcd_extended_function_set(false, true);
for(int y = top; y <= bottom; y++) {
lcd_extended_function_set(true, true);
lcd_set_gdram_address(left,y);
lcd_extended_function_set(false, true);
lcd_write_begin();
for(int x = 0; x < width; x++) {
uint16_t gfx_word = 0x0000;
if((x+1)*char_pcnt <= value) {
// Draw completely filled bytes
gfx_word = 0xFFFF;
} else if((x*char_pcnt) < value) {
// Draw partially filled bytes
gfx_word = int(0x8000) >> (value % char_pcnt)*16/char_pcnt;
}
// Draw the frame around the progress bar
if(y == top || y == bottom) {
// Draw top/bottom border
gfx_word = 0xFFFF;
} else if (x == (width-1)) {
// Draw right border
gfx_word |= 0x0001;
} else if (x == 0) {
// Draw left border
gfx_word |= 0x8000;
}
lcd_write_word(gfx_word);
}
}
// Draw the percentage as text in DDRAM
#if EXTRUDERS == 1
lcd_set_ddram_address(DDRAM_LINE_3 + 1);
#else
lcd_set_ddram_address(DDRAM_LINE_2 + left);
#endif
lcd_write_begin();
if(value > 9) {
lcd_write_number(value,4);
lcd_write_str(F("% "));
} else {
lcd_write_number(value,3);
lcd_write_str(F("% "));
}
}
static void draw_fan_icon(bool whichIcon) {
lcd_set_ddram_address(DDRAM_LINE_1+5);
lcd_write_begin();
lcd_write_word(whichIcon ? CGRAM_ICON_3_WORD : CGRAM_ICON_4_WORD);
}
static void draw_heat_icon(bool whichIcon, bool heating) {
#if EXTRUDERS == 1
lcd_set_ddram_address(DDRAM_LINE_2);
#else
lcd_set_ddram_address(DDRAM_LINE_3);
#endif
lcd_write_begin();
if(heating) {
lcd_write_word(whichIcon ? CGRAM_ICON_1_WORD : CGRAM_ICON_2_WORD);
} else {
lcd_write_byte(' ');
lcd_write_byte(' ');
}
}
static void drawStaticElements() {
lcd_extended_function_set(true, true);
lcd_scroll_or_addr_select(0);
lcd_extended_function_set(false, true);
// Load the animated bed and fan icons
load_cgram_icon(CGRAM_ICON_1_ADDR, heat1_icon);
load_cgram_icon(CGRAM_ICON_2_ADDR, heat2_icon);
load_cgram_icon(CGRAM_ICON_3_ADDR, fan1_icon);
load_cgram_icon(CGRAM_ICON_4_ADDR, fan2_icon);
// Draw the static icons in GDRAM
draw_gdram_icon(1,1,nozzle_icon);
#if EXTRUDERS == 2
draw_gdram_icon(1,2,nozzle_icon);
#endif
draw_gdram_icon(6,2,feedrate_icon);
draw_gdram_icon(1,2,bed_icon);
// Draw the initial fan icon
draw_fan_icon(false);
}
#define FAR(a,b) (((a > b) ? (a-b) : (b-a)) > 1)
static void draw_extruder_1_temp(uint8_t temp, uint8_t target) {
lcd_set_ddram_address(DDRAM_LINE_1+1);
lcd_write_begin();
lcd_write_number(temp);
if(target && FAR(temp, target)) {
lcd_write_str(F("\x1A"));
lcd_write_number(target);
} else {
lcd_write_str(F(" "));
}
}
static void draw_extruder_2_temp(uint8_t temp, uint8_t target) {
lcd_set_ddram_address(DDRAM_LINE_2+1);
lcd_write_begin();
lcd_write_number(temp);
if(target && FAR(temp, target)) {
lcd_write_str(F("\x1A"));
lcd_write_number(target);
} else {
lcd_write_str(F(" "));
}
}
static void draw_bed_temp(uint8_t temp, uint8_t target) {
#if EXTRUDERS == 1
lcd_set_ddram_address(DDRAM_LINE_2+1);
#else
lcd_set_ddram_address(DDRAM_LINE_3+1);
#endif
lcd_write_begin();
lcd_write_number(temp);
if(target && FAR(temp, target)) {
lcd_write_str(F("\x1A"));
lcd_write_number(target);
} else {
lcd_write_str(F(" "));
}
}
static void draw_fan_speed(uint8_t value) {
lcd_set_ddram_address(DDRAM_LINE_1+6);
lcd_write_begin();
lcd_write_number(value,4);
}
static void draw_print_time(uint32_t elapsed) {
const uint8_t hrs = elapsed/3600;
const uint8_t min = (elapsed/60)%60;
char str[7];
sprintf_P(str,hrs > 99 ? PSTR("%03d:%02d") : PSTR(" %02d:%02d"),hrs,min);
lcd_set_ddram_address(DDRAM_LINE_3+5);
lcd_write_begin();
lcd_write_str(str);
}
static void draw_feedrate_percentage(uint8_t percentage) {
// We only have enough room for the feedrate when
// we have one extruder
#if EXTRUDERS == 1
lcd_set_ddram_address(DDRAM_LINE_2+6);
lcd_write_begin();
lcd_write_number(percentage,4);
#endif
}
static void draw_status_message(const char *str) {
lcd_set_ddram_address(DDRAM_LINE_4);
lcd_write_begin();
lcd_write_str(str, 16);
}
static void draw_position(const float x, const float y, const float z) {
char str[7];
lcd_set_ddram_address(DDRAM_LINE_4);
lcd_write_begin();
dtostrf(x, -4, 0, str);
lcd_write_byte('X');
lcd_write_str(str, 4);
dtostrf(y, -4, 0, str);
lcd_write_byte('Y');
lcd_write_str(str, 4);
dtostrf(z, -5, 1, str);
lcd_write_byte('Z');
lcd_write_str(str, 5);
}
static void lcd_onEntry() {
ST7920_CS();
lcd_extended_function_set(false, true);
clear_gdram();
drawStaticElements();
lcd_extended_function_set(true, true);
ST7920_NCS();
}
static void lcd_onExit() {
ST7920_CS();
lcd_extended_function_set(false, true);
lcd_clear();
clear_gdram();
lcd_extended_function_set(true, true);
ST7920_NCS();
}
static void lcd_implementation_status_screen() {
// Retrieve values from Marlin
const uint8_t fan_speed = ((fanSpeeds[0] + 1) * 100) / 256;
const uint8_t percent_done = card.percentDone();
const duration_t elapsed = print_job_timer.duration();
const uint32_t seconds_elapsed = elapsed.value;
const float x_pos = current_position[X_AXIS];
const float y_pos = current_position[Y_AXIS];
const float z_pos = current_position[Z_AXIS];
const uint8_t feedrate_perc = feedrate_percentage;
const float bed_temp = thermalManager.degBed();
const float bed_target = thermalManager.degTargetBed();
const float extruder_1_temp = thermalManager.degHotend(0);
const float extruder_1_target = thermalManager.degTargetHotend(0);
#if EXTRUDERS == 2
const float extruder_2_temp = thermalManager.degHotend(1);
const float extruder_2_target = thermalManager.degTargetHotend(1);
#endif
const char * status_string = lcd_status_message;
const bool blink = lcd_blink();
const bool position_known = axis_known_position[X_AXIS] && axis_known_position[Y_AXIS] && axis_known_position[Z_AXIS];
static bool show_position = false;
static uint8_t last_crc = 0;
const uint8_t crc =
uint8_t(blink) ^
uint8_t(feedrate_perc) ^
uint8_t(fan_speed) ^
uint8_t(extruder_1_target) ^
#if EXTRUDERS == 2
uint8_t(extruder_2_target) ^
#endif
uint8_t(bed_target) ^
uint8_t((uint16_t(status_string) & 0x00FF) >> 0) ^
uint8_t((uint16_t(status_string) & 0xFF00) >> 8);
// Only update unless something important has changed.
if(last_crc != crc) {
last_crc = crc;
// Draw the status screen
ST7920_CS();
lcd_extended_function_set(false, true);
//drawStaticElements();
draw_extruder_1_temp(extruder_1_temp, extruder_1_target);
#if EXTRUDERS == 2
draw_extruder_2_temp(extruder_2_temp, extruder_2_target);
#endif
draw_bed_temp(bed_temp, bed_target);
draw_fan_speed(fan_speed);
draw_print_time(seconds_elapsed);
draw_feedrate_percentage(feedrate_perc);
static const char * status_string_last = 0;
static uint8_t status_string_countdown = 0;
static uint8_t status_string_pos_crc = 0;
// Dismiss the status message automatically after 10 blinks
// if movement is happening on the x axis.
const uint8_t pos_crc = uint8_t(x_pos) ^ uint8_t(y_pos);
if(status_string_last != status_string) {
status_string_countdown = 5;
status_string_last = status_string;
} else if(pos_crc != status_string_pos_crc && status_string_countdown > 0) {
status_string_countdown--;
}
status_string_pos_crc = pos_crc;
if(strlen(status_string) && status_string_countdown > 0) {
draw_status_message(status_string);
show_position = false;
} else if(position_known) {
show_position = true;
}
draw_progress_bar(percent_done);
// Update the fan and bed animations
if(fan_speed > 0) {
draw_fan_icon(blink);
}
if(bed_target > 0) {
draw_heat_icon(blink, true);
} else {
draw_heat_icon(false, false);
}
lcd_extended_function_set(true, true);
ST7920_NCS();
} else if(show_position) {
ST7920_CS();
lcd_extended_function_set(false, true);
draw_position(x_pos, y_pos, z_pos);
lcd_extended_function_set(true, true);
ST7920_NCS();
}
}
static void lcd_in_status(bool inStatus) {
static bool lastInStatus = false;
if(!lastInStatus && inStatus) {
lcd_onEntry();
lastInStatus = true;
}
if(lastInStatus && !inStatus) {
lcd_onExit();
lastInStatus = false;
}
}
void lcd_clear_text_buffer() {
ST7920_CS();
clear_ddram();
ST7920_NCS();
}
#pragma GCC reset_options

@ -25,8 +25,6 @@
#include <U8glib.h>
LULZBOT_LCD_CLEAR_DECL
#define ST7920_CLK_PIN LCD_PINS_D4
#define ST7920_DAT_PIN LCD_PINS_ENABLE
#define ST7920_CS_PIN LCD_PINS_RS
@ -121,10 +119,19 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
ST7920_CS();
u8g_Delay(120); //initial delay for boot up
ST7920_SET_CMD();
#if defined(LULZBOT_LCD_CLEAR_WORKAROUND)
ST7920_WRITE_BYTE(0x20); //non-extended mode
ST7920_WRITE_BYTE(0x08); //display off, cursor+blink off
ST7920_WRITE_BYTE(0x01); //clear DDRAM ram
u8g_Delay(15); //delay for DDRAM clear
ST7920_WRITE_BYTE(0x24); //extended mode
ST7920_WRITE_BYTE(0x26); //extended mode + GDRAM active
#else
ST7920_WRITE_BYTE(0x08); //display off, cursor+blink off
ST7920_WRITE_BYTE(0x01); //clear CGRAM ram
u8g_Delay(15); //delay for CGRAM clear
ST7920_WRITE_BYTE(0x3E); //extended mode + GDRAM active
#endif
for (y = 0; y < (LCD_PIXEL_HEIGHT) / 2; y++) { //clear GDRAM
ST7920_WRITE_BYTE(0x80 | y); //set y
ST7920_WRITE_BYTE(0x80); //set x = 0
@ -134,7 +141,6 @@ uint8_t u8g_dev_rrd_st7920_128x64_fn(u8g_t *u8g, u8g_dev_t *dev, uint8_t msg, vo
ST7920_SET_CMD();
}
ST7920_WRITE_BYTE(0x0C); //display on, cursor+blink off
LULZBOT_LCD_CLEAR
ST7920_NCS();
}
break;
@ -184,6 +190,14 @@ class U8GLIB_ST7920_128X64_RRD : public U8GLIB {
U8GLIB_ST7920_128X64_RRD(uint8_t dummy) : U8GLIB(&u8g_dev_st7920_128x64_rrd_sw_spi) { UNUSED(dummy); }
};
#if ENABLED(LULZBOT_MODERN_UI)
typedef const __FlashStringHelper *progmem_str;
// We have to include the code for the lightweight UI here
// as it relies on macros that are only defined in this file.
#include "ultralcd_impl_st7920_lite_status_screen_impl_spi.h"
#endif
#pragma GCC reset_options
#endif // ULCDST7920_H

Loading…
Cancel
Save