【北京迅为】《i.MX8MM嵌入式Linux开发指南》-第三篇 嵌入式Linux驱动开发篇-第五十二章 设备树常用of函数

news2024/11/13 9:26:24

i.MX8MM处理器采用了先进的14LPCFinFET工艺,提供更快的速度和更高的电源效率;四核Cortex-A53,单核Cortex-M4,多达五个内核 ,主频高达1.8GHz,2G DDR4内存、8G EMMC存储。千兆工业级以太网、MIPI-DSI、USB HOST、WIFI/BT、4G模块、CAN、RS485等接口一应俱全。H264、VP8视频硬编码,H.264、H.265、VP8、VP9视频硬解码,并提供相关历程,支持8路PDM接口、5路SAI接口、2路Speaker。系统支持Android9.0(支持获取root限)Linux4.14.78+Qt5.10.1、Yocto、Ubuntu20、Debian9系统。适用于智能充电桩,物联网,工业控制,医疗,智能交通等,可用于任何通用工业和物联网应用、

【公众号】迅为电子

【粉丝群】258811263(加群获取驱动文档+例程)


第五十二章 设备树常用of函数

本章导读

设备树描述了设备的详细信息,这些信息包括数字类型的、字符串类型的、数组类型的,我们在编写驱动的时候需要获取到这些信息。比如设备树使用 reg 属性描述了某个外设的寄存器地址为 0X02005482,长度为 0X400,我们在编写驱动的时候需要获取到 reg 属性的0X02005482 0X400 这两个值,然后初始化外设。Linux 内核给我们提供了一系列的函数来获取设备树中的节点或者属性信息,这一系列的函数都有一个统一的前缀“of_”,所以在很多资料里面也被叫做 OF 函数。这些 OF 函数原型都定义在 include/linux/of.h 文件中。

52.1章节讲解了设备树中常用的of函数

52.2章节在52.1章节的基础上设计了四个小实验,分别来获取查找的设备节点,获取属性内容,获取reg属性,获取status属性。

本章内容对应视频讲解链接(在线观看):

设备树中常用的of操作函数  https://www.bilibili.com/video/BV1Vy4y1B7ta?p=27

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数”路径下。

52.1 设备树常用of函数

52.1.1 查找节点的of函数

设备都是以节点的形式“挂”到设备树上的,因此要想获取这个设备的其他属性信息,必须先获取到这个设备的节点。Linux 内核使用 device_node 结构体来描述一个节点,此结构体定义在文件include/linux/of.h 中,定义如下:

struct device_node
{
    const char *name; /* 节点名字 */
    const char *type; /* 设备类型 */
    phandle phandle;
    const char *full_name; /* 节点全名 */
    struct fwnode_handle fwnode;
    struct property *properties; /* 属性 */
    struct property *deadprops;  /* removed 属性 */
    struct device_node *parent;  /* 父节点 */
    struct device_node *child;   /* 子节点 */
    struct device_node *sibling;
    struct kobject kobj;
    unsigned long _flags;
    void *data;
#if defined(CONFIG_SPARC)
    const char *path_component_name;
    unsigned int unique_id;
    struct of_irq_controller *irq_trans;
#endif
};

节点的属性信息里面保存了驱动所需要的内容,因此对于属性值的提取非常重要,Linux 内核中使用结构体 property 表示属性,此结构体同样定义在文件 include/linux/of.h 中,内容如下:

struct property
{
    char *name;            /* 属性名字 */
    int length;            /* 属性长度 */
    void *value;           /* 属性值 */
    struct property *next; /* 下一个属性 */
    unsigned long _flags;
    unsigned int unique_id;
    struct bin_attribute attr;
};

获得设备树文件节点里面资源的步骤:

步骤一:查找我们要找的节点。

步骤二:获取我们需要的属性值。

与查找节点有关的 OF 函数有 3个,我们依次来看一下。

1.of_find_node_by_path 函数

 函数

inline struct device_node *of_find_node_by_path(const char *path)

path

带有全路径的节点名,可以使用节点的别名,比如“/backlight”就是 backlight 这个节点的全路径。

返回值

成功:返回找到的节点,失败返回NULL。

功能

通过节点名字查找指定的节点

2 of_get_parent 函数 

函数

struct device_node *of_get_parent(const struct device_node *node)

