如何建设社区网站首页,游网站建设方案内容,搜狗seo刷排名软件,深圳品牌模板网站建设目录
一、pinctrl子系统
1.1 pinctrl子系统编写格式以及引脚属性介绍
1.1.1 iomux节点介绍
1.1.2 pinctrl子节点编写格式
1.1.3 引脚配置信息介绍
1.2 将RGB灯引脚添加到pinctrl子系统
1.2.1 查找RGB灯使用的引脚
1.2.2找到引脚宏定义
1.2.3 设置引脚属性
1.2.4 在…目录
一、pinctrl子系统
1.1 pinctrl子系统编写格式以及引脚属性介绍
1.1.1 iomux节点介绍
1.1.2 pinctrl子节点编写格式
1.1.3 引脚配置信息介绍
1.2 将RGB灯引脚添加到pinctrl子系统
1.2.1 查找RGB灯使用的引脚
1.2.2找到引脚宏定义
1.2.3 设置引脚属性
1.2.4 在iomuxc节点中添加pinctrl子节点
二、GPIO子系统
2.1 在设备树种添加RGB灯的设备树节点
2.2 编译、下载设备树验证修改结果
2.3 GPIO子系统常用的API函数
三、实验
3.1 实验代码
3.1.1 驱动程序
3.1.2 应用程序
3.2 实验准备
3.2.1 Makefile修改
3.3 下载验证 一、pinctrl子系统 pinctrl子系统用于管理芯片的引脚。imx6ull芯片上拥有众多的片上外设大多数外设需要通过芯片的引脚与外部涉笔器件相连实现相对应的控制例如I2C、SPI、LCD、USDHC等等。而芯片的可用引脚除去电源引脚和特定功能引脚数量是有限的芯片的设计厂商为了提高硬件设计的灵活性一个芯片引脚往往可以作为多个片上外设的功能引脚。 在驱动程序中需要手动的设置每个引脚的复用功能不仅增加工作量编写的驱动程序不方便移植可重用性差。更糟糕的时缺乏对引脚的统一管理容易出现引脚的重复定义。且这种重定义引起的错误是很难被发现的。 pinctrl子系统时由芯片厂商实现用于帮助管理芯片引脚并自动完成引脚的初始化所以编写驱动代码的时候知识在设备树中按照规定的格式写出现想要的配置参数即可。
1.1 pinctrl子系统编写格式以及引脚属性介绍
1.1.1 iomux节点介绍
文件位置/ebf_linux_kernel/arch/arm/boot/dts/imx6ull/dtsi文件中查找iomux节点可以看到如下定义
iomuxc: iomuxc20e0000 {compatible fsl,imx6ul-iomuxc;reg 0x20e0000 0x4000;};
compatiable修饰的是与平台驱动做匹配的名字这里则是pinctrl子系统的平台驱动做匹配。reg表示的是引脚配置寄存器的基地址。
imx6ull.dtsi这个文件是芯片厂商官方将芯片的通用部分单独提出来的一些设备树配置。在iomux节点中汇总了所需要引脚的配置信息pinctrl子系统存储使用者iomux节点信息。
设备树主要的配置文件主要在/arch/arm/boot/dts/imx6ull-mmc-npi.dts中打开imx6ull-mmc-npi.dts在文件中搜索“iomux”找到设备树中引用“iomux”节点的位置如下所示。
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 0x1b0b0MX6UL_PAD_ENET1_RX_DATA0__ENET1_RDATA00 0x1b0b0MX6UL_PAD_ENET1_RX_DATA1__ENET1_RDATA01 0x1b0b0MX6UL_PAD_ENET1_TX_EN__ENET1_TX_EN 0x1b0b0MX6UL_PAD_ENET1_TX_DATA0__ENET1_TDATA00 0x1b0b0MX6UL_PAD_ENET1_TX_DATA1__ENET1_TDATA01 0x1b0b0MX6UL_PAD_ENET1_TX_CLK__ENET1_REF_CLK1 0x4001b031;};pinctrl_enet2: enet2grp {fsl,pins MX6UL_PAD_GPIO1_IO07__ENET2_MDC 0x1b0b0MX6UL_PAD_GPIO1_IO06__ENET2_MDIO 0x1b0b0MX6UL_PAD_ENET2_RX_EN__ENET2_RX_EN 0x1b0b0MX6UL_PAD_ENET2_RX_ER__ENET2_RX_ER 0x1b0b0MX6UL_PAD_ENET2_RX_DATA0__ENET2_RDATA00 0x1b0b0MX6UL_PAD_ENET2_RX_DATA1__ENET2_RDATA01 0x1b0b0MX6UL_PAD_ENET2_TX_EN__ENET2_TX_EN 0x1b0b0MX6UL_PAD_ENET2_TX_DATA0__ENET2_TDATA00 0x1b0b0MX6UL_PAD_ENET2_TX_DATA1__ENET2_TDATA01 0x1b0b0MX6UL_PAD_ENET2_TX_CLK__ENET2_REF_CLK2 0x4001b031;};pinctrl_uart1: uart1grp {fsl,pins MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1;};
/*--------------部分省略--------------------------------*/
第2-3行“pinctrl-names”标识指定PIN的状态列表默认设置为“default”。“pinctrl-0pinctrl_hog_1”的意思的是在默认状态下将使用pinctrl_hog_1这个设备点来设置GPIO状态。一个引脚可能有多种状态以串口为例在正常使用的时候将引脚设置为发送引脚、接收引脚而在系统进入休眠模式下为了节省功耗可以将这两个引脚设置为其他模式。其余源码都是pinctrl的子节点它们都是按照一定的格式来编写。
iomuxc {pinctrl-names default,sleep,init;pinctrl-0 pinctrl_uart1;pinctrl-1xxx;pinctrl-2yyy;/*-----------------------省略--------------------------------*/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{...这里设置将引脚设置为其他模式}...
}
pinctrl-names:定义引脚状态pinctrl-0定义第0种状态需要使用到的引脚配置可饮用其他节点表示pinctrl-1定义第1种状态需要使用到的引脚配置。pinctrl-2定义第2种状态需要使用到的引脚配置。
1.1.2 pinctrl子节点编写格式
以“pinctrl-uart1”节点源码为例介绍pinctrl子节点格式规范编写 pinctrl子节点格式规范格式框架如下
pinctrl_自定义名字: 自定义名字 {fsl,pins 引脚复用宏定义 PAD引脚属性引脚复用宏定义 PAD引脚属性;};
这里需要注意的是每个芯片厂商的pinctrl子节点的编写格式并不相同这里不属于设备树的规范是芯片厂商自定义的。如果想添加自己的pinctrl子节点只需要照着上面的格式编写即可。
1.1.3 引脚配置信息介绍
即上图中标号3处的内容也是编写的主要内容-提娜佳引脚配置信息。引脚配置信息有两部分组成一个宏定义和一个16进制数组成。这实际上定义已经配置控制引脚所需要用到的各个寄存器及应写入寄存器的i西南西以上面第一条配置信息为例。
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个复用功能。每个宏定义后面有5个参数名词依次是mug_reg、conf_reg、input_reg、mux_mod、input_val。
mug_reg conf_reg input_reg mux_mod input_val
0x0084 0x0310 0x0000 0x0 0x0
如果将宏定义展开则在设备树中每条配置信息实际是6个参数由于第6个参数设置比较复杂需要根据实际需要设置因此并没有把它放到宏定义里面。以MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 为例宏定义里面的5个参数介绍如下
1、 mux_reg和mux_modemux_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、config_reg引脚PAD属性控制控制寄存器偏移地址。与引脚复用选择寄存器不同引脚属性寄存器应当根据实际需要灵活的配置所以它的值不包含在宏定义中。 3、input_reg和input_valinput_reg暂且称为输入选择寄存器的偏移地址input_val是输入选择寄存器的值。这个寄存器只有某些用作输入的引脚才有正如本例所示UART1_TX用作输出所以这两个参数都是0。
1.2 将RGB灯引脚添加到pinctrl子系统
1.2.1 查找RGB灯使用的引脚 1.2.2找到引脚宏定义
这些引脚都将被复用为GPIO用作驱动LED灯。首先要在“./arch/arm/boot/dts/imx6ul-pinfunc.h”文件内找到对应的宏定义以CSI_HSYNC引脚为例在imx6ul-pinfunc.h中直接搜索“CSI_HSYNC”找到如下结果 同一个引脚的可选复用功能是连续排布的要将其复用为GPIO所以选择“MUX6UL_PAD_CSI_HSYNC_GPIO4_IO20”即可。
1.2.3 设置引脚属性
要写入到设备树中的引脚属性就是引脚属性设置寄存器的值引脚属性配置项很多从GPIO1_IO04为例如下所示。 实际编程中很少手动设置每一个配置项然后将其再组合成一个16进制数通常情况下按照官方的设置如果有需要在对个别参数进行修改。通常情况下用作GOIO的引脚的PAD引脚属性设置为“0x000010B1”。
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子系统相关的内容。
二、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;
};
compatiable与GPIO子系统地平台驱动做匹配regGPIO寄存器地基地址GPIO4地寄存器组地映射地址是0x20a8000~0x20ABFFFinterrupts描述中断相关地信息clocks初始化GPIO外设时钟信息gpio-controller表示gpio4是一个GPIO控制器#gpio-cells表示有多少个cells来描述GPIO引脚interrupt-controller表示gpio4也是一个中断控制器#nterrupt-cells表示用多少个cells来描述一个中断gpio-ranges将gpio编号转化为pin引脚iomux 0 94 17,表示将gpio4的第0个引脚映射为9717表示的是引脚的个数。
gpio4这个节点对整个gpio4进行了描述。使用GPIO子系统时需要往设备树中添加很多设备节点在驱动程序中使用GPIO子系统提供的API实现GPIO的效果。
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,reg_led;pinctrl-0 pinctrl_rgb_led;regb_led_red gpio1 4 GPIO_ACTIVE_LOW; regb_led_green gpio4 20 GPIO_ACTIVE_LOW;regb_led_blue gpio4 19 GPIO_ACTIVE_LOW;status okay;};
第6行设置“compatiable”属性值与led的平台驱动做匹配。第7行指定RGB灯的引脚pinctrl信息上一小节定义了pinctrl节点并且标签设置为“pinctrl_rgb_led”,在这里引用或者pinctrl信息。第8-10行指定引脚使用的哪个GPIO编写格式如下标号1设置引脚名字如果使用GPIO子系统提供的API操作GPIO在驱动程序种会用到这个名字名字是自定义的。标号2指定GPIO组标号3指定GPIO编号标号4这是一个宏定义指定有效电平低电平有效选择“GPIO_ACTIVE_LOW”高电平有效选择“GPIO_ACTIVE_HIGH”
2.2 编译、下载设备树验证修改结果
编译内核时会自动编译设备树我们可以重新编译内核这样做的缺点是编译时间过长在内核目录下执行如下命令只编译设备树 make ARCHarm -j4 CROSS_COMPILEarm-linux-gnueabihf- dtbs 如果执行了“make distclean”清理了内核那么就需要在内核目录下执行如下命令重新配置内核如果编译设备树出错也可以先清理内核然后执行如下命令尝试重新编译
命令 make ARCHarm CROSS_COMPILEarm-linux-gnueabihf- npi_v7_defconfig make ARCHarm -j4 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重启开发板。
重启之后正常情况下会在开发板的“/proc/device-tree”目录下生成“rgb_led”设备树节点如下所示 2.3 GPIO子系统常用的API函数
1、获取 GPIO 编号函数 of_get_named_gpio
GPIO 子系统大多数 API 函数会用到 GPIO 编号。GPIO 编号可以通过 of_get_named_gpio 函数从设备树中获取。 文件位置内核源码/include/linux/of_gpio.hstatic inline int of_get_gpio(struct device_node *np, int index)
{return of_get_gpio_flags(np, index, NULL);
} 参数 np指定设备节点。propnameGPIO属性名与设备树中定义的属性名对应index引脚索引值在设备树中一条引脚属性可以包含很多个引脚该参数用于指定获取哪个引脚返回值 成功获取的GPIO编号这里的GPIO编号是根据引脚属性生成一个非负整数失败返回负数。2、GPIO申请函数gpio_request 文件位置内核源码/drivers/gpio/gpiolib-legacy.c int gpio_request(unsigned gpio, const char *label) 参数 gpio要申请的GPIO编号该值是函数of_get_named_gpio的返回值label引脚名子相当于为申请得到的引脚取了个别名。返回值 成功返回0失败返回负数3、GPIO释放函数 void gpio_free(unsigned gpio) gpio_free函数与gpio_request是一对相反的函数一个申请一个释放。一个GPIO只能申请一次当不再使用某一个引脚时记得将其释放掉。 参数 gpio要释放的GPIO编号返回值 无。 4、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失败返回负数5、GPIO输入设置函数gpio_direction_input 用于将引脚设置为输入模式。 文件位置内核源码/include/asmgeneric/gpio.h static inline int gpio_direction_input(unsigned gpio) 函数参数 gpio要设置的GPIO的编号返回值 成功返回0失败返回负数6、获取GPIO引脚值函数gpio_get_value 用于获取引脚的当前状态。无论引脚被设置为输出或者输入都可以用该函数获取引脚的当前状态。 文件位置内核源码/include/asmgeneric/gpio.h static inline int gpio_get_value_cansleep(unsigned gpio) 函数参数 gpio要获取的GPIO的编号返回值 成功获取得到的引脚状态‘失败返回负数7、设置GPIO输出值goio_set_value 该函数只用于那些设置为输出模式的GPIO 文件位置内核源码/include/asmgeneric/gpio.h static inline int gpio_direction_output(unsigned gpio, int value) 函数参数 gpio设置的GPIO的编号value设置的输出值为1输出高电平为0输出低电平返回值 成功返回0失败返回负数根据上面这些函数就可以在驱动程序中控制GPIO了。 三、实验 3.1 实验代码 程序包含两个C语言文件一个是驱动程序驱动程序在平台总线基础上编写。另一个是测试程序用于测试驱动是否正常。 3.1.1 驱动程序 驱动程序大致分为3个部分第一部分编写平台设备驱动的入口和出口函数。第二部分编写平台设备的.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,.remove led_remove,.driver {.name rgb-leds-platform,.owner THIS_MODULE,.of_match_table rgb_led,}
};/*-------------------------第二部分---------------------------*//*
*驱动初始化函数
*/
static int __init led_platform_driver_init(void)
{int DriverState;DriverState platform_driver_register(led_platform_driver);printk(KERN_EMERG \tDriverState is %d\n,DriverState);return 0;
}/*-------------------------第三部分---------------------------*/
/*
*驱动注销函数
*/
static void __exit led_platform_driver_exit(void)
{printk(KERN_EMERG HELLO WORLD exit!\n);platform_driver_unregister(led_platform_driver);
}module_init(led_platform_driver_init);
module_exit(led_platform_driver_exit);MODULE_LICENSE(GPL);第一部分仅实现.probe函数和.driver当驱动和设备匹配成功后会执行该函数这个函数的实现后面介绍。.driver描述这个驱动的属性包括.name驱动的名字.owner驱动的所有者.of_match_table驱动匹配表用于匹配驱动和设备。驱动设备匹配表定义为“rgb_led”在这个表只有一个匹配值为“.compatible “fire,rgb-led””这个值要与我们在设备树节点的“compatible”属性相同。第二、三部分是平台设备的入口和出口函数函数实现即在入口函数注册平台驱动在出口函数中注销平台驱动。
平台驱动.probe函数实现
当驱动和设备匹配后首先会执行probe函数我们在probe函数中实现RGB的初始化、注册一个字符设备。后面将会在字符设备操作函数open、write中实现对RGB灯的控制。函数源码如下。
*----------------平台驱动函数集-----------------*/
static int led_probe(struct platform_device *pdv)
{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;}第一部分使用of_find_node_by_path函数找到并获取rgb_led在设备树种的设备节点。参数“/rgb_led”是要获取的设备树节点在设备树中的路径如果要获取的节点嵌套在其他子节点中需要写出节点所在的完美路径。第二部分使用函数of_get_named_gpio函数获取GPIO号读取成功则返回读取得到的GPIO号。“rgb_led_red”指定GPIO的名字这个参数要与rgb_led设备树节点中GPIO属性名对应参数“0”指定引脚索引设备树中一条属性只定义一个引脚只有一个所以设置为0.第三部分将GPIO设置为输出模式默认输出电平为高电平。第四部分字符设备相关内容。
字符设备函数
字符设备函数只需要实现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;
}
第一部分定义字符设备操作函数集这里主要实现open函数和write函数即可第二部分实现open函数在平台驱动的probe函数中已经初始化了GPIO。第三部分实现write函数首先使用“copy_from_user”函数将来自应用层的数据“拷贝”打破内核层。得到命令后依此检查后三位根据命令值使用“gpio_direction_output”函数控制RGB灯的亮灭。
3.1.2 应用程序
应用程序则只需要打开设备节点文件写入命令后然后关闭设备节点文件即可。
int main(int argc, char *argv[])
{printf(led_tiny test\n);
/*----------------第一部分------------------------------------------------*//*判断输入的命令是否合法*/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;
} 第一部分判断命令是否有效。再运行应用程序时要传递一个控制命令所以参数长度是2.第二部分打开设备文件。参数“/dev/rgb-leds”用于指定设备节点文件设备节点文件名是在驱动程序中设置的这里保证与驱动一致即可。第三部分由于从main函数中获取的参数是字符串这里首先要将其转化为数字。最后用write函数写入命令后然后关闭文件即可。
3.2 实验准备
3.2.1 Makefile修改
修改Makefile并编译生成驱动程序
KERNEL_DIR/home/geralt/linux_driver/kernel/ebf_linux_kernel_6ull_depth1/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)
第一行变量“KERNEL_DIR”保存的是内核所在路径这个需要根据自己内核所在位置设定第七行“obj-m:rhb-led.o”中的“rgb-led.o”要与驱动源码名对应。Makefile文件修改完执行如下命令编译驱动。第9行“rgb_leds_app.c是需要编译的应用程序。第10行“rgb_leds_app”是编译应用程序后输出的应用程序可执行文件。
命令 make 正常情况下会在当前目录生成.ko驱动文件和rgb_leds_app应用程序可执行文件。
3.3 下载验证
1将前面编译出的.ko驱动和应用程序添加到开发板中。 2执行如下命令加载驱动
命令 insmod ./rgb-leds.ko 3在驱动程序中由于在.probe函数中注册字符设备并创建了设备文件设备和驱动匹配成功后.probe函数执行所以在/dev目录下已经生成了rgb-leds设备节点如下所示。 注意这里没有生成设备节点需要取/boot/uEnv.txtx 取消led的设备树插件
驱动加载后直接运行应用程序如下所示。
命令 ./rgb_leds_app 命令 执行结果如下 命令是一个“unsigned char”型数据只有后三位有效每一位代表一个灯从高到低依次是红、绿、蓝1代表亮0代表灭。 文章转载自: http://www.morning.gbsfs.com.gov.cn.gbsfs.com http://www.morning.azxey.cn.gov.cn.azxey.cn http://www.morning.zyslyq.cn.gov.cn.zyslyq.cn http://www.morning.sjwzl.cn.gov.cn.sjwzl.cn http://www.morning.ggcjf.cn.gov.cn.ggcjf.cn http://www.morning.xrhst.cn.gov.cn.xrhst.cn http://www.morning.fqlxg.cn.gov.cn.fqlxg.cn http://www.morning.jkcpl.cn.gov.cn.jkcpl.cn http://www.morning.rymd.cn.gov.cn.rymd.cn http://www.morning.qrmry.cn.gov.cn.qrmry.cn http://www.morning.brbmf.cn.gov.cn.brbmf.cn http://www.morning.qmncj.cn.gov.cn.qmncj.cn http://www.morning.xtqr.cn.gov.cn.xtqr.cn http://www.morning.nlglm.cn.gov.cn.nlglm.cn http://www.morning.nrwr.cn.gov.cn.nrwr.cn http://www.morning.lqytk.cn.gov.cn.lqytk.cn http://www.morning.nrjr.cn.gov.cn.nrjr.cn http://www.morning.hhfwj.cn.gov.cn.hhfwj.cn http://www.morning.nsmyj.cn.gov.cn.nsmyj.cn http://www.morning.zcwzl.cn.gov.cn.zcwzl.cn http://www.morning.bfbl.cn.gov.cn.bfbl.cn http://www.morning.czxrg.cn.gov.cn.czxrg.cn http://www.morning.ltpph.cn.gov.cn.ltpph.cn http://www.morning.blfgh.cn.gov.cn.blfgh.cn http://www.morning.sblgt.cn.gov.cn.sblgt.cn http://www.morning.hyhzt.cn.gov.cn.hyhzt.cn http://www.morning.pqktp.cn.gov.cn.pqktp.cn http://www.morning.pzbjy.cn.gov.cn.pzbjy.cn http://www.morning.tpmnq.cn.gov.cn.tpmnq.cn http://www.morning.cjqcx.cn.gov.cn.cjqcx.cn http://www.morning.lwzgn.cn.gov.cn.lwzgn.cn http://www.morning.dgsr.cn.gov.cn.dgsr.cn http://www.morning.tqbqb.cn.gov.cn.tqbqb.cn http://www.morning.prgnp.cn.gov.cn.prgnp.cn http://www.morning.flfdm.cn.gov.cn.flfdm.cn http://www.morning.bfhrj.cn.gov.cn.bfhrj.cn http://www.morning.dqxnd.cn.gov.cn.dqxnd.cn http://www.morning.bjndc.com.gov.cn.bjndc.com http://www.morning.bpmfl.cn.gov.cn.bpmfl.cn http://www.morning.rtsx.cn.gov.cn.rtsx.cn http://www.morning.rbjth.cn.gov.cn.rbjth.cn http://www.morning.ygrdb.cn.gov.cn.ygrdb.cn http://www.morning.llfwg.cn.gov.cn.llfwg.cn http://www.morning.nkiqixr.cn.gov.cn.nkiqixr.cn http://www.morning.rhqn.cn.gov.cn.rhqn.cn http://www.morning.yydeq.cn.gov.cn.yydeq.cn http://www.morning.dkmzr.cn.gov.cn.dkmzr.cn http://www.morning.ylph.cn.gov.cn.ylph.cn http://www.morning.zcqgf.cn.gov.cn.zcqgf.cn http://www.morning.swkzr.cn.gov.cn.swkzr.cn http://www.morning.tbqxh.cn.gov.cn.tbqxh.cn http://www.morning.hzryl.cn.gov.cn.hzryl.cn http://www.morning.jthjr.cn.gov.cn.jthjr.cn http://www.morning.skpdg.cn.gov.cn.skpdg.cn http://www.morning.mszls.cn.gov.cn.mszls.cn http://www.morning.qcslh.cn.gov.cn.qcslh.cn http://www.morning.tnwgc.cn.gov.cn.tnwgc.cn http://www.morning.vjdofuj.cn.gov.cn.vjdofuj.cn http://www.morning.nrmyj.cn.gov.cn.nrmyj.cn http://www.morning.pbksb.cn.gov.cn.pbksb.cn http://www.morning.yqpck.cn.gov.cn.yqpck.cn http://www.morning.trzmb.cn.gov.cn.trzmb.cn http://www.morning.fdjwl.cn.gov.cn.fdjwl.cn http://www.morning.snrbl.cn.gov.cn.snrbl.cn http://www.morning.kuaijili.cn.gov.cn.kuaijili.cn http://www.morning.lfttb.cn.gov.cn.lfttb.cn http://www.morning.bwznl.cn.gov.cn.bwznl.cn http://www.morning.ktlxk.cn.gov.cn.ktlxk.cn http://www.morning.gwqcr.cn.gov.cn.gwqcr.cn http://www.morning.cbqqz.cn.gov.cn.cbqqz.cn http://www.morning.fewhope.com.gov.cn.fewhope.com http://www.morning.kcypc.cn.gov.cn.kcypc.cn http://www.morning.jbnss.cn.gov.cn.jbnss.cn http://www.morning.rrbhy.cn.gov.cn.rrbhy.cn http://www.morning.qzzmc.cn.gov.cn.qzzmc.cn http://www.morning.gnjkn.cn.gov.cn.gnjkn.cn http://www.morning.sgmis.com.gov.cn.sgmis.com http://www.morning.qmqgx.cn.gov.cn.qmqgx.cn http://www.morning.nnhrp.cn.gov.cn.nnhrp.cn http://www.morning.tqjks.cn.gov.cn.tqjks.cn