嵌入式linux驱动开发之点亮led未遂(驱动编程思想之初体验)

有了上两篇文章的基础,我们就可以开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的。个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的住,不然真像一些人说的,学了一年嵌入式感觉还没找到门。

另外实践很重要,一年多以前就知道了arm,那时整天用单片机的思维去yy着arm,直到前段时间弄来一块arm板,烧上linux系统后才知道,坑呀!根本不是那回事,所以实践是学习计算机类最重要的基本素质,如果整天看书,那基本上是做不了开发的。

不能再扯了,涉及到linux的驱动开发知识面灰常广,再扯文章就会变得灰常长。首先还是回到led驱动的本身上,自从linux被移植到arm上后,做驱动开发的硬件知识要求有所降低,很多都回归到了软件上,这是系统编程的一大特点,当然 ,也不排除有很多设备的驱动需要我们从头做起。

led的驱动虽然看似很简单,但是要描述清楚估计上万字都不一定够用,本篇文章从初学者的角度出发,重点关注在整个软件的开发的流程和思想,而不过多局限与细节的分析,初学者应该首先把握某一类编程的流程和思想,这样才能入门快,进步迅速。作为一个初学者我一直这样入门效率最高。

下面我们进入主题led驱动的书写:

既然是在linux系统下设备驱动开发,就不同于以往我们单片机下设置一个高电平而了事,在linux系统下开发的驱动程序要想在linux正常工作,一定要符合linux系统的规范,linux下设备被分为三个类型字符设备/块设备/网络设备。上述的三种设备不可能面面俱到,因此还提出了一个杂设备作为补充,看了下网上大部分人都把led的驱动设备归到了杂设备,这是为什么呢?原来是友善之臂的手册上把它归为了杂设备,哈哈,所以这种杂设备这种版本比较流行,木有追求的人们啊! 那我们把它归为那种设备呢?当然是杂设备了,嘿嘿。。。。。。

既然对led我们准备把它作为一个杂设备加入系统,是不是应该有一个名字吧,还应该有个操作符号吧。。。。。。

stop,停止你的YY,关于这个设备的标准形式大神已经帮你定义好了,具体它存在于系统的include下,里面有一个miscdivice.h。

 1 struct miscdevice  {
 2         int minor;
 3         const char *name;
 4         const struct file_operations *fops;
 5         struct list_head list;
 6         struct device *parent;
 7         struct device *this_device;
 8         const char *nodename;
 9         mode_t mode;
10 };

好吧,原来为了统一规范,我们只需按照标准来填充内容就可以啦!要想正确的使用这个描述设备的结构体,必须清楚的了解到太其中的每个成员。天空飘来四个字 f u c k

好,让我们平复一下心情,继续了解它。查了下minor这个单词是次要的,在这里是次设备号的意思。这是因为杂设备为了节约主设备号,采用公用主设备号的方式,次设备号加以区分的方式来描述设备,因此来看这个minor是要必须填写啦!不愧是过了四级的人,第二个直接看懂啦!欧耶~

之前做过一个了解,这第三个在linux驱动中非常的重要,可以称之为核心,我们很大的工作 都要围绕这个file_operations来操作,因此必须要隆重的研究下这个file_operatios这个结构体。

file_operations这个结构体的存在是linux中将所有设备看做文件的基础,这是为什么呢?因为通俗的说就是这个结构体是文件操作和驱动操作的一个关系映射,对于系统的操作函数(诸如read/write)在这个结构体里都有与之对应的对硬件进行操作的函数。wow这个函数居然如此之酷!这样以来,我们还弄清楚了另外一个问题,就是为什么我们不能直接越过操作系统来操作硬件,都是因为有它啊!可见这个结构体在内核中的地位,以及在linux操作系统中的地位。哈哈下面的几个成员,就先不分析啦!我们这次也用不上,感觉在linux下开发驱动真是个力气活啊!

见过file_operations的厉害之后,我们自然知道 现在只要把这个结构体弄清楚就可以敲代码写驱动啦!so,let‘s go!

首先在系统目录include/linux/fs.h中找到这个牛逼的结构体:

 1 struct file_operations {
 2         struct module *owner;
 3         loff_t (*llseek) (struct file *, loff_t, int);
 4         ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
 5         ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
 6         ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 7         ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
 8         int (*readdir) (struct file *, void *, filldir_t);
 9         unsigned int (*poll) (struct file *, struct poll_table_struct *);
10         int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
11         long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
12         long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
13         int (*mmap) (struct file *, struct vm_area_struct *);
14         int (*open) (struct inode *, struct file *);
15         int (*flush) (struct file *, fl_owner_t id);
16         int (*release) (struct inode *, struct file *);
17         int (*fsync) (struct file *, struct dentry *, int datasync);
18         int (*aio_fsync) (struct kiocb *, int datasync);
19         int (*fasync) (int, struct file *, int);
20         int (*lock) (struct file *, int, struct file_lock *);
21         ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
22         unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
23          int (*check_flags)(int);
24         int (*flock) (struct file *, int, struct file_lock *);
25         ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
26         ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
27         int (*setlease)(struct file *, long, struct file_lock **);
28 };

当我知道有这么多成员的时候,当时我就尿了,不过,还好我们只需要实现本次驱动需要的东东!查看了一下手册上的驱动,欧耶~这次我们只需要研究两个成员ower和ioctl。上面可以看出ower是一个结构体成员就是是那个模块,而ioctl是个函数。

那为什么是ioctl函数呢?这个问题弄清楚灰常重要!因为我们前边说过file_operations内的函数是对文件操作的的映射,我们要控制led,实质是控制IO口的电平,这点不管它操作系统的驱动还是单片机的驱动实质都是一样的啦!此时,我瞬间明白鸟,icctl不就是IO Control的缩写吗?不就是文件的控制IO吗?因为我们要控制led,所以需要控制IO口,要控制IO对应的系统调用函数不就是ioctl吗?我们在驱动中需要做的就是给这个ioctl函数进行编写,然后系统就可以通过调用ioctl这个系统函数来通过file_operactions来关联到真正的驱动函数。欧耶 ~      终于都清楚啦!

现在,软件方面的核心都打通啦!既然是驱动,当然少不了硬件啦!

这四个led与固定的四个IO口连接,像这样的东西开发板原理图上是交代的很清楚的,我们必须按照开发板上的关系来进行。这四个led占用的IO寄存器分别为GPB5/GPB6/GPB7/GPB8;这就 好比当年我们单片机的P0/P1一样一样的,P0/P1都被定义在了reg51.h里面,而这里的IO口同样也被定义到了<arch/regs-gpio.h>里,这里我们声明这四个IO口寄存器,这样我们操作IO口就可以控制led了。

1 static unsigned long led_table []= {
2         S3C2410_GPB(5),
3         S3C2410_GPB(6),
4         S3C2410_GPB(7),
5         S3C2410_GPB(8),
6 };

这里需要注意的是与led连接的GPIO口可以用于输入/输出或者其他功能,我们的开发板上led接的是共阳极的,所以我们需要这些GPIO口作为输出口,只要我们输出低电平就可以让led亮了。既然这个IO口有多种功能,那一定有相关的配置寄存器 。所以我们需要将每个led对应的寄存器定义为输出。

1 static unsigned int led_cfg_table  []={
2         S3C2410_GPIO_OUTPUT,
3         S3C2410_GPIO_OUTPUT,
4         S3C2410_GPIO_OUTPUT,
5         S3C2410_GPIO_OUTPUT,
6 };

做好了前面的所有工作,现在就是驱动的核心了。如何控制 ?这的确是一个question?这时忽然想起了,前面分析的file_operations,这个玩意不就是系统和驱动的纽带吗?控制led系统需要ioctl函数,所以在flie_operations中我们也需要一个和驱动直接联系的ioctl函数,那么我们命名这个函数为heatnan_leds_ioctl;函数的原型前面file_operaction 中已经给出了,有种直接领表填单的赶脚!而且下面的开发思路也都清晰起来,那就是需要什么样的功能直接参考file_operactions结构体的参数模型就是了。

针对于led的驱动,要实现应用程序控制led的亮灭——>需要系统调用ioctl函数——>要使系统的ioctl函数能够控制硬件——>需要在file_operations中建立一个真正控制led驱动的函数——>新建控制led的函数(这里命名为heatnan_leds_ioctl).

首先我们建立连接关系:

1 static struct file_operations dev_fops={
2         .owner = THIS_MODULE,
3         .ioctl= heatnan_leds_ioctl,
4 };

建立这个核心纽带后,就要书写heatnan_leds_ioctl这个函数啦!

要书写这个函数必须要对这个函数的两种形式有所了解,即需要对ioctl函数做功课,ioctl在系统函数中有三个参数,第三个参数可选,第一个参数代表操作设备的号,第二个参数代表操作命令,第三个可选参数可以以不同数据类型作为参数传递也不是必须的。

与之在file_operactions中对应的ioctl则多了一个参数,它的前两个参数对应系统函数的第一个参数,控制命令则进行原封不动的接收。

 1 static int heatnan_leds_ioctl(
 2         struct inode *inode,
 3         struct file *file,
 4         unsigned int cmd,
 5         unsigned long arg)
 6 {
 7        if(arg<0||arg>3)
 8        {
 9            return -EINVAL;
10        }
11        switch(cmd)
12        {
13             case 0:
14             case 1:
15                 {
16                       s3c2410_gpio_setpin(led_table[arg],!cmd);
17                       return 0;
18                 }
19             default:
20                       return -EINVAL;
21
22        }
23  }

这个函数一旦建立,我们就可以通过通过操作系统的ioctl函数来间接操作heatnan_leds.ioctl,从而控制led。

下面就是一些程式化的东西了,模块的初始化,模块的退出以及设备的注册等一些列比较俗的问题了。

×××××××××××××××××××累的赶脚,活剥不起来la,linux驱动开发感觉累累的!@

下面贴出整个led驱动代码,主要是仿照数据手册写的,这次主要是入门的学习以及对驱动开发的理解。明天就要进入脱离数据手册的驱动编写辣!好赤鸡的感觉!

 1 #include<linux/kernel.h>
 2 #include<linux/module.h>
 3 #include<linux/miscdevice.h>
 4 #include<linux/gpio.h>
 5 #include<linux/fs.h>
 6 #include<linux/init.h>
 7 #include<mach/regs-gpio.h>
 8 #include<mach/hardware.h>
 9
10 #define DEVICE_NAME "heat_leds"
11
12
13 static unsigned long led_table []= {
14         S3C2410_GPB(5),
15         S3C2410_GPB(6),
16         S3C2410_GPB(7),
17         S3C2410_GPB(8),
18 };
19 static unsigned int led_cfg_table  []={
20         S3C2410_GPIO_OUTPUT,
21         S3C2410_GPIO_OUTPUT,
22         S3C2410_GPIO_OUTPUT,
23         S3C2410_GPIO_OUTPUT,
24 };
25 static int heatnan_leds_ioctl(
26         struct inode *inode,
27         struct file *file,
28         unsigned int cmd,
29         unsigned long arg)
30 {
31        if(arg<0||arg>3)
32        {
33            return -EINVAL;
34        }
35        switch(cmd)
36        {
37             case 0:
38             case 1:
39                 {
40                       s3c2410_gpio_setpin(led_table[arg],!cmd);
41                       return 0;
42                 }
43             default:
44                       return -EINVAL;
45
46        }
47 }
48 static struct file_operations dev_fops={
49         .owner = THIS_MODULE,
50         .ioctl= heatnan_leds_ioctl,
51 };
52 static struct miscdevice misc={
53         .minor=MISC_DYNAMIC_MINOR,
54         .name=DEVICE_NAME,
55         .fops=&dev_fops,
56 };
57 static int __init led_init(void)
58 {
59         int ret;
60         int i;
61         for(i=0;i<4;i++)
62         {
63                 s3c2410_gpio_cfgpin(led_table[i],led_cfg_table[i]);
64                 s3c2410_gpio_cfgpin(led_table[i],0);
65         }
66         ret=misc_register(&misc);
67         printk(DEVICE_NAME"\tinitialized\n");
68         return ret;
69 }
70 static void __exit led_exit(void)
71 {
72         misc_deregister(&misc);
73 }
74 module_init(led_init);
75 module_exit(led_exit);
76 MODULE_LICENSE("GPL");

终于tmd写完了这个驱动函数,可如果事情到这里结束那也是相当完美啊!按照昨天的方法顺利把驱动模块化编译成功弄到了开发板上,

结果

。。。。。。。。

结果,烧上驱动测试程序后灯真的没亮,我用lsmod和dev下豆发现了模块正在运行,灯就是不亮。。。。。。

后来我猜想可能是开发板的内核中已经有了led驱动,我这个led驱动和那个led驱动本质是一样的只是名字不一样那个,应该是冲突了。。。。。

于是卸下屏幕观看,开机,灯全亮,加载我的驱动,灯全灭,欧耶~看来我猜的有道理???????

好了,不说那些桑心的事啦!整理整理发型,明天继续ds18b20,尼玛这个驱动板子上没有,嘿嘿!我让你冲突。。。。

等串口线回来了,一定要把这个问题弄清楚,看是不是这样的!如果有遇到这个问题的大牛不妨分享下经验,灰常感谢!

驱动编程有感:

1 驱动编程有种瞻前顾后的感觉,在linux系统中的编程,眼光停留在硬件上远远不够的,还要注意linux系统的外部接口,只有这样才能做到外部接口和自己写的驱动接口完美衔接。

2 感觉驱动开发就像带着脚镣跳舞,底层的硬件的基本相关函数,linux已经支持,上层的系统接口,linux系统也已经制定了,我们能做的,就是再硬件和系统之间合理周旋。是一个非常考验人的活!前期感觉是累,后期恐怕就是智商挑战了吧!

3 分析linux驱动的时候,可以适当采用倒叙分析,先从操作系统接口开始,一步一步找到和硬件完美的契合点。。。

时间: 12-20

嵌入式linux驱动开发之点亮led未遂(驱动编程思想之初体验)的相关文章

驱动编程思想之初体验 --------------- 嵌入式linux驱动开发之点亮LED

这节我们就开始开始进行实战啦!这里顺便说一下啊,出来做开发的基础很重要啊,基础不好,迟早是要恶补的.个人深刻觉得像这种嵌入式的开发对C语言和微机接口与原理是非常依赖的,必须要有深厚的基础才能hold的住,不然真像一些人说的,学了一年嵌入式感觉还没找到门. 不能再扯了,涉及到linux的驱动开发知识面灰常广,再扯文章就会变得灰常长.首先还是回到led驱动的本身上,自从linux被移植到arm上后,做驱动开发的硬件知识要求有所降低,很多都回归到了软件上,这是系统编程的一大特点,当然 ,也不排除有很多

嵌入式Linux裸机开发(一)——点亮Led

嵌入式Linux裸机开发(一)--点亮Led 开发板:友善之臂smart210 一.电路图查阅 1.底板电路图 查阅开发板底板电路图,查阅LED相关部分 LED电路工作原理: LED的正极接3.3V,负极接地时导通,LED发光. 开发板共有四颗LED,正极接3.3V,负极接开发板的LED1_LED4引脚,如果LED要点亮则需要输入低电平. 2.核心板电路图 查阅核心板电路图可知,LED1-LED4接在SoC的GPJ2_0-GPJ2_3,通过控制GPJ2_0-GPJ2_3的寄存器使对应GPIO引脚

【视频】嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发

嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发 专题简介:自1971年,美国人SamHurst发明了世界上第一个触摸传感器以来,触摸屏技术不断革新,给了程序设计师和UI工程师无限的想象空间,它极大改善了终端用户对各种设备的操作方便程度,现在我们的日常生活如手机.平板等,已经很大程度上依赖于和习惯于使用和操作触摸屏.做为工程师,我们很有必要掌握触摸屏的工作原理和软件驱动方法,如果您对一窥如何在嵌入式中操控和使用触摸屏这一司空见惯却又神奇的技术感兴趣,敬请关注! 1.LINUX驱动

