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

; tm1638cc.s

; s'AVR version by EH 27-AUG-2017

; includes support for up to 8 key buttons

; demo now including a 8*100 hrs elapsed timer and a realtime clock

;

; s'AVR version tested using LED&KEY board

;

; Examples of usage for tm1638 library

; 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: 1.1

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

 

;.NOLIST

 

.INCLUDE "tm1638cc_EH.def"    ; various definitions, originally tm1638cc.h

.INCLUDE "tm1638cc.mac"       ; TM1638 macros for the SPI interface

 

.LIST

.LISTMAC

 

.CSEG

 

.ORG $0000                ;  Start of program code after Reset

 

; Reset and Interrupt Vectors (two address locations each!):

; ATmega328P can use absolute JMP instructions instead of RJMP!!!

; Otherwise a NOP is required after RJMP (or after RETI).

 

       rjmp   init       ;  1 RESET     External Pin, Power-on Reset,

       nop                ;  Brown-out Reset and Watchdog System Reset

       reti               ;  2 INT0addr  INT0 External Interrupt Request 0

       nop

       reti               ;  3 INT1addr  INT1 External Interrupt Request 1

       nop

       reti               ;  4 PCI0addr  PCINT0 Pin Change Interrupt Request 0

       nop

       reti               ;  5 PCI1addr  PCINT1 Pin Change Interrupt Request 1

       nop

       reti               ;  6 PCI2addr  PCINT2 Pin Change Interrupt Request 2

       nop

       reti               ;  7 WDTaddr   WDT Watchdog Time-out Interrupt

       nop

       reti               ;  8 OC2Aaddr  TIMER2 COMPA Timer/Counter2 Compare Match A

       nop

       reti               ;  9 OC2Baddr  TIMER2 COMPB Timer/Counter2 Compare Match B

       nop

       reti               ; 10 OVF2addr  TIMER2 OVF Timer/Counter2 Overflow

       nop

       reti               ; 11 ICP1addr  TIMER1 CAPT Timer/Counter1 Capture Event

       nop

       rjmp   time10ms   ; 12 OC1Aaddr  TIMER1 COMPA Timer/Counter1 Compare Match A

       nop                ;

       reti               ; 13 OC1Baddr  TIMER1 COMPB Timer/Counter1 Compare Match B

       nop

       reti               ; 14 OVF1addr  TIMER1 OVF Timer/Counter1 Overflow

       nop

       reti               ; 15 OC0Aaddr  TIMER0 COMPA Timer/Counter0 Compare Match A

       nop

       reti               ; 16 OC0Baddr  TIMER0 COMPB Timer/Counter0 Compare Match B

       nop

       reti               ; 17 OVF0addr  TIMER0 OVF Timer/Counter0 Overflow

       nop

       reti               ; 18 SPIaddr   SPI, STC SPI Serial Transfer Complete

       nop

       reti               ; 19 URXCaddr  USART, RX USART Rx Complete

       nop

       reti               ; 20 UDREaddr  USART, UDRE USART, Data Register Empty

       nop

       reti               ; 21 UTXCaddr  USART, TX USART, Tx Complete

       nop

       reti               ; 22 ADCCaddr  ADC ADC Conversion Complete

       nop

       reti               ; 23 ERDYaddr  EE READY EEPROM Ready

       nop

       reti               ; 24 ACIaddr   ANALOG COMP Analog Comparator

       nop

       reti               ; 25 TWIaddr   TWI (I2C) 2-wire Serial Interface

       nop

       reti               ; 26 SPMRaddr  SPM READY Store Program Memory Ready

       nop

   

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

;                         INIT and MAIN LOOP

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

.ORG $100                     ; alternately .ORG    INT_VECTORS_SIZE

 

