1、查看原理图,得到控制led的管脚GPF4/GPF5/GPF6
2、查看datasheet,找到LED对应的寄存器(GPFCON, GPFDAT)
目的是得到控制寄存器和数据寄存器的地址以及控制寄存器的控制模式选择
该寄存器占用16个字节,其中4个字节是保留字节
3、开始编写LED驱动程序
1
/*my01leds_driver.c*/
2
#include <linux/module.h>
3
#include <linux/kernel.h>
4
#include <linux/fs.h>
5
#include <linux/init.h>
6
#include <linux/delay.h>
7
#include <asm/uaccess.h>
8
#include <asm/irq.h>
9
#include <asm/io.h>
10
#include <asm/arch/regs-gpio.h>
11
#include <asm/hardware.h>
12
13
static struct class *my01drv_leds_class;
14
static struct class_device *my01drv_leds_dev;
15
16
volatile unsigned long *gpfcon = NULL;
17
volatile unsigned long *gpfdat = NULL;
18
19
static int my01drv_leds_open(struct inode *inode, struct file *file)
20
{
21
/* 配置GPF4,5,6为输入引脚 */
22
*gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
23
*gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
24
return 0;
25
}
26
27
static ssize_t my01drv_leds_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
28
{
29
int val;
30
int num=0;
31
copy_from_user(&val,buf,count);/*从用户空间得到的参数val */
32
if(val == 1) //点灯
33
*gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
34
else //关灯
35
*gpfcon |= ((1<<4)|(1<<5)|(1<<6));
36
37
return 0;
38
}
39
40
static struct file_operations my01drv_leds_fops = {
41
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
42
.open = my01drv_leds_open,
43
.write = my01drv_leds_write,
44
};
45
46
int my01drv_leds_major;
47
static int my01_leds_init(void)
48
{
49
/*向内核注册设备,设备名称为 my01drv_leds ,可以在内核目录下的/proc/device下看到设备名称 */
50
my01drv_leds_major = register_chrdev(0, "my01drv_leds", &my01drv_leds_fops);
51
52
/*注册一个设备类 */
53
54
my01drv_leds_class = class_create(THIS_MODULE,"my01drvleds");
55
56
/*注册一个设备号,使mdev可以在/dev/目录下自动建立设备号 /dev/JIJIJI*/
57
58
my01drv_leds_dev = class_device_create(my01drv_leds_class,NULL,MKDEV(my01drv_leds_major,0),NULL,"JIJIJI");
59
60
/*物理地址映射*/
61
62
gpfcon = (volatile unsigned long *)ioremap(0x56000050,16);
63
64
/*确定gpfdat 寄存器的地址*/
65
gpfdat = gpfcon + 1;
66
67
return 0;
68
}
69
static int my01_leds_exit(void)
70
{
71
/*卸载设备*/
72
unregister_chrdev(my01drv_leds_major, "my01drv_leds");
73
/*卸载设备号,先卸载设备号后卸载设备类,否则在卸载模块会出现段错误*/
74
class_device_create(my01drv_leds_dev);
75
/*卸载设备类*/
76
class_destroy(my01drv_leds_class);
77
/*解除地址映射*/
78
iounmap(gpfcon);
79
}
80
81
/* 这两行指定驱动程序的初始化函数和卸载函数 */
82
module_init(my01_leds_init);
83
module_exit(my01_leds_exit);
84
85
/* 描述驱动程序的一些信息,不是必须的 */
86
MODULE_AUTHOR("jishubao");
87
MODULE_VERSION("0.1.0");
88
MODULE_DESCRIPTION("S3C2410/S3C2440 LED Driver");
89
MODULE_LICENSE("GPL");
90
4、编写测试程序
1
#include <sys/types.h>
2
#include <sys/stat.h>
3
#include <fcntl.h>
4
#include <stdio.h>
5
int main(int argc, char **argv)
6
{
7
int val = 1;
8
int fd = open("/dev/JIJIJI",O_RDWR);
9
if(fd < 0)
10
printf("can't open\n");
11
if(argc != 2)
12
{
13
printf("Usage :\n");
14
printf("%s <on|off>\n", argv[0]);
15
return 0;
16
}
17
if (strcmp(argv[1], "on") == 0)
18
{
19
val = 1;
20
printf("LED ON OK\n");
21
}
22
else
23
{
24
val = 0;
25
printf("LED OFF OK\n");
26
}
27
28
write(fd, &val, 4);
29
return 0;
30
}
5、编写Makefile
1
KERN_DIR = /home/fs/linux/linux-2.6.22.6
2
3
all:
4
make -C $(KERN_DIR) M=`pwd` modules
5
6
clean:
7
make -C $(KERN_DIR) M=`pwd` modules clean
8
rm -rf modules.order
9
10
obj-m += my01leds_driver.o
6、编译驱动文件和测试文件,拷贝到单板运行
1
fs@ubuntu:~/linux/mydrvtest/my01leds_driver$ make
1
fs@ubuntu:~/linux/mydrvtest/my01leds_driver$ arm-linux-gcc -o my01leds_test my01leds_test.c
1
fs@ubuntu:~/linux/mydrvtest/my01leds_driver$ sudo cp my01leds_test my01leds_driver.ko /source/rootfs/fs_2440/
1
# insmod my01leds_driver.ko
1
# ./my01leds_test on
若是卸载出现段错误,请注意是否是驱动程序中的卸载设备类与设备顺序是否有误,正确的是先注册类后注册设备,先卸载设备后卸载设备类
7、基本完成工作目的
后期实时更新链接: