Home Page . . . . . . . Email: . . . . . . . . neelandan-at-gmãil·çöm

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;Speaking Clock - PIC16F84A and ISD2560 #### 26 may 2004 ####
;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
; Adapted from the routines in Blick's propellor clock
	list p=16f84A
        radix   hex
        __config 0x3ff1     ;wdt off, XT osc

;------------------------------------------------------------
;       cpu equates (memory map)
indf	equ	0x00
tmr0	equ	0x01
pcl     equ	0x02
status	equ	0x03
fsr 	equ	0x04
porta	equ	0x05
portb   equ	0x06
eedata	equ	0x08
eeadr	equ	0x09
pclath	equ	0x0a
intcon	equ	0x0b
opti0n	equ	0x81
trisa	equ	0x85
trisb	equ	0x86
eecon1	equ	0x88
eecon2	equ	0x89
;------------------------------------------------------------
;	bit equates
c	equ	0
dc	equ	1
z	equ	2
rp0	equ	5
gie	equ	7
;
;------------------------------------------------------------
;location of words in ISD2560
;------------------------------------------------------------
TIME        equ	0x01 ;"The time now is ... "
ONE         equ	0x02
TWO         equ	0x03
THREE       equ	0x04
FOUR        equ	0x05
FIVE        equ	0x06
SIX         equ	0x07
SEVEN       equ	0x08
EIGHT       equ	0x09
NINE        equ	0x0a
TEN         equ	0x0b
ELEVEN      equ	0x0c
TWELVE      equ	0x0d
THIRTEEN    equ	0x0e
FOURTEEN    equ	0x0f
FIFTEEN     equ	0x10
SIXTEEN     equ	0x11
SEVENTEEN   equ	0x12
EIGHTEEN    equ	0x13
NINETEEN    equ	0x14
TWENTY      equ	0x15
THIRTY      equ	0x16
FORTY       equ	0x17
FIFTY       equ	0x18
MINUTES     equ	0x19
PAST        equ	0x1a
OCLOCK      equ	0x1b
;
;---------------------------------------------------------------
; Variables in RAM.
;---------------------------------------------------------------
safe_w	equ	0x0c	;w stored here during int service
safe_s	equ	0x0d	;status stored here likewise
flags	equ	0x0e	;array of bits as semaphores
hours	equ	0x0f	;in display format, not hex(01-12)
minutes	equ	0x010	;00 to 59 BCD, display format
seconds	equ	0x011	;00 to 59 hex - will have to be
bigtick_hi equ	0x012	;changed if seconds are to be displayed
bigtick_lo equ	0x013	;incremented each interrupt
keys	equ	0x014	;value read from the key port
scratch	equ	0x015	;scratch value
tick	equ	0x016	;used by delay
count	equ	0x017	;the position of words to say
timera	equ	0x19	;delay timer
timerb	equ	0x1a	;delay timer
timerc	equ	0x1b	;delay timer
words	equ	0x1c	;no.of words to be spoken
word0	equ	0x1d	;first phrase, always the same
word1	equ	0x1e	;the first word of the time
word2	equ	0x1f	;the second word
word3	equ	0x20	;the third word
word4	equ	0x21	;the fourth 
word5	equ	0x22	;the fifth
word6	equ	0x23	;the sixth and last.
;
;
;
;---------------------------------------------------------------
;
START_OF_WORDS  equ	word0	;start location for word1,word2,
                            ;word3,word4,word5,word6  in memory
