element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Raspberry Pi
  • Products
  • More
Raspberry Pi
Blog SHARP96 LCD Displays on a PI 2, Windows 10 IoT and a cool demo
  • Blog
  • Forum
  • Documents
  • Quiz
  • Events
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Raspberry Pi to participate - click to join for free!
Featured Articles
Announcing Pi
Technical Specifications
Raspberry Pi FAQs
Win a Pi
GPIO Pinout
Raspberry Pi Wishlist
Comparison Chart
Quiz
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Robert Peter Oakes
  • Date Created: 13 Aug 2015 8:43 AM Date Created
  • Views 1090 views
  • Likes 4 likes
  • Comments 4 comments
Related
Recommended
  • 430boost-sharp96
  • rpi2
  • raspberry-pi
  • raspberry_pi_2
  • raspberrypi
  • iot
  • ti

SHARP96 LCD Displays on a PI 2, Windows 10 IoT and a cool demo

Robert Peter Oakes
Robert Peter Oakes
13 Aug 2015

image

 

OK, so continuing  the series on PI 2 with windows 10 for IoT, I have now added to the previous app and created a demo app for a pair of Sharp 96*96 LCD displays

Again, there where no libraries for the boards. The LS013B4DN04 SPI display is the actual Sharp part and I wrote a library to drive as many of these as you have GPIOpins free on the PI, I then proceeded to put a cool UI ontop of it so I can play with the board under Windows 10.


Here is a video running through the demo app, I will follow up with a software overview and a technical look at the SPI buss as there are some interesting things I did to get things running fast.


You don't have permission to edit metadata of this video.
Edit media
x
image
Upload Preview
image


This is a view of the boards I used, you could just as easily use the Adafruit version without changing the software

imageimage


This is the strip board i made up for the demo, it has minimal wiring so no need to bother with a schematic

image

there are a total of 6 wires to the PI as shown below, Power control and LCD Enable both are tied to 3.3V, one display SPI CS goes to GPIO5, one to GPIO6


imageimage

probably the easies setup for the hardware yet image

So this is the UI I created in full technicolour image


image

The video explains it all but in summary

The two displays have a textbox overlay so you can input your own text and send it to the displays, the long slider will allow you to change the font size. While the display library will support multiple fonts sizes on the screen at once along with graphics elements, the demo does not show this (Yet). clicking wither of the "Update" buttons will send the display content to the respective SPI SHARP96 Display, the left one is connected to CS GPIO5, the right to GPIO6 (The code could be changed easily to use and available GPIO pin).

The fill 1's and 0's will fill a 2D array in memory for each display with all 1's or all 0's depending on the button. All 1's is actually the light grey colour, the 0's gives you a mirror basically as the reflective index of these displays is so high.


The demo button runs through a set of patterns and showcases some of the libraries capabilities

  • Write Pixel
  • Line vertical
  • Line Horizontal
  • Line any direction
  • Arcs
  • Circles (Optimised to only calc 45deg of the circle and still plot all of it. also only integer math involved)
  • Filled Rectangle
  • Write a string or Character supporting the \n \r


all the text can be started at any x,y co-ordinate, not just on a fixed boundary


This is the Libray code, it will also be attached along with the rest of the solution

using System;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.Spi;
using Windows.Devices.Gpio;


namespace SHARP96Driver
{
    public class sharp96Display
    {
        public string DefaultText ;
        public static UInt16 LCD_VERTICAL_MAX = 96; // Y
        public static UInt16 LCD_HORIZONTAL_MAX = 96; // X
        public static int SHARP_BLACK = 0x00;
        public static int SHARP_WHITE = 0xFF;
        public GpioPin PIN;
        public byte cursorX;
        public byte cursorY;
        public byte[,] DisplayBuffer; // A working pixel buffer for your code
        public sharp96Display(string defaultText, int ulValue,  int pin)
        {
            //LCD_VERTICAL_MAX = 96;
            //LCD_HORIZONTAL_MAX = 96;
            //SHARP_BLACK = 0x00;
            //SHARP_WHITE = 0xFF;
            PIN = Sharp96.InitGPIO(pin, GpioPinDriveMode.Output, GpioPinValue.Low);
            cursorX = 0;
            cursorY = 0;
            DisplayBuffer = new byte[LCD_VERTICAL_MAX, LCD_HORIZONTAL_MAX / 8]; // A working pixel buffer for your code
            Sharp96.Sharp96x96_InitializeDisplayBuffer(this, (ulValue==0)? SHARP_BLACK: SHARP_WHITE) ;
            DefaultText = defaultText;
            Sharp96.write(this, defaultText.ToCharArray(), 1, (ulValue==0) ? SHARP_WHITE : SHARP_BLACK);
            if (PIN != null) Sharp96.Sharp96x96_Flush(this);
        }
    }