init:

    ldi    AKKU, HIGH(RAMEND) ; Initialize stack pointer

    out    SPH, AKKU

    ldi    AKKU, LOW(RAMEND)

    out    SPL, AKKU

    rcall  TM1638_INIT        ; Initialize Ports, SPI & TM1638

 

    cli                       ; prepare Timer1 interrupts (100 Hz, 10ms)

    ldi    AKKU, (1<<WGM12) | (1<<CS11) | (1<<CS10) ; CTC OCR1A, /64

    sts    TCCR1B, AKKU       ; works with most standard xtal frequencies

    ; OCR1A will trigger the 100Hz/10ms timer and reset the CTC

    ldi    AKKU, HIGH(XTAL/6400-1)   ; Compare register A high byte

    sts    OCR1AH, AKKU

    ldi    AKKU, LOW(XTAL/6400-1) ; Compare register A low byte

    sts    OCR1AL, AKKU

    ldi    AKKU, (1<<OCIE1A)  ; only 10ms interrupt

    sts    TIMSK1, AKKU       ; Enable Timer1 Compare A

    clr    BUTTONS            ; mark pushed key buttons bitwise (up to 8)

    ldi    yl,low(clk10ms)    ; begin of clock timer values ...

    ldi    yh,high(clk10ms)   ; ... 10ms/sec/min/hrs in SRAM

    clr    AKKU

    FOR COUNT := #12          ; 3x 4 bytes for clock time, elapsed time ...

       st     y+,AKKU        ; clear time locations in SRAM

    ENDF

    sei                       ; enable Interrupts

    ldi    AKKU,1

    mov    TRUE,AKKU          ; register TRUE stays always 1

 

    ldi    AKKU3, 1           ; "88888888", Text block 1

    rcall  TM1638_PRINT_TXT8

    rcall  TM1638_LEDS_BF     ; LEDs back and forth

 

    clr    AKKU3              ; "TM1638 DEMO", Text block 0

    rcall  TM1638_PRINT_MOVETEXT

    rcall  TM1638_LEDS_BF     ; LEDs back and forth

 

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

 

LOOP   ; (LOOP is not an address!)   ; Main Loop

    rcall  TM1638_CLEAR

    ldi    AKKU3, 4                  ; "TEST KEY", Text block 4

    rcall  TM1638_PRINT_TXT8

 

    LOOP                          ; TEST KEY loop

       rcall  delay500ms         ; if S1+S8 are pushed continuously

       clr    BUTTONS

       REPEAT

           rcall  TM1638_KEYPAD  ; check the TM1638 buttons

       UNTIL BUTTONS <> #0       ; wait until any button pressed

       mov    AKKU,BUTTONS       ; to check more than one button

       EXITIF AKKU == #S18       ; exit if both S1 and S8 are pressed

       rcall  TM1638_CLEAR       ; test and display all keys separately

       IF BUTTONS, S1

           ldi    TM1638_SEGM_BYTE,'1'

           ldi    TM1638_GRID_BYTE,0     ; left most digit

       ELSEIF BUTTONS, S2

           ldi    TM1638_SEGM_BYTE,'2'

           ldi    TM1638_GRID_BYTE,2     ; 2nd digit

       ELSEIF BUTTONS, S3

           ldi    TM1638_SEGM_BYTE,'3'

           ldi    TM1638_GRID_BYTE,4     ; 3rd digit

       ELSEIF BUTTONS, S4

           ldi    TM1638_SEGM_BYTE,'4'

           ldi    TM1638_GRID_BYTE,6     ; 4th digit

       ELSEIF BUTTONS, S5

           ldi    TM1638_SEGM_BYTE,'5'

           ldi    TM1638_GRID_BYTE,8     ; 5th digit

       ELSEIF BUTTONS, S6

           ldi    TM1638_SEGM_BYTE,'6'

           ldi    TM1638_GRID_BYTE,10   ; 6th digit

       ELSEIF BUTTONS, S7

           ldi    TM1638_SEGM_BYTE,'7'

           ldi    TM1638_GRID_BYTE,12   ; 7th digit

       ELSEIF BUTTONS, S8

           ldi    TM1638_SEGM_BYTE,'8'

           ldi    TM1638_GRID_BYTE,14   ; 8th digit

       ELSE

           ldi    TM1638_SEGM_BYTE,' '

           ldi    TM1638_GRID_BYTE,0     ; 1st digit

       ENDI

       rcall  TM1638_PRINT_CHAR

    ENDL

 

    LOOP                          ; now loop the various demo programs

       rcall  TM1638_CLEAR

       ldi    AKKU3, 3           ; "SELECT..", Text block 3

       rcall  TM1638_PRINT_TXT8

       rcall  delay500ms         ; if S1+S8 are pushed continuously

       clr    BUTTONS

       REPEAT

           rcall  TM1638_KEYPAD  ; check the TM1638 buttons

       UNTIL BUTTONS <> #0       ; wait until any button pressed

       EXITIF BUTTONS, S8        ; button S8 leaves demos to TEST KEY

                                 ; select demo by button S1..S7

       IF BUTTONS, S1

           FOR COUNT := #3

               rcall  TM1638_TWIST_CW    ; twist LEDs cw

           ENDF

           rcall  TM1638_CLEAR

           FOR COUNT := #3

               rcall  TM1638_TWIST_CCW   ; twist LEDs ccw

           ENDF

           rcall  TM1638_CLEAR

           rcall  TM1638_LEDS_BF         ; LEDs back and forth

       ELSEIF BUTTONS, S2

           clr    AKKU3                 ; "LEDS DIM", Text block 0

           rcall  TM1638_PRINT_TXT8 

           FOR AKKU2 := #1                ; DIM LEDs 1x (or 2x)

               FOR COUNT := #7           ; step darker

                   mov    AKKU, COUNT

                   rcall  TM1638_BRIGHTNESS

                   rcall  Delay500ms

               ENDF

               REPEAT                    ; step brighter again

                   mov    AKKU, COUNT

                   rcall  TM1638_BRIGHTNESS

                   rcall  Delay500ms

                   subi   COUNT,-1

               UNTIL COUNT > #7

           ENDF

           rcall  TM1638_LEDS_BF         ; LEDs back and forth

       ELSEIF BUTTONS, S3

           ldi    AKKU3, 1               ; Textblock "BINARY COUNTER"

           rcall  TM1638_PRINT_MOVETEXT

           clr    BUTTONS

           rcall  TM1638_COUNT_BIN

           rcall  Delay1s

           rcall  TM1638_LEDS_BF         ; LEDs back and forth

       ELSEIF BUTTONS, S4

           ldi    AKKU3, 2               ; Textblock "HEXACECIMAL COUNTER"

           rcall  TM1638_PRINT_MOVETEXT

           clr    BUTTONS

           rcall  TM1638_COUNT_HEX

           rcall  Delay1s

           rcall  TM1638_LEDS_BF         ; LEDs back and forth

       ELSEIF BUTTONS, S5

           ldi    AKKU3, 3              ; Textblock "DECIMAL COUNTER"

           rcall  TM1638_PRINT_MOVETEXT

           clr    BUTTONS

           rcall  TM1638_COUNT_DEC

           rcall  Delay1s

           rcall  TM1638_LEDS_BF         ; LEDs back and forth

       ELSEIF BUTTONS, S6

           ldi    AKKU3, 4               ; Textblock "ELAPSED TIME"

           rcall  TM1638_PRINT_MOVETEXT

           rcall  TM1638_CLEAR

           FOR AKKU2 := #60              ; show elapsed time for 1 minute

               FOR COUNT := #10          ; 1 sec loop

                   rcall  TM1638_PRINT_TIME

                   rcall  Delay100ms

                   clr    BUTTONS

                   rcall  TM1638_KEYPAD  ; check the TM1638 buttons

                   EXITIF BUTTONS, S8    ; button 8 leaves TIME

               ENDF

               EXITIF BUTTONS, S8        ; button 8 leaves TIME

           ENDF

           rcall  TM1638_LEDS_BF ; LEDs back and forth

       ELSEIF BUTTONS, S7        ; show the real time clock until S8

       ; S3 = hour settings

       ; S4 = minute settings

       ; S5 = reset seconds, but only within hrs/min settings

       ; S1 = decrement hrs/min, can hold down for successive decrement

       ; S2 = increment hrs/min, can hold down for successive increment

       ; S7 = leave settings, but stay in the CLOCK loop and prevent S1/S2/S5

       ; S8 = exit clock time and jump to SELECT loop

           ldi    AKKU3, 5              ; Textblock "CLOCK TIME"

           rcall  TM1638_PRINT_MOVETEXT

           rcall  TM1638_CLEAR

           ldi    yl,low(clk10ms)           ; begin of clock values in SRAM

           ldi    yh,high(clk10ms)

           LOOP                             ; CLOCK loop

               clr    BUTTONS

               REPEAT

                   rcall  TM1638_PRINT_CLOCK ; update clock time

                   rcall  TM1638_KEYPAD      ; check the TM1638 buttons

               UNTIL BUTTONS <> #0           ; wait until any button pressed     

               IF BUTTONS, S3                ; set hours

                   LOOP                      ; loop until S4, S7 or S8

                      clr    BUTTONS

                      REPEAT

                          rcall  TM1638_PRINT_CLOCK ; update clock time

                          rcall  TM1638_KEYPAD  ; check the TM1638 buttons

                      UNTIL BUTTONS <> #0   ; wait until any button pressed 

                      IF BUTTONS, S2        ; increment   

                          ldd    AKKU,y+3   ; get hours

                          inc    AKKU

                          std    y+11,AKKU

                          IF AKKU > #23

                              clr AKKU

                              std y+11,AKKU  ; reset hrs

                          ENDI

                          ldd    AKKU,y+11

                          std    y+3,AKKU   ; restore hours

                      ELSEIF BUTTONS, S1     ; decrement

                          ldd    AKKU,y+3

                          subi   AKKU,1     ; (dec would not affect CY)

                          std    y+11,AKKU

                          IF C

                              ldi AKKU,23

                              std y+11,AKKU  ; roll over

                          ENDI

                          ldd    AKKU,y+11

                          std    y+3,AKKU   ; restore hours

                      ELSEIF BUTTONS, S5     ; reset seconds

                          ldi    AKKU,1

                          std    y+9,AKKU   ; mark reset seconds

                      ENDI

                      rcall  TM1638_PRINT_CLOCK ; update clock time

                      EXITIF BUTTONS, S8     ; exit clock time

                      EXITIF BUTTONS, S7     ; exit clock settings

                      EXITIF BUTTONS, S4     ; to switch to minutes

                      rcall  delay500ms     ; for S1/S2 repeat cycle

                   ENDL          

               ELSEIF BUTTONS, S4            ; set minutes

                   LOOP                      ; loop until S3, S7 or S8

                      clr    BUTTONS

                      REPEAT

                          rcall  TM1638_PRINT_CLOCK ; update clock time

                          rcall  TM1638_KEYPAD  ; check the TM1638 buttons

                      UNTIL BUTTONS <> #0   ; wait until any button pressed 

                      IF BUTTONS, S2        ; increment   

                          ldd    AKKU,y+2

                          inc    AKKU

                          std    y+10,AKKU

                          IF AKKU > #59

                              clr AKKU

                              std y+10,AKKU  ; reset min

                          ENDI

                          ldd    AKKU,y+10

                          std    y+2,AKKU   ; restore minutes

                      ELSEIF BUTTONS, S1     ; decrement

                          ldd    AKKU,y+2   ; min

                          subi   AKKU,1     ; (dec would not affect CY)

                          std    y+10,AKKU

                          IF C

                              ldi AKKU,59

                              std y+10,AKKU  ; roll over

                          ENDI

                          ldd    AKKU,y+10

                          std    y+2,AKKU   ; restore minutes

                      ELSEIF BUTTONS, S5     ; reset seconds

                          ldi    AKKU,1

                          std    y+9,AKKU   ; mark reset seconds 

                      ENDI

                      rcall  TM1638_PRINT_CLOCK ; update clock time

                      EXITIF BUTTONS, S8    ; exit clock time

                      EXITIF BUTTONS, S7    ; exit clock settings

                      EXITIF BUTTONS, S3    ; to switch to hours

                      rcall  delay500ms     ; for S1/S2 repeat cycle

                   ENDL

               ENDI

               EXITIF BUTTONS, S8            ; leave CLOCK

           ENDL

           EXITIF BUTTONS, S8                ; leave CLOCK to SELECT

       ENDI                                 ; end of selected loops

    ENDL

ENDL       ; END OF MAIN LOOP

 

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

;                         Subroutines

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

;

; LED chains back and forth

;

TM1638_LEDS_BF:

    rcall  TM1638_LEDS_L

    rcall  TM1638_LEDS_R

    ret

 

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

;

; LED chain to the right

;

TM1638_LEDS_R:

    push   COUNT

    ldi TM1638_GRID_BYTE, 0x01        ; first LED address

    FOR COUNT := #8

       ldi TM1638_SEGM_BYTE,1        ; LED ON

       rcall  TM1638_SEND_DATA

       rcall  Delay100ms

       clr TM1638_SEGM_BYTE          ; LED OFF

       rcall  TM1638_SEND_DATA

       subi   TM1638_GRID_BYTE,-2   ; next LED address

    ENDF

    pop COUNT

    ret

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

;

; LED chain to the left

;

TM1638_LEDS_L:

    push   COUNT

    ldi TM1638_GRID_BYTE, 0x0F    ; first LED address

    FOR COUNT := #8

       ldi    TM1638_SEGM_BYTE,1 ; LED ON

       rcall  TM1638_SEND_DATA

       rcall  Delay100ms

       clr    TM1638_SEGM_BYTE   ; LED OFF

       rcall  TM1638_SEND_DATA

       subi   TM1638_GRID_BYTE,2 ; next LED address

    ENDF

    pop    COUNT

    ret

 

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

;

; binary counter, 8 bit 0x00 through 0xff

;

TM1638_COUNT_BIN:

    push   AKKU

    clr AKKU

    REPEAT

       rcall  TM1638_PRINT_BIN   ; print value in AKKU

       rcall  Delay100ms

       rcall  TM1638_KEYPAD      ; check the TM1638 buttons

       EXITIF BUTTONS, S8        ; button S8 leaves counter demo

       inc    AKKU

    UNTIL AKKU == #0

    pop    AKKU

    ret

 

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

;

; hexadecimal counter, 16 bit AKKU2/AKKU, 0x0000 through 0xffff

;

TM1638_COUNT_HEX:

    push   AKKU

    push   AKKU2

    clr    AKKU

    clr    AKKU2

    REPEAT

       REPEAT

           rcall  TM1638_PRINT_HEX   ; print value

           IF AKKU <> #0xff

               rcall  Delay10ms      ; to prevent a delay during carry

           ENDI

           rcall  TM1638_KEYPAD      ; check the TM1638 buttons

           EXITIF BUTTONS, S8        ; button S8 leaves counter demo

           inc    AKKU

       UNTIL AKKU == #0              ; carry

       inc AKKU2

       EXITIF BUTTONS, S8            ; need 2nd button S8 test to leave

       rcall  TM1638_PRINT_HEX      ; print value

       rcall  Delay10ms

       ;EXITIF AKKU2 == #0x10         ; exit demo at 0x1000

    UNTIL AKKU2 == #0

    pop    AKKU2

    pop    AKKU

    ret

 

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

;

; decimal counter, 16 bit

;

TM1638_COUNT_DEC:

    ;cli

    push   AKKU

    push   AKKU2

    push   AKKU3

    push   XL

    push   XH

    clr    XL

    clr    XH

    REPEAT

       mov AKKU2, XL

       mov AKKU3, XH

       rcall  TM1638_PRINT_DEC   ; print values in AKKU2 and AKKU3

                                 ; as 16 bit number (LO and HI-Byte)

       rcall  Delay10ms

       ;EXITIF XH >= #8          ; stop at max 2048 for the demo

       rcall  TM1638_KEYPAD      ; check the TM1638 buttons

       EXITIF BUTTONS, S8        ; button S8 leaves counter demo

       adiw   XH:XL,1

    UNTIL Z                       ; maximum would be all 16 bits  

    pop    XH

    pop    XL

    pop    AKKU3

    pop    AKKU2

    pop    AKKU

    ;sei

    ret

 

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

;

; twist arround CW from segment a through f

; EH: replaced inner REPEAT loop by a FOR loop

;

