I.MX6ULL_Linux_驱动篇(47)linux RTC驱动
RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的,就和我们使用 Windows 电脑或手机查看时间一样,我们在使用 Linux 设备的时候也需要查看时间。本章我们就来学习一下如何编写 Linux 下的 RTC 驱动程序。
Linux 内核 RTC 驱动简介
RTC 设备驱动是一个标准的字符设备驱动,应用程序通过 open、 release、 read、 write 和 ioctl等函数完成对 RTC 设备的操作。
 Linux 内核将 RTC 设备抽象为 rtc_device 结构体,因此 RTC 设备驱动就是申请并初始化rtc_device,最后将 rtc_device 注册到 Linux 内核里面,这样 Linux 内核就有一个 RTC 设备。
 至于 RTC 设备的操作肯定是用一个操作集合(结构体)来表示的,我们先来看一下 rtc_device 结构体,此结构体定义在 include/linux/rtc.h 文件中,结构体内容如下:
struct rtc_device
{struct device dev;struct module *owner;int id;char name[RTC_DEVICE_NAME_SIZE];const struct rtc_class_ops *ops;struct mutex ops_lock;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;struct rtc_task *irq_task;spinlock_t irq_task_lock;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; /* sub second exp, so needs hrtimer */int pie_enabled;struct work_struct irqwork;/* Some hardware can't support UIE mode */int uie_unsupported;#ifdef CONFIG_RTC_INTF_DEV_UIE_EMULstruct work_struct uie_task;struct timer_list uie_timer;/* Those fields are protected by 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;
#endif
};我们需要重点关注的是 ops 成员变量,这是一个 rtc_class_ops 类型的指针变量, rtc_class_ops为 RTC 设备的最底层操作函数集合,包括从 RTC 设备中读取时间、向 RTC 设备写入新的时间
 值等。因此, rtc_class_ops 是需要用户根据所使用的 RTC 设备编写的,此结构体定义在include/linux/rtc.h 文件中,内容如下:
struct rtc_class_ops {int (*open)(struct device *);void (*release)(struct device *);int (*ioctl)(struct device *, unsigned int, unsigned long);int (*read_time)(struct device *, struct rtc_time *);int (*set_time)(struct device *, struct rtc_time *);int (*read_alarm)(struct device *, struct rtc_wkalrm *);int (*set_alarm)(struct device *, struct rtc_wkalrm *);int (*proc)(struct device *, struct seq_file *);int (*set_mmss64)(struct device *, time64_t secs);int (*set_mmss)(struct device *, unsigned long secs);int (*read_callback)(struct device *, int data);int (*alarm_irq_enable)(struct device *, unsigned int enabled);
};看名字就知道 rtc_class_ops 操作集合中的这些函数是做什么的了,但是我们要注意,rtc_class_ops 中的这些函数只是最底层的 RTC 设备操作函数,并不是提供给应用层的file_operations 函数操作集。 RTC 是个字符设备,那么肯定有字符设备的 file_operations 函数操作集, Linux 内核提供了一个 RTC 通用字符设备驱动文件,文件名为 drivers/rtc/rtc-dev.c, rtcdev.c 文件提供了所有 RTC 设备共用的 file_operations 函数操作集,如下所示:
static const struct file_operations rtc_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_dev_ioctl 函数,函数内容如下(有省略):
218 static long rtc_dev_ioctl(struct file *file,
219                           unsigned int cmd, unsigned long arg)
220 {
221     int err = 0;
222     struct rtc_device *rtc = file->private_data;
223     const struct rtc_class_ops *ops = rtc->ops;
224     struct rtc_time tm;
225     struct rtc_wkalrm alarm;
226     void __user *uarg = (void __user *) arg;
227
228     err = mutex_lock_interruptible(&rtc->ops_lock);
229     if (err)
230         return err;
......
269     switch (cmd) {
......
333         case RTC_RD_TIME: /* 读取时间 */
334             mutex_unlock(&rtc->ops_lock);
335
336             err = rtc_read_time(rtc, &tm);
337             if (err < 0)
338                 return err;
339
340             if (copy_to_user(uarg, &tm, sizeof(tm)))
341                 err = -EFAULT;
342             return err;
343
344         case RTC_SET_TIME: /* 设置时间 */
345             mutex_unlock(&rtc->ops_lock);
346
347             if (copy_from_user(&tm, uarg, sizeof(tm)))
348                 return -EFAULT;
349
350             return rtc_set_time(rtc, &tm);
......
401         default:
402         /* Finally try the driver's ioctl interface */
403             if (ops->ioctl) {
404                 err = ops->ioctl(rtc->dev.parent, cmd, arg);
405                 if (err == -ENOIOCTLCMD)
406                 err = -ENOTTY;
407             } else
408                 err = -ENOTTY;
409         break;
410     }
411
412     done:
413     mutex_unlock(&rtc->ops_lock);
414     return err;
415 }第 333 行, RTC_RD_TIME 为时间读取命令。
 第 336 行,如果是读取时间命令的话就调用 rtc_read_time 函数获取当前 RTC 时钟。
 rtc_read_time 函数, rtc_read_time 会调用__rtc_read_time 函数, __rtc_read_time 函数内容如下:
