RTC定时开机闹钟

  • Post author:
  • Post category:其他


RTC是Real Time Clock的简称,它在硬件电路上单独供电,当系统关机时,CPU和其他外部硬件设备全部掉电,但是RTC仍然继续工作.

HWCR (Hibernate Wakeup Control Register)是一个控制休眠唤醒的寄存器,如果我们要使用休眠状态下RTC唤醒的功能,我们需要打开它的第0位ELAM(RTC Alarm Wakeup enable),当ELAM置1时,使能ELAM功能。

RTCSR (RTC Second Registe)是一个32位的寄存器,它的值以1Hz的频率加1,即每秒自动加1。

RTCSAR (RTC Second Alarm Register)是一个以秒为单位的闹钟寄存器,我们可以将设置的格林威治时间转换成相应的秒数然后写进这个寄存器,即完成了我们设置的闹钟。我们打开HWCR中的ELAM,按power键关机,当RTC检测到RTCSR == RTCSAR的值时,RTC将会唤醒CPU,并从XBOOT开始进行开机启动。

对于设置RTCSAR的操作,我们可以用一个应用rtc_test.c来完成:

  1 #include <stdio.h>
  2 #include <linux/rtc.h>
  3 #include <sys/ioctl.h>
  4 #include <sys/time.h>
  5 #include <sys/types.h>
  6 #include <fcntl.h>
  7 #include <unistd.h>
  8 #include <stdlib.h>
  9 #include <errno.h>
 10 
 11 /*
 12  * This expects the new RTC class driver framework, working with
 13  * clocks that will often not be clones of what the PC-AT had.
 14  * Use the command line to specify another RTC if you need one.
 15  */
 16 static const char default_rtc[] = "/dev/rtc0";
 17 int main(int argc, char **argv)
 18 {
 19     int i, fd, retval, irqcount = 0, alarm_time = 5;
 20     unsigned long tmp, data;
 21     struct rtc_time rtc_tm;
 22     const char *rtc = default_rtc;
 23     switch (argc)
 24     {
 25         case 2:
 26             rtc = argv[1];
 27             /* FALLTHROUGH */
 28         case 1:
 29             break;
 30         default:
 31             fprintf(stderr, "usage:  rtctest [rtcdev]\n");
 32             return 1;
 33     }
 34     fd = open(rtc, O_RDONLY);
 35     if (fd ==  -1)
 36     {
 37         perror(rtc);
 38         exit(errno);
 39     }
 40     fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
 41 
 42 #if 0
 43     /* Turn on update interrupts (one per second) */
 44     retval = ioctl(fd, RTC_UIE_ON, 0);
 45     if (retval == -1)
 46     {
 47         if (errno == ENOTTY)
 48         {
 49             fprintf(stderr,    "\n...Update IRQs not supported.\n");
 50             goto test_READ;
 51         }
 52         perror("RTC_UIE_ON ioctl");
 53         exit(errno);
 54     }
 55     fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:\n",rtc);
 56     fflush(stderr);
 57     for (i=1; i<6; i++)
 58     {
 59         /* This read will block */
 60         retval = read(fd, &data, sizeof(unsigned long));
 61         if (retval == -1)
 62         {
 63             perror("read");
 64             exit(errno);
 65         }
 66         fprintf(stderr, " %d",i);
 67         fflush(stderr);
 68         irqcount++;
 69     }
 70     fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
 71     fflush(stderr);
 72     for (i=1; i<6; i++)
 73     {
 74         struct timeval tv = {5, 0};     /* 5 second timeout on select */
 75         fd_set readfds;
 76         FD_ZERO(&readfds);
 77         FD_SET(fd, &readfds);
 78         /* The select will wait until an RTC interrupt happens. */
 79         retval = select(fd+1, &readfds, NULL, NULL, &tv);
 80         if (retval == -1)
 81         {
 82             perror("select");
 83             exit(errno);
 84         }
 85         /* This read won't block unlike the select-less case above. */
 86         retval = read(fd, &data, sizeof(unsigned long));
 87         if (retval == -1)
 88         {
 89             perror("read");
 90             exit(errno);
 91         }
 92         fprintf(stderr, " %d",i);
 93         fflush(stderr);
 94         irqcount++;
 95     }
 96     /* Turn off update interrupts */
 97     retval = ioctl(fd, RTC_UIE_OFF, 0);
 98     if (retval == -1)
 99     {
100         perror("RTC_UIE_OFF ioctl");
101         exit(errno);
102     }
103 #endif
104 //test_READ:
105     /* Read the RTC time/date */
106 
107     printf("Set the alarm time after: ");
108     scanf("%d", &alarm_time);
109     fprintf(stderr, "seconds");
110     //fprintf(stderr, "Set the alarm time after %d seconds", alarm_time);
111 
112     retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
113     if (retval == -1)
114     {
115         perror("RTC_RD_TIME ioctl");
116         exit(errno);
117     }
118     fprintf(stderr, "\n\nCurrent RTC date\time is %d-%d-%d, %02d:%02d:%02d.\n",rtc_tm.tm_mday, rtc_tm.tm_mon + 1, rtc_tm.tm_year + 1900,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
119     /* Set the alarm to 5 sec in the future, and check for rollover */
120     rtc_tm.tm_sec += alarm_time;
121     if (rtc_tm.tm_sec >= 60)
122     {
123         rtc_tm.tm_sec %= 60;
124         rtc_tm.tm_min++;
125     }
126     if  (rtc_tm.tm_min == 60)
127     {
128         rtc_tm.tm_min = 0;
129         rtc_tm.tm_hour++;
130     }
131     if  (rtc_tm.tm_hour == 24)
132         rtc_tm.tm_hour = 0;
133     retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
134     if (retval == -1)
135     {
136         if (errno == ENOTTY)
137         {
138             fprintf(stderr,"\n...Alarm IRQs not supported.\n");
139             //goto test_PIE;
140         }
141         perror("RTC_ALM_SET ioctl");
142         exit(errno);
143     }
144     /* Read the current alarm settings */
145     retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
146     if (retval == -1)
147     {
148         perror("RTC_ALM_READ ioctl");
149         exit(errno);
150     }
151     fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
152     fflush(stderr);
153     /* Enable alarm interrupts */
154     retval = ioctl(fd, RTC_AIE_ON, 0);
155     if (retval == -1)
156     {
157         perror("RTC_AIE_ON ioctl");
158         exit(errno);
159     }
160 #if 0
161     //fprintf(stderr, "Waiting %d seconds for alarm...", alarm_time);
162     //fflush(stderr);
163     ///* This blocks until the alarm ring causes an interrupt */
164     //retval = read(fd, &data, sizeof(unsigned long));
165     //if (retval == -1)
166     //{
167     //    perror("read");
168     //    exit(errno);
169     //}
170     //irqcount++;
171     //fprintf(stderr, " okay. Alarm rang.\n");
172     /* Disable alarm interrupts */
173     retval = ioctl(fd, RTC_AIE_OFF, 0);
174     if (retval == -1)
175     {
176         perror("RTC_AIE_OFF ioctl");
177         exit(errno);
178     }
179 #endif
180 #if 0
181 test_PIE:
182     /* Read periodic IRQ rate */
183     retval = ioctl(fd, RTC_IRQP_READ, &tmp);
184     if (retval == -1)
185     {
186         /* not all RTCs support periodic IRQs */
187         if (errno == ENOTTY)
188         {
189             fprintf(stderr, "\nNo periodic IRQ support\n");
190             goto done;
191         }
192         perror("RTC_IRQP_READ ioctl");
193         exit(errno);
194     }
195     fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp);
196     fprintf(stderr, "Counting 20 interrupts at:");
197     fflush(stderr);
198     /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
199     for (tmp=2; tmp<=1024; tmp*=2)
200     {
201         retval = ioctl(fd, RTC_IRQP_SET, tmp);
202         if (retval == -1)
203         {
204             /* not all RTCs can change their periodic IRQ rate */
205             if (errno == ENOTTY)
206             {
207                 fprintf(stderr,"\n...Periodic IRQ rate is fixed\n");
208                 goto done;
209             }
210             perror("RTC_IRQP_SET ioctl");
211             exit(errno);
212         }
213         fprintf(stderr, "\n%ldHz:\t", tmp);
214         fflush(stderr);
215         /* Enable periodic interrupts */
216         retval = ioctl(fd, RTC_PIE_ON, 0);
217         if (retval == -1)
218         {
219             perror("RTC_PIE_ON ioctl");
220             exit(errno);
221         }
222         for (i=1; i<21; i++)
223         {
224             /* This blocks */
225             retval = read(fd, &data, sizeof(unsigned long));
226             if (retval == -1)
227             {
228                 perror("read");
229                 exit(errno);
230             }
231             fprintf(stderr, " %d",i);
232             fflush(stderr);
233             irqcount++;
234         }
235         /* Disable periodic interrupts */
236         retval = ioctl(fd, RTC_PIE_OFF, 0);
237         if (retval == -1)
238         {
239             perror("RTC_PIE_OFF ioctl");
240             exit(errno);
241         }
242     }
243 done:
244 #endif
245     fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
246     close(fd);
247     return 0;
248 }

