Lab #3 Narrative — Full Version

;***************************************************************************
;  RTC.asm
;  Real time clock for the 18F8722 on a PIC TRAINER REV-1.28
;  CTEC1904/2016W Lab #3
;
;  Originally:
;  Real time clock for the 18F452 on a Niagara College PICPROTO-II
;  Lab #3 (2010 Fall) solution for CTEC1630 class
;  Written by Mike Boldin
;  October 2010
;
;***************************************************************************
;  Hardware:
;  --------
;  PIC18F8722 MCU
;  CLK = 4.9152 MHz (f_osc)
;  RE2		=> LCD Read/nWrite line
;  RE1		=> LCD enable line
;  RE0		=> LCD register select (RS) line
;  RH0..7	=> LCD data lines (LCD0..7)
;
;  RJ0..1	<= Keypad column lines (KPD COL 1..2)
;  RB2..4	=> Keypad row lines (KPD ROW 1..3)
;
; Key IDs:
; ------
; Col  1   2   3   4
; Row
;  1  [0] [1] 		[0] = Hour up, [1] = Hour down
;
;  2  [4] [5] 		[4] = Minute up, [5] = Minute down
;
;  3  [8] [9]		[8] = Seconds up 10, [9] = Seconds reset
;
;  4
;
;  ALL SW2 OFF
;  ALL SW1 OFF
;  ALL SW3 X
;
;  IMPORTANT NOTE!!!
;  --------------
;  PICTRAINER BOARD uses SHARED I/O -- PORT J is shared between SW1
;   DIP switches, which are wired ACTIVE LOW with 10K pull-up resistors.
;
;***************************************************************************



	cblock 0x000
	BSRTemp		; Temp storage for BSR register during ISR
	WRegTemp	; Temp storage for WREG register during ISR
	StatusTemp	; Temp storage for STATUS register during ISR
	DelayRegA	; Temp register for delay routines
	DelayRegB	; Temp register for delay routines
	DelayRegC	; Temp register for delay routines
	DispTemp	; Buffer for characters to the LCD
	HourTens	; Hour 10's place (0..2)
	HourOnes	; Hour 1's place (0..9, 0..3 if HourTens=2)
	MinuteTens	; Minute 10's place (0..5)
	MinuteOnes	; Minute 1's place (0..9)
	SecondTens	; Second 10's place (0..5)
	SecondOnes	; Second 1's place (0..9)
	RowScan		; Bit of row to assert (1,2,4)
	ColScan		; Bit of column to detect (1,2)
	RowCount	; Row count (0..2)
	ColCount	; Column count (0..1)
	KeyID		; ID of key pressed (0,1,4,5,8,9)
	endc


;***************************************************************************
; Main program begins
;***************************************************************************

	org 0x000000
	; Skip ISR code


;***************************************************************************
;***************************************************************************
; Interrupt Service Routine
;***************************************************************************
;***************************************************************************
;
; The ISR is called when Timer0 has completed its one second count
; sequence. It does so by waiting for sixteen-bit Timer0 to count up and
; roll over from 0xFFFF to zero. Calculations:
;
;   PICPROTO-II w/18F452:
;   --------------------
;   With a clock speed of 3.6864MHz, Fcy=921600Hz and Tcy=1.085uS
;   Prescaled by 1:256, the input to the timer is 3600Hz
;   For a 1s delay we need (1s * 3600Hz) or 3600 clocks to elapse
;   So, the counter must be loaded to start at 65536-3600 = 61936 each time
;
;   PICTRAINER w/18F8722:
;   --------------------
;   With a clock speed of 4.9152MHz, Fcy=1228800Hz and Tcy=814nS
;   Prescaled by 1:256, the input to the timer is 4800Hz
;   For a 1s delay we need (1s * 4800Hz) or 4800 clocks to elapse
;   So, the counter must be loaded to start at 65536-4800 = 60736 each time
;
;***************************************************************************

	org	0x000018		; Low Priority Interrupt vector
ISR
	; Save BSR register
	; Save W register
	; Save STATUS register

	; Timer:  Load high first ... loading low will then load all 16 bits
	; Move clock and update display

EndISR
	; Clear Timer0 interrupt flag
	; Do first as it affects STATUS flags
	;  then restore original copy of STATUS flags
	; Restore BSR register

	retfie			; This will re-enable GIE flag as well