    public static class Sharp96
    {
        //*****************************************************************************
        //1.3-inch screen has 96x96 resolution (9216 pixels stripe array)
        // LCD Screen Dimensions
        //*****************************************************************************
        public const UInt16 LCD_VERTICAL_MAX = 96;  // Y
        public const UInt16 LCD_HORIZONTAL_MAX = 96; // X


        private static bool AutoWrap = true;
        //*****************************************************************************
        // for the Display Driver
        //*****************************************************************************
        public const int SHARP_BLACK = 0x00;
        public const int SHARP_WHITE = 0xFF;
        private const int SHARP_SEND_TOGGLE_VCOM_COMMAND = 0x01;
        private const int SHARP_SKIP_TOGGLE_VCOM_COMMAND = 0x00;
        private const int SHARP_LCD_TRAILER_BYTE = 0x00;
        private const int SHARP_VCOM_TOGGLE_BIT = 0x40;
        private const int SHARP_LCD_CMD_CHANGE_VCOM = 0x00;
        private const int SHARP_LCD_CMD_CLEAR_SCREEN = 0x20;
        private const int SHARP_LCD_CMD_WRITE_LINE = 0x80;
        public static byte[] reverse_data = { 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E, 0x01, 0x09, 0x05, 0x0D, 0x03, 0x0B, 0x07, 0x0F };
        private static int VCOMbit = 0x40;
        private static int flagSendToggleVCOMCommand = 0;
        private const int ClrBlack = SHARP_BLACK;
        public struct Graphics_Rectangle
        {
            public int XMin;
            public int XMax;
            public int YMin;
            public int YMax;
        }
        //*****************************************************************************
        //RaspBerry Pi2  Parameters
        //*****************************************************************************
        private const string SPI_CONTROLLER_NAME = "SPI0";  /* For Raspberry Pi 2, use SPI0                             */
        private const Int32 SPI_CHIP_SELECT_LINE = 0;       /* Line 0 maps to physical pin number 24 on the Rpi2, line 1 to pin 26        */


        private static byte[] writeBuffer2 = new byte[2];// for simple comands like CLS or VCON
        private static byte[] writeBuffer1346 = new byte[1346];// for writing a buffer to the screen
        private static byte[] readBuffer4 = new byte[4]; /*this is defined to hold the output data*/
        private static byte[] writeBuffer4 = new byte[4];//register, then 16 bit value


        private static SpiDevice SpiGPIO;
        public static async Task InitSPI()
        {
            try
            {
                var settings = new SpiConnectionSettings(SPI_CHIP_SELECT_LINE);
                settings.ClockFrequency = 5000000;// 500kHz;
                settings.Mode = SpiMode.Mode0; //Mode0,1,2,3;  MCP23S17 needs mode 0


                string spiAqs = SpiDevice.GetDeviceSelector(SPI_CONTROLLER_NAME);
                var deviceInfo = await DeviceInformation.FindAllAsync(spiAqs);
                SpiGPIO = await SpiDevice.FromIdAsync(deviceInfo[0].Id, settings);
            }


            /* If initialization fails, display the exception and stop running */
            catch (Exception ex)
            {
                //statusText.Text = "\nSPI Initialization Failed";
            }
        }
        public static GpioPin InitGPIO(int GPIOpin, GpioPinDriveMode mode, GpioPinValue HiLow )
        {
            var gpio = GpioController.GetDefault();
            // Show an error if there is no GPIO controller
            if (gpio == null)
            {
                return null;
            }


            var pin = gpio.OpenPin(GPIOpin);


            if (pin == null)
            {
                return null;
            }
            pin.SetDriveMode(mode);
            pin.Write(HiLow);
            return pin;
        }


