rt-thread rtc设备驱动开发

news2025/1/10 1:45:21

基于pico rtc设备驱动开发

      • I/O设备框架
      • RTC设备
      • 功能配置——启用Soft RTC
      • 功能配置——启用NTP时间自动同步
      • 功能配置——启用硬件RTC

RT-Thread 的 RTC (实时时钟)设备为操作系统的时间系统提供了基础服务。应用层对于 RTC 设备一般不存在直接调用的 API ,使用者中间接通过设备的 control 接口完成交互。

I/O设备框架

​ I/O 设备模型框架,如下图所示,它位于硬件和应用程序之间,共分为 I/O 设备管理层、设备驱动框架层、设备驱动层。
在这里插入图片描述
​ I/O 设备管理层实现了对设备驱动程序的封装。应用程序通过 I/O 设备管理接口获得正确的设备驱动,然后通过这个设备驱动与底层 I/O 硬件设备进行数据(或控制)交互。应用层通过该层提供的标准接口访问底层设备,只需关注功能,无需考虑底层的变更,从而降低了代码的耦合性、复杂性,提高了系统的可靠性。
在这里插入图片描述
​ 设备驱动框架层是对同类硬件设备驱动的抽象,为I/O设备管理层提供功能实现调用接口。

​ 设备驱动层是一组驱使硬件设备工作的程序,实现访问硬件设备的功能。

​ 从源码层面解释各层之间的联系,如下图所示。图中,横向表示分层,纵向表示各类的继承派生关系。从下到上不断抽象、屏蔽下层差异,体现了面向对象的抽象的思想。上层为基类,下层为派生类。派生类各自实现父类提供的统一接口。这样,驱动层的不同厂商的相同硬件模块可创建各自的派生类对象,然后对接到框架层同一的基类接口,从而形成多对一的面向抽象的关系。同理,从设备驱动框架层到IO设备管理接口层,又是上层对下层的一次抽象。

​ 在RT-Thread中,I/O设备管理层和驱动设备框架层已完成封装,若新增设备驱动到I/O设备框架时,一般只需要结合硬件设备提供的SDK对设备驱动层进行驱动开发。
在这里插入图片描述
详细可参考:https://www.rt-thread.org/document/site/#/rt-thread-version/rt-thread-standard/programming-manual/device/device

RTC设备

​ 在开启 RTC 设备框架以及 RTC 驱动之后,用户可以 #include <sys/time.h> 用来引用标准的时间操作函数,对软件或硬件RTC设备进行访问和操作。如:

​ 设置日期:rt_err_t set_date(rt_uint32_t year, rt_uint32_t month, rt_uint32_t day)

​ 设置时间:rt_err_t set_time(rt_uint32_t hour, rt_uint32_t minute, rt_uint32_t second)

​ 获取当前时间:time_t time(time_t *t)

功能配置——启用Soft RTC

​ 在rt-thread 源码下的pico设备目录下(如rt-thread-master/bsp/raspberry-pico/),构建dist项目框架。source ~/.env/env.sh 将env工具引入该路径,启动工具,在 menuconfig 中可以启用使用软件模拟 RTC 的功能。
在这里插入图片描述
​ scons编译构建后,将生成的rtthread-pico.uf2文件拷贝至pico设备(RPI-RP2 disk)中。通过串口连接访问设备。在Finsh命令行,执行date命令,查看效果如下图。实际验证中,Soft RTC的时间比实际要快很多,精度不高。
在这里插入图片描述
ps: C语言项目的裁剪配置本质上通过条件编译和宏的展开进行的。RT-Thread借助Kconfig机制实现该功能。Env在根目录下执行menuconfig命令后会递归解析各级Kconfig文件,然后提供如下配置界面,完成相应的配置后并保存,根目录下会存在一份.config文件保存当前选择的配置项,并将.config文件转为RT-Thread的系统配置文件rtconfig.h。详细内容可参考:https://bbs.elecfans.com/jishu_2279148_1_1.html

功能配置——启用NTP时间自动同步

​ 若 RT-Thread 已接入互联网,可启用 NTP 时间自动同步功能,定期同步本地时间。

