Everything You Should Know About HyperDisplay

Pages
Contributors: Liquid Soulder
Favorited Favorite 5

Derive Custom Driver

While you might be totally content to use HyperDisplay as it is, hopefully people like you will add support for ever more displays whether on a traditional digital screen or in some more exotic medium (zen garden printer, anyone?). This last section is supposed to give you an idea about the interface that exists to do just that, which is in fact the whole reason the we created HyperDisplay in the first place.

Adding HD support for another display is very simple -- all you need to do is derive a child class from the HyperDisplay class and implement three functions:

  • a constructor
  • a function to set a single pixel at a particular location
  • a function that tells HD where the next color type will be in a color cycle.

If you want to try it out but don't have a display to use try making a virtual screen that prints X's and O's to the serial monitor.

SimpleBareNecessities.h

This class definition contains the bare minimum that is needed to make an instantiable HD child class, as well as comments about what to do and also optional improvements that you can make. Since the class inherits from HyperDisplay you'll have full access to all the features of HD.

language:c
class bareMinDerived : virtual public hyperdisplay{             // Using hyperdisplay as virtual will allow support for future I/O libraries that also inherit from hyperdisplay 
private:
protected:
public:

    // Constructor: at minimum pass in the size of the display 
    /*
        xSize: number of pixels in the x direction of the display
        ySize: number of pixels in the y direction of the display
    */
    bareMinDerived(uint16_t xSize, uint16_t ySize /* Additional Parameters */);                                             


    // getoffsetColor: allows hyperdisplay to use your custom color type
    /*
        base: the pointer to the first byte of the array that holds the color data
        numPixels: the number of pixels away from the beginning that the function should return the pointer to
    */
    color_t     getOffsetColor(color_t base, uint32_t numPixels);

    // hwPixel: the method by which hyperdisplay actually changes your screen
    /*
        x0, y0: the x and y coordinates at which to place the pixel. 0,0 is the upper-left corner of the screen, x is horizontal and y is vertical
        data: the pointer to where the color data is stored
        colorCycleLength: this indicates how many pixels worth of valid color data exist contiguously after the memory location pointed to by color.  
        startColorOffset: this indicates how many pixels to offset by from the color pointer to arrive at the actual color to display
    */
    void    hwpixel(hd_hw_extent_t x0, hd_hw_extent_t y0, color_t data = NULL, hd_colors_t colorCycleLength = 1, hd_colors_t startColorOffset = 0);

    // Additional hardware drawing functions
    /*
        There are additional hardware drawing functions beyond hwpixel. They are already implemented by default using
        hwpixel so they are not required in order to start drawing. However implementing them with more efficient 
        methods for your particular hardware can reduce overhead and speed up the drawing process.  

        In these functions the coordiantes x0, x1, y0, and y1 are always with respect to the hardware screen. (0,0) is the upper-left pixel
        The variables pertaining to color sequences (data, colorCycleLength, and startColorOffset) always have the same meaning as in hwpixel
        Additional variables will be described in the function prototype in bareMinimumDerivedClass.cpp
    */
    // void hwxline(uint16_t x0, uint16_t y0, uint16_t len, color_t data, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool goLeft = false);       
    // void hwyline(uint16_t x0, uint16_t y0, uint16_t len, color_t data, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool goUp = false);             
    // void hwrectangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, color_t data, bool filled = false, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool gradientVertical = false, bool reverseGradient = false);  
    // void hwfillFromArray(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t numPixels, color_t data);          

    // Additional optional implementations by the user:
    // ================================================

    // getCharInfo: you can create custom fonts without changing how printing functions work
    /*
        character: the byte-sized character to show on screen
        pchar: a pointer to a valid char_info_t object that needs to be filled out peroperly for the given character
    */
    // void getCharInfo(uint8_t character, char_info_t * pchar);       


    // write: you decide what happens when someone calls bareMinDerived.print or bareMinDerived.println
    /*
        val: the byte-sized character value to display
    */
    // size_t write(uint8_t val);                                     
};

SimpleBareNecessities.cpp

This .cpp file contains templates for the methods declared in the 'bareminDerived' class. If you fill out these methods you'll have a working HyperDisplay driver.

language:c
#include "bareNecessities.h"


// Constructor: at minimum pass in the size of the display 
/*
    xSize: number of pixels in the x direction of the display
    ySize: number of pixels in the y direction of the display

    *Note:
    This notation allows you to explicitly state what variables are passed to the parent class's constructor when the derived class' constructor is called.
    Additional direct or virtual base classes can also be initialized by a comma separated list with the same syntax - the 'deepest' base class is listed first.
*/
bareMinDerived::bareMinDerived(uint16_t xSize, uint16_t ySize /* Additional Parameters */) : hyperdisplay(xSize, ySize) /* , anotherVirtualBaseClass(params), aDirectBaseClass(moreParams) */
{
    // Perform setup of the derived class with any additional parameters here.
}



