资源来源于迅为视频学习教程。
本节是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会灭了三秒再亮。
以上就是本节内容