linux驱动程序设计实例.docx
- 文档编号:7298865
- 上传时间:2023-01-22
- 格式:DOCX
- 页数:26
- 大小:68.94KB
linux驱动程序设计实例.docx
《linux驱动程序设计实例.docx》由会员分享,可在线阅读,更多相关《linux驱动程序设计实例.docx(26页珍藏版)》请在冰豆网上搜索。
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_
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 驱动程序 设计 实例