蜂鸣器驱动设计项目文档.docx
《蜂鸣器驱动设计项目文档.docx》由会员分享,可在线阅读,更多相关《蜂鸣器驱动设计项目文档.docx(18页珍藏版)》请在冰豆网上搜索。
蜂鸣器驱动设计项目文档
项目设计报告
项目名称:
基于ARM-Linux的BUZZER驱动设计
项目负责人:
姚雷
项目时间:
2012.12.3---2012.12.7
第一章绪论
1.1驱动背景
Linux设备驱动分类:
1.1.1字符设备驱动:
字符设备是一种按字节来访问的设备,字符驱动则负责驱动字符设备,这样的驱动通常实现open,close,read和write系统调用。
1.1.2块设备驱动:
在大部分的Unix系统,块设备不能按字节处理数据,只能一次传送一个或多个长度是512字节(或一个更大的2次幂的数)的整块数据。
而Linux则允许块设备传送任意数目的字节。
因此,块和字符设备的区别仅仅是驱动的与内核的接口不同。
1.1.3网络接口驱动:
任何网络事务都通过一个接口来进行,一个接口通常是一个硬件设备(eth0),但是它也可以是一个纯粹的软件设备,比如回环接口(lo)。
一个网络接口负责发送和接收数据报文。
1.2嵌入式系统
以应用为中心、以计算机技术为基础,软硬件可裁剪,应用系统对功能、可靠性、成本、体积、功耗和应用环境有特殊要求的与用计算机系统。
是将应用程序、操作系统和计算机硬件集成在一起的系统(技术角度);
嵌入式系统是设计完成复杂功能的硬件和软件,幵使其紧密耦合在一起的计算机系统(系统角度)。
第二章驱动应用技术
2.1驱动程序使用
Linux用户程序通过设备文件(又名:
设备节点)来使用驱动程序操作字符设备和块设备。
2.2设备创建
字符设备通过字符设备文件来存取。
字符设备文件由使用ls-l的输出的第一列的“c”标识。
如果使用ls-l命令,会看到在设备文件项中有2个数(由一个逗号分隔)这些数字就是设备文件的主次设备编号。
主设备号用来标识与设备文件相连的驱动程序。
次编号被驱动程序用来辨别操作的是哪个设备。
**主设备号用来反映设备类型**
**次设备号用来区分同类型的设备**
Linux内核如何给设备分配主设备号?
可以采用静态申请,动态分配两种方法。
2.2.1静态申请
根据Documentation/devices.txt,确定一个没有使用的主设备号,使用register_chrdev_region函数注册设备号,intregister_chrdev_region(dev_tfrom,unsignedcount,constchar*name)。
参数:
from:
希望申请使用的设备号
count:
希望申请使用设备号数目
name:
设备名(体现在/proc/devices)
2.2.2动态申请
使用alloc_chrdev_region分配设备号,intalloc_chrdev_region(dev_t*dev,unsignedbaseminor,unsignedcount,constchar*name)。
参数:
dev:
分配到的设备号
baseminor:
起始次设备号
count:
需要分配的设备号数目
name:
设备名(体现在/proc/devices)
注销设备号:
voidunregister_chrdev_region(dev_tfrom,unsignedcount)。
2.3设备Ioctl控制
在用户空间,使用ioctl系统调用来控制设备,原型如下:
intioctl(intfd,unsignedlongcmd,...)
原型中的点表示这是一个可选的参数,存在与否依赖于控制命令(第2个参数)是否涉及到与设备的数据交互。
ioctl驱动方法有和用户空间版本不同的原型:
int(*ioctl)(structinode*inode,structfile*filp,unsignedintcmd,unsignedlongarg)
cmd参数从用户空间传下来,可选的参数arg以一个unsignedlong的形式传递,不管它是一个整数或一个指针。
如果cmd命令不涉及数据传输,则第3个参数arg的值无任何意义。
在编写ioctl代码之前,首先需要定义命令。
为了防止对错误的设备使用正确的命令,命令号应该在系统范围内是唯一的。
ioctl命令编码被划分为几个位段,include/asm/ioctl.h中定义了这些位字段:
类型(幻数),序号,传送方向,参数的大小。
内核提供了下列宏来帮助定义命令:
_IO(type,nr)没有参数的命令
_IOR(type,nr,datatype)从驱动中读数据
_IOW(type,nr,datatype)写数据到驱动
_IOWR(type,nr,datatype)
双向传送,type和number成员作为参数被传递。
定义好了命令,下一步就是要实现Ioctl函数了,Ioctl函数的实现包括如下3个技术环节:
(1).返回值
(2).参数使用(3).命令操作
Ioctl函数的实现通常是根据命令执行的一个switch语句。
但是,当命令号不能匹配任何一个设备所支持的命令时,通常返回-EINVAL(“非法参数”)。
第三章项目任务及目标实现
1.目标
编写驱动程序对ARM开发板的BUZZER进行控制,通过测试程序来测试该驱动程序能否正常工作。
2.环境
Linux,arm-linux-gcc,mini2440开发板
加载蜂鸣器驱动,insmodpwn.ko,初始化硬件设备。
安装嵌入式交叉编译器(arm-linux-gcc),使得在ARM开发板上运行测试驱动程序,调试程序。
3.项目需求
在开发板上运行测试程序。
可以看到根据你输入参数的大小,蜂鸣器也会发生不同频率的叫声,根据延时的时间实现蜂鸣器响与停止的间隔时间。
对Linux内核进行裁剪,模块化加载驱动。
第四章蜂鸣器工作原理
蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。
有的压电式蜂鸣器外壳上还装有发光二极管。
多谐振荡器由晶体管或集成电路构成。
当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。
接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。
振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
根据S3C2440的手册介绍,S3C2440A内部有5个16位的定时器,定时器0、1、2、3都带有脉冲宽度调制功能(PWM),定时器4是一个没有输出引脚的内部定时器,定时器0有一个用于大电流设备的死区生成器。
总结一下2440内部定时器模块的特性吧:
1)共5个16位的定时器,定时器0、1、2、3都带有脉冲宽度调制功能(PWM);
2)每个定时器都有一个比较缓存寄存器(TCMPB)和一个计数缓存寄存器(TCNTB);
3)定时器0、1共享一个8位的预分频器(预定标器),定时器2、3、4共享另一个8位的预分频器(预定标器),其值范围是0~255;
4)定时器0、1共享一个时钟分频器,定时器2、3、4共享另一个时钟分频器,这两个时钟分频器都能产生5种不同的分频信号值(即:
1/2、1/4、1/8、1/16和TCLK);
5)两个8位的预分频器是可编程的且根据装载的值来对PCLK进行分频,预分频器和钟分频器的值分别存储在定时器配置寄存器TCFG0和TCFG1中;
6)有一个TCON控制寄存器控制着所有定时器的属性和状态,TCON的第0~7位控制着定时器0、第8~11位控制着定时器1、第12~15位控制着定时器2、第16~19位控制着定时器3、第20~22位控制着定时器4。
还是根据S3C2440手册的描述和上图的结构,要开始一个PWM定时器功能的步骤如下(假设使用的是第一个定时器):
1)分别设置定时器0的预分频器值和时钟分频值,以供定时器0的比较缓存寄存器和计数缓存寄存器用;
2)设置比较缓存寄存器TCMPB0和计数缓存寄存器TCNTB0的初始值(即定时器0的输出时钟频率);
3)关闭定时器0的死区生成器(设置TCON的第4位);
4)开启定时器0的自动重载(设置TCON的第3位);
5)关闭定时器0的反相器(设置TCON的第2位);
6)开启定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位);
7)启动定时器0(设置TCON的第0位);
8)清除定时器0的手动更新TCNTB0&TCMPB0功能(设置TCON的第1位)。
由此可以看到,PWM的输出频率跟比较缓存寄存器和计数缓存寄存器的取值有关,而比较缓存寄存器和计数缓存寄存器的值又跟预分频器和时钟分频器的值有关;要使用PWM功能其实也就是对定时器的相关寄存器进行操作。
手册上也有一个公式:
定时器输出频率=PCLK/{预分频器值+1}/时钟分频值。
第五章总体设计
1.处理流程
(1).编写驱动程序代码和测试程序代码,
(2).裁剪并编译内核,生成内核映像,
(3).将内核映像下载到开发板上,并将文件系统也下载到开发板上。
(4).将驱动文件和可执行的测试程序下载到开发板上
(5).加载驱动,然后运行测试程序进行调试。
注意:
蜂鸣器是通过GPB0IO口使用PWM信号驱动工作的,而GPB0口是一个复用的IO口,要使用它得先把他设置成TOUT0PWM输出模式。
2各模块设计
2.1打开设备模块
staticintBUZZER_open(structinode*inode,structfile*file)
{
if(!
down_trylock(&lock))
return0;
else
return-EBUSY;
}
此函数实现了怎么去打开设备,在设备文件上的第一个操作,并不要求驱动程序一定要实现这个方法。
如果该项为NULL,设备的打开操作永远成功。
Open方法是驱动程序用来为以后的操作完成初始化准备工作的。
在大部分驱动程序中,open完成如下工作:
初始化设备,标明次设备号。
2.2关闭设备模块
staticintBUZZER_close(structinode*inode,structfile*file)
{
BUZZER_Stop();
up(&lock);
return0;
}
当设备文件被关闭时调用这个操作。
与open相仿,release也可以没有,此处关闭函数为BUZZER_close。
Release方法的作用正好与open相反。
这个设备方法有时也称为close,它应该:
关闭设备。
注意:
本驱动程序要进行读和写。
2.3开启蜂鸣器并设置蜂鸣器频率
/*freq:
pclk/50/16/65536~pclk/50/16
*ifpclk=50MHz,freqis1Hzto62500Hz
*humanear:
20Hz~20000Hz
*/
staticvoidBUZZER_Start(unsignedlongfreq)
{
unsignedlongtcon;
unsignedlongtcnt;
unsignedlongtcfg1;
unsignedlongtcfg0;
structclk*clk_p;
unsignedlongpclk;
//setGPB0astout0,pwmoutput
s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPB0_TOUT0);
//设置gpio的工作模式,是输入,输出还是其他的,就是设置GPB0为输出模式
tcon=__raw_readl(S3C2410_TCON);
tcfg1=__raw_readl(S3C2410_TCFG1);//读取定时器配置寄存器1的值
tcfg0=__raw_readl(S3C2410_TCFG0);//读取定时器配置寄存器0的值
//prescaler=50
tcfg0&=~S3C2410_TCFG_PRESCALER0_MASK;
tcfg0|=(50-1);//fg0的值为49
//mux=1/16
tcfg1&=~S3C2410_TCFG1_MUX0_MASK;
tcfg1|=S3C2410_TCFG1_MUX0_DIV16;//设置tcfg1的值为0x0011即:
1/16
__raw_writel(tcfg1,S3C2410_TCFG1);//将值tcfg1写入定时器配置寄存器1中
__raw_writel(tcfg0,S3C2410_TCFG0);//将值tcfg0写入定时器配置寄存器0中
clk_p=clk_get(NULL,"pclk");
pclk=clk_get_rate(clk_p);//从系统平台时钟队列中获取pclk的时钟频率,在include/linux/clk.h中定义
tcnt=(pclk/50/16)/freq;//计算定时器0的输出时钟频率(pclk/{prescaler0+1}/dividervalue)
__raw_writel(tcnt,S3C2410_TCNTB(0));//设置定时器0计数缓存寄存器的值
__raw_writel(tcnt/2,S3C2410_TCMPB(0));//设置定时器0比较缓存寄存器的值
tcon&=~0x1f;
tcon|=0xb;//disabledeadzone,auto-reload,inv-off,updateTCNTB0&TCMPB0,starttimer0
__raw_writel(tcon,S3C2410_TCON);//设置定时器控制寄存器的0-4位,即对定时器0进行控制
tcon&=~2;//clearmanualupdatebit
__raw_writel(tcon,S3C2410_TCON);//清除定时器0的手动更新位
}
对GPB0复用口进行复用功能设置,设置为TOUT0PWM输出,置GPIO口为输出功能,往相应的控制寄存器写值,并向相应的数据寄存器写值实现输出低电平还是高电平,控制蜂鸣器的开启与关闭。
蜂鸣器原理图:
2.4关闭蜂鸣器
staticvoidBUZZER_Stop(void)
{
s3c2410_gpio_cfgpin(S3C2410_GPB(0),S3C2410_GPIO_OUTPUT);
s3c2410_gpio_setpin(S3C2410_GPB(0),0);
}
恢复GPB0口为IO口输出功能,由原理图可知直接给低电平可让蜂鸣器停止工作。
2.5ioctl控制模块
staticintBUZZER_ioctl(structinode*inode,structfile*file,unsignedintcmd,unsignedlongarg)
{
//printk("ioctlbuzzer:
%x%lx\n",cmd,arg);
switch(cmd){
caseBUZZER_IOCTL_START:
if(arg==0)
return-EINVAL;
BUZZER_Set_Freq(arg);
break;
caseBUZZER_IOCTL_STOP:
BUZZER_Stop();
break;
}
return0;
}
应用程序向设备发送命令,设备接受到命令并进行解析,并做相应的设置并启动设备工作或停止工作。
如果输入的参数大于0,就让蜂鸣器开始工作,不同的参数,蜂鸣器的频率也不一样。
2.6重要数据结构模块
在Linux字符设备驱动程序设计中,有3种非常重要的数据结构:
Structfile
Structinode
Structfile_operations
StructFile
代表一个打开的文件。
系统中每个打开的文件在内核空间都有一个关联的structfile。
它由内核在打开文件时创建,在文件关闭后释放。
重要成员:
loff_tf_pos/*文件读写位置*/
structfile_operations*f_op
StructInode
用来记录文件的物理上的信息。
因此,它和代表打开文件的file结构是不同的。
一个文件可以对应多个file结构,但只有一个inode结构。
重要成员:
dev_ti_rdev:
设备号
Structfile_operations
一个函数指针的集合,定义能在设备上进行的操作。
结构中的成员指向驱动中的函数,这些函数实现一个特别的操作,对于不支持的操作保留为NULL。
staticstructfile_operationsdev_fops={
.owner=THIS_MODULE,
.open=BUZZER_open,
.release=BUZZER_close,
.ioctl=BUZZER_ioctl,
};
2.7设备注册模块
staticstructmiscdevicemisc={
.minor=MISC_DYNAMIC_MINOR,
.name=DEVICE_NAME,
.fops=&dev_fops,
};
此结构体定义一个混杂设备。
staticint__initdev_init(void)
{
intret;
init_MUTEX(&lock);
ret=misc_register(&misc);
printk(DEVICE_NAME"\tinitialized\n");
returnret;
}
在linux2.6内核中,字符设备使用structcdev来描述。
字符设备的注册可分为如下3个步骤:
分配cdev
初始化cdev
添加cdev
Structcdev的分配可使用cdev_alloc函数来完成。
Structcdev的初始化使用cdev_init函数来完成。
structcdev的注册使用cdev_add函数来完成。
2.8设备注销模块
staticvoid__exitdev_exit(void)
{
misc_deregister(&misc);
}
此函数实现该混杂设备的注销。
3.驱动测试
驱动测试程序如下:
/********buzzer_test.c********/
#include
#include
#include
#include
voiddelay(inttimes)
{
inti;
for(;times>0;times--)
{
for(i=0;i<400;i++);
}
}
intmain(void)
{
intfd;
fd=open("/dev/pwm",0);
if(fd<0){
perror("opendevicepwm");
exit
(1);
}
while
(1)
{
ioctl(fd,1,2000);
sleep
(1);
ioctl(fd,0,2000);
sleep
(1);
}
close(fd);
return0;
}
此测试程序先执行打开设备文件,往设备中写入命令和参数来实现对蜂鸣器的控制。
蜂鸣器的开启与关闭间隔和时间以及蜂鸣器的频率取决与往设备文件里写的命令与参数。
第六章项目总结与体会
经过了一个星期的学习驱动,本次项目是编写蜂鸣器驱动,毕竟自己学过驱动,感觉蛮多知识遗忘掉了,在编写蜂鸣器驱动和测试程序的过程中,发现了自己在驱动方面的不足之处,自己还有很多要学习的地方,在编写驱动时,设备的注册,模块的加载,蜂鸣器的频率等都是必须要考虑的问题,感谢老师们给我这次实训经历,能让自己进一步学习驱动的知识,
对自己以后的择业与就业有了很大的帮助。
第七章参考文献
Linux设备驱动程序(第三版)(美)科波特,2006.1中国电力出版社
Linux驱动开发入门与实战郑强编2011.11清华大学出版社
Linux驱动开发详解宋宝华编2010.11人民邮电出版社