iTOP4412设备驱动学习七--LEDS驱动

资源来源于迅为视频学习教程。

本节是LEDS驱动的调用学习,根据之前的学习的杂项设备的基础上实现驱动LED2的亮灭。

1.LED原理图使用了三极管,首先三极管的了解:

我们这个是NPN鍺管

三极管:电流控制电源(模电术语)。理解起来:电流指1->2之间的电流。内部构造较为复杂。有时间再学习补充。

理解:2和3之间有一个可调电阻,其大小由1过来的电流决定。当左边低电平电流小,电阻无穷大。当左边高电平时电流大,电阻值变小,则LED2有电阻电压差,可以发亮。

2. LED相关头文件

    Linux中申请GPIO的头文件(各平台通用):include/linux/gpio.h  如函数gpio_request等

    三星平台的GPIO配置函数头文件,宏定义头文件:arch/arm/plat-samsung/include/plat/gpio-cfg.h  包括三星所有板子配置函数

    三星平台EXYNOS系列平台,GPIO配置参数宏定义头文件:arch/arm/mach-exynos/include/mach/gpio.h  GPIO管脚拉高拉低配置参数等等

    三星平台4412平台,GPIO宏定义头文件:arch/arm/mach-exynos/include/mach/gpio-exynos4.h  已经包含在头文件gpio.h中,包括4412处理器所有GPIO的宏定义。

3. LED函数

    Linux的GPIO中申请函数和赋值函数:gpio_request,gpio_set_value

    三星平台配置GPIO函数:s3c_gpio_cfgpin

    GPIO配置输出模式的宏变量:s3c_GPIO_OUTPUT

4.函数在内核中的实现定义方法

    函数gpio_request,gpio_set_value

include/linux/gpio.h 
//参数1是GPIO的宏,参数2是提高可读性的,可直接指定一个字符串 44 static inline int gpio_request(unsigned gpio, const char *label) 45 { 46 return -ENOSYS; 47 }
 98 static inline void gpio_set_value(unsigned gpio, int value)
 99 {
100         /* GPIO can never have been requested or set as output */
101         WARN_ON(1);
102 }

arch/arm/plat-samsung/include/plat/gpio-cfg.h
 77 /**
 78  * s3c_gpio_cfgpin() - Change the GPIO function of a pin.
 79  * @pin pin The pin number to configure.
 80  * @to to The configuration for the pin's function.
 81  *
 82  * Configure which function is actually connected to the external
 83  * pin, such as an gpio input, output or some form of special function
 84  * connected to an internal peripheral block.
 85  *
 86  * The @to parameter can be one of the generic S3C_GPIO_INPUT, S3C_GPIO_OUTPUT
 87  * or S3C_GPIO_SFN() to indicate one of the possible values that the helper
 88  * will then generate the correct bit mask and shift for the configuration.
 89  *
 90  * If a bank of GPIOs all needs to be set to special-function 2, then
 91  * the following code will work:
 92  *
 93  *      for (gpio = start; gpio < end; gpio++)
 94  *              s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2));
 95  *
 96  * The @to parameter can also be a specific value already shifted to the
 97  * correct position in the control register, although these are discouraged
 98  * in newer kernels and are only being kept for compatibility.
 99  */
100 extern int s3c_gpio_cfgpin(unsigned int pin, unsigned int to);    //参数1是GPIO宏,参数2要将其配置为什么样的GPIO,使用宏S3C_GPIO_OUTPUT
arch/arm/plat-samsung/include/plat/gpio-cfg.h
 69 /* Defines for generic pin configurations */
 70 #define S3C_GPIO_INPUT  (S3C_GPIO_SPECIAL(0))
 71 #define S3C_GPIO_OUTPUT (S3C_GPIO_SPECIAL(1))          //这个是输出模式的宏变量
 72 #define S3C_GPIO_SFN(x) (S3C_GPIO_SPECIAL(x))

因为系统提供的config中默认是将LEDS打开的,所以我们需要重巡编译内核,去掉LEDS驱动,然后加载我们自己写的LEDS驱动。

4. 修改config重新编译:

 首先修改config,将LED配置关闭

 1 $ make menuconfig
 2 scripts/kconfig/mconf Kconfig
 3 #
 4 # configuration written to .config
 5 #
 6 
 7 
 8 *** End of the configuration.
 9 *** Execute 'make' to start the build or try 'make help'.