;
;---------------------------------------------------------------
;Port A definitions 
;
PORTA_CONFIG	equ	b'00001000'
ISD_CE		equ	0 ;chip enable of isd2560 ------ OUT
ISD_M0		equ	1 ;mode0 select of isd2560 ----- OUT
ISD_PD		equ	2 ;power down mode of isd2560 -- OUT
ISD_EOM		equ	3 ;end of word indicator input - IN
BLINK		equ	4 ;Blinking LED ---------------- OUT
CLAP		equ	4 ;Clap det input--------------- IN
;       Note: the blinking LED and mic input share 
;       one line, because this is open drain and
;       it simplifies setting up the mic volume
;       with the PIC unplugged.
;---------------------------------------------------------------
;
; Port B definitions - keys for setting the time
PORTB_CONFIG equ 0xff
MIN_SET		equ 1		;Minutes set button----------- IN
TEN_SET		equ 2		;Set Tens of MIN-------------- IN
HR_SET		equ 3		;Hours set button------------- IN
;
;---------------------------------------------------------------
;
PORTA_INIT	equ	0xff	;all ones
PORTB_INIT	equ	0xff	;all ones
;
; Start of ROM
;--------
		org	0x00	;Start of code spaceStart
    call	Ram_init	;set variables to nice values
    call	Timer_init	;start timer based interrupt
    call	port_init
    goto    Circle
;--------
;  INTERRUPT SERVICE ROUTINE
;--------
		org	0x04	;interrupt vector
Intsvc	movwf	safe_w		;save w
		swapf	status,w ;exchange w and f
		movwf	safe_s	;save status with swapped 
                                ;nibbles
;--------
; done saving, now start working
;--------
;_____________________________________________________________
;
                        ;4MHz = 4 x 250 x 4000 Hz
	movlw	0x08	;adjust timer reload value
	addwf	tmr0,f	;to make it count to 250
;_____________________________________________________________
; 4000 interrupts every second. Increment the bigtick each time.
	incf	bigtick_lo,f
	btfsc	status,z
	incfsz	bigtick_hi,f
	goto	Bigtick_out
;--------
; here? bigtick has rolled over to zero and one second 
; has passed. Reload bigtick and increment seconds
;--------
	movlw	0xF0		;4000 = 0x0FA0
	movwf	bigtick_hi	;0 - 0x0FA0 = 0xF060
	movlw	0x60
	movwf	bigtick_lo
;-----------
; Increment seconds
;-----------
Inc_sec incf 	seconds,f
	movlw	 0xc4		;check for 60 sec
	addwf 	seconds,w
	btfss 	status,c	;if carry set then not minute
	goto	Bigtick_out	;so return
	clrf  	seconds		;clear seconds
	bsf	flags,1		;notify Keep_time
Bigtick_out
;--------
; done working, start restoring
;--------
	swapf safe_s,w	;fetch status, reswap nibbles
	movwf status	;restore status
	swapf safe_w,f	;swap nibbles in preparation
	swapf safe_w,w	;for the swap restoration of w
	bcf   intcon,2	;clear interrupt flag before
	retfie		;returning from interrupt
;
;-------------------------------------------------------------------
;   Where are the words stored in the ISD chip?
;-------------------------------------------------------------------
;
lookup_table		;location of words in ISD device				
	addwf	pcl,f	;BCD  - word to say
	retlw	TWELVE	;0x12 - twelve - if zero say twelve anyway
	retlw	ONE	;0x01 - one
	retlw	TWO	;0x02 - two	
	retlw	THREE	;0x03 - three
	retlw	FOUR	;0x04 - four
	retlw	FIVE	;0x05 - five
	retlw	SIX	;0x06 - six
	retlw	SEVEN	;0x07 - seven
	retlw	EIGHT	;0x08 - eight
	retlw	NINE	;0x09 - nine
	retlw	0x01	;0x0f - not used
	retlw	0x01	;0x0a - not used
	retlw	0x01	;0x0b - not used
	retlw	0x01	;0x0c - not used
	retlw	0x01	;0x0d - not used
	retlw	0x01	;0x0e - not used
	retlw	TEN	;0x10 - ten
	retlw	ELEVEN	;0x11 - eleven
	retlw	TWELVE	;0x12 - twelve
	retlw	THIRTEEN;0x13 - thirteen
	retlw	FOURTEEN;0x14 - fourteen
	retlw	FIFTEEN	;0x15 - fifteen
	retlw	SIXTEEN	;0x16 - sixteen
	retlw	SEVENTEEN;0x17 - seventeen
	retlw	EIGHTEEN;0x18 - eighteen
	retlw	NINETEEN;0x19 - nineteen
	retlw	0x01    ;additional padding
	retlw	0x01    ;just in case

