linux 内存灯,linux驱动之分离分层的概念(LED点灯)

a4c26d1e5885305701be709a3d33442f.png

一、开发环境

linux内核版本:  2.6.22.6

硬件环境:jz2440 V2开发板  ,使用的是S3C2440

ARM9处理器

,LED使用的GPIO引脚为GPF4,GPF5,GPF6,当输出低电平时,LED亮,高电平LED灭​

二、目的

​采用分离分层的思想编写本驱动程序,主要分为两个部分,与硬件操作相关的led_dev和相对稳定的led_drv;led_dev主要负责分配,设置,注册一个平台设备结构体(platform_device),本程序的目的是想点亮一个led灯,如果控制另外一个led灯,只需要修改此驱动程序即可实现需要的功能;led_drv主要负责分配,设置,注册一个平台驱动结构体(platform_drvier),主要负责注册一个字符设备驱动程序,获取平台设备传来的资源信息

三、驱动程序

驱动程序分为两个部分,分别进行编程​

①.

​led_dev.c

//

参考文件arch\arm\plat-s3c24xx\devs.c

//由于在新浪博客里尖括号符号显示不了,下面引用头文件的​尖括号用“”双引号替代了

#include "linux/platform_device.h"

#include "linux/kernel.h"

#include "linux/module.h"

//该结构体存入了最为重要的设备资源信息,驱动程序通过匹配后可以获取硬件的资源

static struct resource led_resources[] = {

[0] = {

.start

=0x56000050,//GPFCON的起始地址

.end

=0x56000050 +8-1,//GPDCON的结束地址

.flags= IORESOURCE_MEM,//描述的是内存类型的资源信息

},

[1] = {

.start

=6,//点亮GPF6灯

.end

=6,

.flags= IORESOURCE_IRQ,//描述的是中断资源信息。设备驱动会根据flags来获取相应的资源信息。

},

};

//该函数必须要添加,可以什么都不做

static void led_dev_release(struct device *

dev)

{

}

static struct platform_device led_dev = {

.name  = "led_bus",//设备name,通过此来匹配platform_driver

.id= -1,

.dev = {

.release

= led_dev_release,

},

.num_resources  =

ARRAY_SIZE(led_resources),

.resource  = led_resources,

};

static int led_dev_init(void)

{

return platform_device_register(&led_dev);

}

static void led_drv_exit(void)

{

return platform_device_unregister(&led_dev);

}

module_init(led_dev_init);

module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

②.

led_drv.c​

//由于在新浪博客里尖括号符号显示不了,下面引用头文件的​尖括号用“”双引号替代了

#include "linux/kernel.h"

#include "linux/module.h"

#include "linux/platform_device.h"

#include "linux/fs.h"

#include "asm/uaccess.h"

#include "asm/io.h"

#include "linux/cdev.h"

#define DEV_NAME  "led"

static int major=0; //主设备号初始值设为0

static struct cdev led_drv_cdev;

static struct class *led_drv_class;//定义一个类结构体

volatile unsigned long *gpfcon;//定义变量代表GPFCON寄存器

volatile unsigned long *gpfdat;//定义变量代表GPFDAT寄存器

static int pin;//

static int led_drv_open(struct inode *inode, struct file

*file)

{

*gpfcon

&=~((0x3<

*gpfcon

|=((0x1<

return 0;

}

//用于读取LED灯的控制信息​

static ssize_t led_drv_write(struct file *file, const char

__user *buf, size_t size, loff_t *ppos)

{

int val;

int err;

err=copy_from_user(&val, buf,1);//从用户空间buf传值到内核空间val

if(err)//如果没有数据

{

printk("copy_from_user error\n");//打印错误信息

}

if(val==1)//若传进来的值为1,点灯

{

*gpfdat

&=~(1<

}

else//否则灭灯

{

*gpfdat |=(1<

}

return 0;

}

//构造一个file_operations结构体

static struct file_operations led_drv_fops={

.owner=THIS_MODULE,

.open =led_drv_open,

.write =led_drv_write,

};

static int  led_drv_probe(struct

platform_device *pdev)

{

//printk(" led_drv_probe\n ");

// 注册一个字符设备

dev_t devid;

int result;

struct resource *res;//定义一个源结构体,用于获取平台信息

res = platform_get_resource(pdev, IORESOURCE_MEM,

0);//获取平台资源

gpfcon=ioremap(res->start,res->end-res->start+1);//内存地址映射

gpfdat=gpfcon+1;//这里的加1表示地址值加4

res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

pin=res->start;

if(major)//如果主设备号为非0,注册字符设备时就不需要动态分配一个主设备号

{

devid=MKDEV(major,0);

result=register_chrdev_region(devid,1,DEV_NAME);

}

else

{

result=alloc_chrdev_region(&devid, 0,

1,DEV_NAME);

major=MAJOR(devid);

}

if(result<0)

{

printk(" register error \n");

return -EIO;

}

cdev_init(&led_drv_cdev,&led_drv_fops);

cdev_add(&led_drv_cdev,devid,1);

//

创建类和设备节点

led_drv_class=class_create(THIS_MODULE,DEV_NAME);

class_device_create(led_drv_class,NULL,

devid,NULL,DEV_NAME);

return 0;

}

static int led_drv__remove(struct platform_device

*pdev)

{

iounmap(gpfcon);

class_device_destroy(led_drv_class,MKDEV(major,0));

class_destroy(led_drv_class);

cdev_del(&led_drv_cdev);

unregister_chrdev_region(MKDEV(major,0),1);

printk(" led_drv__remove\n ");

return 0;

}

//定义,设置一个​platform_driver

结构体

struct platform_driver led_platform_driver = {

.probe= led_drv_probe,

.remove= led_drv__remove,

.driver= {

.name= "led_bus",

.owner= THIS_MODULE,

}

};

static int led_drv_init(void)

{

return

platform_driver_register(&led_platform_driver);

static void

led_drv_exit(void)//出口函数

{

return

platform_driver_unregister(&led_platform_driver);

}

module_init(led_drv_init);

module_exit(led_drv_exit);

MODULE_LICENSE("GPL");

​③.Makefile文件

KERN_DIR = /work/system/linux-2.6.22.6 //编译驱动程序时,所用到的linux内核所在的目录

all:

make -C $(KERN_DIR) M=`pwd`

modules

clean:

make -C $(KERN_DIR) M=`pwd` modules

clean

rm -rf modules.order

obj-m   +=led_dev.o

obj-m   +=led_drv.o

四、应用程序(led_drv_dev_test.c)

//由于在新浪博客里尖括号符号显示不了,下面引用头文件的​尖括号用“”双引号替代了

​#include

"sys/types.h"

#include "sys/stat.h"

#include "fcntl.h"

#include "string.h"

#include "unistd.h"

// ./led_drv_dev_test on

点灯

// ./led_dev_dev_test

off 灭灯

void print_usage(char *file)//打印用法

{

printf(" %s \n",file);

}

int main(int argc,char **argv)

{

int fd;

int val;

if(argc !=2)//如果输入的参数的个数不为2,打印用法

{

print_usage(argv[0]);

return -1;

}

fd=open("/dev/led",O_RDWR);

if(fd<0)

{

printf("can't open\n");

return -1;

}

if(strcmp(argv[1],"on")==0)//若第二个参数为on,表示开灯

{

val=1;

}

else if(strcmp(argv[1],"off")==0)//若第二个参数为off,表示关灯

{

val=0;

}

else

{

print_usage(argv[0]);

return -1;

}

write(fd,&val,1);//将值传入内核空间,用于控制LED灯

return 0;

}

五、测试​​

①​编译驱动程序和应用程序

编译驱动程序:输入命令

make

编译应用程序:输入命令​

arm-linux-gcc -o led_drv_dev_test

led_drv_dev_test.c,生成二进制可执行文件

②装载驱动程序

输入命令:insmod

led_dev.c

输入命令: insmod

led_drv.c

③执行应用程序

输入命令:./led_drv_dev_test

on  开灯

输入命令 :./led_drv_dev_test

off​  灭灯

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值