static int __rtc_read_time(struct rtc_device *rtc, struct rtc_time *tm)
{int err;if (!rtc->ops)err = -ENODEV;else if (!rtc->ops->read_time)err = -EINVAL;else {memset(tm, 0, sizeof(struct rtc_time));err = rtc->ops->read_time(rtc->dev.parent, tm);if (err < 0) {dev_dbg(&rtc->dev, "read_time: fail to read: %d\n",err);return err;}err = rtc_valid_tm(tm);if (err < 0)dev_dbg(&rtc->dev, "read_time: rtc_time isn't valid\n");}return err;
}从上述代码可以看出, __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 驱动调用流程就很清晰了,如下图所示:
当 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。
I.MX6U 内部 RTC 驱动分析
分析驱动,先从设备树入手,打开 imx6ull.dtsi,在里面找到如下 snvs_rtc 设备节点,节点内容如下所示:
1 snvs_rtc: snvs-rtc-lp {
2     compatible = "fsl,sec-v4.0-mon-rtc-lp";
3     regmap = <&snvs>;
4     offset = <0x34>;
5     interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
6 };第 2 行设置兼容属性 compatible 的值为“fsl,sec-v4.0-mon-rtc-lp”,因此在 Linux 内核源码中搜索此字符串即可找到对应的驱动文件,此文件为 drivers/rtc/rtc-snvs.c,在 rtc-snvs.c 文件中找到如下所示内容:
380 static const struct of_device_id snvs_dt_ids[] = {
381     { .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
382     { /* sentinel */ }
383 };
384 MODULE_DEVICE_TABLE(of, snvs_dt_ids);
385
386 static struct platform_driver snvs_rtc_driver = {
387     .driver = {
388         .name = "snvs_rtc",
389         .pm = SNVS_RTC_PM_OPS,
390         .of_match_table = snvs_dt_ids,
391     },
392     .probe = snvs_rtc_probe,
393 };
394 module_platform_driver(snvs_rtc_driver);第 380~383 行,设备树 ID 表,有一条 compatible 属性,值为“fsl,sec-v4.0-mon-rtc-lp”,因此 imx6ull.dtsi 中的 snvs_rtc 设备节点会和此驱动匹配。
 第 386~393 行,标准的 platform 驱动框架,当设备和驱动匹配成功以后 snvs_rtc_probe 函数就会执行。我们来看一下 snvs_rtc_probe 函数,函数内容如下(有省略):
238 static int snvs_rtc_probe(struct platform_device *pdev)
239 {
240     struct snvs_rtc_data *data;
241     struct resource *res;
242     int ret;
243     void __iomem *mmio;
244
245     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
246     if (!data)
247         return -ENOMEM;
248
249     data->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap");
250
251     if (IS_ERR(data->regmap)) {
252         dev_warn(&pdev->dev, "snvs rtc: you use old dts file, please update it\n");
253         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
254
255         mmio = devm_ioremap_resource(&pdev->dev, res);
256         if (IS_ERR(mmio))
257             return PTR_ERR(mmio);
258
259         data->regmap = devm_regmap_init_mmio(&pdev->dev, mmio, &snvs_rtc_config);
260     } else {
261         data->offset = SNVS_LPREGISTER_OFFSET;
262         of_property_read_u32(pdev->dev.of_node, "offset", &data->offset);
263     }
264
265     if (!data->regmap) {
266         dev_err(&pdev->dev, "Can't find snvs syscon\n");
267         return -ENODEV;
268     }
269
270     data->irq = platform_get_irq(pdev, 0);
271     if (data->irq < 0)
272         return data->irq;
......
285
286     platform_set_drvdata(pdev, data);
287
288     /* Initialize glitch detect */
289     regmap_write(data->regmap, data->offset + SNVS_LPPGDR, SNVS_LPPGDR_INIT);
290
291     /* Clear interrupt status */
292     regmap_write(data->regmap, data->offset + SNVS_LPSR, 0xffffffff);
293
294     /* Enable RTC */
295     snvs_rtc_enable(data, true);
296
297     device_init_wakeup(&pdev->dev, true);
298
299     ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
300                         IRQF_SHARED, "rtc alarm", &pdev->dev);
301     if (ret) {
302         dev_err(&pdev->dev, "failed to request irq %d: %d\n",
303                 data->irq, ret);
304         goto error_rtc_device_register;
305     }
306
307     data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
308                                     &snvs_rtc_ops, THIS_MODULE);
309     if (IS_ERR(data->rtc)) {
310         ret = PTR_ERR(data->rtc);
311         dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
312         goto error_rtc_device_register;
313     }
314
315     return 0;
316
317 error_rtc_device_register:
318 if (data->clk)
319 clk_disable_unprepare(data->clk);
320
321 return ret;
322 }第 253 行,调用 platform_get_resource 函数从设备树中获取到 RTC 外设寄存器基地址。
 第 255 行,调用函数 devm_ioremap_resource 完成内存映射,得到 RTC 外设寄存器物理基地址对应的虚拟地址。
 第 259 行, Linux3.1 引入了一个全新的 regmap 机制, regmap 用于提供一套方便的 API 函数去操作底层硬件寄存器,以提高代码的可重用性。 snvs-rtc.c 文件会采用 regmap 机制来读写
 RTC 底层硬件寄存器。这里使用 devm_regmap_init_mmio 函数将 RTC 的硬件寄存器转化为regmap 形式,这样 regmap 机制的 regmap_write、 regmap_read 等 API 函数才能操作寄存器。
 第 270 行,从设备树中获取 RTC 的中断号。
 第 289 行,设置 RTC_ LPPGDR 寄存器值为 SNVS_LPPGDR_INIT= 0x41736166,这里就是用的 regmap 机制的 regmap_write 函数完成对寄存器进行写操作。
 第 292 行,设置 RTC_LPSR 寄存器,写入 0xffffffff, LPSR 是 RTC 状态寄存器,写 1 清零,因此这一步就是清除 LPSR 寄存器。
 第 295 行,调用 snvs_rtc_enable 函数使能 RTC,此函数会设置 RTC_LPCR 寄存器。
 第299行,调用devm_request_irq函数请求RTC中断,中断服务函数为snvs_rtc_irq_handler,用于 RTC 闹钟中断。
 第 307 行,调用 devm_rtc_device_register 函数向系统注册 rtc_devcie, RTC 底层驱动集为snvs_rtc_ops。snvs_rtc_ops操作集包含了读取/设置RTC时间,读取/设置闹钟等函数。snvs_rtc_ops
 内容如下:
200 static const struct rtc_class_ops snvs_rtc_ops = {
201     .read_time = snvs_rtc_read_time,
202     .set_time = snvs_rtc_set_time,
203     .read_alarm = snvs_rtc_read_alarm,
204     .set_alarm = snvs_rtc_set_alarm,
205     .alarm_irq_enable = snvs_rtc_alarm_irq_enable,
206 };我们就以第 201 行的 snvs_rtc_read_time 函数为例讲解一下 rtc_class_ops 的各个 RTC 底层操作函数该如何去编写。 snvs_rtc_read_time 函数用于读取 RTC 时间值,此函数内容如下所示:
126 static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
127 {
128     struct snvs_rtc_data *data = dev_get_drvdata(dev);
129     unsigned long time = rtc_read_lp_counter(data);
130
131     rtc_time_to_tm(time, tm);
132
133     return 0;
134 }第 129 行,调用 rtc_read_lp_counter 获取 RTC 计数值,这个时间值是秒数。
 第 131 行,调用 rtc_time_to_tm 函数将获取到的秒数转换为时间值,也就是 rtc_time 结构体类型, rtc_time 结构体定义如下:
20 struct rtc_time {
21     int tm_sec;
22     int tm_min;
23     int tm_hour;
24     int tm_mday;
25     int tm_mon;
26     int tm_year;
27     int tm_wday;
28     int tm_yday;
29     int tm_isdst;
30 };最后我们来看一下 rtc_read_lp_counter 函数,此函数用于读取 RTC 计数值,函数内容如下:
50 static u32 rtc_read_lp_counter(struct snvs_rtc_data *data)
51 {
52     u64 read1, read2;
53     u32 val;
54
55     do {
56         regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
57         read1 = val;
58         read1 <<= 32;
59         regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
60         read1 |= val;
61
62         regmap_read(data->regmap, data->offset + SNVS_LPSRTCMR, &val);
63         read2 = val;
64         read2 <<= 32;
65         regmap_read(data->regmap, data->offset + SNVS_LPSRTCLR, &val);
66         read2 |= val;
67 /*
68 * when CPU/BUS are running at low speed, there is chance that
69 * we never get same value during two consecutive read, so here
70 * we only compare the second value.
71 */
72     } while ((read1 >> CNTR_TO_SECS_SH) != (read2 >> CNTR_TO_SECS_SH));
73
74 /* Convert 47-bit counter to 32-bit raw second count */
75     return (u32) (read1 >> CNTR_TO_SECS_SH);
76 }第 56~72 行,读取 RTC_LPSRTCMR 和 RTC_LPSRTCLR 这两个寄存器,得到 RTC 的计数值,单位为秒,这个秒数就是当前时间。这里读取了两次 RTC 计数值,因为要读取两个寄存器,
 因此可能存在读取第二个寄存器的时候时间数据更新了,导致时间不匹配,因此这里连续读两次,如果两次的时间值相等那么就表示时间数据有效。
 第 75 行,返回时间值,注意这里将前面读取到的 RTC 计数值右移了 15 位。
 这个就是 snvs_rtc_read_time 函数读取 RTC 时间值的过程,至于其他的底层操作函数大家自行分析即可,都是大同小异的,这里就不再分析了。关于 I.MX6U 内部 RTC 驱动源码就讲解到这里。
