Linux设备驱动模块设计实例分析

2016-03-22 11:20桂林电子科技大学信息科技学院赵志鹏
电子世界 2016年1期
关键词:设计流程功能模块

桂林电子科技大学信息科技学院 赵志鹏



Linux设备驱动模块设计实例分析

桂林电子科技大学信息科技学院赵志鹏

【摘要】Linux设备驱动属于内核的一部分,在嵌入式系统开发中,驱动程序的设计是硬件设计的关键部分,本文以buzzer的驱动设计为例,详细分析了硬件驱动设计流程,以及在程序设计中的各个重要功能模块及其相互关系。

【关键词】Linux硬件设备驱动;设计流程;功能模块

1 驱动的功能

当前社会中,嵌入式系统开发设计渗透到了生活的各个领域和方方面面,在嵌入式系统开发设计中,软件设计和硬件环环相扣,密不可分,其中硬件是设计的底层和基础,是软件运行的平台,其重要性不言而喻。

作为嵌入式系统硬件设计的重要组成部分,设备驱动程序的编写是我们嵌入式开发人员必须完成的工作。设备的驱动就是驱使硬件设备工作,它直接和底层硬件打交道,按照硬件设备的具体工作方式,执行固定的一些操作,比如进行设备的寄存器读写,完成设备的轮询、中断处理、DMA通信等。

因此,驱动是硬件和应用软件之间交互的一个平台,通过驱动程序,实现硬件功能诉求和软件应用程序完成的一种交流,用户通过应用程序只需要调用系统应用软件的一个标准编程接口就能够完成相应的功能。

2 驱动程序编写步骤

2.1查看分析原理图

buzzer蜂鸣器采用了电磁式蜂鸣器,此电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片以及外壳组成。当电路导通后,振荡器产生的音频新型号电流通过电磁线圈,使电磁线圈产生磁场。振动膜片在电磁线圈和磁铁的相互作用下,周期性的震

动发声,在蜂鸣器报警装置中,采用了PWM的方式,占空比为1/2,报警时间设置为10s。

2.2编写驱动代码

Linux内核的一个模块可以以两种方式被编译和加载: 第一直接编译进Linux内核,随同Linux启动时加载;第二编译成一个可加载和删除的模块,使用insmod加载,但依赖于相关的配置文件,rmmod删除。这种方式控制了内核的大小,而模块一旦被插入内核,它就和内核其他部分一样。

编写一个驱动首先涉及到一个头文件,驱动程序一般包括三个头文件,分别为:。一般在c里面,.h文件一般用作调用库的功能,在很多的场合,源代码不便向用户公开,只要向用户提供头文件和二进制的库即可。在头文件里面,含有各种函数的定义,变量的定义以及头文件的定义,当使用头文件的时候,就可以使用头文件定义的资源了。

在结构上驱动程序一般会包括三个基本的部分,第一个是申请许可证,相当于世界linux内核驱动通用性许可证,有了许可证在编译的时候才不会报错,否则linux内核会说此模块非法加载。第二个为加载函数,这个函数是驱动程序模块的入口,第三个为卸载函数,与加载函数相反,当然标准的驱动程序还包含作者信息、编写日期等信息等,但这个不是必要的。

(1)加载函数

加载函数是驱动一个程序的入口,类似于应用程序中主程序中的main函数,当运行一个驱动程序时,系统首先找到加载函数,怎样识别哪个是加载函数呢?系统内核自己的一套识别机制,在函数中加上__init,系统就会识别这个函数是加载函数。在加载函数中加上static静态变量函数,表明此函数只能被源程序使用,不能被源程序以外的函数调用。

static int __init s5pc100_pwm_init (void)//buzzer加载函数

{

int ret;

devno = MKDEV(pwm_major, pwm_minor);//申请设备号

ret=register_chrdev_region(devno,device_of_number, "buzzer");//注册设备号

if(ret<0) //如果注册不成功,结束程序

goto err1;

cdev_init(&cdev, &s5pc100_pwm_fops);//初始化设备

cdev.owner = THIS_MODULE;

ret=cdev_add(&cdev,devno,device_of_number); //注册设备

}

