LCD Text and Graphics demo forARM's mbed application board

Locked
cfbsoftware
Site Admin
Posts: 545
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

LCD Text and Graphics demo forARM's mbed application board

Post by cfbsoftware » Thu Apr 18, 2013 1:29 pm

These files are the complete source code for an application which shows:
  • the set of characters contained in the 6x8 font
  • high speed scrolling text
  • random display of pixels
  • random display of filled squares
  • a filled square in motion
on the Newhaven 32x128 LCD display on ARM's mbed Application Board.

v4.4.2 or later of Astrobe for Cortex-M3 is required to successfully compile and run the example.

DisplayDemo is the main program. Delays are used as the update of the display is so fast that you would not have time to see what is going on.

Code: Select all

MODULE DisplayDemo;
(* =========================================================================  
   Example Cortex-M3 Oberon Program  
 
   Description:
     Demonstrate text and graphics capabilities of an LCD display.
     
   Target:
     Dependent on the imported modules: Display -> LCDST756R 
     
   Tested on: 
     ARM mbed Application Board with a Newhaven C12832A1Z 128 x 32 LCD display 
   
   (c) 2013 CFB Software   
   http://www.astrobe.com  
   
   ========================================================================= *)

IMPORT Display, LCD := LCDST756R, Main, Out, Random, Timer;


PROCEDURE DisplayChars;
(* Display all the printable characters in the character set 
   implemented in the font used *)
VAR
  i: INTEGER;
BEGIN
  FOR i := ORD(" ") TO ORD("~") DO 
    Out.Char(CHR(i));
    (* Pause for a second at the start of a 
       new line to allow time to read it
       before the screen scrolls *)
    IF Display.colNo = 0 THEN 
      Timer.MSecDelay(1000)
    END;
  END;
  Out.Ln();
  Timer.MSecDelay(1000)
END DisplayChars;


PROCEDURE DisplayText;
(* Display 500 scrolling lines of text *)
VAR
  count: INTEGER;
BEGIN
  FOR count := 1 TO 500 DO 
    Out.String("Line ");
    Out.Int(count, 0);
    Out.Ln()
  END
END DisplayText;


PROCEDURE DisplayDots;
(* Randomly display / clear pixels *)
VAR
  i, x, y: INTEGER;
  black: BOOLEAN;
BEGIN
  LCD.ClearScreen(LCD.White);
  black := TRUE;
  FOR i := 0 TO 100000 DO 
    x := Random.Next(LCD.MaxX);
    y := Random.Next(LCD.MaxY);
    IF black THEN 
      LCD.DrawDot(LCD.Black, x, y)
    ELSE
      LCD.DrawDot(LCD.White, x, y)
    END;
    black := ~black;
    IF i MOD 32 = 0 THEN 
      LCD.Refresh()
    END
  END
END DisplayDots;


PROCEDURE MoveBox(x, y, delay: INTEGER);
(* Display a black square for 1/10th second 
   and then erase it *) 
BEGIN
  LCD.FillRectangle(LCD.Black, x, y, x + 7, y + 7);
  LCD.Refresh();
  Timer.MSecDelay(delay);
  LCD.FillRectangle(LCD.White, x, y, x + 7, y + 7)
END MoveBox;
 

PROCEDURE DisplayBoxes;
(* Display a black square at random positions *)
VAR
  i, x, y: INTEGER;
BEGIN
  LCD.ClearScreen(LCD.White);
  FOR i := 0 TO 50 DO 
    x := Random.Next(LCD.MaxX - 7);
    y := Random.Next(LCD.MaxY - 7);
    MoveBox(x, y, 100)
  END
END DisplayBoxes;


PROCEDURE DisplayMovingBox;
(* Move a box from left to right along a row and 
   then return in the opposite direction on the
   next row, continuing until all rows 
   is reached *)
CONST
  delay = 40;
VAR
  pageNo, xLimit, yLimit, pageLimit, x, y: INTEGER;
BEGIN
  pageLimit := LCD.Pages - 1;
  xLimit := LCD.MaxX - 7;
  FOR pageNo := 0 TO pageLimit DO 
    y := pageNo * 8;
    yLimit := y + 7;
    IF ODD(pageNo) THEN
      FOR x := xLimit TO 0 BY - 1 DO MoveBox(x, y, delay) END; 
      IF pageNo < pageLimit THEN 
        FOR y := y TO yLimit DO MoveBox(0, y, delay) END  
      END 
    ELSE 
      FOR x := 0 TO xLimit DO MoveBox(x, y, delay) END;
      FOR y := y TO yLimit DO MoveBox(xLimit, y, delay) END  
    END
  END 
END DisplayMovingBox;


PROCEDURE Run;
CONST
  Delay = 4000; (* 4 secs *)