驱动开发读书笔记. 0.06 嵌入式linux视频开发之预备知识

驱动开发读书笔记. 0.06  嵌入式linux视频开发之预备知识 由于毕业设计选择了嵌入式linux视频开发相关的项目,于是找了相关的资料,下面是一下预备知识 UVC : UVC,全称为:USB video class 或USB video device class.是Microsoft与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,目前已成为USB org标准之一. UVC linux driver: UVC linux 驱动 需要在编译内核的时候选上 配置内核 Device

嵌入式Linux裸机开发(十五)——LCD

嵌入式Linux裸机开发(十五)--LCD 一.LCD简介 LCD(Liquid Crystal Display)是液晶显示器简称.LCD的构造是在两片平行的玻璃基板当中放置液晶盒,下基板玻璃上设置TFT(薄膜晶体管),上基板玻璃上设置彩色滤光片,通过TFT上的信号与电压改变来控制液晶分子的转动方向,从而达到控制每个像素点偏振光出射与否而达到显示目的. 1.LCD类型 按照背光源的不同,LCD可以分为CCFL和LED两种. A.CCFL 指用CCFL(冷阴极荧光灯管)作为背光光源的液晶显示器(L

嵌入式Linux裸机开发(五)——SDRAM初始化

嵌入式Linux裸机开发(五)--SDRAM初始化 一.SDRAM初始化流程 S5PV210有两个独立的DRAM控制器,一个最大支持512MB,一个最大支持1024MB,但两个控制器必须支持相同类型的内存. 根据三星S5PV210文档可知,DDR2类型内存的初始化流程如下: 1.提供稳压电源给内存控制器和内存芯片,内存控制器必须保持CLE在低电平,此时就会提供稳压电源.注:当CKE引脚为低电平时,XDDR2SEL应该处于高电平 2.根据时钟频率正确配置PhyControl0.ctrl_start

嵌入式Linux裸机开发(十)——SD卡启动

嵌入式Linux裸机开发(十)--SD卡启动 存储设备分类: 磁存储设备:软盘.硬盘.光盘.CD.磁带 Flash:NandFlash.NorFlash 缺点:时序复杂,无坏块处理机制,接口不统一 NandFlash:MLC(可靠性差,容量大).SLC(可靠性高.容量小) 扩展卡式Flash:SD卡.MMC卡.MicroSD(TF卡) 内部为NnadFlash存储颗粒,外部封装了接口,接口标准统一.通用. 缺点:频繁使用导致卡槽接触不可靠 iNand.MoviNand.eSSD: 内部为Nand

嵌入式Linux裸机开发(九)——S5PV210定时器

嵌入式Linux裸机开发(九)--S5PV210定时器 S5PV210内部一共有四类定时器. 一.PWM定时器 1.PWM定时简介 S5PV210内部共有5个32bit的PWM定时器.PWM定时器可以生成内部中断.PWM定时器0.1.2.3具有PWM功能,可以驱动外部I/O信号.PWM定时器4是一个无外部引脚的内部定时器.PWM 定时器使用 PCLK_PSYS 作为时钟源. 每个定时器有一个由定时器时钟驱动的32位递减计数器.递减计数器的初始值是由TCNTBn自动装载而获得的.如果递减计数器减到

嵌入式Linux裸机开发(四)——重定位relocate

嵌入式Linux裸机开发(四)--重定位relocate 一.位置有关编码 汇编源文件被编译成二进制可执行程序时编码方式可能与内存地址有关,也可能与内存地址无关.与内存地址有关的为位置有关编码,与内存地址无关的为位置无关编码. 程序在设计时需要规划一个程序运行时的地址(链接地址),编译连接器在链接时必须指定这个链接地址,得到的二进制程序的程序理论规划的运行时地址和编译连接器指定的链接地址才相同,程序才能正常运行.位置无关编码程序则无需设计程序时规划运行时地址,编译链接器链接时同样无需指定链接地址