SSD1306 I2C driver

Coding and general discussion relating to user created compiler modules

Moderators: David Barker, Jerry Messina

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

SSD1306 I2C driver

Post by SHughes_Fusion » Fri Jan 30, 2015 11:30 am

I've been playing with a small OLED module but I'm hitting a brick wall. The module is this one:
http://aura-comms.com/oled-display-led-module.html

Based on the SSD1306 but using I2C comms. I can't seem to get anything to display - it powers up but with just random pixels displaying. No matter what I try I can't change this.

Does anyone have any experience of these displays / driver in I2C mode? Most of the code available uses SPI mode and most versions of this display seem to have a Reset line which this doesn't.

Before I post my code, has anyone got one of these working and are there any tips or things they've come across that I might have overlooked? I've basically re-created the sample code on the BuyDisplay website which seems to be the source of the actual OLED panel. I've found similar code elsewhere and even looked at the init code on the SSD1306 datasheet and I can't see what I'm doing wrong...

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: SSD1306 I2C driver

Post by Coccoliso » Fri Jan 30, 2015 12:49 pm

Hello SHughes_Fusion,
if you've already downloaded the PDF datasheet as has already happened to me with TFT should initially have a look at Arduino libraries since this LCD has some success among his fans.
Arduino libraries are here:

https://github.com/adafruit/Adafruit_SSD1306
https://github.com/adafruit/Adafruit_SS ... master.zip

I saw that the source refers to both models SPI and I2C.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Fri Jan 30, 2015 1:25 pm

Thanks, that was one of the resources I'd already found but it doesn't seem to help.

Indeed, I have also tried copying and pasting their initialisation code but as it was pretty much the same as mine, albeit in a different order, it made no difference.

Two things I've found that I'm struggling to understand if they make a difference.

Firstly, the display apparently does not transmit ACK pulses. However, from what I can see in both the Swordfish I2C library and the datasheet for my processor (18F26K22) while the ACK pulse is recorded, it does not appear to be used at all.

Second, I've put a scope on the SCL/SDA lines and the rising edge seems very slow. I've reduced the clock as far as I can - to around 62kHz - but even then I don't get a very good 'high' pulse on the SCL line. I'm just using the built-in 10k pull-ups, may be worth reducing the value?

It looks a great little display but most of the online retailers don't give a right lot of support...

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Fri Jan 30, 2015 3:10 pm

Right, I'm getting further. I've found that by issuing the command $A5 in place of $A4 I can turn all pixels on. This suggests my init code is working as expected and the problem is with my write commands.

Unfortunately, I've tried stripping CLS back to the basics and calling the I2C commands direct and I still end up with garbage on the screen...

Jerry Messina
Swordfish Developer
Posts: 1469
Joined: Fri Jan 30, 2009 6:27 pm
Location: US

Re: SSD1306 I2C driver

Post by Jerry Messina » Fri Jan 30, 2015 4:01 pm

Looking at the datasheet for the SSD1306, that has GOT to be one of the worst "I2C" implementations I've ever seen.
What were they thinking with individual SDAin and SDAout lines? No ACK? Are you kidding?

Any chance you could switch to SPI?

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Fri Jan 30, 2015 4:05 pm

I agree, it's not that well done. It's also very bus intensive as each command needs two bytes - all they have done is duplicate the D/C pin from the SPI bus in the first byte. Would work much better if they just implemented a 'data write' command.

Unfortunately the version I have is the 4-pin I2C only one. I may end up dumping it though!

It seems to be recognising commands - I've got it toggling display invert - but nothing I write to the graphics RAM is reflected on the display.

Doing a bit more reading it seems as if many OLEDs sold as having the SSD1306 actually use the SH1106 which is subtly different. Just reading that datasheet to see if that helps...

User avatar
Coccoliso
Posts: 152
Joined: Mon Feb 17, 2014 10:34 am

Re: SSD1306 I2C driver

Post by Coccoliso » Fri Jan 30, 2015 4:51 pm

It seems that it is impractical to use I2C.
With SPI things improve but .. http://forum.arduino.cc/index.php?topic=286837.0
Last edited by Coccoliso on Fri Jan 30, 2015 4:57 pm, edited 2 times in total.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Fri Jan 30, 2015 4:55 pm

I've used similar displays, both OLED and LCD displays based on the SSD1306 and ST7565 via SPI and I don't see any issue with display update.

The OP in that thread seems to have missed the obvious - they are talking about writing a pixel at a time. I write a byte at a time. I've defined various fonts, making sure they are multiples of 8 pixels high and it seems to work fine.

The bus speed may be an issue with this display however, we'll see...

Either way I still can't get it to update the display... Just won't write to graphics RAM no matter what I do!

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Mon Feb 02, 2015 10:36 am

I'm beginning to wonder if the display is faulty. Nothing I try changes what is output. I've tried loads of different configuration settings from various sites on both the SSD1306 and the SH1106 and while the commands are being recognised I just can't get anything to display. Even writing one byte at a time (with full Start/Address/Data/Stop) doesn't do anything.

Time to buy an SPI one I think!

User avatar
RangerBob
Posts: 152
Joined: Thu May 31, 2007 8:52 am
Location: Beds, UK

Re: SSD1306 I2C driver

Post by RangerBob » Mon Feb 02, 2015 5:09 pm

I have a working set of SSD1306 routines but.....

*WARNING*
They are very odd as there is a multi-master I2C on the bus in my application, and I have to keep a polled USB connection going at the same time.

As such the Display I2C commands are bitbanged, whilst saving the MSSP settings for also acting as a I2C slave whilst not sending out data. The constant checking during the send routine is to abort if something else tries speaking at the same time! It also doesn't try to integrate the swordfish GLCD type primatives commands; it is self contained, including font table.
*END WARNING*

I don't have time to clean it up right now, but you should be able to pick the bits you need out of it.

EDIT: see below for updated, slightly more generic version.
Last edited by RangerBob on Tue Feb 03, 2015 11:09 am, edited 1 time in total.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Tue Feb 03, 2015 9:31 am

Thanks a lot for that.

It has just gone to convince me there is something wrong with my display however!

