;********************************************************************** ; * ; Filename: RGBeam 0.0.2.asm * ; Date: 23 Novembre 2006 * ; File Version: 0.2 * ; * ; Author: A.Lambardi * ; Company: * ; * ;********************************************************************** ; * ; Notes: * ; 20 MHz crystal * ; Zero crossing of mains detected via comparator * ; Remote control codes are the one generated by a RC5 remote made * ; by Philips and some others based on SAA3010. * ; Universal remote controls should be fine as long as Philips is * ; supported * ;********************************************************************** list p=16f628a ; list directive to define processor #include ; processor specific variable definitions __CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_OFF & _HS_OSC & _MCLRE_ON & _LVP_OFF ;macros BANK0 MACRO bcf STATUS, RP1 bcf STATUS, RP0 ; Select Bank 0 ENDM BANK1 MACRO bcf STATUS, RP1 bsf STATUS, RP0 ; Select Bank 1 ENDM ;variables w_temp equ 0x70 ; variable used for context saving status_temp equ 0x71 ; variable used for context saving Phase_R equ 0x73 ; phase angle (0 on -> 31 off) for red (R) Phase_G equ 0x74 ; phase angle (0 on -> 31 off) for green (G) Phase_B equ 0x75 ; phase angle (0 on -> 31 off) for red (B) Phase equ 0x76 ; current phase angle (0 -> 31) EE_ad equ 0x77 ; EEprom address to write to EE_dt equ 0x78 ; EEprom data read or to be written Curr_mem equ 0x79 ; colour preset (0->9) on_off equ 0x7A ; current on / off status system_bits equ 0x7C ; remote RC5 system bits command_bits equ 0x7D ; remote RC5 command bits tmp equ 0x7E ; temp register ; Constants _50Hz_n_port equ PORTB ; if == 1 then configure for 50Hz input _50Hz_n_bit equ 0 Out_R_port equ PORTB ; output port to OPTO DIAC R Out_R_bit equ 3 Out_G_port equ PORTB ; output port to OPTO DIAC G Out_G_bit equ 4 Out_B_port equ PORTB ; output port to OPTO DIAC B Out_B_bit equ 5 Echo_LED_port equ PORTB ; monitor LED Echo_LED_bit equ 6 IR_port equ PORTB ; IR remote receiver input IR_bit equ 7 pwm_steps equ 0x20 ; phase angle resolution (1/32 of half mains (110/220Vac) period) off_sts equ 0x00 ; off status on_sts equ 0x01 ; on status tmr2_50 equ 0x9E ; time constant fot 50Hz (0.01/32 s) tmr2_60 equ 0xAF ; time constant fot 60Hz (0.01/32 s) ;********************************************************************** ORG 0x000 ; processor reset vector clrf PCLATH ; clear page bits goto main ; go to beginning of program goto 0 ; a safety measure in case PC gets lost... goto 0 ;##################### Interrupt service routine ########################################### ORG 0x004 ; interrupt vector location movwf w_temp ; save off current W register contents movf STATUS,w ; move status register into W register movwf status_temp ; save off contents of STATUS register bcf STATUS,RP0 ; Bank 0 I_TMR2 btfss PIR1, TMR2IF ; TMR2? goto I_COMP ; No, jump movlw tmr2_50 ; TMR2 service routine - reload timer 2 btfss _50Hz_n_port,_50Hz_n_bit ; load for 60Hz if configured for 60Hz mains movlw tmr2_60 movwf TMR2 ; TMR2 service routine - reload timer 2 movf Phase,w subwf Phase_R,w ; do red btfsc STATUS,Z bsf Out_R_port,Out_R_bit ; turn on output movf Phase,w subwf Phase_G,w ; do green btfsc STATUS,Z bsf Out_G_port,Out_G_bit ; turn on output movf Phase,w subwf Phase_B,w ; do blue btfsc STATUS,Z bsf Out_B_port,Out_B_bit ; turn on output incf Phase ; increment current phase angle bcf PIR1, TMR2IF ; clear interrup flag goto Fine_ISR ; finished I_COMP btfss PIR1, CMIF ; comparator? i.e. zero crossing ? goto Fine_ISR ; No, finished clrf Phase ; yes, clear phase angle then movlw tmr2_50 ; reload timer 2 to resynch to beginning of phase btfss _50Hz_n_port,_50Hz_n_bit ; load for 60Hz if configured for 60Hz mains movlw tmr2_60 movwf TMR2 bcf Out_R_port,Out_R_bit ; turn off output R bcf Out_G_port,Out_G_bit ; turn off output G bcf Out_B_port,Out_B_bit ; turn off output B movf CMCON,F ; needed to clear comparator interrupt flag bcf PIR1,CMIF ; actually clears comparator interrupt flag goto Fine_ISR ; finished Fine_ISR 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 ;############### Main ##################################################################### main bcf STATUS, RP1 bcf STATUS, RP0 ; Select Bank 0 call IO_init ; configure IOs and unused peripherals call Reg_init ; configure variables call Tmr1_init ; configure TMR1 call Tmr2_init ; configure TMR2 call CMP_int_init ; configure COMP INT bsf INTCON, PEIE ; enable peripherals interrupts bsf INTCON, GIE ; global interrupt enable: action ! loop ; check the remote input btfsc IR_port,IR_bit goto loop ; IR detected ! call _w_telecomando ; read from remote: if returns with C == 0 then false alarm.. btfss STATUS,C goto loop ; false alarm, loop. ; now I have remote key code in command_bits register ! movlw on_sts ; check if currently on subwf on_off,w btfss STATUS,Z ; if on, check all keys goto check_on_off_k ; currently off: check on off key only movlw 0x01 ; key 1 subwf command_bits,W btfsc STATUS,Z goto R_up movlw 0x04 ; key 4 subwf command_bits,W btfsc STATUS,Z goto R_down movlw 0x02 ; key 2 subwf command_bits,W btfsc STATUS,Z goto G_up movlw 0x05 ; key 5 subwf command_bits,W btfsc STATUS,Z goto G_down movlw 0x03 ; key 3 subwf command_bits,W btfsc STATUS,Z goto B_up movlw 0x06 ; key 6 subwf command_bits,W btfsc STATUS,Z goto B_down movlw 0x21 ; left arrow on my remote subwf command_bits,W btfsc STATUS,Z goto fr_sin movlw 0x20 ; right arrow on my remote subwf command_bits,W btfsc STATUS,Z goto fr_destra movlw 0x11 ; vol- on my remote: store curren values into preset no... subwf command_bits,W btfsc STATUS,Z goto memo ; now must receive a number to store into preset and store movlw 0x10 ; vol+ on my remote: recall preset no... subwf command_bits,W btfsc STATUS,Z goto recall ; now must receive a number to recall preset from and set. check_on_off_k movlw 0x0C ; on/off key subwf command_bits,W btfsc STATUS,Z goto _turn_on_off ; toggle on / off goto loop memo ; wait and read remote for a preset number (0 to 9) call wait200 ; wait 200ms circa and blink monitor LED memo_1 btfsc IR_port,IR_bit goto memo_1 ; same as 'loop', just accept only keys 0 to 9 and store into EEPROM call _w_telecomando btfss STATUS,C goto memo_1 movlw 0x0A ; check if <= 9 subwf command_bits,W btfsc STATUS,C goto loop ; not a number, get back to main loop movf command_bits,W ; mult. by 3 addwf command_bits,W addwf command_bits,W ; now w holds EEPROM address of chosen preset (for Red, Green is +1, Blue is +2) movwf EE_ad movf Phase_R,W movwf EE_dt call EE_write incf EE_ad ; point to Green movf Phase_G,W movwf EE_dt call EE_write incf EE_ad ; point to Blue movf Phase_B,W movwf EE_dt call EE_write ; done call wait200 ; just wait a little and blink monitor LED goto loop recall ; as in 'memo': just recall preset instead and set RGB values call wait200 recall_1 btfsc IR_port,IR_bit goto recall_1 ; usual remote loop, wait for pulse from remote call _w_telecomando btfss STATUS,C goto recall_1 movlw 0x0A subwf command_bits,W btfsc STATUS,C goto loop ; not a valid number, get back to main loop movf command_bits,W movwf Curr_mem movwf EE_dt movlw (eed & 0xFF) movwf EE_ad call EE_write ; store current preset number into EEprom call EE_to_RGB ; apply changes to RGB call wait200 ; wait and blink monitor LED goto loop fr_sin ; left arrow: surf the presets backward decf Curr_mem ; decrement preset number movlw 0xFF ; check for underflow subwf Curr_mem,W btfss STATUS,Z goto fr_fine movlw 0x09 ; underflow, set to 9 movwf Curr_mem goto fr_fine fr_destra ; right arrow: surf the presets forward incf Curr_mem ; increment preset number movlw 0x0A ; check for overflow throughout 9 subwf Curr_mem,W btfss STATUS,Z goto fr_fine clrf Curr_mem ; overflow, set to 0 goto fr_fine fr_fine movf Curr_mem,W movwf EE_dt ; set current preset as default (into EEPROM) movlw (eed & 0xFF) ; default preset EEPROM address movwf EE_ad call EE_write ; save call EE_to_RGB ; update RGB call wait200 ; wait and blink monitor LED goto loop _w_telecomando ; check for start and toggle bits from remote call wait592 ; delay 1778/3us ; we are at 2/3 of first bit: must be 0 btfsc IR_port,IR_bit goto _w_nok ; no, loop! ; now wait 1778us. Must be 0. call wait1778 btfsc IR_port,IR_bit goto _w_nok ; no, loop allarme! call wait1778 ; ok!, wait for toggle bit and dispose of it. ; now receive 5 "system bits". Don't actually use them. movlw 0x05 movwf tmp _5bits call wait1778 ; wait for one bit rlf IR_port,W rlf system_bits ; move to dest (MSB first) decfsz tmp goto _5bits movlw b'11100000' iorwf system_bits ; clear the three mos significant bits. comf system_bits,F ; Complement. I want a zero to be a IR pulse, ; not really necessary, but helps my 'forma mentis' ; now receive 6 "command bits". Need them. These are the pressed key code movlw 0x06 movwf tmp _6bits call wait1778 ; comments same as above rlf IR_port,W rlf command_bits decfsz tmp goto _6bits movlw b'11000000' iorwf command_bits comf command_bits,F _w_ok bsf STATUS,C ; return SUCCESS return _w_nok bcf STATUS,C ; return FAILURE return wait200 ; wait for 200 ms circa and blink monitor LED bsf Echo_LED_port,Echo_LED_bit ; turn on monitor LED movlw .15 movwf tmp wait200_l call wait006 decfsz tmp goto wait200_l bcf Echo_LED_port,Echo_LED_bit ; turn off monitor LED return wait006 ; wait for 13ms circa clrf TMR1H clrf TMR1L goto tmr1_st wait1778 ; wait for 1778us movlw 0xDD movwf TMR1H movlw 0x46 movwf TMR1L goto tmr1_st wait592 ; wait for 592us movlw 0xF4 movwf TMR1H movlw 0x70 movwf TMR1L tmr1_st bcf PIR1,TMR1IF ; clear TMR1 interrupt flag del TMR1, but do not use interrupt bsf T1CON,TMR1ON loopTmr1 btfss PIR1,TMR1IF ; poll TMR1 IF goto loopTmr1 bcf T1CON,TMR1ON ; time elapsed, turn off timer return R_up bcf INTCON, GIE ; disable interrupts (messing with registers used by ISR) movf Phase_R,f ; zero alread? btfsc STATUS,Z goto R_fatto ; yes, done decf Phase_R ; no, decrement - Remember: zero phase angle -> full light on goto R_fatto ; 'fatto' == 'done' ! R_down bcf INTCON, GIE ; disable interrupts (messing with registers used by ISR) incf Phase_R ; increment to (pwm_steps - 1) max movlw pwm_steps + 1 ; remember: max phase angle -> light off subwf Phase_R,w btfss STATUS,Z ; if > max, make it == max goto R_fatto movlw pwm_steps movwf Phase_R R_fatto bsf INTCON, GIE ; enable interrupts call wait200 goto loop G_up ; same comments as above bcf INTCON, GIE movf Phase_G,f btfsc STATUS,Z goto G_fatto decf Phase_G goto G_fatto G_down bcf INTCON, GIE incf Phase_G movlw pwm_steps + 1 subwf Phase_G,w btfss STATUS,Z goto G_fatto movlw pwm_steps movwf Phase_G G_fatto bsf INTCON, GIE call wait200 goto loop B_up ; same comments as above bcf INTCON, GIE movf Phase_B,f btfsc STATUS,Z goto B_fatto decf Phase_B goto B_fatto B_down bcf INTCON, GIE incf Phase_B movlw pwm_steps + 1 subwf Phase_B,w btfss STATUS,Z goto B_fatto movlw pwm_steps movwf Phase_B B_fatto bsf INTCON, GIE call wait200 goto loop IO_init bsf STATUS,RP0 ; Bank1 movlw b'10000011' ; PORTB <2:6> output, else input movwf TRISB movlw 0xFF ; PORTA input movwf TRISA movlw b'00000000' ; pull ups on PORTB movwf OPTION_REG movlw b'11100001' movwf VRCON ; Vref on, connected to PORTA<2>, low range = 1/24 VDD bcf STATUS,RP0 ; Bank 0 movlw 0x05 movwf CMCON ; Comparator C2, use PORTA<1> e PORTA<2>; on PORTA<2> we have Vref ; ( PORTA<1> is AC input from transformer from zero crossing detection) return Tmr2_init ; used by phase controller movlw b'00000110' ; postscaler = 1; tmr2 on; prescaler = 16 movwf T2CON bcf PIR1, TMR2IF ; clear interrupt flag bsf STATUS,RP0 ; Bank1 movlw 0xFF ; init PR2 movwf PR2 bsf PIE1, TMR2IE ; enable interrupt bcf STATUS,RP0 ; Bank0 bsf INTCON, PEIE ; enable peripheral interrupt return Tmr1_init ; used by remote control routine clrf T1CON ; prescaler = 1, source Fosc/4 , disabled bsf STATUS,RP0 ; Bank1 bcf PIE1, TMR1IE ; disable interrupt bcf STATUS,RP0 ; Bank0 return CMP_int_init ; used to detect zero crossing of mains phase bcf PIR1,CMIF ; clear pending interrupt bsf STATUS,RP0 ; Bank1 bsf PIE1,CMIE bcf STATUS,RP0 ; Bank0 return Reg_init clrf Phase bcf Out_R_port,Out_R_bit ; turn off outputs bcf Out_G_port,Out_G_bit bcf Out_B_port,Out_B_bit bcf Echo_LED_port,Echo_LED_bit ; turn off monitor LED movlw off_sts movwf on_off ; initial status is 'off' return _turn_on_off bcf INTCON, GIE ; disable interrupts (going to mess with registers handled by interrupt. movf on_off,f ; toggle between on and off status btfss STATUS,Z goto sts_is_on sts_is_off movlw on_sts movwf on_off ; now status is 'on' movlw (eed & 0xFF) ; load last used preset movwf EE_ad call EE_read movwf Curr_mem call EE_to_RGB bsf INTCON, GIE ; enable interrupt call wait200 goto loop sts_is_on movlw off_sts movwf on_off ; now status is 'off' movlw pwm_steps movwf Phase_R ; turn off lights movwf Phase_G movwf Phase_B bsf INTCON, GIE ; enable interrupt call wait200 goto loop EE_to_RGB ; recupera dalla EEprom i valori RGB del preset indicato da Curr_mem movf Curr_mem,W ; prendo l'indice del preset e lo molt.per 3 addwf Curr_mem,W addwf Curr_mem,W ; x3 movwf EE_ad call EE_read ; legge R movwf Phase_R ; e lo scrive incf EE_ad call EE_read ; legge G movwf Phase_G ; e lo scrive incf EE_ad call EE_read ; legge B movwf Phase_B ; e lo scrive return EE_write ; store at EEprom address EE_ad data at EE_dt movf EE_dt,W bcf PIR1,EEIF ; clear write completed flag bsf STATUS,RP0 ; sel Bank 1 movwf EEDATA movf EE_ad,W movwf EEADR bcf INTCON,GIE ; Dis. interrupts bsf EECON1,WREN ; enable write movlw 0x55 ; > mandatory security sequence as per specification movwf EECON2 ; > write 55h movlw 0xAA ; > movwf EECON2 ; > write AAh bsf EECON1,WR ; this is a 'write' bcf STATUS,RP0 ; sel Bank 0 EE_dl_loop1 btfss PIR1,EEIF ; wait for write complete goto EE_dl_loop1 bsf STATUS,RP0 ; sel Bank 1 bcf EECON1,EEIF ; clear EE write complete flag bcf EECON1,WREN ; disable write la scrittura bsf INTCON,GIE ; enable interrupts. bcf STATUS,RP0 ; sel Bank 0 return EE_read movf EE_ad,W ; EEprom address to read from bsf STATUS,RP0 ; sel Bank 1 movwf EEADR bsf EECON1, RD ; this is a read movf EEDATA, W ; W = EEDATA movwf EE_dt bcf STATUS,RP0 ; sel Bank 0 return fill (goto 0), (0x400-$) org 0x2100 ; colours preset (10 in tutto, 3 per colore) DE 0x00,0x00,0x1F ; my presets, for now 0x00 e 0x1F. Can DE 0x00,0x1F,0x00 ; be modified via IR remote control DE 0x1F,0x00,0x00 DE 0x1F,0x1F,0x00 DE 0x1F,0x00,0x1F DE 0x00,0x1F,0x1F DE 0x1F,0x1F,0x1F DE 0x10,0x1F,0x1F DE 0x1F,0x1F,0x10 DE 0x10,0x10,0x10 eed DE 0x00 ; default preset. Can be modified via IR remote control end