Initialize
	; Make sure that BSR register points to first block

	; RB2..RB4 are outputs (KPD ROWs)
	; RJ0, RJ1 are inputs (KPD COLs)
	; clear keypad rows (active low)

	; Initialize LCD



;***************************************************************************
; Display message on top line of LCD
;***************************************************************************

	; Set up the lookup table for the name
	; Display the name on the LCD
	; Wait a spell . . .
	; Clear the screen


;***************************************************************************
;  Initialize the time to one second before midnight;
;  First interrupt from timer will display midnight (00:00:00)
;
;  NOTE:  Time digits are stored in ASCII (NOT decimal) format.
;
;***************************************************************************

	; ASCII 2
	; ASCII 3
	; ASCII 5
	; ASCII 9
	; ASCII 5
	; ASCII 9


;***************************************************************************
;  Start the clock
;***************************************************************************
;
; PICPROTO-II w/18F452:
; --------------------
; The prescaler can be used to reduce the Fcy/4 frequency of 921600 Hz
; to 3600 Hz by using a ratio of 256.  With the input to TIMER 0 at 3600 Hz,
; we may generate an interrupt every second by loading the timer with a
; value of (65536-3600)=61936.
;
; PICTRAINER w/18F8722:
; --------------------
; The prescaler can be used to reduce the Fcy/4 frequency of 1228800 Hz
; to 4800 Hz by using a ratio of 256.  With the input to TIMER 0 at 4800 Hz,
; we may generate an interrupt every second by loading the timer with a
; value of (65536-4800)=60736.
;
;*****************************************************************************
        ; Timer0 Config: 	ON,
        ;			16-bit,
        ;			IntClk,
        ;			1:256 prescaler
        ;
	; Set up Timer0 for a loop time of 1 s
	; Load high first, loading low will then load all 16 bits
	; Enable Timer 0 interrupt source
	; Enable all interrupts (Master Enable)


;***************************************************************************
;  Continuously scan matrix keypad; clock is handled by ISR
;***************************************************************************

Main_Loop
	; reset all bits on keypad port -- clear all row bits
	; start at row 1
	; row 0 in terms of key ID

Main_ScanRow
	; clear all column bits
	; assert row
	; start at column 1
	; column 0 in terms of key ID

Main_ScanCol
	; Read all keypad column bits
	; reset "don't care" column bits
	; is the current column lit?
	; no, go to the next column
	; yes, found the key in this column

Main_NextCol
	; Otherwise, scan the next column
	; Stop when you get to column 2

Main_NextRow
	; Move to next row
	; Stop when you get to row 3
	; Continue scanning from row 0


;***************************************************************************
;  Verify a detected key press ...
;***************************************************************************

Main_FoundKey
	; Debounce: wait 20 ms
	; Read contents of column port again
	; is the current column still lit?
	; no, go to the next column

	; Decode key

	; Row ID x 4
	; Row ID x 4 + Col ID
	; Store in KeyID register

	; Wait until key released

	; Read contents of column port again
	; is the current column still lit?
	; no, begin processing the key
	; If key is still pressed down, keep waiting

;***************************************************************************
;  Process the key...
;
;  NOTE:  Changes made to hours, minutes and seconds are displayed only
;         on the next clock tick (by the ISR).
;
;***************************************************************************

Main_IsHourUp
	; Is it HOUR UP?
	; No, try next key

	; Tick to next hour
	; If hour 10's = 2, the day may need to roll over, in which
	;  case the hour is reset to midnight (00)

	; No, it's the same day

Main_NewDay
	; Yes, it's a new day
	;  if the hour 1's is 4 or more (because hour 10's is 2)

Main_SameDay
	; If hour 10's is 0 or 1, thenthe 10's roll over if the 1's
	;  is 10 (i.e., a carry)

Main_RollHourOnes
	; Carry?
	; No, done
	; Yes
	; Roll hour 1's back to zero
	; Carry to 10's place
	; If hour 10's > 2
	; Carry?
	; No, done
	; Yes
	; Roll hour 10's back to zero (the day has rolled over)
	; Restart scanning

Main_IsHourDown
	; Is it HOUR DOWN?
	; Subtract one from hour
	; Is hour 1's < 0
	; No, no borrow; done
	; Correct the 1's digit
	; Yes, borrow from the tens
	; Is there a borrow on the tens?
	; No, done.
	; Yes, wrap around to hour 23
	; Restart scanning