​ 首先在 menuconfig 中按照如下选项开启 NTP 功能:
在这里插入图片描述
​ 开启 NTP 后 RTC 的自动同步功能将会自动开启,还可以设置同步周期和首次同步的延时时间:
在这里插入图片描述
​ 生成执行执行文件,在Finsh命令行,输入 date 即可查看当前当地时区时间,大致效果如下:
在这里插入图片描述

功能配置——启用硬件RTC

查看PICO RP2040设备现有驱动。查看UART和GPIO的驱动源码所在路径,后续将在该路径下添加RTC驱动相关的源码。
在这里插入图片描述
​ 硬件RTC驱动源码开发,参考现有的UART及GPIO的驱动源码,了解其结构及逻辑调用关系。参考PICO RP2040设备SDK下的…/pico-sdk/src/rp2_common/hardware_rtc下的RTC源码,了解当前PICO 设备下所能与硬件操作相关的函数实现。参考/rt-thread/components/drivers/rtc/下的RTC的设备驱动框架源码,了解RTC设备类封装的函数及相关接口体。综上,针对某一操作,传递至设备框架层的函数能够有效调用驱动层的函数,驱动层的函数调用设备SDK衍生出的函数,进而实现对硬件的操作。
在这里插入图片描述
​ 参考看门狗设备使用时序图,RTC设备驱动核心代码如下:

​ 创建RTC设备:
在这里插入图片描述
​ 根据设备框架层rtc.h下的结构体,创建设备驱动层drv_rtc.c下的_rtc_ops对象。
在这里插入图片描述
​ 接下来,依次实现RTC设备初始化函数(pico_rtc_init)、设置时间函数(pico_rtc_set_secs)、获取时间函数(pico_rtc_get_secs),使得RTC具备硬件访问能力。

​ RTC设备驱动程序根据RTC设备模型定义,创建出具备硬件访问能力的RTC设备实例后,将RTC设备通过 rt_hw_rtc_register() 接口注册到RTC设备驱动框架中。
在这里插入图片描述
​ 硬件RTC验证:
在这里插入图片描述
​ ps:设备驱动开发中所涉及到的其他源文件或头文件,可在当前项目下的libraries中的SConscript进行添加。

rtc_driver.c:

#include <rthw.h>
#include <rtthread.h>
#include <rtdevice.h>
#include <stdio.h>
#include <time.h>
#include <string.h>

#include "board.h"
#include "drv_rtc.h"
#include <sys/time.h>

#include "pico.h"
#include "hardware/rtc.h"
#include "hardware/irq.h"
#include "hardware/resets.h"
#include "hardware/clocks.h"

struct rtc_device_object
{
    rt_rtc_dev_t  rtc_dev;
#ifdef RT_USING_ALARM
    struct rt_rtc_wkalarm   wkalarm;
#endif
};

static struct rtc_device_object rtc_device;

bool rtc_running(void) {
    return (rtc_hw->ctrl & RTC_CTRL_RTC_ACTIVE_BITS);
}

static bool valid_datetime(datetime_t *t) {
    // Valid ranges taken from RTC doc. Note when setting an RTC alarm
    // these values are allowed to be -1 to say "don't match this value"
    if (!(t->year >= 0 && t->year <= 4095)) return false;
    if (!(t->month >= 1 && t->month <= 12)) return false;
    if (!(t->day >= 1 && t->day <= 31)) return false;
    if (!(t->hour >= 0 && t->hour <= 23)) return false;
    if (!(t->min >= 0 && t->min <= 59)) return false;
    if (!(t->sec >= 0 && t->sec <= 59)) return false;

    return true;
}

time_t datetime2sec(int year, int mon, int day, int hour, int min, int sec)
{
    struct tm tt;
    memset(&tt, 0, sizeof(tt));
    tt.tm_year = year;
    tt.tm_mon = mon;
    tt.tm_mday = day;
    tt.tm_hour = hour;
    tt.tm_min = min;
    tt.tm_sec = sec;
    return mktime(&tt);
}

