RTC定时开机闹钟
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来完成:
- #include <stdio.h>
- #include <linux/rtc.h>
- #include <sys/ioctl.h>
- #include <sys/time.h>
- #include <sys/types.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <errno.h>
- /*
- * This expects the new RTC class driver framework, working with
- * clocks that will often not be clones of what the PC-AT had.
- * Use the command line to specify another RTC if you need one.
- */
- static const char default_rtc[] = "/dev/rtc0";
- int main(int argc, char **argv)
- {
- int i, fd, retval, irqcount = , alarm_time = ;
- unsigned long tmp, data;
- struct rtc_time rtc_tm;
- const char *rtc = default_rtc;
- switch (argc)
- {
- case :
- rtc = argv[];
- /* FALLTHROUGH */
- case :
- break;
- default:
- fprintf(stderr, "usage: rtctest [rtcdev]\n");
- return ;
- }
- fd = open(rtc, O_RDONLY);
- if (fd == -)
- {
- perror(rtc);
- exit(errno);
- }
- fprintf(stderr, "\n\t\t\tRTC Driver Test Example.\n\n");
- #if 0
- /* Turn on update interrupts (one per second) */
- retval = ioctl(fd, RTC_UIE_ON, );
- if (retval == -)
- {
- if (errno == ENOTTY)
- {
- fprintf(stderr, "\n...Update IRQs not supported.\n");
- goto test_READ;
- }
- perror("RTC_UIE_ON ioctl");
- exit(errno);
- }
- fprintf(stderr, "Counting 5 update (1/sec) interrupts from reading %s:\n",rtc);
- fflush(stderr);
- for (i=; i<; i++)
- {
- /* This read will block */
- retval = read(fd, &data, sizeof(unsigned long));
- if (retval == -)
- {
- perror("read");
- exit(errno);
- }
- fprintf(stderr, " %d",i);
- fflush(stderr);
- irqcount++;
- }
- fprintf(stderr, "\nAgain, from using select(2) on /dev/rtc:");
- fflush(stderr);
- for (i=; i<; i++)
- {
- struct timeval tv = {, }; /* 5 second timeout on select */
- fd_set readfds;
- FD_ZERO(&readfds);
- FD_SET(fd, &readfds);
- /* The select will wait until an RTC interrupt happens. */
- retval = select(fd+, &readfds, NULL, NULL, &tv);
- if (retval == -)
- {
- perror("select");
- exit(errno);
- }
- /* This read won't block unlike the select-less case above. */
- retval = read(fd, &data, sizeof(unsigned long));
- if (retval == -)
- {
- perror("read");
- exit(errno);
- }
- fprintf(stderr, " %d",i);
- fflush(stderr);
- irqcount++;
- }
- /* Turn off update interrupts */
- retval = ioctl(fd, RTC_UIE_OFF, );
- if (retval == -)
- {
- perror("RTC_UIE_OFF ioctl");
- exit(errno);
- }
- #endif
- //test_READ:
- /* Read the RTC time/date */
- printf("Set the alarm time after: ");
- scanf("%d", &alarm_time);
- fprintf(stderr, "seconds");
- //fprintf(stderr, "Set the alarm time after %d seconds", alarm_time);
- retval = ioctl(fd, RTC_RD_TIME, &rtc_tm);
- if (retval == -)
- {
- perror("RTC_RD_TIME ioctl");
- exit(errno);
- }
- fprintf(stderr, "\n\nCurrent RTC date\time is %d-%d-%d, %02d:%02d:%02d.\n",rtc_tm.tm_mday, rtc_tm.tm_mon + , rtc_tm.tm_year + ,rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
- /* Set the alarm to 5 sec in the future, and check for rollover */
- rtc_tm.tm_sec += alarm_time;
- if (rtc_tm.tm_sec >= )
- {
- rtc_tm.tm_sec %= ;
- rtc_tm.tm_min++;
- }
- if (rtc_tm.tm_min == )
- {
- rtc_tm.tm_min = ;
- rtc_tm.tm_hour++;
- }
- if (rtc_tm.tm_hour == )
- rtc_tm.tm_hour = ;
- retval = ioctl(fd, RTC_ALM_SET, &rtc_tm);
- if (retval == -)
- {
- if (errno == ENOTTY)
- {
- fprintf(stderr,"\n...Alarm IRQs not supported.\n");
- //goto test_PIE;
- }
- perror("RTC_ALM_SET ioctl");
- exit(errno);
- }
- /* Read the current alarm settings */
- retval = ioctl(fd, RTC_ALM_READ, &rtc_tm);
- if (retval == -)
- {
- perror("RTC_ALM_READ ioctl");
- exit(errno);
- }
- fprintf(stderr, "Alarm time now set to %02d:%02d:%02d.\n",rtc_tm.tm_hour, rtc_tm.tm_min, rtc_tm.tm_sec);
- fflush(stderr);
- /* Enable alarm interrupts */
- retval = ioctl(fd, RTC_AIE_ON, );
- if (retval == -)
- {
- perror("RTC_AIE_ON ioctl");
- exit(errno);
- }
- #if 0
- //fprintf(stderr, "Waiting %d seconds for alarm...", alarm_time);
- //fflush(stderr);
- ///* This blocks until the alarm ring causes an interrupt */
- //retval = read(fd, &data, sizeof(unsigned long));
- //if (retval == -1)
- //{
- // perror("read");
- // exit(errno);
- //}
- //irqcount++;
- //fprintf(stderr, " okay. Alarm rang.\n");
- /* Disable alarm interrupts */
- retval = ioctl(fd, RTC_AIE_OFF, );
- if (retval == -)
- {
- perror("RTC_AIE_OFF ioctl");
- exit(errno);
- }
- #endif
- #if 0
- test_PIE:
- /* Read periodic IRQ rate */
- retval = ioctl(fd, RTC_IRQP_READ, &tmp);
- if (retval == -)
- {
- /* not all RTCs support periodic IRQs */
- if (errno == ENOTTY)
- {
- fprintf(stderr, "\nNo periodic IRQ support\n");
- goto done;
- }
- perror("RTC_IRQP_READ ioctl");
- exit(errno);
- }
- fprintf(stderr, "\nPeriodic IRQ rate is %ldHz.\n", tmp);
- fprintf(stderr, "Counting 20 interrupts at:");
- fflush(stderr);
- /* The frequencies 128Hz, 256Hz, ... 8192Hz are only allowed for root. */
- for (tmp=; tmp<=; tmp*=)
- {
- retval = ioctl(fd, RTC_IRQP_SET, tmp);
- if (retval == -)
- {
- /* not all RTCs can change their periodic IRQ rate */
- if (errno == ENOTTY)
- {
- fprintf(stderr,"\n...Periodic IRQ rate is fixed\n");
- goto done;
- }
- perror("RTC_IRQP_SET ioctl");
- exit(errno);
- }
- fprintf(stderr, "\n%ldHz:\t", tmp);
- fflush(stderr);
- /* Enable periodic interrupts */
- retval = ioctl(fd, RTC_PIE_ON, );
- if (retval == -)
- {
- perror("RTC_PIE_ON ioctl");
- exit(errno);
- }
- for (i=; i<; i++)
- {
- /* This blocks */
- retval = read(fd, &data, sizeof(unsigned long));
- if (retval == -)
- {
- perror("read");
- exit(errno);
- }
- fprintf(stderr, " %d",i);
- fflush(stderr);
- irqcount++;
- }
- /* Disable periodic interrupts */
- retval = ioctl(fd, RTC_PIE_OFF, );
- if (retval == -)
- {
- perror("RTC_PIE_OFF ioctl");
- exit(errno);
- }
- }
- done:
- #endif
- fprintf(stderr, "\n\n\t\t\t *** Test complete ***\n");
- close(fd);
- return ;
- }
上面的程序中我只保留了设置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中:
- case RTC_ALM_SET:
- mutex_unlock(&rtc->ops_lock);
- if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
- return -EFAULT;
- alarm.enabled = ;
- alarm.pending = ;
- alarm.time.tm_wday = -;
- alarm.time.tm_yday = -;
- alarm.time.tm_isdst = -;
- /* RTC_ALM_SET alarms may be up to 24 hours in the future.
- * Rather than expecting every RTC to implement "don't care"
- * for day/month/year fields, just force the alarm to have
- * the right values for those fields.
- *
- * RTC_WKALM_SET should be used instead. Not only does it
- * eliminate the need for a separate RTC_AIE_ON call, it
- * doesn't have the "alarm 23:59:59 in the future" race.
- *
- * NOTE: some legacy code may have used invalid fields as
- * wildcards, exposing hardware "periodic alarm" capabilities.
- * Not supported here.
- */
- {
- unsigned long now, then;
- err = rtc_read_time(rtc, &tm);
- if (err < )
- return err;
- rtc_tm_to_time(&tm, &now);
- alarm.time.tm_mday = tm.tm_mday;
- alarm.time.tm_mon = tm.tm_mon;
- alarm.time.tm_year = tm.tm_year;
- err = rtc_valid_tm(&alarm.time);
- if (err < )
- return err;
- rtc_tm_to_time(&alarm.time, &then);
- /* alarm may need to wrap into tomorrow */
- if (then < now) {
- rtc_time_to_tm(now + * * , &tm);
- alarm.time.tm_mday = tm.tm_mday;
- alarm.time.tm_mon = tm.tm_mon;
- alarm.time.tm_year = tm.tm_year;
- }
- }
- 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书写如下:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE_TAGS := optional
- LOCAL_MODULE := rtc_test
- LOCAL_SRC_FILES := $(call all-subdir-c-files)
- LOCAL_C_INCLUDES:= $(LOCAL_C_INCLUDES)\
- $(PATH)\
- kernel/arch/mips/xburst/soc-/include\
- kernel/arch/mips/include/asm/mach-generic
- 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唤醒代码。
RTC定时开机闹钟的更多相关文章
- 吃我一记咸鱼突刺——使用板载RTC定时开机
前言 原创文章,转载引用务必注明链接.水平有限,欢迎指正. 2016年3月30日 Lemuntu(Base On Jessie) 3.10.37 原载于Lemaker论坛.汇总于此. 看ATC2603 ...
- 技嘉 gigabyte b75m d3v 主板 定时开机无效问题解决
BIOS 里面设置定时开机后发现到点并没有正常启动~~~ 百思不得解.后来发现原来是WIN8系统下的控制面板的关机并非正常关机,而是不保存设置的非正常关机,在开始菜单右键——关闭或注销——关闭计算机 ...
- Azure 基础 : 使用 Automation 定时开机
不知何时 Azure 为虚机提供了自动关机的功能.这是一个很棒的功能,可以帮助我们定时关闭虚机并释放掉资源以节省开支.如果某台虚机在夜间不需要提供服务,我们就可以把它配置为晚上的某个时间点自动关机: ...
- win7电脑定时开机设置方法
在BIOS设置主界面中选择“Power Management Setup”,进入“电源管理”窗口. 注:缺省情况下,“Resume By Alarm”定时开机选项是关闭的. 将鼠标移到“Resume ...
- Windows定时开机并开启工作软件
开启休眠功能 在搜索窗口中输入“cmd.exe”,在结果中看见了“cmd.exe”,右击选择“以管理员权限运行程序”打开“cmd.exe”命令窗口,输入命令“powercfg -h on”即可开启计算 ...
- wake on lan定时开机部署
在Linux下通过Wake On LAN实现网络唤醒远程开机 我们经常有这样的场景或需求,人在外面,需要将家里的机器或公司的机器开启,进行远程控制操作. 有几种方式可以实现远程开机,一是通过主板的来电 ...
- BeagleBone Black Industrial系统更新设置一贴通
前言 原创文章,转载引用务必注明链接.水平有限,欢迎指正. 本文使用markdown写成,为获得更好的阅读体验,推荐访问我的博客原文: http://www.omoikane.cn/2016/09/1 ...
- Android 每天定时提醒功能实现
android要实现定时的功能那肯定就要用到闹铃相关的技术, 那么android闹铃实现是基于 AlarmManager 这个类的,首先我们来看一下它的几个主要的方法. 打开AlarmManager的 ...
- ***linux下用cron定时执行任务的方法
名称 : crontab 使用权限 : 所有使用者 使用方式 : crontab file [-u user]-用指定的文件替代目前的crontab. crontab-[-u user]-用标准输入 ...
随机推荐
- 数据库监控[Z]
--查看表锁 select * from sys.v_$sqlarea where disk_reads>100 --监控事例的等待 select event,sum(decode(wai ...
- 宏定义 define
#define kOut -1 用一个字符串代替一个数据 用kOut表示-1(一般开头有一个小写的k) 作用: 1.为了让一些数据有意义 #define kUseId asdjlfdjafa #def ...
- 消除多余的row
tableviewName.tableFooterView = [[UIView alloc]initWithFrame:CGRectZero];
- 学php之翻译wordpress(2)
wp-load.php <?php /** * Bootstrap file for setting the ABSPATH constant * and loading the wp-conf ...
- jquery load(URL,FUNCTION(){}) 异步加载页面
$("#btnSearch").click(function () { var queryUrl = '/Report/LoadInsClassifFileNew'; if ($( ...
- WeUI
从WeUI学习到的知识点 WeUI是微信Web服务开发的UI套件, 目前包含12个模块 (Button, Cell, Toast, Dialog, Progress, Msg, Article, ...
- 阿里云CentOS 7.1使用yum安装MySql5.6.24
正确的安装方法: 众所周知,Linux系统自带的repo是不会自动更新每个软件的最新版本(基本都是比较靠后的稳定版),所以无法通过yum方式安装MySQL的高级版本.所以我们需要先安装带有当前可用的m ...
- shell基础——字符串连接
#!/bin/sh str1="hello" str2="world" echo str1=$str1, str2=$str2 strconn1=$str1$s ...
- DM368启动串口打印分析
DM36x initialization passed! TI UBL Version: 1.50 Booting Catalog Boot Loader //启动目 ...
- UML_交互图
交互图(Interaction Diagram)用来描述系统中的对象是如何进行相互作用的.即一组对象是如何进行消息传递的. 当交互图建模时,通常既包括对象(每个对象都扮演某一特定的角色),又包括消息( ...