嵌入式程序设计大作业1.docx
《嵌入式程序设计大作业1.docx》由会员分享,可在线阅读,更多相关《嵌入式程序设计大作业1.docx(12页珍藏版)》请在冰豆网上搜索。
![嵌入式程序设计大作业1.docx](https://file1.bdocx.com/fileroot1/2022-12/14/fdfd45ea-10ff-4f0b-b238-103f65717c28/fdfd45ea-10ff-4f0b-b238-103f65717c281.gif)
嵌入式程序设计大作业1
大连理工大学
本科实验报告
课程名称:
嵌入式程序设计
学院(系):
软件学院
专业:
嵌入式
2011年5月28日
实验3:
ARM7/uClinux驱动设计实验
一、实验目的和要求
通过实验了解uClinux设备驱动程序模块结构;
通过实验掌握uClinux字符设备驱动程序编写;
通过实验了解EmbestEduKitIII44B0核心子板LED驱动程序原理。
二、实验原理和内容
1.uClinux设备驱动程序概述
uClinux设备驱动程序为特定的硬件提供给用户程序的一组标准化接口,它隐藏了设备工作的细节。
用户程序通过标准化系统调用,这些调用和特定的硬件是无关的,再由uClinux内核调用特定的设备驱动程序操作和控制特定的实际的硬件设备。
uClinux系统的设备分为三种类型,分别是字符设备(characterdevice),块设备(blockdevice)和网络接口(networkinterface)。
字符设备是能够像字节流一样被访问的设备,一般不使用缓存技术。
字符设备驱动程序最少应实现open、close、read和write系统调用。
典型的字符设备例子是终端设备(/dev/console)和串口(/dev/ttys0)。
2.LED电路设计
三、主要仪器设备
硬件:
PC机,EmbestEduKitIII44B0核心子板,EmbestARM标准/增强型仿真器
套件;
软件:
RedHat9.0操作系统、uClinux交叉编译工具链、flash烧写工具、超级终
端。
四、实验步骤与操作方法
1.编译驱动程序
当把LED设备驱动程序编写好以后,需要把它编译进内核。
当用户打开设备时,还需要一个在/dev目录的设备文件名称,这样驱动程序才能工作。
下面分步骤来介绍如何把驱动程序编译进内核以及创建设备文件名称。
(1)在/uClinux-dist/linux-2.4.x/drivers目录下新建名为led的文件夹,把led驱动程序和makefile复制到其中。
(2)修改/uClinux-dist/linux-2.4.x/drivers/Makefile文件,在Makefile中添加如下加粗代码。
#MakefileforLEDdrivers
#
mod-subdirs:
=diohilmtdsbusvideomacintoshusbinputtelephonyide\
message/i2omessage/fusionscsimdieee1394pnpisdnatm\
fc4net/hamradioi2cacpibluetoothusb/gadget
subdir-y:
=parportcharblocknetsoundmiscmediacdromhotplug
subdir-y+=led
subdir-m:
=$(subdir-y)
(3)修改/uClinux-dist/linux-2.4.x/Makefile文件,在“DRIVERS-y:
=”之后,添加如下
加粗代码,这样在连接uClinux内核映像文件时,能把led.o连接进去。
DRIVERS-n:
=
DRIVERS-y:
=
DRIVERS-$(CONFIG_ACPI_BOOT)+=drivers/acpi/acpi.o
DRIVERS-$(CONFIG_PARPORT)+=drivers/parport/driver.o
DRIVERS-y+=drivers/char/char.o\
drivers/block/block.o\
drivers/misc/misc.o\
drivers/net/net.o
DRIVERS-y+=drivers/led/led.o
(4)修改/uClinux-dist/vendors/Embest/EduKit/Makefile文件,在”DRIVERS=”后,添加如下加粗代码“led0,c,60,0”。
其中led0为设备名称,c代表字符设备,60为主
设备号,最后一个0是从设备号。
主设备号与驱动程序注册时的主设备号要求一致,
否则用户程序用设备文件名称请求打开设备时,内核无法根据主设备号找到对应的
设备驱动程序。
DEVICES=\
tty,c,5,0console,c,5,1cua0,c,5,64cua1,c,5,65\
\
led0,c,60,0\
(5)完成以上修改后,重新构造romfs和内核映像文件,并烧录到开发板的FLASH上。
(6)最后,启动uClinux。
使用cp命令,简单测试一下LED驱动程序。
把任意一个文件复制到led设备上,检查是否能点亮其中某个LED。
例如在uClinux输入终端执行如下命令:
$cp/bin/init/dev/led0
观察并记录实验现象。
2.测试驱动程序
(1)准备实验环境,使用EmbestEduKit44B0目标开发板附带的串口线分别连接到目标板上的UART0和PC机的串口,将Embest仿真器的JTAG接口与EmbestEduKit
的JTAG接口相连,仿真器的PARALLEL接口与PC机的并口相连。
(2)在PC机上运行Windows附件中自带的超级中断串口通信程序(波特率115200、1位停止位、无检验位、无硬件流控制);或者使用其它串口通信程序。
(3)交叉编译led测试程序test-led。
(4)编译好测试程序后,启动目标板的uClinux操作系统,在PC机上观察超级终端程序主窗口,可以看到如下界面:
Sashcommandshell(version1.1.1)
/>
在PC机上运行tftp服务端程序,设置好传送文件路径,通过以太网使用tftp把
test-led下载到/var目录下。
在uClinux的输入终端里输入如下命令:
>cdvar
/var>tftp–g192.168.0.101–r./test-led(此处IP地址可能不同)
(5)修改test-led程序的属性,使其拥有可执行属性,然后运行test-led。
观察test-led
点亮目标开发板上的LED的情况,并描述。
var>chmod777test-led
var>./test-led
2.编写驱动程序
编写测试程序,让各个LED以人眼能够识别的频率依次闪烁,每个LED持续闪烁大约3秒钟,找到编号为1-4的LED在实验板上的布局,绘制布局图,添加到实验报告中。
四、实验数据记录和处理
驱动程序代码:
/*
*linux/deriver/led/ekii-led.c
*leddriverforEmbestEduKitII
*Copyright(C)2005Embest<>
*/
#include
#include
#include
#include
#include
#include//errorcodes
#include//size_t
#include//mdelay
#include
#include
#undefDEBUG
#ifdefDEBUG
#defineTRACE(str,args...)printk("led:
"str,##args)
#else
#defineTRACE(str,args...)
#endif
//TRACE没用
#defineLED_MAJOR60//设备号
#defineLED_DEVNAME"led"//设备名称
#defineGPC_MASK(3<<8)//掩码.pc8/pc9
#defineGPF_MASK(3<<3)//掩码.pf3/pf4
#defineGET_DATA(c,f)((u8)(((~c&0x80)>>5)|((~c&0x100)>>7)|((~f&0x10)>>3)|((~f&0x08)>>3)))
//把b、f的值组合为一个8位数据
//3210
//pc8pc9pf4pf3
#defineSET_DATA(t,c,f)(c=(((~t&0x08)<<5)|((~t&0x04)<<7)),f=(((~t&0x02)<<3)|((~t&0x01)<<3)))
//给b、f赋值
//t3210
//pc8pc9pf4pf3
#defineLED_LOCK(u)down(&u->lock);//信号量
#defineLED_UNLOCK(u)up(&u->lock);//信号量
structunit{//定义一个结构类型,成员是整型指针,代表各个口寄存器,以及两个变量
structsemaphorelock;//定义信号量
u32*PCONC;/*PCONBregister*/
u32*PDATC;/*PDATBregister*/
u32*PCONF;/*PCONFregister*/
u32*PDATF;/*PDATFregister*/
u32*PUPF;/*PUPFregister*/
u32c;/*storeLED1andLED2value*/
u32f;/*storeLED3andLED4value*/
};
staticchar*version="EmbestEdukit-IIleddriverversion1.0(2005-04-18)<>\n";
staticstructunitled_unit={//给结构体变量部分成员赋值
.PCONC=(u32*)S3C44B0X_PCONC,
.PDATC=(u32*)S3C44B0X_PDATC,
.PUPC=(u32*)S3C44B0X_PUPC,
.PCONF=(u32*)S3C44B0X_PCONF,
.PDATF=(u32*)S3C44B0X_PDATF,
.PUPF=(u32*)S3C44B0X_PUPF,
};
staticvoidled_set_value(structunit*unit,u8val)
{//设置pc、pf口的值,初始化时用到两次,第一次val是0x0f,第二次是0
u32temp;
SET_DATA(val,unit->c,unit->f);
//第一次:
用0x0f给c、f赋值为:
0、0。
点亮4个led
//第二次:
用0给c、f赋值,使pc8、pc9、pf4、pf3均为1,关闭4个led
//写函数还要多次调用本函数
temp=*unit->PDATC;
//取pb口状态
temp&=~GPC_MASK;
//清pb4、pb5
temp|=unit->c;
//用新值更新temp
*unit->PDATC=temp;
//写回pc口
temp=*unit->PDATF;
temp&=~GPF_MASK;
temp|=unit->f;
*unit->PDATF=temp;
}
staticu8led_get_value(structunit*unit)
{
u8temp=GET_DATA(unit->c,unit->f);
returntemp;
}
staticintled_open(structinode*inode,structfile*file)
{
TRACE("open\n");
file->private_data=&led_unit;//这个操作为read服务
MOD_INC_USE_COUNT;
return0;
}
staticintled_release(structinode*inode,structfile*file)
{
TRACE("release\n");
MOD_DEC_USE_COUNT;//模块引用计数自减
return0;
}
staticssize_tled_read(structfile*file,char*buf,size_tcount,loff_t*offset)
{
u8temp;
intret;
structunit*unit=(structunit*)file->private_data;
//使在read函数中可以访问硬件寄存器
TRACE("read\n");
if(count>1)
count=1;
LED_LOCK(unit);//信号量操作
temp=led_get_value(unit);//读数据
ret=copy_to_user(buf,&temp,count)?
-EFAULT:
count;
//把数据从内核空间拷贝到用户空间,
//copy_from_user、copy_to_user函数返回不能被复制的字节数,
//因此,如果完全复制成功,返回值为0。
LED_UNLOCK(unit);
//信号量操作
returnret;
}
staticssize_tled_write(structfile*file,constchar*buf,size_tcount,loff_t*offset)
{
u8temp;
intret;
structunit*unit=(structunit*)file->private_data;
//使在write函数中可以访问硬件寄存器
TRACE("write\n");
if(count>1)
count=1;
LED_LOCK(unit);//信号量操作
ret=copy_from_user(&temp,buf,count)?
-EFAULT:
count;
//把数据从用户空间拷贝到内核空间
if(ret)
led_set_value(unit,temp);
LED_UNLOCK(unit);
//信号量操作
returnret;
}
staticstructfile_operationsled_ops={
owner:
THIS_MODULE,
read:
led_read,
write:
led_write,
open:
led_open,
release:
led_release,
};
/*
*leddeviceinit
*/
staticvoid__initled_init(structunit*unit)
{
u32temp;
/*initdevicelock*/
init_MUTEX(&unit->lock);
/*initioport*/
temp=*unit->PCONC;
temp&=~((3<<16)|(3<<18));//16、17、18、19均0,表示pf3、pf4输入
temp|=((1<<16)|(1<<18));//16、18为1,所以pc8、pc9都是输出口
//pconc8、pconc9:
01表示输出
*unit->PCONC=temp;
temp=*unit->PUPC;
temp|=(3<<8);//8、9置高,pc8、pc9无上拉
*unit->PUPC=temp;
temp=*unit->PCONF;
temp&=~((3<<8)|(3<<6));//6、7、8、9均0,表示pf3、pf4输入
temp|=((1<<8)|(1<<6));//6、8为1,所以pf3、pf4都是输出口
*unit->PCONF=temp;
temp=*unit->PUPF;
temp|=(3<<3);//4、3置高,pf3、pf4无上拉
*unit->PUPF=temp;
/*initdataandturnonled*/
led_set_value(unit,0x0f);
/*delaysometime*/
mdelay(100);
/*turnoffled*/
led_set_value(unit,0x00);
}
/*
*moduleinit
*/
int__initled_init_module(void)
{
intres;
TRACE("init_module\n");
/*printversioninformation*/
printk(KERN_INFO"%s",version);
/*registerleddevice*/
res=register_chrdev(LED_MAJOR,LED_DEVNAME,&led_ops);
if(res<0){
printk("ekii-led.o:
unabletogetmajor%dforleddevice.\n",LED_MAJOR);
returnres;
}
/*thencallled_init()*/
led_init(&led_unit);
return0;
}
/*
*modulecleanup
*/
void__exitled_cleanup(void)
{
intres;
TRACE("cleanup\n");
/*unregisterleddevice*/
res=unregister_chrdev(LED_MAJOR,LED_DEVNAME);
if(res<0)
printk("ekii-led.o:
unabletoreleasemajor%dforleddevice.\n",LED_MAJOR);
}
module_init(led_init_module);
module_exit(led_cleanup);
MODULE_DESCRIPTION("EduKit-IIleddriver");
MODULE_AUTHOR("Embesttech&infoCo.,Ltd.<>");
MODULE_LICENSE("GPL");
测试程序代码:
#include
#include
#include
#include
#defineLED_NUM4
intmain(intargc,char**argv)
{
inti,j,wval,rval,fd;
printf("TestLED...\n");
/*openleddevice*/
fd=open("/dev/led0",O_RDWR);
/*testsingleled*/
while
(1)
{
for(i=0;i<3;i++)
{
for(j=0;j{
wval=1<write(fd,&wval,1);
read(fd,&rval,1);
printf("TurnonLED%d,readbackval=0x%02X\n",j+1,rval);
/*delay500ms*/
usleep(3000*1000);
}
}
}
/*closeleddevice*/
close(fd);
return0;
}
六、实验结果与分析
将驱动程序添加到文件系统,重新编译文件系统和内核映像,将编译好的内核映像烧写到目标板上,重启目标板,使用tftp将驱动测试程序传入到目标板中,运行测试程序,试验箱上的4个LED灯按顺序依次点亮。
七、讨论、建议、质疑
1.考虑到自动匹配不同寄存器的长度,在给寄存器置位时,对数据进行使用取非操作可以自动补齐到寄存器的长度。