linux驱动之点亮LED(直接映射地址)

基于2440 不过ARM9和ARM11架构相差不大

myled.c

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>

volatile unsigned long *gpbcon = 0;
volatile unsigned long *gpbdat = 0;


static int LED_open(struct inode *inode, struct file *file)
{
	/* 配置GPB 5,6,7,8为输出 */
	*gpbcon &= ~((0x3<<(5*2)) | (0x3<<(6*2)) | (0x3<<(7*2)) | (0x3<<(8*2)));
	*gpbcon |=  ((0x1<<(5*2)) | (0x1<<(6*2)) | (0x1<<(7*2)) | (0x1<<(8*2)));
	//打开设备的时候四个灯是亮的
	*gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
	return 0;
}

static ssize_t LED_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
	int val;
	copy_from_user(&val, buf, count);

	if (val == 1)  //如果写入 1 就点灯
	{
		
		*gpbdat &= ~((1<<5) | (1<<6) | (1<<7) | (1<<8));
	}
	else           //否则 灭灯
	{
		
		*gpbdat |=  ((1<<5) | (1<<6) | (1<<7) | (1<<8));
	}
	
	return 0;
}

static struct file_operations LED_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   LED_open,     
    .write  =	LED_write,	   
};


int major;
static int LED_init(void)
{
	major = register_chrdev(0, "myled", &LED_fops); //参数0表示自动寻找设备号
	gpbcon = (volatile unsigned long *)ioremap(0x56000010, 16);//地址重映射
	gpbdat = gpbcon + 1;

	return 0;
}

static void LED_exit(void)
{
	unregister_chrdev(major, "myled"); // 卸载
	iounmap(gpbcon);
}

module_init(LED_init);
module_exit(LED_exit);
MODULE_LICENSE("GPL");

testled.c

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char **argv)
{
	int fd;
	int val = 1;
	fd = open("/dev/myled", O_RDWR);
	if (fd < 0)
	{
		printf("can't open!\n");
	}
	if (argc != 2)
	{
		printf("Usage :\n");
		printf("%s <on|off>\n", argv[0]);
		return 0;
	}

	if (strcmp("on", argv[1]) == 0)    //如果测试程序输入 on 则,将val的值 1 传入驱动程序
	{
		val  = 1;
	}
	else
	{
		val = 0;
	}
	
	write(fd, &val, sizeof(int));   //sizeof(int) 传给驱动的 count
	close(fd);
	return 0;
}

Makefile

关于makefile,只有myled.o和内核地址需要改改,其他copy下就行了。

执行的make用的内核,是用交叉编译链arm-linux-编译出的。(ARCH=arm CROSS_COMPILE=arm-linux-)

ifneq ($(KERNELRELEASE),)

obj-m := myled.o

else
	
KDIR := /opt/opt/EmbedSky/linux-2.6.30.4
all:
	make -C $(KDIR) M=$(PWD) modules 
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers  modul*

endif


编译好模块后得到myled.ko  和 测试程序testled

insmod myled.ko后,注意要做这件事以后可以让程序自动创建):

mknod /dev/myled c 252 0

我来解释下这句话:

mknod 创建节点

/dev/myled 在/dev下创建一个名为myled的节点

c 表示字符设备

252 0 分别表示主设备号和从设备号。252从何而来呢?---->执行命令cat  /proc/devices

这是你就看到了与myled这个模块对应的主设备号252 .  0呢?就表示这类设备中的第几个,0是我随便写的,不冲突就好。


这时候你就可以测试了。./testled on ---->表示全亮 参数off就是全灭

另外,如果./testled 运行时,告诉你权限不够的话,执行chmod +x testled
,改变模式后,再运行就好了。

写在最后:linux驱动学习的过程是很辛苦的,总会有莫名其妙的错误,大家坚持学习吧,毕竟没有一蹴而就的事情。

你可能感兴趣的:(linux驱动,点亮LED,地址直接映射)