RTC 时间查看与设置
RTC 是用来计时的,因此最基本的就是查看时间, Linux 内核启动的时候可以看到系统时钟设置信息,如图所示:
从图中可以看出, Linux 内核在启动的时候将 snvs_rtc 设置为 rtc0,大家的启动信息可能会和图中的不同,但是内容基本上都是一样的。
 如果要查看时间的话输入“date”命令即可,结果如图所示:
设置 RTC 时间
RTC 时间设置需要使用 date和hwclock 命令,输入“date --help”命令即可查看 date 命令如何设置系统时间,结果如图所示:
现在我要设置当前时间为 2019 年 8 月 31 日 18:13:00,因此输入如下命令:
date -s "2019-08-31 18:13:00"设置完成以后再次使用 date 命令查看一下当前时间就会发现时间改过来了:
大家注意我们使用“ date -s”命令仅仅是将当前系统时间设置了,此时间还没有写入到I.MX6U 内部 RTC 里面或其他的 RTC 芯片里面,因此系统重启以后时间又会丢失。我们需要将当前的时间写入到 RTC 里面,这里要用到 hwclock 命令,输入如下命令将系统时间写入到 RTC里面:
hwclock -w //将当前系统时间写入到 RTC 里面时间写入到 RTC 里面以后就不怕系统重启以后时间丢失了,如果 I.MX6U-ALPHA 开发板底板接了纽扣电池,那么开发板即使断电了时间也不会丢失。
相关文章:
 
