S1D13700

This library supports a 320 x 240 GLCD with the S1D13700 controller. It is intended for use with the standard Swordfish GLCD library (GLCD.bas). This will allow use of proportional fonts, bitmap images and vector graphics.

The library is included below. Use with the GLCD library requires an addition to the GLCD library itself. However, this has already been done for you. You do not need to add the module code shown below to the user library folder. Just do one of the following:

  • For licenced users, run the on-line update facility.
  • For SE users, download the latest release.

This will update the GLCD library and add the new S1D13700 library.

The library allows use of either 6800 or 8080 addressing mode - use #option GLCD_MODE to determine which mode is used.

Below are two images of the display in use.

Thanks go to Jack Smith for his help with much of the initial code and for testing the library.

Steven Wright

S1D13700 Graphics Driver

S1D13700 Module

{
********************************************************************************
*  Name    : S1D13700.BAS                                                      *
*  Author  : S Wright                                                          *
*  Notice  : Copyright (c) 2007 J Smith, S Wright                              *
*          : All Rights Reserved                                               *
*  Date    : 27/01/2007                                                        *
*  Version : 1.0                                                               *
*  Notes   : Library for a 320 x 240 graphic LCD with S1D13700 controller      *
*          : working in 6800 or 8080 mode                                      *
********************************************************************************
}
Module S1D13700

// import the graphics module...
#define GLCD_PIXEL_08
#define GLCD_COLOR_01
#define GLCD_XY_16
Include "Graphics.bas"   
Include "System.bas"   
Include "Utils.bas"

// default module options - user options can override these values...
#option GLCD_MODE = 8080         // GLCD interface mode - 6800 or 8080
#option GLCD_DATA = PORTD        // data port
#option GLCD_EN = PORTB.0        // EN pin - 6800 mode
#option GLCD_RD = PORTB.0        // RD pin - 8080 mode
#option GLCD_RW = PORTB.1        // RW pin - 6800 mode
#option GLCD_WR = PORTB.1        // WR pin - 8080 mode
#option GLCD_A0 = PORTB.2        // A0 pin
#option GLCD_CS = PORTB.3        // chip select
#option GLCD_RES = PORTB.5       // reset pin
#option GLCD_ASPECT_RATIO = 100  // aspect ratio, smaller number will squeeze y for GLCD circles and box
#option GLCD_INIT_DELAY = 100    // initialisation delay (ms)

#if Not (GLCD_MODE in (6800, 8080))
   #error GLCD_MODE, "Invalid option. GLCD mode not recognized."
#endif

// validate data port...
#if IsOption(GLCD_DATA) 
   #if Not IsValidPort(GLCD_DATA)
      #error GLCD_DATA, "Invalid option. DATA must be a valid port name."
   #endif
   #ifdef GLCD_DATA@ Then
      #error GLCD_DATA@, "Invalid option. DATA port cannot be a single bit value."
   #endif
#endif

// validate EN pin...
#if IsOption(GLCD_EN) And Not IsValidPortPin(GLCD_EN) 
   #error GLCD_EN, "Invalid option. EN must be a valid port pin."
#endif

// validate RD pin...
#if IsOption(GLCD_RD) And Not IsValidPortPin(GLCD_RD) 
   #error GLCD_RD, "Invalid option. RD must be a valid port pin."
#endif

// validate RW pin...
#if IsOption(GLCD_RW) And Not IsValidPortPin(GLCD_RW) 
   #error GLCD_RW, "Invalid option. RW must be a valid port pin."
#endif

// validate WR pin...
#if IsOption(GLCD_WR) And Not IsValidPortPin(GLCD_WR) 
   #error GLCD_WR, "Invalid option. WR must be a valid port pin."
#endif

// validate A0 pin...
#if IsOption(GLCD_A0) And Not IsValidPortPin(GLCD_A0) 
   #error GLCD_A0, "Invalid option. A0 must be a valid port pin."
#endif

// validate CS pin...
#if IsOption(GLCD_CS) And Not IsValidPortPin(GLCD_CS) 
   #error GLCD_CS, "Invalid option. CS must be a valid port pin."
#endif

