;=======================================================================

; TM1638cc.inc - s'AVR version by EH 01-AUG-2017

; Library to drive the special circuit for LED control TM1638

; from Titan Micro Electronics (TM)

;

; written by Ralf Jardon (xxxxxxxx@xxx.xxx), May-July 2017

;

; Comments related to the datasheet refer to version 1.3 (en)

;

; License: GNU GENERAL PUBLIC LICENSE, Version 3, 29 June 2007

;

; Version: 0.1

;=======================================================================

 

;=======================================================================

; TM1638_INIT:

;=======================================================================

 

TM1638_INIT:

                                        ; initialize Ports

 

    ldi     AKKU, (1<<STB_PIN) | (1<<CLK_PIN) | (1<<DATA_PIN)

    out     DDR_TM1638, AKKU

 

    ldi     AKKU, (1<<STB_PIN) | (1<<CLK_PIN)

    out     PORT_TM1638, AKKU

    rcall   delay1us

 

#if !defined(BITBANGING)                ; initialize SPI-engine

 

    ldi     AKKU, (0<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<DORD)|(1<<CPOL)|(1<<CPHA)|(1<<SPR1)|(1<<SPR0)

    out     SPCR, AKKU

    ldi     AKKU, (1<<SPI2X)

    out     SPSR, AKKU

 

#endif 

                                        ; initialize TM1638

 

    ldi     TM1638_DATA_BYTE, DATA_CMD | WRITE_DATA

    rcall   TM1638_SEND_COMMAND

    ldi     TM1638_DATA_BYTE, DISP_CTRL_CMD | DISP_ON | DISP_PWM_MASK

    rcall   TM1638_SEND_COMMAND

    rcall   TM1638_CLEAR                ; clear Display Memory

    ret

 

;=======================================================================

; TM1638_SEND: generates CLOCK signal and send DATA (bit-banging).

;=======================================================================

 

#ifdef BITBANGING                   ; BITBANGING

 

TM1638_SEND:

    push    COUNT

    FOR COUNT := #8

        rcall   delay1us

        ror TM1638_DATA_BYTE        ; put lowest bit into carry flag

        IF NC

            TM1638_CLK_LOW_DATA_LOW ; carry is not set -> DATA pin LOW

        ELSE

            TM1638_CLK_LOW_DATA_HIGH    ; if carry set  -> DATA pin HIGH

        ENDI

        rcall   delay1us

        TM1638_CLK_HIGH             ; CLOCK pin HIGH

    ENDF                            ; next bit

    pop     COUNT

    ret

 

#else                               ; HARDWARE-SPI

 

TM1638_SEND:                        ; start SPI transfer

    out     SPDR, TM1638_DATA_BYTE  ; write Data into SPI-engine

    REPEAT

    UNTIL   %SPSR,SPIF              ; Wait for transmission complete

    ret

 

#endif

 

;=======================================================================

; TM1638_SEND_COMMAND: Sends Command to TM1638

;=======================================================================

 

TM1638_SEND_COMMAND:

    TM1638_STB_LOW                  ; set STROBE-output LOW, 

                                    ; ready to send commands or data

    rcall   TM1638_SEND             ; send 1 Byte command

    rcall   delay1us

    TM1638_STB_HIGH                 ; set STROBE-output HIGH, 

                                    ; command received

    rcall   delay1us

    ret

 

;=======================================================================

; TM1638_SEND_DATA: Sends DATA to TM1638

; (see datasheet V1.3, page 10 )

;=======================================================================

 

TM1638_SEND_DATA:

    ldi     TM1638_DATA_BYTE, DATA_CMD | FIXED_ADDR ; = 0x44

    rcall   TM1638_SEND_COMMAND         ; send Command 1

    rcall   delay1us

    TM1638_STB_LOW

    ori     TM1638_GRID_BYTE, ADDR_CMD  ; build segment address (0xC0)

    mov     TM1638_DATA_BYTE, TM1638_GRID_BYTE

    rcall   TM1638_SEND                 ; send Command 2 + address

    mov     TM1638_DATA_BYTE, TM1638_SEGM_BYTE

    rcall   TM1638_SEND                 ; send data

    TM1638_STB_HIGH

    ret

 