(2) 卸载函数

卸载函数与加载函数相反,当系统程序运行结束时,就要执行卸载函数,将所申请的空间释放掉,否则常此以往就会使系统的剩余空间越来越少,甚至系统崩溃。

static void __exit s5pc100_pwm_exit (void)

iounmap(gpdcon);//释放gpdcon配置寄存器的虚拟空间

iounmap(timer_base);

cdev_del(&cdev);//卸载设备

unregister_chrdev_region(devno, device_of_number);//卸载设备号

printk (KERN_INFO "Goodbye world ");

}

(3) buzzer采用的io的打开方式

采用了static变量的ioctl的打开方式,ioctl可以控制I/O设备 ,提供了一种获得设备信息和向设备发送控制参数的手段。用于向设备发控制和配置命令 ,有些命令需要控制参数,这些数据是不能用read / write 读写的。这个时候就可以用ioctl的方式来达到这个目的。

static int s5pc100_pwm_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

switch(cmd)//采用switch语句,当有命令响应时执行

{

case PWM_ON:

//0x2<<8开始,0x9<<8 自动重载,反相输出

writel((readl(timer_base + S5PC100_TCON) & ~(0xf << 8))

| 0x2 << 8, timer_base + S5PC100_TCON);

writel((readl(timer_base + S5PC100_TCON) & ~(0xf << 8))

| 0x9 << 8, timer_base + S5PC100_TCON);

//先采用手动更新TCNTB1 , TCMPB1 ,然后采用自动重载

break;

case PWM_OFF: writel(readl(timer_base + S5PC100_TCON) & ~(0xf << 8),

timer_base + S5PC100_TCON);

break;

{

}

r eturn 0;

}

3 buzzer线程模块

图1 buzzer线程流程图

buzzer线程运行在主程序里面,当程序执行的时候,buzzer线程也被执行,字符设备文件被打开(open),buzzer线程执行pthred_cond_wait()函数,进入等待唤醒的状态,当温度、湿度或者光照强度超标的时候,buzzer线程会接收到一个唤醒的信号,获取buzzer模块的掩码,然后采用pwm控制蜂鸣器的亮灭。

4 结论

本文以buzzer的驱动模块设计为实例,详细分析了嵌入式系统设备驱动模块的设计原理和步骤,对驱动模块的编写原理和结构以源代码的方式进行了重点描述,力图以此方式建立一个开发硬件驱动模块的标准流程模板,最后分析了buzzer的线程流程,通过在开发板上对驱动程序的实际测试,该设备驱动模块的设计思想和设计过程完全能够实现设计要求,应用程序调用硬件驱动程序后能够在开发环境正常运行,硬件能够正常工作,达到了预期效果。

参考文献

[1]宋宝华.设备驱动开发详解(第2版)[M].北京:人民邮电出版社.2010.11.

[2]诺儿加德.嵌入式系统硬件与软件结构[M].北京:人民邮电出版社学.2008.2.

[3]田泽主编.嵌入式系统开发与应用[M].北京:北京航空航天大学出版社,2005.

[4]Atmel Corporation ARM7TDMITM (Thumb®) Datasheet[M].1999-01.

赵志鹏(1973—),男,河南西平人,硕士,现供职于桂林电子科技大学信息科技学院,主要从事智能仪器、嵌入式开发应用、及测控方面的教学科研工作。

作者简介:

项目:以应用为导向的嵌入式系统教改研究与实践,编号:2015JGB488。

猜你喜欢
设计流程功能模块
初探VHDL语言在电子设计中的应用
商业模式是新媒体的核心
基于ASP.NET标准的采购管理系统研究
浅谈用户体验在产品设计中的运用
风景区潮汐性人流与可移动建筑设计研究
微课程在《病原生物与免疫学基础》中的设计和应用
输电线路附着物测算系统测算功能模块的研究
功能模块的设计与应用研究