Main_IsMinuteUp
	; Is it MINUTE UP?
	; Add one to minute
	; Tick to next minute
	; If minute 1's > 9
	; Carry?
	; No, done
	; Yes
	; Roll 1's back to zero
	; Carry to 10's place
	; If minute 10's > 5
	; Carry?
	; No, done
	; Yes
	; Roll 10's back to zero
	; Restart scanning

Main_IsMinuteDown
	; Is it MINUTE DOWN?
	; Subtract one from minutes
	; Is minute 1's < 0
	; No, no borrow; done
	; Correct the 1's digit
	; Yes, borrow from the tens
	; Is there a borrow on the tens?
	; No, done.
	; Yes, wrap around to minute 59
	; Restart scanning

Main_IsSecondUp
	; Is it SECONDS UP?
	; Add ten seconds..
	;  Add nine, and next clock tick will add the extra one
	; Add 9 to seconds
	; Is the ones place > 9
	; No, done
	; Yes, wrap around
	; Add one to the tens place
	; Is the tens place 6 (implies seconds >= 60)
	; No, done
	; Correct the tens place (wraparound)
	; Restart scanning

Main_IsSecondReset
	; Is it SECONDS RESET?
	; No, restart scanning (unknown key - just ignore it)
	; Yes, reset seconds to zero
	; Restart scanning



;***************************************************************************
;***************************************************************************
; Subroutines
;***************************************************************************
;***************************************************************************


;***************************************************************************
; Clock_Tick
;***************************************************************************
; Clock Mechanism
;
; NOTE:  This is called by the ISR.  To avoid a race condition with
;        the LCD signals, this routine should not be called from
;        anywhere else!!!
;
;*****************************************************************************

Clock_Tick
	; Tick to next second
	; If second 1's > 9
	; Carry?
	; No, done ticking

	; Yes
	; Roll 1's back to zero
	; Carry to 10's place
	; If second 10's > 5
	; Carry?
	; No, done ticking

	; Yes
	; Roll 10's back to zero
	; Tick to next minute
	; If minute 1's > 9
	; Carry?
	; No, done ticking

	; Yes
	; Roll 1's back to zero
	; Carry to 10's place
	; If minute 10's > 5
	; Carry?
	; No, done ticking

	; Hours are handled specially, because the sequence is:
	;   0...9, 10...19, 20...23, 0...

	; Yes
	; Roll 10's back to zero

	; Tick to next hour
	; If hour 10's = 2, there may be a day rollover
	; No, it's the same day (i.e., hour 10's is 0 or 1)

