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