当前位置: 首页 > news >正文

正点原子嵌入式linux驱动开发——Linux RTC驱动

RTC也就是实时时钟,用于记录当前系统时间,对于Linux系统而言时间是非常重要的,就和使用Windows电脑或手机查看时间一样,在使用Linux设备的时候也需要查看时间。本章就来学习一下如何编写Linux下的RTC驱动程序。

Linux内核RTC驱动简介

RTC设备驱动是一个标准的字符设备驱动,应用程序通过open、release、read、write和ioctl等函数完成对RTC设备的操作,本章主要学习如何使用STM32MP1内部自带的 RTC外设

Linux内核将RTC设备抽象为rtc_device结构体,因此RTC设备驱动就是申请并初始化rtc_device,最后将rtc_device注册到Linux内核里面,这样Linux内核就有一个RTC设备的。至于RTC设备的操作肯定是用一个操作集合(结构体)来表示的,先来看一下rtc_device结构体, 此结构体定义在include/linux/rtc.h文件中,结构体内容如下 (删除条件编译):

rtc_device结构体

需要重点关注的是ops成员变量,这是一个rtc_class_ops类型的指针变量,rtc_class_ops为RTC设备的最底层操作函数集合,包括从RTC设备中读取时间、向RTC设备写入新的时间值等。因此,rtc_class_ops是需要用户根据所使用的RTC设备编写的,此结构体定义在include/linux/rtc.h文件中,内容如下:

rtc_class_ops结构体

看名字就知道rtc_class_ops操作集合中的这些函数是做什么的了,但是要注意,rtc_class_ops中的这些函数只是最底层的RTC设备操作函数,并不是提供给应用层的file_operations函数操作集。RTC是个字符设备,那么肯定有字符设备的file_operations函数操作集,Linux内核提供了一个RTC通用字符设备驱动文件,文件名为drivers/rtc/dev.c dev.c文件提供了所有RTC设备共用的file_operations函数操作集,如下所示:

RTC通用file_operations操作集

示例代码43.1.3,标准的字符设备操作集。应用程序可以通过ioctl函数来设置/读取时间、设置/读取闹钟的操作,对应的rtc_dev_ioctl函数就会执行。rtc_dev_ioctl最终会通过操作rtc_class_ops中的read_time、set_time等函数来对具体RTC设备的读写操作。简单来看一下rtc_dev_ioctl函数,函数内容如下(有省略):

示例代码 43.1.4 rtc_dev_ioctl 函数代码段
202 static long rtc_dev_ioctl (struct file *file,
203                            unsigned int cmd, unsigned long arg)
204 {
205     int err = 0;
206     struct rtc_device *rtc = file->private_data;
207     const struct rtc_class_ops *ops = rtc->ops;
208     struct rtc_time tm;
209     struct rtc_wkalrm alarm;
210     void __user *uarg = (void __user *)arg;
211
212     err = mutex_lock_interruptible(&rtc ops_lock);
213     if (err)
214         return err;
......
253     switch (cmd) {
......
317     case RTC_RD_TIME: /* 读取时间 */
318         mutex_unlock(&rtc->ops_lock);
319
320         err = rtc_read_time(rtc, &tm);
321         if (err < 0)
322             return err
323
324         if (copy_to_user(uarg, &tm, sizeof(tm)))
325             err = -EFAULT;
326         return err;
327
328     case RTC_SET_TIME: /* 设置时间 */
329         mutex_unlock(&rtc->ops_lock);
330
331         if(copy_from_user(&tm, uarg, sizeof(tm)))
332             return -EFAULT;
333
334         return rtc_set_time(rtc, &tm);
......
385     default:
386         /* Finally try the driver's ioctl interface */
387         if (ops->ioctl) {
388             err = ops->ioctl(rtc->dev.parent, cmd, arg);
389             if (err == -ENOIOCTLCMD)
390                 err = -ENOTTY;
391         } else {
392             err = -ENOTTY;
393         }
394         break;
395     }
396
397 done:
398     mutex_unlock(&rtc->ops_lock);
399     return err;
400 }

第317行,RTC_RD_TIME为时间读取命令。

第320行,如果是读取时间命令的话就调用rtc_read_time函数获取当前RTC时钟,rtc_read_time会调用__rtc_read_time函数,__rtc_read_time函数内容如下:

__rtc_read_time函数代码段

