• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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