News:

;) This forum is the property of Proton software developers

Main Menu

Positron8 - Curved Compass Display Demo on an SSD1306 OLED

Started by top204, Sep 16, 2026, 07:03 PM

Previous topic - Next topic

top204

I have been messing around with the SSD1306 and SSD1309 OLED displays since I created the library for them for my MAZOGs game (thanks Jon for your findings on the SPI interface to them), and I stumbled across a mechanism for giving a slightly curved look to a series of 'meter like' tick mark lines on a display, so I created the extra code to make it look like a curved compass.

The code converts the 10-bit ADC value into degrees, and these are displayed on the, SPI operated, SSD1306 OLED display, running on a PIC18F26K22 device operating at 64MHz using its internal oscillator, so no crystal or resonator required.

Below is the code listing for the Curved Compass Demo:
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' A compass display demo on an SSD1306 128x64 OLED Display, with the degrees driven from a potentiometer to the ADC input.
'
' The 10-bit ADC value is converted to degrees, and are displayed with tick marks and letters on a simulated curved display.
' So turning the potentiometer connected to the ADC channel AN0, moves the compass display.
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/positron-compilers-experimenters-notebook.
' https://youtu.be/2QAyEgCaPo0
'
    Device = 18F26K22                                                                           ' Tell the compiler what device to compile for
    Declare Xtal = 64                                                                           ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Float_Display_Type = Fast                                                           ' Use the compiler's faster floating point display routine
    Declare Auto_Heap_Arrays = On                                                               ' Make all arrays "Heap" types, so they always get placed after standard variables
    Declare Auto_Heap_Strings = On                                                              ' Make all Strings "Heap" types, so they always get placed after standard variables
    Declare Auto_Variable_Bank_Cross = On                                                       ' Make sure all multi-byte variables remain within a single RAM bank
'
' Define the pins to use for the SSD1306 OLED
'
$define GLCD_CS_Pin  PORTC.0                                                                    ' The pin that connects to the SSD1306 CS pin
$define GLCD_RST_Pin PORTB.4                                                                    ' The pin that connects to the SSD1306 Reset pin
$define GLCD_DC_Pin  PORTB.5                                                                    ' The pin that connects to the SSD1306 DC pin
$define GLCD_CLK_Pin PORTC.3                                                                    ' The pin that connects to the SSD1306 Clk pin
$define GLCD_DIN_Pin PORTC.5                                                                    ' The pin that connects to the SSD1306 Din pin

    Include "SSD1306.inc"                                                                       ' Load the SSD1306 OLED library routines into the program
    Include "Scale.inc"                                                                         ' Load the Scale procedures into the program
    Include "CourierNew_Bold_12.inc"                                                            ' Load the text font data into the progtam
'
' Meta-macros to define the ADC ANx values for a channel pin on a PIC18F26K22 device
'
$define cAN0  0
$define cAN1  1
$define cAN2  2
$define cAN3  3
$define cAN4  4
$define cAN8  8
$define cAN9  9
$define cAN10 10
$define cAN11 11
$define cAN12 12
$define cAN13 13
$define cAN14 14
$define cAN15 15
$define cAN16 16
$define cAN17 17
$define cAN18 18
$define cAN19 19
'
' Create any global variables, aliases and constants here
'
    Dim wADC_Value As Word

'---------------------------------------------------------------------------------------------
' The main program startes here
' Display a compass on the OLED, and move the degrees by altering a potentiometer
'
Main:
    Setup()                                                                                     ' Setup the program and any peripherals

    Do                                                                                          ' Create a loop
        wADC_Value = ADC_Read10(cAN0)                                                           ' Read the 10-bit ADC value from channel AN0
        Draw_Compass(wADC_Value)                                                                ' Draw the compass display
        GLCD_UpdateDisplay()                                                                    ' Transfer the display buffer to the OLED's screen
    Loop                                                                                        ' Do it forever