从第94行可以看出,__rtc_read_time函数会通过调用rtc_class_ops中的read_time成员变量来从RTC设备中获取当前时间。rtc_dev_ioctl函数对其他的命令处理都是类似的,比如RTC_ALM_READ命令会通过rtc_read_alarm函数获取到闹钟值,而rtc_read_alarm函数经过层层调用,最终会调用rtc_class_ops中的read_alarm函数来获取闹钟值。

至此,Linux内核中RTC驱动调用流程就很清晰了,如下图所示:

Linux RTC驱动调用流程

当rtc_class_ops准备好以后需要将其注册到Linux内核中,这里可以使用rtc_device_register函数完成注册工作。此函数会申请一个rtc_device并且初始化这个rtc_device,最后向调用者返回这个rtc_device,此函数原型如下:

struct rtc_device *rtc_device_register(const char *name, struct device *dev, const struct rtc_class_ops *ops, struct module *owner)

函数参数和返回值含义 如下:

  • name:设备名字。
  • dev:设备。
  • ops:RTC底层驱动函数集。
  • owner:驱动模块拥有者。
  • 返回值: 注册成功的话就返回rtc_device,错误的话会返回一个负值。

当卸载RTC驱动的时候需要调用rtc_device_unregister函数来注销注册的 rtc_device,函数原型如下:

void rtc_device_unregister(struct rtc_device *rtc)

函数参数和返回值含义如下:

  • rtc:要删除的rtc_device。
  • 返回值:无。

还有另外一对rtc_device注册函数devm_rtc_device_register和devm_rtc_device_unregister
分别为注册和注销rtc_device。

STM32MP1内部RTC驱动分析

STM32MP1的RTC驱动不用自己编写,因为ST已经写好了。其实对于大多数的SOC来讲,内部RTC驱动都不需要自己去编写,半导体厂商会编写好。分析驱动,先从设备树入手,打开stm32mp151.dtsi,在里面找到如下rtc设备节点,节点内容如下所示:

stm32mp151.dtsi文件rtc节点

第1747行设置兼容属性compatible的值为“st,stm32mp1-rtc”,因此在Linux内核源码中搜索此字符串即可找到对应的驱动文件,此文件为drivers/rtc/rtc-stm32.c,在rtc-stm32.c文件中找到如下所示内容:

设备platform驱动框架

第719-723行,设备树ID表。第722行,刚好有一个compatible属性和设备树的rtc的
compatible属性值一样,所以rtc设备节点会和此驱动匹配。

第1020-1028行,标准的platform驱动框架,当设备和驱动匹配成功以后stm32_rtc_probe函数就会执行,来看一下stm32_rtc_probe函数,函数内容如下(有省略):

示例代码 43.2.3 stm32_rtc_probe 函数代码段
789 static int stm32_rtc_probe(struct platform_device *pdev)
790 {
791     struct stm32_rtc *rtc;
792     const struct stm32_rtc_registers *regs;
793     struct resource *res;
794     int ret;
795
796     rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
797     if(!rtc)
798         return -ENOMEM;
799
800     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
801     rtc->base = devm_ioremap_resource(&pdev->dev, res);
......
856     ret = clk_prepare_enable(rtc->rtc_ck);
857     if (ret)
858         goto err;
......
872     ret = stm32_rtc_init(pdev, rtc);
873     if(ret)
874         goto err;
875
876     rtc->irq_alarm = platform_get_irq(pdev, 0);
877     if(rtc->irq_alarm <= 0) {
878         ret = rtc->irq_alarm;
879         goto err;
880     }
......
892     rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
893                     &stm32_rtc_ops, THIS_MODULE);
894     if(IS_ERR(rtc->rtc_dev)) {
895         ret = PTR_ERR(rtc->rtc_dev);
896         dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
897             ret);
898         goto err;
899     }
900
901     /* Handle RTC alarm interrupts */
902     ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
903                     stm32_rtc_alarm_irq, IRQF_ONESHOT,
904                     pdev->name, rtc);
905     if(ret) {
906         dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already
claimed\n",
907             rtc->irq_alarm);
908         goto err;
909     }
......
940
941     return 0;
......
954 }

第796行,调用devm_kzalloc申请rtc大小的空间,返回申请空间的首地址。

第800行,调用platform_get_resource函数从设备树中获取到RTC外设寄存器基地址。