;--------
;  SUBROUTINES STARTING HERE
;--------
; clear important bits of ram
;--------
Ram_init movlw	0xf0
	movwf	bigtick_hi
	movlw	0x60
	movwf	bigtick_lo	
	movlw	0x12		;clocks always start
        movwf	hours		;at 12:00. It's official.
	movlw	0x00
	movwf	minutes
	movwf	seconds
	movwf	flags	
	return
;--------
; get timer-based interrupts going
;--------
Timer_init bcf	intcon,2	;clear tmr0 int flag
	bsf	intcon,gie	;enable global interrupts
	bsf	intcon,5	;enable tmr0 int
	clrf	tmr0		;clear timer
	movlw opti0n		;Work around the OPTION
	movwf	fsr		;warning
	movlw	b'01011000'	;set up timer. No prescaler
	movwf	indf		;Port B pullups enabled
	clrf	tmr0		;start timer
	retlw	0
;
;---------------------------------------------------------------
;  Port initialiser 
port_init
	bsf status,rp0          ;select correct bank
	movlw	PORTA_CONFIG
	movwf	trisa^80h
	movlw	PORTB_CONFIG
	movwf	trisb^80h
	bcf	status,rp0      ;back to bank zero
	movlw	PORTA_INIT
	movwf	porta
	movlw	PORTB_INIT
	movwf	portb
	return
;
;---------------------------------------------------------------
;  Time delays
wait
	clrw
	goto $+2
wait1	
	movlw	0x15
	movwf	timerb	
loop81	
	movlw	0xff		;number of wait states
	movwf	timera
loop91	
	decfsz	timera,f
	goto	loop91
	decfsz	timerb,f
	goto	loop81
	retlw	0
;--------
; a short delay routine, 1mS maximum 
;--------
Delay1	 clrw	
Delay	movwf	tick
Delay_loop
	decfsz	tick,f
	goto	Delay_loop	;w is not damaged, so Delay can
	return			;be recalled without reloading
;--------
; test for keypress and call time adjust if needed
;--------
Check_keys movf	portb,w	;get port "b"
	xorwf	keys,w		;compare with previous
	andlw	b'00001110'	;only care about button pins
	btfsc	status,z	;zero set=no buttons
	retlw	0		;return
	xorwf	keys,f		;store key value
        bcf     porta,BLINK     ;turn LED on for feedback
	movlw	0x64		;a fairly long delay will
	movwf	scratch		;prevent key bounces
Key_delay movlw	0xFF
	call	Delay
	decfsz 	scratch,f
	goto	Key_delay
	movf	portb,w	
	btfss	keys,MIN_SET    ;test "minutes" button
	goto	Inc_mins
	btfss	keys,TEN_SET    ;test "tens" button
	goto	Inc_tens
	btfss	keys,HR_SET	;test "hours" button
	goto	Inc_hours
	retlw	0		;must be a glitch. yeah, right!
;--------
; increment ten minutes
;--------
Inc_tens movlw	0x0A
	movwf	scratch		;scratch has ten
Inc_tens_loop
        call	Inc_mins
	decfsz	scratch,f
	goto	Inc_tens_loop	;another minute added
	retlw	0
;--------
; increment one hour
;--------
Inc_hours movlw	0x12
	xorwf	hours,w
	btfsc	status,z
	goto	Inc_hours_12
	movlw	0x07		;this part gets a little sloppy
	addwf	hours,w
	movlw	0x07
	btfss	status,dc
	movlw	1
	addwf	hours,f
	retlw	0
Inc_hours_12 movlw	0x01
	movwf	hours
	retlw	0
;--------
; increment the time based on flags,1 as sent by interrupt routine
; Inc_mins loop also used by time-setting routine
;--------
Keep_time btfss	flags,1	;the minutes flag
	retlw	0	;not this time
	bcf	flags,1	;clear the minutes flag
Inc_mins movlw	0x07	;start incrementing time
	addwf	minutes,w ;add 7 minutes into w
	btfsc	status,dc ;did adding 7 cause digit carry?
	goto	Sixty_mins ;then test for an hour change
	incf	minutes,f  ;otherwise add 1 for real
	retlw	0	;and go back
