嵌入式Linux字符设备驱动LED驱动编写

转载 2016年06月01日 21:50:16

嵌入式Linux字符设备驱动LED驱动编写

标签: linux内核
 105人阅读 评论(0) 收藏 举报
 分类:
 

嵌入式Linux字符设备驱动LED驱动编写嵌入式Linux字符设备驱动开发总结--LED驱动

作者:英贝得教育02就业班   杨广东

 

设备驱动程序是集成在内核中,处理硬件或操作硬件控制器的软件。字符设备还是块设备都为内核提供相同的调用接口,所以内核能以相同的方式处理不同的设备。字符设备提供给应用程序流控制接口有:open/read/write/ioctl/……,添加一个字符设备驱动程序,实际上是给上述操作添加对应的代码

模块的概念:linux内核模块是一种可以被内核动态的加载和卸载的可执行的二进制代码。通过内核模块可以扩展内核的功能,通常内核模块被用于设备驱动,文件系统等。如果没有内核模块,需要向内核添加功能就需要修改代码,重新编译内核,安装新内核等,不仅繁琐而且容易出错,不易于调试。Linux内核是一个整体结构,各种功能结合在一起,linux内核的开发者设计了内核模块机制,从代码的角度看,内核模块是一组可以完成某种功能的函数集合。从执行的角度看,内核模块是一个已经编译但没有连接的程序。内核模块类似应用程序,但是与普通应用程序有所不同,区别在与:

运行环境不同

功能定位不同

函数调用方式不同

 

Linux设备驱动程序与外界的接口可以分为如下3个部分:

1.驱动程序与内核操作系统内核的接口:通过数据结构:file_operation来完成的

2.驱动程序与系统引导内核的接口:利用驱动程序对设备进行初始化

3.驱动程序与设备的接口:描述了驱动程序如何与设备进行交互

 

一.字符驱动的具体流程:

 

所需要的头文件和宏定义:

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/fs.h>

#include <linux/cdev.h>

#include <linux/types.h>

#include <linux/init.h>

#include <linux/delay.h>

#include <asm/irq.h>

#include <mach/regs-gpio.h>

#include <asm/io.h>

#define LED_MAJOR 100 //主设备号

#define LED_SECOND 5 //次设备号

#define IOCTL_LED_ON 0

#define IOCTL_LED_OFF 1

struct cdev led_cdev0; //定义cdev结构体,内核是通过这个结构体来访问驱 //动程序的

dev_t led_t = 0; //无整型32位

 

1.定义设备结构体变量:

 

因为内核是通过cdev结构体来访问驱动的,所以要定义结构体:

struct cdev my_cdve0;

定义好描述字符IO设备的结构体后,就用该结构体来定义一个变量,在内核中就用该变量来代表我们的字符IO设备,在这里就代表的是LED灯。

 

2.定义设备的操作接口和编写接口操作函数:

 

每个设备都对应一些操作,应用程序及是通过这些接口操作函数来使用驱动程序完成对设备的控制,设备的操作接口定义如下:

Struct file_operation led_ops = {

.ownr = THIS_MODULE; //一个宏

.open = myled_open,

ioctl = myled_ioctl,

.release = myled_close,

};

 

相应函数的实现:

static int leds_open(struct inode *inode, struct file *file)

{

int i;

i = (1 << 10) + (1 << 12) + (1 << 16) + (1 << 20);

writel(i,S3C2410_GPBCON); //S3C2410_GPBCON在头文件<mach/regs-gpio.h>

//中已定义

i = 0x0; //灯全亮

writel(i,S3C2410_GPBUP); //在内核中读写函数用writel()和readl();

i = 0xfffffffe; //灯灭

writel(i,S3C2410_GPBDAT);

return 0;

}

 

tatic int leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{ //内核中的参数 //应用程序中传过来的参数

switch(cmd)

{

case IOCTL_LED_ON :

writel(0xffffffde,S3C2410_GPBDAT);

break;

case IOCTL_LED_ONa :

writel(0xffffffbe,S3C2410_GPBDAT);

break;

case IOCTL_LED_ONb:

writel(0xfffffefe,S3C2410_GPBDAT);

break;

case IOCTL_LED_ONc:

writel((~(0x01<<10)&0xfffffffe),S3C2410_GPBDAT);

break;

case IOCTL_LED_ONd:

writel(0x0,S3C2410_GPBDAT);

break;

case IOCTL_LED_OFF:

writel(0xfffffffe,S3C2410_GPBDAT);

break;

default:

break;

}

return 0;

}

 

一个LED灯对应一个字符设备驱动;

tatic int leds_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

{ //内核中的参数 //应用程序中传过来的参数

Dev_t cur_dev_no = inode->i_rdev; //主设备号

Dev_t minordev = MINOR(inode->i_rdev); //次设备号

If(minordev == 0) //表示是第一个灯的驱动,后面以此类推

{

int i = 0;

switch(cmd)

{

case IOCTL_LED_ON:

writel((~(0x01)<<5)&&0xfffffffe,S3C2410_GPBDAT);

Break;

case IOCTL_LED_OFF:

writel(0xfffffffe,S3C2410_GPBDAT);

break;

default:

break;

}

}

If(minordev == 1) //表示是第二个灯的驱动,后面以此类推

{

……

}

return 0;

}

 

3.字符设备驱动模块的初始化和退出函数:

 

字符驱动模块是一个linux模块,所以要遵循linux模块的框架。首先查看自己定义的主设备号在内核中是否已被占用,初始化字符设备结构体变量cdev,向内核注册该字符IO设备驱动,分别通过调用cdev_init(),cdev_add()函数来实现,对于字符驱动模块初始化函数定义如下:

 

Static int__init myled_init(void)

{

led_t = MKDEV(LED_MAJOR,LED_SECOND);

re = register_chrdev_region(LED_MAJOR,1,"my"); //查看此设备号是否已被占用返回值等于0分配成功

if (re < 0)

{

printk(" can't register major number\n");

return -1;

}

cdev_init(&led_cdev,&leds_fops); //初始化cdev结构体

r = cdev_add(&led_cdev,led_t,4); //注册到内核中 4 为要共用一个设备驱动的次设备号

if(r < 0)

{

printk("add wrong\n");

return -1;

}

return 0;

}

 

字符模块退出函数组要就是删除内核中的字符设备:

Static void__exit myled_exit(void)

{

Cdev_del(&led_cdev);

Printk("myled exit now\n");

}

 

完成了模块的初始化和退出函数后,最后向内核申明myled_init和lyled_exit函数以及申明模块license:

module_init(leds_init); 初始化字符驱动设备

module_exit(leds_exit);

MODULE_LICENSE("GPL"); 基于GPL库开发的软件在用到GPL库的这些代码必须开源

 

二.测试程序:

 

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

 

#define IOCTL_LED_ON 0

#define IOCTL_LED_OFF 1

 

int main(int argc, char **argv)

{

int fd = 0;

fd = open(argv[2],0);

if(strcmp(argv[1],"on")==0)

{

ioctl(fd,IOCTL_LED_ON);

}

else if(strcmp(argv[1],"off")==0)

{

ioctl(fd,IOCTL_LED_OFF);

}

return 0;

}

 

如果是一个LED灯一个驱动,测试程序中的fd = open("/dev/ledS0",0)应改为fd = open(argv[2],0);用命令行的模式:

./LES_TEST on /dev/ledS0 这是第二个LED驱动

./LES_TEST on /dev/ledS1这是第二个LED驱动

三.Makefile:

 

obj-m:=dri_led.o

KDIR = /home/kernel/linux-2.6.30.9 //将要使用的内核

 

all:

make -C $(KDIR) SUBDIRS=$(shell pwd) modules

arm-linux-gcc -o led_test led_test.c //对led_test.c进行交叉编译,否则在开发板上不能运行

 

clean:

@rm -rf dri_led *.o

 

 

四.字符IO设备驱动程序测试:

 

Leds0驱动,测试程序,makefile写好之后,把linux-2.6.30.9内核重新编译,通过超级终端把生成的zImage下载到开发板kernel中区(开发板起来之后,6-4-Y-1-4),把在linux下编译好的测试程序的二进制文件和生成的KO文件下载到开发板中去(通过超级终端的菜单栏中的“传送”-“发送文件”),这时开发板中已经有所需要用到的文件。

1.往内核添加驱动模块:

在终端shell下通过执行 Insmod myled.ko

 

