Linux clock子系统及驱动实例
文章目录
- 基本概念
- CLK子系统
- 时钟API的使用
- clock驱动实例
- 1、时钟树
- 2、设备树
- 3、驱动实现
- fixed_clk固定时钟实现
- factor_clk分频时钟实现
- gate_clk门控时钟实现
基本概念
晶振:晶源振荡器
PLL:Phase lock loop,锁相环。用于提升频率
OSC:oscillator的简写,振荡器
CLK子系统
Linux的时钟子系统由CCF(common clock framework)框架管理,CCF向上给用户提供了通用的时钟接口,向下给驱动开发者提供硬件操作的接口。各结构体关系如下:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ySJAJxZO-1676165714838)(pic/image-20230210213920508.png)]](https://img-blog.csdnimg.cn/74e05f276859478083ba4ac041c2444d.png)
CCF框架比较简单,只有这几个结构体。CCF框架分为了consumer、ccf和provider三部分。
consumer:
时钟的使用者,clock子系统向consumer的提供通用的时钟API接口,使其可以屏蔽底层硬件差异。提供给consumer操作的API如下:
struct clk *clk_get(struct device *dev, const char *id);
struct clk *devm_clk_get(struct device *dev, const char *id);
int clk_enable(struct clk *clk);//使能时钟,不会睡眠
void clk_disable(struct clk *clk);//使能时钟,不会睡眠
unsigned long clk_get_rate(struct clk *clk);
void clk_put(struct clk *clk);
long clk_round_rate(struct clk *clk, unsigned long rate);
int clk_set_rate(struct clk *clk, unsigned long rate);
int clk_set_parent(struct clk *clk, struct clk *parent);
struct clk *clk_get_parent(struct clk *clk);
int clk_prepare(struct clk *clk);
void clk_unprepare(struct clk *clk);
int clk_prepare_enable(struct clk *clk) //使能时钟,可能会睡眠
void clk_disable_unprepare(struct clk *clk) //禁止时钟,可能会睡眠
unsigned long clk_get_rate(struct clk *clk) //获取时钟频率
consumer在使用这些API时,必须先调用devm_clk_get()或clk_get()获取一个struct clk *指针句柄,后续都通过传入该句柄来操作,struct clk相当于实例化一个时钟。
ccf:
clock子系统的核心,用一个struct clk_core结构体表示,每个注册设备都对应一个struct clk_core。
provider(时钟的提供者):
struct clk_hw:表示一个具体的硬件时钟。
struct clk_init_data:struct clk_hw结构体成员,用于表示该时钟下的初始化数据,如时钟名字name、操作函数ops等。
// include/linux/clk-provider.h
struct clk_hw{struct clk_core *core;struct clk *clk;const struct clk_init_data *init;
}struct clk_init_data{const char *name; //时钟名字const struct clk_ops *ops; //时钟硬件操作函数集合const char *const *parent_names; //父时钟名字const struct clk_parent_data *parent_data;const struct clk_hw **parent_hws;u8 num_parents;unsigned long flags;
}
struct clk_ops:时钟硬件操作的函数集合,定义了操作硬件的回调函数,consumer在调用clk_set_rate()等API时会调用到struct clk_ops具体指向的函数,这个需要芯片厂商开发clock驱动时去实现。
//include/linux/clk-provider.hstruct clk_ops {int (*prepare)(struct clk_hw *hw);void (*unprepare)(struct clk_hw *hw);int (*is_prepared)(struct clk_hw *hw);void (*unprepare_unused)(struct clk_hw *hw);int (*enable)(struct clk_hw *hw);void (*disable)(struct clk_hw *hw);int (*is_enabled)(struct clk_hw *hw);void (*disable_unused)(struct clk_hw *hw);int (*save_context)(struct clk_hw *hw);void (*restore_context)(struct clk_hw *hw);unsigned long (*recalc_rate)(struct clk_hw *hw,unsigned long parent_rate);long (*round_rate)(struct clk_hw *hw, unsigned long rate,unsigned long *parent_rate);int (*determine_rate)(struct clk_hw *hw,struct clk_rate_request *req);int (*set_parent)(struct clk_hw *hw, u8 index);u8 (*get_parent)(struct clk_hw *hw);int (*set_rate)(struct clk_hw *hw, unsigned long rate,unsigned long parent_rate);int (*set_rate_and_parent)(struct clk_hw *hw,unsigned long rate,unsigned long parent_rate, u8 index);unsigned long (*recalc_accuracy)(struct clk_hw *hw,unsigned long parent_accuracy);int (*get_phase)(struct clk_hw *hw);int (*set_phase)(struct clk_hw *hw, int degrees);int (*get_duty_cycle)(struct clk_hw *hw,struct clk_duty *duty);int (*set_duty_cycle)(struct clk_hw *hw,struct clk_duty *duty);int (*init)(struct clk_hw *hw);void (*terminate)(struct clk_hw *hw);void (*debug_init)(struct clk_hw *hw, struct dentry *dentry);
};
struct clk_ops中每个函数功能在include/linux/clk-provider.h都有具体的说明,在开发clock驱动时,这些函数并不需要全部实现。下面列举几个最常用,也是经常需要实现的函数。
| 函数 | 说明 |
|---|---|
| recalc_rate | 通过查询硬件,重新计算此时钟的速率。可选,但建议——如果未设置此操作,则时钟速率初始化为0。 |
| round_rate | 给定目标速率作为输入,返回时钟实际支持的最接近速率。 |
| set_rate | 更改此时钟的速率。请求的速率由第二个参数指定,该参数通常应该是调用.round_rate返回。第三个参数给出了父速率,这对大多数.set_rate实现有帮助。成功返回0,否则返回-EERROR |
| enable | 时钟enable |
| disable | 时钟disable |
时钟API的使用
对于一般的驱动开发(非clock驱动),我们只需要在dts中配置时钟,然后在驱动调用通用的时钟API接口即可。
1、设备树中配置时钟
mmc0:mmc0@0x12345678{compatible = "xx,xx-mmc0";......clocks = <&peri PERI_MCI0>;//指定mmc0的时钟来自PERI_MCI0,PERI_MCI0的父时钟是periclocks-names = "mmc0"; //时钟名,调用devm_clk_get获取时钟时,可以传入该名字......};
以mmc的设备节点为例,上述mmc0指定了时钟来自PERI_MCI0,PERI_MCI0的父时钟是peri,并将所指定的时钟给它命名为"mmc0"。
2、驱动中使用API接口
简单的使用:
/* 1、获取时钟 */
host->clk = devm_clk_get(&pdev->dev, NULL); //或者devm_clk_get(&pdev->dev, "mmc0")if (IS_ERR(host->clk)) {dev_err(dev, "failed to find clock source\n");ret = PTR_ERR(host->clk);goto probe_out_free_dev;}/* 2、使能时钟 */
ret = clk_prepare_enable(host->clk);
if (ret) {dev_err(dev, "failed to enable clock source.\n");goto probe_out_free_dev;
}probe_out_free_dev:kfree(host);
在驱动中操作时钟,第一步需要获取struct clk指针句柄,后续都通过该指针进行操作,例如:
设置频率:
ret = clk_set_rate(host->clk, 300000);
获得频率:
ret = clk_get_rate(host->clk);
注意:devm_clk_get()的两个参数是二选一,可以都传入,也可以只传入一个参数。
像i2c、mmc等这些外设驱动,通常只需要使能门控即可,因为这些外设并不是时钟源,它们只有开关。如果直接调用clk_ser_rate函数设置频率,clk_set_rate会向上传递,即设置它的父时钟频率。例如在该例子中直接调用clk_set_rate函数,最终设置的是时钟源peri的频率。
clock驱动实例
clock驱动在时钟子系统中属于provider,provider是时钟的提供者,即具体的clock驱动。clock驱动在Linux刚启动的时候就要完成,比initcall都要早期,因此clock驱动是在内核中进行实现。在内核的drivers/clk目录下,可以看到各个芯片厂商对各自芯片clock驱动的实现:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxXnZMfs-1676165714841)(pic/image-20230211094537328.png)]](https://img-blog.csdnimg.cn/3ed18a0a3c7442c2abebba215043b047.png)
下面以一个简单的时钟树,举例说明一个芯片的时钟驱动的大致实现过程:
1、时钟树
通常来说,一个芯片的时钟树是比较固定的,例如,以下时钟树:

时钟树的根节点一般是晶振时钟,上图根节点为24M晶振时钟。根节点下面是PLL,PLL用于提升频率。PPL0下又分频给PERI、DSP和ISP。PLL1分频给DDR和ENC。
对于PLL来说,PLL的频率可以通过寄存器设置,但通常是固定的,所以PLL属于固定时钟。
对PERI、DSP等模块来说,它们的频率来自于PLL的分频,因此这些模块的时钟属于分频时钟。
2、设备树
设备树中表示一个时钟源,应有如下属性,例如24Mosc:
clocks{osc24M:osc24M{compatible = "fixed-clock";#clock-cells = <0>;clock-output-name = "osc24M";clock-frequency = <24000000>;};
};
| 属性 | 说明 |
|---|---|
| compatible | 驱动匹配名字 |
| #clock-cells | 提供输出时钟的路数。#clock-cells为0时,代表输出一路时钟 #clock-cells为1时,代表输出2路时钟。 |
| #clock-output-names | 输出时钟的名字 |
| #clock-frequency | 输出时钟的频率 |
3、驱动实现
clock驱动编写的基本步骤:
- 实现
struct clk_ops相关成员函数 - 定义分配
struct clk_onecell_data结构体,初始化相关数据 - 定义分配
struct clk_init_data结构体,初始化相关数据 - 调用
clk_register将时钟注册进框架 - 调用
clk_register_clkdev注册时钟设备 - 调用
of_clk_add_provider,将clk provider存放到of_clk_provider链表中管理 - 调用
CLK_OF_DECLARE声明驱动
fixed_clk固定时钟实现
fixed_clk针对像PLL这种具有固定频率的时钟,对于PLL,我们只需要实现.recalc_rate函数
设备树:
#define PLL0_CLK 0clocks{osc24M:osc24M{compatible = "fixed-clock";#clock-cells = <0>;clock-output-names = "osc24M";clock-frequency = <24000000>;};pll0:pll0{compatible = "xx, choogle-fixed-clk";#clock-cells = <0>;clock-id = <PLL0_CLK>;clock-frequency = <1000000000>;clock-output-names = "pll0";clocks = <&osc24M>;};
};
驱动:
#include <linux/clk-provier.h>
#include <linux/clkdev.h>
#include <linux/clk.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/delay.h>#define CLOCK_BASE 0X12340000
#define CLOCK_SIZE 0X1000struct xx_fixed_clk{void __iomem *reg;//保存映射后寄存器基址unsigned long fixed_rate;//频率int id;//clock idstruct clk_hw*;
};
static unsigned long xx_pll0_fixed_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{unsigned long recalc_rate;//硬件操作:查询寄存器,获得分频系数,计算频率然后返回return recalc_rate;
}static struct clk_ops xx_pll0_fixed_clk_ops = {.recalc_rate = xx_pll0_fixed_clk_recalc_rate,
};struct clk_ops *xx_fixed_clk_ops[] = {&xx_pll0_fixed_clk_ops,
};struct clk * __init xx_register_fixed_clk(const char *name, const char *parent_name,void __iomem *res_reg, u32 fixed_rate, int id, const struct clk_ops *ops)
{struct xx_fixed_clk *fixed_clk;struct clk *clk;struct clk_init_data init = {};fixed_clk = kzalloc(sizeof(*fixed_clk), GFP_KERNEL);if (!fixed_clk)return ERR_PTR(-ENOMEM);//初始化struct clk_init_data数据init.name = name;init.flags = CLK_IS_BASIC;init.parent_names = parent_name ? &parent_name : NULL;init.num_parents = parent_name ? 1 : 0;fixed_clk->reg = res_reg;//保存映射后的基址fixed_clk->fixed_rate = fixed_rate;//保存频率fixed_clk->id = id;//保存clock idfixed_clk->hw.init = &init;//时钟注册clk = clk_register(NULL, &fixed_clk->hw);if (IS_ERR(clk))kfree(fixed_clk);return clk;
}static void __init of_xx_fixed_clk_init(struct device_node *np)
{struct clk_onecell_data *clk_data;const char *clk_name = np->name;const char *parent_name = of_clk_get_parent_name(np, 0);void __iomem *res_reg = ioremap(CLOCK_BASE, CLOCK_SIZE);//寄存器基址映射u32 rate = -1;int clock_id, index, number;clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);if (!clk_data )return;number = of_property_count_u32_elems(np, "clock-id");clk_data->clks = kcalloc(number, sizeof(struct clk*), GFP_KERNEL);if (!clk_data->clks)goto err_free_data;of_property_read_u32(np, "clock-frequency", &rate);/*** 操作寄存器:初始化PLL时钟频率* ......*/for (index=0; index<number; index++) {of_property_read_string_index(np, "clock-output-names", index, &clk_name);of_property_read_u32_index(np, "clock-id", index, &clock_id);clk_data->clks[index] = xx_register_fixed_clk(clk_name, parent_name, res_reg, rate, clock_id, ak_fixed_clk_ops[pll_id]);if (IS_ERR(clk_data->clks[index])) {pr_err("%s register fixed clk failed: clk_name:%s, index = %d\n",__func__, clk_name, index);WARN_ON(true);continue;}clk_register_clkdev(clk_data->clks[index], clk_name, NULL);//注册时钟设备}clk_data->clk_num = number;if (number == 1) {of_clk_add_provider(np, of_clk_src_simple_get, clk_data->clks[0]);} else {of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);}return;err_free_data:kfree(clk_data);}CLK_OF_DECLARE(xx_fixed_clk, "xx,xx-fixed-clk", of_xx_fixed_clk_init);
factor_clk分频时钟实现
peri的时钟来自于Pll的分频,对于这类时钟,需要实现.round_rate、.set_rate、.recalc_rate。
设备树:
#define PLL0_CLK 0
#defeine PLL0_FACTOR_PERI 0clocks{osc24M:osc24M{//晶振时钟compatible = "fixed-clock";#clock-cells = <0>;clock-output-names = "osc24M";clock-frequency = <24000000>;};pll0:pll0{//pll倍频时钟compatible = "xx, xx-fixed-clk";#clock-cells = <0>;clock-id = <PLL0_CLK>;clock-frequency = <1000000000>;clock-output-names = "pll0";clocks = <&osc24M>;//pll的父时钟为24M晶振};factor_pll0_clk:factor_pll0_clk{//pll分频时钟compatible = "xx,xx-pll0-factor-clk";#clock-cells = <1>;clock-id = <PLL0_FACTOR_PERI>;clock-output-names = "pll0_peri";clocks = <&pll0 PLL0_CLK>;//PERI子系统的父时钟为pll0};
};
驱动:
static long xx_factor_pll0_clk_round_rate(struct clk_hw *hw, unsigned long rate,unsigned long *parent_rate)
{unsigned long round_rate;//返回时钟实际支持的最接近速率return round_rate;
}
static int xx_factor_pll0_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate)
{int ret = 0;//操作寄存器,设置频率return ret;
}static unsigned long xx_factor_pll0_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{unsigned long recalc_rate;//查询寄存器,获得分频系数,计算频率然后返回return recalc_rate;}const struct clk_ops xx_factor_clk_ops = {.round_rate = xx_factor_pll0_clk_round_rate,//给定目标速率作为输入,返回时钟.set_rate = xx_factor_pll0_clk_set_rate,.recalc_rate = xx_factor_pll0_clk_recalc_rate,
}static void __init of_xx_factor_clk_init(struct device_node *np)
{//驱动入口//参考上述pll的注册,唯一不同的就是struct clk_ops的成员函数实现
}CLK_OF_DECLARE(xx_factor_clk, "xx,xx-factor-clk", of_xx_facotr_clk_init);
gate_clk门控时钟实现
门控就是开关,对于门控而言,我们只需要实现struct clk_ops的.enable和.disable
设备树:
#define PLL0_CLK 0
#defeine PLL0_FACTOR_PERI 0
#define PERI_MCI0 0mmc0:mmc0@0x12345678{compatible = "xx,xx-mmc0";......clocks = <&peri PERI_MCI0>;clocks-names = "mmc0";......};clocks{osc24M:osc24M{compatible = "fixed-clock";#clock-cells = <0>;clock-output-names = "osc24M";clock-frequency = <24000000>;};pll0:pll0{compatible = "xx, xx-fixed-clk";#clock-cells = <0>;clock-id = <PLL0_CLK>;clock-frequency = <1000000000>;clock-output-names = "pll0";clocks = <&osc24M>;};factor_pll0_clk:factor_pll0_clk{compatible = "xx,xx-pll0-factor-clk";#clock-cells = <1>;clock-id = <PLL0_FACTOR_PERI>;clock-output-names = "pll0_peri";clocks = <&pll0 PLL0_CLK>;};peri:peri{compatible = "xx,xx-gate-clk";#clock-cells = <1>;/*peri gate*/clock-id = <PERI_MCI0>;clock-output-names = "mci0_peri";clocks = <&factor_pll0_clk PLL0_FACTOR_PERI>;};
};
驱动:
static int xx_gate_clk_enable(struct clk_hw *hw)
{//寄存器操作,打开门控return 0;
}static int xx_gate_clk_disable(struct clk_hw *hw)
{//寄存器操作,门控关return 0;
}const struct clk_ops ak_gate_clk_ops = {.enable = xx_gate_clk_enable,.disable = xx_gate_clk_disable,
}static void __init of_xx_gate_clk_init(struct device_node *np)
{//参考上述fixed_clk的注册,几乎相同,只不过操作函数clk_ops的实现不一样
}CLK_OF_DECLARE(xx_gate_clk, "xx,xx-gate-clk", of_xx_gate_clk_init);
上述只是对clock驱动实现的简单举例,每个芯片厂商在clock驱动的实现上都有很大的差异。对于一般的驱动,只需要会简单的使用内核提供的时钟API接口即可。
相关文章:
Linux clock子系统及驱动实例
文章目录基本概念CLK子系统时钟API的使用clock驱动实例1、时钟树2、设备树3、驱动实现fixed_clk固定时钟实现factor_clk分频时钟实现gate_clk门控时钟实现基本概念 晶振:晶源振荡器 PLL:Phase lock loop,锁相环。用于提升频率 OSC:…...
GIS数据格式坐标转换(地球坐标WGS84、GCJ-02、火星坐标、百度坐标BD-09、国家大地坐标系CGCS2000)
文章目录前言一、坐标系1.地球坐标 (WGS84)2.国测局坐标系(GCJ-02、火星坐标系)3.百度坐标(BD-09)4.国家大地2000坐标系(CGCS2000)二、百度坐标系(BD-09) 与火星坐标系(GCJ-02)的转换1.核心代码2.转换验证百度地图高德地图腾讯地图三、火星坐标系 (GCJ-02) 与百度坐标系 (BD-09…...
流媒体传输系列文章汇总
流媒体传输系列文章汇总 文章目录流媒体传输系列文章汇总引言流媒体交互协议详解视频封装协议详解流媒体环境搭建其他引言 从去年开始编写有关流媒体传输相关知识的文章,已发表文章22篇,阅读量也超过了10万,为了方便各位阅读,本文…...
“万字“ Java I/O流讲解
Java I/O流讲解 每博一文案 谁让你读了这么多书,又知道了双水村以外还有一个大世界,如果从小你就在这个天地里,日出而作,日落而息。 那你现在就会和众乡亲抱同一理想:经过几年的辛劳,像大哥一样娶个满意的…...
数据库(Spring)事务的四种隔离级别
文章目录Spring(数据库)事务隔离级别分为四种(级别递减)1、Serializable(串行化)2、REPEATABLE READ(可重复读)3、READ COMMITTED(读以提交)4、Read Uncommit…...
RabbitMQ详解(一):RabbitMQ相关概念
RabbitMQ是目前非常热门的一款消息中间件,不管是互联网大厂还是中小企业都在大量使用。作为一名合格的开发者,有必要对RabbitMQ有所了解,本系列是RabbitMQ快速入门文章,主要内容包括RabbitMQ是什么、RabbitMQ核心概念、五种消息模…...
ICLR 2023 | GReTo:以同异配关系重新审视动态时空图聚合
©PaperWeekly 原创 作者 | 周正阳单位 | 中国科学技术大学论文简介动态时空图数据结构在多种不同的学科中均普遍存在,如交通流、空气质量观测、社交网络等,这些观测往往会随着时间而变化,进而引发节点间关联的动态时变特性。本文的主要…...
线程池分享总结
线程池介绍 可以复用线程池的每一个资源 控制资源的总量 为什么要使用线程池 问题一:反复创建线程开销大 问题二:过多的线程会占用太多内存 解决以上两个问题的思路 • 用少量的线程——避免内存占用过多 • 让这部分线程都保持工作,且可…...
AOSP Android11系统源码和内核源码
推荐阅读 商务合作 安全产品 安全服务 2023年招聘 安全培训服务 软件定制服务 Android系统定制服务 安全/软件开发的课程列表 1.下载repo工具 (1).创建bin,并加入到PATH中 mkdir ~/binPATH~/bin:$PATH (2).安装依赖库 sudo apt-get install bison g-mult…...
layui框架学习(6:基础菜单)
菜单是应用系统的必备元素,虽然网页中的导航也能作为菜单使用,但菜单和导航的样式和用途有所不同(不同之处详见参考文献5)。Layui中用不同的预设类定义菜单和导航的样式,同时二者依赖的模块也不一样。本文主要学习和记…...
第十三届蓝桥杯 C++ B组省赛 C 题——刷题统计(AC)
1.刷题统计 1.题目描述 小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 aaa 道题目, 周六和周日每天做 bbb 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 nnn 题? 2.输入格式 输入一行包含三个整数 a,ba,ba,b 和 nnn. 3.输出…...
C++中的多态
【1】表现形式:同样的调用语句有多种不同的表现形态 【2】分类:静态联编和动态联编 静态联编有函数重载(运算符重载是特殊的函数重载),模板 【3】重点说下动态联编 【3.1】动态联编的实现需要以下步骤: 有继承关系、父类函数有virtual关…...
Swift如何保证线程安全
Swift可以通过以下几种方式来保证线程安全 使用互斥锁(Mutex):使用互斥锁可以防止多个线程同时访问共享数据,保证线程安全。 使用OSAtomic操作:OSAtomic操作可以在多线程环境中安全地执行原子操作。 使用DispatchQue…...
整型提升+算术转换——“C”
各位CSDN的uu们你们好呀,今天小雅兰的内容是之前操作符那篇博客中没有讲完的内容,整型提升这个小知识点也非常重要,那现在,就让我们进入操作符的世界吧 隐式类型转换 算术转换 操作符的属性 隐式类型转换 表达式求值的顺序一部…...
Freemarker介绍
2. Freemarker介绍 FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与 Web 容器无关,即在 Web 运行时,它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML…...
【软件测试开发】Junit5单元测试框架
目录1. 注解Test 注解BeforeEach BeforeAllAfterEach AfterAll2. 断言 assertassertequalsassertTrue assertFalseassertNull assertNotNull3. 用例执行顺序方法排序,通过 Order 注解来排序4. 测试套件 Suite5. 参数化单参数stringsints6. 参数化多参数CsvSourceCsv…...
【C语言技能树】程序环境和预处理
Halo,这里是Ppeua。平时主要更新C语言,C,数据结构算法......感兴趣就关注我吧!你定不会失望。 🌈个人主页:主页链接 🌈算法专栏:专栏链接 我会一直往里填充内容哒! &…...
数据库的三大范式
1.为什么需要数据库设计 设计数据表的时候,要考虑很多的问题: 用户需要哪些数据,我们在数据表中要保存哪一些数据怎么保证数据表中的数据的正确性如何降低数据表的冗余度开发人员怎么才能更方便的使用数据库 如果数据库设计得不合理的话,可…...
【MT7628】开发环境搭建-Fedora12安装之后无法上网问题解决
1.按照如下图所示,打开Network Connections 2.点击Network Connections,弹出如下界面...
[Android Studio]Android 数据存储-文件存储学习笔记-结合保存QQ账户与密码存储到指定文件中的演练
🟧🟨🟩🟦🟪 Android Debug🟧🟨🟩🟦🟪 Topic 发布安卓学习过程中遇到问题解决过程,希望我的解决方案可以对小伙伴们有帮助。 📋笔记目…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
Zustand 状态管理库:极简而强大的解决方案
Zustand 是一个轻量级、快速和可扩展的状态管理库,特别适合 React 应用。它以简洁的 API 和高效的性能解决了 Redux 等状态管理方案中的繁琐问题。 核心优势对比 基本使用指南 1. 创建 Store // store.js import create from zustandconst useStore create((set)…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
