pw_display: Facade backend implementations and demo
- pw_display
- pw_display_null
- pw_display_host_imgui
- pw_display_pico_ili9341
- pw_display_stm32f429i_disc1_stm32cube_ili9341
- pw_display_teensy_ili9341
- pw_touchscreen
- pw_touchscreen_null
- pw_touchscreen_teensy_xpt2046
- pw_touchscreen_teensy_stmpe610
Change-Id: I898efd067ad1b424a5789033091c2267216d8e19
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/experimental/+/70224
Commit-Queue: Anthony DiGirolamo <tonymd@google.com>
Reviewed-by: Armando Montanez <amontanez@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 153cbd4..ec4fdae 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -41,6 +41,9 @@
"//pw_color:tests_run(//targets/host:host_debug_tests)",
"//pw_draw:tests_run(//targets/host:host_debug_tests)",
"//pw_framebuffer:tests_run(//targets/host:host_debug_tests)",
+
+ # See //applications/pw_lcd_display_host_imgui/README.md for instructions.
+ "//applications/terminal_display:all(//targets/host:host_debug)",
]
}
@@ -50,14 +53,20 @@
deps = [ ":arduino_tests(//targets/arduino:arduino_debug_tests)" ]
if (pw_arduino_build_CORE_NAME == "teensy") {
- deps += []
+ deps += [
+ # See //pw_lcd_display_teensy_ili9341/README.md for instructions.
+ "//applications/terminal_display:all(//targets/arduino:arduino_debug)",
+ ]
}
}
}
group("pico") {
if (PICO_SRC_DIR != "") {
- deps = [ ":pico_tests(//targets/rp2040)" ]
+ deps = [
+ ":pico_tests(//targets/rp2040)",
+ "//applications/terminal_display:all(//targets/rp2040)",
+ ]
}
}
@@ -133,6 +142,7 @@
deps += [
":applications_tests(//targets/stm32f429i_disc1_stm32cube:stm32f429i_disc1_stm32cube_debug)",
"//applications/blinky:blinky(//targets/stm32f429i_disc1_stm32cube:stm32f429i_disc1_stm32cube_debug)",
+ "//applications/terminal_display:all(//targets/stm32f429i_disc1_stm32cube:stm32f429i_disc1_stm32cube_debug)",
]
# STMicroelectronics STM32F439ZI-Nucleo applications steps.
diff --git a/applications/terminal_display/BUILD.gn b/applications/terminal_display/BUILD.gn
new file mode 100644
index 0000000..5f058ce
--- /dev/null
+++ b/applications/terminal_display/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+import("$dir_pw_tokenizer/database.gni")
+import("$dir_pw_unit_test/test.gni")
+
+group("all") {
+ deps = [ ":terminal_demo" ]
+
+ # Build tokenizer_database for elf binaries only
+ if (host_os == "linux" || current_toolchain != "//targets/host:host_debug") {
+ deps += [ ":tokenizer_database" ]
+ }
+}
+
+pw_executable("terminal_demo") {
+ sources = [ "main.cc" ]
+ deps = [
+ "$dir_pw_color",
+ "$dir_pw_coordinates",
+ "$dir_pw_display",
+ "$dir_pw_draw",
+ "$dir_pw_framebuffer",
+ "$dir_pw_log",
+ "$dir_pw_random",
+ "$dir_pw_string",
+ "$dir_pw_touchscreen",
+ "//pw_board_led",
+ "//pw_spin_delay",
+ ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+
+ if (pw_build_EXECUTABLE_TARGET_TYPE == "arduino_executable") {
+ ldflags = [ "-Wl,--print-memory-usage" ]
+ }
+}
+
+pw_tokenizer_database("tokenizer_database") {
+ database = "tokenizer_database.csv"
+ targets = [ ":terminal_demo" ]
+}
diff --git a/applications/terminal_display/main.cc b/applications/terminal_display/main.cc
new file mode 100644
index 0000000..e1b649a
--- /dev/null
+++ b/applications/terminal_display/main.cc
@@ -0,0 +1,316 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+#include <cstdint>
+#include <forward_list>
+
+// #define PW_LOG_LEVEL PW_LOG_LEVEL_INFO
+
+#include "pw_board_led/led.h"
+#include "pw_color/color.h"
+#include "pw_color/colors_pico8.h"
+#include "pw_coordinates/vec2.h"
+#include "pw_coordinates/vec_int.h"
+#include "pw_display/display.h"
+#include "pw_draw/draw.h"
+#include "pw_draw/font6x8.h"
+#include "pw_draw/pigweed_farm.h"
+#include "pw_framebuffer/rgb565.h"
+#include "pw_log/log.h"
+#include "pw_spin_delay/delay.h"
+#include "pw_string/string_builder.h"
+#include "pw_touchscreen/touchscreen.h"
+
+using pw::color::colors_pico8_rgb565;
+
+namespace {
+
+typedef bool (*bool_function_pointer)();
+typedef pw::coordinates::Vec3Int (*vec3int_function_pointer)();
+pw::framebuffer::FramebufferRgb565 frame_buffer = FramebufferRgb565();
+
+const uint8_t banner[] = {
+ 127, 128, 128, 128, 128, 128, 129, 32, 32, 128, 127, 32, 32, 129, 128,
+ 128, 128, 127, 32, 32, 127, 128, 32, 32, 32, 32, 127, 128, 32, 127,
+ 127, 128, 128, 128, 128, 127, 32, 127, 127, 128, 128, 128, 128, 127, 32,
+ 127, 127, 128, 128, 128, 128, 129, 10, 127, 128, 127, 32, 32, 128, 127,
+ 32, 127, 128, 127, 32, 128, 128, 127, 32, 132, 128, 127, 32, 127, 128,
+ 127, 32, 128, 32, 127, 128, 32, 32, 127, 128, 32, 32, 32, 132, 32,
+ 32, 127, 128, 32, 32, 32, 132, 32, 32, 127, 128, 32, 32, 132, 128,
+ 130, 10, 127, 128, 129, 129, 129, 128, 127, 32, 127, 128, 127, 32, 128,
+ 127, 127, 32, 129, 129, 127, 32, 127, 128, 127, 32, 128, 32, 127, 128,
+ 32, 32, 127, 128, 128, 128, 32, 32, 32, 32, 127, 128, 128, 128, 32,
+ 32, 32, 32, 127, 128, 32, 32, 32, 128, 130, 10, 127, 128, 132, 32,
+ 32, 32, 32, 32, 127, 128, 127, 32, 127, 128, 32, 32, 32, 128, 127,
+ 32, 127, 128, 127, 32, 128, 32, 127, 128, 32, 32, 127, 128, 32, 32,
+ 32, 129, 32, 32, 127, 128, 32, 32, 32, 129, 32, 32, 127, 128, 32,
+ 32, 129, 128, 130, 10, 127, 128, 32, 32, 32, 32, 32, 32, 127, 128,
+ 127, 32, 127, 127, 128, 128, 128, 132, 32, 32, 32, 127, 128, 127, 132,
+ 127, 128, 127, 32, 127, 127, 128, 128, 128, 128, 127, 32, 127, 127, 128,
+ 128, 128, 128, 127, 32, 127, 127, 128, 128, 128, 128, 132, 10, 0,
+};
+
+} // namespace
+
+int main() {
+ uint32_t frame_start_millis = pw::spin_delay::Millis();
+ uint32_t frames = 0;
+ int frames_per_second = 0;
+
+ uint32_t time_start_delta = 0;
+ uint32_t delta_time = 30000; // Initial guess
+ float delta_seconds = delta_time * 0.000001;
+
+ uint32_t time_start_button_check = pw::spin_delay::Micros();
+ uint32_t delta_button_check = pw::spin_delay::Micros();
+
+ uint32_t time_start_game_logic = 0;
+ uint32_t delta_game_logic = 0;
+
+ uint32_t time_start_draw_screen = 0;
+ uint32_t delta_screen_draw = 0;
+
+ uint32_t time_start_screen_spi_update = 0;
+ uint32_t delta_screen_spi_update = 0;
+
+ pw::StringBuffer<64> string_buffer;
+
+ pw::board_led::Init();
+ pw::board_led::TurnOn();
+ pw::spin_delay::WaitMillis(500);
+ pw::board_led::TurnOff();
+
+ PW_LOG_INFO("pw::display::Init()");
+ uint16_t* ifb = pw::display::GetInternalFramebuffer();
+ if (ifb != NULL) {
+ frame_buffer.SetFramebufferData(ifb, 320, 240);
+ }
+ frame_buffer.SetPenColor(0x0726);
+ pw::draw::Fill(&frame_buffer);
+
+ pw::display::Init();
+ pw::touchscreen::Init();
+
+ // Touchscreen Functions
+ // Default to using pw::touchscreen
+ bool_function_pointer touch_screen_available_func =
+ &pw::touchscreen::Available;
+ vec3int_function_pointer get_touch_screen_point_func =
+ &pw::touchscreen::GetTouchPoint;
+ bool_function_pointer new_touch_event_func = &pw::touchscreen::NewTouchEvent;
+ // Check if pw::display implements touchscreen functions
+ if (pw::display::TouchscreenAvailable()) {
+ touch_screen_available_func = &pw::display::TouchscreenAvailable;
+ get_touch_screen_point_func = &pw::display::GetTouchPoint;
+ new_touch_event_func = &pw::display::NewTouchEvent;
+ }
+
+ bool touch_screen_exists = (*touch_screen_available_func)();
+
+ Vec2 screen_center =
+ Vec2(pw::display::GetWidth() / 2, pw::display::GetHeight() / 2);
+ Vec2 touch_location;
+ Vec2 touch_location_from_origin;
+ float touch_location_angle;
+ float touch_location_length;
+
+ while (1) {
+ time_start_delta = pw::spin_delay::Micros();
+
+ // Input Update Phase
+ bool new_touch_event = false;
+
+ time_start_button_check = pw::spin_delay::Micros();
+
+ if (touch_screen_exists && (*new_touch_event_func)()) {
+ pw::coordinates::Vec3Int point = (*get_touch_screen_point_func)();
+ if (point.z > 0) {
+ new_touch_event = true;
+ touch_location = Vec2(point.x, point.y);
+ touch_location_from_origin = touch_location - screen_center;
+ touch_location_angle = touch_location_from_origin.angle();
+ touch_location_length = touch_location_from_origin.length();
+
+ // touch_location_angle will fall within the range [-pi, pi]
+ // Map it to [0, 2*pi] instead.
+ if (touch_location_angle < 0)
+ touch_location_angle = kTwoPi + touch_location_angle;
+ // Map to ship angle.
+ touch_location_angle = kTwoPi - touch_location_angle;
+
+ PW_LOG_DEBUG("Touch: x:%d, y:%d, z:%d, angle:%f, degrees:%f length:%f",
+ point.x,
+ point.y,
+ point.z,
+ touch_location_angle,
+ degrees(touch_location_angle),
+ touch_location_length);
+ }
+ }
+
+ delta_button_check = pw::spin_delay::Micros() - time_start_button_check;
+
+ // Game Logic Phase
+ time_start_game_logic = pw::spin_delay::Micros();
+
+ delta_game_logic = pw::spin_delay::Micros() - time_start_game_logic;
+
+ // Draw Phase
+ time_start_draw_screen = pw::spin_delay::Micros();
+
+ frame_buffer.SetPenColor(0);
+ pw::draw::Fill(&frame_buffer);
+
+ pw::draw::TextArea text_area(&frame_buffer, &font6x8);
+
+ text_area.DrawCharacter('\n', 0, 1, colors_pico8_rgb565[COLOR_DARK_BLUE]);
+
+ // Draw the Pigweed "ASCII" banner.
+ int i = 0;
+ while (banner[i] > 0) {
+ text_area.DrawCharacter(banner[i], colors_pico8_rgb565[COLOR_PINK]);
+ i++;
+ }
+
+ for (int c = font6x8.starting_character; c <= font6x8.ending_character;
+ c++) {
+ if (c % 16 == 0) {
+ text_area.DrawCharacter('\n');
+ }
+ frame_buffer.SetPenColor(colors_pico8_rgb565[c % 16]);
+ text_area.DrawCharacter(c);
+ }
+
+ text_area.DrawCharacter('\n');
+ text_area.DrawCharacter('\n');
+ text_area.DrawTestFontSheet(
+ 32, text_area.cursor_x, text_area.cursor_y, 0xFFFF);
+
+ int sprite_pos_x = 10;
+ int sprite_pos_y = 180;
+ int sprite_scale = 4;
+ int border_size = 8;
+
+ // Draw the dark blue border
+ pw::draw::DrawRectWH(
+ &frame_buffer,
+ sprite_pos_x - border_size,
+ sprite_pos_y - border_size,
+ pigweed_farm_sprite_sheet.width * sprite_scale + (border_size * 2),
+ pigweed_farm_sprite_sheet.height * sprite_scale + (border_size * 2),
+ colors_pico8_rgb565[COLOR_DARK_BLUE],
+ true);
+
+ // Shrink the border
+ border_size = 4;
+
+ // Draw the light blue background
+ pw::draw::DrawRectWH(
+ &frame_buffer,
+ sprite_pos_x - border_size,
+ sprite_pos_y - border_size,
+ pigweed_farm_sprite_sheet.width * sprite_scale + (border_size * 2),
+ pigweed_farm_sprite_sheet.height * sprite_scale + (border_size * 2),
+ colors_pico8_rgb565[COLOR_BLUE],
+ true);
+
+ // Draw the Sun
+ frame_buffer.SetPenColor(colors_pico8_rgb565[COLOR_ORANGE]);
+ pw::draw::DrawCircle(
+ &frame_buffer,
+ sprite_pos_x + (pigweed_farm_sprite_sheet.width * sprite_scale) - 32,
+ sprite_pos_y,
+ 20,
+ true);
+ frame_buffer.SetPenColor(colors_pico8_rgb565[COLOR_YELLOW]);
+ pw::draw::DrawCircle(
+ &frame_buffer,
+ sprite_pos_x + (pigweed_farm_sprite_sheet.width * sprite_scale) - 32,
+ sprite_pos_y,
+ 18,
+ true);
+
+ // Draw the farm sprite's shadow
+ pigweed_farm_sprite_sheet.current_index = 1;
+ pw::draw::DrawSprite(&frame_buffer,
+ sprite_pos_x + 2,
+ sprite_pos_y + 2,
+ &pigweed_farm_sprite_sheet,
+ 4);
+
+ // Draw the farm sprite
+ pigweed_farm_sprite_sheet.current_index = 0;
+ pw::draw::DrawSprite(&frame_buffer,
+ sprite_pos_x,
+ sprite_pos_y,
+ &pigweed_farm_sprite_sheet,
+ 4);
+
+ // Print FPS Stats
+ int current_line = 210;
+ int font_height = 9;
+ string_buffer.clear();
+ string_buffer.Format("%.2f (%d) FPS",
+ 1.0 / (((float)delta_time) / 1000 / 1000),
+ frames_per_second);
+ // pw::display::DrawText(0, current_line, COLOR_WHITE,
+ // string_buffer.data());
+
+ current_line += font_height;
+ string_buffer.clear();
+ string_buffer.Format("Loop Time: %u (microseconds) = ", delta_time);
+ // pw::display::DrawText(0, current_line, COLOR_WHITE,
+ // string_buffer.data());
+
+ current_line += font_height;
+ string_buffer.clear();
+ string_buffer.Format(" input:%u + logic:%u + draw:%u + SPI:%u",
+ delta_button_check,
+ delta_game_logic,
+ delta_screen_draw,
+ delta_screen_spi_update);
+ // pw::display::DrawText(0, current_line, COLOR_WHITE,
+ // string_buffer.data());
+
+ delta_screen_draw = pw::spin_delay::Micros() - time_start_draw_screen;
+
+ // SPI Send Phase
+ time_start_screen_spi_update = pw::spin_delay::Micros();
+
+ pw::display::Update(&frame_buffer);
+
+ delta_screen_spi_update =
+ pw::spin_delay::Micros() - time_start_screen_spi_update;
+
+ // FPS Count Update
+ delta_time = pw::spin_delay::Micros() - time_start_delta;
+ delta_seconds = delta_time * 0.000001;
+
+ frames++;
+ if (pw::spin_delay::Millis() > frame_start_millis + 1000) {
+ PW_LOG_INFO("%.2f (%d) FPS",
+ 1.0 / (((float)delta_time) / 1000 / 1000),
+ frames_per_second);
+ PW_LOG_INFO(" input:%u + logic:%u + draw:%u + SPI:%u",
+ delta_button_check,
+ delta_game_logic,
+ delta_screen_draw,
+ delta_screen_spi_update);
+
+ frames_per_second = frames;
+ frames = 0;
+
+ frame_start_millis = pw::spin_delay::Millis();
+ }
+ }
+}
diff --git a/applications/terminal_display/tokenizer_database.csv b/applications/terminal_display/tokenizer_database.csv
new file mode 100644
index 0000000..1257f1c
--- /dev/null
+++ b/applications/terminal_display/tokenizer_database.csv
@@ -0,0 +1,9 @@
+2b4c99c4, ,"■msg♦ input:%u + logic:%u + draw:%u + SPI:%u■module♦■file♦applications/terminal_display/main.cc"
+5c6aec26, ,"■msg♦starfield main()■module♦■file♦applications/starfield_display/main.cc"
+6b150272, ,"■msg♦%.2f (%d) FPS■module♦■file♦applications/terminal_display/main.cc"
+b0deea6b, ,"■msg♦Touch: x:%d, y:%d, z:%d, angle:%f, degrees:%f length:%f■module♦■file♦applications/starfield_display/main.cc"
+b758cad4, ,"■msg♦Touch: x:%d, y:%d, z:%d, angle:%f, degrees:%f length:%f■module♦■file♦applications/terminal_display/main.cc"
+bc3cf216, ,"■msg♦pw::display::Init()■module♦■file♦applications/terminal_display/main.cc"
+bc9b17ad, ,"■msg♦pw::display::Init()■module♦■file♦applications/starfield_display/main.cc"
+c9c46909, ,"■msg♦%.2f (%d) FPS■module♦■file♦applications/starfield_display/main.cc"
+fa6107af, ,"■msg♦ input:%u + logic:%u + draw:%u + SPI:%u■module♦■file♦applications/starfield_display/main.cc"
diff --git a/build_overrides/pigweed.gni b/build_overrides/pigweed.gni
index cfdfc3f..c7d0c75 100644
--- a/build_overrides/pigweed.gni
+++ b/build_overrides/pigweed.gni
@@ -33,6 +33,16 @@
dir_pw_color = get_path_info("//pw_color", "abspath")
dir_pw_coordinates = get_path_info("//pw_coordinates", "abspath")
dir_pw_display = get_path_info("//pw_display", "abspath")
+ dir_pw_display_host_imgui =
+ get_path_info("//pw_display_host_imgui", "abspath")
+ dir_pw_display_null = get_path_info("//pw_display_null", "abspath")
+ dir_pw_display_pico_ili9341 =
+ get_path_info("//pw_display_pico_ili9341", "abspath")
+ dir_pw_display_stm32f429i_disc1_stm32cube_ili9341 =
+ get_path_info("//pw_display_stm32f429i_disc1_stm32cube_ili9341",
+ "abspath")
+ dir_pw_display_teensy_ili9341 =
+ get_path_info("//pw_display_teensy_ili9341", "abspath")
dir_pw_draw = get_path_info("//pw_draw", "abspath")
dir_pw_framebuffer = get_path_info("//pw_framebuffer", "abspath")
dir_pw_spin_delay = get_path_info("//pw_spin_delay", "abspath")
@@ -45,4 +55,9 @@
dir_pw_spin_delay_stm32cube =
get_path_info("//pw_spin_delay_stm32cube", "abspath")
dir_pw_touchscreen = get_path_info("//pw_touchscreen", "abspath")
+ dir_pw_touchscreen_null = get_path_info("//pw_touchscreen_null", "abspath")
+ dir_pw_touchscreen_teensy_stmpe610 =
+ get_path_info("//pw_touchscreen_teensy_stmpe610", "abspath")
+ dir_pw_touchscreen_teensy_xpt2046 =
+ get_path_info("//pw_touchscreen_teensy_xpt2046", "abspath")
}
diff --git a/pw_display_host_imgui/BUILD.gn b/pw_display_host_imgui/BUILD.gn
new file mode 100644
index 0000000..67d67a7
--- /dev/null
+++ b/pw_display_host_imgui/BUILD.gn
@@ -0,0 +1,90 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+import("imgui.gni")
+
+if (dir_pw_third_party_imgui != "") {
+ config("imgui") {
+ include_dirs = [
+ "$dir_pw_third_party_imgui",
+ "$dir_pw_third_party_imgui/backends",
+ "$dir_pw_third_party_imgui/examples/libs/glfw",
+ ]
+
+ if (host_os == "linux") {
+ libs = [
+ # pkg-config --static --libs gl
+ "GL",
+
+ # pkg-config --static --libs glfw3
+ "glfw",
+ "rt",
+ "m",
+ "dl",
+ ]
+ lib_dirs = [
+ "/usr/lib/x86_64-linux-gnu",
+ "/usr/lib",
+ ]
+ } else if (host_os == "mac") {
+ # TODO(tonymd) Test Mac build
+ ldflags = [
+ "-framework OpenGL",
+ "-framework Cocoa",
+ "-framework IOKit",
+ "-framework CoreVideo",
+ ]
+ libs = [ "glfw3" ]
+ lib_dirs = [
+ "$dir_pw_third_party_glfw/lib-universal",
+ "/usr/local/lib",
+ "/opt/local/lib",
+ ]
+ include_dirs += [
+ "$dir_pw_third_party_glfw/include",
+ "/usr/local/include",
+ "/opt/local/include",
+ ]
+ } else if (host_os == "win") {
+ libs = [
+ "glfw3",
+ "gdi32",
+ "opengl32",
+ "imm32",
+ ]
+ include_dirs += [ "$dir_pw_third_party_glfw/include" ]
+ lib_dirs = [ "$dir_pw_third_party_glfw/lib-mingw-w64" ]
+ }
+ }
+
+ _imgui_sources = [
+ "$dir_pw_third_party_imgui/backends/imgui_impl_glfw.cpp",
+ "$dir_pw_third_party_imgui/backends/imgui_impl_opengl3.cpp",
+ "$dir_pw_third_party_imgui/imgui.cpp",
+ "$dir_pw_third_party_imgui/imgui_demo.cpp",
+ "$dir_pw_third_party_imgui/imgui_draw.cpp",
+ "$dir_pw_third_party_imgui/imgui_tables.cpp",
+ "$dir_pw_third_party_imgui/imgui_widgets.cpp",
+ ]
+
+ pw_source_set("pw_display_host_imgui") {
+ configs = [ ":imgui" ]
+ deps = [ "$dir_pw_display:pw_display.facade" ]
+ sources = [ "display.cc" ] + _imgui_sources
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+ }
+}
diff --git a/pw_display_host_imgui/README.md b/pw_display_host_imgui/README.md
new file mode 100644
index 0000000..50410c8
--- /dev/null
+++ b/pw_display_host_imgui/README.md
@@ -0,0 +1,38 @@
+# pw_display_host_imgui
+
+## Setup Instructions
+
+1. Install [ImGui](https://github.com/ocornut/imgui) and [glfw](https://www.glfw.org/).
+
+ ```
+ pw package install imgui
+ pw package install glfw
+ ```
+
+2. Install host OS requiremets (only required for Linux) shown below.
+
+## ImGui Requirements
+
+### Linux
+
+- Debian / Ubuntu
+
+ ```sh
+ sudo apt install libglfw3-dev libglfw3
+ ```
+
+- Arch Linux
+
+ ```sh
+ sudo pacman -S glfw-x11
+ ```
+
+3. Compile with:
+
+```
+gn gen out --args="
+dir_pw_third_party_imgui=\"$PWD/.environment/packages/imgui\"
+"
+ninja -C out host
+./out/host_debug/obj/applications/terminal_display/bin/terminal_demo
+```
diff --git a/pw_display_host_imgui/display.cc b/pw_display_host_imgui/display.cc
new file mode 100644
index 0000000..2d6e60a
--- /dev/null
+++ b/pw_display_host_imgui/display.cc
@@ -0,0 +1,338 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// LCD Facade using imgui running on a host machine.
+// Much of this code is from the imgui example:
+// https://github.com/ocornut/imgui/tree/master/examples/example_glfw_opengl3
+// As well as the wiki page:
+// https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples
+#include "pw_display/display.h"
+
+#if defined(IMGUI_IMPL_OPENGL_ES2)
+#include <GLES2/gl2.h>
+#endif
+#include <GLFW/glfw3.h> // Will drag system OpenGL headers
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cinttypes>
+#include <cstdint>
+
+#include "imgui.h"
+#include "imgui_impl_glfw.h"
+#include "imgui_impl_opengl3.h"
+#include "pw_color/color.h"
+#include "pw_color/colors_pico8.h"
+#include "pw_coordinates/vec_int.h"
+#include "pw_display/display.h"
+#include "pw_framebuffer/rgb565.h"
+
+namespace {
+
+constexpr int kDisplayWidth = 320;
+constexpr int kDisplayHeight = 240;
+constexpr int kDisplayDataSize = kDisplayWidth * kDisplayHeight;
+
+uint16_t internal_framebuffer[kDisplayDataSize];
+
+// OpenGL texture data.
+GLuint lcd_pixel_data[kDisplayDataSize];
+
+// imgui state
+bool show_demo_window = true;
+bool show_another_window = false;
+ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f);
+GLuint lcd_texture = 0;
+GLFWwindow* window;
+int lcd_texture_display_scale = 2;
+int old_lcd_texture_display_scale = 0;
+bool lcd_texture_display_mode_nearest = true;
+bool old_lcd_texture_display_mode_nearest = true;
+
+void CleanupAndExit() {
+ ImGui_ImplOpenGL3_Shutdown();
+ ImGui_ImplGlfw_Shutdown();
+ ImGui::DestroyContext();
+
+ glfwDestroyWindow(window);
+ glfwTerminate();
+ exit(0);
+}
+
+void _SetTexturePixel(
+ GLuint x, GLuint y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ // Calculate target color
+ GLuint target_color;
+ GLubyte* colors = (GLubyte*)&target_color;
+ colors[0] = r;
+ colors[1] = g;
+ colors[2] = b;
+ colors[3] = a;
+ lcd_pixel_data[y * kDisplayWidth + x] = target_color;
+}
+
+void _SetTexturePixel(GLuint x, GLuint y, uint8_t r, uint8_t g, uint8_t b) {
+ _SetTexturePixel(x, y, r, g, b, 255);
+}
+
+void _SetTexturePixel(GLuint x, GLuint y, color_rgb565_t rgb565) {
+ ColorRGBA c(rgb565);
+ _SetTexturePixel(x, y, c.r, c.g, c.b, 255);
+}
+
+void UpdateLcdTexture(FramebufferRgb565* frame_buffer) {
+ // Copy frame_buffer into lcd_pixel_data
+ for (GLuint x = 0; x < kDisplayWidth; x++) {
+ for (GLuint y = 0; y < kDisplayHeight; y++) {
+ color_rgb565_t c = frame_buffer->GetPixel(x, y);
+ _SetTexturePixel(x, y, c);
+ }
+ }
+
+ // Set current texture
+ glBindTexture(GL_TEXTURE_2D, lcd_texture);
+ // Update texture
+ glTexSubImage2D(GL_TEXTURE_2D,
+ 0,
+ 0,
+ 0,
+ kDisplayWidth,
+ kDisplayHeight,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ lcd_pixel_data);
+ // Unbind texture
+ glBindTexture(GL_TEXTURE_2D, NULL);
+}
+
+void SetupLcdTexture(GLuint* out_texture) {
+ // Create a OpenGL texture identifier
+ GLuint image_texture;
+ glGenTextures(1, &image_texture);
+ glBindTexture(GL_TEXTURE_2D, image_texture);
+
+ // Setup filtering parameters for display
+ GLuint display_mode =
+ lcd_texture_display_mode_nearest ? GL_NEAREST : GL_LINEAR;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, display_mode);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, display_mode);
+ glTexParameteri(GL_TEXTURE_2D,
+ GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE); // This is required on WebGL for non
+ // power-of-two textures
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Same
+
+ // Upload pixels into texture
+#if defined(GL_UNPACK_ROW_LENGTH) && !defined(__EMSCRIPTEN__)
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
+#endif
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ GL_RGBA,
+ kDisplayWidth,
+ kDisplayHeight,
+ 0,
+ GL_RGBA,
+ GL_UNSIGNED_BYTE,
+ lcd_pixel_data);
+
+ glBindTexture(GL_TEXTURE_2D, NULL);
+
+ *out_texture = image_texture;
+}
+
+static void glfw_error_callback(int error, const char* description) {
+ fprintf(stderr, "Glfw Error %d: %s\n", error, description);
+}
+
+} // namespace
+
+namespace pw::display {
+
+uint16_t* GetInternalFramebuffer() { return internal_framebuffer; }
+
+int GetWidth() { return kDisplayWidth; }
+int GetHeight() { return kDisplayHeight; }
+
+void Init() {
+ // Setup window
+ glfwSetErrorCallback(glfw_error_callback);
+ if (!glfwInit())
+ return;
+
+ // Decide GL+GLSL versions
+#if defined(IMGUI_IMPL_OPENGL_ES2)
+ // GL ES 2.0 + GLSL 100
+ const char* glsl_version = "#version 100";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
+#elif defined(__APPLE__)
+ // GL 3.2 + GLSL 150
+ const char* glsl_version = "#version 150";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac
+#else
+ // GL 3.0 + GLSL 130
+ const char* glsl_version = "#version 130";
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+ // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+
+ // only glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only
+#endif
+
+ // Create window with graphics context
+ window = glfwCreateWindow(1280, 720, "pw_display", NULL, NULL);
+ if (window == NULL)
+ return;
+ glfwMakeContextCurrent(window);
+ glfwSwapInterval(1); // Enable vsync
+
+ // Setup Dear ImGui context
+ IMGUI_CHECKVERSION();
+ ImGui::CreateContext();
+ ImGuiIO& io = ImGui::GetIO();
+ (void)io;
+ // io.Fonts->AddFontFromFileTTF("NotoSans-Regular.ttf", 32.0);
+
+ // Enable Keyboard Controls
+ io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
+ // Enable Gamepad Controls
+ // io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
+
+ // Setup Dear ImGui style
+ ImGui::StyleColorsDark();
+ // ImGui::StyleColorsLight();
+ // ImGui::StyleColorsClassic();
+
+ // Setup Platform/Renderer backends
+ ImGui_ImplGlfw_InitForOpenGL(window, true);
+ ImGui_ImplOpenGL3_Init(glsl_version);
+
+ SetupLcdTexture(&lcd_texture);
+}
+
+void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
+ if (old_lcd_texture_display_mode_nearest !=
+ lcd_texture_display_mode_nearest) {
+ old_lcd_texture_display_mode_nearest = lcd_texture_display_mode_nearest;
+ SetupLcdTexture(&lcd_texture);
+ }
+ UpdateLcdTexture(frame_buffer);
+
+ // Poll and handle events (inputs, window resize, etc.)
+ glfwPollEvents();
+
+ // Start the Dear ImGui frame
+ ImGui_ImplOpenGL3_NewFrame();
+ ImGui_ImplGlfw_NewFrame();
+ ImGui::NewFrame();
+
+ // 1. Show the big demo window (Most of the sample code is in
+ // ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear
+ // ImGui!).
+ if (show_demo_window) {
+ ImGui::ShowDemoWindow(&show_demo_window);
+ }
+
+ // 2. Show a simple window that we create ourselves. We use a Begin/End pair
+ // to created a named window.
+ {
+ static float f = 0.0f;
+ static int counter = 0;
+
+ ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!"
+ // and append into it.
+
+ ImGui::Text("This is some useful text."); // Display some text (you can use
+ // a format strings too)
+ ImGui::Checkbox(
+ "Demo Window",
+ &show_demo_window); // Edit bools storing our window open/close state
+ ImGui::Checkbox("Another Window", &show_another_window);
+
+ ImGui::SliderFloat("float",
+ &f,
+ 0.0f,
+ 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f
+ ImGui::ColorEdit3(
+ "clear color",
+ (float*)&clear_color); // Edit 3 floats representing a color
+
+ if (ImGui::Button("Button")) // Buttons return true when clicked (most
+ // widgets return true when edited/activated)
+ counter++;
+ ImGui::SameLine();
+ ImGui::Text("counter = %d", counter);
+
+ ImGui::Text("Application average %.3f ms/frame (%.1f FPS)",
+ 1000.0f / ImGui::GetIO().Framerate,
+ ImGui::GetIO().Framerate);
+ ImGui::End();
+
+ ImGui::Begin("Display");
+ ImGui::Text("Pixel Size = %d x %d", kDisplayWidth, kDisplayHeight);
+ ImGui::Checkbox("Nearest neighbor", &lcd_texture_display_mode_nearest);
+ ImGui::SliderInt("Integer Scaling", &lcd_texture_display_scale, 1, 10);
+ ImGui::Image((void*)(intptr_t)lcd_texture,
+ ImVec2(lcd_texture_display_scale * kDisplayWidth,
+ lcd_texture_display_scale * kDisplayHeight));
+ ImGui::End();
+ }
+
+ // 3. Show another simple window.
+ if (show_another_window) {
+ ImGui::Begin(
+ "Another Window",
+ &show_another_window); // Pass a pointer to our bool variable (the
+ // window will have a closing button that will
+ // clear the bool when clicked)
+ ImGui::Text("Hello from another window!");
+ if (ImGui::Button("Close Me"))
+ show_another_window = false;
+ ImGui::End();
+ }
+
+ // Rendering
+ ImGui::Render();
+ int display_w, display_h;
+ glfwGetFramebufferSize(window, &display_w, &display_h);
+ glViewport(0, 0, display_w, display_h);
+ glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w);
+ glClear(GL_COLOR_BUFFER_BIT);
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
+ glfwSwapBuffers(window);
+
+ if (glfwWindowShouldClose(window)) {
+ CleanupAndExit();
+ }
+}
+
+bool TouchscreenAvailable() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::display
diff --git a/pw_display_host_imgui/glfw.gni b/pw_display_host_imgui/glfw.gni
new file mode 100644
index 0000000..d73111f
--- /dev/null
+++ b/pw_display_host_imgui/glfw.gni
@@ -0,0 +1,19 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+declare_args() {
+ # Location of glfw download.
+ # Install with: pw package install glfw
+ dir_pw_third_party_glfw = ""
+}
diff --git a/pw_display_host_imgui/imgui.gni b/pw_display_host_imgui/imgui.gni
new file mode 100644
index 0000000..63b41c4
--- /dev/null
+++ b/pw_display_host_imgui/imgui.gni
@@ -0,0 +1,19 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+declare_args() {
+ # Location of imgui checkout.
+ # Install with: pw package install imgui
+ dir_pw_third_party_imgui = ""
+}
diff --git a/pw_display_null/BUILD.gn b/pw_display_null/BUILD.gn
new file mode 100644
index 0000000..7b02e55
--- /dev/null
+++ b/pw_display_null/BUILD.gn
@@ -0,0 +1,23 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_display_null") {
+ deps = [ "$dir_pw_display:pw_display.facade" ]
+ sources = [ "display.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_display_null/display.cc b/pw_display_null/display.cc
new file mode 100644
index 0000000..f565d5b
--- /dev/null
+++ b/pw_display_null/display.cc
@@ -0,0 +1,48 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display/display.h"
+
+#include <cinttypes>
+
+namespace pw::display {
+
+namespace {
+
+uint16_t internal_framebuffer[320 * 240];
+
+} // namespace
+
+void Init() {}
+
+int GetWidth() { return 320; }
+int GetHeight() { return 240; }
+
+uint16_t* GetInternalFramebuffer() { return internal_framebuffer; }
+
+void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {}
+
+bool TouchscreenAvailable() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::display
diff --git a/pw_display_pico_ili9341/BUILD.gn b/pw_display_pico_ili9341/BUILD.gn
new file mode 100644
index 0000000..2c05f48
--- /dev/null
+++ b/pw_display_pico_ili9341/BUILD.gn
@@ -0,0 +1,29 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pi_pico.gni")
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_display_pico_ili9341") {
+ deps = [
+ "$PICO_ROOT/src/common/pico_base",
+ "$PICO_ROOT/src/common/pico_stdlib",
+ "$PICO_ROOT/src/rp2_common/hardware_spi",
+ "$dir_pw_display:pw_display.facade",
+ ]
+ sources = [ "display.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_display_pico_ili9341/display.cc b/pw_display_pico_ili9341/display.cc
new file mode 100644
index 0000000..188669f
--- /dev/null
+++ b/pw_display_pico_ili9341/display.cc
@@ -0,0 +1,339 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display/display.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cinttypes>
+#include <cstdint>
+
+#include "hardware/spi.h"
+#include "pico/stdlib.h"
+#include "pw_color/color.h"
+#include "pw_framebuffer/rgb565.h"
+
+namespace pw::display {
+
+namespace {
+
+#define SPI_PORT spi0
+
+#define ILI9341_MADCTL 0x36
+#define MADCTL_MY 0x80
+#define MADCTL_MX 0x40
+#define MADCTL_MV 0x20
+#define MADCTL_ML 0x10
+#define MADCTL_RGB 0x00
+#define MADCTL_BGR 0x08
+#define MADCTL_MH 0x04
+
+#define ILI9341_PIXEL_FORMAT_SET 0x3A
+
+constexpr int TFT_DC = 18; // stm32f429i-disc1: PD13
+constexpr int TFT_CS = 5; // stm32f429i-disc1: PC2
+constexpr int TFT_RST = 19; // stm32f429i-disc1: NRST
+constexpr int TFT_MOSI = 3; // stm32f429i-disc1: PF9
+constexpr int TFT_SCLK = 2; // stm32f429i-disc1: PF7
+constexpr int TFT_MISO = 4; // stm32f429i-disc1: PF8
+
+constexpr int kDisplayWidth = 320;
+constexpr int kDisplayHeight = 240;
+constexpr int kDisplayDataSize = kDisplayWidth * kDisplayHeight;
+uint16_t internal_framebuffer[kDisplayDataSize];
+
+// SPI Functions
+// TODO(tonymd): move to pw_spi
+static inline void ChipSelectEnable() {
+ asm volatile("nop \n nop \n nop");
+ gpio_put(TFT_CS, 0);
+ asm volatile("nop \n nop \n nop");
+}
+
+static inline void ChipSelectDisable() {
+ asm volatile("nop \n nop \n nop");
+ gpio_put(TFT_CS, 1);
+ asm volatile("nop \n nop \n nop");
+}
+
+static inline void DataCommandEnable() {
+ asm volatile("nop \n nop \n nop");
+ gpio_put(TFT_DC, 0);
+ asm volatile("nop \n nop \n nop");
+}
+
+static inline void DataCommandDisable() {
+ asm volatile("nop \n nop \n nop");
+ gpio_put(TFT_DC, 1);
+ asm volatile("nop \n nop \n nop");
+}
+
+static void inline SPISendByte(uint8_t data) {
+ ChipSelectEnable();
+ DataCommandDisable();
+ spi_write_blocking(SPI_PORT, &data, 1);
+ ChipSelectDisable();
+}
+
+static void inline SPISendShort(uint16_t data) {
+ ChipSelectEnable();
+ DataCommandDisable();
+
+ uint8_t shortBuffer[2];
+
+ shortBuffer[0] = (uint8_t)(data >> 8);
+ shortBuffer[1] = (uint8_t)data;
+
+ spi_write_blocking(SPI_PORT, shortBuffer, 2);
+
+ ChipSelectDisable();
+}
+
+static void inline SPISendCommand(uint8_t command) {
+ // set data/command to command mode (low).
+ DataCommandEnable();
+
+ ChipSelectEnable();
+
+ // send the command to the display.
+ spi_write_blocking(SPI_PORT, &command, 1);
+
+ // put the display back into data mode (high).
+ DataCommandDisable();
+
+ ChipSelectDisable();
+}
+
+} // namespace
+
+void Init() {
+ stdio_init_all();
+ uint actual_baudrate = spi_init(SPI_PORT, 31250000);
+ spi_set_format(SPI_PORT, 8, (spi_cpol_t)1, (spi_cpha_t)1, SPI_MSB_FIRST);
+
+ // Init pico SPI
+ printf("Actual Baudrate: %i\n", actual_baudrate);
+ gpio_set_function(TFT_MISO, GPIO_FUNC_SPI);
+ gpio_set_function(TFT_SCLK, GPIO_FUNC_SPI);
+ gpio_set_function(TFT_MOSI, GPIO_FUNC_SPI);
+
+ gpio_init(TFT_CS);
+ gpio_init(TFT_DC);
+ gpio_init(TFT_RST);
+
+ gpio_set_dir(TFT_CS, GPIO_OUT);
+ gpio_set_dir(TFT_DC, GPIO_OUT);
+ gpio_set_dir(TFT_RST, GPIO_OUT);
+ gpio_put(TFT_CS, 1);
+ gpio_put(TFT_DC, 0);
+ gpio_put(TFT_RST, 0);
+
+ // Init Display
+ ChipSelectEnable();
+
+ // Toggle Reset pin
+ gpio_put(TFT_RST, 0);
+ sleep_ms(100);
+ gpio_put(TFT_RST, 1);
+ sleep_ms(100);
+
+ // ILI9341 Init Sequence:
+ SPISendCommand(0xEF);
+ SPISendByte(0x03);
+ SPISendByte(0x80);
+ SPISendByte(0x02);
+
+ // ?
+ SPISendCommand(0xCF);
+ SPISendByte(0x00);
+ SPISendByte(0xC1);
+ SPISendByte(0x30);
+
+ // ?
+ SPISendCommand(0xED);
+ SPISendByte(0x64);
+ SPISendByte(0x03);
+ SPISendByte(0x12);
+ SPISendByte(0x81);
+
+ // ?
+ SPISendCommand(0xE8);
+ SPISendByte(0x85);
+ SPISendByte(0x00);
+ SPISendByte(0x78);
+
+ // ?
+ SPISendCommand(0xCB);
+ SPISendByte(0x39);
+ SPISendByte(0x2C);
+ SPISendByte(0x00);
+ SPISendByte(0x34);
+ SPISendByte(0x02);
+
+ // ?
+ SPISendCommand(0xF7);
+ SPISendByte(0x20);
+
+ // ?
+ SPISendCommand(0xEA);
+ SPISendByte(0x00);
+ SPISendByte(0x00);
+
+ // Power control
+ SPISendCommand(0xC0);
+ SPISendByte(0x23);
+
+ // Power control
+ SPISendCommand(0xC1);
+ SPISendByte(0x10);
+
+ // VCM control
+ SPISendCommand(0xC5);
+ SPISendByte(0x3e);
+ SPISendByte(0x28);
+
+ // VCM control
+ SPISendCommand(0xC7);
+ SPISendByte(0x86);
+
+ SPISendCommand(ILI9341_MADCTL);
+ // Rotation
+ // SPISendByte(MADCTL_MX | MADCTL_BGR); // 0
+ // SPISendByte(MADCTL_MV | MADCTL_BGR); // 1 landscape
+ // SPISendByte(MADCTL_MY | MADCTL_BGR); // 2
+ SPISendByte(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); // 3 landscape
+
+ SPISendCommand(ILI9341_PIXEL_FORMAT_SET);
+ SPISendByte(0x55); // 16 bits / pixel
+ // SPISendByte(0x36); // 18 bits / pixel
+
+ // Frame Control (Normal Mode)
+ SPISendCommand(0xB1);
+ SPISendByte(0x00); // division ratio
+ SPISendByte(0x1F); // 61 Hz
+ // SPISendByte(0x1B); // 70 Hz - default
+ // SPISendByte(0x18); // 79 Hz
+ // SPISendByte(0x10); // 119 Hz
+
+ // Display Function Control
+ SPISendCommand(0xB6);
+ SPISendByte(0x08);
+ SPISendByte(0x82);
+ SPISendByte(0x27);
+
+ // Gamma Function Disable?
+ SPISendCommand(0xF2);
+ SPISendByte(0x00);
+
+ // Gamma Set
+ SPISendCommand(0x26);
+ SPISendByte(0x01);
+
+ // Positive Gamma Correction
+ SPISendCommand(0xE0);
+ SPISendByte(0x0F);
+ SPISendByte(0x31);
+ SPISendByte(0x2B);
+ SPISendByte(0x0C);
+ SPISendByte(0x0E);
+ SPISendByte(0x08);
+ SPISendByte(0x4E);
+ SPISendByte(0xF1);
+ SPISendByte(0x37);
+ SPISendByte(0x07);
+ SPISendByte(0x10);
+ SPISendByte(0x03);
+ SPISendByte(0x0E);
+ SPISendByte(0x09);
+ SPISendByte(0x00);
+
+ // Negative Gamma Correction
+ SPISendCommand(0xE1);
+ SPISendByte(0x00);
+ SPISendByte(0x0E);
+ SPISendByte(0x14);
+ SPISendByte(0x03);
+ SPISendByte(0x11);
+ SPISendByte(0x07);
+ SPISendByte(0x31);
+ SPISendByte(0xC1);
+ SPISendByte(0x48);
+ SPISendByte(0x08);
+ SPISendByte(0x0F);
+ SPISendByte(0x0C);
+ SPISendByte(0x31);
+ SPISendByte(0x36);
+ SPISendByte(0x0F);
+
+ // Exit Sleep
+ SPISendCommand(0x11);
+
+ sleep_ms(100);
+
+ // Display On
+ SPISendCommand(0x29);
+
+ sleep_ms(100);
+
+ // Normal display mode on
+ SPISendCommand(0x13);
+
+ // Setup drawing full framebuffers
+
+ // Landscape drawing
+ // Column Address Set
+ SPISendCommand(0x2A);
+ SPISendShort(0);
+ SPISendShort(319);
+ // Page Address Set
+ SPISendCommand(0x2B);
+ SPISendShort(0);
+ SPISendShort(239);
+
+ sleep_ms(10);
+
+ SPISendCommand(0x2C);
+
+ ChipSelectEnable();
+ DataCommandDisable();
+
+ sleep_ms(10);
+
+ // SPI writes from here out use 16 data bits (for drawing the framebuffer).
+ spi_set_format(SPI_PORT, 16, (spi_cpol_t)1, (spi_cpha_t)1, SPI_MSB_FIRST);
+}
+
+int GetWidth() { return kDisplayWidth; }
+int GetHeight() { return kDisplayHeight; }
+
+uint16_t* GetInternalFramebuffer() { return internal_framebuffer; }
+
+void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
+ spi_write16_blocking(SPI_PORT, internal_framebuffer, kDisplayDataSize);
+}
+
+bool TouchscreenAvailable() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::display
diff --git a/pw_display_stm32f429i_disc1_stm32cube_ili9341/BUILD.gn b/pw_display_stm32f429i_disc1_stm32cube_ili9341/BUILD.gn
new file mode 100644
index 0000000..713d569
--- /dev/null
+++ b/pw_display_stm32f429i_disc1_stm32cube_ili9341/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_display_stm32f429i_disc1_stm32cube_ili9341") {
+ deps = [
+ "$dir_pw_display:pw_display.facade",
+ "$dir_pw_third_party/stm32cube",
+ ]
+ sources = [ "display.cc" ]
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+}
diff --git a/pw_display_stm32f429i_disc1_stm32cube_ili9341/display.cc b/pw_display_stm32f429i_disc1_stm32cube_ili9341/display.cc
new file mode 100644
index 0000000..20225f9
--- /dev/null
+++ b/pw_display_stm32f429i_disc1_stm32cube_ili9341/display.cc
@@ -0,0 +1,392 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display/display.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <cinttypes>
+#include <cstdint>
+
+#include "pw_color/color.h"
+#include "pw_framebuffer/rgb565.h"
+#include "stm32cube/stm32cube.h"
+#include "stm32f4xx_hal.h"
+#include "stm32f4xx_hal_spi.h"
+
+namespace pw::display {
+
+namespace {
+
+SPI_HandleTypeDef hspi5;
+
+#define HSPI_INSTANCE &hspi5
+
+// CHIP SELECT PIN AND PORT
+#define LCD_CS_PORT GPIOC
+#define LCD_CS_PIN GPIO_PIN_2
+
+// DATA COMMAND PIN AND PORT
+#define LCD_DC_PORT GPIOD
+#define LCD_DC_PIN GPIO_PIN_13
+
+#define ILI9341_MADCTL 0x36
+#define MADCTL_MY 0x80
+#define MADCTL_MX 0x40
+#define MADCTL_MV 0x20
+#define MADCTL_ML 0x10
+#define MADCTL_RGB 0x00
+#define MADCTL_BGR 0x08
+#define MADCTL_MH 0x04
+
+#define ILI9341_PIXEL_FORMAT_SET 0x3A
+
+constexpr int kDisplayWidth = 320;
+constexpr int kDisplayHeight = 240;
+constexpr int kDisplayDataSize = kDisplayWidth * kDisplayHeight;
+
+uint16_t internal_framebuffer[kDisplayDataSize];
+
+// SPI Functions
+// TODO(tonymd): move to pw_spi
+static inline void ChipSelectEnable() {
+ HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET);
+}
+
+static inline void ChipSelectDisable() {
+ HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_SET);
+}
+
+static inline void DataCommandEnable() {
+ HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_RESET);
+}
+
+static inline void DataCommandDisable() {
+ HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_SET);
+}
+
+static void inline SPISendByte(uint8_t data) {
+ ChipSelectEnable();
+ DataCommandDisable();
+ HAL_SPI_Transmit(HSPI_INSTANCE, &data, 1, 1);
+ ChipSelectDisable();
+}
+
+static void inline SPISendShort(uint16_t data) {
+ ChipSelectEnable();
+ DataCommandDisable();
+
+ uint8_t shortBuffer[2];
+
+ shortBuffer[0] = (uint8_t)(data >> 8);
+ shortBuffer[1] = (uint8_t)data;
+
+ HAL_SPI_Transmit(HSPI_INSTANCE, shortBuffer, 2, 1);
+
+ ChipSelectDisable();
+}
+
+static void inline SPISendCommand(uint8_t command) {
+ // set data/command to command mode (low).
+ DataCommandEnable();
+
+ ChipSelectEnable();
+
+ // send the command to the display.
+ HAL_SPI_Transmit(HSPI_INSTANCE, &command, 1, 1);
+
+ // put the display back into data mode (high).
+ DataCommandDisable();
+
+ ChipSelectDisable();
+}
+
+void MX_GPIO_Init(void) {
+ GPIO_InitTypeDef GPIO_InitStruct = {0};
+
+ // GPIO Ports Clock Enable
+ __HAL_RCC_GPIOC_CLK_ENABLE();
+ __HAL_RCC_GPIOF_CLK_ENABLE();
+ __HAL_RCC_GPIOH_CLK_ENABLE();
+ __HAL_RCC_GPIOA_CLK_ENABLE();
+ __HAL_RCC_GPIOB_CLK_ENABLE();
+ __HAL_RCC_GPIOG_CLK_ENABLE();
+ __HAL_RCC_GPIOE_CLK_ENABLE();
+ __HAL_RCC_GPIOD_CLK_ENABLE();
+
+ HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET);
+
+ GPIO_InitStruct.Pin = LCD_CS_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+ HAL_GPIO_Init(LCD_CS_PORT, &GPIO_InitStruct);
+
+ HAL_GPIO_WritePin(LCD_DC_PORT, LCD_DC_PIN, GPIO_PIN_RESET);
+
+ GPIO_InitStruct.Pin = LCD_DC_PIN;
+ GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
+ HAL_GPIO_Init(LCD_DC_PORT, &GPIO_InitStruct);
+
+ // Reset pin not connected
+
+ __HAL_RCC_SPI5_CLK_ENABLE();
+
+ // SPI5 GPIO Configuration:
+ // PF7 SPI5_SCK
+ // PF8 SPI5_MISO
+ // PF9 SPI5_MOSI
+ GPIO_InitStruct.Pin = GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9;
+ GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStruct.Pull = GPIO_NOPULL;
+ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
+ GPIO_InitStruct.Alternate = GPIO_AF5_SPI5;
+ HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
+}
+
+void MX_SPI5_Init(void) {
+ hspi5.Instance = SPI5;
+ hspi5.Init.Mode = SPI_MODE_MASTER;
+ hspi5.Init.Direction = SPI_DIRECTION_2LINES;
+ hspi5.Init.DataSize = SPI_DATASIZE_8BIT;
+ hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
+ hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
+ hspi5.Init.NSS = SPI_NSS_SOFT;
+ hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
+ hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
+ hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ hspi5.Init.CRCPolynomial = 7;
+ HAL_SPI_Init(&hspi5);
+}
+
+} // namespace
+
+void Init() {
+ MX_GPIO_Init();
+ MX_SPI5_Init();
+ // CS OFF
+ HAL_GPIO_WritePin(LCD_CS_PORT, LCD_CS_PIN, GPIO_PIN_RESET);
+
+ // Init Display
+ ChipSelectEnable();
+
+ // ILI9341 Init Sequence:
+
+ // ?
+ SPISendCommand(0xEF);
+ SPISendByte(0x03);
+ SPISendByte(0x80);
+ SPISendByte(0x02);
+
+ // ?
+ SPISendCommand(0xCF);
+ SPISendByte(0x00);
+ SPISendByte(0xC1);
+ SPISendByte(0x30);
+
+ // ?
+ SPISendCommand(0xED);
+ SPISendByte(0x64);
+ SPISendByte(0x03);
+ SPISendByte(0x12);
+ SPISendByte(0x81);
+
+ // ?
+ SPISendCommand(0xE8);
+ SPISendByte(0x85);
+ SPISendByte(0x00);
+ SPISendByte(0x78);
+
+ // ?
+ SPISendCommand(0xCB);
+ SPISendByte(0x39);
+ SPISendByte(0x2C);
+ SPISendByte(0x00);
+ SPISendByte(0x34);
+ SPISendByte(0x02);
+
+ // ?
+ SPISendCommand(0xF7);
+ SPISendByte(0x20);
+
+ // ?
+ SPISendCommand(0xEA);
+ SPISendByte(0x00);
+ SPISendByte(0x00);
+
+ // Power control
+ SPISendCommand(0xC0);
+ SPISendByte(0x23);
+
+ // Power control
+ SPISendCommand(0xC1);
+ SPISendByte(0x10);
+
+ // VCM control
+ SPISendCommand(0xC5);
+ SPISendByte(0x3e);
+ SPISendByte(0x28);
+
+ // VCM control
+ SPISendCommand(0xC7);
+ SPISendByte(0x86);
+
+ SPISendCommand(ILI9341_MADCTL);
+ // Rotation
+ // SPISendByte(MADCTL_MX | MADCTL_BGR); // 0
+ // SPISendByte(MADCTL_MV | MADCTL_BGR); // 1 landscape
+ // SPISendByte(MADCTL_MY | MADCTL_BGR); // 2
+ SPISendByte(MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_BGR); // 3 landscape
+
+ SPISendCommand(ILI9341_PIXEL_FORMAT_SET);
+ SPISendByte(0x55); // 16 bits / pixel
+ // SPISendByte(0x36); // 18 bits / pixel
+
+ // Frame Control (Normal Mode)
+ SPISendCommand(0xB1);
+ SPISendByte(0x00); // division ratio
+ SPISendByte(0x1F); // 61 Hz
+ // SPISendByte(0x1B); // 70 Hz - default
+ // SPISendByte(0x18); // 79 Hz
+ // SPISendByte(0x10); // 119 Hz
+
+ // Display Function Control
+ SPISendCommand(0xB6);
+ SPISendByte(0x08);
+ SPISendByte(0x82);
+ SPISendByte(0x27);
+
+ // Gamma Function Disable?
+ SPISendCommand(0xF2);
+ SPISendByte(0x00);
+
+ // Gamma Set
+ SPISendCommand(0x26);
+ SPISendByte(0x01);
+
+ // Positive Gamma Correction
+ SPISendCommand(0xE0);
+ SPISendByte(0x0F);
+ SPISendByte(0x31);
+ SPISendByte(0x2B);
+ SPISendByte(0x0C);
+ SPISendByte(0x0E);
+ SPISendByte(0x08);
+ SPISendByte(0x4E);
+ SPISendByte(0xF1);
+ SPISendByte(0x37);
+ SPISendByte(0x07);
+ SPISendByte(0x10);
+ SPISendByte(0x03);
+ SPISendByte(0x0E);
+ SPISendByte(0x09);
+ SPISendByte(0x00);
+
+ // Negative Gamma Correction
+ SPISendCommand(0xE1);
+ SPISendByte(0x00);
+ SPISendByte(0x0E);
+ SPISendByte(0x14);
+ SPISendByte(0x03);
+ SPISendByte(0x11);
+ SPISendByte(0x07);
+ SPISendByte(0x31);
+ SPISendByte(0xC1);
+ SPISendByte(0x48);
+ SPISendByte(0x08);
+ SPISendByte(0x0F);
+ SPISendByte(0x0C);
+ SPISendByte(0x31);
+ SPISendByte(0x36);
+ SPISendByte(0x0F);
+
+ // Exit Sleep
+ SPISendCommand(0x11);
+
+ HAL_Delay(100);
+
+ // Display On
+ SPISendCommand(0x29);
+
+ HAL_Delay(100);
+
+ // Normal display mode on
+ SPISendCommand(0x13);
+
+ // Setup drawing full framebuffers
+
+ // Landscape drawing
+ // Column Address Set
+ SPISendCommand(0x2A);
+ SPISendShort(0);
+ SPISendShort(319);
+ // Page Address Set
+ SPISendCommand(0x2B);
+ SPISendShort(0);
+ SPISendShort(239);
+ SPISendCommand(0x2C);
+
+ ChipSelectEnable();
+ DataCommandDisable();
+
+ // SPI writes from here out use 16 data bits (for drawing the framebuffer).
+ hspi5.Instance = SPI5;
+ hspi5.Init.Mode = SPI_MODE_MASTER;
+ hspi5.Init.Direction = SPI_DIRECTION_2LINES;
+ hspi5.Init.DataSize = SPI_DATASIZE_16BIT;
+ hspi5.Init.CLKPolarity = SPI_POLARITY_LOW;
+ hspi5.Init.CLKPhase = SPI_PHASE_1EDGE;
+ hspi5.Init.NSS = SPI_NSS_SOFT;
+ hspi5.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
+ hspi5.Init.FirstBit = SPI_FIRSTBIT_MSB;
+ hspi5.Init.TIMode = SPI_TIMODE_DISABLE;
+ hspi5.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
+ hspi5.Init.CRCPolynomial = 7;
+ HAL_SPI_Init(&hspi5);
+}
+
+int GetWidth() { return kDisplayWidth; }
+int GetHeight() { return kDisplayHeight; }
+
+uint16_t* GetInternalFramebuffer() { return internal_framebuffer; }
+
+void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
+ // Send 10 rows at a time
+ for (int i = 0; i < 24; i++) {
+ HAL_SPI_Transmit(HSPI_INSTANCE,
+ (uint8_t*)&internal_framebuffer[320 * (10 * i)],
+ 320 * 10,
+ // TODO(tonymd): Figure out what the timeout should be.
+ // 100 here works for 10 rows of 320 pixels.
+ 100);
+ }
+}
+
+bool TouchscreenAvailable() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::display
diff --git a/pw_display_teensy_ili9341/BUILD.gn b/pw_display_teensy_ili9341/BUILD.gn
new file mode 100644
index 0000000..942c9e8
--- /dev/null
+++ b/pw_display_teensy_ili9341/BUILD.gn
@@ -0,0 +1,70 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+
+if (pw_arduino_build_CORE_PATH != "") {
+ # Common library arguments to the arduino_builder tool.
+ _library_args = [
+ "--library-path",
+ rebase_path(
+ "$pw_arduino_build_CORE_PATH/teensy/hardware/teensy/avr/libraries"),
+ rebase_path("libraries"),
+ "--library-names",
+
+ # Arduino Core Libaries. The c/c++ files for these should only be included
+ # once in any given pw_executable.
+ "SPI",
+ "Wire",
+
+ # Additional libraries
+ # Requires SPI+Wire
+ "Adafruit_BusIO-1.7.1",
+
+ # Requires Adafruit_BusIO
+ "Adafruit-GFX-Library-1.10.4",
+
+ # Requires Adafruit-GFX
+ "ILI9341_t3n-a9a6ff5d78b1207ae7fd394f44e83e322863e55f",
+ ]
+
+ _library_c_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-c-files" ],
+ "list lines")
+
+ _library_cpp_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-cpp-files" ],
+ "list lines")
+
+ _library_include_dirs =
+ exec_script(arduino_builder_script,
+ arduino_show_command_args + _library_args +
+ [ "--library-include-dirs" ],
+ "list lines")
+
+ pw_source_set("pw_display_teensy_ili9341") {
+ deps = [
+ "$dir_pw_display:pw_display.facade",
+ "$dir_pw_third_party/arduino:arduino_core_sources",
+ ]
+ sources = [ "display.cc" ] + _library_c_files + _library_cpp_files
+ include_dirs = _library_include_dirs
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+ }
+}
diff --git a/pw_display_teensy_ili9341/README.md b/pw_display_teensy_ili9341/README.md
new file mode 100644
index 0000000..34b3626
--- /dev/null
+++ b/pw_display_teensy_ili9341/README.md
@@ -0,0 +1,15 @@
+# pw_display_teensy_ili9341
+
+## Setup Instructions
+
+1. Install the Teensyduino core and set the required GN args.
+
+ ```sh
+ pw package install teensy
+ ```
+
+2. Edit `//targets/arduino/target_toolchains.gni` and set:
+
+ ```
+ pw_display_BACKEND = "$dir_pw_display_teensy_ili9341"
+ ```
diff --git a/pw_display_teensy_ili9341/display.cc b/pw_display_teensy_ili9341/display.cc
new file mode 100644
index 0000000..28db61c
--- /dev/null
+++ b/pw_display_teensy_ili9341/display.cc
@@ -0,0 +1,81 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_display/display.h"
+
+#include <Arduino.h>
+
+#include <cinttypes>
+#include <cstdint>
+
+#include "ILI9341_t3n.h"
+#include "pw_color/color.h"
+#include "pw_framebuffer/rgb565.h"
+
+namespace pw::display {
+
+namespace {
+
+constexpr int TFT_DC = 10; // stm32f429i-disc1: PD13
+constexpr int TFT_CS = 9; // stm32f429i-disc1: PC2
+constexpr int TFT_RST = 8; // stm32f429i-disc1: NRST
+constexpr int TFT_MOSI = 11; // stm32f429i-disc1: PF9
+constexpr int TFT_SCLK = 13; // stm32f429i-disc1: PF7
+constexpr int TFT_MISO = 12; // stm32f429i-disc1: PF8
+
+constexpr int kDisplayWidth = 320;
+constexpr int kDisplayHeight = 240;
+constexpr int kDisplayDataSize = kDisplayWidth * kDisplayHeight;
+
+ILI9341_t3n tft = ILI9341_t3n(TFT_CS, TFT_DC, TFT_RST);
+uint16_t internal_framebuffer[kDisplayDataSize];
+
+} // namespace
+
+uint16_t* GetInternalFramebuffer() { return internal_framebuffer; }
+
+void Init() {
+ for (int i = 0; i < 320 * 240; i++) {
+ internal_framebuffer[i] = 0x0726;
+ }
+ // SPI Clock: 40MHz writes & 20MHz reads.
+ // Lower write speed to 30MHz if the display doesn't work.
+ tft.begin(30000000u, 2000000);
+ tft.setFrameBuffer(internal_framebuffer);
+ tft.useFrameBuffer(1);
+ tft.setRotation(3);
+ tft.setCursor(0, 0);
+ tft.updateScreen();
+}
+
+int GetWidth() { return tft.width(); }
+int GetHeight() { return tft.height(); }
+
+void Update(pw::framebuffer::FramebufferRgb565* frame_buffer) {
+ tft.updateScreen();
+}
+
+bool TouchscreenAvailable() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::display
diff --git a/pw_touchscreen_null/BUILD.gn b/pw_touchscreen_null/BUILD.gn
new file mode 100644
index 0000000..d3730e3
--- /dev/null
+++ b/pw_touchscreen_null/BUILD.gn
@@ -0,0 +1,22 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+
+pw_source_set("pw_touchscreen_null") {
+ deps = [ "$dir_pw_touchscreen:pw_touchscreen.facade" ]
+ sources = [ "touchscreen.cc" ]
+}
diff --git a/pw_touchscreen_null/touchscreen.cc b/pw_touchscreen_null/touchscreen.cc
new file mode 100644
index 0000000..951e60a
--- /dev/null
+++ b/pw_touchscreen_null/touchscreen.cc
@@ -0,0 +1,37 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_touchscreen/touchscreen.h"
+
+#include <cinttypes>
+
+#include "pw_coordinates/vec_int.h"
+
+namespace pw::touchscreen {
+
+void Init() {}
+
+bool Available() { return false; }
+
+bool NewTouchEvent() { return false; }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ point.x = 0;
+ point.y = 0;
+ point.z = 0;
+ return point;
+}
+
+} // namespace pw::touchscreen
diff --git a/pw_touchscreen_teensy_stmpe610/BUILD.gn b/pw_touchscreen_teensy_stmpe610/BUILD.gn
new file mode 100644
index 0000000..116ee92
--- /dev/null
+++ b/pw_touchscreen_teensy_stmpe610/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+
+if (pw_arduino_build_CORE_PATH != "") {
+ # Get c/c++ files for Adafruit_STMPE610 Arduino Library
+ _library_args = [
+ "--library-path",
+ rebase_path(
+ "$pw_arduino_build_CORE_PATH/teensy/hardware/teensy/avr/libraries"),
+ "--library-names",
+
+ "Adafruit_STMPE610",
+ ]
+ _library_c_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-c-files" ],
+ "list lines")
+ _library_cpp_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-cpp-files" ],
+ "list lines")
+
+ # Get include directories
+ _library_include_only_args = [
+ "--library-path",
+ rebase_path(
+ "$pw_arduino_build_CORE_PATH/teensy/hardware/teensy/avr/libraries"),
+ "--library-names",
+ "SPI",
+ "Wire",
+ "Adafruit_STMPE610",
+ ]
+ _library_include_dirs =
+ exec_script(arduino_builder_script,
+ arduino_show_command_args + _library_include_only_args +
+ [ "--library-include-dirs" ],
+ "list lines")
+ pw_source_set("pw_touchscreen_teensy_stmpe610") {
+ deps = [
+ "$dir_pw_log",
+ "$dir_pw_third_party/arduino:arduino_core_sources",
+ "$dir_pw_touchscreen:pw_touchscreen.facade",
+ ]
+ sources = [ "touchscreen.cc" ] + _library_c_files + _library_cpp_files
+ include_dirs = _library_include_dirs
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+ }
+}
diff --git a/pw_touchscreen_teensy_stmpe610/touchscreen.cc b/pw_touchscreen_teensy_stmpe610/touchscreen.cc
new file mode 100644
index 0000000..1b72f12
--- /dev/null
+++ b/pw_touchscreen_teensy_stmpe610/touchscreen.cc
@@ -0,0 +1,81 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_touchscreen/touchscreen.h"
+
+#include <Arduino.h>
+#include <SPI.h>
+#include <Wire.h>
+
+#include <cinttypes>
+
+#include "Adafruit_STMPE610.h"
+#include "pw_coordinates/vec_int.h"
+#include "pw_log/log.h"
+
+namespace pw::touchscreen {
+namespace {
+
+constexpr int kTouchscreenMinX = 288;
+constexpr int kTouchscreenMaxX = 3715;
+constexpr int kTouchscreenMinY = 350;
+constexpr int kTouchscreenMaxY = 3800;
+
+constexpr int ScreenPixelWidth = 320;
+constexpr int ScreenPixelHeight = 240;
+
+// I2C Usage
+Adafruit_STMPE610 touch_screen = Adafruit_STMPE610();
+
+// I2C Pins;
+// SCL: Teensy Pin 19, stm32f429i-disc1 PA8
+// SDA: Teensy Pin 18, stm32f429i-disc1 PC9
+// INT: stm32f429i-disc1 PA15
+// Note: No interrupt support using the Adafruit_STMPE610 library.
+
+// Hardware SPI Usage:
+// Adafruit_STMPE610 touch_screen Adafruit_STMPE610(uint8_t cs);
+// Software SPI Usage:
+// Adafruit_STMPE610 touch_screen Adafruit_STMPE610(uint8_t cspin,
+// uint8_t mosipin,
+// uint8_t misopin,
+// uint8_t clkpin);
+
+} // namespace
+
+void Init() { touch_screen.begin(); }
+
+bool Available() { return true; }
+
+bool NewTouchEvent() { return touch_screen.touched(); }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ uint16_t x, y;
+ uint8_t z;
+ touch_screen.readData(&x, &y, &z);
+ point.x = map(x, kTouchscreenMinX, kTouchscreenMaxX, 0, ScreenPixelWidth);
+ point.y = map(y, kTouchscreenMinY, kTouchscreenMaxY, 0, ScreenPixelHeight);
+ point.z = z;
+ PW_LOG_DEBUG("Touch: x:%d, y:%d, z:%d, x:%d, y:%d, z:%d",
+ (int)x,
+ (int)y,
+ (int)z,
+ point.x,
+ point.y,
+ point.z);
+ return point;
+}
+
+} // namespace pw::touchscreen
diff --git a/pw_touchscreen_teensy_xpt2046/BUILD.gn b/pw_touchscreen_teensy_xpt2046/BUILD.gn
new file mode 100644
index 0000000..195a603
--- /dev/null
+++ b/pw_touchscreen_teensy_xpt2046/BUILD.gn
@@ -0,0 +1,64 @@
+# Copyright 2022 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_arduino_build/arduino.gni")
+import("$dir_pw_build/target_types.gni")
+
+if (pw_arduino_build_CORE_PATH != "") {
+ # Get c/c++ files for XPT2046_Touchscreen Arduino Library
+ _library_args = [
+ "--library-path",
+ rebase_path(
+ "$pw_arduino_build_CORE_PATH/teensy/hardware/teensy/avr/libraries"),
+ "--library-names",
+
+ # Requires SPI.h
+ "XPT2046_Touchscreen",
+ ]
+ _library_c_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-c-files" ],
+ "list lines")
+ _library_cpp_files = exec_script(
+ arduino_builder_script,
+ arduino_show_command_args + _library_args + [ "--library-cpp-files" ],
+ "list lines")
+
+ # Get include directories for SPI and XPT2046_Touchscreen
+ _library_include_only_args = [
+ "--library-path",
+ rebase_path(
+ "$pw_arduino_build_CORE_PATH/teensy/hardware/teensy/avr/libraries"),
+ "--library-names",
+ "SPI",
+ "XPT2046_Touchscreen",
+ ]
+ _library_include_dirs =
+ exec_script(arduino_builder_script,
+ arduino_show_command_args + _library_include_only_args +
+ [ "--library-include-dirs" ],
+ "list lines")
+
+ pw_source_set("pw_touchscreen_teensy_xpt2046") {
+ deps = [
+ "$dir_pw_third_party/arduino:arduino_core_sources",
+ "$dir_pw_touchscreen:pw_touchscreen.facade",
+ ]
+ sources = [ "touchscreen.cc" ] + _library_c_files + _library_cpp_files
+ include_dirs = _library_include_dirs
+ remove_configs = [ "$dir_pw_build:strict_warnings" ]
+ }
+}
diff --git a/pw_touchscreen_teensy_xpt2046/touchscreen.cc b/pw_touchscreen_teensy_xpt2046/touchscreen.cc
new file mode 100644
index 0000000..d144ad1
--- /dev/null
+++ b/pw_touchscreen_teensy_xpt2046/touchscreen.cc
@@ -0,0 +1,55 @@
+// Copyright 2022 The Pigweed Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy of
+// the License at
+//
+// https://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+#include "pw_touchscreen/touchscreen.h"
+
+#include <Arduino.h>
+
+#include <cinttypes>
+
+#include "XPT2046_Touchscreen.h"
+#include "pw_coordinates/vec_int.h"
+
+namespace pw::touchscreen {
+namespace {
+
+constexpr int TS_CS = 7;
+constexpr int kTouchscreenMinX = 288;
+constexpr int kTouchscreenMaxX = 3715;
+constexpr int kTouchscreenMinY = 350;
+constexpr int kTouchscreenMaxY = 3800;
+
+constexpr int ScreenPixelWidth = 320;
+constexpr int ScreenPixelHeight = 240;
+
+XPT2046_Touchscreen touch_screen(TS_CS);
+
+} // namespace
+
+void Init() { touch_screen.begin(); }
+
+bool Available() { return true; }
+
+bool NewTouchEvent() { return touch_screen.touched(); }
+
+pw::coordinates::Vec3Int GetTouchPoint() {
+ pw::coordinates::Vec3Int point;
+ TS_Point p = touch_screen.getPoint();
+ point.x = map(p.x, kTouchscreenMinX, kTouchscreenMaxX, 0, ScreenPixelWidth);
+ point.y = map(p.y, kTouchscreenMinY, kTouchscreenMaxY, 0, ScreenPixelHeight);
+ point.z = p.z;
+ return point;
+}
+
+} // namespace pw::touchscreen
diff --git a/targets/arduino/target_toolchains.gni b/targets/arduino/target_toolchains.gni
index 5bf5b81..2a6cb86 100644
--- a/targets/arduino/target_toolchains.gni
+++ b/targets/arduino/target_toolchains.gni
@@ -1,4 +1,4 @@
-# Copyright 2021 The Pigweed Authors
+# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -38,6 +38,20 @@
# Configure backend for pw_spin_delay
pw_spin_delay_BACKEND = "$dir_pw_spin_delay_arduino"
+
+ # Configure backend for pw_display
+ # pw_display_null does nothing.
+ pw_display_BACKEND = "$dir_pw_display_null"
+
+ # See //pw_display_teensy_ili9341/README.md for instructions.
+ # pw_display_BACKEND = "$dir_pw_display_teensy_ili9341"
+
+ # Configure backend for pw_touchscreen
+ pw_touchscreen_BACKEND = "$dir_pw_touchscreen_null"
+
+ # These backends are for Teensy core builds.
+ # pw_touchscreen_BACKEND = "$dir_pw_touchscreen_teensy_xpt2046"
+ # pw_touchscreen_BACKEND = "$dir_pw_touchscreen_teensy_stmpe610"
}
}
diff --git a/targets/common_backends.gni b/targets/common_backends.gni
index 00c4edf..8dbabe5 100644
--- a/targets/common_backends.gni
+++ b/targets/common_backends.gni
@@ -1,4 +1,4 @@
-# Copyright 2021 The Pigweed Authors
+# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -20,12 +20,12 @@
pw_assert_BACKEND = dir_pw_assert_basic
# Configure the pw_log facade for Base64 tokenized logging.
- pw_log_BACKEND = dir_pw_log_tokenized
- pw_tokenizer_GLOBAL_HANDLER_WITH_PAYLOAD_BACKEND =
- "$dir_pw_log_tokenized:base64_over_hdlc"
+ # pw_log_BACKEND = dir_pw_log_tokenized
+ # pw_tokenizer_GLOBAL_HANDLER_WITH_PAYLOAD_BACKEND =
+ # "$dir_pw_log_tokenized:base64_over_hdlc"
# Alternately, configure pw_log for plain text logging
- # pw_log_BACKEND = dir_pw_log_basic
+ pw_log_BACKEND = dir_pw_log_basic
# Path to the nanopb installation. Defaults to included git module.
dir_pw_third_party_nanopb = "//third_party/nanopb"
diff --git a/targets/host/target_toolchains.gni b/targets/host/target_toolchains.gni
index c27aabc..439bda7 100644
--- a/targets/host/target_toolchains.gni
+++ b/targets/host/target_toolchains.gni
@@ -1,4 +1,4 @@
-# Copyright 2021 The Pigweed Authors
+# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -16,6 +16,7 @@
import("//targets/common_backends.gni")
import("$dir_pigweed/targets/host/target_toolchains.gni")
+import("$dir_pw_display_host_imgui/imgui.gni")
import("$dir_pw_protobuf_compiler/proto.gni")
import("$dir_pw_third_party/nanopb/nanopb.gni")
@@ -49,6 +50,18 @@
# Configure backend for pw_sys_io facade.
pw_sys_io_BACKEND = dir_pw_sys_io_stdio
+ # Configure backend for pw_display
+ # pw_display_null does nothing.
+ pw_display_BACKEND = "$dir_pw_display_null"
+
+ # pw_display_host_imgui uses imgui and opengl3
+ # See //pw_display_host_imgui/README.md for instructions.
+ if (dir_pw_third_party_imgui != "") {
+ pw_display_BACKEND = "$dir_pw_display_host_imgui"
+ }
+
+ pw_touchscreen_BACKEND = "$dir_pw_touchscreen_null"
+
pw_board_led_BACKEND = dir_pw_board_led_host
pw_spin_delay_BACKEND = dir_pw_spin_delay_host
}
diff --git a/targets/rp2040/BUILD.gn b/targets/rp2040/BUILD.gn
index ede06be..6f761f4 100644
--- a/targets/rp2040/BUILD.gn
+++ b/targets/rp2040/BUILD.gn
@@ -76,6 +76,16 @@
# Configure backend for pw_spin_delay
pw_spin_delay_BACKEND = "$dir_pw_spin_delay_pico"
+ # Configure backend for pw_display
+ # pw_display_null does nothing.
+ pw_display_BACKEND = "$dir_pw_display_null"
+
+ # pw_display_pico_ili9341 is for a ILI9341 screen connected over SPI.
+ # pw_display_BACKEND = "$dir_pw_display_pico_ili9341"
+
+ # Configure backend for pw_touchscreen
+ pw_touchscreen_BACKEND = "$dir_pw_touchscreen_null"
+
current_cpu = "arm"
current_os = ""
}
diff --git a/targets/stm32f429i_disc1_stm32cube/target_toolchains.gni b/targets/stm32f429i_disc1_stm32cube/target_toolchains.gni
index f8c9e61..c3dac58 100644
--- a/targets/stm32f429i_disc1_stm32cube/target_toolchains.gni
+++ b/targets/stm32f429i_disc1_stm32cube/target_toolchains.gni
@@ -1,4 +1,4 @@
-# Copyright 2020 The Pigweed Authors
+# Copyright 2022 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
@@ -90,6 +90,11 @@
pw_spin_delay_BACKEND = dir_pw_spin_delay_stm32cube
+ pw_display_BACKEND = dir_pw_display_stm32f429i_disc1_stm32cube_ili9341
+
+ # Configure backend for pw_touchscreen
+ pw_touchscreen_BACKEND = "$dir_pw_touchscreen_null"
+
dir_pw_third_party_stm32cube = dir_pw_third_party_stm32cube_f4
pw_third_party_stm32cube_PRODUCT = "STM32F429xx"
pw_third_party_stm32cube_CONFIG =