嵌入式硬件综合设计课程设计报告Word格式.docx
《嵌入式硬件综合设计课程设计报告Word格式.docx》由会员分享,可在线阅读,更多相关《嵌入式硬件综合设计课程设计报告Word格式.docx(28页珍藏版)》请在冰点文库上搜索。
指导教师:
石磊
日期:
2016年5月19日
指导老师评阅成绩表
项目方案(10%)
设计质量(35%)
合作情况(5%)
答辩(20%)
设计报告(30%)
总分
课程答辩记录
教师主要
提问记录
1.编译驱动与编译应用程序有什么区别。
2.wake_up_interruptible(&
key_waitq)函数有什么作用。
3.按键消抖的原理。
学生回答
问题情况
1.编译驱动是依赖于与开发板相同版本的linux源码,而编译应用程序则是依赖操作系统提供的接口,和头文件。
2.唤醒注册在等待队列中的进程。
3.按键在按下之后波形不稳定,延时一段时间看按键是否真的按下,一般延时时间是20ms。
课程答辩成绩评定
答辩成绩
是否同意通过
□同意□不同意
答辩教师签名:
年月日
注:
课程设计类课程答辩不通过则课程考核不通过。
摘要
随着物联网的发展,人们对物联网的认识也越来越深,而物联网分为三个大层:
感知层,传输层,应用层。
而感知层是最下面的那一层,也就是各种传感器,这么多传感器,就少不了传感器的驱动编写,而驱动编写就变得很重要,稳定的驱动,才能提供稳定的服务。
为后面的传输,应用提供保障。
本次课程设计主要完成对S3C2440平台上按键控制LED,定时器的程序设计。
在linux-2.6.28内核中编译S3C2440的LED驱动,按键驱动,然后下载到开发板上,加载之后,再PC机上编写相应的应用程序,然后交叉编译后下载到开发板,完成对LED,定时器的控制。
关键词:
物联网linux-2.6.28驱动内核
目录
1引言1
1.1课题背景1
1.2本课题研究的迫切性1
1.3本课题的研究作用1
1.4本文的主要工作1
2系统功能需求2
2.1系统目标2
2.2开发环境及工具2
2.2.1Ubuntu14.102
2.2.2交叉编译工具2
2.2.3Minicom3
2.2.4Linux源文件5
2.2.5FL2440开发板6
2.3按键电路7
2.4LED电路8
3系统实现8
3.1LED驱动8
3.2按键驱动10
3.3应用程序15
4测试与问题18
结论21
参考文献21
引言
1.1课题背景
随着物联网的发展,嵌入式应用范围越来越广,涉及到人们生活的方方面面,如数字通信、信息家电、工业控制、智能交通等。
嵌入式技术与人们的日常生活联系得越来越紧密,消费电子、计算机、通信一体化趋势日益明显,作为计算机领域的一个重要组成部分,嵌入式系统再度成为研究与应用的热点。
本次课程设计主要针对物联网硬件底层开发的学习,了解开发板与宿主机的开发模式,以及字符设备驱动的开发流程,最终实现按键控制LED、定时器。
1.2本课题研究的迫切性
嵌入式硬件综合设计作为一门实践课,极好的将以前学的理论知识与实际结合起来,帮学生有效的提高了动手能力,了解了底层驱动的开发流程,对以后学习也有积极的作用。
1.3本课题的研究作用
了解了底层驱动的基本开发框架和流程,熟悉linux的开发环境,学习了字符设备驱动的编写,了解了内核的配置,编译,交叉环境的配置,宿主机与开发板的通信方式。
1.4本文的主要工作
介绍了基于嵌入式Linux平台的按键驱动程序设计与实现的原理和流程,说明了在开发过程遇到的错误以及完成状况。
系统功能需求
系统目标
利用FL2440按键,编写相关驱动程序,实现对Led灯的控制,S1键开启跑马灯,灯亮的时间为0.5秒,每个灯之间的点亮的时间间隔为1s,S2键控制16进制计时器,时钟为2秒,S3在S1与S2的模式下暂停,Led显示为按下时的状态。
开发环境及工具
2.2.1Ubuntu14.10
在
2.2.2交叉编译工具
什么是交叉编译
在一种计算机环境中运行的编译程序,能编译出在另外一种环境下运行的代码,我们就称这种编译器支持交叉编译。
这个编译过程就叫交叉编译。
简单地说,就是在一个平台上生成另一个平台上的可执行代码。
如keil软件,在keil上编译,但在单片机上运行,典型的交叉编译。
我们在开发板上运行的程序,是在PC机上编译的,所以是交叉编译。
交叉编译器
交叉编译器就是交叉编译的工具,linux2.6.28内核使用的编译器为3.4.1版本,该版本的编译器可以在网上下载,当然FL2440开发板光盘中提供了该编译器。
安装交叉编译器:
只需将该编译器的解压缩文件放在指定的目录下边。
指定目录为:
/usr/local/arm/若local下没有arm文件夹,则需要自己建一个。
在使用的时候有两种方式:
(以编译hello.c程序为例,生成可执行文件hello)
(1)#/usr/local/arm/3.4.1/bin/arm-linux-gcchello.c–ohello
(2)编辑/etc/bash.bashrc文件,
在最后增加路径:
exportPATH=/usr/local/arm/3.4.1/bin:
$PATH,
这样就把/usr/local/arm/3.4.1/bin添加到命令的默认路径,在编译的时候则可直接用如下的命令#arm-linux-gcchello.c–ohello
2.2.3Minicom
Ubuntu的源里有minicom,用下面命令安装minicom。
在终端里输入以下命令:
安装:
sudoget-aptinstallminicom(虚拟机要联网)
配置:
sudominicom-s
会出现图2-1界面:
图2-1
选择Serialportsetup配置如图2-2所示:
图2-2
A选项是选择串口驱动文件,linux不用安装驱动,自己带的有。
如果用的是USB转串口就是上图的这种路径,如果是虚拟机添加的串口就是/dev/ttyS0
E选项波特率和数据停止位设置。
F硬控流,一般设置为NO。
选择Filenamesandpaths,出现图2-3界面:
图2-3
A选项是开发板传到PC机的文件存储路径,可以自己设置。
B选项是PC机下载到开发板的文件存储路径,只有把文件放到这个文件夹,在下载界面才能看到文件。
确定退出之后,选择Savesetupasdfl保存为默认的配置。
之后就会进入minicom的主界面。
按Ctrl+A,再按Z,就能打开图2-4帮助界面:
图2-4
可以看到S是发送文件,R是接收文件。
这些选项都是要按了Ctrl+A之后才可以选择的。
2.2.4Linux源文件
先再根目录下建立一个文件夹FL2440,然后去
http:
//www.kernel.org/pub/linux/kernel/v2.6/下载linux的源码,下载完毕后把压缩包放在刚刚建立的FL2440的文件夹里。
然后用下面命令解压:
tar–jxvflinux-2.6.28.7.tar.bz2
解压后目录下会多一个linux-2.6.28.7目录,然后修改linux-2.6.28.7/Makefile文件的内容。
将ARCH
?
=$(SUBARCH)
CROSS_COMPILE
=
修改为
ARCH
=arm
=arm-linux-
然后再目录下执行makezImage在/arch/arm/boot生成内核镜像。
2.2.5FL2440开发板
FL2440是由飞凌嵌入式技术有限公司设计生产的一款嵌入式开发平台,它基于三星公司的ARM9处理器S3C2440A,内部带有全性能的MMU(内存处理单元),适用于设计移动手持设备类产品。
FL2440开发板采用核心板+底板设计,性能稳定可靠,具有高性能、低功耗、接口丰富和体积小等优良特性。
目前已成功移植Linux,WINCE等操作系统到FL2440开发板。
S3C2440介绍
体系结构:
—16/32位RISC体系结构和ARM920T内核指令集。
—采用ARM920TCPU内核支持ARM调试体系结构。
系统管理器
—支持大/小端模式。
—8个存储器bank,其中6个适用于ROM、SRAM和其它,另外两个适用于ROM/SRAM和同步DRAM。
—支持各种型号的ROM引导(NOR/NANDFLASH、EEPROM,或其它)。
DNANDHFLASH启动引导
—支持从NANDFLASH存储器直接启动。
—采用4KB内部缓冲器进行启动引导。
—启动之后NAND存储器仍然可作为外部存储器使用
中断控制器
—60个中断源(1个看门狗定时器,5个定时器,9个UARTs,24个外部中断,4个DMA,2个RTC,2个ADC,1个IIC,2个SPI,1个SDI,2个USB,1个LCD,1个电池故障,1个NAND和2个摄像头)1个AC97。
—支持电平/边沿触发模式的外部中断源。
—可编程的边沿/电平触发模式选择。
—支持为紧急中断请求提供快速中断(FIQ)服务。
通用I/O端口
—24个外部中断端口。
—多功能输入/输出端口。
看门狗定时器
—16位看门狗定时器。
—在定时器溢出时发生中断请求或系统复位。
工作电压
—内核:
1.2V,最高300MHz。
1.3V,最高400MHz。
—存储器:
1.8V/2.5V/3.0V/3.3V。
—IO口:
3.3V。
按键电路
FL2440有四个按键,接的IO口分别是GPF0,GPF2,GPF3,GPF4。
对应的中断为ENIT0,EINT2,EINT3,EINT4。
按键没有按下,IO口为高电平,按下后IO口为低电平。
电路图如图2-5所示
图2-5
LED电路
FL2440有四颗LED,分别接的IO口为:
GPB5,GPB6,GPB8,GPB10。
当IO口为低电平时点亮LED。
电路图如图2-6所示
图2-6
系统实现
LED驱动
LED驱动框架如图3-1。
图3-1
驱动代码:
从图2-6中可以看出,LED是共阳极。
IO口输出低电平点亮。
由于LED只需要控制,所以在驱动程序员里只用ioctl就可以了,不用写read,write等函数。
IO控制函数,应用程序调用ioctl的时候就会调用这个函数,控制LED。
cmd:
1:
LED点亮0:
LED熄灭。
从电路图看出IO口0点亮,在应用程序里我们一般认为1是点亮,所以在驱动程序里把cmd取反了。
arg:
LED的个数,由于板子上有四个LED,所以输入大于4,返回错误。
staticints3c2440_leds_ioctl(
structinode*inode,
structfile*file,
unsignedintcmd,
unsignedlongarg)
{
switch(cmd)
{
case0:
case1:
if(arg>
4)
{
return-EINVAL;
}
s3c2410_gpio_setpin(led_table[arg],!
cmd);
return0;
default:
return-EINVAL;
}
}
设备结构体,当应用程序调用相应的系统调用后,就会调用驱动里对应的函数。
staticstructfile_operationss3c2440_leds_fops={
.owner=THIS_MODULE,
.ioctl=s3c2440_leds_ioctl,
};
按键驱动
按键驱动框架如图3-2。
图3-2
在这里,按键采用中断的方式,不占用处理器的资源。
OPEN函数,在这个函数里实现了IO口的配置,中断的申请,中断的注册,初始化按键的初始状态,初始化了三个按键消抖定时器。
staticintkeys_open(structinode*inode,structfile*file)
inti;
intret;
for(i=0;
i<
KEY_COUNT;
i++)
s3c2410_gpio_cfgpin(key_irq[i].pin,key_irq[i].pin_set);
set_irq_type(key_irq[i].irq,IRQ_TYPE_EDGE_FALLING);
ret=request_irq(key_irq[i].irq,keys_interrupt,IRQF_DISABLED,key_irq[i].name,(void*)i);
if(ret)
{
break;
}
key_status[i]=KEY_UP;
setup_timer(&
key_timers[i],keys_timer,i);
if(ret)
i--;
for(;
i>
=0;
i--)
disable_irq(key_irq[i].irq);
free_irq(key_irq[i].irq,(void*)i);
return-EBUSY;
return0;
读取函数,返回三个按键的状态。
staticintkeys_read(structfile*file,char__user*buf,size_tcount,loff_t*offp)
unsignedlongret;
if(!
rd_flag)
if(file->
f_flags&
O_NONBLOCK)
return-EAGAIN;
}else
wait_event_interruptible(key_waitq,rd_flag);
rd_flag=0;
ret=copy_to_user(buf,(void*)key_status,min(sizeof(key_status),count));
returnret?
-EFAULT:
min(sizeof(key_status),count);
驱动中的轮询。
这个与应用程序的select使用相对应。
staticunsignedintkeys_poll(structfile*file,structpoll_table_struct*wait)
unsignedintmask=0;
poll_wait(file,&
key_waitq,wait);
if(rd_flag)
mask|=POLLIN|POLLRDNORM;
returnmask;
关闭函数,在应用程序调用close()关闭文件时调用。
staticintkeys_close(structinode*inode,structfile*file)
del_timer(&
key_timers[i]);
disable_irq(key_irq[i].irq);
free_irq(key_irq[i].irq,(void*)i);
设备操作结构体的定义,由于按键是输入设备,所以没有write,因为程序中要实时监测哪个键按下,所以这里用poll来在内核中遍历,来提供给应用程序的select判断资源是否可读取。
staticstructfile_operationskeys_fops=
.open=keys_open,
.release=keys_close,
.read=keys_read,
.poll=keys_poll,
按键中断处理函数。
用request_irq()将相应的中断和中断处理函数关联起来,当有按键按下进入这个函数,然后启动一个消抖定时器。
staticirqreturn_tkeys_interrupt(intirq,void*dev_id)
intkey=(int)dev_id;
if(key_status[key]==KEY_UP)
key_status[key]=KEY_UNCERTAIN;
key_timers[key].expires=jiffies+KEY_TIMER_DELAY1;
add_timer(&
key_timers[key]);
returnIRQ_RETVAL(IRQ_HANDLED);
定时器中断处理函数。
消抖之后进入这里,再读取按键的状态,如果按下,就把可读标志置1。
staticvoidkeys_timer(unsignedlongarg)
intkey=arg;
intup=s3c2410_gpio_getpin(key_irq[key].pin);
up)
if(key_status[key]==KEY_UNCERTAIN)
key_status[key]=KEY_DOWN;
rd_flag=1;
wake_up_interruptible(&
key_waitq);
key_timers[key].expires=jiffies+KEY_TIMER_DELAY2;
}else
key_status[key]=KEY_UP;
应用程序
编写用户程序过程中,通过open函数打开设备,通过调用相应的函数控制设备。
程序流程如图3-3
图3-3
应用程序与驱动调用关系如图3-4。
图3-4
程序代码:
跑马灯线程函数
void*ledpth(void*arg)
int*p=(int*)arg;
intfd=*p;
while
(1)
ledshow(fd);
主函数,创建两个线程。
分别是LED线程是16进制定时器线程。
intmain()
err=pthread_create(&
ledtid,NULL,ledpth,&
ledfd);
timetid,NULL,timepth,&
time);
{ret=select(keyfd+1,&
rds,NULL,NULL,NULL);
if(FD_ISSET(keyfd,&
rds))
ret=read(keyfd,key_status,sizeof(key_status));
if(ret!
=sizeof(key_status))
printf(“readfail\n”);
else
if(key_status[KEY1]==0)
{
ledflag=1;
}
if(key_status[KEY2]==0)
timeflag=1;
if(key_status[KEY3]==0)
if(ledflag==1)
ledflag=0;
if(timeflag==1)
timeflag=0;
}}}}}
测试与问题
加载驱动
图4-1
图4-2
建立设备文件节点
先cat/proc/devices驱动的主设备号
图4-3
然后在/dev目录下建立节点
图4-4
按下S2,跑马灯开启
图4-5
图4-6
按下S3,输出2s定时信息
图4-7
按下S4,定时输出暂停,跑马灯暂停,并且灯的状态就是暂停的那个状态
图4-8
图4-9
再按下S2,跑马灯启动
图4-10
再按下S3,输出2s定时信息
图4-11
结论
本设计经过一个多月的努力,基本实现了题目的要求。
实现了按键控制跑马灯开启与暂停,并且暂停跑马灯是暂停的那个状态。
按键暂停定时2s等功能。
在编写驱动期间,学习到很多课堂上没有的知识,还积累了很多实践经验,增强了动手能力和解决实际问题的能力。
在此之前,对于驱动和其他的编程知识只是略知皮毛,都没有编过驱动程序,对驱动也没有深入了解。
在短短两个月时间里,认真的学习了linux下字符驱动的编写,还有在驱动中申请中断,以及内核空间向用户空间传递数据等相关的编程知识,初步认识到驱动程序对于应用程序的重要性,