// getoffsetColor: allows hyperdisplay to use your custom color type
/*
    base: the pointer to the first byte of the array that holds the color data
    numPixels: the number of pixels away from the beginning that the function should return the pointer to
*/
color_t     bareMinDerived::getOffsetColor(color_t base, uint32_t numPixels)
{
    // This method is requried so that your color type can be totally flexible - be it an enumeration of three colors for an E-ink
    // display or a structure of bytes for 24-bit color it is totally up to you and how your display works.

    // This implementation will depend on how you choose to store colors, however one decent way to do it is provided as a reference:
    // This function returns an offset pointer according to the number of pixels and the _colorMode of the object

    // color_t pret = NULL;                                     
    // your_color_type * ptemp = (your_color_type *)base;   // Here's the magic. Cast the base pointer to a pointer of your color type to allow pointer arithmetic
    // pret = (color_t)(ptemp + numPixels);                 // The offset by the number of pixels. This will account for the number of bytes that your color type occupies
    // return pret;                                         // And return the offset pointer
}

// hwPixel: the method by which hyperdisplay actually changes your screen
/*
    x0, y0: the x and y coordinates at which to place the pixel. 0,0 is the upper-left corner of the screen, x is horizontal and y is vertical
    data: the pointer to where the color data is stored
    colorCycleLength: this indicates how many pixels worth of valid color data exist contiguously after the memory location pointed to by color.  
    startColorOffset: this indicates how many pixels to offset by from the color pointer to arrive at the actual color to display
*/
void        bareMinDerived::hwpixel(hd_hw_extent_t x0, hd_hw_extent_t y0, color_t data, hd_colors_t colorCycleLength, hd_colors_t startColorOffset)
{
    // Here you write the code that sets a pixel. It is up to you what to do with that data. Here are two basic options:

    // 1) Write directly to display ram: if you choose this option and your display supports it then this is all you need to show an image
    // 2) Write to a scratch space: you might use this option to compose a whole image and then show it all on the screen at once. 
    //      In that case you would need your own function that actually gets all that information to the display when the time is right.
}

// Additional hardware drawing functions
/*
    There are additional hardware drawing functions beyond hwpixel. They are already implemented by default using
    hwpixel so they are not required in order to start drawing. However implementing them with more efficient 
    methods for your particular hardware can reduce overhead and speed up the drawing process.  

    In these functions the coordiantes x0, x1, y0, and y1 are always with respect to the hardware screen. (0,0) is the upper-left pixel
    The variables pertaining to color sequences (data, colorCycleLength, and startColorOffset) always have the same meaning as in hwpixel
    Additional variables will be described in the function prototype in bareMinimumDerivedClass.cpp
*/
// void bareMinDerived::hwxline(uint16_t x0, uint16_t y0, uint16_t len, color_t data, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool goLeft = false)    
// void bareMinDerived::hwyline(uint16_t x0, uint16_t y0, uint16_t len, color_t data, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool goUp = false);             
// void bareMinDerived::hwrectangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, color_t data, bool filled = false, uint16_t colorCycleLength = 1, uint16_t startColorOffset = 0, bool gradientVertical = false, bool reverseGradient = false);  
// void bareMinDerived::hwfillFromArray(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint32_t numPixels, color_t data);          

// Additional optional implementations by the user:
// ================================================

// getCharInfo: you can create custom fonts without changing how printing functions work
/*
    character: the byte-sized character to show on screen
    pchar: a pointer to a valid char_info_t object that needs to be filled out peroperly for the given character
*/
// void getCharInfo(uint8_t character, char_info_t * pchar);       


    // write: you decide what happens when someone calls bareMinDerived.print or bareMinDerived.println
/*
    val: the byte-sized character value to display
*/
// size_t write(uint8_t val);  

The Sketch

This sketch creates an instance of your derived display. You can use it to see if everything worked out.

language:c
/*
  HyperDisplay Example 1:  simpleBareNecessities 
  By: Owen Lye
  SparkFun Electronics
  Date: August 17, 2018
  License: This code is public domain but you buy me a beer 
  if you use this and we meet someday (Beerware license).

  Don't expect too much from this code: it just prints a nice message to the serial terminal...
  However it demonstrates instantiation of a class derived from the HyperDisplay library. Once
  you implement the functions:
  - getOffsetColor(color_t base, uint32_t numPixels);
  - hwpixel(uint16_t x0, uint16_t y0, color_t data, uint16_t colorCycleLength, uint16_t startColorOffset);

  Then you will be able to use all these standardized functions (and more to come in the near future!)
  - xline
  - yline
  - rectangle
  - fillFromArray
  - fillWindow
  - line
  - polygon
  - circle

*/

#include "bareNecessities.h"  // Click here to get the library: http://librarymanager/SparkFun_HyperDisplay

#define NUM_PIX_X 64
#define NUM_PIX_Y 64

// Note: this won't make any displays work because the functions are not implemented, however it shows that this implementation is instantiable
bareMinDerived myMinimalDisplay(NUM_PIX_X, NUM_PIX_Y);

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("Example1: Simple Bare Necessities");
  Serial.println("Well, this is a good launch point in case you want to make your own class of displays underneath the hyperdisplay library. Have fun! :D");
}

void loop() {
  // put your main code here, to run repeatedly:
}