OK6410字符设备驱动点亮LED

字符设备驱动模型分为三步:

第一步:驱动设备初始化 (分配cdev、初始化cdev、注册cdev、硬件初始化)

第二步:实现设备操作(读写以及控制设备)

第三步: 驱动注销

虽然这是一个最简单的驱动,而且之前也曾经写过两遍,时隔大半年,今天回过头来重新整还是一波三折!感谢那些热心无私的同学的帮助!

先贴下原理图和相关芯片手册上的一些要用到的东西!

这里LED连接的GPIO引脚 输出高电平是LED关 输出低电平LED亮, LED电路部分的这里就不截图了

OK6410字符设备驱动点亮LED_第1张图片

然后去查看这个GPM的相关寄存器:

OK6410字符设备驱动点亮LED_第2张图片

这里贴上代码:

led.c

#include<linux/module.h>
#include<linux/init.h>
#include<linux/cdev.h>
#include<linux/fs.h>
#include<linux/io.h> /* writel */
#include"led.h"


#define GPMCON  0x7F008820 //LED对应的GPIO控制寄存器地址
#define GPMDATA 0x7F008824

unsigned int *led_config;//LED控制寄存器
unsigned int *led_data;//LED数据寄存器 0表示打开LED 1表示关闭LED

struct cdev cdev_led; //这里定义一个cdev结构体变量
dev_t devno; //用来申请主设备号的变量

//这里硬件操作放到led_open函数中
//控制LED 设置GPIO为输出模式(放到open中) 然后控制GPIO(放到ioctl中)
int led_open(struct inode *node, struct file *filp)
{
	//unsigned int reg;
	led_config = ioremap(GPMCON, 4);//将物理地址转化为虚拟地址,4个字节
	
	//reg = readl(led_config);
	//reg |= 0x1111;
	
	writel(0x1111, led_config);//将值写入配置寄存器 设置为输入
	
	led_data = ioremap(GPMDATA, 4);//将物理地址转化为虚拟地址,4个字节
	
	return 0;

}

//第一步定义命令 第二步实现命令 命令一般放在.h头文件中
long led_icotl(struct file *filp, unsigned int cmd, unsigned long arg)
{
	switch(cmd)
	{
		case LED_ON:
			writel(0x00,led_data);//向32位寄存器中写入一个值
			return 0;
		case LED_OFF:
			writel(0xFF,led_data);//向32位寄存器中写入一个值
			return 0;
		default:
			return -EINVAL;
	}
}

static const struct file_operations led_fops = 
{
	.open = led_open,
	.unlocked_ioctl = led_icotl,
};
/*
struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
	int (*readdir) (struct file *, void *, filldir_t);
	unsigned int (*poll) (struct file *, struct poll_table_struct *);
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
	int (*mmap) (struct file *, struct vm_area_struct *);
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, int datasync);
	int (*aio_fsync) (struct kiocb *, int datasync);
	int (*fasync) (int, struct file *, int);
	int (*lock) (struct file *, int, struct file_lock *);
	ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
	unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
	int (*check_flags)(int);
	int (*flock) (struct file *, int, struct file_lock *);
	ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
	ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
	int (*setlease)(struct file *, long, struct file_lock **);
	long (*fallocate)(struct file *file, int mode, loff_t offset,
			  loff_t len);
};
*/

static int led_init()
{
	cdev_init(&cdev_led, &led_fops);//初始化cdev_led结构;两个参数
	
	alloc_chrdev_region(&devno, 0, 1, "myled");//分配主设备号 采用动态分配方式 
	
	cdev_add(&cdev_led, devno, 1);//注册主设备号
	
	return 0;
}

static void led_exit()
{
	cdev_del(&cdev_led);//第一步 注销设备
	
	unregister_chrdev_region(devno, 1);//第二步 将申请到的主设备号也注销掉
}


module_init(led_init);
module_exit(led_exit);

led.h

//点亮 熄灭 这里定义两个 而且都不需要传参 具体可以结和前一课

#define LED_MAGIC  'L'
#define LED_ON     _IO(LED_MAGIC, 0)
#define LED_OFF    _IO(LED_MAGIC, 1)

Makefile

obj-m := led.o
KDIR := /home/kernel/linux-ok6410
all:
	make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm
clean:
	rm -f *.ko *.o *.mod.o *.mod.c *.symvers *.bak *.order
	

led_app.c

#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include<unistd.h>
#include<stdio.h>
#include"led.h"

int main(int argc, char *argv[])
{
	int fd;
	int cmd;
	
	if(argc < 2)
	{
		printf("please enter the second para! \n");
		return 0;
	}
	
	cmd = atoi(argv[1]);
		
	fd = open("/dev/myled", O_RDWR);
	if(cmd == 1)
		ioctl(fd, LED_ON);
	else
		ioctl(fd, LED_OFF);
	
	return 0;
}

步骤还是一样 现在linux下交叉编译 make  生成led.ko模块 因为我的板子上一些库还没有移植,所以应用程序采用静态编译的方法编译 arm-linux-gcc -static led_app.c -o led_app

同上一篇一样,这里目标文件通过nfs同步到开发板!

insmod led.ko

然后cat /proc/devices 查看myled(上面程序中给led字符设备取的名字)所对应的设备号

然后 maknod /dev/myled c 设备号 0

然后运行应用程序 ./led_app 1 点亮led! 

这里发张点亮led的效果图片:

OK6410字符设备驱动点亮LED_第3张图片

顺便说说前面我驱动程序中应为粗心写错了一个地方:

OK6410字符设备驱动点亮LED_第4张图片

针对上面的程序相信看懂了吧!然后运行应用程序时就出现了这样的情况!

OK6410字符设备驱动点亮LED_第5张图片

这里面有很多有用的信息可以供我们分析错误,这里发一个链接,别人写的一篇博客 根据上面的信息来分析问题找出原因所在:http://blog.csdn.net/kangear/article/details/8217329

点击打开链接


你可能感兴趣的:(嵌入式,linux驱动,ioctl,字符设备驱动,ARM11)