' ***********************************************************************************
' v20260908 - PIC18 simulation/test utility - by COS - dogflu66@yahoo.es.
' Utility to simplify the creation and verification of characters and symbols for a
' 4-digit LED module based on the VK16K33. The resulting segment mask is displayed
' through both the serial console and a KS0108 GLCD for visual verification.
' ***********************************************************************************
' Hardware tested on the COSKIT23A11 board with a 128x64 KS0108-based GLCD.
' The compiler native GLCD functions are used; operation has been tested up to 8 MHz.
' ***********************************************************************************
' Fuses definition.
#define CONFIG1L = 0x00
#define CONFIG1H = 0x38
#define CONFIG2L = 0x1E
#define CONFIG2H = 0x3C
#define CONFIG3L = 0x00
#define CONFIG3H = 0xBF
#define CONFIG4L = 0x80
#define CONFIG4H = 0x00
#define CONFIG5L = 0x0F
#define CONFIG5H = 0xC0
#define CONFIG6L = 0x0F
#define CONFIG6H = 0xE0
#define CONFIG7L = 0x0F
#define CONFIG7H = 0x40

#define CLOCK_FREQUENCY = 8 ' System clock: 8 MHz
#define STRING_MAX_LENGTH = 40
#define SINGLE_DECIMAL_PLACES = 1
#define SIMULATION_WAITMS_VALUE = 0 ' Compile for simulation
' *************************************************
' *************************************************
Include "_Pic18F46K22Lib.bas"
Include "CLib.bas"
Include "Font_01.bas"
' *************************************************
' Main:
' Internal oscillator: 8 MHz.
Call _Setup_Oscillator(_OSC_8MHz_HF)
'Call _SET_PLL4(_PLL_ON)
Call _Interrupts_Mode(_DISABLE_PRIORITY)
'Call _SlewRate(_Disable) 'Enabled by default.
' **************************************************
AllDigital ' Configure I/O
TRISA = 0xFF
TRISB = 0xFF
TRISC = 0xFF
TRISD = 0xFF
TRISE = 0xFF
' **************************************************
' GLCD interface configuration (KS0108).
Define GLCD_DREG = 	 PORTD
Define GLCD_RSREG =  PORTB
Define GLCD_RSBIT =  4
Define GLCD_EREG = 	 PORTB
Define GLCD_EBIT = 	 5
Define GLCD_RWREG =  PORTB
Define GLCD_RWBIT =  3
Define GLCD_CS1REG = PORTB
Define GLCD_CS1BIT = 1
Define GLCD_CS2REG = PORTB
Define GLCD_CS2BIT = 2
ConfigPin RB0 = 	 Output
Symbol GLCD_RESET =  RB0
GLCD_RESET = 0
WaitMs 10
GLCD_RESET = 1
WaitMs 5
GLcdinit  ' Initialize GLCD.
WaitMs 5
' ***************************************************
' CCP5 -> Timer2 -> LATE.2: PWM control for backlight and LEDs.
Call _Setup_TMR2(TMRx_ON | TMRx_PRS_16, 255)
Call _Setup_CCP5(_ECCP_PWM) ' PWM mode.
Call _Setup_CCP_4_5_Timers(_CCP5_TIMER1_TIMER2)
Call _set_pwm5_duty(300)
TRISE.2 = 0
' ***************************************************
' RS-232 serial interface.
UART1_Init 115200
'UART2_Init 4800
WaitMs 1
' ***************************************************
UART1_Write 0x0C
WaitMs 100
UART1_Write "Ready", CrLf
WaitMs 100

' Segment mapping for the tested VK16K33-based module.
' nc = not connected.
Const  dp  = 0x4000
Const _l   = 0x2000
Const _m   = 0x1000
Const _n   = 0x0800
Const _j   = 0x0400
Const _h   = 0x0200
Const _g   = 0x0100
Const _k   = 0x0080
Const _p   = 0x0040
Const _f   = 0x0020
Const _e   = 0x0010
Const _d   = 0x0008
Const _c   = 0x0004
Const _b   = 0x0002
Const _a   = 0x0001

Dim mask As Word
Dim maskStr[6] As String
Dim maskbitStr[16] As String
Dim INK As Bit
Dim i As Byte