BEGIN
  (* Redirect all output to the display *)
  Out.Init(Display.Char);
  Display.Init();
  DisplayChars();
  Timer.MSecDelay(Delay);
  DisplayText();
  Timer.MSecDelay(Delay);
  DisplayDots();
  Timer.MSecDelay(Delay);
  DisplayBoxes();
  Timer.MSecDelay(Delay); 
  DisplayMovingBox();
END Run;


BEGIN
  Run
END DisplayDemo.
Display.mod is used to write consecutive lines of text as on a terminal. The display is refreshed after the last character of a row, or a line feed character is written. If the last line is at the bottom of the display, the next line written causes the existing lines to scroll up.

Code: Select all

MODULE Display;
(* =========================================================================
   Example Cortex-M3 Oberon Module

   Description:
     Write line-oriented output to an LCD display

   Target:
     Dependent on the imported module: LCD

   Tested on:
     ARM mbed Application Board with a Newhaven C12832A1Z 128 x 32 LCD display 

   (c) 2011-2013 CFB Software
   http://www.astrobe.com

   ========================================================================= *)

IMPORT LCD := LCDST756R, Fonts := Fonts6x8;

CONST
  LF = 0AX;
  Cols* = LCD.Cols;
  Rows* = LCD.Rows;

TYPE
  Row = ARRAY Cols OF CHAR;
  Screen = ARRAY Rows + 1 OF Row;

VAR
  rowNo*, colNo*: INTEGER;
  screen: Screen;
  blankRow: Row;
  Refresh*: PROCEDURE;


PROCEDURE WriteChar(ch: CHAR; r, c: INTEGER);
BEGIN
  LCD.DrawChar(LCD.Black, ch, c, r)
END WriteChar;


PROCEDURE WriteRow(row: Row);
VAR
  c: INTEGER;
BEGIN
  FOR c := 0 TO Cols - 1 DO
    WriteChar(row[c], rowNo, c)
  END;
  Refresh()
END WriteRow;


PROCEDURE* NewRow();
BEGIN
  INC(rowNo);
  colNo := 0;
  screen[rowNo] := blankRow
END NewRow;


PROCEDURE ScrollUp;
VAR
  r: INTEGER;
BEGIN
  (* Move rows up *)
  FOR r := 1 TO Rows DO
    rowNo := r - 1;
    screen[rowNo] := screen[r];
    WriteRow(screen[rowNo])
  END;
  NewRow()
END ScrollUp;


PROCEDURE Ln*();
BEGIN
  IF rowNo < Rows THEN
    WriteRow(screen[rowNo]);
    NewRow()
  ELSE
    ScrollUp
  END
END Ln;


PROCEDURE Char*(ch: CHAR);
BEGIN
  IF ch = LF THEN
    Ln
  ELSIF ch >= " " THEN
    screen[rowNo, colNo] := ch;
    INC(colNo);
    IF colNo = Cols THEN Ln END
  END
END Char;


PROCEDURE String*(s: ARRAY OF CHAR);
VAR
  i: INTEGER;