static rt_err_t pico_rtc_init(void){
    // Get clk_rtc freq and make sure it is running
    uint rtc_freq = clock_get_hz(clk_rtc);
    assert(rtc_freq != 0);
    // Take rtc out of reset now that we know clk_rtc is running
    reset_block(RESETS_RESET_RTC_BITS);
    unreset_block_wait(RESETS_RESET_RTC_BITS);
    // Set up the 1 second divider.
    // If rtc_freq is 400 then clkdiv_m1 should be 399
    rtc_freq -= 1;
    // Check the freq is not too big to divide
    assert(rtc_freq <= RTC_CLKDIV_M1_BITS);
    // Write divide value
    rtc_hw->clkdiv_m1 = rtc_freq;
    return RT_EOK; 
}

bool rtc_get_datetime(datetime_t *t) {
    // Make sure RTC is running
    if (!rtc_running()) {
        return false;
    }

    // Note: RTC_0 should be read before RTC_1
    uint32_t rtc_0 = rtc_hw->rtc_0;
    uint32_t rtc_1 = rtc_hw->rtc_1;

    t->dotw  = (int8_t) ((rtc_0 & RTC_RTC_0_DOTW_BITS ) >> RTC_RTC_0_DOTW_LSB);
    t->hour  = (int8_t) ((rtc_0 & RTC_RTC_0_HOUR_BITS ) >> RTC_RTC_0_HOUR_LSB);
    t->min   = (int8_t) ((rtc_0 & RTC_RTC_0_MIN_BITS  ) >> RTC_RTC_0_MIN_LSB);
    t->sec   = (int8_t) ((rtc_0 & RTC_RTC_0_SEC_BITS  ) >> RTC_RTC_0_SEC_LSB);
    t->year  = (int16_t) ((rtc_1 & RTC_RTC_1_YEAR_BITS ) >> RTC_RTC_1_YEAR_LSB);
    t->month = (int8_t) ((rtc_1 & RTC_RTC_1_MONTH_BITS) >> RTC_RTC_1_MONTH_LSB);
    t->day   = (int8_t) ((rtc_1 & RTC_RTC_1_DAY_BITS  ) >> RTC_RTC_1_DAY_LSB);

    return true;
}

static rt_err_t pico_rtc_get_secs(time_t *sec)
{
    struct timeval tv;
    datetime_t t;
    rtc_get_datetime(&t);
    tv.tv_sec = datetime2sec(t.year,t.month,t.day,t.hour,t.min,t.sec);
    *(time_t *) sec = tv.tv_sec;

    return RT_EOK;
}


bool rtc_set_datetime(datetime_t *t) {
    if (!valid_datetime(t)) {
        return false;
    }

    // Disable RTC
    rtc_hw->ctrl = 0;
    // Wait while it is still active
    while (rtc_running()) {
        tight_loop_contents();
    }

    // Write to setup registers
    rtc_hw->setup_0 = (((uint32_t)t->year)  << RTC_SETUP_0_YEAR_LSB ) |
                      (((uint32_t)t->month) << RTC_SETUP_0_MONTH_LSB) |
                      (((uint32_t)t->day)   << RTC_SETUP_0_DAY_LSB);
    rtc_hw->setup_1 = (((uint32_t)t->dotw)  << RTC_SETUP_1_DOTW_LSB) |
                      (((uint32_t)t->hour)  << RTC_SETUP_1_HOUR_LSB) |
                      (((uint32_t)t->min)   << RTC_SETUP_1_MIN_LSB)  |
                      (((uint32_t)t->sec)   << RTC_SETUP_1_SEC_LSB);

    // Load setup values into rtc clock domain
    rtc_hw->ctrl = RTC_CTRL_LOAD_BITS;

    // Enable RTC and wait for it to be running
    rtc_hw->ctrl = RTC_CTRL_RTC_ENABLE_BITS;
    while (!rtc_running()) {
        tight_loop_contents();
    }

    return true;
}

static rt_err_t pico_rtc_set_secs(time_t *sec)
{
    datetime_t t;
    struct tm *local;
    local = localtime(sec);
    t.year = local->tm_year;
    t.month = local->tm_mon;
    t.day = local->tm_mday;
    t.hour = local->tm_hour;
    t.min = local->tm_min;
    t.sec = local->tm_sec;

    rtc_set_datetime(&t);
    return RT_EOK;
}

const static struct rt_rtc_ops _rtc_ops =
{
    pico_rtc_init,
    pico_rtc_get_secs,
    pico_rtc_set_secs,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
};

