linux驱动程序设计实例.docx

上传人:b****3 文档编号:4990994 上传时间:2023-05-07 格式:DOCX 页数:31 大小:75.15KB
下载 相关 举报
linux驱动程序设计实例.docx_第1页
第1页 / 共31页
linux驱动程序设计实例.docx_第2页
第2页 / 共31页
linux驱动程序设计实例.docx_第3页
第3页 / 共31页
linux驱动程序设计实例.docx_第4页
第4页 / 共31页
linux驱动程序设计实例.docx_第5页
第5页 / 共31页
linux驱动程序设计实例.docx_第6页
第6页 / 共31页
linux驱动程序设计实例.docx_第7页
第7页 / 共31页
linux驱动程序设计实例.docx_第8页
第8页 / 共31页
linux驱动程序设计实例.docx_第9页
第9页 / 共31页
linux驱动程序设计实例.docx_第10页
第10页 / 共31页
linux驱动程序设计实例.docx_第11页
第11页 / 共31页
linux驱动程序设计实例.docx_第12页
第12页 / 共31页
linux驱动程序设计实例.docx_第13页
第13页 / 共31页
linux驱动程序设计实例.docx_第14页
第14页 / 共31页
linux驱动程序设计实例.docx_第15页
第15页 / 共31页
linux驱动程序设计实例.docx_第16页
第16页 / 共31页
linux驱动程序设计实例.docx_第17页
第17页 / 共31页
linux驱动程序设计实例.docx_第18页
第18页 / 共31页
linux驱动程序设计实例.docx_第19页
第19页 / 共31页
linux驱动程序设计实例.docx_第20页
第20页 / 共31页
亲,该文档总共31页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

linux驱动程序设计实例.docx

《linux驱动程序设计实例.docx》由会员分享,可在线阅读,更多相关《linux驱动程序设计实例.docx(31页珍藏版)》请在冰点文库上搜索。

linux驱动程序设计实例.docx

linux驱动程序设计实例

AT91SAM9G20驱动程序设计

开发环境:

Vmware+ubuntu10.04

硬件平台:

AT91SAM9G20

Linux版本:

linux2.6.27

一:

led驱动

说明:

因为设计的开发板上没有led灯,便通过PC0来演示,通过示波器来观察引脚端的电平变化。

1.驱动程序:

my_led.c

#include

#include

#include

#include

#include

#include

#include

#defineMY_LED_MAJOR250//定义主设备号

#defineLED_ON0

#defineLED_OFF1

structglobal_dev{

structcdevcdev;

};//定义设备结构体

structglobal_dev*global_devp;//定义一个指向设备结构体的指针

staticintmy_led_open(structinode*inode,structfile*filp)

{

filp->private_data=global_devp;

return0;

}

staticintmy_led_release(structinode*inode,structfile*file)

{

return0;

}

staticintmy_led_ioctl(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongdata)

