SHOW:
|
|
- or go back to the newest paste.
1 | ||
2 | #include <stdio.h> | |
3 | #include <stdlib.h> | |
4 | #include <fcntl.h> | |
5 | #include <unistd.h> | |
6 | #include <time.h> | |
7 | #include <string.h> | |
8 | #include <errno.h> | |
9 | #include <sys/mman.h> | |
10 | #include <sys/time.h> | |
11 | ||
12 | #define GPIO_ADD 0x20200000L // physical address of I/O peripherals on the ARM processor | |
13 | #define GPIO_SEL1 1 // Offset of SEL register for GP17 & GP18 into GPIO bank | |
14 | #define GPIO_SEL2 2 // Offset of SEL register for GP21 into GPIO bank | |
15 | #define GPIO_SET 7 // Offset of PIN HIGH register into GPIO bank | |
16 | #define GPIO_CLR 10 // Offset of PIN LOW register into GPIO bank | |
17 | #define GPIO_INP 13 // Offset of PIN INPUT value register into GPIO bank | |
18 | #define PAGE_SIZE 4096 | |
19 | #define BLOCK_SIZE PAGE_SIZE | |
20 | ||
21 | /* RTC Chip register definitions */ | |
22 | #define SEC_WRITE 0x80 | |
23 | #define MIN_WRITE 0x82 | |
24 | #define HOUR_WRITE 0x84 | |
25 | #define DATE_WRITE 0x86 | |
26 | #define MONTH_WRITE 0x88 | |
27 | #define YEAR_WRITE 0x8C | |
28 | #define SEC_READ 0x81 | |
29 | #define MIN_READ 0x83 | |
30 | #define HOUR_READ 0x85 | |
31 | #define DATE_READ 0x87 | |
32 | #define MONTH_READ 0x89 | |
33 | #define YEAR_READ 0x8D | |
34 | ||
35 | ||
36 | int mem_fd = 0; | |
37 | char *gpio_mmap = NULL; | |
38 | char *gpio_ram = NULL; | |
39 | volatile unsigned int *gpio = NULL; | |
40 | ||
41 | ||
42 | /* These 'defines' map the peripheral pin functions to our circuits DS1302 pins */ | |
43 | /* See DS1302 datasheet REV: 110805, and Broadcom BCM2835-ARM-Peripherals.pdf 6/2/2012 */ | |
44 | #define IO_INPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL | |
45 | #define IO_OUTPUT *(gpio+GPIO_SEL1) &= 0xF8FFFFFFL; *(gpio+GPIO_SEL1) |= 0x01000000L | |
46 | #define SCLK_OUTPUT *(gpio+GPIO_SEL2) &= 0xFF1FFFFFL; *(gpio+GPIO_SEL2) |= 0x00200000L | |
47 | - | #define SCLK_OUTPUT *(gpio+GPIO_SEL2) &= 0xFFFFFFC7L; *(gpio+GPIO_SEL2) |= 0x00000008L |
47 | + | |
48 | #define IO_HIGH *(gpio+GPIO_SET) = 0x00040000L | |
49 | #define IO_LOW *(gpio+GPIO_CLR) = 0x00040000L | |
50 | #define SCLK_HIGH *(gpio+GPIO_SET) = 0x08000000L | |
51 | - | #define SCLK_HIGH *(gpio+GPIO_SET) = 0x00200000L |
51 | + | #define SCLK_LOW *(gpio+GPIO_CLR) = 0x08000000L |
52 | - | #define SCLK_LOW *(gpio+GPIO_CLR) = 0x00200000L |
52 | + | |
53 | #define CE_LOW *(gpio+GPIO_CLR) = 0x00020000L | |
54 | #define IO_LEVEL *(gpio+GPIO_INP) & 0x00040000L | |
55 | ||
56 | ||
57 | void setup_io() | |
58 | { | |
59 | ||
60 | /* open /dev/mem to get acess to physical ram */ | |
61 | if ((mem_fd = open("/dev/mem", O_RDWR|O_SYNC) ) < 0) { | |
62 | printf("can't open /dev/mem. Did you run the program with administrator rights?\n"); | |
63 | exit (-1); | |
64 | } | |
65 | ||
66 | /* Allocate a block of virtual RAM in our application's address space */ | |
67 | if ((gpio_ram = malloc(BLOCK_SIZE + (PAGE_SIZE-1))) == NULL) { | |
68 | printf("allocation error \n"); | |
69 | exit (-1); | |
70 | } | |
71 | ||
72 | /* Make sure the pointer is on 4K boundary */ | |
73 | if ((unsigned long)gpio_ram % PAGE_SIZE) | |
74 | gpio_ram += PAGE_SIZE - ((unsigned long)gpio_ram % PAGE_SIZE); | |
75 | ||
76 | /* Now map the physical addresses of the peripheral control registers | |
77 | into our address space */ | |
78 | gpio_mmap = (unsigned char *)mmap( | |
79 | (caddr_t)gpio_ram, | |
80 | BLOCK_SIZE, | |
81 | PROT_READ|PROT_WRITE, | |
82 | MAP_SHARED|MAP_FIXED, | |
83 | mem_fd, | |
84 | GPIO_ADD | |
85 | ); | |
86 | ||
87 | if ((long)gpio_mmap < 0) { | |
88 | printf("unable to map the memory. Did you run the program with administrator rights?\n"); | |
89 | exit (-1); | |
90 | } | |
91 | ||
92 | /* Always use a volatile pointer to hardware registers */ | |
93 | gpio = (volatile unsigned *)gpio_mmap; | |
94 | ||
95 | /* Now we have access to the hardware reigsters we can start twiddling I/O pins */ | |
96 | ||
97 | /* Switch GPIO 0, 1 and 2 to output mode */ | |
98 | SCLK_OUTPUT; | |
99 | IO_OUTPUT; | |
100 | CE_OUTPUT; | |
101 | ||
102 | /* Set the SCLK, IO and CE pins to default (low) */ | |
103 | SCLK_LOW; | |
104 | IO_LOW; | |
105 | CE_LOW; | |
106 | ||
107 | /* Short delay to allow the I/O lines to settle. */ | |
108 | usleep(2); | |
109 | } | |
110 | ||
111 | ||
112 | unsigned char read_rtc( unsigned char add ) | |
113 | { | |
114 | unsigned char val; | |
115 | int lp; | |
116 | ||
117 | val = add; | |
118 | ||
119 | /* Check LSB is set */ | |
120 | if ( !(add & 1 ) ) { | |
121 | printf("Incorrect read address specified - LSB must be set.\n"); | |
122 | exit (-1); | |
123 | } | |
124 | ||
125 | /* Check address range is valid */ | |
126 | if ( (add < 0x81) || (add > 0x91) ) { | |
127 | printf("Incorrect read address specified - It must be in the range 0x81..0x91\n"); | |
128 | exit (-1); | |
129 | } | |
130 | ||
131 | CE_HIGH; | |
132 | ||
133 | usleep(2); | |
134 | ||
135 | for (lp=0; lp<8; lp++) { | |
136 | if (val & 1) | |
137 | IO_HIGH; | |
138 | else | |
139 | IO_LOW; | |
140 | val >>= 1; | |
141 | usleep(2); | |
142 | SCLK_HIGH; | |
143 | usleep(2); | |
144 | SCLK_LOW; | |
145 | usleep(2); | |
146 | } | |
147 | ||
148 | IO_INPUT; | |
149 | ||
150 | for (lp=0; lp<8; lp++) { | |
151 | usleep(2); | |
152 | val >>= 1; | |
153 | if (IO_LEVEL) | |
154 | val |= 0x80; | |
155 | else | |
156 | val &= 0x7F; | |
157 | SCLK_HIGH; | |
158 | usleep(2); | |
159 | SCLK_LOW; | |
160 | usleep(2); | |
161 | } | |
162 | ||
163 | /* Set the I/O pin back to it's default, output low. */ | |
164 | IO_LOW; | |
165 | IO_OUTPUT; | |
166 | ||
167 | /* Set the CE pin back to it's default, low */ | |
168 | CE_LOW; | |
169 | ||
170 | /* Short delay to allow the I/O lines to settle. */ | |
171 | usleep(2); | |
172 | ||
173 | return val; | |
174 | } | |
175 | ||
176 | ||
177 | void write_rtc( unsigned char add, unsigned char val_to_write ) | |
178 | { | |
179 | unsigned char val; | |
180 | int lp; | |
181 | ||
182 | /* Check LSB is clear */ | |
183 | if ( add & 1 ) { | |
184 | printf("Incorrect write address specified - LSB must be cleared.\n"); | |
185 | exit (-1); | |
186 | } | |
187 | ||
188 | /* Check address range is valid */ | |
189 | if ( (add < 0x80) || (add > 0x90) ) { | |
190 | printf("Incorrect write address specified - It must be in the range 0x80..0x90\n"); | |
191 | exit (-1); | |
192 | } | |
193 | ||
194 | CE_HIGH; | |
195 | ||
196 | usleep(2); | |
197 | ||
198 | val = add; | |
199 | ||
200 | for (lp=0; lp<8; lp++) { | |
201 | if (val & 1) | |
202 | IO_HIGH; | |
203 | else | |
204 | IO_LOW; | |
205 | val >>= 1; | |
206 | usleep(2); | |
207 | SCLK_HIGH; | |
208 | usleep(2); | |
209 | SCLK_LOW; | |
210 | usleep(2); | |
211 | } | |
212 | ||
213 | val = val_to_write; | |
214 | ||
215 | for (lp=0; lp<8; lp++) { | |
216 | if (val & 1) | |
217 | IO_HIGH; | |
218 | else | |
219 | IO_LOW; | |
220 | val >>= 1; | |
221 | usleep(2); | |
222 | SCLK_HIGH; | |
223 | usleep(2); | |
224 | SCLK_LOW; | |
225 | usleep(2); | |
226 | } | |
227 | ||
228 | /* Set the I/O pin back to it's default, output low. */ | |
229 | IO_LOW; | |
230 | ||
231 | /* Set the CE pin back to it's default, low */ | |
232 | CE_LOW; | |
233 | ||
234 | /* Short delay to allow the I/O lines to settle. */ | |
235 | usleep(2); | |
236 | } | |
237 | ||
238 | ||
239 | int main(int argc, char **argv) | |
240 | { | |
241 | int lp; | |
242 | unsigned char val; | |
243 | int year,month,day,hour,minute,second; | |
244 | time_t epoch_time; | |
245 | struct tm time_requested; | |
246 | struct timeval time_setformat; | |
247 | ||
248 | /* Check that the program was called correctly */ | |
249 | if ( argc > 2 ) { | |
250 | printf("Too many arguments specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n"); | |
251 | exit (-1); | |
252 | } | |
253 | ||
254 | /* Set up gpi pointer for direct register access */ | |
255 | setup_io(); | |
256 | ||
257 | if ( argc == 2 ) { | |
258 | /* If the number of arguments are two, that means the user enter a date & time. */ | |
259 | /* Read that value and write it to the RTC chip */ | |
260 | ||
261 | sscanf(argv[1],"%4d%2d%2d%2d%2d%2d",&year,&month,&day,&hour,&minute,&second); | |
262 | ||
263 | /* Validate that the input date and time is basically sensible */ | |
264 | if ( (year < 2000) || (year > 2099) || (month < 1) || (month > 12) || | |
265 | (day < 1) || (day>31) || (hour < 0) || (hour > 23) || (minute < 0) || | |
266 | (minute > 59) || (second < 0) || (second > 59) ) { | |
267 | printf("Incorrect date and time specified.\nRun as:\nrtc-pi\nor\nrtc-pi CCYYMMDDHHMMSS\n"); | |
268 | exit (-1); | |
269 | } | |
270 | ||
271 | /* Got valid input - now write it to the RTC */ | |
272 | /* The RTC expects the values to be written in packed BCD format */ | |
273 | write_rtc(SEC_WRITE, ( (second/10) << 4) | ( second % 10) ); | |
274 | write_rtc(MIN_WRITE, ( (minute/10) << 4) | ( minute % 10) ); | |
275 | write_rtc(HOUR_WRITE, ( (hour/10) << 4) | ( hour % 10) ); | |
276 | write_rtc(DATE_WRITE, ( (day/10) << 4) | ( day % 10) ); | |
277 | write_rtc(MONTH_WRITE, ( (month/10) << 4) | ( month % 10) ); | |
278 | write_rtc(YEAR_WRITE, ( ((year-2000)/10) << 4) | (year % 10) ); | |
279 | ||
280 | /* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */ | |
281 | time_requested.tm_sec = second; | |
282 | time_requested.tm_min = minute; | |
283 | time_requested.tm_hour = hour; | |
284 | time_requested.tm_mday = day; | |
285 | time_requested.tm_mon = month-1; | |
286 | time_requested.tm_year = year-1900; | |
287 | time_requested.tm_wday = 0; /* not used */ | |
288 | time_requested.tm_yday = 0; /* not used */ | |
289 | time_requested.tm_isdst = -1; /* determine daylight saving time from the system */ | |
290 | ||
291 | epoch_time = mktime(&time_requested); | |
292 | ||
293 | /* Now set the clock to this time */ | |
294 | time_setformat.tv_sec = epoch_time; | |
295 | time_setformat.tv_usec = 0; | |
296 | ||
297 | lp = settimeofday(&time_setformat,NULL); | |
298 | ||
299 | /* Check that the change was successful */ | |
300 | if ( lp < 0 ) { | |
301 | printf("Unable to change the system time. Did you run the program as an administrator?\n"); | |
302 | printf("The operation returned the error message \"%s\"\n", strerror( errno ) ); | |
303 | exit (-1); | |
304 | } | |
305 | ||
306 | } else { | |
307 | /* The program was called without a date specified; therefore read the date and time from */ | |
308 | /* the RTC chip and set the system time to this */ | |
309 | second = read_rtc(SEC_READ); | |
310 | minute = read_rtc(MIN_READ); | |
311 | hour = read_rtc(HOUR_READ); | |
312 | day = read_rtc(DATE_READ); | |
313 | month = read_rtc(MONTH_READ); | |
314 | year = read_rtc(YEAR_READ); | |
315 | ||
316 | /* Finally convert to it to EPOCH time, ie the number of seconds since January 1st 1970, and set the system time */ | |
317 | /* Bearing in mind that the format of the time values in the RTC is packed BCD, hence the conversions */ | |
318 | ||
319 | time_requested.tm_sec = (((second & 0x70) >> 4) * 10) + (second & 0x0F); | |
320 | time_requested.tm_min = (((minute & 0x70) >> 4) * 10) + (minute & 0x0F); | |
321 | time_requested.tm_hour = (((hour & 0x30) >> 4) * 10) + (hour & 0x0F); | |
322 | time_requested.tm_mday = (((day & 0x30) >> 4) * 10) + (day & 0x0F); | |
323 | time_requested.tm_mon = (((month & 0x10) >> 4) * 10) + (month & 0x0F) - 1; | |
324 | time_requested.tm_year = (((year & 0xF0) >> 4) * 10) + (year & 0x0F) + 2000 - 1900; | |
325 | time_requested.tm_wday = 0; /* not used */ | |
326 | time_requested.tm_yday = 0; /* not used */ | |
327 | time_requested.tm_isdst = -1; /* determine daylight saving time from the system */ | |
328 | ||
329 | epoch_time = mktime(&time_requested); | |
330 | ||
331 | /* Now set the clock to this time */ | |
332 | time_setformat.tv_sec = epoch_time; | |
333 | time_setformat.tv_usec = 0; | |
334 | ||
335 | lp = settimeofday(&time_setformat,NULL); | |
336 | ||
337 | /* Check that the change was successful */ | |
338 | if ( lp < 0 ) { | |
339 | printf("Unable to change the system time. Did you run the program as an administrator?\n"); | |
340 | printf("The operation returned the error message \"%s\"\n", strerror( errno ) ); | |
341 | exit (-1); | |
342 | } | |
343 | } | |
344 | ||
345 | return 0; | |
346 | } |