;=======================================================================

; TM1638_CLEAR: clears the Display memory and the Grid memory

; to prevent garbage after startup

;=======================================================================

 

TM1638_CLEAR:

    push    COUNT

    clr     COUNT

    REPEAT

        mov     TM1638_GRID_BYTE, COUNT ; Grid

        ldi     TM1638_SEGM_BYTE, 0x00  ; Segment

        rcall   TM1638_SEND_DATA

        subi    COUNT, -2               ; next address

                                        ; (+2, SEG9 and SEG10 not used)

    UNTIL COUNT > #REG_MAX              ; REG_MAX = 0x0F

    pop     COUNT

    ret

 

;=======================================================================

; TM1638_PRINT_DEC 16 bit - leading zeros are suppressed (using T flag)

; AKKU2 (low byte) / AKKU3 (high byte)

;=======================================================================

 

TM1638_PRINT_DEC:

    push    AKKU

    push    AKKU2

    push    AKKU3

 

; ** 10000 **

    ldi     AKKU, '0'

    REPEAT

        inc     AKKU

        subi    AKKU2, low(10000)

        sbci    AKKU3, high(10000)

    UNTIL C

    subi    AKKU2, low(-10000)

    sbci    AKKU3, high(-10000)

    dec     AKKU            ; correct last inc

    clt                     ; clr T flag: prepare decimal place is 0

    IF  AKKU <> #'0'

        mov     TM1638_SEGM_BYTE, AKKU

        ldi     TM1638_GRID_BYTE, 0x06          

        rcall   TM1638_PRINT_CHAR

        set                 ; set T flag: decimal place was 1-9

    ENDI

 

; ** 1000 **

    ldi     AKKU, '0'       ; prepare AKKU = '0'

    REPEAT

        inc     AKKU

        subi    AKKU2, low(1000)

        sbci    AKKU3, high(1000)

    UNTIL C

    subi    AKKU2, low(-1000)

    sbci    AKKU3, high(-1000)

    dec     AKKU            ; correct the last inc

 

; structured version by EH

    IF AKKU <> #'0'

        set             ; no longer leading zero,

                        ; otherwise T stays clear

    ENDI

    IF T

        mov     TM1638_SEGM_BYTE, AKKU

        ori     TM1638_SEGM_BYTE, 0x80  ; EH: add DP

        ldi     TM1638_GRID_BYTE, 0x08

        rcall   TM1638_PRINT_CHAR

    ENDI

 

; ** 100 **

    ldi     AKKU, '0'       ; prepare AKKU = '0'

    REPEAT

        inc     AKKU

        subi    AKKU2, low(100)

        sbci    AKKU3, high(100)

    UNTIL C

    subi    AKKU2, -100

    dec     AKKU            ; correct the last inc

    IF AKKU <> #'0'

        set             ; no longer leading zero,

                        ; otherwise T stays clear

    ENDI

    IF T

        mov     TM1638_SEGM_BYTE, AKKU

        ldi     TM1638_GRID_BYTE, 0x0A

        rcall   TM1638_PRINT_CHAR

    ENDI

 

; ** 10 **

    ldi     AKKU, '0'       ; prepare AKKU = '0'

    REPEAT

        inc     AKKU

        subi    AKKU2, 10

    UNTIL C

 

    subi    AKKU2, -10

    dec     AKKU            ; correct last inc

    IF AKKU <> #'0'

        set             ; no longer leading zero,

                        ; otherwise T stays clear

    ENDI

    IF T                    ; new test if T changed inbetween

        mov     TM1638_SEGM_BYTE, AKKU

        ldi     TM1638_GRID_BYTE, 0x0C

        rcall   TM1638_PRINT_CHAR

    ENDI

 

; ** 1 **

    ldi     AKKU, '0'       ; prepare AKKU = '0'

    add     AKKU, AKKU2     ; add remainder

    mov     TM1638_SEGM_BYTE, AKKU

    ldi     TM1638_GRID_BYTE, 0x0E

    rcall   TM1638_PRINT_CHAR

    set

    pop     AKKU3

    pop     AKKU2

    pop     AKKU

    ret

 