// validate RES pin...
#if IsOption(GLCD_RES) And Not IsValidPortPin(GLCD_RES) 
   #error GLCD_RES, "Invalid option. RES must be a valid port pin."
#endif

// validate initialisation delay...
#if IsOption(GLCD_INIT_DELAY)
   #if Not (GLCD_INIT_DELAY in (0 to 1000))
      #error GLCD_INIT_DELAY, "Invalid option. GLCD initialize delay must be between 0 and 1000 (ms)."
   #endif
#endif 

// now create Data TRIS...
#option _GLCD_DATA_TRIS = GetTRIS(GLCD_DATA)

// GLCD width and height...
Public Const
   GLCDWidth = 320,
   GLCDHeight = 240

// x, y position structure...   
Public Structure TPositionS1D13700
   x As TXY
   y As Byte
End Structure

// x, y position...
Public Dim
   Pos As TPositionS1D13700

// S1D13700 commands...   
Const
   cmdSystem = $40,          // general system settings
   cmdSleep = $53,           // enter into standy mode
   cmdDisplayOff = $58,      // turn the display off
   cmdDisplayOn = $59,       // turn the display on
   cmdScroll = $44,          // setup text and graphics address regions
   cmdCsrForm = $5D,         // set cursor size
   cmdCsrDirRight = $4C,     // cursor moves right after write to display memory
   cmdCsrDirLeft = $4D,      // cursor moves left after write to display memory
   cmdCsrDirUp = $4E,        // cursor moves up after write to display memory
   cmdCsrDirDown = $4F,      // cursor moves down after write to display memory
   cmdCGRAMAddress = $5C,    // configure character generator RAM address
   cmdHDotScroll = $5A,      // set horizontal scroll rate
   cmdOverlay = $5B,         // configure how layers overlay
   cmdSetCsrAddress = $46,   // set the cursor address
   cmdGetCsrAddress = $47,   // read the cursor address
   cmdDisplayWrite = $42,    // write to display memory
   cmdDisplayRead = $43,     // read from display memory
   GLCDDelay = GLCD_INIT_DELAY     

// port and pin settings, these are brought into
// the program by using the above options...
Dim        
   Data As GLCD_DATA,           // data in (PORT) 
   TRISData As _GLCD_DATA_TRIS, // data TRIS
   EN As GLCD_EN.GLCD_EN@,      // EN pin - enable (serves as a strobe) - 6800 mode
   RD As GLCD_RD.GLCD_RD@,      // RD pin - read when L - 8080 mode
   RW As GLCD_RW.GLCD_RW@,      // RW pin - read or write (0 = write, 1 = read) - 6800 mode
   WR As GLCD_WR.GLCD_WR@,      // WR pin - write when L - 8080 mode
   A0 As GLCD_A0.GLCD_A0@,      // A0 pin - sets data destination: 1 = command, 0 = data
   CS As GLCD_CS.GLCD_CS@,      // chip select - 0 = selected, 1 = disabled
   RES As GLCD_RES.GLCD_RES@    // RES pin - normally RES = 1 but pull to 0 for initialization