I.MX6ULL_Linux_驱动篇(47)linux RTC驱动
RTC 也就是实时时钟,用于记录当前系统时间,对于 Linux 系统而言时间是非常重要的,就和我们使用 Windows 电脑或手机查看时间一样,我们在使用 Linux 设备的时候也需要查看时间。本章我们就来学习一下如何编写 Linux 下的 RTC 驱动程…...
 
详解IBM企业架构框架模型CBM
(一):什么是CBM IBM的CBM是组件化业务模型(Component Business Model),是IBM在2003年提出的一种业务架构方法论。 目的是通过将企业的业务活动划分为一些独立、模块化、可重用的业务组件,来识…...
 
宝塔面板安装MySQL数据库并通过内网穿透工具实现公网远程访问
文章目录 前言1.Mysql 服务安装2.创建数据库3.安装 cpolar3.2 创建 HTTP 隧道 4.远程连接5.固定 TCP 地址5.1 保留一个固定的公网 TCP 端口地址5.2 配置固定公网 TCP 端口地址 前言 宝塔面板的简易操作性,使得运维难度降低,简化了 Linux 命令行进行繁琐的配置,下面简单几步,通…...
 
Elasticsearch 性能调优基础知识
Elastic Stack 已成为监控任何环境或应用程序的实际解决方案。 从日志、指标和正常运行时间到性能监控甚至安全,Elastic Stack 已成为满足几乎所有监控需求的一体化解决方案。 Elasticsearch 通过提供强大的分析引擎来处理任何类型的数据,成为这方面的基…...
速盾网络:网络安全守护者
速盾网络作为一家专业的网络安全服务提供商,致力于为企业和个人提供全面、高效、可靠的网络安全解决方案。以下是速盾网络的主要业务介绍: 一、CDN加速 速盾网络拥有全球化的CDN加速网络,通过分布在全球各地的节点,为客户提供快速…...
 
jmeter如何参数化?Jmeter参数化设置的5种方法
jmeter如何参数化?我们使用jmeter在进行测试的时候,测试数据是一项重要的准备工作,每次迭代的数据当不一样的时候,需要进行参数化,从参数化的文件中来读取测试数据。那么,你知道jmeter如何进行参数化吗&…...
 
01AVue入门(持续学习中)
1.使用AVue开发简单的前端页面直接简单到起飞,他是Element PlusVueVite开发的,不需要向元素的前端代码一样一个组件要传很多参数,他可以使用Json文本来控制我们要传入的数据结构来决定显示什么 //我使用的比较新,我们也可以使用cdn直接使用script标签直接引入 2.开发中遇到的坑…...
js 深浅拷贝的区别和实现方法
一:什么浅拷贝: 浅拷贝创建一个新对象,然后将原始对象的所有属性值复制到新对象中。这意味着,如果原始对象的属性值是基本类型(例如数字、字符串),那么这些值会被直接复制到新对象中。但如果属…...
 
【jvm从入门到实战】(九) 垃圾回收(2)-垃圾回收器
垃圾回收器是垃圾回收算法的具体实现。 由于垃圾回收器分为年轻代和老年代,除了G1之外其他垃圾回收器必须成对组合进行使用 垃圾回收器的组合使用关系图如下。 常用的组合如下: Serial(新生代) Serial Old(老年代) Pa…...
C#基础——匿名函数和参数不固定的函数
匿名函数、参数不固定的函数 匿名函数:没有名字,又叫做lambda表达式,具有简洁,灵活,可读的特性。 具名函数:有名字的函数。 1、简洁性:使用更少的代码实现相同的功能 MyDelegate myDelegate…...
PCL 点云匹配 4 之 (非线性迭代点云匹配)lM-ICP
一、IM迭代法 PCL IterativeClosestPointNonLinear 非线性L-M迭代法-CSDN博客 Matlab 非线性迭代法(3)阻尼牛顿法 L-M-CSDN博客 MATLAB实现最小二乘法_matlab最小二乘法-CSDN博客...
MySQL_14.数据库高速缓冲区空间管理
数据库高速缓冲区空间管理 Oracle 用 LRU(Least Recently Used)算法来管理数据高速缓冲区。该算法将最近使用的 数据块按照使用时间的早晚排成队列,当缓冲区占满后,调入新的数据块时,必须清除已有的数据 块,…...
 