'---------------------------------------------------------------------------------------------
' Draw the compass to the display buffer array
' Input     : pValue holds the 10-bit ADC value that will be converted to degrees
' Output    : None
' Notes     : The GLCD_UpdateDisplay() procedure will need to be called after this procedure, so that the OLED's display is updated
'
Proc Draw_Compass(pValue As Word)
    Symbol cIMAGES_COUNT  = 3                                                                   ' \ The number of different versions for each direction image
    Symbol cIMAGES_COUNT_ADD1 = (cIMAGES_COUNT + 1)                                             ' /
    Dim wDegrees          As Word                                                               ' Holds the degrees value (0 to 360)
    Dim wXpos_Offset      As Word                                                               ' Holds the X offset of the tick marks
    Dim wXposWithOffset   As Word                                                               ' Holds the X position of the tick mark with an offset added
    Dim fXposFinal        As Float                                                              ' Holds the final X position for the tick mark line
    Dim bTickMarks        As Byte                                                               ' Holds the tick marks count on the display
    Dim wDirImage         As Word                                                               ' Holds which scaled direction image to display for a big tick mark
    Dim bImageIndex       As Byte                                                               ' Holds which version of the direction image to display
    Dim wImageXpos        As Word                                                               ' Holds the X position of a direction letter\s image

    wDegrees = wScale(pValue, 0, 1023, 0, 360)                                                  ' Convert the 10-bit ADC value (0 to 1023) to degrees 0 to 360
    wXpos_Offset = (360 - wDegrees) / 360.0 * 240.0                                             ' Calculate the X offset for all the tick mark lines

    Clear Global_bDisplayBuffer                                                                 ' Clear the display buffer array
    GLCD_PenColour(clWHITE)                                                                     ' Set the image colour to white
    For bTickMarks = 0 To 23                                                                    ' Create a loop for the 24 tick mark lines
        wXposWithOffset = (64 + (bTickMarks * 10) + wXpos_Offset) // 240                        ' Calculate the X offset for a tick mark line (maximum 240)
        If wXposWithOffset > 2 And wXposWithOffset < 128 Then                                   ' Only draw tick mark lines if they are inside the visible area of the display width
            If wXposWithOffset < 64 Then                                                        ' Is the X offset to the left of the display?
                fXposFinal = wXposWithOffset / 64                                               ' Yes. So convert 0 to 64 into 0.0 to 1.0 to work with the Pow function
                fXposFinal = Pow(fXposFinal, 2.5)                                               ' Add a view of curving by using Pow to tweak the fXposFinal value
                wDirImage = fXposFinal * cIMAGES_COUNT                                          ' Calculate which scaled label to show based on the X position
                fXposFinal = fXposFinal * 64.0                                                  ' Convert 0.0 to 1.0 back into 0 to 64 (left side of the screen)
            Else                                                                                ' Otherwise... The X offset is to the right of the display...
                fXposFinal = (128 - wXposWithOffset) / 64                                       ' So... Convert 0 to 64 into 0.0 to 1.0 to work with the Pow function
                fXposFinal = Pow(fXposFinal, 2.5)                                               ' Add a view of curving by using Pow to tweak the fXposFinal value
                wDirImage = fXposFinal * cIMAGES_COUNT                                          ' Calculate which scaled label to show based on the X position
                fXposFinal = (64 - (fXposFinal * 64.0)) + 64                                    ' Convert 0.0 to 1.0 back into 64 to 128 (right side of the screen)
            EndIf
            If bTickMarks // 3 = 0 Then                                                         ' Is the tick mark value divisible by 3?
                GLCD_Line(fXposFinal, 7, fXposFinal, 15, 1)                                     ' Yes. So draw a big tick mark line
                bImageIndex = ((bTickMarks / 3) * cIMAGES_COUNT_ADD1) + wDirImage               ' Calculate the scaled direction letter\s image to display
                wImageXpos = fXposFinal - 13                                                    ' Calculate the X position of the direction letter\s image
                If fXposFinal > 3 Then                                                          ' \ Is the X position in a suitable range for left and right of the display?
                    If fXposFinal < 125 Then                                                    ' /
                        GLCD_DrawImage(wImageXpos, 17, 26, 12, CRead16 Images[bImageIndex])     ' Yes. So draw a scaled directions letter\s image
                    EndIf
                EndIf
                If wDirImage = cIMAGES_COUNT Then                                               ' Is the direction letter\s image the widest version?
                    GLCD_Line(fXposFinal - 1, 7, fXposFinal - 1, 15, 1)                         ' Yes. So draw the tick mark as a 2 pixel line
                EndIf
            Else                                                                                ' Otherwise... The tick mark value is not divisible by 3...
                GLCD_Line(fXposFinal, 7, fXposFinal, 13, 1)                                     ' So... Draw a tick mark line without any direction letter\s image
            EndIf
        EndIf
    Next
    GLCD_Line(0, 5, 127, 5, 1)                                                                  ' Draw a horizontal line
    GLCD_PenColour(clWHITE)                                                                     ' Set the text colour to white
    GLCD_PrintAtCenter(34, StrDec3(wDegrees))                                                   ' Draw the degrees value
    GLCD_Circle(85, 37, 2, 1)                                                                   ' Draw the degrees symbol
EndProc

