--
-- name    : cucu.jal - zero error clock
-- ver     : 2.1, with interrupts using Roman's method from:
--           http://www.ezy.net.au/~fastvid/one_sec.htm   
--           4.00MHz quartz, alarm and clock data stored in eeprom
-- author  : Vasile Surducan http://www.geocities.com/vsurducan
-- date    : december 2001
-- purpose : 4 digit common anodes clock using modified AN615
--           Ra0: minutes, Ra1: tens of minutes, Ra2: hours, Ra3: tens of
--           hours all active in low state, all conected through 5k6 resistor
--           at pnp transistors bases, emiters at +5V, colectors to anodes
--           Rb0...Rb6=a...f segments all active in low state, conected whith
--           47 ohm resistors to display segments
--           Rb4 button minutes, Rb5 button alarm,
--           Rb6 button hours, all active in low state
--           one buttons pin conected to Rb4, Rb5, Rb6 trough a 3K9 resistor,
--           10k resistor conected to +5V from same point, another buttons pin
--           conected to ground
--           Rb7 conected to buzzer (50k in base of a npn transistor, colector
--           to buzzer, emiter at ground, another pin of buzzer to +5V)


include 16f84_4
include jpic
include jseven
include jdelay

var byte sel_min_units   = 0b_1110
var byte sel_min_tens    = 0b_1101
var byte sel_hours_units = 0b_1011
var byte sel_hours_tens  = 0b_0111
var byte sel_blank       = 0b_1111
port_a_direction = all_output
pin_b7_direction = output
var byte sec         = 0
var byte min_units   = 0
var byte min_tens    = 0
var byte hours_units = 2
var byte hours_tens  = 1
var byte amin_units
var byte amin_tens
var byte ahours_units
var byte ahours_tens
var bit but_flag   = low
var bit alarm_flag = low
var bit cucu_flag  = low
var byte roman_hi  = 0x_0f
var byte roman_mid = 0x_43
var byte roman_lo  = 0x_40

 
 clear_watchdog
 bank_1
 option = 0x_88	     -- prescaler asigned to watchdog, no pullup		
 bank_0
 tmr0 = 0
 asm bsf intcon_gie  -- enable interrupts
 asm bsf intcon_t0ie -- enable overflow interrupts


procedure time is
  asm incf sec, f
 if sec == 60 then 
  asm incf min_units, f
  asm clrf sec
 end if

 if min_units == 10 then
  asm clrf min_units
  asm incf min_tens, f 
 end if
 if min_tens == 6 then
  asm clrf min_tens 
  asm incf hours_units, f 
 end if

 if hours_units == 10 then
  asm clrf hours_units 
  asm incf hours_tens, f
 end if
 if hours_tens == 2 then
  if hours_units == 4 then
   asm clrf hours_tens
   asm clrf hours_units
  end if
 end if

end procedure

procedure isr is
pragma interrupt
 assembler
  local out
     tstf roman_mid      -- roman_mid = 0 ?
     skpnz
     decf roman_hi, f    -- yes, roman_mid = 0 so decrement roman_hi
     decfsz roman_mid, f -- roman_mid = roman_mid - 256
     goto out            -- if nz then not one second yet
     tstf roman_hi       -- test roman_hi
     skpz                -- if z then roman_hi and roman_mid are 0
     goto out            -- if nz then not one second yet
     movlw 0x_0f         
     movwf roman_hi      -- load msb
     movlw 0x_42
     movwf roman_mid     -- load mid
     movlw 0x_40
     addwf roman_lo, f   -- add to remainder already in lsb
     skpnc               -- no overflow
     incf roman_mid, f   -- if c, roman_lo overflowed, increment roman_mid
     call time           -- call user task
out: bcf intcon_t0if     -- reset interrupt flag
  end assembler  
end procedure

procedure tick ( byte in ton ) is
  for 20 loop
   pin_b7 = high
   delay_200us ( ton )
   pin_b7 = low
   delay_200us ( ton )
  end loop
end procedure


procedure button_minutes is
port_a = sel_blank
pin_b4_direction = input
 if ( ! pin_b4 ) then
  tick ( 2 )
  asm incf min_units, f
   if min_units == 10 then
    asm clrf min_units
    asm incf min_tens, f 
    end if
   if min_tens == 6 then
    asm clrf min_tens 
    asm incf hours_units, f 
   end if
 delay_10mS ( 5 )
 end if
pin_b4_direction = output
end procedure