node

要查找的父节点的节点。

返回值

成功:找到的节点,如果为 NULL 表示查找失败。

功能

用于获取指定节点的父节点(如果有父节点的话)。

3 of_get_next_child  函数 

函数

struct device_node *of_get_next_child(const struct device_node *node struct device_node *prev)

node

父节点

prev

前一个子节点,也就是从哪一个子节点开始迭代的查找下一个子节点。可以设置为 NULL,表示从第一个子节点开始。

返回值

成功:找到的下一个子节点。如果为 NULL 表示查找失败。

功能

of_get_next_child 函数用迭代的查找子节点

52.1.2 获取属性值的of函数

与获取属性值的 OF 函数有 5个,我们依次来看一下。

1 of_find_property函数

函数

property *of_find_property(const struct device_node *np,const char *name,int *lenp)

np

设备节点

name

属性名字

lenp

属性值的字节数

返回值

找到的属性

功能

of_find_property 函数用于查找指定的属性

2 of_property_read_u8 函数

 of_property_read_u16函数

 of_property_read_u32函数

 of_property_read_u64函数

函数

int of_property_read_u8(const struct device_node *np,const char *propname,u8 *out_value)

int of_property_read_u16(const struct device_node *np,const char *propname,u16 *out_value)

int of_property_read_u32(const struct device_node *np,const char *propname,u32 *out_value)

int of_property_read_u64(const struct device_node *np,const char *propname,u64 *out_value)

np

设备节点

proname

要读取的属性名字

out_value

读取到的数组值

返回值

0,读取成功,负值,读取失败

功能

有些属性只有一个整型值,这四个函数就是用于读取这种只有一个整型值的属性,分别用于读取 u8、u16、u32 和 u64 类型属性值

3 of_property_read_u8_array 函数

  of_property_read_u16_array 函数

  of_property_read_u32_array 函数

  of_property_read_u64_array 函数

函数

int of_property_read_u8_array(const struct device_node *np,

const char *propname,

u8 *out_values,

size_t sz)

int of_property_read_u16_array(const struct device_node *np,

const char *propname,

u16 *out_values,

size_t sz)

int of_property_read_u32_array(const struct device_node *np,

const char *propname,

u32 *out_values,

size_t sz)

int of_property_read_u64_array(const struct device_node *np,

const char *propname,

u64 *out_values,

size_t sz)

np

设备节点

proname

要读取的属性名字

out_value

读取到的数组值,分别为 u8、u16、u32 和 u64。

sz

要读取的数组元素数量

返回值

0,读取成功,负值,读取失败

功能

这 4 个函数分别是读取属性中 u8、u16、u32 和 u64 类型的数组数据,比如大多数的 reg 属性都是数组数据,可以使用这 4 个函数一次读取出 reg 属性中的所有数据

4 of_property_read_string函数 

函数

int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)

np

设备节点

proname

要读取的属性名字

out_string

读取到的字符串值

返回值

0,读取成功,负值,读取失败

功能

of_property_read_string 函数用于读取属性中字符串值

5 of_iomap  函数 

函数

int of_property_read_string(struct device_node *np,const char *propname,const char **out_string)

np

设备节点

proname

要读取的属性名字

out_string

读取到的字符串值

返回值

0,读取成功,负值,读取失败

功能

of_property_read_string 函数用于读取属性中字符串值

52.2 of函数实验

这里我们以iTOP-IMX8MM开发板为例,我们将编写驱动代码,思路是当我们加载驱动的时候来读取属性和值。我们在Ubuntu的/home/topeet/imx8mm/10/目录下新建driver.c文件。将Makefile文件和build.sh文件拷贝到driver.c同级目录下,修改Makefile为如下所示:

obj-m += led.o
KDIR:=/home/topeet/linux/linux-imx
PWD?=$(shell pwd)
all:
	make -C $(KDIR) M=$(PWD) modules ARCH=arm64
clean:
	make -C $(KDIR) M=$(PWD) clean

52.2.1 查找的节点

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\001”路径下。

打开driver.c文件,代码如下图所示:

/*
 * @Author: topeet
 * @Description: of函数实验查找设备节点
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

static int hello_init(void)
{
    printk("hello world! \n");

    /**********添加我们要查找的节点的代码***********/
    // of_find_node_by_path函数通过路径查找节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
        //判断是否查找节点成功
        printk("of_find_node_by_path is error \n");
        return -1;
    }
    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块。我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并打印了节点的名称。

52.2.2 获取属性内容

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\002”路径下。

我们在上面代码的基础上进行修改,代码如下所示:

/*
 * @Author: topeet
 * @Description: of函数实验获取节点属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;

static int hello_init(void)
{
    printk("hello world! \n");

    /**********添加我们要查找的节点的代码***********/
    // of_find_node_by_path函数通过路径查找节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
        //判断是否查找节点成功
        printk("of_find_node_by_path is error \n");
        return -1;
    }
    

    /**********获取compatible属性内容的代码******/
    // of_find_property函数查找节点属性
    test_node_property = of_find_property(test_device_node, "compatible",&size);
    if(test_node_property == NULL){
        //判断是否查找到节点属性内容
        printk("test_node_property is error \n");
        return -1;
    }
    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);

    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到compatible属性内容。

52.2.3 获取reg属性

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\003”路径下。

我们在上面代码的基础上进行修改,代码如下所示:

/*
 * @Author: topeet
 * @Description: of函数实验获取reg属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;
u32 out_values[2] = {0};

static int hello_init(void)
{
    int ret;
    printk("hello world! \n");

    /**********添加我们要查找的节点的代码***********/
    // of_find_node_by_path函数通过路径查找节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
        //判断是否查找节点成功
        printk("of_find_node_by_path is error \n");
        return -1;
    }

    /**********获取compatible属性内容的代码******/
    // of_find_property函数查找节点属性
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if (test_node_property == NULL)
    {
        //判断是否查找到节点属性内容
        printk("test_node_property is error \n");
        return -1;
    }

    /**********获取reg属性内容的代码************/
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_u32_array is error \n");
        return -1;
    }

    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);
    //打印获取reg属性内容
    printk("out_values[0] is 0x%8x\n", out_values[0]);
    printk("out_values[1] is 0x%8x\n", out_values[1]);
    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到reg属性的值。

52.2.4 获取status属性

程序源码在网盘资料“iTOP-i.MX8MM开发板\02-i.MX8MM开发板网盘资料汇总(不含光盘内容)\嵌入式Linux开发指南(iTOP-i.MX8MM)手册配套资料\2.驱动程序例程\010-设备树常用OF函数\004”路径下。

我们在上面代码的基础上进行修改,代码如下所示: 

/*
 * @Author: topeet
 * @Description: of函数实验获取status属性
 */
#include <linux/init.h>   //初始化头文件
#include <linux/module.h> //最基本的文件,支持动态添加和卸载模块。
#include <linux/of.h>

//定义结构体表示我们的节点
struct device_node *test_device_node;

//定义结构体表示我们的节点属性
struct property *test_node_property;
//定义长度
int size;
u32 out_values[2] = {0};

const char *str;

static int hello_init(void)
{
    int ret;
    printk("hello world! \n");

    /**********添加我们要查找的节点的代码***********/
    // of_find_node_by_path函数通过路径查找节点
    test_device_node = of_find_node_by_path("/test");
    if (test_device_node == NULL)
    {
        //判断是否查找节点成功
        printk("of_find_node_by_path is error \n");
        return -1;
    }

    /**********获取compatible属性内容的代码******/
    // of_find_property函数查找节点属性
    test_node_property = of_find_property(test_device_node, "compatible", &size);
    if (test_node_property == NULL)
    {
        //判断是否查找到节点属性内容
        printk("test_node_property is error \n");
        return -1;
    }

    /**********获取reg属性内容的代码************/
    ret = of_property_read_u32_array(test_device_node, "reg", out_values, 2);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_u32_array is error \n");
        return -1;
    }

    /**********获取status属性内容的代码*********/
    ret = of_property_read_string(test_device_node, "status", &str);
    if (ret < 0)
    {
        //打印获取失败
        printk("of_property_read_string is error  \n");
        return -1;
    }

    //打印节点的名字
    printk("test_device_node name is %s\n", test_device_node->name);
    //打印属性compatible的名字
    printk("test_node_property value is %s\n", (char *)test_node_property->value);
    //打印获取reg属性内容
    printk("out_values[0] is 0x%8x\n", out_values[0]);
    printk("out_values[1] is 0x%8x\n", out_values[1]);
    //打印status属性
    printk("status is %s \n", str);

    return 0;
}

