上一篇文章我们介绍了字符设备驱动程序的框架,本篇文章我们将操作真实的硬件---LED。
作者:沧海猎人 出处:http://blog.csdn.net/embedded_hunter转载请注明出处 嵌入式技术交流QQ群:179012822
一、实验环境
开发机环境
操作系统:ubuntu 9.10
交叉编译环境:arm-linux-gcc 4.2.2 ,安装位置 /usr/local/arm/4.3.2/
6410板子内核源码路径:/work/linux-2.6.36.2-v1.05/
目标板环境:OK6410-A linux2.6.36
二、实验原理
控制LED是最简单的一件事情,我们学习LED驱动程序,就相当于学习其他编程语言是的“hello world”程序一样,是一个入门的程序。
学习驱动程序,必须要对硬件有所了解,接下来看几个与硬件相关的材料。
OK6410 LED原理图
OK6410 LED原理图
从上面的原理图可以得知,LED与CPU引脚的连接方法如下,低电平点亮。
LED1 -GPM0
LED2 -GPM1
LED3 -GPM2
LED4 -GPM3
从数据手册可以找到相应的控制方法。这里我们以LED1为例,介绍一下LED1的操作方法,其他的类似,请大家自行分析。
通过上面可以得知,需要先将GPM0设置为输出方式。将相应的寄存器进行配置。
然后将GPMDAT寄存器的第0位置0灯亮,置1灯灭。
三、实验步骤
1、编写驱动程序
driver_led.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
|
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h> /* copy_to_user,copy_from_user */
#include <linux/miscdevice.h>
#include <linux/pci.h>
#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-bank-m.h>
#include <plat/gpio-cfg.h>
#define LED_MAJOR 240
int
led_open (
struct
inode *inode,
struct
file *filp)
{
unsigned tmp;
tmp = readl(S3C64XX_GPMCON);
tmp = (tmp & ~(0x7U<<1))|(0x1U);
writel(tmp, S3C64XX_GPMCON);
printk(
"#########open######\n"
);
return
0;
}
ssize_t led_read (
struct
file *filp,
char
__user *buf,
size_t
count,loff_t *f_pos)
{
printk(
"#########read######\n"
);
return
count;
}
ssize_t led_write (
struct
file *filp,
const
char
__user *buf,
size_t
count,loff_t *f_pos)
{
char
wbuf[10];
unsigned tmp;
printk(
"#########write######\n"
);
copy_from_user(wbuf,buf,count);
switch
(wbuf[0])
{
case
0:
//off
tmp = readl(S3C64XX_GPMDAT);
tmp |= (0x1U);
writel(tmp, S3C64XX_GPMDAT);
break
;
case
1:
//on
tmp = readl(S3C64XX_GPMDAT);
tmp &= ~(0x1U);
writel(tmp, S3C64XX_GPMDAT);
break
;
default
:
break
;
}
return
count;
}
int
led_release (
struct
inode *inode,
struct
file *filp)
{
printk(
"#########release######\n"
);
return
0;
}
struct
file_operations led_fops ={
.owner = THIS_MODULE,
.open = led_open,
.read = led_read,
.write = led_write,
.release = led_release,
};
int
__init led_init (
void
)
{
int
rc;
printk (
"Test led dev\n"
);
rc = register_chrdev(LED_MAJOR,
"led"
,&led_fops);
if
(rc <0)
{
printk (
"register %s char dev error\n"
,
"led"
);
return
-1;
}
printk (
"ok!\n"
);
return
0;
}
void
__exit led_exit (
void
)
{
unregister_chrdev(LED_MAJOR,
"led"
);
printk (
"module exit\n"
);
return
;
}
module_init(led_init);
module_exit(led_exit);
|
Makefile文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
obj-m := driver_led.o
KDIR :=/work/linux-2.6.36.2-v1.05/
all:
make -C $(KDIR) M=$(shell pwd) modules
install:
cp driver_led.ko /tftpboot/
clean:
make -C $(KDIR) M=$(shell pwd) clean
obj-m := driver_led.o
KDIR :=/work/linux-2.6.36.2-v1.05/
all:
make -C $(KDIR) M=$(shell pwd) modules
install:
cp driver_led.ko /tftpboot/
clean:
make -C $(KDIR) M=$(shell pwd) clean
|
2、编写测试程序
test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int
main (
void
)
{
int
fd;
char
buf[10]={0,1};
fd = open(
"/dev/my_led"
,O_RDWR);
if
(fd < 0)
{
printf
(
"Open /dev/my_led file error\n"
);
return
-1;
}
while
(1)
{
write(fd,&buf[0],1);
sleep(1);
write(fd,&buf[1],1);
sleep(1);
}
close (fd);
return
0;
}
|
3、编译驱动程序与测试程序
编译驱动程序
#make
将驱动程序放到tftp的工作目录 /tftpboot
#make install
编译测试程序
#arm-linux-gcc test.c -o test
将测试程序放到tftp的工作目录 /tftpboot
#cp test /tftpboot
4、将程序下载到开发板
将开发板的IP地址修改,与主机在同一个网段。确保PC的tftp服务开启。
下载程序到开发板
SMDK6410# tftp -l /lib/modules/2.6.36.2/driver_led.ko -r driver_led.ko -g 192.168.1.111 192.168.1.111为服务器IP
SMDK6410# tftp -l test -r test -g 192.168.1.111
5、测试
加载驱动 #insmod /lib/modules/2.6.36.2/driver_led.ko
创建设备文件 #mknod /dev/my_led c 240 0
测试 ./test
[root@FORLINX6410]# ./test
此时可以看到OK6410板子上的LED0在闪烁。
卸载驱动 #rmmod driver_led
从上面的结果我们可以看到,当用户调用相应的文件操作函数时,驱动程序中的相应的函数也会被调用。
大家可以修改相应程序,测试一下其他的情况。
作者:沧海猎人 出处:http://blog.csdn.net/embedded_hunter转载请注明出处 嵌入式技术交流QQ群:179012822
备注: 本驱动使用了最基本的内核函数,可能操作方法与你看过的其他驱动程序不太一样,尤其注册字符设备的函数。 没关系,我们会一步一步介绍相关的其他函数。