' **********************************************************************************
' **********************************************************************************
' **********************************************************************************
'
' 15-segment layout used by the tested LED module.
'
' Physical order of the bits from Bit 15 To Bit 0:
'
'   Bit:     15  14  13  12  11  10   9   8  -  7   6   5   4   3   2   1   0
'   Segment: nc  dp   l   m   n   j   h   g  -  k   p   f   e   d   c   b   a
'   nc: not connected.
'
'     		   	    nc-dp-l-m-n-j-h-g - K-p-f-e-d-c-b-a
'
'       			                 a
' 	 					    *******************
' 	 					    * *      *      * *
'						    *  *     *     *  *
'		    			  f	*   g    h    j   * b
'						    *    *   *   *    *
'       			        *     *  *  *     *
'       			         ***p***   ***k***
' 						    *     *  *  *     *
' 						    *    *   *   *    *
' 					      e	*   n    m    l   * c
'       			        *  *     *     *  *
'           			    * *      *      * *
'		 			        ******************* * dp
'                   			     d
'
' ***********************************************************************************
' Example for character "8" (0x00FF):
' 8 -> mask: 0x00FF
' nc dp  l  m  n  j  h  g  k  p  f  e  d  c  b  a
'  0  0  0  0  0  0  0  0  1  1  1  1  1  1  1  1

maskStr = "0x" + HexStr(mask =  _a | _b | _c | _d | _e | _f | _p | _k) ' "8", 0x00FF

'maskStr = "0x" + HexStr(mask = _g | _h | _j | _n | _m | _l | _p | _k)  ' "*", 0x3FC0
'maskStr = HexStr(mask = 0x203F) ' "Q"

' Output through the RS-232 serial port.
UART_Write "Mask: ", maskStr, CrLf
UART_Write "Segments: nc dp  l  m  n  j  h  g  k  p  f  e  d  c  b  a", CrLf
UART_Write "Bits:      "
maskbitStr = Get_ToBin(mask, 16)
CFor (i = 0; i <= 15; ++i)
	UART_Write maskbitStr(i), "  "
CNext

' Graphical representation on the GLCD.
Draw_Text_HW(0, 0, "Mask: " + maskStr, 1, 1)
INK = 1
VK16K33_DrawDigit(0, mask)

'VK16K33_DrawDigit(2, 0x2470) ' "K"
'VK16K33_DrawDigit(3, _f | _e | _p | _j | _l) ' "K"

' ***************************************************************************************
' ***************************************************************************************
' ***************************************************************************************

End
' FUNCTIONs
'----------------------------------------------------------------------------------------------
' Virtual digit geometry.
Const VK16K33_X0 = 2
Const VK16K33_Y0 = 10
Const VK16K33_X_STEP = 31
Const GLCD_WIDTH = 128
Const GLCD_HIGH = 64
'----------------------------------------------------------------------------------------------
' Routine:      VK16K33_DrawDigit
' Description:  Draws a complete virtual digit from a 16-bit VK16K33 segment mask.
'               Each active mask bit is drawn and each inactive bit is cleared.
' Inputs:       pos  = virtual digit position on the GLCD (0..3)
'               mask = segment mask using bits 0..14; bit 15 is not connected
' Returns:      Byte function used as a procedure; no explicit return value required.
'----------------------------------------------------------------------------------------------
Function VK16K33_DrawDigit(pos As Byte, mask As Word) As Byte
	Dim i As Byte
	For i = 0 To 15
		If mask.i = 1 Then
			INK = 1
		Else
			INK = 0
		Endif
		VK16K33_DrawSegment(pos, i)
	Next i
End Function

'----------------------------------------------------------------------------------------------
' Routine:      VK16K33_DrawSegment
' Description:  Draws or clears one segment of the virtual digit according to INK.
' Inputs:       pos     = virtual digit position on the GLCD (0..3)
'               segment = segment bit/index (0..14)
' Notes:        Segment 15 (nc) is intentionally ignored because it is not connected.
'----------------------------------------------------------------------------------------------
Function VK16K33_DrawSegment(pos As Byte, segment As Byte) As Byte
    Dim x As Byte
    Dim y As Byte

    x = VK16K33_X0 + pos * VK16K33_X_STEP
    y = VK16K33_Y0

    Select Case segment
        Case 0    ' a1 a2
            VK16K33_HLine(x + 3, x + 12, y + 1)
            VK16K33_HLine(x + 16, x + 25, y + 1)
        Case 1    ' b
            VK16K33_VLine(x + 27, y + 3, y + 23)
		Case 2    ' c
            VK16K33_VLine(x + 27, y + 27, y + 47)
        Case 3    ' d1 d2
            VK16K33_HLine(x + 3, x + 12, y + 49)
            VK16K33_HLine(x + 16, x + 25, y + 49)
        Case 4    ' e
            VK16K33_VLine(x + 1, y + 27, y + 47)
		Case 5	  ' f
			VK16K33_VLine(x + 1, y + 3, y + 23)
        Case 6    ' p
            VK16K33_HLine(x + 3, x + 12, y + 25)
        Case 7    ' k
            VK16K33_HLine(x + 16, x + 25, y + 25)
        Case 8    ' g
            Draw_Line(x + 3, y + 3, x + 12, y + 23)
		Case 9    ' h
            VK16K33_VLine(x + 14, y + 4, y + 23)
		Case 10   ' j
            Draw_Line(x + 25, y + 3, x + 16, y + 23)
		Case 11	  ' n
			Draw_Line(x + 3, y + 47, x + 12, y + 28 )
        Case 12   ' m
            VK16K33_VLine(x + 14, y + 27, y + 46)
        Case 13   ' l
            Draw_Line(x + 16, y + 28, x + 25, y + 47)
		Case 14   ' dp
			VK16K33_HLine(x + 28, x + 29, y + 48)
			VK16K33_HLine(x + 28, x + 29, y + 49)
    EndSelect
