CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

SSD1306 driver for use with less RAM
Goto page Previous  1, 2, 3, 4, 5  Next
 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Wed Feb 14, 2018 10:08 am     Reply with quote

As an update, I've just been running this with the SSD1309 (gives larger displays like 2.4"), and it runs fine. Only thing that gave problems was I found these needed a reset signal to stay low for a little while after power was applied to work reliably (ended up using a capacitor on the RES line), for reliable operation. Because this takes time, I had to pause for longer at boot. With this done works well. Smile
-Terppa-



Joined: 08 Jan 2018
Posts: 60
Location: Finland

View user's profile Send private message

PostPosted: Wed Mar 28, 2018 5:02 am     Reply with quote

Thank you very much for this driver Mr Ttelmah!
I have SSD1309 module which is modified on I2C- mode and it works great.
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Tue Apr 03, 2018 7:15 pm     Reply with quote

Compiler: 5.026
Device: PIC18F14K50

Hi TTelmah,

I've successfully used your SSD1306 driver on an 128x64 display but I changed my display to a miniature one (0.49") 64x32.

The display initializes fine and I have just started to look through the pages but perhaps you may have some insight that will be faster than me trying to re-invent the wheel.

I appears that the address mapping is off because if I write the following:
Code:

   size = NORMAL;

   OLED_gotoxy( 0, 0 );
   OLED_text( "ABCDEFGHIJKLMNOP", 26 );

   OLED_gotoxy( 0, 1 );
   OLED_text( "zyxwvutsrqponmlk", 26 );

   OLED_gotoxy( 0, 2 );
   OLED_text( "abcdefghijklmnop", 26 );

   OLED_gotoxy( 0, 3 );
   OLED_text( "ZYXWVUTSRQPONMLK", 26 );

All 4 lines contain text. But the problem is that the first character of each line is cut in half and starts at the 6th character.

So:
First line starts at half of letter 'F' and ends at letter 'P'.
Second starts at half of letter 'u' and ends at letter 'k'.
Third line starts at half of letter 'f'' and ends at letter 'p'.
Fourth line starts at half of letter 'U' and ends at letter 'K'.

When I say "starts at half", each character is 5 pixels wide but letter 'F', I don't see the first two columns.

I'll continue to read through your code and the datasheets but if you know off the top of your head, please let me know.

Thanks!

Benoit
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Wed Apr 04, 2018 6:58 am     Reply with quote

The 64 column displays have the starting value offset.
If you look at the github library here:
<https://github.com/WaiakeaRobotics/Adafruit_SSD1306/blob/master/Adafruit_SSD1306.cpp>
They offset by 0x20, and by 3 on the page address values. You'll need to do the same.
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Wed Apr 04, 2018 7:26 am     Reply with quote

That's what I thought it was by looking closer at the specs this morning. Ok, I'll give it a try tonight.

Thanks a million! You rock!

Benoit
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Wed Apr 04, 2018 9:00 pm     Reply with quote

So I tried the offset and still no success.

I looked at the code you suggested and I don't see in that code where the "void Adafruit_SSD1306::display(void)" function is used and that's where the S_COLUMNADDR and S_PAGEADDR are used. I compared my initialization sequence below with the one you referred to and they are the same except for S_COLUMNADDR and S_PAGEADDR which I added after S_MEMORYMODE and still that doesn't have any effect.

I took your ROM BYTE init_sequence[] structure and added / modified the missing commands (S_COLUMNADDR and S_PAGEADDR):

Code:

ROM BYTE init_sequence[] = S_DISPLAYOFF,           // 0xAE - Turn off oled panel
                           
                           S_SETDISPLAYCLOCKDIV,   // 0xD5 - Set display clock divide ratio/oscillator frequency
                           0x80,                                //      - Set divide ratio
                           
                           S_SETMULTIPLEX,              // 0xA8 - Set multiplex ratio(1 to 64)
                           0x1F,                                //      - 1/32 duty
                           
                           S_SETDISPLAYOFFSET,      // 0xD3 - Set display VERTICAL offset
                           0x00,                               //      - Not offset

                           S_SETSTARTLINE,              // 0x40 - Set start line address

                           S_CHARGEPUMP,               // 0x8D - Set Charge Pump enable/disable
                           0x14,

                           S_MEMORYMODE,              // 0x20 - Set memory adressing mode
                           0x00,

                           S_COLUMNADDR,               // 0x21 - Set column address
                           0x20,0x5F,  <-- Here, based on the calculations, it should be 0x20 + (64-1) = 0x5F since my OLED is a 64x32

                           S_PAGEADDR,                    // 0x22 - Set page address
                           0x00,0x03,

                           S_SEGREMAP | 0x01,            // 0xA1 - Set segment re-map
                           
                           S_COMSCANDEC,                   // 0xC8 - Set COM Output Scan Direction 64 to 0

                           S_SETCOMPINS,                    // 0xDA - Set com pins hardware configuration
                           0x12,

                           S_SETCONTRAST,                  // 0x81 - Set contrast control register
                           0xCF,                                    //        (0x4F is lower contrast)

                           S_SETPRECHARGE,                // 0xD9 - Set pre-charge period
                           0xF1,

                           S_SETVCOMDETECT,              // 0xDB - Set vcomh
                           0x40,                                   //      - Start line

                           S_DISPLAYALLON_RESUME,  // 0xA4 - Resume Entire Display On
                           
                           S_NORMALDISPLAY,             // 0xA6 - Set normal display
                           
                           S_DISPLAYON;                    // 0xAF - Turn on oled panel


As it stands now, I am trying to print the following:

Code:

   OLED_gotoxy( 0, 0 );
   OLED_text( "ABCDE98765", 10 );
   OLED_gotoxy( 0, 2 );
   OLED_text( "abcde01234", 10 );


The screen seems to be split in half with the right half being just random pixels on and off. The left half contains two lines. Each line is cut in half at #9 on the first line and #0 on the second line.

For the life of me, this should be quite simple. It's just a bunch of commands.

Any ideas are welcome.

Thanks again,

Ben
benoitstjean



Joined: 30 Oct 2007
Posts: 566
Location: Ottawa, Ontario, Canada

View user's profile Send private message

PostPosted: Thu Apr 05, 2018 5:06 pm     Reply with quote

Got it! Yes!!

So two things need to be done to the driver in order for the 64x32 OLED (0.49") displays to work:

1) Modify the initialization function to the following:

Code:


ROM BYTE init_sequence[] = S_DISPLAYOFF,           // 0xAE - Turn off oled panel
                           
                           S_SETDISPLAYCLOCKDIV,   // 0xD5 - Set display clock divide ratio/oscillator frequency
                           0x80,                   //      - Set divide ratio
                           
                           S_SETMULTIPLEX,         // 0xA8 - Set multiplex ratio(1 to 64)
                           0x1F,                   //      - 1/32 duty
                           
                           S_SETDISPLAYOFFSET,     // 0xD3 - Set display VERTICAL offset
                           0x00,                   //      - Not offset

                           S_SETSTARTLINE,         // 0x40 - Set start line address

                           S_CHARGEPUMP,           // 0x8D - Set Charge Pump enable/disable
                           0x14,

                           S_MEMORYMODE,           // 0x20 - Set memory adressing mode
                           0x00,

                           S_SEGREMAP | 0x01,      // 0xA1 - Set segment re-map
                           
                           S_COMSCANDEC,           // 0xC8 - Set COM Output Scan Direction 64 to 0

                           S_SETCOMPINS,           // 0xDA - Set com pins hardware configuration
                           0x12,

                           S_SETCONTRAST,          // 0x81 - Set contrast control register
                           0xCF,                   //        (0x4F is lower contrast)

                           S_SETPRECHARGE,         // 0xD9 - Set pre-charge period
                           0xF1,

                           S_SETVCOMDETECT,        // 0xDB - Set vcomh
                           0x40,                   //      - Start line

                           S_DISPLAYALLON_RESUME,  // 0xA4 - Resume Entire Display On
                           
                           S_NORMALDISPLAY,        // 0xA6 - Set normal display
                           
                           S_DISPLAYON;            // 0xAF - Turn on oled panel