BEGIN
  i := 0;
  WHILE (i < LEN(s)) & (s[i] # 0X) DO Char(s[i]); INC(i) END
END String;


PROCEDURE Init*();
BEGIN
  LCD.Init();
  ASSERT(LCD.LoadFont("Fonts6x8"), 100);
END Init;


BEGIN
  (* Initialise the memory representation of the screen *)
  FOR colNo := 0 TO Cols - 1 DO blankRow[colNo] := " " END;
  FOR rowNo := 0 TO Rows - 1 DO screen[rowNo] := blankRow END;
  rowNo := 0;
  colNo := 0;
  Refresh := LCD.Refresh;
END Display.
LCDST756R.mod is the display driver. Two bitmap array variables are used. bitMap0 represents the pixels that can currently be seen on the screen. When characters, lines or pixels are drawn, they just update the second array, bitMap, in memory. When the Refresh function is called corresponding 8-bit columns of pixels in each bitmap array are compared. The data is only sent to the screen whenever the two do not match. Characters are drawn using 6x8 pixel font data stored in the resource file, accessed using the Astrobe library module ResData.

Code: Select all

MODULE LCDST756R;
(* =========================================================================  
   Example Cortex-M3 Oberon Module  
 
   Description:
     Sitronix ST756R LCD Controller Driver

   Target:
     mbed systems
     
   Tested on: 
     ARM mbed Application Board with a Newhaven C12832A1Z 128 x 32 LCD display 
   
   Reference:
     Sitronix ST756R DataSheet Ver 1.5 (10 Mar 2006).
     
   (c) 2013 CFB Software
   http://www.astrobe.com
   
   ========================================================================= *)

IMPORT Timer, MCU, ResData, SPI, SYSTEM;

CONST 
  Black*   = 0;
  White*   = 1;
  
  MaxX* = 127;
  MaxY* = 31;
  
  FontWidth = 6;
  FontHeight = 8;
  
  Cols* = (MaxX + 1) DIV FontWidth;
  Rows* = (MaxY + 1) DIV FontHeight;
  Pages* = (MaxY + 1) DIV 8;
  
  A0 = {6};    (* P0.6  = mbed P8 *)
  CS = {18};   (* P0.18 = mbed P11 *)
  Reset = {8}; (* P0.8  = mbed P6 *)

TYPE
  (* Each bit represents a pixel on the screen *)
  (* Each page can be refreshed invidually *)
  BitMap = ARRAY MaxX + 1 OF SET;
  (* 6 x 8 pixels *)
  FontPattern = ARRAY FontWidth OF BYTE;
  DoubleWord = ARRAY 2 OF INTEGER;
  
VAR
  font: ResData.Resource;
  fontPattern: FontPattern;
  (* In-memory representation of the screen *)
  bitMap0, bitMap: BitMap;
    

  PROCEDURE LoadFont*(name: ARRAY OF CHAR): BOOLEAN;
  BEGIN
    ResData.Open(font, name);
    RETURN ResData.Size(font) > 0 
  END LoadFont;
    
  
  PROCEDURE* DoubleWordToFontPattern(data: ARRAY OF BYTE; VAR fontPattern: FontPattern);
  VAR
    i: INTEGER;
  BEGIN
    FOR i := 0 TO FontWidth - 1 DO  
      fontPattern[i] := data[i]
    END 
  END DoubleWordToFontPattern;
  
  
  PROCEDURE CharToFontPattern(ch: CHAR; VAR fontPattern: FontPattern);
  (* Store the font data for a character in a 2-D 6 x 8 pixel array *) 
  VAR
    i, index: INTEGER;
    data: DoubleWord;
  BEGIN
    IF (ORD(ch) < ORD(" ")) OR (ORD(ch) > ORD("~")) THEN ch := "." END;
    index := (ORD(ch) - ORD(" ")) * 2;
    ResData.GetInt(font, index, data[0]);
    ResData.GetInt(font, index + 1, data[1]);
    DoubleWordToFontPattern(data, fontPattern)
  END CharToFontPattern;
  
   
  PROCEDURE SendData(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0SET, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS);
  END SendData;
  
   
  PROCEDURE SendCommand(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0CLR, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS)
  END SendCommand;

   
  PROCEDURE SetColumnAddr(x: INTEGER);
  CONST 
    ColumnAddrLo = 000H; 
    ColumnAddrHi = 010H; 
  BEGIN
    SendCommand(ColumnAddrLo + x MOD 16);
    SendCommand(ColumnAddrHi + x DIV 16)
  END SetColumnAddr;

   
  PROCEDURE SetPageAddr(n: INTEGER);
  CONST 
    PageAddrSet  = 0B0H; 
  BEGIN
    SendCommand(PageAddrSet + n)
  END SetPageAddr;

  
  PROCEDURE* DrawDot*(colour, x, y: INTEGER);
  BEGIN
    ASSERT((x <= MaxX) & (y <= MaxY) & (x >= 0) & (y >= 0), 100); 
    IF colour = Black THEN 
      bitMap[x] := bitMap[x] + {y}
    ELSE 
      bitMap[x] := bitMap[x] - {y}
    END
  END DrawDot;

  
  PROCEDURE* DrawVerticalLine*(colour: INTEGER; x, y1, y2: INTEGER);
  VAR 
    yBits: SET;
  BEGIN
    ASSERT((x >= 0) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY), 100);
    yBits := {y1..y2};
    IF colour = Black THEN 
      bitMap[x] := bitMap[x] + yBits
    ELSE 
      bitMap[x] := bitMap[x] - yBits
    END
  END DrawVerticalLine;
     
   
  PROCEDURE* FillRectangle*(colour, x1, y1, x2, y2: INTEGER);
  VAR
    x: INTEGER;
    yBits: SET;
  BEGIN
    ASSERT((x >= 0) & (x2 > x1) & (x2 <= MaxX) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY), 100);
    yBits := {y1..y2};
    IF colour = Black THEN 
      FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] + yBits END 
    ELSE 
      FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] - yBits END 
    END 
  END FillRectangle;
    
 
  PROCEDURE* UpdateSegment(VAR segment: ARRAY OF BYTE; pageNo: INTEGER; fontPattern: BYTE);
  BEGIN
    segment[pageNo] := fontPattern
  END UpdateSegment;

   
  PROCEDURE DrawChar*(colour: INTEGER; ch: CHAR; col, row: INTEGER);
  VAR
    i, x: INTEGER;
    fontPattern: FontPattern;
  BEGIN
    CharToFontPattern(ch, fontPattern);
    ASSERT((col >= 0) & (col < Cols) & (row >= 0) & (row < Rows), 100);
    x := (col * FontWidth);
    FOR i := 0 TO FontWidth - 1  DO
      UpdateSegment(bitMap[x + i], row, fontPattern[i]);
    END 
  END DrawChar;
  
   
  PROCEDURE* PixelColumn(columns: ARRAY OF BYTE; pageNo: INTEGER): BYTE;
  (* Returns a column of eight pixels in a page. A 6x8 character consists
     of six of these columns. Each SendCommand sends a single column
     to the display *)
  BEGIN
    RETURN columns[pageNo]
  END PixelColumn;
    
   
  PROCEDURE Refresh*();
  (* Only write the pixel columns that have changed since the last refresh *)
  VAR
    pageNo, x: INTEGER;
    data0, data: BYTE;
    col: INTEGER;
  BEGIN
    FOR pageNo := 0 TO Pages - 1 DO
      SetPageAddr(pageNo);
      col := -1;
      FOR x := 0 TO MaxX DO 
        data0 := PixelColumn(bitMap0[x], pageNo);
        data := PixelColumn(bitMap[x], pageNo);
        IF data # data0 THEN 
          IF col # x THEN 
            SetColumnAddr(x);
            col := x
          END;
          SendData(data);
          INC(col)
        END 
      END 
    END;
    bitMap0 := bitMap 
  END Refresh;

   
  PROCEDURE* ClearScreen*(colour: INTEGER);
  VAR
    x: INTEGER;
  BEGIN
    IF colour = White THEN 
      FOR x := 0 TO MaxX DO 
        bitMap[x] := {}
      END
    ELSE
      FOR x := 0 TO MaxX DO 
        bitMap[x] := {0..31}
      END
    END
  END ClearScreen;

   
  PROCEDURE* ConfigurePins;
  VAR
    s: SET;
  BEGIN
    (* P0.6, P0.8 are GPIO ports *)
    SYSTEM.GET(MCU.PINSEL0, s);
    s := s - {12, 13, 16, 17};
    SYSTEM.PUT(MCU.PINSEL0, s);

    (* P0.18 is GPIO port *)
    SYSTEM.GET(MCU.PINSEL1, s);
    s := s - {4, 5};
    SYSTEM.PUT(MCU.PINSEL1, s);

    (* P0.6, 0.8 and 0.18 are outputs *)
    SYSTEM.GET(MCU.FIO0DIR, s);
    SYSTEM.PUT(MCU.FIO0DIR, s + A0 + CS + Reset)
  END ConfigurePins;

   
  PROCEDURE Init*;
  CONST
    SPIBus = 1;
    nBits = 8;
    useSSEL = FALSE;
  VAR
    i: INTEGER;
  BEGIN
    SPI.Init(SPIBus, nBits, useSSEL);
    ConfigurePins();
    SYSTEM.PUT(MCU.FIO0CLR, A0); 
    SYSTEM.PUT(MCU.FIO0SET, CS); 
    SYSTEM.PUT(MCU.FIO0CLR, Reset); 
    Timer.uSecDelay(100);
    SYSTEM.PUT(MCU.FIO0SET, Reset); 
    Timer.uSecDelay(100);

    SendCommand(0AEH); (* Display off *)
    SendCommand(0A2H); (* Bias voltage *)
 
    SendCommand(0A0H); (* ADC Normal *)
    SendCommand(0C8H); (* COM Scan normal *)
 
    SendCommand(022H); (* Resistor ratio *)
    SendCommand(02FH); (* Power on *)
    SendCommand(040H); (* Display start line 0 *)
 
    SendCommand(081H); (* Set contrast *)
    SendCommand(017H);
 
    SendCommand(0A6H); (* Display normal *)
    ClearScreen(Black);
    bitMap0 := bitMap;
    ClearScreen(White);
    Refresh();
    SendCommand(0AFH); (* DisplayOn *);

  END Init;