{

switch(cmd)

{

caseLED_ON:

at91_set_gpio_value(AT91_PIN_PC0,0);//将PC0引脚置低

break;

caseLED_OFF:

at91_set_gpio_value(AT91_PIN_PC0,1);//将PC1引脚置高

break;

default:

printk("novalidcmdinput!

\n");

break;

}

return0;

}

structfile_operationsmy_led_ctl_ops={

.owner=THIS_MODULE,

.open=my_led_open,

.release=my_led_release,

.ioctl=my_led_ioctl,

};

/*初始化设备结构体*/

staticvoidmy_led_setup(structglobal_dev*dev,intindex)

{

interr;

intdevno=MKDEV(MY_LED_MAJOR,index);

cdev_init(&dev->cdev,&my_led_ctl_ops);

dev->cdev.owner=THIS_MODULE;

dev->cdev.ops=&my_led_ctl_ops;

err=cdev_add(&dev->cdev,devno,1);

if(err)

printk("addmyledsetupfailed!

\n");

}

staticintmy_led_init(void)

{

intret;

dev_tdevno=MKDEV(MY_LED_MAJOR,0);//创建设备号

printk("myfirstdriver--led!

\n");

at91_set_GPIO_periph(AT91_PIN_PC0,1);

at91_set_gpio_output(AT91_PIN_PC0,1);//对PC0引脚的初始化

ret=register_chrdev_region(devno,1,"my_led");//申请设备号

if(ret<0){

printk("my_ledinit_modulefailedwith%d\n",ret);

returnret;

}

else

printk("my_ledinit_modulesuccess!

\n");

global_devp=kmalloc(sizeof(structglobal_dev),GFP_KERNEL);//申请设备内存

memset(global_devp,0,sizeof(structglobal_dev));

my_led_setup(global_devp,0);

returnret;

}

staticvoidmy_led_cleanup(void)

{

cdev_del(&global_devp->cdev);//删除设备

kfree(global_devp);//释放内存

unregister_chrdev_region(MKDEV(MY_LED_MAJOR,0),1);//释放设备号

}

MODULE_LICENSE("MYGPL");

MODULE_AUTHOR("FANY");

module_init(my_led_init);//注册设备

module_exit(my_led_cleanup);//卸载设备

2:

如何将驱动驱动程序编译成模块

①在drivers目录下新建led目录,并在该目录下添加Kconfig,Makefile文件。

Kconfig:

Menu"Mydriversupport"

Config

Trisate"leddriver!

"

Help

Leddriver

Endmenu:

Makefile:

Obj-$(CONFIG_MY_LED)+=my_led.o

②修改linux/drivers目录下的Kconfig,Makefile文件

Kconfig:

Source"drivers/led/Kconfig"

Makefile:

Obj-y+=my_led/

③修改体系结构目录arch/arm目录下的Kconfig文件,否则在配置菜单中将无法看到led的配置选项。

(如果是在drivers目录下新建一文件夹,并在其中添加驱动程序,必须相应的体系结构目录下添加配置选项)。

Kconfig:

Source"driver/led/Kconfig"

3.测试程序:

my_led_test.c

#include

#include

#include

#include

#include

#defineDEVICE_NAME"/dev/my_led"

#defineLED_ON0

#defineLED_OFF1

intmain(void)

{

intfd;

intret;

inti;

printf("my_led_drivertest!

\n");

fd=open(DEVICE_NAME,O_RDONLY);

if(fd==-1)

printf("opendevice%serror!

\n",DEVICE_NAME);

for(i=0;i<50;i++)

{

ioctl(fd,LED_OFF);

sleep

(1);

ioctl(fd,LED_ON);

sleep

(1);

}

ret=close(fd);

printf("ret=%d\n",ret);

printf("closemy_led_driver!

\n");

return0;

}

将测试程序编译成目标平台的可执行文件,并下载到开发板

GCC=/home/zzq/9G20/arm-2007q1/bin/arm-none-linux-gnueabi-gcc#交叉编译器的路径

My_led_test:

my_led_test.c

$(GCC)-omy_led_testmy_led_test.c

clean:

rm-fmy_led_test

学习总结:

熟悉驱动程序的架构,如何将驱动程序添加到内核即如何写测试程序。

二:

按键驱动设计

1.硬件部分:

PC4接按键。

2.驱动程序:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#defineBUTTON_MAJOR245

#defineDEVICE_NAME"/dev/button"

staticvolatileintev_press=0;

staticstructcdevbutton_cdev;

staticvoidbutton_do_tasklet(unsignedlongn);

DECLARE_TASKLET(button_tasklet,button_do_tasklet,0);//定义tasklet并与处理函数关联起来

staticDECLARE_WAIT_QUEUE_HEAD(button_waitq);//静态的初始化一个等待队列

structbutton_irq_desc{

intirq;

intirq_type;

intpin;

intnumber;

char*name;

};

staticstructbutton_irq_descbutton_irq[1]={{AT91_PIN_PB22,AT91_AIC_SRCTYPE_LOW,AT91_PIN_PB22,0,"KEY0"}};

staticintkey_values[1]={0};

//中断处理底半部

staticvoidbutton_do_tasklet(unsignedlongn)

{

wake_up_interruptible(&button_waitq);//唤醒队列

printk("buttonpress!

\n");

}

//中断处理顶半部

staticirqreturn_tbutton_interrupt(intirq,void*dev_id,structpt_regs*regs)

{

intup;

staticintpress_down;

up=gpio_get_value(button_irq[0].pin);

printk("irq\n");

/*按键消抖*/

if(up)press_down=1;//当按键没有按下,置标志位为1.

if(!

up&&(press_down==1)){

press_down=0;//当按键按下,置标志位为0.

ev_press=1;

at91_set_gpio_value(button_irq[0].pin,1);

key_values[button_irq[0].number]=!

up;

tasklet_schedule(&button_tasklet);

}

returnIRQ_RETVAL(IRQ_HANDLED);

}

staticintbutton_open(structinode*inode,structfile*filp)

{

return0;

}

staticintbutton_release(structinode*inode,structfile*filp)

{

return0;

}

staticintbutton_read(structfile*filp,char__user*buff,size_tcount,loff_t*offp)

{

intret;

if(!

ev_press){//当按键没有按下时,读进程挂起,知道按键按下。

wait_event_interruptible(button_waitq,ev_press);

}

ev_press=0;

ret=copy_to_user(buff,(constvoid*)key_values,min(sizeof(key_values),count));

memset((void__user*)key_values,0,sizeof(key_values));

returnret?

-EFAULT:

min(sizeof(key_values),count);

}

staticstructfile_operationsbutton_fops={

.owner=THIS_MODULE,

.open=button_open,

.release=button_release,

.read=button_read,

};

staticintirq_init(void)

{

interr;

at91_set_gpio_input(button_irq[0].pin,1);

at91_set_deglitch(button_irq[0].pin,1);//将PC0设置为中断功能

set_irq_type(button_irq[0].irq,button_irq[0].irq_type);//设置中断类型

at91_set_gpio_value(button_irq[0].pin,1);

err=request_irq(button_irq[0].irq,button_interrupt,IRQF_DISABLED,\

button_irq[0].name,(void*)&button_irq[0]);//申请中断

if(err){

disable_irq(button_irq[0].irq);

free_irq(button_irq[0].irq,(void*)&button_irq[0]);

return-EBUSY;

}

return0;

}

staticint__initbutton_init(void)

{

intret,err;

ret=register_chrdev_region(MKDEV(BUTTON_MAJOR,0),1,DEVICE_NAME);

if(ret<0){

printk("buttoninitfailedwith%d\n",ret);

returnret;

}

cdev_init(&button_cdev,&button_fops);

button_cdev.owner=THIS_MODULE;

button_cdev.ops=&button_fops;

err=cdev_add(&button_cdev,MKDEV(BUTTON_MAJOR,0),1);

if(err<0){

printk("keyaddfailed\n");

returnerr;

}

irq_init();

printk("keydriveraddsuccess!

\n");

return0;

}

staticvoid__exitbutton_exit(void)

{

cdev_del(&button_cdev);

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

disable_irq(button_irq[0].irq);

free_irq(button_irq[0].irq,(void*)&button_irq[0]);

printk("unregisterkeydriver!

\n");

}

module_init(button_init);

module_exit(button_exit);

MODULE_AUTHOR("fany");

MODULE_DESCRIPTION("Atmel9g20keyDriver");

MODULE_LICENSE("GPL");

3.测试程序:

#include

#include

#include

#include

#include

#include

#defineDEVICE_NAME"/dev/button"

intmain(void)

{

intfd,i;

intret;

intkey_value[1];

printf("keytest!

\n");

fd=open(DEVICE_NAME,O_RDWR);

if(fd<0)

printf("opendevice%serror!

\n",DEVICE_NAME);

else

printf("opendevicesuccess!

\n");

while

(1){

ret=read(fd,key_value,1);

if(!

ret){

printf("buttonnotpress!

\n");

}

else

printf("buttonpress!

\n");

printf("key_value%d\n",key_value);

}

close(fd);

printf("closekeydriver!

\n");

return0;

}

4.学习总结:

在linux设备驱动程序中,中断处理程序通常分为两部分:

上半部和下半部。

上半部处理比较紧急的的硬件操作,比如简单的读取寄存器中的状态并清除中断标志后,进行登记中断的工作。

剩下的工作就由下半部来实现。

对阻塞与非阻塞进程的理解,阻塞:

在执行设备操作时,若不能获取设备资源则挂起,直到满足可操作的条件后再进行操作。

非阻塞操作:

在执行设备操作时,若不能获取设备资源则立即返回。

三:

总线驱动

AT91SAM9G20存储器映射图(截取部分),片选4接外设,利用总线对外设进行访问。

但是在linux驱动,不能对物理地址进行操作,可通过内存访问的机制实现对物理地址的访问。

将一段物理地址空间映射到虚拟地址空间中,然后对虚拟地址的操作即是对物理地址的操作。

3.1:

总线驱动

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include"atmel9g20_liu.h"

structgr_liu_info{

void__iomem*virtbase;

void__iomem*regbase;

structresource*res;

u32flags;

};

staticstructgr_liu_infoliu_info;

staticstructcdevatmel9g20_liu_cdev;

unsignedshortliu_read(unsignedaddr)

{

addr&=ATMEL9G20_LIU_MASK;

addr=addr<<1;

addr+=(unsignedlong)liu_info.regbase;

printk("readthevirtualaddris0x%x\n",addr);

returnreadw(addr)&0xff;//读IO内存。

}

EXPORT_SYMBOL(liu_read);

intliu_write(unsignedaddr,unsignedval)

{

addr&=ATMEL9G20_LIU_MASK;

addr=addr<<1;

addr+=(unsignedlong)liu_info.regbase;

printk("writethevirtualaddris0x%x\n",addr);

writew(val&0xff,addr);//写IO内存

return0;

}

EXPORT_SYMBOL(liu_write);

staticintatmel9g20_liu_open(structinode*inode,structfile*filp)

{

return0;

}

staticintatmel9g20_liu_release(structinode*inode,structfile*filp)

{

return0;

}

staticintatmel9g20_

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2