-
Notifications
You must be signed in to change notification settings - Fork 0
/
rtc-pi.c
executable file
·347 lines (279 loc) · 10.5 KB
/
rtc-pi.c
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
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/time.h>
#define GPIO_ADD 0x20200000L // physical address of I/O peripherals on the ARM processor
#define GPIO_SEL1 1 // Offset of SEL register for GP17 & GP18 into GPIO bank
#define GPIO_SEL2 2 // Offset of SEL register for GP21 into GPIO bank
#define GPIO_SET 7 // Offset of PIN HIGH register into GPIO bank
#define GPIO_CLR 10 // Offset of PIN LOW register into GPIO bank
#define GPIO_INP 13 // Offset of PIN INPUT value register into GPIO bank
#define PAGE_SIZE 4096
#define BLOCK_SIZE PAGE_SIZE
/* RTC Chip register definitions */
#define SEC_WRITE 0x80
#define MIN_WRITE 0x82
#define HOUR_WRITE 0x84
#define DATE_WRITE 0x86
#define MONTH_WRITE 0x88
#define YEAR_WRITE 0x8C
#define SEC_READ 0x81
#define MIN_READ 0x83
#define HOUR_READ 0x85
#define DATE_READ 0x87
#define MONTH_READ 0x89
#define YEAR_READ 0x8D
int mem_fd = 0;
char *gpio_mmap = NULL;
char *gpio_ram = NULL;
volatile unsigned int *gpio = NULL;
/* These 'defines' map the peripheral pin functions to our circuits DS1302 pins */
/* See DS1302 datasheet REV: 110805, and Broadcom BCM2835-ARM-Peripherals.pdf 6/2/2012 */
#define IO_INPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL
#define IO_OUTPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL; *(gpio+GPIO_SEL1) |= 0x01000000L
#define SCLK_OUTPUT *(gpio+GPIO_SEL2) &= 0xFFFFFFC7L; *(gpio+GPIO_SEL2) |= 0x00000008L
#define CE_OUTPUT *(gpio+GPIO_SEL1) &= 0xFF1FFFFFL; *(gpio+GPIO_SEL1) |= 0x00200000L
#define IO_HIGH *(gpio+GPIO_SET) = 0x00040000L
#define IO_LOW *(gpio+GPIO_CLR) = 0x00040000L
#define SCLK_HIGH *(gpio+GPIO_SET) = 0x00200000L
#define SCLK_LOW *(gpio+GPIO_CLR) = 0x00200000L
#define CE_HIGH *(gpio+GPIO_SET) = 0x00020000L
#define CE_LOW *(gpio+GPIO_CLR) = 0x00020000L
#define IO_LEVEL *(gpio+GPIO_INP) & 0x00040000L
void setup_io()
{
/* open /dev/mem to get acess to physical ram */
if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) {
printf("can't open /dev/mem. Did you run the program with administrator rights?\n");
exit (-1);
}
/* Allocate a block of virtual RAM in our application's address space */
if ((gpio_ram = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) {
printf("allocation error \n");
exit (-1);
}
/* Make sure the pointer is on 4K boundary */
if ((unsigned long)gpio_ram % PAGE_SIZE)
gpio_ram += PAGE_SIZE - ((unsigned long)gpio_ram % PAGE_SIZE);
/* Now map the physical addresses of the peripheral control registers
into our address space */
gpio_mmap = (unsigned char *)mmap(
(caddr_t)gpio_ram,
BLOCK_SIZE,
PROT_READ|PROT_WRITE,
MAP_SHARED|MAP_FIXED,
mem_fd,
GPIO_ADD
);
if ((long)gpio_mmap < 0) {
printf("unable to map the memory. Did you run the program with administrator rights?\n");
exit (-1);
}
/* Always use a volatile pointer to hardware registers */
gpio = (volatile unsigned *)gpio_mmap;
/* Now we have access to the hardware reigsters we can start twiddling I/O pins */
/* Switch GPIO 0, 1 and 2 to output mode */
SCLK_OUTPUT;
IO_OUTPUT;
CE_OUTPUT;
/* Set the SCLK, IO and CE pins to default (low) */
SCLK_LOW;
IO_LOW;
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
}
unsigned char read_rtc( unsigned char add )
{
unsigned char val;
int lp;
val = add;
/* Check LSB is set */
if ( !(add & 1 ) ) {
printf("Incorrect read address specified - LSB must be set.\n");
exit (-1);
}
/* Check address range is valid */
if ( (add < 0x81) || (add > 0x91) ) {
printf("Incorrect read address specified - It must be in the range 0x81..0x91\n");
exit (-1);
}
CE_HIGH;
usleep(2);
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
IO_INPUT;
for (lp=0; lp<8; lp++) {
usleep(2);
val >>= 1;
if (IO_LEVEL)
val |= 0x80;
else
val &= 0x7F;
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
/* Set the I/O pin back to it's default, output low. */
IO_LOW;
IO_OUTPUT;
/* Set the CE pin back to it's default, low */
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
return val;
}
void write_rtc( unsigned char add, unsigned char val_to_write )
{
unsigned char val;
int lp;
/* Check LSB is clear */
if ( add & 1 ) {
printf("Incorrect write address specified - LSB must be cleared.\n");
exit (-1);
}
/* Check address range is valid */
if ( (add < 0x80) || (add > 0x90) ) {
printf("Incorrect write address specified - It must be in the range 0x80..0x90\n");
exit (-1);
}
CE_HIGH;
usleep(2);
val = add;
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
val = val_to_write;
for (lp=0; lp<8; lp++) {
if (val & 1)
IO_HIGH;
else
IO_LOW;
val >>= 1;
usleep(2);
SCLK_HIGH;
usleep(2);
SCLK_LOW;
usleep(2);
}
/* Set the I/O pin back to it's default, output low. */
IO_LOW;
/* Set the CE pin back to it's default, low */
CE_LOW;
/* Short delay to allow the I/O lines to settle. */
usleep(2);
}
int main(int argc, char **argv)
{
int lp;
unsigned char val;
int year,month,day,hour,minute,second;
time_t epoch_time;
struct tm time_requested;
struct timeval time_setformat;
/* Check that the program was called correctly */
if ( argc > 2 ) {
printf("Too many arguments specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n");
exit (-1);
}
/* Set up gpi pointer for direct register access */
setup_io();
if ( argc == 2 ) {
/* If the number of arguments are two, that means the user enter a date & time. */
/* Read that value and write it to the RTC chip */
sscanf(argv[1],"%4d%2d%2d%2d%2d%2d",&year,&month,&day,&hour,&minute,&second);
/* Validate that the input date and time is basically sensible */
if ( (year < 2000) || (year > 2099) || (month < 1) || (month > 12) ||
(day < 1) || (day>31) || (hour < 0) || (hour > 23) || (minute < 0) ||
(minute > 59) || (second < 0) || (second > 59) ) {
printf("Incorrect date and time specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n");
exit (-1);
}
/* Got valid input - now write it to the RTC */
/* The RTC expects the values to be written in packed BCD format */
write_rtc(SEC_WRITE, ( (second/10) << 4) | ( second % 10) );
write_rtc(MIN_WRITE, ( (minute/10) << 4) | ( minute % 10) );
write_rtc(HOUR_WRITE, ( (hour/10) << 4) | ( hour % 10) );
write_rtc(DATE_WRITE, ( (day/10) << 4) | ( day % 10) );
write_rtc(MONTH_WRITE, ( (month/10) << 4) | ( month % 10) );
write_rtc(YEAR_WRITE, ( ((year-2000)/10) << 4) | (year % 10) );
/* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */
time_requested.tm_sec = second;
time_requested.tm_min = minute;
time_requested.tm_hour = hour;
time_requested.tm_mday = day;
time_requested.tm_mon = month-1;
time_requested.tm_year = year-1900;
time_requested.tm_wday = 0; /* not used */
time_requested.tm_yday = 0; /* not used */
time_requested.tm_isdst = -1; /* determine daylight saving time from the system */
epoch_time = mktime(&time_requested);
/* Now set the clock to this time */
time_setformat.tv_sec = epoch_time;
time_setformat.tv_usec = 0;
lp = settimeofday(&time_setformat,NULL);
/* Check that the change was successful */
if ( lp < 0 ) {
printf("Unable to change the system time. Did you run the program as an administrator?\n");
printf("The operation returned the error message \"%s\"\n", strerror( errno ) );
exit (-1);
}
} else {
/* The program was called without a date specified; therefore read the date and time from */
/* the RTC chip and set the system time to this */
second = read_rtc(SEC_READ);
minute = read_rtc(MIN_READ);
hour = read_rtc(HOUR_READ);
day = read_rtc(DATE_READ);
month = read_rtc(MONTH_READ);
year = read_rtc(YEAR_READ);
/* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */
/* Bearing in mind that the format of the time values in the RTC is packed BCD, hence the conversions */
time_requested.tm_sec = (((second & 0x70) >> 4) * 10) + (second & 0x0F);
time_requested.tm_min = (((minute & 0x70) >> 4) * 10) + (minute & 0x0F);
time_requested.tm_hour = (((hour & 0x30) >> 4) * 10) + (hour & 0x0F);
time_requested.tm_mday = (((day & 0x30) >> 4) * 10) + (day & 0x0F);
time_requested.tm_mon = (((month & 0x10) >> 4) * 10) + (month & 0x0F) - 1;
time_requested.tm_year = (((year & 0xF0) >> 4) * 10) + (year & 0x0F) + 2000 - 1900;
time_requested.tm_wday = 0; /* not used */
time_requested.tm_yday = 0; /* not used */
time_requested.tm_isdst = -1; /* determine daylight saving time from the system */
epoch_time = mktime(&time_requested);
/* Now set the clock to this time */
time_setformat.tv_sec = epoch_time;
time_setformat.tv_usec = 0;
lp = settimeofday(&time_setformat,NULL);
/* Check that the change was successful */
if ( lp < 0 ) {
printf("Unable to change the system time. Did you run the program as an administrator?\n");
printf("The operation returned the error message \"%s\"\n", strerror( errno ) );
exit (-1);
}
}
return 0;
}