Linux RTC 驱动框架
目录
- 一、实时时钟(RTC)介绍
- 1.1 概述
- 1.2 功能
- 1.3 应用场景
- 1.4 工作原理
- 1.5 对外接口
- 1.6 常见 RTC 芯片
- 1.7 在 Linux 系统中的应用
- 1.8 注意事项
- 二、Linux 内核 RTC 驱动框架
- 2.1 相关源码文件介绍
- 2.2 核心数据结构
- 2.2.1 struct rtc_device
- 2.2.2 rtc_class_ops
- 2.2.3 struct rtc_timer
- 2.4 RTC驱动实例分析
- 2.5 提供带有中文注释的drivers/rtc/源码
- 2.6 APP层操作RTC
- 三、参考资料
一、实时时钟(RTC)介绍
1.1 概述
实时时钟(Real-Time Clock,简称 RTC)是一种能够持续记录时间的电子设备。它通常用于计算机、嵌入式系统和其他需要准确时间记录的设备中。RTC 可以在系统关机或断电的情况下继续运行,因此即使在系统重启后也能保持准确的时间。
1.2 功能
时间记录
: 记录当前的日期和时间。闹钟功能
: 可以设置特定的时间点触发中断,用于唤醒系统或执行特定任务。周期性中断
:可以设置周期性的中断,用于定时任务。电源管理
: 通常由电池供电,确保在主电源断开时仍能正常工作。
1.3 应用场景
- 个人电脑: 用于记录 BIOS/UEFI 中的时间。
- 嵌入式系统: 用于工业控制、医疗设备、汽车电子等需要高精度时间的应用。
- 服务器:用于日志记录、定时任务调度等。 物联网设备: 用于时间同步和定时任务。
1.4 工作原理
时钟源
: RTC 通常使用低频晶体振荡器(如 32.768 kHz)作为时钟源,这种频率的振荡器功耗低且精度高。计数器
:内部计数器根据时钟源的脉冲进行计数,记录秒、分钟、小时、日、月和年。寄存器
: 时间和日期信息存储在寄存器中,可以通过 I2C、SPI 或其他接口读取和写入。中断
: 当设置的闹钟时间到达或周期性中断条件满足时,RTC 会触发中断信号。
1.5 对外接口
I2C
: 常见的通信接口,用于与主控制器通信。SPI
: 另一种常见的通信接口,适用于高速通信。GPIO
: 一些简单的 RTC设备可能使用 GPIO 进行通信。
1.6 常见 RTC 芯片
DS1307
: 由 Maxim 生产,广泛用于各种嵌入式系统.PCF8563
: 由 NXP 生产,具有低功耗特性。MCP79410
:由 Microchip 生产,集成了 EEPROM 和时钟功能。RV-1805
: 由 Epson 生产,具有高精度和低功耗特性。
1.7 在 Linux 系统中的应用
- RTC 驱动: Linux 内核提供了 RTC 驱动框架,用于管理和操作 RTC 设备。
- 用户空间工具: hwclock命令用于读取和设置硬件时钟,date 命令用于读取和设置系统时间。
- 系统启动: 在系统启动时,通常会从 RTC 读取时间并设置系统时间。
1.8 注意事项
- 电池寿命: RTC 通常由纽扣电池供电,需要注意电池的寿命和更换。
- 精度校准: 由于环境温度等因素的影响,RTC 的时间可能会有偏差,需要定期校准。
- 中断处理: 闹钟和周期性中断需要正确处理,避免影响系统的正常运行。
二、Linux 内核 RTC 驱动框架
在内核源码中的路径:drivers/rtc
2.1 相关源码文件介绍
class.c
:为底层驱动提供 register 与 unregister 接口用于 RTC 设备的注册/注销。初始化 RTC设备结构、sysfs、proc;interface.c
:提供用户程序与 RTC 的接口函数;dev.c
:将 RTC设备抽象为通用的字符设备,提供文件操作函数(struct file_operations rtc_dev_fops
的成员),可认为是一个字符设备驱动实现;sysfs.c
:管理 RTC 设备的 sysfs 属性,获取 RTC 设备名、日期、时间等;proc.c
:管理 RTC 设备的 procfs 属性,提供中断状态和标志查询;lib.c
:提供 RTC、Data 和 Time之间的转换函数;rtc-xxx.c
:不同 RTC 芯片的实际驱动;rtc.h
: 定义了 RTC 设备的数据结构和操作接口。
2.2 核心数据结构
2.2.1 struct rtc_device
/*** struct rtc_device - 实时时钟设备结构体** 该结构体表示实时时钟 (RTC) 设备的信息和状态。* 包括设备初始化、操作函数指针、中断处理和定时器相关信息。*/
struct rtc_device {// 基本设备结构体struct device dev;// 设备所属模块的所有者struct module *owner;// 设备标识号int id;// 指向包含 RTC 设备操作函数的结构体的指针const struct rtc_class_ops *ops;// 保护操作函数的互斥锁,确保线程安全struct mutex ops_lock;// RTC 字符设备结构体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;// 中断频率int irq_freq;// 用户空间允许的最大频率int max_user_freq;// 定时器队列,用于管理各种定时器struct timerqueue_head timerqueue;// 报警定时器struct rtc_timer aie_timer;// 更新中断定时器struct rtc_timer uie_rtctimer;// 高分辨率定时器,适用于亚秒精度的周期性中断struct hrtimer pie_timer; // 标记是否启用了周期性中断功能int pie_enabled;// 工作结构体,用于处理中断struct work_struct irqwork;// 有些硬件不支持 UIE 模式int uie_unsupported;// 设置 RTC 时钟所需的时间(纳秒)。这会影响设置操作的调用时间。偏移量:// - 0.5 秒会在墙上时间 10.0 秒时在 9.5 秒调用 RTC 设置// - 1.5 秒会在墙上时间 10.0 秒时在 8.5 秒调用 RTC 设置// - -0.5 秒会在墙上时间 10.0 秒时在 10.5 秒调用 RTC 设置long set_offset_nsec;// 标记设备是否已注册bool registered;// 旧 ABI 支持bool nvram_old_abi;struct bin_attribute *nvram;// RTC 范围的最小值time64_t range_min;// RTC 范围的最大值timeu64_t range_max;// 开始秒数time64_t start_secs;// 偏移秒数time64_t offset_secs;// 标记是否设置了开始时间bool set_start_time;#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL// 工作结构体,用于处理 UIE 任务struct work_struct uie_task;// UIE 定时器struct timer_list uie_timer;// 这些字段受 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;
#endifANDROID_KABI_RESERVE(1);
};
UIE 是 “Update Interrupt Enable” 的缩写,它是一种实时时钟(RTC)设备的功能。具体来说,UIE 模式用于在 RTC 时间更新时生成中断。以下是 UIE 模式的详细解释:
-
时间更新中断:
- 当 RTC 的时间每秒钟更新时,会触发一个中断。这个中断可以被用户空间程序捕获和处理。
- 例如,某些应用程序可能需要在每一秒的边界上执行特定的操作,UIE 模式可以提供这种精确的时间同步。
-
应用场景:
- 日志记录:在需要精确时间戳的日志记录系统中,UIE 中断可以确保日志条目的时间戳非常准确。
- 定时任务:需要在固定时间间隔内执行的任务,可以通过 UIE 中断来触发。
-
实现细节:
- 在
struct rtc_device
结构体中,有几个与 UIE 相关的字段:int uie_unsupported
:标记某些硬件是否不支持 UIE 模式。long set_offset_nsec
:设置 RTC 时钟所需的时间偏移量,影响中断的触发时间。struct work_struct uie_task
和struct timer_list uie_timer
:用于处理 UIE 中断任务和定时器。unsigned int uie_irq_active:1
:标记 UIE 中断是否激活。unsigned int stop_uie_polling:1
:标记是否停止 UIE 轮询。unsigned int uie_task_active:1
:标记 UIE 任务是否激活。unsigned int uie_timer_active:1
:标记 UIE 定时器是否激活。
- 在
-
配置和启用:
- UIE 模式通常需要在内核配置中启用,例如通过
CONFIG_RTC_INTF_DEV_UIE_EMUL
配置选项。 - 应用程序可以通过 RTC 设备文件接口(如
/dev/rtc0
)来启用或禁用 UIE 模式。
- UIE 模式通常需要在内核配置中启用,例如通过
总结来说,UIE 模式是为了在 RTC 时间更新时生成中断,以便应用程序能够精确地响应时间变化。这对于需要高精度时间同步的应用非常有用。
2.2.2 rtc_class_ops
/** RTC 类操作结构体,定义了与 RTC 设备交互的各种方法。* 这些方法中的 `device` 参数是指物理设备,该设备位于硬件所在的总线上(如 I2C、Platform、SPI 等),* 并且已传递给 `rtc_device_register()` 函数。通常,`driver_data` 包含设备状态,包括 RTC 的 `rtc_device` 指针。** 大多数这些方法在调用时会持有 `rtc_device.ops_lock` 锁,通过 `rtc_*(struct rtc_device *, ...)` 调用。** 当前的例外情况主要是文件系统钩子:* - `proc()` 钩子用于 procfs*/
struct rtc_class_ops {/** ioctl 方法,用于处理 RTC 设备的 I/O 控制命令。* @param dev: RTC 设备* @param cmd: 命令码* @param arg: 命令参数* @return: 成功返回 0,失败返回负错误码*/int (*ioctl)(struct device *dev, unsigned int cmd, unsigned long arg);/** read_time 方法,用于读取 RTC 设备的时间。* @param dev: RTC 设备* @param tm: 存储读取时间的结构体指针* @return: 成功返回 0,失败返回负错误码*/int (*read_time)(struct device *dev, struct rtc_time *tm);/** set_time 方法,用于设置 RTC 设备的时间。* @param dev: RTC 设备* @param tm: 包含要设置时间的结构体指针* @return: 成功返回 0,失败返回负错误码*/int (*set_time)(struct device *dev, struct rtc_time *tm);/** read_alarm 方法,用于读取 RTC 设备的闹钟设置。* @param dev: RTC 设备* @param alrm: 存储读取闹钟设置的结构体指针* @return: 成功返回 0,失败返回负错误码*/int (*read_alarm)(struct device *dev, struct rtc_wkalrm *alrm);/** set_alarm 方法,用于设置 RTC 设备的闹钟。* @param dev: RTC 设备* @param alrm: 包含要设置闹钟的结构体指针* @return: 成功返回 0,失败返回负错误码*/int (*set_alarm)(struct device *dev, struct rtc_wkalrm *alrm);/** proc 方法,用于处理 procfs 文件系统的请求。* @param dev: RTC 设备* @param seq: 序列文件指针* @return: 成功返回 0,失败返回负错误码*/int (*proc)(struct device *dev, struct seq_file *seq);/** alarm_irq_enable 方法,用于启用或禁用 RTC 设备的闹钟中断。* @param dev: RTC 设备* @param enabled: 启用或禁用标志* @return: 成功返回 0,失败返回负错误码*/int (*alarm_irq_enable)(struct device *dev, unsigned int enabled);/** read_offset 方法,用于读取 RTC 设备的时间偏移量。* @param dev: RTC 设备* @param offset: 存储读取时间偏移量的指针* @return: 成功返回 0,失败返回负错误码*/int (*read_offset)(struct device *dev, long *offset);/** set_offset 方法,用于设置 RTC 设备的时间偏移量。* @param dev: RTC 设备* @param offset: 要设置的时间偏移量* @return: 成功返回 0,失败返回负错误码*/int (*set_offset)(struct device *dev, long offset);ANDROID_KABI_RESERVE(1);
};
2.2.3 struct rtc_timer
/*** @brief RTC定时器结构体* * 该结构体用于表示RTC(实时时钟)定时器,包含了定时器所需的信息和配置,如定时周期、回调函数等。*/
struct rtc_timer {/*** @brief 定时器队列节点* * 该字段用于将定时器插入到定时器队列中,以管理多个定时器的到期时间。*/struct timerqueue_node node;/*** @brief 定时周期* * 该字段表示定时器的周期时间,使用ktime_t类型来存储时间间隔。*/ktime_t period;/*** @brief 定时器回调函数指针* * 当定时器到期时,将调用此字段指向的函数。该函数将接收一个指向RTC设备的指针作为参数。*/void (*func)(struct rtc_device *rtc);/*** @brief 指向RTC设备的指针* * 该字段用于关联定时器和特定的RTC设备,使得定时器可以操作或访问该设备。*/struct rtc_device *rtc;/*** @brief 定时器启用状态* * 该字段用于指示定时器是否已启用。当定时器被禁用时,其值为0;当定时器被启用时,其值为非0。*/int enabled;
};
2.4 RTC驱动实例分析
drivers/rtc/rtc-rk808.c
// SPDX-License-Identifier: GPL-2.0-only
/** RTC driver for Rockchip RK808** Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd** Author: Chris Zhong <zyw@rock-chips.com>* Author: Zhang Qing <zhangqing@rock-chips.com>*/#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/mfd/rk808.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>/* RTC_CTRL_REG bitfields */
#define BIT_RTC_CTRL_REG_STOP_RTC_M BIT(0)/* RK808 has a shadowed register for saving a "frozen" RTC time.* When user setting "GET_TIME" to 1, the time will save in this shadowed* register. If set "READSEL" to 1, user read rtc time register, actually* get the time of that moment. If we need the real time, clr this bit.*/
#define BIT_RTC_CTRL_REG_RTC_GET_TIME BIT(6)
#define BIT_RTC_CTRL_REG_RTC_READSEL_M BIT(7)
#define BIT_RTC_INTERRUPTS_REG_IT_ALARM_M BIT(3)
#define RTC_STATUS_MASK 0xFE
#define RTC_ALARM_STATUS BIT(6)#define SECONDS_REG_MSK 0x7F
#define MINUTES_REG_MAK 0x7F
#define HOURS_REG_MSK 0x3F
#define DAYS_REG_MSK 0x3F
#define MONTHS_REG_MSK 0x1F
#define YEARS_REG_MSK 0xFF
#define WEEKS_REG_MSK 0x7#define RTC_NEED_TRANSITIONS BIT(0)
/* REG_SECONDS_REG through REG_YEARS_REG is how many registers? */#define NUM_TIME_REGS (RK808_WEEKS_REG - RK808_SECONDS_REG + 1)
#define NUM_ALARM_REGS (RK808_ALARM_YEARS_REG - RK808_ALARM_SECONDS_REG + 1)struct rk_rtc_compat_reg {unsigned int ctrl_reg;unsigned int status_reg;unsigned int alarm_seconds_reg;unsigned int int_reg;unsigned int seconds_reg;
};struct rk808_rtc {struct rk808 *rk808;struct rtc_device *rtc;struct rk_rtc_compat_reg *creg;int irq;unsigned int flag;
};/** 该函数用于处理 Rockchip 日历与公历(Gregorian calendar)之间的转换。* Rockchip 日历在 RK808 中将 11 月视为有 31 天。我们定义 2016 年 1 月 1 日为两个日历同步的基准日期,* 并根据该日期进行其他日期的相对转换。* 注意:其他系统软件(例如固件)在读取相同硬件时,必须实现完全相同的转换算法,并使用相同的基准日期。** @param tm 指向 rtc_time 结构体的指针,包含待转换的时间信息** @return 返回一个 time64_t 类型的值,表示从基准日期(2016年1月1日)开始的年份偏移量,* 如果当前月份大于11月,则额外加1,以补偿11月多出的一天。*/
static time64_t nov2dec_transitions(struct rtc_time *tm)
{// 计算年份偏移量,并检查是否需要补偿11月多出的一天return (tm->tm_year + 1900) - 2016 + (tm->tm_mon + 1 > 11 ? 1 : 0);
}/*** 将 Rockchip 日期表示转换为公历(Gregorian)日期。* * 此函数用于处理从 Rockchip 特定日期表示法转换为标准公历的逻辑。* 特别地,它处理从11月31日转换为12月1日的特殊情况。* 转换过程分为两个主要步骤:* 1. 首先,使用 `rtc_tm_to_time64` 函数将输入的 Rockchip 日期转换为 Unix 时间戳。* 2. 然后,根据自输入日期以来发生的11月31日到12月1日的转换次数调整时间戳。* 这种调整是必要的,因为在 Rockchip 表示法中,11月31日被视为12月1日。* 调整后的时间戳再使用 `rtc_time64_to_tm` 函数转换回公历格式。* * @param tm 指向 `rtc_time` 结构的指针,该结构包含 Rockchip 格式的日期和时间信息。* 经过转换后,此结构将更新为对应的公历日期和时间。*/
static void rockchip_to_gregorian(struct rtc_time *tm)
{// 将 Rockchip 日期和时间转换为 Unix 时间戳time64_t time = rtc_tm_to_time64(tm);// 根据11月31日到12月1日的转换次数调整时间戳rtc_time64_to_tm(time + nov2dec_transitions(tm) * 86400, tm);
}/*** 将公历日期转换为Rockchip格式的日期* 此函数旨在处理特定的日期转换问题,即将公历日期转换为Rockchip硬件时钟可以理解的格式* 其中包括处理从11月到12月的过渡,这是Rockchip硬件时钟处理日期的一种特殊需求* * @param tm 指向RTC时间结构的指针,该结构包含日期和时间信息*/
static void gregorian_to_rockchip(struct rtc_time *tm)
{// 计算从11月到12月的过渡天数time64_t extra_days = nov2dec_transitions(tm);// 将RTC时间结构转换为自1970年1月1日以来的秒数time64_t time = rtc_tm_to_time64(tm);// 根据过渡天数调整时间,并将结果转换回RTC时间结构rtc_time64_to_tm(time - extra_days * 86400, tm);/* * 如果调整后的日期导致我们回到了11月(这可能发生在特定的年份),则进行补偿* 这种补偿机制可以确保日期正确地向前推进,即使在复杂的闰年情况下也是如此* (该补偿机制将在2381年之前有效)*/if (nov2dec_transitions(tm) < extra_days) {// 如果当前月份是11月,则简单地将日期推进一天if (tm->tm_mon + 1 == 11)tm->tm_mday++; /* This may result in 31! */// 否则,重新计算时间,确保日期正确地反映了从11月到12月的过渡elsertc_time64_to_tm(time - (extra_days - 1) * 86400, tm);}
}/* Read current time and date in RTC */
/*** 从RTC设备读取当前时间。** 此函数通过I2C总线与RTC芯片通信,读取当前的时间和日期,并将读取的数据转换为可使用的格式。** @param dev RTC设备的device结构指针。* @param tm 用于存储读取到的时间和日期信息的rtc_time结构指针。** @return 返回0表示成功,返回负值表示失败。*/static int rk808_rtc_readtime(struct device *dev, struct rtc_time *tm)
{struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);struct rk808 *rk808 = rk808_rtc->rk808;u8 rtc_data[NUM_TIME_REGS];int ret;/* 强制立即更新影子寄存器 */ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,BIT_RTC_CTRL_REG_RTC_GET_TIME,BIT_RTC_CTRL_REG_RTC_GET_TIME);if (ret) {dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);return ret;}/** 设置GET_TIME位后,不能立即读取RTC时间。需要等待大约31.25微秒,* 这是32kHz时钟的一个周期。如果在这里清除GET_TIME位,则I2C传输时间* 肯定超过31.25微秒:在400kHz总线频率下为16 * 2.5微秒。*/ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,BIT_RTC_CTRL_REG_RTC_GET_TIME, 0);if (ret) {dev_err(dev, "Failed to update bits rtc_ctrl: %d\n", ret);return ret;}/* 批量读取RTC数据 */ret = regmap_bulk_read(rk808->regmap, rk808_rtc->creg->seconds_reg,rtc_data, NUM_TIME_REGS);if (ret) {dev_err(dev, "Failed to bulk read rtc_data: %d\n", ret);return ret;}/* 将BCD编码的时间数据转换为二进制并填充到tm结构中 */tm->tm_sec = bcd2bin(rtc_data[0] & SECONDS_REG_MSK);tm->tm_min = bcd2bin(rtc_data[1] & MINUTES_REG_MAK);tm->tm_hour = bcd2bin(rtc_data[2] & HOURS_REG_MSK);tm->tm_mday = bcd2bin(rtc_data[3] & DAYS_REG_MSK);tm->tm_mon = (bcd2bin(rtc_data[4] & MONTHS_REG_MSK)) - 1;tm->tm_year = (bcd2bin(rtc_data[5] & YEARS_REG_MSK)) + 100;tm->tm_wday = bcd2bin(rtc_data[6] & WEEKS_REG_MSK);/* 如果需要转换,调用rockchip_to_gregorian进行转换 */if (rk808_rtc->flag & RTC_NEED_TRANSITIONS)rockchip_to_gregorian(tm);/* 打印调试信息 */dev_dbg(dev, "RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour, tm->tm_min, tm->tm_sec);return ret;
}/*** rk808_rtc_set_time - 设置RTC(实时时钟)的时间和日期* @dev: 设备结构体指针,代表RTC设备* @tm: 指向rtc_time结构体的指针,包含要设置的日期和时间信息* * 此函数将给定的日期和时间信息写入到RTC芯片中,以更新RTC的当前时间和日期设置* 它首先将日期和时间信息转换为BCD格式,然后通过regmap接口将这些信息写入到RTC的相应寄存器中* * 返回值:* 成功时返回0,失败时返回负的错误代码*/
static int rk808_rtc_set_time(struct device *dev, struct rtc_time *tm)
{// 获取RTC设备的驱动数据struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);// 获取RK808芯片的数据struct rk808 *rk808 = rk808_rtc->rk808;// 定义一个数组来存储RTC数据u8 rtc_data[NUM_TIME_REGS];// 定义返回值变量int ret;// 调试信息,显示正在设置的日期和时间dev_dbg(dev, "set RTC date/time %4d-%02d-%02d(%d) %02d:%02d:%02d\n",1900 + tm->tm_year, tm->tm_mon + 1, tm->tm_mday, tm->tm_wday,tm->tm_hour, tm->tm_min, tm->tm_sec);// 如果需要转换,则将格里高利日期转换为适合RTC的格式if (rk808_rtc->flag & RTC_NEED_TRANSITIONS)gregorian_to_rockchip(tm);// 将时间数据从二进制转换为BCD格式,并存储到rtc_data数组中rtc_data[0] = bin2bcd(tm->tm_sec);rtc_data[1] = bin2bcd(tm->tm_min);rtc_data[2] = bin2bcd(tm->tm_hour);rtc_data[3] = bin2bcd(tm->tm_mday);rtc_data[4] = bin2bcd(tm->tm_mon + 1);rtc_data[5] = bin2bcd(tm->tm_year - 100);rtc_data[6] = bin2bcd(tm->tm_wday);// 停止RTC,以便更新RTC寄存器ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,BIT_RTC_CTRL_REG_STOP_RTC_M,BIT_RTC_CTRL_REG_STOP_RTC_M);if (ret) {dev_err(dev, "Failed to update RTC control: %d\n", ret);return ret;}// 将rtc_data数组中的数据批量写入到RTC寄存器中ret = regmap_bulk_write(rk808->regmap, rk808_rtc->creg->seconds_reg,rtc_data, NUM_TIME_REGS);if (ret) {dev_err(dev, "Failed to bull write rtc_data: %d\n", ret);return ret;}// 再次启动RTCret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,BIT_RTC_CTRL_REG_STOP_RTC_M, 0);if (ret) {dev_err(dev, "Failed to update RTC control: %d\n", ret);return ret;}return 0;
}/*** rk808_rtc_readalarm - 读取RTC报警时间* @dev: 设备结构体指针* @alrm: RTC报警时间结构体指针** 此函数从RTC中读取报警时间,并将其填充到alrm参数中。它首先读取报警时间寄存器,* 然后根据寄存器的值更新alrm结构体中的时间字段。此外,它还会读取中断寄存器以确定* 报警是否已启用。** 返回值: 成功时返回0,失败时返回负错误代码*/
static int rk808_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
{// 获取RTC设备的驱动数据struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);// 获取RK808芯片结构体指针struct rk808 *rk808 = rk808_rtc->rk808;// 定义一个数组来存储报警时间寄存器的值u8 alrm_data[NUM_ALARM_REGS];// 定义一个变量来存储中断寄存器的值uint32_t int_reg;// 定义一个变量来存储函数执行结果int ret;// 从RTC中读取报警时间寄存器的值ret = regmap_bulk_read(rk808->regmap,rk808_rtc->creg->alarm_seconds_reg, alrm_data,NUM_ALARM_REGS);if (ret) {// 如果读取失败,打印错误信息并返回错误代码dev_err(dev, "Failed to read RTC alarm date REG: %d\n", ret);return ret;}// 将读取的寄存器值转换为报警时间alrm->time.tm_sec = bcd2bin(alrm_data[0] & SECONDS_REG_MSK);alrm->time.tm_min = bcd2bin(alrm_data[1] & MINUTES_REG_MAK);alrm->time.tm_hour = bcd2bin(alrm_data[2] & HOURS_REG_MSK);alrm->time.tm_mday = bcd2bin(alrm_data[3] & DAYS_REG_MSK);alrm->time.tm_mon = (bcd2bin(alrm_data[4] & MONTHS_REG_MSK)) - 1;alrm->time.tm_year = (bcd2bin(alrm_data[5] & YEARS_REG_MSK)) + 100;// 如果需要转换,将Rockchip日历时间转换为公历时间if (rk808_rtc->flag & RTC_NEED_TRANSITIONS)rockchip_to_gregorian(&alrm->time);// 读取中断寄存器的值以确定报警是否已启用ret = regmap_read(rk808->regmap, rk808_rtc->creg->int_reg, &int_reg);if (ret) {// 如果读取失败,打印错误信息并返回错误代码dev_err(dev, "Failed to read RTC INT REG: %d\n", ret);return ret;}// 打印调试信息,显示读取的报警时间dev_dbg(dev, "alrm read RTC date/time %ptRd(%d) %ptRt\n", &alrm->time,alrm->time.tm_wday, &alrm->time);// 根据中断寄存器的值设置报警启用状态alrm->enabled = (int_reg & BIT_RTC_INTERRUPTS_REG_IT_ALARM_M) ? 1 : 0;// 函数执行成功,返回0return 0;
}/*** 停止RTC闹钟。* * 该函数通过清除相应的中断使能位和闹钟状态位来停止RTC的闹钟功能。主要用于禁用RTC的闹钟功能。* * @param rk808_rtc 指向rk808_rtc结构的指针,包含RTC操作所需的信息。* @return 成功返回0,失败返回负的错误码。*/static int rk808_rtc_stop_alarm(struct rk808_rtc *rk808_rtc)
{struct rk808 *rk808 = rk808_rtc->rk808;int ret;/* 禁用RTC闹钟中断 */ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,BIT_RTC_INTERRUPTS_REG_IT_ALARM_M, 0);/** 必须在闹钟触发1秒后或禁用闹钟后清除RTC闹钟状态(BIT(6))。*/ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,RTC_ALARM_STATUS);return ret;
}static int rk808_rtc_start_alarm(struct rk808_rtc *rk808_rtc)
{struct rk808 *rk808 = rk808_rtc->rk808;int ret;ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->int_reg,BIT_RTC_INTERRUPTS_REG_IT_ALARM_M,BIT_RTC_INTERRUPTS_REG_IT_ALARM_M);return ret;
}/*** rk808_rtc_setalarm - 设置RTC闹钟* @dev: 设备结构体指针* @alrm: 闹钟数据结构指针,包含闹钟时间和是否启用闹钟的信息* * 此函数负责将给定的闹钟时间设置到RTC芯片中,并根据alrm->enabled决定是否启用闹钟。* 它首先停止当前的闹钟,然后将时间数据从二进制转换为BCD格式,并写入到RTC的相关寄存器中。* 如果需要转换,会将时间从格里高利历转换为适合RTC芯片的格式。* * 返回值: 0表示成功,负值表示出错*/
static int rk808_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{// 获取RTC设备的私有数据结构struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);// 获取主设备结构体指针struct rk808 *rk808 = rk808_rtc->rk808;// 定义一个数组来存储闹钟数据u8 alrm_data[NUM_ALARM_REGS];int ret;// 停止当前的闹钟ret = rk808_rtc_stop_alarm(rk808_rtc);if (ret) {// 如果停止闹钟失败,打印错误信息并返回错误码dev_err(dev, "Failed to stop alarm: %d\n", ret);return ret;}// 打印设置的闹钟时间信息dev_dbg(dev, "alrm set RTC date/time %ptRd(%d) %ptRt\n", &alrm->time,alrm->time.tm_wday, &alrm->time);// 如果需要转换,将时间从格里高利历转换为适合RTC芯片的格式if (rk808_rtc->flag & RTC_NEED_TRANSITIONS)gregorian_to_rockchip(&alrm->time);// 将时间数据从二进制转换为BCD格式,并存储到数组中alrm_data[0] = bin2bcd(alrm->time.tm_sec);alrm_data[1] = bin2bcd(alrm->time.tm_min);alrm_data[2] = bin2bcd(alrm->time.tm_hour);alrm_data[3] = bin2bcd(alrm->time.tm_mday);alrm_data[4] = bin2bcd(alrm->time.tm_mon + 1);alrm_data[5] = bin2bcd(alrm->time.tm_year - 100);// 将闹钟数据写入到RTC的相关寄存器中ret = regmap_bulk_write(rk808->regmap,rk808_rtc->creg->alarm_seconds_reg, alrm_data,NUM_ALARM_REGS);if (ret) {// 如果写入失败,打印错误信息并返回错误码dev_err(dev, "Failed to bulk write: %d\n", ret);return ret;}// 如果闹钟被启用,启动闹钟if (alrm->enabled) {ret = rk808_rtc_start_alarm(rk808_rtc);if (ret) {// 如果启动闹钟失败,打印错误信息并返回错误码dev_err(dev, "Failed to start alarm: %d\n", ret);return ret;}}// 返回成功return 0;
}static int rk808_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
{struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);if (enabled)return rk808_rtc_start_alarm(rk808_rtc);return rk808_rtc_stop_alarm(rk808_rtc);
}/** We will just handle setting the frequency and make use the framework for* reading the periodic interupts.** @freq: Current periodic IRQ freq:* bit 0: every second* bit 1: every minute* bit 2: every hour* bit 3: every day*/
static irqreturn_t rk808_alarm_irq(int irq, void *data)
{struct rk808_rtc *rk808_rtc = data;struct rk808 *rk808 = rk808_rtc->rk808;struct i2c_client *client = rk808->i2c;int ret;ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,RTC_STATUS_MASK);if (ret) {dev_err(&client->dev, "%s:Failed to update RTC status: %d\n",__func__, ret);return ret;}rtc_update_irq(rk808_rtc->rtc, 1, RTC_IRQF | RTC_AF);dev_dbg(&client->dev, "%s:irq=%d\n", __func__, irq);return IRQ_HANDLED;
}static const struct rtc_class_ops rk808_rtc_ops = {.read_time = rk808_rtc_readtime,.set_time = rk808_rtc_set_time,.read_alarm = rk808_rtc_readalarm,.set_alarm = rk808_rtc_setalarm,.alarm_irq_enable = rk808_rtc_alarm_irq_enable,
};#ifdef CONFIG_PM_SLEEP
/* Turn off the alarm if it should not be a wake source. */
static int rk808_rtc_suspend(struct device *dev)
{struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);if (device_may_wakeup(dev))enable_irq_wake(rk808_rtc->irq);return 0;
}/* Enable the alarm if it should be enabled (in case it was disabled to* prevent use as a wake source).*/
static int rk808_rtc_resume(struct device *dev)
{struct rk808_rtc *rk808_rtc = dev_get_drvdata(dev);if (device_may_wakeup(dev))disable_irq_wake(rk808_rtc->irq);return 0;
}
#endifstatic SIMPLE_DEV_PM_OPS(rk808_rtc_pm_ops, rk808_rtc_suspend, rk808_rtc_resume);static struct rk_rtc_compat_reg rk808_creg = {.ctrl_reg = RK808_RTC_CTRL_REG,.status_reg = RK808_RTC_STATUS_REG,.alarm_seconds_reg = RK808_ALARM_SECONDS_REG,.int_reg = RK808_RTC_INT_REG,.seconds_reg = RK808_SECONDS_REG,
};static struct rk_rtc_compat_reg rk817_creg = {.ctrl_reg = RK817_RTC_CTRL_REG,.status_reg = RK817_RTC_STATUS_REG,.alarm_seconds_reg = RK817_ALARM_SECONDS_REG,.int_reg = RK817_RTC_INT_REG,.seconds_reg = RK817_SECONDS_REG,
};/*** @brief 实现 RK808 芯片的 RTC 设备探测功能。* * 此函数在加载相应的驱动程序时初始化 RTC 设备。* 主要任务包括:* - 检查设备树中是否启用了 RTC 设备。* - 为 RTC 设备结构分配内存。* - 根据不同的芯片变体设置 RTC 控制寄存器。* - 启动 RTC 并启用影子计时器。* - 注册 RTC 设备并请求报警中断。* * @param pdev 平台设备指针* @return 成功返回 0,失败返回负的错误码*/
static int rk808_rtc_probe(struct platform_device *pdev)
{struct rk808 *rk808 = dev_get_drvdata(pdev->dev.parent);struct rk808_rtc *rk808_rtc;struct device_node *np;int ret;// 根据芯片变体检查 RTC 设备是否启用switch (rk808->variant) {case RK805_ID:case RK808_ID:case RK816_ID:case RK818_ID:np = of_get_child_by_name(pdev->dev.parent->of_node, "rtc");if (np && !of_device_is_available(np)) {dev_info(&pdev->dev, "设备已禁用\n");return -EINVAL;}break;default:break;}// 为 RTC 设备结构分配内存rk808_rtc = devm_kzalloc(&pdev->dev, sizeof(*rk808_rtc), GFP_KERNEL);if (rk808_rtc == NULL)return -ENOMEM;// 根据不同的芯片变体设置控制寄存器switch (rk808->variant) {case RK808_ID:case RK818_ID:rk808_rtc->creg = &rk808_creg;rk808_rtc->flag |= RTC_NEED_TRANSITIONS;break;case RK805_ID:case RK816_ID:rk808_rtc->creg = &rk808_creg;break;case RK809_ID:case RK817_ID:rk808_rtc->creg = &rk817_creg;break;default:rk808_rtc->creg = &rk808_creg;break;}// 设置平台设备数据platform_set_drvdata(pdev, rk808_rtc);rk808_rtc->rk808 = rk808;// 启动 RTC 并启用影子计时器ret = regmap_update_bits(rk808->regmap, rk808_rtc->creg->ctrl_reg,BIT_RTC_CTRL_REG_STOP_RTC_M |BIT_RTC_CTRL_REG_RTC_READSEL_M,BIT_RTC_CTRL_REG_RTC_READSEL_M);if (ret) {dev_err(&pdev->dev, "Failed to update RTC control: %d\n", ret);return ret;}ret = regmap_write(rk808->regmap, rk808_rtc->creg->status_reg,RTC_STATUS_MASK);if (ret) {dev_err(&pdev->dev, "Failed to write RTC status: %d\n", ret);return ret;}// 启用设备唤醒功能device_init_wakeup(&pdev->dev, 1);// 分配 RTC 设备rk808_rtc->rtc = devm_rtc_allocate_device(&pdev->dev);if (IS_ERR(rk808_rtc->rtc))return PTR_ERR(rk808_rtc->rtc);// 设置 RTC 操作函数rk808_rtc->rtc->ops = &rk808_rtc_ops;// 获取 RTC 中断号rk808_rtc->irq = platform_get_irq(pdev, 0);if (rk808_rtc->irq < 0)return rk808_rtc->irq;// 请求报警中断ret = devm_request_threaded_irq(&pdev->dev, rk808_rtc->irq, NULL,rk808_alarm_irq, 0, "RTC alarm",rk808_rtc);if (ret) {dev_err(&pdev->dev, "Failed to request alarm IRQ %d: %d\n",rk808_rtc->irq, ret);return ret;}// 注册 RTC 设备return rtc_register_device(rk808_rtc->rtc);
}static struct platform_driver rk808_rtc_driver = {.probe = rk808_rtc_probe,.driver = {.name = "rk808-rtc",.pm = &rk808_rtc_pm_ops,},
};module_platform_driver(rk808_rtc_driver);MODULE_DESCRIPTION("RTC driver for the rk808 series PMICs");
MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
MODULE_AUTHOR("Zhang Qing <zhangqing@rock-chips.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:rk808-rtc");
2.5 提供带有中文注释的drivers/rtc/源码
在本文章的附带绑定资源中!
2.6 APP层操作RTC
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/rtc.h>
#include <time.h>
#include <string.h>
#include <errno.h>#define RTC_DEVICE "/dev/rtc0"void print_time(struct rtc_time *rtc_tm)
{printf("RTC date/time: %04d-%02d-%02d %02d:%02d:%02d\n",rtc_tm->tm_year + 1900, rtc_tm->tm_mon + 1, rtc_tm->tm_mday,rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
}int main(int argc, char *argv[])
{int fd;struct rtc_time rtc_tm;time_t rawtime;struct tm *timeinfo;// 打开RTC设备fd = open(RTC_DEVICE, O_RDWR);if (fd == -1) {perror("打开RTC设备失败");return errno;}// 读取当前时间if (ioctl(fd, RTC_RD_TIME, &rtc_tm) == -1) {perror("读取RTC时间失败");close(fd);return errno;}printf("当前");print_time(&rtc_tm);// 设置新的时间time(&rawtime);timeinfo = localtime(&rawtime);rtc_tm.tm_year = timeinfo->tm_year;rtc_tm.tm_mon = timeinfo->tm_mon;rtc_tm.tm_mday = timeinfo->tm_mday;rtc_tm.tm_hour = timeinfo->tm_hour;rtc_tm.tm_min = timeinfo->tm_min;rtc_tm.tm_sec = timeinfo->tm_sec;if (ioctl(fd, RTC_SET_TIME, &rtc_tm) == -1) {perror("设置RTC时间失败");close(fd);return errno;}printf("设置后的");print_time(&rtc_tm);// 关闭RTC设备close(fd);return 0;
}
三、参考资料
- Linux下RTC子系统驱动
- Linux NVMEM子系统:概述以及RK3588 OTP实例
相关文章:
Linux RTC 驱动框架
目录 一、实时时钟(RTC)介绍1.1 概述1.2 功能1.3 应用场景1.4 工作原理1.5 对外接口1.6 常见 RTC 芯片1.7 在 Linux 系统中的应用1.8 注意事项 二、Linux 内核 RTC 驱动框架2.1 相关源码文件介绍2.2 核心数据结构2.2.1 struct rtc_device2.2.2 rtc_class…...
智能订餐:基于JSP的管理系统设计与优化
相关技术介绍 2.1 B/S结构 目前使用较多的开发结构模式大致可以包括C/S模式和B/S模式[5]。其中,C/S模式全称为客户端/服务器模式(Client/Server模式),B/S模式全称为浏览器/服务器模式(Browser/Server模式)。…...
Java基本查询(四)
JPA 查询All示例 JPA教程 - JPA查询All示例 我们可以在JPQL中使用带有子查询的ALL运算符。 List l em.createQuery("SELECT e FROM Professor e WHERE e.salary < " "ALL (SELECT d.salary FROM e.directs d)").getResultList();例子 下面的代码来自…...
【双指针算法】--复写零
文章目录 1. 题目2. 题目解析3. 代码 1. 题目 在线oj 给你一个长度固定的整数数组 arr ,请你将该数组中出现的每个零都复写一遍,并将其余的元素向右平移。 注意:请不要在超过该数组长度的位置写入元素。请对输入的数组 就地 进行上述修改&a…...
leetcode hot100 两两交换链表之中的节点
24. 两两交换链表中的节点 已解答 中等 相关标签 相关企业 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换) # Definition fo…...
Yolo11改进策略:主干网络改进|FastVit与Yolo11完美融合,重参数重构Yolo11网络(全网首发)
文章目录 摘要FastViT:一种使用结构重新参数化的快速混合视觉变换器1、简介2、相关工作3、体系结构3.1、概述3.2、FastViT3.2.1、重新参数化跳过连接3.2.2、线性训练时间过参数化3.2.3、大核卷积4、实验4.1、图像分类4.2、鲁棒性评价4.3、3D Hand网格估计4.4、语义分割和目标检…...
vscode打开下一个文件的时候上一个文件会关闭
解决方法: 你可以通过设置 workbench.editor.enablePreview 来控制在 VS Code 中打开文件时是否会关闭上一个文件。将其设置为 false 可以防止这种行为。 {"workbench.editor.enablePreview": false } 在设置编辑器中显示 "workbench.editor.enab…...
windows使用zip包安装MySQL
windows通过zip包安装MySQL windows通过zip包安装MySQL下载MySQL的zip安装包创建安装目录和数据目录解压zip安装包创建配置目录 etc 和 配置文件 my.ini安装MySQL进入解压后的bin目录执行命令初始化执行命令安装 验证安装查看服务已安装 启动MySQL查看服务运行情况修改密码创建…...
【LeetCode每日一题】——415.字符串相加
文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 字符串 二【题目难度】 简单 三【题目编号】 415.字符串相加 四【题目描述】 给定两个字符…...
基于角色的访问控制(RBAC)在异构系统中的应用
在数字化蓬勃发展的当下,企业运营高度依赖各类异构系统。基于角色的访问控制(RBAC)成为管理这些系统中用户权限的核心策略。RBAC 依据组织内的岗位、职责定义角色,按角色分配权限,用户凭借所属角色获取相应访问权&…...
FPGA实现MIPI转FPD-Link车载同轴视频传输方案,基于IMX327+FPD953架构,提供工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐本博主所有FPGA工程项目-->汇总目录我这里已有的 MIPI 编解码方案 3、本 MIPI CSI-RX IP 介绍4、详细设计方案设计原理框图IMX327 及其配置FPD-Link视频串化-解串方案MIPI CSI RX图像 ISP 处理图像缓存HDMI输出工程源码架构 5、…...
远程控制macOS一直卡在100%,能连接上了却只显示了壁纸?
前言 前段时间有个朋友过来咨询关于Windows使用第三方远程软件(向日葵、Todesk等)远程连接控制macOS系统,但出现了一些奇奇怪怪的问题。 比如在连接的时候,一直卡在100%连接,对方的电脑却已经显示已经被控制的状态。…...
springboot中使用gdal将表中的空间数据转shapefile文件
springboot中使用gdal将表中的空间数据转shapefile文件 代码: // 样本导出-将样本表导出为shapefile,复制样本shp文件到临时目录下 sampleDir是文件夹pathpublic void setYbShapeFile(Yb yb, File sampleDir) {// 创建 前时项 和 后时项 文件夹File y…...
捋一捋相关性运算,以及DTD和NLP中的应用
捋一捋相关性运算,以及DTD和NLP中的应用 相关性和相干性,有木有傻傻分不清相关性数字信号的相关运算同维度信号的相关理解 相关--互相关--相干 回声消除过程如何套用这些知识相关性/相干性检测在DT中的应用参考 相关性和相干性,有木有傻傻分不清 这是容易混淆的两个…...
Calcite Web 项目常见问题解决方案
Calcite Web 项目常见问题解决方案 calcite-web Authoritative front-end development resources for Calcite design initiative. Includes extendable base components and styles, as well as a modular and efficient framework for ArcGIS properties. [这里是图片001] 项…...
3090. 每个字符最多出现两次的最长子字符串
题目内容: 给你一个字符串 s ,请找出满足每个字符最多出现两次的最长子字符串,并返回该 子字符串 的 最大 长度。 示例 1: 输入: s "bcbbbcba" 输出: 4 解释: 以下子字符串长…...
基于微信小程序的短视频系统(SpringBoot)+文档
💗博主介绍💗:✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示:文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…...
Git的简介
文章目录 一.Git是什么二.核心概念三.工作流程四.Git的优势 下载Git 推荐官网下载 官网地址 一.Git是什么 Git是一个分布式版本控制系统,用于跟踪文件的变化并协调多人对同一项目的开发工作。它就像是一个时光机器,能够记录文件在不同时间点的状态&…...
【XR】ATW
异步时间扭曲(Asynchronous Timewarp,简称ATW)是一种在虚拟现实(VR)技术中使用的算法,它通过生成中间帧来减少因帧率不足导致的抖动和延迟,从而提高用户体验。ATW技术在两个线程中完成时间扭曲的…...
记Fastjson2的一个报ConcurrentModificationException的bug
错误背景:fastjson2的parseObject方法,在spring webflux项目中被调用,有时会报java.util.ConcurrentModificationException错误。报错处的代码如下图: 改了半天与并发安全相关的代码,还是会报此错误。后来改变思路搜…...
JAVAweb学习日记(二)JavaScript
一、概念 二、JavaScript引入方式 三、JavaScript书写语法 输出语句: 变量: 数据类型、运算符、流程控制语句: 数据类型: 运算符: 字符串如果是 数字字符构成,先把读到的数字转为数字类型,后续…...
FPGA学习(基于小梅哥Xilinx FPGA)学习笔记
文章目录 一、整个工程的流程二、基于Vivado的FPGA开发流程实践(二选一多路器)什么是二选一多路器用verilog语言,Vivado软件进行该电路实现1、设计输入:Design Sources中的代码2、分析和综合:分析设计输入中是否有错误…...
Linux的VIM基本操作
编辑模式转换 编辑指令 linux用vim编辑代码,再用gcc进行编译 编辑命令如下 gcc编译如下...
2024年12月英语六级CET6写作与翻译笔记
目录 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可行的目标 2 翻译 2.1 洋山港(Yangshan Port) 2.2 中国航天事业 2.3 北斗卫星导航系统 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可…...
学习记录:electron主进程与渲染进程直接的通信示例【开箱即用】
electron主进程与渲染进程直接的通信示例 1. 背景: electronvue实现桌面应用开发 2.异步模式 2.1使用.send 和.on的方式 preload.js中代码示例: const { contextBridge, ipcRenderer} require(electron);// 暴露通信接口 contextBridge.exposeInMa…...
领域自适应
领域自适应(Domain Adaptation)是一种技术,用于将机器学习模型从一个数据分布(源域)迁移到另一个数据分布(目标域)。这在源数据和目标数据具有不同特征分布但任务相同的情况下特别有用。领域自适…...
Kubeadm+Containerd部署k8s(v1.28.2)集群(非高可用版)
KubeadmContainerd部署k8s(v1.28.2)集群(非高可用版) 文章目录 KubeadmContainerd部署k8s(v1.28.2)集群(非高可用版)一.环境准备1.服务器准备2.环境配置3.设置主机名4.修改国内镜像源地址5.配置时间同步6.配置内核转发及网桥过滤 …...
重温设计模式--职责链模式
文章目录 职责链模式的详细介绍C 代码示例C示例代码2 职责链模式的详细介绍 定义与概念 职责链模式(Chain of Responsibility Pattern)是一种行为型设计模式,它旨在将请求的发送者和多个接收者解耦,让多个对象都有机会处理请求&a…...
单点登录平台Casdoor搭建与使用,集成gitlab同步创建删除账号
一,简介 一般来说,公司有很多系统使用,为了实现统一的用户名管理和登录所有系统(如 GitLab、Harbor 等),并在员工离职时只需删除一个主账号即可实现权限清除,可以采用 单点登录 (SSO) 和 集中式…...
【Rust自学】5.1. 定义并实例化struct
喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(・ω・) 5.1.1. 什么是struct struct的中文意思为结构体,它是一种自定义的数据类型,它允许程序为相关联的值命名和打包&am…...
vue-cli 5接入模块联邦 module federation
vue-cli 5接入模块联邦 module federation 模块联邦概念实现思路配置遇到的问题: 模块联邦概念 模块联邦由webpack 5最先推出的,让应用加载远程的代码模块来实现不同的Web应用共享代码片段.模块联邦分为两个角色,一个是生产者,一个是消费者.生产者暴露代码供消费者消费 (用一个…...
[前端]mac安装nvm(node.js)多版本管理
NVM功能简介 NVM(Node Version Manager)是一个用于管理多个Node.js版本的命令行工具,它允许开发者在同一台机器上安装、切换和卸载不同版本的Node.js,从而解决版本不兼容的问题。以下是NVM的一些主要功能和用途: 安装和…...
thinkphp8+layui分页
前端 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>demo</title><link href"//unpkg.com/layui2.9.20/dist/css/layui.css" rel"stylesheet"> </head> <…...
【NLP高频面题 - Transformer篇】Transformer的位置编码是如何计算的?
【NLP高频面题 - Transformer篇】Transformer的位置编码是如何计算的? 重要性:★★★ NLP Github 项目: NLP 项目实践:fasterai/nlp-project-practice 介绍:该仓库围绕着 NLP 任务模型的设计、训练、优化、部署和应用…...
VSCode搭建Java开发环境 2024保姆级安装教程(Java环境搭建+VSCode安装+运行测试+背景图设置)
名人说:一点浩然气,千里快哉风。—— 苏轼《水调歌头》 创作者:Code_流苏(CSDN) 目录 一、Java开发环境搭建二、VScode下载及安装三、VSCode配置Java环境四、运行测试五、背景图设置 很高兴你打开了这篇博客,更多详细的安装教程&…...
计算机网络压缩版
计算机网络到现在零零散散也算过了三遍,一些协议大概了解,但总是模模糊糊的印象,现在把自己的整体认识总结一下,(本来想去起名叫《看这一篇就够了》,但是发现网上好的文章太多了,还是看这篇吧&a…...
大语言模型中的Agent;常见的Agent开发工具或框架
大语言模型中的Agent 大语言模型中的Agent是指以大语言模型为核心驱动,具有自主理解、感知、规划、记忆和使用工具等能力,能够自动化执行复杂任务的系统.以下是一些例子: AutoGPT:它相当于一个完整的工具包,可以为各种项目构建和运行自定义AI Agent。使用OpenAI的GPT-4和…...
设计模式-备忘录模式
背景 游戏角色恢复问题:角色有攻击力和防御力,在Boss站前保存攻击力和防御力,大战之后,攻击力和防御力下降,从备忘录恢复到大战前的状态。 传统思路: 一个游戏对象,对应一个保存状态对象。 …...
重温设计模式--状态模式
文章目录 状态模式(State Pattern)概述状态模式UML图作用:状态模式的结构环境(Context)类:抽象状态(State)类:具体状态(Concrete State)类&#x…...
道可云人工智能元宇宙每日资讯|2024(GIAC)智能视听大会在青岛举行
道可云元宇宙每日简报(2024年12月23日)讯,今日元宇宙新鲜事有: 2024(GIAC)智能视听大会在青岛举行 12月22日,2024(GIAC)智能视听大会在青岛举行。大会以“数字文化 智能…...
理解神经网络
神经网络是一种模拟人类大脑工作方式的计算模型,是深度学习和机器学习领域的基础。 基本原理 神经网络的基本原理是模拟人脑神经系统的功能,通过多个节点(也叫神经元)的连接和计算,实现非线性模型的组合和输出。每个…...
初学stm32 --- NVIC中断
目录 STM32 NVIC 中断优先级管理 NVIC_Type: ISER[8]: ICER[8]: ISPR[8]: ICPR[8]: IABR[8]: IP[240]: STM32 的中断分组: 中断优先级分组函数 NVIC_PriorityGroupConfig 中断初始化函…...
机器人角度参考方式
机器人的角度可以根据需求和系统设计来决定。通常情况下,机器人角度(如航向角或偏航角)有两种常见的参考方式: 参考开机时的 0:这是最常见的方式,机器人在开机时会将当前的方向作为 0(即参考方向…...
不同路径
不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 问总共有多少条不同的路径ÿ…...
WPS工具栏灰色怎么办
WPS离线不登录,开启工具栏等相关功能 当你在使用WPS的过程中,若因网络问题或其他特殊原因,导致无法登录使用WPS时,可根据以下步骤开启离线兼容模式,开启此模式后,可在未登录的状态下,激活并使用…...
js的节流与防抖方法封装
简介 节流(Throttling)和 防抖(Debouncing) 是防止频繁触发函数执行的两种技术,它们的目标都是减少函数的执行次数。 区别 防抖(Debouncing) :当用户停止触发事件后,经…...
深信服企业级数据备份与恢复系统(整机裸机恢复)
概述 深信服企业级数据备份与恢复系统可实现无需搭建目标环境,目标机可以是没有操作系统的物理主机或虚拟机,实现异构环境下的裸机恢复。 深信服企业级数据备份与恢复系统支持的多种连接恢复方式: 1. PXE连接恢复:PXE连接需要做…...
uniapp 微信小程序 页面部分截图实现
uniapp 微信小程序 页面部分截图实现 原理都是将页面元素画成canvas 然后将canvas转化为图片,问题是我页面里边本来就有一个canvas,ucharts图画的canvas我无法画出这块。 想了一晚上,既然canvas最后能转化为图片,那我直接…...
Sequelize ORM sql 语句工具
Sequelize ORM sql 语句工具 sequelize orm中文网 视频学习长乐未央 初始化配置 Sequelize orm 配置文章落日沉溺于海 在命令行中全局安装 npm i -g sequelize-clisequelize 执行需要匹配 mysql2 对应的依赖(安装 mysql2) npm i sequelize mysql2初始化…...
搭建Nacos注册中心
Nacos介绍 目前开源的注册中心框架有很多,国内比较常见的有: Eureka:Netflix公司出品,目前被集成在SpringCloud当中,一般用于Java应用 Nacos:Alibaba公司出品,目前被集成在SpringCloudAlibaba中…...