leetcode 974. 和可被 K 整除的子数组(优质解法)
代码: class Solution {public int subarraysDivByK(int[] nums, int k) {HashMap<Integer,Integer> hashMapnew HashMap();hashMap.put(0,1);int count0; //记录子数组的个数int last0; //前一个下标的前缀和int now0; //当前下标的前缀和for(int i0;…...
 
【技术】MySQL 日期时间操作
MySQL 日期时间操作 MySQL 系统时间MySQL 时间格式化MySQL 年月日时分秒周MySQL 日期计算时分秒时差日期差日期加减 MySQL 系统时间 now():系统时间,年月日时分秒current_date:系统时间,年月日current_time:系统时间&…...
测试理论知识三:测试用例、测试策略
1.测试用例 完全的测试是不可能的,对任何程序的测试必定是不完全的,那么,最显然的测试策略就是努力使测试尽可能完全。 进行测试前,推荐先使用黑盒测试的方法设计测试用例,然后使用白盒测试方法来补充的测试用例。 2…...
 
【clickhouse】在CentOS中离线安装clickhouse
https://packages.clickhouse.com/rpm/stable/ 通过如下命令检查是否安装过clickhouse [root172 ~]# rpm -qa | grep clickhouse 把rpm安装包放到opt/lzh目录 按照如下命令顺序安装 [root172 /]# rpm -ivh /opt/lzh/clickhouse-common-static-22.1.2.2-2.x86_64.rpm [root…...
 
微信商户号申请0.2费率
我们都知道,目前市场上的支付宝或者微信商户收款,无论是线上收款还是实体店收款,一般都采用0.6%的收款费率,1万元就是60元。 其实这不低的。 大多数线下实体店商家可能使用的聚合支付码可能是0.38%,1万元是38。 虽然不…...
 
基于单片机设计的电子指南针(LSM303DLH模块(三轴磁场 + 三轴加速度)
一、前言 本项目是基于单片机设计的电子指南针,主要利用STC89C52作为主控芯片和LSM303DLH模块作为指南针模块。通过LCD1602液晶显示屏来展示检测到的指南针信息。 在日常生活中,指南针是一种非常实用的工具,可以帮助我们确定方向࿰…...
深度学习 该用什么标准判断差异最小
决定差异最小的标准通常依赖于您的具体问题和任务。以下是一些常见的用于评估预测性能的标准和思路: 1. **均方根误差 (RMSE):** RMSE 是预测值和真实值之间差异的平方的平均值的平方根。它对较大的误差更加敏感。 from sklearn.metrics import mean_squared_error…...
 
汽车制造厂设备故障预测与健康管理PHM
在现代汽车制造工业中,设备的可靠性和稳定性对于保证生产线的高效运行至关重要。为了提高生产效率、降低维修成本以及确保产品质量,汽车制造厂逐渐采用设备故障预测与健康管理(PHM)系统,以实现对设备状态的实时监测和预…...
进程地址空间(比特课总结)
一、进程地址空间 1. 环境变量 1 )⽤户级环境变量与系统级环境变量 全局属性:环境变量具有全局属性,会被⼦进程继承。例如当bash启动⼦进程时,环 境变量会⾃动传递给⼦进程。 本地变量限制:本地变量只在当前进程(ba…...
 
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
 
跨链模式:多链互操作架构与性能扩展方案
跨链模式:多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈:模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展(H2Cross架构): 适配层…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
 
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