第801行,调用函数devm_ioremap_resource 完成内存映射,得到RTC外设寄存器物理基地址对应的虚拟地址。

第856行,调用clk_prepare_enable函数使能时钟。

第872行,初始化STM32MP1 rtc的寄存器。

第876行,获取设备树的中断号。

第892行,调用devm_rtc_device_register函数向系统注册rtc_devcie,RTC底层驱动集为stm32_rtc_ops。stm32_rtc_ops操作集包含了读取/设置RTC时间,读取/设置闹钟等函数。

第902行,调用devm_request_threaded_irq函数请求RTC中断,中断服务函数为stm32_rtc_alarm_irq,用于RTC闹钟中断。

stm32_rtc_ops内容如下所示:

rtc_class_ops操作集

就以第624行的stm32_rtc_read_time函数为例讲解一下rtc_class_ops的各个RTC底层操作函数该如何去编写。stm32_rtc_read_time函数用于读取RTC时间值,此函数内容如下所示:

stm32_rtc_read_time代码段

第371-372行,调用readl_relaxed读取STM32MP1的RTC_TR和RTC_DR这两个寄存器的值,其中TR寄存器为RTC时间寄存器,保存着时、分、秒信息;DR为RTC的日期寄存器,保存着年、月、日信息。通过这两个寄存器就可以得到RTC时间。

第374-381行,前两行获取到了TR和DR这两个寄存器的值,这里需要从这两个寄存器值中提取出具体的年、月、日和时、分、秒信息。

第385行,上面得到的时间信息为BCD格式的,这里通过bcd2tm函数将BCD格式转换
为rtc_time格式,rtc_time结构体定义如下:

rtc_time结构体

RTC时间查看与设置

使能内部RTC

在Linux内核移植的时候,设备树是经过精简的,没有启动RTC功能。打开stm32mp157d-atk.dts文件,添加如下代码:

示例代码43.3.1.1 rtc节点信息 
1 &rtc { 
2     status = "okay"; 
3 };

追加的RTC节点内容很简单,就是把status属性改为“okay”。接着重新编译设备树然后使用新编译的stm32mp157d-atk.dtb文件启动开发板。

查看时间

RTC是用来记时的,因此最基本的就是查看时间,Linux内核启动的时候可以看到系统时钟设置信息,如下图所示:

Linux启动log信息

从上图中可以看出,Linux内核在启动的时候将rtc设置为rtc0,大家的启动信息可能会和上图中不同,但是基本上都是一样的。

如果要查看时间的话输入“date”命令即可,结果如下图所示:

当前时间值

从上图可以看出,当前时间为2000年1月1日03:30:29,很明显时间不对,需要重新设置RTC时间。

RTC时间设置也是使用的date命令,输入“date --help”命令即可查看date命令如何设置系统时间,结果如下图所示:

date命令帮助信息

比如现在设置当前时间为2021年5月2日18:53:00,因此输入如下命令:

date -s "2021-05-02 18:53:00"

设置完成以后再次使用date命令查看一下当前时间就会发现时间改过来了。

注意使用“date -s”命令仅仅是修改了当前时间,此时间还没有写入到STM32MP1内部RTC里面或其他的RTC芯片里面,因此系统重启以后时间又会丢失。需要将当前的时间写入到RTC里面,这里要用到hwclock命令,输入如下命令将系统时间写入到RTC里面:

hwclock -w //将当前系统时间写入到 RTC里面

时间写入到RTC里面以后就不怕系统重启以后时间丢失了,如果STM32MP1开发板底板接了纽扣电池,那么开发板即使断电了时间也不会丢失。可以尝试一下不断电重启和断电重启这两种情况下开发板时间会不会丢失。

总结

这一章节比较简单,因为Linux内核已经实现了RTC的驱动,对我们来说我只要会用“date”命令和“hwclock”命令去修改使用RTC就可以了。

相关文章:

正点原子嵌入式linux驱动开发——Linux RTC驱动

RTC也就是实时时钟&#xff0c;用于记录当前系统时间&#xff0c;对于Linux系统而言时间是非常重要的&#xff0c;就和使用Windows电脑或手机查看时间一样&#xff0c;在使用Linux设备的时候也需要查看时间。本章就来学习一下如何编写Linux下的RTC驱动程序。 Linux内核RTC驱动…...

基于EasyCVR技术的大数据视频汇聚与智能分析平台设计方案