2) In this function, add the 4-line snippet indicated below:

Code:

void OLED_address(unsigned int8 x, unsigned int8 y)
{
   //routine to move the memory pointers to x,y.
   //x is 0 to 127 (column), y (row) is 0 to 7 (page only)
#ifdef SH1106
   x+=2;
#endif
   i2c_start();
   i2c_write (SSDADDR); //select the display   
   i2c_write(COMMAND_ONLY); //we are sending command(s)

   //-------------- ADD FROM HERE --------------
   #define OLED_64x32
   #ifdef OLED_64x32
   x+= 0x20; // Add 0x20 offset
   #endif
   //------------------- TO HERE ------------------

   i2c_write(S_ROWADDRESS | y); // 0xB0 - select the display row
   i2c_write(S_SETLOWCOLUMN | ( x  & 0x0F )); // 0x00 - low col address
   i2c_write(S_SETHIGHCOLUMN | (( x >> 4 ) & 0x0F)); // 0x10 - high col address
   
   i2c_stop();
} //also made more efficient



So this works on the 0.49" OLED displays found on Ebay like this one:
<https://www.ebay.ca/itm/Blue-0-49-inch-OLED-Display-Module-128-32-0-49-I2C-IIC-for-Arduino-Best-glf/152750867470?ssPageName=STRK%3AMEBIDX%3AIT&_trksid=p2057872.m2749.l2649>

Note that when I say "works", I mean that displaying the following code lines works fine:

Code:

   OLED_commands( init_sequence, sizeof( init_sequence ));                       // Initialise OLED display

   set = TRUE;                                                                   // Set background to black
   size = NORMAL;

   OLED_CLS();

   OLED_gotoxy( 0, 0 );
   OLED_text( "ABCDE98765", 10 );
   OLED_gotoxy( 0, 1 );
   OLED_text( "abcde01234", 10 );

   OLED_gotoxy( 0, 2 );
   OLED_text( "abcdefghiJ", 10 );
   OLED_gotoxy( 0, 3 );
   OLED_text( "ZYXWVUTSRQ", 10 );

   while( 1 );


Prior to that, the lines would start to get displayed at the half of the 5th character then garbage was on the screen. Now everything is properly aligned.

Thanks!

Ben
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Sat Apr 14, 2018 11:14 pm     Reply with quote

Well done,
Sorry I wasn't able to help much. I've been away.
adrianleal



Joined: 16 May 2018
Posts: 1

View user's profile Send private message

AdrianLeal
PostPosted: Wed May 16, 2018 3:37 pm     Reply with quote

Ttelmah wrote:
I do a large logo on one of my units.

I just have it declared in ROM as:
Code:

ROM BYTE const Plogo[] =
{
//With 512 bytes of data here.
};

//Then send it with:
//Move a line from the ROM array into RAM
void get_line(ROM BYTE *array, BYTE * target)
{
   int16 ctr;
   for (ctr=0;ctr<64;ctr++)
      *(target++) = array[ctr*8];
}

