徐州做网站企业有保障的注册代理
- 作者: 五速梦信息网
- 时间: 2026年04月20日 07:06
当前位置: 首页 > news >正文
徐州做网站企业,有保障的注册代理,麻栗坡网站建设,新网站怎么做seo优化设备树#xff08;DeviceTree#xff09;是一种硬件描述机制#xff0c;用于在嵌入式系统和操作系统中描述硬件设备的特性、连接关系和配置信息。它提供了一种与平台无关的方式来描述硬件#xff0c;使得内核与硬件之间的耦合度降低#xff0c;提高了系统的可移植性和可维…设备树DeviceTree是一种硬件描述机制用于在嵌入式系统和操作系统中描述硬件设备的特性、连接关系和配置信息。它提供了一种与平台无关的方式来描述硬件使得内核与硬件之间的耦合度降低提高了系统的可移植性和可维护性。
为什么需要设备树这种机制 在Linux内核V2.6版本之前尤其是ARM架构下用于描述不同硬件信息的文件大多存放在arch/arm/plat-xxx和arch/arm/mach-xxx文件夹下。这些文件包含了大量的板级硬件信息而这些信息对于内核的通用性来说并无实质性帮助反而导致了内核代码的冗余和复杂化。随着每年新推出的ARM架构芯片和基于这些芯片的板子数量不断增加板级信息文件也呈指数级增长这使得内核维护变得异常困难。 设备树通过一种结构化的方式来描述硬件平台包括CPU、内存、外设、总线等资源。这种描述方式使得驱动程序和内核代码能够更容易地适应不同的硬件平台而无需对内核代码进行大量的修改。通过更换设备树文件.dtb即可实现不同主板的无差异支持从而大大提高了代码的可移植性和可重用性。 2. 设备树基础知识 设备树专业术语介绍: DTSDevice Tree Source DTS 是设备树的源文件 采用一种类似于文本的语法来描述硬件设备的结构、 属性和连接关系。 DTS 文件以.dts 为扩展名 通常由开发人员编写。 它是人类可读的形式 用于描述设备树的层次结构和属性信息。 DTSIDevice Tree Source Include DTSI 文件是设备树源文件的包含文件。 它扩展了 DTS文件的功能 用于定义可重用的设备树片段。DTSI 文件以.dtsi 为扩展名 可以在多个 DTS 文件中包含和共享。 通过使用 DTSI 可以提高设备树的可重用性和可维护性和 C 语言中头文件的作用相同 。 DTBDevice Tree Blob DTB 是设备树的二进制表示形式。 DTB 文件是通过将 DTS 或 DTSI文件编译而成的二进制文件 以.dtb 为扩展名。 DTB 文件包含了设备树的结构、 属性和连接信息 被操作系统加载和解析。 在运行时 操作系统使用 DTB 文件来动态识别和管理硬件设备。 DTCDevice Tree Compiler DTC 是设备树的编译器。 它是一个命令行工具 用于将 DTS和 DTSI 文件编译成 DTB 文件。 DTC 将文本格式的设备树源代码转换为二进制的设备树表示形 式 以便操作系统能够加载和解析。 DTC 是设备树开发中一个重要的工具。 DTS、 DTSI、 DTB 和 DTC 之间的关系 开发人员使用文本编辑器编写 DTS 和 DTSI 文件 描述硬件设备的层次结构、 属性和连接关系。DTSI 文件可以在多个 DTS 文件中包含和共享 以提高设备树的可重用性和可维护性。使用 DTC 编译器 开发人员将 DTS 和 DTSI 文件编译成二进制的 DTB 文件 操作系统在启动过程中加载和解析 DTB 文件 以识别和管理硬件设备。 设备树文件路径 ARM体系结构: I.MX6ULL (内核目录)/arch/arm/boot/dts瑞芯微的RK3568 (内核目录)/arch/arm64/boot/dts/rockchipDTC工具的使用 在 Linux 内核源码中 DTCDevice Tree Compiler 的源代码和相关工具通常存放在(内核目录)/scripts/dtc/目录中 在编译完源码之后 dtc 设备树编译器会默认生成 如果没有生成相应的 dtc 可执行文件可以查看在内核默认配置文件中((内核目录)/.config) CONFIG_DTC 是否使能。 ① 设备树的编译 dtc -I dts -O dtb -o output.dtb input.dts其中 input.dts是输入的设备树源文件 output.dtb是编译后的二进制设备树文件。 编译器会验证设备树源文件的语法和语义 生成与硬件描述相对应的设备树表示形式。 ②设备树的反编译 dtc -I dtb -O dts -o output.dts input.dtb将二进制设备树文件反编译为设备树源文件 input.dtb 是输入的二进制设备树文件output.dts是反编译后的设备树源文件 3.设备树基本语法 3.1根节点 设备树使用一种层次结构其中的根节点Root Node是整个设备树的起始点和顶层节点。 /dts-v1/; // 设备树版本信息 /{ // 根节点开始// 可以在里面添加描述根节点的属性和配置 }; 子节点格式如下 [label:] node-name[unit-address] {[properties definitions][child nodes] };节点标签Label 可选 节点标签是一个可选的标识符 用于在设备树中引用该节点。 标签允许其他节点直接引用此节点 以便在设备树中建立引用关系。节点名称Node Name 节点名称是一个字符串 用于唯一标识该节点在设备树中的位置。 节点名称通常是硬件设备的名称 但必须在设备树中是唯一的。单元地址Unit Address可选单元地址用于标识设备的实例。 它可以是一个整数、 一个十六进制值或一个字符串 具体取决于设备的要求。 单元地址的目的是区分相同类型的设备的不同实例 例如在下图 中名为 cpu 的节点通过它们的单元地址值 0 和 1 来区分 名称为 ethernet 的节点通过其单元地址值 fe002000 和 fe003000 来区分。 属性定义Properties Definitions 属性定义是一组键值对 用于描述设备的配置和特性。 属性可以根据设备的需求进行定义 例如寄存器地址、 中断号、 时钟频率等子节点Child Nodes 子节点是当前节点的子项 用于进一步描述硬件设备的子组件或配置。 子节点可以包含自己的属性定义和更深层次的子节点 形成设备树的层次结构。 例子如下: /dts-v1/; /{ uart0: uartfe001000 {compatiblens16550;reg0xfe001000 0x100;}; };可以使用以下方法来修改uartfe001000这个node // 根节点之外使用label来引用node uart0 {status “disabled”; }; 根节点之外使用全路径 {/uartfe001000} {status “disabled”; };address-cells 和 size-cells 属性 #address-cells 属性是一个位于设备树根节点的特殊属性 它指定了设备树中地址单元的位数。 地址单元是设备树中用于表示设备地址的单个单位。 它通常是一个整数 可以是十进制或十六进制值 #size-cells属性也是一个位于设备树根节点的特殊属性 它指定了设备树中大小单元的位数。 大小单元是设备树中用于表示设备大小的单个单位。 它通常是一个整数 可以是十进制或十六进制值。 示例1 node1 {#address-cells 1;#size-cells 1;node1-child {reg 0x02200000 0x4000;// 其他属性和子节点的定义}; }; / 解释: node1-child 节点的 reg 属性使用了 0x02200000 0x4000 表示地址和大小。 由于 #address-cells 的值为 1 表示使用一个单元来表示地址。 #size-cells 的值也为 1 表示使用一个单元来表示大小。 解释后的地址和大小值如下 地址部分 0x02200000 被解释为一个地址单元 地址为 0x02200000。 大小部分 0x4000 被解释为一个大小单元 大小为 0x4000
/示例2 node1 {#address-cells 2;#size-cells 0;node1-child {reg 0x0000 0x0001;// 其他属性和子节点的定义}; }; / 解释: node1-child 节点的 reg 属性使用了 0x0000 0x0001 表示地址。 由于#address-cells 的值为 2 表示使用两个单元来表示地址。 #size-cells 的值为 0 表示不使用单元来表示大小。 解释后的地址值如下 地址部分 0x0000 0x0001 被解释为两个地址单元 其中第一个地址单元为 0x0000 第二个地址单元为 0x0001。 / model属性 model 属性用于描述设备的型号或者名称可选 my_device{compatible device;model My Device; };status属性 status 属性用于描述设备或节点的状态 “okay” 表示设备或节点正常工作 可用。“disabled” 表示设备或节点被禁用 不可用。“reserved” 表示设备或节点已被保留 暂时不可用。“fail” 表示设备或节点初始化或操作失败 不可用 my_device{compatible device;model My Device;status okay; };compatible属性 compatible 属性用于描述设备的兼容性信息用于识别设备节点与驱动程序之间的匹配关系 compatible 属性的值是一个字符串或字符串列表 建议取这样的形式 “manufacturer,model”即“厂家名,模块名”。 示例 my_device {compatible vendor,device; // 指定设备节点与特定厂商的特定设备兼容// 其他属性和子节点的定义 };compatible [vendor,device1, vendor,device2]; //用于指定设备节点与多个设备兼容 通常用于设备节点具有多种变体或配置compatible vendor,// 指定设备节点与特定厂商的所有设备兼容 不考虑具体的设备标识。3.2 aliases节点 aliases 节点是一个特殊的节点 用于定义设备别名位于设备树的根部 /dts-v1/; /{aliases{mmc0 sdmmc0;serial0 /simplefe000000/seria111c500;}; }; /*mmc0 别名与设备树中的 sdmmc0 节点相关联。 通过使用别名 mmc0 其他设备节点或客户端程序可以更方便地引用 sdmmc0 节点 而不必直接使用其完整路径。
serial0 别名与设备树中的路径 /simplefe000000/seria111c500 相关联。 通过使用别名 serial0 其他设备节点或客户端程序可以更方便地引用该路径 而不必记住整个路径字符串。 /注 aliases 节点中定义的别名只在设备树内部可见 不能在设备树之外引用。它们主要用于设备树的内部组织和引用 以提高可读性和可维护性。 3.3 chosen节点 chosen 节点是设备树中的一个特殊节点 用于传递和存储系统引导和配置的相关信息。它位于设备树的根部。 chosen节点包含以下子节点和属性: bootargs 用于存储引导内核时传递的命令行参数。 它可以包含诸如内核参数、 设备树参数等信息。 在引导过程中 操作系统或引导加载程序可以读取该属性来获取启动参数。stdout-path用于指定用于标准输出的设备路径。 在引导过程中 操作系统可以使用该属性来确定将控制台输出发送到哪个设备 例如串口或显示屏。firmware-name用于指定系统固件的名称。 它可以用于标识所使用的引导加载程序或固件的类型和版本。linux,initrd-start 和 linux,initrd-end 这些属性用于指定 Linux 内核初始化 RAM 磁盘initrd 的起始地址和结束地址。 这些信息在引导过程中被引导加载程序使用 以将 initrd 加载到内存中供内核使用。其他自定义属性 chosen 节点还可以包含其他自定义属性 用于存储特定于系统引导和配置的信息。 chosen {bootargs root/dev/nfs rw nfsroot192.168.1.1 consolettyS0,115200; }; / 通过这些命令行参数 操作系统或引导加载程序可以配置内核在引导过程中正确地加载NFS 根文件系统 并将控制台输出发送到指定的串口设备。 */通过使用 chosen 节点 系统引导过程中的相关信息可以方便地传递给操作系统或引导加载程序。 3.3 device_type节点 device_type 节点是用于描述设备类型的节点 1. cpu 表示中央处理器2. memory 表示内存设备3. display 显示设备液晶显示屏4. serial 表示串行通信设备串口5. ethernet 表示以太网设备6. usb 表示通用串行总线设备7. i2c 表示使用I2CInter-Integrated Circuit总线通信的设备8. spi 表示使用SPISerial Peripheral Interface总线通信的设备9. gpio: 表示通用输入/输出设备10.pwm 表示脉宽调制设备 3.4 板子启动后查看设备树
ls /sys/firmware/
devicetree fdt/sys/firmware/devicetree 目录下是以目录结构程现的 dtb 文件, 根节点对应 base 目录,每一个节点对应一个目录, 每一个属性对应一个文件。这些属性的值如果是字符串可以使用 cat 命令把它打印出来对于数值可以用hexdump 把它打印出来。 4. 内核对设备树的处理 从源代码文件 dts 文件开始设备树的处理过程为 ①dts 在 PC 机上被编译为 dtb 文件 ② u-boot 把 dtb 文件传给内核 ③ 内核解析 dtb 文件把每一个节点都转换为 device_node 结构体 ④ 对于某些 device_node 结构体会被转换为 platform_device 结构体。 device_node 结构体定义和property结构体在内核源码的“/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; // 已删除的属性列表struct device_node *parent; // 父设备节点指针struct device_node *child; // 子设备节点指针struct device_node *sibling; // 兄弟设备节点指针struct kobject kobj; // 内核对象用于 sysfsunsigned long _flags; // 设备节点的标志位void *data; // 与设备节点相关的数据指针 #if defined(CONFIG_SPARC)const char *path_component_name; // 设备节点的路径组件名称unsigned int unique_id; // 设备节点的唯一标识struct of_irq_controller *irq_trans; // 设备节点的中断控制器 #endif };struct property {char *name; // 属性的名称int length; // 属性值的长度字节数void *value; // 属性值的指针struct property *next; // 下一个属性节点指针unsigned long _flags; // 属性的标志位unsigned int unique_id; // 属性的唯一标识 struct bin_attribute attr;// 内核对象二进制属性 };哪些设备树节点会被转换为 platform_device 根据规则 1 首先遍历根节点下包含 compatible 属性的子节点 对于每个子节点 创建一个对应的 platform_device。根据规则 2 遍历包含 compatible 属性为 “simple-bus”、 “simple-mfd” 、“arm、amba-bus”、“isa” 的节点以及它们的子节点。 如果子节点包含 compatible 属性值则会创建一个对应的 platform_device。根据规则 3 总线 I2C、 SPI 节点下的子节点 不转换为 platform_device某个总线下到子节点 应该交给对应的总线驱动程序来处理, 它们不应该被转换为platform_device。 5. of操作函数 5.1获取设备树节点 of_find_node_by_name 函数 函数原型: struct device_node *of_find_node_by_name(struct device_node *from, const char *nam e); 函数参数和返回值含义如下 from开始查找的节点如果为 NULL 表示从根节点开始查找整个设备树。 name要查找的节点名字。 返回值 找到的节点如果为 NULL 表示查找失败。of_find_node_by_path 函数 函数原型 struct device_node *of_find_node_by_path(const char *path); 函数参数和返回值含义如下 path带有全路径的节点名可以使用节点的别名比如“/backlight”就是 backlight 这个 节点的全路径。 返回值 找到的节点如果为 NULL 表示查找失败of_get_parent 函数 函数原型 struct device_node *of_get_parent(const struct device_node *node); 头文件 #include linux/of.h 函数作用 该函数接收一个指向设备树节点的指针 node 并返回该节点的父节点的指针。 参数含义 node 要获取父节点的设备树节点指针。 返回值 如果找到匹配的节点 则返回对应的 struct device_node 指针。 如果未找到匹配的节点 则返回 NULL。of_get_next_child 函数 函数原型 struct device_node *of_get_next_child(const struct device_node *node, struct device_node *prev); 头文件 #include linux/of.h 函数作用 该函数接收两个参数 node 是当前节点 prev 是上一个子节点。 它返回下一个子节点的指针。 参数含义 node 当前节点 用于指定要获取子节点的起始节点。 prev 上一个子节点 用于指定从哪个子节点开始获取下一个子节点。 如果为 NULL 则从起始节点的第一个子节点开始。 返回值 如果找到匹配的节点 则返回对应的 struct device_node 指针。 如果未找到匹配的节点 则返回 NULL。5.2提取属性值 of_find_property函数 函数原型: property *of_find_property(const struct device_node *np, const char *name,int *lenp) 函数参数和返回值含义如下 np设备节点。 name 属性名字。 lenp属性值的字节数 返回值 找到的属性。of_property_count_elems_of_size 函数 int of_property_count_elems_of_size(const struct device_node *np, const char *propname, int elem_size) 函数参数和返回值含义如下 np设备节点。 proname 需要统计元素数量的属性名字。 elem_size元素长度。 返回值 得到的属性元素数量。of_property_read_u32_index 函数 int of_property_read_u32_index(const struct device_node *np, const char *propname, u32 index, u32 *out_value) 函数参数和返回值含义如下 np设备节点。 proname 要读取的属性名字。 index要读取的值标号。 out_value读取到的值 返回值 0 读取成功负值读取失败 -EINVAL 表示属性不存在 -ENODATA 表示没有要读取的数据 -EOVERFLOW 表示属性值列表太小。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_n_addr_cells 函数 int of_n_addr_cells(struct device_node *np) 函数参数和返回值含义如下 np设备节点。 返回值 获取到的#address-cells 属性值。of_n_size_cells 函数 int of_n_size_cells(struct device_node np) 函数参数和返回值含义如下 np设备节点。 返回值 获取到的#size-cells 属性值。6.pinctrl和gpio子系统 在设备树实验开始之前我们先了解一下pinctrl和gpio子系统 Pinctrl子系统 pinctrl引脚控制 用于描述和配置硬件设备上的引脚功能和连接方式。 它是设备树的一部分 用于在启动过程中传递引脚配置信息给操作系统和设备驱动程序 以便正确地初始化和控制引脚。 在设备树中 pinctrl引脚控制 使用了客户端和服务端的概念来描述引脚控制的关系和配置。 现在的芯片动辄几百个引脚在使用到 GPIO 功能时让你一个引脚一个引脚去找对应的寄存器配置过程烦躁且费劲。 通过把引脚的复用、配置分离出来做成Pinctrl子系统给GPIO、I2C等使用。 my_device_100ask_imx6ull{pinctrl-names default;pinctrl-0 myled_for_gpio_subsys;status okay;};解释如下: pinctrl-names 属性 pinctrl-names default; 这一行定义了一个或多个引脚配置状态的名称。在这个例子中只定义了一个名为 default 的状态。这意味着当设备启动或需要配置引脚时会查找名为 default 的引脚配置。pinctrl-0 属性 pinctrl-0 myled_for_gpio_subsys; 这一行指定了对应于 default 状态由 pinctrl-names 属性定义的引脚配置引用。myled_for_gpio_subsys 是一个对设备树中另一个节点的引用这个节点应该包含了具体的引脚配置信息比如哪些引脚被选中、它们的电气特性如驱动能力、上拉/下拉等以及它们被配置为什么功能如GPIO、I2C等。GPIO子系统 GPIOGeneral Purpose Input/Output通用输入输出子系统是Linux内核中用于管理通用输入输出引脚的一个子系统。GPIO引脚是芯片上的一种常见资源可以用于实现各种简单的输入输出功能。 以前我们通过寄存器来操作 GPIO 引脚即使 LED 驱动程序对于不同的板子它的代码也完全不同。 当 BSP 工程师实现了 GPIO 子系统后我们就可以 在设备树里指定 GPIO 引脚在驱动代码中使用 GPIO 子系统的标准函数获得 GPIO、设置 GPIO 方向、读取/设置 GPIO 值 /dts-v1/; /{… my_device_100ask_imx6ull{compatible 100ask,leddrv;pinctrl-names default;pinctrl-0 myled_for_gpio_subsys;led-gpios gpio5 3 GPIO_ACTIVE_LOW;status okay;}; };iomuxc_snvs {…imx6ul-evk {… / 省略其他代码 /myled_for_gpio_subsys: myled_for_gpio_subsys { /! Function assigned for the core: Cortex-A7[ca7] /fsl,pins MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x000110A0/ 数值定义在IMX6ULLRM.pdf文档中的1529页*/;};}; };解释如下: led-gpios 属性 gpio5这是一个对GPIO控制器的引用指向设备树中定义的GPIO控制器节点之一。在这个例子中它指的是编号为5的GPIO控制器。这个引用告诉系统LED灯连接到了哪个GPIO控制器上。3这个数字指定了GPIO控制器上的具体引脚编号。在这个例子中LED灯连接到了GPIO控制器5的第3个引脚上。GPIO_ACTIVE_LOW这是一个标志用于指定GPIO引脚的活动状态。GPIO_ACTIVE_LOW意味着当引脚处于低电平时即0V或接近0VLED灯被认为是激活的即点亮。相反如果引脚处于高电平通常是3.3V或5V取决于系统的电源电压LED灯则被认为是非激活的即熄灭。pinctrl-names 和 pinctrl-0 属性虽然它们本身不直接属于GPIO子系统但与GPIO引脚的使用密切相关 总的来说这些代码片段共同描述了如何将一个GPIO引脚GPIO5的引脚3配置为LED灯的控制引脚并指定了该引脚在低电平时激活LED灯的行为。同时它们还引用了一个引脚配置通过myled_for_gpio_subsys 这里我们使用Pins_Tool_for_i.MX_Processors_v6_x64工具打IMX6ULL 的配置文件“MCIMX6Y2xxx08.mex”就可以在 GUI 界面中选择引脚 配置它的功能这就可以自动生成 Pinctrl 的子节点信息。
设备树下的 LED 驱动实验 注本实验使用的是韦东山I.MX6U开发板 100ask_imx6ull-14x14.dts 完成编写后在(内核目录)编译dts文件 book100ask:~/100ask_imx6ull-sdk/Linux-4.9.88\( make dtbs将编译好的dtb文件拷贝到网络文件系统中 cp /home/book/100ask_imx6ull-sdk/Linux-4.9.88/arch/arm/boot/dts/100ask_imx6ull-14x14.dtb ~/nfs_rootfs/7.1编写驱动文件 led_driver.c #include asm-generic/errno-base.h #include asm-generic/int-ll64.h #include asm/gpio.h #include linux/compiler.h #include linux/err.h #include linux/export.h #include linux/gpio/consumer.h #include linux/gpio/driver.h #include linux/ioport.h #include linux/kdev_t.h #include linux/leds.h #include linux/printk.h #include linux/stddef.h #include linux/types.h #include linux/kernel.h #include linux/delay.h #include linux/ide.h #include linux/init.h #include linux/module.h #include linux/errno.h #include linux/gpio.h #include linux/cdev.h #include linux/device.h #include linux/of_gpio.h #include linux/semaphore.h #include linux/timer.h #include linux/irq.h #include linux/wait.h #include linux/poll.h #include linux/fs.h #include linux/fcntl.h #include linux/platform_device.h #include asm/mach/map.h #include asm/uaccess.h #include asm/io.h#define LED_DEV_COUNT 1 #define LED_DEV_NAME dts_plat_led/* led_dev结构体 */ struct led_dev {dev_t dev_id; /* 设备id */struct cdev cdev; /* cdev*/struct class *class; /* class*/struct device *device; /* device*/int major; /* major*/struct device_node *node; /* led_node*/struct gpio_desc *led_gpio; /* led_gpio*/ };static struct led_dev led_dev;static int led_open (struct inode *node, struct file *filp){printk(%s %s line:%d\n, __FILE__,__FUNCTION__,__LINE__);/* 设置GPIO引脚为输出模式输出低电平 */gpiod_direction_output(led_dev.led_gpio, 0);return 0; }static ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *offset){int status;int err;printk(%s %s line:%d\n, __FILE__,__FUNCTION__,__LINE__);err copy_from_user(status, buf, 1);/* 设置GPIO的值 */gpiod_set_value(led_dev.led_gpio,status);return 1; }static const struct file_operations led_fops {.owner THIS_MODULE,.open led_open,.write led_write, };/* 当驱动与设备匹配成功后执行此函数 */ int led_probe(struct platform_device *led_device){printk(%s %s line:%d\n, __FILE__,__FUNCTION__,__LINE__);// 1.gpio/* led-gpios gpio5 3 GPIO_ACTIVE_LOW; */led_dev.led_gpio gpiod_get(led_device-dev, led, 0);if(IS_ERR(led_dev.led_gpio)){dev_err(led_device-dev, Failed to get GPIO for led);return PTR_ERR(led_dev.led_gpio);}// 2.注册file_operations结构体// 2.1注册设备号if(led_dev.major){led_dev.dev_id MKDEV(led_dev.major, 0);register_chrdev_region(led_dev.dev_id, LED_DEV_COUNT, LED_DEV_NAME);}else{alloc_chrdev_region(led_dev.dev_id,0, LED_DEV_COUNT, LED_DEV_NAME);led_dev.major MAJOR(led_dev.dev_id);}// 2.2添加cdev结构体cdev_init(led_dev.cdev, led_fops);cdev_add(led_dev.cdev, led_dev.dev_id, LED_DEV_COUNT);// 2.3创建classled_dev.class class_create(THIS_MODULE, LED_DEV_NAME);if(IS_ERR(led_dev.class)){return PTR_ERR(led_dev.class);}// 2.4 创建设备 /dev/dts_plat_ledled_dev.device device_create(led_dev.class, NULL, led_dev.dev_id, NULL, LED_DEV_NAME);if(IS_ERR(led_dev.device)){return PTR_ERR(led_dev.device);}return 0; }/* remove 函数移除 platform 驱动的时候此函数会执行 */ int led_remove(struct platform_device *led_device){gpio_set_value((unsigned int)led_dev.led_gpio,1); // turn off LEDcdev_del(led_dev.cdev);unregister_chrdev_region(led_dev.dev_id, LED_DEV_COUNT);device_destroy(led_dev.class, led_dev.dev_id);class_destroy(led_dev.class);gpiod_put(led_dev.led_gpio); // release gpioreturn 0;}static const struct of_device_id leds_table[] { {.compatible 100ask,leddrv}, /* 100ask_imx6ull-14x14.dts中定义compatible相同时才匹配成功 */{} };static struct platform_driver led_driver {.driver {.name imx6ul-led, /* driver name */.owner THIS_MODULE,.of_match_table leds_table, /* device tree match table */ },.probe led_probe, /* device probe function */ .remove led_remove, /* device remove function */ } ;static int __init led_init(void){printk(%s %s line %d\n, __FILE__,__FUNCTION__,__LINE__);return platform_driver_register(led_driver); } static void __exit led_exit(void){printk(%s %s line %d\n, __FILE__,__FUNCTION__,__LINE__);platform_driver_unregister(led_driver); }module_init(led_init); module_exit(led_exit); MODULE_LICENSE(GPL); MODULE_AUTHOR(Pumpk1n);led_test.c #include asm-generic/fcntl.h #include linux/string.h #include stdio.h #include sys/types.h #include sys/stat.h #include string.h #include unistd.hint main(int argc, char **argv) {int fd;int status;if(argc ! 3){printf(Usage: %s /dev/dts_plat_led on\n, argv[0]); // turn on ledprintf(Usage: %s /dev/dts_plat_led off\n, argv[0]);// turn off ledreturn -1;}fd open(argv[1],O_RDWR);if(fd 0){printf(Error opening /dev/dts_plat_led\n);return -1;}if(strcmp(argv[2], on) 0){// turn on the LEDstatus 1;write(fd,status,1);}else if(strcmp(argv[2], off) 0){// turn off the LEDstatus 0;write(fd,status,1);}return 0; }Makefile文件 KERN_DIR /home/book/100ask_imx6ull-sdk/Linux-4.9.88obj-m led_driver.oall: \)(MAKE) -C \((KERN_DIR) Mpwd modules\)(CROSS_COMPILE)gcc -o led_test led_test.c clean:make -C $(KERN_DIR) Mpwd modules cleanrm -rf modules.orderrm -f led_test执行命令: make 编译 led_driver.c文件为 led_driver.ko内核模块文件 编译led_test.c文件为led_test可执行文件 拷贝这两个文件到网络文件系统中 cp led_test led_driver.ko /nfs_rootfs/串口连接开发板 挂载到网络文件系统中 [root100ask:]# mount -t nfs -o nolock,vers3 192.168.5.11:/home/book/nfs_rootfs /mnt将/mnt/100ask_imx6ull-14x14.dtb文件复制到/boot/下面 [root100ask:]# cp /mnt/100ask_imx6ull-14x14.dtb /boot/重启开发板等待重启完成后重新挂载网络文件系统中 [root100ask:]# reboot [root100ask:]# mount -t nfs -o nolock,vers3 192.168.5.11:/home/book/nfs_rootfs /mnt进入到/mnt/目录中并向内核加载模块文件led_driver.ko [root100ask:]# cd /mnt [root100ask:mnt]# insmod led_driver.ko运行测试文件测试实验结果 [root100ask:/mnt]# ./led_test /dev/dts_plat_led on [root100ask:/mnt]# ./led_test /dev/dts_plat_led off
- 上一篇: 徐州做网站公司哪家好虚拟主机部署网站
- 下一篇: 许昌大成建设集团网站长沙专业网站建设怎么做
相关文章
-
徐州做网站公司哪家好虚拟主机部署网站
徐州做网站公司哪家好虚拟主机部署网站
- 技术栈
- 2026年04月20日
-
徐州专业做网站较好的公司优化网站界面的工具
徐州专业做网站较好的公司优化网站界面的工具
- 技术栈
- 2026年04月20日
-
徐州专业三合一网站开发教育网站制作开发
徐州专业三合一网站开发教育网站制作开发
- 技术栈
- 2026年04月20日
-
许昌大成建设集团网站长沙专业网站建设怎么做
许昌大成建设集团网站长沙专业网站建设怎么做
- 技术栈
- 2026年04月20日
-
许昌旅游网站建设现状外贸营销型网站案例
许昌旅游网站建设现状外贸营销型网站案例
- 技术栈
- 2026年04月20日
-
许昌企业网站建设公司深圳创新创业大赛
许昌企业网站建设公司深圳创新创业大赛
- 技术栈
- 2026年04月20日