Sixty_mins 
        movwf	minutes	;save the minutes
	movlw	0x60	;test for 60
	xorwf	minutes,w ;are minutes at 60?
	btfss	status,z
	retlw	0	;no? go back
	clrf	minutes	;otherwise zero minutes
	goto	Inc_hours ;and increment hours
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%%                                                         %%%%%%%%%
;%%%%		LIST OF WORDS NECESSARY                       %%%%%%%%%
;%%%%                                                         %%%%%%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;%%%% 1. Counting words                                       %%%%%%%%%
;%%%%	One, two, three, four, five, six, seven, eight, nine, %%%%%%%%%
;%%%%	ten, eleven, twelve, thirteen, fourteen, fifteen,     %%%%%%%%%
;%%%%	sixteen, seventeen, eighteen, nineteen, twenty,       %%%%%%%%%
;%%%%   thirty, fourty, fifty                                 %%%%%%%%%
;%%%% 2. 'Glue' words                                         %%%%%%%%%
;%%%%	Minutes, past, O'clock, The time now is               %%%%%%%%%
;%%%%                                                         %%%%%%%%%
;%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;**********************************************************************
;Name		: GetWords
;Description    : used to ascertain which words are needed to say time
;Inputs		: none
;Outputs	: none
;**********************************************************************
GetWords
	movlw	TIME		;Start by saying "The time now is ..."
	movwf	word0
sub00h
	movf	minutes,w	;check whether minutes equal zero
	bz	min_00		;if true then goto min_00
	goto	next3		;if false then goto next3
;
;**********************************************************************
; 1st word = hour 0-12
; 2nd word = eng:'o'clock'
; 3rd word = 0xff
; 4th word = 0xff
; 5th word = 0xff
; 6th word = 0xff
;**********************************************************************
min_00
	movf	hours,w
	call	lookup_table
	movwf	word1
	movlw	OCLOCK
	movwf	word2
	movlw	0xff
	movwf	word3
	return		;all words are known - return
;
;**********************************************************************
; 1st word = minute
; 2nd word = 'MINUTES'
; 3rd word = 'PAST'
; 4th word = hour
; 5th word = 0xff
; 6th word = 0xff
;**********************************************************************
next3
	movf	minutes,w
	andlw	0xf0	; mask - test upper nibble only!
	xorlw	0x20    ; xor with 20 and check whether zero
	bz	min_20	;if msb minute=2 then goto min_20
	xorlw	0x10	;10 = 20 xor 30
	bz	min_30	;if msb minute=3 then goto min_30
	xorlw	0x70    ;70 = 20 xor 30 xor 40
	bz	min_40	;if msb minute=4 then goto min_40
	xorlw	0x10    ;10 = 20 xor 30 xor 40 xor 50
	bz	min_50	;if msb minute=5 then goto min_50
			;else msb minute=0, lsb minute=1-19
;
;**********************************************************************
; 1st word = number 1-19
;**********************************************************************
min_10
	movf	minutes,w
	call	lookup_table
	movwf	word1
further
	goto 	minlsb0
;
;**********************************************************************
; 1st word = twenty,thirty,fourty or fifty
;**********************************************************************
min_20
	movlw	TWENTY
	movwf	word1
	goto	next4
min_30
	movlw	THIRTY
	movwf	word1
	goto	next4
min_40
	movlw	FORTY
	movwf	word1
	goto	next4
min_50
	movlw	FIFTY
	movwf	word1

next4
	movf	minutes,w ;test if the lsb minute is zero
	andlw	0x0f	;mask - test lower nibble only!
	xorlw	0x00
	bz	minlsb0	;if true goto minlsb0
			;else carry on
;
;**********************************************************************
; 2nd word = minute lsb 1-9
; 3rd word = 'MINUTES'
; 4th word = 'PAST'
; 5th word = hour 1-12
; 6th word = 0xff
;**********************************************************************
	movf	minutes,w
	andlw	0x0f
	call	lookup_table
	movwf	word2
	movlw	MINUTES
	movwf	word3
	movlw	PAST
	movwf	word4
	movf	hours,w
	call	lookup_table
	movwf	word5
	movlw	0xff
	movwf	word6
	return		;all words are known - return