I borrowed your I2C library and after modifying the timings (I'm running at 64MHz - long story) I still don't get anywhere. Commands are executing fine, display RAM just does not update.

I've tweaked by display init routine to match yours - I had a few different settings but no two people seem to agree as to what settings to use anyway! They don't seem ideal for my display as the display flickers slightly - I think you are using a 16 line display? So the clock will be too slow. Shouldn't affect the operation of the display though.

I'm using the following routine to clear the display RAM:

Code: Select all

Public Sub Cls()
  Dim i, j As Byte
	
  For i = 0 To D_PAGES
    Set_Start_Page(i)
    Set_Start_Column(D_OFFSET)
 
    DispI2C_Start
    Address_Display
    Start_Data
  
    For j = 0 To D_COLUMNS-1
      Display_Data($00)
    Next 
    DispI2C_Stop
  Next 
End Sub
The low-level subs are:

Code: Select all

    
  Dim DispI2C_WriteByte As OLEDI2C.WriteByte
  Dim DispI2C_Init As OLEDI2C.Initialize
  Dim DispI2C_Start As OLEDI2C.Start
  Dim DispI2C_Stop As OLEDI2C.Stop

    // Write the display address to start a transmission
Public Sub Address_Display()
  DispI2C_WriteByte(D_Addr)
End Sub

    // Send a command to the display. Send the first byte with Co set and D/C cleared.
Public Sub Display_Command(Cmd As Byte)
  DispI2C_Start
  Address_Display
  DispI2C_WriteByte($80)
  DispI2C_WriteByte(Cmd)
  DispI2C_Stop
End Sub

Public Sub Display_Command_1D(Cmd As Byte, Data As Byte)
  DispI2C_Start
  Address_Display
  DispI2C_WriteByte($80)
  DispI2C_WriteByte(Cmd)
  DispI2C_WriteByte($00)
  DispI2C_WriteByte(Data)
  DispI2C_Stop
End Sub

Public Sub Display_Command_2D(Cmd As Byte, Data1 As Byte, Data2 As Byte)
  DispI2C_Start
  Address_Display
  DispI2C_WriteByte($80)
  DispI2C_WriteByte(Cmd)
  DispI2C_WriteByte($00)
  DispI2C_WriteByte(Data1)
  DispI2C_WriteByte($00)
  DispI2C_WriteByte(Data2)
  DispI2C_Stop
End Sub

    // Send data to the display(Data as byte)
Public Sub Display_Data(Data As Byte)
  DispI2C_WriteByte(Data)
End Sub    

    // Start a data transmission. Must call this before using above to send bulk data. Clears Co and sets D/C.
Public Sub Start_Data()
  DispI2C_WriteByte($40)
End Sub

Public Sub Set_Start_Page(d As Byte)
  d = d And $07
  Display_Command($B0 Or d)
End Sub

Sub Set_Start_Column(d As Byte)
  Display_Command($00 Or (d Mod 16))                // Set lower nibble
  Display_Command($10 Or (d/16))                    // Set upper nibble
End Sub
I should say I've tried various combinations of $00 and $80 when sending commands - the datasheet is clear as mud as to what should be used here and again different sample code I've found does different things.

I don't know if this is a 'safe' assumption, but if a random pattern is visible (and I can toggle all pixels on with command $A5) then I've got enough configuration done that I should be able to display something?

Some of the CLS code may seem a little redundant - calling one-line subs for instance - but this is because I've tried to integrate this display in to a library I've written which supports several different LCD and OLED displays, most of which use the SPI bus.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Tue Feb 03, 2015 10:53 am

Further to this, I've adapted your Fill_RAM code - previously I'd been ignoring the ACK signal.

The Fill_RAM command is completing without problem but the display is not updating.

That leaves me with two possible options - either something fundamental is going wrong with my configuration / init of the display, or the display is faulty.

My init code for the display is:

Code: Select all

  Set_Display_Off                               // $AE - Display Off
  Set_Start_Column(0)                           // $00 / $10
  Set_Start_Page(0)                             // $B0
  Set_Addressing_Mode($02)                      // $20 / $02 - Set Page Addressing Mode (0x00/0x01/0x02)    
  Set_Display_Clock($80)                        // $D5 / $80 - Set Clock as ??? Frames/Sec       
  Set_Multiplex_Ratio(D_ROWS-1)                 // $A8 / $3F - Duty - comes from number of rows    (O_ROWS)
  Set_Display_Offset(0)                         // $D3 / $00 - Shift Mapping RAM Counter (0x00~0x3F)
  Set_Charge_Pump_Enable                        // $8D / $14
  Set_Segment_Remap                             // $A1 - Set SEG/Column Mapping (0x00/0x01)
  Set_Common_Reverse                            // $C8 - Set COM/Row Scan Direction (0x00/0x08)
  Set_Common_Config($02)                        // $DA / $12 - Set Alternative Configuration (0x00/0x10)
  Set_Contrast_Control(D_CONTRAST)              // $81 / $8F - Set SEG Output Current
  Set_Precharge_Period($D2)                     // $D9 / $D2 - Set Pre-Charge as 
  Set_VCOMH($20)                                // Set VCOM Deselect Level
  Set_Display_Start_Line(0)                     // $40
  Set_Display_From_RAM                          // $A4 - Display comes from RAM
  Set_Inverse_Display_Off                       // $A6 - Non-inverted display
  Set_Display_On                                // $AF - Display On 

User avatar
RangerBob
Posts: 152
Joined: Thu May 31, 2007 8:52 am
Location: Beds, UK

Re: SSD1306 I2C driver

Post by RangerBob » Tue Feb 03, 2015 11:35 am

Ok, I've gone through and stripped out the more weird stuff from the code, now its just a single file that uses the inbuilt MSSP routines.

As you said, different displays seem to demand different setup commands and I can't even get hold of the datasheet for your display! The display I'm using is a univision UG-9616GLBBG01 (96x16 unit). The Univision datasheet has a very defined start up set of registers though. I think some of your registers are a bit funky, might be worth checking them (e.g. Set segment remap to $A1, is that right? it's supposed to be either 0 or 1? Same with Set_Start_line, my datasheet says $00-$3F, you appear to have it at $40?). Try putting my default values in to start and see if you get anything, nothing should be damaged.

As I said I can't download the datasheet for your unit (can't be bothered to log into their forum), but its very odd that they say ACK's don't work for I2C. That to me is pretty fundamental about I2C! You get ACKs to show that the darn thing is working!

One other thing to watch is that the display is correctly reset. My displays can act very weird if they are not reset. They can get into a funny state that puts random dots across the display.

Here's my tidied up code if that helps, along with a small example program and proof that I'm not talking rubbish ;):

Code: Select all

{
****************************************************************
*  Name    : SSD1306.BAS                                       *
*  Author  : Nathan Herbert - RangerBob                        *
*  Notice  : Copyright (c) 2014 SM                             *
*          : All Rights Reserved                               *
*  Date    : 21/01/2014                                        *
*  Version : 1.0                                               *
*  Notes   :                                                   *
*          :                                                   *
****************************************************************
}


{
Notes:

SSD1306 controller has 128 columns x 8 Pages (64) in memory! 
This means you can write into the non-visible memory and
have it easily scoll 

}

Module OLED

Include "i2c.bas"
Include "convert.bas"

#option OLED_ADDRESS = $78

#option OLED_CABLE = RIGHT_EXIT
#if Not (OLED_CABLE in (LEFT_EXIT, RIGHT_EXIT))
   #error OLED_CABLE, "Invalid option. OLED_CABLE must be LEFT or RIGHT."
#endif

Const 
	Address = OLED_ADDRESS

Public Const
	DirRight 		= $00,
	DirLeft 		= $01,
	ModeOff			= $00,
	ModeBlink		= %00110000,
	ModeFade		= %00100000,
	MaxBrightness	= $3F,
	ScrollOff		= 0,
	ScrollOn		= 1,
	ScrollDelayed 	= 7,
	Int_5_frames	= %00000000,
	Int_64_frames	= %00000001,
	Int_128_frames	= %00000010,
	Int_256_frames	= %00000011,
	Int_3_frames	= %00000100,
	Int_4_frames	= %00000101,
	Int_25_frames	= %00000110,
	Int_2_frames	= %00000111
	

Public Structure TImage
   Header As Byte
   Width As Byte
   Height As Byte
End Structure

Public Dim
	Scrolling As Byte,
	Brightness As Byte

Sub Write_Command(pData As Byte) 

	Start()
	I2C.WriteByte(Address)    		// Address + write
	I2C.WriteByte($00)          	// command
	I2C.WriteByte(pData)
	Stop()
	
End Sub

Sub Write_Data(pData As Byte)
 
	Start()
	I2C.WriteByte(Address)    		// Address + write
	I2C.WriteByte($40)          	// data
	I2C.WriteByte(pData)
	Stop()
	
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Instruction Setting
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Sub Set_Start_Column(d As Byte)

	Write_Command($00+( d Mod 16))		// Set Lower Column Start Address for Page Addressing Mode
						//   Default => 0x00
	Write_Command($10+( d /16))		// Set Higher Column Start Address for Page Addressing Mode
						//   Default => 0x10
End Sub


Sub Set_Addressing_Mode(d As Byte)

	Write_Command($20)			// Set Memory Addressing Mode
	Write_Command(d)			//   Default => 0x02
						//     0x00 => Horizontal Addressing Mode
						//     0x01 => Vertical Addressing Mode
						//     0x02 => Page Addressing Mode
End Sub


Sub Set_Column_Address(a As Byte, b As Byte)

	Write_Command($21)			// Set Column Address
	Write_Command(a)			//   Default => 0x00 (Column Start Address)
	Write_Command(b)			//   Default => 0x7F (Column End Address)
End Sub


Sub Set_Page_Address(a As Byte, b As Byte)

	Write_Command($22)			// Set Page Address
	Write_Command(a)			//   Default => 0x00 (Page Start Address)
	Write_Command(b)			//   Default => 0x07 (Page End Address)
End Sub


Sub Set_Start_Line(d As Byte)

	Write_Command($40 Or d)			// Set Display Start Line
						//   Default => 0x40 (0x00)
End Sub


Sub Set_Contrast_Control(d As Byte)

	Write_Command($81)			// Set Contrast Control
	Write_Command(d)			//   Default => 0x7F
End Sub