2.改变环境变量,如下:

在终端shell下通过执行chmod来改变环境变量

chmod +x ledtest(把测试程序改成可执行的二进制文件)

 

3.创建设备文件节点(一个LED灯一个驱动):

在终端shell下通过执行mknod命令创建文件节点

增加节点:mknod /dev/ledS0 c 100 0

增加节点:mknod /dev/ledS1 c 100 1

增加节点:mknod /dev/ledS2 c 100 2

增加节点:mknod /dev/ledS3 c 100 3

 

/dev/ledS0:字符设备名,包括路径

C :设备类型,c表示字符设备

100 :主设备号

0 :次设备号

 

4.在shell下执行led_test测试应用程序:

./LES_TEST on /dev/ledS0

 

五:总结

 

应用程序通过调用API:open(),ioctl()函数来访问内核,ioctl函数传过来的设备号,到内核中指针数组找相对应主设备号的,通过这个设备号找到该设备的结构体然后调用相应的函数以实现相应的功能:

 

应用层:

fd = open("/dev/ledS0",0);

ioctl(fd,IOCTL_LED_OFF);

 

内核层:

标准输入

标准输出

出错

 

 

&cdev

 

 

0

1

2

 

 

100 //主设备号与cdev结构体的首地址

Cdev 结构体

Struct file_operation*ops

Struct moduleowner

Dev_t dev

Struct list_head list

 

 

 

 

 

*open

*write

*ioctl

*read

 

 

 

Struct file_opertion*ops:(对应着相应的实现函数)

 

 

 

 

 

 

六.配置menucofig

 

1.把要加到内核中的驱动文件拷贝到drivers/char目录下

2.修改kconfig文件添加如下代码:

config HT_LED

Bool "LED"

default y

---help---

This is my first drive -LED!

3.修改makefile添加如下代码:

obj-$(CONFIG_HT_LED) +=dri_led.o

 

 

配置成功,重新编译生成zImage

相关文章推荐

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

现在,我们来编写自己第一个字符设备驱动 —— 点亮LED。 硬件平台:Exynos4412(FS4412) 编写驱动分下面几步: a -- 查看原理图、数据手册,了解设备的操作方法; b -- 在...

嵌入式驱动编写-点亮LED驱动程序

在开发板上,有三个LED灯.如何通过应用程序点亮这三个灯如何编写驱动程序 操作硬件的时候,我们需要准备开发板的原理图和开发手册,,根据这两个文档来进行配置 ...

嵌入式Linux字符设备驱动LED驱动编写

嵌入式Linux字符设备驱动LED驱动编写嵌入式Linux字符设备驱动开发总结--LED驱动 作者:英贝得教育02就业班   杨广东   设备驱动程序是集成在内核中,处理硬件或操作硬件控...

嵌入式Linux字符设备入门之--LED驱动详解

一、实验环境PC机:Redhat9.0  内核版本:Linux2.4开发板:FL2440     内核版本:Linux2.6      CPU:s3c2440二、实验步骤1、硬件连接。       F...

嵌入式Linux之字符设备驱动

嵌入式Linux之字符设备驱动本文档从Linux字符设备入手,描述一个字符设备在内核编译产生,进而在应用层被调用的过程。通过对字符设备的研究,一窥Linux设备驱动程序的工作机制。...
  • iemink
  • iemink
  • 2016年01月31日 15:31
  • 191

嵌入式Linux字符设备驱动模型详解

在Linux系统中,设备的类型非常多。比如:字符设备,块设备,网络设备接口设备,PCI设备,USB设备,平台设备,混杂设备。设备类型不同,对应的驱动模型也不同。Linux下开发设备驱动程序要遵循内核模...

嵌入式linux字符设备驱动

1. 我们需要先调用register_chrdev_region()或 alloc_chrdev_region()来向系统申请设备号     int register_chrdev_region...
  • maopig
  • maopig
  • 2012年03月30日 10:42
  • 1042

【嵌入式Linux驱动开发】三、字符设备驱动(二)

1. open函数 int  open(struct  inode, );

linux 字符设备驱动-led

  • 2017年07月25日 15:07
  • 6KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:嵌入式Linux字符设备驱动LED驱动编写
举报原因:
原因补充:

(最多只允许输入30个字)