上面的程序中我只保留了设置RTC ALARM的操作,其他的全注释掉了。设置ALARM时需要ioctl RTC_ALM_SET和RTC_AIE_ON两个宏定义,其中RTC_ALM_SET设置的闹钟只有在24内有效,我们通过120~132行的代码也可以看出应用只设置了以时分秒为单位的值,假入我们将天/月/年也设上,133行的

retval = ioctl(fd, RTC_ALM_SET, &

rtc_tm);

会调到kernel/drivers/rtc/rtc-dev.c的rtc_dev_ioctl中:

 1     case RTC_ALM_SET:
 2         mutex_unlock(&rtc->ops_lock);
 3 
 4         if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
 5             return -EFAULT;
 6 
 7         alarm.enabled = 0;
 8         alarm.pending = 0;
 9         alarm.time.tm_wday = -1;
10         alarm.time.tm_yday = -1;
11         alarm.time.tm_isdst = -1;
12 
13         /* RTC_ALM_SET alarms may be up to 24 hours in the future.
14          * Rather than expecting every RTC to implement "don't care"
15          * for day/month/year fields, just force the alarm to have
16          * the right values for those fields.
17          *
18          * RTC_WKALM_SET should be used instead.  Not only does it
19          * eliminate the need for a separate RTC_AIE_ON call, it
20          * doesn't have the "alarm 23:59:59 in the future" race.
21          *
22          * NOTE:  some legacy code may have used invalid fields as
23          * wildcards, exposing hardware "periodic alarm" capabilities.
24          * Not supported here.
25          */
26         {
27             unsigned long now, then;
28 
29             err = rtc_read_time(rtc, &tm);
30             if (err < 0)
31                 return err;
32             rtc_tm_to_time(&tm, &now);
33 
34             alarm.time.tm_mday = tm.tm_mday;
35             alarm.time.tm_mon = tm.tm_mon;
36             alarm.time.tm_year = tm.tm_year;
37             err  = rtc_valid_tm(&alarm.time);
38             if (err < 0)
39                 return err;
40             rtc_tm_to_time(&alarm.time, &then);
41 
42             /* alarm may need to wrap into tomorrow */
43             if (then < now) {
44                 rtc_time_to_tm(now + 24 * 60 * 60, &tm);
45                 alarm.time.tm_mday = tm.tm_mday;
46                 alarm.time.tm_mon = tm.tm_mon;
47                 alarm.time.tm_year = tm.tm_year;
48             }
49         }
50 
51         return rtc_set_alarm(rtc, &alarm);

