0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

浅析Linux RTC实时时钟

嵌入式攻城狮 来源:嵌入式攻城狮 作者:嵌入式攻城狮 2022-11-25 15:07 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

实时时钟是个常用的外设,可以用来获取年、月、日和时间等信息。目前大多数的芯片内部都自带了实时时钟外设模块。例如本实验所使用的I.MX6ULL芯片内部SNVS就提供了RTC(实时计数器)功能。SNVS(安全的非易性存储)里面主要是一些低功耗的外设,其可以在芯片掉电后由电池供电继续运行。RTC需要外接晶振来提供时钟,本实验中I.MX6ULL芯片外接了一个32.768KHz的晶振,原理图如下

ccc42222-6c8d-11ed-8abf-dac502259ad0.png

1. Linux内核RTC驱动简介

RTC 设备驱动是一个标准的字符设备驱动,应用程序通过open、release、read、write和ioctl等函数完成对 RTC 设备的操作

内核将 RTC 设备抽象为rtc_device结构体,RTC设备驱动就是申请并初始化rtc_device,最后将 rtc_device 注册到Linux内核里面,此结构体定义在include/linux/rtc.h文件中

structrtc_device
{
structdevicedev;/*设备*/
structmodule*owner;

intid;/*ID*/
charname[RTC_DEVICE_NAME_SIZE];/*名字*/

conststructrtc_class_ops*ops;/*RTC设备底层操作函数*/
structmutexops_lock;

structcdevchar_dev;/*字符设备*/
unsignedlongflags;
......
......
};

结构体中的ops成员变量是RTC设备的底层操作函数集合,是一个 rtc_class_ops 类型的指针变量,需要用户根据所使用的RTC设备编写的,此结构体定义在include/linux/rtc.h 文件中,内容如下

structrtc_class_ops{
int(*open)(structdevice*);
void(*release)(structdevice*);
int(*ioctl)(structdevice*,unsignedint,unsignedlong);
int(*read_time)(structdevice*,structrtc_time*);
int(*set_time)(structdevice*,structrtc_time*);
int(*read_alarm)(structdevice*,structrtc_wkalrm*);
int(*set_alarm)(structdevice*,structrtc_wkalrm*);
int(*proc)(structdevice*,structseq_file*);
int(*set_mmss64)(structdevice*,time64_tsecs);
int(*set_mmss)(structdevice*,unsignedlongsecs);
int(*read_callback)(structdevice*,intdata);
int(*alarm_irq_enable)(structdevice*,unsignedintenabled);
};

rtc_class_ops 是最底层的 RTC 设备操作函数,并不是提供给应用层的。内核提供了一个 RTC 通用字符设备驱动文件,文件名为 drivers/rtc/rtc-dev.c, 理论提供了所有 RTC 设备共用的 file_operations 函数操作集,如下所示:

staticconststructfile_operationsrtc_dev_fops={
.owner=THIS_MODULE,
.llseek=no_llseek,
.read=rtc_dev_read,
.poll=rtc_dev_poll,
.unlocked_ioctl=rtc_dev_ioctl,
.open=rtc_dev_open,
.release=rtc_dev_release,
.fasync=rtc_dev_fasync,
};
应用程序可以通过 ioctl 函数来设置/读取时间、设置/读取闹钟的操作,那么对应的 rtc_dev_ioctl 函数就会执行,rtc_dev_ioctl 最终会通过操作 rtc_class_ops 中的 read_time、 set_time 等函数来对具体 RTC 设备的读写操作。内核中 RTC 驱动调用流程图如下示

cce959f2-6c8d-11ed-8abf-dac502259ad0.png

2.Linux内核RTC驱动分析

一般情况下,半导体厂商都会编写好内部RTC驱动,无需我们自已动手编写。但是有必要了解一下是如何编写的

打开imx6ull.dtsi,然后找到 snvs_rtc 节点内容,如下所示:

snvs_rtc:snvs-rtc-lp{
compatible="fsl,sec-v4.0-mon-rtc-lp";
regmap=<&snvs>;
offset=<0x34>;
interrupts=19IRQ_TYPE_LEVEL_HIGH>,
20IRQ_TYPE_LEVEL_HIGH>;
};

根据compatible属性值,在Linux源码中搜索"fsl,sec-v4.0-mon-rtc-lp"符串,即可找到对应的驱动文件drivers//rtc/rtc-snvs.c

