从最简单的LED驱动入手,彻底细致的分析一遍mini2440的LED驱动。
官方手册上写,mini2440的四个LED与CPU的GPIO相连,LED1, LED2, LED3, LED4分别对应的
是GPB5, GPB6, GPB7, GPB8。那什么是GPIO呢?
GPIO是通用输入输出口的简称,只需要设置相应的CPU寄存器,就可以改变引脚的用途。
控制硬件,其实就是控制对应的寄存器。
四个LED的采用GPBCON寄存器上的4组2bit位来配置对应引脚的状态。4组2bit位的功能都
一样:00表示输入,01表示输出,10为特殊功能,11是保留的。
LED1对应的是GPB5, GPB5使用[11:10]位
LED2对应的是GPB6, GPB6使用[12:13]位
LED3对应的是GPB7, GPB7使用[14:15]位
LED4对应的是GPB8, GPB8使用[16:17]位
驱动需要先设置LED为输出状态,也就是要把对应的GPBX设置为01。
四个LED采用CPBDAT寄存器来对应4个LED的数值状态,GPBDAT5就对应GPB5,GPBDAT6就对
应GPB6,以此类推。手册上写低电平有效,就是说当GPBDAT寄存器位置为0时,LED就发光。
在三星官方的手册S3C2440.pdf中描述的寄存器状态如下:
GPB8 [17:16] 00 = Input 01 = Output
10 = nXDREQ1 11 = Reserved
GPB7 [15:14] 00 = Input 01 = Output
10 = nXDACK1 11 = Reserved
GPB6 [13:12] 00 = Input 01 = Output
10 = nXBREQ 11 = reserved
GPB5 [11:10] 00 = Input 01 = Output
10 = nXBACK 11 = reserved
GPBDAT Bit Description
GPB[10:0] [10:0] When the port is configured as input port, the corresponding
bit is the pin state. When the port is configured as output port, the pin state is the same
as the corresponding bit. When the port is configured as functional pin, the
undefined value will be read.
贴出添加注释后的代码,非常简单的驱动。
#include <linux/miscdevice.h> #include <linux/delay.h> #include <asm/irq.h> #include <mach/regs-gpio.h> #include <mach/hardware.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/mm.h> #include <linux/fs.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/moduleparam.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/ioctl.h> #include <linux/cdev.h> #include <linux/string.h> #include <linux/list.h> #include <linux/pci.h> #include <asm/uaccess.h> #include <asm/atomic.h> #include <asm/unistd.h> #define DEVICE_NAME "leds" /* /dev目录下的设备名 */ /* GPBX */ static unsigned long led_table [] = { S3C2410_GPB5, S3C2410_GPB6, S3C2410_GPB7, S3C2410_GPB8, }; /* GPBX的输出状态 */ static unsigned int led_cfg_table [] = { S3C2410_GPB5_OUTP, S3C2410_GPB6_OUTP, S3C2410_GPB7_OUTP, S3C2410_GPB8_OUTP, }; /* 驱动接口函数 * ioctl的内核空间版本和用户控件的版本不同 * 内核版为: * int (*ioctl)( struct inode *inode, struct file *file, unsigned int * cmd, unsigned long arg); * */ static int sbc2440_leds_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { switch(cmd) { case 0: case 1: if (arg > 4) { return -EINVAL; /* Invalid argument,非法参数 */ } /* 设置数据寄存器GPBDAT * 低电平有效,用户程序传来的cmd取反 * */ s3c2410_gpio_setpin(led_table[arg], !cmd); return 0; default: return -EINVAL; } } /* 接口对象 */ static struct file_operations dev_fops = { .owner = THIS_MODULE, .ioctl = sbc2440_leds_ioctl, }; /* 设备对象 */ static struct miscdevice misc = { .minor = MISC_DYNAMIC_MINOR, /* 动态设备号 */ .name = DEVICE_NAME, /* 将在/dev目录生成led设备 */ .fops = &dev_fops, /* 驱动接口 */ }; static int __init dev_init(void) { int ret; int i; for (i = 0; i < 4; i++) { /*设置GPIO对应的配置寄存器GPIOCON为输出状态*/ s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); /*设置GPIO对应的数据寄存器GPIODAT为低电平 *在模块加载结束后,四个LED应该是全部都是发光状态*/ s3c2410_gpio_setpin(led_table[i], 0); } ret = misc_register(&misc); /* 注册设备 */ printk (DEVICE_NAME"\tinitialized\n"); /* dmesg */ return ret; } static void __exit dev_exit(void) { misc_deregister(&misc); /* 注销设备 */ } module_init(dev_init); /* 声明加载模块初始化函数 */ module_exit(dev_exit); /* 声明卸载模块清除函数 */ MODULE_LICENSE("GPL"); /* 许可证声明 */ MODULE_AUTHOR("FriendlyARM Inc."); /* 作者信息 */