嵌入式开发记录-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

 

posted @ 2020-07-11 23:09  笑不出花的旦旦  阅读(211)  评论(0)    收藏  举报