;=======================================================================

; TM1638_PRINT_HEX 8 bit from AKKU

; EH: fully structured now

;=======================================================================

 

TM1638_PRINT_HEX:

    push    AKKU

    swap    AKKU

    ldi     TM1638_GRID_BYTE, 0x0C

    rcall   hex_digit

    swap    AKKU

    ldi     TM1638_GRID_BYTE, 0x0E

    rcall   hex_digit

    pop     AKKU

    ret

 

hex_digit:

    push    AKKU

    andi    AKKU, $0F

    IF  AKKU >= #10

        subi    AKKU, -('A'-'9'-1)

    ENDI

    subi    AKKU, -'0'

    mov     TM1638_SEGM_BYTE, AKKU

    rcall   TM1638_PRINT_CHAR

    pop     AKKU

    ret

 

;=======================================================================

; TM1638_PRINT_BIN 8 bit from AKKU

;=======================================================================

; s'AVR version is more clear and efficient compared to original version

; EH: DP between position 4 and 3 added to split the nibbles

; REPEAT loop replaced by FOR loop

;=======================================================================

 

TM1638_PRINT_BIN:

    push    AKKU

    push    AKKU2

    push    COUNT

    mov     AKKU2, AKKU;

    ldi     TM1638_GRID_BYTE, 0x00      ; segment start address   

    FOR COUNT := #8

        rol     AKKU2;

        IF NC

            ldi     AKKU, '0'

        ELSE

            ldi     AKKU, '1'

        ENDI

        mov     TM1638_SEGM_BYTE, AKKU

        IF COUNT == #5

            ori     TM1638_SEGM_BYTE,0x80   ; add DP to split nibbles

        ENDI

        rcall   TM1638_PRINT_CHAR

        subi    TM1638_GRID_BYTE, -2    ; next segment address

    ENDF

    pop     COUNT

    pop     AKKU2

    pop     AKKU

    ret

 

;=======================================================================

; TM1638_PRINT_CHAR: prints a single character using the FONT table.

; Char is given by TM1638_SEGM_BYTE, Position is given by TM1638_GRID_BYTE

; DP support added by EH: Bit 7 is marking a DP to be printed as well

;=======================================================================

 

TM1638_PRINT_CHAR:

    push    ZH

    push    ZL

    push    AKKU

    push    TM1638_SEGM_BYTE

    ldi     ZL, LOW(2*FONTS)        ; start address of FONTS

    ldi     ZH, HIGH(2*FONTS)

    IF  TM1638_SEGM_BYTE,7         ; DP marking?

        andi    TM1638_SEGM_BYTE, 0x7f  ; mask off DP

        ldi     AKKU,1              ; remember the DP

    ELSE

        clr AKKU

    ENDI

    subi    TM1638_SEGM_BYTE, ASCII_OFFSET; - 0x30

    IF TM1638_SEGM_BYTE >= #0x1A

        subi    TM1638_SEGM_BYTE, CHAR_OFFSET   ; if char, sub 7

    ENDI

    add     ZL, TM1638_SEGM_BYTE    ; select char address

    IF C                            ; EH: added carry

        inc ZH

    ENDI

    lpm     TM1638_SEGM_BYTE,Z      ; simplified by EH

    IF AKKU == #1

        ori TM1638_SEGM_BYTE, 0x80  ; add DP segment to char

    ENDI

    rcall   TM1638_SEND_DATA        ; send data

    pop     TM1638_SEGM_BYTE

    pop     AKKU

    pop     ZL

    pop     ZH

    ret

 

;=======================================================================

; TM1638_PRINT_TXT8, print 8 characters, block size is 10 (due to EOT)

; AKKU3 = Textblock Number, 0x00 = EOT

; subroutine added by EH

;=======================================================================

 

