mirror of
https://github.com/QIDITECH/klipper.git
synced 2026-01-30 23:48:43 +03:00
klipper update
This commit is contained in:
21
klippy/extras/display/__init__.py
Normal file
21
klippy/extras/display/__init__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Package definition for the extras/display directory
|
||||
#
|
||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
from . import display
|
||||
|
||||
def load_config(config):
|
||||
return display.load_config(config)
|
||||
|
||||
def load_config_prefix(config):
|
||||
if not config.has_section('display'):
|
||||
raise config.error(
|
||||
"A primary [display] section must be defined in printer.cfg "
|
||||
"to use auxilary displays")
|
||||
name = config.get_name().split()[-1]
|
||||
if name == "display":
|
||||
raise config.error(
|
||||
"Section name [display display] is not valid. "
|
||||
"Please choose a different postfix.")
|
||||
return display.load_config(config)
|
||||
BIN
klippy/extras/display/__init__.pyc
Normal file
BIN
klippy/extras/display/__init__.pyc
Normal file
Binary file not shown.
461
klippy/extras/display/display.cfg
Normal file
461
klippy/extras/display/display.cfg
Normal file
@@ -0,0 +1,461 @@
|
||||
# This file defines the default layout of the printer's lcd display.
|
||||
|
||||
# It is not necessary to edit this file to change the display.
|
||||
# Instead, one may override any of the sections defined here by
|
||||
# defining a section with the same name in the main printer.cfg config
|
||||
# file.
|
||||
|
||||
|
||||
######################################################################
|
||||
# Helper macros for showing common screen values
|
||||
######################################################################
|
||||
|
||||
[display_template _heater_temperature]
|
||||
param_heater_name: "extruder"
|
||||
text:
|
||||
{% if param_heater_name in printer %}
|
||||
{% set heater = printer[param_heater_name] %}
|
||||
# Show glyph
|
||||
{% if param_heater_name == "heater_bed" %}
|
||||
{% if heater.target %}
|
||||
{% set frame = (printer.toolhead.estimated_print_time|int % 2) + 1 %}
|
||||
~bed_heat{frame}~
|
||||
{% else %}
|
||||
~bed~
|
||||
{% endif %}
|
||||
{% else %}
|
||||
~extruder~
|
||||
{% endif %}
|
||||
# Show temperature
|
||||
{ "%3.0f" % (heater.temperature,) }
|
||||
# Optionally show target
|
||||
{% if heater.target and (heater.temperature - heater.target)|abs > 2 %}
|
||||
~right_arrow~
|
||||
{ "%0.0f" % (heater.target,) }
|
||||
{% endif %}
|
||||
~degrees~
|
||||
{% endif %}
|
||||
|
||||
[display_template _fan_speed]
|
||||
text:
|
||||
{% if 'fan' in printer %}
|
||||
{% set speed = printer.fan.speed %}
|
||||
{% if speed %}
|
||||
{% set frame = (printer.toolhead.estimated_print_time|int % 2) + 1 %}
|
||||
~fan{frame}~
|
||||
{% else %}
|
||||
~fan1~
|
||||
{% endif %}
|
||||
{ "{:>4.0%}".format(speed) }
|
||||
{% endif %}
|
||||
|
||||
[display_template _printing_time]
|
||||
text:
|
||||
{% set ptime = printer.idle_timeout.printing_time %}
|
||||
{ "%02d:%02d" % (ptime // (60 * 60), (ptime // 60) % 60) }
|
||||
|
||||
[display_template _print_status]
|
||||
text:
|
||||
{% if printer.display_status.message %}
|
||||
{ printer.display_status.message }
|
||||
{% elif printer.idle_timeout.printing_time %}
|
||||
{% set pos = printer.toolhead.position %}
|
||||
{ "X%-4.0fY%-4.0fZ%-5.2f" % (pos.x, pos.y, pos.z) }
|
||||
{% else %}
|
||||
Ready
|
||||
{% endif %}
|
||||
|
||||
|
||||
######################################################################
|
||||
# Default 16x4 display
|
||||
######################################################################
|
||||
|
||||
[display_data _default_16x4 extruder]
|
||||
position: 0, 0
|
||||
text:
|
||||
{% set active_extruder = printer.toolhead.extruder %}
|
||||
{ render("_heater_temperature", param_heater_name=active_extruder) }
|
||||
|
||||
[display_data _default_16x4 fan]
|
||||
position: 0, 10
|
||||
text: { render("_fan_speed") }
|
||||
|
||||
[display_data _default_16x4 heater_bed]
|
||||
position: 1, 0
|
||||
text: { render("_heater_temperature", param_heater_name="heater_bed") }
|
||||
|
||||
[display_data _default_16x4 speed_factor]
|
||||
position: 1, 10
|
||||
text:
|
||||
~feedrate~
|
||||
{ "{:>4.0%}".format(printer.gcode_move.speed_factor) }
|
||||
|
||||
[display_data _default_16x4 print_progress]
|
||||
position: 2, 0
|
||||
text: { "{:^10.0%}".format(printer.display_status.progress) }
|
||||
[display_data _default_16x4 progress_bar]
|
||||
position: 2, 1 # Draw graphical progress bar after text is written
|
||||
text: { draw_progress_bar(2, 0, 10, printer.display_status.progress) }
|
||||
|
||||
[display_data _default_16x4 printing_time]
|
||||
position: 2, 10
|
||||
text: { "%6s" % (render("_printing_time").strip(),) }
|
||||
|
||||
[display_data _default_16x4 print_status]
|
||||
position: 3, 0
|
||||
text: { render("_print_status") }
|
||||
|
||||
|
||||
######################################################################
|
||||
# Alternative 16x4 layout for multi-extruders
|
||||
######################################################################
|
||||
|
||||
[display_data _multiextruder_16x4 extruder]
|
||||
position: 0, 0
|
||||
text: { render("_heater_temperature", param_heater_name="extruder") }
|
||||
|
||||
[display_data _multiextruder_16x4 fan]
|
||||
position: 0, 10
|
||||
text: { render("_fan_speed") }
|
||||
|
||||
[display_data _multiextruder_16x4 extruder1]
|
||||
position: 1, 0
|
||||
text: { render("_heater_temperature", param_heater_name="extruder1") }
|
||||
|
||||
[display_data _multiextruder_16x4 print_progress]
|
||||
position: 1, 10
|
||||
text: { "{:^6.0%}".format(printer.display_status.progress) }
|
||||
[display_data _multiextruder_16x4 progress_bar]
|
||||
position: 1, 11 # Draw graphical progress bar after text is written
|
||||
text: { draw_progress_bar(1, 10, 6, printer.display_status.progress) }
|
||||
|
||||
[display_data _multiextruder_16x4 heater_bed]
|
||||
position: 2, 0
|
||||
text: { render("_heater_temperature", param_heater_name="heater_bed") }
|
||||
|
||||
[display_data _multiextruder_16x4 printing_time]
|
||||
position: 2, 10
|
||||
text: { "%6s" % (render("_printing_time").strip(),) }
|
||||
|
||||
[display_data _multiextruder_16x4 print_status]
|
||||
position: 3, 0
|
||||
text: { render("_print_status") }
|
||||
|
||||
|
||||
######################################################################
|
||||
# Default 20x4 display
|
||||
######################################################################
|
||||
|
||||
[display_data _default_20x4 extruder]
|
||||
position: 0, 0
|
||||
text: { render("_heater_temperature", param_heater_name="extruder") }
|
||||
|
||||
[display_data _default_20x4 heater_bed]
|
||||
position: 0, 10
|
||||
text: { render("_heater_temperature", param_heater_name="heater_bed") }
|
||||
|
||||
[display_data _default_20x4 extruder1]
|
||||
position: 1, 0
|
||||
text: { render("_heater_temperature", param_heater_name="extruder1") }
|
||||
|
||||
[display_data _default_20x4 fan]
|
||||
position: 1, 10
|
||||
text:
|
||||
{% if 'fan' in printer %}
|
||||
{ "Fan {:^4.0%}".format(printer.fan.speed) }
|
||||
{% endif %}
|
||||
|
||||
[display_data _default_20x4 speed_factor]
|
||||
position: 2, 0
|
||||
text:
|
||||
~feedrate~
|
||||
{ "{:^4.0%}".format(printer.gcode_move.speed_factor) }
|
||||
|
||||
[display_data _default_20x4 print_progress]
|
||||
position: 2, 8
|
||||
text:
|
||||
{% if 'virtual_sdcard' in printer and printer.virtual_sdcard.progress %}
|
||||
~sd~
|
||||
{% else %}
|
||||
~usb~
|
||||
{% endif %}
|
||||
{ "{:^4.0%}".format(printer.display_status.progress) }
|
||||
|
||||
[display_data _default_20x4 printing_time]
|
||||
position: 2, 14
|
||||
text:
|
||||
~clock~
|
||||
{ render("_printing_time") }
|
||||
|
||||
[display_data _default_20x4 print_status]
|
||||
position: 3, 0
|
||||
text: { render("_print_status") }
|
||||
|
||||
|
||||
######################################################################
|
||||
# Default 16x4 glyphs
|
||||
######################################################################
|
||||
|
||||
[display_glyph extruder]
|
||||
data:
|
||||
................
|
||||
................
|
||||
..************..
|
||||
.....******.....
|
||||
..************..
|
||||
.....******.....
|
||||
..************..
|
||||
................
|
||||
....********....
|
||||
....******.*....
|
||||
....********....
|
||||
................
|
||||
......****......
|
||||
.......**.......
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph bed]
|
||||
data:
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
................
|
||||
...*********....
|
||||
..*.........*...
|
||||
.*************..
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph bed_heat1]
|
||||
data:
|
||||
................
|
||||
................
|
||||
..*....*....*...
|
||||
.*....*....*....
|
||||
..*....*....*...
|
||||
...*....*....*..
|
||||
..*....*....*...
|
||||
.*....*....*....
|
||||
..*....*....*...
|
||||
................
|
||||
................
|
||||
...*********....
|
||||
..*.........*...
|
||||
.*************..
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph bed_heat2]
|
||||
data:
|
||||
................
|
||||
................
|
||||
..*....*....*...
|
||||
...*....*....*..
|
||||
..*....*....*...
|
||||
.*....*....*....
|
||||
..*....*....*...
|
||||
...*....*....*..
|
||||
..*....*....*...
|
||||
................
|
||||
................
|
||||
...*********....
|
||||
..*.........*...
|
||||
.*************..
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph fan1]
|
||||
data:
|
||||
................
|
||||
................
|
||||
....***.........
|
||||
...****....**...
|
||||
...****...****..
|
||||
....***..*****..
|
||||
.....*....****..
|
||||
.......**.......
|
||||
.......**.......
|
||||
..****....*.....
|
||||
..*****..***....
|
||||
..****...****...
|
||||
...**....****...
|
||||
.........***....
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph fan2]
|
||||
data:
|
||||
................
|
||||
................
|
||||
.......****.....
|
||||
.......****.....
|
||||
.......***......
|
||||
..**...**.......
|
||||
..***...........
|
||||
..****.**.****..
|
||||
..****.**.****..
|
||||
...........***..
|
||||
.......**...**..
|
||||
......***.......
|
||||
.....****.......
|
||||
.....****.......
|
||||
................
|
||||
................
|
||||
|
||||
[display_glyph feedrate]
|
||||
data:
|
||||
................
|
||||
................
|
||||
***.***.***.**..
|
||||
*...*...*...*.*.
|
||||
**..**..**..*.*.
|
||||
*...*...*...*.*.
|
||||
*...***.***.**..
|
||||
................
|
||||
**...*..***.***.
|
||||
*.*.*.*..*..*...
|
||||
**..***..*..**..
|
||||
*.*.*.*..*..*...
|
||||
*.*.*.*..*..***.
|
||||
................
|
||||
................
|
||||
................
|
||||
|
||||
# In addition to the above glyphs, 16x4 displays also have the
|
||||
# following hard-coded single character glyphs: right_arrow, degrees.
|
||||
|
||||
|
||||
######################################################################
|
||||
# Default 20x4 glyphs
|
||||
######################################################################
|
||||
|
||||
[display_glyph extruder]
|
||||
hd44780_slot: 0
|
||||
hd44780_data:
|
||||
..*..
|
||||
.*.*.
|
||||
.*.*.
|
||||
.*.*.
|
||||
.*.*.
|
||||
*...*
|
||||
*...*
|
||||
.***.
|
||||
|
||||
[display_glyph bed]
|
||||
hd44780_slot: 1
|
||||
hd44780_data:
|
||||
.....
|
||||
*****
|
||||
*.*.*
|
||||
*...*
|
||||
*.*.*
|
||||
*****
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph bed_heat1]
|
||||
hd44780_slot: 1
|
||||
hd44780_data:
|
||||
.*..*
|
||||
*..*.
|
||||
.*..*
|
||||
*..*.
|
||||
.....
|
||||
*****
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph bed_heat2]
|
||||
hd44780_slot: 1
|
||||
hd44780_data:
|
||||
*..*.
|
||||
.*..*
|
||||
*..*.
|
||||
.*..*
|
||||
.....
|
||||
*****
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph fan]
|
||||
hd44780_slot: 2
|
||||
hd44780_data:
|
||||
.....
|
||||
*..**
|
||||
**.*.
|
||||
..*..
|
||||
.*.**
|
||||
**..*
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph feedrate]
|
||||
hd44780_slot: 3
|
||||
hd44780_data:
|
||||
***..
|
||||
*....
|
||||
**...
|
||||
*.***
|
||||
..*.*
|
||||
..**.
|
||||
..*.*
|
||||
.....
|
||||
|
||||
[display_glyph clock]
|
||||
hd44780_slot: 4
|
||||
hd44780_data:
|
||||
.....
|
||||
.***.
|
||||
*..**
|
||||
*.*.*
|
||||
*...*
|
||||
.***.
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph degrees]
|
||||
hd44780_slot: 5
|
||||
hd44780_data:
|
||||
.**..
|
||||
*..*.
|
||||
*..*.
|
||||
.**..
|
||||
.....
|
||||
.....
|
||||
.....
|
||||
.....
|
||||
|
||||
[display_glyph usb]
|
||||
hd44780_slot: 6
|
||||
hd44780_data:
|
||||
.***.
|
||||
.***.
|
||||
.***.
|
||||
*****
|
||||
*****
|
||||
*****
|
||||
..*..
|
||||
..*..
|
||||
|
||||
[display_glyph sd]
|
||||
hd44780_slot: 6
|
||||
hd44780_data:
|
||||
.....
|
||||
..***
|
||||
.****
|
||||
*****
|
||||
*****
|
||||
*****
|
||||
*****
|
||||
.....
|
||||
|
||||
# In addition to the above glyphs, 20x4 displays also have the
|
||||
# following hard-coded glyphs: right_arrow.
|
||||
271
klippy/extras/display/display.py
Normal file
271
klippy/extras/display/display.py
Normal file
@@ -0,0 +1,271 @@
|
||||
# Basic LCD display support
|
||||
#
|
||||
# Copyright (C) 2018-2022 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018 Aleph Objects, Inc <marcio@alephobjects.com>
|
||||
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging, os, ast
|
||||
from . import hd44780, hd44780_spi, st7920, uc1701, menu
|
||||
|
||||
# Normal time between each screen redraw
|
||||
REDRAW_TIME = 0.500
|
||||
# Minimum time between screen redraws
|
||||
REDRAW_MIN_TIME = 0.100
|
||||
|
||||
LCD_chips = {
|
||||
'st7920': st7920.ST7920, 'emulated_st7920': st7920.EmulatedST7920,
|
||||
'hd44780': hd44780.HD44780, 'uc1701': uc1701.UC1701,
|
||||
'ssd1306': uc1701.SSD1306, 'sh1106': uc1701.SH1106,
|
||||
'hd44780_spi': hd44780_spi.hd44780_spi
|
||||
}
|
||||
|
||||
# Storage of [display_template my_template] config sections
|
||||
class DisplayTemplate:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
name_parts = config.get_name().split()
|
||||
if len(name_parts) != 2:
|
||||
raise config.error("Section name '%s' is not valid"
|
||||
% (config.get_name(),))
|
||||
self.name = name_parts[1]
|
||||
self.params = {}
|
||||
for option in config.get_prefix_options('param_'):
|
||||
try:
|
||||
self.params[option] = ast.literal_eval(config.get(option))
|
||||
except ValueError as e:
|
||||
raise config.error(
|
||||
"Option '%s' in section '%s' is not a valid literal" % (
|
||||
option, config.get_name()))
|
||||
gcode_macro = self.printer.load_object(config, 'gcode_macro')
|
||||
self.template = gcode_macro.load_template(config, 'text')
|
||||
def get_params(self):
|
||||
return self.params
|
||||
def render(self, context, **kwargs):
|
||||
params = dict(self.params)
|
||||
params.update(**kwargs)
|
||||
if len(params) != len(self.params):
|
||||
raise self.printer.command_error(
|
||||
"Invalid parameter to display_template %s" % (self.name,))
|
||||
context = dict(context)
|
||||
context.update(params)
|
||||
return self.template.render(context)
|
||||
|
||||
# Store [display_data my_group my_item] sections (one instance per group name)
|
||||
class DisplayGroup:
|
||||
def __init__(self, config, name, data_configs):
|
||||
# Load and parse the position of display_data items
|
||||
items = []
|
||||
for c in data_configs:
|
||||
pos = c.get('position')
|
||||
try:
|
||||
row, col = [int(v.strip()) for v in pos.split(',')]
|
||||
except:
|
||||
raise config.error("Unable to parse 'position' in section '%s'"
|
||||
% (c.get_name(),))
|
||||
items.append((row, col, c.get_name()))
|
||||
# Load all templates and store sorted by display position
|
||||
configs_by_name = {c.get_name(): c for c in data_configs}
|
||||
printer = config.get_printer()
|
||||
gcode_macro = printer.load_object(config, 'gcode_macro')
|
||||
self.data_items = []
|
||||
for row, col, name in sorted(items):
|
||||
c = configs_by_name[name]
|
||||
if c.get('text'):
|
||||
template = gcode_macro.load_template(c, 'text')
|
||||
self.data_items.append((row, col, template))
|
||||
def show(self, display, templates, eventtime):
|
||||
context = self.data_items[0][2].create_template_context(eventtime)
|
||||
context['draw_progress_bar'] = display.draw_progress_bar
|
||||
def render(name, **kwargs):
|
||||
return templates[name].render(context, **kwargs)
|
||||
context['render'] = render
|
||||
for row, col, template in self.data_items:
|
||||
text = template.render(context)
|
||||
display.draw_text(row, col, text.replace('\n', ''), eventtime)
|
||||
context.clear() # Remove circular references for better gc
|
||||
|
||||
# Global cache of DisplayTemplate, DisplayGroup, and glyphs
|
||||
class PrinterDisplayTemplate:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.display_templates = {}
|
||||
self.display_data_groups = {}
|
||||
self.display_glyphs = {}
|
||||
self.load_config(config)
|
||||
def get_display_templates(self):
|
||||
return self.display_templates
|
||||
def get_display_data_groups(self):
|
||||
return self.display_data_groups
|
||||
def get_display_glyphs(self):
|
||||
return self.display_glyphs
|
||||
def _parse_glyph(self, config, glyph_name, data, width, height):
|
||||
glyph_data = []
|
||||
for line in data.split('\n'):
|
||||
line = line.strip().replace('.', '0').replace('*', '1')
|
||||
if not line:
|
||||
continue
|
||||
if len(line) != width or line.replace('0', '').replace('1', ''):
|
||||
raise config.error("Invalid glyph line in %s" % (glyph_name,))
|
||||
glyph_data.append(int(line, 2))
|
||||
if len(glyph_data) != height:
|
||||
raise config.error("Glyph %s incorrect lines" % (glyph_name,))
|
||||
return glyph_data
|
||||
def load_config(self, config):
|
||||
# Load default display config file
|
||||
pconfig = self.printer.lookup_object('configfile')
|
||||
filename = os.path.join(os.path.dirname(__file__), 'display.cfg')
|
||||
try:
|
||||
dconfig = pconfig.read_config(filename)
|
||||
except Exception:
|
||||
raise self.printer.config_error("Cannot load config '%s'"
|
||||
% (filename,))
|
||||
# Load display_template sections
|
||||
dt_main = config.get_prefix_sections('display_template ')
|
||||
dt_main_names = { c.get_name(): 1 for c in dt_main }
|
||||
dt_def = [c for c in dconfig.get_prefix_sections('display_template ')
|
||||
if c.get_name() not in dt_main_names]
|
||||
for c in dt_main + dt_def:
|
||||
dt = DisplayTemplate(c)
|
||||
self.display_templates[dt.name] = dt
|
||||
# Load display_data sections
|
||||
dd_main = config.get_prefix_sections('display_data ')
|
||||
dd_main_names = { c.get_name(): 1 for c in dd_main }
|
||||
dd_def = [c for c in dconfig.get_prefix_sections('display_data ')
|
||||
if c.get_name() not in dd_main_names]
|
||||
groups = {}
|
||||
for c in dd_main + dd_def:
|
||||
name_parts = c.get_name().split()
|
||||
if len(name_parts) != 3:
|
||||
raise config.error("Section name '%s' is not valid"
|
||||
% (c.get_name(),))
|
||||
groups.setdefault(name_parts[1], []).append(c)
|
||||
for group_name, data_configs in groups.items():
|
||||
dg = DisplayGroup(config, group_name, data_configs)
|
||||
self.display_data_groups[group_name] = dg
|
||||
# Load display glyphs
|
||||
dg_prefix = 'display_glyph '
|
||||
self.display_glyphs = icons = {}
|
||||
dg_main = config.get_prefix_sections(dg_prefix)
|
||||
dg_main_names = {c.get_name(): 1 for c in dg_main}
|
||||
dg_def = [c for c in dconfig.get_prefix_sections(dg_prefix)
|
||||
if c.get_name() not in dg_main_names]
|
||||
for dg in dg_main + dg_def:
|
||||
glyph_name = dg.get_name()[len(dg_prefix):]
|
||||
data = dg.get('data', None)
|
||||
if data is not None:
|
||||
idata = self._parse_glyph(config, glyph_name, data, 16, 16)
|
||||
icon1 = [(bits >> 8) & 0xff for bits in idata]
|
||||
icon2 = [bits & 0xff for bits in idata]
|
||||
icons.setdefault(glyph_name, {})['icon16x16'] = (icon1, icon2)
|
||||
data = dg.get('hd44780_data', None)
|
||||
if data is not None:
|
||||
slot = dg.getint('hd44780_slot', minval=0, maxval=7)
|
||||
idata = self._parse_glyph(config, glyph_name, data, 5, 8)
|
||||
icons.setdefault(glyph_name, {})['icon5x8'] = (slot, idata)
|
||||
|
||||
def lookup_display_templates(config):
|
||||
printer = config.get_printer()
|
||||
dt = printer.lookup_object("display_template", None)
|
||||
if dt is None:
|
||||
dt = PrinterDisplayTemplate(config)
|
||||
printer.add_object("display_template", dt)
|
||||
return dt
|
||||
|
||||
class PrinterLCD:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.reactor = self.printer.get_reactor()
|
||||
# Load low-level lcd handler
|
||||
self.lcd_chip = config.getchoice('lcd_type', LCD_chips)(config)
|
||||
# Load menu and display_status
|
||||
self.menu = None
|
||||
name = config.get_name()
|
||||
if name == 'display':
|
||||
# only load menu for primary display
|
||||
self.menu = menu.MenuManager(config, self)
|
||||
self.printer.load_object(config, "display_status")
|
||||
# Configurable display
|
||||
templates = lookup_display_templates(config)
|
||||
self.display_templates = templates.get_display_templates()
|
||||
self.display_data_groups = templates.get_display_data_groups()
|
||||
self.lcd_chip.set_glyphs(templates.get_display_glyphs())
|
||||
dgroup = "_default_16x4"
|
||||
if self.lcd_chip.get_dimensions()[0] == 20:
|
||||
dgroup = "_default_20x4"
|
||||
dgroup = config.get('display_group', dgroup)
|
||||
self.show_data_group = self.display_data_groups.get(dgroup)
|
||||
if self.show_data_group is None:
|
||||
raise config.error("Unknown display_data group '%s'" % (dgroup,))
|
||||
# Screen updating
|
||||
self.printer.register_event_handler("klippy:ready", self.handle_ready)
|
||||
self.screen_update_timer = self.reactor.register_timer(
|
||||
self.screen_update_event)
|
||||
self.redraw_request_pending = False
|
||||
self.redraw_time = 0.
|
||||
# Register g-code commands
|
||||
gcode = self.printer.lookup_object("gcode")
|
||||
gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', name,
|
||||
self.cmd_SET_DISPLAY_GROUP,
|
||||
desc=self.cmd_SET_DISPLAY_GROUP_help)
|
||||
if name == 'display':
|
||||
gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', None,
|
||||
self.cmd_SET_DISPLAY_GROUP)
|
||||
def get_dimensions(self):
|
||||
return self.lcd_chip.get_dimensions()
|
||||
def handle_ready(self):
|
||||
self.lcd_chip.init()
|
||||
# Start screen update timer
|
||||
self.reactor.update_timer(self.screen_update_timer, self.reactor.NOW)
|
||||
# Screen updating
|
||||
def screen_update_event(self, eventtime):
|
||||
if self.redraw_request_pending:
|
||||
self.redraw_request_pending = False
|
||||
self.redraw_time = eventtime + REDRAW_MIN_TIME
|
||||
self.lcd_chip.clear()
|
||||
# update menu component
|
||||
if self.menu is not None:
|
||||
ret = self.menu.screen_update_event(eventtime)
|
||||
if ret:
|
||||
self.lcd_chip.flush()
|
||||
return eventtime + REDRAW_TIME
|
||||
# Update normal display
|
||||
try:
|
||||
self.show_data_group.show(self, self.display_templates, eventtime)
|
||||
except:
|
||||
logging.exception("Error during display screen update")
|
||||
self.lcd_chip.flush()
|
||||
return eventtime + REDRAW_TIME
|
||||
def request_redraw(self):
|
||||
if self.redraw_request_pending:
|
||||
return
|
||||
self.redraw_request_pending = True
|
||||
self.reactor.update_timer(self.screen_update_timer, self.redraw_time)
|
||||
def draw_text(self, row, col, mixed_text, eventtime):
|
||||
pos = col
|
||||
for i, text in enumerate(mixed_text.split('~')):
|
||||
if i & 1 == 0:
|
||||
# write text
|
||||
self.lcd_chip.write_text(pos, row, text.encode())
|
||||
pos += len(text)
|
||||
else:
|
||||
# write glyph
|
||||
pos += self.lcd_chip.write_glyph(pos, row, text)
|
||||
return pos
|
||||
def draw_progress_bar(self, row, col, width, value):
|
||||
pixels = -1 << int(width * 8 * (1. - value) + .5)
|
||||
pixels |= (1 << (width * 8 - 1)) | 1
|
||||
for i in range(width):
|
||||
data = [0xff] + [(pixels >> (i * 8)) & 0xff] * 14 + [0xff]
|
||||
self.lcd_chip.write_graphics(col + width - 1 - i, row, data)
|
||||
return ""
|
||||
cmd_SET_DISPLAY_GROUP_help = "Set the active display group"
|
||||
def cmd_SET_DISPLAY_GROUP(self, gcmd):
|
||||
group = gcmd.get('GROUP')
|
||||
new_dg = self.display_data_groups.get(group)
|
||||
if new_dg is None:
|
||||
raise gcmd.error("Unknown display_data group '%s'" % (group,))
|
||||
self.show_data_group = new_dg
|
||||
|
||||
def load_config(config):
|
||||
return PrinterLCD(config)
|
||||
BIN
klippy/extras/display/display.pyc
Normal file
BIN
klippy/extras/display/display.pyc
Normal file
Binary file not shown.
276
klippy/extras/display/font8x14.py
Normal file
276
klippy/extras/display/font8x14.py
Normal file
@@ -0,0 +1,276 @@
|
||||
# Fonts for connected displays
|
||||
#
|
||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
|
||||
######################################################################
|
||||
# Font - VGA 8x14, Row Major, MSB, 2 bytes padding
|
||||
#
|
||||
# Font comes from fntcol16.zip package found at:
|
||||
# ftp://ftp.simtel.net/pub/simtelnet/msdos/screen/fntcol16.zip
|
||||
# (c) Joseph Gil
|
||||
#
|
||||
# Indivdual fonts are public domain
|
||||
######################################################################
|
||||
|
||||
VGA_FONT = [
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7e\x81\xa5\x81\x81\xbd\x99\x81\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7e\xff\xdb\xff\xff\xc3\xe7\xff\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x6c\xfe\xfe\xfe\xfe\x7c\x38\x10\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x10\x38\x7c\xfe\x7c\x38\x10\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x3c\xe7\xe7\xe7\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x7e\xff\xff\x7e\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x18\x3c\x3c\x18\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\xff\xff\xff\xff\xff\xe7\xc3\xc3\xe7\xff\xff\xff\xff\xff\x00',
|
||||
b'\x00\x00\x00\x00\x00\x3c\x66\x42\x42\x66\x3c\x00\x00\x00\x00\x00',
|
||||
b'\x00\xff\xff\xff\xff\xc3\x99\xbd\xbd\x99\xc3\xff\xff\xff\xff\x00',
|
||||
b'\x00\x00\x00\x1e\x0e\x1a\x32\x78\xcc\xcc\xcc\x78\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x66\x66\x66\x3c\x18\x7e\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3f\x33\x3f\x30\x30\x30\x70\xf0\xe0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7f\x63\x7f\x63\x63\x63\x67\xe7\xe6\xc0\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x18\xdb\x3c\xe7\x3c\xdb\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x80\xc0\xe0\xf8\xfe\xf8\xe0\xc0\x80\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x02\x06\x0e\x3e\xfe\x3e\x0e\x06\x02\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x66\x66\x66\x66\x66\x66\x00\x66\x66\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7f\xdb\xdb\xdb\x7b\x1b\x1b\x1b\x1b\x00\x00\x00\x00',
|
||||
b'\x00\x00\x7c\xc6\x60\x38\x6c\xc6\xc6\x6c\x38\x0c\xc6\x7c\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe\xfe\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x7e\x18\x18\x18\x7e\x3c\x18\x7e\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x7e\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x18\x18\x18\x18\x18\x7e\x3c\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x18\x0c\xfe\x0c\x18\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x30\x60\xfe\x60\x30\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xc0\xc0\xc0\xfe\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x28\x6c\xfe\x6c\x28\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x10\x38\x38\x7c\x7c\xfe\xfe\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\xfe\xfe\x7c\x7c\x38\x38\x10\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x3c\x3c\x3c\x18\x18\x00\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x66\x66\x66\x24\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x6c\x6c\xfe\x6c\x6c\x6c\xfe\x6c\x6c\x00\x00\x00\x00',
|
||||
b'\x00\x18\x18\x7c\xc6\xc2\xc0\x7c\x06\x86\xc6\x7c\x18\x18\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\xc2\xc6\x0c\x18\x30\x66\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x6c\x6c\x38\x76\xdc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x30\x30\x30\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x0c\x18\x30\x30\x30\x30\x30\x18\x0c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x30\x18\x0c\x0c\x0c\x0c\x0c\x18\x30\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x66\x3c\xff\x3c\x66\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x18\x30\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\xfe\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x02\x06\x0c\x18\x30\x60\xc0\x80\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xce\xde\xf6\xe6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x38\x78\x18\x18\x18\x18\x18\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\x06\x0c\x18\x30\x60\xc6\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\x06\x06\x3c\x06\x06\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x0c\x1c\x3c\x6c\xcc\xfe\x0c\x0c\x1e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\xc0\xc0\xc0\xfc\x06\x06\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x60\xc0\xc0\xfc\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\xc6\x06\x0c\x18\x30\x30\x30\x30\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\xc6\x7c\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\xc6\x7e\x06\x06\x0c\x78\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x30\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x06\x0c\x18\x30\x60\x30\x18\x0c\x06\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7e\x00\x00\x7e\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x60\x30\x18\x0c\x06\x0c\x18\x30\x60\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\x0c\x18\x18\x00\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\xde\xde\xde\xdc\xc0\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfc\x66\x66\x66\x7c\x66\x66\x66\xfc\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x66\xc2\xc0\xc0\xc0\xc2\x66\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xf8\x6c\x66\x66\x66\x66\x66\x6c\xf8\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\x66\x62\x68\x78\x68\x62\x66\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\x66\x62\x68\x78\x68\x60\x60\xf0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x66\xc2\xc0\xc0\xde\xc6\x66\x3a\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\xc6\xc6\xfe\xc6\xc6\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x1e\x0c\x0c\x0c\x0c\x0c\xcc\xcc\x78\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xe6\x66\x6c\x6c\x78\x6c\x6c\x66\xe6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xf0\x60\x60\x60\x60\x60\x62\x66\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xee\xfe\xfe\xd6\xc6\xc6\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x6c\xc6\xc6\xc6\xc6\xc6\x6c\x38\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfc\x66\x66\x66\x7c\x60\x60\x60\xf0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\xd6\xde\x7c\x0c\x0e\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfc\x66\x66\x66\x7c\x6c\x66\x66\xe6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7c\xc6\xc6\x60\x38\x0c\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7e\x7e\x5a\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6\x6c\x38\x10\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\xc6\xc6\xd6\xd6\xfe\x7c\x6c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\x6c\x38\x38\x38\x6c\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x66\x66\x66\x66\x3c\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\xc6\x8c\x18\x30\x60\xc2\xc6\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x30\x30\x30\x30\x30\x30\x30\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x80\xc0\xe0\x70\x38\x1c\x0e\x06\x02\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x10\x38\x6c\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00',
|
||||
b'\x00\x30\x30\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xe0\x60\x60\x78\x6c\x66\x66\x66\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7c\xc6\xc0\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x1c\x0c\x0c\x3c\x6c\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x6c\x64\x60\xf0\x60\x60\x60\xf0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\x7c\x0c\xcc\x78\x00\x00',
|
||||
b'\x00\x00\x00\xe0\x60\x60\x6c\x76\x66\x66\x66\xe6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x18\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x06\x06\x00\x0e\x06\x06\x06\x06\x66\x66\x3c\x00\x00',
|
||||
b'\x00\x00\x00\xe0\x60\x60\x66\x6c\x78\x6c\x66\xe6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x18\x18\x18\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xec\xfe\xd6\xd6\xd6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x66\x66\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xdc\x66\x66\x66\x7c\x60\x60\xf0\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x76\xcc\xcc\xcc\x7c\x0c\x0c\x1e\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xdc\x76\x66\x60\x60\xf0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7c\xc6\x70\x1c\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x10\x30\x30\xfc\x30\x30\x30\x36\x1c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x66\x66\x66\x66\x3c\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xc6\xc6\xd6\xd6\xfe\x6c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xc6\x6c\x38\x38\x6c\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xc6\xc6\xc6\xc6\x7e\x06\x0c\xf8\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xfe\xcc\x18\x30\x66\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x0e\x18\x18\x18\x70\x18\x18\x18\x0e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x18\x18\x18\x18\x00\x18\x18\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x70\x18\x18\x18\x0e\x18\x18\x18\x70\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x76\xdc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x10\x38\x6c\xc6\xc6\xfe\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3c\x66\xc2\xc0\xc0\xc2\x66\x3c\x0c\x06\x7c\x00\x00',
|
||||
b'\x00\x00\x00\xcc\xcc\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x0c\x18\x30\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x10\x38\x6c\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xcc\xcc\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x60\x30\x18\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x38\x6c\x38\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x3c\x66\x60\x66\x3c\x0c\x06\x3c\x00\x00\x00',
|
||||
b'\x00\x00\x10\x38\x6c\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xcc\xcc\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x60\x30\x18\x00\x7c\xc6\xfe\xc0\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x66\x66\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x18\x3c\x66\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x60\x30\x18\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\xc6\xc6\x10\x38\x6c\xc6\xc6\xfe\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x38\x6c\x38\x00\x38\x6c\xc6\xc6\xfe\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x18\x30\x60\x00\xfe\x66\x60\x7c\x60\x66\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\xcc\x76\x36\x7e\xd8\xd8\x6e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x3e\x6c\xcc\xcc\xfe\xcc\xcc\xcc\xce\x00\x00\x00\x00',
|
||||
b'\x00\x00\x10\x38\x6c\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x60\x30\x18\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x30\x78\xcc\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x60\x30\x18\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xc6\xc6\x00\xc6\xc6\xc6\xc6\x7e\x06\x0c\x78\x00\x00',
|
||||
b'\x00\x00\xc6\xc6\x38\x6c\xc6\xc6\xc6\xc6\x6c\x38\x00\x00\x00\x00',
|
||||
b'\x00\x00\xc6\xc6\x00\xc6\xc6\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x18\x18\x3c\x66\x60\x60\x66\x3c\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x38\x6c\x64\x60\xf0\x60\x60\x60\xe6\xfc\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x66\x66\x3c\x18\x7e\x18\x7e\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\xf8\xcc\xcc\xf8\xc4\xcc\xde\xcc\xcc\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x0e\x1b\x18\x18\x18\x7e\x18\x18\x18\x18\xd8\x70\x00\x00',
|
||||
b'\x00\x00\x18\x30\x60\x00\x78\x0c\x7c\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x0c\x18\x30\x00\x38\x18\x18\x18\x18\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x18\x30\x60\x00\x7c\xc6\xc6\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x18\x30\x60\x00\xcc\xcc\xcc\xcc\xcc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x76\xdc\x00\xdc\x66\x66\x66\x66\x66\x00\x00\x00\x00',
|
||||
b'\x00\x76\xdc\x00\xc6\xe6\xf6\xfe\xde\xce\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x3c\x6c\x6c\x3e\x00\x7e\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x38\x6c\x6c\x38\x00\x7c\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x30\x30\x00\x30\x30\x60\xc6\xc6\x7c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\xfe\xc0\xc0\xc0\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\xfe\x06\x06\x06\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\xc0\xc0\xc6\xcc\xd8\x30\x60\xdc\x86\x0c\x18\x3e\x00\x00',
|
||||
b'\x00\x00\xc0\xc0\xc6\xcc\xd8\x30\x66\xce\x9e\x3e\x06\x06\x00\x00',
|
||||
b'\x00\x00\x00\x18\x18\x00\x18\x18\x3c\x3c\x3c\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x36\x6c\xd8\x6c\x36\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\xd8\x6c\x36\x6c\xd8\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x11\x44\x00',
|
||||
b'\x00\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x55\xaa\x00',
|
||||
b'\x00\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\xdd\x77\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\xf8\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\xf8\x18\xf8\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\xf6\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xfe\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xf8\x18\xf8\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\xf6\x06\xf6\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xfe\x06\xf6\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\xf6\x06\xfe\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\xfe\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\xf8\x18\xf8\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\x1f\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\x1f\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\xff\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x1f\x18\x1f\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\x37\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x37\x30\x3f\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x3f\x30\x37\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\xf7\x00\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xff\x00\xf7\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x37\x30\x37\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xff\x00\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\xf7\x00\xf7\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\xff\x00\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\xff\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\xff\x00\xff\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\x3f\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x1f\x18\x1f\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x1f\x18\x1f\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x36\x36\x36\x36\x36\x36\x36\xff\x36\x36\x36\x36\x36\x36\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\xff\x18\xff\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\xf8\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\x00',
|
||||
b'\x00\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\xf0\x00',
|
||||
b'\x00\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x0f\x00',
|
||||
b'\x00\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x76\xdc\xd8\xd8\xdc\x76\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x7c\xc6\xfc\xc6\xc6\xfc\xc0\xc0\x40\x00\x00',
|
||||
b'\x00\x00\x00\xfe\xc6\xc6\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\xfe\x6c\x6c\x6c\x6c\x6c\x6c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\xfe\xc6\x60\x30\x18\x30\x60\xc6\xfe\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7e\xd8\xd8\xd8\xd8\x70\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x66\x66\x66\x66\x7c\x60\x60\xc0\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x76\xdc\x18\x18\x18\x18\x18\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x7e\x18\x3c\x66\x66\x66\x3c\x18\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x6c\xc6\xc6\xfe\xc6\xc6\x6c\x38\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x38\x6c\xc6\xc6\xc6\x6c\x6c\x6c\xee\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x1e\x30\x18\x0c\x3e\x66\x66\x66\x3c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x7e\xdb\xdb\x7e\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x03\x06\x7e\xdb\xdb\xf3\x7e\x60\xc0\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x1c\x30\x60\x60\x7c\x60\x60\x30\x1c\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x7c\xc6\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\xfe\x00\x00\xfe\x00\x00\xfe\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x18\x18\x7e\x18\x18\x00\x00\xff\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x30\x18\x0c\x06\x0c\x18\x30\x00\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x0c\x18\x30\x60\x30\x18\x0c\x00\x7e\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x0e\x1b\x1b\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00',
|
||||
b'\x00\x18\x18\x18\x18\x18\x18\x18\x18\xd8\xd8\x70\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x18\x18\x00\x7e\x00\x18\x18\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x76\xdc\x00\x76\xdc\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x38\x6c\x6c\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x0f\x0c\x0c\x0c\x0c\x0c\xec\x6c\x3c\x1c\x00\x00\x00\x00',
|
||||
b'\x00\x00\xd8\x6c\x6c\x6c\x6c\x6c\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x70\xd8\x30\x60\xc8\xf8\x00\x00\x00\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x7c\x7c\x7c\x7c\x7c\x7c\x00\x00\x00\x00\x00',
|
||||
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
|
||||
]
|
||||
BIN
klippy/extras/display/font8x14.pyc
Normal file
BIN
klippy/extras/display/font8x14.pyc
Normal file
Binary file not shown.
134
klippy/extras/display/hd44780.py
Normal file
134
klippy/extras/display/hd44780.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# Support for HD44780 (20x4 text) LCD displays
|
||||
#
|
||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
|
||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||
LINE_LENGTH_DEFAULT=20
|
||||
LINE_LENGTH_OPTIONS={16:16, 20:20}
|
||||
|
||||
TextGlyphs = { 'right_arrow': b'\x7e' }
|
||||
|
||||
HD44780_DELAY = .000040
|
||||
|
||||
class HD44780:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
# pin config
|
||||
ppins = self.printer.lookup_object('pins')
|
||||
pins = [ppins.lookup_pin(config.get(name + '_pin'))
|
||||
for name in ['rs', 'e', 'd4', 'd5', 'd6', 'd7']]
|
||||
self.hd44780_protocol_init = config.getboolean('hd44780_protocol_init',
|
||||
True)
|
||||
self.line_length = config.getchoice('line_length', LINE_LENGTH_OPTIONS,
|
||||
LINE_LENGTH_DEFAULT)
|
||||
mcu = None
|
||||
for pin_params in pins:
|
||||
if mcu is not None and pin_params['chip'] != mcu:
|
||||
raise ppins.error("hd44780 all pins must be on same mcu")
|
||||
mcu = pin_params['chip']
|
||||
self.pins = [pin_params['pin'] for pin_params in pins]
|
||||
self.mcu = mcu
|
||||
self.oid = self.mcu.create_oid()
|
||||
self.mcu.register_config_callback(self.build_config)
|
||||
self.send_data_cmd = self.send_cmds_cmd = None
|
||||
self.icons = {}
|
||||
# framebuffers
|
||||
self.text_framebuffers = [bytearray(b' '*2*self.line_length),
|
||||
bytearray(b' '*2*self.line_length)]
|
||||
self.glyph_framebuffer = bytearray(64)
|
||||
self.all_framebuffers = [
|
||||
# Text framebuffers
|
||||
(self.text_framebuffers[0], bytearray(b'~'*2*self.line_length),
|
||||
0x80),
|
||||
(self.text_framebuffers[1], bytearray(b'~'*2*self.line_length),
|
||||
0xc0),
|
||||
# Glyph framebuffer
|
||||
(self.glyph_framebuffer, bytearray(b'~'*64), 0x40) ]
|
||||
def build_config(self):
|
||||
self.mcu.add_config_cmd(
|
||||
"config_hd44780 oid=%d rs_pin=%s e_pin=%s"
|
||||
" d4_pin=%s d5_pin=%s d6_pin=%s d7_pin=%s delay_ticks=%d" % (
|
||||
self.oid, self.pins[0], self.pins[1],
|
||||
self.pins[2], self.pins[3], self.pins[4], self.pins[5],
|
||||
self.mcu.seconds_to_clock(HD44780_DELAY)))
|
||||
cmd_queue = self.mcu.alloc_command_queue()
|
||||
self.send_cmds_cmd = self.mcu.lookup_command(
|
||||
"hd44780_send_cmds oid=%c cmds=%*s", cq=cmd_queue)
|
||||
self.send_data_cmd = self.mcu.lookup_command(
|
||||
"hd44780_send_data oid=%c data=%*s", cq=cmd_queue)
|
||||
def send(self, cmds, is_data=False):
|
||||
cmd_type = self.send_cmds_cmd
|
||||
if is_data:
|
||||
cmd_type = self.send_data_cmd
|
||||
cmd_type.send([self.oid, cmds], reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
#logging.debug("hd44780 %d %s", is_data, repr(cmds))
|
||||
def flush(self):
|
||||
# Find all differences in the framebuffers and send them to the chip
|
||||
for new_data, old_data, fb_id in self.all_framebuffers:
|
||||
if new_data == old_data:
|
||||
continue
|
||||
# Find the position of all changed bytes in this framebuffer
|
||||
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
|
||||
if n != o]
|
||||
# Batch together changes that are close to each other
|
||||
for i in range(len(diffs)-2, -1, -1):
|
||||
pos, count = diffs[i]
|
||||
nextpos, nextcount = diffs[i+1]
|
||||
if pos + 4 >= nextpos and nextcount < 16:
|
||||
diffs[i][1] = nextcount + (nextpos - pos)
|
||||
del diffs[i+1]
|
||||
# Transmit changes
|
||||
for pos, count in diffs:
|
||||
chip_pos = pos
|
||||
self.send([fb_id + chip_pos])
|
||||
self.send(new_data[pos:pos+count], is_data=True)
|
||||
old_data[:] = new_data
|
||||
def init(self):
|
||||
curtime = self.printer.get_reactor().monotonic()
|
||||
print_time = self.mcu.estimated_print_time(curtime)
|
||||
# Program 4bit / 2-line mode and then issue 0x02 "Home" command
|
||||
if self.hd44780_protocol_init:
|
||||
init = [[0x33], [0x33], [0x32], [0x28, 0x28, 0x02]]
|
||||
else:
|
||||
init = [[0x02]]
|
||||
# Reset (set positive direction ; enable display and hide cursor)
|
||||
init.append([0x06, 0x0c])
|
||||
for i, cmds in enumerate(init):
|
||||
minclock = self.mcu.print_time_to_clock(print_time + i * .100)
|
||||
self.send_cmds_cmd.send([self.oid, cmds], minclock=minclock)
|
||||
self.flush()
|
||||
def write_text(self, x, y, data):
|
||||
if x + len(data) > self.line_length:
|
||||
data = data[:self.line_length - min(x, self.line_length)]
|
||||
pos = x + ((y & 0x02) >> 1) * self.line_length
|
||||
self.text_framebuffers[y & 1][pos:pos+len(data)] = data
|
||||
def set_glyphs(self, glyphs):
|
||||
for glyph_name, glyph_data in glyphs.items():
|
||||
data = glyph_data.get('icon5x8')
|
||||
if data is not None:
|
||||
self.icons[glyph_name] = data
|
||||
def write_glyph(self, x, y, glyph_name):
|
||||
data = self.icons.get(glyph_name)
|
||||
if data is not None:
|
||||
slot, bits = data
|
||||
self.write_text(x, y, [slot])
|
||||
self.glyph_framebuffer[slot * 8:(slot + 1) * 8] = bits
|
||||
return 1
|
||||
char = TextGlyphs.get(glyph_name)
|
||||
if char is not None:
|
||||
# Draw character
|
||||
self.write_text(x, y, char)
|
||||
return 1
|
||||
return 0
|
||||
def write_graphics(self, x, y, data):
|
||||
pass
|
||||
def clear(self):
|
||||
spaces = b' ' * 2*self.line_length
|
||||
self.text_framebuffers[0][:] = spaces
|
||||
self.text_framebuffers[1][:] = spaces
|
||||
def get_dimensions(self):
|
||||
return (self.line_length, 4)
|
||||
BIN
klippy/extras/display/hd44780.pyc
Normal file
BIN
klippy/extras/display/hd44780.pyc
Normal file
Binary file not shown.
125
klippy/extras/display/hd44780_spi.py
Normal file
125
klippy/extras/display/hd44780_spi.py
Normal file
@@ -0,0 +1,125 @@
|
||||
# Support for HD44780 (20x4 text) LCD displays
|
||||
#
|
||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
|
||||
# Copyright (C) 2021 Marc-Andre Denis <marcadenis@msn.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
from .. import bus
|
||||
|
||||
LINE_LENGTH_DEFAULT=20
|
||||
LINE_LENGTH_OPTIONS={16:16, 20:20}
|
||||
|
||||
TextGlyphs = { 'right_arrow': b'\x7e' }
|
||||
|
||||
|
||||
|
||||
class hd44780_spi:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.hd44780_protocol_init = config.getboolean('hd44780_protocol_init',
|
||||
True)
|
||||
# spi config
|
||||
self.spi = bus.MCU_SPI_from_config(
|
||||
config, 0x00, pin_option="latch_pin")
|
||||
self.mcu = self.spi.get_mcu()
|
||||
#self.spi.spi_send([0x01,0xa0])
|
||||
self.data_mask = (1<<1)
|
||||
self.command_mask = 0
|
||||
self.enable_mask = (1<<3)
|
||||
|
||||
self.icons = {}
|
||||
self.line_length = config.getchoice('line_length', LINE_LENGTH_OPTIONS,
|
||||
LINE_LENGTH_DEFAULT)
|
||||
|
||||
# framebuffers
|
||||
self.text_framebuffers = [bytearray(b' '*2*self.line_length),
|
||||
bytearray(b' '*2*self.line_length)]
|
||||
self.glyph_framebuffer = bytearray(64)
|
||||
self.all_framebuffers = [
|
||||
# Text framebuffers
|
||||
(self.text_framebuffers[0], bytearray(b'~'*2*self.line_length),
|
||||
0x80),
|
||||
(self.text_framebuffers[1], bytearray(b'~'*2*self.line_length),
|
||||
0xc0),
|
||||
# Glyph framebuffer
|
||||
(self.glyph_framebuffer, bytearray(b'~'*64), 0x40) ]
|
||||
def send_4_bits(self, cmd, is_data, minclock):
|
||||
if is_data:
|
||||
mask = self.data_mask
|
||||
else:
|
||||
mask = self.command_mask
|
||||
self.spi.spi_send([(cmd & 0xF0) | mask], minclock)
|
||||
self.spi.spi_send([(cmd & 0xF0) | mask | self.enable_mask], minclock)
|
||||
self.spi.spi_send([(cmd & 0xF0) | mask], minclock)
|
||||
def send(self, cmds, is_data=False, minclock=0):
|
||||
for data in cmds:
|
||||
self.send_4_bits(data,is_data,minclock)
|
||||
self.send_4_bits(data<<4,is_data,minclock)
|
||||
def flush(self):
|
||||
# Find all differences in the framebuffers and send them to the chip
|
||||
for new_data, old_data, fb_id in self.all_framebuffers:
|
||||
if new_data == old_data:
|
||||
continue
|
||||
# Find the position of all changed bytes in this framebuffer
|
||||
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
|
||||
if n != o]
|
||||
# Batch together changes that are close to each other
|
||||
for i in range(len(diffs)-2, -1, -1):
|
||||
pos, count = diffs[i]
|
||||
nextpos, nextcount = diffs[i+1]
|
||||
if pos + 4 >= nextpos and nextcount < 16:
|
||||
diffs[i][1] = nextcount + (nextpos - pos)
|
||||
del diffs[i+1]
|
||||
# Transmit changes
|
||||
for pos, count in diffs:
|
||||
chip_pos = pos
|
||||
self.send([fb_id + chip_pos])
|
||||
self.send(new_data[pos:pos+count], is_data=True)
|
||||
old_data[:] = new_data
|
||||
def init(self):
|
||||
curtime = self.printer.get_reactor().monotonic()
|
||||
print_time = self.mcu.estimated_print_time(curtime)
|
||||
# Program 4bit / 2-line mode and then issue 0x02 "Home" command
|
||||
if self.hd44780_protocol_init:
|
||||
init = [[0x33], [0x33], [0x32], [0x28, 0x28, 0x02]]
|
||||
else:
|
||||
init = [[0x02]]
|
||||
# Reset (set positive direction ; enable display and hide cursor)
|
||||
init.append([0x06, 0x0c])
|
||||
for i, cmds in enumerate(init):
|
||||
minclock = self.mcu.print_time_to_clock(print_time + i * .100)
|
||||
self.send(cmds, minclock=minclock)
|
||||
self.flush()
|
||||
def write_text(self, x, y, data):
|
||||
if x + len(data) > self.line_length:
|
||||
data = data[:self.line_length - min(x, self.line_length)]
|
||||
pos = x + ((y & 0x02) >> 1) * self.line_length
|
||||
self.text_framebuffers[y & 1][pos:pos+len(data)] = data
|
||||
def set_glyphs(self, glyphs):
|
||||
for glyph_name, glyph_data in glyphs.items():
|
||||
data = glyph_data.get('icon5x8')
|
||||
if data is not None:
|
||||
self.icons[glyph_name] = data
|
||||
def write_glyph(self, x, y, glyph_name):
|
||||
data = self.icons.get(glyph_name)
|
||||
if data is not None:
|
||||
slot, bits = data
|
||||
self.write_text(x, y, [slot])
|
||||
self.glyph_framebuffer[slot * 8:(slot + 1) * 8] = bits
|
||||
return 1
|
||||
char = TextGlyphs.get(glyph_name)
|
||||
if char is not None:
|
||||
# Draw character
|
||||
self.write_text(x, y, char)
|
||||
return 1
|
||||
return 0
|
||||
def write_graphics(self, x, y, data):
|
||||
pass
|
||||
def clear(self):
|
||||
spaces = b' ' * 2*self.line_length
|
||||
self.text_framebuffers[0][:] = spaces
|
||||
self.text_framebuffers[1][:] = spaces
|
||||
def get_dimensions(self):
|
||||
return (self.line_length, 4)
|
||||
BIN
klippy/extras/display/hd44780_spi.pyc
Normal file
BIN
klippy/extras/display/hd44780_spi.pyc
Normal file
Binary file not shown.
761
klippy/extras/display/menu.cfg
Normal file
761
klippy/extras/display/menu.cfg
Normal file
@@ -0,0 +1,761 @@
|
||||
# This file defines the default layout of the printer's menu.
|
||||
|
||||
# It is not necessary to edit this file to change the menu. Instead,
|
||||
# one may override any of the sections defined here by defining a
|
||||
# section with the same name in the main printer.cfg config file.
|
||||
|
||||
### DEFAULT MENU ###
|
||||
# Main
|
||||
# + Tune
|
||||
# + Speed: 000%
|
||||
# + Flow: 000%
|
||||
# + Offset Z:00.00
|
||||
# + OctoPrint
|
||||
# + Pause printing
|
||||
# + Resume printing
|
||||
# + Abort printing
|
||||
# + SD Card
|
||||
# + Start printing
|
||||
# + Resume printing
|
||||
# + Pause printing
|
||||
# + Cancel printing
|
||||
# + ... (files)
|
||||
# + Control
|
||||
# + Home All
|
||||
# + Home Z
|
||||
# + Home X/Y
|
||||
# + Steppers off
|
||||
# + Fan: OFF
|
||||
# + Fan speed: 000%
|
||||
# + Lights: OFF
|
||||
# + Lights: 000%
|
||||
# + Move 10mm
|
||||
# + Move X:000.0
|
||||
# + Move Y:000.0
|
||||
# + Move Z:000.0
|
||||
# + Move E:+000.0
|
||||
# + Move 1mm
|
||||
# + Move X:000.0
|
||||
# + Move Y:000.0
|
||||
# + Move Z:000.0
|
||||
# + Move E:+000.0
|
||||
# + Move 0.1mm
|
||||
# + Move X:000.0
|
||||
# + Move Y:000.0
|
||||
# + Move Z:000.0
|
||||
# + Move E:+000.0
|
||||
# + Temperature
|
||||
# + Ex0:000 (0000)
|
||||
# + Ex1:000 (0000)
|
||||
# + Bed:000 (0000)
|
||||
# + Preheat PLA
|
||||
# + Preheat all
|
||||
# + Preheat hotend
|
||||
# + Preheat hotbed
|
||||
# + Preheat ABS
|
||||
# + Preheat all
|
||||
# + Preheat hotend
|
||||
# + Preheat hotbed
|
||||
# + Cooldown
|
||||
# + Cooldown all
|
||||
# + Cooldown hotend
|
||||
# + Cooldown hotbed
|
||||
# + Filament
|
||||
# + Ex0:000 (0000)
|
||||
# + Load Fil. fast
|
||||
# + Load Fil. slow
|
||||
# + Unload Fil.fast
|
||||
# + Unload Fil.slow
|
||||
# + Feed: 000.0
|
||||
# + Setup
|
||||
# + Save config
|
||||
# + Restart
|
||||
# + Restart host
|
||||
# + Restart FW
|
||||
# + PID tuning
|
||||
# + Tune Hotend PID
|
||||
# + Tune Hotbed PID
|
||||
# + Calibration
|
||||
# + Delta cal. auto
|
||||
# + Delta cal. man
|
||||
# + Start probing
|
||||
# + Move Z: 000.00
|
||||
# + Test Z: ++
|
||||
# + Accept
|
||||
# + Abort
|
||||
# + Bed probe
|
||||
# + Dump parameters
|
||||
|
||||
### menu main ###
|
||||
[menu __main]
|
||||
type: list
|
||||
name: Main
|
||||
|
||||
### menu tune ###
|
||||
[menu __main __tune]
|
||||
type: list
|
||||
enable: {printer.idle_timeout.state == "Printing"}
|
||||
name: Tune
|
||||
|
||||
[menu __main __tune __speed]
|
||||
type: input
|
||||
name: Speed: {'%3d' % (menu.input*100)}%
|
||||
input: {printer.gcode_move.speed_factor}
|
||||
input_min: 0.01
|
||||
input_max: 5
|
||||
input_step: 0.01
|
||||
realtime: True
|
||||
gcode:
|
||||
M220 S{'%d' % (menu.input*100)}
|
||||
|
||||
[menu __main __tune __flow]
|
||||
type: input
|
||||
name: Flow: {'%3d' % (menu.input*100)}%
|
||||
input: {printer.gcode_move.extrude_factor}
|
||||
input_min: 0.01
|
||||
input_max: 2
|
||||
input_step: 0.01
|
||||
realtime: True
|
||||
gcode:
|
||||
M221 S{'%d' % (menu.input*100)}
|
||||
|
||||
[menu __main __tune __offsetz]
|
||||
type: input
|
||||
name: Offset Z:{'%05.3f' % menu.input}
|
||||
input: {printer.gcode_move.homing_origin.z}
|
||||
input_min: -5
|
||||
input_max: 5
|
||||
input_step: 0.005
|
||||
realtime: True
|
||||
gcode:
|
||||
SET_GCODE_OFFSET Z={'%.3f' % menu.input} MOVE=1
|
||||
|
||||
|
||||
### menu octoprint ###
|
||||
[menu __main __octoprint]
|
||||
type: list
|
||||
name: OctoPrint
|
||||
|
||||
[menu __main __octoprint __pause]
|
||||
type: command
|
||||
enable: {printer.idle_timeout.state == "Printing"}
|
||||
name: Pause printing
|
||||
gcode:
|
||||
{action_respond_info('action:pause')}
|
||||
|
||||
[menu __main __octoprint __resume]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Resume printing
|
||||
gcode:
|
||||
{action_respond_info('action:resume')}
|
||||
|
||||
[menu __main __octoprint __abort]
|
||||
type: command
|
||||
enable: {printer.idle_timeout.state == "Printing"}
|
||||
name: Abort printing
|
||||
gcode:
|
||||
{action_respond_info('action:cancel')}
|
||||
|
||||
### menu virtual sdcard ###
|
||||
[menu __main __sdcard]
|
||||
type: vsdlist
|
||||
enable: {('virtual_sdcard' in printer)}
|
||||
name: SD Card
|
||||
|
||||
[menu __main __sdcard __start]
|
||||
type: command
|
||||
enable: {('virtual_sdcard' in printer) and printer.virtual_sdcard.file_path and not printer.virtual_sdcard.is_active}
|
||||
name: Start printing
|
||||
gcode: M24
|
||||
|
||||
[menu __main __sdcard __resume]
|
||||
type: command
|
||||
enable: {('virtual_sdcard' in printer) and printer.print_stats.state == "paused"}
|
||||
name: Resume printing
|
||||
gcode:
|
||||
{% if "pause_resume" in printer %}
|
||||
RESUME
|
||||
{% else %}
|
||||
M24
|
||||
{% endif %}
|
||||
|
||||
[menu __main __sdcard __pause]
|
||||
type: command
|
||||
enable: {('virtual_sdcard' in printer) and printer.print_stats.state == "printing"}
|
||||
name: Pause printing
|
||||
gcode:
|
||||
{% if "pause_resume" in printer %}
|
||||
PAUSE
|
||||
{% else %}
|
||||
M25
|
||||
{% endif %}
|
||||
|
||||
[menu __main __sdcard __cancel]
|
||||
type: command
|
||||
enable: {('virtual_sdcard' in printer) and (printer.print_stats.state == "printing" or printer.print_stats.state == "paused")}
|
||||
name: Cancel printing
|
||||
gcode:
|
||||
{% if 'pause_resume' in printer %}
|
||||
CANCEL_PRINT
|
||||
{% else %}
|
||||
M25
|
||||
M27
|
||||
M26 S0
|
||||
TURN_OFF_HEATERS
|
||||
{% if printer.toolhead.position.z <= printer.toolhead.axis_maximum.z - 5 %}
|
||||
G91
|
||||
G0 Z5 F1000
|
||||
G90
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
### menu control ###
|
||||
[menu __main __control]
|
||||
type: list
|
||||
name: Control
|
||||
|
||||
[menu __main __control __home]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Home All
|
||||
gcode: G28
|
||||
|
||||
[menu __main __control __homez]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Home Z
|
||||
gcode: G28 Z
|
||||
|
||||
[menu __main __control __homexy]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Home X/Y
|
||||
gcode: G28 X Y
|
||||
|
||||
[menu __main __control __disable]
|
||||
type: command
|
||||
name: Steppers off
|
||||
gcode:
|
||||
M84
|
||||
M18
|
||||
|
||||
[menu __main __control __fanonoff]
|
||||
type: input
|
||||
enable: {'fan' in printer}
|
||||
name: Fan: {'ON ' if menu.input else 'OFF'}
|
||||
input: {printer.fan.speed}
|
||||
input_min: 0
|
||||
input_max: 1
|
||||
input_step: 1
|
||||
gcode:
|
||||
M106 S{255 if menu.input else 0}
|
||||
|
||||
[menu __main __control __fanspeed]
|
||||
type: input
|
||||
enable: {'fan' in printer}
|
||||
name: Fan speed: {'%3d' % (menu.input*100)}%
|
||||
input: {printer.fan.speed}
|
||||
input_min: 0
|
||||
input_max: 1
|
||||
input_step: 0.01
|
||||
gcode:
|
||||
M106 S{'%d' % (menu.input*255)}
|
||||
|
||||
[menu __main __control __caselightonoff]
|
||||
type: input
|
||||
enable: {'output_pin caselight' in printer}
|
||||
name: Lights: {'ON ' if menu.input else 'OFF'}
|
||||
input: {printer['output_pin caselight'].value}
|
||||
input_min: 0
|
||||
input_max: 1
|
||||
input_step: 1
|
||||
gcode:
|
||||
SET_PIN PIN=caselight VALUE={1 if menu.input else 0}
|
||||
|
||||
[menu __main __control __caselightpwm]
|
||||
type: input
|
||||
enable: {'output_pin caselight' in printer}
|
||||
name: Lights: {'%3d' % (menu.input*100)}%
|
||||
input: {printer['output_pin caselight'].value}
|
||||
input_min: 0.0
|
||||
input_max: 1.0
|
||||
input_step: 0.01
|
||||
gcode:
|
||||
SET_PIN PIN=caselight VALUE={menu.input}
|
||||
|
||||
### menu move 10mm ###
|
||||
[menu __main __control __move_10mm]
|
||||
type: list
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move 10mm
|
||||
|
||||
[menu __main __control __move_10mm __axis_x]
|
||||
type: input
|
||||
name: Move X:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.x}
|
||||
input_min: {printer.toolhead.axis_minimum.x}
|
||||
input_max: {printer.toolhead.axis_maximum.x}
|
||||
input_step: 10.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 X{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_10mm __axis_y]
|
||||
type: input
|
||||
name: Move Y:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.y}
|
||||
input_min: {printer.toolhead.axis_minimum.y}
|
||||
input_max: {printer.toolhead.axis_maximum.y}
|
||||
input_step: 10.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Y{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_10mm __axis_z]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move Z:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.z}
|
||||
input_min: 0
|
||||
input_max: {printer.toolhead.axis_maximum.z}
|
||||
input_step: 10.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Z{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_10mm __axis_e]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move E:{'%+06.1f' % menu.input}
|
||||
input: 0
|
||||
input_min: -{printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_max: {printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_step: 10.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
M83
|
||||
G1 E{menu.input} F240
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
### menu move 1mm ###
|
||||
[menu __main __control __move_1mm]
|
||||
type: list
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move 1mm
|
||||
|
||||
[menu __main __control __move_1mm __axis_x]
|
||||
type: input
|
||||
name: Move X:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.x}
|
||||
input_min: {printer.toolhead.axis_minimum.x}
|
||||
input_max: {printer.toolhead.axis_maximum.x}
|
||||
input_step: 1.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 X{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_1mm __axis_y]
|
||||
type: input
|
||||
name: Move Y:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.y}
|
||||
input_min: {printer.toolhead.axis_minimum.y}
|
||||
input_max: {printer.toolhead.axis_maximum.y}
|
||||
input_step: 1.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Y{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_1mm __axis_z]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move Z:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.z}
|
||||
input_min: 0
|
||||
input_max: {printer.toolhead.axis_maximum.z}
|
||||
input_step: 1.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Z{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_1mm __axis_e]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move E:{'%+06.1f' % menu.input}
|
||||
input: 0
|
||||
input_min: -{printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_max: {printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_step: 1.0
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
M83
|
||||
G1 E{menu.input} F240
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
### menu move 0.1mm ###
|
||||
[menu __main __control __move_01mm]
|
||||
type: list
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move 0.1mm
|
||||
|
||||
[menu __main __control __move_01mm __axis_x]
|
||||
type: input
|
||||
name: Move X:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.x}
|
||||
input_min: {printer.toolhead.axis_minimum.x}
|
||||
input_max: {printer.toolhead.axis_maximum.x}
|
||||
input_step: 0.1
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 X{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_01mm __axis_y]
|
||||
type: input
|
||||
name: Move Y:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.y}
|
||||
input_min: {printer.toolhead.axis_minimum.y}
|
||||
input_max: {printer.toolhead.axis_maximum.y}
|
||||
input_step: 0.1
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Y{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_01mm __axis_z]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move Z:{'%05.1f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.z}
|
||||
input_min: 0
|
||||
input_max: {printer.toolhead.axis_maximum.z}
|
||||
input_step: 0.1
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G90
|
||||
G1 Z{menu.input}
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
[menu __main __control __move_01mm __axis_e]
|
||||
type: input
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Move E:{'%+06.1f' % menu.input}
|
||||
input: 0
|
||||
input_min: -{printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_max: {printer.configfile.config.extruder.max_extrude_only_distance|default(50)}
|
||||
input_step: 0.1
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
M83
|
||||
G1 E{menu.input} F240
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
|
||||
### menu temperature ###
|
||||
[menu __main __temp]
|
||||
type: list
|
||||
name: Temperature
|
||||
|
||||
[menu __main __temp __hotend0_target]
|
||||
type: input
|
||||
enable: {('extruder' in printer) and ('extruder' in printer.heaters.available_heaters)}
|
||||
name: {"Ex0:%3.0f (%4.0f)" % (menu.input, printer.extruder.temperature)}
|
||||
input: {printer.extruder.target}
|
||||
input_min: 0
|
||||
input_max: {printer.configfile.config.extruder.max_temp}
|
||||
input_step: 1
|
||||
gcode: M104 T0 S{'%.0f' % menu.input}
|
||||
|
||||
[menu __main __temp __hotend1_target]
|
||||
type: input
|
||||
enable: {('extruder1' in printer) and ('extruder1' in printer.heaters.available_heaters)}
|
||||
name: {"Ex1:%3.0f (%4.0f)" % (menu.input, printer.extruder1.temperature)}
|
||||
input: {printer.extruder1.target}
|
||||
input_min: 0
|
||||
input_max: {printer.configfile.config.extruder1.max_temp}
|
||||
input_step: 1
|
||||
gcode: M104 T1 S{'%.0f' % menu.input}
|
||||
|
||||
[menu __main __temp __hotbed_target]
|
||||
type: input
|
||||
enable: {'heater_bed' in printer}
|
||||
name: {"Bed:%3.0f (%4.0f)" % (menu.input, printer.heater_bed.temperature)}
|
||||
input: {printer.heater_bed.target}
|
||||
input_min: 0
|
||||
input_max: {printer.configfile.config.heater_bed.max_temp}
|
||||
input_step: 1
|
||||
gcode: M140 S{'%.0f' % menu.input}
|
||||
|
||||
[menu __main __temp __preheat_pla]
|
||||
type: list
|
||||
name: Preheat PLA
|
||||
|
||||
[menu __main __temp __preheat_pla __all]
|
||||
type: command
|
||||
enable: {('extruder' in printer) and ('heater_bed' in printer)}
|
||||
name: Preheat all
|
||||
gcode:
|
||||
M140 S60
|
||||
M104 S200
|
||||
|
||||
[menu __main __temp __preheat_pla __hotend]
|
||||
type: command
|
||||
enable: {'extruder' in printer}
|
||||
name: Preheat hotend
|
||||
gcode: M104 S200
|
||||
|
||||
[menu __main __temp __preheat_pla __hotbed]
|
||||
type: command
|
||||
enable: {'heater_bed' in printer}
|
||||
name: Preheat hotbed
|
||||
gcode: M140 S60
|
||||
|
||||
[menu __main __temp __preheat_abs]
|
||||
type: list
|
||||
name: Preheat ABS
|
||||
|
||||
[menu __main __temp __preheat_abs __all]
|
||||
type: command
|
||||
enable: {('extruder' in printer) and ('heater_bed' in printer)}
|
||||
name: Preheat all
|
||||
gcode:
|
||||
M140 S110
|
||||
M104 S245
|
||||
|
||||
[menu __main __temp __preheat_abs __hotend]
|
||||
type: command
|
||||
enable: {'extruder' in printer}
|
||||
name: Preheat hotend
|
||||
gcode: M104 S245
|
||||
|
||||
[menu __main __temp __preheat_abs __hotbed]
|
||||
type: command
|
||||
enable: {'heater_bed' in printer}
|
||||
name: Preheat hotbed
|
||||
gcode: M140 S110
|
||||
|
||||
[menu __main __temp __cooldown]
|
||||
type: list
|
||||
name: Cooldown
|
||||
|
||||
[menu __main __temp __cooldown __all]
|
||||
type: command
|
||||
enable: {('extruder' in printer) and ('heater_bed' in printer)}
|
||||
name: Cooldown all
|
||||
gcode:
|
||||
M104 S0
|
||||
M140 S0
|
||||
|
||||
[menu __main __temp __cooldown __hotend]
|
||||
type: command
|
||||
enable: {'extruder' in printer}
|
||||
name: Cooldown hotend
|
||||
gcode: M104 S0
|
||||
|
||||
[menu __main __temp __cooldown __hotbed]
|
||||
type: command
|
||||
enable: {'heater_bed' in printer}
|
||||
name: Cooldown hotbed
|
||||
gcode: M140 S0
|
||||
|
||||
### menu filament ###
|
||||
|
||||
[menu __main __filament]
|
||||
type: list
|
||||
name: Filament
|
||||
|
||||
[menu __main __filament __hotend0_target]
|
||||
type: input
|
||||
enable: {'extruder' in printer}
|
||||
name: {"Ex0:%3.0f (%4.0f)" % (menu.input, printer.extruder.temperature)}
|
||||
input: {printer.extruder.target}
|
||||
input_min: 0
|
||||
input_max: {printer.configfile.config.extruder.max_temp}
|
||||
input_step: 1
|
||||
gcode: M104 T0 S{'%.0f' % menu.input}
|
||||
|
||||
[menu __main __filament __loadf]
|
||||
type: command
|
||||
name: Load Fil. fast
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__filament__load
|
||||
M83
|
||||
G1 E50 F960
|
||||
RESTORE_GCODE_STATE NAME=__filament__load
|
||||
|
||||
[menu __main __filament __loads]
|
||||
type: command
|
||||
name: Load Fil. slow
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__filament__load
|
||||
M83
|
||||
G1 E50 F240
|
||||
RESTORE_GCODE_STATE NAME=__filament__load
|
||||
|
||||
[menu __main __filament __unloadf]
|
||||
type: command
|
||||
name: Unload Fil.fast
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__filament__load
|
||||
M83
|
||||
G1 E-50 F960
|
||||
RESTORE_GCODE_STATE NAME=__filament__load
|
||||
|
||||
[menu __main __filament __unloads]
|
||||
type: command
|
||||
name: Unload Fil.slow
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__filament__load
|
||||
M83
|
||||
G1 E-50 F240
|
||||
RESTORE_GCODE_STATE NAME=__filament__load
|
||||
|
||||
[menu __main __filament __feed]
|
||||
type: input
|
||||
name: Feed: {'%.1f' % menu.input}
|
||||
input: 5
|
||||
input_step: 0.1
|
||||
gcode:
|
||||
SAVE_GCODE_STATE NAME=__filament__load
|
||||
M83
|
||||
G1 E{'%.1f' % menu.input} F60
|
||||
RESTORE_GCODE_STATE NAME=__filament__load
|
||||
|
||||
### menu setup ###
|
||||
[menu __main __setup]
|
||||
type: list
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Setup
|
||||
|
||||
[menu __main __setup __save_config]
|
||||
type: command
|
||||
name: Save config
|
||||
gcode: SAVE_CONFIG
|
||||
|
||||
[menu __main __setup __restart]
|
||||
type: list
|
||||
name: Restart
|
||||
|
||||
[menu __main __setup __restart __host_restart]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Restart host
|
||||
gcode: RESTART
|
||||
|
||||
[menu __main __setup __restart __firmware_restart]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Restart FW
|
||||
gcode: FIRMWARE_RESTART
|
||||
|
||||
[menu __main __setup __tuning]
|
||||
type: list
|
||||
name: PID tuning
|
||||
|
||||
[menu __main __setup __tuning __hotend_pid_tuning]
|
||||
type: command
|
||||
enable: {(not printer.idle_timeout.state == "Printing") and ('extruder' in printer)}
|
||||
name: Tune Hotend PID
|
||||
gcode: PID_CALIBRATE HEATER=extruder TARGET=210 WRITE_FILE=1
|
||||
|
||||
[menu __main __setup __tuning __hotbed_pid_tuning]
|
||||
type: command
|
||||
enable: {(not printer.idle_timeout.state == "Printing") and ('heater_bed' in printer)}
|
||||
name: Tune Hotbed PID
|
||||
gcode: PID_CALIBRATE HEATER=heater_bed TARGET=60 WRITE_FILE=1
|
||||
|
||||
[menu __main __setup __calib]
|
||||
type: list
|
||||
name: Calibration
|
||||
|
||||
[menu __main __setup __calib __delta_calib_auto]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Delta cal. auto
|
||||
gcode:
|
||||
G28
|
||||
DELTA_CALIBRATE
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man]
|
||||
type: list
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Delta cal. man
|
||||
|
||||
[menu __main __setup __calib __bedprobe]
|
||||
type: command
|
||||
enable: {not printer.idle_timeout.state == "Printing"}
|
||||
name: Bed probe
|
||||
gcode: PROBE
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man __start]
|
||||
type: command
|
||||
name: Start probing
|
||||
gcode:
|
||||
G28
|
||||
DELTA_CALIBRATE METHOD=manual
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man __move_z]
|
||||
type: input
|
||||
name: Move Z: {'%03.2f' % menu.input}
|
||||
input: {printer.gcode_move.gcode_position.z}
|
||||
input_step: 1
|
||||
realtime: True
|
||||
gcode:
|
||||
{%- if menu.event == 'change' -%}
|
||||
G1 Z{'%.2f' % menu.input}
|
||||
{%- elif menu.event == 'long_click' -%}
|
||||
G1 Z{'%.2f' % menu.input}
|
||||
SAVE_GCODE_STATE NAME=__move__axis
|
||||
G91
|
||||
G1 Z2
|
||||
G1 Z-2
|
||||
RESTORE_GCODE_STATE NAME=__move__axis
|
||||
{%- endif -%}
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man __test_z]
|
||||
type: input
|
||||
name: Test Z: {['++','+','+.01','+.05','+.1','+.5','-.5','-.1','-.05','-.01','-','--'][menu.input|int]}
|
||||
input: 6
|
||||
input_min: 0
|
||||
input_max: 11
|
||||
input_step: 1
|
||||
gcode:
|
||||
{%- if menu.event == 'long_click' -%}
|
||||
TESTZ Z={['++','+','+.01','+.05','+.1','+.5','-.5','-.1','-.05','-.01','-','--'][menu.input|int]}
|
||||
{%- endif -%}
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man __accept]
|
||||
type: command
|
||||
name: Accept
|
||||
gcode: ACCEPT
|
||||
|
||||
[menu __main __setup __calib __delta_calib_man __abort]
|
||||
type: command
|
||||
name: Abort
|
||||
gcode: ABORT
|
||||
|
||||
[menu __main __setup __dump]
|
||||
type: command
|
||||
name: Dump parameters
|
||||
gcode:
|
||||
{% for name1 in printer %}
|
||||
{% for name2 in printer[name1] %}
|
||||
{ action_respond_info("printer['%s'].%s = %s"
|
||||
% (name1, name2, printer[name1][name2])) }
|
||||
{% else %}
|
||||
{ action_respond_info("printer['%s'] = %s" % (name1, printer[name1])) }
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
1067
klippy/extras/display/menu.py
Normal file
1067
klippy/extras/display/menu.py
Normal file
File diff suppressed because it is too large
Load Diff
BIN
klippy/extras/display/menu.pyc
Normal file
BIN
klippy/extras/display/menu.pyc
Normal file
Binary file not shown.
108
klippy/extras/display/menu_keys.py
Normal file
108
klippy/extras/display/menu_keys.py
Normal file
@@ -0,0 +1,108 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Support for menu button press tracking
|
||||
#
|
||||
# Copyright (C) 2018 Janar Sööt <janar.soot@gmail.com>
|
||||
# Copyright (C) 2020 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
|
||||
LONG_PRESS_DURATION = 0.800
|
||||
TIMER_DELAY = .200
|
||||
|
||||
class MenuKeys:
|
||||
def __init__(self, config, callback):
|
||||
self.printer = config.get_printer()
|
||||
self.reactor = self.printer.get_reactor()
|
||||
self.callback = callback
|
||||
buttons = self.printer.load_object(config, "buttons")
|
||||
# Register rotary encoder
|
||||
encoder_pins = config.get('encoder_pins', None)
|
||||
encoder_steps_per_detent = config.getchoice('encoder_steps_per_detent',
|
||||
{2: 2, 4: 4}, 4)
|
||||
if encoder_pins is not None:
|
||||
try:
|
||||
pin1, pin2 = encoder_pins.split(',')
|
||||
except:
|
||||
raise config.error("Unable to parse encoder_pins")
|
||||
buttons.register_rotary_encoder(pin1.strip(), pin2.strip(),
|
||||
self.encoder_cw_callback,
|
||||
self.encoder_ccw_callback,
|
||||
encoder_steps_per_detent)
|
||||
self.encoder_fast_rate = config.getfloat('encoder_fast_rate',
|
||||
.030, above=0.)
|
||||
self.last_encoder_cw_eventtime = 0
|
||||
self.last_encoder_ccw_eventtime = 0
|
||||
# Register click button
|
||||
self.is_short_click = False
|
||||
self.click_timer = self.reactor.register_timer(self.long_click_event)
|
||||
self.register_button(config, 'click_pin', self.click_callback, False)
|
||||
# Register other buttons
|
||||
self.register_button(config, 'back_pin', self.back_callback)
|
||||
self.register_button(config, 'up_pin', self.up_callback)
|
||||
self.register_button(config, 'down_pin', self.down_callback)
|
||||
self.register_button(config, 'kill_pin', self.kill_callback)
|
||||
|
||||
def register_button(self, config, name, callback, push_only=True):
|
||||
pin = config.get(name, None)
|
||||
if pin is None:
|
||||
return
|
||||
buttons = self.printer.lookup_object("buttons")
|
||||
if config.get('analog_range_' + name, None) is None:
|
||||
if push_only:
|
||||
buttons.register_button_push(pin, callback)
|
||||
else:
|
||||
buttons.register_buttons([pin], callback)
|
||||
return
|
||||
amin, amax = config.getfloatlist('analog_range_' + name, count=2)
|
||||
pullup = config.getfloat('analog_pullup_resistor', 4700., above=0.)
|
||||
if push_only:
|
||||
buttons.register_adc_button_push(pin, amin, amax, pullup, callback)
|
||||
else:
|
||||
buttons.register_adc_button(pin, amin, amax, pullup, callback)
|
||||
|
||||
# Rotary encoder callbacks
|
||||
def encoder_cw_callback(self, eventtime):
|
||||
fast_rate = ((eventtime - self.last_encoder_cw_eventtime)
|
||||
<= self.encoder_fast_rate)
|
||||
self.last_encoder_cw_eventtime = eventtime
|
||||
if fast_rate:
|
||||
self.callback('fast_up', eventtime)
|
||||
else:
|
||||
self.callback('up', eventtime)
|
||||
|
||||
def encoder_ccw_callback(self, eventtime):
|
||||
fast_rate = ((eventtime - self.last_encoder_ccw_eventtime)
|
||||
<= self.encoder_fast_rate)
|
||||
self.last_encoder_ccw_eventtime = eventtime
|
||||
if fast_rate:
|
||||
self.callback('fast_down', eventtime)
|
||||
else:
|
||||
self.callback('down', eventtime)
|
||||
|
||||
# Click handling
|
||||
def long_click_event(self, eventtime):
|
||||
self.is_short_click = False
|
||||
self.callback('long_click', eventtime)
|
||||
return self.reactor.NEVER
|
||||
|
||||
def click_callback(self, eventtime, state):
|
||||
if state:
|
||||
self.is_short_click = True
|
||||
self.reactor.update_timer(self.click_timer,
|
||||
eventtime + LONG_PRESS_DURATION)
|
||||
elif self.is_short_click:
|
||||
self.reactor.update_timer(self.click_timer, self.reactor.NEVER)
|
||||
self.callback('click', eventtime)
|
||||
|
||||
# Other button callbacks
|
||||
def back_callback(self, eventtime):
|
||||
self.callback('back', eventtime)
|
||||
|
||||
def up_callback(self, eventtime):
|
||||
self.callback('up', eventtime)
|
||||
|
||||
def down_callback(self, eventtime):
|
||||
self.callback('down', eventtime)
|
||||
|
||||
def kill_callback(self, eventtime):
|
||||
self.printer.invoke_shutdown("Shutdown due to kill button!")
|
||||
BIN
klippy/extras/display/menu_keys.pyc
Normal file
BIN
klippy/extras/display/menu_keys.pyc
Normal file
Binary file not shown.
257
klippy/extras/display/st7920.py
Normal file
257
klippy/extras/display/st7920.py
Normal file
@@ -0,0 +1,257 @@
|
||||
# Support for ST7920 (128x64 graphics) LCD displays
|
||||
#
|
||||
# Copyright (C) 2018 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
from .. import bus
|
||||
from . import font8x14
|
||||
|
||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||
|
||||
# Spec says 72us, but faster is possible in practice
|
||||
ST7920_CMD_DELAY = .000020
|
||||
ST7920_SYNC_DELAY = .000045
|
||||
|
||||
TextGlyphs = { 'right_arrow': b'\x1a' }
|
||||
CharGlyphs = { 'degrees': bytearray(font8x14.VGA_FONT[0xf8]) }
|
||||
|
||||
class DisplayBase:
|
||||
def __init__(self):
|
||||
# framebuffers
|
||||
self.text_framebuffer = bytearray(b' '*64)
|
||||
self.glyph_framebuffer = bytearray(128)
|
||||
self.graphics_framebuffers = [bytearray(32) for i in range(32)]
|
||||
self.all_framebuffers = [
|
||||
# Text framebuffer
|
||||
(self.text_framebuffer, bytearray(b'~'*64), 0x80),
|
||||
# Glyph framebuffer
|
||||
(self.glyph_framebuffer, bytearray(b'~'*128), 0x40),
|
||||
# Graphics framebuffers
|
||||
] + [(self.graphics_framebuffers[i], bytearray(b'~'*32), i)
|
||||
for i in range(32)]
|
||||
self.cached_glyphs = {}
|
||||
self.icons = {}
|
||||
def flush(self):
|
||||
# Find all differences in the framebuffers and send them to the chip
|
||||
for new_data, old_data, fb_id in self.all_framebuffers:
|
||||
if new_data == old_data:
|
||||
continue
|
||||
# Find the position of all changed bytes in this framebuffer
|
||||
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
|
||||
if n != o]
|
||||
# Batch together changes that are close to each other
|
||||
for i in range(len(diffs)-2, -1, -1):
|
||||
pos, count = diffs[i]
|
||||
nextpos, nextcount = diffs[i+1]
|
||||
if pos + 5 >= nextpos and nextcount < 16:
|
||||
diffs[i][1] = nextcount + (nextpos - pos)
|
||||
del diffs[i+1]
|
||||
# Transmit changes
|
||||
for pos, count in diffs:
|
||||
count += pos & 0x01
|
||||
count += count & 0x01
|
||||
pos = pos & ~0x01
|
||||
chip_pos = pos >> 1
|
||||
if fb_id < 0x40:
|
||||
# Graphics framebuffer update
|
||||
self.send([0x80 + fb_id, 0x80 + chip_pos], is_extended=True)
|
||||
else:
|
||||
self.send([fb_id + chip_pos])
|
||||
self.send(new_data[pos:pos+count], is_data=True)
|
||||
old_data[:] = new_data
|
||||
def init(self):
|
||||
cmds = [0x24, # Enter extended mode
|
||||
0x40, # Clear vertical scroll address
|
||||
0x02, # Enable CGRAM access
|
||||
0x26, # Enable graphics
|
||||
0x22, # Leave extended mode
|
||||
0x02, # Home the display
|
||||
0x06, # Set positive update direction
|
||||
0x0c] # Enable display and hide cursor
|
||||
self.send(cmds)
|
||||
self.flush()
|
||||
def cache_glyph(self, glyph_name, base_glyph_name, glyph_id):
|
||||
icon = self.icons.get(glyph_name)
|
||||
base_icon = self.icons.get(base_glyph_name)
|
||||
if icon is None or base_icon is None:
|
||||
return
|
||||
all_bits = zip(icon[0], icon[1], base_icon[0], base_icon[1])
|
||||
for i, (ic1, ic2, b1, b2) in enumerate(all_bits):
|
||||
x1, x2 = ic1 ^ b1, ic2 ^ b2
|
||||
pos = glyph_id*32 + i*2
|
||||
self.glyph_framebuffer[pos:pos+2] = [x1, x2]
|
||||
self.all_framebuffers[1][1][pos:pos+2] = [x1 ^ 1, x2 ^ 1]
|
||||
self.cached_glyphs[glyph_name] = (base_glyph_name, (0, glyph_id*2))
|
||||
def set_glyphs(self, glyphs):
|
||||
for glyph_name, glyph_data in glyphs.items():
|
||||
icon = glyph_data.get('icon16x16')
|
||||
if icon is not None:
|
||||
self.icons[glyph_name] = icon
|
||||
# Setup animated glyphs
|
||||
self.cache_glyph('fan2', 'fan1', 0)
|
||||
self.cache_glyph('bed_heat2', 'bed_heat1', 1)
|
||||
def write_text(self, x, y, data):
|
||||
if x + len(data) > 16:
|
||||
data = data[:16 - min(x, 16)]
|
||||
pos = [0, 32, 16, 48][y] + x
|
||||
self.text_framebuffer[pos:pos+len(data)] = data
|
||||
def write_graphics(self, x, y, data):
|
||||
if x >= 16 or y >= 4 or len(data) != 16:
|
||||
return
|
||||
gfx_fb = y * 16
|
||||
if gfx_fb >= 32:
|
||||
gfx_fb -= 32
|
||||
x += 16
|
||||
for i, bits in enumerate(data):
|
||||
self.graphics_framebuffers[gfx_fb + i][x] = bits
|
||||
def write_glyph(self, x, y, glyph_name):
|
||||
glyph_id = self.cached_glyphs.get(glyph_name)
|
||||
if glyph_id is not None and x & 1 == 0:
|
||||
# Render cached icon using character generator
|
||||
glyph_name = glyph_id[0]
|
||||
self.write_text(x, y, glyph_id[1])
|
||||
icon = self.icons.get(glyph_name)
|
||||
if icon is not None:
|
||||
# Draw icon in graphics mode
|
||||
self.write_graphics(x, y, icon[0])
|
||||
self.write_graphics(x + 1, y, icon[1])
|
||||
return 2
|
||||
char = TextGlyphs.get(glyph_name)
|
||||
if char is not None:
|
||||
# Draw character
|
||||
self.write_text(x, y, char)
|
||||
return 1
|
||||
font = CharGlyphs.get(glyph_name)
|
||||
if font is not None:
|
||||
# Draw single width character
|
||||
self.write_graphics(x, y, font)
|
||||
return 1
|
||||
return 0
|
||||
def clear(self):
|
||||
self.text_framebuffer[:] = b' '*64
|
||||
zeros = bytearray(32)
|
||||
for gfb in self.graphics_framebuffers:
|
||||
gfb[:] = zeros
|
||||
def get_dimensions(self):
|
||||
return (16, 4)
|
||||
|
||||
# Display driver for stock ST7920 displays
|
||||
class ST7920(DisplayBase):
|
||||
def __init__(self, config):
|
||||
printer = config.get_printer()
|
||||
# pin config
|
||||
ppins = printer.lookup_object('pins')
|
||||
pins = [ppins.lookup_pin(config.get(name + '_pin'))
|
||||
for name in ['cs', 'sclk', 'sid']]
|
||||
mcu = None
|
||||
for pin_params in pins:
|
||||
if mcu is not None and pin_params['chip'] != mcu:
|
||||
raise ppins.error("st7920 all pins must be on same mcu")
|
||||
mcu = pin_params['chip']
|
||||
self.pins = [pin_params['pin'] for pin_params in pins]
|
||||
# prepare send functions
|
||||
self.mcu = mcu
|
||||
self.oid = self.mcu.create_oid()
|
||||
self.mcu.register_config_callback(self.build_config)
|
||||
self.send_data_cmd = self.send_cmds_cmd = None
|
||||
self.is_extended = False
|
||||
# init display base
|
||||
DisplayBase.__init__(self)
|
||||
def build_config(self):
|
||||
# configure send functions
|
||||
self.mcu.add_config_cmd(
|
||||
"config_st7920 oid=%u cs_pin=%s sclk_pin=%s sid_pin=%s"
|
||||
" sync_delay_ticks=%d cmd_delay_ticks=%d" % (
|
||||
self.oid, self.pins[0], self.pins[1], self.pins[2],
|
||||
self.mcu.seconds_to_clock(ST7920_SYNC_DELAY),
|
||||
self.mcu.seconds_to_clock(ST7920_CMD_DELAY)))
|
||||
cmd_queue = self.mcu.alloc_command_queue()
|
||||
self.send_cmds_cmd = self.mcu.lookup_command(
|
||||
"st7920_send_cmds oid=%c cmds=%*s", cq=cmd_queue)
|
||||
self.send_data_cmd = self.mcu.lookup_command(
|
||||
"st7920_send_data oid=%c data=%*s", cq=cmd_queue)
|
||||
def send(self, cmds, is_data=False, is_extended=False):
|
||||
cmd_type = self.send_cmds_cmd
|
||||
if is_data:
|
||||
cmd_type = self.send_data_cmd
|
||||
elif self.is_extended != is_extended:
|
||||
add_cmd = 0x22
|
||||
if is_extended:
|
||||
add_cmd = 0x26
|
||||
cmds = [add_cmd] + cmds
|
||||
self.is_extended = is_extended
|
||||
cmd_type.send([self.oid, cmds], reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
#logging.debug("st7920 %d %s", is_data, repr(cmds))
|
||||
|
||||
# Helper code for toggling the en pin on startup
|
||||
class EnableHelper:
|
||||
def __init__(self, pin_desc, spi):
|
||||
self.en_pin = bus.MCU_bus_digital_out(spi.get_mcu(), pin_desc,
|
||||
spi.get_command_queue())
|
||||
def init(self):
|
||||
mcu = self.en_pin.get_mcu()
|
||||
curtime = mcu.get_printer().get_reactor().monotonic()
|
||||
print_time = mcu.estimated_print_time(curtime)
|
||||
# Toggle enable pin
|
||||
minclock = mcu.print_time_to_clock(print_time + .100)
|
||||
self.en_pin.update_digital_out(0, minclock=minclock)
|
||||
minclock = mcu.print_time_to_clock(print_time + .200)
|
||||
self.en_pin.update_digital_out(1, minclock=minclock)
|
||||
# Force a delay to any subsequent commands on the command queue
|
||||
minclock = mcu.print_time_to_clock(print_time + .300)
|
||||
self.en_pin.update_digital_out(1, minclock=minclock)
|
||||
|
||||
# Display driver for displays that emulate the ST7920 in software.
|
||||
# These displays rely on the CS pin to be toggled in order to initialize the
|
||||
# SPI correctly. This display driver uses a software SPI with an unused pin
|
||||
# as the MISO pin.
|
||||
class EmulatedST7920(DisplayBase):
|
||||
def __init__(self, config):
|
||||
# create software spi
|
||||
ppins = config.get_printer().lookup_object('pins')
|
||||
sw_pin_names = ['spi_software_%s_pin' % (name,)
|
||||
for name in ['miso', 'mosi', 'sclk']]
|
||||
sw_pin_params = [ppins.lookup_pin(config.get(name), share_type=name)
|
||||
for name in sw_pin_names]
|
||||
mcu = None
|
||||
for pin_params in sw_pin_params:
|
||||
if mcu is not None and pin_params['chip'] != mcu:
|
||||
raise ppins.error("%s: spi pins must be on same mcu" % (
|
||||
config.get_name(),))
|
||||
mcu = pin_params['chip']
|
||||
sw_pins = tuple([pin_params['pin'] for pin_params in sw_pin_params])
|
||||
speed = config.getint('spi_speed', 1000000, minval=100000)
|
||||
self.spi = bus.MCU_SPI(mcu, None, None, 0, speed, sw_pins)
|
||||
# create enable helper
|
||||
self.en_helper = EnableHelper(config.get("en_pin"), self.spi)
|
||||
self.en_set = False
|
||||
# init display base
|
||||
self.is_extended = False
|
||||
DisplayBase.__init__(self)
|
||||
def send(self, cmds, is_data=False, is_extended=False):
|
||||
# setup sync byte and check for exten mode switch
|
||||
sync_byte = 0xfa
|
||||
if not is_data:
|
||||
sync_byte = 0xf8
|
||||
if self.is_extended != is_extended:
|
||||
add_cmd = 0x22
|
||||
if is_extended:
|
||||
add_cmd = 0x26
|
||||
cmds = [add_cmd] + cmds
|
||||
self.is_extended = is_extended
|
||||
# copy data to ST7920 data format
|
||||
spi_data = [0] * (2 * len(cmds) + 1)
|
||||
spi_data[0] = sync_byte
|
||||
i = 1
|
||||
for b in cmds:
|
||||
spi_data[i] = b & 0xF0
|
||||
spi_data[i + 1] = (b & 0x0F) << 4
|
||||
i = i + 2
|
||||
# check if enable pin has been set
|
||||
if not self.en_set:
|
||||
self.en_helper.init()
|
||||
self.en_set = True
|
||||
# send data
|
||||
self.spi.spi_send(spi_data, reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
#logging.debug("st7920 %s", repr(spi_data))
|
||||
BIN
klippy/extras/display/st7920.pyc
Normal file
BIN
klippy/extras/display/st7920.pyc
Normal file
Binary file not shown.
240
klippy/extras/display/uc1701.py
Normal file
240
klippy/extras/display/uc1701.py
Normal file
@@ -0,0 +1,240 @@
|
||||
# Support for UC1701 (and similar) 128x64 graphics LCD displays
|
||||
#
|
||||
# Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018 Eric Callahan <arksine.code@gmail.com>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
from .. import bus
|
||||
from . import font8x14
|
||||
|
||||
BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000
|
||||
|
||||
TextGlyphs = { 'right_arrow': b'\x1a', 'degrees': b'\xf8' }
|
||||
|
||||
class DisplayBase:
|
||||
def __init__(self, io, columns=128, x_offset=0):
|
||||
self.send = io.send
|
||||
# framebuffers
|
||||
self.columns = columns
|
||||
self.x_offset = x_offset
|
||||
self.vram = [bytearray(self.columns) for i in range(8)]
|
||||
self.all_framebuffers = [(self.vram[i], bytearray(b'~'*self.columns), i)
|
||||
for i in range(8)]
|
||||
# Cache fonts and icons in display byte order
|
||||
self.font = [self._swizzle_bits(bytearray(c))
|
||||
for c in font8x14.VGA_FONT]
|
||||
self.icons = {}
|
||||
def flush(self):
|
||||
# Find all differences in the framebuffers and send them to the chip
|
||||
for new_data, old_data, page in self.all_framebuffers:
|
||||
if new_data == old_data:
|
||||
continue
|
||||
# Find the position of all changed bytes in this framebuffer
|
||||
diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data))
|
||||
if n != o]
|
||||
# Batch together changes that are close to each other
|
||||
for i in range(len(diffs)-2, -1, -1):
|
||||
pos, count = diffs[i]
|
||||
nextpos, nextcount = diffs[i+1]
|
||||
if pos + 5 >= nextpos and nextcount < 16:
|
||||
diffs[i][1] = nextcount + (nextpos - pos)
|
||||
del diffs[i+1]
|
||||
# Transmit changes
|
||||
for col_pos, count in diffs:
|
||||
# Set Position registers
|
||||
ra = 0xb0 | (page & 0x0F)
|
||||
ca_msb = 0x10 | ((col_pos >> 4) & 0x0F)
|
||||
ca_lsb = col_pos & 0x0F
|
||||
self.send([ra, ca_msb, ca_lsb])
|
||||
# Send Data
|
||||
self.send(new_data[col_pos:col_pos+count], is_data=True)
|
||||
old_data[:] = new_data
|
||||
def _swizzle_bits(self, data):
|
||||
# Convert from "rows of pixels" format to "columns of pixels"
|
||||
top = bot = 0
|
||||
for row in range(8):
|
||||
spaced = (data[row] * 0x8040201008040201) & 0x8080808080808080
|
||||
top |= spaced >> (7 - row)
|
||||
spaced = (data[row + 8] * 0x8040201008040201) & 0x8080808080808080
|
||||
bot |= spaced >> (7 - row)
|
||||
bits_top = [(top >> s) & 0xff for s in range(0, 64, 8)]
|
||||
bits_bot = [(bot >> s) & 0xff for s in range(0, 64, 8)]
|
||||
return (bytearray(bits_top), bytearray(bits_bot))
|
||||
def set_glyphs(self, glyphs):
|
||||
for glyph_name, glyph_data in glyphs.items():
|
||||
icon = glyph_data.get('icon16x16')
|
||||
if icon is not None:
|
||||
top1, bot1 = self._swizzle_bits(icon[0])
|
||||
top2, bot2 = self._swizzle_bits(icon[1])
|
||||
self.icons[glyph_name] = (top1 + top2, bot1 + bot2)
|
||||
def write_text(self, x, y, data):
|
||||
if x + len(data) > 16:
|
||||
data = data[:16 - min(x, 16)]
|
||||
pix_x = x * 8
|
||||
pix_x += self.x_offset
|
||||
page_top = self.vram[y * 2]
|
||||
page_bot = self.vram[y * 2 + 1]
|
||||
for c in bytearray(data):
|
||||
bits_top, bits_bot = self.font[c]
|
||||
page_top[pix_x:pix_x+8] = bits_top
|
||||
page_bot[pix_x:pix_x+8] = bits_bot
|
||||
pix_x += 8
|
||||
def write_graphics(self, x, y, data):
|
||||
if x >= 16 or y >= 4 or len(data) != 16:
|
||||
return
|
||||
bits_top, bits_bot = self._swizzle_bits(data)
|
||||
pix_x = x * 8
|
||||
pix_x += self.x_offset
|
||||
page_top = self.vram[y * 2]
|
||||
page_bot = self.vram[y * 2 + 1]
|
||||
for i in range(8):
|
||||
page_top[pix_x + i] ^= bits_top[i]
|
||||
page_bot[pix_x + i] ^= bits_bot[i]
|
||||
def write_glyph(self, x, y, glyph_name):
|
||||
icon = self.icons.get(glyph_name)
|
||||
if icon is not None and x < 15:
|
||||
# Draw icon in graphics mode
|
||||
pix_x = x * 8
|
||||
pix_x += self.x_offset
|
||||
page_idx = y * 2
|
||||
self.vram[page_idx][pix_x:pix_x+16] = icon[0]
|
||||
self.vram[page_idx + 1][pix_x:pix_x+16] = icon[1]
|
||||
return 2
|
||||
char = TextGlyphs.get(glyph_name)
|
||||
if char is not None:
|
||||
# Draw character
|
||||
self.write_text(x, y, char)
|
||||
return 1
|
||||
return 0
|
||||
def clear(self):
|
||||
zeros = bytearray(self.columns)
|
||||
for page in self.vram:
|
||||
page[:] = zeros
|
||||
def get_dimensions(self):
|
||||
return (16, 4)
|
||||
|
||||
# IO wrapper for "4 wire" spi bus (spi bus with an extra data/control line)
|
||||
class SPI4wire:
|
||||
def __init__(self, config, data_pin_name):
|
||||
self.spi = bus.MCU_SPI_from_config(config, 0, default_speed=10000000)
|
||||
dc_pin = config.get(data_pin_name)
|
||||
self.mcu_dc = bus.MCU_bus_digital_out(self.spi.get_mcu(), dc_pin,
|
||||
self.spi.get_command_queue())
|
||||
def send(self, cmds, is_data=False):
|
||||
self.mcu_dc.update_digital_out(is_data,
|
||||
reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
self.spi.spi_send(cmds, reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
|
||||
# IO wrapper for i2c bus
|
||||
class I2C:
|
||||
def __init__(self, config, default_addr):
|
||||
self.i2c = bus.MCU_I2C_from_config(config, default_addr=default_addr,
|
||||
default_speed=400000)
|
||||
def send(self, cmds, is_data=False):
|
||||
if is_data:
|
||||
hdr = 0x40
|
||||
else:
|
||||
hdr = 0x00
|
||||
cmds = bytearray(cmds)
|
||||
cmds.insert(0, hdr)
|
||||
self.i2c.i2c_write(cmds, reqclock=BACKGROUND_PRIORITY_CLOCK)
|
||||
|
||||
# Helper code for toggling a reset pin on startup
|
||||
class ResetHelper:
|
||||
def __init__(self, pin_desc, io_bus):
|
||||
self.mcu_reset = None
|
||||
if pin_desc is None:
|
||||
return
|
||||
self.mcu_reset = bus.MCU_bus_digital_out(io_bus.get_mcu(), pin_desc,
|
||||
io_bus.get_command_queue())
|
||||
def init(self):
|
||||
if self.mcu_reset is None:
|
||||
return
|
||||
mcu = self.mcu_reset.get_mcu()
|
||||
curtime = mcu.get_printer().get_reactor().monotonic()
|
||||
print_time = mcu.estimated_print_time(curtime)
|
||||
# Toggle reset
|
||||
minclock = mcu.print_time_to_clock(print_time + .100)
|
||||
self.mcu_reset.update_digital_out(0, minclock=minclock)
|
||||
minclock = mcu.print_time_to_clock(print_time + .200)
|
||||
self.mcu_reset.update_digital_out(1, minclock=minclock)
|
||||
# Force a delay to any subsequent commands on the command queue
|
||||
minclock = mcu.print_time_to_clock(print_time + .300)
|
||||
self.mcu_reset.update_digital_out(1, minclock=minclock)
|
||||
|
||||
# The UC1701 is a "4-wire" SPI display device
|
||||
class UC1701(DisplayBase):
|
||||
def __init__(self, config):
|
||||
io = SPI4wire(config, "a0_pin")
|
||||
DisplayBase.__init__(self, io)
|
||||
self.contrast = config.getint('contrast', 40, minval=0, maxval=63)
|
||||
self.reset = ResetHelper(config.get("rst_pin", None), io.spi)
|
||||
def init(self):
|
||||
self.reset.init()
|
||||
init_cmds = [0xE2, # System reset
|
||||
0x40, # Set display to start at line 0
|
||||
0xA0, # Set SEG direction
|
||||
0xC8, # Set COM Direction
|
||||
0xA2, # Set Bias = 1/9
|
||||
0x2C, # Boost ON
|
||||
0x2E, # Voltage regulator on
|
||||
0x2F, # Voltage follower on
|
||||
0xF8, # Set booster ratio
|
||||
0x00, # Booster ratio value (4x)
|
||||
0x23, # Set resistor ratio (3)
|
||||
0x81, # Set Electronic Volume
|
||||
self.contrast, # Electronic Volume value
|
||||
0xAC, # Set static indicator off
|
||||
0x00, # NOP
|
||||
0xA6, # Disable Inverse
|
||||
0xAF] # Set display enable
|
||||
self.send(init_cmds)
|
||||
self.send([0xA5]) # display all
|
||||
self.send([0xA4]) # normal display
|
||||
self.flush()
|
||||
|
||||
# The SSD1306 supports both i2c and "4-wire" spi
|
||||
class SSD1306(DisplayBase):
|
||||
def __init__(self, config, columns=128, x_offset=0):
|
||||
cs_pin = config.get("cs_pin", None)
|
||||
if cs_pin is None:
|
||||
io = I2C(config, 60)
|
||||
io_bus = io.i2c
|
||||
else:
|
||||
io = SPI4wire(config, "dc_pin")
|
||||
io_bus = io.spi
|
||||
self.reset = ResetHelper(config.get("reset_pin", None), io_bus)
|
||||
DisplayBase.__init__(self, io, columns, x_offset)
|
||||
self.contrast = config.getint('contrast', 239, minval=0, maxval=255)
|
||||
self.vcomh = config.getint('vcomh', 0, minval=0, maxval=63)
|
||||
self.invert = config.getboolean('invert', False)
|
||||
def init(self):
|
||||
self.reset.init()
|
||||
init_cmds = [
|
||||
0xAE, # Display off
|
||||
0xD5, 0x80, # Set oscillator frequency
|
||||
0xA8, 0x3f, # Set multiplex ratio
|
||||
0xD3, 0x00, # Set display offset
|
||||
0x40, # Set display start line
|
||||
0x8D, 0x14, # Charge pump setting
|
||||
0x20, 0x02, # Set Memory addressing mode
|
||||
0xA1, # Set Segment re-map
|
||||
0xC8, # Set COM output scan direction
|
||||
0xDA, 0x12, # Set COM pins hardware configuration
|
||||
0x81, self.contrast, # Set contrast control
|
||||
0xD9, 0xA1, # Set pre-charge period
|
||||
0xDB, self.vcomh, # Set VCOMH deselect level
|
||||
0x2E, # Deactivate scroll
|
||||
0xA4, # Output ram to display
|
||||
0xA7 if self.invert else 0xA6, # Set normal/invert
|
||||
0xAF, # Display on
|
||||
]
|
||||
self.send(init_cmds)
|
||||
self.flush()
|
||||
|
||||
# the SH1106 is SSD1306 compatible with up to 132 columns
|
||||
class SH1106(SSD1306):
|
||||
def __init__(self, config):
|
||||
x_offset = config.getint('x_offset', 0, minval=0, maxval=3)
|
||||
SSD1306.__init__(self, config, 132, x_offset=x_offset)
|
||||
BIN
klippy/extras/display/uc1701.pyc
Normal file
BIN
klippy/extras/display/uc1701.pyc
Normal file
Binary file not shown.
Reference in New Issue
Block a user