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 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: 19520

View user's profile Send private message

SSD1306 driver for use with less RAM
PostPosted: Sat Oct 10, 2015 4:35 am     Reply with quote

Updated October 2018. An optional alternative way of running for PCD chips.

Updated Nov2017 - I've changed the font and bar graph output to be 0-100
Second amendment Dec 2017 Now supports a new #define to operate the SH1106 display as well. Also slightly more efficient

This is an example of a slightly 'different' SSD1306/SH1106 driver, for people working with restricted RAM.

The problem is that these chips do not allow a read, when in serial mode.
Now if (for instance), you set a pixel, on a 'clear' screen, then this is
not a problem. However if you then go to set a second pixel, you need to test
if any other pixel in the same byte is already set. So you need to 'know'
what is on the screen. The solution with the standard driver, is to have a
RAM buffer carrying a 'copy' of the screen, and then you can test the byte
in this, and if another pixel is set, send the whole byte with both pixels
to the display. However if you are trying to drive the screen on a chip
with very limited RAM, you don't have the space for such a buffer....

This driver allows some operations on the display, using varying amounts of
RAM.

It is in two parts. The first is a 'text' driver for the screen, which
uses a 6*8 font which is designed to be clear even at the small sizes on
the 0.96" displays. It gives you 21*8 text on the screen, with the ability
to also use the font 'double size' to give 10*4 text. It includes
thirteen extra characters in the font, which allow a 'bar graph' to be drawn
using 9 characters, and giving 0-100 (actually 101). for a empty bar to a
full bar. Again the double size mode can be used, to give a bar nearly the
width of the screen.

Key thing 'for' these screens, is they work in a wide range of light
conditions and at low temperatures where LCD's give problems - and they
are cheap!...

The driver is for I2C mode.

Look at the comments in the driver and example, to see how it works:
ssd1306.h
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

//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

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),                  // rotate screen 180
       S_COMSCANDEC,                       
       S_SETCOMPINS,                 
       0x12,
       S_SETCONTRAST,
       0xEF,                                //experiment.... 0xCf for 1306
       S_SETPRECHARGE,
       0xF1,
       S_SETVCOMDETECT,
       0x40,
       S_DISPLAYALLON_RESUME,
       S_NORMALDISPLAY,
       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
#ifdef PSV
void OLED_commands(byte * commands, unsigned int8 number)
#else
void OLED_commands(rom byte* commands, unsigned int8 number)
#endif
//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
{
   unsigned int8 ctr; //updated to allow 128bytes on PIC24/30 etc..
   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); }

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
#ifdef PSV
void FONT_line(byte * font_data, unsigned int8 count)
#else
void FONT_line(ROM byte * font_data, unsigned int8 count)
#endif
{
   //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               


Now an example program using the driver in 'text mode' only:

mainssdtext.c
Code:

#include <18F13K22.h>
#fuses INTRC_IO, PLLEN, NOXINST, NOMCLR, NOLVP, STVREN, PCLKEN, NOFCMEN
#fuses NOIESO, NOPUT, BORV22, NODEBUG, NOPROTECT, NOWDT, WDT512
#fuses HFOFST, NOCPD, NOCPB, NOWRTD, NOWRTC, NOWRTB
#USE DELAY(INTERNAL=32MHz)

#USE I2C(I2C1, MASTER, FAST=400000, FORCE_HW)

//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

#define SSDADDR 0x78 //address for the chip - usually 0x7C or 0x78.

#include <string.h>
#include "ssd1306.h" //The OLED driver

void main()
{
   int8 ctr;
   char text[9]; //temporary text buffer

   delay_ms(250);                //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;
   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(2000);  //pause for two seconds
   
   OLED_gotoxy(0,4);
   size=LARGE;
   OLED_text(text,strlen(text)); //try some large text
   delay_ms(2000);

   //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(2000);
   //Now printf in normal mode to the same screen 
   size=NORMAL;
   printf(OLED_putc,"ABCDEFGHI");

   delay_ms(2000); //delay again so you can see what it has done   
   //Now mixed size with a bar graph
   size=LARGE;
   printf(OLED_putc,"\fV=");

   for (ctr=0;ctr<101;ctr++)
   {
       size=NORMAL;
       OLED_gotoxy(4,1);
       printf(OLED_putc,"%3d  ",ctr);
       OLED_gotoxy(1,3);
       size=LARGE;
       OLED_textbar(ctr);
       delay_ms(250); //So you can see what is displayed!...
   }
   while(TRUE)
       ; //and stop
}


On the chip used here, this uses 37% ROM (about 3K), and 23% RAM (58 bytes).

Now a larger demo, using the graphics mode
Code:

//same processor header
//display dimensions - the physical LCD
#define S_LCDWIDTH               128
#define S_LCDHEIGHT              64

//#define TEXT_ONLY //REM this out

//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.

#include <string.h>
#include "ssd1306.h" //The OLED driver

void main()
{
   int8 ctr;
   char text[9]; //temporary text buffer
   //Remember if you want to use this for longer text, you'll need to
   //expand the buffer....

   delay_ms(250);                //OLED takes time to wake
   //now try to initialise OLED
   OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
   
   //First try some text
   OLED_CLS(); //clear the physical screen
   set=TRUE;
   clear_window(); //clear the window buffer
   OLED_gotoxy(0,0);
   strcpy(text,"Hello");
   size=NORMAL;
   OLED_text(text,strlen(text));
   delay_ms(1000);
   
   //now some graphics
   line(0,0,64,16); //draw line in window
   line(10,10,61,0);//and a second
   //now write the window to the screen
   draw_window(64,0); //at 64 pixels across and top of screen

   delay_ms(1000);
   set=FALSE; //inverted drawing
   clear_window(); //clear the window to fully set!
   line(0,0,60,15);
   line(20,10,40,10);
   circle(32,8,8,1); //just touch the edge of the window
   draw_window(64,4); //put this window half way down screen

   delay_ms(1000);
   while (TRUE)
      ; //and stop
}


This is significantly larger (uses 192 bytes of RAM - this is the 128 byte
graphic 'window'), and over 5KB ROM. However still fits merrily in the
small chip. If you don't need things like the circle function, removing
these will reduce the ROM size a little.

Hopefully of use to somebody!... Smile

As a final comment, somebody posted asking for the minimum code to set a pixel on such a display. The following 'main' does this:

Code:

void main()
{
   int8 temp=1;
   delay_ms(250);                //OLED takes time to wake
   //now try to initialise OLED
   OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
   
   OLED_CLS(); //clear the physical screen
   //Now just write a single byte to the start of the RAM

   OLED_data(&temp, 1);
   while (TRUE)
      ;
}


Now one final thing. Remember this chip is a 3.3v device. Using 3.3v I2c, you need small pull-ups to run this fast (I used 1.2KR).

You can also use size=DOUBLE_HEIGHT; which is very nice for the bargraph.

But I hear you say, it says it can use 5v Vcc. Yes it can, but the core _chip_ is 3.3v. It has it's own regulator on board for the logic, and the logic can run at 3.3v max. Beware not to overdrive it if running off a 5v device....

Have had it going successfully off a 5v device, by setting this to use SMBUS levels (many PIC's support this), and having the pull-ups to 3.3v. Or if you pull-up to 3.3v, and use _software_ I2C, on a port that supports TTL levels it also works OK.

I've updated this to allow some expansions to the font if required. Have changed the master code involved here, and posted some example expansions below. 5th Jan 2018.

Separate update October 2018.

If using PCD, using ROM *, is quite inefficient because of the maths involved in 3 bytes/word, and then the gap. On some chips this also seems to cause issues.
There is an alternative:

Code:

#include <your_processor_file>
#device PSV=16
//Then the rest of your processor setup

#define PSV
//Then include the SSD driver fonts etc.

//Then your code.


I've modified the driver, so if it sees 'PSV' defined, it changes the pointers from ROM pointers to normal RAM pointers. The PSV option enables a special mode, where the DsPIC's map 32K or their ROM, into the RAM address space, allowing normal RAM pointers to access this.
Has a slight cost, in that only two bytes get packed per instruction word when this is done. However does bring improvements to overall code efficiency.


Last edited by Ttelmah on Wed Jun 24, 2020 4:16 am; edited 11 times in total
MikeW



Joined: 15 Sep 2003
Posts: 184
Location: Warrington UK

View user's profile Send private message

PostPosted: Sun Nov 29, 2015 2:29 am     Reply with quote

@Ttelmah

Absolutely brilliant work !!

Works very well.

I have been trying to understand the bar graph routine "OLED_textbar"
but haven't managed to tweak it yet.

I want to have the bar graph display from 0 to 100, not 0 to 102.

Can you mod it for percentage please.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Mon Dec 28, 2015 8:04 am     Reply with quote

Just use it, and accept it'll not quite get to the top!... Smile

The reason it goes 0-102, is to save code space. The font characters are 6 dots wide. The 'bar' font characters give a single vertical strip at the left, then two strips, then three up to all six 'on', Then the 'blank character with no vertical bars, and then for the other end a single bar at the right, then one on the left and one on the right, two on the left etc.

So if you draw nine character, you have 54 vertical bars 'possible'. The left strip and right strip are left permanently on to 'delimit' the graph, so you have 52 useable strips. The incoming number is divided by 2 (binary division which implies just one rotation - quick and small), one is added, and the resulting number determines how many bars are turned on. 100, would turn on 51 strips out of the possible 52.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

ADC readings
PostPosted: Mon Dec 28, 2015 8:31 am     Reply with quote

This copied from a private message sent to me, however posting as a general reply:

Quote:

hi Ttelmah;

i need your help about i2c ssd1306,
your code work properly good, but i cant use adc conversation,

i tried all combinations of edit of code.. Confused

can you send me any example of work with adc and ssd1306
i use 18f4550 (but this is not important)

actually i tried other driver(high sized ram capability) other forum sites.
i didnt work too.

i tried nokia 3310 and nokia1100 lcd with adc. there is no problem with this display.

pls say to me where is my wrong ??
im waiting your ASAP reply,

thanks for now...


It sounds rather as if you are approaching things the wrong way.

You should not be modifying the code.
The place to start is with things in sections.
First ignore the 'ADC readings'. Just get the hardware working with the display.

Do something like the basic demo program posted here. You have not said what PIC you are using, what clock rate etc..

Movement should be:
1) Get the PIC working. Can you flash an LED, and get it to flash at the correct rate (when you program 1 second, does it flash in one second)?.
2) Then get the display working. the basic demo program here, with your fuses and clock (and the pin numbers your PIC is using).
3) Then put this to one side, with the display code 'unchanged', and run a basic program loop to read the ADC. Use this code (or any other), as a _library_. Don't change this, but call it from your code. So something like:

Code:

//This part to suit _your_ PIC, as already tested to work.
#include <18F13K22.h>
#device ADC=10
#fuses INTRC_IO, PLLEN, NOXINST, NOMCLR, NOLVP, STVREN, PCLKEN, NOFCMEN
#fuses NOIESO, NOPUT, BORV22, NODEBUG, NOPROTECT, NOWDT, WDT512
#fuses HFOFST, NOCPD, NOCPB, NOWRTD, NOWRTC, NOWRTB
#USE DELAY(INTERNAL=32MHz)

#USE I2C(I2C1, MASTER, FAST=400000, FORCE_HW)

//display dimensions - the physical LCD
#define S_LCDWIDTH               128
#define S_LCDHEIGHT              64

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

#define SSDADDR 0x78 //address for the chip - usually 0x7C or 0x78.

#include <string.h>
#include "ssd1306.h" //The OLED driver

void main(void)
{
    int16 adcval; //variable to store the ADC reading
    setup_adc_ports(sAN0 | VSS_VDD); //set this up for the pins
    //and Vref you are going to use - demo on just AN0, and supply
    setup_adc(ADC_CLOCK_DIV_64);  //select according to data sheet
    //for your clock rate/chip.
    OLED_CLS();
    size=LARGE;
    while (TRUE)
    {
        //now the main code loop
        delay_ms(500); //slow the loop to a sensible rate
        adcval=read_adc(); //read the adc
        //Now integer maths to give result in mV
        adcval=(((int32)adcval*3300)+1650)/1024;
        //This gives and int16 value of 0-3298 for 0 to 3.298v into the ADC
        //assuming 3.3v supply
        oled_gotoxy(0,0);
        printf(OLED_putc("%5.3LWV  ",adcval);
        //display voltage on the LCD
    }
}


This then displays the voltage as a nice x.xxxV on the LCD.
MikeW



Joined: 15 Sep 2003
Posts: 184
Location: Warrington UK

View user's profile Send private message

PostPosted: Sat Jan 02, 2016 1:52 am     Reply with quote

@Ttelmah

"Just use it, and accept it'll not quite get to the top!.."

Now thats a challenge I will take on.

watch this space ....
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Mon Jan 04, 2016 2:08 am     Reply with quote

It's actually possible to modify it with a little finangling.

Key is the definitions of the 'right hand' characters for the graph.

These are:
five clear bars, and the right bar on.
one bar at the left and one at the right.
two on the left and one on the right.
three on the left and one on the right.
four on the left and one on the right.
All on.

If you change the font definitions so these only use the left hand five pixels:

These are:
four clear bars, and the fifth bar on.
one bar at the left and the fifth on.
two on the left and the fifth on.
three on the left and the fifth on.
five on.

Then change the code to:
Code:

void OLED_textbar(unsigned int8 width)
{
   //This draws a bar graph using text characters
   int8 ctr;
   unsigned int8 bar[9];
   //This version is 0-100
   //prints at current text location.
   width/=2;
   width+=1;  //ensure >0
   if (width>50)
      width=51;
   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);
}


It should give a 0-100 bar. I'll leave you to do the font change needed (characters 135 and on).
MikeW



Joined: 15 Sep 2003
Posts: 184
Location: Warrington UK

View user's profile Send private message

Finally cracked the 0-100 textbar
PostPosted: Sun Jan 15, 2017 2:25 am     Reply with quote

I finally had some time over Xmas to finish my modification.

// modified to get 0-100

replace the last part of the font data with the following
Code:

0x82, 0x82, 0x82, 0x82,0xFE , 0x00, //final one closing shape 135
0xFE, 0x82, 0x82, 0x82, 0xFE, 0x00, //single left and right
0xFE, 0xFE, 0x82, 0x82, 0xFE, 0x00,
0xFE, 0xFE, 0xFE, 0x82, 0xFE, 0x00, //Now working in from the left
0xFE, 0xFE, 0xFE, 0x82, 0xFE, 0x00,
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x00 //final one closing shape 140


now to the function, it aint pretty, but it works.
Code:

void OLED_textbar_New(unsigned int8 width)
{
//This draws a bar graph using text characters
int8 ctr;
unsigned int8 bar[9];
int8 Frig;

//This version is 0-100
//prints at current text location.

Frig=width; // save it for later


if (width>100) width=100;

width/=2;
width+=1; //ensure >0

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

if (bar[7]==134) width++;

bar[8]=135+width;

if (Frig==100) bar[8]=140; // frig to fill the last hole

OLED_text(bar,9);
}
benoitstjean



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

View user's profile Send private message

PostPosted: Sun Dec 24, 2017 2:53 pm     Reply with quote

Compiler: 5.026
Device: PIC24EP512GP806

Hi Ttelmah,

I'm using your code in text mode and it works and has worked for the last hour. But all of a sudden, it seems that 'something' is happening where, almost as if the display was separated in equal number of pixels per line and pixels per column for "large" fonts, what would be the very last column and last line are just random on and off garbled pixels all over the place without any specific pattern.

The last column of crap seems to be about 8-9 pixels wide and the last line of crap is about 4 pixels high.

Any idea what could cause this? It just happened all of a sudden. Even unplugging/replugging the entire unit, reprogramming the PIC, these lines keep appearing as garbage.

