浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52

首先我提一下我过程中遇到的一些问题。

1.led驱动模块的编译

2.led驱动内核模块的安装

3.开发板nand的意外处理

4.当前linux kerner版本与led内核模块版本的一致性

接下来我来谈谈具体我做的一个过程

看原理图之前我先略微的讲下什么上拉电阻下拉电阻,下面会用到

上拉是将不确定的信号通过一个电阻与电源相连,固定在高电平。下拉是将不确定的信号通过一个电阻与地相连,固定在低电平。上拉是对器件注入电流,下拉是对器件导出电流,当一个接有上拉电阻的I/O端口设置为输入状态的时候,它的常态为高电平,可用于检测低电平的输入。

首先看LED的原理图

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第1张图片

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第2张图片

从上面的原理图可以得知,LED与CPU引脚的连接方法如下,低电平点亮。

     LED1 -GPM0

     LED2 -GPM1

     LED3 -GPM2

     LED4 -GPM3

s3c6410包含GPA,GPB,GPC......GPQ17组I/O端口,我这里也没有读完上s3c6410的datasheet,主要看了下led的寄存器的三个功能

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第3张图片

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第4张图片

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第5张图片

 

GPMCON用来设置端口功能,(00表示输入,01表示输出,10表示特殊功能,11表示保留不用),GPMDAT用来读/写数据,GPMPUD用来决定是否内部使用上拉电阻,某位为0时,相应引脚无内部上拉,某位为1时候,相应引脚内部上拉)。

s3c6410_leds.c程序代码

//#include <linux/config.h> /*包涵内核中的多个头文件,以下头文件都是内核当中已经存在的*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <asm/irq.h>
#include <mach/gpio.h>
#include <plat/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/hardware.h>
#include <linux/io.h>
        
#define DEVICE_NAME	"leds" /*定义设备名字为leds*/
#define LED_MAJOR 231 /*定义led主设备号为231*/
        
        
        
static unsigned long led_table [] = {
       S3C64XX_GPM(0),
       S3C64XX_GPM(1),
       S3C64XX_GPM(2),
       S3C64XX_GPM(3),
};
        
static unsigned int led_cfg_table [] = {
	S3C64XX_GPM_OUTPUT(0),
	S3C64XX_GPM_OUTPUT(1),
	S3C64XX_GPM_OUTPUT(2),
	S3C64XX_GPM_OUTPUT(3),
};
/*这里IOCTL的工作大概分两个步骤,定义命令,执行命令,但大家会发现这里并有defineIOCTL,但是我们会发现下面的case0,case1,这里其实是直接用定义为了数字,比如0表示灯灭,1表示灯亮*/
static int s3c6410_leds_ioctl(
	struct inode *inode, 
	struct file *file, 
	unsigned int cmd, 
	unsigned long arg)
{
	unsigned long tmp;
	switch(cmd) {
	case 0:
	case 1:
		if (arg > 4) {
			return -EINVAL;
		}
		tmp = __raw_readl(S3C64XX_GPMDAT);
		if(cmd)
			tmp &= (~(1<<arg));
		else
			tmp |= (1<<arg);
		__raw_writel(tmp,S3C64XX_GPMDAT);
//		gpio_set_value(led_table[arg], !cmd);
		return 0;
	default:
		return -EINVAL;
	}
}
/*struct file_operations函数来定义我们能进行哪些操作,比如IOCTL*/
static struct file_operations s3c6410_leds_fops = {
	.owner	=	THIS_MODULE,
	.ioctl	=	s3c6410_leds_ioctl,
};/*大家可以上去看一下s3c6410_leds_ioctl的定义*/
        
        
static struct cdev cdev_leds;
struct class * my_class;
        