Dim
   Layers As Byte,              // layers 1 - 3 on/off
   Layer1 As Layers.Booleans(2),
   Layer2 As Layers.Booleans(4),
   Layer3 As Layers.Booleans(6),
   CurrentLayer As Byte,        // current drawing layer
   CurrentLayerMemPos As Word
{
********************************************************************************
* Name    : SetData (PRIVATE)                                                  *
* Purpose : Write a data byte to GLCD                                          *
********************************************************************************
}     
Sub SetData(pData As Byte)                
#if GLCD_MODE = 6800
   A0 = 0               // access display RAM data
   RW = 0               // write mode
   TRISData = $00       // set data bus to output
   Data = pData         // write to the bus
   CS = 0
   EN = 1               // write to GLCD
   DelayUS(1)
   EN = 0
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#elseif GLCD_MODE = 8080                   
   A0 = 0               // setup for data write
   RD = 1               // RD high for writing
   TRISData = $00       // set data bus to output
   Data = pData         // write to the bus
   CS = 0
   WR = 0               // write low starts write sequence
   DelayUS(1)           // keep WR low to allow command to register
   WR = 1               // de-assert write
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#endif
End Sub
{
********************************************************************************
* Name    : GetData (PRIVATE)                                                  *
* Purpose : Read byte from GLCD                                                *
********************************************************************************
}     
Function GetData() As Byte
#if GLCD_MODE = 6800
   A0 = 1               // access display RAM data
   RW = 1               // read mode
   TRISData = $FF       // set data bus to input
   CS = 0
   EN = 1               // read from GLCD
   DelayUS(2)
   Result = Data        // read from the bus
   EN = 0
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#elseif GLCD_MODE = 8080                  
   A0 = 1               // setup for data read
   WR = 1               // WR high for reading
   TRISData = $FF       // set data bus to input
   CS = 0
   RD = 0               // read low starts read sequence
   DelayUS(2)           // keep RD low to allow data to be read
   Result = Data        // read from the bus
   RD = 1               // de-assert read
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#endif
End Function  
{
********************************************************************************
* Name    : Command (PRIVATE)                                                  *
* Purpose : Write a command byte to GLCD                                       *
********************************************************************************
}     
Sub Command(pCommand As Byte)                 
#if GLCD_MODE = 6800
   A0 = 1               // instruction mode
   RW = 0               // write mode
   TRISData = $00       // set data bus to output
   Data = pCommand      // write to the bus
   CS = 0
   EN = 1               // write to GLCD
   DelayUS(1)
   EN = 0
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#elseif GLCD_MODE = 8080                        
   A0 = 1               // setup for command
   RD = 1               // RD high for writing
   TRISData = $00       // set data bus to output
   Data = pCommand      // write to the bus
   CS = 0
   WR = 0               // write low starts write sequence
   DelayUS(1)           // keep WR low to allow command to register
   WR = 1               // de-assert write
   CS = 1
   DelayUS(1)           // prevent further write/read immediately
#endif
End Sub
{
********************************************************************************
* Name    : CsrMemPosition                                                     *
* Purpose : Positions cursor in graphics memory                                *
********************************************************************************
} 
Sub CsrMemPosition(pMemStart As Word)
   Command(cmdSetCsrAddress)  // command - next two bytes are low & high bytes of memory start
   SetData(pMemStart.Byte0)   // low byte
   SetData(pMemStart.Byte1)   // high byte
End Sub
{
********************************************************************************
* Name    : SetPosition (PRIVATE)                                              *
* Purpose : Set GLCD x and y positions                                         *
********************************************************************************
}     
Sub SetPosition()
   Dim MemPos As Word
   MemPos = CurrentLayerMemPos + Pos.x + (40 * Pos.y)   // find position in graphics
                                                        // memory (40 bytes across 320 pixel screen)
   CsrMemPosition(MemPos)
End Sub
{
********************************************************************************
* Name    : WriteByte                                                          *
* Purpose : Write a byte                                                       *
********************************************************************************
} 
Public Sub WriteByte(pValue As Byte)              
   SetPosition
   Command(cmdDisplayWrite)
   SetData(pValue)
End Sub 
{
********************************************************************************
* Name    : ReadByte                                                           *
* Purpose : Read a byte                                                        *
********************************************************************************
} 
Public Function ReadByte() As Byte                
   SetPosition
   Command(cmdDisplayRead)
   ReadByte = GetData  
End Function
{
********************************************************************************
* Name    : SetPixel                                                           *
* Purpose : Set pixel at pixel location x,y                                    *
********************************************************************************
} 
Public Sub SetPixel(pX, pY As TXY)          
   Dim XBit As Byte
   Dim Pixel As Byte
   Dim LastPosX As TXY

   If (pX < GLCDWidth) And (pY < GLCDHeight) Then

      LastPosX = Pos.x
      XBit = %10000000 >> (pX Mod 8)
      Pos.y = pY
      Pos.x = pX / 8

      SetPosition
      Command(cmdDisplayRead)
      Pixel = GetData  


      // pen is white...
      If Pen.Color = 0 Then
         If Pen.Mode = pmCopy Then
            XBit = Not XBit
            Pixel = Pixel And XBit
         EndIf

      // pen is black...
      Else

         // pen copy or merge...
         If Pen.Mode <> pmXOR Then
            Pixel = Pixel Or XBit
         // pen XOR
         Else
            If (Pixel And XBit) = 0 Then
               Pixel = Pixel Or XBit
            Else
               XBit = Not XBit
               Pixel = Pixel And XBit
            EndIf      
         EndIf
      EndIf

      SetPosition
      Command(cmdDisplayWrite)
      SetData(Pixel)

      Pos.x = LastPosX
   EndIf