I will investigate more because there's no reason for that as it worked for the last hour and it just suddenly occurred but if you have an idea of what could cause this, let me know.

This is not an Adafruit display. I had this kicking around for like 3 years and I had ordered it from Hobby King. I never got to use it and paid like 2$ for it. It's just today I decided to have a try and *that* happened after working for a while.

Thanks!

Ben


[EDIT] The "problem" went away on its own. I had to shut off all my stuff because I was leaving and when I returned a few hours later, I turned it back on and this time the display was fine. So I'm wondering if somehow, the pixels stay charged for a while and a simple power reset doesn't work and the only way around this is to power-off the display for a prolonged period? Anyhow, it's weird.

Ben
benoitstjean



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

View user's profile Send private message

PostPosted: Wed Dec 27, 2017 10:01 am     Reply with quote

If any user uses the sample code along with the sample main() code, just take note that in TTelmah's main example <mainssdtext.c>, he used <char text[9];> as a temporary text buffer.

Since the display handles at least 10 large characters wide per line with his driver, trying to print 10 characters caused half the line to be erased because it is trying to print a string longer than the buffer.

So change the '[9]' to '[11]' and that will get rid of that problem.

I'm pretty sure that it's not a bug on his behalf and more of an on-the-fly thing without really paying attention. In any case, I know I didn't pay attention to that detail and used his code as-is and I ran into the display problem. I thought it was in his driver code until I realized the problem was at the very begining and I should have paid a closer attention from the start.

Driver works well.

Ben
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jan 05, 2018 4:36 am     Reply with quote

Not even that.
Remember the display can show 21 characters per line in the smallest font. I made the buffer large enough for the strings I was displaying. It is totally up to you how long you make your buffer.... Smile

As a further comment, I've just updated the actual routines to allow some further expansions. I've updated the 'master' routines above, and then posted some expansions below.
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Fri Jan 05, 2018 5:42 am     Reply with quote

Now adding some larger font abilities.
Requires the newer driver now at the top of the thread.
Code:

//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
#ifdef PSV
void OLED_Bigtext(unsigned int8 * text, byte * font, int8 allowed)
#else
void OLED_Bigtext(unsigned int8 * text, ROM byte * font, int8 allowed)
#endif
{
   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
}

//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)


Now font include files
NUM12.h
Code:

//Big  size numeric font
ROM byte bignum[] =
{
   /*  '.' (12 pixels wide) */
   //     ###     
   //     ###     
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,
   
   /*  '/' (12 pixels wide) */
   //         ###
   //         ## 
   //        ### 
   //        ### 
   //        ##   
   //       ###   
   //       ##   
   //      ###   
   //      ###   
   //      ##     
   //     ###     
   //     ##     
   //     ##     
   //    ###     
   //    ##       
   //   ###       
   //   ##       
   //   ##       
   //  ###       
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x0E, 0x00, 0x01, 0xC0,
   0x00, 0x38, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /*  '0' (12 pixels wide) */
   //    #####   
   //  ###  ###   
   //  ###  ###   
   // ###    ### 
   // ###    ### 
   // ###    ### 
   // ###    ### 
   // ###    ### 
   // ###    ### 
   // ###    ### 
   // ###    ### 
   //  ###  ###   
   //  ###  ###   
   //   #####     
   0xF8, 0xFE, 0xFE, 0x07, 0x01, 0x01, 0x07, 0xFF, 0xFE, 0xF8, 0x00, 0x00,
   0x07, 0x1F, 0x3F, 0x38, 0x20, 0x20, 0x38, 0x1F, 0x1F, 0x07, 0x00, 0x00,

   /*  '1' (12 pixels wide) */
   //      ##     
   //    ####     
   //  ######     
   // ##  ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //     ###     
   //  ######### 
   0x08, 0x0C, 0x04, 0x06, 0xFE, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x20, 0x20, 0x20, 0x3F, 0x3F, 0x3F, 0x20, 0x20, 0x20, 0x00, 0x00,

   /*  '2' (12 pixels wide) */
   //    ######   
   //  ###  #### 
   //  ##    ### 
   //        ### 
   //        ### 
   //       ###   
   //       ###   
   //      ###   
   //     ###     
   //    ###     
   //   ###       
   //   ##    ## 
   //  ######### 
   //  ######### 
   0x00, 0x06, 0x06, 0x03, 0x01, 0x81, 0xE3, 0xFF, 0x7F, 0x1E, 0x00, 0x00,
   0x00, 0x30, 0x3C, 0x3E, 0x37, 0x33, 0x31, 0x30, 0x38, 0x38, 0x00, 0x00,

   /*  '3' (12 pixels wide) */
   //   ######   
   // ###   #### 
   // ##     ### 
   //        ### 
   //       ###   
   //      ###   
   //   ######   
   //       ###   
   //        ### 
   //        ### 
   //        ### 
   // ##     ### 
   // ###   ###   
   //  #######   
   0x06, 0x06, 0x43, 0x41, 0x41, 0x61, 0xF3, 0xFF, 0x9E, 0x0E, 0x00, 0x00,
   0x18, 0x38, 0x30, 0x20, 0x20, 0x20, 0x30, 0x3F, 0x1F, 0x0F, 0x00, 0x00,

   /*  '4' (12 pixels wide) */
   //       ###   
   //      ####   
   //      ####   
   //     #####   
   //    ######   
   //    ## ###   
   //   ##  ###   
   //  ###  ###   
   //  ##   ### ##
   // ############
   //       ###   
   //       ###   
   //       ###   
   //      ##### 
   0x00, 0x80, 0xC0, 0xF0, 0x38, 0x1E, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00,
   0x02, 0x03, 0x03, 0x02, 0x02, 0x22, 0x3F, 0x3F, 0x3F, 0x22, 0x03, 0x03,

   /*  '5' (12 pixels wide) */
   //         ## 
   //   ######## 
   //   ######## 
   //   ##       
   //   ##       
   //   ##       
   //   ##       
   //   ######   
   //   ##  ###   
   //        ### 
   //        ### 
   //        ### 
   //  ##    ### 
   //  ###  ###   
   //   ######   
   0x00, 0x00, 0xFE, 0xFE, 0x86, 0x86, 0x86, 0x86, 0x07, 0x07, 0x00, 0x00,
   0x00, 0x30, 0x71, 0x61, 0x40, 0x40, 0x61, 0x7F, 0x3F, 0x1E, 0x00, 0x00,

   /*  '6' (12 pixels wide) */
   //       #### 
   //     ###     
   //    ###     
   //   ###       
   //  ###       
   //  ###       
   // #########   
   // ####   ### 
   // ###     ###
   // ###     ###
   // ###     ###
   //  ##     ###
   //  ###   ### 
   //   #######   
   0xC0, 0xF0, 0xF8, 0xFC, 0x4E, 0x46, 0x43, 0xC1, 0xC1, 0x81, 0x00, 0x00,
   0x07, 0x1F, 0x3F, 0x30, 0x20, 0x20, 0x20, 0x30, 0x3F, 0x1F, 0x0F, 0x00,

   /*  '7' (12 pixels wide) */
   //  ##########
   //  ##########
   //  ##     ## 
   //  ##    ### 
   //        ##   
   //        ##   
   //       ##   
   //       ##   
   //      ##     
   //      ##     
   //     ###     
   //     ##     
   //    ###     
   //    ##       
   0x00, 0x0F, 0x0F, 0x03, 0x03, 0x03, 0xC3, 0xFB, 0x3F, 0x0F, 0x03, 0x00,
   0x00, 0x00, 0x00, 0x30, 0x3C, 0x1F, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,

   /*  '8' (12 pixels wide) */
   //   #######   
   //  ###   ### 
   // ###     ###
   // ###     ###
   // ###     ###
   //  ###   ### 
   //   #######   
   //  ###   ### 
   // ###     ###
   // ###     ###
   // ###     ###
   // ###     ###
   //  ###   ### 
   //   #######   
   0x1C, 0xBE, 0xFF, 0xE3, 0x41, 0x41, 0x41, 0xE3, 0xFF, 0xBE, 0x1C, 0x00,
   0x0F, 0x1F, 0x3F, 0x30, 0x20, 0x20, 0x20, 0x30, 0x3F, 0x1F, 0x0F, 0x00,

   /*  '9' (12 pixels wide) */
   //    ######   
   //  ###   ### 
   // ###     ## 
   // ###     ###
   // ###     ###
   // ###     ###
   //  ###   ####
   //   #########
   //         ###
   //        ### 
   //        ### 
   //       ###   
   //      ###   
   //   ####     
   0x3C, 0x7E, 0xFE, 0xC3, 0x81, 0x81, 0x81, 0xC3, 0xFF, 0xFE, 0xF8, 0x00,
   0x00, 0x00, 0x20, 0x20, 0x20, 0x30, 0x18, 0x1E, 0x0F, 0x07, 0x01, 0x00
   
#IFDEF BIG_PLUS
//Otherwise we add six more characters
    // @260 'C' (13 pixels wide)
   //             
   //      #####   
   //    ##     # 
   //   #        #
   //   #        #
   //  #           
   //  #           
   //  #           
   //  #           
   //  #           
   //   #         
   //   #        #
   //    ##     # 
   //      #####   
   //             
   //
   , 0x00, 0xC0, 0x30, 0x08, 0x08, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x10, 0x00,   
   0x00, 0x07, 0x18, 0x20, 0x20, 0x40, 0x40, 0x40, 0x40, 0x40, 0x20, 0x18, 0x00,


   // @286 'F' (13 pixels wide)
   //             
   //     ########
   //      #     #
   //      #     #
   //      #       
   //      #    # 
   //      #    # 
   //      ###### 
   //      #    # 
   //      #    # 
   //      #       
   //      #       
   //      #       
   //    #####     
   //             
   //             

   0x00, 0x00, 0x00, 0x04, 0x04, 0xFC, 0x04, 0x04, 0x00, 0x00, 0xC0, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x40, 0x7F, 0x41, 0x41, 0x41, 0x41, 0x47, 0x70, 0x00,
   // @312 '©' (13 pixels wide)
   //             
   //     #####   
   //    #     #   
   //   #       # 
   //  #   ####  #
   //  #  #    # #
   // #  #     #  #
   // #  #        #
   // #  #        #
   // #  #     #  #
   //  #  #   #  #
   //  #   ###   #
   //   ##     ## 
   //     #####   
   //             
   //             

   0xC0, 0x30, 0x08, 0xC8, 0x24, 0x14, 0x14, 0x14, 0x24, 0x48, 0x08, 0x30, 0xC0,
   0x03, 0x0C, 0x10, 0x23, 0x44, 0x48, 0x48, 0x48, 0x48, 0x26, 0x10, 0x0C, 0x03,
   // @338 '°' (13 pixels wide)
   //             
   //             
   //       ##     
   //      #  #   
   //      #  #   
   //       ##     
   //             
   //             
   //             
   //             
   //             
   //             
   //             
   //             
   //             
   //             

   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x24, 0x24, 0x18, 0x00, 0x00, 0x00, 0x00,
   // @364 '²' (13 pixels wide)
   //       ##     
   //      #  #   
   //         #   
   //         #   
   //        #     
   //       #     
   //       #  #   
   //     #####   
   //             
   //             
   //             
   //             
   //             
   //             
   //             
   //             

   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x01, 0x41, 0x87, 0x89, 0x71, 0x02, 0x00, 0x00, 0x00,
   // @390 '³' (13 pixels wide)
   //      ##     
   //     #  #     
   //        #     
   //      ###     
   //     #   #   
   //         #   
   //         #   
   //     #  #     
   //      ##     
   //             
   //             
   //             
   //             
   //             
   //             
   //             
 
   0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x49, 0x90, 0x90, 0x71, 0x0E, 0x00, 0x00, 0x00, 0x00   
#ENDIF
};


