1.. _seed-0104: 2 3===================== 40104: Display Support 5===================== 6.. seed:: 7 :number: 104 8 :name: Display Support 9 :status: Accepted 10 :proposal_date: 2023-06-12 11 :cl: 150793 12 :authors: Chris Mumford 13 :facilitator: Anthony DiGirolamo 14 15------- 16Summary 17------- 18Add support for graphics displays. This includes display drivers for a few 19popular display controllers, framebuffer management, and a framework to simplify 20adding a graphics display to a Pigweed application. 21 22---------- 23Motivation 24---------- 25Pigweed currently has no specific support for a display device. Projects that 26require a display currently must do the full implementation, including the 27display driver in most instances, to add display support. 28 29This proposes the addition of a basic framework for display devices, as well 30as implementations for a few common Pigweed test devices - specifically the 31STM32F429I. This enables developers to quickly and easily add display support 32for supported devices and an implementation to model when adding new device 33support. 34 35-------- 36Proposal 37-------- 38This proposes no changes to existing modules, but suggests several new libraries 39all within a single new module titled ``pw_display`` that together define a 40framework for rendering to displays. 41 42 43New Libraries 44============= 45.. list-table:: 46 :widths: 5 45 47 :header-rows: 1 48 49 * - Library 50 - Function 51 52 * - pw_display/display 53 - Manage draw thread, framebuffers, and driver 54 55 * - pw_display/driver 56 - Display driver interface definition 57 58 * - pw_display/pixel_pusher 59 - Transport of pixel data to display controller 60 61 * - pw_display/drivers/ili9341 62 - Display driver for the ILI9341 display controller 63 64 * - pw_display/drivers/imgui 65 - Host display driver using `Dear ImGui <https://www.dearimgui.com/>`_ 66 67 * - pw_display/drivers/mipi 68 - Display driver for `MIPI DSI <https://www.mipi.org/specifications/dsi>`_ controllers 69 70 * - pw_display/drivers/null 71 - Null display driver for headless devices 72 73 * - pw_display/drivers/st7735 74 - Display driver for the `ST7735 <https://www.displayfuture.com/Display/datasheet/controller/ST7735.pdf>`_ display controller 75 76 * - pw_display/drivers/st7789 77 - Display driver for the ST7789 display controller 78 79 * - pw_display/draw 80 - Very basic drawing library for test and bring-up purposes 81 82 * - pw_display/framebuffer 83 - Manage access to pixel buffer. 84 85 * - pw_display/framebuffer_mcuxpresso 86 - Specialization of the framebuffer for the MCUxpresso devices 87 88 * - pw_display/geometry 89 - Basic shared math types such as 2D vectors, etc. 90 91 92Geometry 93======== 94``pw_display/geometry`` contains two helper structures for common values usually 95used as a pair. 96 97.. code-block:: cpp 98 99 namespace pw::display { 100 101 template <typename T> 102 struct Size { 103 T width; 104 T height; 105 }; 106 107 template <typename T> 108 struct Vector2 { 109 T x; 110 T y; 111 }; 112 113 } // namespace pw::display 114 115 116Framebuffer 117=========== 118A framebuffer is a small class that provides access to a pixel buffer. It 119keeps a copy of the pixel buffer metadata and provides accessor methods for 120those values. 121 122.. code-block:: cpp 123 124 namespace pw::display { 125 126 enum class PixelFormat { 127 None, 128 RGB565, 129 }; 130 131 class Framebuffer { 132 public: 133 // Construct a default invalid framebuffer. 134 Framebuffer(); 135 136 Framebuffer(void* data, 137 PixelFormat pixel_format, 138 pw::math::Size<uint16_t> size, 139 uint16_t row_bytes); 140 141 Framebuffer(const Framebuffer&) = delete; 142 Framebuffer(Framebuffer&& other); 143 144 Framebuffer& operator=(const Framebuffer&) = delete; 145 Framebuffer& operator=(Framebuffer&&); 146 147 bool is_valid() const; 148 149 pw::ConstByteSpan data() const; 150 pw::ByteSpan data(); 151 152 PixelFormat pixel_format() const; 153 154 pw::math::Size<uint16_t> size(); 155 156 uint16_t row_bytes() const; 157 }; 158 159 } // namespace pw::display 160 161FrameBuffer is a moveable class that is intended to signify read/write 162privileges to the underlying pixel data. This makes it easier to track when the 163pixel data may be read from, or written to, without conflict. 164 165The framebuffer does not own the underlying pixel buffer. In other words 166the deletion of a framebuffer will not free the underlying pixel data. 167 168Framebuffers do not have methods for reading or writing to the underlying pixel 169buffer. This is the responsibility of the the selected graphics library which 170can be given the pixel buffer pointer retrieved by calling ``data()``. 171 172.. code-block:: cpp 173 174 constexpr size_t kWidth = 64; 175 constexpr size_t kHeight = 32; 176 uint16_t pixel_data[kWidth * kHeight]; 177 178 void DrawScreen(Framebuffer* fb) { 179 // Clear framebuffer to black. 180 std::memset(fb->data(), 0, fb->height() * fb->row_bytes()); 181 182 // Set first pixel to white. 183 uint16_t* pixel_data = static_cast<uint16_t*>(fb->data()); 184 pixel_data[0] = 0xffff; 185 } 186 187 Framebuffer fb(pixel_data, {kWidth, kHeight}, 188 PixelFormat::RGB565, 189 kWidth * sizeof(uint16_t)); 190 DrawScreen(&fb); 191 192FramebufferPool 193=============== 194The FramebufferPool is intended to simplify the use of multiple framebuffers 195when multi-buffered rendering is being used. It is a collection of framebuffers 196which can be retrieved, used, and then returned to the pool for reuse. All 197framebuffers in the pool share identical attributes. A framebuffer that is 198returned to a caller of ``GetFramebuffer()`` can be thought of as "on loan" to 199that caller and will not be given to any other caller of ``GetFramebuffer()`` 200until it has been returned by calling ``ReleaseFramebuffer()``. 201 202.. code-block:: cpp 203 204 namespace pw::display { 205 206 class FramebufferPool { 207 public: 208 using BufferArray = std::array<void*, FRAMEBUFFER_COUNT>; 209 210 // Constructor parameters. 211 struct Config { 212 BufferArray fb_addr; // Address of each buffer in this pool. 213 pw::math::Size<uint16_t> dimensions; // width/height of each buffer. 214 uint16_t row_bytes; // row bytes of each buffer. 215 pw::framebuffer::PixelFormat pixel_format; 216 }; 217 218 FramebufferPool(const Config& config); 219 virtual ~FramebufferPool(); 220 221 uint16_t row_bytes() const; 222 223 pw::math::Size<uint16_t> dimensions() const; 224 225 pw::framebuffer::PixelFormat pixel_format() const; 226 227 // Return a framebuffer to the caller for use. This call WILL BLOCK until a 228 // framebuffer is returned for use. Framebuffers *must* be returned to this 229 // pool by a corresponding call to ReleaseFramebuffer. This function will only 230 // return a valid framebuffer. 231 // 232 // This call is thread-safe, but not interrupt safe. 233 virtual pw::framebuffer::Framebuffer GetFramebuffer(); 234 235 // Return the framebuffer to the pool available for use by the next call to 236 // GetFramebuffer. 237 // 238 // This may be called on another thread or during an interrupt. 239 virtual Status ReleaseFramebuffer(pw::framebuffer::Framebuffer framebuffer); 240 }; 241 242 } // namespace pw::display 243 244An example use of the framebuffer pool is: 245 246.. code-block:: cpp 247 248 // Retrieve a framebuffer for drawing. May block if pool has no framebuffers 249 // to issue. 250 FrameBuffer fb = framebuffer_pool.GetFramebuffer(); 251 252 // Draw to the framebuffer. 253 UpdateDisplay(&fb); 254 255 // Return the framebuffer to the pool for reuse. 256 framebuffer_pool.ReleaseFramebuffer(std::move(fb)); 257 258DisplayDriver 259============= 260A DisplayDriver is usually the sole class responsible for communicating with the 261display controller. Its primary responsibilities are the display controller 262initialization, and the writing of pixel data when a display update is needed. 263 264This proposal supports multiple heterogenous display controllers. This could be: 265 2661. A single display of any given type (e.g. ILI9341). 2672. Two ILI9341 displays. 2683. Two ILI9341 displays and a second one of a different type. 269 270Because of this approach the DisplayDriver is defined as an interface: 271 272.. code-block:: cpp 273 274 namespace pw::display { 275 276 class DisplayDriver { 277 public: 278 // Called on the completion of a write operation. 279 using WriteCallback = Callback<void(framebuffer::Framebuffer, Status)>; 280 281 virtual ~DisplayDriver() = default; 282 283 virtual Status Init() = 0; 284 285 virtual void WriteFramebuffer(pw::framebuffer::Framebuffer framebuffer, 286 WriteCallback write_callback) = 0; 287 288 virtual pw::math::Size<uint16_t> size() const = 0; 289 }; 290 291 } // namespace pw::display 292 293Each driver then provides a concrete implementation of the driver. Below is the 294definition of the display driver for the ILI9341: 295 296.. code-block:: cpp 297 298 namespace pw::display { 299 300 class DisplayDriverILI9341 : public DisplayDriver { 301 public: 302 struct Config { 303 // Device specific initialization parameters. 304 }; 305 306 DisplayDriverILI9341(const Config& config); 307 308 // DisplayDriver implementation: 309 Status Init() override; 310 void WriteFramebuffer(pw::framebuffer::Framebuffer framebuffer, 311 WriteCallback write_callback) override; 312 Status WriteRow(span<uint16_t> row_pixels, 313 uint16_t row_idx, 314 uint16_t col_idx) override; 315 pw::math::Size<uint16_t> size() const override; 316 317 private: 318 enum class Mode { 319 kData, 320 kCommand, 321 }; 322 323 // A command and optional data to write to the ILI9341. 324 struct Command { 325 uint8_t command; 326 ConstByteSpan command_data; 327 }; 328 329 // Toggle the reset GPIO line to reset the display controller. 330 Status Reset(); 331 332 // Set the command/data mode of the display controller. 333 void SetMode(Mode mode); 334 // Write the command to the display controller. 335 Status WriteCommand(pw::spi::Device::Transaction& transaction, 336 const Command& command); 337 }; 338 339 } // namespace pw::display 340 341Here is an example retrieving a framebuffer from the framebuffer pool, drawing 342into the framebuffer, using the display driver to write the pixel data, and then 343returning the framebuffer back to the pool for use. 344 345.. code-block:: cpp 346 347 FrameBuffer fb = framebuffer_pool.GetFramebuffer(); 348 349 // DrawScreen is a function that will draw to the framebuffer's underlying 350 // pixel buffer using a drawing library. See example above. 351 DrawScreen(&fb); 352 353 display_driver_.WriteFramebuffer( 354 std::move(framebuffer), 355 [&framebuffer_pool](pw::framebuffer::Framebuffer fb, Status status) { 356 // Return the framebuffer back to the pool for reuse once the display 357 // write is complete. 358 framebuffer_pool.ReleaseFramebuffer(std::move(fb)); 359 }); 360 361In the example above that the framebuffer (``fb``) is moved when calling 362``WriteFramebuffer()`` passing ownership to the display driver. From this point 363forward the application code may not access the framebuffer in any way. When the 364framebuffer write is complete the framebuffer is then moved to the callback 365which in turn moves it when calling ``ReleaseFramebuffer()``. 366 367``WriteFramebuffer()`` always does a write of the full framebuffer - sending all 368pixel data. 369 370``WriteFramebuffer()`` may be a blocking call, but on some platforms the driver 371may use a background write and the write callback is called when the write 372is complete. The write callback **may be called during an interrupt**. 373 374PixelPusher 375=========== 376Pixel data for Simple SPI based display controllers can be written to the 377display controller using ``pw_spi``. There are some controllers which use 378other interfaces (RGB, MIPI, etc.). Also, some vendors provide an API for 379interacting with these display controllers for writing pixel data. 380 381To allow the drivers to be hardware/vendor independent the ``PixelPusher`` 382may be used. This defines an interface whose sole responsibility is to write 383a framebuffer to the display controller. Specializations of this will use 384``pw_spi`` or vendor proprietary calls to write pixel data. 385 386.. code-block:: cpp 387 388 namespace pw::display { 389 390 class PixelPusher { 391 public: 392 using WriteCallback = Callback<void(framebuffer::Framebuffer, Status)>; 393 394 virtual ~PixelPusher() = default; 395 396 virtual Status Init( 397 const pw::framebuffer_pool::FramebufferPool& framebuffer_pool) = 0; 398 399 virtual void WriteFramebuffer(framebuffer::Framebuffer framebuffer, 400 WriteCallback complete_callback) = 0; 401 }; 402 403 } // namespace pw::display 404 405Display 406======= 407Each display has: 408 4091. One and only one display driver. 4102. A reference to a single framebuffer pool. This framebuffer pool may be shared 411 with other displays. 4123. A drawing thread, if so configured, for asynchronous display updates. 413 414.. code-block:: cpp 415 416 namespace pw::display { 417 418 class Display { 419 public: 420 // Called on the completion of an update. 421 using WriteCallback = Callback<void(Status)>; 422 423 Display(pw::display_driver::DisplayDriver& display_driver, 424 pw::math::Size<uint16_t> size, 425 pw::framebuffer_pool::FramebufferPool& framebuffer_pool); 426 virtual ~Display(); 427 428 pw::framebuffer::Framebuffer GetFramebuffer(); 429 430 void ReleaseFramebuffer(pw::framebuffer::Framebuffer framebuffer, 431 WriteCallback callback); 432 433 pw::math::Size<uint16_t> size() const; 434 }; 435 436 } // namespace pw::display 437 438Once applications are initialized they typically will not directly interact with 439display drivers or framebuffer pools. These will be utilized by the display 440which will provide a simpler interface. 441 442``Display::GetFramebuffer()`` must always be called on the same thread and is not 443interrupt safe. It will block if there is no available framebuffer in the 444framebuffer pool waiting for a framebuffer to be returned. 445 446``Display::ReleaseFramebuffer()`` must be called for each framebuffer returned by 447``Display::GetFramebuffer()``. This will initiate the display update using the 448displays associated driver. The ``callback`` will be called when this update is 449complete. 450 451A simplified application rendering loop would resemble: 452 453.. code-block:: cpp 454 455 // Get a framebuffer for drawing. 456 FrameBuffer fb = display.GetFramebuffer(); 457 458 // DrawScreen is a function that will draw to |fb|'s pixel buffer using a 459 // drawing library. See example above. 460 DrawScreen(&fb); 461 462 // Return the framebuffer to the display which will be written to the display 463 // controller by the display's display driver. 464 display.ReleaseFramebuffer(std::move(fb), [](Status){}); 465 466Drawing Library 467=============== 468``pw_display/draw`` was created for testing and verification purposes only. It is 469not intended to be feature rich or performant in any way. This is small 470collection of basic drawing primitives not intended to be used by shipping 471applications. 472 473.. code-block:: cpp 474 475 namespace pw::display { 476 477 void DrawLine(pw::framebuffer::Framebuffer& fb, 478 int x1, 479 int y1, 480 int x2, 481 int y2, 482 pw::color::color_rgb565_t pen_color); 483 484 // Draw a circle at center_x, center_y with given radius and color. Only a 485 // one-pixel outline is drawn if filled is false. 486 void DrawCircle(pw::framebuffer::Framebuffer& fb, 487 int center_x, 488 int center_y, 489 int radius, 490 pw::color::color_rgb565_t pen_color, 491 bool filled); 492 493 void DrawHLine(pw::framebuffer::Framebuffer& fb, 494 int x1, 495 int x2, 496 int y, 497 pw::color::color_rgb565_t pen_color); 498 499 void DrawRect(pw::framebuffer::Framebuffer& fb, 500 int x1, 501 int y1, 502 int x2, 503 int y2, 504 pw::color::color_rgb565_t pen_color, 505 bool filled); 506 507 void DrawRectWH(pw::framebuffer::Framebuffer& fb, 508 int x, 509 int y, 510 int w, 511 int h, 512 pw::color::color_rgb565_t pen_color, 513 bool filled); 514 515 void Fill(pw::framebuffer::Framebuffer& fb, 516 pw::color::color_rgb565_t pen_color); 517 518 void DrawSprite(pw::framebuffer::Framebuffer& fb, 519 int x, 520 int y, 521 pw::draw::SpriteSheet* sprite_sheet, 522 int integer_scale); 523 524 void DrawTestPattern(); 525 526 pw::math::Size<int> DrawCharacter(int ch, 527 pw::math::Vector2<int> pos, 528 pw::color::color_rgb565_t fg_color, 529 pw::color::color_rgb565_t bg_color, 530 const FontSet& font, 531 pw::framebuffer::Framebuffer& framebuffer); 532 533 pw::math::Size<int> DrawString(std::wstring_view str, 534 pw::math::Vector2<int> pos, 535 pw::color::color_rgb565_t fg_color, 536 pw::color::color_rgb565_t bg_color, 537 const FontSet& font, 538 pw::framebuffer::Framebuffer& framebuffer); 539 540 } // namespace pw::display 541 542Class Interaction Diagram 543========================= 544.. mermaid:: 545 :alt: Framebuffer Classes 546 :align: center 547 548 classDiagram 549 class FramebufferPool { 550 uint16_t row_bytes() 551 PixelFormat pixel_format() 552 dimensions() : Size~uint16_t~ 553 row_bytes() : uint16_t 554 GetFramebuffer() : Framebuffer 555 556 BufferArray buffer_addresses_ 557 Size~uint16_t~ buffer_dimensions_ 558 uint16_t row_bytes_ 559 PixelFormat pixel_format_ 560 } 561 562 class Framebuffer { 563 is_valid() : bool const 564 data() : void* const 565 pixel_format() : PixelFormat const 566 size() : Size~uint16_t~ const 567 row_bytes() uint16_t const 568 569 void* pixel_data_ 570 Size~uint16_t~ size_ 571 PixelFormat pixel_format_ 572 uint16_t row_bytes_ 573 } 574 575 class DisplayDriver { 576 <<DisplayDriver>> 577 Init() : Status 578 WriteFramebuffer(Framebuffer fb, WriteCallback cb): void 579 dimensions() : Size~uint16_t~ 580 581 PixelPusher& pixel_pusher_ 582 } 583 584 class Display { 585 DisplayDriver& display_driver_ 586 const Size~uint16_t~ size_ 587 FramebufferPool& framebuffer_pool_ 588 589 GetFramebuffer() : Framebuffer 590 } 591 592 class PixelPusher { 593 Init() : Status 594 WriteFramebuffer(Framebuffer fb, WriteCallback cb) : void 595 } 596 597 <<interface>> DisplayDriver 598 FramebufferPool --> "FRAMEBUFFER_COUNT" Framebuffer : buffer_addresses_ 599 600 Display --> "1" DisplayDriver : display_driver_ 601 Display --> "1" FramebufferPool : framebuffer_pool_ 602 DisplayDriver --> "1" PixelPusher : pixel_pusher_ 603 604--------------------- 605Problem investigation 606--------------------- 607With no direct display support in Pigweed and no example programs implementing 608a solution Pigweed developers are essentially on their own. Depending on their 609hardware this means starting with a GitHub project with a sample application 610from MCUXpresso or STMCube. Each of these use a specific HAL and may be 611coupled to other frameworks, such as FreeRTOS. This places the burden of 612substituting the HAL calls with the Pigweed API, making the sample program 613with the application screen choice, etc. 614 615This chore is time consuming and often requires that the application developer 616acquire some level of driver expertise. Having direct display support in 617Pigweed will allow the developer to more quickly add display support. 618 619The primary use-case being targeted is an application with a single display 620using multiple framebuffers with display update notifications delivered during 621an interrupt. The initial implementation is designed to support multiple 622heterogenous displays, but that will not be the focus of development or testing 623for the first release. 624 625Touch sensors, or other input devices, are not part of this effort. Display 626and touch input often accompany each other, but to simplify this already large 627display effort, touch will be added in a separate activity. 628 629There are many other embedded libraries for embedded drawing. Popular libraries 630are LVGL, emWin, GUIslice, HAGL, µGFX, and VGLite (to just name a few). These 631existing solutions generally offer one or more of: display drivers, drawing 632library, widget library. The display drivers usually rely on an abstraction 633layer, which they often refer to as a HAL, to interface with the underlying 634hardware API. This HAL may rely on macros, or sometimes a structure with 635function pointers for specific operations. 636 637The approach in this SEED was selected because it offers a low level API focused 638on display update performance. It offers no drawing or GUI library, but should 639be easily interfaced with those libraries. 640 641--------------- 642Detailed design 643--------------- 644This proposal suggests no changes to existing APIs. All changes introduce new 645modules that leverage the existing API. It supports static allocation of the 646pixel buffers and all display framework objects. Additionally pixel buffers 647may be hard-coded addresses or dynamically allocated from SRAM. 648 649The ``Framebuffer`` class is intended to simplify code that interacts with the 650pixel buffer. It includes the pixel buffer format, dimensions, and the buffer 651address. The framebuffer is 16 bytes in size (14 when packed). Framebuffer 652objects are created when requested and moved as a means of signifying ownership. 653In other words, whenever code has an actual framebuffer object it is allowed 654to both write to and read from the pixel buffer. 655 656The ``FramebufferPool`` is an object intended to simplify the management of a 657collection of framebuffers. It tracks those that are available for use and 658loans out framebuffers when requested. For single display devices this is 659generally not a difficult task as the application would maintain an array of 660framebuffers and a next available index. In this case framebuffers are always 661used in order and the buffer collection is implemented as a queue. 662 663Because RAM is often limited, the framebuffer pool is designed to be shared 664between multiple displays. Because display rendering and update may be at 665different speeds framebuffers do not need to be retrieved 666(via ``GetFramebuffer()``) and returned (via ``ReleaseFramebuffer()``) in the same 667order. 668 669Whenever possible asynchronous display updates will be used. Depending on the 670implementation this usually offloads the CPU from the pixel writing to the 671display controller. In this case the CPU will initiate the update and using 672some type of notification, usually an interrupt raised by a GPIO pin connected 673to the display, will be notified of the completion of the display update. 674Because of this the framebuffer pool ``ReleaseFramebuffer()`` call is interrupt 675safe. 676 677``FramebufferPool::GetFramebuffer()`` will block indefinitely if no framebuffer 678is available. This unburdens the application drawing loop from the task of 679managing framebuffers or tracking screen update completion. 680 681Testing 682======= 683All classes will be accompanied by a robust set of unit tests. These can be 684run on the host or the device. Test applications will be able to run on a 685workstation (i.e. not an MCU) in order to enable tests that depend on 686hardware available in most CPUs - like an MMU. This will enable the use of 687`AddressSanitizer <https://github.com/google/sanitizers/wiki/AddressSanitizer>`_ 688based tests. Desktop tests will use 689`Xvfb <https://www.x.org/releases/X11R7.6/doc/man/man1/Xvfb.1.xhtml>`_ to allow 690them to be run in a headless continuous integration environment. 691 692Performance 693=========== 694Display support will include performance tests. Although this proposal does not 695include a rendering library, it will include support for specific platforms 696that will utilize means of transferring pixel data to the display controller 697in the background. 698 699------------ 700Alternatives 701------------ 702One alternative is to create the necessary port/HAL, the terminology varies by 703library, for the popular embedded graphics libraries. This would make it easier 704for Pigweed applications to add display support - bot only for those supported 705libraries. This effort is intended to be more focused on performance, which is 706not always the focus of other libraries. 707 708Another alternative is to do nothing - leaving the job of adding display 709support to the developers. As a significant percentage of embedded projects 710contain a display, it will beneficial to have built-in display support in 711Pigweed. This will allow all user to benefit by the shared display expertise, 712continuous integration, testing, and performance testing. 713 714-------------- 715Open questions 716-------------- 717 718Parameter Configuration 719======================= 720One open question is what parameters to specify in initialization parameters 721to a driver ``Init()`` function, which to set in build flags via ``config(...)`` 722in GN, and which to hard-code into the driver. The most ideal, from the 723perspective of reducing binary size, is to hard-code all values in a single 724block of contiguous data. The decision to support multiple displays requires 725that the display initialization parameters, at least some of them, be defined 726at runtime and cannot be hard-coded into the driver code - that is, if the 727goal is to allow two of the same display to be in use with different settings. 728 729Additionally many drivers support dozens of configuration values. The ILI9341 730has 82 different commands, some with complex values like gamma tables or 731multiple values packed into a single register. 732 733The current approach is to strike a balance where the most commonly set 734values, for example display width/height and pixel format, are configurable 735via build flags, and the remainder is hard-coded in the driver. If a developer 736wants to set a parameter that is currently hard-coded in the driver, for 737example display refresh rate or gamma table, they would need to copy the display 738driver from Pigweed, or create a Pigweed branch. 739 740``Display::WriteFramebuffer()`` always writes the full framebuffer. It is expected 741that partial updates will be supported. This will likely come as a separate 742function. This is being pushed off until needed to provide as much experience 743with the various display controller APIs as possible to increase the likelihood 744of a well crafted API. 745 746Module Hierarchy 747================ 748At present Pigweed's module structure is flat and at the project root level. 749There are currently 134 top level ``pw_*`` directories. This proposal could 750significantly increase this count as each new display driver will be a new 751module. This might be a good time to consider putting modules into a hierarchy. 752 753Pixel Pusher 754============ 755``PixelPusher`` was created to remove the details of writing pixels from the 756display driver. Many displays support multiple ways to send pixel data. For 757example the ILI9341 supports SPI and a parallel bus for pixel transport. 758The `STM32F429I-DISC1 <https://www.st.com/en/evaluation-tools/32f429idiscovery.html>`_ 759also has a display controller (`LTDC 760<https://www.st.com/resource/en/application_note/an4861-lcdtft-display-controller-ltdc-on-stm32-mcus-stmicroelectronics.pdf>`_) 761which uses an STM proprietary API. The ``PixelPusher`` was essentially created 762to allow that driver to use the LTDC API without the need to be coupled in any 763way to a vendor API. 764 765At present some display drivers use ``pw_spi`` to send commands to the display 766controller, and the ``PixelPusher`` for writing pixel data. It will probably 767be cleaner to move the command writes into the ``PixelPusher`` and remove any 768``pw_spi`` interaction from the display drivers. At this time ``PixelPusher`` 769should be renamed. 770 771Copyrighted SDKs 772================ 773Some vendors have copyrighted SDKs which cannot be included in the Pigweed 774source code unless the project is willing to have the source covered by more 775than one license. Additionally some SDKs have no simple download link and the 776vendor requires that a developer use a web application to build and download 777an SDK with the desired components. NXP's 778`MCUXpresso SDK Builder <https://mcuxpresso.nxp.com/en/welcome>`_ is an example 779of this. This download process makes it difficult to provide simple instructions 780to the developer and for creating reliable builds as it may be difficult to 781select an older SDK for download. 782