蜂鸣器驱动程序设计 2.docx
- 文档编号:6825908
- 上传时间:2023-01-10
- 格式:DOCX
- 页数:19
- 大小:436.87KB
蜂鸣器驱动程序设计 2.docx
《蜂鸣器驱动程序设计 2.docx》由会员分享,可在线阅读,更多相关《蜂鸣器驱动程序设计 2.docx(19页珍藏版)》请在冰豆网上搜索。
蜂鸣器驱动程序设计2
合肥师范学院
嵌入式系统开发技术
课程设计
专业:
计算机科学与技术(嵌入式)
班级:
嵌入式应用技术
设计题目:
蜂鸣器驱动程序设计
1.绪论3
1.1概要3
1.2设计内容4
2.开发环境的搭建4
2.1Redhat的安装4
2.2安装arm-linux-gcc交叉编译器9
2.3安装及编译linux-2.6.29-mini2440-20090708内核9
3.字符设备驱动相关知识9
3.1模块机制9
3.2字符设备开发基本步骤10
3.3主设备号和次设备号11
3.4实现字符驱动程序12
4.蜂鸣器原理14
4.1蜂鸣器的种类和工作原理14
4.2开发板上蜂鸣器原理图分析15
4.3GPB0参数15
5.总体设计16
5.1设计思路16
5.2设计步骤16
6.驱动及测试程序17
6.1beep.c17
6.2beep_tset.c21
7.运行结果及截图22
综合设计总结与思考25
1.绪论
1.1概要
linux驱动在本质上就是一种软件程序,上层软件可以在不用了解硬件特性的情况下,通过驱动提供的接口,和计算机硬件进行通信。
系统调用是内核和应用程序之间的接口,而驱动程序是内核和硬件之间的接口,也就是内核和硬件之间的桥梁。
它为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以象操作普通文件一样对硬件设备进行操作。
linux驱动程序是内核的一部分,管理着系统中的设备控制器和相应的设备。
它主要完成这么几个功能:
对设备初始化和释放;传送数据到硬件和从硬件读取数据;检测和处理设备出现的错误。
一般来说,一个驱动可以管理一种类型的设备。
例如不同的U盘都属于massstorage设备,我们不需要为每一个U盘编写驱动,而只需要一个驱动就可以管理所有这些massstorage设备。
为方便我们加入各种驱动来支持不同的硬件,内核抽象出了很多层次结构,这些层次结构是linux设备驱动的上层。
它们抽象出各种的驱动接口,驱动只需要填写相应的回调函数,就能很容易把新的驱动添加到内核。
一般来说,linux驱动可以分为三类,就是块设备驱动,字符设备驱动和网络设备驱动。
块设备的读写都有缓存来支持,并且块设备必须能够随机存取。
块设备驱动主要用于磁盘驱动器。
而字符设备的I/O操作没有通过缓存。
字符设备操作以字节为基础,但不是说一次只能执行一个字节操作。
例如对于字符设备我们可以通过mmap一次进行大量数据交换。
字符设备实现比较简单和灵活。
1.2设计内容
本次设计是简单的字符设备驱动设计,基于mini2440的蜂鸣器的驱动设计。
2.开发环境的搭建
2.1Redhat的安装
创建一个虚拟机:
点击菜单栏File->New->Virtualmachine。
点击下一步。
选择Typical选项。
选择Linux下的RedHatLinux
填写虚拟机的命名和存储地址。
选择磁盘大小
2.2安装arm-linux-gcc交叉编译器
将arm-linux-gcc-4.3.2.tgz复制到虚拟机的root目录下
解压文件:
tarzxvfarm-linux-gcc-4.3.2.tgz
在bash_profile里添加路径:
gedit~/.bash_profiel
路径/root/usr/local/arm/4.3.2/bin
source~/.bash_profile使更改生效
2.3安装及编译linux-2.6.29-mini2440-20090708内核
复制内核到root目录下
解压内核文件tarzxvflinux-2.6.29-mini2440-20090708.tgz
使内核文件生效:
cpconfig_mini2440_n35.config
使用make命令完成编译
3.字符设备驱动相关知识
3.1模块机制
Linux提供了机制被称为模块(Module)的机制
提供了对许多模块支持,包括但不限于,设备驱动
每个模块由目标代码组成(没有连接成一个完整可执行程序)
insmod将模块动态加载到正在运行内核
rmmod程序移除模块
Linux内核模块的程序结构
●staticint__initbeep_init(void)---模块初始化函数
通过alloc_chrdev_region来分配设备号beep_cdev来对设备进行各种操作。
比如在加载内核模块时,模块的加载函数会自动被内核执行,完成模块的相关初始化工作
●staticvoid__exitbeep_exit(void)---模块卸载函数(必须)
当通过unregister_chrdev_region命令卸载某模块时,模块的卸载函数会自动被内核执行,完成与模块装载函数相反的功能
●MODULE_LICENSE()---模块许可证声明(必须)
模块许可证(LICENSE)声明描述内核模块的许可权限
如果不声明LICENSE,模块被加载时,将收到内核被污染(kerneltainted)的警告
●其他一些声明MODULE_XXXXX()---模块声明(可选)
模块加载函数
staticint__initinitialization_function(void)
{
/*初始化代码*/
}
module_init(initialization_function);
模块卸载函数
staticvoid__exitcleanup_function(void)
{
/*释放资源*/
}
module_exit(cleanup_function);
3.2字符设备开发基本步骤
●确定主设备号和次设备号
●实现字符驱动程序
实现file_operations结构体
实现初始化函数,注册字符设备
实现销毁函数,释放字符设备
●创建设备文件节点
3.3主设备号和次设备号
●主设备号是内核识别一个设备的标识。
整数(占12bits),范围从0到4095,通常使用1到255
●次设备号由内核使用,用于正确确定设备文件所指的设备。
整数(占20bits),范围从0到1048575,一般使用0到255
●设备编号的内部表达
dev_t类型(32位):
用来保存设备编号(包括主设备号(12位)和次设备号(20位))
从dev_t获得主设备号和次设备号:
MAJOR(dev_t);
MINOR(dev_t);
将主设备号和次设备号转换成dev_t类型:
MKDEV(intmajor,intminor);
●分配主设备号
手工分配主设备号:
找一个内核没有使用的主设备号来使用。
#include
intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);
●动态分配主设备号:
#include
intalloc_chrdev_resion(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);
●释放设备号
voidunregister_chrdev_region(dev_tfirst,unsignedintcount);
3.4实现字符驱动程序
●cdev结构体
structcdev
{
dev_tbeep_devno;/*设备号*/
unsignedint*map;
structfile_operations*ops;/*文件操作结构体*/
unsignedintcount;
};
●file_operations结构体
字符驱动和内核的接口:
在include/linux/fs.h定义
字符驱动只要实现一个file_operations结构体
并注册到内核中,内核就有了操作此设备的能力。
●file_operations的主要成员:
open:
打开设备
release:
关闭设备
read:
从设备上读数据
write:
向设备上写数据
ioctl:
I/O控制函数
●ioctl函数
为设备驱动程序执行“命令”提供了一个特有的入口点
用来设置或者读取设备的属性信息。
intioctl(structinode*inode,structfile*filp,
unsignedintcmd,unsignedlongarg);
●cmd参数的定义
不推荐用0x1,0x2,0x3之类的值
Linux对ioctl()的cmd参数有特殊的定义
构造命令编号的宏:
_IO(type,nr)用于构造无参数的命令编号;
_IOR(type,nr,datatype)用于构造从驱动程序中读取数据的命令编号;
_IOW(type,nr,datatype)用于写入数据的命令;
_IOWR(type,nr,datatype)用于双向传输。
type和number位字段通过参数传入,而size位字段通过对datatype参数取sizeof获得。
●Ioctl函数模板
intxxx_ioctl(structinode*inode,structfile*filp,unsignedintcmd,
unsignedlongarg)
{
...
switch(cmd)
{
caseXXX_CMD1:
...
break;
caseXXX_CMD2:
...
break;
default:
///*不能支持的命令*/
return-ENOTTY;
}
return0;
}
3.5字符设备驱动结构
4.蜂鸣器原理
4.1蜂鸣器的种类和工作原理
蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。
压电式蜂鸣器主要由多谐振荡器、压电蜂鸣片、阻抗匹配器及共鸣箱、外壳等组成。
有的压电式蜂鸣器外壳上还装有发光二极管。
多谐振荡器由晶体管或集成电路构成。
当接通电源后(1.5~15V直流工作电压),多谐振荡器起振,输出1.5~2.5kHZ的音频信号,阻抗匹配器推动压电蜂鸣片发声。
电磁式蜂鸣器由振荡器、电磁线圈、磁铁、振动膜片及外壳等组成。
接通电源后,振荡器产生的音频信号电流通过电磁线圈,使电磁线圈产生磁场。
振动膜片在电磁线圈和磁铁的相互作用下,周期性地振动发声。
有源蜂鸣器和无源蜂鸣器的区别:
这个“源”字是不是指电源,而是指震荡源,即有源蜂鸣器内有振荡源而无源蜂鸣器内部没有振荡源。
有振荡源的通电就可以发声,没有振荡源的需要脉冲信号驱动才能发声。
4.2开发板上蜂鸣器原理图分析
由原理图可以得知,蜂鸣器是通过GPB0IO口使用PWM信号驱动工作的,而GPB0口是一个复用的IO口,要使用它得先把他设置成TOUT0PWM输出模式。
4.3GPB0参数
5.总体设计
5.1设计思路
Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载:
(1)直接编译进Linux内核,随同Linux启动时加载;
(2)编译成一个可加载和删除的模块,使用insmod加载(modprobe和insmod命令类似,但依赖于相关的配置文件),rmmod删除。
这种方式控制了内核的大小,而模块一旦被插入内核,它就和内核其他部分一样。
这次的蜂鸣器驱动就采用动态模块加载的方式
5.2设计步骤
Ø编写简单的字符设别驱动程序框架
Ø编写控制蜂鸣器控制开关函数
Ø编译模块,生成.ko
Ø编写用户层测试程序
Ø编译用户层测试程序,生成可执行程序beep_test
Ø将生成的.ko模块和应用层测试程序beep_test下载到目标板
Ø用insmod装载模块
Ø创建设备节点
mknod/dev/beepc2530
Ø运行用户层测试程序beep_test
#./beep_test
如果你的beep_test的属性不是可执行的,可以用chmod777beep_test将其设置成可执行
程序。
6.驱动及测试程序
6.1beep.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
structcdevbeep_cdev;
dev_tbeep_devno;
unsignedint*map;
intbeep_open(structinode*node,structfile*fp)
{
unsignedintdata;
request_mem_region(0xE02000A0,4,"beep");
map=ioremap(0xE02000A0,8);
data=ioread32(map);
data=data&(~0x1<<1);
data=data&(~0x1<<2);
data=data&(~0x1<<3);
data=data|0x1;
iowrite32(data,map);
printk("beep_open\n");
return0;
}
voidbeep_start(void)
{
unsignedintdata;
data=ioread32(map+0x1);
data=data|0x1;
iowrite32(data,map+0x1);
}
voidbeep_stop(void)
{
unsignedintdata;
data=ioread32(map+0x1);
data&=~0x1;
iowrite32(data,map+0x1);
}
intbeep_close(structinode*node,structfile*fp)
{
return0;
}
ssize_tbeep_read(structfile*fp,char__user*buff,size_tlen,loff_t*lfot)
{
charload[100]="hello\n";
printk("beep_read\n");
copy_to_user(buff,load,100);
return0;
}
ssize_tbeep_write(structfile*fp,constchar__user*buff,size_tlen,loff_t*lfot)
{
charload[100];
printk("beep_write\n");
copy_from_user(load,buff,100);
printk("%s\n",load);
return0;
}
intbeep_ioctl(structinode*node,structfile*fp,unsignedintcmd,unsignedlongparm)
{
printk("beep_ioctl\n");
switch(cmd)
{
case0:
beep_stop();
break;
case1:
beep_start();
break;
}
return0;
}
structfile_operationsfops=
{
.open=beep_open,
.release=beep_close,
.read=beep_read,
.write=beep_write,
.ioctl=beep_ioctl
};
staticint__initbeep_init(void)
{
intmajor;
alloc_chrdev_region(&beep_devno,0,1,"beep");
major=MAJOR(beep_devno);
printk("major%d\n",major);
cdev_init(&beep_cdev,&fops);
beep_cdev.owner=THIS_MODULE;
beep_cdev.dev=beep_devno;
beep_cdev.ops=&fops;
beep_cdev.count=1;
cdev_add(&beep_cdev,beep_devno,1);
return0;
}
staticvoid__exitbeep_exit(void)
{
unregister_chrdev_region(beep_devno,1);
cdev_del(&beep_cdev);
}
module_init(beep_init);
module_exit(beep_exit);
6.2beep_tset.c
#include
#include
intmain(void)
{
intfp;
charbuff[100]="happy!
\n";
fp=open("/dev/beep",O_RDWR);
while
(1)
{
ioctl(fp,1,0);
sleep
(1);
ioctl(fp,0,0);
sleep
(1);
}
close(fp);
return1;
}
7.运行结果及截图
综合设计总结与思考
在这一次的嵌入式培训中,老师要求我们做的是一个嵌入式驱动设计,即做一个简单的蜂鸣器的驱动,首先我们的专业就是嵌入式应用技术,利用这次培训对我们的专业大概有了理解及未来的发展方向也有些确定。
在以前的学习过程中也很少接触到这些技术上面的具体应用,所以我们的基础比较薄弱。
但是经过这几次的培训课程,我们发现功夫不负有心人,只要认真努力就会学习到不少东西。
首先这次的是基于linux内核系统的应用。
安装redhat开发环境。
在里面写代码并且进行编译。
经过老师细心的讲解,才理解一些关于驱动开发的知识,刚接触这些感觉到开发其实不是一件简单的事情。
主要就是编写驱动的代码,分为很多模块,需要通过很多函数及其它们之间的调用。
通过这次的培训,真的发现自己所学到的东西真的很少,而且大部分都是理论知识。
并且没有真正运用到实践当中,所以以后要更加努力的去学习,把自己的各方面知识掌握、巩固、强化,将所学知识融汇到实践操作中。
教师评阅
考勤情况
设计态度
设计完成情况
实验报告
优
良
中
差
优
良
中
差
优
良
中
差
优
良
中
差
成绩:
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 蜂鸣器驱动程序设计 蜂鸣器 驱动程序 设计