        //*******************************************************************************
        //
        //! Reverses the bit order.- Since the bit reversal function is called
        //! frequently by the several driver function this function is implemented
        //! to maximize code execution
        //
        // { 0x00,0x08,0x04,0x0C,0x02,0x0A,0x06,0x0E,0x01,0x09,0x05,0x0D,0x03,0x0B,0x07,0x0F }
        //
        //*******************************************************************************
        static byte Sharp96x96_reverse(byte x)
        {
            byte b = 0;


            b = (byte)(reverse_data[x & 0xF] << 4);
            b |= reverse_data[(x & 0xF0) >> 4];
            return b;
        }


        //*****************************************************************************
        //
        //! Initialize DisplayBuffer.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //!
        //! \param ulValue is the foreground color of the buffered data.
        //!
        //! This function initializes the display buffer and discards any cached data.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_InitializeDisplayBuffer(sharp96Display display, int ulValue)
        {
            UInt16 i = 0, j = 0;
            for (i = 0; i < LCD_VERTICAL_MAX; i++)
                for (j = 0; j < (LCD_HORIZONTAL_MAX >> 3); j++)
                    display.DisplayBuffer[i, j] = (byte)ulValue;
        }


        //*****************************************************************************
        //
        //! Draws a pixel on the screen.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //! \param lX is the X coordinate of the pixel.
        //! \param lY is the Y coordinate of the pixel.
        //! \param ulValue is the color of the pixel.
        //!
        //! This function sets the given pixel to a particular color.  The coordinates
        //! of the pixel are assumed to be within the extents of the display.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_PixelDraw(sharp96Display display, UInt16 lX, UInt16 lY, int ulValue)
        {
            if ((lY >= LCD_VERTICAL_MAX) || (lX >= LCD_HORIZONTAL_MAX)) return;
            if (ClrBlack == ulValue)
            {
                display.DisplayBuffer[lY, lX >> 3] &= (byte)(~(0x80 >> (lX & 0x7)));
            }
            else
            {
                display.DisplayBuffer[lY, lX >> 3] |= (byte)((0x80 >> (lX & 0x7)));
            }
        }