10 
11 $ make zImage -j4
12 ...
13   OBJCOPY arch/arm/boot/Image
14   Kernel: arch/arm/boot/Image is ready
15   GZIP    arch/arm/boot/compressed/piggy.gzip
16   SHIPPED arch/arm/boot/compressed/lib1funcs.S
17   AS      arch/arm/boot/compressed/lib1funcs.o
18   AS      arch/arm/boot/compressed/piggy.gzip.o
19   LD      arch/arm/boot/compressed/vmlinux
20   OBJCOPY arch/arm/boot/zImage
21   Kernel: arch/arm/boot/zImage is ready

将新的zImage烧写到板子中,烧写方法同之前操作。

5.LEDS驱动代码:

此代码与上次相比增加了hello_pobe和hello_ioctl中的调用。

leds驱动源码leds.c:

  1 #include <linux/init.h>
  2 #include <linux/module.h>
  3 
  4 #include <linux/platform_device.h>
  5 #include <linux/miscdevice.h>
  6 #include <linux/fs.h>
  7 
  8 #include <linux/gpio.h>  //通用的一般会被其他的包含
  9 #include <plat/gpio-cfg.h> //三星平台
 10 #include <mach/gpio.h>  //EXYNOS平台
 11 #include <mach/gpio-exynos4.h>  //4412平台
 12 
 13 #define DRIVER_NAME "hello_ctl"
 14 #define DEVICE_NAME "hello_ctl"
 15 
 16 //参数1是GPIO的值为0或1,参数2是GPIO的个数
 17 static long hello_ctl(struct file *files, unsigned int cmd, unsigned long arg){
 18     printk(KERN_EMERG "cmd is %d, args is %d
", cmd, arg);
 19     if(cmd > 1){
 20         printk(KERN_EMERG "ERROR: cmd is 0 or 1
");
 21     }
 22     if(arg > 1){
 23         printk(KERN_EMERG "ERROR: arg is only 1
");
 24     }
 25     gpio_set_value(EXYNOS4_GPL2(0), cmd);
 26     return 0;
 27 }
 28 static int hello_release(struct inode *inode, struct file *file){
 29     printk(KERN_EMERG "hello release!
");
 30     return 0;
 31 }
 32 static int hello_open(struct inode *inode,  struct file *file){
 33     printk(KERN_EMERG "hello open
");
 34     return 0;
 35 }
 36 static struct file_operations hello_ops = {
 37     .owner = THIS_MODULE,
 38     .open = hello_open,
 39     .release = hello_release,
 40     .unlocked_ioctl = hello_ctl,
 41 };
 42 
 43 static struct miscdevice hello_dev = {
 44     .minor = MISC_DYNAMIC_MINOR,
 45     .name = DEVICE_NAME,
 46     .fops = &hello_ops,
 47 };
 48 
 49 //资源申请也在probe中
 50 static int hello_probe(struct platform_device *pdv){
 51     int ret;
 52     printk(KERN_EMERG "initialized
");
 53 
 54     ret = gpio_request(EXYNOS4_GPL2(0), "LEDS");    //参数宏在文件中查找:arch/arm/mach-exynos/include/mach/gpio-exynos4.h
 55     if(ret < 0){
 56         printk(KERN_EMERG "gpio_request EXYNOS4_GPL2(0) failed
");
 57         return ret;
 58     }
 59 
 60     s3c_gpio_cfgpin(EXYNOS4_GPL2(0), S3C_GPIO_OUTPUT);
 61 
 62     gpio_set_value(EXYNOS4_GPL2(0), 0);
 63 
 64     misc_register(&hello_dev);
 65     return 0;
 66 }
 67 static int hello_remove(struct platform_device *pdv){
 68     printk(KERN_EMERG "remove
");
 69     misc_deregister(&hello_dev);
 70     return 0;
 71 }
 72 static void hello_shutdown(struct platform_device *pdev){
 73     ;
 74 }
 75 static int hello_suspend(struct platform_device *pdv, pm_message_t pmt){
 76     return 0;
 77 }
 78 static int hello_resume(struct platform_device *pdv){
 79     return 0;
 80 }
 81 
 82 struct platform_driver hello_driver = {
 83     .probe = hello_probe,
 84     .remove = hello_remove,
 85     .shutdown = hello_shutdown,
 86     .suspend = hello_suspend,
 87     .resume = hello_resume,
 88     .driver = {
 89         .name = DRIVER_NAME,
 90         .owner = THIS_MODULE,
 91     }
 92 };
 93 
 94 static int hello_init(void){
 95     int DriverState;
 96 
 97     printk(KERN_EMERG "hello world enter!
");
 98     DriverState = platform_driver_register(&hello_driver);
 99 
100     printk(KERN_EMERG "DriverState is %d
", DriverState);
101     return 0;
102 }
103 
104 static void hello_exit(void){
105     printk(KERN_EMERG "hello world exit!
");
106     platform_driver_unregister(&hello_driver);
107 }
108 
109 module_init(hello_init);
110 module_exit(hello_exit);
111 
112 MODULE_LICENSE("Dual BSD/GPL");

Makefile

 1 obj-m += leds.o
 2 
 3 KDIR := /home/nan/iTOP4412/iTop4412_Kernel_3.0
 4 
 5 PWD ?= $(shell pwd)
 6 
 7 all:
 8     make -C $(KDIR) M=$(PWD) modules
 9 clean:
10     rm -rf *.o

上层调用invoke_leds.c:

 1 #include <stdio.h>
 2 #include <sys/type.h>
 3 #include <sys/stat.h>
 4 #include <fcntl.h>
 5 #include <unistd.h>
 6 #include <sys/ioctl.h>
 7 
 8 int main()
 9 {
10     int fd;
11     char *hello_node = "/dev/hello_ctl"
12 
13     if((fd = open(hello_node, O_RDWR|O_NDELAY)) < 0){
14         printf("App open %s failed!
", hello_node);
15     }
16     else{
17         printf("App open %s success
", hello_node);
18         ioctl(fd, 1, 1);   //1:cmd 2:arg
19         sleep(3);
20         ioctl(fd, 0, 1);
21         sleep(3);
22         ioctl(fd, 1, 1);
23     }
24     close(fd);
25 
26     return 0;
27 }

然后编译:

 1 nan@nanzh:~/iTOP4412/7$ make
 2 make -C /home/nan/iTOP4412/iTop4412_Kernel_3.0 M=/home/nan/iTOP4412/7 modules
 3 make[1]: Entering directory '/home/nan/iTOP4412/iTop4412_Kernel_3.0'
 4   CC [M]  /home/nan/iTOP4412/7/leds.o
 5 /home/nan/iTOP4412/7/leds.c: In function 'hello_ctl':
 6 /home/nan/iTOP4412/7/leds.c:18: warning: format '%d' expects type 'int', but argument 3 has type 'long unsigned int'
 7   Building modules, stage 2.
 8   MODPOST 1 modules
 9   CC      /home/nan/iTOP4412/7/leds.mod.o
10   LD [M]  /home/nan/iTOP4412/7/leds.ko
11 make[1]: Leaving directory '/home/nan/iTOP4412/iTop4412_Kernel_3.0'
12 
13 nan@nanzh:~/iTOP4412/7$ arm-none-linux-gnueabi-gcc invoke_leds.c -o invoke_leds -static
14 nan@nanzh:~/iTOP4412/7$ ls invoke_leds*
15 invoke_leds  invoke_leds.c

拷贝到板子上运行:

root@iTOP4412-ubuntu-desktop:~# cat /proc/modules 
root@iTOP4412-ubuntu-desktop:~# ls /dev/leds
ls: cannot access /dev/leds: No such file or directory
root@iTOP4412-ubuntu-desktop:~/tests/7# insmod leds.ko 
[  946.850211] hello world enter!
[  946.852131] initialized
[  946.875142] DriverState is 0
root@iTOP4412-ubuntu-desktop:~/tests/7# ./invoke_leds 
[  973.092455] hello open
[  973.093867] cmd is 1, args is 1
App open /dev/hello_ctl success
[  976.100390] cmd is 0, args is 1
[  979.102297] cmd is 1, args is 1
[  979.104001] hello release!
root@iTOP4412-ubuntu-desktop:~/tests/7# rmmod leds
[ 1078.413217] hello world exit!
[ 1078.414806] remove

板子上的现象就是边上LED2一开始是亮的,被uboot点亮,然后运行invoke_leds会灭了三秒再亮。

以上就是本节内容

原文地址:https://www.cnblogs.com/nanzh/p/12547392.html