北邮嵌入式实验报告Word文件下载.docx
《北邮嵌入式实验报告Word文件下载.docx》由会员分享,可在线阅读,更多相关《北邮嵌入式实验报告Word文件下载.docx(37页珍藏版)》请在冰点文库上搜索。
当我们想用远端档案时,只需调用“mount”就可以远端系统挂接在自己的档案系统之下。
每次重启宿主PC机时,都须先输入命令:
servicenfsrestart,以启动nfs服务。
3.实验七
实验七通过用c语言编写的简单程序HelloWorld,测试前面几个实验是否成功配置好环境,从超级终端可以看到HelloWorld程序的运行结果。
实验步骤如下:
1)硬件连接:
连接宿主PC机和一台PXA270-RP目标板。
2)打开宿主PC机电源,进入Linux操作系统。
3)启动RedHat9.0的图形界面,如下图,若您是以root身份登陆在文本模式下,则输入命令startx启动图形界面。
进入RedHat9.0图形界面后,打开一个终端窗(Terminal)。
4)输入minicom然后回车,minicim设置为8NI无流控。
5)打开PXA270_RP目标板电源,按目标板上的BOOT键,在minicom中应该会看到如下图:
6)在minicom终端窗口中,如图,输入下列四条命令
root
ifconfigeth192.168.0.50up
mount-onolock192.168.0.100:
//mnt
cd/mnt
此时,先将该窗口最小化,在后面的第10操作步骤中还将会回到该窗口中进行操作。
7)宿主机上打开一个终端窗口(Terminal),点击【红帽/SystemTools/Terminal】启动终端窗口,输入下列4条命令:
①cd/home
②mkdirHW
③cdHW
④viHelloWorld.c/*请您输入程序7.1程序清单*/
此时会显示一个空白的屏幕,这条命令的含义是,使用Vi编辑器,对一个名叫HelloWorld.c的文件进行编辑,我们看到的空白窗口是对文件进行编辑的窗口,如下图。
就像在Windows系统下面使用写字板等一样道理。
在vi里面先单击键盘A键,然后左下角会变成—INSER。
输入程序的时候和其他编辑器是一样的,如下图。
当输入程序完毕后,单击键盘Esc键,然后按“:
”(冒号)此时左下角会出现冒号然后输入“wq”最后按“Enter”确认存盘退出vi编辑器,如下图。
8)在上面同一个终端窗口中,输入下列2条命令交叉编译HelloWorld.c源程序,并查看生成的.o目标文件,如图7-10,图7-11:
①arm-linux-gcc–oHelloWorldHelloWorld.c
②ls
等到再次出现提示符,代表程序已经正确编译。
如果此步出现错误信息,请查看错误信息,并且重新编辑原来的C文件,修改错误。
直到正确编译。
9)重新打开第7步最小化的开有minicom的终端窗口,即到PXA270-RP目标板的mnt目录下,请您输入下列3条命令,运行HelloWorld编译成功的HelloWorld目标程序:
①cdhome/HW/*回到minicom中目标板的/mnt/home/HW目录下*/
③./HelloWorld/*此时会看到如下图*/
四、驱动程序
4.设备驱动程序的概念
设备驱动程序实际是处理和操作硬件控制器的软件,从本质上讲,是内核中具有最高特权级的、驻留内存的、可共享的底层硬件处理例程。
驱动程序是内核的一部分,是操作系统内核与硬件设备的直接接口,驱动程序屏蔽了硬件的细节,完成以下功能:
对设备初始化和释放;
对设备进行管理,包括实时参数设置,以及提供对设备的操作接口;
读取应用程序传送给设备文件的数据或者回送应用程序请求的数据;
检测和处理设备出现的错误。
Linux操作系统将所有的设备全部看成文件,并通过文件的操作界面进行操作。
对用户程序而言,设备驱动程序隐藏了设备的具体细节,对各种不同设备提供了一致的接口,一般来说,是把设备映射为一个特殊的设备文件,用户程序可以像对其他文件一样对此设备文件进行操作。
这意味着:
由于每一个设备至少由文件系统的一个文件代表,因而都有一个“文件名”。
应用程序通常可以通过系统调用open()打开设备文件,建立起与目标设备的连接。
打开了代表着目标设备的文件,即建立起与设备的连接后,可以通过read()、write()、ioctl()等常规的文件操作对目标设备进行操作。
设备文件的属性由三部分信息组成:
第一部分是文件的类型,第二部分是一个主设备号,第三部分是一个次设备号。
其中类型和主设备号结合在一起惟一地确定了设备文件驱动程序及其界面,而次设备号则说明目标设备是同类设备中的第几个。
由于Linux中将设备当做文件处理,所以对设备进行操作的调用格式与对文件的操作类似,主要包括open()、read()、write()、ioctl()、close()等。
应用程序发出系统调用命令后,会从用户态转到核心态,通过内核将open()这样的系统调用转换成对物理设备的操作。
5.驱动程序结构
一个设备驱动程序模块的基本框架
在系统内部,I/O设备的存取通过一组固定的入口点来进行,入口点也可以理解为设备的句柄,就是对设备进行操作的基本函数。
字符型设备驱动程序提供如下几个入口点:
open入口点。
打开设备准备I/O操作。
对字符设备文件进行打开操作,都会调用设备的open入口点。
open子程序必须对将要进行的I/O操作做好必要的准备工作,如清除缓冲区等。
如果设备是独占的,即同一时刻只能有一个程序访问此设备,则open子程序必须设置一些标志以表示设备处于忙状态。
close入口点。
关闭一个设备。
当最后一次使用设备完成后,调用close子程序。
独占设备必须标记设备方可再次使用。
read入口点。
从设备上读数据。
对于有缓冲区的I/O操作,一般是从缓冲区里读数据。
对字符设备文件进行读操作将调用read子程序。
write入口点。
往设备上写数据。
对于有缓冲区的I/O操作,一般是把数据写入缓冲区里。
对字符设备文件进行写操作将调用write子程序。
ioctl入口点。
执行读、写之外的操作。
select入口点。
检查设备,看数据是否可读或设备是否可用于写数据。
select系统调用在检查与设备文件相关的文件描述符时使用select入口点。
6.设备注册和初始化
设备的驱动程序在加载的时候首先需要调用入口函数init_module(),该函数最重要的一个工作就是向内核注册该设备,对于字符设备调用register_chrdev()完成注册。
register_chrdev的定义为:
intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);
其中,major是为设备驱动程序向系统申请的主设备号,如果为0,则系统为此驱动程序动态分配一个主设备号。
name是设备名,fops是对各个调用的入口点说明。
此函数返回0时表示成功;
返回-EINVAL,表示申请的主设备号非法,主要原因是主设备号大于系统所允许的最大设备号;
返回-EBUSY,表示所申请的主设备号正在被其他设备程序使用。
如果动态分配主设备号成功,此函数将返回所分配的主设备号。
如果register_chrdev()操作成功,设备名就会出现在/proc/dvices文件中。
Linux在/dev目录中为每个设备建立一个文件,用ls–l命令列出函数返回值,若小于0,则表示注册失败;
返回0或者大于0的值表示注册成功。
注册以后,Linux将设备名与主、次设备号联系起来。
当有对此设备名的访问时,Linux通过请求访问的设备名得到主、次设备号,然后把此访问分发到对应的设备驱动,设备驱动再根据次设备号调用不同的函数。
当设备驱动模块从Linux内核中卸载,对应的主设备号必须被释放。
字符设备在cleanup_module()函数中调用unregister_chrdev()来完成设备的注销。
unregister_chrdev()的定义为:
intunregister_chrdev(unsignedintmajor,constchar*name);
包括设备注册在内,设备驱动的初始化函数主要完成的功能是有以下5项。
(1)对驱动程序管理的硬件进行必要的初始化。
对硬件寄存器进行设置。
比如,设置中断掩码,设置串口的工作方式、并口的数据方向等。
(2)初始化设备驱动相关的参数。
一般说来,每个设备都要定义一个设备变量,用以保存设备相关的参数。
在这一步骤里对设备变量中的项进行初始化。
(3)在内核注册设备。
调用register_chrdev()函数来注册设备。
(4)注册中断。
如果设备需要IRQ支持,则要使用request_irq()函数注册中断。
(5)其他初始化工作。
初始化部分一般还负责给设备驱动程序申请包括内存、时钟、I/O端口等在内的系统资源,这些资源也可以在open子程序或者其他地方申请。
这些资源不用时,应该释放,以利于资源的共享。
若驱动程序是内核的一部分,初始化函数则要按如下方式声明:
int__initchr_driver_init(void);
其中__init是必不可少的,在系统启动时会由内核调用chr_driver_init,完成驱动程序的初始化。
当驱动程序是以模块的形式编写时,则要按照如下方式声明:
intinit_module(void)
当运行后面介绍的insmod命令插入模块时,会调用init_module函数完成初始化工作。
7.设备驱动程序的开发过程
由于嵌入式设备由于硬件种类非常丰富,在默认的内核发布版中不一定包括所有驱动程序。
所以进行嵌入式Linux系统的开发,很大的工作量是为各种设备编写驱动程序。
除非系统不使用操作系统,程序直接操纵硬件。
嵌入式Linux系统驱动程序开发与普通Linux开发没有区别。
可以在硬件生产厂家或者Internet上寻找驱动程序,也可以根据相近的硬件驱动程序来改写,这样可以加快开发速度。
实现一个嵌入式Linux设备驱动的大致流程如下。
(1)查看原理图,理解设备的工作原理。
一般嵌入式处理器的生产商提供参考电路,也可以根据需要自行设计。
(2)定义设备号。
设备由一个主设备号和一个次设备号来标识。
主设备号惟一标识了设备类型,即设备驱动程序类型,它是块设备表或字符设备表中设备表项的索引。
次设备号仅由设备驱动程序解释,区分被一个设备驱动控制下的某个独立的设备。
(3)实现初始化函数。
在驱动程序中实现驱动的注册和卸载。
(4)设计所要实现的文件操作,定义file_operations结构。
(5)实现所需的文件操作调用,如read、write等。
(6)实现中断服务,并用request_irq向内核注册,中断并不是每个设备驱动所必需的。
(7)编译该驱动程序到内核中,或者用insmod命令加载模块。
(8)测试该设备,编写应用程序,对驱动程序进行测试。
五、基本接口实验
在完成了基本实验后,我们开始着手基本接口实验。
在这些实验中,我们学习如何编写设备驱动程序,及如何用测试程序检验驱动程序是否正确,并通过改写测试程序正常地对驱动程序进行相关操作。
8.实验十二简单设备驱动程序
本次实验的任务是编写一个字符型设备驱动程序,并学习在应用程序中调用驱动。
考虑到我们初次接触驱动程序的编写,对此还十分陌生,因此指导书中提供了本次实验所要用到的程序源代码。
虽然这样一个字符型设备驱动程序并没有任何实际作用,但是它让我们轻松掌握了嵌入式驱动的编写过程,因为复杂繁琐的驱动,其骨架都是相同的。
因此,看懂本实验的源代码,学习并模仿其编写方法,对于后续实验有着非常重要的意义。
9.实验十三CPUGPIO驱动程序设计
在本实验中,我们要编写第一个针对实际硬件的驱动程序。
我们知道,凡是操作系统控制外部设备,即使是最简单的硬件电路,也是需要驱动的。
本实验涉及的外部硬件只有电阻和发光二极管。
我们使用自己编写的驱动程序与应用程序控制GPIO96的电平,通过LED的亮灭来判断,是否CPU做出了正确的响应。
补充代码
(1)
//-------------------WRITE-----------------------
ssize_tSIMPLE_GPIO_LED_write(structfile*file,constchar*buf,size_tcount,loff_t*f_ops)
{
#ifdefOURS_GPIO_LED_DEBUG
printk("
SIMPLE_GPIO_LED_write[--kernel--]\n"
);
#endif
returncount;
}
补充代码
(2)
//-------------------OPEN------------------------
ssize_tSIMPLE_GPIO_LED_open(structinode*inode,structfile*file)
SIMPLE_GPIO_LED_open[--kernel--]\n"
MOD_INC_USE_COUNT;
return0;
补充代码(3)
//-------------------------------------------------
structfile_operationsGPIO_LED_ctl_ops={
open:
SIMPLE_GPIO_LED_open,
read:
SIMPLE_GPIO_LED_read,
write:
SIMPLE_GPIO_LED_write,
ioctl:
SIMPLE_GPIO_LED_ioctl,
release:
SIMPLE_GPIO_LED_release,
};
实验作业
要求在目标板上LED闪烁产生亮7秒,灭2秒的效果
在测试程序中有这样一段代码:
while
(1)
{ioctl(fd,LED_OFF);
sleep
(1);
//休眠1秒
ioctl(fd,LED_ON);
}
只需将上面的代码改为如下代码即可:
sleep
(2);
//灭2秒
sleep(7);
//亮7秒
}
10.实验十四中断实验
在理论课中,我们学习了许多中断方面的知识,包括中断向量、中断优先级、中断过程等。
在PXA270系统里,中断控制器分外部设备和PXA270X处理器设备产生的两个层次的中断,前者是初级的中断源,后者是次级中断源,大量的次级中断源通常被映射为一个初级中断源。
补充代码1
voidshowversion(void)
printk("
*********************************************\n"
\t%s\t\n"
VERSION);
*********************************************\n\n"
staticintSimpleINT_temp_count=0;
补充代码2
//-------------------READ------------------------
ssize_tSIMPLE_INT_read(structfile*file,char*buf,size_tcount,loff_t*f_ops)
#ifdefOURS_INT_DEBUG
printk("
SIMPLE_INT_read[--kernel--]\n"
#endif
补充代码3
ssize_tSIMPLE_INT_write(structfile*file,constchar*buf,size_tcount,loff_t*f_ops)
SIMPL_INT_write[--kernel--]\n"
补充代码4
structfile_operationsINT_ctl_ops={
SIMPLE_INT_open,
SIMPLE_INT_read,
SIMPLE_INT_write,
SIMPLE_INT_ioctl,
SIMPLE_INT_release,
通过此实验,我了解了硬件中断管脚与中断号的对应关系,以及中断号与中断处理程序的对应关系,对于今后编写更为复杂的中断程序打下基础。
11.实验十五数码管显示实验
在此实验中,我们要编写针对74LV164的驱动程序,并用其串并转换功能来控制八段LED数码管的显示。
ssize_tSERIAL_LED_read(structfile*file,char*buf,size_tcount,loff_t*f_ops)
#ifdefOURS_HELLO_DEBUG
SERIAL_LED_read[--kernel--]\n"
ssize_tSERIAL_LED_write(structfile*file,constchar*buf,size_tcount,loff_t*f_ops)
#ifdefOURS_HELLO_DEBUG
SERIAL_LED_write[--kernel--]\n"
write_byte(*buf);
//-------------------IOCTL-----------------------
ssize_tSERIAL_LED_ioctl(structinode*inode,structfile*file,unsignedintcmd,longdata)
SERIAL_LED_ioctl[--kernel--]\n"
return0;
补充代码5
ssize_tSERIAL_LED_open(structinode*inode,structfile*file)
SERIAL_LED_open[--kernel--]\n"
补充代码6
//-------------------RELEASE/CLOSE---------------
ssize_tSERIAL_LED_release(structinode*inode,structfile*file)
SERIAL_LED_release[--kernel--]\n"
MOD_DEC_USE_COUNT;
补充代码7
structfile_operationsSERIAL_LED_ops={
SERIAL_LED_open,
SERIAL_LED_read,
SERIAL_LED_write,
SERIAL_LED_ioctl,
SERIAL_LED_release,
补充代码8
staticint__initHW_SERIAL_LED_init(void)
intret=-ENODEV;
ret=devfs_register_chrdev(SERIAL_LED_MAJOR,"
serial_led_ctl"
&
SERIAL_LED_ops);
sh