嵌入式开发记录 day42 设备树GPIO驱动例程
1、在Linux中引入设备树,方便管理设备、并减少了代码量,在原始的平台文件platform下,管理GPIO管脚很混乱;
设备树为了方便管理GPIO管脚,添加了pinctrl子系统,用于管理GPIO;
另外,pinctrl的引入只是方便了GPIO的管理,但是还无法完全代替原来的GPIO子系统;因此原来基于platform下的GPIO子系统与pinctrl是共同存在的;
2、GPIO参考文档
linux的GPIO系统官方文档:Documentation/devicetree/bindings/gpio/gpio.txt
Example of a node using GPIOs: node { enable-gpios = <&qe_pio_e 18 GPIO_ACTIVE_HIGH>; };
设备树的节点,可以包含,互相也可以引用--看到“&”,表示做了引用(也可以理解面向对象编程的重写);
设备树的节点,“xxx :xxx”表示这个节点可以引用
&bank gpioa1 gpiob1
18表示gpio在bank中编号
GPIO_ACTIVE_HIGH 表示高电平(测试无效,是设备树中强制规定必须有2个cells)
三星的GPIO系统官方文档:Documentation/devicetree/bindings/gpio/gpio-samsung.txt
3、pinctrl文档
linux的pinctrl系统官方文档:Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
三星的pinctrl系统官方文档:Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
源码文档:
dt-bindings/pinctrl/samsung.h
arch/arm/boot/dts/exynos4412-pinctrl.dtsi
4、硬件连接信息
硬件:4412的两个led灯
gpl2-0对应<&gpl2 0 GPIO_ACTIVE_HIGH>
gpk1-1对应<&gpk1 1 GPIO_ACTIVE_HIGH>
vim arch/arm/boot/dts/exynos4412-itop-elite.dts,设备树增加属性两个属性:
gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>;
gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>;
leds_test_node:leds_test_node { compatible = "leds_test"; status = "disabled"; gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>; // 在前面实验基础上,添加下面这两行代码 gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>; // status = "okay"; // 在下面引用使能 };
屏蔽掉其它代码对它们的引用
led3 { label = "red:user"; // gpios = <&gpk1 1 GPIO_ACTIVE_HIGH>; // 屏蔽掉这行 default-state = "off"; };
5、驱动代码
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> // 获取GPIO管脚属性信息 #include <linux/miscdevice.h> #include <linux/fs.h> #define DRIVER_NAME "leds_test" int gpio_pin[2] = {-1}; static int leds_probe(struct platform_device * pdev) { struct device_node *node = pdev->dev.of_node; int ret; printk("led init\n"); /* 1、设备树这样写 gpios1 = <&gpl2 0 GPIO_ACTIVE_HIGH>; gpios2 = <&gpk1 1 GPIO_ACTIVE_HIGH>; 这样获取gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0); 2、设备树这样写 gpios = <&gpl2 0 GPIO_ACTIVE_HIGH; &gpk1 1 GPIO_ACTIVE_HIGH>; 这样获取gpio_pin[0] = of_get_named_gpio(node, "gpios", 0); --> &gpl2 0 GPIO_ACTIVE_HIGH 这样获取gpio_pin[1] = of_get_named_gpio(node, "gpios", 1); --> &gpk1 1 GPIO_ACTIVE_HIGH */ gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0); // 获取GPIO管脚的宏定义 类似于之前EXYNOS_GPIO... gpio_pin[1] = of_get_named_gpio(node, "gpios2", 0); // 获取到管脚可以申请GPIO ret = gpio_request(gpio_pin[0], "led1"); if(ret!=0){ printk("gpio_pin[0] request %d failed.", gpio_pin[0]); return ret; } if (gpio_pin[1] < 0) printk("gpio_pin[1] is not available \n"); ret = gpio_request(gpio_pin[1], "led2"); if(ret!=0){ printk("gpio_pin[1] request %d failed.", gpio_pin[1]); return ret; } printk("gpio_pin[0] is %d\n",gpio_pin[0]); printk("gpio_pin[1] is %d\n",gpio_pin[1]); gpio_free(gpio_pin[0]); gpio_free(gpio_pin[1]); gpio_direction_output(gpio_pin[0],0); // 设置直接输出模式 gpio_set_value(gpio_pin[0], 1); // 设置高电平输出 gpio_direction_output(gpio_pin[1],0); gpio_set_value(gpio_pin[1], 1); return 0; } static int leds_remove(struct platform_device * pdev) { printk(KERN_ALERT "Goodbye, curel world, this is remove\n"); return 0; } static const struct of_device_id of_leds_dt_match[] = { {.compatible = DRIVER_NAME}, {}, }; MODULE_DEVICE_TABLE(of,of_leds_dt_match); static struct platform_driver leds_driver = { .probe = leds_probe, .remove = leds_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_leds_dt_match, }, }; static int leds_init(void) { printk(KERN_ALERT "Hello, world\n"); return platform_driver_register(&leds_driver); return 0; } static void leds_exit(void) { printk(KERN_ALERT "Goodbye, curel world\n"); platform_driver_unregister(&leds_driver); } module_init(leds_init); module_exit(leds_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("rty"); MODULE_DESCRIPTION("topeet4412_regiter_dev_drv");
6、函数说明
int of_get_named_gpio(struct device_node *np, const char *propname, int index); // 功能:of_get_named_gpio,从设备树中提取gpio 口; // 参数np:设备节点指针; // 参数propname:属性名; // 参数index:gpio口引脚标号; // 返回值: 成功,得到GPIO 口编号;失败,负数,绝对值是错误码。 // of_get_named_gpio(node,"gpios1",0) // of_get_named_gpio(node,"gpios2",0)
7、测试
1、必须先编译修改后设备树文件,并烧写;
2、编译上面源文件为模块,并加载,此时开发板两个灯都是亮的;
3、打印书输出:
[root@iTOP-4412]# insmod itop4412_of_get_gpios.ko [ 1630.825660] itop4412_of_get_gpios: loading out-of-tree module taints kernel. [ 1630.832394] Hello, world [ 1630.834104] led init [ 1630.836076] gpio_pin[0] is 120 // 将管脚编号处理,得到编号后,便可以对管脚操作 [ 1630.839013] gpio_pin[1] is 91
8、给上面的驱动添加file_operations就可以对外提供接口,就可以读写等操作;
#include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/of.h> #include <linux/of_gpio.h> #include <linux/miscdevice.h> #include <linux/fs.h> // file_operations #define DRIVER_NAME "leds_test" int gpio_pin[2] = {-1}; int leds_open(struct inode *inode,struct file *filp) { printk("Device Opened Success!\n"); return nonseekable_open(inode,filp); } int leds_release(struct inode *inode,struct file *filp) { printk("Device Closed Success!\n"); return 0; } long leds_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) { printk("debug: leds_ioctl cmd is %d,arg is %d\n" , cmd,arg); switch(cmd) { case 0: case 1: gpio_set_value(gpio_pin[arg], cmd); break; default: return -EINVAL; } return 0; } static struct file_operations leds_ops = { .owner = THIS_MODULE, .open = leds_open, .release= leds_release, .unlocked_ioctl = leds_ioctl, }; static struct miscdevice leds_dev = { .minor = MISC_DYNAMIC_MINOR, .fops = &leds_ops, .name = DRIVER_NAME, }; static int leds_probe(struct platform_device * pdev) { struct device_node *node = pdev->dev.of_node; int ret; printk("led init\n"); gpio_pin[0] = of_get_named_gpio(node, "gpios1", 0); gpio_pin[1] = of_get_named_gpio(node, "gpios2", 0); ret = gpio_request(gpio_pin[0], "led1"); if(ret!=0){ printk("gpio_pin[0] request %d failed.", gpio_pin[0]); return ret; } if (gpio_pin[1] < 0) printk("gpio_pin[1] is not available \n"); ret = gpio_request(gpio_pin[1], "led2"); if(ret!=0){ printk("gpio_pin[1] request %d failed.", gpio_pin[1]); return ret; } printk("gpio_pin[0] is %d\n",gpio_pin[0]); printk("gpio_pin[1] is %d\n",gpio_pin[1]); gpio_free(gpio_pin[0]); gpio_free(gpio_pin[1]); gpio_direction_output(gpio_pin[0],0); gpio_set_value(gpio_pin[0], 1); gpio_direction_output(gpio_pin[1],0); gpio_set_value(gpio_pin[1], 1); ret = misc_register(&leds_dev); if(ret<0){ printk("led:register device failed!\n"); goto exit; } return 0; exit: misc_deregister(&leds_dev); return ret; } static int leds_remove(struct platform_device * pdev) { printk(KERN_ALERT "Goodbye, curel world, this is remove\n"); misc_deregister(&leds_dev); return 0; } static const struct of_device_id of_leds_dt_match[] = { {.compatible = DRIVER_NAME}, {}, }; MODULE_DEVICE_TABLE(of,of_leds_dt_match); static struct platform_driver leds_driver = { .probe = leds_probe, .remove = leds_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = of_leds_dt_match, }, }; static int leds_init(void) { printk(KERN_ALERT "Hello, world\n"); return platform_driver_register(&leds_driver); return 0; } static void leds_exit(void) { printk(KERN_ALERT "Goodbye, curel world\n"); platform_driver_unregister(&leds_driver); } module_init(leds_init); module_exit(leds_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("rty"); MODULE_DESCRIPTION("topeet4412_regiter_dev_drv");
9、使用应用读写驱动
#include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc,char **argv) { int fd; if(argc!=4){ printf("arvc is 4:\nargv1 is device_node ,\nargv2 is cmd,\nargv2 is lednum\n"); } if((fd = open(argv[1], O_RDWR|O_NOCTTY|O_NDELAY))<0){ printf("open %s failed\n",argv[1]); return -1; } else{ printf("open %s ok\n",argv[1]); ioctl(fd,atoi(argv[2]),atoi(argv[3])); } close(fd); return 0; }
10、待测试
1、将驱动编译成模块,应用编译成可执行的.o文件
2、加载驱动文件,可查看节点
[root@iTOP-4412]# insmod itop4412_of_get_gpios_1.ko // 加载驱动 [ 739.139973] Hello, world [ 739.141536] led init [ 739.143360] gpio_pin[0] is 120 [ 739.146260] gpio_pin[1] is 91 [root@iTOP-4412]# ls /dev/leds_test // 驱动中杂项设备注册,会生成设备节点 /dev/leds_test [root@iTOP-4412]# ./ledstest.o /dev/leds_test 1 0 // 测试应用,控制led 操作设备需要操作设备节点 [ 347.342746] Device Opened Success! [ 347.344880] debug: leds_ioctl cmd is 1,arg is 0 [ 347.349226] Device Closed Success! open /dev/leds_test ok
11、GPIO子系统:
GPIO子系统使用struct gpio结构来描述
/** * struct gpio - a structure describing a GPIO with configuration * @gpio: the GPIO number * @flags: GPIO configuration as specified by GPIOF_* * @label: a literal description string of this GPIO */ struct gpio { unsigned gpio; unsigned long flags; const char *label; };

浙公网安备 33010602011771号