End Sub
{
********************************************************************************
* Name    : GetPixel                                                           *
* Purpose : Return pixel colour at pixel position x, y                         *
********************************************************************************
}
Public Function GetPixel(pX, pY As TXY) As TColor             
   Dim Pixel As Byte
   Pos.y = pY
   Pos.x = pX / 8
   SetPosition
   Command(cmdDisplayRead)
   Pixel = GetData << (pX Mod 8) 
   Result = Pixel.7  
End Function
{
********************************************************************************
* Name    : Cls                                                                *
* Purpose : Clear one of the three graphic screens                             *
********************************************************************************
} 
Public Sub Cls(pLayer As Byte)                       
   Dim MemStart, MemPos As Word
   MemStart = (pLayer - 1) * 9600
   CsrMemPosition(MemStart)   // move to start of graphic memory for layer
   Command(cmdDisplayWrite)   // set to write to display memory
   MemPos = 0
   Repeat
      SetData($00)           
      Inc(MemPos)
   Until MemPos = 9600
   CsrMemPosition(MemStart)   // move to start of graphic memory for layer
   Command(cmdDisplayOn)      // command for screen on to remove cursor
   SetData(Layers)            // sets layers 1 - 3, cursor off
End Sub  
{
********************************************************************************
* Name    : ApplyLine (PRIVATE)                                                *
* Purpose : Applies mask to line                                               *
********************************************************************************
} 
Private Function ApplyLine(pLine, pMask As Byte) As Byte
   If Brush.Color = 0 Then   // brush is white...
      ApplyLine = pLine And (Not pMask)
   Else                      // brush is black...
      ApplyLine = pLine Or pMask
   EndIf
End Function
{
********************************************************************************
* Name    : Fill                                                               *
* Purpose : Fast fill screen area with brush color.                            *
*         : Assumes pX1 < pX2 and pY1 < pY2                                    *
********************************************************************************
} 
Public Sub Fill(pX1, pY1, pX2, pY2 As TXY)
   Dim LastPosX As Word
   Dim MaskL, MaskR As Byte
   Dim Line As Byte
   LastPosX = Pos.x
   MaskL = $FF >> (pX1 Mod 8)
   MaskR = $FF << (7 - pX2 Mod 8)
   Pos.y = pY1
   Repeat
      Pos.x = pX1 / 8
      Line = ReadByte()
      If pX2 / 8 = pX1 / 8 Then       // start & end of rectangle in same byte
         WriteByte(ApplyLine(Line, MaskL And MaskR))
      Else
         WriteByte(ApplyLine(Line, MaskL))     
         Inc(Pos.x)
         SetPosition()
         Command(cmdDisplayWrite)     // set to write to display memory
         While Pos.x < pX2 / 8
            If Brush.Color = 0 Then   // brush is white...
               SetData($00)
            Else                      // brush is black...
               SetData($FF)
            EndIf
            Inc(Pos.x)
         Wend
         Line = ReadByte()            // right side of rectangle
         WriteByte(ApplyLine(Line,MaskR))
      EndIf
      Inc(Pos.y)
   Until Pos.y > pY2