在rtc-dev.c的rtc_dev_ioctl中,RTC_ALM_SET的case会先将RTC寄存器RTCSR中的时间读出来,并转换成格林威治时间,应用设置下来的rtc_tm的天/月/年会被读取出来的tm中的天/月/年所覆盖,所以即使应用设置了也没有用。如果应用需要设置超过24小时以外的闹钟可以ioctl

RTC_WKALM_SET

的rtc_tm到驱动中。


ioctl RTC_ALM_SET后还需再ioctl一次RTC_AIE_ON。如果应用不ioctl

RTC_AIE_ON的话,

应用设置的rtc_tm不会被设到rtc驱动中struct rtc_class_ops的.set_alarm成员指针指向的函数,即rtc_tm的值不会被设到RTCSAR寄存器中,闹钟设置不会生效。

在Android4.3环境上交叉编译成

rtc_test

的可执行文件,rtc_test.c和Android.mk都放在external/test/下,其中Android.mk书写如下:

 1 LOCAL_PATH := $(call my-dir)
 2 
 3 include $(CLEAR_VARS)
 4 LOCAL_MODULE_TAGS := optional
 5 LOCAL_MODULE := rtc_test
 6 LOCAL_SRC_FILES := $(call all-subdir-c-files)
 7 LOCAL_C_INCLUDES:= $(LOCAL_C_INCLUDES)\
 8         $(PATH)\
 9         kernel/arch/mips/xburst/soc-4775/include\
10         kernel/arch/mips/include/asm/mach-generic
11 include $(BUILD_EXECUTABLE)

在项目目录执行 source build/envsetup.h和lunch后,再到external/test下执行mm,编译好的

rtc_test

放在out/target/product/xxxx/system/bin/rtc_test

adb push rtc_test到开发板的/system/bin下,并设置成777权限后即可使用此应用设置RTC的ALARM。

执行

rtc_test

时需要我们输入一个距离现在时间的定时闹钟,假如我们输入60,即60s,然后按power键关机,待到过了60s(RTCAR == RTCSAR)时,RTC会唤醒CPU启动开机流程。

由于我们在按power键关机后有几种不同的状态,比如板子不接USB充电线/板子接USB充电线/板子接USB充电线但隔了一段时间后又拔除,这几种情况都处于不同的XBOOT流程,所以我们还需在XBOOT中加入处于不同关机状态的RTC唤醒代码。

转载于:https://www.cnblogs.com/watson/p/3586792.html