mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 10:12:52 +00:00
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
081d8d492a | ||
|
|
e18c1bb39b | ||
|
|
70ee367c28 | ||
|
|
5c9c233568 | ||
|
|
c58a26dce2 |
@@ -40,6 +40,7 @@ from esphome.cpp_generator import ( # noqa: F401
|
||||
progmem_array,
|
||||
safe_exp,
|
||||
set_cpp_standard,
|
||||
shared_progmem_array,
|
||||
statement,
|
||||
static_const_array,
|
||||
static_function,
|
||||
|
||||
@@ -234,7 +234,10 @@ async def to_code(config: ConfigType) -> None:
|
||||
sequence = []
|
||||
for seq in init_sequences:
|
||||
sequence.extend(seq)
|
||||
cg.add(var.add_init_sequence(sequence))
|
||||
# init_lcd_() stops at a 0 command
|
||||
sequence.append(0)
|
||||
table = cg.shared_progmem_array("ili9xxx_init_sequence", cg.uint8, sequence)
|
||||
cg.add(var.add_init_sequence(table))
|
||||
|
||||
if pixel_mode := config.get(CONF_PIXEL_MODE):
|
||||
cg.add(var.set_pixel_mode(pixel_mode))
|
||||
|
||||
@@ -32,7 +32,7 @@ void ILI9XXXDisplay::set_madctl() {
|
||||
void ILI9XXXDisplay::setup() {
|
||||
this->setup_pins_();
|
||||
this->init_lcd_(this->init_sequence_);
|
||||
this->init_lcd_(this->extra_init_sequence_.data());
|
||||
this->init_lcd_(this->extra_init_sequence_);
|
||||
switch (this->pixel_mode_) {
|
||||
case PIXEL_MODE_16:
|
||||
if (this->is_18bitdisplay_) {
|
||||
|
||||
@@ -58,7 +58,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
||||
}
|
||||
}
|
||||
|
||||
void add_init_sequence(const std::vector<uint8_t> &sequence) { this->extra_init_sequence_ = sequence; }
|
||||
// `sequence` is a PROGMEM table that must end with a 0 command.
|
||||
void add_init_sequence(const uint8_t *sequence) { this->extra_init_sequence_ = sequence; }
|
||||
void set_dc_pin(GPIOPin *dc_pin) { dc_pin_ = dc_pin; }
|
||||
float get_setup_priority() const override;
|
||||
void set_reset_pin(GPIOPin *reset) { this->reset_pin_ = reset; }
|
||||
@@ -114,7 +115,8 @@ class ILI9XXXDisplay : public display::DisplayBuffer,
|
||||
void reset_();
|
||||
|
||||
uint8_t const *init_sequence_{};
|
||||
std::vector<uint8_t> extra_init_sequence_;
|
||||
// Shared PROGMEM table ending in a 0 command, like the built-in sequences
|
||||
const uint8_t *extra_init_sequence_{nullptr};
|
||||
int16_t width_{0}; ///< Display width as modified by current rotation
|
||||
int16_t height_{0}; ///< Display height as modified by current rotation
|
||||
int16_t offset_x_{0};
|
||||
|
||||
@@ -19,7 +19,12 @@ from esphome.core import (
|
||||
TimePeriodNanoseconds,
|
||||
TimePeriodSeconds,
|
||||
)
|
||||
from esphome.helpers import cpp_string_escape, indent, indent_all_but_first_and_last
|
||||
from esphome.helpers import (
|
||||
cpp_string_escape,
|
||||
ensure_unique_string,
|
||||
indent,
|
||||
indent_all_but_first_and_last,
|
||||
)
|
||||
from esphome.types import Expression, SafeExpType, TemplateArgsType
|
||||
from esphome.util import OrderedDict
|
||||
from esphome.yaml_util import ESPHomeDataBase
|
||||
@@ -463,6 +468,36 @@ def progmem_array(id_, rhs) -> "MockObj":
|
||||
return obj
|
||||
|
||||
|
||||
def shared_progmem_array(
|
||||
name: str, type_: "MockObjClass", rhs: SafeExpType, *, share: bool = True
|
||||
) -> "MockObj":
|
||||
"""Emit a global PROGMEM array once per distinct type and contents; later calls reuse it.
|
||||
|
||||
The array is ``static constexpr``, so elements must be constant expressions and lambdas
|
||||
must be captureless. Its name is made unique against every config id and variable.
|
||||
``share=False`` always emits a new array, e.g. for lambdas that may keep static state.
|
||||
"""
|
||||
from esphome.config import iter_ids
|
||||
from esphome.config_validation import RESERVED_IDS
|
||||
|
||||
arrays: dict[str, MockObj] = CORE.data.setdefault("shared_progmem_array", {})
|
||||
rhs = safe_exp(rhs)
|
||||
key = f"{type_} {rhs}"
|
||||
if share and (array := arrays.get(key)) is not None:
|
||||
return array
|
||||
used = {str(i) for i, _ in iter_ids(CORE.config)}
|
||||
used |= {str(i) for i in CORE.variables}
|
||||
used |= set(RESERVED_IDS) | CORE.loaded_integrations
|
||||
id_ = ID(ensure_unique_string(name, used), is_declaration=True, type=type_)
|
||||
# Global, so any scope can use it; anything a lambda references is already declared.
|
||||
CORE.add_global(ProgmemAssignmentExpression(type_, id_, rhs))
|
||||
array = MockObj(id_, ".")
|
||||
CORE.register_variable(id_, array)
|
||||
if share:
|
||||
arrays[key] = array
|
||||
return array
|
||||
|
||||
|
||||
def static_const_array(id_, rhs) -> "MockObj":
|
||||
rhs = safe_exp(rhs)
|
||||
obj = MockObj(id_, ".")
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
esphome:
|
||||
name: test
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: arduino
|
||||
|
||||
spi:
|
||||
clk_pin: GPIO18
|
||||
mosi_pin: GPIO23
|
||||
|
||||
display:
|
||||
- platform: ili9xxx
|
||||
id: first
|
||||
model: ST7789V
|
||||
cs_pin: GPIO5
|
||||
dc_pin: GPIO17
|
||||
invert_colors: false
|
||||
init_sequence:
|
||||
- [0x36, 0x48]
|
||||
- [0x3A, 0x55]
|
||||
- platform: ili9xxx
|
||||
id: second
|
||||
model: ST7789V
|
||||
cs_pin: GPIO4
|
||||
dc_pin: GPIO16
|
||||
invert_colors: false
|
||||
init_sequence:
|
||||
- [0x36, 0x48]
|
||||
- [0x3A, 0x55]
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from collections.abc import Callable
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
|
||||
def test_ili9xxx_placement_new_uses_model_subclass(
|
||||
@@ -29,3 +30,19 @@ def test_ili9xxx_placement_new_uses_model_subclass(
|
||||
assert "new(tft_display) ili9xxx::ILI9XXXST7789V()" in main_cpp
|
||||
# Base-class default constructor must NOT be used.
|
||||
assert "new(tft_display) ili9xxx::ILI9XXXDisplay()" not in main_cpp
|
||||
|
||||
|
||||
def test_extra_init_sequence_is_terminated_progmem_table(
|
||||
generate_main: Callable[[str | Path], str],
|
||||
component_config_path: Callable[[str], Path],
|
||||
) -> None:
|
||||
"""A custom init sequence is a 0 terminated PROGMEM table shared by equal displays."""
|
||||
main_cpp = generate_main(component_config_path("init_sequence.yaml"))
|
||||
|
||||
tables = re.findall(r"(\w+)->add_init_sequence\((\w+)\);", main_cpp)
|
||||
assert {t[0] for t in tables} == {"first", "second"}
|
||||
assert len({t[1] for t in tables}) == 1
|
||||
assert (
|
||||
f"static constexpr uint8_t {tables[0][1]}[] PROGMEM = {{54, 1, 72, 58, 1, 85, 0}};"
|
||||
in main_cpp
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import math
|
||||
import pytest
|
||||
|
||||
from esphome import cpp_generator as cg, cpp_types as ct
|
||||
from esphome.core import CORE, ID
|
||||
|
||||
|
||||
class TestExpressions:
|
||||
@@ -808,3 +809,39 @@ async def test_templatable__lambda_with_std_string() -> None:
|
||||
result = await cg.templatable(lambda_obj, [], ct.std_string)
|
||||
|
||||
assert isinstance(result, cg.LambdaExpression)
|
||||
|
||||
|
||||
class TestSharedProgmemArray:
|
||||
def test_identical_contents_share_one_array(self) -> None:
|
||||
CORE.config = {}
|
||||
a = cg.shared_progmem_array("table", ct.uint8, [1, 2, 3])
|
||||
b = cg.shared_progmem_array("table", ct.uint8, [1, 2, 3])
|
||||
c = cg.shared_progmem_array("table", ct.uint8, [4])
|
||||
assert a is b
|
||||
assert str(a) != str(c)
|
||||
assert sum("PROGMEM" in str(st) for st in CORE.global_statements) == 2
|
||||
assert not any("PROGMEM" in str(st) for st in CORE.main_statements)
|
||||
|
||||
def test_share_false_always_emits_a_new_array(self) -> None:
|
||||
CORE.config = {}
|
||||
a = cg.shared_progmem_array("table", ct.uint8, [1], share=False)
|
||||
b = cg.shared_progmem_array("table", ct.uint8, [1], share=False)
|
||||
assert str(a) != str(b)
|
||||
assert sum("PROGMEM" in str(st) for st in CORE.global_statements) == 2
|
||||
|
||||
def test_same_contents_different_type_are_separate(self) -> None:
|
||||
CORE.config = {}
|
||||
a = cg.shared_progmem_array("table", ct.uint8, [1])
|
||||
b = cg.shared_progmem_array("table", ct.uint16, [1])
|
||||
assert str(a) != str(b)
|
||||
|
||||
def test_name_avoids_config_ids(self) -> None:
|
||||
CORE.config = {"sensor": [{"id": ID("table", is_declaration=True)}]}
|
||||
array = cg.shared_progmem_array("table", ct.uint8, [1])
|
||||
assert str(array) == "table_2"
|
||||
|
||||
def test_name_avoids_registered_variables(self) -> None:
|
||||
CORE.config = {}
|
||||
CORE.register_variable(ID("table", is_declaration=True), cg.MockObj("table"))
|
||||
array = cg.shared_progmem_array("table", ct.uint8, [1])
|
||||
assert str(array) == "table_2"
|
||||
|
||||
Reference in New Issue
Block a user