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]-用标准输入 ...
随机推荐
- 关于JS变量和作用域
ECMAScript 变量:1.基本类型值(简单数据段) 2.引用类型值(可能由过个值构成的对象) → 保存在内存中的对象 动态属性: 只能给引用型值动态添加新属性,以便将来使用. 复制变量值 : 基 ...
- CheckStyle插件
如今代码静态检查越来越重要,已经成为构建高质量软件的不可或缺的一个验证步骤.如果你使用的是java语言,那么CheckStyle则是一个利器. CheckStyle能够帮助程序员检查代码是否符合制定的 ...
- VS C4819 编译错误解决方法
偶尔用别人的代码,出现: warning C4819: The file contains a character that cannot be represented ). Save the fil ...
- crontab指令详解
引用:http://www.cnblogs.com/xiaoluo501395377/archive/2013/04/06/3002602.html 具体指令请参考文章:linux指令. 详细版推荐原 ...
- 解决在IE浏览器下 boder边框出现断裂或虚线的问题
ie6.0下面经常会出现border边框断断续续的问题,等深一步了解了div之后自然会经常碰到这种问题了,不过初学div+css 的一般不会用遇到这个问题,因为初学者不会偷懒,等我们觉得用的很熟了,各 ...
- 重读LPTHW-Lesson25
1.ex25接触了字符串函数split().列表函数sorted().pop()函数,整理一下其用法以及其他常见的字符串.列表操作函数如下. (1)split()函数 split()是拆分字符串函数, ...
- 使用SelectClipRgn注意事项
SelectClipRgn 函数功能:该函数选择一个区域作为指定设备环境的当前剪切区域. 函数原型:int SelectClipRgn(HDc hdc, HRGN hrgn): 参数: hdc:设备环 ...
- MYSQL 数据类型的 3 个注意
注意 1. bit(Length) 这种数据类型中,最大长度只可以是64.就是说 bit(2) 对 bit(64) 对 bit(65) 错 bit(100) 错 注 ...
- 定制化Azure站点Java运行环境(3)
定制化Azure Website提供的默认的Tomcat和JDK环境 在我们之前的测试中,如果你访问你的WEB站点URL时不加任何上下文,实际上你看到的web界面是系统自带的测试页面index.jsp ...
- CentOS下安装Nginx并添加nginx_upload_module
安装前,最好能保证依赖的系统软件已经升级. yum update CentOS上安装Nginx,如果只是简单安装,不附加其他第三方模块,一句话可以搞定: yum install nginx ...