' Include "TM1651Lib.bas"
' PIC18F46K22, 64MHz.
'--------------------------------------------------------------------------------------------
' By COS, 2025/01/25, PIC18 Simulator IDE V5.91
' Library to interface with the TM1651 (Charging status panel only).
'--------------------------------------------------------------------------------------------
' Important: Options menu -> Initialize variables in declaration -> not selected.
' Pin names printed on the PCB for battery level control with TM1651 -> CLK and DIO
'--------------------------------------------------------------------------------------------
' TM1651 pin configuration:
' TM1651_CLK: Pin mode set as normal I/O.
' TM1651_DIO: Pin mode set as open-drain.
' TM1651 command definitions:
' Mode command setting.
' Const TM1651_MODE = 0x40 '01000000, write mode, auto increment address
' Address command setting.
' Const TM1651_ADDRESS = 0xC0 '11000000, first digit address
' Display control command setting.
' Const TM1651_CONTROL_OFF = 0x80 '10000000, display off
' Const TM1651_CONTROL_ON = 0x88 '10001000, display on.
'---------------------------------------------------------------------------------------------
' Configuration
' Assign pin definitions
Symbol TM1651_CLK = LATB.1 		'(CLK) Assign port pin for TM1651 clock.
Symbol TM1651_DIO = LATB.2 		'(DIO) Assign port pin for TM1651 data.
Symbol TM1651_DIO_IN = PORTB.2	'(DIO) Read pin for TM1651 data input.
Symbol TM1651_CLK_IO = TRISB.1	'(CLK) Set as output.
Symbol TM1651_DIO_IO = TRISB.2	'(DIO) Set as open-drain.

' Number of display digits
Const DigitsTM1651_NUMBER = 4 'Number of digits supported by TM1651.

' Delay settings.
Const TM1651_DELAY1 = 5 ' Microseconds
Const TM1651_DELAY2 = 5 ' Microseconds

' Variable to store segment data.
Dim TM1651_Seg As Byte

'--------------------------------------------------------------------
' Set the TM1651_DIO pin as open-drain.
Function TM1651_DIO_HIGH() As Bit
    TM1651_DIO_IO = 1 ' Set as input, pulled high by internal pull-up resistor.
    ' Open-collector design requires delay for state change.
    ASM: NOP
    ASM: NOP
    ASM: NOP
    ASM: NOP
    ASM: NOP
    ASM: NOP
    ASM: NOP
    ASM: NOP
End Function

' Set the TM1651_DIO pin as output.
Function TM1651_DIO_LOW() As Bit
    TM1651_DIO_IO = 0 ' Set as output.
    TM1651_DIO = 0 ' Set pin low.
End Function

' Predefined brightness levels.
Const TM1651_LOW_BRIGHT = 0x00
Const TM1651_MID_BRIGHT = 0x02
Const TM1651_MAX_BRIGHT = 0x07

'---------------------------------------------------------------------
' Start communication with TM1651.
Function TM1651_Start() As Bit
    TM1651_CLK = 1
    TM1651_DIO_HIGH() ' Set to input (open-drain)
    WaitUs TM1651_DELAY1
    TM1651_DIO_LOW() ' Set to output (low)
End Function

' Wait for acknowledgement from TM1651.
Function TM1651_Ask() As Byte
    Dim counter As Word
    counter = 100 ' Limit waiting time.
    TM1651_DIO_HIGH()
    TM1651_CLK = 0
    WaitUs TM1651_DELAY2
    While TM1651_DIO = 1 And counter > 0
        WaitUs 1
        counter--
    Wend
    TM1651_CLK = 1
    WaitUs TM1651_DELAY2
    TM1651_CLK = 0
    If counter = 0 Then
        ReturnValue 1 ' TM1651 not responding.
    Else
        ReturnValue 0 ' TM1651 acknowledged.
    Endif
End Function

' Stop communication with TM1651.
Function TM1651_Stop() As Bit
    TM1651_CLK = 0
    WaitUs TM1651_DELAY2
    TM1651_DIO_LOW()
    WaitUs TM1651_DELAY2
    TM1651_CLK = 1
    WaitUs TM1651_DELAY2
    TM1651_DIO_HIGH()
End Function

' Initialize the display
' Example (Main): SmgDisplay() 'Inicializa el display
Function TM1651_Init() As Bit 'Write display register
    TM1651_CLK_IO = 0 'Output
    TM1651_Seg = %01111111 'All on

    TM1651_Start()
    TM1651_WriteByte(0x40) '40H address is automatically incremented by 1 mode, 44H fixed address mode
    TM1651_Ask()
    TM1651_Stop()

	TM1651_Full()

	TM1651_Bright(TM1651_MAX_BRIGHT)
End Function

' Write a byte to TM1651.
Function TM1651_WriteByte(oneByte As Byte) As Bit
    Dim i As Byte
    CFor (i = 0; i < 8; i++)
        TM1651_CLK = 0
        If (oneByte & 0x01) Then
            TM1651_DIO_HIGH()
        Else
            TM1651_DIO_LOW()
        Endif
        WaitUs TM1651_DELAY1
        oneByte = oneByte >> 1
        TM1651_CLK = 1
        WaitUs TM1651_DELAY1
    CNext
    TM1651_CLK = 0
End Function

' Illuminates a certain segment (segment: 0 to 3, 1 On/0 Off).
Function TM1651_OutSeg(Seg As Byte, sBit As Bit) As Bit
    TM1651_Start()
    TM1651_WriteByte(0xC0) 'Set the first address
    TM1651_Ask()
    TM1651_Seg.Seg = sBit
    TM1651_WriteByte(TM1651_Seg) 'Send data
    TM1651_Ask()
    TM1651_Stop
End Function

' All segments Off.
Function TM1651_Clear() As Bit
    TM1651_Seg = 0
    TM1651_Start()
    TM1651_WriteByte(0xC0) 'Set the first address
    TM1651_Ask()
    TM1651_WriteByte(TM1651_Seg) 'Send data
    TM1651_Ask()
    TM1651_Stop
End Function

' All segments On.
Function TM1651_Full() As Bit
    TM1651_Seg = %01111111
    TM1651_Start()
    TM1651_WriteByte(0xC0) 'Set the first address
    TM1651_Ask()
    TM1651_WriteByte(TM1651_Seg) 'Send data
    TM1651_Ask()
    TM1651_Stop
End Function

'Set brightness of the display segments.
Function TM1651_Bright(Bright As Byte) As Bit
    'Initialization slightly
    TM1651_Start()
    TM1651_WriteByte(0x88 Or Bright)
    TM1651_Ask()
    TM1651_Stop()
End Function