END LCDST756R.
You do not have the required permissions to view the files attached to this post.

captbill
Posts: 21
Joined: Mon Jun 16, 2014 2:57 pm

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by captbill » Thu Oct 16, 2014 10:08 pm

Hi Chris,
I just received a 1.3" OLED unit I am hoping to get working. Can you tell me the corresponding pin name on the Newhaven SPI pins? I have SLC,SDA,RST,and D/T on the pin names on this screen. I see the ouputs from the mBed mc as P0.6, 0.8, and 0.18. I would hate to fry this thing.

It is a 128x64 screen. It would appear that simply changing MaxY* = 31 to 63 should get it going.

Regards,
Bill

cfbsoftware
Site Admin
Posts: 545
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by cfbsoftware » Fri Oct 17, 2014 12:11 pm

The schematics for the mbed application board with the newhaven display can be downloaded from here:

http://developer.mbed.org/media/uploads/chris/mbed-014.1_b.pdf

SLC and SDA pins are for an I2C interface. That is completely different from the SPI interface used for the Newhaven display - the pins used there are MISO, MOSI etc.

Even if your display used SPI it is highly unlikely it would work by just changing the display dimensions. Although these displays have similar capabilities they all seem to use their own command sets for doing things. You would need to implement the command set for your display.

Apart from the Newhaven example (LCDST756R.Mod) there is also an example for the Epson S1D15G00 LCD Controller Driver for 132x132 LCD Nokia-type display (LCDEpson.mod) included in the Astrobe examples. If you look at both of those files you will see both the similarities and the differences. If you still want to give it a go you will need to obtain the datasheet for the driver for your display and hopefully some code examples. Just start with something simple like turning the display on / off or displaying single pixels to get confidence that you are on the right track and then you can build on it from there.

