rt-thread rtc设备驱动开发

这篇具有很好参考价值的文章主要介绍了rt-thread rtc设备驱动开发。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


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

I/O设备框架

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

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

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

​ 在RT-Thread中,I/O设备管理层和驱动设备框架层已完成封装,若新增设备驱动到I/O设备框架时,一般只需要结合硬件设备提供的SDK对设备驱动层进行驱动开发。
rt-thread rtc设备驱动开发,实时音视频,驱动开发
详细可参考: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 的功能。
rt-thread rtc设备驱动开发,实时音视频,驱动开发
​ scons编译构建后,将生成的rtthread-pico.uf2文件拷贝至pico设备(RPI-RP2 disk)中。通过串口连接访问设备。在Finsh命令行,执行date命令,查看效果如下图。实际验证中,Soft RTC的时间比实际要快很多,精度不高。
rt-thread 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 功能:
rt-thread rtc设备驱动开发,实时音视频,驱动开发
​ 开启 NTP 后 RTC 的自动同步功能将会自动开启,还可以设置同步周期和首次同步的延时时间:
rt-thread rtc设备驱动开发,实时音视频,驱动开发
​ 生成执行执行文件,在Finsh命令行,输入 date 即可查看当前当地时区时间,大致效果如下:
rt-thread rtc设备驱动开发,实时音视频,驱动开发

功能配置——启用硬件RTC

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

​ 创建RTC设备:
rt-thread rtc设备驱动开发,实时音视频,驱动开发
​ 根据设备框架层rtc.h下的结构体,创建设备驱动层drv_rtc.c下的_rtc_ops对象。
rt-thread rtc设备驱动开发,实时音视频,驱动开发
​ 接下来,依次实现RTC设备初始化函数(pico_rtc_init)、设置时间函数(pico_rtc_set_secs)、获取时间函数(pico_rtc_get_secs),使得RTC具备硬件访问能力。

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

rtc_driver.c:文章来源地址https://www.toymoban.com/news/detail-629834.html

#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);

到了这里,关于rt-thread rtc设备驱动开发的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • RT-Thread HWTIMER设备(学习)

    硬件定时器一般有2种工作模式,定时器模式和计数器模式。不管是工作在哪一种模式,实质都是通过内部计数器模块对脉冲信号进行计数,下面是定时器的一些重要概念。 计数器模式:对外部输入引脚的外部脉冲信号计数。 定时器模式:对内部脉冲信号计数。定时器常用作

    2024年02月07日
    浏览(28)
  • RT-Thread系列10——ETH网口设备

    ==== 文章汇总(有代码汇总) ==== 目标:使用网口和电脑通信。 RT-Thread studio,版本: 2.2.6,不一样其实区别也不大 RT-Thread:标准版,4.0.3版本 芯片包版本:0.1.9 开发板:自己做的,主控STM32F407VET6。 网口芯片:太网口LAN8720A cubemx版本:6.8.1 cubemx版本不一样的话,网口的配置过

    2024年02月21日
    浏览(23)
  • 字符设备驱动实例(PWM和RTC)

    目录 五、PWM 六、RTC         PWM(Pulse Width Modulation,脉宽调制器),顾名思义就是一个输出脉冲宽度可以调整的硬件器件,其实它不仅脉冲宽度可调,频率也可以调整。它的核心部件是一个硬件定时器,其工作原理可以用下图来说明。         PWM 管脚默认输出高电平,在

    2024年02月12日
    浏览(25)
  • 【STM32&RT-Thread零基础入门】 3. PIN设备(GPIO)的使用

    硬件:STM32F103ZET6、ST-LINK、usb转串口工具、4个LED灯、1个蜂鸣器、4个1k电阻、2个按键、面包板、杜邦线 在嵌入式系统中,GPIO是最常用的一种设备,在RT-Thread操作系统中,把GPIO命名为PIN设备。 RT-Thread通过PIN设备对芯片的GPIO引脚进行管理,应用程序可以通过其提供的一组PIN设备

    2024年02月13日
    浏览(35)
  • RT-Thread GD32F4xx CAN驱动

      在RT-Thread的bsp文档中没有找到GD32F4xx的CAN驱动文件,此处参考STM32的drv_can编写CAN驱动。

    2024年02月02日
    浏览(39)
  • [攻城狮计划]RT-Thread—详解UART设备(基于RA2E1)

    🚀🚀开启攻城狮的成长之旅!这是我参与的由 CSDN博客专家 架构师李肯和 瑞萨MCU 联合发起的「 致敬未来的攻城狮计划 」的第4天,点击查看活动计划详情 🚀🚀首先非常感谢李老师能给我参加这个计划的机会,让我有机会接触到许多的开发板,同时也感谢瑞萨官方 为我们

    2024年02月13日
    浏览(70)
  • NUCLEO-F411RE RT-Thread 体验 (2) - GCC环境 Pin 驱动的移植

    前面控制LED的函数,其实还是调用的hal的函数,RT-Thread分离了驱动层与应用层,驱动层往下对接hal库,往上对接pin组件。 驱动层代码路径如图: pin组件代码位于 第一次编译 报错提示没有board.h,在Core/Inc目录新建一个board.h,内容如下: 第二次编译 无错误。 编译无报错。 添加

    2024年02月09日
    浏览(23)
  • NUCLEO-F411RE RT-Thread 体验 (9) - GCC环境 PWM的驱动移植以及简单使用

    驱动位于drv_pwm.c文件中,components层代码位于rt_drv_pwm.c中。 修改Makefile文件 修改配置文件rtconfig.h LED2链接PA5,而TIM2_CHANNEL1可从PA5输出PWM,所以我们需要配置TIM2,并使能TIM2_CH1. 修改RT-Thread-basic/Core/Src/stm32f4xx_hal_msp.c HAL_TIM_MspPostInit 函数在stm32_hw_pwm_init函数中被调用。主要配置

    2024年02月10日
    浏览(28)
  • RT-Thread STM32 GoKit V2.1 开发板BSP说明

    本文档为刘恒为 GoKit V2.1 开发板提供的 BSP (板级支持包) 说明。 主要内容如下: 开发板资源介绍 BSP 快速上手 进阶使用方法 通过阅读快速上手章节开发者可以快速地上手该 BSP,将 RT-Thread 运行在开发板上。在进阶使用指南章节,将会介绍更多高级功能,帮助开发者利用 RT-

    2024年02月04日
    浏览(27)
  • RT-Thread开发,使用SCons编译,生成静态库,并进行链接生成MCU程序

            SCons 是一个开放源代码、以 Python 语言编写的下一代的程序建造工具。它最初的名字是 ScCons, 基于由 perl 语言编写的 Cons 软件开发而成,它在 2000 年 8 月获得了由 Software Carpentry 举办的 SC 建造比赛的大奖。现在 ScCons 已经被改名为 SCons,目的是为了表示不再与 Sof

    2024年02月11日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包