; ******************************************** ; Electronic Die ; Pete Griffiths, 17 May 2004 ; email: picdie@petesworld.demon.co.uk ; Copyright Pete Griffiths 2004 ; This program is free software; you can redistribute ; it and/or modify it under the terms of the GNU ; General Public License version 2 as published by ; the Free Software Foundation ; ; Use PIC type 12F675 with internal 4Mhz clock ; ; ******************************************** ; #include "P12F675.INC" ; ; You must preserve the OSCCAL data in the destination device ; before writing this code to it. ; ; Set config register for: ; Code / Memory protection off ; Brown-out detect off ; MCLR disabled (use pin for IO) ; Watch Dog timer disabled ; Power on reset delay enabled ; Enable Internal Clock Osc, (set pins for IO) ; __CONFIG _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_OFF & _WDT_OFF & _PWRTE_ON & _INTRC_OSC_NOCLKOUT ; ******************************************** ; Equates and Variables cblock 0x20 dtime ;delay sub ltime ;delay sub rollCnt ;pseudo roll dieNum ;pseudo roll throw ;actual throw num blankto ;blank time out count endc ; ******************************************** ; Point reset vector to program start ; org 0x00 nop nop goto _main ; ******************************************** ; SUBROUTINE ; Lookup Table for GPIO Port Display Data ; Lookup table takes value in W and returns with ; port data for die in W ; Output low = LED on, high = LED off since PIC can sink ; 25mA but only source 20mA. ; ; As the 12F675 GPIO 3 port can only be configured as an input ; the LEDs connect to GPIO ports 0,1,2 and 4 ; where A=GPIO 0, B=1, C=2 and D=4. Arrange LEDs as shown below. ; For 3-4.5v supply, use 270R resistors between each LED and Vdd ; B C ; D A D ; C B ; _lookup addwf PCL,F ;Add W to PCL for computed goto. nop ; This needed because we enter with ; 1 =< W =< 6 and table reads with 0 offset ; DxBCA retlw b'10110' ;die 1 retlw b'10101' ;die 2 retlw b'10010' ;die 3 retlw b'10001' ;die 4 retlw b'10000' ;die 5 retlw b'00001' ;die 6 ; ; ******************************************** ; Subroutine ; Accurate delay routines ; Two routines. Both called with required delay period in W ; One has delay of W x 1mS. Other has delay of W x 100mS ; Accurate for 4Mhz clock ; _Delay movwf dtime ; Call for W x 1mS __Dcall call __1mS decfsz dtime,F goto __Dcall __DlyEnd return _LDelay movwf ltime ; Call for W x 100mS __Dlcall movlw d'100' call _Delay decfsz ltime,F goto __Dlcall return __1mS movlw 0xC6 _next nop addlw 0xFF btfss STATUS,Z goto _next nop nop nop return ; ******************************************** ; SUBROUTINE ; Displays a rolling die thats starts fast and slows to ; a stop about 3 seconds after SW1 released. ; _roller movlw 0x30 ; must be even number movwf rollCnt _rsetone clrf dieNum _rloop incf dieNum,F movf dieNum,W sublw 0x07 btfsc STATUS,Z goto _rsetone movf dieNum,W call _lookup movwf GPIO movf rollCnt,W call _Delay incf rollCnt,F incf rollCnt,F movf rollCnt,W sublw 0x80 ; must be an even number btfss STATUS,Z ; or it never breaks out of the loop goto _rloop return ; ; ******************************************** ; Entry point for program from Reset ; Initialise GPIO 5,3 input, 4,2,1,0 outputs ; Set GPIO to digital IO ; Set INTCON register for interupt on GPIO 5 change ; disable Global interupts so it wakes from sleep ; but doesn't service interupt. ; Enable weak pull up on GPIO5 input. ; set outputs to high so LEDs are off _intvect _main bcf STATUS,RP0 ; Sel Bank 0 movlw 0x07 ; movwf CMCON ; Disable Analogue on GPIO pins movlw b'00001001' ; movwf INTCON ; Enable GPIO state change int ; See 12F675 data sheet P19 bsf STATUS,RP0 ; Sel Bank 1 call 0x3FF ; calls RETLW with factory setting movwf OSCCAL ; Set int OSC to factory calibrated clrf ANSEL ; Disable Analogue on GPIO pins movlw b'11101000' ; Specifiy GPIO port direction movwf TRISIO ; Set GPIO ports as xxIOIOOO bcf OPTION_REG,NOT_GPPU ; enable weak pull-up bsf WPU, 5 ; enable wpu on GPIO 5 only bsf IOC, 5 ; enable Int-On-Change GPIO 5 bcf STATUS,RP0 ; Sel Bank 0 movlw 0x20 ; set up 20mS wait call _Delay ; after reset ; ******************************************** ; put PIC to sleep and wake on SW1 / GPIO 5 ; then 'throw' die ; port GPIO 5 uses internal weak pull up with SW1 n/o to ground. ; Uses interupt on change to wake PIC ; _sleepsw1 movf GPIO,W ; Read GPIO clears Int-On-Change flag ; must read into W not back to F ; as it reads port not the output latch ; which may result in output data being ; inadvertently altered ; see 12F675 data sheet, P19 Section 3.1 bcf INTCON,GPIF bsf STATUS,RP0 ; Sel bank 1 movlw 0xFF ; Setup W for TRISIO all input movwf TRISIO ; Write to TRISIO. Reduce power in sleep mode sleep ; Go to sleep nop ; movlw b'11101000' ; Wake from sleep and set movwf TRISIO ; TRISIO for correct input and output bcf STATUS,RP0 ; Sel Bank 0 movf GPIO,W ; Read GPIO register bcf INTCON,GPIF ; and clear GPIF flag in interrupt register. movlw d'5' ; load W with 5 (5mS) call _Delay ; and call delay to debounce switch down. ; ******************************************** ; once PIC wakes from sleep the throw counter ; runs while SW1 is held down ; Given the 4Mhz clock speed of the PIC this results in a ; fairly random number being generated ; ; Turn of LEDs while SW1 is down ; _throwdie movlw 0x17 ; Turn off LEDs while counting movwf GPIO clrf throw ; Reset throw to 0 for each new throw. ; Number of instruction cycles per loop for this block is 12. Code _evenup wastes cycles to ; ensure that the same number of cycles elapse between checks of the SW1 switch so that ; there is an equal probability of it being released on any value of throw count. ; Block will loop 83,333 times per second with 4Mhz clock (1uS) instruction cycle ; or 833 times in 10mS. Since the throw is determined by the length of time the SW1 switch ; is held down, it results in a fairly even distribution of numbers. Tested with 500 ; consecutive throws gave a mean value of 3.492. _loop incf throw,F ; add 1 to throw #1 movf throw,W ; get throw and put in W #1 xorlw 7 ; compare to 7 #1 ; (result is only 0 when both No's equal) btfss STATUS,Z ; if less than 7 #1 or #2 if Z=1 goto _evenup ; go to _evenup #2 only for Z=0 clrf throw ; else set throw #1 Z=1 incf throw,F ; count back to 1 #1 Z=1 goto _sw1down ; and goto sw1 check #2 Z=1 _evenup nop ; This is here to even out the #1 Z=0 nop ; number of instructions executed #1 Z=0 nop ; when throw count isn't reset to 1 #1 Z=0 _sw1down btfss GPIO,5 ; Is SW1 down? #1 goto _loop ; keep counting if it is #2 ; we don't debounce the swich because ; any bounce just helps to make it ; more random ; ******************************************** ; switch released ; call rolling subroutine ; then display the thrown number on the LEDs ; call _roller ; Switch has been released so call rolling die sub movf throw,W ; Finished rolling it so get throw value call _lookup ; call display data lookup movwf GPIO ; and display the throw on the LEDs ; ******************************************** ; display thrown number for 20 seconds ; then blank display to save battery ; and go back to sleep awaiting next 'throw' ; checks for SW1 throw while displaying previous throw ; in which case it throws again without sleeping. ; movlw 0xD0 ; Set count for 20 second display timeout movwf blankto ; move to working file register _blankdly movlw 0x01 ; call delay subroutine call _LDelay ; with W = 100mS btfss GPIO,5 ; on return check if SW1 has been pressed goto _throwdie ; if it has, goto the main throw code decfsz blankto,F ; otherwise decrement display timeout goto _blankdly ; and carry on with the timeout count ; the fadeout section of code causes the LEDs to fade down at the end of the ; 20 second timeout period. This uses a single count value for the fadeout period ; and this is used to control the on/off duty cycle of the LEDs as they fade down ; so the period remains constant but the on/off ratio goes from 99/1 -> 1/99 _fadeout movlw 0xFE ; Set length of fadeout period > = longer movwf blankto ; but don't set to 0xFF _fading movf throw,w ; get last throw number call _lookup ; find LED data for throw number movwf GPIO ; display throw movf blankto,W ; get value in blankto, put in W call _fadelay ; and call fade delay subroutine movlw 0x17 ; set data for all LEDs off in W movwf GPIO ; write to LEDS comf blankto,W ; complement blankto and put in W call _fadelay ; and call fade delay routine decfsz blankto,F ; -1 from blankto goto _fading ; keep fading until it reaches 0 goto _sleepsw1 ; goto sleep. ; ********************************************* ; Subroutine ; used by fadeout to give slow fade of LEDs before ; sleeping _fadelay movwf dtime ; Enter with W set to outer loop count so save this _fouter btfss GPIO,5 ; check if SW1 has been pressed goto _throwdie ; if it has, goto the main throw code movlw 0x16 ; Inner loop count movwf ltime _finner decfsz ltime,F goto _finner decfsz dtime,F goto _fouter return ; ********************************************* dt " Electronic Die for 12F675 - Pete Griffiths 2004. R6.17052004" end