void draw_logo(void)
{
   BYTE line[64];
   signed int16 y;
   //routine to write the logo
   //Logo is in a 512 byte array, designed to occupy the second half of the screen
   //each row on the screen is every eighth byte along the array
   set=TRUE;
   size=NORMAL;
   OLED_cls();
   for (y=7;y>=0;y--)
   {
      OLED_address(62,7-y); //position the display
      get_line(&logo[y],line); //64 bytes
      OLED_data(line,64);
   }



The key is the use of OLED_address then OLED_data to send a line of data.

It's a decision you would have to make as to whether you should write a version accepting constant data or (as I do), just buffer the lines.

This occupies the right hand half of the screen.

Have fun. Smile



Hello
the code works perfect Thank you very much
I am occupying it with PIC18F4550 at 48Mhz. with USB connection with frimware load by Bootloader.

I wanted to incorporate characters like the Bluetooth symbol; Logo and others. But here I can not do it.

You could publish or send me a more explicit code. I'm not very good at this.

Thank you so much Confused Confused
_________________
AdrianLeal
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue May 22, 2018 2:21 am     Reply with quote

adrianleal wrote:

I wanted to incorporate characters like the Bluetooth symbol; Logo and others.

The Bluetooth symbol is a stylized letter "B".
You could start with the definition for "B" from
Ttelmah's font table, and edit it to look more
like the Bluetooth symbol.

Here is the line from the font table for "B":
Code:
0xFE, 0x92, 0x92, 0x92, 0x6C, 0x00 

I have translated each byte into its binary
sequence below. An 'x' represents a '1' bit
and a '-' represents a '0' bit. So 0xFE is
1111 1110, or xxxx xxx-.

In the pattern below, you can see the letter 'B'
laying on it's side, facing down.
Code:

xxxxxxx-  0xFE 
x--x--x-  0x92 
x--x--x-  0x92 
-xx-xx--  0x6C 
--------  0x00

You can try to modify it into a Bluetooth symbol, but
you don't have very many dot positions to work with.
These characters use a 5x7 font cell. (5 dots wide,
and 7 dots tall).
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Sat May 26, 2018 1:34 pm     Reply with quote

If you don't want small characters, modify one of the characters in the 'big' fonts. These are twice the size, designed to give much nicer shaped characters, and more space for changes. These are 16*12 pixels. However you only then get 10 characters per line and four lines of text.
Manu59114



Joined: 22 Jan 2018
Posts: 34
Location: North of France

View user's profile Send private message

PostPosted: Sun Jul 29, 2018 4:33 pm     Reply with quote

Hi Ttelmah,

I migrated my (your) ssd1306 code from a PIC18f46K80 to a dsPIC33EP512MU810 and everything works fine with no compilation errors, but when i want to draw my logo with draw_logo nothing happens.

Same code on the PIC18, i can see my logo!

There is a special thing to know with a dsPIC33?

Wish you a great day.

Manu
_________________
CCS + ICD3 + PIC18F46K80
Ttelmah



Joined: 11 Mar 2010
Posts: 19569

View user's profile Send private message

PostPosted: Mon Jul 30, 2018 1:10 am     Reply with quote

I'm using it on two PIC24/33's without problems.

Obvious key thing is the declaration of the logo. Needs to be:
Code:

ROM byte const Logo[] = {
////data here
};

Careful not to use 'int', since on a PIC24/30/33, this is an int16.

As shown, draw_logo requires a 512byte array. There is no reason it can't be tweaked to send a different size.

Is the display working otherwise?. I had issues on the faster processors with needing a longer delay at boot. Also using the SSD1309 displays, these would not reliably initialise without a reset signal. Obvious things also apply, making sure the clock settings are right so you are not overclocking the I2C.
Manu59114



Joined: 22 Jan 2018
Posts: 34
Location: North of France

View user's profile Send private message

PostPosted: Mon Jul 30, 2018 3:04 am     Reply with quote

thanks Tthelma for your quick reply.
Yes, the 0.93 oled display works fine.

What is strange is that i can do everything, write text in different size, but not draw_logo!

Same code on my PIC18 works fine!

Code:

#include <TEST 1 CCS.h>
                 
#include <math.h>
#include <string.h>
#include <stdio.h> 

#ZERO_RAM                 

 // ========   OLED DISPLAY
int8 DEGREE=0;
int8 LETTC=0;                         
unsigned int8 ctr=100;
//display dimensions - the physical LCD
 #define S_LCDWIDTH               128
 #define S_LCDHEIGHT              64
 //If you want to use the SH1106, add this #define
 //#define SH1106

#define TEXT_ONLY //If this is defined, gives a smaller text driver only

//Size of graphics 'window' see the graphics section for explanation
 //#define WINDOW_WIDTH 64
 //#define WINDOW_HEIGHT 16 //sizes of the graphic window in pixels
 //height must be a multiple of 8. //these must now be defined

 #define SSDADDR 0x78 //address for the chip - usually 0x7C or 0x78.
 #define BIG_PLUS
 
               
 #include "ssd1306.h" //The OLED driver
 #include "courier.h" //The OLED driver   
 #include <NUM12.h> //Load the large font for numeric and controls
// END OLED DISPLAY
   
         
int1 level=0;

             
                                                   
void main()
{

//set_pullup(true, PIN_D10);
//set_pullup(true, PIN_D9);                         
   setup_timer1(TMR_INTERNAL | TMR_DIV_BY_1, 0);
   
       
   //*****************************************************************************
   //********************* TEST & INIT OLED DISPLAY ******************************
   //*****************************************************************************
      int8 ctr;
      char text[9]; //temporary text buffer

      delay_ms(500);                //OLED takes time to wake
                                                       
      //now try to initialise OLED
      OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
     
      //Now try some text
      set=TRUE; // ecran mode non inversé           
      size=NORMAL; // Taille police normale                     
      OLED_CLS(); // Effacer l'écran physique 
     
      draw_logo();  // DESSINE LE LOGO  DEFINIT DANS LA TABLEAU DE 512 BYTES (DRIVER SSD1306)
     
    delay_ms(2000);  //pause for two seconds                       
    OLED_CLS(); // Effacer l'écran physique 
                               
      size=NORMAL;       
     
   //Example blinking LED program
   while(true)                                                                                     
   { /*                 
      output_low(LED);
      delay_ms(DELAY);           
      output_high(LED);                                                               
      delay_ms(DELAY);       
      */                                     
                                                                                                   
     level = input_state(SW3);
     output_bit( LED_RED, !level);
     output_bit( LED_YELLOW, level);         
     output_toggle( LED_GREEN);
     
     
     
    size=NORMAL;   
    OLED_CLS(); //clear the physical screen
    OLED_gotoxy(0,0);                       
    strcpy(text,"Hello");
    OLED_text(text,strlen(text)); //This is the fastest way to write text
    delay_ms(1000);  //pause for two seconds
     
    OLED_gotoxy(0,4);
    size= LARGE;
    OLED_text(text,strlen(text)); //try some large text
    delay_ms(1000);                                     

    //Now the printf mode
    size=LARGE;                                   
    //now testing putc
    printf(OLED_putc,"\f12345\n\r67890\n\r");
    //Note how the line feed moves down by the large line when in
    //large mode
    delay_ms(1000);
    //Now printf in normal mode to the same screen 
    size=NORMAL;                         
    printf(OLED_putc,"ABCDEFGHI");

    delay_ms(1000); //delay again so you can see what it has done   
    //Now mixed size with a bar graph
    size=LARGE;                                   
    printf(OLED_putc,"\fV=");
    delay_ms(1000); //delay again so you can see what it has done 
   
    draw_logo();  // Draw the logo
   
   
    delay_ms(1000); //delay again so you can see what it has done
     
     level = input_state(PIN_D10);
     fprintf(UART_PORT1,"PIN_D10=%d",level);   
     fprintf(UART_PORT1,"HELLO WORLD"); 
     delay_ms(250);
                                   
     output_toggle(PIN_D9);
     output_toggle(PIN_D10);   
        }                                     

}


Code:

/*
 Now the big limitation.....
 In serial modes (SPI/I2C), this chip provides no ability to read back it's RAM.
 So we have a problem. If we want to write a line across the screen, and leave
 another line that is already there 'undestroyed' where they cross, how can we
 'know' the other line is there?. Basically the host chip needs to have a copy
 of the display memory so it can hold a copy of any graphics and know what is
 going on. Problem is that this is just not possible, on a PIC with limited RAM.
 So this driver works by overwriting for all text writes....

 But see further down for the 'exception' to this.
 */
 //It is most efficiently used by preparing the whole line of text first
 //and then sending this - it then uses a 'burst' transmission, to give very
 //fast updates. It does offer a 'putc' though, but this is slower.
 //It does not properly handle wrapping at the end of the line.
 //The putc function adds support for \n, \r, and \f.
 //Functions:
 //    OLED_CLS(); //clears the screen
 //    OLED_gotoxy(x, y); //goto column/row 0-20 for the column                         
 //                             //0-7 for the row
 //    OLED_text(*text,  number);
 //                             //This sends 'number' bytes from the array
 //                             //pointed to by 'text', to the display
 //    OLED_putc(c);            //sends 'c' to the display. Beware though
 //                             //if you go beyond the end of the line
 //                             //- you'll get partial characters....
 //    OLED_textbar(width);     //Displays a bargraph. With width=50
 //                             //you get a 50:50 display of bar/void.
 //Two global variables affect how things are displayed.
 //    size=NORMAL;
 //    size=LARGE;              //switches between showing 21*8 & 10*4
 //    size=DOUBLE_HEIGHT //Gives 21*4 - great for the bargraph
 //    set=TRUE;                //default. Pixels are 'set' when written, so
 //                             //turn on.
 //    set=FALSE;               //all write functions now invert.
 //The CLS will now set the screen white. Text characters print in black

 //Then the second part of the driver is a 'window' driver. With this you can
 //define a small graphic 'window', and draw things into this. This can then be
 //rapidly copied to the display. So you could (for instance), plot a tiny graph,
 //end then draw this on the display.
 //The window must be a multiple of 8 pixels high, and can only be placed
 //at a 'text' location, so you can't put it (say) 12 pixels down the screen,
 //but only 8, 16, 24 etc..
 //The size of the window determines how much RAM is used. So a 64*16 window
 //uses 128 bytes of RAM (64*16/8).
 //Neat thing is though, that you can draw an image on the window, put this on
 //the screen, and then draw a second image, and put this somewhere else,
 //without using any more memory. At the moment, I have only implemented two
 //functions to draw to this window.
 //If you don't want the graphic ability, if you #define TEXT_ONLY, then only
 //the text mode driver will be loaded.
 //With the graphic driver being used, the following extra functions are
 //available:
 //   clear_window();          //clears all pixels 'black' (if set==TRUE), or white.
 //   set_pixel(x, y);         //sets a pixel at x,y in the window.
 //                            //x=0 to WINDOW_WIDTH-1 (left to right)
 //                            //y=0 to WINDOW_HEIGHT-1 (top to bottom)
 //   line(x1, y1, x2, y2);    //draws a line from x1,y1 to x2, y2
 //   rect(x1, y1, x2, y2);    //draws a rectangle
 //   circle(x, y, radius, fill); //draws a circle
 //   //This draws a circle of radius 'radius' centred at x,y. If 'fill' is       
 //   //true this is filled....
 //
 //   draw_window(x, int8 y);  //This draws the window onto the screen at
 //                            //x=0 to 128, y=0 to 7.                                     
 //What you do is simply draw the shape you want into the window, and then
 //this can be drawn onto the screen.
 //I have not included font drawing, since this takes a lot more space....
 //'set' again controls whether a pen, or eraser is used. So (for example), if you
 //wanted a 'thick' circle, you could either draw several using the pen,
 //without 'fill' enabled, or could draw one with fill, then change set to
 //false, and draw a smaller one, to give a thick ring.


 #define COMMAND_ONLY 0b00000000 //next byte is a command only
 #define DATA_ONLY 0b01000000 //next byte is data

 //directly from the data sheet - commands - not all used
 #define S_EXTERNALVCC            0x1
 #define S_SWITCHCAPVCC           0x2
 #define S_SETLOWCOLUMN           0x00
 #define S_SETHIGHCOLUMN          0x10
 #define S_MEMORYMODE             0x20
 #define S_COLUMNADDR             0x21
 #define S_PAGEADDR               0x22
 #define S_SETSTARTLINE           0x40
 #define S_ROWADDRESS             0xB0
 #define S_SETCONTRAST            0x81
 #define S_CHARGEPUMP             0x8D
 #define S_SEGREMAP               0xA0
 #define S_DISPLAYALLON_RESUME    0xA4
 #define S_DISPLAYALLON           0xA5
 #define S_NORMALDISPLAY          0xA6
 #define S_INVERTDISPLAY          0xA7
 #define S_SETMULTIPLEX           0xA8
 #define S_DISPLAYOFF             0xAE
 #define S_DISPLAYON              0xAF
 #define S_COMSCANINC             0xC0
 #define S_COMSCANDEC             0xC8
 #define S_SETDISPLAYOFFSET       0xD3
 #define S_SETCOMPINS             0xDA
 #define S_SETVCOMDETECT          0xDB
 #define S_SETDISPLAYCLOCKDIV     0xD5
 #define S_SETPRECHARGE           0xD9                                                                     
 #define DIV_RATIO                0x80 //recommended ratio
 #define MULTIPLEX                0x3F //and multiplex
 #define INT_VCC                  0x14

ROM BYTE const Plogo[] =
{                                 
0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1C,
0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x1E, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x1C,
0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x1C, 
0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x1C,
0x00, 0x7F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x7F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x7F, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x78,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC,
0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xFC,
0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00,
0x00, 0x3F, 0xC7, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x00, 0xFC, 0x00, 0x0E, 0x00, 0x10,
0x00, 0x7C, 0x00, 0x7C, 0x00, 0x0F, 0x00, 0x38, 0x00, 0x78, 0x00, 0x3E, 0x00, 0x0F, 0x80, 0x38,
0x00, 0x78, 0x00, 0x1E, 0x00, 0x0F, 0xC0, 0x1C, 0x00, 0xF0, 0x00, 0x1E, 0x00, 0x0F, 0xE0, 0x1C,
0x00, 0xF0, 0x00, 0x1E, 0x00, 0x0E, 0xF0, 0x1C, 0x00, 0x78, 0x00, 0x1E, 0x00, 0x0E, 0x7C, 0x1C,
0x00, 0x78, 0x00, 0x3E, 0x00, 0x0E, 0x3E, 0x7C, 0x00, 0x7C, 0x00, 0x7C, 0x00, 0x0E, 0x1F, 0xF8,
0x00, 0x7E, 0x00, 0xFC, 0x00, 0x0E, 0x0F, 0xF8, 0x00, 0x3F, 0xC7, 0xFC, 0x00, 0x0E, 0x03, 0xE0,
0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x07, 0xFC, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x0F, 0xFF, 0xFC,
0x00, 0x00, 0xFF, 0xFC, 0x00, 0x0F, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x0C, 0x0C,
0x00, 0x3F, 0xFF, 0x00, 0x00, 0x00, 0x0C, 0x0C, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x00, 0x1C, 0x0C,
0x00, 0x7F, 0xC0, 0x00, 0x00, 0x00, 0x1E, 0x1C, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x3C,
0x00, 0x7F, 0xC0, 0x00, 0x00, 0x07, 0xFF, 0xFC, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0x0F, 0xF7, 0xF8,
0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0F, 0xC3, 0xF0, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x0C, 0x00, 0x00,
0x00, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x01, 0xFE, 0x00,
0x00, 0x00, 0x07, 0xFC, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x07, 0xFF, 0x80,
0x00, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0x3B, 0x80, 0x00, 0x00, 0x00, 0x04, 0x00, 0x0E, 0x39, 0xC0,
0x00, 0x00, 0xFE, 0x00, 0x00, 0x0E, 0x39, 0xC0, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x0E, 0x39, 0xC0,
0x00, 0x0F, 0xFF, 0xE0, 0x00, 0x0E, 0x3B, 0xC0, 0x00, 0x1F, 0xFF, 0xF0, 0x00, 0x0E, 0x3F, 0x80,
0x00, 0x3F, 0xFF, 0xF8, 0x00, 0x0E, 0x3F, 0x00, 0x00, 0x7F, 0x3C, 0xFC, 0x00, 0x04, 0x3E, 0x00,
0x00, 0x7C, 0x3C, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x3C, 0x3E, 0x00, 0x00, 0x00, 0x00,
0x00, 0x78, 0x3C, 0x1E, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x78, 0x3C, 0x1E, 0x00, 0x0F, 0xFF, 0x80,
0x00, 0x70, 0x3C, 0x1E, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x70, 0x3C, 0x1E, 0x00, 0x00, 0x07, 0x80,
0x00, 0x70, 0x3C, 0x3E, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x3C, 0x7C, 0x00, 0x00, 0x01, 0xC0,
0x00, 0x78, 0x3D, 0xFC, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x78, 0x3F, 0xF8, 0x00, 0x00, 0x0F, 0xC0,
0x00, 0x78, 0x3F, 0xF8, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x38, 0x3F, 0xE0, 0x00, 0x0F, 0xFF, 0x00,
0x00, 0x00, 0x3F, 0x80, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xC0,
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0x80, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0x00,
0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x00,
0x00, 0x00, 0x00, 0x3C, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0xCF, 0x80,
0x00, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0x03, 0xC0, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x0E, 0x01, 0xC0,
0x00, 0x1F, 0xFF, 0xFF, 0xE0, 0x0E, 0x01, 0xC0, 0x00, 0x3F, 0xFF, 0xFF, 0xF0, 0x0E, 0x01, 0xC0,
0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x0F, 0x03, 0xC0, 0x00, 0x7F, 0xFF, 0xFF, 0xF0, 0x07, 0xFF, 0x80,
0x00, 0x7C, 0x00, 0x3C, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x03, 0xFF, 0x00,
0x00, 0x78, 0x00, 0x3C, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x78, 0x00, 0x3C, 0x00, 0x00, 0x01, 0x80,
0x00, 0x78, 0x00, 0x3C, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xF0,
0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0xFF, 0xF8, 0x10, 0x00, 0x00, 0x3C, 0x00, 0x0F, 0xFF, 0xF8,
0xF0, 0x00, 0x00, 0xFC, 0x00, 0x0F, 0x01, 0x80, 0xF8, 0x00, 0x07, 0xFC, 0x00, 0x0E, 0x01, 0x80,
0xF8, 0x00, 0x1F, 0xFC, 0x00, 0x0E, 0x01, 0x80, 0xFC, 0x00, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00,
0x7E, 0x03, 0xFF, 0xE0, 0x00, 0x01, 0xFC, 0x00, 0x3F, 0x9F, 0xFF, 0x80, 0x00, 0x03, 0xFF, 0x00,
0x1F, 0xFF, 0xFC, 0x00, 0x00, 0x07, 0xFF, 0x80, 0x0F, 0xFF, 0xE0, 0x00, 0x00, 0x0F, 0xBF, 0x80,
0x07, 0xFF, 0x00, 0x00, 0x00, 0x0F, 0x39, 0xC0, 0x01, 0xFF, 0xC0, 0x00, 0x00, 0x0E, 0x39, 0xC0,
0x00, 0x7F, 0xF8, 0x00, 0x00, 0x0E, 0x39, 0xC0, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x0E, 0x3B, 0xC0,
0x00, 0x03, 0xFF, 0xE0, 0x00, 0x0E, 0x3F, 0x80, 0x00, 0x00, 0xFF, 0xFC, 0x00, 0x0E, 0x3F, 0x80,
0x00, 0x00, 0x1F, 0xFC, 0x00, 0x06, 0x3F, 0x00, 0x00, 0x00, 0x07, 0xFC, 0x00, 0x00, 0x38, 0x00,
0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, 0x00
};                                                                           
 
 //Font 6*8 - slightly clearer than most fonts this size.
 ROM BYTE font[] =
 {
         0x00, 0x00, 0x00, 0x00, 0x00, 0x00,      // Code for char
         0x00, 0x00, 0xBE, 0x00, 0x00, 0x00,      // Code for char !
         0x00, 0x00, 0x03, 0x00, 0x03, 0x00,      // Code for char "
         0x50, 0xF8, 0x50, 0xF8, 0x50, 0x00,      // Code for char #
         0x48, 0x54, 0xFE, 0x54, 0x24, 0x00,      // Code for char $
         0x98, 0x58, 0x20, 0xD0, 0xC8, 0x00,      // Code for char %                                     
         0x60, 0x9C, 0xAA, 0x44, 0x80, 0x00,      // Code for char &                                 
         0x00, 0x00, 0x00, 0x03, 0x00, 0x00,      // Code for char '
         0x00, 0x38, 0x44, 0x82, 0x00, 0x00,      // Code for char (
         0x00, 0x82, 0x44, 0x38, 0x00, 0x00,      // Code for char )
         0x02, 0x06, 0x03, 0x06, 0x02, 0x00,      // Code for char *
         0x10, 0x10, 0x7C, 0x10, 0x10, 0x00,      // Code for char +
         0xA0, 0x60, 0x00, 0x00, 0x00, 0x00,      // Code for char ,
         0x10, 0x10, 0x10, 0x10, 0x10, 0x00,      // Code for char -
         0xC0, 0xC0, 0x00, 0x00, 0x00, 0x00,      // Code for char .
         0x80, 0x40, 0x20, 0x10, 0x08, 0x00,      // Code for char /
         0x7C, 0xA2, 0x92, 0x8A, 0x7C, 0x00,      // Code for char 0
         0x00, 0x84, 0xFE, 0x80, 0x00, 0x00,      // Code for char 1
         0xC4, 0xA2, 0x92, 0x92, 0x8C, 0x00,      // Code for char 2
         0x44, 0x82, 0x92, 0x92, 0x6C, 0x00,      // Code for char 3
         0x18, 0x14, 0x12, 0xFE, 0x10, 0x00,      // Code for char 4
         0x9E, 0x92, 0x92, 0x92, 0x62, 0x00,      // Code for char 5
         0x7C, 0x92, 0x92, 0x92, 0x64, 0x00,      // Code for char 6
         0x06, 0x02, 0xE2, 0x12, 0x0E, 0x00,      // Code for char 7
         0x6C, 0x92, 0x92, 0x92, 0x6C, 0x00,      // Code for char 8
         0x4C, 0x92, 0x92, 0x92, 0x7C, 0x00,      // Code for char 9
         0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00,      // Code for char :
         0xAC, 0x6C, 0x00, 0x00, 0x00, 0x00,      // Code for char ;
         0x00, 0x10, 0x28, 0x44, 0x82, 0x00,      // Code for char <
         0x48, 0x48, 0x48, 0x48, 0x48, 0x00,      // Code for char =
         0x00, 0x82, 0x44, 0x28, 0x10, 0x00,      // Code for char >
         0x04, 0x02, 0xB2, 0x12, 0x0C, 0x00,      // Code for char ?
         0x7C, 0x82, 0xBA, 0xAA, 0xBC, 0x00,      // Code for char @
         0xF8, 0x14, 0x12, 0x14, 0xF8, 0x00,      // Code for char A
         0xFE, 0x92, 0x92, 0x92, 0x6C, 0x00,      // Code for char B
         0x7C, 0x82, 0x82, 0x82, 0x44, 0x00,      // Code for char C
         0xFE, 0x82, 0x82, 0x44, 0x38, 0x00,      // Code for char D
         0xFE, 0x92, 0x92, 0x82, 0x82, 0x00,      // Code for char E
         0xFE, 0x12, 0x12, 0x02, 0x02, 0x00,      // Code for char F
         0x7C, 0x82, 0x92, 0x92, 0xF4, 0x00,      // Code for char G
         0xFE, 0x10, 0x10, 0x10, 0xFE, 0x00,      // Code for char H
         0x00, 0x82, 0xFE, 0x82, 0x00, 0x00,      // Code for char I
         0x60, 0x80, 0x80, 0x80, 0x7E, 0x00,      // Code for char J
         0xFE, 0x10, 0x18, 0x24, 0xC2, 0x00,      // Code for char K
         0xFE, 0x80, 0x80, 0x80, 0x80, 0x00,      // Code for char L
         0xFE, 0x04, 0x38, 0x04, 0xFE, 0x00,      // Code for char M
         0xFE, 0x04, 0x08, 0x10, 0xFE, 0x00,      // Code for char N
         0x7C, 0x82, 0x82, 0x82, 0x7C, 0x00,      // Code for char O
         0xFE, 0x12, 0x12, 0x12, 0x0C, 0x00,      // Code for char P
         0x7C, 0x82, 0xA2, 0xC2, 0xFC, 0x00,      // Code for char Q
         0xFE, 0x12, 0x12, 0x12, 0xEC, 0x00,      // Code for char R
         0x4C, 0x92, 0x92, 0x92, 0x64, 0x00,      // Code for char S
         0x02, 0x02, 0xFE, 0x02, 0x02, 0x00,      // Code for char T
         0x7E, 0x80, 0x80, 0x80, 0x7E, 0x00,      // Code for char U
         0x3E, 0x40, 0x80, 0x40, 0x3E, 0x00,      // Code for char V
         0xFE, 0x80, 0x70, 0x80, 0xFE, 0x00,      // Code for char W
         0xC6, 0x28, 0x10, 0x28, 0xC6, 0x00,      // Code for char X
         0x06, 0x08, 0xF0, 0x08, 0x06, 0x00,      // Code for char Y
         0xC2, 0xA2, 0x92, 0x8A, 0x86, 0x00,      // Code for char Z
         0x00, 0xFE, 0x82, 0x82, 0x00, 0x00,      // Code for char [
         0x08, 0x10, 0x20, 0x40, 0x80, 0x00,      // Code for char BackSlash
         0x00, 0x82, 0x82, 0xFE, 0x00, 0x00,      // Code for char ]
         0x00, 0x08, 0x04, 0x02, 0x04, 0x08,      // Code for char ^
         0x80, 0x80, 0x80, 0x80, 0x80, 0x00,      // Code for char _
         0x00, 0x00, 0x02, 0x04, 0x00, 0x00,      // Code for char `
         0x40, 0xA8, 0xA8, 0xA8, 0xF0, 0x00,      // Code for char a
         0xFE, 0x88, 0x88, 0x88, 0x70, 0x00,      // Code for char b
         0x70, 0x88, 0x88, 0x88, 0x10, 0x00,      // Code for char c
         0x70, 0x88, 0x88, 0x88, 0xFE, 0x00,      // Code for char d
         0x70, 0xA8, 0xA8, 0xA8, 0x30, 0x00,      // Code for char e
         0x10, 0xFC, 0x12, 0x12, 0x04, 0x00,      // Code for char f
         0x90, 0xA8, 0xA8, 0xA8, 0x70, 0x00,      // Code for char g
         0xFE, 0x10, 0x10, 0x10, 0xE0, 0x00,      // Code for char h
         0x00, 0x90, 0xF4, 0x80, 0x00, 0x00,      // Code for char i
         0x40, 0x80, 0x80, 0x90, 0x74, 0x00,      // Code for char j
         0xFE, 0x20, 0x50, 0x88, 0x00, 0x00,      // Code for char k
         0x7E, 0x80, 0x80, 0x00, 0x00, 0x00,      // Code for char l
         0xF8, 0x08, 0x70, 0x08, 0xF0, 0x00,      // Code for char m
         0xF8, 0x08, 0x08, 0x08, 0xF0, 0x00,      // Code for char n
         0x70, 0x88, 0x88, 0x88, 0x70, 0x00,      // Code for char o
         0xF8, 0x28, 0x28, 0x28, 0x10, 0x00,      // Code for char p
         0x10, 0x28, 0x28, 0xF8, 0x80, 0x00,      // Code for char q
         0xF8, 0x08, 0x08, 0x08, 0x10, 0x00,      // Code for char r
         0x90, 0xA8, 0xA8, 0xA8, 0x48, 0x00,      // Code for char s
         0x08, 0x08, 0xFE, 0x88, 0x88, 0x00,      // Code for char t
         0x78, 0x80, 0x80, 0x80, 0xF8, 0x00,      // Code for char u
         0x38, 0x40, 0x80, 0x40, 0x38, 0x00,      // Code for char v
         0xF8, 0x80, 0x70, 0x80, 0xF8, 0x00,      // Code for char w
         0x88, 0x50, 0x20, 0x50, 0x88, 0x00,      // Code for char x
         0x18, 0xA0, 0xA0, 0xA0, 0x78, 0x00,      // Code for char y
         0x88, 0xC8, 0xA8, 0x98, 0x88, 0x00,      // Code for char z
         0x00, 0x10, 0x6C, 0x82, 0x00, 0x00,      // Code for char {
         0x00, 0x00, 0xFE, 0x00, 0x00, 0x00,      // Code for char |
         0x00, 0x82, 0x6C, 0x10, 0x00, 0x00,      // Code for char }
         0x00, 0x08, 0x04, 0x08, 0x10, 0x08,      // Code for char ~
         0x7C, 0x7C, 0x00, 0x00, 0x00, 0x00,      // Code for char 
 //Characters 32 to 127       
 //The next eleven characters are above 127, and give the shapes used for
 //the bar graph capability - remove if not needed
         0x82, 0x82, 0x82, 0x82, 0x82, 0x82,      //top and bottom bars only 128
         
         0xFE, 0x82, 0x82, 0x82, 0x82, 0x82,      //Open for bar 129
         0xFE, 0xFE, 0x82, 0x82, 0x82, 0x82,      //second bar
         0xFE, 0xFE, 0xFE, 0x82, 0x82, 0x82,
         0xFE, 0xFE, 0xFE, 0xFE, 0x82, 0x82,
         0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x82,             
         0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,     
         //Full block for bar 134
         
         0x82, 0x82, 0x82, 0x82, 0xFE, 0xFE,      //final one cloing shape 135
         0xFE, 0x82, 0x82, 0x82, 0xFE, 0xFE,      //single left and double right       
         0xFE, 0xFE, 0x82, 0x82, 0xFE, 0xFE,
         0xFE, 0xFE, 0xFE, 0x82, 0xFE, 0xFE,
         0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,       
         0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE       //New closing point for 0..100
         //final one closing shape 140
 };
 //Character 140
 
 // ACTION A REALISER POUR ENDORMIR LE SSD1306
 ROM BYTE SLEEP_sequence[] =S_SETCONTRAST,
 0x00,
 S_DISPLAYOFF;
                                   
 //ACTION POUR REVEILLER LE SSD1306
 ROM BYTE WAKEUP_sequence[] =
 S_DISPLAYON,
 S_SETCONTRAST,
 0xCF; //  0x00 to 0xFF
 
 
  //ROTATE 180°
 ROM BYTE ROTATE_180_sequence[] =
(S_SEGREMAP | 0x1),
S_COMSCANDEC;

  //ROTATE 0°
 ROM BYTE ROTATE_0_sequence[] =
S_SEGREMAP,   
S_COMSCANINC; // flip Haut/bas
         
                                     
 
 ROM BYTE init_sequence[] = S_DISPLAYOFF,
        S_SETDISPLAYCLOCKDIV,
        DIV_RATIO,                 
        S_SETMULTIPLEX,
        MULTIPLEX,
        S_SETDISPLAYOFFSET,
        0,                                   // no offset   
        S_SETSTARTLINE,
        S_CHARGEPUMP,                                                                                                               
        INT_VCC,                             // using internal VCC
        S_MEMORYMODE,                        //Since byte is vertical writing column by column
        0,                                   // default horizontal addressing
        (S_SEGREMAP | 0x1),                  // (S_SEGREMAP | 0x1),     rotate screen 180
        S_COMSCANDEC,                // S_COMSCANDEC, S_COMSCANINC,       
        S_SETCOMPINS,                 
        0x12,
        S_SETCONTRAST,
        0xEF,                                //experiment.... 0xCf for 1306
        S_SETPRECHARGE,
        0xF1,
        S_SETVCOMDETECT,
        0x40,
        S_DISPLAYALLON_RESUME,                 
        S_NORMALDISPLAY,                     //S_NORMALDISPLAY   S_INVERTDISPLAY   
        S_DISPLAYON;                         //switch on OLED
       
       
 //Initilalisation sequence 
 #define NORMAL 0
 #define DOUBLE_HEIGHT 1
 #define DOUBLE_WIDTH 2
 #define LARGE DOUBLE_HEIGHT+DOUBLE_WIDTH
 unsigned int8 O_current_col,O_current_row; //where text is currently 'working'
 int8 size=NORMAL;

 //Global flag for drawing mode
 int1 set=TRUE; //allow funstions to set or reset - inverts drawing functions

 #ifndef TEXT_ONLY
 unsigned int8 window_buffer[WINDOW_WIDTH*WINDOW_HEIGHT/8];
 //so with the example given, 128 bytes of RAM - much more practical on small chips!...
 //This is the 'graphic window' buffer, so not needed for text only
 #endif
 

     
 void OLED_commands(rom unsigned int8 * commands, unsigned int8 number)
 //send a multiple command, or commands to the display - number says how many
 //from a ROM buffer
 {
    int8 ctr; //counter for the transmission
    i2c_start ();
    i2c_write (SSDADDR); //select the display
    i2c_write (COMMAND_ONLY); //we are sending a command     
    for (ctr=0;ctr<number;ctr++)
    {   
       I2c_write(commands[ctr]);
    }
    i2c_stop();
 }

 void OLED_data(unsigned int8 * data, unsigned int8 number)
 //send 'number' bytes of data to display - from RAM
 {
    int8 ctr;                                                                                   
    i2c_start ();
    i2c_write (SSDADDR); //select the display
    i2c_write (DATA_ONLY); //we are sending data(s)
    for (ctr=0;ctr<number;ctr++)
       i2c_write(data[ctr]); //send the byte(s)
    i2c_stop ();   
 }

 void OLED_address(unsigned int8 x, unsigned int8 y)
 {
    //routine to move the memory pointers to x,y.
    //x is 0 to 127 (column), y (row) is 0 to 7 (page only)
 #ifdef SH1106
    x+=2;
 #endif
    i2c_start();
    i2c_write(SSDADDR); //select the display   
    i2c_write(COMMAND_ONLY); //we are sending command(s)
    i2c_write(S_ROWADDRESS | y); //select the display row
    i2c_write(S_SETLOWCOLUMN | (x & 0x0F)); //low col address
    i2c_write(S_SETHIGHCOLUMN | ((x>>4) & 0x0F)); //high col address
    i2c_stop();
 } //also made more efficient


 void OLED_gotoxy(unsigned int8 x, unsigned int8 y)
 {
    //text x,y position bases on 8 lines/character and 6 columsn
    //0 to 20 columns, 0 to 7 rows
    if (x>(S_LCDWIDTH/6)-1) return;
    if (y>(S_LCDHEIGHT/8)-1) return;
    O_current_col=x; //efficient *6
    O_current_row=y;
    OLED_address((unsigned int16)x*4+(unsigned int16)x*2,y); //position display
 }

 void OLED_CLS(void)
 {
    unsigned int8 row, col;

    //Just fill the memory with zeros
    for (row=0;row<S_LCDHEIGHT/8;row++)
    {
       OLED_address(0,row); //take the addresses back to 0,0 0,1 etc..
       i2c_start();
       i2c_write(SSDADDR); //select the display
       i2c_write(DATA_ONLY); //we are sending data(s)
       for (col=0;col<S_LCDWIDTH;col++)
       {
          if (set)
             i2c_write (0); //send 1024 zeros
          else
             i2c_write(255); //or the inverse
       }
       i2c_stop ();
    }
    OLED_gotoxy(0,0); //and text back to the top corner   
 }

 //Macros to efficiently double bits from a nibble
 #define DOUBLE_BIT(N, S, D) if (bit_test(S,N)) { bit_set(D,(N*2)); bit_set(D,(N*2)+1); }
 #define DOUBLE_B_HIGH(N, S, D) if (bit_test(S,N+4)) { bit_set(D,(N*2)); bit_set(D,(N*2)+1); }
 
  //Then send it with:
 //Move a line from the ROM array into RAM

 
 void invert(unsigned int8 * buffer, unsigned int8 number) //routine to invert
 {//data when'set==FALSE'
    do
    {
       *buffer^=0xFF;
       buffer++;
    } while(--number); //invert all the bits in the buffer
 }

 //Change here to allow multiple fonts
 //This routine can be used by multiple output routines
 void FONT_line(ROM unsigned int8 * font_data, int8 count)
 {
    //new function to transfer a line of data from the font table.
    //designed to optimise the handling of double size fonts
    //Sends one line of 'count' characters from the font to the display,
    //with doubling of width if necessary. Maximum 12 chars.
    unsigned int8 cols[24], ctr=0, width, inc_col, tchr, temp=0;
    if (size & DOUBLE_WIDTH)
    {
       width=count*2;
       inc_col=2;
    }
    else
    {
       width=count;
       inc_col=1;
    }
       

    for (ctr=0;ctr<width;ctr+=inc_col) //for columns
    {
       cols[ctr]=0;
       tchr=font_data[temp++]; //one byte of character from the font
       if (size & DOUBLE_HEIGHT)
       {
          DOUBLE_BIT(0,tchr,cols[ctr])
          DOUBLE_BIT(1,tchr,cols[ctr])
          DOUBLE_BIT(2,tchr,cols[ctr])
          DOUBLE_BIT(3,tchr,cols[ctr])               
       } //efficently double the bits from the low nibble
       else
          cols[ctr]=tchr;
       if (size & DOUBLE_WIDTH)
          cols[ctr+1]=cols[ctr]; //duplicate the byte
    }
    if (set==FALSE)
       invert(cols,width);
    OLED_data(cols,width);
    //Now if doubling in height repeat usiong the other nibble
    if (size & DOUBLE_HEIGHT)
    {
       temp=0; //back to the start of the font character
       OLED_address((unsigned int16)O_current_col*4+O_current_col*2,O_current_row+1);  //next row
       for (ctr=0;ctr<width;ctr+=inc_col) //through the columns again
       {
          cols[ctr]=0;
          tchr=font_data[temp++]; //one byte of character from the font
          DOUBLE_B_HIGH(0,tchr,cols[ctr])
          DOUBLE_B_HIGH(1,tchr,cols[ctr])
          DOUBLE_B_HIGH(2,tchr,cols[ctr])
          DOUBLE_B_HIGH(3,tchr,cols[ctr])
          //efficently double the bits from the high nibble
          if (size & DOUBLE_WIDTH)
             cols[ctr+1]=cols[ctr]; //duplicate the byte if double_width
       }
       if (set==FALSE)
          invert(cols,width);         
       //Now send the bytes for the second row
       OLED_data(cols,width);
    }
 }

 //Fastest text mode. No support for any control. Standard font only.
 void OLED_text(unsigned int8 * text, unsigned int8 number)
 {
    unsigned int8 inc_col;
    unsigned int16 temp;
    //size allows double height & double width
    //Here double height/width
    //for this I have to do two transfers each of double the amount of data
    //and reposition between each
    if (size & DOUBLE_WIDTH)
    {
       inc_col=2;
    }
    else
    {
       inc_col=1;
    }
       
    do {
       temp=(*text)-32;
       if (temp>108)
          temp=0; //block illegal characters         
       temp=((unsigned int16)temp*2)+((unsigned int16)temp*4); //efficient *6
       FONT_line(&font[temp],6); //six characters from the font
       //Now because I'll be in the wrong position (may be one line down)
       //have to re-locate
       O_current_col+=inc_col;
       OLED_address((unsigned int16)O_current_col*4+O_current_col*2,O_current_row);  //ready for next character
       text++; //and select the next character
    } while (--number); //and on to the next character
 }

 void OLED_putc(unsigned int8 chr)
 {
    //this is a putc wrapper for the text function - note much slower
    //than sending the entire line directly
    if (chr=='\f')
    {
       OLED_CLS(); //handle Clear screen (form feed)
       return;
    }
    if (chr=='\r')
    {
       OLED_gotoxy(0,O_current_row); //carriage return
       return;
    }
    if (chr=='\n')
    {
       if (size==NORMAL)
          OLED_gotoxy(O_current_col,O_current_row+1);
       else
          OLED_gotoxy(O_current_col,O_current_row+2);
       return;
    }
    OLED_text(&chr,1);
 }

 void OLED_textbar(unsigned int8 width)
 {
    //This draws a bar graph using text characters
    int8 ctr;
    unsigned int8 bar[9];
    //graph is now 0 to 100.
    //prints at current text location.
    width/=2;
    width+=2;  //ensure >0 - g1ves 2 to 52
    if (width>52)
       width=52;
    for (ctr=0;ctr<8;ctr++)
    {
       if (width>=6)
       {
          bar[ctr]=134; //full bar
          width-=6;
       }
       else
       {       
          bar[ctr]=128+width; //partial bars
          width=0;
       }
    }
    //now handle the right hand end of the bar
    bar[8]=135+width;
    OLED_text(bar,9);
 }

 //Now comes the 'exception' part to the driver
 //Basic line & circle code writing to a small _window_ that can then
 //be burst transmitted to the LED. Neat thing is that the same window
 //can be used multiple times. So (for instance) you could write text on the
 //left of the display, then have a 64*32 window. Draw something into this
 //and display it at 64, 0 (text row) on the display, then draw something
 //different (using the same window), and put this at 64, 4. The location
 //the window is drawn has to be a byte boundary (so 0 to 7, for 0 to 63
 //on the display.
 //The sequence would be clear_window, line, circle etc.. Then draw_window(x,y)
 //If you don't want to use the smace for this, then #define TEXT_ONLY at the start
 //of the code

 #ifndef TEXT_ONLY
 void clear_window(void)
 {
    memset(window_buffer,(set)?0:255,WINDOW_WIDTH*WINDOW_HEIGHT/8); //clear the buffer
 }

 //Basic pixel routine
 #inline
 void set_pixel(unsigned int8 x, unsigned int8 y)
 {
    unsigned int16 locn; //This can be int8, if buffer is restricted to max 256 bytes
    //potentially slightly faster. However 1616 is 'generic'.
    if (x>=WINDOW_WIDTH) return;
    if (y>=WINDOW_HEIGHT) return; //Limit check - ensures I do not try to write
    //outside buffer boundaries
    locn=((y/8)*WINDOW_WIDTH)+x; //location in buffer
    //handle setting or resetting the pixel according to flag 'set'
    if (set)
       bit_set(window_buffer[locn],(y & 7)); //set the bit (to 1)
    else
       bit_clear(window_buffer[locn],(y & 7)); //set the bit (to 0)
 }                                                                                                                                         

 //efficient line routine
 void line(unsigned int8 x1, unsigned int8 y1, unsigned int8 x2, unsigned int8 y2)
 {
    unsigned int16 i;                                                                                                         
    int1 _plot;
    signed int16 _dx,_dy;
    unsigned int16 _ix,_iy,_inc,_plotx,_ploty,_x,_y;   
    //line from X1,Y1 to X2,Y2
    //first the differences between the coordinate pairs
    _dx=(signed int16) x2-x1;
    _dy=(signed int16) y2-y1;
    // ix & iy are the absolute increments required
    _ix=(_dx<0)?(-_dx):_dx;
    _iy=(_dy<0)?(-_dy):_dy;
    // we must step the longest length (x or y)
    _inc=(_ix>_iy)?_ix:_iy;   
    // make dx and dy the step required.
    if (_dx>0) _dx=1;   else if (_dx<0) _dx=-1;
    if (_dy>0) _dy=1;   else if (_dy<0) _dy=-1;
    // actual plotting points
    _plotx=x1;
    _ploty=y1;   
    // start at 0
    _x=0;
    _y=0;
    // set endpoint
    set_pixel(_plotx,_ploty);   
    /* we implement Bressenhams algorithm for a line using integer steps
    and only plotting the point when we get to a new co-ord pair */
    for (i=0;i<=_inc;++i)
    {
       _x += _ix;
       _y += _iy;
       // do not plot yet
       _plot=FALSE;
       //if we are at a new pair - set the plot flag and increment
       //the phsical plotting point
       if (_x > _inc)
       {
          _plot = TRUE;
          _x -= _inc;
          _plotx += _dx;
       }
       if (_y > _inc)
       {
          _plot = TRUE;
          _y -= _inc;
          _ploty += _dy;
       }
       // now plot the point
       if (_plot)
       {
          set_pixel(_plotx,_ploty); //this automatically handles set/reset
       }
    }   
 }

 void rect(unsigned int8 x1, unsigned int8 y1, unsigned int8 x2, unsigned int8 y2)
 {
    //outline a rectangle
    line(x1, y1, x2, y1);      //Just draw four sides
    line(x1, y2, x2, y2);
    line(x1, y1, x1, y2);
    line(x2, y1, x2, y2);   
 }

 void circle(unsigned int8 x, unsigned int8 y, unsigned int8 radius, int1 fill)
 {
    signed int8  a, b, P;
    a = 0;
    b = radius;
    P = 1 - radius;
    do
    {
       if(fill)
       {
          line(x-a, y+b, x+a, y+b);
          line(x-a, y-b, x+a, y-b);
          line(x-b, y+a, x+b, y+a);
          line(x-b, y-a, x+b, y-a); //inefficient but easy to code....
       }
       else
       {
          set_pixel(a+x, b+y);
          set_pixel(b+x, a+y);
          set_pixel(x-a, b+y);
          set_pixel(x-b, a+y);
          set_pixel(b+x, y-a);
          set_pixel(a+x, y-b);
          set_pixel(x-a, y-b);
          set_pixel(x-b, y-a);  //othewise draw the octant points
       }
       if(P < 0)
          P += 3 + 2 * a++;
       else
          P += 5 + 2 * (a++ - b--);
     } while(a <= b);
 }

 void draw_window(unsigned int8 x, unsigned int8 y)
 {
    unsigned int8 yctr;
    unsigned int16 transfer=WINDOW_WIDTH;
    //routine to copy the window to the display.
    //x is in pixels, y in bytes (0 to 8). Uses burst transmission for each line
    if ((x+transfer)>=S_LCDWIDTH)
    {
       //here the window would go off the edge of the screen...
       transfer=(S_LCDWIDTH-1)-x;
    }     
    for (yctr=0;yctr<(WINDOW_HEIGHT/8);yctr++)
    {
       if (y+yctr>7) return; //off the end of RAM
       OLED_address(x,y+yctr); //position to the byte at the start of the line
       //transfer the line
       OLED_data(window_buffer+((unsigned int16)yctr*WINDOW_WIDTH),transfer);
       //again int16 only needed here if buffer>256 bytes
    }
 }
 #endif   
 
//Add this to the driver if you want to use larger fonts.
 //Now handling for big font modes
 #define NUM_ONLY 0
 #define NUM_PLUS 1
 #define UC_ONLY 2
 #define ALL_ALPHA 3 //defines for what character set is allowed
 //This allows the same functions to be used with only partial fonts.
 #define LETTC ':'
 #define LETTF ';'
 #define COPYWRITE '<'
 #define DEGREE '='
 #define SQUARED '>'
 #DEFINE CUBED '?'//defines for extra symbols from the 'BIG_PLUS' font.

 //New generic big font function
 //New function to generate large characters from 12*16 fonts
 //supporting double size/width on this
 void OLED_Bigtext(unsigned int8 * text, ROM byte * font, int8 allowed)
 {
    unsigned int8 inc_col, inc_row;
    unsigned int16 temp;
    int8 row,col,firstcol;
    char low, high;
    //size allows double height & double width
    //Here double height/width
    //for this I have to do two transfers each of double the amount of data
    //and reposition between each
     
    if (size & DOUBLE_WIDTH)
       inc_col=4;
    else
       inc_col=2;
     
    if (size & DOUBLE_HEIGHT)
       inc_row=2;
    else
       inc_row=1;   
     
    row=O_current_row;
    firstcol=col=O_current_col; //start all text from this point
     
    //Now low and high limits for text parsing
    high = 0x7F; //default high limit
    low = ' '; //default low limit
     
    if (allowed==UC_ONLY)
       high = 'Z'; //Limit to upper case
    else if (allowed==NUM_PLUS)
       high = '?'; //limit to six characters after numeris
    else if (allowed==NUM_ONLY)
       high = '9'; //just numeric
    if (allowed==NUM_ONLY || allowed ==NUM_PLUS)
       low='.';  //Move the lowest value up to decimal point
     
    do //step through a 'string' buffer
    {
       //First check if a number 
       temp=(*text);
       text++;
       if (temp=='\0')  //control characters EOS, and LF only
          break; //exit
       if (temp=='\n')
       {
          //implement crude line feed only
          if (size==NORMAL)
          {
             row+=2;
             OLED_gotoxy(firstcol,row);
             col=firstcol;
          }
          else
          {
             row+=4;
             OLED_gotoxy(firstcol,row);
             col=firstcol;
          }
          continue; //and loop again             
       }
       if (temp<' ')
          continue;
       if (temp>high)
          continue;  //upper limit
       if (temp<low)
          continue; //ignore below lower limit
       temp-=low; //low also defines the start of the font table
           
       //Now we need two rows from the font table   
       temp=((unsigned int16)temp*8)+((unsigned int16)temp*16); //efficient *24
       FONT_line(&font[temp],12); //double sized font.
       //Now feed to the new location for top row
       //OLED_address((unsigned int16)col*4+col*2,row+inc_row);
       OLED_gotoxy(col,row+inc_row);
       FONT_line(&font[temp+12],12);
       //Now because I'll be in the wrong position
       //have to re-locate
       col+=inc_col;
       OLED_gotoxy(col,row);
       //OLED_address((unsigned int16)col*4+col*2,row);  //ready for next character
    } while (TRUE); //and on to the next character
 }
 
 
 
 
//============== ADDED TO DRAW A LOGO AT STARTUP FOR EXAMPLE ================= 

 void get_line(ROM BYTE *array, BYTE * target)
 {
    int16 ctr;
    for (ctr=0;ctr<128;ctr++)
       *(target++) = array[ctr*8];
 }
                               
 
 void draw_logo(void)
 { 
    BYTE line[128];                         
    signed int16 y;
    //routine to write the logo
    //Logo is in a 512 byte array, designed to occupy the second half of the screen
    //each row on the screen is every eighth byte along the array
    set=TRUE;
    size=NORMAL;
    OLED_cls();
    for (y=7;y>=0;y--)       
    //for (y=0;y<=7;y++)
    {                                         
       OLED_address(0,7-y); //position the display
      get_line(&Plogo[y],line); //64 bytes
       OLED_data(line,128);
    }                 
   
   
 }
//== END ======== ADDED TO DRAW A LOGO AT STARTUP FOR EXAMPLE =================     
   
       
 //New function to generate large numbers only using 'bignum' font, and
 //supporting double size/width on this - macro expansion using above function
 #IFNDEF BIG_PLUS //Switch to allow a few extra characters in the 'bignum' font
    #define OLED_Bnum(x) OLED_bigtext(x, bignum, NUM_ONLY)
 #ELSE
    #define OLED_Bnum(x) OLED_bigtext(x, bignum, NUM_PLUS)
 #ENDIF                                                             

 //Similar expansion using 'large_font' for the whole standard char map
 #define OLED_Btext(x) OLED_bigtext(x, courier, ALL_ALPHA)     
 
 
                 

_________________
CCS + ICD3 + PIC18F46K80
Manu59114



Joined: 22 Jan 2018
Posts: 34
Location: North of France

View user's profile Send private message

PostPosted: Tue Jul 31, 2018 1:22 am     Reply with quote

Hello Ttelmah
Hello World!

Some news...

When i use this code with a PIC18 to draw a logo (1024 bytes array),
Everything is fine.

Same code on a dsPIC33.....
i have a black screen.
Code:

//============== ADDED TO DRAW A LOGO AT STARTUP FOR EXAMPLE ================= 

 void get_line(ROM BYTE *array, BYTE * target)
 {
    int16 ctr;
    for (ctr=0;ctr<128;ctr++)
    {
    *(target++) = array[ctr*8];
    }
 }
                   
                               
 void draw_logo(void)
 {             
    BYTE line[128];
    signed int16 y;
    //routine to write the logo
    //Logo is in a 1024 byte array,
    //each row on the screen is every eighth byte along the array
    set=TRUE;                                                                                                                                                                                     
    size=NORMAL;
    OLED_cls();
    //for (y=7;y>=0;y--)
   
    for (y=0;y<=7;y++)
    {
       OLED_address(0,7-y); //position the display                                                                                   
      get_line(&Plogo[y],line); //128 bytes
       OLED_data(line,128);
    }
 }
//== END ======== ADDED TO DRAW A LOGO AT STARTUP FOR EXAMPLE =================
     


If i replace the value of this line:
Code:
OLED_data(line,128);

with
Code:

OLED_data(line,127);

I can see my logo with the last vertical line missing!

....i have a headache..

any idea?

many thanks to all
Manu
_________________
CCS + ICD3 + PIC18F46K80
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page Previous  1, 2, 3, 4, 5  Next
Page 2 of 5

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group