TM1638_TWIST_CW:

    push   AKKU

    push   COUNT

    ldi    AKKU, 1            ; start with segment a

    clr    TM1638_GRID_BYTE

    REPEAT

       FOR COUNT := #8

           mov    TM1638_SEGM_BYTE,AKKU    

           rcall  TM1638_SEND_DATA   ; activates only one LED

                              ; position is given by GRID_BYTE (digit)

                              ; and SEGM_BYTE (LED segment)

           subi   TM1638_GRID_BYTE,-2

       ENDF

       rcall  delay100ms

       clr    TM1638_GRID_BYTE

       rol    AKKU           ; next LED segment

    ;UNTIL AKKU == #0b01000000

    UNTIL AKKU,6              ; until segment g (not shown)

    pop    COUNT

    pop    AKKU

    ret

 

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

;

; twist arround CCW from segment f through a

; EH: replaced inner REPEAT loop by a FOR loop

;

TM1638_TWIST_CCW:

    push   AKKU

    push   COUNT

    ldi    AKKU, 0b00100000   ; start with segment f

    clr    TM1638_GRID_BYTE

    REPEAT

       FOR COUNT := #8

           mov    TM1638_SEGM_BYTE,AKKU    

           rcall  TM1638_SEND_DATA   ; activates only one LED

                              ; position is given by GRID_BYTE (digit)

                              ; and SEGM_BYTE (LED segment)

           subi   TM1638_GRID_BYTE,-2

       ENDF

       rcall  delay100ms

       clr    TM1638_GRID_BYTE

       ror    AKKU           ; next LED segment

    UNTIL C                   ; until shifted out

    pop    COUNT

    pop    AKKU

    ret

 

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

; other subroutines and TM1638 character font:

 

; TM1638 library, compiled from "tm1638cc.inc_EH.s" using label prefix _I:

.INCLUDE "tm1638cc.inc_EH.asm"

 

; delay subroutines, compiled from "tm1638cc_delay_EH.s", label prefix _D:

.INCLUDE "tm1638cc_delay_EH.asm"

 

; interrupt based timer, compiled from "tm1638cc_timer.s", label prefix _T:

.INCLUDE "tm1638cc_timer_EH.asm"

 

; NOTE: in general all included .asm files being generated by s'AVR

; must use DIFFERENT label prefixes (could be any from _A through _Z)

 

.INCLUDE "tm1638cc_font.inc"             ; TM1638 character font

 

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

; constant text:

 

MOVETEXT:                                ; 35+1 characters each

.db "            TM1638 DEMO            ",0  ; TEXT_BLOCK 0

.db "          BINARY COUNTER           ",0  ; TEXT_BLOCK 1

.db "       HEXADECIMAL COUNTER         ",0  ; TEXT_BLOCK 2

.db "         DECIMAL COUNTER           ",0  ; TEXT_BLOCK 3

.db "       ELAPSED TIME        ",0,0,0,0,0,0,0,0,0     ; TEXT BLOCK 4

.db "       CLOCK TIME        ",0,0,0,0,0,0,0,0,0,0,0   ; TEXT BLOCK 5

 

TEXT10:            ; 8+2 characters each

.db "LEDS DIM",0,0 ; TEXT10, Block 0

.db "88888888",0,0 ; TEXT10, Block 1, showing all 7 segments w/o DP

.db "(-88.88)",0,0 ; TEXT10, Block 2, showing extra characters ( - . )

.db "SELECT..",0,0 ; TEXT10, Block 3, select demo by button

.db "TEST KEY",0,0 ; TEXT10, Block 4, test keys

 

.DSEG

 

.ORG   SRAM_START ; 0x0100

 

; keep the next 2x 4 bytes together (being addressed by YH:YL)

 

clk10ms:   .db 0      ; 0 count 10ms clock time

clksec:    .db 0      ; 1 clock seconds

clkmin:    .db 0      ; 2 clock minutes

clkhrs:    .db 0      ; 3 clock hours

 

cnt10ms:   .db 0      ; 4 count 10ms elapsed time

cntsec:    .db 0      ; 5 count seconds

cntmin:    .db 0      ; 6 count minutes

cnthrs:    .db 0      ; 7 count hours

cnthrsh:   .db 0      ; 8 count 100s of hrs, shown by LEDs 1 through 8

 

rstsec:    .db 0      ; 9 rst = 1 indicates that seconds should be reset

setmin:    .db 0      ; used to set minutes

sethrs:    .db 0      ; used to set hours

 

.EXIT