这是学习驱动开发中,在MICRO 2440开发板上写的第一个驱动程序。实现对S3C2440的GPIO的控制,是一个ARM-LINUX上最简单的驱动。本驱动通过S3C2440的GPB5~8控制4个LED,属MISC(混杂)驱动,其实MISC也是一种特殊的字符驱动,只不过是把主设备号为10的字符驱动归类为MISC类驱动。
MISC类驱动结构如下:
static struct miscdevice misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &dev_fops,
};
minor为指定次设备号,等于MISC_DYNAMIC_MINOR表示为动态获取次设备号。
name为设备名。
fops为 file_operations结构,设备的操作函数指针集合。
file_operations结构:
static struct file_operations dev_fops = {
.owner = THIS_MODULE,
.ioctl = sbc2440_leds_ioctl,
};
该设备省略了OPEN和RELESE操作函数,即默认为打开状态。我们只需要对IO口操作就可以实现LED灯的亮和灭,所以只需要一个IOCTL操作函数就可以满足要求了。
GPIO口的设置:
S3C2440设置GPIO口主要是设置3个寄存器。分别为GPxCON, GPxDAT, GPxUP(x=A….J)。
GPxCON用于设置IO口的功能,每两位对应一个IO口,输入=00,输出=01;特殊功能=10;
GPxDAT为IO口的数据寄存器,每一位对应一个IO口。
GPxUP为是否使用上拉电阻,0为使用,注意的是并不是每组IO口都有内部集成上拉电阻,如果没有集成,则没该寄存器。
设置这三个寄存器相对应的函数为:s3c2410_gpio_cfgpin,s3c2410_gpio_setpin。
驱动程序代码:
加载函数:
static int __init dev_init(void)
{
int ret;
int i;
for (i = 0; i < 4; i++) {
s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]);
s3c2410_gpio_setpin(led_table[i], 0);
}
ret = misc_register(&misc);
printk (DEVICE_NAME"tinitializedn");
return ret;
}
在加载函数里,主要完成的就是初始化IO口和注册一个MISC类设备,MISC类在misc_register这个调用里,会帮我们根据struct miscdevice里面的成员帮我们注册一个字符设备,所以我们这个MISC的驱动加载函数比较短。
卸载函数:
static void __exit dev_exit(void)
{
misc_deregister(&misc);
}
在卸载函数里,只需要调用misc_deregister卸载掉MISC设备就OK了。
IOCTL函数:
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;
}
s3c2410_gpio_setpin(led_table[arg], !cmd);
return 0;
default:
return -EINVAL;
}
}
在这个MISC设备里,主要用到的是控制函数。通过之前介绍的操作IO寄存器的函数,我们就可以很轻松地对IO口进行控制。
编译、加载:
把源码放进/drivers/char下。
修改内核源码目录linux-2.6.32.2/drivers/char下的Kconfig文件,添加以下文本把驱动程序加进代码树。
config LOCKER_LEDS
tristate "Locker_Liang leds"
depends on MACH_MINI2440
default m if MACH_MINI2440
help
Locker_Liang leds.
config"为定义了一新的配置选项。Tristate为选项名称。depends on为基于哪类处理器。Default表示如果上一个处理器不可用则默认选这个。Help为帮助信息,下一行为帮助信息。Kconfig的语法详细说明看Kconfig文档。
修改Makefile,添加
obj-$(CONFIG_LOCKER_LEDS)+= MY_leds.o
这里注意的是LOCKER_LEDS一定要和Kconfig上的相同,.o文件也要和源文件名相同。
在linux-2.6.32.2目录输入make menuconfig,在 Device Drivers -》Character devices中选中我们添加的模块,选为M,即编译为模块方式。
输入make modules编译模块。这时在/drivers/char就会生成一个MY_leds.ko文件。把该文件拷贝到开发板上,输入insmod MY_leds.ko 就可以加载模块到内核中。并且自动生成设备文件。输入rmmod MY_leds.ko可以卸载模块。
这样做就可以对设备操作了,但还有一个问题就是复位后,还要重新加载驱动模块和重新生成设备文件。这时我们可以在/etc/init.d/rcS文件里,加上加载驱动模块和生成设备文件的命令,这样每次开机后都会自动完成设置。
完整代码:
#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEVICE_NAME "MY_leds" static unsigned long led_table [] = { S3C2410_GPB(5), S3C2410_GPB(6), S3C2410_GPB(7), S3C2410_GPB(8), }; static unsigned int led_cfg_table [] = { S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, S3C2410_GPIO_OUTPUT, }; 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; } 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, .fops = &dev_fops, }; static int __init dev_init(void) { int ret; int i; for (i = 0; i < 4; i++) { s3c2410_gpio_cfgpin(led_table[i], led_cfg_table[i]); s3c2410_gpio_setpin(led_table[i], 0); } ret = misc_register(&misc); printk (DEVICE_NAME"tinitializedn"); return ret; } static void __exit dev_exit(void) { misc_deregister(&misc); } module_init(dev_init); module_exit(dev_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Locker_Liang Inc."); 测试程序: #include #include #include #include #define IOCTL_LED_ON 1 #define IOCTL_LED_OFF 0 void usage(char *exename) { printf("Usage:n"); printf(" %s printf(" led_no = 1, 2, 3 or 4n"); } int main(int argc, char **argv) { unsigned int led_no; int fd = -1; if (argc != 3) goto err; fd = open("/dev/MY_leds", 0); // 打开设备 if (fd < 0) { printf("Can't open /dev/ledsn"); return -1; } led_no = strtoul(argv[1], 0, 0) - 1; // 操作哪个LED? if (led_no > 3) goto err; if (!strcmp(argv[2], "on")) { ioctl(fd, IOCTL_LED_ON,led_no); // 点亮它 } else if (!strcmp(argv[2], "off")) { ioctl(fd, IOCTL_LED_OFF,led_no); // 熄灭它 } else { goto err; } close(fd); return 0; err: if (fd > 0) close(fd); usage(argv[0]); return -1; } 测试指令 ./test 1 on
上一篇:Linux(AT91SAM9260)增加UBIFS文件系统支持
下一篇:移植U-BOOT-2.14.07至MICRO2440开发板
推荐阅读最新更新时间:2024-11-20 11:00
设计资源 培训 开发板 精华推荐
- 具有峰值电流软启动、小尺寸的 LTC3632EMS8E、3.3V、20mA 稳压器的典型应用电路
- ☆半桥测试工程 v0.2
- 使用 Analog Devices 的 LTC2862ACDD-2 的参考设计
- 谐振传感器的磁调谐和提高灵敏度的方法
- VM801B43A-PL,Embedded Video Engine Plus Development Module 支持3.3/5V MCU Adapter Board,预装4.3 - 480x272分辨率TFT LCD显示面板,珍珠边框
- 基于ESP8266 集成显示的温湿度计
- DC1766A-B,使用 LTC6957-3 低相位噪声、双 CMOS 输出缓冲器/驱动器/逻辑转换器的演示板
- LT6656ACS6-2.5、2.5V 升压输出电流电压基准的典型应用
- AD9119-CBLTX-EBZ,使用 AD9119 高性能、11 位、2.8 GSPS 射频数模转换器的评估板
- 【立创开发板】wygxjs的游戏机