;;================================================= ;; DOA_7seg_2803.asm ;; ;; part of the PHCC (PIC HomeCockpit Controller) Project ;; ;; This is the 16x 7-Segment Display controller that consists ;; of a PIC, 16 7-Segment displays, 8 Resistors, ;; two 74HCT164 Shift Registers and two ULN2803 Darlington Arrays. ;; ;; Copyright (c) 2004 by Manuel Bessler ;; ;; The full text of the legal notices is contained in the file called ;; COPYING, included with this distribution. ;; ;; This program is free software; you can redistribute it and/or ;; modify it under the terms of the GNU General Public License ;; as published by the Free Software Foundation; either version 2 ;; of the License, or (at your option) any later version. ;; ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; ;; You should have received a copy of the GNU General Public License ;; along with this program; if not, write to the Free Software ;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ;; ;;================================================= ;; ;; ;;================================================= ;; Pin Assingments: ;; RA0 output, CLK for 74x164 shift registers ;; RA1 output, goes to shift register serial input ;; RA2 input, AP2PP CLK ;; RA3 input, AP2PP Data ;; RA4 output, to segment #7 ;; RA5 output, to segment #8 ;; RC0 output, to segment #1 ;; RC1 output, to segment #2 ;; RC2 output, to segment #3 ;; RC3 output, to segment #4 ;; RC4 output, to segment #5 ;; RC5 output, to segment #6 ;; ;; - uses internal RC OSC ;; ;;================================================= ;; Things added: ;; ;; ;; ;; ;;================================================= LIST P=18F242, R=DEC ; what the PIC we use, decimal system as default IFDEF __18F242 #include "p18f242.inc" ; include appropriate processor definitions ; set config bits: CONFIG OSC = HSPLL CONFIG OSCS = OFF CONFIG PWRT = ON CONFIG BOR = ON CONFIG BORV = 20 CONFIG WDT = OFF CONFIG WDTPS = 32 CONFIG CCP2MUX = OFF CONFIG STVR = ON CONFIG LVP = OFF CONFIG DEBUG = OFF CONFIG CP0 = OFF CONFIG CP1 = OFF CONFIG CPB = OFF CONFIG CPD = OFF CONFIG WRT0 = OFF CONFIG WRT1 = OFF CONFIG WRTC = OFF CONFIG WRTB = OFF CONFIG WRTD = OFF CONFIG EBTR0 = OFF CONFIG EBTR1 = OFF CONFIG EBTRB = OFF ENDIF ;;;; @40Mhz ;; here you need to specify a unique 8bit device address for this DOA daughterboard ;;#define DEVADDR 0x30 #define DO_A_CLK PORTB, 0 ;; Digital-In Type A Clock line #define DO_A_DATA PORTB, 1 ;; Digital-In Type A Data line #define DO_A_CLK_TRIS TRISB, 0 ;; direction register setup #define DO_A_DATA_TRIS TRISB, 1 ;; direction register setup #define STATE_ADDR 0x00 #define STATE_SUBADDR 0x01 #define STATE_DATA 0x02 #define SHREG_CLR PORTA, 5 #define SHREG_DATA PORTA, 3 #define SHREG_CLK PORTA, 2 #define SHREG_CLR_TRIS TRISA, 5 #define SHREG_DATA_TRIS TRISA, 3 #define SHREG_CLK_TRIS TRISA, 2 #define FREQTIMESFOUR 10 ;; for delays, speciefies the multiple of 4 MHz we're running at. eg. for 16MHz this would be 4, for 40MHz it would be 10 ; define SRAM data areas/variables/file registers CBLOCK 0x0 Wsave, BSRsave, STATUSsave tmp, jmptmp ap2pp_state, ap2pp_bitcounter, ap2pp_addr, ap2pp_subaddr, ap2pp_data, ap2pp_tmp delaycounter_freq delaycounter_inner,delaycounter_outer actdispl counter segtmp, dimtmp segdata:32 arraytmp dimming init_tmr1L, init_tmr1H ENDC ;;------------------------------- ;; program start point ;;------------------------------- ORG 0x000 ; program start point at 0x000 BRA init ;;------------------------------- ;; (high priority) interrupt vector ;;------------------------------- ORG 0x0008 interrupt: ; global interrupts automatically disabled on entry! ;; save registers MOVWF Wsave MOVFF STATUS, STATUSsave MOVFF BSR, BSRsave ;; start interrupt processing ;; portb pin change BTFSC INTCON, INT0IF ; was it a pos edge trigger pin RB0 ? CALL DOAint ;; timer0 ;; BTFSC INTCON, TMR0IF ; was it a timer0 overflow ? ;; CALL tmr0int ;; timer1 BTFSC PIR1, TMR1IF ; was it a timer1 overflow ? CALL tmr1int ;; timer3 ; BTFSC PIR2, TMR3IF ; was it a timer3 overflow ? ; CALL tmr3int BRA end_interrupt ; non-handled interrupt? then go to end end_interrupt: ;; restore addessing MOVFF BSRsave, BSR MOVF Wsave, W MOVFF STATUSsave, STATUS RETFIE ;;------------------------------- ;; generic initialization ;;------------------------------- init: CLRF PORTA ; set portA pins low CLRF PORTB ; set portB pins low CLRF PORTC MOVLW 0x0F ; 0x0F => all ANx ports digital MOVWF ADCON1 ; IFNDEF __18F252 IFNDEF __18F242 MOVLW 0x07 MOVWF CMCON ; all comparators off ENDIF ENDIF ;; SETtting a bit in the TRISx registers -> input ;; CLEARing a bit in the TRISx registers -> output ;;------------------------------- ;; application specific initialization ;;------------------------------- CLRF INTCON ; all interrupts off/reset CALL setup_7seg CALL setup_AP2PP ;timer and interrupt stuff CALL setup_timer1 ; aaaaaand ACTION ... BCF RCON, IPEN ; don't use interrupt priorities BSF INTCON, GIE ; globally enable ints BSF INTCON, PEIE ; enable peripheral ints CALL delay250ms ;;------------------------------- ;; main loop ;;------------------------------- Main: NOP NOP NOP NOP NOP BRA Main ;================================================ ;;------------------------------- ;; interrupt handlers ;;------------------------------- tmr1int: BCF T1CON, TMR1ON ; stop timer1 MOVF init_tmr1H, W ; re-load timer1 MOVWF TMR1H MOVF init_tmr1L, W MOVWF TMR1L ; low byte MUST come after high byte CALL next_display ; do something on interrupt tmr1int_done: BCF PIR1, TMR1IF ; clear timer1 interrupt flag BSF PIE1, TMR1IE ; enable timer1 interrupt BSF T1CON, TMR1ON ; re-start timer1 RETURN ;;-------------------------------------- ;;;; DOAint: see below in the AP2PP section ;;-------------------------------------- ;;-------------------------------------- ;;------------------------------- ;; RETURN to next display routine ;;------------------------------- next_display: MOVLW 16 ; number of common cathodes(anodes) SUBWF actdispl, W ; W = actdispl - 16 BTFSS STATUS,C ; Carry is clear if result is negative BRA no_display_scan_restart display_scan_restart: LFSR FSR1, segdata ; setup address to first set of 16 displays LFSR FSR2, segdata+16 ; setup address to second set of 16 displays CLRF actdispl ; set the number of the active display to zero RCALL clear_164 ; reset shift registers RCALL segments_off RCALL shift_one_into164 RCALL segments_out BRA next_display_end no_display_scan_restart: MOVF actdispl, W MOVWF counter RCALL clear_164 ; reset shift registers RCALL segments_off ; this avoids ghosting of display segments in neighboring displays RCALL shift_one_into164 ; the cycling bit next_in_shift: RCALL shift_zero_into164 ; push the cycling bit to correct position in shift register DECFSZ counter, F BRA next_in_shift RCALL segments_out next_display_end: INCF actdispl, F ; next active display RETURN ;;------------------------------- ;; routine to ouput segment data to port pins ;;------------------------------- segments_out: MOVLW b'11111100' ANDWF PORTA, F ; make sure segment pins are zero'd out for later ORing MOVLW b'00000011' ANDWF PORTB, F ; make sure segment pins are zero'd out for later ORing ;;; MOVFF INDF1, segtmp ; load segment data for displays 1-16 MOVFF POSTINC1, segtmp RLNCF segtmp, F ; shift segment data for displays 1-16 left ... RLNCF segtmp, F ; ... twice for RB<2:7> and save in segtmp since we need it again MOVF segtmp, W ; ANDLW b'11111100' IORWF PORTB, F ; OR segtmp in WREG with rest of PORTB MOVF segtmp, W ; here we need segtmp again for pins on RA<0:1> ANDLW b'00000011' ; mask out unused part IORWF PORTA, F ; OR segtmp in WREG with rest of PORTA MOVFF POSTINC2, PORTC ; segments of displays 17-32 are connected to RC<0:7> RETURN ;;------------------------------- ;; turn off all segments routine ;;------------------------------- segments_off: ; turn off all segmnts MOVLW b'00000011' ANDWF PORTB, F MOVLW b'11111100' ANDWF PORTA, F CLRF PORTC RETURN ;;------------------------------- ;; routine to send out serial bit to 74x164 ;;------------------------------- shift_zero_into164: BCF SHREG_DATA ;serial data low NOP BSF SHREG_CLK ; raise the CLK to 74x164 to clock in the serial data bit NOP BCF SHREG_CLK ; and lower the CLK to 74x164 again RETURN shift_one_into164: BSF SHREG_DATA ;serial data high NOP BSF SHREG_CLK ; raise the CLK to 74x164 to clock in the serial data bit NOP BCF SHREG_CLK ; and lower the CLK to 74x164 again RETURN ;;------------------------------- clear_164: BCF SHREG_CLR NOP NOP BSF SHREG_CLR RETURN ;;------------------------------- ;;------------------------------- ;;------------------------------- ;; setup_timer1 ;; gets timer1 ready to run ;;------------------------------- setup_timer1: MOVLW 0xE0 ; initial timer setup @40MHz MOVWF init_tmr1H ; first High byte, MOVLW 0x00 MOVWF init_tmr1L ; then the low byte ! MOVLW b'10000100' ; 16bit access, (load H first, ; then L which triggers load from buffer to real register) ; other clk source, ; 1:1 prescaler (00), ; t1osc disabled, ; don't care, ; src is Fosc/4, ; timer not yet started MOVWF T1CON ; set up timer1 MOVFF init_tmr1H, TMR1H ; load timer1 with value for Xms MOVFF init_tmr1L, TMR1L ; low byte MUST come after high byte BCF PIR1, TMR1IF ; clear timer1 interrupt flag BSF PIE1, TMR1IE ; enable timer1 interrupt BSF T1CON, TMR1ON ; start timer1 RETURN ;;------------------------------- ;; setup_7seg ;;------------------------------- ;; sets up variables and port pins for the seven segment displays ;; (BEWARE: this does not use std 7seg pin assignments) ;;------------------------------- setup_7seg: BCF SHREG_CLR_TRIS BCF SHREG_DATA_TRIS BCF SHREG_CLK_TRIS BCF SHREG_CLR BCF SHREG_DATA BCF SHREG_CLK LFSR FSR1, segdata MOVLW 32 MOVWF counter clear_next_digit: CLRF POSTINC1 ;; SETF POSTINC1 DECFSZ counter, F BRA clear_next_digit ; RB2-7 are segments 0-5 of displays 1-16 ; RA0 connected to segment 6 ; RA1 connected to segment 7 BCF TRISA, 0 BCF TRISA, 1 BCF TRISB, 2 BCF TRISB, 3 BCF TRISB, 4 BCF TRISB, 5 BCF TRISB, 6 BCF TRISB, 7 MOVLW b'00000000' MOVWF TRISC ; PortC all outputs for segments of displays 17-32 BSF SHREG_CLR ; release clear of 74x164 CLRF actdispl MOVLW 1 MOVWF dimming MOVWF dimtmp RETURN ;;------------------------------- ;; AP2PP ;; Adressable PIC-TO-PIC protocol comm receiver (bit-banging type) routines ;;------------------------------- ;; Pins used: ;; DO_A_CLK: Clock ;; DO_A_DATA: Data ;; ;; protocol: ;; data bit has to be stable before rising edge of clock ;; gets saved into ;;------------------------------- setup_AP2PP: BSF DO_A_CLK_TRIS ; pin is input BSF DO_A_DATA_TRIS ; pin is input BSF INTCON2, INTEDG0 ; throw interrupt on rising edge on RB0 BCF INTCON, INT0IF ; clear int0 interrupt flag BSF INTCON, INT0IE ; enable int0/rb0 interrupt CLRF ap2pp_state ; start in idle state MOVLW 0x08 ; protocol wants 8 bits per DOA_addr MOVWF ap2pp_bitcounter ; this register keeps track of the number of the number of missing bits RETURN ;;------------------------------- DOAint: CALL AP2PP_recv BCF INTCON, INT0IF ; clear int0 interrupt flag BSF INTCON, INT0IE ; enable int0/rb0 interrupt RETURN ;;------------------------------- AP2PP_recv: BCF STATUS, C ; use the Carry bit for temp storage of received data bit BTFSC DO_A_DATA ; read received data bit from port BSF STATUS, C ; RRCF ap2pp_tmp, F ; shift carry into tmp var MOVF ap2pp_state, W MOVWF jmptmp ; need tmp copy for shifting for address calc BCF STATUS, C ; clean carry RLCF jmptmp, F ; shift state left MOVLW HIGH(statejmptbl) ; load high part of jump table address BTFSC STATUS, C ; INCF WREG, W ; if carry then w=w+1 MOVWF PCLATH ; move into high byte of program counter MOVLW LOW(statejmptbl) ; load low part of jump table address ADDWF jmptmp, W BTFSC STATUS, C INCF PCLATH, F MOVWF PCL ; state machine jump table statejmptbl: BRA state_addr ; BRA state_subaddr ; BRA state_data ; ;;------------------------------- state_addr: DECFSZ ap2pp_bitcounter, F RETURN ; bitcounter zero? then next state MOVLW STATE_SUBADDR MOVWF ap2pp_state MOVLW 0x06 ; protocol wants 6 bits per DOA_subaddr MOVWF ap2pp_bitcounter ; this register keeps track of the number of the number of missing bits MOVF ap2pp_tmp, W ; load tmp var MOVWF ap2pp_addr ; and store in its final place RETURN ; we're done, next bits are for subaddr ;;------------------------------- state_subaddr: DECFSZ ap2pp_bitcounter, F RETURN ; bitcounter zero? then next state MOVLW STATE_DATA MOVWF ap2pp_state MOVLW 0x08 ; protocol wants 8 bits per DOA_data MOVWF ap2pp_bitcounter ; this register keeps track of the number of the number of missing bits MOVF ap2pp_tmp, W ; load tmp var MOVWF ap2pp_subaddr ; and store in its final place ; since subaddr is only 6 bit and gets shifted in from the left, we need to pad the upper two bits BCF STATUS, C ; subaddr is only 6 bit, RRCF ap2pp_subaddr, F ; so we need to BCF STATUS, C ; left pad it RRCF ap2pp_subaddr, F ; with two zero bits RETURN ; we're done, next bits are for data ;;------------------------------- state_data: DECFSZ ap2pp_bitcounter, F RETURN ; bitcounter zero? then initial state MOVLW STATE_ADDR MOVWF ap2pp_state MOVLW 0x08 ; protocol wants 8 bits per DOA_addr MOVWF ap2pp_bitcounter ; this register keeps track of the number of the number of missing bits MOVLW DEVADDR ; load my device address CPFSEQ ap2pp_addr ; check if the packet devaddr == my devaddr RETURN ; not equal then skip MOVF ap2pp_tmp, W ; else: load tmp var MOVWF ap2pp_data ; and store in its final place ; now we can update the requested display BTFSC ap2pp_subaddr, 5 ; check if individual light has to be updated BRA set_individual set_array: BCF ap2pp_subaddr, 5 ; filter out the channel from subaddr LFSR FSR0, segdata ; set up addressing to displays MOVF ap2pp_subaddr, W ; load subaddr into W MOVFF ap2pp_data, PLUSW0 ; move ap2pp_data to memory pointed to by FSR0+W(ap2pp_subaddr) RETURN ; we're done, next bits are for data set_individual: BCF ap2pp_subaddr, 5 ; filter out the channel from subaddr LFSR FSR0, segdata ; set up addressing to displays MOVF ap2pp_subaddr, W ; load subaddr into W MOVFF PLUSW0, arraytmp ; store segdata array in arraytmp MOVLW 0x7F CPFSLT ap2pp_data BRA check BRA set_state check: MOVLW 0x80 CPFSGT ap2pp_data BRA next_check BRA clear_state next_check: MOVLW 0x7F CPFSEQ ap2pp_data BRA set_state clear_state: MOVF ap2pp_data, W ANDWF arraytmp MOVF ap2pp_subaddr, W MOVFF arraytmp, PLUSW0 RETURN ; we're done, next bits are for data set_state: MOVF ap2pp_data, W ; load data IORWF arraytmp ; and OR it with the current segdata MOVF ap2pp_subaddr, W ; load channel offset MOVFF arraytmp, PLUSW0 ; and store new segdata RETURN ; we're done, next bits are for data ;;------------------------------- ;;------------------------------- ;;------------------------------- ;; delay routines ;;------------------------------- delay1ms: MOVLW FREQTIMESFOUR MOVWF delaycounter_freq delay1_freq: MOVLW 1 MOVWF delaycounter_outer ; takes value in W for delay lenght in msec delay1_outer: MOVLW 200 ; 200*5usec = 1msec MOVWF delaycounter_inner delay1_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F BRA delay1_inner DECFSZ delaycounter_outer,F BRA delay1_outer DECFSZ delaycounter_freq,F BRA delay1_freq RETURN ;;------------------------------- delay250ms: MOVLW FREQTIMESFOUR MOVWF delaycounter_freq delay_250_freq: MOVLW 250 ; 250 * 1msec = 250msec MOVWF delaycounter_outer delay250_outer: MOVLW 200 ; 200*5usec = 1msec MOVWF delaycounter_inner delay250_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F BRA delay250_inner DECFSZ delaycounter_outer,F BRA delay250_outer DECFSZ delaycounter_freq,F BRA delay_250_freq RETURN ;;------------------------------- delayXms: MOVLW FREQTIMESFOUR MOVWF delaycounter_freq delay_freq: MOVWF delaycounter_outer ; takes value in W for delay lenght in msec delay_outer: MOVLW 200 ; 200*5usec = 1msec MOVWF delaycounter_inner delay_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F BRA delay_inner DECFSZ delaycounter_outer,F BRA delay_outer DECFSZ delaycounter_freq,F BRA delay_freq RETURN delay250us: MOVLW FREQTIMESFOUR MOVWF delaycounter_freq delay_us_250_freq: MOVLW 1 MOVWF delaycounter_outer delay_us_250_outer: MOVLW 50 ; 50*5usec = 250usec @4MHz MOVWF delaycounter_inner delay_us_250_inner: ; inner loop takes 5usec @4MHz NOP NOP DECFSZ delaycounter_inner,F BRA delay_us_250_inner DECFSZ delaycounter_outer,F BRA delay_us_250_outer DECFSZ delaycounter_freq,F BRA delay_us_250_freq RETURN delay1sec: CALL delay250ms CALL delay250ms CALL delay250ms CALL delay250ms RETURN ;;================================================= ;; End of program ;;================================================= END