Main Site Documentation

Help With LCD


#1

I have a FEZ Cobra and a NewHaven 2.7" LCD hooked up, and I can’t tell if I’ve blown the screen, or I’m doing something wrong.

What I’d really really love is if someone could look over the LCD spec and my code and see if there’s anything glaringly obvious wrong, but that’s probably 30+ minutes of someone’s time.

Instead, I’d settle for a quick 1 minute scan of the code as a general does anything look crazy especially with regards to the pins or the pins i’m using or anything.

The code seems to physically execute, my logic probe seems to indicate that the CS/etc lines are at least toggling, and my LED starts blinking, but the LCD remains dark. I don’t know if I’m talking too fast, or I have the data MSB/LSB backwards or what.

The display is: http://www.newhavendisplay.com/nhd2712864ucy3-p-3621.html
The Spec sheet is: http://www.newhavendisplay.com/redirect.html?goto=www.newhavendisplay.com%2Fspecs%2FNHD-2.7-12864UCY3.pdf&action=url
The code I ported from is: http://www.newhavendisplay.com/app_notes/OLED_2_7_12864.txt
The resultant port is below.

Any look at all or comments would be beyond fantastic. I’d love to see if this screen is alive at all. Assume for the moment I have all my wires hooked up to the ports seen in init.

edit: Code Snipped


#2

I switched to SPI mode. About to find out how it goes…


#3

It’s alive!

edit: image attached, which btw is showing a checkerboard test pattern

MUAHAHAHAHAH :slight_smile:


#4

Nicely done. Please upload images instead of using links. Those external links will die in future.


#5

You’re just everywhere today (and very helpful).

My image was 2.3 MB and on this windows install I didn’t have any tools for resizing besides paint, so I figured the imgur would be ok. Noted for the future.

By the way, if anyone ever needs to talk to a SSD1325 (as is in a NH 2.7" OLED), the current iteration of the library for SPI is ported and attached, though I will be looking at RLP next weekend for the display write per your comment in my other thread. The non SPI code is tested and doesn’t work currently. I suspect it’s a write speed issue or something. SPI is better anyway- less pins and it can write the whole display about as fast as you can blink.

Clock.cs:


//#define LargeHeap

using System;
using System.Threading;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.Hardware;

using GHIElectronics.NETMF.FEZ;
using BTrotter_DotNetMF_Libs;

namespace BTrotter_DotNetMF_Clock
{
    public class Program
    {
        public static OutputPort led;
        public static InputPort btn;

        public static void btnWait()
        {
            // wait until unpressed
            while (!btn.Read())
            {
                Thread.Sleep(1);
            }
            // wait until pressed
            while (btn.Read())
            {
                Thread.Sleep(1);
            }
        }

        public static void Main()
        {
 #if LargeHeap
            // http://wiki.tinyclr.com/index.php?title=Custom_Heap
            // set the heap size to 4MB
            if (Configuration.Heap.SetCustomHeapSize(4 * 1024 * 1024))
            {
                // this will only take effect after resetting the system
                PowerState.RebootDevice(false);
            }
            // ... 
            // you can now use large objects up to 4MB
            // allocate a 1MB buffer
            //LargeBuffer lb = new LargeBuffer(1024 * 1024);
            // use the buffer
            //lb.Bytes[5] = 123;
            // ....
            // when done, dispose the object to empty the memory
            //lb.Dispose();
 #endif

            // no TFT
            GHIElectronics.NETMF.Hardware.Configuration.LCD.Set(GHIElectronics.NETMF.Hardware.Configuration.LCD.HeadlessConfig);

            led = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, false);
            btn = new InputPort((Cpu.Pin)GHIElectronics.NETMF.FEZ.FEZ_Pin.Digital.ButtonDown, true, Port.ResistorMode.PullUp);

            led.Write(false);
            NH2_7.initPorts();
            NH2_7.OLED_12864_Init();
            led.Write(true);


            Bitmap bmp = new Bitmap(128, 64);
            Font font = Resources.GetFont(Resources.FontResources.NinaB);
            bmp.DrawText("12:00", font, Microsoft.SPOT.Presentation.Media.Color.White, 0, 0);

            while (true)
            {
                NH2_7.Checkerboard_12864();
                btnWait();
                NH2_7.Grayscale_12864();
                btnWait();
                NH2_7.DrawBitmap(ref bmp);
                btnWait();
            }
        }

    }
}

NH2_7.cs:


 #define UseSPI

using System;
using Microsoft.SPOT;
using System.Threading;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation;
using GHIElectronics.NETMF.FEZ;

namespace BTrotter_DotNetMF_Libs
{
    public sealed class NH2_7
    {
        public const byte Max_Column = 0x3F;			// 128/2-1 (Total Columns Devided by 2)
        public const byte Max_Row = 0x3F;				// 64-1
        public const byte Brightness = 0x7F;

        //--------------------------------------------------------------------------
        // Core Hardware Support for FEZ Cobra
        //--------------------------------------------------------------------------
 #if UseSPI
        /**
         * 1 -> ground
         * 2 -> VDD
         * 3 NC
         * 4- CMD/DATA (0/1)  IO49
         * 5,6 -> must ground
         * 7 -> SCLK  IO27
         * 8 -> SDIN  IO24
         * 10-14 -> optional(?) ground
         * 16 -> /res IO28
         * 17 -> /CS  IO50
         * 18 NC
         * 19 -> must ground
         * 20 -> must ground
         * 
         * rising edge of SCLK
         * positive logic
         * MSB first
         */
        public static SPI spi;
        public static OutputPort spiCMD_Data;
        public static OutputPort resControl; // active low

        public static void initPorts()
        {
            resControl = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO28, true); // default to asserted

            SPI.Configuration spiconfig = new SPI.Configuration(
                (Cpu.Pin)FEZ_Pin.Digital.IO50, // CS line
                false, // CS active low
                0, // CS setup time in ms
                0, // CS hold time (after data) in ms
                true, // clock idle high
                true, // sample on rising edge
                4000, // 4000 KHz = 4 MHz
                SPI.SPI_module.SPI1 // spans IO 24 => SPI out, 25 => SPI in, 27 => SCLK
            );
            spi = new SPI(spiconfig);

            spiCMD_Data = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO49, true); // default to data 
        }

        public static void Send(ref byte[] data, bool cmd)
        {
            spiCMD_Data.Write(!cmd); // 0 = command, 1 = data
            spi.Write(data);
        }

        public static void Send(byte data, bool cmd)
        {
            spiCMD_Data.Write(!cmd); // 0 = command, 1 = data
            spi.Write(new byte[] { data });
        }

        public static void shutdown()
        {
            resControl.Write(false); // reset on
            spi.Dispose();
            spiCMD_Data.Write(true); // data mode
        }
 #else

        ///
        /// <summary>GPIOC bank index</summary>
        ///
        public enum ControlPin : int
        {
            RES = 0, // RESET active low
            CS1 = 1, // CHIP SELECT active low
            RDWR = 2, // R/W, 1 = read, 0 = write
            ENA = 3, // OPERATION ENABLE 1->0 start read/write
            DC = 4  // COMMAND/DATA 0 = command, 1 = data
        }

        public const bool InitialState_RES = false; // false = active
        public const bool InitialState_CS1 = true; // true = inactive
        public const bool InitialState_ENA = true; // high = inactive
        public const bool InitialState_DC = true; // true = data
        public const bool InitialState_RW = true; // true = read

        ///
        ///<summary>Control Pins</summary>
        ///
        public static OutputPort[] GPIOC;

        ///
        ///<summary>Data Pins</summary>
        ///
        public static OutputPort[] GPIOB;

        public static void GPIO_WriteMSB(byte data)
        {
            byte x = 128;
            for (int i = 7; i >= 0; i--)
            {
                bool onoff = ((data & x) > 0);
                GPIOB[i].Write(onoff);

                // D: 7  6  5  4 3 2 1 0
                //  128 64 32 16 8 4 2 1
                x /= 0x2;
            }
        }

        public static void GPIO_WriteLSB(byte data)
        {
            byte x = 1;
            for (int i = 0; i < 8; i++)
            {
                GPIOB[i].Write((data & x) > 0);

                // D: 7  6  5  4 3 2 1 0
                //  128 64 32 16 8 4 2 1
                x *= 0x2;
            }
        }

        public static void GPIO_ResetControlBits(int pin)
        {
            GPIOC[pin].Write(false);
        }

        public static void GPIO_SetControlBits(int pin)
        {
            GPIOC[pin].Write(true);
        }

        public static void initPorts()
        {
            GPIOC = new OutputPort[5]; // need 5 pins here for control
            GPIOB = new OutputPort[8]; // need 8 pins here for data

            // Init Control Pins
            GPIOC[(int)ControlPin.DC] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO15, InitialState_DC);
            GPIOC[(int)ControlPin.RDWR] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO16, InitialState_RW);
            GPIOC[(int)ControlPin.ENA] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO17, InitialState_ENA);
            GPIOC[(int)ControlPin.CS1] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO18, InitialState_CS1);
            GPIOC[(int)ControlPin.RES] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO19, InitialState_RES);

            // Init Data Pins
            GPIOB[7] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO32, false);
            GPIOB[6] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO31, false);
            GPIOB[5] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO30, false);
            GPIOB[4] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO29, false);
            GPIOB[3] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO28, false);
            GPIOB[2] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO27, false);
            GPIOB[1] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO25, false);
            GPIOB[0] = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.IO24, false);
        }

        public static void shutdown()
        {
            // clear control pins to initial state
            GPIOC[(int)ControlPin.RES].Write(InitialState_RES);
            GPIOC[(int)ControlPin.CS1].Write(InitialState_CS1);
            GPIOC[(int)ControlPin.ENA].Write(InitialState_ENA);
            GPIOC[(int)ControlPin.DC].Write(InitialState_DC);
            GPIOC[(int)ControlPin.RDWR].Write(InitialState_RW);

            // clear data pins
            for (int i = 0; i < 8; i++)
            {
                GPIOB[i].Write(false);
            }
        }

        //--------------------------------------------------------------------------
        //--------------------------------------------------------------------------
        public static void oled_Command(byte Data)
        {
            GPIO_Write(Data);//6800 mode
            GPIO_ResetControlBits((int)ControlPin.DC); // set command
            GPIO_ResetControlBits((int)ControlPin.CS1); // select chip
            GPIO_ResetControlBits((int)ControlPin.RDWR); // write
            GPIO_SetControlBits((int)ControlPin.ENA); // set up for 1->0 transition
            //OLED_uDelay(1); // 100 usec = 0.1 msec
            Thread.Sleep(1);
            GPIO_ResetControlBits((int)ControlPin.ENA); // transition to 0, enable
            //OLED_uDelay(1); // 100 usec = 0.1 msec
            Thread.Sleep(1);
            GPIO_SetControlBits((int)ControlPin.RDWR); // switch to read
            GPIO_SetControlBits((int)ControlPin.CS1); // de-select
            GPIO_SetControlBits((int)ControlPin.DC); // switch to data mode
        }
        //--------------------------------------------------------------------------
        //--------------------------------------------------------------------------
        public static void oled_Data(byte Data)
        {
            GPIO_Write(Data);//6800 mode
            GPIO_SetControlBits((int)ControlPin.DC); // set data
            GPIO_ResetControlBits((int)ControlPin.CS1); // select chip
            GPIO_ResetControlBits((int)ControlPin.RDWR); // write
            GPIO_SetControlBits((int)ControlPin.ENA); // set up for 1->0 transition
            //OLED_uDelay(100); // 100 usec = 0.1 msec
            Thread.Sleep(1);
            GPIO_ResetControlBits((int)ControlPin.ENA); // transition to 0, enable
            //OLED_uDelay(100); // 100 usec = 0.1 msec
            Thread.Sleep(1);
            GPIO_SetControlBits((int)ControlPin.RDWR); // switch to read
            GPIO_SetControlBits((int)ControlPin.CS1);  // de-select
            GPIO_SetControlBits((int)ControlPin.DC); // stay in data mode
        }
 #endif

        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Instruction Setting
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Set_Column_Address_12864(byte a, byte b)
        {
 #if UseSPI
            byte[] cmd = new byte[] {0x15, a, b};
            Send(ref cmd, true);
 #else
            oled_Command(0x15);			// Set Column Address
            oled_Command(a);			//   Default => 0x00
            oled_Command(b);			//   Default => 0x3F (Total Columns Divided by 2)
 #endif
        }

        public static void Set_Row_Address_12864(byte a, byte b)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0x75, a, b };
            Send(ref cmd, true);
 #else
            oled_Command(0x75);			// Set Row Address
            oled_Command(a);			//   Default => 0x00
            oled_Command(b);			//   Default => 0x4F
 #endif
        }

        public static void Set_Contrast_Current_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] {0x81, d};
            Send(ref cmd, true);
 #else
            oled_Command(0x81);			// Set Contrast Value
            oled_Command(d);			//   Default => 0x40
 #endif
        }

        public static void Set_Current_Range_12864(byte d)
        {
            // oled_Command(0x84|d);
            Send((byte) (0x84 | d), true);			// Set Current Range
            //   Default => 0x84
            //     0x84 (0x00) => Quarter Current Range
            //     0x85 (0x01) => Half Current Range
            //     0x86 (0x02) => Full Current Range
        }

        public static void Set_Remap_Format_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xA0, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xA0);			// Set Re-Map & Data Format
            oled_Command(d);			//   Default => 0x00
            //     Column Address 0 Mapped to SEG0
            //     Disable Nibble Re-Map
            //     Horizontal Address Increment
            //     Scan from COM0 to COM[N-1]
            //     Disable COM Split Odd Even
 #endif
        }

        public static void Set_Start_Line_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xA1, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xA1);			// Set Display Start Line
            oled_Command(d);			//   Default => 0x00
 #endif
        }

        public static void Set_Display_Offset_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xA2, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xA2);			// Set Display Offset
            oled_Command(d);			//   Default => 0x00
 #endif
        }

        public static void Set_Display_Mode_12864(byte d)
        {
            // oled_Command(0xA4|d);
 #if UseSPI
            Send((byte)(0xA4 | d), true);		// Set Display Mode
 #else
            oled_Command((byte)(0xA4 | d));			// Set Display Mode
 #endif
            //   Default => 0xA4
            //     0xA4 (0x00) => Normal Display
            //     0xA5 (0x01) => Entire Display On, All Pixels Turn On at GS Level 15
            //     0xA6 (0x02) => Entire Display Off, All Pixels Turn Off
            //     0xA7 (0x03) => Inverse Display
        }

        public static void Set_Multiplex_Ratio_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xA8, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xA8);			// Set Multiplex Ratio
            oled_Command(d);			//   Default => 0x5F
 #endif
        }

        public static void Set_Master_Config_12864(byte d)
        {
            // oled_Command(0x02|d);
 #if UseSPI
            byte[] cmd = new byte[] { 0xAD, (byte)(0x02 | d) };
            Send(ref cmd, true);
 #else
            oled_Command(0xAD);			// Set Master Configuration
            oled_Command((byte)(0x02 | d));			//   Default => 0x03
            //     0x02 (0x00) => Select External VCC Supply
            //     0x03 (0x01) => Select Internal DC/DC Voltage Converter
 #endif
        }

        public static void Set_Display_On_Off_12864(byte d)
        {
            // oled_Command(0xAE|d);
 #if UseSPI
            Send((byte)(0xAE | d), true);		// Set Display On/Off
 #else
            oled_Command((byte)(0xAE | d));			// Set Display On/Off
 #endif
            //   Default => 0xAE
            //     0xAE (0x00) => Display Off
            //     0xAF (0x01) => Display On
        }

        public static void Set_Phase_Length_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xB1, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xB1);			// Phase 1 & 2 Period Adjustment
            oled_Command(d);			//   Default => 0x53 (5 Display Clocks [Phase 2] / 3 Display Clocks [Phase 1])
            //     D[3:0] => Phase 1 Period in 1~15 Display Clocks
            //     D[7:4] => Phase 2 Period in 1~15 Display Clocks
 #endif
        }

        public static void Set_Frame_Frequency_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xB2, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xB2);			// Set Frame Frequency (Row Period)
            oled_Command(d);			//   Default => 0x25 (37 Display Clocks)
 #endif
        }

        public static void Set_Display_Clock_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xB3, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xB3);			// Display Clock Divider/Osciallator Frequency
            oled_Command(d);			//   Default => 0x41
            //     D[3:0] => Display Clock Divider
            //     D[7:4] => Oscillator Frequency
 #endif
        }

        public static void Set_Precharge_Compensation_12864(byte a, byte b = 0)
        {
            // oled_Command(0x08|a);
 #if UseSPI
            byte[] cmd;
            if (a == 0x20) cmd = new byte[] { 0xB4, b, 0xB0, (byte)(0x08 | a) };
            else cmd = new byte[] { 0xB4, b };
            Send(ref cmd, true);
 #else
            oled_Command(0xB4);			// Set Pre-Charge Compensation Level
            oled_Command(b);			//   Default => 0x00 (No Compensation)

            if (a == 0x20)
            {
                oled_Command(0xB0);		            // Set Pre-Charge Compensation Enable
                oled_Command((byte) (0x08 | a));	//   Default => 0x08
                //     0x08 (0x00) => Disable Pre-Charge Compensation
                //     0x28 (0x20) => Enable Pre-Charge Compensation
            }
 #endif
        }

        public static void Set_Precharge_Voltage_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xBC, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xBC);			// Set Pre-Charge Voltage Level
            oled_Command(d);			//   Default => 0x10 (Connect to VCOMH)
 #endif
        }

        public static void Set_VCOMH_12864(byte d)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xBE, d };
            Send(ref cmd, true);
 #else
            oled_Command(0xBE);			// Set Output Level High Voltage for COM Signal
            oled_Command(d);			//   Default => 0x1D (0.81*VREF)
 #endif
        }

        public static void Set_VSL_12864(byte d)
        {
            //oled_Command(0x02|d);
 #if UseSPI
            byte[] cmd = new byte[] { 0xBF, (byte)(0x02 | d) };
            Send(ref cmd, true);
 #else
            oled_Command(0xBF);			// Set Segment Low Voltage Level
            oled_Command((byte)(0x02 | d));   			//   Default => 0x0E
            //     0x02 (0x00) => Keep VSL Pin Floating
            //     0x0E (0x0C) => Connect a Capacitor between VSL Pin & VSS
 #endif
        }

        public static void GA_Option_12864(bool enabled)
        {
            byte d = (byte)(enabled ? 0x01 : 0x00);
 #if UseSPI
            byte[] cmd = new byte[] { 0x23, d };
            Send(ref cmd, true);
 #else
            oled_Command(0x23);			// Graphic Acceleration Command Options
            oled_Command(d);			//   Default => 0x01
            //     Enable Fill Rectangle
            //     Disable Wrap around in Horizontal Direction During Copying & Scrolling
            //     Disable Reverse Copy
 #endif
        }

        public static void Draw_Rectangle_12864(byte startCol, byte endCol, byte startRow, byte endRow, byte level)
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0x24, startCol, startRow, endCol, endRow, level };
            Send(ref cmd, true);
 #else
            oled_Command(0x24);		        // Draw Rectangle
            oled_Command(startCol);			//   Column Address of Start
            oled_Command(startRow);			//   Row Address of Start
            oled_Command(endCol);			//   Column Address of End (Total Columns Devided by 2)
            oled_Command(endRow);			//   Row Address of End
            oled_Command(level);			//   Gray Scale Level
 #endif
            //OLED_uDelay(200);
            Thread.Sleep(1);
        }

        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Regular Pattern (Full Screen)
        //
        //    a: Two Pixels Data
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Fill_RAM_12864(byte fill)
        {
            GA_Option_12864(true);
            // fill a rectangle covering the whole screen with fill value
            Draw_Rectangle_12864(0x00, 0x3F, 0x00, 0x5F, fill);
        }

        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Regular Pattern (Partial or Full Screen)
        //
        //    a: Column Address of Start
        //    b: Column Address of End (Total Columns Devided by 2)
        //    c: Row Address of Start
        //    d: Row Address of End
        //    e: Two Pixels Data
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Fill_Block_12864(byte startCol, byte endCol, byte startRow, byte endRow, byte level)
        {
            GA_Option_12864(true);
            Draw_Rectangle_12864(startCol, endCol, startRow, endRow, level);
        }


        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Checkboard (Full Screen)
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Checkerboard_12864()
        {
            Set_Column_Address_12864(0x00, 0x3F);
            Set_Row_Address_12864(0x00, 0x5F);

 #if UseSPI
            byte[] d = new byte[5120];
            int offset = 0;
            for (int i = 0; i < 40; i++)
            {
                for (int j = 0; j < 64; j++)
                {
                    d[offset++] = 0xF0;
                }
                for (int j = 0; j < 64; j++)
                {
                    d[offset++] = 0x0F;
                }
            }
            Send(ref d, false);
 #else
            for (int i = 0; i < 40; i++)
            {
                for (int j = 0; j < 64; j++)
                {
                    oled_Data(0xF0);
                }
                for (int j = 0; j < 64; j++)
                {
                    oled_Data(0x0F);
                }
            }
 #endif
        }

        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Gray Scale Bar (Full Screen)
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Grayscale_12864()
        {
            //   Level 16 => Column 1~8
            Fill_Block_12864(0x00, 0x03, 0x00, 0x3F, 0xFF);

            //   Level 15 => Column 9~16
            Fill_Block_12864(0x04, 0x07, 0x00, 0x3F, 0xEE);

            //   Level 14 => Column 17~24
            Fill_Block_12864(0x08, 0x0B, 0x00, 0x3F, 0xDD);

            //   Level 13 => Column 25~32
            Fill_Block_12864(0x0C, 0x0F, 0x00, 0x3F, 0xCC);

            //   Level 12 => Column 33~40
            Fill_Block_12864(0x10, 0x13, 0x00, 0x3F, 0xBB);

            //   Level 11 => Column 41~48
            Fill_Block_12864(0x14, 0x17, 0x00, 0x3F, 0xAA);

            //   Level 10 => Column 49~56
            Fill_Block_12864(0x18, 0x1B, 0x00, 0x3F, 0x99);

            //   Level 9 => Column 57~64
            Fill_Block_12864(0x1C, 0x1F, 0x00, 0x3F, 0x88);

            //   Level 8 => Column 65~72
            Fill_Block_12864(0x20, 0x23, 0x00, 0x3F, 0x77);

            //   Level 7 => Column 73~80
            Fill_Block_12864(0x24, 0x27, 0x00, 0x3F, 0x66);

            //   Level 6 => Column 81~88
            Fill_Block_12864(0x28, 0x2B, 0x00, 0x3F, 0x55);

            //   Level 5 => Column 89~96
            Fill_Block_12864(0x2C, 0x2F, 0x00, 0x3F, 0x44);

            //   Level 4 => Column 97~104
            Fill_Block_12864(0x30, 0x33, 0x00, 0x3F, 0x33);

            //   Level 3 => Column 105~112
            Fill_Block_12864(0x34, 0x37, 0x00, 0x3F, 0x22);

            //   Level 2 => Column 113~120
            Fill_Block_12864(0x38, 0x3B, 0x00, 0x3F, 0x11);

            //   Level 1 => Column 121~128
            Fill_Block_12864(0x3C, 0x3F, 0x00, 0x3F, 0x00);
        }


        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Character (5x7)
        //
        //    a: Database
        //    b: Ascii
        //    c: Start X Address
        //    d: Start Y Address
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        /*
        public static void Show_Font57_12864(byte db, byte asc, byte startX, byte startY)
        {
            byte *Src_Pointer=0;
            byte i,Font,MSB,LSB;

	        switch(db)
	        {
		        case 1:
			        Src_Pointer=&Ascii_1[(asc-1)][0];
			        break;
		        case 2:
			        //Src_Pointer=&Ascii_2[(b-1)][0];
			        break;
	        }

	        Set_Remap_Format_12864(0x54);
	        for(i=0;i<=4;i+=2)
	        {
		        LSB=*Src_Pointer;
		        Src_Pointer++;
		        if(i == 4)
		        {
			        MSB=0x00;
		        }
		        else
		        {
			        MSB=*Src_Pointer;
			        Src_Pointer++;
		        }
 		        Set_Column_Address_12864(startX,startX);
		        Set_Row_Address_12864(startY,startY+7);

		        Font=((MSB&0x01)<<4)|(LSB&0x01);
		        Font=Font|(Font<<1)|(Font<<2)|(Font<<3);
		        oled_Data(Font);

		        Font=((MSB&0x02)<<3)|((LSB&0x02)>>1);
		        Font=Font|(Font<<1)|(Font<<2)|(Font<<3);
		        oled_Data(Font);

		        Font=((MSB&0x04)<<2)|((LSB&0x04)>>2);
		        Font=Font|(Font<<1)|(Font<<2)|(Font<<3);
		        oled_Data(Font);

		        Font=((MSB&0x08)<<1)|((LSB&0x08)>>3);
		        Font=Font|(Font<<1)|(Font<<2)|(Font<<3);
		        oled_Data(Font);

		        Font=((MSB&0x10)<<3)|((LSB&0x10)>>1);
		        Font=Font|(Font>>1)|(Font>>2)|(Font>>3);
		        oled_Data(Font);

		        Font=((MSB&0x20)<<2)|((LSB&0x20)>>2);
		        Font=Font|(Font>>1)|(Font>>2)|(Font>>3);
		        oled_Data(Font);

		        Font=((MSB&0x40)<<1)|((LSB&0x40)>>3);
		        Font=Font|(Font>>1)|(Font>>2)|(Font>>3);
		        oled_Data(Font);

		        Font=(MSB&0x80)|((LSB&0x80)>>4);
		        Font=Font|(Font>>1)|(Font>>2)|(Font>>3);
		        oled_Data(Font);
		        startX++;
	        }
	        Set_Remap_Format_12864(0x50);
        }


        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show String
        //
        //    a: Database
        //    b: Start X Address
        //    c: Start Y Address
        //    * Must write "0" in the end...
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Show_String_12864(byte db, byte *Data_Pointer, byte startX, byte startY)
        {
            byte *Src_Pointer;

	        Src_Pointer=Data_Pointer;
	        Show_Font57_12864(1,96,startX,startY);			// No-Break Space
						        //   Must be written first before the string start...

	        while(true)
	        {
		        Show_Font57_12864(db,*Src_Pointer,startX,startY);
		        Src_Pointer++;
		        startX+=3;
		        if(*Src_Pointer == 0) break;
	        }
        }


        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Show Pattern (Partial or Full Screen)
        //
        //    a: Column Address of Start
        //    b: Column Address of End (Total Columns Divided by 2)
        //    c: Row Address of Start
        //    d: Row Address of End
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Show_Pattern_12864(byte *Data_Pointer, byte a, byte b, byte c, byte d)
        {
            byte *Src_Pointer;
            byte i,j;
	
	        Src_Pointer=Data_Pointer;
	        Set_Column_Address_12864(a,b);
	        Set_Row_Address_12864(c,d);

	        for(i=0;i<(d-c+1);i++)
	        {
		        for(j=0;j<(b-a+1);j++)
		        {
			        oled_Data(*Src_Pointer);
			        Src_Pointer++;
		        }
	        }
        }
         */


        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Vertical Scrolling (Full Screen)
        //
        //    a: Scrolling Direction
        //       "0x00" (Upward)
        //       "0x01" (Downward)
        //    b: Set Numbers of Row Scroll per Step
        //    c: Set Time Interval between Each Scroll Step
        //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void Set_Gray_Scale_Table_12864()
        {
 #if UseSPI
            byte[] cmd = new byte[] { 0xB8, 0x01, 0x11, 0x22, 0x32, 0x43, 0x54, 0x65, 0x76 };
            Send(ref cmd, true);
 #else
            oled_Command(0xB8);			// Set Gray Scale Table
            oled_Command(0x01);			//   Gray Scale Level 1
            oled_Command(0x11);			//   Gray Scale Level 3 & 2
            oled_Command(0x22);			//   Gray Scale Level 5 & 4
            oled_Command(0x32);			//   Gray Scale Level 7 & 6
            oled_Command(0x43);			//   Gray Scale Level 9 & 8
            oled_Command(0x54);			//   Gray Scale Level 11 & 10
            oled_Command(0x65);			//   Gray Scale Level 13 & 12
            oled_Command(0x76);			//   Gray Scale Level 15 & 14
 #endif
        }

        //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Initialization
        //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        public static void OLED_12864_Init()
        {
 #if UseSPI
            resControl.Write(false); // set reset
 #else
            GPIO_ResetControlBits((int)ControlPin.RES);	 // set reset
 #endif

            // original delay = 201*200=40200 usec = 40.2 msec
            /*
	        for(i=0;i<200;i++)
	        {
		        OLED_uDelay(200);
	        }
             */
            Thread.Sleep(41); // stay in reset 40.2 ms

 #if UseSPI
            resControl.Write(true); // come out of reset
 #else
            GPIO_SetControlBits((int)ControlPin.RES);  // come out of reset
 #endif


            Set_Display_On_Off_12864(0x00);		// Display Off (0x00/0x01)
            Set_Display_Clock_12864(0x91);		// Set Clock as 135 Frames/Sec
            Set_Multiplex_Ratio_12864(0x3F);		// 1/64 Duty (0x0F~0x5F)
            Set_Display_Offset_12864(0x4C);		// Shift Mapping RAM Counter (0x00~0x5F)
            Set_Start_Line_12864(0x00);			// Set Mapping RAM Display Start Line (0x00~0x5F)
            Set_Master_Config_12864(0x00);		// Disable Embedded DC/DC Converter (0x00/0x01)
            Set_Remap_Format_12864(0x50);			// Set Column Address 0 Mapped to SEG0
            //     Disable Nibble Remap
            //     Horizontal Address Increment
            //     Scan from COM[N-1] to COM0
            //     Enable COM Split Odd Even
            Set_Current_Range_12864(0x02);		// Set Full Current Range
            Set_Gray_Scale_Table_12864();			// Set Pulse Width for Gray Scale Table
            Set_Contrast_Current_12864(Brightness);	// Set Scale Factor of Segment Output Current Control
            Set_Frame_Frequency_12864(0x51);		// Set Frame Frequency
            Set_Phase_Length_12864(0x55);			// Set Phase 1 as 5 Clocks & Phase 2 as 5 Clocks
            Set_Precharge_Voltage_12864(0x10);		// Set Pre-Charge Voltage Level
            Set_Precharge_Compensation_12864(0x20, 0x02);	// Set Pre-Charge Compensation
            Set_VCOMH_12864(0x1C);			// Set High Voltage Level of COM Pin
            Set_VSL_12864(0x0D);				// Set Low Voltage Level of SEG Pin
            Set_Display_Mode_12864(0x00);			// Normal Display Mode (0x00/0x01/0x02/0x03)

            Fill_RAM_12864(0x00);				// Clear Screen

            Set_Display_On_Off_12864(0x01);		// Display On (0x00/0x01)
        }

        public static byte[] BitmapToGray4(ref Bitmap bmp)
        {
            // barf for bad bitmaps
            if ((bmp.Width > 128) || (bmp.Height > 64)) return (new byte[]{});
            byte[] bmpData = bmp.GetBitmap(); // 64 * 128 * 4 channels * 8 bits = 32768 bytes RGBA

            int inOffset = 0;
            int outOffset = 0;
            byte[] retval = new byte[8192]; // 64 * 128 bytes
            byte r,g,b;

            for (int y = 0; y < 64; y++)
            {
                for (int x = 0; x < 128; x++)
                {
                    r = bmpData[inOffset++];
                    g = bmpData[inOffset++];
                    b = bmpData[inOffset++];
                    //a = bmpData[inOffset++];
                    inOffset++; // skip a

                    // convert to 4 bit grayscale
                    retval[outOffset++] = (byte)(((((float)r) * .30) + (((float)g) * .59) + (((float)b) * .11)) / 16.0);
                }
            }

            return retval;
        }

        public static byte[] BitmapDoubleStuff(ref byte[] bitmapData)
        {
            // only accept 128x64
            if (bitmapData.Length != 8192) return (new byte[] {});

            byte[] retval = new byte[4096];
            int offset = 0;
            bool highNIB = false;
            byte last = 0;

            // data is arranged in rows bytes 0 - 127 = r0c0-c127, bytes 128-255 = r1c0-c127
            for (int y = 0; y < 64; y++)
            {
                for (int x = 0; x < 128; x++)
                {
                    if (!highNIB) {
                        last = (byte) (bitmapData[(y * 128) + x] & 0x0F);
                        highNIB = true;
                    }
                    else
                    {
                        retval[offset++] = (byte)(((bitmapData[(y * 128) + x] & 0x0F) << 4) | last);
                        highNIB = false;
                    }
                }
            }

            return retval;
        }

        public static void DrawBitmap(ref Bitmap bmp)
        {
            byte[] bmpData = bmp.GetBitmap(); // 64 * 128 * 4 channels * 8 bits = 32768 bytes RGBA
            byte[] retval = new byte[4096];
            int inOffset = 0;
            int outOffset = 0;
            float tmpF = 0;
            int tmpI = 0;
            byte gray;
            bool highNIB = false;
            byte grayLast = 0;

            // barf for bad bitmaps
            if ((bmp.Width > 128) || (bmp.Height > 64)) return;

            // go top top left corner
            Set_Column_Address_12864(0x00, 0x3F);
            Set_Row_Address_12864(0x00, 0x7F);            

            while (inOffset<32768) {
                // convert to 4 bit grayscale
                //gray = (byte)(((((float)bmpData[inOffset++]) * .30) + (((float)bmpData[inOffset++]) * .59) + (((float)bmpData[inOffset++]) * .11)) / 16.0);
                //gray = (byte)(((float)bmpData[inOffset++] + (float)bmpData[inOffset++] + (float)bmpData[inOffset++]) / 48.0);
                for (tmpF = 0, tmpI = 0; tmpI < 3; tmpI++)
                {
                    tmpF += bmpData[inOffset++];
                }
                gray = (byte)(tmpF / 48.0);
                inOffset++; // skip a
                if (!highNIB)
                {
                    grayLast = (byte)(gray & 0x0F);
                    highNIB = true;
                }
                else
                {
                    retval[outOffset++] = ((byte)(((gray & 0x0F) << 4) | grayLast));
                    highNIB = false;
                }
            }            
            bmpData = new byte[] { }; // free mem
            Send(ref retval, false);
            return;
        }

        public static void DrawBitmap(ref byte[] bitmapData)
        {
            // only accept 128x64 already stuffed
            if (bitmapData.Length != 4096) return;

            Fill_RAM_12864(0x00);				// Clear Screen
            // go top top left corner
            Set_Column_Address_12864(0x00, 0x3F);
            Set_Row_Address_12864(0x00, 0x7F);
            Send(ref bitmapData, false);
        }

        //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        //  Demonstration
        //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
        /*
        public int oled_12864()
        {
            OLED_12864_Init();

            Set_Column_Address_12864(0x00,0x3F);
            Set_Row_Address_12864(0x00,0x3F);

            int counter = 0;
            int datum;
            byte[] Buff;
            for(int i=0;i<64;i++){ //these loops expand the bitmap data because the bmp file is monochrome image (only ON or OFF, no grayscale)
	            for(int j=0;j<16;j++){   
	            datum = Buff[counter];
	            datum = ((datum >> 6) & 0x03);//move over and mask the highest 2 bits
	            if(datum == 0x03){oled_Data(0xFF);}//if both bits are high, send 0xFF to OLED
	            else if(datum == 0x02){oled_Data(0x0F);}//if one bit is high, send 0x0F to OLED
	            else if(datum == 0x01){oled_Data(0xF0);}
	            else oled_Data(0x00);//if neither bit is high, send 0x00 to OLED

	            datum = Buff[counter];
	            datum = ((datum >> 4) & 0x03);//move over and mask the 3&4th bits
	            if(datum == 0x03){oled_Data(0xFF);}
	            else if(datum == 0x02){oled_Data(0x0F);}
	            else if(datum == 0x01){oled_Data(0xF0);}
	            else oled_Data(0x00);

	            datum = Buff[counter];
	            datum = ((datum >> 2) & 0x03);//move over and mask the 5&6th bits
	            if(datum == 0x03){oled_Data(0xFF);}
	            else if(datum == 0x02){oled_Data(0x0F);}
	            else if(datum == 0x01){oled_Data(0xF0);}
	            else oled_Data(0x00);

	            datum = Buff[counter];
	            datum = (datum & 0x03);//mask the lowest 2 bits
	            if(datum == 0x03){oled_Data(0xFF);}
	            else if(datum == 0x02){oled_Data(0x0F);}
	            else if(datum == 0x01){oled_Data(0xF0);}
	            else oled_Data(0x00);
	
	            counter++;
	            }
            }

            return 1;
        }
         */
    }
}


#6

I forwarded a request to increase the limits.

By the way, the community likes seeing code contributions on Codeshare :wink:


#7

I’m building an OLED NTP networked clock. I’ll post the full source there once complete.


#8

Max file size increased.


#9

Cool. Well hopefully we won’t double your server storage requirements. An alternative would be to have a GD routine that accepts larger images but resizes them to fit in the previous (or current) size limitations.

In another thread I posted I had RLP working, which really kicks some tail. What was a 2-4 second screen update before is now a blink. I’ll be posting a complete project when I’m a bit further along. Its going to be a long term deal getting improved a little at a time. This weekend I hope to add some fonts and get things a bit more efficient rather than thread.sleeping then display updating. I also hope to push all of the LCD routines into RLP, then the driver will be all in one place and all I’ll need to expose to C# are a few graphics accelerator functions and a bitmap writer.