int rt_hw_rtc_init(void)
{
    rt_err_t ret = RT_EOK;
    rtc_device.rtc_dev.ops = &_rtc_ops;
    ret = rt_hw_rtc_register(&rtc_device.rtc_dev, "rtc", RT_DEVICE_FLAG_RDWR, RT_NULL);
    if(ret != RT_EOK){
        return ret;
    }
#ifdef RT_USING_ALARM
    rt_rtc_alarm_init();
#endif
}
INIT_DEVICE_EXPORT(rt_hw_rtc_init);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/771791.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

three.js学习

前言&#xff1a; three.js基本使用没问下&#xff0c;下面进入自定义图形 效果展示 实现 使用BufferGeometry()自定义 <script setup lang"ts"> import { ref, onMounted } from vue import * as THREE from three // 导入轨道控制器 import { OrbitContro…

Java对象深拷贝、浅拷贝之枚举类型

问题&#xff1a;为什么属于引用类型的enum不会有深拷贝浅拷贝的问题&#xff1f; 解释&#xff1a; 在Java中&#xff0c;枚举类型是一种特殊的类类型。每个枚举值都是该枚举类型的一个实例&#xff0c;并且这些实例在枚举类型被初始化时就已经被创建。这些实例在程序的整个…

2023年7月18日,File类,IO流,线程

File类 1. 概述 File&#xff0c;是文件和目录路径的抽象表示 File只关注文件本身的信息&#xff0c;而不能操作文件里的内容 。如果需要读取或写入文件内容&#xff0c;必须使用IO流来完成。 在Java中&#xff0c;java.io.File 类用于表示文件或目录的抽象路径名。它提供了一…

elementUI el-radio 无法点击的问题

<el-form-item label"B端客户类型" prop"user_type"><template slot"label"><span>B端客户类型</span><el-tooltip effect"dark" placement"top" content"B端大客户账期有效,只有设置该类型…

数据结构双向链表,实现增删改查

一、双向链表的描述 在单链表中&#xff0c;查找直接后继结点的执行时间为O(1)&#xff0c;而查找直接前驱的执行时间为O(n)。为克服单链表这种单向性的缺点&#xff0c;可以用双向链表。 在双向链表的结点中有两个指针域&#xff0c;一个指向直接后继&#xff0c;另一个指向直…

AJAX: 事件循环(举例细论)

概念&#xff1a;执行任务和收集异步任务&#xff0c;在调用栈空闲时&#xff0c;反复调用任务队列里回调函数的一种执行机制 原因&#xff1a;JavaScript 是单线程的&#xff0c;为了不阻塞 JS 引擎&#xff0c;设计执行代码的模型 JS内代码如何执行&#xff1a; 执行同步代…

暴雪娱乐遭DDoS攻击,《暗黑破坏神》等多款游戏受影响

6月25日上午11点&#xff0c;有游戏玩家反应Blizzard Battle.net无法登入、连线缓慢及网站问题&#xff0c;暴雪也证实其电玩平台遭到DDoS攻击。 暴雪娱乐的 Battle.net在线服务遭到分布式拒绝服务&#xff08;DDoS&#xff09;攻击&#xff0c;导致玩家无法正常登录游戏或游戏…

Spring Cloud Alibaba【Nacos配置动态刷新、Nacos集群架构介绍 、Nacos的数据持久化、认识分布式流量防护 】(五)

目录 分布式配置中心_Nacos配置动态刷新 分布式配置中心_Dubbo服务对接分布式配置中心 分布式配置中心_Nacos集群架构介绍 分布式配置中心_Nacos的数据持久化 分布式配置中心_Nacos集群配置 分布式流量防护_认识分布式流量防护 分布式流量防护_认识Sentinel 分布式配置…

WIN无法访问linux开启的SAMBA服务器

WIN无法访问linux开启的SAMBA服务器 打开搜索框“管理Windows凭据” 点击编辑

Goby 漏洞发布|天擎终端安全管理系统 YII_CSRF_TOKEN 远程代码执行漏洞

漏洞名称&#xff1a;天擎终端安全管理系统 YII_CSRF_TOKEN 远程代码执行漏洞 English Name&#xff1a;Tianqing terminal security management system YII_CSRF_TOKEN remote code execution vulnerability CVSS core: 9.8 影响资产数&#xff1a;875 漏洞描述&#xff1…