End Function

'-----------------------------------------------------------------------------
' Primitive drawing helpers used by the virtual segment display.
'-----------------------------------------------------------------------------
Function VK16K33_Pixel(x As Byte, y As Byte) As Byte
    If INK = 1 Then
		If x < 128 And y < 64 Then GLcdpset x, y
	Else
		If x < 128 And y < 64 Then GLcdpreset x, y
	Endif
End Function
' Draws a horizontal line using the current INK state.
Function VK16K33_HLine(x1 As Byte, x2 As Byte, y As Byte) As Byte
    Dim x As Byte
    If x2 < x1 Then Exit
    CFor (x = x1; x <= x2; ++x)
        VK16K33_Pixel(x, y)
    CNext
End Function
' Draws a vertical line using the current INK state.
Function VK16K33_VLine(x As Byte, y1 As Byte, y2 As Byte) As Byte
    Dim y As Byte
    If y2 < y1 Then Exit
    CFor (y = y1; y <= y2; ++y)
        VK16K33_Pixel(x, y)
    CNext
End Function

'-------------------------------------------------------------------------
' Routine:      Draw_Line
'               Draws a line on a graphic LCD using Bresenham
' Inputs:       x1,y1  = starting point
'               x2,y2  = ending point
'               color  = ON (1) - OFF (0) to turn each pixel on or off
'-------------------------------------------------------------------------
Function Draw_Line(x1 As Word, y1 As Word, x2 As Word, y2 As Word) As Byte', color As Bit) As Byte

   '-----------------------Variable declaration -------------------
   Dim dx, dy  As Word                      ' Absolute distances in X and Y
   Dim incx, incy As Short                  ' Increment (+1) or decrement (−1)
   Dim err, diff As Integer                 ' Decision parameter and helper
   Dim i As Word                            ' Loop counter

   ' Initial values ----------------------------------------------------
   incx = 1                                 ' Assume moving to the right…
   incy = 1                                 ' …and downward (usual coordinate system)
   i = 0                                    ' Counter start

   dx = Abs(CInteger x2 - x1)               ' ΔX = |x2 − x1|
   dy = Abs(CInteger y2 - y1)               ' ΔY = |y2 − y1|

   If x1 > x2 Then incx = -1                ' Adjust direction in X
   If y1 > y2 Then incy = -1                ' Adjust direction in Y

   '----------------------------Branch 1 ---------------------------------
   ' Case: slope |m| ≤ 1  →  X is the dominant axis
   If dx >= dy Then
      dy *= 2                               ' Avoid fractions: 2·dy
      err   = dy - dx                       ' Initial error   P = 2·dy - dx
      diff = err - dx                       ' diff = P - dx = 2·dy - 2·dx

      CFor (i = 0; i <= dx; ++i)            ' Iterate for as many steps as ΔX
         'ST7920_Pixel(x1, y1, color)          ' Draw current pixel
		 VK16K33_Pixel(x1, y1)
		 'FB_Pixel(x1, y1, color)

         If err < 0 Then                    ' Still below the ideal line
            err += dy                       ' Correct error with 2·dy only
            x1 += incx                      ' Move 1 px in X
         Else                               ' We cross/touch the ideal line
            err += diff                     ' Correct with (2·dy - 2·dx)
            x1 += incx                      ' Move 1 px in X
            y1 += incy                      ' Move 1 px in Y
         Endif
      CNext

   '----------------------------Branch 2 ---------------------------------
   ' Case: slope |m|  > 1 →  Y is the dominant axis
   Else
      dx *= 2                               ' 2·dx to avoid division
      err  = dx - dy                        ' P = 2·dx - dy
      diff = err - dy                       ' diff = 2·dx - 2·dy

      CFor (i = 0; i <= dy; ++i)            ' Iterate for as many steps as ΔY
         'ST7920_Pixel(x1, y1, color)       ' Draw pixel
		 VK16K33_Pixel(x1, y1)
		 'FB_Pixel(x1, y1, color)


         If err < 0 Then                    ' Still within the error band
            err += dx                       ' Correct with 2·dx
            y1 += incy                      ' Move 1 px in Y
         Else
            err += diff                     ' Correct with (2·dx - 2·dy)
            x1 += incx                      ' Also move in X
            y1 += incy                      ' …and in Y
         Endif
      CNext
   Endif

