嵌入式开发记录-day19 GPIO-led驱动、向内核模块传参数
1、包含相关头文件
1 linux平台GPIO申请和赋值函数:include/linux/gpio.h 2 int gpio_request(unsigned gpio, const char *label) // 申请GPIO 传入端口ID、名字 3 void gpio_set_value(unsigned gpio, int value) // 端口赋值 4 void gpio_get_value(unsigned gpio, int value) // 获取端口值 5 void gpio_free(unsigned gpio); // 申请了GPIO以后需要释放 6 7 三星平台配置函数: 8 arch/arm/plat-samsung/include/plat/gpio-cfg.h //包括三星所有处理器都适用 9 //设置管脚的输入输出、推挽、中断等 10 // s3c_gpio_cfgpin() - Change the GPIO function of a pin. 11 int s3c_gpio_cfgpin(unsigned int pin, unsigned int to); 12 13 三星EXYNOS系列,GPIO配置宏定义 14 arch/arm/mach-exynos/include/mach/gpio.h 15 包含管脚的输出属性、管脚拉高、拉低 16 比如:#define GPIO_OUTPUT 1 17 18 三星4412平台,GPIO的相关,宏定义 19 arch/arm/mach-exynos/include/mach/gpio-exynos4.h 管脚的宏定义 20 比如:#define EXYNOS4_GPL2(_nr) (EXYNOS4_GPIO_L2_START + (_nr))
2、led的驱动LedDriver.c
1 /* 2 LED字符驱动 3 */ 4 // 驱动注册所需头文件 包含结构体,注册和卸载所需的函数 5 #include <linux/platform_device.h> 6 #include <linux/module.h> 7 #include <linux/init.h> 8 #include <linux/miscdevice.h> // 注册杂项设备头文件 9 #include <linux/fs.h> // 设备结点文件 10 11 // linux下GPIO的申请、赋值函数 12 #include <linux/gpio.h> 13 // 三星平台配置管脚为输出模式 14 #include <plat/gpio-cfg.h> 15 // 管脚输出属性的宏定义GPIO_OUTPUT 16 #include <mach/gpio.h> 17 // exynos4平台的管脚ID的宏定义EXYNOS4_GPL2() 18 #include <mach/gpio-exynos4.h> 19 20 #define DRIVER_NAME "hello_ctl" 21 #define DEVICE_NAME "hello_ctl" 22 23 24 MODULE_LICENSE("Dual BSD/GPL"); 25 MODULE_AUTHOR("TOPEET"); 26 27 static int hello_open(struct inode *node, struct file *f) 28 { 29 printk(KERN_EMERG "\t func hello open\n"); 30 return 0; 31 } 32 33 static int hello_release(struct inode *node, struct file *f) 34 { 35 printk(KERN_EMERG "\t hello release\n"); 36 return 0; 37 } 38 // 管脚输出使能配置 39 static long hello_ioctl(struct file *f, unsigned int cmd, unsigned long arg) 40 { 41 printk(KERN_EMERG "\t hello ioctl\n"); 42 printk("func leds:cmd is %d ...arg is %d\n",cmd,arg); 43 44 if(cmd > 1){ 45 printk(KERN_EMERG "func leds:cmd only be 1 or 0\n"); 46 } 47 if(arg > 1){ 48 printk(KERN_EMERG "func leds:cmd only be 1\n"); 49 } 50 51 // 给对应管脚赋值 52 gpio_set_value(EXYNOS4_GPL2(0),cmd); 53 return 0; 54 } 55 56 // 文件操作,对应生成的设备结点具体如何操作 57 static struct file_operations hello_ops = { 58 .owner = THIS_MODULE, 59 .open = hello_open, 60 // int (*open) (struct inode *, struct file *); 61 .release = hello_release, 62 // int (*release) (struct inode *, struct file *); 63 .unlocked_ioctl = hello_ioctl, 64 // long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);long); 65 66 }; 67 68 static struct miscdevice hello_dev = { 69 .minor = MISC_DYNAMIC_MINOR, // 自动分配ID 70 .name = DEVICE_NAME, // 设备结点名称 71 .fops = &hello_ops, // 结构体 72 }; 73 74 // int (*probe)(struct platform_device *); 75 // LED管脚初始化 76 static int hello_probe(struct platform_device *p) 77 { 78 int ret = 0; 79 printk(KERN_EMERG "\t initialized...\n"); 80 81 // 1、向linux内核申请GPIO,释放时需要释放对应的端口 82 ret = gpio_request(EXYNOS4_GPL2(0),"LEDS"); 83 if(ret < 0){ 84 printk(KERN_EMERG "\t func gpio_request failed...\n"); 85 return -1; 86 } 87 // 2、配置管脚EXYNOS4_GPL2(0)为输出 88 s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT); 89 // 3、设置管脚输出低电平 90 gpio_set_value(EXYNOS4_GPL2(0),0); // /GPL2_0 此管脚设置低电平 91 92 misc_register(&hello_dev); 93 return 0; 94 } 95 // int (*remove)(struct platform_device *); 96 static int hello_remove(struct platform_device *p) 97 { 98 printk(KERN_EMERG "\t hello_ct123 removed\n"); 99 // 卸载模块需要释放端口 100 gpio_free(EXYNOS4_GPL2(0)); 101 misc_deregister(&hello_dev); 102 return 0; 103 } 104 // int (*suspend)(struct platform_device *, pm_message_t state); 105 static int hello_suspend(struct platform_device *p, pm_message_t state) 106 { 107 return 0; 108 } 109 // int (*resume)(struct platform_device *); 110 static int hello_resume(struct platform_device *p) 111 { 112 return 0; 113 } 114 // void (*shutdown)(struct platform_device *); 115 static void hello_shutdown(struct platform_device *p) 116 { 117 118 } 119 120 // 3、结构体数据初始化 121 // 注册驱动传入的结构体 122 struct platform_driver hello_driver = { 123 .probe = hello_probe, // 用于初始化模块 124 .remove = hello_remove, // 移除模块时,执行的动作 125 .suspend = hello_suspend,// 模块挂起时,执行的动作 126 .resume = hello_resume, // 挂起的模块,恢复运行执行动作 127 .shutdown = hello_shutdown, 128 .driver = { 129 .name = DRIVER_NAME, // 驱动名称 130 .owner = THIS_MODULE,// 驱动所有者,THIS_MODULE宏定义 131 }, 132 }; 133 // 2、模块初始化相关实现 134 // 模块的入口函数 加载模块执行动作 135 static int hello_init(void) 136 { 137 int DriverState; 138 printk(KERN_EMERG "HELLO WORLD enter!\n"); 139 DriverState = platform_driver_register(&hello_driver); // 注册驱动 转而执行初始化动作 140 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态 141 142 return 0; 143 } 144 // 模块退出函数 卸载模块所执行 145 static void hello_exit(void) 146 { 147 printk(KERN_EMERG "HELLO WORLD exit!\n"); 148 platform_driver_unregister(&hello_driver); // 卸载驱动函数 149 } 150 151 //1、 入口函数module_nint 152 module_init(hello_init); 153 module_exit(hello_exit);
Led主要的初始化在static int hello_probe(...)函数中完成,并通过hello_ioctl(...)接收外部参数,控制led亮灭
3、led配置步骤:
1、ret = gpio_request(EXYNOS4_GPL2(0),"LEDS"); // 向linux内核申请端口 // 使用三星平台宏定义配置端口ID 为输出状态 2、s3c_gpio_cfgpin(EXYNOS4_GPL2(0),S3C_GPIO_OUTPUT); 3、gpio_set_value(EXYNOS4_GPL2(0),0); // 给端口ID 赋值高低电平
4、LED驱动测试应用invoke_led.c
1 /** 2 LED 驱动应用 3 */ 4 #include <stdio.h> 5 #include <sys/types.h> 6 #include <sys/stat.h> 7 #include <fcntl.h> 8 #include <unistd.h> // sleep() 9 #include <sys/ioctl.h> 10 11 int main(void) 12 { 13 int fd; 14 char* hello_node = "/dev/hello_ctl"; 15 fd = open(hello_node,O_RDWR|O_NDELAY); // 非阻塞打开 16 if(fd < 0){ 17 printf("func open app %s failed\n",hello_node); 18 return -1; 19 }else{ 20 printf("App open %s success\n",hello_node); 21 ioctl(fd,1,1); // 应用程序向驱动传值 点亮led 22 sleep(5); // 延时5秒 23 ioctl(fd,0,1); // 应用程序向驱动传值 熄灭led 24 sleep(5); // 延时5秒 25 ioctl(fd,1,1); // 应用程序向驱动传值 点亮led 26 } 27 close(fd); 28 return 0; 29 }
4、编译
使用Makefile将LedDriver.c编译成模块.ko文件,将invoke_led.c编译成.o文件
5、测试(需要注意 内核中不能包含leds的驱动,否则申请GPIO端口会失败)
// 加载模块 加载之前led默认是点亮的,加载以后是熄灭 [root@iTOP-4412]# insmod /mnt/disk/LedDriver.ko [ 40.508396] HELLO WORLD enter! [ 40.510072] initialized... [ 40.514741] DriverState is 0 // 执行应用 点亮5s熄灭5s,然后点亮 [root@iTOP-4412]# ./mnt/disk/invoke_led [ 53.189605] func hello open App open /dev/hel[ 53.194581] hello ioctl [ 53.197179] func leds:cmd is 1 ...arg is 1 lo_ctl success [ 58.201347] hello ioctl [ 58.202490] func leds:cmd is 0 ...arg is 1 [ 63.206677] hello ioctl [ 63.207822] func leds:cmd is 1 ...arg is 1 [ 63.211920] hello release
6、向内核模块传递参数moduleparam.c
与程序中的main函数类似,也可以通过参数列表传递参数;
所需要的头文件:#include <linux/moduleparam.h>、#include <linux/stat.h>
1 #include <linux/init.h> 2 /*包含初始化宏定义的头文件,代码中的module_init和module_exit在此文件中*/ 3 #include <linux/module.h> 4 /*包含初始化加载模块的头文件,代码中的MODULE_LICENSE在此头文件中*/ 5 /*定义module_param module_param_array的头文件*/ 6 #include <linux/moduleparam.h> 7 /*定义module_param module_param_array中perm的头文件 定义权限宏定义S_IRUSR*/ 8 #include <linux/stat.h> 9 10 MODULE_LICENSE("Dual BSD/GPL"); 11 /*声明是开源的,没有内核版本限制*/ 12 MODULE_AUTHOR("iTOPEET_dz"); 13 /*声明作者*/ 14 15 static int module_arg1,module_arg2; 16 static int int_array[50]; 17 static int int_num; 18 19 // 向内核传入单个参数 参数名称 、参数类型、参数访问权限 20 module_param(module_arg1,int,S_IRUSR); // 所有文件所有者可以读 21 module_param(module_arg2,int,S_IRUSR); 22 // 向内核传入多个参数 23 module_param_array(int_array,int,&int_num,S_IRUSR); 24 25 static int hello_init(void) 26 { 27 int i; 28 printk(KERN_EMERG "hello init...\n"); 29 30 printk(KERN_EMERG "module_arg1 is %d!\n",module_arg1); 31 printk(KERN_EMERG "module_arg2 is %d!\n",module_arg2); 32 33 for(i=0;i<int_num;i++){ 34 printk(KERN_EMERG "int_array[%d] is %d!\n",i,int_array[i]); 35 } 36 37 printk(KERN_EMERG "Hello World enter!\n"); 38 /*打印信息,KERN_EMERG表示紧急信息*/ 39 return 0; 40 } 41 42 static int hello_exit(void) 43 { 44 printk(KERN_EMERG "hello exit...\n"); 45 return 0; 46 } 47 48 // 模块的入口函数 49 module_init(hello_init); 50 51 module_exit(hello_exit);
7、编译成模块.ko文件,并加载
[root@iTOP-4412]# insmod ./mnt/disk/moduleparam.ko module_arg1=10 module_arg2=20 int_array=12,13,14,15,16,17,18 [ 202.143973] hello init... [ 202.145154] module_arg1 is 10! [ 202.148149] module_arg2 is 20! [ 202.151198] int_array[0] is 12! [ 202.154312] int_array[1] is 13! [ 202.157504] int_array[2] is 14! [ 202.160572] int_array[3] is 15! [ 202.163687] int_array[4] is 16! [ 202.166820] int_array[5] is 17! [ 202.169936] int_array[6] is 18! [ 202.173068] Hello World enter!
8、查看传入内核的参数
[root@iTOP-4412]# cat /sys/module/moduleparam/parameters/module_arg1 10 [root@iTOP-4412]# cat /sys/module/moduleparam/parameters/module_arg2 20 [root@iTOP-4412]# cat /sys/module/moduleparam/parameters/int_array 12,13,14,15,16,17,18

浙公网安备 33010602011771号