当前位置: 首页 > news >正文

廊坊网站推广的公司猫咪多用户wordpress

廊坊网站推广的公司,猫咪多用户wordpress,学做网页设计,wordpress目录分类在前面章节#xff0c;我们有过使用寄存器去编写字符设备的经历了。这种直接在驱动代码中#xff0c; 通过寄存器映射来对外设进行使用的编程方式#xff0c;从驱动开发者的角度可以说是灾难。 因为每当芯片的寄存器发生了改动#xff0c;那么底层的驱动几乎得重写。 那么…在前面章节我们有过使用寄存器去编写字符设备的经历了。这种直接在驱动代码中 通过寄存器映射来对外设进行使用的编程方式从驱动开发者的角度可以说是灾难。 因为每当芯片的寄存器发生了改动那么底层的驱动几乎得重写。 那么在这个问题上我们更进了一步学会了使用设备树来描述外设的各种信息比如寄存器地址 而不是将寄存器的这些内容放在驱动代码里。 这样即使设备信息修改了我们还是可以通过设备树的接口函数去灵活的获取设备的信息。 极大得提高了驱动的复用能力。 现在我们可以通过在驱动程序代码里使用设备树接口来获取到外设的信息了。 但是在前面的设备树演示中我们还是将寄存器操作具体细节体现在了驱动中比如置位操作。 那么在驱动中有没有更通用的方法可以不涉及到具体的寄存器操作的内容呢 对于有些外设是具备抽象条件的也就是说我们可以将对这些外设的操作统一起来。 比如本章中将为大家介绍的pinctrl子系统和GPIO子系统。 本章将会使用GPIO子系统实现LED驱动GPIO子系统要用到pinctrl子系统所以本章将pinctrl子系统和GPIO子系统放在一块讲解。 1.1. pinctrl子系统 pinctrl子系统主要用于管理芯片的引脚。imx6ull芯片拥有众多的片上外设 大多数外设需要通过芯片的引脚与外部设备器件相连实现相对应的控制例如我们熟悉的I2C、SPI、LCD、USDHC等等。 而我们知道芯片的可用引脚除去电源引脚和特定功能引脚数量是有限的芯片的设计厂商为了提高硬件设计的灵活性 一个芯片引脚往往可以做为多个片上外设的功能引脚以IIC1所对应的引脚为例如下图所示。 I2C1的SCL和SDA的功能引脚不单单只可以使用在I2C上也可以作为多个外设的功能引脚如普通的GPIO引脚串口的接收发送引脚等 在设计硬件时我们可以根据需要灵活的选择其中的一个。设计完硬件后每个引脚的功能就确定下来了假设我们将上面的两个引脚连接 到其他用串口控制的外部设备上那么这两个引脚功能就做为了UART4的接收、发送引脚。在编程过程中无论是裸机还是驱动 一般首先要设置引脚的复用功能并且设置引脚的PAD属性驱动能力、上下拉等等。 在驱动程序中我们需要手动设置每个引脚的复用功能不仅增加了工作量编写的驱动程序不方便移植 可重用性差等。更糟糕的是缺乏对引脚的统一管理容易出现引脚的重复定义。 假设我们在I2C1的驱动中将UART4_RX_DATA引脚和UART4_TX_DATA引脚复用为SCL和SDA 恰好在编写UART4驱动驱动时没有注意到UART4_RX_DATA引脚和UART4_TX_DATA引脚已经被使用 在驱动中又将其初始化为UART4_RX和UART4_TX这样IIC1驱动将不能正常工作并且这种错误很难被发现。 pinctrl子系统是由芯片厂商来实现的,简单来说用于帮助我们管理芯片引脚并自动完成引脚的初始化 而我们要做的只是在设备树中按照规定的格式写出想要的配置参数即可。 1.1.1. pinctrl子系统编写格式以及引脚属性详解 1.1.1.1. iomuxc节点介绍 首先我们在内核源码/arch/arm/boot/dts/imx6ull.dtsi文件中查找iomuxc节点可以看到如下定义 imx6ull.dtsi中iomuxc部分 iomuxc: iomuxc20e0000 {compatible fsl,imx6ul-iomuxc;reg 0x20e0000 0x4000;}; compatible 修饰的是与平台驱动做匹配的名字,这里则是与pinctrl子系统的平台驱动做匹配。 reg 表示的是引脚配置寄存器的基地址。 imx6ull.dtsi这个文件是芯片厂商官方将芯片的通用的部分单独提出来的一些设备树配置。 在iomuxc节点中汇总了所需引脚的配置信息pinctrl子系统存储使用着iomux节点信息。 我们的设备树主要的配置文件在./arch/arm/boot/dts/imx6ull-mmc-npi.dts中 打开imx6ull-mmc-npi.dts在文件中搜索“iomuxc”找到设备树中引用“iomuxc”节点的位置如下所示。 imx6ull-mmc-npi.dts中iomuxc部分内容 iomuxc {pinctrl-names default;pinctrl-0 pinctrl_hog_1;pinctrl_hog_1: hoggrp-1 {fsl,pins MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0x17059 /* SD1 RESET */;};pinctrl_enet1: enet1grp {fsl,pins MX6UL_PAD_ENET1_RX_EN__ENET1_RX_EN 0x1b0b0MX6UL_PAD_ENET1_RX_ER__ENET1_RX_ER 0x1b0b0/*------------以下省略-----------------*/;};pinctrl_enet2: enet2grp {fsl,pins MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0/*------------以下省略-----------------*/;};pinctrl_uart1: uart1grp {fsl,pins MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1;}; 在这里通过“iomuxc”在“iomuxc”节点下追加内容。结合设备树源码介绍如下 第2-3行“pinctrl-names”标识指定PIN的状态列表默认设置为“default”。 “pinctrl-0 pinctrl_hog_1”的意思的在默认设置下将使用pinctrl_hog_1这个设备节点来设置我们的GPIO端口状态 pinctrl_hog_1内容是支持热插拔相关的我们暂时不用理会。一个引脚可能有多种状态以上面串口举例 在正常使用的时候我们将引脚设置为发送引脚、接收引脚而在系统进入休眠模式时 为了节省功耗我们可以将这两个引脚设置为其他模式如设置为GPIO功能并设置为高电平等。如下代码所示。 其余源码都是pinctrl子节点它们都是按照一定的格式规范来编写。 举例说明 iomuxc {pinctrl-names default,sleep,init;pinctrl-0 pinctrl_uart1;pinctrl-1 xxx;pinctrl-2 yyy; ...pinctrl_uart1: uart1grp {fsl,pins MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1;};xxx: xxx_grp {...这里设置将引脚设置为其他模式}yyy: yyy_grp {...这里设置将引脚设置为其他模式}... } 1.1.1.2. pinctrl子节点编写格式 接下来以“pinctrl_uart1”节点源码为例介绍pinctrl子节点格式规范编写 pinctrl子节点格式规范格式框架如下: pinctrl_自定义名字: 自定义名字 {fsl,pins 引脚复用宏定义 PAD引脚属性引脚复用宏定义 PAD引脚属性; }; 这里我们需要知道每个芯片厂商的pinctrl子节点的编写格式并不相同这不属于设备树的规范是芯片厂商自定义的。 如果我们想添加自己的pinctrl节点只要依葫芦画瓢按照上面的格式编写即可。 接下来我们重点讲解上图的标号3处的内容也是我们编写的主要内容–添加引脚配置信息。 1.1.1.3. 引脚配置信息介绍 引脚的配置信息一眼看去由两部分组成一个宏定义和一个16进制数组成。这实际上定义已经配置 控制引脚所需要用到的各个寄存器的地址及应写入寄存器值的信息以上图的第一条配置信息为例说明。 MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1 MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 是定义在“./arch/arm/boot/dts/imx6ul-pinfunc.h”文件内的一个宏定义。 从上图可以看出以“MX6UL_PAD_UART1_TX_DATA__xxx”命名的宏定义共有8个 之前我们讲过引脚复用功能选择寄存器很容易联想到这8个宏就是用来定义“UART1_TX_DATA”引脚的8个复用功能。 宏定义“MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX”将“UART1_TX_DATA”引脚复用为UART1的TX引脚。 每个宏定义后面有5个参数名字依次为 mux_reg、conf_reg、input_reg、mux_mode、input_val。 1 2 mux_reg conf_reg input_reg mux_mode input_val 0x0084 0x0310 0x0000 0x0 0x0如果将宏定义展开则在设备树中每条配置信息实际是6个参数由于第6个参数设置较为复杂需要根据实际需要设置 因此并没有把它放到宏定义里面。以MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX为例宏定义中5个参数参数介绍如下 1. mux_reg 和 mux_mode :mux_reg是引脚复用选择寄存器偏移地址mux_mode是引脚复用选择寄存器模式选择位的值。 UART1_TX引脚复用选择寄存器IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA定义如下所示。 mux_reg 0x0084与IM6ULL用户手册偏移地址一致, mux_mode 0。 设置复用选择寄存器IOMUXC_SW_MUX_CTL_PAD_UART1_TX_DATA[MUX_MODE] 0将其复用为UART1_TX功能。 2. conf_reg 引脚PAD属性控制寄存器偏移地址。与引脚复用选择寄存器不同 引脚属性寄存器应当根据实际需要灵活的配置所以它的值并不包含在宏定义中 它的值是我们上面所说的“第六个”参数。 从上图可以看到conf_reg 0x0310对应UART1_TX引脚的引脚属性寄存器的偏移地址。而这个寄存器包含很多配置项 上图中是部分配置项这些配置项在裸机部分已有详细介绍忘记的朋友可以回去再看下裸机部分详细解释。 3. input_reg 和 input_val input_reg暂且称为输入选择寄存器偏移地址。input_val是输入选择寄存器的值。 这个寄存器只有某些用作输入的引脚才有正如本例所示UART1_TX用作输出所以这两个参数都是零。 “输入选择寄存器”理解稍微有点复杂结合下图介绍如下。 从上图可以看出如果引脚用作输出我们我们只需要配置引脚复用选择寄存器和引脚PAD属性设置寄存器。 如果用作输入时还增加了引脚输入选择寄存器输入选择寄存器的作用也很明显在多个可选输入中选择一个连接到片上外设。 引脚PAD属性值 在pinctrl子系统中一条配置信息由一个宏定义和一个参数组成将宏定义展开就是六个参数。 结合上面分析我们知道这6个参数就是IOMUX相关的三个寄存器偏移地址和寄存器的值(引脚用作输出时实际只有四个有效 输入选择寄存器偏移地址和它的值全为0)至于为什么要将pad属性寄存器的值单独列出前面也说过了pad属性配置选项非常多 配置灵活。在pinctrl子系统中添加的PAD属性值就是引脚PAD属性设置寄存器的值16进制。 有关PAD属性设置内容已经在裸机部分GPIO章节详细介绍,忘记的同学可以回去再回顾下,这里便不再赘述了。 1.1.2. 将RGB灯引脚添加到pinctrl子系统 本小节假设没有看过裸机部分RGB灯章节我们从看原理图开始一步步将RGB灯用到的三个引脚添加到pinctrl子系统中。 1.1.2.1. 查找RGB灯使用的引脚 RGB灯对应的原理图如下所示。 根据网络名在核心板上找到对应的引脚如下。 rgb_led_red: GPIO1_IO04 rgb_led_green: CSI_HSYNC rgb_led_blue: CSI_VSYNC 1.1.2.2. 找到引脚配置宏定义 这些引脚都将被复用为GPIO用作驱动LED灯。首先要在“./arch/arm/boot/dts/imx6ul-pinfunc.h”文件内找到对应的宏定义 以CSI_HSYNC引脚为例在imx6ul-pinfunc.h中直接搜索“CSI_HSYNC”找到如下结果 同一个引脚的可选复用功能是连续排布的我们要将其复用为GPIO所以选择“MX6UL_PAD_CSI_HSYNC__GPIO4_IO20”即可。 其他的两个引脚最终得到的结果如下 CSI_HSYNCMX6UL_PAD_CSI_HSYNC__GPIO4_IO20 CSI_VSYNCMX6UL_PAD_CSI_VSYNC__GPIO4_IO19 1.1.2.3. 设置引脚属性 我们要写入到设备树中的引脚属性实际就是引脚属性设置寄存器的值。引脚属性配置项很多以GPIO1_IO04为例如下所示。 实际编程中我们几乎不会手动设置每一个配置项然后再将其组合成一个16进制数通常情况下我们直接参照官方的设置 设备树中其他的pinctrl子节点的配置就是很好的一个参考如果有需要再对个别参数进行修改。 通常情况下用作GPIO的引脚PAD属性设置为“0x000010B1” 1.1.2.4. 在iomuxc节点中添加pinctrl子节点 添加子节点很简单我们只需要将前面选择好的配置信息按照之前讲解的格式写入到设备树中即可添加完成后如下所示。 iomuxc {pinctrl-names default;pinctrl-0 pinctrl_hog_1;/*----------新添加的内容--------------*/pinctrl_rgb_led:rgb_led{fsl,pins MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0x000010B1MX6UL_PAD_CSI_HSYNC__GPIO4_IO20 0x000010B1MX6UL_PAD_CSI_VSYNC__GPIO4_IO19 0x000010B1; }; 新增的节点名为“rgb_led”名字任意选取长度不要超过32个字符最好能表达出节点的信息。 “pinctrl_rgb_led”节点标签“pinctrl_”是固定的格式后面的内容自定义的我们将通过这个标签引用这个节点。 在添加完pinctrl子节点后系统会根据我们添加的配置信息将引脚初始化为GPIO功能。 到这里关于pinctrl子系统的使用就已经讲解完毕了接下来介绍GPIO子系统相关的内容。 1.2. GPIO子系统 在没有使用GPIO子系统之前如果我们想点亮一个LED首先要得到led相关的配置寄存器再手动地读、改、写这些配置寄存器实现 控制LED的目的。有了GPIO子系统之后这部分工作由GPIO子系统帮我们完成我们只需要调用GPIO子系统提供的API函数即可完成GPIO的 控制动作。 在imx6ull.dtsi文件中的GPIO子节点记录着GPIO控制器的寄存器地址下面我们以GPIO4为例介绍GPIO子节点相关内容: gpio4: gpio20a8000 {compatible fsl,imx6ul-gpio, fsl,imx35-gpio;reg 0x20a8000 0x4000;interrupts GIC_SPI 72 IRQ_TYPE_LEVEL_HIGH,GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH;clocks clks IMX6UL_CLK_GPIO4;gpio-controller;#gpio-cells 2;interrupt-controller;#interrupt-cells 2;gpio-ranges iomuxc 0 94 17, iomuxc 17 117 12; }; compatible 与GPIO子系统的平台驱动做匹配。 reg GPIO寄存器的基地址GPIO4的寄存器组是的映射地址为0x20a8000-0x20ABFFF interrupts 描述中断相关的信息 clocks 初始化GPIO外设时钟信息 gpio-controller 表示gpio4是一个GPIO控制器 #gpio-cells 表示有多少个cells来描述GPIO引脚 interrupt-controller 表示gpio4也是个中断控制器 #interrupt-cells :表示用多少个cells来描述一个中断 gpio-ranges 将gpio编号转换成pin引脚iomuxc 0 94 17表示将gpio4的第0个引脚引脚映射为94 17表示的是引脚的个数。 gpio4这个节点对整个gpio4进行了描述。使用GPIO子系统时需要往设备树中添加设备节点在驱动程序中使用GPIO子系统提供的API 实现控制GPIO的效果。 1.2.1. 在设备树中添加RGB灯的设备树节点 相比之前led灯的设备树节点(没有使用GPIO子系统)这里只需要增加GPIO属性定义基于GPIO子系统的rgb_led设备树节点 添加到“./arch/arm/boot/dts/imx6ull-mmc-npi.dtb”设备树的根节点内。 添加完成后的设备树如下所示。 /*添加rgb_led节点*/ rgb_led{#address-cells 1;#size-cells 1;pinctrl-names default;compatible fire,rgb-led;pinctrl-0 pinctrl_rgb_led;rgb_led_red gpio1 4 GPIO_ACTIVE_LOW;rgb_led_green gpio4 20 GPIO_ACTIVE_LOW;rgb_led_blue gpio4 19 GPIO_ACTIVE_LOW;status okay; }; 第6行设置“compatible”属性值与led的平台驱动做匹配。 第7行指定RGB灯的引脚pinctrl信息上一小节我们定义了pinctrl节点并且标签设置为“pinctrl_rgb_led” 在这里我们引用了这个pinctrl信息。 第8-10行指定引脚使用的哪个GPIO,编写格式如下所示。 标号①设置引脚名字如果使用GPIO子系统提供的API操作GPIO,在驱动程序中会用到这个名字名字是自定义的。 标号②指定GPIO组。 标号③指定GPIO编号。 编号④这是一个宏定义指定有效电平低电平有效选择“GPIO_ACTIVE_LOW”高电平有效选择“GPIO_ACTIVE_HIGH”。 1.2.2. 编译、下载设备树验证修改结果 前两小节我们分别在设备树中将RGB灯使用的引脚添加到pinctrl子系统然后又在设备树中添加了rgb_led设备树节点。 这一小节将会编译、下载修改后的设备树用新的设备树启动系统然后检查是否有rgb_led设备树节点产生。 编译内核时会自动编译设备树我们可以直接重新编译内核这样做的缺点是编译时间会很长。 在内核目录下~/ebf_linux_kernel执行如下命令只编译设备树 命令 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- dtbs 如果执行了“make distclean”清理了内核那么就需要在内核目录下执行如下命令重新配置内核 如果编译设备树出错也可以先清理内核然后执行如下命令尝试重新编译。 命令 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- npi_v7_defconfig make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- dtbs 编译成功后会在“./arch/arm/boot/dts”目录下生成“imx6ull-mmc-npi.dtb”文件将其替换掉板子/usr/lib/linux-image-4.19.35-imx6/ 目录下的imx6ull-mmc-npi.dtb文件并输入 sudo reboot 重启开发板。 #这里操作命令仅作为参考实际根据自己电脑情况进行修改#将生成的设备树拷贝到共享文件夹 cp arch/arm/boot/dts/imx6ull-mmc-npi.dtb /home/Embedfire/wokdfir#挂载nfs共享文件夹(在开发板上) sudo mount -f nfs 192.168.0.231:/home/Embedfire/wokdfir /mnt#复制设备树到共享文件夹(在开发板上) cp /mnt/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/#重启开发板 sudo reboot 使用新的设备树重新启动之后正常情况下会在开发板的“/proc/device-tree”目录下生成“rgb_led”设备树节点。如下所示。 1.2.3. GPIO子系统常用API函数讲解 之前两小节我们修改设备树并编译、下载到开发板。设备树部分已经完成了这里介绍GPIO子系统常用的几个API函数 然后就可以使用GPIO子系统编写RGB驱动了。 1. 获取GPIO编号函数of_get_named_gpio GPIO子系统大多数API函数会用到GPIO编号。GPIO编号可以通过of_get_named_gpio函数从设备树中获取。 of_get_named_gpio函数内核源码include/linux/of_gpio.h static inline int of_get_named_gpio(struct device_node *np, const char *propname, int index)参数 np 指定设备节点。 propname GPIO属性名与设备树中定义的属性名对应。 index 引脚索引值在设备树中一条引脚属性可以包含多个引脚该参数用于指定获取那个引脚。 返回值 成功 获取的GPIO编号这里的GPIO编号是根据引脚属性生成的一个非负整数 失败: 返回负数。 2. GPIO申请函数gpio_request gpio_request函数内核源码drivers/gpio/gpiolib-legacy.c static inline int gpio_request(unsigned gpio, const char *label); 参数 gpio: 要申请的GPIO编号该值是函数of_get_named_gpio的返回值。 label: 引脚名字相当于为申请得到的引脚取了个别名。 返回值 成功: 返回0 失败: 返回负数。 3. GPIO释放函数 gpio_free函数内核源码drivers/gpio/gpiolib-legacy.c static inline void gpio_free(unsigned gpio); gpio_free函数与gpio_request是一对相反的函数一个申请一个释放。一个GPIO只能被申请一次 当不再使用某一个引脚时记得将其释放掉。 4. GPIO输出设置函数gpio_direction_output 用于将引脚设置为输出模式。 gpio_direction_output函数内核源码include/asm-generic/gpio.h static inline int gpio_direction_output(unsigned gpio , int value); 函数参数 gpio: 要设置的GPIO的编号。 value: 输出值1表示高电平。0表示低电平。 返回值 成功: 返回0 失败: 返回负数。 5. GPIO输入设置函数gpio_direction_input 用于将引脚设置为输入模式。 gpio_direction_input函数内核源码include/asm-generic/gpio.h static inline int gpio_direction_input(unsigned gpio); 6. 获取GPIO引脚值函数gpio_get_value 用于获取引脚的当前状态。无论引脚被设置为输出或者输入都可以用该函数获取引脚的当前状态。 gpio_get_value函数内核源码include/asm-generic/gpio.h static inline int gpio_get_value(unsigned gpio); 7. 设置GPIO输出值gpio_set_value 该函数只用于那些设置为输出模式的GPIO. gpio_direction_output函数内核源码include/asm-generic/gpio.h: static inline int gpio_direction_output(unsigned gpio, int value); 函数参数 gpio 设置的GPIO的编号。 value 设置的输出值为1输出高电平为0输出低电平。 返回值 成功: 返回0 失败: 返回负数 根据上面这些函数我们就可以在驱动程序中控制IO口了。 1.3. 实验说明与代码讲解 硬件介绍 本节实验使用到 EBF6ULL-PRO 开发板上的 RGB 彩灯 硬件原理图分析 参考”字符设备驱动–点亮LED灯”章节 1.3.1. 实验代码讲解 本章的示例代码目录为linux_driver/gpio_subsystem_rgb_led 程序包含两个C语言文件一个是驱动程序驱动程序在平台总线基础上编写。 另一个是一个简单的测试程序用于测试驱动是否正常。 1.3.1.1. 驱动程序讲解 驱动程序大致分为三个部分第一部分编写平台设备驱动的入口和出口函数。第二部分编写平台设备的.probe函数, 在probe函数中实现字符设备的注册和RGB灯的初始化。第三部分编写字符设备函数集实现open和write函数。 平台驱动入口和出口函数实现 平台驱动框架: /*------------------第一部分----------------*/ static const struct of_device_id rgb_led[] { { .compatible fire,rgb-led},{ /* sentinel */ } };/*定义平台驱动结构体*/ struct platform_driver led_platform_driver {.probe led_probe,.driver {.name rgb-leds-platform,.owner THIS_MODULE,.of_match_table rgb_led,} };/*------------------第二部分----------------*/ /*驱动初始化函数*/ static int __init led_platform_driver_init(void) {int error;error platform_driver_register(led_platform_driver);printk(KERN_EMERG \tDriverState %d\n,error);return 0; }/*------------------第三部分----------------*/ /*驱动注销函数*/ static void __exit led_platform_driver_exit(void) {printk(KERN_EMERG platform_driver_exit!\n);platform_driver_unregister(led_platform_driver); }module_init(led_platform_driver_init); module_exit(led_platform_driver_exit);MODULE_LICENSE(GPL); 第2-15行为代码的第一部分仅实现.probe函数和.driver当驱动和设备匹配成功后会执行该函数 这个函数的函数实现我们在后面介绍。.driver描述这个驱动的属性包括.name驱动的名字.owner驱动的所有者, .of_match_table驱动匹配表用于匹配驱动和设备。驱动设备匹配表定义为“rgb_led”在这个表里只有一个匹配值 “.compatible “fire,rgb-led” ”这个值要与我们在设备树中rgb_led设备树节点的“compatible”属性相同。 第17-40行第二、三部分是平台设备的入口和出口函数函数实现很简单在入口函数中注册平台驱动在出口函数中注销平台驱动。 平台驱动.probe函数实现 当驱动和设备匹配后首先会probe函数我们在probe函数中实现RGB的初始化、注册一个字符设备。 后面将会在字符设备操作函数open、write中实现对RGB等的控制。函数源码如下所示。 static int led_probe(struct platform_device *pdv) {unsigned int register_data 0; //用于保存读取得到的寄存器值int ret 0; //用于保存申请设备号的结果printk(KERN_EMERG \t match successed \n);/*------------------第一部分---------------*//*获取RGB的设备树节点*/rgb_led_device_node of_find_node_by_path(/rgb_led);if(rgb_led_device_node NULL){printk(KERN_EMERG \t get rgb_led failed! \n);}/*------------------第二部分---------------*/rgb_led_red of_get_named_gpio(rgb_led_device_node, rgb_led_red, 0);rgb_led_green of_get_named_gpio(rgb_led_device_node, rgb_led_green, 0);rgb_led_blue of_get_named_gpio(rgb_led_device_node, rgb_led_blue, 0);printk(rgb_led_red %d,\n rgb_led_green %d,\n rgb_led_blue %d,\n, rgb_led_red,\rgb_led_green,rgb_led_blue);/*------------------第三部分---------------*/gpio_direction_output(rgb_led_red, 1);gpio_direction_output(rgb_led_green, 1);gpio_direction_output(rgb_led_blue, 1);/*------------------第四部分---------------*//*---------------------注册 字符设备部分-----------------*///第一步//采用动态分配的方式获取设备编号次设备号为0//设备名称为rgb-leds可通过命令cat /proc/devices查看//DEV_CNT为1当前只申请一个设备编号ret alloc_chrdev_region(led_devno, 0, DEV_CNT, DEV_NAME);if(ret 0){printk(fail to alloc led_devno\n);goto alloc_err;}//第二步//关联字符设备结构体cdev与文件操作结构体file_operationsled_chr_dev.owner THIS_MODULE;cdev_init(led_chr_dev, led_chr_dev_fops);//第三步//添加设备至cdev_map散列表中ret cdev_add(led_chr_dev, led_devno, DEV_CNT);if(ret 0){printk(fail to add cdev\n);goto add_err;}//第四步/*创建类 */class_led class_create(THIS_MODULE, DEV_NAME);/*创建设备*/device device_create(class_led, NULL, led_devno, NULL, DEV_NAME);return 0;add_err://添加设备失败时需要注销设备号unregister_chrdev_region(led_devno, DEV_CNT);printk(\n error! \n); alloc_err:return -1; } 实现字符设备函数 字符设备函数我们只需要实现open函数和write函数。函数源码如下。 /*------------------第一部分---------------*/ /*字符设备操作函数集*/ static struct file_operations led_chr_dev_fops {.owner THIS_MODULE,.open led_chr_dev_open,.write led_chr_dev_write, };/*------------------第二部分---------------*/ /*字符设备操作函数集open函数*/ static int led_chr_dev_open(struct inode *inode, struct file *filp) {printk(\n open form driver \n);return 0; }/*------------------第三部分---------------*/ /*字符设备操作函数集write函数*/ static ssize_t led_chr_dev_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {unsigned char write_data; //用于保存接收到的数据int error copy_from_user(write_data, buf, cnt);if(error 0) {return -1;}/*设置 GPIO1_04 输出电平*/if(write_data 0x04){gpio_direction_output(rgb_led_red, 0); // GPIO1_04引脚输出低电平红灯亮}else{gpio_direction_output(rgb_led_red, 1); // GPIO1_04引脚输出高电平红灯灭}/*设置 GPIO4_20 输出电平*/if(write_data 0x02){gpio_direction_output(rgb_led_green, 0); // GPIO4_20引脚输出低电平绿灯亮}else{gpio_direction_output(rgb_led_green, 1); // GPIO4_20引脚输出高电平绿灯灭}/*设置 GPIO4_19 输出电平*/if(write_data 0x01){gpio_direction_output(rgb_led_blue, 0); // GPIO4_19引脚输出低电平蓝灯亮}else{gpio_direction_output(rgb_led_blue, 1); // GPIO4_19引脚输出高电平蓝灯灭}return 0; } 代码3-8行:定义字符设备操作函数集这里主要实现open和write函数即可。 代码12-16行实现open函数在平台驱动的prob函数中已经初始化了GPIO,这里不用做任何操作 代码20-60行write函数实现也很简单首先使用“copy_from_user”函数将来自应用层的数据“拷贝”内核层。 得到命令后就依次检查后三位根据命令值使用“gpio_direction_output”函数控制RGB灯的亮灭。 1.3.1.2. 应用程序讲解 应用程序编写比较简单我们只需要打开设备节点文件写入命令然后关闭设备节点文件即可。源码如下所示。 int main(int argc, char *argv[]) {/*判断输入的命令是否合法*/if(argc ! 2){printf( commend error ! \n);return -1;}/*打开文件*/int fd open(/dev/rgb-leds, O_RDWR);if(fd 0){printf(open file : %s failed !\n, argv[0]);return -1;}unsigned char commend atoi(argv[1]); //将受到的命令值转化为数字;/*判断命令的有效性*//*写入命令*/int error write(fd,commend,sizeof(commend));if(error 0){printf(write file error! \n);close(fd);/*判断是否关闭成功*/}/*关闭文件*/error close(fd);if(error 0){printf(close file error! \n);}return 0; } 结合代码各部分说明如下 代码4-8行判断命令是否有效。再运行应用程序时我们要传递一个控制命令所以参数长度是2。 代码11-16行打开设备文件。参数“/dev/rgb-leds”用于指定设备节点文件设备节点文件名是在驱动程序中设置的 这里保证与驱动一致即可。 代码18-35行由于从main函数中获取的参数是字符串这里首先要将其转化为数字。最后条用write函数写入命令然后关闭文件即可。 1.3.2. 实验准备 1.3.2.1. Makefile修改说明 修改Makefile并编译生成驱动程序 Makefile程序并没有大的变化修改后的Makefile如下所示。 Makefile文件 KERNEL_DIR../ebf_linux_kernel/build_image/buildARCHarm CROSS_COMPILEarm-linux-gnueabihf- export ARCH CROSS_COMPILEobj-m : rgb-leds.oapp_in rgb_leds_app.c app_out rgb_leds_appall:$(MAKE) -C $(KERNEL_DIR) M$(CURDIR) modules$(CROSS_COMPILE)gcc -o $(app_out) $(app_in).PHONY:clean clean:$(MAKE) -C $(KERNEL_DIR) M$(CURDIR) cleanrm $(app_out) 代码第1行变量“KERNEL_DIR”保存的是内核所在路径这个需要根据自己内核所在位置设定。 代码第7行“obj-m : rgb-leds.o”中的“rgb-leds.o”要与驱动源码名对应。Makefiel 修改完成后执行如下命令编译驱动。 代码第9行“rgb_leds_app.c” 是需要编译的应用程序。 代码第10行“rgb_leds_app” 是编译应用程序后输出的应用程序可以执行文件。 命令 make 正常情况下会在当前目录生成.ko驱动文件和rgb_leds_app应用程序可执行文件。 1.3.3. 下载验证 前两小节我们已经编译出了.ko驱动和应用程序将驱动程序和应用程序添加到开发板中 执行如下命令加载驱动 命令 insmod ./rgb-leds.ko 正常情况下输出结果如下所示。 在驱动程序中我们在.probe函数中注册字符设备并创建了设备文件设备和驱动匹配成功后.probe函数已经执行 所以正常情况下在“/dev/”目录下已经生成了“rgb-leds”设备节点如下所示。 驱动加载成功后直接运行应用程序如下所示。 ./rgb_leds_app 命令 执行结果如下 命令是一个“unsigned char”型数据只有后三位有效每一位代表一个灯从高到低依次代表红、绿、蓝1表示亮0表示灭。 例如命令4 则亮红灯命令7则三个灯全亮。
http://www.dnsts.com.cn/news/71821.html