TM1638_PRINT_TXT8:

    push    COUNT

    push    ZL

    push    ZH

    ldi     ZL, LOW(TEXT10*2)       ; load text start address

    ldi     ZH, HIGH(TEXT10*2)

    ldi     TM1638_GRID_BYTE, 0x00  ; first segment

    WHILE AKKU3 <> #0

        adiw    ZL, TEXT_BLK10      ; each TEXT_BLOCK has 10 chars

                                    ; calculate address of current block

        dec     AKKU3

    ENDW

    FOR COUNT := #8

        lpm     TM1638_SEGM_BYTE, Z+    ; load first char of current block

        EXITIF TM1638_SEGM_BYTE == #0   ; early EOT?

        rcall   TM1638_PRINT_CHAR       ; print char

        subi    TM1638_GRID_BYTE, -2    ; go to next segment address

    ENDF

    pop     ZH

    pop     ZL

    pop     COUNT

    ret

 

;=======================================================================

; TM1638_PRINT_TXT35, print 35 characters, block size is 36 (due to EOT)

; AKKU3 = Text block Number, 0x00 = EOT

; EH: replaced LOOP/ENDL by FOR/ENF

;=======================================================================

 

TM1638_PRINT_TXT35:

    push    AKKU

    push    ZL

    push    ZH

    push    COUNT

    ldi     ZL, LOW(MOVETEXT*2)     ; load text start address

    ldi     ZH, HIGH(MOVETEXT*2)

    ldi     TM1638_GRID_BYTE, 0x00  ; first segment

    WHILE AKKU3 <> #0

        adiw    ZL, TEXT_BLK36      ; each TEXT_BLOCK has 36 chars

                                    ; calculate address of current block

        dec     AKKU3

    ENDW

    FOR COUNT := #35

        lpm     AKKU, Z+            ; load first char of current block

        EXITIF AKKU == #0           ; early EOT ?

        mov     TM1638_SEGM_BYTE, AKKU

        rcall   TM1638_PRINT_CHAR   ; print char

        subi    TM1638_GRID_BYTE, -2    ; go to next segment address

    ENDF

    pop     COUNT

    pop     ZH

    pop     ZL

    pop     AKKU

    ret

;=======================================================================

; TM1638_PRINT_MOVETEXT, move 35 characters, block size is 36 (due to EOT)

; AKKU3 = Text block Number, 0x00 = EOT

; REPEAT loop replaced by FOR loop

;=======================================================================

 

TM1638_PRINT_MOVETEXT:

    push    ZL

    push    ZH

    push    COUNT

    push    AKKU2

    push    AKKU

    ldi     ZL, LOW(MOVETEXT*2)     ; load text start address

    ldi     ZH, HIGH(MOVETEXT*2)

    WHILE AKKU3 <> #0

        adiw    ZL, TEXT_BLK36      ; each TEXT_BLOCK has 36 chars

                                    ; calculate address of current block

        dec     AKKU3

    ENDW

    clr     TM1638_GRID_BYTE   

    clr     COUNT

    clr     AKKU2

    REPEAT

        FOR COUNT := #8

            lpm     AKKU, Z+        ; load first char of current block

            EXITIF AKKU == #0       ; EH: earlier EOT

            mov     TM1638_SEGM_BYTE, AKKU

            rcall   TM1638_PRINT_CHAR   ; print current char

            subi    TM1638_GRID_BYTE, -2    ; go to next segment address

        ENDF

        EXITIF AKKU == #0           ; EH: another EXITIF required

        rcall   Delay100ms          ; text loop

        rcall   Delay100ms

        sbiw    ZL, 7               ; add 7 (8-1) to shift display

        inc     AKKU2

    UNTIL AKKU2 >= #TEXT_BLK36-8

    pop     AKKU

    pop     AKKU2

    pop     COUNT

    pop     ZH

    pop     ZL

    ret

 

;=======================================================================

; TM1638_BRIGHTNESS

; AKKU = value between 0-7

;=======================================================================

 

TM1638_BRIGHTNESS:

    ldi     TM1638_DATA_BYTE, DISP_CTRL_CMD | DISP_ON

    add     TM1638_DATA_BYTE, AKKU

    rcall   TM1638_SEND_COMMAND

    ret