;
;**********************************************************************
; 2nd word = 'MINUTES'
; 3rd word = 'PAST'
; 4th word = hour 1-12
; 5th word = 0xff
; 6th word = 0xff
;**********************************************************************
minlsb0
	movlw	MINUTES
	movwf	word2
	movlw	PAST
	movwf	word3
	movf	hours,w
	call	lookup_table
	movwf	word4
	movlw	0xff
	movwf	word5
	return		;all words are known - return

;----------------------------------------------------------
; 	check for a hand clap
clap_det

	bsf	porta,BLINK	;turn LED off
	btfss   porta,CLAP	;someone is clapping
        goto    isd

        movf    bigtick_hi,w    ;get msb of tick
        xorlw   0xf0            ;check for start
        bnz     $+2
        bcf     porta,BLINK     ;turn LED ON
        return
;
;**********************************************************************
;Name		: isd
;Description    : driver for speech chip ISD2560
;Inputs		: none
;Outputs	: none
;**********************************************************************
isd 	
	call	GetWords	;get words to tell time
	movlw	0x07		;maximum no. of words to broadcast
	movwf	words		;store in  register
	movlw	START_OF_WORDS  ;memory location of word0
	movwf	fsr		;indirect addressing - pointer
talk	
	movf	indf,w		;store word in count - 
	movwf	count		;how many time we should pulse ISD_/CE
	addlw	0xe2		;is that count > words stored in ISD?
	bc	theend		;if so, end of words to say.
	bsf	porta,BLINK	;turn the LED off, again.
	bcf	porta,ISD_PD	;turn isd ON
	bsf	porta,ISD_CE	;make /CE inactive
	bsf	porta,ISD_M0	;take isd2560 MO pin high
	call	wait		;wait for it to stabilise
again	
	decf	count,f		;pulse ISD_/CE signal, location-1 times
	bz	play		;reached, then goto play
	bcf	porta,ISD_CE	;take isd2560 /CE pin low
	call	Delay1
	bsf	porta,ISD_CE	;take isd2560 /CE pin high
	call	Delay1
	goto	again		;pulse again or play word
play
	bsf	porta,BLINK	;turn it off, again.
	bcf	porta,ISD_M0	;take isd2560 M0 pin low
	call	Delay1
	bcf	porta,ISD_CE	;pulse isd2560 /CE pin
	bsf	porta,ISD_CE
noready	
 	btfsc	porta,ISD_EOM	;see if isd2560 has finished
 	goto	noready		;if /EOM=1 then wait until finished
	bsf	porta,ISD_PD	;take PD pin high - reset isd's address
	bsf	porta,ISD_CE	;take /CE high, too
	call	wait1		;wait atleast 12.5msec before PD low
	bcf	porta,ISD_PD	;wake the recorder up 
	call	wait1		;wait for device to power up
	incf	fsr,f		;get address of next word
	decfsz 	words,f		;reduce the number of words to say
	goto	talk		;still another word to broadcast
theend	
	bsf	porta,ISD_PD	;power down the speech recorder
	return			;word=0xff/or all said, 
				;no more words to broadcast 
				;- return from routine					
;___________________________________________________________________
;
;
; End of subroutines   
; Main Program starts here  
;
;___________________________________________________________________
;
;--------
; Done initializing, start the endless loop.
;--------
;
Circle		;begin the big loop
;
;--------
;  check if somebody has clapped and speak the time
;--------
;
  call 	clap_det
;
;--------
;
; check seconds flag and increment time if a minute has passed
;	
;--------
;
 call	Keep_time
;
;--------
;
; check keyboard and adjust time
;
;--------
;
 call	Check_keys
;
;--------
; gentlemen, that's a clock, keep it rolling
;--------
;
 goto	Circle
;
;--------

 end ;end of file
;--------
;______________________________________________________________________

Home Page . . . . . . . Email: . . . . . . . . neelandan-at-gmãil·çöm