'---------------------------------------------------------------------------------------------
' Draw an image to the OLED's video display RAM buffer, using the address of a flash memory table
' Input     : pXpos holds the X position of the image
'           : pYpos holds the Y position of the image
'           : pWidth holds the width of the image
'           : pHeight holds the height of the image
'           : pImageAddr holds the flash memory address of the image
'           : Global_GLCD_tPenColour holds the colour of the image (0 for black and 1 for white)
' Output    : None
' Notes     : The GLCD_UpdateDisplay() procedure will need to be called after this procedure, so that the OLED's display is updated
'
Proc GLCD_DrawImage(pXpos As Byte, pYpos As Byte, pWidth As Byte, pHeight As Byte, pImageAddr As Word)
    Dim bBytesAmount As Byte                                                                    ' Holds the amount of bytes to read from the pixel data
    Dim bWidth       As Byte                                                                    ' Holds a copy of the pWidth parameetr
    Dim bMask        As Byte                                                                    ' Holds the bit mask
    Dim bBitsValue   As Byte                                                                    ' Holds the byte read from flash memory
    Dim bXpos        As Byte                                                                    ' Holds the working X position of the image
    Dim wWorkingAddr As Word                                                                    ' Holds a copy of the original flash memory address to work with

    bBytesAmount = pWidth + 7                                                                   ' \ Calculate the amount of flash memory bytes to read
    bBytesAmount = bBytesAmount >> 3                                                            ' /
    While pHeight > 0                                                                           ' Loop until all the height of the image is complete
        bXpos = pXpos                                                                           ' Transfer pXpos into bXpos, so it can be worked on without altering the original
        wWorkingAddr = pImageAddr                                                               ' Transfer pImageAddr into wWorkingAddr, so it can be worked on without altering the original
        bWidth = pWidth                                                                         ' Transfer pWidth into bWidth, so it can be worked on without altering the original
        bMask = 1                                                                               ' Set the mask for bit-o of the flash memory value
        While bWidth > 0                                                                        ' Loop until all the width of the image is complete
            bBitsValue = cPtr8(wWorkingAddr)                                                    ' Read a byte from flash memory
            bBitsValue = bBitsValue & bMask                                                     ' Mask the value to extract the relevant bit
            If bBitsValue <> 0 Then                                                             ' Is the bit set?
                GLCD_SetPixel(bXpos, pYpos, Global_GLCD_tPenColour)                             ' Yes. So set a pixel in the OLED's video display RAM buffer
            EndIf
            Inc bXpos                                                                           ' Move to the next horizontal pixel
            bMask = bMask << 1                                                                  ' Rotate the mask left
            If bMask = 0 Then                                                                   ' Has the mask reached a value of 0?
                bMask = 1                                                                       ' Yes. So set it back to 1
                Inc wWorkingAddr                                                                ' Increment the flash memory address
            EndIf
            Dec bWidth                                                                          ' Decrement the width value
        Wend                                                                                    ' Close the width loop
        pImageAddr = pImageAddr + bBytesAmount                                                  ' Calculate the next sequence of flash memory bytes to read
        Inc pYpos                                                                               ' Move to the next vertical pixel down
        Dec pHeight                                                                             ' Decrement the height value
    Wend                                                                                        ' Close the height loop
EndProc

'---------------------------------------------------------------------------------------------
' Read the 10-bit ADC directly on a PIC18F26K22 device
' Input     : pChan holds the ADC channel to read (0 to X)
' Output    : Returns the 10-bit ADC value
' Notes     : None
'
Proc ADC_Read10(pChan As Byte), Word
    Dim wADRES As ADRESL.Word                                                                   ' Create a 16-bit SFR from ADRESL and ADRESH

    ADCON0 = %00000001                                                                          ' Clear the channel bits and enable the ADC peripheral
    pChan = pChan << 2                                                                          ' Move the channel bits into the correct position for ADCON0
    ADCON0 = ADCON0 | pChan                                                                     ' Or the channel bits into ADCON0
    DelayUS 50                                                                                  ' Give time for the ADC capacitor to stabilise
    ADCON0bits_GO_DONE = 1                                                                      ' \ Wait for the ADC to complete its reading
    Repeat : Until ADCON0bits_GO_DONE = 0                                                       ' /
    Result = wADRES                                                                             ' Load the result with the ADC value
EndProc

'---------------------------------------------------------------------------------------------
' Setup the ADC on a PIC18F26K22 device
' Input     : None
' Output    : None
' Notes     : Set for 10-bit operation
'           : Uses the Gnd and VDD as vrefs
'           : Uses the FRC oscillator
'           : Sets AN0 as an analogue pin
'
Proc ADC_Init()
    ADCON1 = $00
    ADCON2 = %10001111
    ADRESL = $00
    ADRESH = $00
    ADCON0 = $00
    ANSELAbits_ANSA0 = 1                                                                        ' Setup AN0 as an analogue pin
EndProc

'---------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
    Osc_64MHz()                                                                                 ' Set the device to 64MHz operation using its internal oscillator
    GLCD_Setup()                                                                                ' Setup the SSD1306 OLED
    GLCD_SetFont(CourierNewBold_12)                                                             ' Set the text font
    ADC_Init()                                                                                  ' Initialise the ADC
EndProc

'---------------------------------------------------------------------------------------------
' Set the PIC18F26K22 device to 64MHz operation using its internal oscillator
' Input     : None
' Output    : None
' Notes     : None
'
Proc Osc_64MHz()
    OSCCON  = $70
    OSCCON2 = $04
    OSCTUNE = $40
    DelayMS 100
EndProc

'---------------------------------------------------------------------------------------------
' Flash memory data for the 26x12 direction letter\s images
'
    Dim Image_N0 As Flash8 = %00000000, %01001000, %00000000, %00000000, %00000000, %01001000, %00000000, %00000000,_
                             %00000000, %01011000, %00000000, %00000000, %00000000, %01011000, %00000000, %00000000,_
                             %00000000, %01011000, %00000000, %00000000, %00000000, %01011000, %00000000, %00000000,_
                             %00000000, %01101000, %00000000, %00000000, %00000000, %01101000, %00000000, %00000000,_
                             %00000000, %01101000, %00000000, %00000000, %00000000, %01101000, %00000000, %00000000,_
                             %00000000, %01001000, %00000000, %00000000, %00000000, %01001000, %00000000, %00000000

    Dim Image_N1 As Flash8 = %00000000, %10001000, %00000000, %00000000, %00000000, %10011000, %00000000, %00000000,_
                             %00000000, %10011000, %00000000, %00000000, %00000000, %10011000, %00000000, %00000000,_
                             %00000000, %10011000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %10101000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %11001000, %00000000, %00000000, %00000000, %11001000, %00000000, %00000000,_
                             %00000000, %11001000, %00000000, %00000000, %00000000, %10001000, %00000000, %00000000

    Dim Image_N2 As Flash8 = %00000000, %10001100, %00000001, %00000000, %00000000, %10011100, %00000001, %00000000,_
                             %00000000, %10011100, %00000001, %00000000, %00000000, %10111100, %00000001, %00000000,_
                             %00000000, %10111100, %00000001, %00000000, %00000000, %10111100, %00000001, %00000000,_
                             %00000000, %11101100, %00000001, %00000000, %00000000, %11101100, %00000001, %00000000,_
                             %00000000, %11101100, %00000001, %00000000, %00000000, %11001100, %00000001, %00000000,_
                             %00000000, %11001100, %00000001, %00000000, %00000000, %10001100, %00000001, %00000000

    Dim Image_N3 As Flash8 = %00000000, %00000011, %00000011, %00000000, %00000000, %00000111, %00000011, %00000000,_
                             %00000000, %00001111, %00000011, %00000000, %00000000, %00001111, %00000011, %00000000,_
                             %00000000, %00011011, %00000011, %00000000, %00000000, %00110011, %00000011, %00000000,_
                             %00000000, %00110011, %00000011, %00000000, %00000000, %01100011, %00000011, %00000000,_
                             %00000000, %11000011, %00000011, %00000000, %00000000, %11000011, %00000011, %00000000,_
                             %00000000, %10000011, %00000011, %00000000, %00000000, %00000011, %00000011, %00000000

    Dim Image_NE0 As Flash8 = %00000000, %11010010, %00000001, %00000000, %00000000, %11010010, %00000001, %00000000,_
                              %00000000, %01010110, %00000000, %00000000, %00000000, %01010110, %00000000, %00000000,_
                              %00000000, %01010110, %00000000, %00000000, %00000000, %11010110, %00000001, %00000000,_
                              %00000000, %11011010, %00000001, %00000000, %00000000, %01011010, %00000000, %00000000,_
                              %00000000, %01011010, %00000000, %00000000, %00000000, %01011010, %00000000, %00000000,_
                              %00000000, %11010010, %00000001, %00000000, %00000000, %11010010, %00000001, %00000000

    Dim Image_NE1 As Flash8 = %00000000, %11010001, %00000011, %00000000, %00000000, %11010011, %00000011, %00000000,_
                              %00000000, %01010011, %00000000, %00000000, %00000000, %01010011, %00000000, %00000000,_
                              %00000000, %01010011, %00000000, %00000000, %00000000, %11010101, %00000011, %00000000,_
                              %00000000, %11010101, %00000011, %00000000, %00000000, %01010101, %00000000, %00000000,_
                              %00000000, %01011001, %00000000, %00000000, %00000000, %01011001, %00000000, %00000000,_
                              %00000000, %11011001, %00000011, %00000000, %00000000, %11010001, %00000011, %00000000

    Dim Image_NE2 As Flash8 = %11000000, %11011000, %00001111, %00000000, %11000000, %11011001, %00001111, %00000000,_
                              %11000000, %11011001, %00000000, %00000000, %11000000, %11011011, %00000000, %00000000,_
                              %11000000, %11011011, %00000000, %00000000, %11000000, %11011011, %00001111, %00000000,_
                              %11000000, %11011110, %00001111, %00000000, %11000000, %11011110, %00000000, %00000000,_
                              %11000000, %11011110, %00000000, %00000000, %11000000, %11011100, %00000000, %00000000,_
                              %11000000, %11011100, %00001111, %00000000, %11000000, %11011000, %00001111, %00000000

    Dim Image_NE3 As Flash8 = %00011000, %11011000, %01111111, %00000000, %00111000, %11011000, %01111111, %00000000,_
                              %01111000, %11011000, %00000000, %00000000, %01111000, %11011000, %00000000, %00000000,_
                              %11011000, %11011000, %00000000, %00000000, %10011000, %11011001, %01111111, %00000000,_
                              %10011000, %11011001, %01111111, %00000000, %00011000, %11011011, %00000000, %00000000,_
                              %00011000, %11011110, %00000000, %00000000, %00011000, %11011110, %00000000, %00000000,_
                              %00011000, %11011100, %01111111, %00000000, %00011000, %11011000, %01111111, %00000000

    Dim Image_E0 As Flash8 = %00000000, %00111000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000,_
                             %00000000, %00111000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %00111000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000

    Dim Image_E1 As Flash8 = %00000000, %01111000, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000,_
                             %00000000, %01111000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %00001000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000,_
                             %00000000, %01111000, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000

    Dim Image_E2 As Flash8 = %00000000, %01111100, %00000000, %00000000, %00000000, %01111100, %00000000, %00000000,_
                             %00000000, %00001100, %00000000, %00000000, %00000000, %00001100, %00000000, %00000000,_
                             %00000000, %00001100, %00000000, %00000000, %00000000, %01111100, %00000000, %00000000,_
                             %00000000, %01111100, %00000000, %00000000, %00000000, %00001100, %00000000, %00000000,_
                             %00000000, %00001100, %00000000, %00000000, %00000000, %00001100, %00000000, %00000000,_
                             %00000000, %01111100, %00000000, %00000000, %00000000, %01111100, %00000000, %00000000

    Dim Image_E3 As Flash8 = %00000000, %11111110, %00000011, %00000000, %00000000, %11111110, %00000011, %00000000,_
                             %00000000, %00000110, %00000000, %00000000, %00000000, %00000110, %00000000, %00000000,_
                             %00000000, %00000110, %00000000, %00000000, %00000000, %11111110, %00000011, %00000000,_
                             %00000000, %11111110, %00000011, %00000000, %00000000, %00000110, %00000000, %00000000,_
                             %00000000, %00000110, %00000000, %00000000, %00000000, %00000110, %00000000, %00000000,_
                             %00000000, %11111110, %00000011, %00000000, %00000000, %11111110, %00000011, %00000000

    Dim Image_SE0 As Flash8 = %00000000, %11100100, %00000000, %00000000, %00000000, %11101110, %00000000, %00000000,_
                              %00000000, %00101010, %00000000, %00000000, %00000000, %00101010, %00000000, %00000000,_
                              %00000000, %00100010, %00000000, %00000000, %00000000, %11100110, %00000000, %00000000,_
                              %00000000, %11101100, %00000000, %00000000, %00000000, %00101000, %00000000, %00000000,_
                              %00000000, %00101010, %00000000, %00000000, %00000000, %00101010, %00000000, %00000000,_
                              %00000000, %11101110, %00000000, %00000000, %00000000, %11100100, %00000000, %00000000

    Dim Image_SE1 As Flash8 = %00000000, %11100110, %00000001, %00000000, %00000000, %11101111, %00000001, %00000000,_
                              %00000000, %00101001, %00000000, %00000000, %00000000, %00101001, %00000000, %00000000,_
                              %00000000, %00100011, %00000000, %00000000, %00000000, %11100111, %00000001, %00000000,_
                              %00000000, %11101110, %00000001, %00000000, %00000000, %00101100, %00000000, %00000000,_
                              %00000000, %00101001, %00000000, %00000000, %00000000, %00101001, %00000000, %00000000,_
                              %00000000, %11101111, %00000001, %00000000, %00000000, %11100110, %00000001, %00000000

    Dim Image_SE2 As Flash8 = %00000000, %11001111, %00000111, %00000000, %10000000, %11011111, %00000111, %00000000,_
                              %10000000, %11011001, %00000000, %00000000, %10000000, %11011001, %00000000, %00000000,_
                              %10000000, %11000011, %00000000, %00000000, %00000000, %11001111, %00000111, %00000000,_
                              %00000000, %11011110, %00000111, %00000000, %00000000, %11011100, %00000000, %00000000,_
                              %10000000, %11011001, %00000000, %00000000, %10000000, %11011001, %00000000, %00000000,_
                              %00000000, %11011111, %00000111, %00000000, %00000000, %11001111, %00000111, %00000000

    Dim Image_SE3 As Flash8 = %11100000, %11000011, %01111111, %00000000, %11110000, %11000111, %01111111, %00000000,_
                              %00011000, %11001110, %00000000, %00000000, %00011000, %11001100, %00000000, %00000000,_
                              %01111000, %11000000, %00000000, %00000000, %11110000, %11000011, %01111111, %00000000,_
                              %11000000, %11000111, %01111111, %00000000, %00000000, %11001110, %00000000, %00000000,_
                              %00011000, %11001100, %00000000, %00000000, %00111000, %11001110, %00000000, %00000000,_
                              %11110000, %11000111, %01111111, %00000000, %11100000, %11000011, %01111111, %00000000

    Dim Image_S0 As Flash8 = %00000000, %00100000, %00000000, %00000000, %00000000, %01110000, %00000000, %00000000,_
                             %00000000, %01010000, %00000000, %00000000, %00000000, %01010000, %00000000, %00000000,_
                             %00000000, %00010000, %00000000, %00000000, %00000000, %00110000, %00000000, %00000000,_
                             %00000000, %01100000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000,_
                             %00000000, %01010000, %00000000, %00000000, %00000000, %01010000, %00000000, %00000000,_
                             %00000000, %01110000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000

    Dim Image_S1 As Flash8 = %00000000, %00110000, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000,_
                             %00000000, %01001000, %00000000, %00000000, %00000000, %01001000, %00000000, %00000000,_
                             %00000000, %00011000, %00000000, %00000000, %00000000, %00111000, %00000000, %00000000,_
                             %00000000, %01110000, %00000000, %00000000, %00000000, %01100000, %00000000, %00000000,_
                             %00000000, %01001000, %00000000, %00000000, %00000000, %01001000, %00000000, %00000000,_
                             %00000000, %01111000, %00000000, %00000000, %00000000, %00110000, %00000000, %00000000

    Dim Image_S2 As Flash8 = %00000000, %01111000, %00000000, %00000000, %00000000, %11111100, %00000000, %00000000,_
                             %00000000, %11001100, %00000000, %00000000, %00000000, %11001100, %00000000, %00000000,_
                             %00000000, %00011100, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000,_
                             %00000000, %11110000, %00000000, %00000000, %00000000, %11100000, %00000000, %00000000,_
                             %00000000, %11001100, %00000000, %00000000, %00000000, %11001100, %00000000, %00000000,_
                             %00000000, %11111000, %00000000, %00000000, %00000000, %01111000, %00000000, %00000000

    Dim Image_S3 As Flash8 = %00000000, %11111000, %00000000, %00000000, %00000000, %11111100, %00000001, %00000000,_
                             %00000000, %10000110, %00000011, %00000000, %00000000, %00000110, %00000011, %00000000,_
                             %00000000, %00011110, %00000000, %00000000, %00000000, %11111100, %00000000, %00000000,_
                             %00000000, %11110000, %00000001, %00000000, %00000000, %10000000, %00000011, %00000000,_
                             %00000000, %00000110, %00000011, %00000000, %00000000, %10001110, %00000011, %00000000,_
                             %00000000, %11111100, %00000001, %00000000, %00000000, %11111000, %00000000, %00000000

    Dim Image_SW0 As Flash8 = %00000000, %01010010, %00000001, %00000000, %00000000, %01010111, %00000001, %00000000,_
                              %00000000, %01010101, %00000001, %00000000, %00000000, %01010101, %00000001, %00000000,_
                              %00000000, %01010001, %00000001, %00000000, %00000000, %01010011, %00000001, %00000000,_
                              %00000000, %01010110, %00000001, %00000000, %00000000, %01010100, %00000001, %00000000,_
                              %00000000, %10100101, %00000000, %00000000, %00000000, %10100101, %00000000, %00000000,_
                              %00000000, %10100111, %00000000, %00000000, %00000000, %10100010, %00000000, %00000000

    Dim Image_SW1 As Flash8 = %10000000, %10001001, %00001000, %00000000, %11000000, %10001011, %00001000, %00000000,_
                              %01000000, %10001010, %00001000, %00000000, %01000000, %01010010, %00000101, %00000000,_
                              %11000000, %01010000, %00000101, %00000000, %11000000, %01010001, %00000101, %00000000,_
                              %10000000, %01010011, %00000101, %00000000, %00000000, %01010011, %00000101, %00000000,_
                              %01000000, %01010010, %00000101, %00000000, %01000000, %00100010, %00000010, %00000000,_
                              %11000000, %00100011, %00000010, %00000000, %10000000, %00100001, %00000010, %00000000

    Dim Image_SW2 As Flash8 = %11000000, %00110011, %01100010, %00000000, %11100000, %00110111, %01100110, %00000000,_
                              %01100000, %00110110, %01100111, %00000000, %01100000, %01100110, %00110101, %00000000,_
                              %11100000, %01100000, %00110101, %00000000, %11000000, %01100011, %00110101, %00000000,_
                              %10000000, %01100111, %00110101, %00000000, %00000000, %01100111, %00110101, %00000000,_
                              %01100000, %11000110, %00011101, %00000000, %01100000, %11000110, %00011000, %00000000,_
                              %11000000, %11000111, %00011000, %00000000, %11000000, %11000011, %00011000, %00000000

    Dim Image_SW3 As Flash8 = %11111000, %00011000, %00001110, %00000011, %11111100, %00111001, %00001110, %00000011,_
                              %10000110, %00110011, %10001110, %00000001, %00000110, %00110011, %10011011, %00000001,_
                              %00011110, %00110000, %10011011, %00000001, %11111100, %01100000, %11011011, %00000000,_
                              %11110000, %01100001, %11011011, %00000000, %10000000, %01100011, %11011011, %00000000,_
                              %00000110, %11000011, %11110001, %00000000, %10001110, %11000011, %01110001, %00000000,_
                              %11111100, %11000001, %01110001, %00000000, %11111000, %11000000, %01110001, %00000000

    Dim Image_W0 As Flash8 = %00000000, %10101000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %10101000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %10101000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %10101000, %00000000, %00000000, %00000000, %10101000, %00000000, %00000000,_
                             %00000000, %01010000, %00000000, %00000000, %00000000, %01010000, %00000000, %00000000,_
                             %00000000, %01010000, %00000000, %00000000, %00000000, %01010000, %00000000, %00000000

    Dim Image_W1 As Flash8 = %00000000, %00100010, %00000010, %00000000, %00000000, %00100010, %00000010, %00000000,_
                             %00000000, %00100010, %00000010, %00000000, %00000000, %01010100, %00000001, %00000000,_
                             %00000000, %01010100, %00000001, %00000000, %00000000, %01010100, %00000001, %00000000,_
                             %00000000, %01010100, %00000001, %00000000, %00000000, %01010100, %00000001, %00000000,_
                             %00000000, %01010100, %00000001, %00000000, %00000000, %10001000, %00000000, %00000000,_
                             %00000000, %10001000, %00000000, %00000000, %00000000, %10001000, %00000000, %00000000

    Dim Image_W2 As Flash8 = %00000000, %00100011, %00000110, %00000000, %00000000, %01110011, %00000110, %00000000,_
                             %00000000, %01110011, %00000110, %00000000, %00000000, %01010110, %00000011, %00000000,_
                             %00000000, %01010110, %00000011, %00000000, %00000000, %01010110, %00000011, %00000000,_
                             %00000000, %01010110, %00000011, %00000000, %00000000, %01010110, %00000011, %00000000,_
                             %00000000, %11011100, %00000001, %00000000, %00000000, %10001100, %00000001, %00000000,_
                             %00000000, %10001100, %00000001, %00000000, %00000000, %10001100, %00000001, %00000000

    Dim Image_W3 As Flash8 = %11000000, %01110000, %00011000, %00000000, %11000000, %01110001, %00011000, %00000000,_
                             %10000000, %01110001, %00001100, %00000000, %10000000, %11011001, %00001100, %00000000,_
                             %10000000, %11011001, %00001100, %00000000, %00000000, %11011011, %00000110, %00000000,_
                             %00000000, %11011011, %00000110, %00000000, %00000000, %11011011, %00000110, %00000000,_
                             %00000000, %10001110, %00000111, %00000000, %00000000, %10001110, %00000011, %00000000,_
                             %00000000, %10001110, %00000011, %00000000, %00000000, %10001110, %00000011, %00000000

    Dim Image_NW0 As Flash8 = %00000000, %10101001, %00000010, %00000000, %00000000, %10101001, %00000010, %00000000,_
                              %00000000, %10101011, %00000010, %00000000, %00000000, %10101011, %00000010, %00000000,_
                              %00000000, %10101011, %00000010, %00000000, %00000000, %10101011, %00000010, %00000000,_
                              %00000000, %10101101, %00000010, %00000000, %00000000, %10101101, %00000010, %00000000,_
                              %00000000, %01001101, %00000001, %00000000, %00000000, %01001101, %00000001, %00000000,_
                              %00000000, %01001001, %00000001, %00000000, %00000000, %01001001, %00000001, %00000000

    Dim Image_NW1 As Flash8 = %00100000, %10001010, %00001000, %00000000, %01100000, %10001010, %00001000, %00000000,_
                              %01100000, %10001010, %00001000, %00000000, %01100000, %01010010, %00000101, %00000000,_
                              %01100000, %01010010, %00000101, %00000000, %10100000, %01010010, %00000101, %00000000,_
                              %10100000, %01010010, %00000101, %00000000, %10100000, %01010010, %00000101, %00000000,_
                              %00100000, %01010011, %00000101, %00000000, %00100000, %00100011, %00000010, %00000000,_
                              %00100000, %00100011, %00000010, %00000000, %00100000, %00100010, %00000010, %00000000

    Dim Image_NW2 As Flash8 = %01100000, %00110110, %01100010, %00000000, %11100000, %00110110, %01100111, %00000000,_
                              %11100000, %00110110, %01100111, %00000000, %11100000, %01100111, %00110101, %00000000,_
                              %11100000, %01100111, %00110101, %00000000, %11100000, %01100111, %00110101, %00000000,_
                              %11100000, %01100111, %00110101, %00000000, %11100000, %01100111, %00110101, %00000000,_
                              %11100000, %11000111, %00011101, %00000000, %01100000, %11000111, %00011000, %00000000,_
                              %01100000, %11000111, %00011000, %00000000, %01100000, %11000110, %00011000, %00000000

    Dim Image_NW3 As Flash8 = %00000011, %00011011, %00001110, %00000011, %00000111, %00111011, %00001110, %00000011,_
                              %00001111, %00110011, %10001110, %00000001, %00001111, %00110011, %10011011, %00000001,_
                              %00011011, %00110011, %10011011, %00000001, %00110011, %01100011, %11011011, %00000000,_
                              %00110011, %01100011, %11011011, %00000000, %01100011, %01100011, %11011011, %00000000,_
                              %11000011, %11000011, %11110001, %00000000, %11000011, %11000011, %01110001, %00000000,_
                              %10000011, %11000011, %01110001, %00000000, %00000011, %11000011, %01110001, %00000000