Clock_NewDay
	; Yes, it may be new day (if hour 1's > 3)

Clock_SameDay
	; No, it is the same day (hour tens will roll over
	;   from 0 to 1 or from 1 to 2)

Clock_RollHourOnes
	; Carry?
	; No, done ticking

	; Yes
	; Reset hour 1's back to zero
	; Carry to 10's place
	; If hour 10's > 2
	; Carry?
	; No, done ticking

	; Yes
	; Reset hour 10's back to zero (day will have rolled over)

Clock_DisplayTime
	call	LCD_Home		; Reset to line 1, column 1

	; NOTE:  IF YOU ARE *NOT* STORING TIME DIGITS IN ASCII FORMAT
	;         YOU NEED addlw 0x30 IN BETWEEN THE movf AND call
	;         INSTRUCTIONS!

	movf	HourTens, w
	call	LCD_Char
	movf	HourOnes, w
	call	LCD_Char
	movlw	A_COLON
	call	LCD_Char
	movf	MinuteTens, w
	call	LCD_Char
	movf	MinuteOnes, w
	call	LCD_Char
	movlw	A_COLON
	call	LCD_Char
	movf	SecondTens, w
	call	LCD_Char
	movf	SecondOnes, w
	call	LCD_Char

Clock_SayTime
	; Convert Hours 10's place from ASCII to decimal (movlw + subwf)
	; Hour = 10 x Hours 10's place (movlw + mullw)
	; Convert Hours 1's place from ASCII to decimal (movlw + subwf)
	; Hour = 10 x Hours 10's place + Hour's 1's place (addwf)

Clock_FindHour
	; Is hour > 0
	; No, hour must == 0, i.e., midnight or noon
	; Yes, display "Twelve"

	; Is hour > 1
	; No, hour must == 1, i.e., 1 am or 1 pm
	; Yes, display "One"

	; Is hour > 2
	; No, hour must == 2, i.e., 2 am or 2 pm
	; Yes, display "Two"

	; Is hour > 3
	; No, hour must == 3, i.e., 3 am or 3 pm
	; Yes, display "Three"

	; Is hour > 4
	; No, hour must == 4, i.e., 4 am or 4 pm
	; Yes, display "Four"

	; Is hour > 5
	; No, hour must == 5, i.e., 5 am or 5 pm
	; Yes, display "Five"

	; Is hour > 6
	; No, hour must == 6, i.e., 6 am or 6 pm
	; Yes, display "Six"

	; Is hour > 7
	; No, hour must == 7, i.e., 7 am or 7 pm
	; Yes, display "Seven"

	; Is hour > 8
	; No, hour must == 8, i.e., 8 am or 8 pm
	; Yes, display "Eight"

	; Is hour > 9
	; No, hour must == 9, i.e., 9 am or 9 pm
	; Yes, display "Nine"

	; Is hour > 10
	; No, hour must == 10, i.e., 10 am or 10 pm
	; Yes, display "Ten"

	; Is hour > 11
	; No, hour must == 11, i.e., 11 am or 11 pm
	; Yes, display "Eleven"

	; Here, hour >= 12 (i.e., p.m.)
	; So, subtract 12 and check again

	; movlw
	; subwf

	goto	Clock_FindHour


Clock_HourTwelve
	movlw	upper	StringTwelve
	movwf	TBLPTRU
	movlw	high	StringTwelve
	movwf	TBLPTRH
	movlw	low	StringTwelve
	movwf	TBLPTRL
	goto	Clock_SayHour

Clock_HourEleven

Clock_HourTen

Clock_HourNine

Clock_HourEight

Clock_HourSeven

Clock_HourSix

Clock_HourFive

Clock_HourFour

Clock_HourThree

Clock_HourTwo

Clock_HourOne



Clock_SayHour
	; Display the hour on line 2
	call	LCD_Line2
	; space between hour and minutes

; Figure out how to convert the minutes to text

	; Convert Minutes 10's place from ASCII to decimal

	; Is minute > 9?
	; No, minute <= 9

	; Is minute > 19?
	; No, minute <= 19

	; Is minute > 29?
	; No, minute <= 29

	; Is minute > 39?
	; No, minute <= 39

	; Is minute > 49?
	; No, minute <= 49

	; Yes, minute > 49


Clock_MinuteZero
	movlw	upper	StringOClock
	movwf	TBLPTRU
	movlw	high	StringOClock
	movwf	TBLPTRH
	movlw	low	StringOClock
	movwf	TBLPTRL
	goto	Clock_SayMinute

Clock_MinuteTen

Clock_MinuteTwenty

Clock_MinuteThirty

Clock_MinuteForty

Clock_MinuteFifty


Clock_SayMinute
	; display the minute after the hour on line 2 of the LCD
	; Pad it out with 7 spaces using a loop:
	; "Six Ten" is the shortest at 7 characters;
	; "Eleven O'Clock" (or "Twelve O'Clock") is the longest at
	;   14 characters

	; OR

	; IN THE STRING TABLES, make all the minute strings the same length
	; with spaces

	return


;*****************************************************************************
;*****************************************************************************
; Zero-terminated String Tables
;*****************************************************************************
;*****************************************************************************

StringName
	data	"YOUR NAME HERE", 0

StringOClock
	data	"O'Clock", 0

StringOne
	data	"One", 0

StringTwo
	data	"Two", 0

StringThree
	data	"Three", 0

StringFour
	data	"Four", 0

StringFive
	data	"Five", 0

StringSix
	data	"Six", 0

StringSeven
	data	"Seven", 0

StringEight
	data	"Eight", 0

StringNine
	data	"Nine", 0

StringTen
	data	"Ten", 0

StringEleven
	data	"Eleven", 0

StringTwelve
	data	"Twelve", 0

StringTwenty
	data	"Twenty", 0

StringThirty
	data	"Thirty", 0

StringForty
	data	"Forty", 0

StringFifty
	data	"Fifty", 0

Back to CTEC1904