captbill
Posts: 21
Joined: Mon Jun 16, 2014 2:57 pm

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by captbill » Fri Oct 17, 2014 9:08 pm

Chris,
I honestly assumed that everything was handled on the MC with these OLEDs. Where is the driver chip, haha? Surprise, surpise. Please pardon my haste.

This is a lot more LCD than I bargained for. Quite powerful.It is the ssd1306 OLED. This one actually is tri-state SPI,I2C, or parallel.
I found a good source to port from. I hope to get it working over the weekend.

Have you seen this OLED in action? It looks to be quite speedy. It's got built in 2x buffering for compositing! This looks like the ultimate screen for gauges.

http://youtu.be/oi30pz3viSw

cfbsoftware
Site Admin
Posts: 545
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by cfbsoftware » Sat Oct 18, 2014 5:44 am

That sounds similar to the one I have on the EmbeddedArtists LPCXpresso Base Board. It uses the SSD1305 Driver / Controller. I haven't yet attempted to use it with Oberon. However, if you hit any problems with your code and there is some compatibility I might be able to use it to help with some troubleshooting.

captbill
Posts: 21
Joined: Mon Jun 16, 2014 2:57 pm

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by captbill » Mon Oct 20, 2014 4:46 am

Hi Chris,
I believe this should do the trick except I cannot figure, for the life of me, why this .mod won't compile. It says I am missing a ";". Am I casting the hex values incorrectly? Am I missing the obvious?
All the commands and the C++ that I used to port the init sequence are there as comments for you.

Could you take a look? (I tried loading this as an attachment. It said no .mod files allowed)

Regards,
Bill

Code: Select all

MODULE SSD1306_OLED;
   
IMPORT Timer, MCU, ResData, SPI, SYSTEM;

CONST 

  Black*   = 0;
  White*   = 1;
  
  MaxX* = 127;
  MaxY* = 63;
  
  FontWidth = 6;
  FontHeight = 8;
  
  Cols* = (MaxX + 1) DIV FontWidth;
  Rows* = (MaxY + 1) DIV FontHeight;
  Pages* = (MaxY + 1) DIV 8;
  
  A0 = {6};    (* P0.6  = mbed P8 *)
  CS = {18};   (* P0.18 = mbed P11 *)
  Reset = {8}; (* P0.8  = mbed P6 *)  

TYPE
  (* Each bit represents a pixel on the screen *)
  (* Each page can be refreshed invidually *)
  BitMap = ARRAY MaxX + 1 OF SET;
  (* 6 x 8 pixels *)
  FontPattern = ARRAY FontWidth OF BYTE;
  
VAR
  font: ResData.Resource;
  fontPattern: FontPattern;
  (* In-memory representation of the screen *)
  bitMap0, bitMap: BitMap;
  