staticconststructof_device_idsnvs_dt_ids[]={
{.compatible="fsl,sec-v4.0-mon-rtc-lp",},
{/*sentinel*/}
};
MODULE_DEVICE_TABLE(of,snvs_dt_ids);
staticstructplatform_driversnvs_rtc_driver={
.driver={
.name="snvs_rtc",
.pm=SNVS_RTC_PM_OPS,
.of_match_table=snvs_dt_ids,
},
.probe=snvs_rtc_probe,
};
module_platform_driver(snvs_rtc_driver);

可见这是一个标准的platform驱动,当驱动和设备匹配以后snvs_rtc_probe函数就会执行

staticintsnvs_rtc_probe(structplatform_device*pdev){
structsnvs_rtc_data*data;
structresource*res;
intret;
void__iomem*mmio;

data=devm_kzalloc(&pdev->dev,sizeof(*data),GFP_KERNEL);
if(!data)
return-ENOMEM;

data->regmap=syscon_regmap_lookup_by_phandle(pdev->dev.of_node,"regmap");
if(IS_ERR(data->regmap)){
dev_warn(&pdev->dev,"snvsrtc:youuseolddtsfile,pleaseupdateit
");
//从设备树中获取RTC外设寄存器基地址
res=platform_get_resource(pdev,IORESOURCE_MEM,0);
//内存映射,获得RTC外设寄存器物理基地址对应的虚拟地址
mmio=devm_ioremap_resource(&pdev->dev,res);
if(IS_ERR(mmio))
returnPTR_ERR(mmio);
//采用regmap机制来读写RTC底层硬件寄存器
data->regmap=devm_regmap_init_mmio(&pdev->dev,mmio,&snvs_rtc_config);
}else{
data->offset=SNVS_LPREGISTER_OFFSET;
of_property_read_u32(pdev->dev.of_node,"offset",&data->offset);
}

if(!data->regmap){
dev_err(&pdev->dev,"Can'tfindsnvssyscon
");
return-ENODEV;
}
//从设备树中获取RTC的中断号
data->irq=platform_get_irq(pdev,0);
if(data->irq< 0)
returndata->irq;
......

platform_set_drvdata(pdev,data);

//用regmap机制的regmap_write函数完成对寄存器进行写操作
regmap_write(data->regmap,data->offset+SNVS_LPPGDR,SNVS_LPPGDR_INIT);
//清除LPSR寄存器
regmap_write(data->regmap,data->offset+SNVS_LPSR,0xffffffff);
//使能RTC
snvs_rtc_enable(data,true);
device_init_wakeup(&pdev->dev,true);
//请求RTC中断
ret=devm_request_irq(&pdev->dev,data->irq,
snvs_rtc_irq_handler,
IRQF_SHARED,"rtcalarm",&pdev->dev);
if(ret){
dev_err(&pdev->dev,"failedtorequestirq%d:%d
",data->irq,ret);
gotoerror_rtc_device_register;
}
//向系统注册rtc_devcie
data->rtc=devm_rtc_device_register(&pdev->dev,pdev->name,&snvs_rtc_ops,THIS_MODULE);
if(IS_ERR(data->rtc)){
ret=PTR_ERR(data->rtc);
dev_err(&pdev->dev,"failedtoregisterrtc:%d
",ret);
gotoerror_rtc_device_register;
}

return0;

error_rtc_device_register:
if(data->clk)
clk_disable_unprepare(data->clk);
returnret;
}

RTC 底层驱动snvs_rtc_ops操作集包含了读取/设置RTC时间,读取/设置闹钟等函数。其内容如下

staticconststructrtc_class_opssnvs_rtc_ops={
.read_time=snvs_rtc_read_time,
.set_time=snvs_rtc_set_time,
.read_alarm=snvs_rtc_read_alarm,
.set_alarm=snvs_rtc_set_alarm,
.alarm_irq_enable=snvs_rtc_alarm_irq_enable,
};

以 snvs_rtc_read_time 函数为例,介绍RTC底层操作函数该如何去编写,该函数用于读取RTC时间值

staticintsnvs_rtc_read_time(structdevice*dev,structrtc_time*tm){
structsnvs_rtc_data*data=dev_get_drvdata(dev);
//获取RTC计数值,该值是秒数
unsignedlongtime=rtc_read_lp_counter(data);
//将获取到的秒数转换为时间值,也就是rtc_time结构体类型
rtc_time_to_tm(time,tm);

return0;
}

rtc_time 结构体定义如下:

structrtc_time{
inttm_sec;
inttm_min;
inttm_hour;
inttm_mday;
inttm_mon;
inttm_year;
inttm_wday;
inttm_yday;
inttm_isdst;
};

rtc_read_lp_counter 函数,此函数用于读取 RTC 计数值,函数内容如下

staticu32rtc_read_lp_counter(structsnvs_rtc_data*data){
u64read1,read2;
u32val;
//读取RTC_LPSRTCMR和RTC_LPSRTCLR这两个寄存器,得到RTC的计数值
do{
regmap_read(data->regmap,data->offset+SNVS_LPSRTCMR,&val);
read1=val;
read1<<= 32;
regmap_read(data->regmap,data->offset+SNVS_LPSRTCLR,&val);
read1|=val;
regmap_read(data->regmap,data->offset+SNVS_LPSRTCMR,&val);
read2=val;
read2<<= 32;
regmap_read(data->regmap,data->offset+SNVS_LPSRTCLR,&val);
read2|=val;
}while((read1>>CNTR_TO_SECS_SH)!=(read2>>CNTR_TO_SECS_SH));

/*Convert47-bitcounterto32-bitrawsecondcount*/
return(u32)(read1>>CNTR_TO_SECS_SH);
}

3. RTC时间相关设置

RTC 是用来计时的,最基本的就是查看时间,Linux内核启动时可以看到系统时钟设置信息

cd0f0404-6c8d-11ed-8abf-dac502259ad0.png

查看时间命令:date

设置当前时间:date -s

date-s"2022-08-151300"
将当前时间写入到RTC里:hwclock -w

cd213840-6c8d-11ed-8abf-dac502259ad0.png


审核编辑 :李倩


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11628

    浏览量

    217990
  • 晶振
    +关注

    关注

    35

    文章

    3442

    浏览量

    72660
  • 时钟
    +关注

    关注

    11

    文章

    1953

    浏览量

    134550

原文标题:浅析 Linux RTC 实时时钟

文章出处:【微信号:嵌入式攻城狮,微信公众号:嵌入式攻城狮】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    芯伯乐PCF8563:0.25µA低功耗实时时钟解决方案

    在各类需要精准计时、日历功能的嵌入式系统中,实时时钟芯片(RTC)是不可或缺的核心元件。芯伯乐PCF8563作为一款基于IIC接口的超低功耗实时时钟/日历芯片,以其高精度、小封装、强抗干扰能力
    的头像 发表于 11-28 18:34 129次阅读
    芯伯乐PCF8563:0.25µA低功耗<b class='flag-5'>实时时钟</b>解决方案

    RVMCU课堂「20」: 手把手教你玩转RVSTAR—实时时钟RTC

    实时时钟(Real-Time Clock,RTC)常用于制作时钟日历。RTC电路分属于两个电源域:备份域和VDD电源域。RTC的核心计数部
    发表于 10-29 08:10

    ‌bq3285实时时钟(RTC)芯片技术文档总结

    bq3285 在断电期间对时钟、日历和存储寄存器进行写保护。然后,备用电池维护数据并作时钟和日历。 bq3285 是一款完全兼容的实时时钟,适用于 IBM AT 兼容计算机和其他应用。唯一的外部组件是一个 32.768kH
    的头像 发表于 09-23 10:49 559次阅读
    ‌bq3285<b class='flag-5'>实时时钟</b>(<b class='flag-5'>RTC</b>)芯片技术文档总结

    实时时钟芯片与晶振的不同之处

    实时时钟芯片和晶振在电子设备中都扮演着提供时钟信号的重要角色,但它们的本质、功能和复杂程度却大相径庭。简单来说,晶振是产生稳定频率的“心脏”,而实时时钟芯片则是管理和分配这些“心跳”的“大脑”。
    的头像 发表于 07-24 17:04 1328次阅读
    <b class='flag-5'>实时时钟</b>芯片与晶振的不同之处

    Analog Devices / Maxim Integrated MAX31329 I2C实时时钟 (RTC)数据手册

    Analog Devices MAX31329 I^2^C实时时钟 (RTC) 是一 款低电流计时器件,可提供纳安级 (nA) 的计时电流,从而延长电池寿命。该器件 集成了32.768kHz晶体
    的头像 发表于 06-27 10:35 585次阅读
    Analog Devices / Maxim Integrated MAX31329 I2C<b class='flag-5'>实时时钟</b> (<b class='flag-5'>RTC</b>)数据手册

    频控器件企业泰晶科技车规级超高精度实时时钟RTC赋能汽车电子

    剩余续航与充电桩位置提示……   这些“便捷”与“精确”的背后都依赖于电子设备的“时序基准源”——实时时钟(Real-Time Clock, RTC)。凭借超低功耗、极高精度和断电持续运行等优异产品特性,RTC成为汽车内多个系统
    的头像 发表于 06-25 11:50 1520次阅读
    频控器件企业泰晶科技车规级超高精度<b class='flag-5'>实时时钟</b><b class='flag-5'>RTC</b>赋能汽车电子

    第二十八章 RTC——实时时钟

    本文介绍了W55MH32的RTC外设,其为掉电可运行的32位计数器,常用LSE 32.768KHz时钟源,引入UNIX时间戳概念。还介绍相关库函数,及配置RTC、处理中断、显示时间和校准LSI频率的实验。
    的头像 发表于 06-20 14:08 1106次阅读
    第二十八章 <b class='flag-5'>RTC</b>——<b class='flag-5'>实时时钟</b>

    ST M41T66Q6F 低功耗串行实时时钟RTC)内置32.768 kHz振荡器参数特性 EDA模型与数据手册

    ST M41T66Q6F 低功耗串行实时时钟RTC)内置32.768 kHz振荡器参数特性 EDA模型与数据手册
    的头像 发表于 06-16 17:39 941次阅读
    ST M41T66Q6F 低功耗串行<b class='flag-5'>实时时钟</b>(<b class='flag-5'>RTC</b>)内置32.768 kHz振荡器参数特性 EDA模型与数据手册

    RTC实时时钟芯片D8563和D1302简介

    RTC实时时钟芯片,具有功耗低、走时精准、外围简单等特点,二者基本特性如下: D8563和D1302二者基本特性比对 三、引脚信息 引脚信息比对 四、参考设计 1、D8563参考设计: 2、D1302参考设计: 审核编辑 黄宇
    的头像 发表于 06-11 09:54 697次阅读
    <b class='flag-5'>RTC</b><b class='flag-5'>实时时钟</b>芯片D8563和D1302简介

    “耐高温!”RTC时钟芯片+电池的应用案例(二)

    实时时钟,简称RTC,是广泛应用于电子产品的重要元器件。爱普生RTC实时时钟具有高精度、高稳定性和多功能等特点,广泛应用于多个行业。RTC
    的头像 发表于 06-04 17:35 1344次阅读
    “耐高温!”<b class='flag-5'>RTC</b><b class='flag-5'>时钟</b>芯片+电池的应用案例(二)

    实时时钟模块选择指南和比较表

    爱普生提供内置 32.768 kHz 晶体单元的多种实时时钟模块。除了单纯的计时功能外,还有即使在高温环境下也能保持准确计时的产品,以及配备其他各种功能的产品。您可以使用下面的流程图和产品比较表来
    发表于 03-14 10:28 0次下载

    RA4000CE爱普生RTC实时时钟模块:车载BMS系统的理想选择

    爱普生RTC模块集成32.768kHz石英晶体振荡器与实时时钟芯片,为BMS提供精确的时间和日期信息,助力系统执行时间相关操作。该模块采用QMEMS技术和半导体技术,具备高精度和低电流损耗特性,配备
    的头像 发表于 03-12 17:16 1046次阅读

    爱普生实时时钟与晶振技术赋能NIC网络接口卡

    在网络技术飞速发展的当下,NIC网络接口卡(网卡)作为设备与网络连接的关键桥梁,其时间同步精度直接决定了网络性能的稳定性和效率。爱普生(EPSON)凭借其领先的实时时钟RTC)与晶振技术,为NIC
    的头像 发表于 03-12 13:38 699次阅读
    爱普生<b class='flag-5'>实时时钟</b>与晶振技术赋能NIC网络接口卡

    Abracon推出内置XO实时时钟

    Abracon近期推出内置XO实时时钟(RTC)是现代电子系统中的核心组件,专为追求极低功耗与高精度时间记录的应用而设计。 与传统基于外部晶体的RTC不同,Abracon的内置XO RTC
    的头像 发表于 02-06 11:20 1276次阅读

    RTC时钟芯片+电池的应用案例(一)

    实时时钟,简称RTC,是广泛应用于电子产品的重要元器件。爱普生RTC实时时钟具有高精度、高稳定性和多功能的特点,目前广泛应用于多个行业。下面通过几个视频来详细了解一下爱普生
    的头像 发表于 01-08 11:25 2416次阅读
    <b class='flag-5'>RTC</b><b class='flag-5'>时钟</b>芯片+电池的应用案例(一)