首先我提一下我过程中遇到的一些问题。
1.led驱动模块的编译
2.led驱动内核模块的安装
3.开发板nand的意外处理
4.当前linux kerner版本与led内核模块版本的一致性
接下来我来谈谈具体我做的一个过程
看原理图之前我先略微的讲下什么上拉电阻下拉电阻,下面会用到
上拉是将不确定的信号通过一个电阻与电源相连,固定在高电平。下拉是将不确定的信号通过一个电阻与地相连,固定在低电平。上拉是对器件注入电流,下拉是对器件导出电流,当一个接有上拉电阻的I/O端口设置为输入状态的时候,它的常态为高电平,可用于检测低电平的输入。
首先看LED的原理图
从上面的原理图可以得知,LED与CPU引脚的连接方法如下,低电平点亮。
LED1 -GPM0
LED2 -GPM1
LED3 -GPM2
LED4 -GPM3
s3c6410包含GPA,GPB,GPC......GPQ17组I/O端口,我这里也没有读完上s3c6410的datasheet,主要看了下led的寄存器的三个功能
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
makefile这里相对来说还是比较简单的,这个可以当做一个模板,因为需要修改的仅仅是obj -m :=s3c6410_leds.o 我的C文件就是s3c6410_leds.c .还有就是修改KDIR的路径,改成当前内核源码的路径。然后我make,结果出错了
我看了我下makefile是没问题的,但是看到出错信息里面提示的makefile的第一个字母M是大写Makefile,然后我改了下。结果好了
接下来我用nfs共享目录来进行led驱动模块向内核进行加载结果是这样
不能加载,错误提示里面显示不能创建一个类然后我cat /proc/devices 查看了下,我内核里原本已经有一个LED的驱动了
既然这样吧,我决定重新编译个内核,没有led驱动的内核,make menuconfig里面在device drivers里面的char类驱动设备里面把led的驱动设置为M。然后保存退出,编译,make zImage然后把新的内核烧到板子上去接下来再试试加载内核模块
好了,终于加载上去了。这里有一个要注意的,你的LED内核模块版本和当前板子上跑的linux系统的内核的版本需要一致才行。如图
这是查看的板子上linux内核的版本
这是查看的编译的LED驱动模块的版本OK,都是linux2.6.28.6.