(*SSD1306_I2C_ADDRESS =              0x3D;
  
  SSD1306_LCDWIDTH =                 128;
  SSD1306_LCDHEIGHT =                64;
  SSD1306_SETCONTRAST =              0x81;
  SSD1306_DISPLAYALLON_RESUME =      0xA4;
  SSD1306_DISPLAYALLON =             0xA5;
  SSD1306_NORMALDISPLAY =            0xA6;
  SSD1306_INVERTDISPLAY =            0xA7;
  SSD1306_DISPLAYOFF =               0xAE;
  SSD1306_DISPLAYON =                0xAF;
  SSD1306_SETDISPLAYOFFSET =         0xD3;
  SSD1306_SETCOMPINS =               0xDA;
  
  SSD1306_SETVCOMDETECT =            0xDB;

  SSD1306_SETDISPLAYCLOCKDIV =       0xD5;
  SSD1306_SETPRECHARGE =             0xD9; 

  SSD1306_SETMULTIPLEX =             0xA8;

  SSD1306_SETLOWCOLUMN =             0x00;
  SSD1306_SETHIGHCOLUMN =            0x10;

  SSD1306_SETSTARTLINE =             0x40;

  SSD1306_MEMORYMODE =               0x20;
  SSD1306_COLUMNADDR =               0x21;
  SSD1306_PAGEADDR   =               0x22;

  SSD1306_COMSCANINC =               0xC0;
  SSD1306_COMSCANDEC =               0xC8;

  SSD1306_SEGREMAP =                 0xA0;

  SSD1306_CHARGEPUMP =               0x8D;

  SSD1306_EXTERNALVCC =              0x1;
  SSD1306_SWITCHCAPVCC =             0x2;

  SSD1306_ACTIVATE_SCROLL =          0x2F;
  SSD1306_DEACTIVATE_SCROLL =        0x2E;
  SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3;
  SSD1306_RIGHT_HORIZONTAL_SCROLL =  0x26;
  SSD1306_LEFT_HORIZONTAL_SCROLL =   0x27;
  SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29;
  SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL  = 0x2A;   *)    

  PROCEDURE LoadFont*(name: ARRAY OF CHAR): BOOLEAN;
  BEGIN
    ResData.Open(font, name);
    RETURN ResData.Size(font) > 0 
  END LoadFont;


  (* Store the font data for a character in a 2-D pixel array *) 
  PROCEDURE CharToFontPattern(ch: CHAR; VAR fontPattern: FontPattern);
  VAR
    i, index: INTEGER;
  BEGIN
    IF (ORD(ch) < ORD(" ")) OR (ORD(ch) > ORD("~")) THEN ch := "." END;
    index := (ORD(ch) - ORD(" ")) * 8;
    FOR i := 0 TO FontWidth - 1 DO  
      ResData.GetByte(font, index + i, fontPattern[i]);
    END 
  END CharToFontPattern;
  
   
  PROCEDURE SendData(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0SET, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS);
  END SendData;
  
   
  PROCEDURE SendCommand(data: INTEGER);
  BEGIN
    SYSTEM.PUT(MCU.FIO0CLR, A0);
    SYSTEM.PUT(MCU.FIO0CLR, CS);
    SPI.SendData(data);
    SYSTEM.PUT(MCU.FIO0SET, CS)
  END SendCommand;

   
  PROCEDURE SetColumnAddr(x: INTEGER);
  CONST 
    ColumnAddrLo = 0X00; 
    ColumnAddrHi = 0X10; 
  BEGIN
    SendCommand(ColumnAddrLo + x MOD 16);
    SendCommand(ColumnAddrHi + x DIV 16)
  END SetColumnAddr;

   
  PROCEDURE SetPageAddr(n: INTEGER);
  CONST 
    PageAddrSet  = 0X22; 
  BEGIN
    SendCommand(PageAddrSet + n)
  END SetPageAddr;

  
  PROCEDURE* DrawDot*(colour, x, y: INTEGER);
  BEGIN
    IF (x <= MaxX) & (y <= MaxY) & (x >= 0) & (y >= 0) THEN 
      IF colour = Black THEN 
        bitMap[x] := bitMap[x] + {y}
      ELSE 
        bitMap[x] := bitMap[x] - {y}
      END
    END 
  END DrawDot;


  PROCEDURE* DrawVerticalLine*(colour: INTEGER; x, y1, y2: INTEGER);
  VAR 
    yBits: SET;
  BEGIN
    IF (x >= 0) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY) THEN 
      yBits := {y1..y2};
      IF colour = Black THEN 
        bitMap[x] := bitMap[x] + yBits
      ELSE 
        bitMap[x] := bitMap[x] - yBits
      END
    END 
  END DrawVerticalLine;
     
   
  PROCEDURE* FillRectangle*(colour, x1, y1, x2, y2: INTEGER);
  VAR
    x: INTEGER;
    yBits: SET;
  BEGIN
    IF (x >= 0) & (x2 > x1) & (x2 <= MaxX) & (y1 >= 0) & (y2 >= y1) & (y2 <= MaxY) THEN
      yBits := {y1..y2};
      IF colour = Black THEN 
        FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] + yBits END 
      ELSE 
        FOR x := x1 TO x2 DO bitMap[x] := bitMap[x] - yBits END 
      END 
    END 
  END FillRectangle;
    
 
  PROCEDURE* DrawFontChar(fontPattern: FontPattern; col, row: INTEGER);
  VAR
    i, x, adr, fontAdr: INTEGER;
    fontData: BYTE;
  BEGIN
    ASSERT((col >= 0) & (col < Cols) & (row >= 0) & (row < Rows), 100);
    x := (col * FontWidth);
    adr := SYSTEM.ADR(bitMap[x]) + row;
    fontAdr := SYSTEM.ADR(fontPattern);
    FOR i := 0 TO FontWidth - 1  DO
      SYSTEM.GET(fontAdr, fontData, 1);
      SYSTEM.PUT(adr, fontData, 4) 
    END 
  END DrawFontChar;
  
   
  PROCEDURE DrawChar*(colour: INTEGER; ch: CHAR; col, row: INTEGER);
  VAR
    fontPattern: FontPattern;
  BEGIN
    CharToFontPattern(ch, fontPattern);
    DrawFontChar(fontPattern, col, row)
  END DrawChar;

  
  PROCEDURE Refresh*();
  (* Only write the pixel columns that have changed since the last refresh *)
  VAR
    pageNo, x, adr0, adr: INTEGER;
    data0, data: BYTE;
    col: INTEGER;
  BEGIN
    FOR pageNo := 0 TO Pages - 1 DO
      SetPageAddr(pageNo);
      col := -1;
      adr0 := SYSTEM.ADR(bitMap0) + pageNo;
      adr := SYSTEM.ADR(bitMap) + pageNo;
      FOR x := 0 TO MaxX DO 
        SYSTEM.GET(adr0, data0, 4);
        SYSTEM.GET(adr, data, 4);
        IF data # data0 THEN 
          IF col # x THEN 
            SetColumnAddr(x);
            col := x
          END;
          SendData(data);
          INC(col)
        END 
      END 
    END;
    bitMap0 := bitMap 
  END Refresh;

   
  PROCEDURE* ClearScreen*(colour: INTEGER);
  VAR
    x: INTEGER;
  BEGIN
    IF colour = White THEN 
      FOR x := 0 TO MaxX DO 
        bitMap[x] := {}
      END
    ELSE
      FOR x := 0 TO MaxX DO 
        bitMap[x] := {0..31}
      END
    END
  END ClearScreen;

  
  PROCEDURE* ConfigureSPI1Pins;
  VAR
    s: SET;
  BEGIN 
    (* SPI1 *)
    (* Setup    SCK1, SSEL1, MISO1, MOSI1, no SSEL *)
    (* PS0 bits 15:14 12:13  17:16, 19:18 := 10B *) 
    SYSTEM.GET(MCU.PINSEL0, s);
    s := s + {15, 17, 19} - {14, 16, 18};
    SYSTEM.PUT(MCU.PINSEL0, s)
  END ConfigureSPI1Pins;
   

  PROCEDURE* ConfigureGPIOPins;
  VAR
    s: SET;
  BEGIN
    (* P0.6, P0.8 are GPIO ports *)
    SYSTEM.GET(MCU.PINSEL0, s);
    s := s - {12, 13, 16, 17};
    SYSTEM.PUT(MCU.PINSEL0, s);

    (* P0.18 is GPIO port *)
    SYSTEM.GET(MCU.PINSEL1, s);
    s := s - {4, 5};
    SYSTEM.PUT(MCU.PINSEL1, s);

    (* P0.6, 0.8 and 0.18 are outputs *)
    SYSTEM.GET(MCU.FIO0DIR, s);
    SYSTEM.PUT(MCU.FIO0DIR, s + A0 + CS + Reset)
  END ConfigureGPIOPins;

   
  PROCEDURE Init*;
  CONST
    nBits = 8;
  BEGIN
    SPI.Init(SPI.SPI1, nBits, ConfigureSPI1Pins);
    ConfigureGPIOPins();
    SYSTEM.PUT(MCU.FIO0CLR, A0); 
    SYSTEM.PUT(MCU.FIO0SET, CS); 
    SYSTEM.PUT(MCU.FIO0CLR, Reset); 
    Timer.uSecDelay(100);
    SYSTEM.PUT(MCU.FIO0SET, Reset); 
    Timer.uSecDelay(100);

    SendCommand(0XAE); (* Display off *)
    
    SendCommand(0A2H); (* Bias voltage *) 
    SendCommand(0A0H); (* ADC Normal *)
    
    SendCommand(0XD5); (* COM Scan normal *)
    SendCommand(0X80); (* suggested ratio 0x80 *)
 
    SendCommand(0XA8); (* Set Multiplex *)
    SendCommand(0X3F);
    
    SendCommand(0XD3); (* Set display offset *)
    SendCommand(0X0);  (* no offset *)
 
    SendCommand(0X40); (* Set start line *)
    SendCommand(0X0);
    
    SendCommand(0X8D);  (* Set charge pump*)
    SendCommand(0X14);  (* set for internal vcc. 0x10 for external vcc*)
    
    SendCommand(0X20); (* Set memory mode *)
    SendCommand(0X00);
    
    SendCommand(0XA0); (* Set segremap *)
    SendCommand(0X1);
    
    SendCommand(0XC8); (* ComScanDecr *)
    
    SendCommand(0XDA); (* Set com pins *)
    SendCommand(0X12);
    
    SendCommand(0X81); (* Set contrast *)
    SendCommand(0XCF);
    
    SendCommand(0XD); (* Set precharge *)
    SendCommand(0XF1);
    
    SendCommand(0XDB); (* Set V com detect *)
    SendCommand(0X40);
    
    SendCommand(0XA4); (* Display all on resume *)
    
    SendCommand(0XA6); (* Normal display *) 
   
    ClearScreen(Black);
    bitMap0 := bitMap;
    ClearScreen(White);
    Refresh();
    SendCommand(0XAF); (* DisplayOn *);
    
