嵌入式Linux实验报告.docx
《嵌入式Linux实验报告.docx》由会员分享,可在线阅读,更多相关《嵌入式Linux实验报告.docx(33页珍藏版)》请在冰豆网上搜索。
嵌入式Linux实验报告
嵌入式程序设计
实验报告
评语:
成绩
教师:
年月日
班级:
学号:
姓名:
地点:
EII-506
时间:
2013年6月
实验一开发环境的搭建与配置
【实验目的】
1)熟悉嵌入式Linux开发平台。
2)掌握嵌入式Linux开发平台的开发环境搭建与配置。
3)了解minicom配置串口通信参数的过程。
4)了解嵌入式Linux的启动过程。
5)掌握程序交叉编译运行及调试的一般方法。
6)掌握网络文件系统NFS的配置方法。
7)掌握嵌入式系统内核的编译、文件系统的打包及镜像的下载方法。
【实验内容】
1)连接实验开发板与宿主机。
2)在虚拟机中的CentOS(宿主机)搭建开发环境。
3)在宿主机中配置minicom。
4)分析嵌入式Linux的启动过程。
5)在宿主机上编写简单的C语言程序并用交叉编译工具进行编译,然后传输到目标机上运行。
6)在宿主机上编写简单的C语言程序并用交叉编译工具进行编译,用gdbserver进行远程调试。
7)配置NFS并用NFS进行文件拷贝。
8)嵌入式系统内核编译与文件系统的打包。
9)内核文件镜像与文件系统镜像的下载(从宿主机下载到目标机)。
【实验步骤】
连接实验开发板,对虚拟机进行设置
工具链的配置
tftp的安装
进入minicom软件,配置串口通信参数
有关串口通信选项的含义:
"Filenamesandpaths":
选择需要传输的文件和路径
"Filetransferprotocols":
选择传输文件的通信协议
"Serialportsetup":
设置串口通信参数
"Savesetupasdfl":
将设置好的各项参数保存为dfl
"Savesetupas":
将设置好的各项参数保存为自定义的文件名
"Exit":
退出返回到minicom设置好后的终端
"ExitfromMinicom":
从minicom命令中退出返回Linux终端
将光标移到"Serialportsetup",按回车键会弹出串口通信参数的配置菜单。
实验开发板的启动
嵌入式Linux系统的启动过程分析
1)启动Bootloader
Bootloader是嵌入式系统的引导加载程序,它是系统上电后运行的第一段程序,其作用类似于PC机上的BIOS。
在本系统中这段程序的起始地址为0x00000000。
Bootloader在完成初始化RAM、初始化串口、检测处理器类型、设置Linux启动参数后,开始调用Linux内核。
本系统Linux内核镜像zImage放在Flash中,Bootloader首先把它拷贝到RAM中,然后跳转到RAM中对zImage进行解压缩。
解压缩后启动内核。
2)加载内核
内核启动后先进行一系列与内核相关的初始化,然后调用第一个用户进程——init进程并等待用户进程的执行。
具体的过程如下:
进行与体系结构相关的第一个初始化工作,首先通过检测出来的处理器类型进行处理器内核的初始化,然后进行内存结构的初始化,最后开启MMU,创建内核页表,映射所有的物理内存和IO空间;
创建异常向量表和初始化中断处理函数;
初始化系统核心进程调度器和时钟中断处理机制;
初始化串口控制台,在minicom中看到的系统启动过程中的信息都是通过串口输出的;
创建和初始化系统cache,为各种内存调用机制提供缓存,包括动态内存分配、虚拟文件系统及页缓存;
初始化内存管理,检测内存大小及被内核占用的内存情况;
初始化系统的进程间通信机制(IPC);
创建init进程,结束内核的启动。
3)执行init进程。
内核被加载后,第一个运行的程序便是/sbin/init,init进程是所有进程的发起者和控制者,它的进程号是1。
init进程首先读取/etc/inittab文件,并依据此文件来进行初始化工作(首先进行一系列的硬件初始化,然后通过命令行传递过来的参数挂载根文件系统。
最后执行一些其它的进程)。
init配置文件每行的基本格式为“id:
runlevel_ignored:
action:
process”,其中某些部分可以为空。
各部分的具体内容如下:
id:
指定启动进程的控制终端,如果所启动的进程并不是可以交互的shell,应该会有个控制终端(在PC机上该字段表示配置行的惟一标识)。
runlevel_ignored:
该字段是忽略掉的,配置inittab时空着它就行了(在PC机上该字段用来配置所启动进程适用的系统运行级别)。
4)执行/bin/login程序。
有些嵌入式系统在init进程执行完后会执行/bin/login。
login程序会提示使用者输入账号及密码,接着编码并确认密码的正确性,如果账号与密码相符,则为使用者初始化环境,并将控制权交给shell,即等待用户登录。
本系统在执行完init进程后直接开始执行/bin/sh,进入shell交互程序(跳过了执行/bin/login这一步)。
这个可以通过图27中的语句“ttyS0:
:
askfirst:
-bin/sh”来说明。
程序的交叉编译及运行
gdbserver远程调试
NFS的配置
内核配置与编译
文件系统的打包
内核与文件系统的下载
实验二并发Web服务器的实现
一、【实验目的】
1)熟悉Linux网络编程。
2)了解Web服务器原理。
3)掌握嵌入式Linux多进程、多线程、I/O多路复用三种方式并发服务器的实现。
二、【实验内容】
1)用多进程实现Web服务器。
2)用多线程实现Web服务器。
3)用I/O多路复用方式实现Web服务器。
三、【实验步骤】
1.环境配置
2.实现多进程Web服务器
1)用arm-linux-gcc命令编译源程序,得到可执行程序web_server_process。
2)用vi文本编译器创建文件index.html,用于测试Web服务器。
3)编辑文件index.html,然后保存并退出vi编辑器。
4)在/mnt/nfs/web目录下创建子目录cgi-bin,并用vi文本编辑器在cgi-bin目录下创建文件hello.cgi,用于测试Web服务器。
5)编辑文件hello.cgi,然后保存并退出vi编辑器。
6)用ls命令可以看到root用户对文件hello.cgi没有执行权限,通过chmod命令来修改hello.cgi的权限,使它称为可执行文件。
7)用命令service启动宿主机上的nfs服务,并用exportfs命令查看nfs的共享目录。
然后在目标机上挂载nfs
8)在目标机中运行web_server_process。
9)打开宿主机的浏览器,输入http:
//192.168.0.5/file,查看执行结果
10)在宿主机的浏览器中输入http:
//192.168.0.5,查看执行结果。
11)在宿主机的浏览器中输入http:
//192.168.0.5/index.html,查看执行结果。
12)在宿主机的浏览器中输入http:
//192.168.0.5/cgi-bin/hello.cgi,查看执行结果。
13)在宿主机的浏览器中输入http:
//192.168.0.5/web_server_process.c,查看执行结果。
3.实现多线程Web服务器
1)用arm-linux-gcc命令编译源程序,得到可执行程序web_server_thread。
2)在目标机中运行web_server_thread。
3)在宿主机的浏览器中输入http:
//192.168.0.5,查看执行结果。
4)在宿主机的浏览器中输入http:
//192.168.0.5/cgi-bin/hello.cgi,查看执行结果。
5)在宿主机的浏览器中输入http:
//192.168.0.5/web_server_thread.c,查看执行结果。
4.实现I/O多路复用方式的Web服务器
1)用arm-linux-gcc命令编译源程序,得到可执行程序web_server_select。
2)在目标机中运行web_server_select。
3)在宿主机的浏览器中输入http:
//192.168.0.5,查看执行结果。
4)在宿主机的浏览器中输入http:
//192.168.0.5/cgo-bin/hello.cgi,查看执行结果。
5)在宿主机的浏览器中输入http:
//192.168.0.5/web_server_select.c,查看执行结果。
实验三嵌入式Linux驱动
一、【实验目的】
1)熟悉嵌入式Linux驱动程序编写框架。
2)了解七段数码管驱动程序的工作原理,熟练掌握该驱动程序在嵌入式开发平台的移植和注册使用。
3)了解16键矩阵键盘驱动程序的工作原理,熟练掌握该驱动程序在嵌入式开发平台的移植和注册使用。
二、【实验内容】
1)学习Linux驱动源代码,分析代码中各个函数模块的功能作用。
2)在宿主机上交叉编译七段数码管驱动程序,然后移植到目标机上。
3)在目标机上注册驱动程序,验证驱动的功能。
三、【实验步骤】
1.了解七段数码管工作原理
七段数码管是显示数字的电子元件,因为借助七个发光二极管以不同组合来显示数字,所以称为七段数码管(如图1)。
七段数码管分为共阴极和共阳极,共阳极的七段数码管的正极(或者阳极)为八个发光二极管的共有正极,其他接点为独立发光二极管的负极(或者阴极),使用者只需要把正极接电,不同的负极接地就可以控制七段数码管显示不同的数字。
共阴极的七段数码管与共阳极的只是接电的接法相反而已。
图1
2.开发板七段数码管电路介绍
开发板上有四个七段共阴数码管,2个一组,第一组七段数码管使用系统LED_CS2作为其位选使能信号,两个数码管的段选信号分别使用数据总线的D0~D7位和D8~D15位,如图2所示。
图2
第二组七段数码管使用系统LED_CS3作为其位选使能信号,两个数码管的段选信号分别使用数据总线的D0~D7位和D8~D15位,如图3所示。
图3
分析可知,对七段数码管的操作主要是对其位选和段选信号的控制。
其
中位选信号决定显示哪个七段数码管,段选信号决定其显示的字型信息(共阴极七段数码管段选控制信息如表1),这也是驱动程序和硬件关联的主要部分。
D7
D6
D5
D4
D3
D2
D1
D0
字型
Dp
G
F
E
D
C
B
A
编码
0
0
1
1
1
1
1
1
1
0X3F
1
0
0
0
0
0
1
1
0
0X06
2
0
1
0
1
1
0
1
1
0X5B
3
0
1
0
0
1
1
1
1
0X4F
4
0
1
1
0
0
1
1
0
0X66
5
0
1
1
0
1
1
0
1
0X6D
6
0
1
1
1
1
1
0
1
0X7D
7
0
0
0
0
0
1
1
1
0X07
8
0
1
1
1
1
1
1
1
0X7F
9
0
1
1
0
1
1
1
1
0X6F
A
0
1
1
1
0
1
1
1
0X77
B
0
1
1
1
1
1
0
0
0X7C
C
0
0
1
1
1
0
0
1
0X39
D
0
1
0
1
1
1
1
0
0X5E
E
0
1
1
1
1
0
0
1
0X79
F
0
1
1
1
0
0
0
1
0X71
表1
3.七段数码管驱动程序分析
1)添加驱动程序所需的头文件和变量:
SEG_CS1和SEG_CS2就是上面硬件接口所提及的两组七段数码管的位选使能信号,LED[10]数组中保存的就是在共阴极数码管上面显示0~9的段选信号。
Seg这个结构体用于保存4个数码管即时显示的数字的段选信号。
#include
#include
#include
#include
#include
MODULE_LICENSE("GPL");//用于声明描述内核模块的许可权限,如果不声明LICENSE,模块被加载时将收到内核被污染(kerneltainted)的警告
charLED_MODULE=0;
#defineDEVICE_NAME"xidian_seg7"
#defineSEG_CS10x10300000
#defineSEG_CS20x10400000
staticcharLED[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7F,0x6F};
unsignedlong*CS1_Address,*CS2_Address;
structseg
{
charLED1_Val;
charLED2_Val;
charLED3_Val;
charLED4_Val;
charnegative;
};
2)同时更新所有七段数码管驱动显示函数:
CS1_address对应第一组七段数码管的位选信号,该组第一个数码管的段选信号保存在short变量的低8位,该组第二个数码管的段选信号保存在short变量的高8位。
CS2_address对应第二组七段数码管,其余操作和第一组的七段数码管一致。
staticvoidUpdateled(structseg*seg_7)
{
unsignedshortbuff=0x00;
buff=seg_7->LED1_Val;
buff=buff|(seg_7->LED2_Val<<8);
writew(buff,CS1_Address);
buff=0x00;
buff=seg_7->LED3_Val;
buff=buff|(seg_7->LED4_Val<<8);
writew(buff,CS2_Address);
return;
}
3)写具体某位七段数码光驱动管显示函数:
position对应的是4个七段数码管的相对位置,value就是需要更新的七段数码管要显示数字的段选信号值。
voidvalue_seting(structseg*seg_7,charposition,charvalue)
{
if(seg_7->negative==0)
value=~value&~(0x1<<7);
else
value=(0x1<<7)|value;
if(position==1)
seg_7->LED1_Val=value;
elseif(position==2)
seg_7->LED2_Val=value;
elseif(position==3)
seg_7->LED3_Val=value;
elseif(position==4)
seg_7->LED4_Val=value;
}
4)实现七段数码管驱动写操作函数:
把用户写入的数码管显示更新数据,转换成为要显示数字对应的段选信号,并且保存在led_forall数组中,并且调用Value_setting更新显示数据,最后调用Updateled()更新实际的数码管显示信息。
staticssize_tseg7_write(structfile*file,constchar*buffer,size_tcount,loff_t*ppos)
{
inti;
structseg*seg_7=file->private_data;
charled_forall[4];
printk(KERN_EMERG"TheModuleiswritten,seg7_write\n");
if(count!
=4)
{
printk(KERN_EMERG"thecountofinputisnot4!
!
");
return0;
}
if(copy_from_user(led_forall,buffer,4))
return–EFAULT;
for(i=1;i<=4;i++)
{
value_seting(seg_7,i,LED[(int)led_forall[i-1]]);
}
Updateled(seg_7);
return0;
}
5)实现七段数码管驱动IOCTL操作函数。
staticintseg7_ioctl(structinode*ip,structfile*fp,unsignedintcmd,unsignedlongarg)
{
charval=0x00;
structseg*seg_7=fp->private_data;
if(!
arg)
return-EINVAL;
if(copy_from_user(&val,(int*)arg,sizeof(char)))
return-EFAULT;
switch(cmd){
case1:
value_seting(seg_7,1,val);
break;
case2:
value_seting(seg_7,2,val);
break;
case3:
value_seting(seg_7,3,val);
break;
case4:
value_seting(seg_7,4,val);
break;
case0:
seg_7->negative=LED_MODULE;
break;
default:
printk(KERN_EMERG"ioctlparameterinputerror,pleaseinputnumber0-4");
break;
}
Updateled(seg_7);
return0;
}
6)实现七段数码管驱动打开操作函数。
staticintseg7_open(structinode*inode,structfile*filp)
{
structseg*seg_7;
printk(KERN_EMERG"TheModuleisopen,seg7_open\n");
seg_7=kmalloc(sizeof(structseg),GFP_KERNEL);//分配内存(在内核空间)seg_7->negative=LED_MODULE;
filp->private_data=seg_7;
return0;
}
7)实现七段数码管驱动释放函数。
staticintseg7_release(structinode*inode,structfile*filp)
{
printk(KERN_EMERG"TheModuleisrelease,seg7_release\n");
kfree(filp->private_data);
return0;
}
8)七段数码管驱动文件结构体定义:
定义七段数码管驱动程序的打开,写入,释放,Ioctl操作,属主信息。
staticstructfile_operationsEmdoor_fops={
open:
seg7_open,
write:
seg7_write,
release:
seg7_release,
ioctl:
seg7_ioctl,
owner:
THIS_MODULE,
};
9)实现七段数码管驱动初始化函数:
映射七段数码管的位选信号的物理实际地址,注册设备。
staticint__initseg7_init(void)
{
intret;
printk(KERN_EMERG"TheModuleisInit,seg7_init\n");
CS1_Address=ioremap(SEG_CS1,4);
CS2_Address=ioremap(SEG_CS2,4);
ret=register_chrdev(56,DEVICE_NAME,&Emdoor_fops);
if(ret<0){
printk(DEVICE_NAME"can'tgetmajornumber\n");
returnret;
}
return0;
}
10)实现七段数码管驱动模块退出函数与模块描述:
取消对七段数码管的位选信号的物理地址的映射,释放设备。
staticvoid__exitseg7_exit(void)
{
printk(KERN_EMERG"TheModuleisExit,seg7_exit\n");
iounmap(CS1_Address);
iounmap(CS2_Address);
unregister_chrdev(56,DEVICE_NAME);
}
module_init(seg7_init);
module_exit(seg7_exit);
MODULE_AUTHOR("Mr.han");
MODULE_DESCRIPTION("Thisisa7SegmentLeddriverdemo");
11)七段数码管驱动程序的Makefile
CFLAGS+=$(DEBFLAGS)-Wall
ifneq($(KERNELRELEASE),)
obj-m:
=xidian_seg7.o
else
KERNELDIR?
=../linux-2.6
PWD:
=$(shellpwd)
ALL:
$(MAKE)$(CFLAGS)-C$(KERNELDIR)M=$(PWD)modules
endif
clean:
rm-fr*.o*.ko*.symvers*~core.depend.*.cmd*.mod.c.tmp_versions
4.编写七段数码管驱动的测试程序和Makefile文件
1)seg7_test.c源代码:
#include
#include
#include
#include
#include
typedefunsignedcharu8;
#defineSEG_DEV"/dev/xidian_seg7"
charnumber[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7F,0x6F};
voidclear_led(intfd)
{
inti;
c