static int __init s3c6410_leds_init(void) /*函数初始化,配置I/O口的模式*/
{
	int ret;
	unsigned long tmp;
	int i;
	dev_t devno;
	printk(KERN_NOTICE "enter s3c6410_leds_init\n");
        
        
	devno = MKDEV(LED_MAJOR,0);
        
	ret = register_chrdev_region(devno,1,DEVICE_NAME);/*注册LED设备*/
	ret = 0;
	if(ret<0)
	{
		printk(KERN_NOTICE "can not register led device");
		return ret;
	}
	        
	cdev_init(&cdev_leds,&s3c6410_leds_fops);
	cdev_leds.owner = THIS_MODULE;
        
	ret =cdev_add(&cdev_leds,devno,1);
	if(ret)
	{
		printk(KERN_NOTICE "can not add leds device");
		return ret;
	}
        
	my_class = class_create(THIS_MODULE,"my_class");
	if(IS_ERR(my_class))
	{
		printk("Err: Failed in creating class\n");
		return -1;	
	}
        
	device_create(my_class,NULL,MKDEV(LED_MAJOR,0),NULL,DEVICE_NAME);
        
	//gpm0-3 pull up
	tmp = __raw_readl(S3C64XX_GPMPUD);
	tmp &= (~0xFF);
	tmp |= 0xaa;
	__raw_writel(tmp,S3C64XX_GPMPUD);
        
	//gpm0-3 output mode
	tmp = __raw_readl(S3C64XX_GPMCON);
	tmp &= (~0xFFFF);
	tmp |= 0x1111;
	__raw_writel(tmp,S3C64XX_GPMCON);
	        
	//gpm0-3 output 0
	tmp = __raw_readl(S3C64XX_GPMDAT);
	tmp |= 0x10;
	__raw_writel(tmp,S3C64XX_GPMDAT);
        
	//printk("S3C64XX_GPMCON is %x\n",__raw_readl(S3C64XX_GPMCON));
	//printk("S3C64XX_GPMDAT is %x\n",__raw_readl(S3C64XX_GPMDAT));
	//printk("S3C64XX_GPMPUD is %x\n",__raw_readl(S3C64XX_GPMPUD));
        
	printk(DEVICE_NAME " initialized\n");
        
	return 0;
}
        
static void __exit s3c6410_leds_exit(void) /*模块卸载函数,大家可以参考我前一篇《字符设备驱动》*/
{
	cdev_del(&cdev_leds); /*删除字符设备结构体*/
        
	unregister_chrdev_region(MKDEV(LED_MAJOR,0),1); /*释放设备号*/
        
	printk(KERN_NOTICE "s3c6410_leds_exit\n");
}
        
        
module_init(s3c6410_leds_init);/*首先从这里开始分析,这里是驱动程序的初始化函数*/
        
module_exit(s3c6410_leds_exit);
        
MODULE_LICENSE("GPL");因为我这里不想直接编译进内核,所以采取内核模块加载的方式把led的内核模块加载进内核,那么我这里就要自己写一个简单的makefile

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第6张图片

makefile这里相对来说还是比较简单的,这个可以当做一个模板,因为需要修改的仅仅是obj -m :=s3c6410_leds.o 我的C文件就是s3c6410_leds.c .还有就是修改KDIR的路径,改成当前内核源码的路径。然后我make,结果出错了

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第7张图片

我看了我下makefile是没问题的,但是看到出错信息里面提示的makefile的第一个字母M是大写Makefile,然后我改了下。结果好了

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第8张图片

接下来我用nfs共享目录来进行led驱动模块向内核进行加载结果是这样

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第9张图片

不能加载,错误提示里面显示不能创建一个类然后我cat /proc/devices 查看了下,我内核里原本已经有一个LED的驱动了

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第10张图片

既然这样吧,我决定重新编译个内核,没有led驱动的内核,make menuconfig里面在device drivers里面的char类驱动设备里面把led的驱动设置为M。然后保存退出,编译,make zImage然后把新的内核烧到板子上去接下来再试试加载内核模块

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第11张图片

好了,终于加载上去了。这里有一个要注意的,你的LED内核模块版本和当前板子上跑的linux系统的内核的版本需要一致才行。如图

浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52_第12张图片

这是查看的板子上linux内核的版本

这是查看的编译的LED驱动模块的版本OK,都是linux2.6.28.6.——————————————————————————————我早上细看了下内核配置,如图

选上这个应该就能解决不同版本的模块的问题了...但不知道是否会出现BUG。

你可能感兴趣的:(浅谈linux驱动(3)-linux系统下led驱动2012年02月21日 23:13:52)