首先我提一下我过程中遇到的一些问题。
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.——————————————————————————————我早上细看了下内核配置,如图

选上这个应该就能解决不同版本的模块的问题了...但不知道是否会出现BUG。
本文介绍了一种基于S3C6410处理器的LED驱动开发过程,包括驱动模块的编译与安装、开发板NAND闪存的意外处理等。详细讲述了LED原理图分析、GPIO配置及内核模块与系统版本一致性问题。

被折叠的 条评论
为什么被折叠?



