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