        //*****************************************************************************
        //
        //! Draws a horizontal line.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //! \param lX1 is the X coordinate of the start of the line.
        //! \param lX2 is the X coordinate of the end of the line.
        //! \param lY is the Y coordinate of the line.
        //! \param ulValue is the color of the line.
        //!
        //! This function draws a horizontal line on the display.  The coordinates of
        //! the line are assumed to be within the extents of the display.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_LineDrawH(sharp96Display display, int lX1, int lX2, int lY, int ulValue)
        {
            // sanity check
            if(lX1 > lX2) { int temp = lX2; lX2 = lX1; lX1 = temp; }


                int xi = 0;
                int x_index_min = lX1 >> 3;
                int x_index_max = lX2 >> 3;
                int ucfirst_x_byte, uclast_x_byte;


                //calculate first byte
                //mod by 8 and shift this # bits
                ucfirst_x_byte = (byte)(0xFF >> (lX1 & 0x7));
                //calculate last byte
                //mod by 8 and shift this # bits
                uclast_x_byte = (byte)(0xFF << (7 - (lX2 & 0x7)));
            try
            {
                //check if more than one data byte
                if (x_index_min != x_index_max)
                {


                    //black pixels (clear bits)
                    if (ClrBlack == ulValue)
                    {
                        //write first byte
                        display.DisplayBuffer[lY, x_index_min] &= (byte)(~ucfirst_x_byte);


                        //write middle bytes
                        for (xi = x_index_min; xi < x_index_max - 1; xi++)
                        {
                            display.DisplayBuffer[lY, xi + 1] = 0x00;
                        }


                        //write last byte
                        display.DisplayBuffer[lY, xi + 1] &= (byte)(~uclast_x_byte);
                    }
                    //white pixels (set bits)
                    else
                    {
                        //write first byte
                        display.DisplayBuffer[lY, xi] |= (byte)(ucfirst_x_byte);


                        //write middle bytes
                        for (xi = x_index_min; xi < x_index_max - 1; xi++)
                        {
                            display.DisplayBuffer[lY, xi + 1] = 0xFF;
                        }


                        //write last byte
                        display.DisplayBuffer[lY, xi + 1] |= (byte)(uclast_x_byte);
                    }
                }
                //only one data byte
                else
                {
                    //calculate value of single byte
                    ucfirst_x_byte &= uclast_x_byte;


                    //draw black pixels (clear bits)
                    if (ClrBlack == ulValue)
                    {
                        display.DisplayBuffer[lY, xi] &= (byte)(~ucfirst_x_byte);
                    }
                    //white pixels (set bits)
                    else
                    {
                        display.DisplayBuffer[lY, xi] |= (byte)(ucfirst_x_byte);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
    }


        //*****************************************************************************
        //
        //! Draws a vertical line.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //! \param lX is the X coordinate of the line.
        //! \param lY1 is the Y coordinate of the start of the line.
        //! \param lY2 is the Y coordinate of the end of the line.
        //! \param ulValue is the color of the line.
        //!
        //! This function draws a vertical line on the display.  The coordinates of the
        //! line are assumed to be within the extents of the display.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_LineDrawV(sharp96Display display, int lX, int lY1, int lY2, int ulValue)
        {
            int yi = 0;
            int x_index = lX >> 3;
            int data_byte;


            //calculate data byte
            //mod by 8 and shift this # bits
            data_byte = (0x80 >> (lX & 0x7));


            //write data to the display buffer
            for (yi = lY1; yi <= lY2; yi++)
            {
                //black pixels (clear bits)
                if (ClrBlack == ulValue)
                {
                    display.DisplayBuffer[yi,x_index] &= (byte)(~data_byte);
                }
                //white pixels (set bits)
                else
                {
                    display.DisplayBuffer[yi,x_index] |= (byte)(data_byte);
                }
            }
        }


        //*****************************************************************************
        //
        //! Draws a line.
        //!
        //! \param context is a pointer to the drawing context to use.
        //! \param x1 is the X coordinate of the start of the line.
        //! \param y1 is the Y coordinate of the start of the line.
        //! \param x2 is the X coordinate of the end of the line.
        //! \param y2 is the Y coordinate of the end of the line.
        //!
        //! This function draws a line, utilizing Sharp96x96_LineDrawH() and
        //! Sharp96x96_LineDrawV() to draw the line as efficiently as possible.
        //! it proceeds using Bresenham's line drawing algorithm.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_drawLine(sharp96Display display, UInt16 x1, UInt16 y1, UInt16 x2, UInt16 y2, int ulValue)
            {
            UInt16 error, deltaX, deltaY;
            int yStep;
            bool SwapXY;


            // is this a vertical line as we can call an optimized routine for it
            if(x1 == x2)
            {
                Sharp96x96_LineDrawV(display, x1, y1, y2, ulValue);
                return;
            }
                    // is this a horizontal line as we can call an optimized routine for it
                    if (y1 == y2)
            {
                        Sharp96x96_LineDrawH(display, x1, y1, y2, ulValue);
                        return;
            }


            // Determine if the line is steep.  A steep line has more motion in the Y
            // direction than the X direction.
            if(((y2 > y1) ? (y2 - y1) : (y1 - y2)) > ((x2 > x1) ? (x2 - x1) : (x1 - x2)))
            {
                SwapXY = true;
            }
            else
            {
                SwapXY = false;
            }


            // If the line is steep, then swap the X and Y coordinates.
            if(SwapXY)
            {
                error = x1;
                x1 = y1;
                y1 = error;
                error = x2;
                x2 = y2;
                y2 = error;
            }


            //
            // If the starting X coordinate is larger than the ending X coordinate,
            // then swap the start and end coordinates.
            //
            if(x1 > x2)
            {
                error = x1;
                x1 = x2;
                x2 = error;
                error = y1;
                y1 = y2;
                y2 = error;
            }
            // Compute the difference between the start and end coordinates in each axis.
            deltaX = (UInt16)(x2 - x1);
            deltaY = (UInt16)((y2 > y1) ? (y2 - y1) : (y1 - y2));


            // Initialize the error term to negative half the X delta.
            error = (UInt16)(-deltaX / 2);


            // Determine the direction to step in the Y axis when required.
            if(y1<y2) yStep = 1;
            else yStep = -1;


            // Loop through all the points along the X axis of the line.
            for(; x1 <= x2; x1++)
            {
                // See if this is a steep line.
                if(SwapXY)
                {
                            // Plot this point of the line, swapping the X and Y coordinates.
                            Sharp96x96_PixelDraw(display, y1, x1, ulValue);
                }
                else
                {
                            // Plot this point of the line, using the coordinates as is.
                            Sharp96x96_PixelDraw(display, x1, y1, ulValue);
                        }
                        // Increment the error term by the Y delta.
                        error += deltaY;
                // See if the error term is now greater than zero.
                if(error > 0)
                {
                    // Take a step in the Y axis.
                    y1 = (UInt16)(y1 + yStep); // this could be a - or a + step
                    // Decrement the error term by the X delta.
                    error -= deltaX;
                }
            }
        }


        //*****************************************************************************
        //
        //! Fills a rectangle.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //! \param pRect is a pointer to the structure describing the rectangle.
        //! \param ulValue is the color of the rectangle.
        //!
        //! This function fills a rectangle on the display.  The coordinates of the
        //! rectangle are assumed to be within the extents of the display, and the
        //! rectangle specification is fully inclusive (in other words, both sXMin and
        //! sXMax are drawn, along with sYMin and sYMax).
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_RectFill(sharp96Display display, Graphics_Rectangle pRect, int ulValue)
        {
            // Bounds Checking, if there all out, simply return, if any are in then do what can be done
            if ((pRect.XMin >= LCD_HORIZONTAL_MAX) && (pRect.YMin >= LCD_VERTICAL_MAX) && (pRect.XMax >= LCD_HORIZONTAL_MAX) && (pRect.YMax >= LCD_VERTICAL_MAX)) return;
            if (pRect.XMin >= LCD_HORIZONTAL_MAX) pRect.XMin = LCD_HORIZONTAL_MAX-1;
            if (pRect.YMin >= LCD_VERTICAL_MAX) pRect.YMin = LCD_VERTICAL_MAX-1;
            if (pRect.XMax >= LCD_HORIZONTAL_MAX) pRect.XMax = LCD_HORIZONTAL_MAX-1;
            if (pRect.YMax >= LCD_VERTICAL_MAX) pRect.YMax = LCD_VERTICAL_MAX-1;
            int xi = 0;
            int yi = 0;
            int x_index_min = pRect.XMin >> 3;
            int x_index_max = pRect.XMax >> 3;
            byte ucfirst_x_byte, uclast_x_byte;




            //calculate first byte
            //mod by 8 and shift this # bits
            ucfirst_x_byte = (byte)(0xFF >> (pRect.XMin & 0x7));


            //calculate last byte
            //mod by 8 and shift this # bits
            uclast_x_byte = (byte)(0xFF << (7-(pRect.XMax & 0x7)));


            //check if more than one data byte
            if(x_index_min != x_index_max)
            {
         //write bytes
         for (yi = pRect.YMin; yi<= pRect.YMax; yi++)
         {
         //black pixels (clear bits)
         if(ClrBlack == ulValue)
         {
                        //write first byte
                        display.DisplayBuffer[yi, x_index_min] &= (byte)~ucfirst_x_byte;


         //write middle bytes
         for(xi = x_index_min+1; xi<x_index_max; xi++)
         {
                            display.DisplayBuffer[yi, xi] = 0x00;
         }


                        //write last byte
                        display.DisplayBuffer[yi, x_index_max] &= (byte)~uclast_x_byte;
                    }
         //white pixels (set bits)
         else
         {
                        //write first byte
                        display.DisplayBuffer[yi, x_index_min] |= (byte)ucfirst_x_byte;


         //write middle bytes
         for(xi = x_index_min+1; xi<x_index_max; xi++)
         {
                            display.DisplayBuffer[yi, xi] = 0xFF;
         }


                        //write last byte
                        display.DisplayBuffer[yi, x_index_max] |= (byte)uclast_x_byte;
         }
         }   
         }
         //only one data byte
         else
         {
         //calculate value of single byte
         ucfirst_x_byte &= uclast_x_byte;


         //black pixels (clear bits)
         if(ClrBlack == ulValue)
         {
                    //write bytes
                    for (yi = pRect.YMin; yi <= pRect.YMax; yi++)
                        display.DisplayBuffer[yi, x_index_min] &= (byte)~ucfirst_x_byte;
         }
         //white pixels (set bits)
         else
         {
                    for (yi = pRect.YMin; yi <= pRect.YMax; yi++)
                        display.DisplayBuffer[yi, x_index_min] |= (byte)ucfirst_x_byte;
         }
         }
        }


        //*****************************************************************************
        //
        //! Flushes any cached drawing operations.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //!
        //!
        //! This functions flushes any cached drawing operations to the display.  This
        //! is useful when a local frame buffer is used for drawing operations, and the
        //! flush would copy the local frame buffer to the display.
        //! it builds a byte array for the call for a single refresh operation
        //! for more info see http://www.sharpmemorylcd.com/resources/programming_memory_lcd_app_note.pdf
        //! \return None.
        //
        //*****************************************************************************
        public static async Task Sharp96x96_Flush(sharp96Display display)
        {
            byte xi = 0;
            byte yi = 0;
            //image update mode(1X000000b)
            int bufferIndex = 0;
            int command = SHARP_LCD_CMD_WRITE_LINE;


            //COM inversion bit
            command = command ^ VCOMbit;
            writeBuffer1346[bufferIndex++] = (byte)command;
            flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;


            for (yi = 0; yi < LCD_VERTICAL_MAX; yi++) // Vertical (y)
            {
                writeBuffer1346[bufferIndex++] = Sharp96x96_reverse((byte)(yi+1)) ; // Write row Address


                for (xi = 0; xi < (LCD_HORIZONTAL_MAX >> 3); xi++) // Horizontal  - should be 12 bytes for 96 bits
                {
                    writeBuffer1346[bufferIndex++] = display.DisplayBuffer[yi, xi];
                }
                writeBuffer1346[bufferIndex++] = SHARP_LCD_TRAILER_BYTE;
            }
            writeBuffer1346[bufferIndex] = SHARP_LCD_TRAILER_BYTE; // this is the last byte so it will overflow with a ++
            HAL_LCD_writeCommandOrData(display, writeBuffer1346);
        }


        //*****************************************************************************
        //
        //! Send command to clear screen.
        //!
        //! \param pvDisplayData is a pointer to the driver-specific data for this
        //! display driver.
        //! \param ulValue is the background color of the buffered data.
        //!
        //! This function sets every pixel to the background color.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_ClearScreen(sharp96Display display, int ulValue)
        {
            Sharp96x96_ClearScreen(display);


            if (ClrBlack == ulValue)
                Sharp96x96_InitializeDisplayBuffer(display, SHARP_BLACK);
            else
                Sharp96x96_InitializeDisplayBuffer(display, SHARP_WHITE);
            Sharp96x96_Flush(display);
        }
        public static void Sharp96x96_ClearScreen(sharp96Display display)
        {
            //clear screen mode(0X100000b)
            int command = SHARP_LCD_CMD_CLEAR_SCREEN;
            //COM inversion bit
            command = command ^ VCOMbit;


            writeBuffer2[0] = (byte)command;
            writeBuffer2[1] = SHARP_LCD_TRAILER_BYTE;
            HAL_LCD_writeCommandOrData(display, writeBuffer2);
            flagSendToggleVCOMCommand = SHARP_SKIP_TOGGLE_VCOM_COMMAND;
        }


        //*****************************************************************************
        //
        // Writes command or data to the LCD Driver
        //
        // \param ucCmdData is the 8 or 16 bit command to send to the LCD driver
        // Uses the SET_LCD_DATA macro
        //
        // \return None
        //
        //*****************************************************************************
        static void HAL_LCD_writeCommandOrData(sharp96Display display, byte[] command)
        {
            try
            {// ignoring real SS lines CS0 and CS1 as there wrong polarity
                display.PIN.Write(GpioPinValue.High);
                SpiGPIO.Write(command);
                display.PIN.Write(GpioPinValue.Low);
            }
            catch (Exception e)
            {
                throw new Exception(e.InnerException.Message); // Help Me !!!!
            }
        }


        //*****************************************************************************
        //
        //! Send toggle VCOM command.
        //!
        //! This function toggles the state of VCOM which prevents a DC bias from being
        //! built up within the panel.
        //!
        //! \return None.
        //
        //*****************************************************************************
        public static void Sharp96x96_SendToggleVCOMCommand(sharp96Display display)
        {
            VCOMbit ^= SHARP_VCOM_TOGGLE_BIT;


            if (SHARP_SEND_TOGGLE_VCOM_COMMAND == flagSendToggleVCOMCommand)
            {
                //clear screen mode(0X100000b)
                int command = SHARP_LCD_CMD_CHANGE_VCOM;
                //COM inversion bit
                command = command ^ VCOMbit;
                writeBuffer2[0] = (byte)command;
                writeBuffer2[1] = SHARP_LCD_TRAILER_BYTE;
                HAL_LCD_writeCommandOrData(display,writeBuffer2);
            }


            flagSendToggleVCOMCommand = SHARP_SEND_TOGGLE_VCOM_COMMAND;
        }


        public static void Arc(sharp96Display display, UInt16 lX, UInt16 lY, UInt16 Radius, UInt16 startAngle, UInt16 endAngle, int ulValue)
        {
            ArcEx(display, lX, lY, Radius, startAngle, endAngle, 0.1, ulValue);
        }
        public static void ArcEx(sharp96Display display, UInt16 lX, UInt16 lY, UInt16 Radius, UInt16 startAngle, UInt16 endAngle, double increment, int ulValue)
        {
            double startA = startAngle;
            double endA = endAngle;
            if (startA > 360) startA = 360;
            if (endA > 360) endA = 360;
            if (increment > 10) increment = 10.0;
            if (increment < 0.1) increment = 0.1;


            for (double i = startA; i < endA; i += increment)
            {
                double angle = i * System.Math.PI / 180;
                byte x = (byte)(lX + Radius * System.Math.Sin(angle));
                byte y = (byte)(lY - Radius * System.Math.Cos(angle));
                Sharp96x96_PixelDraw(display, x, y, ulValue);
            }
        }
        public static void DrawCircle(sharp96Display display, UInt16 x0, UInt16 y0, UInt16 radius, int ulValue)
        {
            // Based on https://en.wikipedia.org/wiki/Midpoint_circle_algorithm
            int x = radius;
            int y = 0;
            int decisionOver2 = 1 - x;   // Decision criterion divided by 2 evaluated at x=r, y=0


            while (x >= y)
            {
                Sharp96x96_PixelDraw(display, (UInt16)(x + x0), (UInt16)(y + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(y + x0), (UInt16)(x + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(-x + x0), (UInt16)(y + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(-y + x0), (UInt16)(x + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(-x + x0), (UInt16)(-y + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(-y + x0), (UInt16)(-x + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(x + x0), (UInt16)(-y + y0), ulValue);
                Sharp96x96_PixelDraw(display, (UInt16)(y + x0), (UInt16)(-x + y0), ulValue);
                y++;
                if (decisionOver2 <= 0)
                {
                    decisionOver2 += 2 * y + 1;   // Change in decision criterion for y -> y+1
                }
                else
                {
                    x--;
                    decisionOver2 += 2 * (y - x) + 1;   // Change for y -> y+1, x -> x-1
                }
            }
        }
        public static void write(sharp96Display display, char[] c, byte textsize, int ulValue)
        {
            for (int len = 0; len < c.Length; len++)
            {
                write(display, c[len], textsize, ulValue);
            }
        }
        public static void write(sharp96Display display, char c, byte textsize, int ulValue)
        {
            // bounds check
            if (display.cursorY >= LCD_VERTICAL_MAX) return; // were off the screen
            if (c == '\n') // do we have a new line, if so simply adjust cursor position
            {
                display.cursorY += (byte)(textsize * 8);   // next line based on font size
                display.cursorX = 0;               // back to  character 0
            }
            else if (c == '\r')
            {
                display.cursorX = 0;               // back to  character 0
            }
            else
            {
                drawChar(display, display.cursorX, display.cursorY, (byte)c, textsize, ulValue);
                display.cursorX += (byte)(textsize * 6);
                if (AutoWrap && (display.cursorX > (LCD_HORIZONTAL_MAX - textsize * 6)))
                {
                    display.cursorY += (byte)(textsize * 8);   // next line based on font size
                    display.cursorX = 0;               // back to  character 0
                }
            }
        }
        public static void drawChar(sharp96Display display, UInt16 x, UInt16 y,  byte c, UInt16 size, int ulValue)
        {
            if ((x >= LCD_HORIZONTAL_MAX) || (y >= LCD_VERTICAL_MAX) || ((x + 6 * size - 1) < 0) || ((y + 8 * size - 1) < 0))
                return;  // bounds checks


            for (byte i = 0; i < 6; i++) // 5*7 font + space
            {
                UInt16 line; // index into character font array
                if (i == 5) line = 0x0; // space between characters
                else line = DisplayFont.font[((c * 5) + i)];


                for (byte j = 0; j < 8; j++) // now process each bit of the character
                {// we have a bit and normal colour or no bit and inverted
                    if (
                        (((line & 0x1)==1) && (ulValue != 0)) || (((line & 0x1) == 0) && (ulValue == 0))
                        )       // do we have a bit and black pixels (clear bits)     
                    {
                        if (size == 1) // default size
                            Sharp96x96_PixelDraw(display, (UInt16)(x + i),(UInt16)(y + j), 0xFF);
                        else
                        {  // scaled up font
                            Graphics_Rectangle pRect;
                            pRect.XMin = x + (i * size);
                            pRect.YMin = y + (j * size);
                            pRect.XMax = pRect.XMin + size-1;
                            pRect.YMax = pRect.YMin + size-1;
                            Sharp96x96_RectFill(display, pRect, 0xFF);
                        }
                    }
                    else // no bit so clear it, we need to handle reverse colour at some point
                    {
                        if (size == 1) // default size
                            Sharp96x96_PixelDraw(display, (UInt16)(x + i),(UInt16)(y + j), 0x00);
                        else
                        {  // big size
                            Graphics_Rectangle pRect;
                            pRect.XMin = x + (i * size);
                            pRect.YMin = y + (j * size);
                            pRect.XMax = pRect.XMin + size-1;
                            pRect.YMax = pRect.YMin + size-1;
                            Sharp96x96_RectFill(display, pRect, 0x00);
                        }
                    }
                    line >>= 1; // next bit in the line
                }
            }
        }
        public static void setCursor(sharp96Display display, UInt16 x, UInt16 y)
        {
            // needs some bounds checking
            display.cursorX = (byte)x;
            display.cursorY = (byte)y;
        }
    }
}

I will this time leave it to the video to describe it, please feel free to post questions though.


UPDATE: 13th August 2015:- Minor update to the VCOM routine to prevent screen BIAS effect, I noticed it was not working so I fixed it. the problem was it flipflops the bit but as I was doing it twice for each tick due to two displays, each display never got the alterate bit so it did not work. I moved the bit to the Sharp96Display class to it is unique to each display and adapted the routing. Now working well.

Here is what happens without it, the right display (On the first image) is supposed to be an even gray, as you can see, the old image remains

the other image was the result of being left on all night but with the bug i found unfixed and the third image shows what you see when you put another image on the display, as you can see, there is still text from the previous text

imageimageimage

Now with the working code, the images will over a few minutes or more, fix themselves to no real harm done but somthing to pay attention to.

The attached code now contains the fix. image



At this point I want to thank TI and Adafruit for their great graphics linraries that where the inspiration for this code. The above code is heavily modified and adapted from anything I saw at either site but their code helped immensly in figuring out the algorithms and base set of needed functions. The above code will also drive both thier products so win win all arround.


The TI PDF can be found here: http://www.ti.com/lit/ug/slau553/slau553.pdf

Adafruits link here: https://www.adafruit.com/products/1393

Sharps Datasheet here http://www.sharpmemorylcd.com/resources/ls013b4dn04_application_info.pdf

and a really useful APP note here: http://www.sharpmemorylcd.com/resources/programming_memory_lcd_app_note.pdf

The one thing not mentioned clearly anywhere is the need to swap the bits of the addressing around before sending the commands to the display, all other bytes go as normal but the address does not... wierd but its sorted in the code.



Attachments:
SHARP96Driver.zip
  • Sign in to reply
  • Robert Peter Oakes
    Robert Peter Oakes over 10 years ago in reply to vish

    No, not that I am aware of, but if you are able to code in Python or C++ it should not be difficult to convert my code

     

    Peter

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • vish
    vish over 10 years ago

    Hi,

    is there a driver for raspbian available?

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 10 years ago

    Great post Peter.

     

    DAB

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • balearicdynamics
    balearicdynamics over 10 years ago

    Great post !

     

    Enrico

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube