Netduino home hardware projects downloads community

Jump to content


The Netduino forums have been replaced by new forums at community.wildernesslabs.co. This site has been preserved for archival purposes only and the ability to make new accounts or posts has been turned off.
Photo

netduino driver for the AdaFruit SSD1306 OLED display


  • Please log in to reply
12 replies to this topic

#1 Fabien Royer

Fabien Royer

    Advanced Member

  • Members
  • PipPipPip
  • 406 posts
  • LocationRedmond, WA

Posted 17 January 2011 - 06:50 AM

Back in January, I adapted the Arduino driver written by Limor Fried for the SSD1306 monochrome OLED display to the netduino.


At the time, I could not figure out why I was unable to communicate with the display over hardware SPI and had resorted to bit banging data, which was quite slow. Last night, I revisited the driver and figured out the issue I had in January. I overhauled the code which now works properly @ 40 MHz using hardware SPI.


http://www.youtube.com/watch?v=kzfLVBSOO_I


The driver and the sample code is located here.


Cheers,

-Fabien.



Edited by Fabien Royer, 01 June 2011 - 04:17 PM.


#2 Fabien Royer

Fabien Royer

    Advanced Member

  • Members
  • PipPipPip
  • 406 posts
  • LocationRedmond, WA

Posted 01 June 2011 - 04:18 PM

Bump!

#3 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 01 June 2011 - 04:37 PM

subtle bumb :) Nice work, didn't see it before. Fabien, perhaps you could add your drivers to the wiki as well? It will be better archived then on a forum.
"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#4 Fabien Royer

Fabien Royer

    Advanced Member

  • Members
  • PipPipPip
  • 406 posts
  • LocationRedmond, WA

Posted 01 June 2011 - 06:18 PM

Hi Stephan,

The repository for the drivers is here: http://netduinohelpers.codeplex.com/

I believe that it would be preferable to reference the codeplex repository in the Wiki instead of actually checking code in a Wiki.

Cheers,
-Fabien.

#5 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 01 June 2011 - 06:21 PM

Hi Stephan,

Who? :unsure:

I believe that it would be preferable to reference the codeplex repository in the Wiki instead of actually checking code in a Wiki.

Absolutely, that was what I ment ;)

By the way, I see that repository is ment for generic Netduino libraries, am I right?
"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#6 Fabien Royer

Fabien Royer

    Advanced Member

  • Members
  • PipPipPip
  • 406 posts
  • LocationRedmond, WA

Posted 01 June 2011 - 07:07 PM

Who? :unsure:


Oops! Sorry Stefan :)

By the way, I see that repository is ment for generic Netduino libraries, am I right?


Yes, the netduino.helpers library is generally designed to work with all netduino SKUs, with few exceptions.

-Fabien.

#7 Fabien Royer

Fabien Royer

    Advanced Member

  • Members
  • PipPipPip
  • 406 posts
  • LocationRedmond, WA

Posted 01 June 2011 - 07:57 PM

FYI, I just added a starter Wiki page for netduino libraries: http://wiki.netduino...-libraries.ashx

Cheers,
-Fabien.

#8 Stefan

Stefan

    Moderator

  • Members
  • PipPipPip
  • 1965 posts
  • LocationBreda, the Netherlands

Posted 01 June 2011 - 09:06 PM

Thanks :)
"Fact that I'm a moderator doesn't make me an expert in things." Stefan, the eternal newb!
My .NETMF projects: .NETMF Toolbox / Gadgeteer Light / Some PCB designs

#9 dino4net

dino4net

    Advanced Member

  • Members
  • PipPipPip
  • 40 posts

Posted 04 November 2013 - 04:37 PM

        public const int Width = 128;        public const int Height = 32;        private const int I2CTransactionTimeout = 1000; // ms        public AdaFruitSSD1306(ushort I2C_ADDRESS = 0x3C, int I2C_ClockRateKHz = 400, Cpu.Pin reset = (Cpu.Pin) 54)        {            AutoRefreshScreen = false;            I2c = new I2CDevice.Configuration(I2C_ADDRESS, I2C_ClockRateKHz);            //SoftwareI2CBus i2cbus = new SoftwareI2CBus(SecretLabs.NETMF.Hardware.Netduino.Pins.GPIO_PIN_A4, SecretLabs.NETMF.Hardware.Netduino.Pins.GPIO_PIN_A5);            //I2c = i2cbus.CreateI2CDevice(new I2CDevice.Configuration(0x3C, I2CTransactionTimeout));            //Connect the RST pin of the display to this Netduino pin            resetPin = new OutputPort(SecretLabs.NETMF.Hardware.NetduinoPlus.Pins.GPIO_PIN_D10, false);            //resetPin = new OutputPort(reset, false);        }        protected void SendCommand(Command cmd) {            SpiBuffer[0] = (byte)cmd;            if (Spi != null)                Spi.Write(SpiBuffer);            if (I2c != null)            {                I2CBus.GetInstance().Write(I2c, new byte[] {0x00, (byte)cmd}, I2CTransactionTimeout);            }        }        public void InvertDisplay(bool cmd) {            if (Spi != null)                dcPin.Write(DisplayCommand);            if (cmd) {                SendCommand(Command.INVERTDISPLAY);            } else {                SendCommand(Command.NORMALDISPLAY);            }            if (Spi != null)                dcPin.Write(Data);        }        public virtual void Refresh() {            if (Spi != null)                Spi.Write(displayBuffer);            else if (I2c != null)            {                byte[] I2CCommand = new byte[displayBuffer.Length + 1];                I2CCommand[0] = 0x40;                Array.Copy(displayBuffer, 0, I2CCommand, 1, displayBuffer.Length);                I2CBus.GetInstance().Write(I2c, I2CCommand, I2CTransactionTimeout);            }        }        public void Initialize(VccType vcctype = VccType.SWITCHCAPVCC) {            if (Spi != null)            {                resetPin.Write(true);                Thread.Sleep(1);            // VDD (3.3V) goes high at start, lets just chill for a ms                resetPin.Write(false);      // bring reset low                Thread.Sleep(10);           // wait 10ms                resetPin.Write(true);       // bring out of reset                dcPin.Write(DisplayCommand);                SendCommand(Command.DISPLAYOFF);  // 0xAE                SendCommand(Command.SETLOWCOLUMN | (Command)0x0);  // low col = 0                SendCommand(Command.SETHIGHCOLUMN | (Command)0x0);  // hi col = 0                SendCommand(Command.SETSTARTLINE | (Command)0x0); // line #0                SendCommand(Command.SETCONTRAST);  // 0x81                if (vcctype == VccType.EXTERNALVCC)                {                    SendCommand((Command)0x9F);  // external 9V                }                else                {                    SendCommand((Command)0xCF);  // chargepump                }                SendCommand((Command)0xA1);  // setment remap 95 to 0 (?)                SendCommand(Command.NORMALDISPLAY); // 0xA6                SendCommand(Command.DISPLAYALLON_RESUME); // 0xA4                SendCommand(Command.SETMULTIPLEX); // 0xA8                //SendCommand((Command)0x3F);  // 0x3F 1/64 duty                SendCommand((Command)0x1F);  // 0x1F 1/32 duty                SendCommand(Command.SETDISPLAYOFFSET); // 0xD3                SendCommand((Command)0x0); // no offset                SendCommand(Command.SETDISPLAYCLOCKDIV);  // 0xD5                SendCommand((Command)0x80);  // the suggested ratio 0x80                SendCommand(Command.SETPRECHARGE); // 0xd9                if (vcctype == VccType.EXTERNALVCC)                {                    SendCommand((Command)0x22); // external 9V                }                else                {                    SendCommand((Command)0xF1); // DC/DC                }                SendCommand(Command.SETCOMPINS); // 0xDA                //SendCommand((Command)0x12); // disable COM left/right remap                SendCommand((Command)0x02); ////128_32 = 02       128_64 = 12                SendCommand(Command.SETVCOMDETECT); // 0xDB                SendCommand((Command)0x40); // 0x20 is default?                SendCommand(Command.MEMORYMODE); // 0x20                SendCommand((Command)0x00); // 0x0 act like ks0108                // left to right scan                SendCommand(Command.SEGREMAP | (Command)0x1);     //0xA0                SendCommand(Command.COMSCANDEC);          //0xC8                SendCommand(Command.CHARGEPUMP); //0x8D                if (vcctype == VccType.EXTERNALVCC)                {                    SendCommand((Command)0x10);  // disable                }                else                {                    SendCommand((Command)0x14);  // disable                    }                SendCommand(Command.DISPLAYON);//--turn on oled panel                // Switch to 'data' mode                dcPin.Write(Data);            }            else            {                resetPin.Write(true);                Thread.Sleep(1);            // VDD (3.3V) goes high at start, lets just chill for a ms                resetPin.Write(false);      // bring reset low                Thread.Sleep(10);           // wait 10ms                resetPin.Write(true);       // bring out of reset                byte[] intBuffer;                if (Height == 32)                {                    intBuffer = new byte[] {                        (byte)Command.DISPLAYOFF,                       //turn off display(RESET=OFF)                        0x00,0x00,                                      //low column nibble(RESET=0),high column nibble(RESET=0)                        0xB0,                                           //start page address(RESET=0)                        (byte)Command.MEMORYMODE,0x00,                  //memory address mode(RESET=02 [page])                        (byte)Command.SETDISPLAYCLOCKDIV,0x80,          //oscillator frequency and divider(RESET=80)00                        (byte)Command.SETMULTIPLEX,0x1F,                //mux ratio(RESET=3F [64 lines]                        (byte)Command.SETDISPLAYOFFSET,0x00,            //display offset,COM vertical shift(RESET=0)                        (byte)Command.CHARGEPUMP,(byte)(vcctype == VccType.EXTERNALVCC? 0x10 : 0x14),   //enable charge pump(RESET=10 [OFF])                        0xA1,0x00,                                      //segment remap(RESET=SEG0, COL0) 00 mirror image                        (byte)Command.COMSCANDEC,                       //COM output scan(RESET=C0, C8 flips display)                        (byte)Command.SETCOMPINS,0x02,                  //COM pins hardware config(RESET=12[alternate])                        (byte)Command.SETCONTRAST,0xCF,                 //contrast CF(RESET=7F)                        (byte)Command.SETPRECHARGE,(byte)(vcctype == VccType.EXTERNALVCC? 0x22 : 0xF1),   //pre-charge period F1(RESET=22)                        (byte)Command.SETVCOMDETECT,0x30,               //Vcom deselect(RESET=20)                        (byte)Command.DISPLAYALLON_RESUME,              //turn all on ignore RAM A5/RAM A4(RESET=A4)                        (byte)Command.NORMALDISPLAY,                    //normal display A6/inverted A7(RESET=A6)                        (byte)Command.DISPLAYON};                       //turn on display                }                else if (Height == 64)                {                    intBuffer = new byte[] {                        (byte)Command.DISPLAYOFF,                       //turn off display(RESET=OFF)                        0x00,0x00,                                      //low column nibble(RESET=0),high column nibble(RESET=0)                        0xB0,                                           //start page address(RESET=0)                        (byte)Command.MEMORYMODE,0x00,                  //memory address mode(RESET=02 [page])                        (byte)Command.SETDISPLAYCLOCKDIV,0x80,          //oscillator frequency and divider(RESET=80)00                        (byte)Command.SETMULTIPLEX,0x3F,                //mux ratio(RESET=3F [64 lines]                        (byte)Command.SETDISPLAYOFFSET,0x00,            //display offset,COM vertical shift(RESET=0)                        (byte)Command.CHARGEPUMP,(byte)(vcctype == VccType.EXTERNALVCC? 0x10 : 0x14),   //enable charge pump(RESET=10 [OFF])                        0xA1,0x00,                                      //segment remap(RESET=SEG0, COL0) 00 mirror image                        (byte)Command.COMSCANDEC,                       //COM output scan(RESET=C0, C8 flips display)                        (byte)Command.SETCOMPINS,0x12,                  //COM pins hardware config(RESET=12[alternate])                        (byte)Command.SETCONTRAST,0xCF,                 //contrast CF(RESET=7F)                        (byte)Command.SETPRECHARGE,(byte)(vcctype == VccType.EXTERNALVCC? 0x22 : 0xF1),   //pre-charge period F1(RESET=22)                        (byte)Command.SETVCOMDETECT,0x40,               //Vcom deselect(RESET=20)                        (byte)Command.DISPLAYALLON_RESUME,              //turn all on ignore RAM A5/RAM A4(RESET=A4)                        (byte)Command.NORMALDISPLAY,                    //normal display A6/inverted A7(RESET=A6)                        (byte)Command.DISPLAYON};                       //turn on display                }                I2CBus.GetInstance().Write(I2c, intBuffer, I2CTransactionTimeout);            }        }

