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)系统,以实现对设备状态的实时监测和预…...

如何在看板中有效管理突发紧急任务
在看板中有效管理突发紧急任务需要:设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP(Work-in-Progress)弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中,设立专门的紧急任务通道尤为重要,这能…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...

PH热榜 | 2025-06-08
1. Thiings 标语:一套超过1900个免费AI生成的3D图标集合 介绍:Thiings是一个不断扩展的免费AI生成3D图标库,目前已有超过1900个图标。你可以按照主题浏览,生成自己的图标,或者下载整个图标集。所有图标都可以在个人或…...

归并排序:分治思想的高效排序
目录 基本原理 流程图解 实现方法 递归实现 非递归实现 演示过程 时间复杂度 基本原理 归并排序(Merge Sort)是一种基于分治思想的排序算法,由约翰冯诺伊曼在1945年提出。其核心思想包括: 分割(Divide):将待排序数组递归地分成两个子…...

门静脉高压——表现
一、门静脉高压表现 00:01 1. 门静脉构成 00:13 组成结构:由肠系膜上静脉和脾静脉汇合构成,是肝脏血液供应的主要来源。淤血后果:门静脉淤血会同时导致脾静脉和肠系膜上静脉淤血,引发后续系列症状。 2. 脾大和脾功能亢进 00:46 …...
跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践
在电商行业蓬勃发展的当下,多平台运营已成为众多商家的必然选择。然而,不同电商平台在商品数据接口方面存在差异,导致商家在跨平台运营时面临诸多挑战,如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...

鸿蒙Navigation路由导航-基本使用介绍
1. Navigation介绍 Navigation组件是路由导航的根视图容器,一般作为Page页面的根容器使用,其内部默认包含了标题栏、内容区和工具栏,其中内容区默认首页显示导航内容(Navigation的子组件)或非首页显示(Nav…...
k8s从入门到放弃之Pod的容器探针检测
k8s从入门到放弃之Pod的容器探针检测 在Kubernetes(简称K8s)中,容器探测是指kubelet对容器执行定期诊断的过程,以确保容器中的应用程序处于预期的状态。这些探测是保障应用健康和高可用性的重要机制。Kubernetes提供了两种种类型…...
ubuntu清理垃圾
windows和ubuntu 双系统,ubuntu 150GB,开发用,基本不装太多软件。但是磁盘基本用完。 1、查看home目录 sudo du -h -d 1 $HOME | grep -v K 上面的命令查看$HOME一级目录大小,发现 .cache 有26GB,.local 有几个GB&am…...