courier.h
Code:

/* Character bitmaps for Courier 12 pixels wide */
ROM byte courier[] =
{
   // space
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,   
   
   /* @0 '!' (12 pixels wide) */
   //           
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //           
   //     ##     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @22 '"' (12 pixels wide) */
   //           
   //           
   //    ### ###
   //    ### ###
   //     #   # 
   //     #   # 
   //     #   # 
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x0C, 0x7C, 0x0C, 0x00, 0x0C, 0x7C, 0x0C, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @44 '#' (12 pixels wide) */
   //           
   //     ## ## 
   //     ## ## 
   //     ## ## 
   //     ## ## 
   //   ########
   //    ## ##   
   //   ########
   //    ## ##   
   //    ## ##   
   //    ## ##   
   //    ## ##   
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0xA0, 0xE0, 0xFE, 0xBE, 0xE0, 0xFE, 0xBE, 0xA0, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x0F, 0x0F, 0x00, 0x0F, 0x0F, 0x00, 0x00, 0x00,

   /* @66 '$' (12 pixels wide) */
   //      #     
   //    ###### 
   //   ##   ## 
   //   ##   ## 
   //   ###     
   //    ####   
   //     ####   
   //       ### 
   //   ##   ## 
   //   ##   ## 
   //   ######   
   //      #     
   //      #     
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x1C, 0x3E, 0x72, 0x63, 0xE2, 0xCE, 0x8E, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x07, 0x07, 0x04, 0x1C, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @88 '%' (12 pixels wide) */
   //           
   //    ##     
   //   #  #     
   //   #  #     
   //    ##   ##
   //      #### 
   //    ####   
   //   ##   ## 
   //       #  #
   //       #  #
   //        ## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x8C, 0xD2, 0x52, 0x6C, 0x60, 0xA0, 0xB0, 0x10, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x03, 0x00,

   /* @110 '&' (12 pixels wide) */
   //           
   //           
   //     ####   
   //    ##     
   //    ##     
   //    ##     
   //     ##     
   //    ### ## 
   //   ## ###   
   //   ##  ##   
   //    ### ## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0xB8, 0xFC, 0xC4, 0x04, 0x84, 0x80, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x05, 0x03, 0x07, 0x04, 0x00, 0x00,

   /* @132 ''' (12 pixels wide) */
   //           
   //           
   //      ###   
   //      ###   
   //       #   
   //       #   
   //       #   
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x7C, 0x0C, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @154 '(' (12 pixels wide) */
   //           
   //       ##   
   //       ##   
   //      ##   
   //     ###   
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ###   
   //      ##   
   //       ##   
   //       ##   
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0xF8, 0x1E, 0x06, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x1E, 0x18, 0x00, 0x00, 0x00,

   /* @176 ')' (12 pixels wide) */
   //           
   //    ##     
   //    ##     
   //     ##     
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //     ##     
   //    ###     
   //    ##     
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x06, 0x0E, 0xF8, 0xF0, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x18, 0x1C, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00,

   /* @198 '*' (12 pixels wide) */
   //           
   //      ##   
   //      ##   
   //   ########
   //   ########
   //     ####   
   //    ###### 
   //    ##  ## 
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x18, 0xD8, 0xF8, 0x7E, 0x7E, 0xF8, 0xD8, 0x18, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @220 '+' (12 pixels wide) */
   //           
   //           
   //           
   //      #     
   //      #     
   //      #     
   //   ####### 
   //      #     
   //      #     
   //      #     
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0xF8, 0x40, 0x40, 0x40, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @242 ',' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //      ##   
   //      #     
   //     ##     
   //     #     
   //     #     
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0E, 0x02, 0x00, 0x00, 0x00, 0x00,

   /* @264 '-' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //           
   //   ####### 
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @286 '.' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //     ##     
   //     ##     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @308 '/' (12 pixels wide) */
   //         ##
   //         ##
   //        ## 
   //        ## 
   //       ##   
   //       ##   
   //      ##   
   //     ##     
   //     ##     
   //    ##     
   //    ##     
   //   ##       
   //   ##       
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xC0, 0x70, 0x3C, 0x0F, 0x03, 0x00,
   0x00, 0x00, 0x00, 0x18, 0x1E, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @330 '0' (12 pixels wide) */
   //           
   //     ###   
   //    ## ##   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //    ## ##   
   //     ###   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0xF8, 0xFC, 0x06, 0x02, 0x06, 0xFC, 0xF8, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x06, 0x03, 0x01, 0x00, 0x00,

   /* @352 '1' (12 pixels wide) */
   //           
   //      ##   
   //   #####   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //   ########
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00,

   /* @374 '2' (12 pixels wide) */
   //           
   //     ####   
   //    ##  ## 
   //   ##   ## 
   //   ##   ## 
   //       ##   
   //      ##   
   //     ##     
   //    ##     
   //   ##       
   //   ####### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x18, 0x1C, 0x86, 0xC2, 0x62, 0x3E, 0x1C, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x06, 0x07, 0x05, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00,

   /* @396 '3' (12 pixels wide) */
   //           
   //   ######   
   //  ##    ## 
   //        ## 
   //       ##   
   //    #####   
   //       ### 
   //        ## 
   //        ## 
   //  ##    ## 
   //   ######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x06, 0x22, 0x22, 0x22, 0x72, 0xFE, 0xCC, 0x00, 0x00,
   0x00, 0x00, 0x02, 0x06, 0x04, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @418 '4' (12 pixels wide) */
   //           
   //      ###   
   //      ###   
   //     ####   
   //     # ##   
   //    ## ##   
   //    #  ##   
   //   ##  ##   
   //   ####### 
   //       ##   
   //     ##### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x80, 0xE0, 0x38, 0x0E, 0xFE, 0xFE, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x01, 0x05, 0x05, 0x07, 0x07, 0x05, 0x00, 0x00,

   /* @440 '5' (12 pixels wide) */
   //           
   //    ###### 
   //    ##     
   //    ##     
   //    ##     
   //    #####   
   //    #   ## 
   //        ## 
   //        ## 
   //   #    ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x7E, 0x3E, 0x22, 0x22, 0xE2, 0xC2, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x02, 0x04, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @462 '6' (12 pixels wide) */
   //           
   //      #### 
   //    ###     
   //    ##     
   //   ##       
   //   ## ###   
   //   ###  ## 
   //   ##   ## 
   //   ##   ## 
   //    ##  ## 
   //     ####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0xF0, 0xFC, 0x4C, 0x26, 0x22, 0xE2, 0xC2, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @484 '7' (12 pixels wide) */
   //           
   //  #######   
   //  #    ##   
   //       ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //     ##     
   //     ##     
   //     ##     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x06, 0x02, 0x02, 0x02, 0xF2, 0xFE, 0x0E, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @506 '8' (12 pixels wide) */
   //           
   //    #####   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //    #####   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0xDC, 0xFE, 0x22, 0x22, 0x22, 0xFE, 0xDC, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @528 '9' (12 pixels wide) */
   //           
   //    ####   
   //   ##  ##   
   //   ##   ## 
   //   ##   ## 
   //   ##  ### 
   //    ### ## 
   //        ## 
   //       ##   
   //      ###   
   //   ####     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x3C, 0x7E, 0x42, 0x42, 0x26, 0xFC, 0xF8, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x06, 0x03, 0x03, 0x00, 0x00, 0x00,

   /* @550 ':' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //     ##     
   //     ##     
   //           
   //           
   //           
   //     ##     
   //     ##     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @572 ';' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //       ##   
   //       ##   
   //           
   //           
   //           
   //      ##   
   //      #     
   //     #     
   //     #     
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x30, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x06, 0x02, 0x00, 0x00, 0x00, 0x00,

   /* @594 '<' (12 pixels wide) */
   //           
   //           
   //         ##
   //       ##   
   //      #     
   //    ##     
   //  ##       
   //    ##     
   //      #     
   //       ##   
   //         ##
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x40, 0x40, 0xA0, 0xA0, 0x10, 0x08, 0x08, 0x04, 0x04, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x02, 0x04, 0x04, 0x00,

   /* @616 '=' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //  #########
   //           
   //  #########
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @638 '>' (12 pixels wide) */
   //           
   //           
   //  ##       
   //    ##     
   //      #     
   //       ##   
   //         ##
   //       ##   
   //      #     
   //    ##     
   //  ##       
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x04, 0x08, 0x08, 0x10, 0xA0, 0xA0, 0x40, 0x40, 0x00,
   0x00, 0x00, 0x04, 0x04, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @660 '?' (12 pixels wide) */
   //           
   //           
   //    #####   
   //   ##   ## 
   //   ##   ## 
   //        ## 
   //      ###   
   //     ##     
   //     ##     
   //           
   //     ##     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x18, 0x1C, 0x84, 0xC4, 0x44, 0x7C, 0x38, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @682 '@' (12 pixels wide) */
   //           
   //     ###   
   //    #   #   
   //   #    #   
   //   #    #   
   //   #  ###   
   //   # #  #   
   //   # #  #   
   //   #  ###   
   //   #       
   //    #   #   
   //     ###   
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0xF8, 0x04, 0xC2, 0x22, 0x22, 0xFC, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x04, 0x08, 0x09, 0x09, 0x05, 0x00, 0x00, 0x00,

   /* @704 'A' (12 pixels wide) */
   //           
   //           
   //   ######   
   //     ####   
   //     #  #   
   //    ##  ## 
   //    ##  ## 
   //    ###### 
   //   ##    ##
   //   ##    ##
   //  ####  ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x04, 0xE4, 0xFC, 0x8C, 0x8C, 0xFC, 0xE0, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x00, 0x04, 0x07, 0x07, 0x04,

   /* @726 'B' (12 pixels wide) */
   //           
   //           
   //  #######   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ######   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //  #######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x44, 0x44, 0x44, 0xFC, 0xB8, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @748 'C' (12 pixels wide) */
   //           
   //           
   //    ##### #
   //   ##    ##
   //  ##      #
   //  ##       
   //  ##       
   //  ##       
   //  ##      #
   //   ##    # 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xF0, 0xF8, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x08, 0x1C, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x04, 0x02, 0x01, 0x00,

   /* @770 'D' (12 pixels wide) */
   //           
   //           
   //  #######   
   //   ##   ## 
   //   ##    ##
   //   ##    ##
   //   ##    ##
   //   ##    ##
   //   ##    ##
   //   ##   ## 
   //  #######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x06, 0x03, 0x01, 0x00,

   /* @792 'E' (12 pixels wide) */
   //           
   //           
   //  ######## 
   //   ##    # 
   //   ##    # 
   //   ##  #   
   //   #####   
   //   ##  #   
   //   ##    # 
   //   ##    # 
   //  ######## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x44, 0x44, 0xE4, 0x04, 0x1C, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x07, 0x00, 0x00,

   /* @814 'F' (12 pixels wide) */
   //           
   //           
   //  #########
   //   ##     #
   //   ##     #
   //   ##  #   
   //   #####   
   //   ##  #   
   //   ##       
   //   ##       
   //  #####     
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x44, 0x44, 0xE4, 0x04, 0x04, 0x1C, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @836 'G' (12 pixels wide) */
   //           
   //           
   //    #### # 
   //   ##   ## 
   //  ##     # 
   //  ##       
   //  ##       
   //  ##  #####
   //  ##    ## 
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xF0, 0xF8, 0x0C, 0x04, 0x84, 0x84, 0x88, 0x9C, 0x80, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @858 'H' (12 pixels wide) */
   //           
   //           
   //  #### ####
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ####### 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //  #### ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x44, 0x40, 0x44, 0xFC, 0xFC, 0x04, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00,

   /* @880 'I' (12 pixels wide) */
   //           
   //           
   //   ########
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //   ########
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x04, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00,

   /* @902 'J' (12 pixels wide) */
   //           
   //           
   //    #######
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //  ##   ##   
   //  ##   ##   
   //  ##   ##   
   //   #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x80, 0x80, 0x04, 0x04, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x00,
   0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00, 0x00,

   /* @924 'K' (12 pixels wide) */
   //           
   //           
   //  #### ####
   //   ##   ## 
   //   ##  ##   
   //   ## ##   
   //   ####     
   //   #####   
   //   ##  ##   
   //   ##   ## 
   //  ####  ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0xC4, 0xE0, 0xB4, 0x1C, 0x0C, 0x04, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x01, 0x07, 0x06, 0x04, 0x00,

   /* @946 'L' (12 pixels wide) */
   //           
   //           
   //  ######   
   //    ##     
   //    ##     
   //    ##     
   //    ##     
   //    ##    #
   //    ##    #
   //    ##    #
   //  #########
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x00, 0x00, 0x80, 0x00,
   0x00, 0x00, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x04, 0x07, 0x00,

   /* @968 'M' (12 pixels wide) */
   //           
   //           
   // ###     ###
   //  ##     ##
   //  ###   ###
   //  #### ####
   //  ## # # ##
   //  ## ### ##
   //  ##  #  ##
   //  ##     ##
   // ##### #####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x04, 0xFC, 0xFC, 0x30, 0xE0, 0x80, 0xE0, 0x30, 0xFC, 0xFC, 0x04,
   0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x01, 0x04, 0x04, 0x07, 0x07, 0x04,

   /* @990 'N' (12 pixels wide) */
   //           
   //           
   //  ###  ####
   //   ##   ## 
   //   ###  ## 
   //   #### ## 
   //   ## # ## 
   //   ## #### 
   //   ##  ### 
   //   ##   ## 
   //  ####  ## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x30, 0xE0, 0x84, 0xFC, 0xFC, 0x04, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x01, 0x07, 0x07, 0x00, 0x00,

   /* @1012 'O' (12 pixels wide) */
   //           
   //           
   //    #####   
   //   ##   ## 
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xF0, 0xF8, 0x0C, 0x04, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x06, 0x03, 0x01, 0x00,

   /* @1034 'P' (12 pixels wide) */
   //           
   //           
   //  #######   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ######   
   //   ##       
   //   ##       
   //  ######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x84, 0x84, 0x84, 0xFC, 0x78, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00,

   /* @1056 'Q' (12 pixels wide) */
   //           
   //           
   //    #####   
   //   ##   ## 
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //   ##   ## 
   //    #####   
   //     ##  ##
   //    ###### 
   //           
   //           
   //           
   0x00, 0x00, 0xF0, 0xF8, 0x0C, 0x04, 0x04, 0x04, 0x0C, 0xF8, 0xF0, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x16, 0x1C, 0x1C, 0x14, 0x16, 0x1B, 0x09, 0x00,

   /* @1078 'R' (12 pixels wide) */
   //           
   //           
   //  #######   
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   #####   
   //   ##  ##   
   //   ##   ## 
   //   ##   ## 
   //  #####  ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x44, 0x44, 0xC4, 0xBC, 0x38, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x04, 0x00, 0x03, 0x07, 0x04, 0x04,

   /* @1100 'S' (12 pixels wide) */
   //           
   //           
   //    ###### 
   //   ##   ## 
   //   ##   ## 
   //   ###     
   //    #####   
   //       ### 
   //   ##   ## 
   //   ##   ## 
   //   ######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x38, 0x7C, 0x64, 0x44, 0xC4, 0xDC, 0x9C, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x07, 0x07, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @1122 'T' (12 pixels wide) */
   //           
   //           
   //  ######## 
   //  #  ##  # 
   //  #  ##  # 
   //  #  ##  # 
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //   ######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x3C, 0x04, 0x04, 0xFC, 0xFC, 0x04, 0x04, 0x3C, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x00, 0x00, 0x00,

   /* @1144 'U' (12 pixels wide) */
   //           
   //           
   //  #### ####
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0xFC, 0xFC, 0x04, 0x00, 0x04, 0xFC, 0xFC, 0x04, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x04, 0x07, 0x03, 0x00, 0x00,

   /* @1166 'V' (12 pixels wide) */
   //           
   //           
   //  #### ####
   //   ##   ## 
   //   ##   ## 
   //    ## ##   
   //    ## ##   
   //    ## ##   
   //     # #   
   //     ###   
   //     ###   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x1C, 0xFC, 0xE4, 0x00, 0xE4, 0xFC, 0x1C, 0x04, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00,

   /* @1188 'W' (12 pixels wide) */
   //           
   //           
   // ##### #####
   //  ##     ##
   //  ##  #  ##
   //  ## ### ##
   //  ## ### ##
   //   # # # # 
   //   ### ### 
   //   ### ### 
   //   ##   ## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x04, 0x7C, 0xFC, 0x04, 0xE4, 0x70, 0xE4, 0x04, 0xFC, 0x7C, 0x04,
   0x00, 0x00, 0x00, 0x07, 0x07, 0x03, 0x00, 0x03, 0x07, 0x07, 0x00, 0x00,

   /* @1210 'X' (12 pixels wide) */
   //           
   //           
   //  #### ####
   //   ##   ## 
   //    ## ##   
   //     ###   
   //     ###   
   //     ###   
   //    ## ##   
   //   ##   ## 
   //  #### ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x0C, 0x1C, 0xF4, 0xE0, 0xF4, 0x1C, 0x0C, 0x04, 0x00,
   0x00, 0x00, 0x04, 0x06, 0x07, 0x05, 0x00, 0x05, 0x07, 0x06, 0x04, 0x00,

   /* @1232 'Y' (12 pixels wide) */
   //           
   //           
   //  ####  ####
   //   ##    ##
   //    ##  ## 
   //     ####   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //    ###### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x04, 0x0C, 0x1C, 0x34, 0xE0, 0xE0, 0x34, 0x1C, 0x0C, 0x04,
   0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x00, 0x00,

   /* @1254 'Z' (12 pixels wide) */
   //           
   //           
   //   ####### 
   //   #    ## 
   //   #   ##   
   //      ##   
   //      #     
   //     ##     
   //    ##   # 
   //   ##    # 
   //   ####### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x1C, 0x04, 0x84, 0xE4, 0x34, 0x1C, 0x0C, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x06, 0x07, 0x05, 0x04, 0x04, 0x04, 0x07, 0x00, 0x00,

   /* @1276 '[' (12 pixels wide) */
   //           
   //      #### 
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      #### 
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x02, 0x02, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x10, 0x10, 0x00, 0x00,

   /* @1298 '\' (12 pixels wide) */
   //   ##       
   //   ##       
   //    ##     
   //    ##     
   //     ##     
   //     ##     
   //      ##   
   //       ##   
   //       ##   
   //        ## 
   //        ## 
   //         ##
   //         ##
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x03, 0x0F, 0x3C, 0x70, 0xC0, 0x80, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x1E, 0x18, 0x00,

   /* @1320 ']' (12 pixels wide) */
   //           
   //    ####   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //    ####   
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00,

   /* @1342 '^' (12 pixels wide) */
   //      #     
   //     # #   
   //     # #   
   //    #   #   
   //   #     # 
   //   #     # 
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x30, 0x08, 0x06, 0x01, 0x06, 0x08, 0x30, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @1364 '_' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   // ###########
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,

   /* @1386 '`' (12 pixels wide) */
   //     #     
   //      #     
   //       #   
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

   /* @1408 'a' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    #####   
   //        ## 
   //        ## 
   //    ###### 
   //   ##   ## 
   //   ##  ### 
   //    ### ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0xF0, 0xE0, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x02, 0x07, 0x07, 0x04, 0x00,

   /* @1430 'b' (12 pixels wide) */
   //           
   //  ###       
   //   ##       
   //   ##       
   //   ## ###   
   //   ###  ## 
   //   ##    ##
   //   ##    ##
   //   ##    ##
   //   ###  ## 
   //  ### ###   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x02, 0xFE, 0xFE, 0x20, 0x10, 0x10, 0x30, 0xE0, 0xC0, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x02, 0x04, 0x04, 0x06, 0x03, 0x01, 0x00,

   /* @1452 'c' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    #### # 
   //   ##   ## 
   //  ##     # 
   //  ##       
   //  ##     # 
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x10, 0x20, 0x70, 0x00, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x06, 0x03, 0x00, 0x00,

   /* @1474 'd' (12 pixels wide) */
   //           
   //       ### 
   //        ## 
   //        ## 
   //    ### ## 
   //   ##  ### 
   //  ##    ## 
   //  ##    ## 
   //  ##    ## 
   //   ##  ### 
   //    ### ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x22, 0xFE, 0xFE, 0x00, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x02, 0x07, 0x07, 0x04, 0x00,

   /* @1496 'e' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    #####   
   //   ##   ## 
   //  ##     ##
   //  #########
   //  ##       
   //   ##    ##
   //    ###### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0xB0, 0x90, 0x90, 0x90, 0xB0, 0xE0, 0xC0, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x04, 0x06, 0x02, 0x00,

   /* @1518 'f' (12 pixels wide) */
   //           
   //      ######
   //     ##     
   //     ##     
   //   ####### 
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //   ####### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x10, 0x10, 0xFC, 0xFE, 0x12, 0x12, 0x12, 0x02, 0x02,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00, 0x00,

   /* @1540 'g' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    ### ###
   //   ##  ### 
   //  ##    ## 
   //  ##    ## 
   //  ##    ## 
   //   ##  ### 
   //    ### ## 
   //        ## 
   //        ## 
   //    #####   
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x20, 0xF0, 0xF0, 0x10, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x26, 0x24, 0x24, 0x22, 0x3F, 0x1F, 0x00, 0x00,

   /* @1562 'h' (12 pixels wide) */
   //           
   //  ###       
   //   ##       
   //   ##       
   //   ## ###   
   //   ###  ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //  #### ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x02, 0xFE, 0xFE, 0x20, 0x10, 0x10, 0xF0, 0xE0, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00,

   /* @1584 'i' (12 pixels wide) */
   //           
   //      ##   
   //      ##   
   //           
   //    ####   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //   ########
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x10, 0x10, 0xF6, 0xF6, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00,

   /* @1606 'j' (12 pixels wide) */
   //           
   //      ##   
   //      ##   
   //           
   //   ######   
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //       ##   
   //   #####   
   //           
   //           
   0x00, 0x00, 0x00, 0x10, 0x10, 0x10, 0x16, 0xF6, 0xF0, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x3F, 0x1F, 0x00, 0x00, 0x00,

   /* @1628 'k' (12 pixels wide) */
   //           
   //  ###       
   //   ##       
   //   ##       
   //   ## #### 
   //   ## ##   
   //   ####     
   //   ####     
   //   ## ##   
   //   ##  ##   
   //  ### #####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x02, 0xFE, 0xFE, 0xC0, 0xF0, 0x30, 0x10, 0x10, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x00, 0x05, 0x07, 0x06, 0x04, 0x04, 0x00,

   /* @1650 'l' (12 pixels wide) */
   //           
   //    ####   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //   ########
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x04, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00,

   /* @1672 'm' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  ######## 
   //   ## ## ##
   //   ## ## ##
   //   ## ## ##
   //   ## ## ##
   //   ## ## ##
   //  ### ## ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0xF0, 0xF0, 0x10, 0xF0, 0xF0, 0x10, 0xF0, 0xE0, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x00, 0x07, 0x07, 0x00, 0x07, 0x07, 0x04,

   /* @1694 'n' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  ### ###   
   //   ###  ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //  #### ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0xF0, 0xF0, 0x20, 0x10, 0x10, 0xF0, 0xE0, 0x00, 0x00,
   0x00, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00, 0x04, 0x07, 0x07, 0x04, 0x00,

   /* @1716 'o' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    #####   
   //   ##   ## 
   //  ##     ##
   //  ##     ##
   //  ##     ##
   //   ##   ## 
   //    #####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x10, 0x30, 0xE0, 0xC0, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x04, 0x04, 0x06, 0x03, 0x01, 0x00,

   /* @1738 'p' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  ### ###   
   //   ###  ## 
   //   ##    ##
   //   ##    ##
   //   ##    ##
   //   ###  ## 
   //   ## ###   
   //   ##       
   //   ##       
   //  #####     
   //           
   //           
   0x00, 0x00, 0x10, 0xF0, 0xF0, 0x20, 0x10, 0x10, 0x30, 0xE0, 0xC0, 0x00,
   0x00, 0x00, 0x20, 0x3F, 0x3F, 0x22, 0x24, 0x04, 0x06, 0x03, 0x01, 0x00,

   /* @1760 'q' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    ### ###
   //   ##  ### 
   //  ##    ## 
   //  ##    ## 
   //  ##    ## 
   //   ##  ### 
   //    ### ## 
   //        ## 
   //        ## 
   //      #####
   //           
   //           
   0x00, 0x00, 0xC0, 0xE0, 0x30, 0x10, 0x10, 0x20, 0xF0, 0xF0, 0x10, 0x00,
   0x00, 0x00, 0x01, 0x03, 0x06, 0x04, 0x24, 0x22, 0x3F, 0x3F, 0x20, 0x00,

   /* @1782 'r' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  #### ### 
   //    ###  ##
   //    ##     
   //    ##     
   //    ##     
   //    ##     
   //  #######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0x10, 0xF0, 0xF0, 0x20, 0x10, 0x10, 0x30, 0x20, 0x00,
   0x00, 0x00, 0x04, 0x04, 0x07, 0x07, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00,

   /* @1804 's' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //    ###### 
   //   ##   ## 
   //   ####     
   //    #####   
   //       ### 
   //   ##   ## 
   //   ######   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x60, 0xF0, 0xD0, 0xD0, 0x90, 0xB0, 0x30, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x06, 0x06, 0x04, 0x04, 0x05, 0x07, 0x03, 0x00, 0x00,

   /* @1826 't' (12 pixels wide) */
   //           
   //    ##     
   //    ##     
   //    ##     
   //  #######   
   //    ##     
   //    ##     
   //    ##     
   //    ##     
   //    ##   # 
   //     ####   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0x10, 0xFE, 0xFE, 0x10, 0x10, 0x10, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x04, 0x02, 0x00, 0x00,

   /* @1848 'u' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  ###  ### 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##   ## 
   //   ##  ### 
   //    ### ###
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0xF0, 0xF0, 0x00, 0x00, 0x10, 0xF0, 0xF0, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x03, 0x07, 0x04, 0x04, 0x02, 0x07, 0x07, 0x04, 0x00,

   /* @1870 'v' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  #### ####
   //   ##   ## 
   //   ##   ## 
   //    ## ##   
   //    ## ##   
   //     ###   
   //     ###   
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0x70, 0xF0, 0x90, 0x00, 0x90, 0xF0, 0x70, 0x10, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x07, 0x01, 0x00, 0x00, 0x00,

   /* @1892 'w' (12 pixels wide) */
   //           
   //           
   //           
   //           
   // ####   ####
   //  ##     ##
   //  ##  #  ##
   //  ## ### ##
   //   ### ### 
   //   ### ### 
   //   ##   ## 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x10, 0xF0, 0xF0, 0x10, 0x80, 0xC0, 0x80, 0x10, 0xF0, 0xF0, 0x10,
   0x00, 0x00, 0x00, 0x07, 0x07, 0x03, 0x00, 0x03, 0x07, 0x07, 0x00, 0x00,

   /* @1914 'x' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  #### ####
   //    ## ##   
   //     ###   
   //     ###   
   //     ###   
   //    ## ##   
   //  #### ####
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x10, 0x10, 0x30, 0xF0, 0xC0, 0xF0, 0x30, 0x10, 0x10, 0x00,
   0x00, 0x00, 0x04, 0x04, 0x06, 0x07, 0x01, 0x07, 0x06, 0x04, 0x04, 0x00,

   /* @1936 'y' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //  ####  ####
   //   ##    ##
   //    ##  ## 
   //    ##  ## 
   //     # ##   
   //     ####   
   //      ##   
   //      ##   
   //     ##     
   //   #####   
   //           
   //           
   0x00, 0x00, 0x10, 0x30, 0xF0, 0xD0, 0x00, 0x00, 0xD0, 0xF0, 0x30, 0x10,
   0x00, 0x00, 0x00, 0x20, 0x20, 0x33, 0x3E, 0x2F, 0x03, 0x00, 0x00, 0x00,

   /* @1958 'z' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //   ####### 
   //   #    ## 
   //       ##   
   //     ###   
   //    ##     
   //   ##    # 
   //   ####### 
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x30, 0x10, 0x90, 0x90, 0xD0, 0x70, 0x30, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x06, 0x07, 0x05, 0x04, 0x04, 0x04, 0x06, 0x00, 0x00,

   /* @1980 '{' (12 pixels wide) */
   //           
   //      ##   
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //    ##     
   //     ##     
   //     ##     
   //     ##     
   //     ##     
   //      ##   
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x80, 0xFC, 0x7E, 0x02, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x1F, 0x10, 0x00, 0x00, 0x00, 0x00,

   /* @2002 '|' (12 pixels wide) */
   //           
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFE, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x1F, 0x00, 0x00, 0x00, 0x00,

   /* @2024 '}' (12 pixels wide) */
   //           
   //     ##     
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //       ##   
   //      ##   
   //      ##   
   //      ##   
   //      ##   
   //     ##     
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x7E, 0xFC, 0x80, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x1F, 0x0F, 0x00, 0x00, 0x00, 0x00,

   /* @2046 '~' (12 pixels wide) */
   //           
   //           
   //           
   //           
   //           
   //    ##     
   //   #  #  # 
   //       ##   
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   //           
   0x00, 0x00, 0x00, 0x40, 0x20, 0x20, 0x40, 0x80, 0x80, 0x40, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};