procedure button_hours is
port_a = sel_blank		; display off
pin_b6_direction = input	; enable button
 if ( ! pin_b6 )   then		; read button and
  tick ( 2 )			; tick to hear that !
  asm incf hours_units, f	; simple clock operations
   if hours_units == 10 then
    asm clrf hours_units 
    asm incf hours_tens, f
   end if
   if hours_tens == 2 then
    if hours_units == 4 then
     asm clrf hours_tens
     asm clrf hours_units
    end if
   end if
  delay_10mS ( 5 )		; delay required for reading button
 end if
pin_b6_direction = output	; disable button
end procedure

procedure button_alarm is
port_a = sel_blank		; display off
pin_b5_direction = input	; enable button
 if ( ! pin_b5 )  then		; read button
   if ! but_flag then		; if alarm preset,
    tick ( 1 )
     eeprom_put( 1, min_units ) ; store actual time
     eeprom_put( 2, min_tens )
     eeprom_put( 3, hours_units )
     eeprom_put( 4, hours_tens )
     but_flag = ! but_flag
   elsif but_flag then		; if time preset/display 
    tick ( 3 )
     eeprom_put( 5, min_units ) ; store alarm time and
     eeprom_put( 6, min_tens )
     eeprom_put( 7, hours_units )
     eeprom_put( 8, hours_tens )
     eeprom_get( 1, min_units ) ; load previously saved time, WARNING
     eeprom_get( 2, min_tens )  ; this reglage must be done in less 
     eeprom_get( 3, hours_units ) ; than 1 minute so don't sleep !
     eeprom_get( 4, hours_tens )
    but_flag = ! but_flag	; reset the flag for next operation
   end if
  delay_10mS ( 5 ) 		; delay required for buttons
  pin_b5_direction = output     ; disable button
 end if
end procedure


procedure display is

  port_b_direction = all_output		    ; prepare displaying
  port_a = sel_min_units		    ; mux min_unit and
  port_b = ! seven_from_digit( min_units )  ; display
  delay_1ms( 3 )			    ; keep it for good visibility
  port_a = sel_min_tens			    ; bla-bla, the same for the rest
  port_b = ! seven_from_digit( min_tens )
  delay_1ms( 3 )
  port_a = sel_hours_units
  port_b = ! seven_from_digit( hours_units )
  delay_1ms( 3 )
 if hours_tens == 0 then
   port_a = sel_blank
  elsif hours_tens > 0 then
   port_a = sel_hours_tens
   port_b = ! seven_from_digit( hours_tens)
   delay_1ms( 3 )
 end if
end procedure



procedure cucu ( byte in cuchours_tens, byte in cuchours_units,
                 byte in cucmin_tens, byte in cucmin_units ) is
 
 if ( cucmin_units == min_units ) &
    ( cucmin_tens == min_tens ) &
    ( cuchours_units == hours_units ) &
    ( cuchours_tens == hours_tens ) then
   if ! cucu_flag then
   for 5 loop
    tick ( 1 )  
    tick ( 2 )   
   end loop
    cucu_flag = ! cucu_flag		; stop the noise 
   end if
 end if
  if min_units > cucmin_units then	; enable for the next time 
   cucu_flag = low
  end if
end procedure

procedure alarm is
 eeprom_get( 5, amin_units )		; read the stored alarm value
 eeprom_get( 6, amin_tens )
 eeprom_get( 7, ahours_units )
 eeprom_get( 8, ahours_tens )
 if ( amin_units == min_units ) &	; and compare with actual one
    ( amin_tens == min_tens ) &
    ( ahours_units == hours_units ) &
    ( ahours_tens == hours_tens) then
    port_a = sel_blank			; blank the display
   if ! alarm_flag then			; and make noise
    for 50 loop
     tick ( 1 )
     tick ( 2 )
     display
     tick ( 3 )    
    end loop
    alarm_flag = ! alarm_flag		; stop, oh my ears
   end if
  end if
  if min_units > amin_units then	; enable after one minute
   alarm_flag = low
  end if
end procedure

 

 forever loop
  
  cucu ( 0, 8, 0, 0 ) ; play cucu every time when I'm sure at the office
  button_minutes      ; read buttons, 	
  cucu ( 0, 9, 0, 0 )
  button_hours
  cucu ( 1, 0, 0, 0 )
  button_alarm
  cucu ( 1, 1, 0, 0 )  
  cucu ( 1, 2, 0, 0 )  
  cucu ( 1, 3, 0, 0 )
  alarm		      ; and play alarm 
  cucu ( 1, 4, 0, 0 )
  display	      ; display something, it's a clock !		
  cucu ( 1, 5, 0, 0 )
  cucu ( 1, 6, 0, 0 )
 end loop

    Source: geocities.com/vsurducan/electro/PIC

               ( geocities.com/vsurducan/electro)                   ( geocities.com/vsurducan)