End Function

'------------------------------------------------------------------------
' Routine:      Draw_Text_HW
'               Prints a string on the GLCD while allowing independent
'               “zoom” factors for width (horizontal) and height (vertical).
'
' Inputs:       text_Str[]  → ASCII text (max. 40 characters)
'               x , y       → upper-left corner of the first character
'               height        → vertical scale factor   (1 = 5×7 normal)
'               width       → horizontal scale factor (1 = 5×7 normal)
'               color       → pixel state: ON (1) - OFF (0)
'
' Returns:      Return.HB   → X coordinate of the last pixel drawn
'               Return.LB   → Y coordinate of the last pixel drawn
'------------------------------------------------------------------------
Function Draw_Text_HW(x As Byte, y As Byte, text_Str[40] As String, height As Byte, width As Byte) As Word

    '----------------- Working variables ------------------------

    Dim i As Byte            ' column 0..4 (+ sep col=5)
    Dim row As Byte          ' row 0..6 (row 7 = spacing)
    Dim k As Byte            ' vertical zoom counter
    Dim xx As Byte           ' horizontal repeat counter
    Dim sepW As Byte         ' separator width (6th column)
    Dim charW As Byte        ' total char width incl. separator
    Dim nchar As Byte        ' string index
    Dim colByte As Byte      ' current 5x7 column (in 8 bits)
    Dim yc As Byte           ' current Y
    Dim x0 As Byte           ' base X for current char
    Dim XY As Word           ' return value (HB=X, LB=Y)
    Dim pixelMask(6) As Byte ' 5 glyph columns + 1 separator (=0)
    Dim width_Cpy As Byte
    ' Normalize zooms (min = 1)
    If height = 0 Then height = 1
    If width  = 0 Then width  = 1
    width_Cpy = width
    ' If x/y already outside, exit
    If x >= GLCD_WIDTH Or y >= GLCD_HIGH Then
        XY.HB = x
        XY.LB = y
        ReturnValue XY
        Exit
    Endif
    ' Character loop
    nchar = 0
    While text_Str(nchar) <> 0
        ' Load glyph (5 cols) or leave blank if non-printable
        If text_Str(nchar) <= "~" Then
            For i = 0 To 4
                pixelMask(i) = Font_01(text_Str(nchar), i)
            Next i
        Else
            For i = 0 To 4
				pixelMask(i) = 0
            Next i
        Endif
        pixelMask(5) = 0     ' 6th col = separator
        ' Narrow chars (., :, -, space)
        If text_Str(nchar) = "." Or text_Str(nchar) = ":" Or text_Str(nchar) = "-" Or text_Str(nchar) = " " Then
            width = 1        ' only one pixel-column
        Else
            width = width_Cpy
        Endif
        ' Separator width & total char width
        If width <= 2 Then sepW = 1 Else sepW = 2
        charW = CByte(5 * width + sepW)
        ' Line break if char doesn’t fit
        If x + charW >= GLCD_WIDTH Then
            x = 0
            y = y + (7 * height) + 1
            If y >= GLCD_HIGH Then
                XY.HB = x
                XY.LB = y
                ReturnValue XY
                Exit
            Endif
        Endif
        ' Draw 5 columns + separator
        x0 = x
        For i = 0 To 5
            Dim colRepeats As Byte
            If i = 5 Then
                colRepeats = sepW
                colByte = 0
            Else
                colRepeats = width
                colByte = pixelMask(i)
                colByte.7 = 0
            Endif
            ' Repeat horizontally
            For xx = 1 To colRepeats
                ' Rows 0..6 (row 7 = vertical spacing)
                For row = 0 To 6 '7
                    yc = y + row * height
                    For k = 0 To (height - 1)
                        If x < GLCD_WIDTH And yc < GLCD_HIGH Then
                            If colByte.row Then
								INK = 1
                                VK16K33_Pixel(x, yc)
                                'FB_Pixel(x, yc, colByte.row)
                            Else
                                INK = 0
                                VK16K33_Pixel(x, yc)
                                'FB_Pixel(x, yc, !colByte.row)
                            Endif
                        Endif
                        yc = yc + 1
                    Next k
                Next row
                'If x < 255 Then x = x + 1   ' next subcolumn
                ++x
            Next xx
        Next i
        nchar = nchar + 1   ' next character
    Wend
    ' Return last position reached
    XY.HB = x
    XY.LB = y + (7 * height)
    ReturnValue XY
End Function