static void hello_exit(void)
{
    printk("goodbye \n");
}

module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");

保存driver.c文件,编译driver.c为驱动模块,如下图所示:

驱动编译完,我们通过nfs将编译好的驱动程序加载模块,然后我们进入共享目录,加载刚刚编译好的driver.ko,如下图所示: 

由上图可知,我们已经查找到设备节点,并且已经获取到status属性。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1960586.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NAS变身云盘管理大师:群晖部署AList全攻略!

文章目录 📖 介绍 📖🏡 演示环境 🏡📒 文章内容 📒🔑 AList功能简介🚀 快速部署📝 操作步骤🐳 Docker容器部署:灵活与强大📝 操作步骤📝 群晖部署🎈 获取方式 🎈⚓️ 相关链接 ⚓️📖 介绍 📖 在这个数字化时代,我们似乎都不可避免地拥有多个…

【数据结构】:大厂面试经典链表OJ题目详解

反转链表 206. 反转链表 - 力扣&#xff08;LeetCode&#xff09; 思路解透 本题就是通过不停地将最先的 head 节点位置的后一位插到最前面&#xff0c;完成链表的反转 本题需要两个节点变量 cur&#xff1a;其任务就是定位到原 head 节点位置的前一位&#xff0c;然后将自己…

列表(list)—python

一、列表的定义方式 列表内的每一个数据称为元素&#xff0c;列表以[ ]作为标识&#xff0c;列表内的每个元素之间用逗号隔开。 列表的基本语法如下&#xff1a; #字面量 [元素1,元素2,元素3,元素4,……]#定义变量 变量名称[元素1,元素2,元素3,元素4,……]#定义空列表 变量名…

Linux的防火墙

一、防火墙概述 防火墙是一种计算机硬件和软件的结合&#xff0c;使internet和intranet之间建立一个安全网关&#xff08;Security Gateway&#xff09;&#xff0c;从而保护内网免受非法用户侵入的技术。 防火墙主要由服务访问规则、验证工具、包过滤和应用网关4个部分组成。…

安防视频监控EasyCVR视频汇聚平台无法编辑设备通道信息的原因排查及解决

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台基于云边端一体化架构&#xff0c;兼容性强、支持多协议接入&#xff0c;包括国标GB/T 28181协议、部标JT808、GA/T 1400协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SD…

ECharts - 坐标轴刻度数值处理

写图表时&#xff0c;Y轴的数值过大&#xff0c;不太可能直接展示&#xff0c;这时候就得简写了&#xff0c;或者百分比展示的也要处理&#xff0c;如下图&#xff1a; yAxis: {type: value,// Y轴轴线axisLine: { show: false }, // 刻度线axisTick: { show: false },// 轴刻度…

ECharts实现按月统计和MTBF统计

一、数据准备 下表是小明最近一年的旅游记录 create_datecity_namecost_money2023-10-10 10:10:10北京14992023-11-11 11:11:11上海29992023-12-12 12:12:12上海19992024-01-24 12:12:12北京1232024-01-24 12:12:12上海2232024-02-24 12:12:12广州5642024-02-24 12:12:12北京…

学习笔记之Java篇(0729)

p 数组 大纲知识点数组的概念数组的定义、四个特点数组的常见操作普通遍历、for-each遍历、java.util.Array类用法多维数组多维数组的内存结构、存储表格、Javabean和数组存储表格常见算法冒泡排序基础算法、冒泡排序优化算法、二分法查找&#xff08;折半查找&#xff09; 1、…

【JavaWeb】Filter

基本使用 使用了过滤器之后&#xff0c;要想访问web服务器上的资源&#xff0c;必须先经过滤器&#xff0c;过滤器处理完毕之后&#xff0c;才可以访问对应的资源。过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;登录校验、统一编码处理、敏感字符处理等。 使用操…

nginx 子路径映射配置

如果子路径转发到另一个服务器IP&#xff0c;配置如下&#xff0c;注意都要以“/”结尾。 #指定nginx进程数 worker_processes 1; pcre_jit on;events {# 连接数上限worker_connections 30000; }#http服务 http {server {listen 20012;# 监听的端口号server_name localho…

Spring IoC DI(笔记)

一.了解SpringIoC&DI 1.1IoC 通过前面的学习,我们知道了Spring是一个开源框架,他让我们的开发更加简单.他支持广泛的应用场 景,有着活跃而庞大的社区,这也是Spring能够长久不衰的原因&#xff0c;但是这个概念相对来说,还是比较抽象&#xff0c;我们用一句更具体的话来概…

纯原创【车牌识别】基于图像处理的车牌识别——matlab项目实战(含GUI界面)详解

摘要 车牌识别系统乃计算机视觉与模式识别技术于智能交通领域的重要研究课题之一。其作用在于从复杂背景里提取运动中的汽车牌照&#xff0c;进而识别出车牌号码。车牌识别技术在高速公路电子收费、日常停车场管理以及交通违章监控等场景得到广泛运用。它的问世对于维护交通安全…

代码随想录二刷(链表章节)

代码随想录二刷(链表章节) 链表就是通过指针串联在一起的线性结构&#xff0c;每个节点都是由一个数据域和指针域(存放下一个节点的指针)。 双链表就是每个节点中既有指向前一个节点的&#xff0c;也有指向后一个节点的。 循环链表就是把头和尾连起来。 性能分析如下&#xf…

Java面试八股之@Autowired 和 @Resource的区别

Autowired 和 Resource的区别 在Spring框架中&#xff0c;Autowired 和 Resource 是两个常用的依赖注入注解&#xff0c;但它们有一些关键的区别。下面是这两个注解的主要差异&#xff1a; 1. 注解来源 Autowired&#xff1a; 是Spring框架提供的注解&#xff0c;位于包 or…

TerraSAR-XTanDEM-X卫星详解(一)

全球SAR卫星大盘点与回波数据处理专栏目录 1. TerraSAR-X简介 TerraSAR-X(Terra Synthetic Aperture Radar-X)和TanDEM-X(TerraSAR-X add-on for Digital Elevation Measurement)是由德国宇航中心(DLR)和EADS Astrium公司共同推出的一对双子卫星。Terra源自拉丁语,是地…

解决win10蓝屏“选择一个选项”的问题!

今天台式机开机&#xff0c;出现蓝屏问题&#xff0c;记录一下。 一、问题 启动修复不行&#xff0c;系统还原没还原点&#xff0c;系统映像恢复没有文件。难道要重装系统&#xff1f;手上只能Win7和XP的启动盘。此路不通。 二、解决 使用命令提示符。输入&#xff1a; bcdb…

《花100块做个摸鱼小网站! · 序》灵感来源

序 大家好呀&#xff0c;我是summo&#xff0c;这次来写写我在上班空闲(摸鱼)的时候做的一个小网站的事。去年阿里云不是推出了个活动嘛&#xff0c;2核2G的云服务器一年只要99块钱&#xff0c;懂行的人应该知道这个价格在业界已经是非常良心了&#xff0c;虽然优惠只有一年&a…

PMP考试难吗?好不好学?

PMP 并不难&#xff0c;虽然新考纲大家都说开盲盒&#xff0c;做阅读理解&#xff0c;但线上考试成绩出的快&#xff0c;晒 3A 的也不少。给大家分享下我的备考经历&#xff0c;希望能给后面备考的同学一点参考吧。 现在的新考纲是要学习三本书的&#xff0c;《PMBOK》第六版、…

弘景光电:技术实力与创新驱动并进

在光学镜头及摄像模组产品领域&#xff0c;广东弘景光电科技股份有限公司&#xff08;以下简称“弘景光电”&#xff09;无疑是一颗耀眼的明星。自成立以来&#xff0c;弘景光电凭借其强大的研发实力、卓越的产品性能、精密的制造工艺以及严格的质量管理体系&#xff0c;在光学…

使用 useSeoMeta 进行 SEO 配置

title: 使用 useSeoMeta 进行 SEO 配置 date: 2024/7/30 updated: 2024/7/30 author: cmdragon excerpt: 摘要&#xff1a;本文介绍了Nuxt3中的useSeoMeta组合函数&#xff0c;用于简化和优化网站的SEO配置。通过这个工具&#xff0c;开发者可以在Nuxt3项目中方便地设置页面…