标注工具Labelimg,正常运行显示,但是对图片点击Create RectBox画矩形框开始闪退

问题描述*&#xff1a;标注工具Labelimg&#xff0c;正常运行显示&#xff0c;但是对图片点击Create RectBox画矩形框开始闪退&#xff0c;闪退出现以下代码 File “C:\ProgramData\anaconda3\Lib\site-packages\libs\canvas.py”, line 530, in paintEvent p.drawLine(self.p…

接口测试 Fiddler 保存会话 (请求)

目录 前言&#xff1a; 为什么要保存请求&#xff1f; 保存单个请求 打开保存的请求文件 乱码的解决方法 保存所有请求 自动保存请求的猜想 自动保存已实现 前言&#xff1a; 在进行接口测试时&#xff0c;Fiddler是一个非常有用的工具&#xff0c;它可以帮助您捕获和…

【蓝图】p27开关门互动实现

p27开关门互动实现 创建一个门 添加初学者内容包 拖拽一个门到场景中 添加一个碰撞 创建盒体触发器 左侧模式->基础->盒体触发器&#xff0c;拖拽到门上&#xff0c;调整大小 开关门互动实现 做一个开门互动 要把开门逻辑写在关卡蓝图里 门设置为可移动 打开关卡蓝…

【JAVA】方法的使用:方法语法、方法调用、方法重载、递归练习

&#x1f349;内容专栏&#xff1a;【JAVA】 &#x1f349;本文脉络&#xff1a;JAVA方法的使用&#xff0c;递归练习 &#x1f349;本文作者&#xff1a;Melon_西西 &#x1f349;发布时间 &#xff1a;2023.7.19 目录 1. 什么是方法(method) 2 方法定义 2.1 方法定义语法格…

【OC总结- Block】

文章目录 前言2. Block2.1 Block的使用规范2.2 __block修饰符2.3 Block的类型2.4 Block的循环引用及解决循环引用的场景引入解决循环引用Block循环引用场景 2.5 Block的实现及其本质2.5.1 初始化部分2.5.2 调用部分2.5.3 捕获变量 Block本质2.6 Block捕获变量 和 对象2.7 Block…

【算法基础:数据结构】2.2 字典树/前缀树 Trie

文章目录 知识点cpp结构体模板 模板例题835. Trie字符串统计❤️❤️❤️❤️❤️&#xff08;重要&#xff01;模板&#xff01;&#xff09;143. 最大异或对&#x1f62d;&#x1f62d;&#x1f62d;&#x1f62d;&#x1f62d;&#xff08;Trie树的应用&#xff09; 相关题目…

河北幸福消费金融基于 Apache Doris 构建实时数仓,查询提速 400 倍!

本文导读&#xff1a; 随着河北幸福消费金融的客户数量和放贷金额持续上升&#xff0c;如何依托大数据、数据分析等技术来提供更好决策支持、提高工作效率和用户体验&#xff0c;成为了当前亟需解决的问题。基于此&#xff0c;公司决定搭建数据中台&#xff0c;从基于 TDH 的离…

IIS Express本地开发测试如何映射到外网访问?

1.IIS Express是什么 IIS Express是为开发人员优化的轻量级、自包含版本的IIS。它具有IIS 7及以上的所有核心功能&#xff0c;以及为简化网站开发而设计的附加功能。 IIS Express&#xff08;跟ASP.NET开发服务器一样&#xff09;可以快速地从硬盘上的某个文件夹上启动网站…

GO语言Metex

Mutex互斥锁 type Mutex struct{state int32 // 0表示未加锁 1表示加锁&#xff0c;原子操作sema uint32 // 信号量&#xff0c;用作等待队列 } Mutex正常模式: 尝试加锁的G会先自旋几次,若获不到锁,则加入等待队列. 正常模式下,自选和等待队列的一起竞争 因为G频繁的挂起…

适合小公司的自动化部署脚本

背景&#xff08;偷懒&#xff09; 在小小的公司里面&#xff0c;挖呀挖呀挖。快挖不动了&#xff0c;一件事重复个5次&#xff0c;还在人肉手工&#xff0c;身体和心理就开始不舒服了&#xff0c;并且违背了个人的座右铭&#xff1a;“偷懒”是人类进步的第一推动力。 每次想…