Hey Fabien,

I got this working with the I2C version and it's tested with 64 and 32 lines. Do you want to add it to your driver?



#10 Frode

Frode

    Advanced Member

  • Members
  • PipPipPip
  • 202 posts
  • LocationNorway

Posted 18 February 2014 - 04:24 PM

Great work, Dino4net! I'm waiting for my 128x32 display to arrive, and this will be VERY VERY helpful!



#11 Frode

Frode

    Advanced Member

  • Members
  • PipPipPip
  • 202 posts
  • LocationNorway

Posted 20 February 2014 - 11:14 PM

A few notes after playing around with the SSD1306 display, using a combination of Fabien Royer and Dino4net's codespieces.

 

I'm using two 4.7K resistors as pull-up resistors to 5V.

 

In the I2CBus class found on the forum I had to comment out the two last lines in the Write() method, the two lines where it throws an exception if the transferred data length isn't correct. Half of the time the exception would be raised.

 

I also made a change to the DrawString method, so that it looks like this:

public void DrawString(int x, int line, string str){    foreach (Char c in str)    {        DrawCharacter(x, line, c);        x += 6; // 6 pixels wide        if (line >= Height / 8)        {            break;        // ran out of space :(        }        if (x + 6 >= Width)        {            x = 0;    // ran out of this line            line++;        }    }    if (AutoRefreshScreen)    {        Refresh();    }}public enum Align{    Left,    Center,    Right}public void DrawString(Align align, int line, string str){    switch (align)    {        case Align.Left:            DrawString(0, line, str);            break;        case Align.Center:            DrawString((127 / 2) - (str.Length * 6 / 2), line, str);            break;        case Align.Right:            DrawString((127 - (6 * str.Length)), line, str);            break;        default:            DrawString(0, line, str);            break;    }}

I changed the original DrawString so that it doesn't blank out the entire screen when you write more text than the display has room for, and it also fixes a bug where the bottom right character didn't work.

 

Also, the DrawString(Align, int, str) overload makes it easy to do center and right aligning of a text.

 

Here is an example of how to use the class:

Display.AdaFruitSSD1306 oled = new Display.AdaFruitSSD1306(0x3C);oled.Initialize();oled.AutoRefreshScreen = true;oled.DrawString(Display.AdaFruitSSD1306.Align.Left, 0, "Left");oled.DrawString(Display.AdaFruitSSD1306.Align.Center, 1, "Center");oled.DrawString(Display.AdaFruitSSD1306.Align.Right, 2, "Right");

I hope this is helpful for the next guy.



#12 dino4net

dino4net

    Advanced Member

  • Members
  • PipPipPip
  • 40 posts

Posted 15 May 2014 - 03:46 PM

Hello,

 

As the code here became a little hard to read and many asked for a complete solution.

 

Here it is ... with a tool to create the image array ...

Attached Files



#13 gismo

gismo

    Advanced Member

  • Members
  • PipPipPip
  • 110 posts

Posted 05 August 2015 - 09:03 PM

I'm currently working on support for the very similar SH1106 OLED driver. I was able to get I2C version working with very few modifications, but I cannot get the SPI version to work correctly(which I prefer at this point). Anyone want to collaborate on getting SH1106 working?






0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users

home    hardware    projects    downloads    community    where to buy    contact Copyright © 2016 Wilderness Labs Inc.  |  Legal   |   CC BY-SA
This webpage is licensed under a Creative Commons Attribution-ShareAlike License.