(*  Init sequence
  #if defined SSD1306_128_64
    // Init sequence for 128x64 OLED module
    ssd1306_command(SSD1306_DISPLAYOFF);                    // 0xAE
    ssd1306_command(SSD1306_SETDISPLAYCLOCKDIV);            // 0xD5
    ssd1306_command(0x80);                                  // the suggested ratio 0x80
    ssd1306_command(SSD1306_SETMULTIPLEX);                  // 0xA8
    ssd1306_command(0x3F);
    ssd1306_command(SSD1306_SETDISPLAYOFFSET);              // 0xD3
    ssd1306_command(0x0);                                   // no offset
    ssd1306_command(SSD1306_SETSTARTLINE | 0x0);            // line #0
    ssd1306_command(SSD1306_CHARGEPUMP);                    // 0x8D
    if (vccstate == SSD1306_EXTERNALVCC) 
      { ssd1306_command(0x10); }
    else 
      { ssd1306_command(0x14); }
    ssd1306_command(SSD1306_MEMORYMODE);                    // 0x20
    ssd1306_command(0x00);                                  // 0x0 act like ks0108
    ssd1306_command(SSD1306_SEGREMAP | 0x1);
    ssd1306_command(SSD1306_COMSCANDEC);
    ssd1306_command(SSD1306_SETCOMPINS);                    // 0xDA
    ssd1306_command(0x12);
    ssd1306_command(SSD1306_SETCONTRAST);                   // 0x81
    if (vccstate == SSD1306_EXTERNALVCC) 
      { ssd1306_command(0x9F); }
    else 
      { ssd1306_command(0xCF); }
    ssd1306_command(SSD1306_SETPRECHARGE);                  // 0xd9
    if (vccstate == SSD1306_EXTERNALVCC) 
      { ssd1306_command(0x22); }
    else 
      { ssd1306_command(0xF1); }
    ssd1306_command(SSD1306_SETVCOMDETECT);                 // 0xDB
    ssd1306_command(0x40);
    ssd1306_command(SSD1306_DISPLAYALLON_RESUME);           // 0xA4
    ssd1306_command(SSD1306_NORMALDISPLAY);                 // 0xA6
  #endif*)


  END Init;

END SSD1306_OLED.

cfbsoftware
Site Admin
Posts: 545
Joined: Fri Dec 31, 2010 12:30 pm
Contact:

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by cfbsoftware » Mon Oct 20, 2014 10:44 am

Hi Bill,

0X00 is the way you write hexadecimal constants in the C programming language. In Oberon hexadecimal numeric constants must start with a digit and end with an 'H'. e.g.

Code: Select all

C          Oberon
0X00       0H
0X10       10H
0XA0       0A0H
or, if you prefer, you can use leading zeroes to make them all the same length:

Code: Select all

C          Oberon
0X00       000H
0X10       010H
0XA0       0A0H
There are quite a few you will have to fix in your source code before it will compile. Only the first error was reported this time.

The forum will accept zip files as attachments,

Regards,
Chris

captbill
Posts: 21
Joined: Mon Jun 16, 2014 2:57 pm

Re: LCD Text and Graphics demo forARM's mbed application boa

Post by captbill » Tue Oct 21, 2014 1:35 pm

Hi Chris,
I got the hex worked out. That certainly wasn't going to compile. Haha.

Next is fairly tall list of issues I need to work through. It needs 4-wire SPI. This chip uses a non-standard SPI model that involves a "D/C" or Data/Command line for fast switching between commands and moving pixel data into the onboard RAM buffer. I think I have the basic model figured out and even have the necessary procedures set up. The SendCommand and SendData routines need tweaking is all incorporating the pull-down D/C wire. The RAM buffer is really nice since you can buffer graphics elements in the 2048k RAM and drive them very fast and with simple commands from the MC. This means we could load our fonts and graphics from our .rc files (ResData.mod) at startup using this setup. Once the basics are sorted with this peculiarity, then the ssd1305 and also the newer ssd1351 are easily implemented.

I bought an Ebay special that is not documented at all. I did find some good forum info on these units, but I wish I had ordered one from Adafruit for porting ease. Really hard to debug/test without a working power-on.

Perhaps I should set this up for the ssd1305 first for you since this should be done anyway, plus it is the simpler unit. I do believe the ssd1305 uses the same D/C mechanism but I need to look further at it. If we have just one of these puppies running we can run all of these in the same fashion.

It would be great to see if this code will run the OLED on your Xilinx board. Let me adapt it over to the ssd1305 and clean it up some. Maybe you could give it a go.
Regards,
Bill

Locked