mirror of
https://github.com/esphome/esphome.git
synced 2024-11-22 11:47:30 +01:00
Add st7920 display, (#1440)
Co-authored-by: Oxan van Leeuwen <oxan@oxanvanleeuwen.nl> Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
parent
de871862a8
commit
94b28102f5
@ -129,6 +129,7 @@ esphome/components/ssd1351_base/* @kbx81
|
||||
esphome/components/ssd1351_spi/* @kbx81
|
||||
esphome/components/st7735/* @SenexCrenshaw
|
||||
esphome/components/st7789v/* @kbx81
|
||||
esphome/components/st7920/* @marsjan155
|
||||
esphome/components/substitutions/* @esphome/core
|
||||
esphome/components/sun/* @OttoWinter
|
||||
esphome/components/switch/* @esphome/core
|
||||
|
0
esphome/components/st7920/__init__.py
Normal file
0
esphome/components/st7920/__init__.py
Normal file
42
esphome/components/st7920/display.py
Normal file
42
esphome/components/st7920/display.py
Normal file
@ -0,0 +1,42 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import display, spi
|
||||
from esphome.const import CONF_ID, CONF_LAMBDA, CONF_WIDTH, CONF_HEIGHT
|
||||
|
||||
AUTO_LOAD = ["display"]
|
||||
CODEOWNERS = ["@marsjan155"]
|
||||
DEPENDENCIES = ["spi"]
|
||||
|
||||
st7920_ns = cg.esphome_ns.namespace("st7920")
|
||||
ST7920 = st7920_ns.class_(
|
||||
"ST7920", cg.PollingComponent, display.DisplayBuffer, spi.SPIDevice
|
||||
)
|
||||
ST7920Ref = ST7920.operator("ref")
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
display.FULL_DISPLAY_SCHEMA.extend(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(ST7920),
|
||||
cv.Required(CONF_WIDTH): cv.int_,
|
||||
cv.Required(CONF_HEIGHT): cv.int_,
|
||||
}
|
||||
)
|
||||
.extend(cv.polling_component_schema("60s"))
|
||||
.extend(spi.spi_device_schema())
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config):
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await spi.register_spi_device(var, config)
|
||||
|
||||
if CONF_LAMBDA in config:
|
||||
lambda_ = await cg.process_lambda(
|
||||
config[CONF_LAMBDA], [(ST7920Ref, "it")], return_type=cg.void
|
||||
)
|
||||
cg.add(var.set_writer(lambda_))
|
||||
cg.add(var.set_width(config[CONF_WIDTH]))
|
||||
cg.add(var.set_height(config[CONF_HEIGHT]))
|
||||
|
||||
await display.register_display(var, config)
|
146
esphome/components/st7920/st7920.cpp
Normal file
146
esphome/components/st7920/st7920.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "st7920.h"
|
||||
#include "esphome/core/log.h"
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace st7920 {
|
||||
|
||||
static const char *const TAG = "st7920";
|
||||
|
||||
// ST7920 COMMANDS
|
||||
static const uint8_t LCD_DATA = 0xFA;
|
||||
static const uint8_t LCD_COMMAND = 0xF8;
|
||||
static const uint8_t LCD_CLS = 0x01;
|
||||
static const uint8_t LCD_HOME = 0x02;
|
||||
static const uint8_t LCD_ADDRINC = 0x06;
|
||||
static const uint8_t LCD_DISPLAYON = 0x0C;
|
||||
static const uint8_t LCD_DISPLAYOFF = 0x08;
|
||||
static const uint8_t LCD_CURSORON = 0x0E;
|
||||
static const uint8_t LCD_CURSORBLINK = 0x0F;
|
||||
static const uint8_t LCD_BASIC = 0x30;
|
||||
static const uint8_t LCD_GFXMODE = 0x36;
|
||||
static const uint8_t LCD_EXTEND = 0x34;
|
||||
static const uint8_t LCD_TXTMODE = 0x34;
|
||||
static const uint8_t LCD_STANDBY = 0x01;
|
||||
static const uint8_t LCD_SCROLL = 0x03;
|
||||
static const uint8_t LCD_SCROLLADDR = 0x40;
|
||||
static const uint8_t LCD_ADDR = 0x80;
|
||||
static const uint8_t LCD_LINE0 = 0x80;
|
||||
static const uint8_t LCD_LINE1 = 0x90;
|
||||
static const uint8_t LCD_LINE2 = 0x88;
|
||||
static const uint8_t LCD_LINE3 = 0x98;
|
||||
|
||||
void ST7920::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up ST7920...");
|
||||
this->dump_config();
|
||||
this->spi_setup();
|
||||
this->init_internal_(this->get_buffer_length_());
|
||||
display_init_();
|
||||
}
|
||||
|
||||
void ST7920::command_(uint8_t value) {
|
||||
this->enable();
|
||||
this->send_(LCD_COMMAND, value);
|
||||
this->disable();
|
||||
}
|
||||
|
||||
void ST7920::data_(uint8_t value) {
|
||||
this->enable();
|
||||
this->send_(LCD_DATA, value);
|
||||
this->disable();
|
||||
}
|
||||
|
||||
void ST7920::send_(uint8_t type, uint8_t value) {
|
||||
this->write_byte(type);
|
||||
this->write_byte(value & 0xF0);
|
||||
this->write_byte(value << 4);
|
||||
}
|
||||
|
||||
void ST7920::goto_xy_(uint16_t x, uint16_t y) {
|
||||
if (y >= 32 && y < 64) {
|
||||
y -= 32;
|
||||
x += 8;
|
||||
} else if (y >= 64 && y < 64 + 32) {
|
||||
y -= 32;
|
||||
x += 0;
|
||||
} else if (y >= 64 + 32 && y < 64 + 64) {
|
||||
y -= 64;
|
||||
x += 8;
|
||||
}
|
||||
this->command_(LCD_ADDR | y); // 6-bit (0..63)
|
||||
this->command_(LCD_ADDR | x); // 4-bit (0..15)
|
||||
}
|
||||
|
||||
void HOT ST7920::write_display_data() {
|
||||
uint8_t i, j, b;
|
||||
for (j = 0; j < this->get_height_internal() / 2; j++) {
|
||||
this->goto_xy_(0, j);
|
||||
this->enable();
|
||||
for (i = 0; i < 16; i++) { // 16 bytes from line #0+
|
||||
b = this->buffer_[i + j * 16];
|
||||
this->send_(LCD_DATA, b);
|
||||
}
|
||||
for (i = 0; i < 16; i++) { // 16 bytes from line #32+
|
||||
b = this->buffer_[i + (j + 32) * 16];
|
||||
this->send_(LCD_DATA, b);
|
||||
}
|
||||
this->disable();
|
||||
App.feed_wdt();
|
||||
}
|
||||
}
|
||||
|
||||
void ST7920::fill(Color color) { memset(this->buffer_, color.is_on() ? 0xFF : 0x00, this->get_buffer_length_()); }
|
||||
|
||||
void ST7920::dump_config() {
|
||||
LOG_DISPLAY("", "ST7920", this);
|
||||
LOG_PIN(" CS Pin: ", this->cs_);
|
||||
ESP_LOGCONFIG(TAG, " Height: %d", this->height_);
|
||||
ESP_LOGCONFIG(TAG, " Width: %d", this->width_);
|
||||
}
|
||||
|
||||
float ST7920::get_setup_priority() const { return setup_priority::PROCESSOR; }
|
||||
|
||||
void ST7920::update() {
|
||||
this->clear();
|
||||
if (this->writer_local_.has_value()) // call lambda function if available
|
||||
(*this->writer_local_)(*this);
|
||||
this->write_display_data();
|
||||
}
|
||||
|
||||
int ST7920::get_width_internal() { return this->width_; }
|
||||
|
||||
int ST7920::get_height_internal() { return this->height_; }
|
||||
|
||||
size_t ST7920::get_buffer_length_() {
|
||||
return size_t(this->get_width_internal()) * size_t(this->get_height_internal()) / 8u;
|
||||
}
|
||||
|
||||
void HOT ST7920::draw_absolute_pixel_internal(int x, int y, Color color) {
|
||||
if (x >= this->get_width_internal() || x < 0 || y >= this->get_height_internal() || y < 0) {
|
||||
ESP_LOGW(TAG, "Position out of area: %dx%d", x, y);
|
||||
return;
|
||||
}
|
||||
int width = this->get_width_internal() / 8u;
|
||||
if (color.is_on()) {
|
||||
this->buffer_[y * width + x / 8] |= (0x80 >> (x & 7));
|
||||
} else {
|
||||
this->buffer_[y * width + x / 8] &= ~(0x80 >> (x & 7));
|
||||
}
|
||||
}
|
||||
|
||||
void ST7920::display_init_() {
|
||||
ESP_LOGD(TAG, "Initializing display...");
|
||||
this->command_(LCD_BASIC); // 8bit mode
|
||||
this->command_(LCD_BASIC); // 8bit mode
|
||||
this->command_(LCD_CLS); // clear screen
|
||||
delay(12); // >10 ms delay
|
||||
this->command_(LCD_ADDRINC); // cursor increment right no shift
|
||||
this->command_(LCD_DISPLAYON); // D=1, C=0, B=0
|
||||
this->command_(LCD_EXTEND); // LCD_EXTEND);
|
||||
this->command_(LCD_GFXMODE); // LCD_GFXMODE);
|
||||
this->write_display_data();
|
||||
}
|
||||
|
||||
} // namespace st7920
|
||||
} // namespace esphome
|
50
esphome/components/st7920/st7920.h
Normal file
50
esphome/components/st7920/st7920.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/components/display/display_buffer.h"
|
||||
#include "esphome/components/spi/spi.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace st7920 {
|
||||
|
||||
class ST7920;
|
||||
|
||||
using st7920_writer_t = std::function<void(ST7920 &)>;
|
||||
|
||||
class ST7920 : public PollingComponent,
|
||||
public display::DisplayBuffer,
|
||||
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, spi::CLOCK_PHASE_TRAILING,
|
||||
spi::DATA_RATE_1MHZ> {
|
||||
public:
|
||||
void set_writer(st7920_writer_t &&writer) { this->writer_local_ = writer; }
|
||||
void set_height(uint16_t height) { this->height_ = height; }
|
||||
void set_width(uint16_t width) { this->width_ = width; }
|
||||
|
||||
// ========== INTERNAL METHODS ==========
|
||||
// (In most use cases you won't need these)
|
||||
void setup() override;
|
||||
void dump_config() override;
|
||||
float get_setup_priority() const override;
|
||||
void update() override;
|
||||
void fill(Color color) override;
|
||||
void write_display_data();
|
||||
|
||||
protected:
|
||||
void draw_absolute_pixel_internal(int x, int y, Color color) override;
|
||||
int get_height_internal() override;
|
||||
int get_width_internal() override;
|
||||
size_t get_buffer_length_();
|
||||
void display_init_();
|
||||
void command_(uint8_t value);
|
||||
void data_(uint8_t value);
|
||||
void send_(uint8_t type, uint8_t value);
|
||||
void goto_xy_(uint16_t x, uint16_t y);
|
||||
void start_transaction_();
|
||||
void end_transaction_();
|
||||
|
||||
int16_t width_ = 128, height_ = 64;
|
||||
optional<st7920_writer_t> writer_local_{};
|
||||
};
|
||||
|
||||
} // namespace st7920
|
||||
} // namespace esphome
|
@ -2038,6 +2038,14 @@ display:
|
||||
backlight_pin: GPIO4
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: st7920
|
||||
width: 128
|
||||
height: 64
|
||||
cs_pin:
|
||||
number: GPIO23
|
||||
inverted: true
|
||||
lambda: |-
|
||||
it.rectangle(0, 0, it.get_width(), it.get_height());
|
||||
- platform: st7735
|
||||
model: 'INITR_BLACKTAB'
|
||||
cs_pin: GPIO5
|
||||
|
Loading…
Reference in New Issue
Block a user