一、背景需求 大数据中心的数据建设如火如荼&#xff0c;针对其中城市中的视频监管及算法分析&#xff0c;各卡口监控、治安监控&#xff0c;电警监控不同网络、不同地域&#xff0c;如何进行视频融合、进行统一监管&#xff0c;则是大数据中心解决方案数据汇聚的重中之重。 现…...

骨传导耳机到底好用吗,到底骨传导耳机是不是噱头呢?

随着社会的飞速发展以及科技的不断提升&#xff0c;人们对健康的关注度也逐渐提高起来。而在这种背景下&#xff0c;骨传导耳机以其独特不可替代的优势&#xff0c;吸引了一大群骨传导爱好者的目光。 那么骨传导耳机是不是噱头呢&#xff1f;其实这种耳机不仅不会堵塞耳道&…...

bitsandbytes 遇到CUDA Setup failed despite GPU being available.

使用conda 管理环境时加载大模型会遇到bitsandbytes无法识别cuda的情况&#xff1a; 此处windows系统&#xff1a; pip install bitsandbytes-windowslinux 系统&#xff1a; 将bitsandbytes版本降低至0.39.0 pip install bitsandbytes0.39.0...

【机器学习】决策树与分类案例分析

决策树与分类案例分析 文章目录 决策树与分类案例分析1. 认识决策树2. 分类3. 决策树的划分依据4. 决策树API5. 案例&#xff1a;鸢尾花分类6. 决策树可视化7. 总结 1. 认识决策树 决策树思想的来源非常朴素&#xff0c;程序设计中的条件分支结构就是if-else结构&#xff0c;最…...

基于物联网、大数据、云计算、人工智能等技术的智慧工地源码(Java+Spring Cloud +UniApp +MySql)

智慧工地是指利用物联网、大数据、云计算、人工智能等技术手段&#xff0c;为建筑施工现场提供智能硬件及物联网平台的解决方案&#xff0c;实现建筑工地的实时化、可视化、多元化、智慧化、便捷化。智慧工地的建设目标是实现全天候的管理监控&#xff0c;提高施工效率和质量&a…...

Py之pypdf:pypdf的简介、安装、使用方法之详细攻略

Py之pypdf&#xff1a;pypdf的简介、安装、使用方法之详细攻略 目录 pypdf的简介 pypdf的安装 pypdf的使用方法 1、基础用法 pypdf的简介 pypdf是一个免费的、开源的纯python PDF库&#xff0c;能够拆分、合并、裁剪和转换PDF文件的页面。它还可以为PDF文件添加自定义数据…...

谷歌Bard更新!会有哪些体验升级?

今年2月&#xff0c;谷歌的对话机器人Bard在发布会上翻车&#xff0c;遭到了科技圈的群嘲。如今半年过去了&#xff0c;Bard卷土重来&#xff0c;在9月发布了它的重磅更新“扩展插件”&#xff0c;集成了Gmail、Google Docs、Youtube 、Google Drive、Google Maps、Google Flig…...

[SHCTF 2023 校外赛道] reverse

week1 ez_asm 想不到第1题是个汇编&#xff0c;咱也不知道拿啥能弄成c&#xff0c;不过这题也不难&#xff0c;直接能看懂&#xff0c;关键部分。 取出异或0x1e然后保存&#xff0c;再取出-0xa再保存。 .text:0000000000401566 loc_401566: …...

pytorch:Model模块专题

一、说明 关于pytorch使用中&#xff0c;模块扮演重要校色&#xff0c;大部分功能不能密集展现&#xff0c;因此&#xff0c;我们这个文章中&#xff0c;将模块的种种功能详细演示一遍。 二、模块 PyTorch使用模块来表示神经网络。模块包括&#xff1a; 有状态计算的构建块。…...

Spring更加简单的读取和存储对象

前言&#xff1a;在上篇文章中&#xff0c;小编写了一个Spring的创建和使用的相关博客&#xff1a;Spring的创建和使用-CSDN博客&#xff0c;但是&#xff0c;操作/思路比较麻烦&#xff0c;那么本文主要带领大家走进&#xff1a;Spring更加简单的读取和存储对象&#xff01; 本…...

Webpack5 系列:Babel 的配置

1.前言 本篇将介绍对于项目中 JS 文件的处理。 2.babel-loader 2-1.依赖安装 npm install -D babel-loader babel/core babel/preset-env2-2.Loader 配置 webpack.config.js module: {rules: [{test: /\.?js$/,exclude: /node_modules/,use: {loader: babel-loader}}] }…...

