RTC模块
更新时间:2024-04-11 18:44:01 阅读量: 综合文库 文档下载
RTC(Real-Time Clock)实时时钟为操作系统提供了一个可靠的时间,并且在断电的情况下,RTC实时时钟也可以通过电池供电,一直运行下去。
RTC通过STRB/LDRB这两个ARM指令向CPU传送8位数据(BCD码)。数据包括秒,分,小时,日期,天,月和年。RTC实时时钟依靠一个外部的32.768Khz的石英晶体,产生周期性的脉冲信号。每一个信号到来时,计数器就加1,通过这种方式,完成计时功能。
RTC实时时钟有如下一些特性:
1,BCD数据:这些数据包括秒、分、小时、日期、、星期几、月和年。 2,闰年产生器
3,报警功能:报警中断或者从掉电模式唤醒
4,解决了千年虫问题 (详见http://baike.http://www.wodefanwen.com//view/9349.htm) 5,独立电源引脚RTCVDD
6,支持ms中断作为RTOS内核时钟 7,循环复位(round reset)功能
如图,RTC实时时钟的框架图,XTIrtc和XTOrtc产生脉冲信号,即外部晶振。传给2^15的一个时钟分频器,得到一个128Hz的频率,这个频率用来产生滴答计数。当时钟计数为0时,产生一个TIME TICK中断信号。时钟控制器用来控制RTC实时时钟的功能。复位寄存器用来重置SEC和MIN寄存器。闰年发生器用来产生闰年逻辑。报警发生器用来控制是否产生报警信号。
1,闰年产生器:
闰年产生器可以基于BCDDATE,BCDMON,BCDYEAR决定每月最后一天的日期是28、29、30、31.一个8位计数器只能表示两位BCD码,每一位BCD码由4位表示。因此不能支持。因此不能决定00年是否为闰年,例如不能区别1900和2000年。RTC模块通过硬件逻辑支持2000年为闰年。因此这两位00指的是2000,而不是1900 2,后备电池:
即使系统电源关闭,RTC模块可以由后备电池通过RTCVDD引脚供电。当系统电源关闭时,CPU和RTC的接口应该被阻塞,后备电池应该只驱动晶振电路和BCD计数器,以消耗最少的电池。 3,报警功能:
在正常模式和掉电模式下,RTC在指定的时刻会产生一个报警信号。正常模式下,报警中断ALMINT有效,对应INT_RTC引脚。掉电模式下,报警中断ALMINT有效外还产生一个唤醒信号PMWKUP,对应PMWKUP引脚。RTC报警寄存器RTCALM决定是否使能报警状态和设置报警条件
RTC工作原理上网查一下,很多。而且不同板子的RTC寄存器也不同,这里以S3C2440为例
下面是RTC实时时钟构架:
与RTC核心有关的文件有:
/drivers/rtc/class.c 这个文件向linux设备模型核心注册了一个类RTC,然后向驱动程序提供了注册/注销接口
/drivers/rtc/rtc-dev.c 这个文件定义了基本的设备文件操作函数,如:open,read等
/drivers/rtc/interface.c 顾名思义,这个文件主要提供了用户程序与RTC驱动的接口函数,用户程序一般通过ioctl与RTC驱动交互,这里定义了每个ioctl命令需要调用的函数
/drivers/rtc/rtc-sysfs.c 与sysfs有关
/drivers/rtc/rtc-proc.c 与proc文件系统有关
/include/linux/rtc.h 定义了与RTC有关的数据结构
static char __initdata banner[] = \Electronics\\n\//标志语
static int __init s3c_rtc_init(void) //初始化模块
{
printk(banner);
return platform_driver_register(&s3c2410_rtc_driver);
}
static void __exit s3c_rtc_exit(void) //卸载模块 {
platform_driver_unregister(&s3c2410_rtc_driver); }
module_init(s3c_rtc_init); module_exit(s3c_rtc_exit);
void platform_driver_unregister(struct platfort_driver *drv) {
driver_unregister(&drv->driver); }
RTC实时时钟的平台驱动设备定义:
static struct platform_driver s3c2410_rtc_driver = { .probe =
s3c_rtc_probe, //RTC探测函数 .remove =
__devexit_p(s3c_rtc_remove), //RTC移除函数 .suspend =
s3c_rtc_suspend, //RTC挂起函数 .resume =
s3c_rtc_resume, //RTC恢复函数 .driver = {
.name =
\//驱动名字 .owner =
THIS_MODULE, //驱动模块 }, };
当调用plat_driver_register()函数注册驱动以后,会触发平台设备和驱动的匹配函数platform_match()。匹配成功,则会调用平台驱动中的probe()函数,RTC实时时钟驱动中对应的函数就是s3c_rtc_probe()。主要任务有以下:(请参考下面源代码) 1,读取平台设备的资源结构体s3c_rtc_resource中的第二个中断号,即滴答中断号 2,读取平台设备的资源结构体s3c_rtc_resource中的第一个中断号,即报警中断号
3,将RTC实时时钟的寄存器映射为虚拟地址,返回虚拟基地址 4,重新打开RTC实时时钟,通过调用s3c_rtc_enable()函数 5,设置RTC滴答中断间隔,并打开RTC滴答中断
6,调用rtc_device_register()函数注册RTC并退出,返回struct rtc_device 结构体 7,设置平台设备驱动的驱动数据dev->driver_dat为struct rtc_device指针
static int __devinit s3c_rtc_probe(struct platform_device *pdev) {
struct rtc_device *rtc; struct resource *res; int ret;
pr_debug(\
/* find the IRQs */
s3c_rtc_tickno = platform_get_irq(pdev, 1); //1代表第二个中断 这里被赋值46
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, \ return -ENOENT; }
s3c_rtc_alarmno = platform_get_irq(pdev, 0); //0代表第一个中断,这里被赋值24
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, \ return -ENOENT; }
pr_debug(\ s3c_rtc_tickno, s3c_rtc_alarmno);
/* get the memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (res == NULL) {
dev_err(&pdev->dev, \ return -ENOENT; }
s3c_rtc_mem = request_mem_region(res->start, res->end-res->start+1, pdev->name);
if (s3c_rtc_mem == NULL) {
dev_err(&pdev->dev, \ ret = -ENOENT; goto err_nores; }
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1); if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, \ ret = -EINVAL; goto err_nomap; }
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
pr_debug(\ readb(s3c_rtc_base + S3C2410_RTCCON));
s3c_rtc_setfreq(&pdev->dev, 1);
device_init_wakeup(&pdev->dev, 1);
/* register RTC and exit */
rtc = rtc_device_register(\ THIS_MODULE);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, \ ret = PTR_ERR(rtc); goto err_nortc; }
rtc->max_user_freq = 128;
platform_set_drvdata(pdev, rtc); return 0;
err_nortc:
s3c_rtc_enable(pdev, 0); iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores: return ret; }
RTC实时时钟设备由结构体struct rtc_device 表示struct rtc_device {
struct device dev; 设备结构体
struct module *owner; 身所在的模块
int id; 备的ID号
char name[RTC_DEVICE_NAME_SIZE];
const struct rtc_class_ops *ops; 数集
struct mutex ops_lock;
struct cdev char_dev; 个字符设备
unsigned long flags; 态标志
unsigned long irq_data; spinlock_t irq_lock; 锁
wait_queue_head_t irq_queue; 头
struct fasync_struct *async_queue;
struct rtc_task *irq_task; 务结构体
spinlock_t irq_task_lock; int irq_freq; int max_user_freq; //内嵌 //指向自//设//RTC名字//类操作函//互斥锁//内嵌一//RTC状//中断数据//中断自旋//中断等待队列//异步队列 //RTC的任//自旋锁 //中断频率 最大的用户频
率
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL struct work_struct uie_task; struct timer_list uie_timer;
/* Those fields are protected by rtc->irq_lock */ unsigned int oldsecs;
unsigned int uie_irq_active:1; unsigned int stop_uie_polling:1; unsigned int uie_task_active:1; unsigned int uie_timer_active:1;T #endif };
RTC平台设备结构体:
struct platform_device s3c_device_rtc = { .name = \ .id = -1,
.num_resources = ARRAY_SIZE(s3c_rtc_resource), .resource = s3c_rtc_resource, };
s3c2440处理器的RTC资源如下代码:
static struct resource s3c_rtc_resource[] = { [0] = {
.start = S3C24XX_PA_RTC,
.end = S3C24XX_PA_RTC + 0xff, .flags = IORESOURCE_MEM, },
[1] = {
.start = IRQ_RTC, .end = IRQ_RTC,
.flags = IORESOURCE_IRQ, },
[2] = {
.start = IRQ_TICK, .end = IRQ_TICK,
.flags = IORESOURCE_IRQ } };
RTC实时时钟的使能函数s3c_rtc_enable()
RTC实时时钟可以设置相应的寄存器来控制实时时钟的状态。这些状态包括使实时时钟开始工作,也包括使实时时钟停止工作。s3c_rtc_enable()函数用来设置实时时钟的工作状态。第一个参数是RTC的平台设备指针,第二个参数是使能标志en,en等于0时,表示实时时钟停止工作,en不等于0时,表示实时时钟开始工作。
static void s3c_rtc_enable(struct platform_device *pdev, int en) {
void __iomem *base = s3c_rtc_base; //将虚拟地址s3c_rtc_base赋给base指针
unsigned int tmp;
if (s3c_rtc_base == NULL) //如果为空,则返回。这表示没有成功申请到内存,设备驱动退出
return;
if (!en) { //如果en等于0,表示不允许RTC实时时钟工作,这时,需要RTCCON寄存器的最低位置0,表示不允许实时时钟计数。同时,需
要将TICNT寄存器的最高位置为0,表示不允许实时时钟产生报警中断 tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON); //不允许实时时钟计数
tmp = readb(base + S3C2410_TICNT);
writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT); //不允许实时时钟产生报警中断
} else {
/* re-enable the device, and check it is ok */
if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){ //将RTCCON的最低位置为0,使实时时钟工作起来
dev_info(&pdev->dev, \
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON); }
if ((readb(base + S3C2410_RTCCON) &
S3C2410_RTCCON_CNTSEL)){ //将RTCCON第2位置为0,不使用BCD计数选择器
dev_info(&pdev->dev, \
tmp = readb(base + S3C2410_RTCCON); writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON); }
if ((readb(base + S3C2410_RTCCON) &
S3C2410_RTCCON_CLKRST)){ //将RTCCON的第3位置为0,不重新设置计数器
dev_info(&pdev->dev, \
tmp = readb(base + S3C2410_RTCCON); writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON); } } }
set_rtc_setfreq()函数用来设置时钟脉冲中断的频率,即多少时间产生一次中断。第一个参数表示RTC的设备结构体,第二个参数表示频率,即多久产生一次中断。如果freq等于1,则表示1秒钟产生一次中断;等于2,表示每秒产生2次中断 static int s3c_rtc_setfreq(struct device *dev, int freq)
{
unsigned int tmp;
if (!is_power_of_2(freq)) //判断是不是2的倍数,不是返回
return -EINVAL;
spin_lock_irq(&s3c_rtc_pie_lock);
tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE; tmp |= (128 / freq)-1; //时钟脉冲1秒中产生128次时钟滴答。Period = (n+1) / 128 second => freq = 128 / (n+1) => n = 128 / freq - 1
writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock);
return 0; }
RTC设备注册函数rtc_device_register()
rtc实时时钟设备必须注册到内核中才能可以使用。在注册设备的过程中,将设备提供的应用程序的接口ops也指定到设备上。这样,当应用程序读取设备的数据时,就可以调用这些底层的驱动函数
struct rtc_device *rtc_device_register(const char *name, struct device *dev, const struct rtc_class_ops *ops, struct module *owner) {
struct rtc_device *rtc; int id, err;
if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) { //分配一个ID号,用来把一个数字与一个指针联系起来
err = -ENOMEM; goto exit; }
mutex_lock(&idr_lock); //加锁
err = idr_get_new(&rtc_idr, NULL, &id); //得到一个ID号 mutex_unlock(&idr_lock); //释放自旋锁
if (err < 0) goto exit;
id = id & MAX_ID_MASK;
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL); if (rtc == NULL) {
err = -ENOMEM; goto exit_idr; }
//初始化RTC设备结构体的相关成员。将ops操作函数赋值给ret->ops结构体指针。将用户可以设置的最大频率设为64 rtc->id = id; rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64; rtc->dev.parent = dev; rtc->dev.class = rtc_class;
rtc->dev.release = rtc_device_release;
//初始化锁和设置设备的名字 mutex_init(&rtc->ops_lock); spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock); init_waitqueue_head(&rtc->irq_queue);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE); dev_set_name(&rtc->dev, \
rtc_dev_prepare(rtc); //设置RTC设备的设备号
err = device_register(&rtc->dev); //向内核注册实时时钟设备 if (err)
goto exit_kfree;
//下面是向文件系统注册设备,这样就可以通过文件系统访问相应的设备
rtc_dev_add_device(rtc); rtc_sysfs_add_device(rtc); rtc_proc_add_device(rtc);
dev_info(dev, \ rtc->name, dev_name(&rtc->dev));
return rtc;
exit_kfree: kfree(rtc);
exit_idr:
mutex_lock(&idr_lock); idr_remove(&rtc_idr, id); mutex_unlock(&idr_lock);
exit:
dev_err(dev, \ name, err); return ERR_PTR(err); }
rtc_class_ops是一个对设备进行操作的抽象结构体。内核允许为设备建立一个设备文件,对设备文件的所有操作,就相当于对设备的操作。这样的好处是,用户程序可以使用访问普通文件的方法,来访问设备文件,进而访问设备。这样的方法,极大的减轻了程序员的编程负担,程序员不必熟悉新的驱动接口,就能够访问设备
struct rtc_class_ops {
int (*open)(struct device *); //打开一个设备,在该函数中可以对设备进行初始化。如果这个函数被赋值NULL,那么设备打开永远成功,并不会对设备产生影响
void (*release)(struct device *); //释放open()函数中申请的资源。其将在文件引用计数为0时,被系统调用。对应的应用程序的close()方法,但并不是每一次调用close()都会触发release()函数。其会在对设备文件的所有打开都释放后,才会被调用
int (*ioctl)(struct device *, unsigned int, unsigned long); //提供了一种执行设备特定命令的方法。例如,使设备复位,既不是读操作也不是写操作,不适合用read()和write()方法来实现。如果在应用程序中给ioctl传入没有定义的命令,那么将返回-ENOTTY的错误,表示设备不支持这个命令
int (*read_time)(struct device *, struct rtc_time *); //读取RTC设备的当前时间
int (*set_time)(struct device *, struct rtc_time *); //设置RTC设备的当前时间
int (*read_alarm)(struct device *, struct rtc_wkalrm *); //读取RTC设备的报警时间
int (*set_alarm)(struct device *, struct rtc_wkalrm *); //设置RTC设备的报警时间,当时间到达时,会产生中断信号
int (*proc)(struct device *, struct seq_file *); //用来读取proc文件系统的数据
int (*set_mmss)(struct device *, unsigned long secs);
int (*irq_set_state)(struct device *, int enabled); //设置中断状态 int (*irq_set_freq)(struct device *, int freq); //设置中断频率,最大不能超过64
int (*read_callback)(struct device *, int data);
int (*alarm_irq_enable)(struct device *, unsigned int enabled); //用来设置中断使能状态
int (*update_irq_enable)(struct device *, unsigned int enabled); //更新中断使能状态 };
实时时钟RTC的rtc_class_ops结构体定义如下: static const struct rtc_class_ops s3c_rtcops = { .open = s3c_rtc_open, .release = s3c_rtc_release, .read_time = s3c_rtc_gettime, .set_time = s3c_rtc_settime, .read_alarm = s3c_rtc_getalarm, .set_alarm = s3c_rtc_setalarm, .irq_set_freq = s3c_rtc_setfreq, .irq_set_state = s3c_rtc_setpie, .proc = s3c_rtc_proc, };
RTC设备打开函数由s3c_rtc_open()来实现,用户空间调用open时,最终会调用s3c_rtc_open()函数。该函数只要申请了两个中断,一个报警中断,一个计时中断。 static int s3c_rtc_open(struct device *dev) {
struct platform_device *pdev = to_platform_device(dev); //从device结构体转到platform_device
struct rtc_device *rtc_dev = platform_get_drvdata(pdev); //从pdev->dev的私有数据中得到rtc_device
int ret;
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, \//申请一个报警中断,将中断函数设为s3c_rtc_alarmirq(),并传递rtc_dev作为参数
if (ret) {
dev_err(dev, \ return ret; }
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, \//申请一个计数中断,将中断函数设为s3c_rtc_tickirq(),并传递rtc_dev作为参数
if (ret) {
dev_err(dev, \ goto tick_err; }
return ret;
tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev); return ret; }
RTC设备释放函数由s3c_rtc_release()来实现。用户空间调用close()时,最终会调用s3c_rtc_release()函数。该函数主要释放s3c_rtc_open()函数申请的两个中断 static void s3c_rtc_release(struct device *dev) {
struct platform_device *pdev = to_platform_device(dev); //从device结构体转到platform_device
struct rtc_device *rtc_dev = platform_get_drvdata(pdev); //从
pdev->dev的私有数据中得到rtc_device
/* do not clear AIE here, it may be needed for wake */
s3c_rtc_setpie(dev, 0);
free_irq(s3c_rtc_alarmno, rtc_dev); free_irq(s3c_rtc_tickno, rtc_dev); }
RTC实时时钟获得时间安函数
当调用read()函数时会间接的调用s3c_rtc_gettime()函数来获得实时时钟的时间。时间值分别保存在RTC实时时钟的各个寄存器中。这些寄存器是秒寄存器、日期寄存器、分钟寄存器、和小时寄存器。s3c_rtc_gettime()函数会使用一个struct rtc_time 的机构体来表示一个时间值
struct rtc_time { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year;
int tm_wday; //这三个RTC实时时钟未用 int tm_yday; int tm_isdst; };
存储在RTC实时时钟寄存器中的值都是以BCD码保存的。但是Linux驱动程序中使用二进制码形式。通过bcd2bin()
unsigned bcd2bin(unsigned char val) {
return (val & 0x0f) + (val >> 4) * 10; }
unsigned char bin2bcd(unsigned val) {
return ((val / 10) << 4) + val % 10; }
从RTC实时时钟得到时间的函数是s3c_rtc_gettime()。第一个参数是RTC设备结构体指针,第二个参数是前面提到的struct rtc_time。
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm) {
unsigned int have_retried = 0;
void __iomem *base = s3c_rtc_base;
retry_get_time:
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN); rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR); rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE); rtc_tm->tm_mon = readb(base + S3C2410_RTCMON); rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR); rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
/* the only way to work out wether the system was mid-update * when we read it is to check the second counter, and if it * is zero, then we re-try the entire read */
if (rtc_tm->tm_sec == 0 && !have_retried) { //如果秒寄存器中是0,则表示过去了一分钟,那么小时,天,月,等寄存器中的值都可能已经变化,则重新读取这些寄存器的值
have_retried = 1; goto retry_get_time; }
pr_debug(\ rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday, rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec); // 转化为二进制存储
rtc_tm->tm_sec = bcd2bin(rtc_tm->tm_sec); rtc_tm->tm_min = bcd2bin(rtc_tm->tm_min); rtc_tm->tm_hour = bcd2bin(rtc_tm->tm_hour); rtc_tm->tm_mday = bcd2bin(rtc_tm->tm_mday); rtc_tm->tm_mon = bcd2bin(rtc_tm->tm_mon); rtc_tm->tm_year = bcd2bin(rtc_tm->tm_year);
rtc_tm->tm_year += 100; //因为存储器中存放的是从1900年开始的时间,所有加上100(这是2000年开始,自己改变这个值) rtc_tm->tm_mon -= 1;
return 0; }
同理,下面看设置时钟函数
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm) {
void __iomem *base = s3c_rtc_base;
int year = tm->tm_year - 100; //理由如上
pr_debug(\ tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec);
/* we get around y2k by simply not supporting it */
if (year < 0 || year >= 100) { //由于寄存器的限制,RTC实时时钟只支持100年时间
dev_err(dev, \ return -EINVAL; }
//转化为BCD码写到相应的寄存器
writeb(bin2bcd(tm->tm_sec), base + S3C2410_RTCSEC); writeb(bin2bcd(tm->tm_min), base + S3C2410_RTCMIN); writeb(bin2bcd(tm->tm_hour), base + S3C2410_RTCHOUR); writeb(bin2bcd(tm->tm_mday), base + S3C2410_RTCDATE); writeb(bin2bcd(tm->tm_mon + 1), base + S3C2410_RTCMON); writeb(bin2bcd(year), base + S3C2410_RTCYEAR);
return 0; }
在正常模式和掉电模式下,RTC在指定的时刻会产生一个报警信号。正常模式下,报警中断ALMINT有效,对应INT_RTC引脚。掉电模式下,报警 中断ALMINT有效外还产生一个唤醒信号PMWKUP,对应PMWKUP引脚。RTC报警寄存器RTCALM决定是否使能报警状态和设置报警条件
这个指定的时刻由年、月、日、分、秒等组成,在Linux中由struct rtc_time结构体表示。这里struct rtc_time结构体被包含在struct rtc_wkalrm结构体中。
s3c_rtc_getalarm()函数用来获得这个时刻。该函数第一个参数是RTC设备结构体,第二个参数是包含报警时刻的rtc_wkalarm结构体。
static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm) {
struct rtc_time *alm_tm = &alrm->time; void __iomem *base = s3c_rtc_base; unsigned int alm_en;
alm_tm->tm_sec = readb(base + S3C2410_ALMSEC); alm_tm->tm_min = readb(base + S3C2410_ALMMIN); alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR); alm_tm->tm_mon = readb(base + S3C2410_ALMMON); alm_tm->tm_mday = readb(base + S3C2410_ALMDATE); alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
alm_en = readb(base + S3C2410_RTCALM);
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
pr_debug(\ alm_en,
alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday, alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
/* decode the alarm enable field */
if (alm_en & S3C2410_RTCALM_SECEN)
alm_tm->tm_sec = bcd2bin(alm_tm->tm_sec); else
alm_tm->tm_sec = 0xff;
if (alm_en & S3C2410_RTCALM_MINEN)
alm_tm->tm_min = bcd2bin(alm_tm->tm_min); else
alm_tm->tm_min = 0xff;
if (alm_en & S3C2410_RTCALM_HOUREN)
alm_tm->tm_hour = bcd2bin(alm_tm->tm_hour); else
alm_tm->tm_hour = 0xff;
if (alm_en & S3C2410_RTCALM_DAYEN)
alm_tm->tm_mday = bcd2bin(alm_tm->tm_mday); else
alm_tm->tm_mday = 0xff;
if (alm_en & S3C2410_RTCALM_MONEN) {
alm_tm->tm_mon = bcd2bin(alm_tm->tm_mon); alm_tm->tm_mon -= 1; } else {
alm_tm->tm_mon = 0xff;
}
if (alm_en & S3C2410_RTCALM_YEAREN)
alm_tm->tm_year = bcd2bin(alm_tm->tm_year); else
alm_tm->tm_year = 0xffff;
return 0; }
同理,报警时间设置函数如下:
static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) {
struct rtc_time *tm = &alrm->time; //得到RTC报警时间 void __iomem *base = s3c_rtc_base; //得到寄存器的虚拟内存地址的基地址
unsigned int alrm_en; //是否使能报警
pr_debug(\n\
alrm->enabled,
tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff, tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec); //打印一些调试信息
alrm_en = readb(base + S3C2410_RTCALM) &
S3C2410_RTCALM_ALMEN; //读出RTCALM的第6位,表示所有报警功能都打开
writeb(0x00, base + S3C2410_RTCALM); //将00写入RTCALM,使所有的功能都不可以用
if (tm->tm_sec < 60 && tm->tm_sec >= 0) { //大于0小于60,则设置报警秒寄存器ALMSEC的值,并设置RTCALM寄存器的第0位为1,表示打开秒报警功能
alrm_en |= S3C2410_RTCALM_SECEN;
writeb(bin2bcd(tm->tm_sec), base + S3C2410_ALMSEC); }
if (tm->tm_min < 60 && tm->tm_min >= 0) { alrm_en |= S3C2410_RTCALM_MINEN;
writeb(bin2bcd(tm->tm_min), base + S3C2410_ALMMIN); }
if (tm->tm_hour < 24 && tm->tm_hour >= 0) { alrm_en |= S3C2410_RTCALM_HOUREN;
writeb(bin2bcd(tm->tm_hour), base + S3C2410_ALMHOUR); }
pr_debug(\//打印报警使能状态
writeb(alrm_en, base + S3C2410_RTCALM);
s3c_rtc_setaie(alrm->enabled);
if (alrm->enabled) //使能中断唤醒功能 enable_irq_wake(s3c_rtc_alarmno); else
disable_irq_wake(s3c_rtc_alarmno);
return 0; }
RTC设置脉冲中断使能函数s3c_rtc_setpie() 该函数用来设置是否允许脉冲中断。
第一个参数是RTC设备结构体,第二个参数表示是否允许脉冲中断。enabled等于1表示允许,等于0表示不允许
static int s3c_rtc_setpie(struct device *dev, int enabled) {
unsigned int tmp;
pr_debug(\
spin_lock_irq(&s3c_rtc_pie_lock);
tmp = readb(s3c_rtc_base + S3C2410_TICNT) &
~S3C2410_TICNT_ENABLE; //读出TICNT的值,清除最高位
if (enabled) //如果enabled不等于0,则设置tmp变量最高位为允许脉冲中断
tmp |= S3C2410_TICNT_ENABLE;
writeb(tmp, s3c_rtc_base + S3C2410_TICNT); spin_unlock_irq(&s3c_rtc_pie_lock);
return 0; }
在proc文件系统中,可以读取proc文件系统来判断RTC实时时钟是否支持脉冲中断。脉冲中断由TICNT寄存器的最高位决定,最高位为1则表示使能脉冲中断,为0则表示不允许脉冲中断。proc文件系统中的读取命令,一般为cat命令,会调用内核中的s3c_rtc_proc()函数
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq) {
unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
seq_printf(seq, \
(ticnt & S3C2410_TICNT_ENABLE) ? \ return 0; }
正在阅读:
RTC模块04-11
法理学第1阶段测试题09-09
歌曲军港之夜主持人串词;军港之夜歌词02-09
重庆市三峡名校联盟2015 - 2016学年高二化学12月联考试题09-24
南方医科大学01-13
2022护士节文案_适合护士节发的文案_护士节文案句子04-04
茶修随想 - - - - 教养是航空母舰 知识是拐杖03-20
摘抄人生感悟哲理句子11-20
筹资过程中的内部控制02-28
- 计算机试题
- 【2012天津卷高考满分作文】鱼心人不知
- 教育心理学历年真题及答案--浙江教师资格考试
- 20180327-第六届“中金所杯”全国大学生金融知识大赛参考题库
- 洪林兴达煤矿2018年度水情水害预测预报
- 基本要道讲义
- 机电设备安装试运行异常现象分析与对策
- 《有机化学》复习资料-李月明
- 非常可乐非常MC2--非常可乐广告策划提案 - 图文
- 2011中考数学真题解析4 - 科学记数法(含答案)
- 企业人力资源管理师三级07- 09年真题及答案
- 基于单片机的光控自动窗帘控制系统设计说明书1 - 图文
- 20160802神华九江输煤皮带机安装方案001
- (共53套)新人教版一生物必修2(全册)教案汇总 word打印版
- 2014行政管理学总复习
- 中国银监会关于加强地方政府融资平台贷款风险监管的指导意见
- 民宿酒店核心竞争与研究
- 游园活动谜语大全2012
- 河南省天一大联考2016届高三英语5月阶段性测试试题(六)(A卷)
- 小型超市管理系统毕业论文详细设计4
- 模块
- RTC
- 妇产科习题一(胎膜早破 - 胎盘早剥 - 胎儿宫内窘迫)
- 证券基础知识重点摘要2012已修改
- 第八章寡头垄断企业的竞争行为 - 产业经济学
- (2014年)大学物理(上)复习题1
- maidas预应力混凝土梁的施工阶段分析
- 通用可变增益放大器(DOC)
- 超声流量计
- 课外阅读导读和练习资料
- 最全小学安全教育教案(精编)
- 南开17春秋学期《物权法》在线作业
- “十二五”时期洛南县民俗村建设规划
- 咸丰县第一中学
- 山东省五级中医药师承教育工作第一批指导老师、继承人名单汇总表
- 民主法治村先进事迹材料
- 2018年云南大理事业单位考试题答案
- 信息技术等级证书考试选择题
- 最新农贸市场项目环境影响报告书 - 图文
- 《中国的行政区划》导学案
- 作业
- 冲击英语专业四级语法与词汇 全真模拟练习1.2