Sub Set_Charge_Pump(d As Byte)

	Write_Command($8D)			// Set Charge Pump
	Write_Command($10 Or d)			//   Default => 0x10
						//     0x10 (0x00) => Disable Charge Pump
						//     0x14 (0x04) => Enable Charge Pump
End Sub


Sub Set_Segment_Remap(d As Byte)

	Write_Command($A0 Or d)			// Set Segment Re-Map
						//   Default => 0xA0
						//     0xA0 (0x00) => Column Address 0 Mapped to SEG0
						//     0xA1 (0x01) => Column Address 0 Mapped to SEG127
End Sub


Sub Set_Entire_Display(d As Byte)

	Write_Command($A4 Or d)			// Set Entire Display On / Off
						//   Default => 0xA4
						//     0xA4 (0x00) => Normal Display
						//     0xA5 (0x01) => Entire Display On
End Sub


Sub Set_Inverse_Display(d As Byte)

	Write_Command($A6 Or d)			// Set Inverse Display On/Off
						//   Default => 0xA6
						//     0xA6 (0x00) => Normal Display
						//     0xA7 (0x01) => Inverse Display On
End Sub


Sub Set_Multiplex_Ratio(d As Byte)

	Write_Command($A8)			// Set Multiplex Ratio
	Write_Command(d)			//   Default => 0x3F (1/64 Duty)
End Sub


Sub Set_Display_On_Off(d As Byte)

	Write_Command($AE Or d)			// Set Display On/Off
						//   Default => 0xAE
						//     0xAE (0x00) => Display Off
						//     0xAF (0x01) => Display On
End Sub


Sub Set_Start_Page(d As Byte)

	Write_Command($B0 Or d)			// Set Page Start Address for Page Addressing Mode
						//   Default => 0xB0 (0x00)
End Sub


Sub Set_Common_Remap(d As Byte)

	Write_Command($C0 Or d)			// Set COM Output Scan Direction
						//   Default => 0xC0
						//     0xC0 (0x00) => Scan from COM0 to 63
						//     0xC8 (0x08) => Scan from COM63 to 0
End Sub


Sub Set_Display_Offset(d As Byte)

	Write_Command($D3)			// Set Display Offset
	Write_Command(d)			//   Default => 0x00
End Sub


Sub Set_Display_Clock(d As Byte)

	Write_Command($D5)			// Set Display Clock Divide Ratio / Oscillator Frequency
	Write_Command(d)			//   Default => 0x80
						//     D[3:0] => Display Clock Divider
						//     D[7:4] => Oscillator Frequency
End Sub


Sub Set_Precharge_Period(d As Byte)

	Write_Command($D9)			// Set Pre-Charge Period
	Write_Command(d)			//   Default => 0x22 (2 Display Clocks [Phase 2] / 2 Display Clocks [Phase 1])
						//     D[3:0] => Phase 1 Period in 1~15 Display Clocks
						//     D[7:4] => Phase 2 Period in 1~15 Display Clocks
End Sub


Sub Set_Common_Config(d As Byte)

	Write_Command($DA)			// Set COM Pins Hardware Configuration
	Write_Command($02 Or d)			//   Default => 0x12 (0x10)
						//     Alternative COM Pin Configuration
						//     Disable COM Left/Right Re-Map
End Sub


Sub Set_VCOMH(d As Byte)

	Write_Command($DB)			// Set VCOMH Deselect Level
	Write_Command(d)			//   Default => 0x20 (0.77*VCC)
End Sub


Sub Set_NOP()

	Write_Command($E3)			// Command for No Operation
End Sub


//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Global Variables
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Const
	XLevelL		= $00,
	XLevelH		= $10,
	XLevel		= ((XLevelH And $0F)*16+XLevelL),
	Max_Column	= 96,
	Max_Row		= 16
	


Function ReadDataByte() As Byte
   Asm-
      TBLRD *+
      movff TABLAT, result
   End Asm
End Function

//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Patterns
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Use Monochrome image converter : Image with Y - Direction Bytes, Y must be multiple of 8

Public Const Batteryimg(40) As Byte =
      ($01,18,16,0,
      $7F,$41,$5D,$5D,$41,$5D,$5D,$41,$5D,$5D,$41,$5D,$5D,$41,$7F,$22,$3E,$00,
      $02,$04,$08,$10,$08,$04,$02,$01,$02,$04,$08,$04,$0E,$1F,$1F,$0A,$0A,$00)


Const Ascii_1() As Byte = 		// Refer to "Times New Roman" Font Database...
						//   Basic Characters