【Spring】DI依赖注入,Lombok以及SpEL

文章目录 1.什么是DI依赖注入2. set方法注入3. ref属性4. 有参构造方法注入5. Lombok6. SpEL 1.什么是DI依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种设计模式&#xff0c;也是Spring框架的核心概念之一。其基本思想是将程序中的各…...

甘特图组件DHTMLX Gantt用例 - 如何自定义任务、月标记和网格新外观

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求&#xff0c;是最完善的甘特图图表库。 本文将为大家揭示DHTMLX Gantt自定义的典型用例&#xff0c;包括自定义任务、网格的新外观等&#xff0c;来展示其功能的强大性&…...

auto自动类型推导总结

auto 自动推导的规则很多、很细&#xff0c;当涉及移动语义、模板等复杂的规则时&#xff0c;很容易绕进去。因此&#xff0c;在使用 auto 进行自动推导时&#xff0c;牢记以下几点&#xff1a; auto 推导出的是 “值类型”&#xff0c;不会是 “引用类型”。auto 可以和 cons…...

透视2023,如何看清中国SaaS的未来之路?

导读&#xff1a;什么是更适合中国市场的SaaS道路&#xff1f; 如果用一个关键词概括2023年的SaaS产业&#xff0c;很多人会想到&#xff1a;难。 在过去一年时间内&#xff0c;SaaS产业投融资环境巨变&#xff0c;一级市场投融资笔数和金额骤减。根据IT桔子数据&#xff0c;20…...

分类预测 | Matlab实现KOA-CNN-LSTM-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-LSTM-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-LSTM-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Mat…...

博客系统-项目测试

自动化博客项目 用户注册登录验证效验个人博客列表页博客数量不为 0 博客系统主页写博客 我的博客列表页效验 刚发布的博客的标题和时间查看 文章详情页删除文章效验第一篇博客 不是 "自动化测试" 注销退出到登录页面,用户名密码为空 用户注册 Order(1)Parameterized…...

Inspeckage,动态分析安卓 APP 的 Xposed 模块

前提 我在不久前写过《 APP 接口拦截与参数破解》的博文&#xff1b;最近爬取APP数据时又用到了相关技术&#xff0c;故在此详细描述一下 Inspeckage 的功能。&#xff08;环境准备本文不再赘述&#xff09; 功能 在电脑上访问 http://127.0.0.1:8008 就可以看到 inspeckage…...

Windows详细安装和彻底删除RabbitMQ图文流程

RabbiitMQ简介 RabbitMQ是实现了高级消息队列协议&#xff08;AMQP&#xff1a;Advanced Message Queue Protocol&#xff09;的开源消息代理软件&#xff08;亦称面向消息的中间件&#xff09;。RabbitMQ服务器是用Erlang语言编写的&#xff0c;而聚类和故障转移是构建在开放…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

Cesium1.95中高性能加载1500个点

一、基本方式&#xff1a; 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器

——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的​​一体化测试平台​​&#xff0c;覆盖应用全生命周期测试需求&#xff0c;主要提供五大核心能力&#xff1a; ​​测试类型​​​​检测目标​​​​关键指标​​功能体验基…...

python/java环境配置

环境变量放一起 python&#xff1a; 1.首先下载Python Python下载地址&#xff1a;Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个&#xff0c;然后自定义&#xff0c;全选 可以把前4个选上 3.环境配置 1&#xff09;搜高级系统设置 2…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

QT: `long long` 类型转换为 `QString` 2025.6.5

在 Qt 中&#xff0c;将 long long 类型转换为 QString 可以通过以下两种常用方法实现&#xff1a; 方法 1&#xff1a;使用 QString::number() 直接调用 QString 的静态方法 number()&#xff0c;将数值转换为字符串&#xff1a; long long value 1234567890123456789LL; …...

是否存在路径(FIFOBB算法)

题目描述 一个具有 n 个顶点e条边的无向图&#xff0c;该图顶点的编号依次为0到n-1且不存在顶点与自身相连的边。请使用FIFOBB算法编写程序&#xff0c;确定是否存在从顶点 source到顶点 destination的路径。 输入 第一行两个整数&#xff0c;分别表示n 和 e 的值&#xff08;1…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...