;********************************************************************** ; This file is a basic code template for assembly code generation * ; on the PIC16F873A. This file contains the basic code * ; building blocks to build upon. * ; * ; If interrupts are not used all code presented between the ORG * ; 0x004 directive and the label main can be removed. In addition * ; the variable assignments for 'w_temp' and 'status_temp' can * ; be removed. * ; * ; Refer to the MPASM User's Guide for additional information on * ; features of the assembler (Document DS33014). * ; * ; Refer to the respective PIC data sheet for additional * ; information on the instruction set. * ; * ;********************************************************************** ; * ; Filename: vfdclock.asm * ; Date: * ; File Version: * ; * ; Author: * ; Company: * ; * ; * ;********************************************************************** ; * ; Files required: * ; * ; * ; * ;********************************************************************** ; * ; Notes: * ; * ; * ; * ; * ;********************************************************************** list p=16f873A ; list directive to define processor #include ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _WRT_OFF & _LVP_OFF & _CPD_OFF ; '__CONFIG' directive is used to embed configuration data within .asm file. ; The lables following the directive are located in the respective .inc file. ; See respective data sheet for additional information on configuration word. ; Port A bits #define CDSIN 0 ; CDS analog input for dimmer control #define DIG_6 1 ; DIGIT 6 output #define DIG_5 2 ; DIGIT 5 output #define DIG_4 3 ; DIGIT 4 output #define BEEPER 4 ; BEEPER output (negative) #define DIG_3 5 ; DIGIT 3 output ; Port B bits #define SEG_G 0 ; SEGMENT G output #define SEG_DP 1 ; SEGMENT DP output #define DIG_1 2 ; DIGIT 1 output #define DIG_2 3 ; DIGIT 2 output #define SW1 4 ; SW 1 in (negative) #define SW2 5 ; SW 2 in (negative) #define SW3 6 ; SW 3 in (negative) #define SW4 7 ; SW 4 in (negative) ; Port C bits #define SEG_F 0 ; SEGMENT F output #define SEG_H 1 ; SEGMENT H output #define PWM 2 ; PWM output #define SEG_A 3 ; SEGMENT A output #define SEG_B 4 ; SEGMENT B output #define SEG_C 5 ; SEGMENT C output #define SEG_E 6 ; SEGMENT E output #define SEG_D 7 ; SEGMENT D output #define DIGIT_BASE 0x20 ; Digit buffer base address ; BCD buffers digit_1 equ 0x20 ; Digit 1 data digit_2 equ 0x21 ; Digit 2 data digit_3 equ 0x22 ; Digit 3 data digit_4 equ 0x23 ; Digit 4 data digit_5 equ 0x24 ; Digit 5 data digit_6 equ 0x25 ; Digit 6 data ; BCD buffer control bit #define WITH_DP 7 ; With DP #define BLANKING 6 ; Turn off the digit ; Display control regs digit_ptr equ 0x26 ; Digit pointer digit_ctrl equ 0x27 ; Digit control reg #define digit_on 0 ; Control flag digit ON digit_on_time equ 0x28 ; ON duration (for brightness control) digit_off_time equ 0x29 ; OFF duration (for brughtness control) digit_temp equ 0x2a ; Digit function internal temp ; Current time holding reg time_sec equ 0x30 ; Time second time_min equ 0x31 ; Time min time_hr equ 0x32 ; Time hour count50 equ 0x33 ; 1/50 divide counter ; BCD conversion bcd_d1 equ 0x34 ; BCD 1's order output bcd_d10 equ 0x35 ; BCD 10's order output bcd_d100 equ 0x36 ; BCD 100's order output bcd_temp equ 0x37 ; BCD converter internal temp wait_cnt equ 0x38 ; SW bounce remover sw_prev equ 0x39 sw_latch equ 0x3a ;***** VARIABLE DEFINITIONS w_temp EQU 0x70 ; variable used for context saving status_temp EQU 0x71 ; variable used for context saving pclath_temp EQU 0x72 ; variable used for context saving ;********************************************************************** ORG 0x000 ; processor reset vector nop ; nop required for icd goto main ; go to beginning of program ORG 0x004 ; interrupt vector location movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register bcf STATUS,RP0 ; ensure file register bank set to 0 movwf status_temp ; save off contents of STATUS register movf PCLATH,w ; move pclath register into w register movwf pclath_temp ; save off contents of PCLATH register ; isr code can go here or be located as a call subroutine elsewhere btfsc INTCON, TMR0IF goto isr_tmr0 btfsc PIR1, TMR1IF goto isr_tmr1 goto isr_tmr1 ; isr return code isr_ret bcf STATUS,RP0 ; ensure file register bank set to 0 movf pclath_temp,w ; retrieve copy of PCLATH register movwf PCLATH ; restore pre-isr PCLATH register contents movf status_temp,w ; retrieve copy of STATUS register movwf STATUS ; restore pre-isr STATUS register contents swapf w_temp,f swapf w_temp,w ; restore pre-isr W register contents retfie ; return from interrupt main ; remaining code goes here ; Initialize ; Setup port direction banksel TRISA movlw B'11000001' ; NA/NA/G3/BZ/G4/G5/G6/CDS(analog) movwf TRISA movlw B'11110000' ; SW4/SW3/SW2/SW1/G2/G1/DP/G movwf TRISB clrf TRISC ; D/E/C/B/A/PWM/H/F (all output) ; Setup weak pull-up with PORT B and TMR0 prescaler ; Setup OPTION_REG movlw B'00000100' ; /RBPU, Sel Fosc/4, TMR0 Prescaler = 1/32 movwf OPTION_REG ; Start PWM 50kHz, Duty=0.5 banksel PR2 movlw D'63' ; PR2 <= 63 movwf PR2 banksel CCPR1L movlw D'20' ; CCPR1L <= 128 >> 2 movwf CCPR1L movlw B'00001100' ; Select PWM mode movwf CCP1CON bsf T2CON, TMR2ON ; Start PWM ; Setup interrupt banksel PIE1 bsf PIE1, TMR1IE ; Enable TMR1 INT banksel INTCON bsf INTCON, PEIE ; Enable peripheral INT (used by TMR1) bsf INTCON, TMR0IE ; Enable TMR0 INT bsf INTCON, GIE ; Enable Global INT ; Setup A/D converter banksel ADCON1 movlw B'00001110' ; ADFM=0, AN0=analog, other ANs = digital movwf ADCON1 banksel ADCON0 movlw B'11000001' ; Int RC clock, channel = 0, ADON movwf ADCON0 ; Setup TMR1 movlw B'00000101' ; Prescaler = 1/1, Sel Fosc/4, TMR1 ON movwf T1CON movlw 0x07 ; Set 0x07 pritor to access tables movwf PCLATH clrf time_sec ; Clear time at boot clrf time_min clrf time_hr movlw D'50' movwf count50 ; Turn off beeper (negative) bsf PORTA, BEEPER ; Free running "main" loop head loop ; SW bounce remover movfw PORTB andlw 0xf0 xorwf sw_prev, W btfss STATUS, Z goto sw_end sw1_check btfss PORTB, SW1 goto sw1_pressed bcf sw_latch, SW1 goto sw2_check sw1_pressed btfsc sw_latch, SW1 goto sw2_check bsf sw_latch, SW1 ; SW1 pressed ; increment hour movlw D'23' xorwf time_hr, W btfss STATUS, Z incf time_hr, F btfsc STATUS, Z clrf time_hr sw2_check btfss PORTB, SW2 goto sw2_pressed bcf sw_latch, SW2 goto sw3_check sw2_pressed btfsc sw_latch, SW2 goto sw3_check bsf sw_latch, SW2 ; SW2 pressed ; increment min movlw D'59' xorwf time_min, W btfss STATUS, Z incf time_min, F btfsc STATUS, Z clrf time_min sw3_check btfss PORTB, SW3 goto sw3_pressed bcf sw_latch, SW3 goto sw4_check sw3_pressed btfsc sw_latch, SW3 goto sw4_check bsf sw_latch, SW3 ; SW3 pressed ; reset sec clrf time_sec movlw D'50' movwf count50 sw4_check btfss PORTB, SW4 goto sw4_pressed bcf sw_latch, SW4 goto sw_end sw4_pressed btfsc sw_latch, SW4 goto sw_end bsf sw_latch, SW4 ; SW4 pressed ; toggle pwm on/off (display on/off) btfss T2CON, TMR2ON goto pwm_enable ; pwm_disable bcf T2CON, TMR2ON clrf CCP1CON ; Clear CCP1CON makes pwm pin low level. goto sw_end pwm_enable movlw B'00001100' ; Select PWM mode movwf CCP1CON bsf T2CON, TMR2ON ; Start PWM sw_end movfw PORTB andlw 0xf0 movwf sw_prev ; Copy sec to digit5,6 movfw time_sec call getbcd movfw bcd_d1 movwf digit_6 movfw bcd_d10 movwf digit_5 ; Copy min to digit3,4 movfw time_min call getbcd movfw bcd_d1 addlw B'10000000' movwf digit_4 movfw bcd_d10 movwf digit_3 ; Copy hour to digit1,2 movfw time_hr call getbcd movfw bcd_d1 addlw B'10000000' movwf digit_2 movfw bcd_d10 movwf digit_1 ; Brightness control ;call wait_10us ; This sampling time seems not to be required. ;call wait_10us ; Because it always connected to ch1 ? bsf ADCON0, GO_DONE wait_ad_done btfsc ADCON0, GO_DONE goto wait_ad_done ; if advalue > 123 goto max_brightness movfw ADRESH sublw D'123' btfss STATUS, C goto max_brightness ; where x <= 123 ; On time = 256 - (253 - x) = 3 + x ; Off time = 256 - (117 + x) = 139 - x movfw ADRESH sublw D'253' movwf digit_on_time movlw D'117' addwf ADRESH, W movwf digit_off_time goto loop max_brightness ; On time = 256-130 = 126 ; Off time = 256-240 = 16 movlw D'130' movwf digit_on_time movlw D'240' movwf digit_off_time goto loop ; Dynamic display driver ; Invoked when TMR0 overflows isr_tmr0 bcf INTCON, TMR0IF btfss digit_ctrl, digit_on goto isr_tmr0_off_digit bcf digit_ctrl, digit_on ; Set ON duration movfw digit_on_time movwf TMR0 ; Read segment data from buffer movlw DIGIT_BASE ; w = *(DIGIT_BASE + digit_ptr) addwf digit_ptr, W movwf FSR movfw INDF movwf digit_temp ; Check blanking bit btfsc digit_temp, BLANKING goto isr_ret ; Turn on DP if required btfsc digit_temp, WITH_DP bsf PORTB, SEG_DP ; Get font data call getfont ; Handle segment G movwf digit_temp ; Segment G resides on PORTB (as PWM uses PORTC<2>) btfsc digit_temp, 2 bsf PORTB, SEG_G ; RC2(CCP1) is used for PWM, mask it. andlw B'11111011' movwf PORTC ; Turn on corresponding digit movfw digit_ptr xorlw 0 btfsc STATUS, Z bsf PORTB, DIG_1 movfw digit_ptr xorlw 1 btfsc STATUS, Z bsf PORTB, DIG_2 movfw digit_ptr xorlw 2 btfsc STATUS, Z bsf PORTA, DIG_3 movfw digit_ptr xorlw 3 btfsc STATUS, Z bsf PORTA, DIG_4 movfw digit_ptr xorlw 4 btfsc STATUS, Z bsf PORTA, DIG_5 movfw digit_ptr xorlw 5 btfsc STATUS, Z bsf PORTA, DIG_6 goto isr_ret isr_tmr0_off_digit bsf digit_ctrl, digit_on ; Set off duration movfw digit_off_time movwf TMR0 ; Turn off all digit/segment movlw B'11010001' andwf PORTA, F movlw B'11110000' andwf PORTB, F clrf PORTC ; Increment digit pointer movfw digit_ptr ; if(digit_ptr == 5) xorlw D'5' ; digit_ptr = 0; btfss STATUS, Z ; else incf digit_ptr, F ; digity_ptr ++; btfsc STATUS, Z clrf digit_ptr goto isr_ret ; Clock manipulator ; Invoked when TMR1 overflows isr_tmr1 bcf PIR1, TMR1IF ; Timer counter compensation movlw 0x02 addwf TMR1L, F btfsc STATUS, C incf TMR1H, F movlw 0x06 movwf TMR1H ; 50Hz divider decfsz count50, F goto isr_ret movlw D'50' movwf count50 ; Enter here at every second ; Second incf time_sec, F movfw time_sec xorlw D'60' btfss STATUS, Z goto isr_ret clrf time_sec ; Minute incf time_min, F movfw time_min xorlw D'60' btfss STATUS, Z goto isr_ret clrf time_min ; Hour incf time_hr, F movfw time_hr xorlw D'24' btfss STATUS, Z goto isr_ret clrf time_hr goto isr_ret ; BCD conversion ; Input W , Output 3 digit of bcd at bcd_d100, bcd_d10, bcd_d1 getbcd clrf bcd_d100 clrf bcd_d10 movwf bcd_temp getbcd_loop1 sublw D'99' btfsc STATUS, C goto getbcd_loop2 incf bcd_d100, F movlw D'100' subwf bcd_temp, F movfw bcd_temp goto getbcd_loop1 getbcd_loop2 movfw bcd_temp sublw D'9' btfsc STATUS, C goto getbcd_lt10 incf bcd_d10, F movlw D'10' subwf bcd_temp, F movfw bcd_temp goto getbcd_loop2 getbcd_lt10 movfw bcd_temp movwf bcd_d1 return ; 10us timer (not used) ;wait_10us ; nop ; nop ; movlw 9 ; movwf wait_cnt ;wait_10us_0 ; decfsz wait_cnt, F ;4+3*9-1=30 ; goto wait_10us_0 ; return ;30+2=32 ORG 0x700 ; Font data getfont andlw 0x0f addwf PCL, F ; DECBAGHF retlw B'11100000' ; 0 retlw B'00000110' ; 1 retlw B'11011000' ; 2 retlw B'10111000' ; 3 retlw B'00000111' ; 4 retlw B'10101001' ; 5 retlw B'11110000' ; 6 retlw B'00011010' ; 7 retlw B'11111001' ; 8 retlw B'00011011' ; 9 ; Followings are not used, difficult to get non-numeric result retlw B'00000000' ; a retlw B'00000000' ; b retlw B'00000000' ; c retlw B'00000000' ; d retlw B'00000000' ; e retlw B'00000000' ; f END ; directive 'end of program'