End Sub
{
********************************************************************************
* Name    : WriteImageByte (PRIVATE)                                           *
* Purpose : Write an image byte to GLCD at pixel position x, y                 *
********************************************************************************
} 
#define GLCD_SETIMAGE
Sub WriteImageByte(pX As Word, pY As Byte, pValue As Byte)
   Dim Line, ShiftValue, XBit, XBitReverse As Byte
   Dim LastPosX As Word

   LastPosX = Pos.x           // save x position
   XBit = pX Mod 8            // offset
   XBitReverse = 8 - XBit     // reverse offset
   Pos.x = pX / 8             // align to page
   Pos.y = pY                 // save y

   // get first byte...
   Line = ReadByte   

   // mask, if needed...
   If Pen.Mode = pmCopy Then
      Line = Line >> XBitReverse
      Line = Line << XBitReverse
   EndIf

   // merge then write data...   
   ShiftValue = pValue >> XBit
   If Pen.Mode = pmXOR Then
      Line = Line Xor ShiftValue
   Else
      Line = Line Or ShiftValue
   EndIf
   WriteByte(Line)

   // get next byte...
   If XBit <> 0 Then
      Inc(Pos.x)
      Line = ReadByte

      // mask, if needed...
      If Pen.Mode = pmCopy Then
         Line = Line << XBit
         Line = Line >> XBit
      EndIf

      // merge then write data...
      ShiftValue = pValue << XBitReverse
      If Pen.Mode = pmXOR Then
         Line = Line Xor ShiftValue
      Else
         Line = Line Or ShiftValue
      EndIf   
      WriteByte(Line) 
   EndIf

   // restore x position...
   Pos.x = LastPosX  
End Sub 
{
********************************************************************************
* Name    : ReadImageByte (PRIVATE)                                            *
* Purpose : Read an image byte                                                 *
********************************************************************************
} 
Function ReadImageByte() As Byte
   ASM-
      TBLRD *+
      movff TABLAT, result
   End ASM
End Function
{
********************************************************************************
* Name    : SetImage                                                           *
* Purpose : BitBlit image to GLCD at pixel location x, y.                      *
*         : NOTE - This subroutine only supports $01 (monochrome) bitmaps.     *
*         : It requires images drawn with bytes in the x-direction.            *
********************************************************************************
} 
Public Sub SetImage(pX, pY As TXY, pImageAdr As Word, pMode As Byte = cmCopy)
   Dim Image As TImage
   Dim x,Width As Word
   Dim y,Height As Byte
   Dim Line As Byte
   Dim LastPen As TPen

   // get bitmap ID, width and height...
   TABLEPTR = pImageAdr
   Image.Header = ReadImageByte
   Image.Width = ReadImageByte
   Image.Height = ReadImageByte
   Inc(TABLEPTR)

   LastPen = Pen
   Pen.Mode = pMode

   // byte aligned bit blit... 
   x = pX
   Width = Image.Width / 8
   While Width > 0
      y = pY
      Height = Image.Height 
      While Height > 0
         WriteImageByte(x,y,ReadImageByte)
         Inc(y)
         Dec(Height)
      Wend
      Inc(x, 8)
      Dec(Width)
   Wend 

   // non byte aligned bit blit...
   pX = x
   Image.Width = Image.Width Mod 8   
   If Image.Width > 0 Then
      y = pY
      Height = Image.Height
      While Height > 0     
         Line = ReadImageByte
         Width = Image.Width
         x = pX
         While Width > 0
            Pen.Color = Line.7
            SetPixel(x,y)
            Line = Line << 1
            Dec(Width)
            Inc(x)
         Wend
         Dec(Height)
         Inc(y)
      Wend
   EndIf   
   Pen = LastPen   
End Sub
{
********************************************************************************
* Name    : SetImage                                                           *
* Purpose : BitBlit image To GLCD at pixel location x, y.                      *
*         : NOTE - This subroutine only supports $01 (monochrome) bitmaps.     *
*         : It requires images drawn with bytes in the x-direction.            *
********************************************************************************
} 
Public Sub SetImage(pX, pY As TXY, ByRefConst pImage() As Byte, pMode As Byte = cmCopy)
   SetImage(pX, pY, @pImage, pMode)
End Sub 
{
********************************************************************************
* Name    : ShowLayer (Public)                                                 *
* Purpose : Turns on/off graphic layers                                        *
********************************************************************************
} 
Public Sub ShowLayer(pLayer As Byte, pOn As Boolean)              
   Layers.Bits(2 * pLayer) = Bit(pOn)
   Command(cmdDisplayOn)      // command for screen on
   SetData(Layers)            // sets layers 1 - 3, cursor off
End Sub
{
********************************************************************************
* Name    : SwitchLayer (Public)                                               *
* Purpose : Switches current drawing layer                                     *
********************************************************************************
} 
Public Sub SwitchLayer(pLayer As Byte)              
   CurrentLayer = pLayer
   CurrentLayerMemPos = 9600 * (CurrentLayer - 1)
