forked from Anobium/Great-Cow-BASIC-Demonstration-Sources
-
Notifications
You must be signed in to change notification settings - Fork 0
/
i2c_DS3231_16F1937_LCD.gcb
704 lines (574 loc) · 20.9 KB
/
i2c_DS3231_16F1937_LCD.gcb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
'''A demonstration program for GCGB and GCB.
'''--------------------------------------------------------------------------------------------------------------------------------
'''This program shows the real time clock on a LCD. The LCD is driven by a LCD I2C adapter.
'''Two types of LCD are supported using this method, as shown below:
''': Set LCD_10 to 10 for the YwRobot LCD1602 IIC V1 or the Sainsmart LCD_PIC I2C adapter
''': Set LCD_10 to 12 for the Ywmjkdz I2C adapter with pot bent over top of chip
'''The DS3231 is attached to the I2C ports with appropiate resistors to the supply voltage. Typically a 5k or 2.2k resistor 'pulling up' to the supply voltage can be used.
''':
''': There are two pushbuttons: Mode and Set equates to Key1 and Key2. Connected to portb.0 and portb.1 resepectively.
''':
''': During normal operation, the Mode button will toggle
''': between 12- and 24-hours modes.
''':
''': This has two modes US date and UK date style.
''': Change ''':clocktype''': to 0 for US and 1 for UK
''':
''': To set a new date and time, press the Set button
''': which will take you consecutively to Month, Day, Year,
''': Day of the Week, Hour, Minute and Second. Within each of
''': these, press Mode to cycle through the possible values.
''': You may hold the Mode button to increment automatically,
''': or press momentarily for a single increment. Time is
''': always set in 24-hour clock format.
''':
''': Based on the Thomas Henry works of 5/26/2014
''': Evan R Venn/Anobium --- 4/1/2015
''': Evan R Venn/Anobium --- 10/1/2015 - Minor improvements to cater for Oscicallor stopping
''': Evan R Venn/Anobium --- 11/1/2015 - added Hardware i2c support
''':
'''@author EvanV
'''@licence GPL
'''@version 1.2a
'''@date 23.02.15
'''********************************************************************************
; ----- Configuration
#chip 16F1937, 32
#option explicit
#config Osc = intOSC, MCLRE_ON, PLLEN_ON, VCAPEN_OFF
#include <DS3231.h>
#include <I2CEEPROM.h> ; Memory chips: AT24C32 (storage capacity 32K)
#define AT24C32 0xAE ;Eeprom address
; ----- Define Hardware settings
dir setBut in
dir modeBut in
; Define Hardware I2C settings for SSP module
#define HI2C_BAUD_RATE 400
#define HI2C_DATA PORTC.4
#define HI2C_CLOCK PORTC.3
;I2C pins need to be input for SSP module
Dir HI2C_DATA in
Dir HI2C_CLOCK in
;MASTER I2C Device for SSP module
HI2CMode Master
; Define I2C settings - CHANGE PORTS
' #define I2C_MODE Master
' #define I2C_DATA PORTC.4
' #define I2C_CLOCK PORTC.3
' #define I2C_DISABLE_INTERRUPTS ON
' ; #define I2C_BIT_DELAY 20 us width of data bit on SDA
' ; #define I2C_CLOCK_DELAY 10 us width of clock pulse on SCL
' ; #define I2C_END_DELAY 10 us interval between clock pulses
'''Set up LCD
#define LCD_IO 10
#define LCD_I2C_Address_1 0x4E
#define clocktype 1 ;0 is US or 1 is UK style
#define debounce 50 mS ;settling time for switches
#define delay 150 mS ;delay time for pushbutton
#define pressed 1 ;flag for pressed pushbutton
#define setBut portb.0 ;Set pushbutton
#define modeBut portb.1 ;Mode pushbutton
;----- Variables
Dim Alarm1track, Alarm2track, CENTURY_FLAG, POWERLOSS, EEPROMVAL, DAYSMAX, DAYSMAX as Byte
dim changed as bit
dim hour, min, sec, am_pm as byte
dim oldsec as byte
dim date, month, year, DOW, century as byte
dim mode, Oldmode, dayMax as byte
oldsec = 255 ; init value
century = 20 ; will be updated by Century calculation... if a century roll ever occurs!! Required in this example code.
;----- Tables
table monthDays ;# of days in months. Required in this example code.
31
29 ;leap years only
31
30
31
30
31
31
30
31
30
31
end table
;----- Program
CLS
#ifdef HI2C_DATA
Locate 0, 12
Print "i2cHW"
HI2CReStart ; is DS3231 present?
HI2CSend(DS_AddrWrite)
HI2CStop
if HI2CAckPollState = True then ; is DS3231 present?
Print "No DS3231" ; user message
locate 1,0
Print "Stopped" ; user message
end
end if
#endif
#ifdef I2C_DATA
Locate 0, 12
Print "i2cSW"
I2CStart ; is DS3231 present?
I2CSend(DS_AddrWrite)
I2CStop
if I2CSendState = False then ; is DS3231 present?
Print "No DS3231" ; user message
locate 1,0
Print "Stopped" ; user message
end
end if
#endif
locate 0,0
Print "DS3231"
locate 1,0
Print "Great Cow Basic"
wait 2 s
CLS
; example uses of the DS3231 functions
;
; aging offset; cheap board from ebay! drifts
; locate 3,0
DS3231_WriteRegister ( 0x10, 0b0000001 )
; print ByteToBin( DS3231_ReadRegister ( 0x10) )
; Example of setting the clock
; DS3231_SetClock( 15, 33 , 0 , 1 , 4 , 1 , 15 ) ;h ours, minutes, seconds, day of week, date, month, year
DS3231_ReadDate(DOW, date, month, year, century_flag ) ;get initial date
if century_flag = 1 then
DS3231_HandleCentury ( century_flag, Century )
end if
DS3231_ReadHourMode(mode) ;and the current mode
DS3231_SetHourMode(mode) ;update hour mode from RTC
DS3231_EnableOscillator( true ) ;and, ensure the clock is running
;
;Example of configuration of the 32K output and the SQW
;
DS3231_Set32kHz ( true ) ;and, enable the 32kHz Output (EN32kHz).
DS3231_EnableSQW
DS3231_SetSQW 0 ;and, turn ON output as we will use as the alarm port!
DS3231_DisableSQW
;Another method to turn off SQW by masking the current state of the control address
;state = DS3231_ReadControl
;state = state and 0b00111111
;DS3231_SetControl ( state )
;Another method to turn ON SQW by masking the current state
;state = DS3231_ReadControl
;state = state AND 0b11111011
;state = state OR 0b01000000
;DS3231_SetControl ( state )
;Examples of how to set the Alarms
;You must do the following:
;1. DS3231_SetAlarm1 ( hour, minute, second, DOW, date ) or DS3231_SetAlarm2
;2. DS3231_SetAlarmMask1 ( DS3231_Alarm1Assertion_Seconds ) to set the mask, see below for the options.
;3. DS3231_ClearAlarm1 or DS3231_ClearAlarm2
;4. DS3231_EnableAlarm1Interrupt if you want to raise the SQW as external interrupt
;5. DS3231_EnableSQWInterruptControl
;You can test the status of the alarm by raising an interrupt in your PIC but you can also test DS3231_Alarm[1|2]Status. DS3231_Alarm[1|2]Status. will return true | false.
;After the alarm event has occured you MUST clear the alarm, using DS3231_ClearAlarm1 or DS3231_ClearAlarm2
;DS3231_DisableAlarm1Interrupt ;turn off the SQW interrupt
;DS3231_DisableAlarm2Interrupt ;turn off the SQW interrupt
; set Alarm1 - alert every time the Seconds is each 30th second
DS3231_SetAlarm1 ( 11, 45, 30, 0, 1 ) ;DS3231_SetAlarm ( [0 | 1] , hour, minute, second, DOW, date )
; These are defined for usage
; DS3231_Alarm1Assertion_EverySecond = 0x0F
; DS3231_Alarm1Assertion_Seconds = 0x0E
; DS3231_Alarm1Assertion_MinutesSeconds = 0x0C
; DS3231_Alarm1Assertion_HoursMinutesSeconds = 0x08
; DS3231_Alarm1Assertion_DateHoursMinutesSeconds = 0x00
; DS3231_Alarm1Assertion_DayHoursMinutesSeconds = 0x00
DS3231_SetAlarmMask1 ( DS3231_Alarm1Assertion_Seconds ) ;DS3231_SetAlarmMask1 ( alarmAssertionMatch )
DS3231_ClearAlarm1
DS3231_EnableAlarm1Interrupt
DS3231_ReadAlarm1( hour, min, sec, DOW, date )
; set Alarm2 - alert every Minute
DS3231_SetAlarm2 ( 10, 1, 0, 1 ) ;DS3231_SetAlarm2 ( hour, minute, DOW, date )
; These are defined for usage
; DS3231_Alarm2Assertion_EveryMinute = 0x07
; DS3231_Alarm2Assertion_Minutes = 0x06
; DS3231_Alarm2Assertion_HoursMinutes = 0x04
; DS3231_Alarm2Assertion_DateHoursMinutes = 0x00
; DS3231_Alarm2Assertion_DayHoursMinutesSeconds = 0x00
DS3231_SetAlarmMask2 ( DS3231_Alarm2Assertion_EveryMinute )
DS3231_ClearAlarm2
DS3231_EnableAlarm2Interrupt
DS3231_ReadAlarm2( hour, min, DOW, date ) ; just an example....
DS3231_EnableSQWInterruptControl ; enable output on SQW on alarm
;DS3231_DisableAlarm1 ; if you want to turn the alarm off
;DS3231_DisableAlarm2 ; if you want to turn the alarm off
;End of example Alarm setup commands
;Main body of program commences here.
DS3231_ReadDate(DOW, date, month, year, century_flag ) ;get initial date. This is required for main routine.
if century_flag = 1 then
DS3231_HandleCentury ( century_flag, Century )
end if
DS3231_ReadHourMode(mode) ;and the current mode
' initialise a tracking varaible
powerloss = false
dim EEpromAddress as word
EEpromAddress = 0
; display all register realtime display
' do forever
' for xloop = 0 to 0x12
' locate xloop, 0
' print ByteToBin( DS3231_ReadRegister ( xloop) )
' next
' if DS3231_Alarm1Status = 1 then ; display message
' DS3231_ClearAlarm1
' end if
'
'
' if DS3231_Alarm2Status = 1 then ; display message
' DS3231_ClearAlarm2
'
' end if
'
' loop
do
if DS3231_OscillatorStopFlagStatus = true then ; has a power loss occurred? and the oscillator has stopped?
powerloss = true
DS3231_ClearOscillatorStopFlag
end if
if powerloss = true then
locate 0, 13
print "!"
end if
updateDisp ;show time and date
if setBut = pressed then
changeSetting ;change time and date
end if
if modeBut = pressed then
changeMode ;12 or 24 hour mode
end if
;Alarm Handling code. Simply tests the status and displays a message on the LCD
if DS3231_Alarm1Status = 1 then ; display message
locate 0,15
print "*1"
DS3231_ClearAlarm1
Alarm1track = sec
end if
if DS3231_Alarm2Status = 1 then ; display message
locate 1,15
print "*2"
DS3231_ClearAlarm2
Alarm2track = sec
end if
if ((Alarm1track + 4) % 60 ) < sec then ; clear the line after time delay of 4 secs
locate 0,15
print " "
end if
if ((Alarm2track + 4) % 60 ) < sec then ; clear the line after time delay of 4 secs
locate 1,15
print " "
end if
;End of Alarm Handling code.
eeprom_wr_byte ( AT24C32 , EEpromAddress, sec )
eeprom_rd_byte ( AT24C32 , EEpromAddress, eepromval )
if sec <> eepromval then
locate 1,0
print "!"
end if
EEpromAddress++
EEpromAddress = EEpromAddress % 4096
loop
end
;----- Subroutines
sub printTime
if mode = 24 then
if hour < 10 then ;left pad with zero
Print "0"
end if
end if
Print hour
Print ":"
if min < 10 then ;left pad with zero
Print "0"
end if
Print min
Print ":"
if sec < 10 then ;left pad with zero
Print "0"
end if
Print sec
if mode = 12 then ;Print a.m or p.m
if am_pm then ;only in 12-hour mode
Print " p.m. "
else
Print " a.m. "
end if
else
Print " " ;wipe out garbage
end if
end sub
;-----
sub printDate
select case DOW ;day of the week
case 1:
Print "Sun:"
case 2:
Print "Mon:"
case 3:
Print "Tue:"
case 4:
Print "Wed:"
case 5:
Print "Thu:"
case 6:
Print "Fri:"
case 7:
Print "Sat:"
case else
Print "Err:"
end select
if clocktype = 0 then
Print month
Print "/"
Print date
Print "/" ;make four-digit year
Print century
if year < 10 then
Print "0"
end if
else
Print date
Print "/"
Print month
Print "/" ;make four-digit year
Print century
if year < 10 then
Print "0"
end if
end if
Print year
Print " " ;blank any garbage
end sub
;-----
sub updateDisp
;for best efficiency, date is only updated once a day
DS3231_ReadTime(hour, min, sec, am_pm)
if oldsec <> sec then
if !(hour | min | sec) then ;time to update
DS3231_ReadDate(DOW, date, month, year, century_flag )
if century_flag = 1 then
DS3231_HandleCentury ( century_flag, Century )
CLS
end if
end if
locate 0,0 ;home the cursor
printTime ;show time
locate 1,0 ;show the date
printDate
oldsec = sec
end if
end sub
;-----
sub changeSetting
changed = false ;assume no change will be made
DS3231_ReadHourMode(Oldmode) ;and the current mode
mode = 24 ;ensure the display is correct
DS3231_SetHourMode(24) ;change settings in 24-hour mode
do while setBut = pressed ;wait for Set release
wait debounce
loop
wait while ( setBut = pressed ) or ( modeBut = pressed )
if clocktype = 0 then ; handle US and UK clock type
SetMonth
SetDate
else
SetDate
SetMonth
end if
SetYear
;***** Day of the Week
cls
Print "Set Weekday:"
locate 1,0
printDate
do
do while modeBut = pressed ;Mode button held
DOW++ ;increment while pressed
if DOW > 7 then ;1 = Sunday, etc.
DOW = 1
end if
locate 1,0 ;Print updated display
printDate
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
;***** Hour
cls
Print "Set Hours:"
locate 1,0
if OldMode = 12 and am_pm then ; make correction of AM_PM mode by adding 12 hours to handle mode changes
hour = hour + 12
end if
printTime
do
do while modeBut = pressed ;Mode button held
hour++ ;increment while pressed
if hour > 23 then ;remember, set in 24-hour mode
hour = 0
end if
locate 1,0 ;Print updated display
printTime
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
;***** Minutes
cls
Print "Set Minutes:"
locate 1,0
printTime
do
do while modeBut = pressed ;Mode button held
min++ ;increment while pressed
if min > 59 then ;0 to 59 minutes
min = 0
end if
locate 1,0 ;Print updated display
printTime
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
;***** Second
cls
Print "Set Seconds:"
locate 1,0
printTime
do
do while modeBut = pressed ;Mode button held
sec++ ;increment while pressed
if sec > 59 then ;0 to 59 seconds
sec = 0
end if
locate 1,0 ;Print updated display
printTime
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
;***** Execute the new settings
if changed then
cls
Print "Press Set, or"
locate 1,0
Print "Mode to revert"
do while (setBut <> pressed) ;wait for the Set button
wait debounce
if ModeBut = pressed then
cls ;clear last message
DS3231_SetHourMode(Oldmode) ;restore current mode
exit Sub ;do not set, exit set routine
end if
loop
DS3231_SetHourMode(24) ;change settings in 24-hour mode
DS3231_SetClock(hour, min, sec, DOW, date, month, year)
powerloss = false ; reset tracker variable
DS3231_ClearOscillatorStopFlag
end if
do while (setBut = pressed) ;wait for Set release
loop
wait debounce ;let it settle
cls ;clear last message
DS3231_SetHourMode(Oldmode) ;restore current mode
mode = Oldmode ;restore mode
end sub
;-----
sub changeMode
DS3231_SetHourMode(24) ;change settings in 24-hour mode
if mode = 12 then ;toggle between 12 and 24
mode = 24
else
mode = 12
end if
DS3231_SetHourMode(mode) ;change to new mode
do while modeBut = pressed ;wait for a release
wait debounce
loop
end sub
sub setMonth
;***** Month
cls
Print "Set Month:"
locate 1,0
printDate
do
do while modeBut = pressed ;Mode button held
month++ ;increment while pressed
if month > 12 then
month = 1
end if
locate 1,0 ;Print updated display
printDate
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
wait debounce
loop while setBut <> pressed ;Set moves you on
readTable monthDays, month, daysMax
do while setBut = pressed ;wait for Set release
wait debounce
loop
end Sub
sub SetDate
;***** Date
cls
Print "Set Date:"
locate 1,0
printDate
readTable monthDays, month, daysMax
do
do while modeBut = pressed ;Mode button held
date++ ;increment while pressed
if date > daysMax then ;stay within legal days
date = 1
end if
locate 1,0 ;Print updated display
printDate
wait while modeBut = pressed ;wait for repeatt
changed = true ;a change was made
loop
wait debounce
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
end sub
sub SetYear
;***** Year
cls
Print "Set Year:"
locate 1,0
printDate
do
do while modeBut = pressed ;Mode button held
year++ ;increment while pressed
if year > 99 then ;this millennium only
year = 0
end if
locate 1,0 ;Print updated display
printDate
wait while modeBut = pressed ;wait for repeat
changed = true ;a change was made
loop
wait debounce
loop while setBut <> pressed
do while setBut = pressed ;wait for Set release
wait debounce
loop
end sub