View difference between Paste ID: YJ22Sayh and gF09XSFn
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
}