(	
	$00,$00,$00,$00,$00,		//   (  0)    - $0020 Space
	$00,$00,$4F,$00,$00,		//   (  1)  ! - $0021 Exclamation Mark
	$00,$07,$00,$07,$00,		//   (  2)  " - $0022 Quotation Mark
	$14,$7F,$14,$7F,$14,		//   (  3)  # - $0023 Number Sign
	$24,$2A,$7F,$2A,$12,		//   (  4)  $ - $0024 Dollar Sign
	$23,$13,$08,$64,$62,		//   (  5)  % - $0025 Percent Sign
	$36,$49,$55,$22,$50,		//   (  6)  & - $0026 Ampersand
	$00,$05,$03,$00,$00,		//   (  7)  ' - $0027 Apostrophe
	$00,$1C,$22,$41,$00,		//   (  8)  ( - $0028 Left Parenthesis
	$00,$41,$22,$1C,$00,		//   (  9)  ) - $0029 Right Parenthesis
	$14,$08,$3E,$08,$14,		//   ( 10)  * - $002A Asterisk
	$08,$08,$3E,$08,$08,		//   ( 11)  + - $002B Plus Sign
	$00,$50,$30,$00,$00,		//   ( 12)  , - $002C Comma
	$08,$08,$08,$08,$08,		//   ( 13)  - - $002D Hyphen-Minus
	$00,$60,$60,$00,$00,		//   ( 14)  . - $002E Full Stop
	$20,$10,$08,$04,$02,		//   ( 15)  / - $002F Solidus
	$3E,$51,$49,$45,$3E,		//   ( 16)  0 - $0030 Digit Zero
	$00,$42,$7F,$40,$00,		//   ( 17)  1 - $0031 Digit One
	$42,$61,$51,$49,$46,		//   ( 18)  2 - $0032 Digit Two
	$21,$41,$45,$4B,$31,		//   ( 19)  3 - $0033 Digit Three
	$18,$14,$12,$7F,$10,		//   ( 20)  4 - $0034 Digit Four
	$27,$45,$45,$45,$39,		//   ( 21)  5 - $0035 Digit Five
	$3C,$4A,$49,$49,$30,		//   ( 22)  6 - $0036 Digit Six
	$01,$71,$09,$05,$03,		//   ( 23)  7 - $0037 Digit Seven
	$36,$49,$49,$49,$36,		//   ( 24)  8 - $0038 Digit Eight
	$06,$49,$49,$29,$1E,		//   ( 25)  9 - $0039 Dight Nine
	$00,$36,$36,$00,$00,		//   ( 26)  : - $003A Colon
	$00,$56,$36,$00,$00,		//   ( 27)  ; - $003B Semicolon
	$08,$14,$22,$41,$00,		//   ( 28)  < - $003C Less-Than Sign
	$14,$14,$14,$14,$14,		//   ( 29)  = - $003D Equals Sign
	$00,$41,$22,$14,$08,		//   ( 30)  > - $003E Greater-Than Sign
	$02,$01,$51,$09,$06,		//   ( 31)  ? - $003F Question Mark
	$32,$49,$79,$41,$3E,		//   ( 32)  @ - $0040 Commercial At
	$7E,$11,$11,$11,$7E,		//   ( 33)  A - $0041 Latin Capital Letter A
	$7F,$49,$49,$49,$36,		//   ( 34)  B - $0042 Latin Capital Letter B
	$3E,$41,$41,$41,$22,		//   ( 35)  C - $0043 Latin Capital Letter C
	$7F,$41,$41,$22,$1C,		//   ( 36)  D - $0044 Latin Capital Letter D
	$7F,$49,$49,$49,$41,		//   ( 37)  E - $0045 Latin Capital Letter E
	$7F,$09,$09,$09,$01,		//   ( 38)  F - $0046 Latin Capital Letter F
	$3E,$41,$49,$49,$7A,		//   ( 39)  G - $0047 Latin Capital Letter G
	$7F,$08,$08,$08,$7F,		//   ( 40)  H - $0048 Latin Capital Letter H
	$00,$41,$7F,$41,$00,		//   ( 41)  I - $0049 Latin Capital Letter I
	$20,$40,$41,$3F,$01,		//   ( 42)  J - $004A Latin Capital Letter J
	$7F,$08,$14,$22,$41,		//   ( 43)  K - $004B Latin Capital Letter K
	$7F,$40,$40,$40,$40,		//   ( 44)  L - $004C Latin Capital Letter L
	$7F,$02,$0C,$02,$7F,		//   ( 45)  M - $004D Latin Capital Letter M
	$7F,$04,$08,$10,$7F,		//   ( 46)  N - $004E Latin Capital Letter N
	$3E,$41,$41,$41,$3E,		//   ( 47)  O - $004F Latin Capital Letter O
	$7F,$09,$09,$09,$06,		//   ( 48)  P - $0050 Latin Capital Letter P
	$3E,$41,$51,$21,$5E,		//   ( 49)  Q - $0051 Latin Capital Letter Q
	$7F,$09,$19,$29,$46,		//   ( 50)  R - $0052 Latin Capital Letter R
	$46,$49,$49,$49,$31,		//   ( 51)  S - $0053 Latin Capital Letter S
	$01,$01,$7F,$01,$01,		//   ( 52)  T - $0054 Latin Capital Letter T
	$3F,$40,$40,$40,$3F,		//   ( 53)  U - $0055 Latin Capital Letter U
	$1F,$20,$40,$20,$1F,		//   ( 54)  V - $0056 Latin Capital Letter V
	$3F,$40,$38,$40,$3F,		//   ( 55)  W - $0057 Latin Capital Letter W
	$63,$14,$08,$14,$63,		//   ( 56)  X - $0058 Latin Capital Letter X
	$07,$08,$70,$08,$07,		//   ( 57)  Y - $0059 Latin Capital Letter Y
	$61,$51,$49,$45,$43,		//   ( 58)  Z - $005A Latin Capital Letter Z
	$00,$7F,$41,$41,$00,		//   ( 59)  [ - $005B Left Square Bracket
	$02,$04,$08,$10,$20,		//   ( 60)  \ - $005C Reverse Solidus
	$00,$41,$41,$7F,$00,		//   ( 61)  ] - $005D Right Square Bracket
	$04,$02,$01,$02,$04,		//   ( 62)  ^ - $005E Circumflex Accent
	$40,$40,$40,$40,$40,		//   ( 63)  _ - $005F Low Line
	$01,$02,$04,$00,$00,		//   ( 64)  ` - $0060 Grave Accent
	$20,$54,$54,$54,$78,		//   ( 65)  a - $0061 Latin Small Letter A
	$7F,$48,$44,$44,$38,		//   ( 66)  b - $0062 Latin Small Letter B
	$38,$44,$44,$44,$20,		//   ( 67)  c - $0063 Latin Small Letter C
	$38,$44,$44,$48,$7F,		//   ( 68)  d - $0064 Latin Small Letter D
	$38,$54,$54,$54,$18,		//   ( 69)  e - $0065 Latin Small Letter E
	$08,$7E,$09,$01,$02,		//   ( 70)  f - $0066 Latin Small Letter F
	$06,$49,$49,$49,$3F,		//   ( 71)  g - $0067 Latin Small Letter G
	$7F,$08,$04,$04,$78,		//   ( 72)  h - $0068 Latin Small Letter H
	$00,$44,$7D,$40,$00,		//   ( 73)  i - $0069 Latin Small Letter I
	$20,$40,$44,$3D,$00,		//   ( 74)  j - $006A Latin Small Letter J
	$7F,$10,$28,$44,$00,		//   ( 75)  k - $006B Latin Small Letter K
	$00,$41,$7F,$40,$00,		//   ( 76)  l - $006C Latin Small Letter L
	$7C,$04,$18,$04,$7C,		//   ( 77)  m - $006D Latin Small Letter M
	$7C,$08,$04,$04,$78,		//   ( 78)  n - $006E Latin Small Letter N
	$38,$44,$44,$44,$38,		//   ( 79)  o - $006F Latin Small Letter O
	$7C,$14,$14,$14,$08,		//   ( 80)  p - $0070 Latin Small Letter P
	$08,$14,$14,$18,$7C,		//   ( 81)  q - $0071 Latin Small Letter Q
	$7C,$08,$04,$04,$08,		//   ( 82)  r - $0072 Latin Small Letter R
	$48,$54,$54,$54,$20,		//   ( 83)  s - $0073 Latin Small Letter S
	$04,$3F,$44,$40,$20,		//   ( 84)  t - $0074 Latin Small Letter T
	$3C,$40,$40,$20,$7C,		//   ( 85)  u - $0075 Latin Small Letter U
	$1C,$20,$40,$20,$1C,		//   ( 86)  v - $0076 Latin Small Letter V
	$3C,$40,$30,$40,$3C,		//   ( 87)  w - $0077 Latin Small Letter W
	$44,$28,$10,$28,$44,		//   ( 88)  x - $0078 Latin Small Letter X
	$0C,$50,$50,$50,$3C,		//   ( 89)  y - $0079 Latin Small Letter Y
	$44,$64,$54,$4C,$44,		//   ( 90)  z - $007A Latin Small Letter Z
	$00,$08,$36,$41,$00,		//   ( 91)   - $007B Left Curly Bracket
	$00,$00,$7F,$00,$00,		//   ( 92)  | - $007C Vertical Line
	$00,$41,$36,$08,$00,		//   ( 93)   - $007D Right Curly Bracket
	$02,$01,$02,$04,$02,		//   ( 94)  ~ - $007E Tilde
	$3E,$55,$55,$41,$22,		//   ( 95)  C - $0080 <Control>
	$00,$00,$00,$00,$00,		//   ( 96)    - $00A0 No-Break Space
	$00,$00,$79,$00,$00,		//   ( 97)  ! - $00A1 Inverted Exclamation Mark
	$18,$24,$74,$2E,$24,		//   ( 98)  c - $00A2 Cent Sign
	$48,$7E,$49,$42,$40,		//   ( 99)  L - $00A3 Pound Sign
	$5D,$22,$22,$22,$5D,		//   (100)  o - $00A4 Currency Sign
	$15,$16,$7C,$16,$15,		//   (101)  Y - $00A5 Yen Sign
	$00,$00,$77,$00,$00,		//   (102)  | - $00A6 Broken Bar
	$0A,$55,$55,$55,$28,		//   (103)    - $00A7 Section Sign
	$00,$01,$00,$01,$00,		//   (104)  " - $00A8 Diaeresis
	$00,$0A,$0D,$0A,$04,		//   (105)    - $00AA Feminine Ordinal Indicator
	$08,$14,$2A,$14,$22,		//   (106) << - $00AB Left-Pointing Double Angle Quotation Mark
	$04,$04,$04,$04,$1C,		//   (107)    - $00AC Not Sign
	$00,$08,$08,$08,$00,		//   (108)  - - $00AD Soft Hyphen
	$01,$01,$01,$01,$01,		//   (109)    - $00AF Macron
	$00,$02,$05,$02,$00,		//   (110)    - $00B0 Degree Sign
	$44,$44,$5F,$44,$44,		//   (111) +- - $00B1 Plus-Minus Sign
	$00,$00,$04,$02,$01,		//   (112)  ` - $00B4 Acute Accent
	$7E,$20,$20,$10,$3E,		//   (113)  u - $00B5 Micro Sign
	$06,$0F,$7F,$00,$7F,		//   (114)    - $00B6 Pilcrow Sign
	$00,$18,$18,$00,$00,		//   (115)  . - $00B7 Middle Dot
	$00,$40,$50,$20,$00,		//   (116)    - $00B8 Cedilla
	$00,$0A,$0D,$0A,$00,		//   (117)    - $00BA Masculine Ordinal Indicator
	$22,$14,$2A,$14,$08,		//   (118) >> - $00BB Right-Pointing Double Angle Quotation Mark
	$17,$08,$34,$2A,$7D,		//   (119) /4 - $00BC Vulgar Fraction One Quarter
	$17,$08,$04,$6A,$59,		//   (120) /2 - $00BD Vulgar Fraction One Half
	{$30,$48,$45,$40,$20,		//   (121)  ? - $00BF Inverted Question Mark
	$70,$29,$26,$28,$70,		//   (122) `A - $00C0 Latin Capital Letter A with Grave
	$70,$28,$26,$29,$70,		//   (123) 'A - $00C1 Latin Capital Letter A with Acute
	$70,$2A,$25,$2A,$70,		//   (124) ^A - $00C2 Latin Capital Letter A with Circumflex
	$72,$29,$26,$29,$70,		//   (125) ~A - $00C3 Latin Capital Letter A with Tilde
	$70,$29,$24,$29,$70,		//   (126) "A - $00C4 Latin Capital Letter A with Diaeresis
	$70,$2A,$2D,$2A,$70,		//   (127)  A - $00C5 Latin Capital Letter A with Ring Above
	$7E,$11,$7F,$49,$49,		//   (128) AE - $00C6 Latin Capital Letter Ae
	$0E,$51,$51,$71,$11,		//   (129)  C - $00C7 Latin Capital Letter C with Cedilla
	$7C,$55,$56,$54,$44,		//   (130) `E - $00C8 Latin Capital Letter E with Grave
	$7C,$55,$56,$54,$44,		//   (131) 'E - $00C9 Latin Capital Letter E with Acute
	$7C,$56,$55,$56,$44,		//   (132) ^E - $00CA Latin Capital Letter E with Circumflex
	$7C,$55,$54,$55,$44,		//   (133) "E - $00CB Latin Capital Letter E with Diaeresis
	$00,$45,$7E,$44,$00,		//   (134) `I - $00CC Latin Capital Letter I with Grave
	$00,$44,$7E,$45,$00,		//   (135) 'I - $00CD Latin Capital Letter I with Acute
	$00,$46,$7D,$46,$00,		//   (136) ^I - $00CE Latin Capital Letter I with Circumflex
	$00,$45,$7C,$45,$00,		//   (137) "I - $00CF Latin Capital Letter I with Diaeresis
	$7F,$49,$49,$41,$3E,		//   (138)  D - $00D0 Latin Capital Letter Eth
	$7C,$0A,$11,$22,$7D,		//   (139) ~N - $00D1 Latin Capital Letter N with Tilde
	$38,$45,$46,$44,$38,		//   (140) `O - $00D2 Latin Capital Letter O with Grave
	$38,$44,$46,$45,$38,		//   (141) 'O - $00D3 Latin Capital Letter O with Acute
	$38,$46,$45,$46,$38,		//   (142) ^O - $00D4 Latin Capital Letter O with Circumflex
	$38,$46,$45,$46,$39,		//   (143) ~O - $00D5 Latin Capital Letter O with Tilde
	$38,$45,$44,$45,$38,		//   (144) "O - $00D6 Latin Capital Letter O with Diaeresis
	$22,$14,$08,$14,$22,		//   (145)  x - $00D7 Multiplcation Sign
	$2E,$51,$49,$45,$3A,		//   (146)  O - $00D8 Latin Capital Letter O with Stroke
	$3C,$41,$42,$40,$3C,		//   (147) `U - $00D9 Latin Capital Letter U with Grave
	$3C,$40,$42,$41,$3C,		//   (148) 'U - $00DA Latin Capital Letter U with Acute
	$3C,$42,$41,$42,$3C,		//   (149) ^U - $00DB Latin Capital Letter U with Circumflex
	$3C,$41,$40,$41,$3C,		//   (150) "U - $00DC Latin Capital Letter U with Diaeresis
	$0C,$10,$62,$11,$0C,		//   (151) `Y - $00DD Latin Capital Letter Y with Acute
	$7F,$12,$12,$12,$0C,		//   (152)  P - $00DE Latin Capital Letter Thom
	$40,$3E,$01,$49,$36,		//   (153)  B - $00DF Latin Capital Letter Sharp S
	$20,$55,$56,$54,$78,		//   (154) `a - $00E0 Latin Small Letter A with Grave
	$20,$54,$56,$55,$78,		//   (155) 'a - $00E1 Latin Small Letter A with Acute
	$20,$56,$55,$56,$78,		//   (156) ^a - $00E2 Latin Small Letter A with Circumflex
	$20,$55,$56,$55,$78,		//   (157) ~a - $00E3 Latin Small Letter A with Tilde
	$20,$55,$54,$55,$78,		//   (158) "a - $00E4 Latin Small Letter A with Diaeresis
	$20,$56,$57,$56,$78,		//   (159)  a - $00E5 Latin Small Letter A with Ring Above
	$24,$54,$78,$54,$58,		//   (160) ae - $00E6 Latin Small Letter Ae
	$0C,$52,$52,$72,$13,		//   (161)  c - $00E7 Latin Small Letter c with Cedilla
	$38,$55,$56,$54,$18,		//   (162) `e - $00E8 Latin Small Letter E with Grave
	$38,$54,$56,$55,$18,		//   (163) 'e - $00E9 Latin Small Letter E with Acute
	$38,$56,$55,$56,$18,		//   (164) ^e - $00EA Latin Small Letter E with Circumflex
	$38,$55,$54,$55,$18,		//   (165) "e - $00EB Latin Small Letter E with Diaeresis
	$00,$49,$7A,$40,$00,		//   (166) `i - $00EC Latin Small Letter I with Grave
	$00,$48,$7A,$41,$00,		//   (167) 'i - $00ED Latin Small Letter I with Acute
	$00,$4A,$79,$42,$00,		//   (168) ^i - $00EE Latin Small Letter I with Circumflex
	$00,$4A,$78,$42,$00,		//   (169) "i - $00EF Latin Small Letter I with Diaeresis
	$31,$4A,$4E,$4A,$30,		//   (170)    - $00F0 Latin Small Letter Eth
	$7A,$11,$0A,$09,$70,		//   (171) ~n - $00F1 Latin Small Letter N with Tilde
	$30,$49,$4A,$48,$30,		//   (172) `o - $00F2 Latin Small Letter O with Grave
	$30,$48,$4A,$49,$30,		//   (173) 'o - $00F3 Latin Small Letter O with Acute
	$30,$4A,$49,$4A,$30,		//   (174) ^o - $00F4 Latin Small Letter O with Circumflex
	$30,$4A,$49,$4A,$31,		//   (175) ~o - $00F5 Latin Small Letter O with Tilde
	$30,$4A,$48,$4A,$30,		//   (176) "o - $00F6 Latin Small Letter O with Diaeresis
	$08,$08,$2A,$08,$08,		//   (177)  + - $00F7 Division Sign
	$38,$64,$54,$4C,$38,		//   (178)  o - $00F8 Latin Small Letter O with Stroke
	$38,$41,$42,$20,$78,		//   (179) `u - $00F9 Latin Small Letter U with Grave
	$38,$40,$42,$21,$78,		//   (180) 'u - $00FA Latin Small Letter U with Acute
	$38,$42,$41,$22,$78,		//   (181) ^u - $00FB Latin Small Letter U with Circumflex
	$38,$42,$40,$22,$78,		//   (182) "u - $00FC Latin Small Letter U with Diaeresis
	$0C,$50,$52,$51,$3C,		//   (183) 'y - $00FD Latin Small Letter Y with Acute
	$7E,$14,$14,$14,$08,		//   (184)  p - $00FE Latin Small Letter Thom
	$0C,$51,$50,$51,$3C,		//   (185) "y - $00FF Latin Small Letter Y with Diaeresis
	$1E,$09,$09,$29,$5E,		//   (186)  A - $0104 Latin Capital Letter A with Ogonek
	$08,$15,$15,$35,$4E,		//   (187)  a - $0105 Latin Small Letter A with Ogonek
	$38,$44,$46,$45,$20,		//   (188) 'C - $0106 Latin Capital Letter C with Acute
	$30,$48,$4A,$49,$20,		//   (189) 'c - $0107 Latin Small Letter C with Acute
	$38,$45,$46,$45,$20,		//   (190)  C - $010C Latin Capital Letter C with Caron
	$30,$49,$4A,$49,$20,		//   (191)  c - $010D Latin Small Letter C with Caron
	$7C,$45,$46,$45,$38,		//   (192)  D - $010E Latin Capital Letter D with Caron
	$20,$50,$50,$7C,$03,		//   (193) d' - $010F Latin Small Letter D with Caron
	$1F,$15,$15,$35,$51,		//   (194)  E - $0118 Latin Capital Letter E with Ogonek
	$0E,$15,$15,$35,$46,		//   (195)  e - $0119 Latin Small Letter E with Ogonek
	$7C,$55,$56,$55,$44,		//   (196)  E - $011A Latin Capital Letter E with Caron
	$38,$55,$56,$55,$18,		//   (197)  e - $011B Latin Small Letter E with Caron
	$00,$44,$7C,$40,$00,		//   (198)  i - $0131 Latin Small Letter Dotless I
	$7F,$48,$44,$40,$40,		//   (199)  L - $0141 Latin Capital Letter L with Stroke
	$00,$49,$7F,$44,$00,		//   (200)  l - $0142 Latin Small Letter L with Stroke
	$7C,$08,$12,$21,$7C,		//   (201) 'N - $0143 Latin Capital Letter N with Acute
	$78,$10,$0A,$09,$70,		//   (202) 'n - $0144 Latin Small Letter N with Acute
	$7C,$09,$12,$21,$7C,		//   (203)  N - $0147 Latin Capital Letter N with Caron
	$78,$11,$0A,$09,$70,		//   (204)  n - $0148 Latin Small Letter N with Caron
	$38,$47,$44,$47,$38,		//   (205) "O - $0150 Latin Capital Letter O with Double Acute
	$30,$4B,$48,$4B,$30,		//   (206) "o - $0151 Latin Small Letter O with Double Acute
	$3E,$41,$7F,$49,$49,		//   (207) OE - $0152 Latin Capital Ligature Oe
	$38,$44,$38,$54,$58,		//   (208) oe - $0153 Latin Small Ligature Oe
	$7C,$15,$16,$35,$48,		//   (209)  R - $0158 Latin Capital Letter R with Caron
	$78,$11,$0A,$09,$10,		//   (210)  r - $0159 Latin Small Letter R with Caron
	$48,$54,$56,$55,$20,		//   (211) 'S - $015A Latin Capital Letter S with Acute
	$20,$48,$56,$55,$20,		//   (212) 's - $015B Latin Small Letter S with Acute
	$48,$55,$56,$55,$20,		//   (213)  S - $0160 Latin Capital Letter S with Caron
	$20,$49,$56,$55,$20,		//   (214)  s - $0161 Latin Small Letter S with Caron
	$04,$05,$7E,$05,$04,		//   (215)  T - $0164 Latin Capital Letter T with Caron
	$08,$3C,$48,$22,$01,		//   (216) t' - $0165 Latin Small Letter T with Caron
	$3C,$42,$45,$42,$3C,		//   (217)  U - $016E Latin Capital Letter U with Ring Above
	$38,$42,$45,$22,$78,		//   (218)  u - $016F Latin Small Letter U with Ring Above
	$3C,$43,$40,$43,$3C,		//   (219) "U - $0170 Latin Capital Letter U with Double Acute
	$38,$43,$40,$23,$78,		//   (220) "u - $0171 Latin Small Letter U with Double Acute
	$0C,$11,$60,$11,$0C,		//   (221) "Y - $0178 Latin Capital Letter Y with Diaeresis
	$44,$66,$55,$4C,$44,		//   (222) 'Z - $0179 Latin Capital Letter Z with Acute
	$48,$6A,$59,$48,$00,		//   (223) 'z - $017A Latin Small Letter Z with Acute
	$44,$64,$55,$4C,$44,		//   (224)  Z - $017B Latin Capital Letter Z with Dot Above
	$48,$68,$5A,$48,$00,		//   (225)  z - $017C Latin Small Letter Z with Dot Above
	$44,$65,$56,$4D,$44,		//   (226)  Z - $017D Latin Capital Letter Z with Caron
	$48,$69,$5A,$49,$00,		//   (227)  z - $017E Latin Small Letter Z with Caron
	$00,$02,$01,$02,$00,		//   (228)  ^ - $02C6 Modifier Letter Circumflex Accent
	$00,$01,$02,$01,$00,		//   (229)    - $02C7 Caron
	$00,$01,$01,$01,$00,		//   (230)    - $02C9 Modifier Letter Macron
	$01,$02,$02,$01,$00,		//   (231)    - $02D8 Breve
	$00,$00,$01,$00,$00,		//   (232)    - $02D9 Dot Above
	$00,$02,$05,$02,$00,		//   (233)    - $02DA Ring Above
	$02,$01,$02,$01,$00,		//   (234)  ~ - $02DC Small Tilde
	$7F,$05,$15,$3A,$50,		//   (235) Pt - $20A7 Peseta Sign
	$3E,$55,$55,$41,$22,		//   (236)  C - $20AC Euro Sign
	$18,$14,$08,$14,$0C,		//   (237)    - $221E Infinity
	$44,$4A,$4A,$51,$51,		//   (238)  < - $2264 Less-Than or Equal to
	$51,$51,$4A,$4A,$44,		//   (239)  > - $2265 Greater-Than or Equal to }
	$74,$42,$41,$42,$74			//   (240)    - $2302 House  
)
'

Const Symbols() As Byte = 		// Custom 6 Byte wide symbol Database...
(	            
	$08,$04,$08,$10,$08,$08,		//   (153)    - AC Symbol Left 
	$3E,$22,$22,$22,$22,$22,		//   (154)    - Battery Symbol Left 0
	$3E,$3E,$22,$22,$22,$22,		//   (155)    - Battery Symbol Left 10
    $3E,$3E,$3E,$22,$22,$22,		//   (156)    - Battery Symbol Left 20
    $3E,$3E,$3E,$3E,$22,$22,		//   (157)    - Battery Symbol Left 30
    $3E,$3E,$3E,$3E,$3E,$22,		//   (158)    - Battery Symbol Left 40
    $3E,$3E,$3E,$3E,$3E,$3E,		//   (159)    - Battery Symbol Left 50
    $3E,$3E,$3E,$3E,$3E,$3E,		//   (160)    - Battery Symbol Left 60
    $3E,$3E,$3E,$3E,$3E,$3E,		//   (161)    - Battery Symbol Left 70
    $3E,$3E,$3E,$3E,$3E,$3E,		//   (162)    - Battery Symbol Left 80
    $3E,$3E,$3E,$3E,$3E,$3E,		//   (163)    - Battery Symbol Left 90
    
	$1C,$3E,$3E,$14,$14,$00,		//   (164)    - AC Symbol Right 0
	$22,$22,$22,$36,$1C,$00,		//   (165)    - Battery Symbol Right 0
	$22,$22,$22,$36,$1C,$00,		//   (166)    - Battery Symbol Right 10
	$22,$22,$22,$36,$1C,$00,		//   (167)    - Battery Symbol Right 20
	$22,$22,$22,$36,$1C,$00,		//   (168)    - Battery Symbol Right 30
	$22,$22,$22,$36,$1C,$00,		//   (169)    - Battery Symbol Right 40
	$22,$22,$22,$36,$1C,$00,		//   (170)    - Battery Symbol Right 50
	$3E,$22,$22,$36,$1C,$00,		//   (171)    - Battery Symbol Right 60
	$3E,$3E,$22,$36,$1C,$00,		//   (172)    - Battery Symbol Right 70
	$3E,$3E,$3E,$36,$1C,$00,		//   (173)    - Battery Symbol Right 80
    $3E,$3E,$3E,$3E,$1C,$00, 		//   (174)    - Battery Symbol Right 90
    $00,$00,$00,$00,$00,$00,		// 	 (175)    - GPS Off Left 	(blank for now)
	$00,$00,$00,$00,$00,$00,		// 	 (176)    - GPS Searching Left 	(blank for now)
	$00,$00,$4E,$7C,$38,$74,		//   (177)	  - GPS Locked icon Left
	$00,$00,$00,$00,$00,$00,		//   (178)    - GPS off Right
	$00,$00,$00,$00,$00,$00,		//   (179)    - GPS Searching Right
	$69,$42,$04,$00,$00,$00  		//   (180)    - GPS Locked icon Right

)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Deactivate Scrolling (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Deactivate_Scroll()
	Write_Command($2E)			// Deactivate Scrolling
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Activate Scrolling (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Activate_Scroll()
	Write_Command($2F)			// Activate Scrolling
End Sub


Public Sub Blink(pMode As Byte, pInterval As Byte) ' pInterval X 8 frames
	
	pInterval = pInterval And %00001111
	pMode = pMode Or pInterval
	
	Write_Command($23)
	Write_Command(pMode)	
 
End Sub


//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show Character (5x7)
//
//    b: Ascii
//    c: Start Page
//    d: Start Column
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Sub Show_Char(Ascii As Byte, Page As Byte, Column As Byte)

	Dim i As Byte

	#if OLED_CABLE = LEFT_EXIT
   		column = column + $20
	#endif

	Ascii = Ascii - $20
	
	If Ascii < 121 Then				// Normal Char Set
		TABLEPTR = @Ascii_1 + (Ascii * 5)

		Set_Start_Page(Page)
		Set_Start_Column(Column)
	
		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
		
		For i=0 To 4
			I2C.WriteByte(ReadDataByte)
		Next
		I2C.WriteByte($00)          	// data
	
		Stop()
	Else							// 6 Byte wide symbols
		ascii = ascii - 121
		TABLEPTR = @Symbols + (Ascii * 6)

		Set_Start_Page(Page)
		Set_Start_Column(Column)
	
		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
		
		For i=0 To 5
			I2C.WriteByte(ReadDataByte)
		Next
		I2C.WriteByte($00)          	// data
	
		Stop()
	EndIf			
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show String
//
//    b: Start Page
//    c: Start Column
//    * Must write "0" in the end...
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Show_String(pText As String, Page As Byte, Column As Byte)

   Dim Index, Data As Byte  

   Index = 0
   Data = pText(0)
   	If Scrolling > ScrollOff Then
		Deactivate_Scroll()
	EndIf

	While Data <> 0
      Show_Char(Data,Page,Column)
      Inc(Index)
      Column = Column+6
      Data = pText(index)
   	Wend
	
	If Scrolling = ScrollOn Then
		Activate_Scroll()
	EndIf
	
End Sub
	
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show Regular Pattern (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Sub Fill_RAM(pData As Byte)

	Dim i,j As Byte

	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf

	For i = 0 To 7
		Set_Start_Page(i)
		Set_Start_Column($00)
	
		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
			
		For j = 0 To 128
			I2C.WriteByte(pData)
		Next	
	
		Stop()
	Next

  	If Scrolling = ScrollOn Then
		Activate_Scroll()
	EndIf
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show Regular Pattern (Partial or Full Screen)
//
//    a: Start Page
//    b: End Page
//    c: Start Column
//    d: Total Columns
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Fill_Block(pData As Byte, StartPage As Byte, EndPage As Byte, StartCol As Byte, TotCol As Byte)

	Dim i,j As Byte
	
	#if OLED_CABLE = LEFT_EXIT
   		StartCol = StartCol + $20
	#endif
	
	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf

	For i = StartPage To ( EndPage )  ' + 1
	
		Set_Start_Page(i)
		Set_Start_Column(StartCol)

		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
		
		For j = 0 To TotCol
			I2C.WriteByte(pData)
		Next

		Stop()
	Next

	If Scrolling = ScrollOn Then
		Activate_Scroll()
	EndIf

End Sub

Public Sub Checkerboard()

	Dim i,j As Byte
	
	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf

	For i = 0 To 7
	
		Set_Start_Page(i)
		Set_Start_Column(0)

		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
		
		For j = 0 To 63
			I2C.WriteByte($55)
			I2C.WriteByte($AA)
		Next

		Stop()
	Next

	If Scrolling = ScrollOn Then
		Activate_Scroll()
	EndIf

End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Fade In (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Fade_In()

	Dim i As Word	
	Set_Display_On_Off($01)
	For i=0 To MaxBrightness 
		Set_Contrast_Control(i)
	Next
End Sub


//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Fade Out (Full Screen)
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Fade_Out()

	Dim i As Word	
	For i = MaxBrightness To 0  Step - 1
		Set_Contrast_Control(i)
	Next
	Set_Display_On_Off($00)
End Sub

Public Sub SetContrast(pByte As Byte)

	Set_Contrast_Control(pByte)
	
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Continuous Horizontal Scrolling (Partial or Full Screen)
//
//    a: Scrolling Direction
//       "0x00" (Rightward)
//       "0x01" (Leftward)
//    b: Define Start Page Address
//    c: Define End Page Address
//    d: Set Time Interval between Each Scroll Step in Terms of Frame Frequency
//    e: Delay Time

	// OLED.Horizontal_Scroll(dirleft, 1, 2, $06)		// Bottom line scroll
	// OLED.Horizontal_Scroll(dirleft, 0, 2, $06)		// All scroll
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Horizontal_Scroll(Dir As Byte, StartPage As Byte, EndPage As Byte, Interval As Byte)

	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf
	Write_Command($26 Or Dir)			// Horizontal Scroll Setup
	Write_Command($00)				//           => (Dummy Write for First Parameter)
	Write_Command(StartPage)
	Write_Command(Interval)
	Write_Command(EndPage)
	Write_Command($00)
	Write_Command($FF)
	Write_Command($2F)			// Activate Scrolling
	Scrolling = ScrollOn
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Continuous Vertical / Horizontal / Diagonal Scrolling (Partial or Full Screen)
//
//    a: Scrolling Direction
//       "0x00" (Vertical & Rightward)
//       "0x01" (Vertical & Leftward)
//    b: Define Start Row Address (Horizontal / Diagonal Scrolling)
//    c: Define End Page Address (Horizontal / Diagonal Scrolling)
//    d: Set Top Fixed Area (Vertical Scrolling)
//    e: Set Vertical Scroll Area (Vertical Scrolling)
//    f: Set Numbers of Row Scroll per Step (Vertical / Diagonal Scrolling)
//    g: Set Time Interval between Each Scroll Step in Terms of Frame Frequency
//    * d+e must be less than or equal to the Multiplex Ratio...
{		Continuous_Scroll(0x00,0x07,0x07,0x00,0x08,0x01,0x06);
						// Upward - Top Area
		Continuous_Scroll(0x00,0x07,0x07,0x00,0x08,0x07,0x06);
						// Downward - Top Area
		Continuous_Scroll(0x00,0x00,0x00,0x00,0x08,0x01,0x06);
						// Up & Rightward - Top Area
		Continuous_Scroll(0x01,0x00,0x00,0x00,0x08,0x07,0x06);
						// Down & Leftward - Top Area
		Continuous_Scroll(0x01,0x01,0x01,0x00,0x08,0x01,0x06);
						// Upward - Top Area
						// Leftward - Bottom Area
		Continuous_Scroll(0x00,0x01,0x01,0x00,0x08,0x07,0x06);
						// Downward - Top Area
						// Rightward - Bottom Area   
		OLED.Continuous_Scroll($00,$00,$02,$00,$16,$01,$06) // Diagonal whole screen
		
		}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Continuous_Scroll(Dir As Byte, StartRow As Byte, EndPage As Byte, FixArea As Byte, VertArea As Byte, ScrollStep As Byte, Interval As Byte)


	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf

	Write_Command($A3)			// Set Vertical Scroll Area
	Write_Command(FixArea)			//   Default => 0x00 (Top Fixed Area)
	Write_Command(VertArea)		//   Default => 0x40 (Vertical Scroll Area)

	Write_Command($29 + Dir)			// Continuous Vertical & Horizontal Scroll Setup
	Write_Command($00)			//           => (Dummy Write for First Parameter)
	Write_Command(StartRow)
	Write_Command(Interval)
	Write_Command(EndPage)
	Write_Command(ScrollStep)
	Write_Command($2F)			// Activate Scrolling
	Scrolling = ScrollOn
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Connection Test
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Test(pTest As Byte)
	Select pTest
		Case 1
			Fill_RAM($FF)
		Case 2
			Checkerboard
		Else
			Fill_RAM($00)
	End Select			
End Sub

//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Show Pattern (Partial or Full Screen)
//
//    a: Start Page (0 or 1)
//    b: Start Column
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub Show_Image(ByRefConst Data_Pointer() As Byte, Page As Byte, Column As Byte)

	Dim i,j As Byte
	Dim Image As TImage
	
	TABLEPTR = @Data_Pointer
	ReadDataByte	// dud $01
	Image.Width = ReadDataByte
   	Image.Height = (ReadDataByte / 8)
   	ReadDataByte	// dud $00
	

   	#if OLED_CABLE = LEFT_EXIT
   		column = column + $20
	#endif
	
	If Scrolling = ScrollOn Then
		Deactivate_Scroll()
	EndIf
	
	For i = Page To (image.Height -1)
		Set_Start_Page(i)
		Set_Start_Column(Column)
	
		Start()
		I2C.WriteByte(Address)    		// Address + write
		I2C.WriteByte($40)          	// data
		
		For j= 0 To (image.Width - 1)
			I2C.WriteByte(ReadDataByte)
		Next
    
		Stop()
		
	Next
	
	If Scrolling = ScrollOn Then
		Activate_Scroll()
	EndIf

End Sub

Public Sub CLS()

	Fill_RAM($00)				// Clear Screen
	
End Sub

//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//  Initialization
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Public Sub OLED_Init()
	
	Scrolling = ScrollOff
	Brightness = MaxBrightness

    I2C.Initialize()		
	
	Set_Display_On_Off($00)					// Display Off ($00/$01) 
	
	Set_Display_On_Off($00)				// Display Off ($00/$01)
	Set_Display_Clock($A2)				// Set Clock as 120 Frames/Sec
	Set_Multiplex_Ratio($0F)			// 1/16 Duty ($0F~$3F)
	Set_Display_Offset($00)				// Shift Mapping RAM Counter ($00~$3F)  *
	Set_Charge_Pump($04)				// Enable Embedded DC/DC Converter ($00/$04)
	Set_Addressing_Mode($02)			// Set Page Addressing Mode ($00/$01/$02)
	Set_Start_Line($00)					// Set Mapping RAM Display Start Line ($00~$3F) *
	
	#if OLED_CABLE = RIGHT_EXIT
		Set_Segment_Remap($01)			// Set SEG/Column Mapping ($00/$01)       		
		Set_Common_Remap($08)			// Set COM/Row Scan Direction ($00/$08)
 	#else
		Set_Segment_Remap($00)			// Set SEG/Column Mapping ($00/$01)       		' was 01
		Set_Common_Remap($00)			// Set COM/Row Scan Direction ($00/$08) 	' was 08
		Set_Display_Offset($00)
	#endif
	
    Set_Common_Config($00)				// Set Sequential Configuration ($00/$10) *
	Set_Contrast_Control(MaxBrightness)	// Set SEG Output Current
	Set_Precharge_Period($D2)			// Set Pre-Charge as 13 Clocks & Discharge as 2 Clock
	Set_VCOMH($20)						// Set VCOM Deselect Level
	Set_Entire_Display($00)				// Disable Entire Display On ($00/$01)
	Set_Inverse_Display($00)			// Disable Inverse Display On ($00/$01)
	Deactivate_Scroll()
	
	Fill_RAM($00)						// Clear Screen
	
	Set_Display_On_Off($01)				// Display On ($00/$01)
	
End Sub


{
	Examples:

	// Scrolling (Full Screen)
		Vertical_Scroll($00,$00,Max_Row,$01,$60);
						// Upward
		Delay(1);
		Vertical_Scroll($01,$00,Max_Row,$01,$60);
						// Downward
		Vertical_Scroll($01,$00,$20,$01,$60);
						// Downward
		Delay(1);
		Vertical_Scroll($00,$00,$20,$01,$60);
						// Upward
	// Scrolling (Partial Screen)
		Deactivate_Scroll();
		Continuous_Scroll($00,$07,$07,$00,$08,$01,$06,$01);
						// Upward - Top Area
		Continuous_Scroll($00,$07,$07,$00,$08,$07,$06,$01);
						// Downward - Top Area
		Continuous_Scroll($00,$00,$00,$00,$08,$01,$06,$02);
						// Up & Rightward - Top Area
		Continuous_Scroll($01,$00,$00,$00,$08,$07,$06,$02);
						// Down & Leftward - Top Area
		Continuous_Scroll($01,$01,$01,$00,$08,$01,$06,$02);
						// Upward - Top Area
						// Leftward - Bottom Area
		Continuous_Scroll($00,$01,$01,$00,$08,$07,$06,$02);
						// Downward - Top Area
						// Rightward - Bottom Area
		Deactivate_Scroll();
}

Here's an example for driving it:

Code: Select all

Device = 18F27J53
Clock = 8

Public Config
   OSC = INTOSCPLL,                  ' internal osc 8MHz - No output on RA6
   PLLDIV = 2,                        ' usb 48MHz
   CPUDIV = OSC1,                     ' 48MHz cpu clock
   FCMEN = Off,
   IESO = Off,
   WDTEN = Off,
   WDTPS = 2048,
   STVREN = On,
   XINST = Off
   'DEBUG = OFF
   
Include "SSD1306MSSP.bas"

// Turn on PLL. Oled Setup will be slow but should be fine.
OSCCON = %01110100

//18f27j53 has MSSP1 on RB4 & 5
  Input(PORTB.4)                 
  Input(PORTB.5)                 
  
OLED_Init()

OLED.CLS()
OLED.Deactivate_Scroll()
Checkerboard()

DelayMS(5000)
OLED.CLS()
Show_Image(Batteryimg,$00,$00)
Show_String("Test Message",0, 18)
Show_String("Line 2",1, 17)

DelayMS(5000)
OLED.CLS()
Show_Image(Batteryimg,$00,$00)
Show_String("Test Message",0, 18)
Show_String("Line 2",1, 17)

DelayMS(5000)
OLED.CLS()
Show_Image(Batteryimg,$00,$00)
Show_String("Diagonal Scroll",0, 18)
Show_String("WooT!",1, 18)
OLED.Continuous_Scroll($00,$00,$02,$00,$16,$01,Int_128_frames) // Diagonal whole screen scrolling! Woot!

While true
	DelayMS(100)
Wend
Attachments
IMG_3341.JPG
IMG_3341.JPG (60.86 KiB) Viewed 24932 times

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Tue Feb 03, 2015 12:00 pm

RangerBob wrote:I think some of your registers are a bit funky, might be worth checking them (e.g. Set segment remap to $A1, is that right? it's supposed to be either 0 or 1? Same with Set_Start_line, my datasheet says $00-$3F, you appear to have it at $40?). Try putting my default values in to start and see if you get anything, nothing should be damaged.

As I said I can't download the datasheet for your unit (can't be bothered to log into their forum), but its very odd that they say ACK's don't work for I2C. That to me is pretty fundamental about I2C! You get ACKs to show that the darn thing is working!

One other thing to watch is that the display is correctly reset. My displays can act very weird if they are not reset. They can get into a funny state that puts random dots across the display.
That is one trouble - I've not actually got a datasheet for the display and the link is nothing to do with the seller - I wonder if Hobby Components actually know other people are linking to their stuff like that...

The Ack thing is to do with how the SSD1306 implements I2C - I think the display I have actually does it correctly so will Ack. The chip has SDAin and SDAout pins, the Hobby Components display only used one while you are supposed to connect them together if you want Acks.

I did wonder about Reset - the display I have doesn't have a Reset pin and many of these small OLEDs don't. I've traced the pin and it seems to be pulled high with a resistor. Or at least it is marked R4 on the PCB but it doesn't actually look like a resistor - it is very thick, looks like some capacitors I've used - the body is black rather than brown - and when probed gives a resistance value that changes, a bit like when you probe a capacitor.

As for the setup, if you look at the SSD1306 datasheet the actual command for segment remap is $A0 or $A1, similar for Set_Start_Line - you add $40 to the line to get the actual command. Just a different way to do things I think - some people have a sub they call with 0/1 as options, I have individual subs for each option.

SHughes_Fusion
Posts: 219
Joined: Wed Sep 11, 2013 1:27 pm
Location: Chesterfield

Re: SSD1306 I2C driver

Post by SHughes_Fusion » Tue Feb 03, 2015 12:10 pm

I think you've hit the problem.

I forced a reset - used a pair of tweezers to force 0V on the reset pin - and the display is working.

So... The question is, how is this supposed to reset? Have they fitted the wrong components or is there some other power-up problem going on?

Bit annoying that I've wasted several days chasing a problem which is elsewhere...

Post Reply