Now the courier font is a complete 16*12 font from characters ' ' to '~'. This though uses over 2K of ROM!... Don't try to use on a small chip....
This is output using the new OLED_Btext routine (actually a #define), that accepts an alpha text string, and will accept LF, and terminate when it gets to the NULL ('\0') character.

Separately there is also a new 'NUM12' font. This draws just numbers (and decimal point and '/' ), making them as tall as possible in the larger font space. This uses 288 characters of ROM. There is a new optional #define 'BIG_PLUS'. If this is defined, this font gains six more characters (taking it up to 432 characters of ROM), and adding characters for the letter C, F, a copywrite symbol, degree symbol, and squared and cubed. These are not mapped into the normal character locations for these, so have to be used with explicit defines.
Code:

//same processor header
//display dimensions - the physical LCD
#define S_LCDWIDTH               128
#define S_LCDHEIGHT              64

//#define TEXT_ONLY //REM this out

//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 <string.h>
#include "ssd1306.h" //The OLED driver
#include <NUM12.h> //Load the large font for numeric and controls

void main()
{
   int8 ctr;
   char text[9]; //temporary text buffer
   //Remember if you want to use this for longer text, you'll need to
   //expand the buffer....

   delay_ms(250);                //OLED takes time to wake
   //now try to initialise OLED
   OLED_commands(init_sequence,sizeof(init_sequence)); //initialise the OLED
   
   //First try some text
   OLED_CLS(); //clear the physical screen
   set=TRUE;
   strcpy(text,"Hello");
   size=NORMAL;
   OLED_text(text,strlen(text)); //Standard font
   delay_ms(1000);
   sprintf(text,"123.4%c%c",DEGREE,LETTC); //number & symbols
   OLED_CLS();
   size=DOUBLE_HEIGHT;
   OLED_Bnum(text); //Now display this big font doubled in height
   while (TRUE)
      ;
}


If you load the courier font, you can instead use the 'OLED_Btext' function, and output the full normal character set, but at the cost of a lot of ROM. The smaller 'NUM12' font offers characters 16 pixels high, and in both case these can be doubled in height/width, to give 2 lines and just 5 characters across at the largest!...

Hopefully this has all posted correctly, and makes sense.


Last edited by Ttelmah on Thu Oct 04, 2018 3:05 am; edited 3 times in total
MikeW



Joined: 15 Sep 2003
Posts: 184
Location: Warrington UK

View user's profile Send private message

picture of new fonts
PostPosted: Mon Jan 08, 2018 5:39 am     Reply with quote

this is a picture of 2 of the new fonts, on 2 different size displays



[img]http://imgbox.com/cUncmQWi[/img]
Manu59114



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

View user's profile Send private message

PostPosted: Wed Jan 31, 2018 5:59 am     Reply with quote

Hello everybody

A very big thank you for this brilliant code to drive a SSD1306 driver with a small RAM space. it's work perfect! i like the printf (OLED_putc) function! easy to use.

Tested on a PIC18F46K80 with 32MHz and 64MHz internal oscillator, Slow and fast I2C Hardware mode (Fast=80000 (80000 because there is a capricious PCF8574 I/O extender on the bus and fast speed "Fast=400000" without the I/O extender

1/
Now, I'm searching to display a simple logo at startup of my application with this code in "TEXT_ONLY" mode
but I don't know how to send a complete array of 1024 byte to the display

before to used the code of Ttelmah, I used first the code from SuperFranz (Thank you very much SuperFranz) and i was able to display a logo. (......const INT LOGOS [1024] ={ array of 1024 bytes....the picture}; )

Is there anyone on this forum who could open my eyes on how to do this job?

2/
Because, my application is powered with a battery, i try to obtain the minimum power consumption with this SS1306 Oiled display but it seems (i measured) that the power consumption of the display with all oled dot OFF is 1.6mA! it's too much!

Is there is a SLEEP command to send to the display?
I read on the web that there is a DISPLAY_ON/OFF (sleep!!!) command, a brightness Command....and a CHARGE Pump Command (internal switching regulator.......i suppose that it take some mA!)

To sleep the module, could it be with
Code:

ROM BYTE SLEEP_sequence[] =S_SETCONTRAST,
0x00,
S_DISPLAYOFF,
S_CHARGEPUMP,
0x10; // Charge pump OFF

ROM BYTE WAKEUP_sequence[] =S_CHARGEPUMP,
INT_VCC; // Charge pump ON (0x14)
S_DISPLAYON,
S_SETCONTRAST,
0xCF; //  0x00 to 0xFF


//OLED Display Sleep
OLED_commands(SLEEP_sequence, sizeof(SLEEP_sequence)); // SLEEP MODE


//OLED Display Wakeup
OLED_commands(WAKEUP_sequence, sizeof(WAKEUP_sequence)); // Wakeup sequence

I cannot try because I'm not close to my wish board for a moment! ;-(
Do you have any experiences with that?

Wish you a great day to all.
Mani Wink
_________________
CCS + ICD3 + PIC18F46K80
Manu59114



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

View user's profile Send private message

PostPosted: Wed Jan 31, 2018 1:14 pm     Reply with quote

Hello Again,

I just try this:


Code:

ROM BYTE SLEEP_sequence[] =S_SETCONTRAST,
0x00,
S_DISPLAYOFF;


ROM BYTE WAKEUP_sequence[] =
S_DISPLAYON,
S_SETCONTRAST,
0xCF; //  0x00 to 0xFF


//OLED Display Sleep
OLED_commands(SLEEP_sequence, sizeof(SLEEP_sequence)); // SLEEP MODE


//OLED Display Wakeup
OLED_commands(WAKEUP_sequence, sizeof(WAKEUP_sequence)); // Wakeup sequence



It works perfectly!!!
Before 1.6mA

Now with DISPLAYOFF command : 0.00645mA! (6.45µA)

I put definitely to the trash my old LCD Display 8x2.


I'm happy! Very Happy

Best regard
Manu
_________________
CCS + ICD3 + PIC18F46K80
Ttelmah



Joined: 11 Mar 2010
Posts: 19520

View user's profile Send private message

PostPosted: Wed Feb 07, 2018 2:05 am     Reply with quote

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
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page 1, 2, 3, 4, 5  Next
Page 1 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