相关文章:

  • 重庆市建设工程施工安全管理总站网站做seo需要些什么软件
  • 网加速器网络优化工程师是干嘛的
  • 无锡seo网站管理成都网站建设定
  • 跨境购物网站建设sae wordpress 更新
  • 做折扣的网站公司简介模板怎么做
  • 山西企业建站系统平台开源零代码平台
  • 沈阳网站建设思路深圳网页设计公司建设
  • 怎么制作网站的网页设计soho 网站建设
  • 乔智云智能建站江宁住房和城乡建设局网站
  • 广州化妆品网站制作如何引用404做网站
  • 淄博网站开发选网泰wordpress底部添加工信部链接
  • 长春火车站照片建设网站后需要什么知识
  • 做网站用的图片c 做网站怎么发布
  • 外贸网站建设工作室厦门网站制作系统
  • 秦皇岛做网站多少钱jsp如何进行购物网站开发
  • 上海建设工程交易中心网站软件正版化情况及网站建设情况
  • 衡水网站建设一多软件郴州信息港网站
  • php网站优点网络培训班心得体会800字
  • 电脑用虚拟机做网站兰州建设网站
  • 色彩搭配的网站绍兴网站制作套餐
  • 做平面设计在什么网站能挣钱网站正在建设中 英语
  • 做企业网站服务器在国外内蒙古旅游攻略
  • 福建公司网站开发网站建设服务器和空间费
  • 做网站维护要多少钱一年可以在自己的电脑上做网站吗
  • 网站建设开发成本白银市建设局网站
  • 设备租赁业务网站如何做中铁建设门户网登录咋进不去了
  • ps做 网站标准尺寸wordpress菜单分列
  • 药物研发网站怎么做品牌建设及龙头企业
  • 网站 关键词库单页面网站制作技术
  • 怎样在百度能搜到自己的网站网站建设公司美工如何找