End Sub
{
********************************************************************************
* Name    : Initialize                                                         *
* Purpose : Configure the GLCD before use.                                     *
*         : Set up ports for I/O and perform RESET on LCD                      *
********************************************************************************
} 
Sub Initialize()                    
Dim i As Word
   SetAllDigital()
   Pos.x = 0
   Pos.y = 0  
#if GLCD_MODE = 6800
   Output(EN)                 // enable
   Output(RW)                 // read/write
#elseif GLCD_MODE = 8080
   Output(RD)                 // read
   Output(WR)                 // write
#endif
   Output(A0)                 // data or instruction
   Output(CS)                 // chip select
   Output(RES)                // reset
#if GLCD_MODE = 6800
   DelayMS(GLCDDelay)         // start up delay, allow GLCD to settle
   RES = 0                    // RES = 0 to reset
   EN = 0                     // start with EN = 0
   A0 = 0
   Data = 0
   CS = 0
   DelayMS(5)
   RES = 1
   DelayMS(10)                // time for reset to finish
#elseif GLCD_MODE = 8080
   RES = 1
   RD = 1                     
   WR = 1
   A0 = 0
   CS = 1
   DelayMS(GLCDDelay)         // start up delay, allow GLCD to settle
   RES = 0                    // RST = 0 to reset 
   DelayMS(5)                 // 1 ms minimum, 5ms for safety
   RES = 1
   DelayMS(5)                 // 3 ms minimum, 5ms for safety
   CS = 1
#endif
   Command(cmdSystem)         // execute 'system set' instruction
   DelayMS(1)
   SetData($30)               // control byte sets internal char gen (see DS) 
   SetData($87)               // FX - horizontal char size - 1 (see DS)
   SetData($07)               // FY - vertical char size - 1
   SetData($27)               // C/R - bytes per line horizintally - 1
   SetData($42)               // TC/R - provides overscan idle time (see DS)
   SetData($EF)               // LF - height of frame in lines - 1 ($EF = 239)
   SetData($28)               // AP[L] - low byte of AP (horizontal address of virtual screen)
   SetData($00)               // AP[H] - high byte of AP (horizontal address of virtual screen)
   Command(cmdScroll)         // sets scroll address and lines per scroll block
   SetData($00)               // graphics layer 1 start[L] ($0000)
   SetData($00)               // graphics layer 1 start[H] 
   SetData($EF)               // graphics layer 1 lines per block - 1 ($EF = 239)
   SetData($80)               // graphics layer 2 start[L] ($2580 = 9600 = 320 x 240 / 8)
   SetData($25)               // graphics layer 2 start[H]
   SetData($EF)               // graphics layer 2 lines per block - 1 ($EF = 239)
   SetData($00)               // graphics layer 3 start[L] ($4B00 = 19200 = 9600 + 320 x 240 / 8)
   SetData($4B)               // graphics layer 3 start[H]
   SetData($00)               // no graphics layer 4
   SetData($00)
   Command(cmdHDotScroll)     // allow screen to scroll by pixels
   SetData($00)               // set to zero pixels
   Command(cmdOverlay)        // controls how graphics layers are combined
   SetData($1C)               // set to priority OR L1 > L2 > L3, all graphics layers
   Command(cmdDisplayOff)     // command for screen off...
   SetData($54)               // but sets screens 1 - 3 for turning on later
   Command(cmdCsrForm)        // select cursor size and shape
   SetData($00)               // cursor width - 1
   SetData($80)               // cursor height - 1 (plus block style)
   Command(cmdCsrDirRight)    // set cursor to increment to right
   CsrMemPosition(0)          // cursor at start of graphics layer 1
   Command(cmdDisplayWrite)   // set to write to display memory
   For i = 1 To 28800         // clear layers 1 - 3 of display memory
      SetData($00)           
   Next
   CsrMemPosition(0)          // cursor at start of graphics layer 1
   Command(cmdDisplayOn)      // command for screen on...
   SetData($54)               // for screens 1 - 3
   Layers = %01010100         // default to all three graphics layers on (1 - 3)
   SwitchLayer(1)             // start with layer 1 as drawing layer
End Sub 


// configure the module
Initialize