'
' A flash memory table holding the addresses of all the image tables above
'
    Dim Images As Flash16 = {Image_N0,
                             Image_N1,
                             Image_N2,
                             Image_N3,
                             Image_NE0,
                             Image_NE1,
                             Image_NE2,
                             Image_NE3,
                             Image_E0,
                             Image_E1,
                             Image_E2,
                             Image_E3,
                             Image_SE0,
                             Image_SE1,
                             Image_SE2,
                             Image_SE3,
                             Image_S0,
                             Image_S1,
                             Image_S2,
                             Image_S3,
                             Image_SW0,
                             Image_SW1,
                             Image_SW2,
                             Image_SW3,
                             Image_W0,
                             Image_W1,
                             Image_W2,
                             Image_W3,
                             Image_NW0,
                             Image_NW1,
                             Image_NW2,
                             Image_NW3}

'---------------------------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC18Fx6K22 devices.
' OSC pins RA6 and RA7 are general purpose I/O.
'
Config_Start
    FOSC     = INTIO67                                                  ' Internal oscillator
    PRICLKEN = Off                                                      ' Primary clock disabled
    MCLRE    = EXTMCLR                                                  ' MCLR pin enabled
    WDTEN    = Off                                                      ' Watchdog Timer disabled
    PLLCFG   = Off                                                      ' Oscillator used directly
    FCMEN    = Off                                                      ' Fail-Safe Clock Monitor disabled
    IESO     = Off                                                      ' Oscillator Switchover mode disabled
    PWRTEN   = On                                                       ' Power up timer enabled
    BOREN    = Off                                                      ' Brown-out Reset disabled
    BORV     = 190                                                      ' VBOR set to 1.9 V nominal
    WDTPS    = 128                                                      ' Watchdog Timer Postscale is 1:128
    HFOFST   = Off                                                      ' The Access clock is not held Off until the HF-INTOSC is stable
    PBADEN   = Off                                                      ' PORTB<4:0> pins are configured as digital on reset
    CCP2MX   = PORTC1                                                   ' CCP2 input/output is multiplexed with RC1
    CCP3MX   = PORTB5                                                   ' P3A/CCP3 input/output is multiplexed with RB5
    T3CMX    = PORTC0                                                   ' T3CKI is on RC0
    P2BMX    = PORTB5                                                   ' P2B is on RB5
    STVREN   = On                                                       ' Stack full/underflow will cause a reset
    LVP      = Off                                                      ' Single-Supply ICSP disabled
    Debug    = Off                                                      ' Background debugger disabled. RB6 and RB7 configured as general purpose I/O pins
    XINST    = Off                                                      ' Extra mnemonics disabled
    Cp0      = Off                                                      ' Block 0 (000800-001FFF) not code protected
    CP1      = Off                                                      ' Block 1 (002000-003FFF) not code protected
    CPB      = Off                                                      ' Boot block (000000-0007FF) not code protected
    CPD      = Off                                                      ' Data EEPROM not code protected
    WRT0     = Off                                                      ' Block 0 (000800-001FFF) not write protected
    WRT1     = Off                                                      ' Block 1 (002000-003FFF) not write protected
    WRTB     = Off                                                      ' Boot block (000000-0007FF) not write protected
    WRTC     = On                                                       ' Configuration registers (300000-3000FF) write protected
    WRTD     = Off                                                      ' Data EEPROM not write protected
    EBTR0    = Off                                                      ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1    = Off                                                      ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTRB    = Off                                                      ' Boot block (000000-0007FFh) not protected from table reads executed in other blocks
Config_End

Below is a screenshot of the above code, running in the proteus simulator:

Compass_SSD1306_Screenshot.jpg

And a video of it operating is shown below:


The complete source code for the curved compass is attached to this post, and it is named: "Positron8 - Curved_Compass_Display - SSD1306.zip"

It also contains the SSD1306 128x64 OLED library and the font data, and the Proteus simulator files.

Regards
Les